a2b8989cbf
Adds the IAsbCustomSerializableType binary fast-path + per-operation
request-body NBFX-token builders. RegisterItems and UnregisterItems
now compose end-to-end through SoapEnvelope + encode_envelope to a
byte stream that round-trips back to the original ItemIdentity array.
Three pieces:
1. F21 NBFX gains `Bytes8/16/32` text records (records 0x9E/0xA0/0xA2
plus +1 WithEndElement variants). WCF's `XmlDictionaryWriter.
WriteBase64` emits these in binary form — not actual base64 text —
so they're required for the `<ASBIData>` content.
2. `mxaccess-asb::contracts::ItemIdentity` ports `AsbContracts.cs:533-633`:
* Wire layout: u16 kind + u16 reference_type +
AsbBinary.WriteUnicodeString(Name) + AsbBinary.WriteUnicodeString
(ContextName) + u64 Id + u8 IdSpecified.
* `AsbBinary.WriteUnicodeString` per cs:1622-1633: u32 byte-length
+ UTF-16LE bytes; null/empty collapse to a 4-byte zero header.
* `encode_item_identity_array` / `decode_item_identity_array`
mirror `WriteArrayToStream` — 4-byte int32 count + each
element's `WriteToStream` output. Per `AsbDataCustomSerializer`
at cs:1583-1591.
* `absolute_by_name(...)` convenience constructor matching
`MxAsbDataClient.CreateAbsoluteItem` at cs:172-194.
3. `mxaccess-asb::operations` builds SOAP body NBFX token streams:
* `build_register_items_request_body(items, require_id, register_only)`
— RegisterItems contract per cs:119-143.
* `build_unregister_items_request_body(items)` — UnregisterItems
per cs:145-159.
* Internal `BodyField` helper assembles the wire shape:
`<RegisterItemsRequest xmlns="urn:msg.data.asb.iom:2">
<Items><ASBIData>{Bytes(payload)}</ASBIData></Items>
<RequireId>true|false</RequireId>
<RegisterOnly>true|false</RegisterOnly>
</RegisterItemsRequest>`
15 new tests cover:
* ItemIdentity round-trip (default, with id, unicode name).
* AsbBinary unicode-string null/empty/value semantics.
* Byte-layout pinning (21 bytes for default ItemIdentity, le-int32
array count).
* ItemIdentity array round-trip.
* `<ASBIData>` Bytes record round-trip across NBFX widths
(Bytes8/16/32 selected by length).
* RegisterItems body → SoapEnvelope → encode → decode → recover the
ItemIdentity array end-to-end.
* RequireId / RegisterOnly Bool wire form.
* UnregisterItems body uses correct outer element name and omits
the RegisterItems-only fields.
Stubbed for next F25 iteration: per-operation Read / Write /
PublishWriteComplete / CreateSubscription / AddMonitoredItems /
DeleteMonitoredItems / Publish builders, response decoders, and the
`AsbClient` network loop.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>