yeg
2026-08-30 b985e6140e7f395ead7dc55e41e6b16d97f9f02b
ruoyi-device/src/main/java/com/ruoyi/device/simulator/BtsSimulatorStateStore.java
@@ -9,12 +9,45 @@
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 void saveAndDeleteCollectorHistory(SimulatorState state, String collectorCode)
    {
        throw new UnsupportedOperationException("Atomic collector deletion is not supported");
    }
    /** 读取短期保留的最近请求。 */
    List<RecentRequest> loadRecentRequests();
@@ -38,22 +71,32 @@
        }
    }
    record DeviceState(int deviceId, String ip, boolean online, List<ChannelState> channels)
    record DeviceState(int deviceId, String ip, boolean online, List<ChannelState> channels,
            BtsDeviceProfile profile)
    {
        public DeviceState { channels = List.copyOf(channels); }
        public DeviceState
        {
            channels = List.copyOf(channels);
            profile = profile == null ? BtsDeviceProfile.virtual(channels.size()) : profile;
        }
        /** 兼容旧版只保存拓扑和通道状态的模拟设备。 */
        public DeviceState(int deviceId, String ip, boolean online, List<ChannelState> channels)
        {
            this(deviceId, ip, online, channels, BtsDeviceProfile.virtual(channels.size()));
        }
    }
    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,
            List<CollectorMeasurementRecord> history, CollectorHistoryFaults historyFaults)
            long historyRecordCount, CollectorHistoryFaults historyFaults)
    {
        public CollectorState
        {
            channels = List.copyOf(channels);
            latestMeasurements = List.copyOf(latestMeasurements);
            history = List.copyOf(history);
            historyFaults = historyFaults == null ? CollectorHistoryFaults.none() : historyFaults;
        }
@@ -63,7 +106,7 @@
        {
            this(collectorId, deviceCode, ip, mac, name, manufacturer, model, channelCount, online,
                    DevelopmentCollectorSimulation.DEFAULT_SAMPLE_INTERVAL_MS, 1L, null,
                    DevelopmentCollectorSimulation.defaultChannels(channelCount), List.of(), List.of(),
                    DevelopmentCollectorSimulation.defaultChannels(channelCount), List.of(), 0L,
                    CollectorHistoryFaults.none());
        }
    }
@@ -83,6 +126,17 @@
            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 CollectorHistoryFaults(List<Long> missingSequences, Long duplicateSequence,
            Long conflictingSequence, boolean outOfOrder)
    {
@@ -99,6 +153,32 @@
    record ChannelState(int channelId, boolean online, DeviceWorkStatus workStatus) { }
    /** BTS 检测柜的可配置厂商 profile;参数用于发现展示和边界内的演示数据生成。 */
    record BtsDeviceProfile(int devtype, int subDeviceId, String name, String manufacturer, String model,
            String serialNumber, String firmwareVersion, int channelCount, BigDecimal currentMinA,
            BigDecimal currentMaxA, BigDecimal voltageMinV, BigDecimal voltageMaxV, int sampleIntervalMs)
    {
        public BtsDeviceProfile
        {
            if (devtype < 0 || subDeviceId < 0 || channelCount < 1 || sampleIntervalMs < 1)
                throw new IllegalArgumentException("Invalid BTS device profile limits");
            if (name == null || name.isBlank() || manufacturer == null || manufacturer.isBlank()
                    || model == null || model.isBlank() || serialNumber == null || serialNumber.isBlank()
                    || firmwareVersion == null || firmwareVersion.isBlank())
                throw new IllegalArgumentException("BTS device profile metadata must not be blank");
            if (currentMinA == null || currentMaxA == null || voltageMinV == null || voltageMaxV == null
                    || currentMinA.compareTo(currentMaxA) > 0 || voltageMinV.compareTo(voltageMaxV) > 0)
                throw new IllegalArgumentException("Invalid BTS device profile ranges");
        }
        public static BtsDeviceProfile virtual(int channelCount)
        {
            return new BtsDeviceProfile(22, 1, "dev-sim 设备", "开发模拟器", "BTS Virtual Cabinet",
                    "SIM-VIRTUAL", "dev-sim", channelCount, new BigDecimal("-6.000"),
                    new BigDecimal("6.000"), BigDecimal.ZERO, new BigDecimal("5.000"), 100);
        }
    }
    record ChannelTombstone(int deviceId, String ip, int channelId, int remainingDiscoveries) { }
    record RecentRequest(Instant receivedAt, String command, String request) { }