Index: docs/SDD/Seq_RealtimeDataTransfer.puml =================================================================== diff -u -r6d2ef8c97f4bb34204e95811839b3995000c47c1 -r8b98ff34313ee5d3f5d49672d622162acfc755c9 --- docs/SDD/Seq_RealtimeDataTransfer.puml (.../Seq_RealtimeDataTransfer.puml) (revision 6d2ef8c97f4bb34204e95811839b3995000c47c1) +++ docs/SDD/Seq_RealtimeDataTransfer.puml (.../Seq_RealtimeDataTransfer.puml) (revision 8b98ff34313ee5d3f5d49672d622162acfc755c9) @@ -2,52 +2,90 @@ title Real-time Data Transfer — End-to-End Scenario -participant "CANDumpPlayer" as CDP participant "CAN Bus\n(SocketCAN)" as CANBUS -participant "CloudConnect\n(CloudConnectController)" as LRC -participant "CloudConnectServer" as RS -participant "Luis application" as APP -participant "Cloud pipeline\n(planned)" as PIPE +participant "CloudConnect\n(CloudConnectController)" as CC +participant "AWS IoT Core\n(MQTT broker)" as AWS -note over CDP - Replays a candump log onto SocketCAN - at a configurable speed multiplier. - See Seq_CANDumpPlayer for detail. +== Startup (main.cpp, on the controller thread) == + +CC -> CC : listenForApp()\nbind /tmp/cloudconnect.sock +CC -> AWS : connectToCloud() → MqttClient::open()\nconnectToHostEncrypted(), mTLS, MQTT 3.1.1, port 8883 +AWS --> CC : CONNACK +CC -> CC : onCloudStateChanged(Connected)\nreset CAN/MQTT stats counters +CC -> AWS : SUBSCRIBE {prefix}/{deviceId}/clinical (QoS 1) +CC -> CC : startCan() → CanInterface::init() + +note over CC + Order is deliberate: the app socket and the broker + connection come up before CAN, so frames arriving + before CONNACK are dropped rather than queued. end note -CDP -> CANBUS : writeFrame(can_id, payload) -CANBUS -> LRC : didFrameReceive(QCanBusFrame) +== Steady state: CAN frame → cloud == -LRC -> LRC : MessageDispatcher reassembles\nmulti-frame Can::Message\n(see Seq_CANToCloud for detail) +CANBUS -> CC : didFrameReceive(QCanBusFrame) -alt action == Drop (or msgId not in _msgHandling) - LRC -> LRC : discard +CC -> CC : MessageDispatcher::onFrameReceive(canId, payload)\nreassembles multi-frame Can::Message\n→ didActionReceive(Can::Message) + +CC -> CC : record CAN stats\n(count, inter-arrival, running average) + +alt msgId not in _canRouting + CC -> CC : discard (no action defined) +else action == Drop + CC -> CC : discard else action == SendDelta AND payload unchanged\n(cached data.chopped(1) == new data.chopped(1)) - LRC -> LRC : discard (no change) + CC -> CC : discard (no change) else action == SendAlways OR SendDelta with new payload - LRC -> LRC : canMessageToProtobufByteArray()\n→ serialised protobuf bytes + CC -> CC : leahi::canMessageToProtobufByteArray()\n→ serialised protobuf bytes - LRC -> RS : send(topic, seq++, protobuf payload) - RS -> APP : CloudConnectFrame frame over\n/tmp/cloudconnect.sock + alt serialisation succeeded + CC -> AWS : PUBLISH (QoS 1, retain=false)\ntopic {prefix}/{deviceId}/clinical - LRC -->> PIPE : route → spool → publish + alt publish accepted by the client + CC -> CC : _inflight[pubId] = { Sent, msgId, sequence, sentUs } + AWS --> CC : PUBACK + CC -> CC : onCloudMessageStatusChanged(Acknowledged)\nmark ACK, record publish→ack latency + else publish rejected + CC -> CC : log warning, message lost\n(no spool today) + end + else serialisation failed + CC -> CC : log warning, message dropped + end + + CC -> CC : cachedMsg = msg (delta cache updated) end -note over PIPE - NOT YET IMPLEMENTED. +== Echo path (loopback via own subscription) == - The Connectivity Agent process and its UDS have been - removed. The replacement in-process pipeline is: +AWS -> CC : PUBLISH on the subscribed topic\n→ didMessageReceived(payload, topic) - Router → resolve MQTT topic from Type, - build Envelope - MessageSpool → durable insert BEFORE publish - MqttPublisher → QoS 1 PUBLISH to AWS IoT Core - → PUBACK → MessageSpool::remove() - SpoolDrain → replay spooled messages on reconnect +CC -> CC : parse Envelope → Header (msgId, sequence, timestamp) +CC -> CC : match _inflight by (msgId, sequence)\nrecord publish→receive latency +CC -> CC : msgIdToProtoName() → DescriptorPool lookup\n→ DynamicMessageFactory → MessageToJsonString()\n→ log decoded message - Until this lands, nothing is sent to the cloud. - See docs/SDD/AgentMigrationPlan.md Phases 2-3. +note over CANBUS, AWS + The device both publishes to and subscribes to the same + topic. The echo is a round-trip latency measurement for + bring-up, not a cloud→device command channel. end note +note over CC + No persistence today. A message that cannot be published + is logged and lost; the spool-on-failure design + (AgentMigrationPlan.md section 4.6) is still outstanding. + + Bring-up shortcuts currently in CloudConnectController: + * unknown routing action defaults to SendAlways, not Drop + * mqttTopic() ignores the per-message Topic and always + returns the "clinical" suffix + * the delta cache's "received" flag is never set, so the + SendDelta suppression branch never fires +end note + +note over CC + CloudConnectServer still binds the app Unix domain socket + at startup, but no message is written to it — the CAN path + now publishes straight to AWS IoT Core. +end note + @enduml