| | |
| | | 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) |
| | | { |
| | |
| | | } |
| | | catch (BtsProtocolException unsupported) |
| | | { |
| | | // 客户端不提供 inquiredf(实测 7.6.0.1475):本来源不按设备数据行接收,逐点回落到轮询槽位。 |
| | | return null; |
| | | } |
| | | catch (BtsTransportException unanswered) |
| | | { |
| | | // 探测说支持、发出去却不作答(已登记不再试):同样回落到轮询槽位,不把来源不可达说成"不按行接收"。 |
| | | return null; |
| | | } |
| | | if (state.count() < 1) return new SampleWatermark(null, 0L); |
| | |
| | | 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); |
| | |
| | | |
| | | private BtsXmlCodec.DataFileState dataFileState(ChannelIdentity 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) |
| | |
| | | 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) |
| | | { |