| | |
| | | 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; |
| | |
| | | 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; |
| | |
| | | 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) |
| | |
| | | |
| | | 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, |
| | |
| | | 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 |