docs(driver-expansion): resolve all open design concerns from the review pass
Every concern the 7-agent review parked is now decided and integrated: - universal browser: in-flight capture coalescing keyed (driverType, config-hash) + global cap of 4 concurrent captures; CanBrowse catches a throwing TryCreate and defensively shuts down the throwaway instance; cleanup ShutdownAsync bounded at 10s (R2-01); picker Refresh = close + re-capture. Coalesced sessions share an immutable tree, so DisposeAsync drops the session's reference, not the tree. - mtconnect: UNAVAILABLE pinned to BadNoCommunication (fleet's BadCommunicationError stays reserved for the driver's own transport failures); TIME_SERIES sampleCount flows into ArrayDim; Subscribe timeout bounds only the stream-start handshake; library version/TFM folded into the license checklist. - mqtt: browse rebirth is an explicit DriverOperator-gated "Request rebirth" button (RequestRebirthAsync(scope)) - never fired by open/root/expand; hand-rolled reconnect loop committed for P1 (MQTTnet v5 dropped ManagedMqttClient); Wave-2-start library re-verification checkbox; implement from the Sparkplug v3.0 spec text. - bacnet: P1 opens with a first-spike checklist (package TFM, BBMD/ segmented-RPM/COV API smoke, unicast-I-Am fixture behavior); AdminUI-node browse registers a second foreign device - BBMD FD-table sizing note. - sql-poll: split-node browse needs env parity for Sql__ConnectionStrings__ refs on admin nodes (actionable error + session-only pasted literal); one-row-per-key query contract (last-wins + rate-limited warning); absent key -> BadNoData; operationTimeout > commandTimeout authoring rule; Browser->Driver.Sql SqlClient transitive on AdminUI accepted on record. - omron: FINS framer gated on W227 + live golden vectors; CIP string layout is the first P1 live-gate item; AbCip harness static-init anti-pattern note; writable-defaults-true operator warning. - modbus-rtu: first P1 step confirms pymodbus exposes framer=rtu on a TCP server (custom-script fallback otherwise). - program doc: new cross-cutting rule - driver ctors must be connection-free (the universal browser's CanBrowse throwaway instances depend on it).
This commit is contained in:
@@ -57,7 +57,7 @@ only (it owns the shared `TagConfigJson` field readers used by the parser).
|
||||
|
||||
```xml
|
||||
<!-- Directory.Packages.props -->
|
||||
<PackageVersion Include="BACnet" Version="4.0.0" /> <!-- confirm the exact current release when implementing -->
|
||||
<PackageVersion Include="BACnet" Version="4.0.0" /> <!-- placeholder — the exact version + net10 TFM load is item 1 of the P1 first-spike checklist (§10) -->
|
||||
<!-- Driver.Bacnet.csproj -->
|
||||
<PackageReference Include="BACnet" />
|
||||
```
|
||||
@@ -238,6 +238,16 @@ open. That is the documented **P-later graduation** to a bespoke lazy browser:
|
||||
**v1 uses the universal browser. State this clearly in the PR: BACnet ships with `SupportsOnlineDiscovery
|
||||
= true` and zero browser code.**
|
||||
|
||||
**Deployment note — AdminUI node network posture.** The universal browser runs its
|
||||
`InitializeAsync → DiscoverAsync` capture **in the AdminUI process**, so the **AdminUI node itself needs
|
||||
UDP reachability to the BACnet network** for the picker to browse — this is the same in-process posture as
|
||||
the existing OpcUaClient/Galaxy browsers, but BACnet is the **first UDP/broadcast** one, so firewall/VLAN
|
||||
rules that only whitelist the runtime driver node will silently break browse. When the AdminUI node is
|
||||
off-segment, its browse capture registers as a **second foreign device** with the site BBMD (in addition
|
||||
to the runtime driver's standing registration) for the duration of the capture — some BBMDs cap their
|
||||
foreign-device-table entries, so size the FD table (and any per-source firewall rules) for **both**
|
||||
registrations, not one.
|
||||
|
||||
---
|
||||
|
||||
## 5. TagConfig + driver-config JSON
|
||||
@@ -475,8 +485,10 @@ live-gate discipline:
|
||||
route** (and bridged docker networks don't forward subnet broadcasts into containers either). So the
|
||||
suite as run from the dev machine has exactly two working paths: (a) **directed unicast Who-Is** at
|
||||
the fixture's host IP:port (host-mode `bacserv` answers a unicast Who-Is with a unicast I-Am to the
|
||||
request's source address — verify this against the pinned `bacserv` build early, since spec-strict
|
||||
stacks may broadcast the I-Am, which would never arrive); and (b) the **foreign-device/BBMD leg** —
|
||||
request's source address — **verifying this against the pinned `bacserv`/`BasicServer` builds is item 3
|
||||
of the P1 first-spike checklist (§10)**, since spec-strict stacks may broadcast the I-Am, which would
|
||||
never arrive; if they broadcast, this leg collapses to the BBMD/foreign-device leg below); and (b) the
|
||||
**foreign-device/BBMD leg** —
|
||||
register with a BBMD container on the docker host; BVLC `Forwarded-NPDU` relay is *unicast* and routes
|
||||
across subnets, so this leg exercises real broadcast distribution cross-VM and is exactly the field
|
||||
topology. A genuinely broadcast-only leg (plain Who-Is with no BBMD) can only run **on the docker host
|
||||
@@ -498,6 +510,26 @@ slightly above OpcUaClient, driven by the callback→async adapter + COV lifetim
|
||||
Each phase is independently shippable (mirror how OpcUaClient landed across PRs):
|
||||
|
||||
- **P1 — Connect + Read + Discover (+ universal browse) + BBMD. (MVP, delivers real value.)**
|
||||
|
||||
**P1 opens with a first-spike verification checklist.** The library and fixture facts below came from
|
||||
research (`docs/research/drivers/bacnet-ip.md`) and were **unverifiable offline** — verify them on the
|
||||
wire before building anything on top of them:
|
||||
|
||||
1. **NuGet package reality.** Confirm the exact current version of the `BACnet` package (ela-compil
|
||||
fork) and that its TFM set actually restores + loads on **net10.0** (pin the confirmed version in
|
||||
`Directory.Packages.props` — see the §2 placeholder).
|
||||
2. **API-surface smoke test.** Exercise the specific API surface this design depends on:
|
||||
BBMD/foreign-device registration (`Register-Foreign-Device` + TTL), **segmented**
|
||||
ReadPropertyMultiple (a large `object-list` fetch), and SubscribeCOV callback delivery
|
||||
(`OnCOVNotification`). Any gap here reshapes §3/§4 before real work starts.
|
||||
3. **Fixture unicast I-Am behavior.** Verify the pinned `bacserv` and `BasicServer` fixture builds
|
||||
answer a **unicast** Who-Is with a **unicast** I-Am to the request's source address — a spec-strict
|
||||
stack may broadcast the I-Am, which would **silently break the hermetic cross-VM test path** §9
|
||||
relies on (the directed-Who-Is leg from the dev machine). If either fixture broadcasts, adjust the
|
||||
cross-VM suite to the **BBMD/foreign-device leg only** and keep directed-unicast tests on the
|
||||
docker host itself.
|
||||
|
||||
Then the P1 build proper:
|
||||
`.Contracts` (`BacnetDriverOptions`, enums, `BacnetTagAddress`, `BacnetEquipmentTagParser`);
|
||||
`BacnetClientAdapter` (async wrap); `BacnetDriver : IDriver, ITagDiscovery, IReadable,
|
||||
IHostConnectivityProbe` with `SupportsOnlineDiscovery=true` + `RediscoverPolicy =>
|
||||
@@ -518,7 +550,9 @@ Each phase is independently shippable (mirror how OpcUaClient landed across PRs)
|
||||
|
||||
1. **BBMD / foreign-device registration + UDP broadcast reachability (HIGH — the top risk).** The server
|
||||
is usually not on the controllers' L2 segment, so correct foreign-device registration + TTL renewal is
|
||||
load-bearing and is exactly what's hardest to reproduce in docker/CI. Mitigation: build BBMD in from
|
||||
load-bearing and is exactly what's hardest to reproduce in docker/CI. Note the AdminUI browse capture
|
||||
adds a **second** foreign-device registration off-segment (§4.2 deployment note) — account for it in
|
||||
BBMD FD-table sizing. Mitigation: build BBMD in from
|
||||
P1, test explicitly with the `bacnet-docker` multi-network compose, gate a live suite against a real
|
||||
site BBMD.
|
||||
2. **COV lifetime/renewal + non-COV fallback (HIGH, P2).** Subscriptions silently expire if not renewed;
|
||||
|
||||
Reference in New Issue
Block a user