fix(abcip): native browse paging/pacing/template lease (#572); differential equal-refusal + budget isolation (#575)
#572: listing continues at max(instance in page)+1 (no ascending-order assumption, no-progress guard kept); browsePageDelayMs (default 5 ms) paces pages; DiscoverAsync reads templates through one batch reader (IAbCipTemplateReaderFactory.CreateBatch) holding one lease. #575: differential harness reports a same-status template refusal as an equal refusal; offline test pins the open-attempt budget without relying on the abort ending the phase. Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
+27
-3
@@ -77,7 +77,7 @@ the driver's `DriverConfig` JSON. Key fields:
|
||||
- **`Tags`** — pre-declared `AbCipTagDefinition` list; `Members` for UDT fan-out, `SafetyTag` for GuardLogix safety-partition tags.
|
||||
- **`Probe`** — connectivity-probe `Enabled` / `Interval` / `Timeout` / `ProbeTagPath`.
|
||||
- **`Transport`** — absent (default ⇒ `Native`), `Native`, or `Libplctag` (fallback); see [Transport](#transport-transport-option).
|
||||
- **Discovery** — `EnableControllerBrowse` (`@tags` walk) and `EnableDeclarationOnlyUdtGrouping` (whole-UDT read fast path).
|
||||
- **Discovery** — `EnableControllerBrowse` (`@tags` walk), `EnableDeclarationOnlyUdtGrouping` (whole-UDT read fast path) and `browsePageDelayMs` (native browse pacing, see [Native browse](#native-browse-paging-pacing-and-template-leases)).
|
||||
- **Alarms** — `EnableAlarmProjection` + `AlarmPollInterval` for the ALMD projection.
|
||||
|
||||
Full per-field descriptions live in `AbCipDriverOptions.cs`. The JSON skeleton is
|
||||
@@ -195,8 +195,11 @@ The driver has two interchangeable wire transports, selected by one `DriverConfi
|
||||
[native-ab-transport-gate-results](../plans/2026-10-07-native-ab-transport-gate-results.md).
|
||||
- **libplctag cannot be deleted** until a bench controller allows write verification.
|
||||
- Wide-struct fragmented writes (8-byte alignment) are unit-tested only, never proven on a controller (#567).
|
||||
- Native browse: listing pages are unpaced, and template reads outside an enumeration open a connection per
|
||||
call (#572).
|
||||
- Native browse (#572, resolved in code): listing paging no longer assumes ascending instance order, pages
|
||||
are paced, and a browse's template reads share one lease. One residual: whether a real controller ever
|
||||
lists a page out of order is unobserved — the reader is now correct either way. `browsePageDelayMs` is
|
||||
not editable in the AdminUI form (its typed model drops unknown keys on save, #576); author it in the
|
||||
driver-config JSON.
|
||||
- Micro800, routed and `logixpccc` paths are covered by `ab_server` only. The Micro800 profile passes
|
||||
natively. The AbLegacy default excludes `logixpccc` (and PLC-5).
|
||||
- Follow-up issues for this workstream: #564 to #577.
|
||||
@@ -241,6 +244,27 @@ integration fixture is down during normal dev (Docker host fixture currently off
|
||||
See [Uns.md §Array tags](../Uns.md#array-tags-1-d) for the cross-driver coverage matrix
|
||||
and the UI authoring flow.
|
||||
|
||||
## Native browse: paging, pacing and template leases
|
||||
|
||||
Native symbol browse (`transport` = `Native`; libplctag pages inside its own library and is unaffected):
|
||||
|
||||
- **Paging continues past the highest instance seen**, not the last entry of a page. The Symbol Object
|
||||
listing is requested from `start`; the next request starts at `max(instance in the reply) + 1`, so the
|
||||
order a controller lists a page in does not matter. A "more" reply whose highest instance is still below
|
||||
its `start` (or equals `uint.MaxValue`) made no progress and fails with `BadReply` ("made no progress")
|
||||
rather than looping against the controller.
|
||||
- **Pages are paced.** `browsePageDelayMs` (driver config, default **5**, `0` = back-to-back, negative =
|
||||
`0`) pauses before every page after the first. 5 ms is small against a page's own round-trip, so a
|
||||
50k-symbol browse gains a few seconds, but it breaks the back-to-back burst (one request in flight, roughly
|
||||
a Studio 5000 online browse) into slices the controller's message processing can interleave with. Raise it
|
||||
for a controller that is tight on CPU; the pause applies per listing (the controller listing and each
|
||||
program listing).
|
||||
- **Template reads in a browse share one lease.** `DiscoverAsync` reads the UDT templates (and nested
|
||||
templates) of a device through a single batch reader (`IAbCipTemplateReaderFactory.CreateBatch`); the
|
||||
native batch reader holds one device lease from its first read until the browse of that device ends,
|
||||
instead of leasing per template. `FetchUdtShapeAsync` outside a discovery run is unchanged: one
|
||||
self-contained lease per call.
|
||||
|
||||
## Controller-discovered UDT member variables
|
||||
|
||||
When `EnableControllerBrowse` is set, the driver walks the controller's `@tags` symbol table and
|
||||
|
||||
Reference in New Issue
Block a user