yeg
4 天以前 6b99cc391064cb38c706979fafcecb9f50ea1587
battery-device-bts/src/test/java/com/battery/device/bts/NewareBtsXmlCodecTest.java
@@ -12,6 +12,7 @@
import java.util.Set;
import org.junit.jupiter.api.Test;
import com.battery.device.api.TestDataObservationAdapter.CabinetRealtime;
import com.battery.device.api.TestDataObservationAdapter.StepRecord;
import com.battery.device.api.model.ChannelIdentity;
import com.battery.device.api.model.DeviceWorkStatus;
import com.battery.device.api.model.RealtimeSnapshot;
@@ -237,6 +238,111 @@
    }
    @Test
    void buildsTheStepLayerRequestWithoutTheOptionalElements()
    {
        String request = codec.downloadStepLayerRequest(CHANNEL);
        assertTrue(request.contains("<cmd>downloadStepLayer</cmd>"));
        assertTrue(request.contains("devtype=\"25\" devid=\"81\" subdevid=\"1\" chlid=\"2\""));
        assertTrue(request.contains("testid=\"0\""));
        assertTrue(request.contains("dcir=\"0\""));
    }
    @Test
    void parsesRealStepLayerWithNormalisedCycleAndDerivedStartTime()
    {
        // 现场实测原文(`26.117.165.166` 的 `25-81-1-1`,测试 316)。三处要点都在这个夹具里:
        // cycleid 是 1 起要减一;只有 endatime,开始时间由 endatime − steptime 还原;
        // stepindex 才是身份(stepid 在循环里会重复)。
        String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><bts version=\"1.0\">"
                + "<cmd>downloadStepLayer_resp</cmd>"
                + "<downloadStepLayer devtype=\"25\" devid=\"81\" subdevid=\"1\" chlid=\"1\" testid=\"316\" />"
                + "<list count=\"4\">"
                + "<data startseqid=\"1\" endseqid=\"2\" stepindex=\"1\" stepid=\"1\" cycleid=\"1\" steptype=\"rest\""
                + " steptime=\"23100\" endatime=\"2026-10-10 09:09:14\" startvolt=\"0.806611083984375\""
                + " endvolt=\"0.80638037109375\" startcurr=\"0\" endcurr=\"0\" cap=\"0\" eng=\"0\" dcir=\"0\" />"
                + "<data startseqid=\"3\" endseqid=\"304\" stepindex=\"2\" stepid=\"2\" cycleid=\"1\" steptype=\"cc\""
                + " steptime=\"18000000\" endatime=\"2026-10-10 14:09:14\" startvolt=\"1.07542861328125\""
                + " endvolt=\"1.972108203125\" startcurr=\"0.563903076171875\" endcurr=\"0.563332702636719\""
                + " cap=\"2.81630277777778\" eng=\"5.58447055555556\" dcir=\"0\" />"
                + "<data startseqid=\"305\" endseqid=\"307\" stepindex=\"3\" stepid=\"3\" cycleid=\"1\" steptype=\"rest\""
                + " steptime=\"83600\" endatime=\"2026-10-10 14:10:38\" startvolt=\"1.7850955078125\""
                + " endvolt=\"1.42794287109375\" startcurr=\"0\" endcurr=\"0\" cap=\"0\" eng=\"0\" dcir=\"0\" />"
                + "<data startseqid=\"308\" endseqid=\"309\" stepindex=\"4\" stepid=\"4\" cycleid=\"1\" steptype=\"dc\""
                + " steptime=\"27600\" endatime=\"2026-10-10 14:11:05\" startvolt=\"1.27452001953125\""
                + " endvolt=\"0.49604326171875\" startcurr=\"-0.562971618652344\" endcurr=\"-0.5622275390625\""
                + " cap=\"0.00431017035590278\" eng=\"0.00426197048611111\" dcir=\"0\" /></list></bts>";
        List<StepRecord> steps = codec.parseStepLayer(xml, Instant.parse("2026-10-10T14:12:00Z"));
        assertEquals(4, steps.size());
        StepRecord first = steps.get(0);
        assertEquals(0, first.cycleNo());
        assertEquals(1, first.stepNo());
        assertEquals("rest", first.stepType());
        assertEquals("搁置", first.stepName());
        assertEquals("2026-10-10T01:09:14Z", first.endedAt().toString());
        // 开始时间 = 结束时间 − 23100 ms;与该测试首行 atime(09:08:51 设备本地)对得上。
        assertEquals("2026-10-10T01:08:50.900Z", first.startedAt().toString());
        assertEquals("neware:316:step:1", first.sourceRecordId());
        assertEquals("Asia/Hong_Kong", first.timezoneId());
        assertEquals("2026-10-10 09:09:14", first.deviceTimeRaw());
        StepRecord charge = steps.get(1);
        assertEquals("充电", charge.stepName());
        assertEquals(2.81630277777778, charge.capacityAh(), 1e-12);
        assertEquals(5.58447055555556, charge.energyWh(), 1e-12);
        assertEquals(1.972108203125, charge.endVoltageV(), 1e-12);
        assertEquals(0.563332702636719, charge.endCurrentA(), 1e-12);
        assertEquals(3, charge.measurements().get("startSeqId").intValue());
        assertEquals(304, charge.measurements().get("endSeqId").intValue());
        StepRecord discharge = steps.get(3);
        assertEquals("放电", discharge.stepName());
        assertEquals(0.00431017035590278, discharge.capacityAh(), 1e-15);
    }
    @Test
    void leavesStepCapacityEmptyWhenTheDirectionIsUnknown()
    {
        // 方向未知时不给单项容量能量:cap 是单一值,方向没确认就不知道它属于哪一侧(ADR-0069 §3)。
        String xml = "<bts><cmd>downloadStepLayer_resp</cmd>"
                + "<downloadStepLayer devtype=\"25\" devid=\"81\" subdevid=\"1\" chlid=\"1\" testid=\"9\" />"
                + "<list count=\"1\"><data startseqid=\"1\" endseqid=\"9\" stepindex=\"1\" stepid=\"1\" cycleid=\"2\""
                + " steptype=\"--\" steptime=\"60000\" endatime=\"2026-10-10 09:09:14\" startvolt=\"1\" endvolt=\"1\""
                + " startcurr=\"0\" endcurr=\"0\" cap=\"1.5\" eng=\"2.5\" dcir=\"0\" /></list></bts>";
        StepRecord step = codec.parseStepLayer(xml, Instant.EPOCH).get(0);
        assertNull(step.stepName());
        assertNull(step.capacityAh());
        assertNull(step.energyWh());
        assertEquals(1, step.cycleNo());
        // 明细里保留的是设备原文事实(起始电压、首末序号),没有按方向分派的单项值。
        assertEquals(1.0, step.measurements().get("startVoltage").doubleValue(), 1e-9);
        assertEquals(9, step.measurements().get("endSeqId").intValue());
    }
    @Test
    void reportsAnEmptyStepLayerInsteadOfFailing()
    {
        // 该通道的缓冲里没有测试时(实测 D 那台就是),应答是空列表而不是错误。
        List<StepRecord> steps = codec.parseStepLayer(
                "<bts><cmd>downloadStepLayer_resp</cmd><list count=\"0\" /></bts>", Instant.EPOCH);
        assertTrue(steps.isEmpty());
    }
    @Test
    void rejectsAStepLayerResponseToAnotherCommand()
    {
        BtsProtocolException exception = assertThrows(BtsProtocolException.class,
                () -> codec.parseStepLayer("<bts><cmd>inquire_resp</cmd><list count=\"0\" /></bts>", Instant.EPOCH));
        assertEquals(BtsProtocolException.Reason.UNEXPECTED_COMMAND, exception.reason());
    }
    @Test
    void keepsOnlyAttributesOutsideTheKnownFieldSetAsDynamicMeasurements()
    {
        // 字段集随客户端构建不同(8.0.0.484 有 barcode/auxtemp,8.0.0.530 没有,实测三种构建互不相同)。