yeg
2026-09-05 27428b31edee97703b529559146cdd7eef87a7a9
battery-device-simulator/src/main/java/com/battery/device/simulator/BtsDevelopmentSimulator.java
@@ -20,6 +20,7 @@
import com.battery.device.model.ChannelIdentity;
import com.battery.device.model.AdapterCapabilities;
import com.battery.device.api.DeviceObservationAdapter;
import com.battery.device.api.TestDataObservationAdapter;
import com.battery.device.simulator.BtsSimulatorStateStore;
import com.battery.device.simulator.BtsSimulatorStateStore.ChannelState;
import com.battery.device.simulator.BtsSimulatorStateStore.ChannelTombstone;
@@ -43,7 +44,7 @@
import com.battery.device.simulator.DevelopmentCollectorSimulation.CollectorRealtimeSnapshot;
/** 提供由开发后端托管的临时只读 BTS 模拟端点。 */
public final class BtsDevelopmentSimulator implements DeviceObservationAdapter
public final class BtsDevelopmentSimulator implements TestDataObservationAdapter
{
    public static final String HOST = "127.0.0.1";
    public static final int DEFAULT_PORT = 15020;
@@ -494,6 +495,46 @@
        BtsPlaybackState playback = playbackState(btsKey(device, channelId), Instant.now());
        return Math.max(100L, 60000L / Math.max(1, playback.speedMultiplier()));
    }
    @Override
    public synchronized CabinetRealtime latestCabinet(ChannelIdentity channel)
    {
        BtsHistoryRecord record = btsRecords(channel.devid(), channel.chlid()).stream()
                .max(java.util.Comparator.comparing(BtsHistoryRecord::deviceTime)).orElse(null);
        return record == null ? null : new CabinetRealtime(record.sequenceId(), record.deviceTime(), record.currentA(),
                record.voltageV(), record.capacityAh(), record.energyWh(), record.cycleNo(), record.stepNo(),
                record.stepName(), record.stepType(), record.serverReceivedAt());
    }
    @Override
    public synchronized CollectorRealtime latestCollector(long collectorId)
    {
        CollectorRealtimeSnapshot snapshot = collectorRealtime((int) collectorId);
        return new CollectorRealtime(snapshot.collectorCode(), snapshot.online(), snapshot.sampleIntervalMs(),
                snapshot.measurements().stream().map(item -> new CollectorMeasurement(item.channelCode(), item.sequenceId(),
                        item.deviceTime(), item.deviceTimeRaw(), item.serverReceivedAt(), item.metricCode(), item.value(),
                        item.unit())).toList());
    }
    @Override
    public synchronized List<StepRecord> stepRecords(ChannelIdentity channel)
    {
        return btsSteps(channel.devid(), channel.chlid()).stream().map(item -> new StepRecord(item.recordNo(),
                item.cycleNo(), item.stepNo(), item.stepName(), item.stepType(), item.startedAt(), item.endedAt(),
                item.endVoltageV(), item.capacityAh(), item.energyWh())).toList();
    }
    @Override
    public synchronized List<CycleRecord> cycleRecords(ChannelIdentity channel)
    {
        return btsCycles(channel.devid(), channel.chlid()).stream().map(item -> new CycleRecord(item.recordNo(),
                item.cycleNo(), item.startedAt(), item.endedAt(), item.chargeCapacityAh(), item.dischargeCapacityAh(),
                item.chargeEnergyWh(), item.dischargeEnergyWh(), item.efficiency())).toList();
    }
    @Override
    public synchronized long persistenceIntervalMillis(ChannelIdentity channel)
    { return persistenceIntervalMillis(channel.devid(), channel.chlid()); }
    /** 按实际出现的循环和工步汇总 record,不补齐缺失行。 */
    public synchronized List<BtsStepRecord> btsSteps(int deviceId, int channelId)
@@ -1004,7 +1045,19 @@
    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 startVoltageV, double endVoltageV, double chargePeakVoltageV, double dischargeValleyVoltageV,
            String recordNo)
    {
        public 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)
        {
            this(cycleNo, stepNo, cyclePosition, stepName, stepType, recordCount, startedAt, endedAt,
                    durationSeconds, capacityAh, energyWh, startVoltageV, endVoltageV, chargePeakVoltageV,
                    dischargeValleyVoltageV, null);
        }
    }
    public record BtsCycleRecord(String recordNo, int cycleNo, long recordCount, Instant startedAt, Instant endedAt,
            double chargeCapacityAh, double dischargeCapacityAh, double chargeEnergyWh, double dischargeEnergyWh,
@@ -1031,7 +1084,7 @@
        private double startVoltage; private double endVoltage; private double chargePeak = Double.NEGATIVE_INFINITY; private double dischargeValley = Double.POSITIVE_INFINITY;
        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, position, 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, "STEP-" + cycle + "-" + step + "-" + last.toEpochMilli()); }
    }
    private static final class BtsCycleAccumulator