yeg
2026-09-09 f38ca8a9f8454ac2eebdc268c89efd0e436b3cf2
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
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 replaceCollectors(String adapterCode, List<CollectorCandidate> 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();
    }
 
    /** 按设备编码查询指定时刻未过期的采集器观测;在线和登记状态由业务层校验。 */
    default Optional<CollectorCandidate> findCollector(String deviceCode, Instant now)
    {
        return snapshot(now).collectors().stream().filter(item -> item.deviceCode().equals(deviceCode)).findFirst();
    }
 
    record CandidateSnapshot(List<CabinetCandidate> cabinets, List<CollectorCandidate> collectors)
    {
        public CandidateSnapshot
        {
            cabinets = List.copyOf(cabinets);
            collectors = List.copyOf(collectors);
        }
    }
 
    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)
    {
        public CabinetCandidate { channels = List.copyOf(channels); }
        public boolean online() { return channels.stream().anyMatch(ChannelCandidate::online); }
    }
 
    record ChannelCandidate(ChannelIdentity identity, boolean online, DeviceWorkStatus workStatus,
            Instant observedAt, RealtimeSnapshot latestSnapshot)
    {
        /** 兼容仅有状态的旧发现候选。 */
        public ChannelCandidate(ChannelIdentity identity, boolean online, DeviceWorkStatus workStatus,
                Instant observedAt)
        {
            this(identity, online, workStatus, observedAt, null);
        }
    }
 
    record CollectorCandidate(String adapterCode, String deviceCode, String ip, String mac, String name,
            String manufacturer, String model, int channelCount, boolean online,
            Instant observedAt, Instant expiresAt) { }
}