feat(mqtt): write-through (IWritable) — Sparkplug NCMD/DCMD + plain publish (design P3)
#508
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
MqttDriverships read/subscribe/discover only, so every MQTT node materializes read-only. This wasdeferred by design (see the plan's Deferred / out of scope section and design §3.7 / §10 P3), not missed —
this issue tracks it.
Shape, from the design:
read
.../stateand write.../set).exists:
SparkplugCodecencodes today for the rebirth NCMD path, so the write leg reuses it rather thanadding a second encoder.
Galaxy's fire-and-forget: assume Good, and let the broker's echoed value correct the node. Note this
interacts with write-outcome self-correction (#5): Galaxy can never surface a write failure for the same
reason, and MQTT will inherit that property.
WriteIdempotentdefault-off — a Sparkplug command is not necessarily idempotent, so the resiliencepipeline must not retry it by default.
Do not skip:
MqttDrivercurrently does not implementIWritableat all, which is what makes the nodesread-only. Adding the interface without the topic config would make nodes appear writable and silently
no-op — worse than the current honest read-only.
Acceptance. Write via both NodeIds (raw + UNS — v3 routes either to the same driver ref under the same
realm-qualified
WriteOperategate), verified live against the Mosquitto fixture for Plain and against theC# edge-node simulator for NCMD/DCMD.
Implemented in master
2a7d1167, live gate passed.writeTopicRaw/Scalar) or a bare JSON literal (Json)DCMDfor a device metric,NCMDfor an edge-node metricDatatype, noseqBoth QoS 0,
retain:false— retain deliberately not configurable, for the reasonRebirthRequesteralready documents: a retained command is replayed on every future subscribe, including the device's own
reconnect.
The "do not skip" warning drove the design.
writeTopicis a separate key and its absence ismeaningful:
BadNotWritable, and the node discoversViewOnly. Browse-time writability is computedby the same encoder the write path uses, so a node can never advertise writability it would then
refuse. Reusing the read topic was rejected outright — it would echo back and read as a confirmed device
value.
Optimistic-Good as specified, and it does inherit Galaxy's property: a failed device write can never
be surfaced. A failed publish is reported Bad.
WriteIdempotentstays default-off.Live gate — 21/21 against the real Mosquitto fixture, 6 of them new, each observing the published
message with an independent broker subscriber (a fake asserting "publish was called" could not
distinguish working from publishing to the wrong topic). Acceptance asked for both NodeIds; routing via
raw/UNS is
DriverHostActor's shared write path, unchanged by this driver-level change.The gate caught something worth knowing: the NCMD test failed first time because NCMD is a shared
channel — the driver publishes its own
Node Control/Rebirthon the exact topic an edge-node metricwrite uses, and it arrived first. Correct driver behaviour, wrong test. Now matched on metric name, and
documented in
docs/drivers/Mqtt.md, because anything consuming that topic needs to know. DCMD has nocontention.
Not covered: a
Jsonwrite emits a bare JSON literal —jsonPathis a read-side extraction from adifferent topic, and reconstructing a wrapped document from it would be guesswork.