From b78295e8da96c3e5e76befeef9582ce5273dbb74 Mon Sep 17 00:00:00 2001
From: yeg <1347747024@qq.com>
Date: 星期二, 29 九月 2026 13:10:23 +0800
Subject: [PATCH] 按代码规范检查整改实体注释、Service Javadoc 与分层边界

---
 battery-device-bts/src/main/java/com/battery/device/bts/BtsXmlCodec.java |  175 ++++++++++++++++++++++++++++------------------------------
 1 files changed, 84 insertions(+), 91 deletions(-)

diff --git a/battery-device-bts/src/main/java/com/battery/device/bts/BtsXmlCodec.java b/battery-device-bts/src/main/java/com/battery/device/bts/BtsXmlCodec.java
index d9a0468..16c5b49 100644
--- a/battery-device-bts/src/main/java/com/battery/device/bts/BtsXmlCodec.java
+++ b/battery-device-bts/src/main/java/com/battery/device/bts/BtsXmlCodec.java
@@ -23,24 +23,22 @@
 import org.w3c.dom.NodeList;
 import org.xml.sax.InputSource;
 import org.xml.sax.helpers.DefaultHandler;
-import com.battery.device.model.ChannelIdentity;
-import com.battery.device.model.DetectedChannel;
-import com.battery.device.model.DetectedDeviceMetadata;
-import com.battery.device.model.DeviceWorkStatus;
-import com.battery.device.model.HistoryAvailability;
-import com.battery.device.model.HistoryPage;
-import com.battery.device.model.HistorySample;
-import com.battery.device.model.Measurement;
-import com.battery.device.model.RealtimeSnapshot;
+import com.battery.device.api.model.ChannelIdentity;
+import com.battery.device.api.model.DeviceConnectionStatus;
+import com.battery.device.api.model.DetectedChannel;
+import com.battery.device.api.model.DetectedDeviceMetadata;
+import com.battery.device.api.model.DeviceWorkStatus;
+import com.battery.device.api.model.Measurement;
+import com.battery.device.api.model.PlannedStep;
+import com.battery.device.api.model.RealtimeSnapshot;
 
 public final class BtsXmlCodec
 {
     private static final DateTimeFormatter BTS_TIME = DateTimeFormatter.ofPattern("yyyy-MM-dd HH.mm.ss");
     private static final Set<String> SNAPSHOT_ATTRIBUTES = Set.of(
             "dev", "cycle_id", "step_id", "step_type", "workstatus", "barcode", "current", "voltage",
-            "capacity", "energy", "totaltime", "relativetime", "auxtemp", "auxvol", "open_or_close", "log_code");
-    private static final Set<String> HISTORY_ATTRIBUTES = Set.of(
-            "seqid", "stepid", "cycleid", "steptype", "testtime", "atime", "volt", "curr", "cap", "eng");
+            "capacity", "energy", "totaltime", "relativetime", "auxtemp", "auxvol", "open_or_close", "log_code",
+            "device_time", "deviceTime", "time", "atime", "testtime");
 
     private final String adapterCode;
     private final ZoneId deviceZone;
@@ -52,11 +50,13 @@
         this.deviceZone = deviceZone;
     }
 
+    /** 构造通道发现的请求报文({@code getdevinfo})。 */
     public String getDeviceInfoRequest()
     {
         return envelope("<cmd>getdevinfo</cmd>");
     }
 
+    /** 构造按通道批量查询实时快照的请求报文({@code inquire});只接受属于本实例的通道身份。 */
     public String inquireRequest(Set<ChannelIdentity> channels, boolean includeBarcode)
     {
         StringBuilder list = new StringBuilder("<cmd>inquire</cmd><list count=\"").append(channels.size()).append("\">");
@@ -72,36 +72,9 @@
         return envelope(list.append("</list>").toString());
     }
 
-    /**
-     * 查询各通道当前 BTS 测试的历史数据上传进度。
-     */
-    public String inquireHistoryRequest(Set<ChannelIdentity> channels)
-    {
-        StringBuilder list = new StringBuilder("<cmd>inquiredf</cmd><list count=\"").append(channels.size()).append("\">");
-        for (ChannelIdentity channel : channels)
-        {
-            requireOwned(channel);
-            list.append("<chl devtype=\"").append(channel.devtype())
-                    .append("\" devid=\"").append(channel.devid())
-                    .append("\" subdevid=\"").append(channel.subdevid())
-                    .append("\" chlid=\"").append(channel.chlid())
-                    .append("\" testid=\"0\"/>");
-        }
-        return envelope(list.append("</list>").toString());
-    }
 
-    public String downloadRequest(ChannelIdentity channel, String btsTestId, long startPosition, int limit)
-    {
-        requireOwned(channel);
-        if (btsTestId == null || btsTestId.isBlank()) throw new IllegalArgumentException("btsTestId must not be blank");
-        if (startPosition < 1) throw new IllegalArgumentException("startPosition must be positive");
-        if (limit < 1 || limit > 1000) throw new IllegalArgumentException("BTS history limit must be between 1 and 1000");
-        return envelope("<cmd>download</cmd><download devtype=\"" + channel.devtype()
-                + "\" devid=\"" + channel.devid() + "\" subdevid=\"" + channel.subdevid()
-                + "\" chlid=\"" + channel.chlid() + "\" auxid=\"0\" testid=\"" + escape(btsTestId)
-                + "\" startpos=\"" + startPosition + "\" count=\"" + limit + "\"/>");
-    }
 
+    /** 解析 {@code getdevinfo_resp},按本实例的适配器编码生成检测柜通道清单。 */
     public List<DetectedChannel> parseDeviceInfo(String xml)
     {
         Document document = parse(xml, "getdevinfo_resp");
@@ -122,6 +95,7 @@
         return List.copyOf(channels);
     }
 
+    /** 解析 {@code inquire_resp} 并按通道身份归集实时快照;{@code receivedAt} 记为服务器接收时间。 */
     public Map<ChannelIdentity, RealtimeSnapshot> parseSnapshots(String xml, Instant receivedAt)
     {
         Document document = parse(xml, "inquire_resp");
@@ -136,66 +110,19 @@
                         "Invalid BTS dev identity: " + element.getAttribute("dev"));
             ChannelIdentity identity = new ChannelIdentity(adapterCode, integer(device[0], "devtype"), integer(device[1], "devid"),
                     integer(device[2], "subdevid"), integer(device[3], "chlid"));
+            Instant deviceTime = optionalDeviceTime(element);
             snapshots.put(identity, new RealtimeSnapshot(identity,
                     DeviceWorkStatus.fromBts(element.getAttribute("workstatus")), optionalInteger(element, "cycle_id"),
                     optionalInteger(element, "step_id"), optionalAttribute(element, "step_type"), optionalAttribute(element, "barcode"),
                     optionalDecimal(element, "current"), optionalDecimal(element, "voltage"), optionalDecimal(element, "capacity"),
                     optionalDecimal(element, "energy"), optionalLong(element, "totaltime"), optionalLong(element, "relativetime"),
-                    optionalAttribute(element, "log_code"), receivedAt, dynamicMeasurements(element, SNAPSHOT_ATTRIBUTES)));
+                    optionalAttribute(element, "log_code"), receivedAt, dynamicMeasurements(element, SNAPSHOT_ATTRIBUTES),
+                    DeviceConnectionStatus.ONLINE, deviceTime, parsePlannedSteps(element), null));
         }
         return Map.copyOf(snapshots);
     }
 
-    /**
-     * 将 BTS 历史清单映射为厂商无关的通道历史可用性。
-     */
-    public List<HistoryAvailability> parseHistoryAvailability(String xml)
-    {
-        Document document = parse(xml, "inquiredf_resp");
-        NodeList nodes = firstElement(document, "list").getElementsByTagName("chl");
-        List<HistoryAvailability> availability = new ArrayList<>(nodes.getLength());
-        for (int i = 0; i < nodes.getLength(); i++)
-        {
-            Element element = (Element) nodes.item(i);
-            ChannelIdentity channel = new ChannelIdentity(adapterCode, integerAttribute(element, "devtype"),
-                    integerAttribute(element, "devid"), integerAttribute(element, "subdevid"),
-                    integerAttribute(element, "chlid"));
-            availability.add(new HistoryAvailability(channel, requiredAttribute(element, "testid"),
-                    longAttribute(element, "count"), booleanText(element.getTextContent(), "uploadComplete")));
-        }
-        return List.copyOf(availability);
-    }
 
-    public HistoryPage parseHistory(String xml, ChannelIdentity channel, String requestedTestId, long requestedStart)
-    {
-        Document document = parse(xml, "download_resp");
-        Element download = firstElement(document, "download");
-        String btsTestId = requiredAttribute(download, "testid");
-        // download 是单通道响应,身份不一致时拒绝结果,避免历史事实被归入错误测试。
-        if (integerAttribute(download, "devtype") != channel.devtype()
-                || integerAttribute(download, "devid") != channel.devid()
-                || integerAttribute(download, "subdevid") != channel.subdevid()
-                || integerAttribute(download, "chlid") != channel.chlid()
-                || !btsTestId.equals(requestedTestId))
-            throw new BtsProtocolException(BtsProtocolException.Reason.IDENTITY_MISMATCH,
-                    "BTS history response identity does not match the request");
-        NodeList nodes = firstElement(document, "list").getElementsByTagName("data");
-        List<HistorySample> samples = new ArrayList<>(nodes.getLength());
-        long next = requestedStart;
-        for (int i = 0; i < nodes.getLength(); i++)
-        {
-            Element element = (Element) nodes.item(i);
-            long sequence = longAttribute(element, "seqid");
-            String rawTime = requiredAttribute(element, "atime");
-            samples.add(new HistorySample(sequence, integerAttribute(element, "stepid"), integerAttribute(element, "cycleid"),
-                    requiredAttribute(element, "steptype"), optionalLong(element, "testtime"), parseDeviceTime(rawTime), rawTime,
-                    optionalDecimal(element, "volt"), optionalDecimal(element, "curr"), optionalDecimal(element, "cap"),
-                    optionalDecimal(element, "eng"), dynamicMeasurements(element, HISTORY_ATTRIBUTES)));
-            // 解析层保留重复和乱序事实;业务层只展示并记录这些设备数据事实。
-            next = Math.max(next, sequence + 1);
-        }
-        return new HistoryPage(channel, btsTestId, requestedStart, next, samples);
-    }
 
     private Document parse(String xml, String expectedCommand)
     {
@@ -258,7 +185,14 @@
         try
         {
             // BTS 时间不带时区,按适配器配置的现场时区解释后统一转换为 UTC 时间点。
-            return LocalDateTime.parse(value, BTS_TIME).atZone(deviceZone).toInstant();
+            try
+            {
+                return Instant.parse(value);
+            }
+            catch (DateTimeParseException ignored)
+            {
+                return LocalDateTime.parse(value, BTS_TIME).atZone(deviceZone).toInstant();
+            }
         }
         catch (DateTimeParseException ex)
         {
@@ -267,6 +201,65 @@
         }
     }
 
+    private Instant optionalDeviceTime(Element element)
+    {
+        for (String name : List.of("device_time", "deviceTime", "time", "atime", "testtime"))
+        {
+            String value = optionalAttribute(element, name);
+            if (value != null) return parseDeviceTime(value);
+        }
+        return null;
+    }
+
+    private List<PlannedStep> parsePlannedSteps(Element channel)
+    {
+        List<PlannedStep> steps = new ArrayList<>();
+        NodeList nodes = channel.getElementsByTagName("plannedStep");
+        if (nodes.getLength() == 0) nodes = channel.getElementsByTagName("planStep");
+        if (nodes.getLength() == 0) nodes = channel.getElementsByTagName("step");
+        for (int i = 0; i < nodes.getLength(); i++)
+        {
+            Element step = (Element) nodes.item(i);
+            String number = firstOptionalAttribute(step, "step_no", "stepNo", "step_id", "stepid", "id");
+            String type = firstOptionalAttribute(step, "step_type", "stepType", "steptype", "type");
+            if (number == null || type == null) continue;
+            steps.add(new PlannedStep(integer(number, "planned step number"), type,
+                    firstOptionalAttribute(step, "name", "step_name", "stepName"),
+                    optionalDecimalFromNames(step, "target_voltage", "targetVoltage", "voltage"),
+                    optionalDecimalFromNames(step, "target_current", "targetCurrent", "current"),
+                    optionalLongFromNames(step, "duration_seconds", "durationSeconds", "duration")));
+        }
+        return List.copyOf(steps);
+    }
+
+    private static String firstOptionalAttribute(Element element, String... names)
+    {
+        for (String name : names)
+        {
+            String value = optionalAttribute(element, name);
+            if (value != null) return value;
+        }
+        return null;
+    }
+
+    private static BigDecimal optionalDecimalFromNames(Element element, String... names)
+    {
+        String value = firstOptionalAttribute(element, names);
+        return value == null ? null : decimal(value, names[0]);
+    }
+
+    private static Long optionalLongFromNames(Element element, String... names)
+    {
+        String value = firstOptionalAttribute(element, names);
+        if (value == null) return null;
+        try { return Long.parseLong(value); }
+        catch (NumberFormatException ex)
+        {
+            throw new BtsProtocolException(BtsProtocolException.Reason.INVALID_VALUE,
+                    "Invalid planned step duration: " + value, ex);
+        }
+    }
+
     private void requireOwned(ChannelIdentity channel)
     {
         if (!adapterCode.equals(channel.adapterCode())) throw new IllegalArgumentException("Channel belongs to another adapter");

--
Gitblit v1.7.1