From 9c93934b70ccfca081102944a47f3baafad3c218 Mon Sep 17 00:00:00 2001
From: yeg <1347747024@qq.com>
Date: 星期一, 28 九月 2026 16:43:57 +0800
Subject: [PATCH] 实现新威 7.6 的工步与循环数据取数

---
 ruoyi-admin/src/test/java/com/battery/business/service/impl/TestDataReceptionServiceImplTest.java |   67 +++++++++++++++++++++++++++++++--
 1 files changed, 62 insertions(+), 5 deletions(-)

diff --git a/ruoyi-admin/src/test/java/com/battery/business/service/impl/TestDataReceptionServiceImplTest.java b/ruoyi-admin/src/test/java/com/battery/business/service/impl/TestDataReceptionServiceImplTest.java
index c6eb367..a7761fc 100644
--- a/ruoyi-admin/src/test/java/com/battery/business/service/impl/TestDataReceptionServiceImplTest.java
+++ b/ruoyi-admin/src/test/java/com/battery/business/service/impl/TestDataReceptionServiceImplTest.java
@@ -185,11 +185,12 @@
         Instant started = Instant.parse("2026-09-02T04:00:10Z");
         Instant ended = Instant.parse("2026-09-02T04:00:50Z");
         when(fixture.simulator.stepRecords(any(ChannelIdentity.class))).thenReturn(List.of(new StepRecord(
-                "STEP-1", 1, 2, "恒流充电", "cc", started, ended, 3.4, 2.5, 8.4, ended.toString(), "UTC", ended,
+                "STEP-1", 1, 2, "恒流充电", "cc", started, ended, 3.4, 2.6, 2.5, 8.4, ended.toString(), "UTC", ended,
                 java.util.Map.of("stepDuration", new BigDecimal("40.125"), "startVoltage", new BigDecimal("3.1"),
                         "chargeMedianVoltage", new BigDecimal("3.25"), "dischargeMedianVoltage", BigDecimal.ZERO))));
         when(fixture.simulator.cycleRecords(any(ChannelIdentity.class))).thenReturn(List.of(new CycleRecord(
-                "CYCLE-1-1-1", 1, started, ended, 2.5, 2.4, 8.4, 8.0, 96, ended.toString(), "UTC", ended)));
+                "CYCLE-1-1-1", 1, started, ended, 2.5, 2.4, 8.4, 8.0, 96d, ended.toString(), "UTC", ended,
+                CycleRecord.SOURCE_DEVICE)));
         when(fixture.mapper.insertStepRecord(any())).thenReturn(1);
         when(fixture.mapper.insertCycleRecord(any())).thenReturn(1);
 
@@ -207,8 +208,29 @@
         assertEquals(BigDecimal.ZERO, metrics.getBigDecimal("dischargeMedianVoltage"));
         assertEquals("CYCLE-1-1-1", cycle.getValue().getSourceRecordId());
         assertEquals("record:CYCLE-1-1-1", cycle.getValue().getDedupeKey());
+        // 设备或文件直接提供的循环统计保留原有来源类别。
+        assertEquals(CycleRecord.SOURCE_DEVICE, cycle.getValue().getSourceKind());
+        // 工步结束时的设备读数:电压与电流一起落库。
+        assertEquals(new BigDecimal("3.4"), step.getValue().getVoltageV());
+        assertEquals(new BigDecimal("2.6"), step.getValue().getCurrentA());
         assertEquals(Date.from(ended), step.getValue().getServerReceivedAtUtc());
         assertEquals(Date.from(ended), cycle.getValue().getServerReceivedAtUtc());
+    }
+
+    /** 接收调度每秒运行,但来源按自己的写库节奏产出工步:不按来源节奏限流会每秒重查厂商库。 */
+    @Test void readsTheStepLayerOncePerSourceCadenceInsteadOfEverySchedulerTick()
+    {
+        Fixture fixture = fixture();
+        when(fixture.simulator.stepRecords(any(ChannelIdentity.class))).thenReturn(List.of());
+        when(fixture.simulator.cycleRecords(any(ChannelIdentity.class))).thenReturn(List.of());
+
+        TestDataReceptionServiceImpl service = fixture.service();
+        service.captureReceivedData();
+        service.captureReceivedData();
+        service.captureReceivedData();
+
+        verify(fixture.simulator, times(1)).stepRecords(any(ChannelIdentity.class));
+        verify(fixture.simulator, times(1)).cycleRecords(any(ChannelIdentity.class));
     }
 
     @Test
@@ -251,19 +273,54 @@
         fixture.tests.selectTestById(1L).setReceptionBoundaryAtUtc(Date.from(CAPTURED_AT));
         Instant pausedAt = CAPTURED_AT.minusSeconds(10);
         when(fixture.simulator.stepRecords(any())).thenReturn(List.of(new StepRecord("paused", 1, 1, "工步", "cc",
-                pausedAt.minusSeconds(20), pausedAt, 3, 1, 2, pausedAt.toString(), "UTC", pausedAt, java.util.Map.of())));
+                pausedAt.minusSeconds(20), pausedAt, 3d, 1.5d, 1d, 2d, pausedAt.toString(), "UTC", pausedAt,
+                java.util.Map.of())));
         when(fixture.simulator.cycleRecords(any())).thenReturn(List.of(new CycleRecord("paused", 1,
-                pausedAt.minusSeconds(20), pausedAt, 1, 1, 2, 2, 100, pausedAt.toString(), "UTC", pausedAt)));
+                pausedAt.minusSeconds(20), pausedAt, 1, 1, 2, 2, 100d, pausedAt.toString(), "UTC", pausedAt,
+                CycleRecord.SOURCE_DEVICE)));
         fixture.service().captureReceivedData();
         verify(fixture.mapper, never()).insertStepRecord(any());
         verify(fixture.mapper, never()).insertCycleRecord(any());
+    }
+
+    /** 循环统计的来源类别随记录落库:聚合值必须能与设备或文件提供的值区分。 */
+    @Test void persistsCycleSourceKindAndKeepsMissingMetricsEmpty()
+    {
+        Fixture fixture = fixture();
+        when(fixture.simulator.cycleRecords(any(ChannelIdentity.class))).thenReturn(List.of(
+                new CycleRecord("neware:442:cycle:3", 3, CAPTURED_AT.minusSeconds(600), CAPTURED_AT, 2.375, 1.383,
+                        4.059, 1.715, null, CAPTURED_AT.toString(), "Asia/Hong_Kong", CAPTURED_AT,
+                        CycleRecord.SOURCE_AGGREGATED)));
+        when(fixture.simulator.stepRecords(any(ChannelIdentity.class))).thenReturn(List.of(new StepRecord(
+                "neware:442:step:3:7", 3, 7, null, "7", CAPTURED_AT.minusSeconds(60), CAPTURED_AT, 1.3, 0.4, null, null,
+                CAPTURED_AT.toString(), "Asia/Hong_Kong", CAPTURED_AT,
+                java.util.Map.of("chargeCapacity", new BigDecimal("13.888888888889")))));
+        when(fixture.mapper.insertCycleRecord(any())).thenReturn(1);
+        when(fixture.mapper.insertStepRecord(any())).thenReturn(1);
+
+        fixture.service().captureReceivedData();
+
+        ArgumentCaptor<TTestCycleRecord> cycle = ArgumentCaptor.forClass(TTestCycleRecord.class);
+        verify(fixture.mapper).insertCycleRecord(cycle.capture());
+        assertEquals(CycleRecord.SOURCE_AGGREGATED, cycle.getValue().getSourceKind());
+        // 没有可用充电容量就没有效率:空值不是零。
+        assertNull(cycle.getValue().getEfficiency());
+        assertEquals(new BigDecimal("2.375"), cycle.getValue().getChargeCapacityAh());
+
+        ArgumentCaptor<TTestStepRecord> step = ArgumentCaptor.forClass(TTestStepRecord.class);
+        verify(fixture.mapper).insertStepRecord(step.capture());
+        // 方向未确认的工步没有单项容量与能量:落库保持空值,四组明细仍在扩展里。
+        assertNull(step.getValue().getCapacityAh());
+        assertNull(step.getValue().getEnergyWh());
+        assertTrue(step.getValue().getExtensionMeasurementsJson().contains("chargeCapacity"));
     }
 
     @Test void deletedAggregateDoesNotReturnOnRepeatedObservation()
     {
         Fixture fixture = fixture();
         when(fixture.simulator.stepRecords(any())).thenReturn(List.of(new StepRecord("deleted", 1, 1, "工步", "cc",
-                CAPTURED_AT.minusSeconds(10), CAPTURED_AT, 3, 1, 2, CAPTURED_AT.toString(), "UTC", CAPTURED_AT, java.util.Map.of())));
+                CAPTURED_AT.minusSeconds(10), CAPTURED_AT, 3d, 1.5d, 1d, 2d, CAPTURED_AT.toString(), "UTC", CAPTURED_AT,
+                java.util.Map.of())));
         when(fixture.mapper.wasAggregateDeleted(1L, "STEP", 4L, "dev-sim:22:1:1:1", "record:deleted")).thenReturn(true);
         fixture.service().captureReceivedData();
         verify(fixture.mapper, never()).insertStepRecord(any());

--
Gitblit v1.7.1