From 1de35281a699b8d2ca90777f63c7e032c3c9dc3d Mon Sep 17 00:00:00 2001
From: yeg <1347747024@qq.com>
Date: 星期一, 12 十月 2026 13:34:34 +0800
Subject: [PATCH] 对接第 6 台新威上位机(7.6 客户端,走库路线),并修两处对接中暴露的问题

---
 battery-device-bts/src/main/java/com/battery/device/bts/NewareBtsAdapter.java |   67 ++++++++++++++++++++++++++++++++-
 1 files changed, 64 insertions(+), 3 deletions(-)

diff --git a/battery-device-bts/src/main/java/com/battery/device/bts/NewareBtsAdapter.java b/battery-device-bts/src/main/java/com/battery/device/bts/NewareBtsAdapter.java
index 5ec2f15..d85470e 100644
--- a/battery-device-bts/src/main/java/com/battery/device/bts/NewareBtsAdapter.java
+++ b/battery-device-bts/src/main/java/com/battery/device/bts/NewareBtsAdapter.java
@@ -62,6 +62,11 @@
     private final AdapterCapabilities capabilities;
     /** 每通道最近一次工步层读取;见 {@link #stepLayer}。 */
     private final Map<String, StepLayer> stepLayers = new ConcurrentHashMap<>();
+    /**
+     * 该客户端有没有数据文件命令({@code inquiredf} / {@code download} / {@code downloadStepLayer})。
+     * 未知为 null;探测一次后固定下来——见 {@link #supportsDataFileCommands}。
+     */
+    private volatile Boolean dataFileCommandsSupported;
 
     public NewareBtsAdapter(String adapterCode, BtsTransport transport, BtsXmlCodec codec)
     {
@@ -191,6 +196,12 @@
         }
         catch (BtsProtocolException unsupported)
         {
+            // 客户端不提供 inquiredf(实测 7.6.0.1475):本来源不按设备数据行接收,逐点回落到轮询槽位。
+            return null;
+        }
+        catch (BtsTransportException unanswered)
+        {
+            // 探测说支持、发出去却不作答(已登记不再试):同样回落到轮询槽位,不把来源不可达说成"不按行接收"。
             return null;
         }
         if (state.count() < 1) return new SampleWatermark(null, 0L);
@@ -256,9 +267,19 @@
     private StepLayer fetchStepLayer(ChannelIdentity channel)
     {
         long fetchedAtNanos = System.nanoTime();
+        // 客户端没有这条命令时一条都不发:见 supportsDataFileCommands。
+        if (!supportsDataFileCommands(channel)) return new StepLayer(List.of(), null, fetchedAtNanos);
         Instant receivedAt = Instant.now();
-        List<StepRecord> layers = codec.parseStepLayer(
-                transport.exchange(codec.downloadStepLayerRequest(channel)), receivedAt);
+        List<StepRecord> layers;
+        try
+        {
+            layers = codec.parseStepLayer(transport.exchange(codec.downloadStepLayerRequest(channel)), receivedAt);
+        }
+        catch (BtsTransportException unanswered)
+        {
+            rememberUnanswered(unanswered);
+            return new StepLayer(List.of(), null, fetchedAtNanos);
+        }
         if (layers.isEmpty()) return new StepLayer(List.of(), null, fetchedAtNanos);
         // 通道仍在动(运行、暂停、保护中)时最后一步没结束:它和它所在的循环都先按住。
         if (!isStillRunning(channel)) return new StepLayer(layers, null, fetchedAtNanos);
@@ -343,7 +364,18 @@
 
     private BtsXmlCodec.DataFileState dataFileState(ChannelIdentity channel)
     {
-        return codec.parseDataFileState(transport.exchange(codec.inquiredfRequest(channel)));
+        if (!supportsDataFileCommands(channel))
+            throw new BtsProtocolException(BtsProtocolException.Reason.MISSING_FIELD,
+                    "该客户端不提供 inquiredf");
+        try
+        {
+            return codec.parseDataFileState(transport.exchange(codec.inquiredfRequest(channel)));
+        }
+        catch (BtsTransportException unanswered)
+        {
+            rememberUnanswered(unanswered);
+            throw unanswered;
+        }
     }
 
     private List<CabinetRealtime> download(ChannelIdentity channel, long testId, long startPosition, int count)
@@ -352,6 +384,35 @@
                 Instant.now());
     }
 
+    /**
+     * 这台客户端有没有数据文件命令({@code inquiredf}/{@code download}/{@code downloadStepLayer})。
+     *
+     * <p>为什么必须先问一句再发:**对不支持这些命令的客户端发出去,代价不只是超时**。2026-10-12 实测
+     * (客户端 7.6.0.1475):发一条 {@code inquiredf} 之后,该接口的 {@code inquire} 连续 8 分钟无应答
+     * ({@code getdevinfo} 仍正常)——那等于干扰了那台上位机的对外接口。所以不支持的客户端
+     * **一条都不发**,逐点回落到轮询槽位、工步层为空。
+     *
+     * <p>探测依据是 {@code inquire} 应答的字段:7.6 的应答不带 {@code cycle_id},8.0 的带;
+     * 实测三个构建(7.6.0.1475 / 8.0.0.484 / 8.0.0.530)里"有没有 {@code cycle_id}"与
+     * "有没有这三条数据文件命令"同向。探测本身读不到应答时**抛出**(不吞):那说明来源不可达,
+     * 按契约要抛异常,不能被当成"该来源不按数据行接收"。
+     */
+    private boolean supportsDataFileCommands(ChannelIdentity channel)
+    {
+        Boolean known = dataFileCommandsSupported;
+        if (known != null) return known;
+        RealtimeSnapshot snapshot = inquire(Set.of(channel)).get(channel);
+        boolean supported = snapshot != null && snapshot.cycleNo() != null;
+        dataFileCommandsSupported = supported;
+        return supported;
+    }
+
+    /** 探测说支持、真发出去却不作答:记下来不再反复试——反复发一条无应答的命令会持续干扰那台接口。 */
+    private void rememberUnanswered(BtsTransportException failure)
+    {
+        if (failure.reason() == BtsTransportException.Reason.TIMEOUT) dataFileCommandsSupported = false;
+    }
+
     /** 行的序号比水位大就是要的行;序号反而更小说明换了测试,此时按设备时间判定。 */
     private static boolean isAfter(SampleWatermark watermark, CabinetRealtime row)
     {

--
Gitblit v1.7.1