| | |
| | | public interface BtsSimulatorStateStore |
| | | { |
| | | int CURRENT_VERSION = 1; |
| | | int MAX_HISTORY_BATCH_SIZE = 1000; |
| | | /** 开发演示只保留有限历史,避免 Redis 随运行时间无限增长。 */ |
| | | long MAX_HISTORY_RECORDS = 20_000L; |
| | | |
| | | /** 读取已有状态;仅在状态不存在时原子写入并返回默认状态。 */ |
| | | /** 读取已有状态,仅在不存在时保存默认拓扑。 */ |
| | | SimulatorState loadOrInitialize(SimulatorState defaultState); |
| | | |
| | | /** 开发模拟器启动时清理主状态中的旧内嵌历史,保留独立存储的历史。 */ |
| | | default SimulatorState resetHistoryForDemoStartup(SimulatorState state) { return state; } |
| | | |
| | | /** 完整保存下一版模拟器状态。 */ |
| | | /** 保存设备配置和观测序号;返回前必须完成写入。 */ |
| | | void save(SimulatorState state); |
| | | |
| | | /** 状态和独立历史全部校验通过后,提交兼容旧格式所需的迁移。 */ |
| | | default void completeValidatedRestore(SimulatorState state) { } |
| | | |
| | | /** 原子保存主状态并追加本轮新产生的原始历史。 */ |
| | | default void save(SimulatorState state, List<CollectorHistoryAppend> historyAppends) |
| | | { |
| | | if (!historyAppends.isEmpty()) |
| | | throw new UnsupportedOperationException("Collector history append is not supported"); |
| | | save(state); |
| | | } |
| | | |
| | | /** 按原始写入顺序或反向顺序读取有界历史批次。 */ |
| | | default CollectorHistoryBatch loadCollectorHistoryPage(String collectorCode, long offset, int limit, |
| | | boolean reverse) |
| | | { |
| | | if (offset < 0) throw new IllegalArgumentException("offset must not be negative"); |
| | | if (limit < 1 || limit > MAX_HISTORY_BATCH_SIZE) |
| | | throw new IllegalArgumentException("limit must be between 1 and " + MAX_HISTORY_BATCH_SIZE); |
| | | return new CollectorHistoryBatch(offset, 0, false, 0, List.of()); |
| | | } |
| | | |
| | | /** 读取检测柜模拟历史;正式适配器不依赖该演示扩展。 */ |
| | | default BtsHistoryBatch loadBtsHistoryPage(String historyKey, long offset, int limit) |
| | | { |
| | | return new BtsHistoryBatch(offset, 0, false, 0, List.of()); |
| | | } |
| | | |
| | | /** 追加检测柜模拟历史,供重启续采合同使用。 */ |
| | | default void appendBtsHistory(String historyKey, BtsHistoryRecord record) { } |
| | | |
| | | /** 读取检测柜通道的播放状态;实现可在不支持播放持久化时返回 null。 */ |
| | | default BtsPlaybackState loadBtsPlaybackState(String historyKey) { return null; } |
| | | |
| | | /** 保存检测柜通道的播放状态。 */ |
| | | default void saveBtsPlaybackState(String historyKey, BtsPlaybackState state) { } |
| | | |
| | | /** 删除检测柜通道的播放状态。 */ |
| | | default void deleteBtsPlaybackState(String historyKey) { } |
| | | |
| | | /** 删除检测柜通道的持久化历史和对应索引。 */ |
| | | default void deleteBtsHistory(String historyKey) { } |
| | | |
| | | /** 原子提交检测柜新增历史与推进后的播放状态。 */ |
| | | default void saveBtsPlaybackAndAppendHistory(String historyKey, BtsPlaybackState state, |
| | | List<BtsHistoryRecord> records) |
| | | { |
| | | for (BtsHistoryRecord record : records) appendBtsHistory(historyKey, record); |
| | | saveBtsPlaybackState(historyKey, state); |
| | | } |
| | | |
| | | /** 原子保存删除采集器后的主状态,并删除该采集器的独立历史。 */ |
| | | default void saveAndDeleteCollectorHistory(SimulatorState state, String collectorCode) |
| | | { |
| | | throw new UnsupportedOperationException("Atomic collector deletion is not supported"); |
| | | } |
| | | |
| | | /** 读取短期保留的最近请求。 */ |
| | | List<RecentRequest> loadRecentRequests(); |
| | |
| | | void reset(SimulatorState defaultState); |
| | | |
| | | record SimulatorState(int version, List<DeviceState> devices, List<CollectorState> collectors, |
| | | int nextDeviceId, int nextCollectorId, List<ChannelTombstone> tombstones, |
| | | Boolean softwareAccessible, long observationSequence) |
| | | { |
| | | public SimulatorState(int version, List<DeviceState> devices, List<CollectorState> collectors, |
| | | int nextDeviceId, int nextCollectorId, List<ChannelTombstone> tombstones) |
| | | { |
| | | this(version, devices, collectors, nextDeviceId, nextCollectorId, tombstones, true, 0L); |
| | | } |
| | | |
| | | public SimulatorState |
| | | { |
| | | if (version != CURRENT_VERSION) |
| | | throw new IllegalStateException("Unsupported BTS simulator state version: " + version); |
| | | softwareAccessible = softwareAccessible == null ? true : softwareAccessible; |
| | | devices = List.copyOf(devices); |
| | | collectors = List.copyOf(collectors); |
| | | tombstones = List.copyOf(tombstones); |
| | |
| | | |
| | | record CollectorState(int collectorId, String deviceCode, String ip, String mac, String name, |
| | | String manufacturer, String model, int channelCount, boolean online, int sampleIntervalMs, |
| | | long nextSequence, Instant lastSampledAt, List<CollectorChannelState> channels, |
| | | List<CollectorRealtimeMeasurement> latestMeasurements, |
| | | long historyRecordCount, CollectorHistoryFaults historyFaults) |
| | | List<CollectorChannelState> channels, List<CollectorRealtimeMeasurement> latestMeasurements) |
| | | { |
| | | public CollectorState |
| | | { |
| | | channels = List.copyOf(channels); |
| | | latestMeasurements = List.copyOf(latestMeasurements); |
| | | historyFaults = historyFaults == null ? CollectorHistoryFaults.none() : historyFaults; |
| | | } |
| | | |
| | | /** 兼容只包含发现配置的旧模拟状态;Redis 读取边界会补齐采样合同。 */ |
| | | public CollectorState(int collectorId, String deviceCode, String ip, String mac, String name, |
| | | String manufacturer, String model, int channelCount, boolean online) |
| | | { |
| | | this(collectorId, deviceCode, ip, mac, name, manufacturer, model, channelCount, online, |
| | | DevelopmentCollectorSimulation.DEFAULT_SAMPLE_INTERVAL_MS, 1L, null, |
| | | DevelopmentCollectorSimulation.defaultChannels(channelCount), List.of(), 0L, |
| | | CollectorHistoryFaults.none()); |
| | | DevelopmentCollectorSimulation.DEFAULT_SAMPLE_INTERVAL_MS, |
| | | DevelopmentCollectorSimulation.defaultChannels(channelCount), List.of()); |
| | | } |
| | | } |
| | | |
| | | record CollectorChannelState(String channelCode, List<CollectorMetricState> metrics) |
| | | record CollectorChannelState(String channelCode, List<CollectorMetricState> metrics, DeviceWorkStatus workStatus) |
| | | { |
| | | public CollectorChannelState { metrics = List.copyOf(metrics); } |
| | | public CollectorChannelState |
| | | { |
| | | metrics = List.copyOf(metrics); |
| | | workStatus = workStatus == null ? DeviceWorkStatus.WORKING : workStatus; |
| | | } |
| | | |
| | | public CollectorChannelState(String channelCode, List<CollectorMetricState> metrics) |
| | | { |
| | | this(channelCode, metrics, DeviceWorkStatus.WORKING); |
| | | } |
| | | } |
| | | |
| | | record CollectorMetricState(String metricCode, BigDecimal value, String unit) { } |
| | |
| | | record CollectorRealtimeMeasurement(String adapterCode, String collectorCode, String channelCode, |
| | | long sequenceId, Instant deviceTime, String deviceTimeRaw, Instant serverReceivedAt, |
| | | String metricCode, BigDecimal value, String unit) { } |
| | | |
| | | record CollectorMeasurementRecord(String adapterCode, String collectorCode, String channelCode, |
| | | long sequenceId, Instant deviceTime, String deviceTimeRaw, Instant serverReceivedAt, |
| | | String metricCode, BigDecimal value, String unit) { } |
| | | |
| | | record CollectorHistoryAppend(String collectorCode, List<CollectorMeasurementRecord> records) |
| | | { |
| | | public CollectorHistoryAppend { records = List.copyOf(records); } |
| | | } |
| | | |
| | | record CollectorHistoryBatch(long offset, long nextOffset, boolean hasMore, long total, |
| | | List<CollectorMeasurementRecord> records) |
| | | { |
| | | public CollectorHistoryBatch { records = List.copyOf(records); } |
| | | } |
| | | |
| | | record BtsHistoryRecord(long sequenceId, Instant deviceTime, double currentA, double voltageV, |
| | | double capacityAh, double energyWh, int cycleNo, int stepNo, String stepName, String stepType, |
| | | Instant serverReceivedAt) |
| | | { |
| | | /** 兼容现有调用方;新生成记录应传入服务器接收时间。 */ |
| | | public BtsHistoryRecord(long sequenceId, Instant deviceTime, double currentA, double voltageV, |
| | | double capacityAh, double energyWh, int cycleNo, int stepNo, String stepName, String stepType) |
| | | { |
| | | this(sequenceId, deviceTime, currentA, voltageV, capacityAh, energyWh, cycleNo, stepNo, |
| | | stepName, stepType, Instant.now()); |
| | | } |
| | | |
| | | /** 为仅包含核心测量值的历史记录补齐默认工步信息。 */ |
| | | public BtsHistoryRecord(long sequenceId, Instant deviceTime, double currentA, double voltageV, |
| | | double capacityAh, double energyWh) |
| | | { |
| | | this(sequenceId, deviceTime, currentA, voltageV, capacityAh, energyWh, 1, 1, "恒流充电", "cc"); |
| | | } |
| | | } |
| | | |
| | | /** 检测柜单通道演示播放状态;与测试的接收状态相互独立。 */ |
| | | record BtsPlaybackState(boolean playing, int speedMultiplier, int cycleNo, int stepNo, |
| | | String stepName, String stepType, Instant simulatedAt, Instant wallClockAt, |
| | | long nextSequence, boolean naturallyFinished) |
| | | { |
| | | public BtsPlaybackState |
| | | { |
| | | if (speedMultiplier != 1 && speedMultiplier != 60 && speedMultiplier != 600) |
| | | throw new IllegalArgumentException("speedMultiplier must be 1, 60 or 600"); |
| | | if (cycleNo < 1 || stepNo < 1 || nextSequence < 1 || stepName == null || stepName.isBlank() |
| | | || stepType == null || stepType.isBlank()) |
| | | throw new IllegalArgumentException("Invalid BTS playback state"); |
| | | } |
| | | } |
| | | |
| | | record BtsHistoryBatch(long offset, long nextOffset, boolean hasMore, long total, |
| | | List<BtsHistoryRecord> records) |
| | | { |
| | | public BtsHistoryBatch { records = List.copyOf(records); } |
| | | } |
| | | |
| | | record CollectorHistoryFaults(List<Long> missingSequences, Long duplicateSequence, |
| | | Long conflictingSequence, boolean outOfOrder) |
| | | { |
| | | public CollectorHistoryFaults |
| | | { |
| | | missingSequences = missingSequences == null ? List.of() : List.copyOf(missingSequences); |
| | | } |
| | | |
| | | public static CollectorHistoryFaults none() |
| | | { |
| | | return new CollectorHistoryFaults(List.of(), null, null, false); |
| | | } |
| | | } |
| | | |
| | | record ChannelState(int channelId, boolean online, DeviceWorkStatus workStatus) { } |
| | | |
| | |
| | | |
| | | record ChannelTombstone(int deviceId, String ip, int channelId, int remainingDiscoveries) { } |
| | | |
| | | record RecentRequest(Instant receivedAt, String command, String request) { } |
| | | record RecentRequest(Instant receivedAt, String command, String request, String response) { } |
| | | } |