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);
|
}
|
|
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;
|
}
|
}
|
}
|