[M5] mxaccess-asb: F25 step 5 — KeepAlive + Read + one-way client ops
Extends AsbClient with one-way operation support (`IsOneWay = true`
in IASBIDataV2) plus the KeepAlive and Read operations.
Client API additions:
* `send_envelope_one_way(env)` — frames in SizedEnvelope, writes,
returns immediately. No response read. Mirrors WCF's IsOneWay
semantics for KeepAlive / Disconnect / AuthenticateMe.
* `send_signed_envelope_one_way(action, body, force_hmac)` —
one-way variant that runs the body through F23's authenticator
signing path so the ConnectionValidator header is attached.
* `keep_alive()` — sends an empty `KeepAliveRequest` with default
signing. Used to keep the channel alive past the WCF inactivity
timeout (30s default at `MxAsbDataClient.cs:683`).
* `read(items)` — sends a signed Read envelope, decodes
ReadResponse with both Status and Values arrays.
Operations API additions:
* `build_keep_alive_request_body()` — empty wrapper element +
asb.contracts.messages namespace. Mirror of `AsbContracts.cs:117`
(`public sealed class KeepAlive : ConnectedRequest;`).
* `ReadResponse { status: Vec<ItemStatus>, values: Vec<RuntimeValue> }`
per `AsbContracts.cs:169-179`.
* `decode_read_response(body_tokens)` — pulls both ASBIData
payloads, decodes Status as ItemStatus[], decodes Values via
`decode_runtime_value_array` (4-byte int32 count + per-element
`RuntimeValue::decode` from F24).
5 new tests:
* KeepAlive body shape (empty wrapper, correct namespace).
* ReadResponse decoder round-trip with both Status and Values.
* ReadResponse decoder graceful handling when Values is absent
(returns empty vec).
* End-to-end client::keep_alive — peer drains SizedEnvelope but
doesn't respond; client returns Ok().
* End-to-end client::read — peer responds with synthetic
ReadResponse, client recovers Values[0].timestamp_binary == 1234
and Values[0].status round-trip.
Stubbed for next F25 iterations:
* AsbClient::connect — DH Connect + AuthenticateMe handshake. Needs
ConnectRequest / ConnectResponse builders (regular WCF XML, not
the IAsbCustomSerializableType fast-path).
* Write / PublishWriteComplete / CreateSubscription /
AddMonitoredItems / Publish / Disconnect operation wrappers.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+5
-1
@@ -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 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 landed in this commit:
|
||||
- F25 step 5: extends `AsbClient` with one-way operation support + `KeepAlive` + `Read` wrappers. New `send_envelope_one_way` / `send_signed_envelope_one_way` mirror WCF's `[OperationContract(IsOneWay = true)]` semantics — write the SizedEnvelope and return immediately. New `client::keep_alive` ports `MxAsbDataClient`'s channel inactivity-keepalive (`AsbContracts.cs:117` — empty wrapper element + ConnectionValidator header). New `client::read` + `decode_read_response` (in operations) decode `Status` (`Vec<ItemStatus>`) + `Values` (`Vec<RuntimeValue>`) from the dual-`<ASBIData>`-payload `ReadResponse` body shape. RuntimeValue array decode mirrors `AsbContracts.cs:771-780` (4-byte int32 count + per-element `WriteToStream`). 5 new tests: keep_alive body shape (empty wrapper), ReadResponse round-trip with Status + Values, ReadResponse-with-no-Values graceful handling, plus two end-to-end client tests via `tokio::io::duplex` peer (keep_alive one-way send drains the SizedEnvelope but produces no response, read round-trips Status + Values from a synthetic ReadResponse).
|
||||
|
||||
**Earlier slices:**
|
||||
- F25 step 4 (commit `1e59249`):
|
||||
- F25 step 4: `mxaccess-asb::client::AsbClient` — async network loop generic over `AsyncRead + AsyncWrite + Unpin + Send`. Wraps the F19-F25.3 stack into a single struct with: `send_preamble` (writes the canonical NMF preamble + waits for PreambleAck; errors on Fault), `send_envelope` (frames in `SizedEnvelope`, writes, reads response, decodes back to `DecodedEnvelope`), `send_signed_envelope` (calls F23 authenticator's `sign` on the unsigned body bytes, attaches a `ConnectionValidator` header, sends), `register_items` / `unregister_items` thin wrappers, `send_end` (writes record `0x07` + shutdowns the stream), and `authenticator_mut` accessor for the future Connect/AuthenticateMe flow. Generic transport means tests use `tokio::io::duplex` for in-memory verification — no live ASB endpoint needed. 6 new tests cover preamble round-trip, fault propagation through preamble, full RegisterItems request → response round-trip via in-memory peer, send-before-preamble guard, send-end record byte (`0x07`), and `PreambleMode` re-export shape. **Note**: the signing path currently hashes the NBFX-encoded body; .NET hashes the XML-text `request.ToXml()`. Functionally present but byte-non-identical to .NET's MAC for the same payload. Live-probe iteration needs to reconcile this — flagged as `TODO` in the doc comment.
|
||||
|
||||
**Earlier slices:**
|
||||
|
||||
Reference in New Issue
Block a user