yeg
2026-09-01 997af825e260e88553ee98a08762e8e041520a9b
修复模拟器开机未重置夹具生命周期

一键关机后再次开机时清理检测柜历史和播放状态,重新从首循环首工步初始化,同时保留采集器历史。补充电源循环回归测试,覆盖旧循环进度和历史清理。
2个文件已修改
53 ■■■■■ 已修改文件
ruoyi-device/src/main/java/com/ruoyi/device/bts/BtsDevelopmentSimulator.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-device/src/test/java/com/ruoyi/device/bts/BtsDevelopmentSimulatorRecordStepCycleTest.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-device/src/main/java/com/ruoyi/device/bts/BtsDevelopmentSimulator.java
@@ -228,11 +228,23 @@
    }
    /**
     * 模拟现场人工统一开关机:关机停止采样但保留历史,开机后检测柜通道保持待机。
     * 模拟现场人工统一开关机:关机停止采样,开机重启检测柜夹具生命周期并保持通道待机。
     * 采集器历史继续保留,检测柜夹具历史在关机后的开机上升沿清理。
     * 该操作只改变模拟设备事实,不代表系统具备真实设备控制能力。
     */
    public synchronized void setAllDevicesPoweredOn(boolean poweredOn)
    {
        boolean wasPoweredOff = (!state.devices().isEmpty() || !state.collectors().isEmpty())
                && state.devices().stream().noneMatch(DeviceState::online)
                && state.collectors().stream().noneMatch(CollectorState::online);
        Instant changedAt = Instant.now();
        if (poweredOn && wasPoweredOff)
        {
            // 一键开机代表新一轮演示;清理检测柜夹具,避免序号从 1 重放并与旧生命周期冲突。
            for (DeviceState device : state.devices())
                for (ChannelState channel : device.channels())
                    resetBtsFixtureLifecycle(btsKey(device, channel.channelId()), changedAt);
        }
        List<DeviceState> devices = state.devices().stream()
                .map(device -> new DeviceState(device.deviceId(), device.ip(), poweredOn,
                        device.channels().stream()
@@ -240,13 +252,21 @@
                                        DeviceWorkStatus.STOPPED))
                                .toList(), device.profile()))
                .toList();
        Instant changedAt = Instant.now();
        List<CollectorState> collectors = state.collectors().stream()
                .map(collector -> DevelopmentCollectorSimulation.resize(collector, collector.ip(),
                        collector.channelCount(), poweredOn, changedAt))
                .toList();
        persist(new SimulatorState(state.version(), devices, collectors, state.nextDeviceId(),
                state.nextCollectorId(), state.tombstones()));
    }
    /** 清理检测柜夹具的历史和播放指针;采集器历史由总电源开关独立保留。 */
    private void resetBtsFixtureLifecycle(String key, Instant now)
    {
        btsHistory.remove(key);
        stateStore.deleteBtsHistory(key);
        stateStore.deleteBtsPlaybackState(key);
        savePlayback(key, defaultPlayback(now));
    }
    /** 按每台采集器自己的频率推进实时和历史采样。 */
@@ -371,10 +391,7 @@
        DeviceState device = requireDevice(deviceId);
        requireChannel(device, channelId);
        String key = btsKey(device, channelId);
        btsHistory.remove(key);
        stateStore.deleteBtsHistory(key);
        stateStore.deleteBtsPlaybackState(key);
        savePlayback(key, defaultPlayback(Instant.now()));
        resetBtsFixtureLifecycle(key, Instant.now());
    }
    /** 返回播放状态及当前通道的 record、step、cycle 数据。 */
ruoyi-device/src/test/java/com/ruoyi/device/bts/BtsDevelopmentSimulatorRecordStepCycleTest.java
@@ -151,6 +151,30 @@
    }
    @Test
    void powerCycleResetsBtsFixtureLifecycleAndClearsHistory() throws Exception
    {
        InMemoryBtsSimulatorStateStore store = new InMemoryBtsSimulatorStateStore();
        try (BtsDevelopmentSimulator simulator = new BtsDevelopmentSimulator(0, store))
        {
            simulator.updateChannel(1, 1, true, DeviceWorkStatus.WORKING);
            simulator.nextBtsCycle(1, 1);
            assertEquals(2, simulator.btsPlayback(1, 1).playback().cycleNo());
            simulator.advanceBts(Instant.now().plusSeconds(60));
            int historyCount = simulator.btsRecords(1, 1).size();
            simulator.setAllDevicesPoweredOn(false);
            simulator.setAllDevicesPoweredOn(true);
            BtsDevelopmentSimulator.PlaybackSnapshot playback = simulator.btsPlayback(1, 1);
            assertEquals(1, playback.playback().cycleNo());
            assertEquals(1, playback.playback().stepNo());
            assertFalse(playback.playback().naturallyFinished());
            assertTrue(historyCount > 0);
            assertEquals(0, playback.records().size());
        }
    }
    @Test
    void playbackFollowsExcelFixtureThroughAllCyclesAndRecords() throws Exception
    {
        Instant start = Instant.parse("2026-08-31T00:00:00Z");