From b41d317e9b76b1158a9d21257698e55194bb39f5 Mon Sep 17 00:00:00 2001
From: yeg <1347747024@qq.com>
Date: 星期五, 04 九月 2026 22:52:09 +0800
Subject: [PATCH] 建立电池检测模块骨架与运行时装配

---
 ruoyi-admin/src/main/java/com/ruoyi/web/controller/device/BtsDevelopmentSimulatorController.java |  169 ++++++++++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 157 insertions(+), 12 deletions(-)

diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/device/BtsDevelopmentSimulatorController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/device/BtsDevelopmentSimulatorController.java
index 7563d63..a424ac9 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/device/BtsDevelopmentSimulatorController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/device/BtsDevelopmentSimulatorController.java
@@ -15,29 +15,44 @@
 import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.server.ResponseStatusException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Profile;
 import com.ruoyi.common.annotation.Anonymous;
-import com.ruoyi.device.bts.BtsDevelopmentSimulator;
-import com.ruoyi.device.bts.BtsDevelopmentSimulator.StateSnapshot;
-import com.ruoyi.device.discovery.DeviceDiscoveryCandidateStore;
-import com.ruoyi.device.model.DeviceWorkStatus;
-import com.ruoyi.device.simulator.BtsSimulatorStateStore.CollectorHistoryFaults;
-import com.ruoyi.device.simulator.BtsSimulatorStateStore.CollectorMetricState;
-import com.ruoyi.device.simulator.DevelopmentCollectorSimulation.CollectorHistoryPage;
-import com.ruoyi.device.simulator.DevelopmentCollectorSimulation.CollectorRealtimeSnapshot;
+import com.battery.device.simulator.BtsDevelopmentSimulator;
+import com.battery.device.simulator.BtsDevelopmentSimulator.StateSnapshot;
+import com.battery.device.discovery.DeviceDiscoveryCandidateStore;
+import com.battery.device.model.DeviceWorkStatus;
+import com.battery.device.simulator.BtsSimulatorStateStore.CollectorHistoryFaults;
+import com.battery.device.simulator.BtsSimulatorStateStore.CollectorMetricState;
+import com.battery.device.simulator.DevelopmentCollectorSimulation.CollectorHistoryPage;
+import com.battery.device.simulator.DevelopmentCollectorSimulation.CollectorRealtimeSnapshot;
+import com.battery.device.simulator.BtsDevelopmentSimulator.PlaybackSnapshot;
 
 /** 本机临时 BTS 模拟器控制 API。 */
 @Anonymous
 @RestController
+@Profile("dev-simulator")
 public class BtsDevelopmentSimulatorController
 {
     private final BtsDevelopmentSimulator simulator;
     private final DeviceDiscoveryCandidateStore candidateStore;
+    private final com.ruoyi.config.DevelopmentCollectorStatusSynchronizer statusSynchronizer;
 
+    @Autowired
     public BtsDevelopmentSimulatorController(BtsDevelopmentSimulator simulator,
-            DeviceDiscoveryCandidateStore candidateStore)
+            DeviceDiscoveryCandidateStore candidateStore,
+            com.ruoyi.config.DevelopmentCollectorStatusSynchronizer statusSynchronizer)
     {
         this.simulator = simulator;
         this.candidateStore = candidateStore;
+        this.statusSynchronizer = statusSynchronizer;
+    }
+
+    /** 保留独立模拟器测试和工具调用的构造入口,不触发数据库同步。 */
+    public BtsDevelopmentSimulatorController(BtsDevelopmentSimulator simulator,
+            DeviceDiscoveryCandidateStore candidateStore)
+    {
+        this(simulator, candidateStore, null);
     }
 
     /** 返回临时控制页;匿名放行后仍由本机拦截器限制来源。 */
@@ -63,13 +78,27 @@
         return simulator.snapshot();
     }
 
+    /** 模拟现场人工统一开关全部检测柜和采集器,不发送任何设备控制命令。 */
+    @PostMapping("/dev/bts-simulator/power")
+    public StateSnapshot setPower(@RequestBody SimulatorPowerCommand command)
+    {
+        if (command == null || command.poweredOn() == null)
+            throw badRequest(new IllegalArgumentException("poweredOn must be true or false"));
+        simulator.setAllDevicesPoweredOn(command.poweredOn());
+        synchronizeOnlineStatusIfConfigured();
+        return simulator.snapshot();
+    }
+
     /** 添加一台虚拟设备。 */
     @PostMapping("/dev/bts-simulator/devices")
     public StateSnapshot createDevice(@RequestBody CreateDeviceCommand command)
     {
         try
         {
-            simulator.addDevice(command.ip(), command.channelCount());
+            if (command.model() == null || command.model().isBlank())
+                simulator.addDevice(command.ip(), command.channelCount());
+            else
+                simulator.addNewareDevice(command.ip(), command.model());
             return simulator.snapshot();
         }
         catch (IllegalArgumentException exception)
@@ -85,6 +114,8 @@
         try
         {
             simulator.updateDevice(deviceId, command.ip(), command.channelCount(), command.online());
+            if (command.model() != null && !command.model().isBlank())
+                simulator.configureNewareProfile(deviceId, command.model());
             return simulator.snapshot();
         }
         catch (IllegalArgumentException exception)
@@ -131,6 +162,7 @@
         try
         {
             simulator.updateCollector(collectorId, command.ip(), command.channelCount(), command.online());
+            synchronizeOnlineStatusIfConfigured();
             return simulator.snapshot();
         }
         catch (IllegalArgumentException exception)
@@ -236,14 +268,125 @@
         }
     }
 
+    /** 返回检测柜通道播放、record、step 和 cycle 的当前快照。 */
+    @GetMapping("/dev/bts-simulator/devices/{deviceId}/channels/{channelId}/playback")
+    public PlaybackSnapshot btsPlayback(@PathVariable int deviceId, @PathVariable int channelId)
+    {
+        try { return simulator.btsPlayback(deviceId, channelId); }
+        catch (IllegalArgumentException exception) { throw badRequest(exception); }
+    }
+
+    @GetMapping("/dev/bts-simulator/devices/{deviceId}/channels/{channelId}/records")
+    public List<com.battery.device.simulator.BtsSimulatorStateStore.BtsHistoryRecord> btsRecords(
+            @PathVariable int deviceId, @PathVariable int channelId)
+    {
+        try { return simulator.btsRecords(deviceId, channelId); }
+        catch (IllegalArgumentException exception) { throw badRequest(exception); }
+    }
+
+    @GetMapping("/dev/bts-simulator/devices/{deviceId}/channels/{channelId}/steps")
+    public List<BtsDevelopmentSimulator.BtsStepRecord> btsSteps(
+            @PathVariable int deviceId, @PathVariable int channelId)
+    {
+        try { return simulator.btsSteps(deviceId, channelId); }
+        catch (IllegalArgumentException exception) { throw badRequest(exception); }
+    }
+
+    @GetMapping("/dev/bts-simulator/devices/{deviceId}/channels/{channelId}/cycles")
+    public List<BtsDevelopmentSimulator.BtsCycleRecord> btsCycles(
+            @PathVariable int deviceId, @PathVariable int channelId)
+    {
+        try { return simulator.btsCycles(deviceId, channelId); }
+        catch (IllegalArgumentException exception) { throw badRequest(exception); }
+    }
+
+    /** 设置检测柜通道演示倍速。 */
+    @PutMapping("/dev/bts-simulator/devices/{deviceId}/channels/{channelId}/playback")
+    public PlaybackSnapshot configureBtsPlayback(@PathVariable int deviceId, @PathVariable int channelId,
+            @RequestBody PlaybackCommand command)
+    {
+        try
+        {
+            if (command.speedMultiplier() != null) simulator.setBtsSpeed(deviceId, channelId, command.speedMultiplier());
+            if (command.playing() != null)
+            {
+                if (command.playing()) simulator.playBts(deviceId, channelId);
+                else simulator.pauseBts(deviceId, channelId);
+            }
+            return simulator.btsPlayback(deviceId, channelId);
+        }
+        catch (IllegalArgumentException exception) { throw badRequest(exception); }
+    }
+
+    @PostMapping("/dev/bts-simulator/devices/{deviceId}/channels/{channelId}/playback/play")
+    public PlaybackSnapshot playBts(@PathVariable int deviceId, @PathVariable int channelId)
+    {
+        try { simulator.playBts(deviceId, channelId); return simulator.btsPlayback(deviceId, channelId); }
+        catch (IllegalArgumentException exception) { throw badRequest(exception); }
+    }
+
+    @PostMapping("/dev/bts-simulator/devices/{deviceId}/channels/{channelId}/playback/pause")
+    public PlaybackSnapshot pauseBts(@PathVariable int deviceId, @PathVariable int channelId)
+    {
+        try { simulator.pauseBts(deviceId, channelId); return simulator.btsPlayback(deviceId, channelId); }
+        catch (IllegalArgumentException exception) { throw badRequest(exception); }
+    }
+
+    @PostMapping("/dev/bts-simulator/devices/{deviceId}/channels/{channelId}/playback/resume")
+    public PlaybackSnapshot resumeBts(@PathVariable int deviceId, @PathVariable int channelId)
+    {
+        try { simulator.resumeBts(deviceId, channelId); return simulator.btsPlayback(deviceId, channelId); }
+        catch (IllegalArgumentException exception) { throw badRequest(exception); }
+    }
+
+    @PostMapping("/dev/bts-simulator/devices/{deviceId}/channels/{channelId}/playback/next-step")
+    public PlaybackSnapshot nextBtsStep(@PathVariable int deviceId, @PathVariable int channelId)
+    {
+        try { simulator.nextBtsStep(deviceId, channelId); return simulator.btsPlayback(deviceId, channelId); }
+        catch (IllegalArgumentException exception) { throw badRequest(exception); }
+    }
+
+    @PostMapping("/dev/bts-simulator/devices/{deviceId}/channels/{channelId}/playback/next-cycle")
+    public PlaybackSnapshot nextBtsCycle(@PathVariable int deviceId, @PathVariable int channelId)
+    {
+        try { simulator.nextBtsCycle(deviceId, channelId); return simulator.btsPlayback(deviceId, channelId); }
+        catch (IllegalArgumentException exception) { throw badRequest(exception); }
+    }
+
+    @PostMapping("/dev/bts-simulator/devices/{deviceId}/channels/{channelId}/playback/reset")
+    public PlaybackSnapshot resetBts(@PathVariable int deviceId, @PathVariable int channelId)
+    {
+        try { simulator.resetBts(deviceId, channelId); return simulator.btsPlayback(deviceId, channelId); }
+        catch (IllegalArgumentException exception) { throw badRequest(exception); }
+    }
+
     private ResponseStatusException badRequest(IllegalArgumentException exception)
     {
         return new ResponseStatusException(HttpStatus.BAD_REQUEST, exception.getMessage(), exception);
     }
 
-    public record CreateDeviceCommand(String ip, int channelCount) { }
+    private void synchronizeOnlineStatusIfConfigured()
+    {
+        if (statusSynchronizer != null) statusSynchronizer.synchronizeOnlineStatus();
+    }
 
-    public record DeviceStateCommand(String ip, int channelCount, boolean online) { }
+    public record CreateDeviceCommand(String ip, int channelCount, String model)
+    {
+        public CreateDeviceCommand(String ip, int channelCount)
+        {
+            this(ip, channelCount, null);
+        }
+    }
+
+    public record SimulatorPowerCommand(Boolean poweredOn) { }
+
+    public record DeviceStateCommand(String ip, int channelCount, boolean online, String model)
+    {
+        public DeviceStateCommand(String ip, int channelCount, boolean online)
+        {
+            this(ip, channelCount, online, null);
+        }
+    }
 
     public record ChannelStateCommand(boolean online, DeviceWorkStatus workStatus) { }
 
@@ -263,4 +406,6 @@
     public record CollectorChannelCommand(List<CollectorMetricCommand> metrics) { }
 
     public record CollectorMetricCommand(String metricCode, BigDecimal value, String unit) { }
+
+    public record PlaybackCommand(Integer speedMultiplier, Boolean playing) { }
 }

--
Gitblit v1.7.1