yeg
4 天以前 46634e427fdd43e80c017938d1aa35f25a3166ac
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
package com.battery.device.api.discovery;
 
import java.time.Instant;
import java.util.List;
import java.util.Optional;
import com.battery.device.api.model.ChannelIdentity;
import com.battery.device.api.model.DeviceWorkStatus;
import com.battery.device.api.model.RealtimeSnapshot;
 
/** 保存未登记发现候选和正式设备的实时观测快照。 */
public interface DeviceDiscoveryCandidateStore
{
    /** upsert 本轮检测柜观测,未返回设备保留到逻辑过期。 */
    void recordCabinets(List<CabinetCandidate> candidates);
 
    /** 删除指定适配器的全部发现候选;仅供人工重置或实机联调清理。 */
    void removeAdapter(String adapterCode);
 
    /** 返回指定时刻尚未过期的候选和实时状态。 */
    CandidateSnapshot snapshot(Instant now);
 
    /** 按设备编码查询指定时刻未过期的检测柜观测;在线和登记状态由业务层校验。 */
    default Optional<CabinetCandidate> findCabinet(String deviceCode, Instant now)
    {
        return snapshot(now).cabinets().stream().filter(item -> item.deviceCode().equals(deviceCode)).findFirst();
    }
 
    record CandidateSnapshot(List<CabinetCandidate> cabinets)
    {
        public CandidateSnapshot
        {
            cabinets = List.copyOf(cabinets);
        }
    }
 
    /**
     * 未登记的候选设备。{@code lastDataAt} 取该设备各通道最近的采样行时间,
     * 供操作员判断哪几台设备现在真的在产出数据;它不改变候选的取舍,登记范围由操作员决定。
     */
    record CabinetCandidate(String adapterCode, Long adapterId, String deviceCode, int devtype, int devid,
            String ip, String mac, String name, String manufacturer, String model,
            List<ChannelCandidate> channels, Instant observedAt, Instant expiresAt, Instant lastDataAt)
    {
        public CabinetCandidate { channels = List.copyOf(channels); }
        public boolean online() { return channels.stream().anyMatch(ChannelCandidate::online); }
 
        /** 兼容不含最近数据时间的既有候选。 */
        public CabinetCandidate(String adapterCode, Long adapterId, String deviceCode, int devtype, int devid,
                String ip, String mac, String name, String manufacturer, String model,
                List<ChannelCandidate> channels, Instant observedAt, Instant expiresAt)
        {
            this(adapterCode, adapterId, deviceCode, devtype, devid, ip, mac, name, manufacturer, model,
                    channels, observedAt, expiresAt, null);
        }
    }
 
    /**
     * 通道候选;{@code lastDataAt} 是该通道来源里最近一次采样行的设备时间。
     * {@code sourceDisplayNo} 是来源自己的通道叫法(见 {@code DetectedChannel}),
     * 只用于界面与登记显示,不参与通道身份。
     */
    record ChannelCandidate(ChannelIdentity identity, boolean online, DeviceWorkStatus workStatus,
            Instant observedAt, RealtimeSnapshot latestSnapshot, Instant lastDataAt, String sourceDisplayNo)
    {
        /** 兼容仅有状态的旧发现候选。 */
        public ChannelCandidate(ChannelIdentity identity, boolean online, DeviceWorkStatus workStatus,
                Instant observedAt)
        {
            this(identity, online, workStatus, observedAt, null, null, null);
        }
 
        /** 兼容不含最近数据时间的既有候选。 */
        public ChannelCandidate(ChannelIdentity identity, boolean online, DeviceWorkStatus workStatus,
                Instant observedAt, RealtimeSnapshot latestSnapshot)
        {
            this(identity, online, workStatus, observedAt, latestSnapshot, null, null);
        }
 
        /** 兼容不带来源通道叫法的既有候选。 */
        public ChannelCandidate(ChannelIdentity identity, boolean online, DeviceWorkStatus workStatus,
                Instant observedAt, RealtimeSnapshot latestSnapshot, Instant lastDataAt)
        {
            this(identity, online, workStatus, observedAt, latestSnapshot, lastDataAt, null);
        }
    }
}