From 997af825e260e88553ee98a08762e8e041520a9b Mon Sep 17 00:00:00 2001
From: yeg <1347747024@qq.com>
Date: 星期二, 01 九月 2026 22:28:32 +0800
Subject: [PATCH] 修复模拟器开机未重置夹具生命周期

---
 ruoyi-device/src/main/java/com/ruoyi/device/bts/BtsDevelopmentSimulator.java                    |   29 +++++++++++++++++++++++------
 ruoyi-device/src/test/java/com/ruoyi/device/bts/BtsDevelopmentSimulatorRecordStepCycleTest.java |   24 ++++++++++++++++++++++++
 2 files changed, 47 insertions(+), 6 deletions(-)

diff --git a/ruoyi-device/src/main/java/com/ruoyi/device/bts/BtsDevelopmentSimulator.java b/ruoyi-device/src/main/java/com/ruoyi/device/bts/BtsDevelopmentSimulator.java
index 140acd0..5126a8f 100644
--- a/ruoyi-device/src/main/java/com/ruoyi/device/bts/BtsDevelopmentSimulator.java
+++ b/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 数据。 */
diff --git a/ruoyi-device/src/test/java/com/ruoyi/device/bts/BtsDevelopmentSimulatorRecordStepCycleTest.java b/ruoyi-device/src/test/java/com/ruoyi/device/bts/BtsDevelopmentSimulatorRecordStepCycleTest.java
index be948f4..ea03041 100644
--- a/ruoyi-device/src/test/java/com/ruoyi/device/bts/BtsDevelopmentSimulatorRecordStepCycleTest.java
+++ b/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");

--
Gitblit v1.7.1