@startuml Seq_RealtimeDataTransfer title Real-time Data Transfer — End-to-End Scenario participant "CAN Bus\n(SocketCAN)" as CANBUS participant "CloudConnect\n(CloudConnectController)" as CC participant "AWS IoT Core\n(MQTT broker)" as AWS == 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 == Steady state: CAN frame → cloud == CANBUS -> CC : didFrameReceive(QCanBusFrame) 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)) CC -> CC : discard (no change) else action == SendAlways OR SendDelta with new payload CC -> CC : leahi::canMessageToProtobufByteArray()\n→ serialised protobuf bytes alt serialisation succeeded CC -> AWS : PUBLISH (QoS 1, retain=false)\ntopic {prefix}/{deviceId}/clinical 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 == Echo path (loopback via own subscription) == AWS -> CC : PUBLISH on the subscribed topic\n→ didMessageReceived(payload, topic) 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 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