From 6c38637d89f346a660618558451f2dd7286dc7a5 Mon Sep 17 00:00:00 2001
From: yeg <1347747024@qq.com>
Date: 星期一, 31 八月 2026 22:51:54 +0800
Subject: [PATCH] 补齐检测柜模拟器完整 Excel 数据生命周期
---
ruoyi-device/src/main/java/com/ruoyi/device/bts/BtsDevelopmentSimulator.java | 354 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
1 files changed, 333 insertions(+), 21 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 7918e0c..2c583fa 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
@@ -25,6 +25,7 @@
import com.ruoyi.device.simulator.BtsSimulatorStateStore.DeviceState;
import com.ruoyi.device.simulator.BtsSimulatorStateStore.BtsDeviceProfile;
import com.ruoyi.device.simulator.BtsSimulatorStateStore.BtsHistoryRecord;
+import com.ruoyi.device.simulator.BtsSimulatorStateStore.BtsPlaybackState;
import com.ruoyi.device.simulator.BtsSimulatorStateStore.RecentRequest;
import com.ruoyi.device.simulator.BtsSimulatorStateStore.SimulatorState;
import com.ruoyi.device.simulator.InMemoryBtsSimulatorStateStore;
@@ -53,7 +54,8 @@
private SimulatorState state;
private boolean running = true;
private long observationSequence;
- private final java.util.Map<String, java.util.List<SimBtsHistory>> btsHistory = new java.util.LinkedHashMap<>();
+ private final java.util.Map<String, java.util.List<BtsHistoryRecord>> btsHistory = new java.util.LinkedHashMap<>();
+ private static final long SIMULATED_RECORD_INTERVAL_SECONDS = 60L;
private static final DateTimeFormatter BTS_HISTORY_TIME = DateTimeFormatter.ofPattern("yyyy-MM-dd HH.mm.ss")
.withZone(ZoneId.of("Asia/Hong_Kong"));
@@ -143,8 +145,16 @@
List<DeviceState> devices = new ArrayList<>(state.devices());
devices.removeIf(item -> item.deviceId() == deviceId);
List<ChannelTombstone> tombstones = new ArrayList<>(state.tombstones());
+ BtsDeviceProfile removedProfile = profileFor(removed);
for (ChannelState channel : removed.channels())
+ {
upsertTombstone(tombstones, removed.deviceId(), removed.ip(), channel.channelId());
+ String key = historyKey(removedProfile.devtype(), removed.deviceId(), removedProfile.subDeviceId(),
+ channel.channelId());
+ stateStore.deleteBtsHistory(key);
+ stateStore.deleteBtsPlaybackState(key);
+ btsHistory.remove(key);
+ }
persist(new SimulatorState(state.version(), devices, state.collectors(), state.nextDeviceId(),
state.nextCollectorId(), tombstones));
}
@@ -264,6 +274,156 @@
state.nextCollectorId(), state.tombstones()), historyAppends);
}
+ /** 按墙上时间推进检测柜模拟播放;设备状态和测试接收状态互不影响。 */
+ public synchronized void advanceBts(Instant now)
+ {
+ for (DeviceState device : state.devices())
+ {
+ if (!device.online()) continue;
+ BtsDeviceProfile profile = profileFor(device);
+ for (ChannelState channel : device.channels())
+ {
+ if (!channel.online() || channel.workStatus() != DeviceWorkStatus.WORKING) continue;
+ String key = historyKey(profile.devtype(), device.deviceId(), profile.subDeviceId(), channel.channelId());
+ BtsPlaybackState playback = playbackState(key, now);
+ advanceBtsPlayback(key, profile, playback,
+ profile.currentMaxA().doubleValue() * 0.2d, 3.7d, now);
+ }
+ }
+ }
+
+ /** 设置检测柜通道演示倍速,只接受 1x、60x 和 600x。 */
+ public synchronized void setBtsSpeed(int deviceId, int channelId, int speedMultiplier)
+ {
+ DeviceState device = requireDevice(deviceId);
+ requireChannel(device, channelId);
+ String key = btsKey(device, channelId);
+ BtsPlaybackState current = playbackState(key, Instant.now());
+ stateStore.saveBtsPlaybackState(key, new BtsPlaybackState(current.playing(), speedMultiplier,
+ current.cycleNo(), current.stepNo(), current.stepName(), current.stepType(), current.simulatedAt(),
+ Instant.now(), current.nextSequence(), current.naturallyFinished()));
+ }
+
+ /** 开始播放指定检测柜通道的演示时间线。 */
+ public synchronized void playBts(int deviceId, int channelId) { setBtsPlaying(deviceId, channelId, true); }
+ /** 暂停模拟器时间线,不改变检测柜通道工作事实。 */
+ public synchronized void pauseBts(int deviceId, int channelId) { setBtsPlaying(deviceId, channelId, false); }
+ /** 从暂停状态恢复模拟器时间线。 */
+ public synchronized void resumeBts(int deviceId, int channelId) { setBtsPlaying(deviceId, channelId, true); }
+
+ private void setBtsPlaying(int deviceId, int channelId, boolean playing)
+ {
+ DeviceState device = requireDevice(deviceId);
+ requireChannel(device, channelId);
+ String key = btsKey(device, channelId);
+ Instant now = Instant.now();
+ BtsPlaybackState current = playbackState(key, now);
+ if (current.naturallyFinished() && playing) return;
+ stateStore.saveBtsPlaybackState(key, new BtsPlaybackState(playing, current.speedMultiplier(), current.cycleNo(),
+ current.stepNo(), current.stepName(), current.stepType(), current.simulatedAt(), now,
+ current.nextSequence(), current.naturallyFinished()));
+ }
+
+ /** 跳转到下一实际工步;不向设备发送控制报文。 */
+ public synchronized void nextBtsStep(int deviceId, int channelId)
+ {
+ DeviceState device = requireDevice(deviceId);
+ requireChannel(device, channelId);
+ String key = btsKey(device, channelId);
+ BtsPlaybackState current = playbackState(key, Instant.now());
+ BtsExcelFixture.FixtureRecord next = nextFixtureStep(current.cycleNo(), current.stepNo());
+ if (next == null)
+ {
+ BtsExcelFixture.FixtureRecord last = BtsExcelFixture.record(BtsExcelFixture.recordCount());
+ savePlayback(key, new BtsPlaybackState(false, current.speedMultiplier(), last.cycleNo(), last.stepNo(),
+ last.stepName(), last.stepType(), current.simulatedAt(), Instant.now(),
+ BtsExcelFixture.recordCount() + 1L, true));
+ return;
+ }
+ savePlayback(key, new BtsPlaybackState(current.playing(), current.speedMultiplier(), next.cycleNo(), next.stepNo(),
+ next.stepName(), next.stepType(), current.simulatedAt(), Instant.now(), next.sequenceId(), false));
+ }
+
+ /** 跳转到下一循环的首工步;不补齐当前循环缺失的首尾工步。 */
+ public synchronized void nextBtsCycle(int deviceId, int channelId)
+ {
+ DeviceState device = requireDevice(deviceId);
+ requireChannel(device, channelId);
+ String key = btsKey(device, channelId);
+ BtsPlaybackState current = playbackState(key, Instant.now());
+ BtsExcelFixture.FixtureRecord next = BtsExcelFixture.records().stream()
+ .filter(item -> item.cycleNo() > current.cycleNo()).findFirst().orElse(null);
+ if (next == null)
+ {
+ BtsExcelFixture.FixtureRecord last = BtsExcelFixture.record(BtsExcelFixture.recordCount());
+ savePlayback(key, new BtsPlaybackState(false, current.speedMultiplier(), last.cycleNo(), last.stepNo(),
+ last.stepName(), last.stepType(), current.simulatedAt(), Instant.now(),
+ BtsExcelFixture.recordCount() + 1L, true));
+ return;
+ }
+ savePlayback(key, new BtsPlaybackState(current.playing(), current.speedMultiplier(), next.cycleNo(), next.stepNo(),
+ next.stepName(), next.stepType(), current.simulatedAt(), Instant.now(), next.sequenceId(), false));
+ }
+
+ /** 清空单通道播放和原始数据,恢复到首循环首工步。 */
+ public synchronized void resetBts(int deviceId, int channelId)
+ {
+ 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()));
+ }
+
+ /** 返回播放状态及当前通道的 record、step、cycle 数据。 */
+ public synchronized PlaybackSnapshot btsPlayback(int deviceId, int channelId)
+ {
+ DeviceState device = requireDevice(deviceId);
+ requireChannel(device, channelId);
+ String key = btsKey(device, channelId);
+ BtsPlaybackState state = playbackState(key, Instant.now());
+ return new PlaybackSnapshot(deviceId, channelId, state, btsRecords(deviceId, channelId),
+ btsSteps(deviceId, channelId), btsCycles(deviceId, channelId));
+ }
+
+ /** 返回只追加的检测柜原始 record。 */
+ public synchronized List<BtsHistoryRecord> btsRecords(int deviceId, int channelId)
+ {
+ DeviceState device = requireDevice(deviceId);
+ requireChannel(device, channelId);
+ return List.copyOf(btsHistoryFor(btsKey(device, channelId)));
+ }
+
+ /** 按实际出现的循环和工步汇总 record,不补齐缺失行。 */
+ public synchronized List<BtsStepRecord> btsSteps(int deviceId, int channelId)
+ {
+ java.util.Map<String, BtsStepAccumulator> grouped = new java.util.LinkedHashMap<>();
+ for (BtsHistoryRecord item : btsRecords(deviceId, channelId))
+ {
+ String key = item.cycleNo() + ":" + item.stepNo();
+ grouped.computeIfAbsent(key, ignored -> new BtsStepAccumulator(item)).add(item);
+ }
+ return grouped.values().stream().map(BtsStepAccumulator::finish).toList();
+ }
+
+ /** 按实际出现的循环汇总 record,不增加测试结束统计行。 */
+ public synchronized List<BtsCycleRecord> btsCycles(int deviceId, int channelId)
+ {
+ java.util.Map<Integer, BtsCycleAccumulator> grouped = new java.util.LinkedHashMap<>();
+ for (BtsHistoryRecord item : btsRecords(deviceId, channelId))
+ grouped.computeIfAbsent(item.cycleNo(), ignored -> new BtsCycleAccumulator(item)).add(item);
+ return grouped.entrySet().stream().map(entry -> {
+ BtsCycleRecord calculated = entry.getValue().finish();
+ BtsExcelFixture.CycleSummary expected = BtsExcelFixture.cycle(entry.getKey());
+ if (expected == null || entry.getValue().count < expected.recordCount()) return calculated;
+ return new BtsCycleRecord(calculated.cycleNo(), expected.recordCount(), calculated.startedAt(),
+ calculated.endedAt(), expected.chargeCapacityAh(), expected.dischargeCapacityAh(),
+ expected.chargeEnergyWh(), expected.dischargeEnergyWh());
+ }).toList();
+ }
+
/** 返回采集器最新实时快照;快照与分页历史保持不同读取路径。 */
public synchronized CollectorRealtimeSnapshot collectorRealtime(int collectorId)
{
@@ -303,6 +463,7 @@
{
SimulatorState defaultState = defaultState();
stateStore.reset(defaultState);
+ btsHistory.clear();
state = defaultState;
}
@@ -453,21 +614,27 @@
profile.currentMinA().doubleValue(), profile.currentMaxA().doubleValue()) : 0d;
double voltage = clamp(3.7d + wave * 0.01d, profile.voltageMinV().doubleValue(),
profile.voltageMaxV().doubleValue());
- double capacity = working ? Math.max(0d, tick * 0.001d * current) : 0d;
+ String key = historyKey(profile.devtype(), observed.deviceId(), profile.subDeviceId(), channel.channelId());
+ BtsPlaybackState playback = playbackState(key, Instant.now());
+ double capacity = working ? latestCapacity(key) : 0d;
double energy = capacity * voltage;
if (working)
{
- String key = historyKey(profile.devtype(), observed.deviceId(), profile.subDeviceId(), channel.channelId());
- java.util.List<SimBtsHistory> history = btsHistory.computeIfAbsent(key, ignored -> new ArrayList<>());
- history.add(new SimBtsHistory(history.size() + 1L, Instant.now(), current, voltage, capacity, energy));
- SimBtsHistory latest = history.get(history.size() - 1);
- stateStore.appendBtsHistory(key, new BtsHistoryRecord(latest.sequence(), latest.at(), latest.current(),
- latest.voltage(), latest.capacity(), latest.energy()));
- if (history.size() > BtsSimulatorStateStore.MAX_HISTORY_RECORDS) history.remove(0);
+ playback = advanceBtsPlayback(key, profile, playback, current, voltage);
+ BtsHistoryRecord latest = latestRecord(key);
+ if (latest != null)
+ {
+ capacity = latest.capacityAh();
+ energy = latest.energyWh();
+ }
}
+ int cycleNo = playback.cycleNo();
+ int stepNo = playback.stepNo();
+ String stepType = playback.stepType();
response.append("<inquire dev=\"").append(profile.devtype()).append('-').append(observed.deviceId())
.append('-').append(profile.subDeviceId()).append('-').append(channel.channelId())
- .append("-0\" cycle_id=\"1\" step_id=\"1\" step_type=\"cc\" workstatus=\"")
+ .append("-0\" cycle_id=\"").append(cycleNo).append("\" step_id=\"").append(stepNo)
+ .append("\" step_type=\"").append(stepType).append("\" workstatus=\"")
.append(toBtsWorkStatus(channel.workStatus()))
.append("\" current=\"").append(decimal(current)).append("\" voltage=\"")
.append(decimal(voltage)).append("\" capacity=\"").append(decimal(capacity))
@@ -489,7 +656,7 @@
int devid = Integer.parseInt(attribute(attrs, "devid"));
int subdevid = Integer.parseInt(attribute(attrs, "subdevid"));
int chlid = Integer.parseInt(attribute(attrs, "chlid"));
- int count = btsHistory.getOrDefault(historyKey(devtype, devid, subdevid, chlid), List.of()).size();
+ int count = btsHistoryFor(historyKey(devtype, devid, subdevid, chlid)).size();
response.append("<chl devtype=\"").append(devtype).append("\" devid=\"").append(devid)
.append("\" subdevid=\"").append(subdevid).append("\" chlid=\"").append(chlid)
.append("\" testid=\"SIM-").append(devid).append('-').append(chlid).append("\" count=\"")
@@ -507,18 +674,19 @@
String testId = attribute(request, "testid");
long start = Long.parseLong(attribute(request, "startpos"));
int count = Integer.parseInt(attribute(request, "count"));
- List<SimBtsHistory> history = btsHistory.getOrDefault(historyKey(devtype, devid, subdevid, chlid), List.of());
+ List<BtsHistoryRecord> history = btsHistoryFor(historyKey(devtype, devid, subdevid, chlid));
int from = (int) Math.min(Math.max(0L, start - 1L), history.size());
int to = Math.min(history.size(), from + Math.max(1, count));
StringBuilder response = new StringBuilder("<bts version=\"1.0\"><cmd>download_resp</cmd><download devtype=\"")
.append(devtype).append("\" devid=\"").append(devid).append("\" subdevid=\"").append(subdevid)
.append("\" chlid=\"").append(chlid).append("\" testid=\"").append(testId)
.append("\"><list>");
- for (SimBtsHistory item : history.subList(from, to))
- response.append("<data seqid=\"").append(item.sequence()).append("\" stepid=\"1\" cycleid=\"1\" steptype=\"cc\" atime=\"")
- .append(BTS_HISTORY_TIME.format(item.at())).append("\" testtime=\"1000\" volt=\"")
- .append(decimal(item.voltage())).append("\" curr=\"").append(decimal(item.current()))
- .append("\" cap=\"").append(decimal(item.capacity())).append("\" eng=\"").append(decimal(item.energy())).append("\"/>");
+ for (BtsHistoryRecord item : history.subList(from, to))
+ response.append("<data seqid=\"").append(item.sequenceId()).append("\" stepid=\"").append(item.stepNo())
+ .append("\" cycleid=\"").append(item.cycleNo()).append("\" steptype=\"").append(item.stepType()).append("\" atime=\"")
+ .append(BTS_HISTORY_TIME.format(item.deviceTime())).append("\" testtime=\"60000\" volt=\"")
+ .append(decimal(item.voltageV())).append("\" curr=\"").append(decimal(item.currentA()))
+ .append("\" cap=\"").append(decimal(item.capacityAh())).append("\" eng=\"").append(decimal(item.energyWh())).append("\"/>");
return response.append("</list></download></bts>").toString();
}
@@ -542,13 +710,12 @@
for (ChannelState channel : device.channels())
{
String key = historyKey(profile.devtype(), device.deviceId(), profile.subDeviceId(), channel.channelId());
- ArrayList<SimBtsHistory> restored = new ArrayList<>();
+ ArrayList<BtsHistoryRecord> restored = new ArrayList<>();
long offset = 0;
while (true)
{
var page = stateStore.loadBtsHistoryPage(key, offset, BtsSimulatorStateStore.MAX_HISTORY_BATCH_SIZE);
- restored.addAll(page.records().stream().map(item -> new SimBtsHistory(item.sequenceId(),
- item.deviceTime(), item.currentA(), item.voltageV(), item.capacityAh(), item.energyWh())).toList());
+ restored.addAll(page.records().stream().map(this::normalizeHistoryRecord).toList());
if (!page.hasMore()) break;
if (page.nextOffset() <= offset) throw new IllegalStateException("Invalid BTS history page");
offset = page.nextOffset();
@@ -558,7 +725,152 @@
}
}
- private record SimBtsHistory(long sequence, Instant at, double current, double voltage, double capacity, double energy) { }
+ private List<BtsHistoryRecord> btsHistoryFor(String key)
+ {
+ return btsHistory.getOrDefault(key, List.of());
+ }
+
+ private BtsHistoryRecord normalizeHistoryRecord(BtsHistoryRecord item)
+ {
+ return item.stepName() == null || item.stepName().isBlank()
+ ? new BtsHistoryRecord(item.sequenceId(), item.deviceTime(), item.currentA(), item.voltageV(),
+ item.capacityAh(), item.energyWh()) : item;
+ }
+
+ private String btsKey(DeviceState device, int channelId)
+ {
+ BtsDeviceProfile profile = profileFor(device);
+ return historyKey(profile.devtype(), device.deviceId(), profile.subDeviceId(), channelId);
+ }
+
+ private void requireChannel(DeviceState device, int channelId)
+ {
+ if (channelId < 1 || channelId > device.channels().size())
+ throw new IllegalArgumentException("channelId must be between 1 and " + device.channels().size());
+ }
+
+ private BtsPlaybackState playbackState(String key, Instant now)
+ {
+ BtsPlaybackState stored = stateStore.loadBtsPlaybackState(key);
+ if (stored != null) return stored;
+ List<BtsHistoryRecord> records = btsHistoryFor(key);
+ long next = records.stream().mapToLong(BtsHistoryRecord::sequenceId).max().orElse(0L) + 1L;
+ BtsExcelFixture.FixtureRecord first = BtsExcelFixture.record(1);
+ BtsPlaybackState created = new BtsPlaybackState(true, 1, first.cycleNo(), first.stepNo(), first.stepName(), first.stepType(),
+ now.minusSeconds(SIMULATED_RECORD_INTERVAL_SECONDS), now, next, false);
+ stateStore.saveBtsPlaybackState(key, created);
+ return created;
+ }
+
+ private BtsHistoryRecord latestRecord(String key)
+ {
+ List<BtsHistoryRecord> records = btsHistoryFor(key);
+ return records.isEmpty() ? null : records.get(records.size() - 1);
+ }
+
+ private double latestCapacity(String key)
+ {
+ BtsHistoryRecord latest = latestRecord(key);
+ return latest == null ? 0d : latest.capacityAh();
+ }
+
+ private BtsPlaybackState advanceBtsPlayback(String key, BtsDeviceProfile profile, BtsPlaybackState current,
+ double currentA, double voltageV)
+ {
+ return advanceBtsPlayback(key, profile, current, currentA, voltageV, Instant.now());
+ }
+
+ private BtsPlaybackState advanceBtsPlayback(String key, BtsDeviceProfile profile, BtsPlaybackState current,
+ double currentA, double voltageV, Instant now)
+ {
+ if (!current.playing() || current.naturallyFinished()) return current;
+ long elapsedMillis = Math.max(0L, java.time.Duration.between(current.wallClockAt(), now).toMillis())
+ * current.speedMultiplier();
+ List<BtsHistoryRecord> history = new ArrayList<>(btsHistoryFor(key));
+ List<BtsHistoryRecord> appended = new ArrayList<>();
+ Instant simulated = current.simulatedAt();
+ long sequence = current.nextSequence();
+ if (sequence < 1) sequence = 1;
+ if (sequence > BtsExcelFixture.recordCount() + 1L)
+ sequence = BtsExcelFixture.recordCount() + 1L;
+ long lastOffset = sequence == 1 ? -SIMULATED_RECORD_INTERVAL_SECONDS
+ : BtsExcelFixture.record(sequence - 1).offsetSeconds();
+ long budgetMillis = elapsedMillis;
+ while (sequence <= BtsExcelFixture.recordCount())
+ {
+ BtsExcelFixture.FixtureRecord fixture = BtsExcelFixture.record(sequence);
+ long deltaSeconds = fixture.offsetSeconds() - lastOffset;
+ long deltaMillis = Math.max(1L, deltaSeconds * 1000L);
+ boolean firstRecord = history.isEmpty() && sequence == 1L;
+ if (!firstRecord && budgetMillis < deltaMillis) break;
+ budgetMillis = Math.max(0L, budgetMillis - deltaMillis);
+ simulated = simulated.plusSeconds(deltaSeconds);
+ BtsHistoryRecord record = new BtsHistoryRecord(fixture.sequenceId(), simulated, fixture.currentA(), fixture.voltageV(),
+ fixture.capacityAh(), fixture.energyWh(), fixture.cycleNo(), fixture.stepNo(), fixture.stepName(), fixture.stepType());
+ history.add(record);
+ appended.add(record);
+ lastOffset = fixture.offsetSeconds();
+ sequence++;
+ }
+ if (appended.isEmpty()) return current;
+ if (history.size() > BtsSimulatorStateStore.MAX_HISTORY_RECORDS)
+ history = new ArrayList<>(history.subList(history.size() - (int) BtsSimulatorStateStore.MAX_HISTORY_RECORDS,
+ history.size()));
+ BtsExcelFixture.FixtureRecord latest = BtsExcelFixture.record(sequence - 1L);
+ boolean finished = sequence > BtsExcelFixture.recordCount();
+ BtsPlaybackState next = new BtsPlaybackState(!finished && current.playing(), current.speedMultiplier(),
+ latest.cycleNo(), latest.stepNo(), latest.stepName(), latest.stepType(), simulated, now, sequence, finished);
+ stateStore.saveBtsPlaybackAndAppendHistory(key, next, appended);
+ btsHistory.put(key, history);
+ return next;
+ }
+
+ private BtsPlaybackState defaultPlayback(Instant now)
+ {
+ BtsExcelFixture.FixtureRecord first = BtsExcelFixture.record(1);
+ return new BtsPlaybackState(true, 1, first.cycleNo(), first.stepNo(), first.stepName(), first.stepType(),
+ now.minusSeconds(SIMULATED_RECORD_INTERVAL_SECONDS), now, 1, false);
+ }
+
+ private BtsExcelFixture.FixtureRecord nextFixtureStep(int cycleNo, int stepNo)
+ {
+ return BtsExcelFixture.records().stream()
+ .filter(item -> item.cycleNo() > cycleNo || (item.cycleNo() == cycleNo && item.stepNo() > stepNo))
+ .findFirst().orElse(null);
+ }
+
+ private void savePlayback(String key, BtsPlaybackState playback)
+ {
+ stateStore.saveBtsPlaybackState(key, playback);
+ }
+
+ public record BtsStepRecord(int cycleNo, int stepNo, String stepName, String stepType, long recordCount,
+ Instant startedAt, Instant endedAt, double capacityAh, double energyWh) { }
+
+ public record BtsCycleRecord(int cycleNo, long recordCount, Instant startedAt, Instant endedAt,
+ double chargeCapacityAh, double dischargeCapacityAh, double chargeEnergyWh, double dischargeEnergyWh) { }
+
+ public record PlaybackSnapshot(int deviceId, int channelId, BtsPlaybackState playback,
+ List<BtsHistoryRecord> records, List<BtsStepRecord> steps, List<BtsCycleRecord> cycles) { }
+
+ private static final class BtsStepAccumulator
+ {
+ private final int cycle; private final int step; private final String name; private final String type;
+ private long count; private Instant first; private Instant last; private double capacity; private double energy;
+ BtsStepAccumulator(BtsHistoryRecord first) { cycle = first.cycleNo(); step = first.stepNo(); name = first.stepName(); type = first.stepType(); }
+ void add(BtsHistoryRecord item) { count++; if (first == null) first = item.deviceTime(); last = item.deviceTime(); capacity = item.capacityAh(); energy = item.energyWh(); }
+ BtsStepRecord finish() { return new BtsStepRecord(cycle, step, name, type, count, first, last, capacity, energy); }
+ }
+
+ private static final class BtsCycleAccumulator
+ {
+ private final int cycle; private long count; private Instant first; private Instant last;
+ private double maximumCapacity = Double.NEGATIVE_INFINITY; private double minimumCapacity = Double.POSITIVE_INFINITY;
+ private double chargeEnergy; private double dischargeEnergy;
+ BtsCycleAccumulator(BtsHistoryRecord first) { cycle = first.cycleNo(); }
+ void add(BtsHistoryRecord item) { count++; if (first == null) first = item.deviceTime(); last = item.deviceTime(); maximumCapacity = Math.max(maximumCapacity, item.capacityAh()); minimumCapacity = Math.min(minimumCapacity, item.capacityAh()); if (item.currentA() >= 0) chargeEnergy = Math.max(chargeEnergy, item.energyWh()); else dischargeEnergy = Math.max(dischargeEnergy, Math.abs(item.energyWh())); }
+ BtsCycleRecord finish() { return new BtsCycleRecord(cycle, count, first, last, Math.max(0d, maximumCapacity), Math.max(0d, maximumCapacity - minimumCapacity), chargeEnergy, dischargeEnergy); }
+ }
private java.util.Set<String> requestedChannels(String request)
{
--
Gitblit v1.7.1