Index: SDDs/Class_Overview.png =================================================================== diff -u Binary files differ Index: SDDs/Class_Overview.puml =================================================================== diff -u --- SDDs/Class_Overview.puml (revision 0) +++ SDDs/Class_Overview.puml (revision e02b828378df3c67f94e711a947aa9cf75c94513) @@ -0,0 +1,181 @@ +@startuml Class_Overview + +title Leahi Real-time CDT — Class Overview + +skinparam classAttributeIconSize 0 +skinparam class { + BackgroundColor<> LightYellow + BackgroundColor<> LightCyan +} + +package "LeahiRt (process)" { + class LeahiRtController { + -_settings: QSettings + -_canInterface: Can::CanInterface + -_canThread: QThread + -_dispatcher: Can::MessageDispatcher + -_msgHandling: QHash + -_msgCache: QMap> + -_agentInterface: AgentInterface + -_agentThread: QThread + -_txSequence: quint16 + __ + +LeahiRtController(configPath, msgHandlingPath) + +connectToAgent() + -loadMsgHandling(msgHandlingPath) + __ + -onFrameReceive(QCanBusFrame) + -onMessageReceive(Can::Message) + -onAgentDisconnect() + } +} + +package "Comms (lib)" { + class "Can::CanInterface" as CanInterface { + -_canDevice: QSharedPointer + -_rxFrameCount: FrameCount + -_txFrameCount: FrameCount + -_erFrameCount: FrameCount + __ + +init(thread: QThread&): bool + +quitDevice() + +enableConsoleOut(bool) + +status(): QString + __ + +didFrameReceive(QCanBusFrame) <> + +didFrameTransmit(bool) <> + +didFrameError(QString) <> + +didFrameWritten(qint64) <> + __ + -onFrameReceive() <> + -onFrameTransmit(QCanBusFrame) <> + -onFrameError(CanBusError) <> + -onFrameWritten(qint64) <> + } + + class AgentInterface { + -_socket: QLocalSocket + -_reconnectTimer: QTimer + -_socketPath: QString + -_rxBuf: QByteArray + -_rxMsg: AgentMessage + __ + +init(socketPath, reconnectIntervalMs, thread): bool + +send(msgId, sequence, payload): bool + +quit() <> + __ + +didMessageReceive(MsgId, seq, payload) <> + +didConnect() <> + +didDisconnect() <> + __ + -onConnected() <> + -onDisconnected() <> + -onError(LocalSocketError) <> + -onReadyRead() <> + -onReconnectTimer() <> + } +} + +package "MsgUtils (lib)" { + class "Can::MessageDispatcher" as MessageDispatcher { + -_messageList: QHash + -_rxSequence: Sequence + -_builder: Can::MessageBuilder + __ + +didActionReceive(Can::Message) <> + __ + +onFrameReceive(CanId, payload) <> + -buildMessage(CanId, payload): bool + -rxCount(): Sequence + } + + class "Can::MessageBuilder" as MessageBuilder { + __ + +buildFrames(MsgId, data, frameList, sequence): bool + +buildMessage(payload, message, canId): bool + +enableConsoleOut(bool) + } + + class AgentMessage { + -_headerBuf: QByteArray + -_rxMsgId: MsgId + -_rxSequence: quint16 + -_rxPayloadLen: quint32 + -_rxPayload: QByteArray + __ + +{static} build(msgId, sequence, payload): QByteArray + +feed(bytes: QByteArray&): FeedResult + +msgId(): MsgId + +sequence(): quint16 + +payload(): QByteArray + +reset() + } + + enum "AgentMessage::MsgId" as AgentMsgId { + ClinicalData = 0x0001 + Diagnostic = 0x0002 + Ack = 0x0003 + Alarms = 0x0004 + Audit = 0x0005 + DeviceLogFile = 0x0006 + TreatmentLogFile = 0x0007 + CloudSyncLogFile = 0x0008 + } + + enum "AgentMessage::FeedResult" as FeedResult { + Incomplete + Complete + HeaderError + PayloadError + } +} + +package "AgentSim (process)" { + class AgentSimController <> { + -_settings: QSettings + -_server: QLocalServer + -_client: QLocalSocket* + -_rxBuf: QByteArray + -_rxMsg: AgentMessage + __ + +listen(): bool + -handleMessage(AgentMessage) + -logMessage(MsgId, seq, payload) + __ + -onNewConnection() <> + -onDisconnected() <> + -onReadyRead() <> + } +} + +package "CANDumpPlayer (process)" { + class "CANDumpPlayer\n(main)" <> { + +can_interface: string + +candump_file: string + +--speed: double + +--test: bool + __ + Parses candump log and replays\nframes onto SocketCAN at a\nconfigurable speed multiplier + } +} + +' --- composition --- +LeahiRtController *-- CanInterface +LeahiRtController *-- AgentInterface +LeahiRtController *-- MessageDispatcher + +' --- internal composition --- +MessageDispatcher *-- MessageBuilder + +' --- usage --- +AgentInterface ..> AgentMessage : uses +AgentSimController ..> AgentMessage : uses + +' --- enum nesting --- +AgentMessage +-- AgentMsgId +AgentMessage +-- FeedResult + +' --- layout hints --- +CanInterface -[hidden]- AgentInterface + +@enduml Index: SDDs/CodeGenPipeline.png =================================================================== diff -u Binary files differ Index: SDDs/CodeGenPipeline.puml =================================================================== diff -u --- SDDs/CodeGenPipeline.puml (revision 0) +++ SDDs/CodeGenPipeline.puml (revision e02b828378df3c67f94e711a947aa9cf75c94513) @@ -0,0 +1,79 @@ +@startuml CodeGenPipeline + +title Build-time Code Generation Pipeline + +skinparam activityShape octagon +skinparam defaultTextAlignment center + +|Input| +start +:LeahiUnhandled.conf\n(CAN message definitions); + +fork + + ' ── Pipeline A: Protobuf schema ────────────────────────────── + |cmake| + :generate_protobuf()\ninvokes GenerateProtobuf.py; + + |MsgUtils Python| + :MsgData loads .conf —\nparses message names, IDs,\nfield names and types; + + |Jinja2 Templates| + :MsgDefs_proto.jinja\nrenders LeahiMsgDefs.proto; + + |protoc| + :protoc compiles .proto\ninto C++ stubs\n(LeahiMsgDefs.pb.h / .pb.cc); + + |CMake build| + :pb stubs compiled\ninto MsgUtils library; + +fork again + + ' ── Pipeline B: C++ message structs ───────────────────────── + |cmake| + :generate_msg_defs_cpp()\ninvokes GenerateMsgDefsCpp.py; + + |MsgUtils Python| + :MsgData loads .conf —\nparses message names, IDs,\nfield names and types; + + |Jinja2 Templates| + :MsgDefs_h.jinja renders\nLeahiMsgDefs.h\n(typed structs + toProtobuf /\nfromQByteArray + msgIdString()); + :MsgDefs_cpp.jinja renders\nLeahiMsgDefs.cpp\n(method implementations +\ncanMessageToProtobufByteArray() +\nmsgIdString() switch body); + :MsgProtoUtils_h.jinja renders\nLeahiMsgProtoUtils.h; + :MsgProtoUtils_cpp.jinja renders\nLeahiMsgProtoUtils.cpp; + + |CMake build| + :MsgDefs + MsgProtoUtils compiled\ninto MsgUtils library; + +fork again + + ' ── Pipeline C: Message handling INI ──────────────────────── + |cmake| + :generate_msg_handling_ini()\ninvokes GenerateMsgHandlingIni.py; + + |MsgUtils Python| + :MsgHandlingIni loads .conf —\nseeds each msgId with\naction=drop, topic=; + :loadIni() merges existing\nLeahiRtMsgHandling.ini —\npreserves hand-edited\naction + topic values; + + |Jinja2 Templates| + :MsgHandlingIni.jinja renders\nLeahiRtMsgHandling.ini\n(per-msgId: msg_id, action, topic); + +end fork + +|Runtime| +:LeahiRtController::loadMsgHandling()\nreads LeahiRtMsgHandling.ini at startup; +:onMessageReceive() looks up msgId\nin _msgHandling → drop / send_always /\nsend_delta policy, MQTT topic; +:canMessageToProtobufByteArray()\nconverts CAN → protobuf bytes; +:msgIdString() used in log output; +stop + +note right + Pipelines A and B are triggered by + the MsgUtils library cmake target; + Pipeline C is triggered by the + LeahiRt executable cmake target. + All three regenerate whenever + the .conf file changes. +end note + +@enduml Index: SDDs/Comms_Overview.png =================================================================== diff -u Binary files differ Index: SDDs/Comms_Overview.puml =================================================================== diff -u --- SDDs/Comms_Overview.puml (revision 0) +++ SDDs/Comms_Overview.puml (revision e02b828378df3c67f94e711a947aa9cf75c94513) @@ -0,0 +1,35 @@ +@startuml Comms_Overview + +title Leahi Real-time CDT — Signal/Slot Wiring Overview + +participant "Can::CanInterface" as CANI +participant "LeahiRtController" as LRC +participant "Can::MessageDispatcher" as MDISP +participant "AgentInterface" as AI +participant "Unix Domain\nSocket" as UDS +participant "AgentSimController" as ASC + +== CAN frame → assembled message == + +CANI -> LRC : didFrameReceive(QCanBusFrame)\n→ onFrameReceive() +LRC -> MDISP : onFrameReceive(canId, payload) +MDISP -> LRC : didActionReceive(Can::Message)\n→ onMessageReceive() + +== Message handling & forwarding == + +LRC -> LRC : look up policy in _msgHandling\n(drop / send_always / send_delta) +LRC -> AI : send(topic, sequence, protobuf payload) +AI -> UDS : write(AgentMessage frame) + +== AgentSim receives == + +UDS -> ASC : readyRead → onReadyRead() +ASC -> ASC : feed() → didMessageReceive() + +== Connection events == + +AI -> LRC : didConnect()\n→ (future) +AI -> LRC : didDisconnect()\n→ onAgentDisconnect()\n resets _msgCache received flags +AI -> LRC : didMessageReceive()\n→ (future inbound) + +@enduml Fisheye: Tag e02b828378df3c67f94e711a947aa9cf75c94513 refers to a dead (removed) revision in file `SDDs/RealtimeDataTransfer.puml'. Fisheye: No comparison available. Pass `N' to diff? Index: SDDs/Seq_RealtimeDataTransfer.png =================================================================== diff -u Binary files differ Index: SDDs/Seq_RealtimeDataTransfer.puml =================================================================== diff -u --- SDDs/Seq_RealtimeDataTransfer.puml (revision 0) +++ SDDs/Seq_RealtimeDataTransfer.puml (revision e02b828378df3c67f94e711a947aa9cf75c94513) @@ -0,0 +1,33 @@ +@startuml Seq_RealtimeDataTransfer + +title Real-time Data Transfer — End-to-End Scenario + +participant "CANDumpPlayer" as CDP +participant "CAN Bus\n(SocketCAN)" as CANBUS +participant "LeahiRt\n(LeahiRtController)" as LRC +participant "Unix Domain\nSocket" as UDS +participant "AgentSim\n(AgentSimController)" as ASC + +note over CDP + Replays a candump log onto SocketCAN + at a configurable speed multiplier. + See Seq_CANDumpPlayer for detail. +end note + +CDP -> CANBUS : writeFrame(can_id, payload) +CANBUS -> LRC : didFrameReceive(QCanBusFrame) + +LRC -> LRC : MessageDispatcher reassembles\nmulti-frame Can::Message\n(see Seq_CANToCloud for detail) + +alt action == drop (or msgId not in _msgHandling) + LRC -> LRC : discard +else action == send_delta AND payload unchanged\n(cached data.chopped(1) == new data.chopped(1)) + LRC -> LRC : discard (no change) +else action == send_always OR send_delta with new payload + LRC -> LRC : canMessageToProtobufByteArray()\n→ serialised protobuf bytes + LRC -> UDS : AgentMessage frame\n(msgId=topic, seq++, protobuf payload) + UDS -> ASC : readyRead + ASC -> ASC : AgentMessage::feed() → Complete\nlogMessage() + Envelope parse\n(see Seq_AgentSim for detail) +end + +@enduml Index: SDDs/SoftwareArchitecture.png =================================================================== diff -u Binary files differ Index: SDDs/SoftwareArchitecture.puml =================================================================== diff -u --- SDDs/SoftwareArchitecture.puml (revision 0) +++ SDDs/SoftwareArchitecture.puml (revision e02b828378df3c67f94e711a947aa9cf75c94513) @@ -0,0 +1,82 @@ +@startuml SoftwareArchitecture + +skinparam componentStyle rectangle +skinparam defaultTextAlignment center +skinparam component { + BackgroundColor<> LightYellow + BackgroundColor<> LightGray +} + +title Leahi Real-time CDT — Component Architecture + +node "Leahi Device" { + + node "LeahiRt (process)" { + component "LeahiRtController" as LRC + component "Can::CanInterface" as CAN + component "AgentInterface" as AI + } + + node "MsgUtils (lib)" { + component "Can::MessageDispatcher\n(per-CAN-id reassembly)" as MDISP + component "Can::MessageBuilder\n(frame ↔ message)" as MB + component "AgentMessage\n(framing + CRC)" as AM + component "LeahiMsgDefs\n(generated C++)" as MD <> + } + + node "Comms (lib)" { + component "AgentInterface\nimpl" as AI_IMPL + } + + interface "CAN Bus\n(SocketCAN)" as CANBUS + interface "Unix Domain Socket\n(/tmp/leahi_rt.sock)" as UDS +} + +node "CANDumpPlayer (tool)" { + component "CAN dump replay\n(writeFrame → SocketCAN)" as CDP +} + +node "AgentSim (tool / future Agent)" { + component "AgentSimController" as ASC + component "AgentMessage\n(framing + CRC)" as AM2 +} + +cloud "Cloud Service\n(out of scope)" as CLOUD <> + +node "Build-time (scripts)" { + component "MsgUtils Python\n(msgutils)" as PYUTILS <> + component "Jinja2 Templates\n(MsgDefs_*.jinja)" as JINJA <> + component "protoc" as PROTOC <> + component "LeahiMsgDefs.proto\n(generated)" as PROTO <> + component "LeahiMsgDefs.h/.cpp\n(generated)" as GENCPP <> + component "Protobuf stubs\n(.pb.h/.pb.cc)" as PBSTUBS <> +} + +CDP --> CANBUS : writeFrame (replay) +CANBUS --> CAN +CAN --> LRC : didFrameReceive +LRC --> MDISP : onFrameReceive(canId, payload) +MDISP --> MB : buildMessage() +MB --> MDISP : Can::Message +MDISP --> LRC : didActionReceive(Can::Message) +LRC --> MD : canMessageToProtobufByteArray() +MD --> AM : protobuf bytes +LRC --> AI : send(MsgId, seq, payload) +AI --> AI_IMPL +AI_IMPL --> AM : build() +AI_IMPL --> UDS : write frame + +UDS --> ASC : readyRead +ASC --> AM2 : feed() +ASC --> CLOUD : (future) forward payload + +PYUTILS --> JINJA : renders +JINJA --> PROTO : generates +JINJA --> GENCPP : generates +PROTO --> PROTOC : input +PROTOC --> PBSTUBS : generates + +GENCPP ..> MD : compiled into +PBSTUBS ..> MD : compiled into + +@enduml