yeg
4 天以前 3194ad3ce5526148df9009aeaa917b888dc53b40
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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
package com.battery.device.api.model;
 
import java.time.Instant;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
 
/** 厂商无关的一次通道观测结果。不得携带测试、持久化或传输层对象。 */
public record DeviceObservation(
        DeviceIdentity device,
        ChannelIdentity channel,
        DeviceConnectionStatus connectionStatus,
        DeviceWorkStatus channelWorkStatus,
        List<PlannedStep> plannedSteps,
        Instant deviceTime,
        Map<String, Measurement> measurements,
        ObservationFailureReason failureReason)
{
    public DeviceObservation
    {
        device = Objects.requireNonNull(device, "device");
        channel = Objects.requireNonNull(channel, "channel");
        if (!device.equals(DeviceIdentity.from(channel)))
            throw new IllegalArgumentException("device and channel identities do not match");
        connectionStatus = Objects.requireNonNull(connectionStatus, "connectionStatus");
        channelWorkStatus = channelWorkStatus == null ? DeviceWorkStatus.UNOBSERVABLE : channelWorkStatus;
        plannedSteps = plannedSteps == null ? List.of() : List.copyOf(plannedSteps);
        measurements = measurements == null ? Map.of() : Map.copyOf(measurements);
        if (connectionStatus == DeviceConnectionStatus.ONLINE && failureReason != null)
            throw new IllegalArgumentException("online observation cannot contain a failure reason");
    }
 
    /** 将已有实时快照提升为统一观测模型,兼容旧适配器实现。 */
    public static DeviceObservation from(RealtimeSnapshot snapshot)
    {
        Objects.requireNonNull(snapshot, "snapshot");
        DeviceConnectionStatus status = snapshot.connectionStatus() == null
                ? DeviceConnectionStatus.ONLINE : snapshot.connectionStatus();
        if (status == DeviceConnectionStatus.OFFLINE)
            return offline(snapshot.channel(), snapshot.failureReason() == null ? "OFFLINE" : snapshot.failureReason());
        Map<String, Measurement> measurements = new LinkedHashMap<>(snapshot.measurements());
        addScalar(measurements, "current", snapshot.currentA(), "A");
        addScalar(measurements, "voltage", snapshot.voltageV(), "V");
        addScalar(measurements, "capacity", snapshot.capacityAh(), "Ah");
        addScalar(measurements, "energy", snapshot.energyWh(), "Wh");
        return new DeviceObservation(DeviceIdentity.from(snapshot.channel()), snapshot.channel(), status,
                snapshot.workStatus(),
                snapshot.plannedSteps(), snapshot.deviceTime(), measurements,
                parseFailureReason(snapshot.failureReason()));
    }
 
    private static void addScalar(Map<String, Measurement> measurements, String code,
            java.math.BigDecimal value, String unit)
    {
        if (value != null) measurements.putIfAbsent(code, new Measurement(code, value, null, unit, "DEVICE"));
    }
 
    /** 返回本次观测是否确认设备在线。 */
    public boolean online()
    {
        return connectionStatus == DeviceConnectionStatus.ONLINE;
    }
 
    /** 兼容调用方以通道工作状态命名的读取方式。 */
    public DeviceWorkStatus workStatus()
    {
        return channelWorkStatus;
    }
 
    /** 创建不携带过期设备事实的离线观测。 */
    public static DeviceObservation offline(ChannelIdentity channel, String failureReason)
    {
        Objects.requireNonNull(channel, "channel");
        ObservationFailureReason reason = parseFailureReason(failureReason);
        return new DeviceObservation(DeviceIdentity.from(channel), channel, DeviceConnectionStatus.OFFLINE,
                DeviceWorkStatus.UNOBSERVABLE, List.of(), null, Map.of(),
                reason == null ? ObservationFailureReason.OFFLINE : reason);
    }
 
    /** 按失败分类创建不携带过期设备事实的离线观测;通道工作状态一律为 UNOBSERVABLE。 */
    public static DeviceObservation offline(ChannelIdentity channel, ObservationFailureReason failureReason)
    {
        Objects.requireNonNull(channel, "channel");
        return new DeviceObservation(DeviceIdentity.from(channel), channel, DeviceConnectionStatus.OFFLINE,
                DeviceWorkStatus.UNOBSERVABLE, List.of(), null, Map.of(),
                failureReason == null ? ObservationFailureReason.OFFLINE : failureReason);
    }
 
    private static ObservationFailureReason parseFailureReason(String value)
    {
        if (value == null || value.isBlank()) return null;
        try
        {
            return ObservationFailureReason.valueOf(value.trim().toUpperCase(java.util.Locale.ROOT));
        }
        catch (IllegalArgumentException ignored)
        {
            return ObservationFailureReason.INVALID_RESPONSE;
        }
    }
}