[M5] mxaccess-asb: F25 step 8 — subscription operations

CreateSubscription / AddMonitoredItems / Publish / DeleteSubscription.
Completes the IASBIDataV2 read-and-subscribe path; remaining ops
(Write/PublishWriteComplete/DeleteMonitoredItems) are mechanical
extensions of the same pattern.

Contracts:
* `MonitoredItemValue` codec (IAsbCustomSerializableType binary
  fast-path: ItemIdentity + RuntimeValue + AsbVariant per
  `AsbContracts.cs:1064-1068`) with array codec (4-byte int32
  count + per-element body, mirrors `WriteArrayToStream` at
  `cs:1095-1103`).

Request builders:
* `build_create_subscription_request_body(max_queue_size,
  sample_interval)` — primitive fields per `cs:215-223`.
* `build_delete_subscription_request_body(subscription_id)` —
  primitive field per `cs:232-237`.
* `build_publish_request_body(subscription_id)` — primitive field
  per `cs:287-292`.
* `build_add_monitored_items_request_body(subscription_id, items,
  require_id)` — minimal MonitoredItem shape (Item +
  SampleInterval + Buffered). Full optional-field set
  (Active/TimeDeadband/ValueDeadband/UserData) deferred to a later
  iteration once a live capture confirms the WCF DataContract
  XML wire form.

Response decoders:
* `decode_create_subscription_response` — single int64
  SubscriptionId field. Decoder accepts Int64Text, Int32Text,
  Zero/One, or numeric-string Chars (covers all WCF binary
  numeric encodings).
* `decode_add_monitored_items_response` — Status array +
  ItemCapabilities-presence flag (mirrors RegisterItemsResponse).
* `decode_publish_response` — Status array + Values
  (MonitoredItemValue) array.

`BodyField::Int64Element` variant added for the primitive
SubscriptionId / MaxQueueSize / SampleInterval fields. `uint64`
helper casts to i64 (covers proven value range; if ulong > i64::MAX
ever appears we'll add UInt64Text to F21's NbfxText enum).

Client wrappers (4 new methods on AsbClient):
* `create_subscription(max_queue_size, sample_interval)`
* `add_monitored_items(subscription_id, items, require_id)`
* `publish(subscription_id)`
* `delete_subscription(subscription_id)`

11 new tests cover:
* MonitoredItemValue round-trip + array round-trip.
* CreateSubscription request body shape (Int64 payloads).
* CreateSubscription response decoder via Int64Text.
* CreateSubscription response decoder via Chars text fallback.
* CreateSubscription response missing-field error.
* AddMonitoredItems body carries SubscriptionId + MonitoredItem
  elements.
* AddMonitoredItems response Status round-trip.
* DeleteSubscription body carries SubscriptionId.
* Publish request body shape.
* Publish response Status + Values round-trip.

Workspace: 691 tests pass (was 680, +11). The asb-subscribe example
can now do create_subscription → add_monitored_items → publish-loop
→ delete_subscription once wire-byte reconciliation against a live
capture confirms the MonitoredItem XML shape.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Joseph Doherty
2026-05-05 12:57:59 -04:00
parent c6570dcd06
commit b543eb1f84
5 changed files with 774 additions and 13 deletions
+5 -1
View File
@@ -46,7 +46,11 @@ move to `## Resolved` with a date + commit hash.
**Resolves when:** F19-F26 are all closed and the four DoD bullets above pass.
**Cumulative execution log.** F19 + F23 (`ed17c07`); F24 (`7611d9e`); F20 (`9dfd193`); F22 (`43c10a1`); F21 (`5f98558`); F25 step 1 (`25dbd8d`); F25 step 2 (`a2b8989`); F25 step 3 (`c4bf0a0`); F25 step 4 (`1e59249`); F25 step 5 (`9b8133f`); F25 step 6 (`321b796`); F25 step 7 (`1b1ee1e`); F26 step 1 (`8a0f92b`); F26 step 2 (`14bb529`); `examples/asb-subscribe.rs` rewrite landed in this commit:
**Cumulative execution log.** F19 + F23 (`ed17c07`); F24 (`7611d9e`); F20 (`9dfd193`); F22 (`43c10a1`); F21 (`5f98558`); F25 step 1 (`25dbd8d`); F25 step 2 (`a2b8989`); F25 step 3 (`c4bf0a0`); F25 step 4 (`1e59249`); F25 step 5 (`9b8133f`); F25 step 6 (`321b796`); F25 step 7 (`1b1ee1e`); F26 step 1 (`8a0f92b`); F26 step 2 (`14bb529`); example rewrite (`c6570dc`); F25 step 8 landed in this commit:
- F25 step 8: subscription operations — `CreateSubscription`, `AddMonitoredItems`, `Publish`, `DeleteSubscription`. New `MonitoredItemValue` codec in contracts.rs (`IAsbCustomSerializableType` binary fast-path: ItemIdentity + RuntimeValue + AsbVariant per `cs:1064-1068`). New `MinimalMonitoredItem` request struct exposing only the proven fields (Item, SampleInterval, Buffered) — optional Active/TimeDeadband/ValueDeadband/UserData deferred to a later iteration once a live capture confirms the WCF DataContract XML shape. Per-operation builders, response decoders, and client wrappers follow the established F25 pattern. New `BodyField::Int64Element` variant for the `<SubscriptionId>` / `<MaxQueueSize>` / `<SampleInterval>` primitive fields. The subscription path lifts the `examples/asb-subscribe.rs` "Read-loop" caveat — once wire-byte reconciliation lands, the example can do `create_subscription → add_monitored_items → publish-loop → delete_subscription`. 11 new tests cover MonitoredItemValue round-trip + array, CreateSubscription request body shape + response decode (Int64 + Chars text fallback + missing-field error), AddMonitoredItems request body shape + response decode, DeleteSubscription request body, Publish request + response (with full Status + Values round-trip via the in-memory body synthesis pattern).
**Earlier slices:**
- example rewrite (commit `c6570dc`):
- `examples/asb-subscribe.rs` rewrite: replaces the M5 placeholder with an actual end-to-end demo that exercises the F25 + F26 stack: `AsbTransport::connect` (TCP + preamble + DH handshake) → `register_items``read``disconnect``send_end`. Reads endpoint config from `MX_ASB_HOST`, `MX_ASB_PASSPHRASE`, `MX_ASB_VIA`, `MX_TEST_TAG` env vars (analogous to the NMX `connect-write-read` example's pattern). Defaults port 5074 when host omits one; defaults via URI to `net.tcp://{host}/ASBService` when `MX_ASB_VIA` is unset. Without `MX_LIVE` set, prints the `Setup-LiveProbeEnv.ps1` hint and exits cleanly. Connection-id is a fresh 16-byte random buffer (matches .NET's `Guid.NewGuid()` at `MxAsbDataClient.cs:36`). The example is a Read-loop until F25 subscription ops land — at that point the example will gain a Publish-loop and live up to its name.
**Earlier slices:**