| | |
| | | for (BtsHistoryRecord item : btsRecords(deviceId, channelId)) |
| | | { |
| | | String key = item.cycleNo() + ":" + item.stepNo(); |
| | | grouped.computeIfAbsent(key, ignored -> new BtsStepAccumulator(item)).add(item); |
| | | int position = BtsExcelFixture.records().stream() |
| | | .filter(record -> record.cycleNo() == item.cycleNo()) |
| | | .map(BtsExcelFixture.FixtureRecord::stepNo).distinct().toList().indexOf(item.stepNo()) + 1; |
| | | grouped.computeIfAbsent(key, ignored -> new BtsStepAccumulator(item, position)).add(item); |
| | | } |
| | | return grouped.values().stream().map(BtsStepAccumulator::finish).toList(); |
| | | } |
| | |
| | | |
| | | /** 返回检测柜测试方案事实;方案行来自 Excel 夹具,当前状态来自该通道播放状态。 */ |
| | | public synchronized BtsTestSnapshot btsTest(int deviceId, int channelId) |
| | | { return btsTest(deviceId, channelId, null); } |
| | | |
| | | /** 返回指定循环的检测柜测试方案;未开始接收的测试固定使用第一个循环作为核对上下文。 */ |
| | | public synchronized BtsTestSnapshot btsTest(int deviceId, int channelId, Integer forcedCycleNo) |
| | | { |
| | | DeviceState device = requireDevice(deviceId); |
| | | requireChannel(device, channelId); |
| | | BtsPlaybackState playback = playbackState(btsKey(device, channelId), Instant.now()); |
| | | int requestedCycleNo = playback.cycleNo(); |
| | | int requestedCycleNo = forcedCycleNo == null ? playback.cycleNo() : forcedCycleNo; |
| | | int currentCycleNo = requestedCycleNo; |
| | | List<BtsExcelFixture.FixtureRecord> currentCycleRecords = BtsExcelFixture.records().stream() |
| | | .filter(item -> item.cycleNo() == requestedCycleNo).toList(); |
| | |
| | | firstByStep.putIfAbsent(item.stepNo(), item); |
| | | |
| | | List<BtsTestPlanRow> rows = new ArrayList<>(); |
| | | int cyclePosition = 1; |
| | | for (BtsExcelFixture.FixtureRecord first : firstByStep.values()) |
| | | { |
| | | List<BtsExcelFixture.FixtureRecord> sameCycle = currentCycleRecords.stream() |
| | | .filter(item -> item.stepNo() == first.stepNo()).toList(); |
| | | BtsExcelFixture.FixtureRecord last = sameCycle.get(sameCycle.size() - 1); |
| | | // step 号在每个循环内重复,跨循环时必须按当前循环的工步位置判断,不能把上一循环的同号工步当成当前状态。 |
| | | String status = playback.naturallyFinished() ? "已完成" |
| | | String status = forcedCycleNo != null ? "待执行" : playback.naturallyFinished() ? "已完成" |
| | | : playback.stepNo() > first.stepNo() ? "已完成" |
| | | : playback.stepNo() == first.stepNo() ? "进行中" : "待执行"; |
| | | rows.add(new BtsTestPlanRow("STEP", first.stepNo(), first.stepName(), |
| | | rows.add(new BtsTestPlanRow("STEP", first.stepNo(), cyclePosition++, first.stepName(), |
| | | Math.max(0d, last.offsetSeconds() - first.offsetSeconds()), first.voltageV(), status, |
| | | null, null)); |
| | | } |
| | | rows.add(new BtsTestPlanRow("CYCLE", BtsExcelFixture.startStepNo(), "循环", 0d, null, |
| | | playback.naturallyFinished() ? "已完成" : "进行中", currentCycleNo, |
| | | rows.add(new BtsTestPlanRow("CYCLE", BtsExcelFixture.startStepNo(), null, "循环", 0d, null, |
| | | forcedCycleNo != null ? "待执行" : playback.naturallyFinished() ? "已完成" : "进行中", currentCycleNo, |
| | | (long) currentCycleRecords.size())); |
| | | rows.add(new BtsTestPlanRow("END", 0, "结束", 0d, null, |
| | | playback.naturallyFinished() ? "已完成" : "待执行", null, (long) BtsExcelFixture.recordCount())); |
| | | rows.add(new BtsTestPlanRow("END", 0, null, "结束", 0d, null, |
| | | forcedCycleNo != null ? "待执行" : playback.naturallyFinished() ? "已完成" : "待执行", null, (long) BtsExcelFixture.recordCount())); |
| | | return new BtsTestSnapshot(currentCycleNo, playback.stepNo(), BtsExcelFixture.cycleCount(), rows); |
| | | } |
| | | |
| | |
| | | stateStore.saveBtsPlaybackState(key, playback); |
| | | } |
| | | |
| | | public record BtsStepRecord(int cycleNo, int stepNo, String stepName, String stepType, long recordCount, |
| | | public record BtsStepRecord(int cycleNo, int stepNo, Integer cyclePosition, String stepName, String stepType, long recordCount, |
| | | Instant startedAt, Instant endedAt, double durationSeconds, double capacityAh, double energyWh, |
| | | double startVoltageV, double endVoltageV, double chargePeakVoltageV, double dischargeValleyVoltageV) { } |
| | | |
| | |
| | | double chargeCapacityAh, double dischargeCapacityAh, double chargeEnergyWh, double dischargeEnergyWh, |
| | | double efficiency) { } |
| | | |
| | | public record BtsTestPlanRow(String rowType, int stepNo, String stepName, double durationSeconds, |
| | | public record BtsTestPlanRow(String rowType, int stepNo, Integer cyclePosition, String stepName, double durationSeconds, |
| | | Double voltageV, String executionStatus, Integer cycleCount, Long recordCount) { } |
| | | |
| | | public record BtsTestSnapshot(int currentCycleNo, int currentStepNo, int plannedCycleCount, |
| | |
| | | |
| | | private static final class BtsStepAccumulator |
| | | { |
| | | private final int cycle; private final int step; private final String name; private final String type; |
| | | private final int cycle; private final int step; private final int position; private final String name; private final String type; |
| | | private long count; private Instant first; private Instant last; private double duration; private double capacity; private double energy; |
| | | private double startVoltage; private double endVoltage; private double chargePeak = Double.NEGATIVE_INFINITY; private double dischargeValley = Double.POSITIVE_INFINITY; |
| | | BtsStepAccumulator(BtsHistoryRecord first) { cycle = first.cycleNo(); step = first.stepNo(); name = first.stepName(); type = first.stepType(); } |
| | | BtsStepAccumulator(BtsHistoryRecord first, int position) { cycle = first.cycleNo(); step = first.stepNo(); this.position = position; name = first.stepName(); type = first.stepType(); } |
| | | void add(BtsHistoryRecord item) { count++; if (first == null) { first = item.deviceTime(); startVoltage = item.voltageV(); } last = item.deviceTime(); endVoltage = item.voltageV(); duration = first == null ? 0 : Math.max(0, java.time.Duration.between(first, last).toMillis() / 1000d); capacity = item.capacityAh(); energy = item.energyWh(); if (item.currentA() >= 0) chargePeak = Math.max(chargePeak, item.voltageV()); else dischargeValley = Math.min(dischargeValley, item.voltageV()); } |
| | | BtsStepRecord finish() { return new BtsStepRecord(cycle, step, name, type, count, first, last, duration, capacity, energy, startVoltage, endVoltage, chargePeak == Double.NEGATIVE_INFINITY ? 0 : chargePeak, dischargeValley == Double.POSITIVE_INFINITY ? 0 : dischargeValley); } |
| | | BtsStepRecord finish() { return new BtsStepRecord(cycle, step, position, name, type, count, first, last, duration, capacity, energy, startVoltage, endVoltage, chargePeak == Double.NEGATIVE_INFINITY ? 0 : chargePeak, dischargeValley == Double.POSITIVE_INFINITY ? 0 : dischargeValley); } |
| | | } |
| | | |
| | | private static final class BtsCycleAccumulator |