From 76cd5948e95ddbde9dd4411b466ee75ba20b562e Mon Sep 17 00:00:00 2001
From: yeg <1347747024@qq.com>
Date: 星期四, 03 九月 2026 17:28:29 +0800
Subject: [PATCH] 实现测试预警异常管理
---
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TTestLiveServiceImpl.java | 57 ++++++++++++++++++++++++++++++++++++++++++---------------
1 files changed, 42 insertions(+), 15 deletions(-)
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TTestLiveServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TTestLiveServiceImpl.java
index 97cfc6e..1869330 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TTestLiveServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TTestLiveServiceImpl.java
@@ -11,10 +11,9 @@
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
import jakarta.annotation.PreDestroy;
import org.springframework.stereotype.Service;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import com.ruoyi.device.bts.BtsDevelopmentSimulator;
import com.ruoyi.device.bts.BtsDevelopmentSimulator.CollectorSnapshot;
@@ -28,6 +27,7 @@
import com.ruoyi.system.service.ITestLiveService.TestPlanRow;
import com.ruoyi.system.service.ITestLiveService.TestPlanSnapshot;
import com.ruoyi.system.service.ITTestService;
+import com.ruoyi.system.mapper.TTestMapper;
/** 将设备最新快照转换为测试视角,并托管 SSE 连接生命周期。 */
@Service
@@ -36,26 +36,30 @@
private final ITTestService tests;
private final DeviceDiscoveryCandidateStore candidates;
private final BtsDevelopmentSimulator simulator;
+ private final TTestMapper mapper;
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
- private final ConcurrentMap<String, LiveSource> lastSources = new ConcurrentHashMap<>();
+
+ @Autowired
+ public TTestLiveServiceImpl(ITTestService tests, DeviceDiscoveryCandidateStore candidates,
+ BtsDevelopmentSimulator simulator, TTestMapper mapper)
+ { this.tests = tests; this.candidates = candidates; this.simulator = simulator; this.mapper = mapper; }
public TTestLiveServiceImpl(ITTestService tests, DeviceDiscoveryCandidateStore candidates,
BtsDevelopmentSimulator simulator)
- { this.tests = tests; this.candidates = candidates; this.simulator = simulator; }
+ { this(tests, candidates, simulator, null); }
@Override public LiveState current(Long testId)
{
TTest test = tests.selectTestById(testId);
List<CabinetCandidate> cabinets = candidates.snapshot(Instant.now()).cabinets();
List<LiveSource> sources = new ArrayList<>();
- boolean freezeSnapshots = "PAUSED".equals(test.getReceptionStatus())
- || "ENDED".equals(test.getReceptionStatus());
+ boolean receiving = "RECEIVING".equals(test.getReceptionStatus());
for (TTestBattery battery : test.getBatteries() == null ? List.<TTestBattery>of() : test.getBatteries())
{
if (battery.getBinding() != null)
- sources.add(snapshotFor(testId, cabinetSource(battery, cabinets), freezeSnapshots));
+ sources.add(snapshotFor(cabinetSource(battery, cabinets), receiving));
if (battery.getCollectorId() != null)
- sources.add(snapshotFor(testId, collectorSource(battery), freezeSnapshots));
+ sources.add(snapshotFor(collectorSource(battery), receiving));
}
List<Map<String, Object>> exceptions = new ArrayList<>();
exceptions.addAll(sources.stream().filter(source -> !"COLLECTOR".equals(source.sourceType())
@@ -64,6 +68,12 @@
.map(source -> Map.<String, Object>of("sourceType", source.sourceType(), "sourceId", source.sourceId(),
"batteryNo", source.batteryNo(), "status", source.deviceStatus(), "message", "数据来源当前不可观测"))
.toList());
+ if (mapper != null) {
+ mapper.selectWarningExceptions(testId, "OPEN").forEach(item -> exceptions.add(Map.of(
+ "exceptionId", item.getExceptionId(), "sourceType", item.getSourceType(),
+ "metricCode", item.getMetricCode(), "value", item.getAbnormalValue(), "unit", item.getUnit(),
+ "status", item.getExceptionStatus(), "message", "测试预警异常")));
+ }
String testStatus = test.getTestStatus();
TestPlanSnapshot testPlan = detectorTestPlan(test);
return new LiveState(testId, testStatus, test.getReceptionStatus(), Instant.now(), sources, exceptions, testPlan);
@@ -93,12 +103,17 @@
return null;
}
- private LiveSource snapshotFor(Long testId, LiveSource fresh, boolean freeze)
+ private LiveSource snapshotFor(LiveSource fresh, boolean receiving)
{
- String key = testId + ":" + fresh.sourceType() + ":" + fresh.sourceId() + ":" + fresh.batteryNo();
- if (freeze) return lastSources.getOrDefault(key, fresh);
- lastSources.put(key, fresh);
+ // 暂停或停止只停止数据请求;设备状态仍以本次查询的最新事实为准,采集快照不继续展示。
+ if (!receiving)
+ return withoutSnapshot(fresh);
return fresh;
+ }
+
+ private LiveSource withoutSnapshot(LiveSource source)
+ {
+ return new LiveSource(source.sourceType(), source.sourceId(), source.batteryNo(), source.deviceStatus(), Map.of());
}
@Override public SseEmitter stream(Long testId)
@@ -153,7 +168,8 @@
&& Objects.equals(channel.identity().subdevid(), binding.getSubdevid())
&& Objects.equals(channel.identity().chlid(), binding.getChlid()))
return new LiveSource("CABINET_CHANNEL", binding.getChannelId(), battery.getBatteryNo(),
- channel.online() ? channel.workStatus().name() : "OFFLINE", snapshot(channel));
+ channel.online() ? channel.workStatus().name() : "OFFLINE",
+ channel.online() ? snapshot(channel) : Map.of());
return new LiveSource("CABINET_CHANNEL", binding.getChannelId(), battery.getBatteryNo(), "UNKNOWN", Map.of());
}
@@ -163,6 +179,11 @@
if (channel.latestSnapshot() == null) return value;
value.put("source", "检测柜");
value.put("observedAt", channel.latestSnapshot().receivedAt());
+ // BTS 核心采样字段是快照的一等事实,不能只依赖动态 measurements 展示。
+ value.put("currentA", channel.latestSnapshot().currentA());
+ value.put("voltageV", channel.latestSnapshot().voltageV());
+ value.put("capacityAh", channel.latestSnapshot().capacityAh());
+ value.put("energyWh", channel.latestSnapshot().energyWh());
// 实时方案需要设备当前循环和实际工步;这些字段只用于快照展示,不形成历史采集事实。
value.put("cycleNo", channel.latestSnapshot().cycleNo());
value.put("stepNo", channel.latestSnapshot().stepNo());
@@ -172,7 +193,13 @@
Measurement measurement = entry.getValue();
return Map.of("metricCode", entry.getKey(), "value", measurement.value(), "unit", measurement.unit());
}).toList());
- value.put("units", channel.latestSnapshot().measurements().values().stream().map(Measurement::unit).distinct().toList());
+ List<String> units = new ArrayList<>();
+ if (channel.latestSnapshot().currentA() != null) units.add("A");
+ if (channel.latestSnapshot().voltageV() != null) units.add("V");
+ if (channel.latestSnapshot().capacityAh() != null) units.add("Ah");
+ if (channel.latestSnapshot().energyWh() != null) units.add("Wh");
+ units.addAll(channel.latestSnapshot().measurements().values().stream().map(Measurement::unit).toList());
+ value.put("units", units.stream().filter(item -> item != null && !item.isBlank()).distinct().toList());
return value;
}
@@ -187,7 +214,7 @@
collector.latestMeasurements().stream().map(item -> item.serverReceivedAt()).max(Instant::compareTo)
.ifPresent(observedAt -> value.put("observedAt", observedAt));
return new LiveSource("COLLECTOR", (long) collector.collectorId(), battery.getBatteryNo(),
- collector.online() ? "ONLINE" : "OFFLINE", value);
+ collector.online() ? "ONLINE" : "OFFLINE", collector.online() ? value : Map.of());
}
return new LiveSource("COLLECTOR", battery.getCollectorId(), battery.getBatteryNo(), "UNKNOWN", Map.of());
}
--
Gitblit v1.7.1