@startuml Class_Overview title Leahi Real-time CDT — Class Overview skinparam classAttributeIconSize 0 skinparam class { BackgroundColor<> LightYellow BackgroundColor<> LightCyan BackgroundColor<> #E8F0E8 } package "CloudConnect (process)" { class CloudConnectController { -_canInterface: Can::CanInterface -_dispatcher: Can::MessageDispatcher -_canCache: QMap> -_canRouting: QHash -_appServerSocketPath: QString -_appServer: QSharedPointer -_mqttClient: MqttClient -_topicPrefix: QString -_deviceId: QString __ +CloudConnectController(parent) +loadConfig(configPath): bool +initThread(QThread&) +startCan(): bool +listenForApp(): bool +connectToCloud(): bool -loadCanRouting(canRoutingPath): bool -mqttTopic(Topic): QString __ -onCanFrameReceive(QCanBusFrame) <> -onCanMessageReceive(Can::Message) <> -onCloudStateChanged(ClientState) <> -onCloudMessageStatusChanged(id, status, props) <> -onCloudMessageReceived(QByteArray, QMqttTopicName) <> } enum "CloudConnectController::CanAction" as CanAction { Drop SendAlways SendDelta } class "CloudConnectController::CanRouting" as CanRouting { +action: CanAction = Drop +topic: CloudConnectFrame::Topic = NormalPriority } } package "Comms (lib)" { class "Can::CanInterface" as CanInterface { -_canDevice: QSharedPointer -_rxFrameCount: FrameCount -_txFrameCount: FrameCount -_erFrameCount: FrameCount __ +init(): bool +quitDevice() +enableConsoleOut(bool) +status(): QString __ +didFrameReceive(QCanBusFrame) <> +didFrameTransmit(bool) <> +didFrameError(QString) <> +didFrameWritten(qint64) <> __ -onFrameReceive() <> -onFrameTransmit(QCanBusFrame) <> -onFrameError(CanBusError) <> -onFrameWritten(qint64) <> } class MqttClient { -_client: QMqttClient* -_config: Config -_sslConfig: QSslConfiguration __ +init(Config): bool +open(): bool +close() +publish(topic, payload, msgId&, qos = 1): bool +subscribe(topicFilter, qos = 1): bool __ +didStateChanged(ClientState) <> +didMessageStatusChanged(id, status, props) <> +didMessageReceived(QByteArray, QMqttTopicName) <> __ -onStateChanged() <> } class "MqttClient::Config" as MqttConfig { +endpoint: QString +port: quint16 = 8883 +certPath: QString +keyPath: QString +caPath: QString +clientId: QString +keepAliveSecs: quint16 = 30 +cleanSession: bool = true } class CloudConnectClient { -_socket: QLocalSocket -_reconnectTimer: QTimer -_socketPath: QString -_rxBuf: QByteArray -_rxMsg: CloudConnectFrame __ +init(socketPath, reconnectIntervalMs, thread): bool +send(topic, sequence, payload): bool +quit() <> __ +didMessageReceive(Topic, seq, payload) <> +didConnect() <> +didDisconnect() <> __ -onConnected() <> -onDisconnected() <> -onError(LocalSocketError) <> -onReadyRead() <> -onReconnectTimer() <> } class CloudConnectServer { -_server: QLocalServer -_client: QLocalSocket* -_rxBuf: QByteArray -_rxMsg: CloudConnectFrame __ +listen(socketPath): bool +send(topic, sequence, payload): bool +isConnected(): bool __ +didMessageReceive(Topic, seq, payload) <> +didConnect() <> +didDisconnect() <> __ -onNewConnection() <> -onDisconnected() <> -onReadyRead() <> } 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 CloudConnectFrame { -_headerBuf: QByteArray -_rxTopic: Topic -_rxSequence: quint16 -_rxPayloadLen: quint32 -_rxPayload: QByteArray __ +{static} build(topic, sequence, payload): QByteArray +read(bytes: QByteArray&): ReadState +topic(): Topic +sequence(): quint16 +payload(): QByteArray +reset() } enum "CloudConnectFrame::Topic" as Topic { HighPriority = 0x0001 NormalPriority = 0x0002 DeviceLogFile = 0x0003 TreatmentLogFile = 0x0004 CloudSyncLogFile = 0x0005 } enum "CloudConnectFrame::ReadState" as ReadState { Incomplete Complete HeaderError PayloadError } } package "MsgUtils (lib)" { class "leahi (generated)" as MsgDefs <> { +{static} msgIdString(MsgId): QString +{static} msgIdToProtoName(msgId): std::string +{static} canMessageToProtobufByteArray(when, msg, out): bool __ Generated from LeahiUnhandled.conf — see CodeGenPipeline } class "leahi::messages (protobuf)" as Proto <> { Envelope Header <474 message types> } } package "Tools (processes)" { class "CANDumpPlayer\n(main)" <> { +can_interface: string +candump_file: string +--speed: double +--test: bool __ Replays a candump log onto\nSocketCAN at a configurable\nspeed multiplier } class "DCSsim\n(DCSSimController)" <> { -_server: SslTcpServer __ Local MQTT broker simulator.\nTLS/mTLS via SslTcpServer,\nhand-rolled MqttPacket codec.\nStands in for AWS IoT Core. } } package "luis (process)" { class "luis application" as LUIS <> { -_ccClient: CloudConnectClient } } ' --- composition --- CloudConnectController *-- CanInterface CloudConnectController *-- MessageDispatcher CloudConnectController *-- MqttClient CloudConnectController o-- CloudConnectServer ' --- nested types --- CloudConnectController +-- CanAction CloudConnectController +-- CanRouting MqttClient +-- MqttConfig CloudConnectFrame +-- Topic CloudConnectFrame +-- ReadState ' --- internal composition --- MessageDispatcher *-- MessageBuilder ' --- external client --- LUIS *-- CloudConnectClient CloudConnectClient --> CloudConnectServer : connects via LocalSocket\n(CloudConnectFrame stream) ' --- usage --- CloudConnectController ..> MsgDefs : serialize / decode MsgDefs ..> Proto : builds CanRouting ..> Topic : uses CloudConnectServer ..> CloudConnectFrame : uses ' --- layout hints --- CanInterface -[hidden]- CloudConnectClient @enduml