Initial release
This commit is contained in:
+240
@@ -0,0 +1,240 @@
|
||||
# Architecture
|
||||
|
||||
This document describes the internal architecture of the MARTe2 Integrated Components.
|
||||
|
||||
---
|
||||
|
||||
## 1. Repository Overview
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────────────────────────────┐
|
||||
│ MARTe2 Application │
|
||||
│ ┌──────────┐ ┌───────────────────────┐ ┌──────────────────┐ │
|
||||
│ │ GAM(s) │ │ DebugBrokerWrapper │ │ FastScheduler │ │
|
||||
│ │ │◄──│ (Registry-patched) │ │ (unmodified) │ │
|
||||
│ └──────────┘ └──────────┬────────────┘ └──────────────────┘ │
|
||||
│ │ RT-path API │
|
||||
└────────────────────────────┼───────────────────────────────────────┘
|
||||
│
|
||||
┌────────▼────────┐
|
||||
│ DebugServiceI │ ← Abstract singleton interface
|
||||
└────────┬────────┘
|
||||
┌────────▼────────┐
|
||||
│ DebugService │
|
||||
│ TCP/UDP │
|
||||
└────────┬────────┘
|
||||
┌───────────────┼──────────────┐
|
||||
│ │ │
|
||||
TCP 8080 UDP 8081 TCP 8082
|
||||
(commands) (telemetry) (TcpLogger)
|
||||
│ │
|
||||
┌────────▼───────────────▼──────┐
|
||||
│ Client/debugger │
|
||||
│ Go web client (browser) │
|
||||
└───────────────────────────────┘
|
||||
|
||||
┌────────────────────────────────────────────────────────────────────┐
|
||||
│ MARTe2 Application │
|
||||
│ ┌─────────────────────────────────────────────┐ │
|
||||
│ │ UDPStreamer DataSource │ │
|
||||
│ │ - Receives signals from GAMs via IOGAM │ │
|
||||
│ │ - Serialises to UDPS binary protocol │ │
|
||||
│ │ - Manages per-client sessions │ │
|
||||
│ └──────────────────────┬──────────────────────┘ │
|
||||
└─────────────────────────┼──────────────────────────────────────────┘
|
||||
UDP 44500
|
||||
│
|
||||
┌───────────▼──────────────┐
|
||||
│ Common/Client/go │
|
||||
│ udpsprotocol package │
|
||||
│ (decode CONFIG+DATA) │
|
||||
└───────────┬──────────────┘
|
||||
│ WebSocket
|
||||
┌───────────▼──────────────┐
|
||||
│ Browser oscilloscope │
|
||||
│ (Chart.js plots) │
|
||||
└──────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. UDPS Binary Protocol
|
||||
|
||||
Defined in `Common/UDP/UDPSProtocol.h` and shared between:
|
||||
|
||||
- `UDPStreamer` (C++ producer)
|
||||
- `DebugService` (C++ producer for trace packets)
|
||||
- `Common/Client/go/udpsprotocol` (Go decoder)
|
||||
|
||||
### Packet Header (17 bytes, little-endian, packed)
|
||||
|
||||
| Offset | Size | Type | Field | Description |
|
||||
|---|---|---|---|---|
|
||||
| 0 | 4 | uint32 | `magic` | Always `0x53504455` ('UDPS' LE) |
|
||||
| 4 | 1 | uint8 | `type` | Packet type (DATA=0, CONFIG=1, ACK=2, CONNECT=3, DISCONNECT=4) |
|
||||
| 5 | 4 | uint32 | `counter` | Per-update sequence number (same across all fragments) |
|
||||
| 9 | 2 | uint16 | `fragmentIdx` | 0-based fragment index |
|
||||
| 11 | 2 | uint16 | `totalFragments` | Total fragments for this update |
|
||||
| 13 | 4 | uint32 | `payloadBytes` | Bytes of payload following this header |
|
||||
|
||||
### CONFIG Payload
|
||||
|
||||
Sent when the signal set changes or a client connects:
|
||||
```
|
||||
[uint32 numSigs]
|
||||
numSigs × UDPSSignalDescriptor (136 bytes each, packed)
|
||||
[uint8 publishMode]
|
||||
```
|
||||
|
||||
### DATA Payload (Strict / Decimate modes)
|
||||
```
|
||||
[uint64 HRT timestamp]
|
||||
per-signal data in CONFIG order (quantised or raw, no inter-signal padding)
|
||||
```
|
||||
|
||||
### DATA Payload (Accumulate mode)
|
||||
```
|
||||
[uint64 HRT timestamp]
|
||||
[uint32 numSamples]
|
||||
for each signal: if scalar → numSamples elements; else → NumElements once
|
||||
```
|
||||
|
||||
### Quantization
|
||||
|
||||
When `QuantizedType` is set on a signal, the server maps `[RangeMin, RangeMax]` to the
|
||||
full integer range of the quantized type before transmission. The Go client reverses this
|
||||
using the `Scale` and `Offset` fields from `UDPSSignalDescriptor`.
|
||||
|
||||
---
|
||||
|
||||
## 3. DebugService Architecture
|
||||
|
||||
### 3.1 Registry Patching (Zero-Code-Change Instrumentation)
|
||||
|
||||
`DebugService::PatchRegistry()` replaces the `ObjectBuilder` for all `MemoryMap*Broker`
|
||||
types in the MARTe2 `ClassRegistryDatabase`. Any subsequent `ConfigureApplication()` call
|
||||
will instantiate `DebugBrokerWrapper<T>` objects instead of the originals. No application
|
||||
source changes are needed.
|
||||
|
||||
Wrapped types: `MemoryMapInputBroker`, `MemoryMapOutputBroker`,
|
||||
`MemoryMapSynchronisedInputBroker`, `MemoryMapSynchronisedOutputBroker`,
|
||||
`MemoryMapMultiBufferBroker`, `MemoryMapMultiBufferOutputBroker`,
|
||||
`MemoryMapAsynchronousInputBroker`, `MemoryMapAsynchronousOutputBroker`,
|
||||
`MemoryMapInterpolatedInputBroker`, `MemoryMapStatefulOutputBroker`,
|
||||
`MemoryMapStatefulInputBroker`.
|
||||
|
||||
### 3.2 Signal Registration
|
||||
|
||||
```
|
||||
ConfigureApplication()
|
||||
└─► DebugBrokerWrapper::Init()
|
||||
└─► DebugBrokerHelper::InitSignals()
|
||||
├─► DebugServiceI::RegisterSignal() (canonical + GAM alias)
|
||||
└─► DebugServiceI::RegisterBroker()
|
||||
```
|
||||
|
||||
Each signal is registered twice:
|
||||
1. **Canonical**: `<DataSourcePath>.<SignalName>` (e.g. `App.Data.DDB.Counter`)
|
||||
2. **GAM alias**: `<GAMPath>.In.<SignalName>` or `<GAMPath>.Out.<SignalName>`
|
||||
|
||||
Both map to the same `DebugSignalInfo*`. `AliasMatch()` in `DebugServiceBase.cpp`
|
||||
performs bidirectional suffix matching so short unqualified names work in commands.
|
||||
|
||||
### 3.3 RT Hot Path
|
||||
|
||||
```
|
||||
RealTimeThread::Execute()
|
||||
└─► DebugBrokerWrapper::Execute()
|
||||
├─► Base::Execute() (actual data movement)
|
||||
└─► DebugBrokerHelper::Process()
|
||||
├─► For each active signal:
|
||||
│ └─► DebugServiceI::ProcessSignal()
|
||||
│ ├─► if isForcing: memcpy forcedValue → signal memory
|
||||
│ ├─► if isTracing & decimation fires: push to TraceRingBuffer
|
||||
│ └─► if breakOp set: evaluate condition → SetPaused(true)
|
||||
└─► (output brokers only) ConsumeStepIfNeeded()
|
||||
```
|
||||
|
||||
### 3.4 TraceRingBuffer
|
||||
|
||||
Single-producer/single-consumer circular byte buffer (4 MB default).
|
||||
Entry format: `[ID:4][Timestamp:8][Size:4][Data:N]`.
|
||||
|
||||
- `Push()` serialised by `tracePushMutex` (multiple RT threads may write)
|
||||
- `Pop()` called exclusively by the Streamer thread
|
||||
- Corrupt entries detected by `size >= bufferSize`; discarded gracefully
|
||||
|
||||
### 3.5 DebugService Threads
|
||||
|
||||
| Thread | Role |
|
||||
|---|---|
|
||||
| `Server()` | Accepts one TCP client; reads text commands; writes JSON/text responses |
|
||||
| `Streamer()` | Drains `TraceRingBuffer`; assembles/sends UDP datagrams; polls monitored signals |
|
||||
|
||||
### 3.6 DebugServiceI Abstraction
|
||||
|
||||
`DebugServiceI.h` defines a pure-virtual singleton so transport implementations
|
||||
(`DebugService` TCP/UDP, `WebDebugService` HTTP/SSE) share the same broker injection layer.
|
||||
|
||||
```cpp
|
||||
DebugServiceI::SetInstance(this); // concrete implementation registers itself
|
||||
DebugServiceI *svc = DebugServiceI::GetInstance(); // broker wrapper retrieves it
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. UDPStreamer DataSource
|
||||
|
||||
`UDPStreamer` is a standard MARTe2 `DataSourceI` that:
|
||||
|
||||
1. Maintains a list of registered client sessions (UDP source address + port)
|
||||
2. On each `Synchronise()` call (once per RT cycle):
|
||||
- Serialises all configured signals into one or more UDPS DATA packets
|
||||
- Sends to all connected clients
|
||||
3. Handles `CONNECT` / `DISCONNECT` / `ACK` packets from clients
|
||||
4. Sends `CONFIG` packets when the signal list changes or a new client connects
|
||||
5. Applies quantization (`QuantizedType`, `RangeMin`, `RangeMax`) per signal
|
||||
|
||||
### Packed Signals (Accumulate mode)
|
||||
|
||||
When a signal has `NumberOfElements > 1` and `SamplingRate` is configured:
|
||||
- `TimeMode = FirstSample`: the `TimeSignal` value anchors the burst timestamp
|
||||
- The Go client interpolates per-sample timestamps as `t0 + e/SamplingRate * dt`
|
||||
- `UDPStreamer` packs all elements contiguously with no per-element header
|
||||
|
||||
---
|
||||
|
||||
## 5. Go Client Packages
|
||||
|
||||
### `Common/Client/go/udpsprotocol`
|
||||
|
||||
Pure Go decoder for UDPS packets:
|
||||
- `Decoder` — reassembles fragments; returns complete CONFIG or DATA payloads
|
||||
- `SignalDescriptor` — mirrors `UDPSSignalDescriptor` from C++
|
||||
- `DataPacket` — decoded signal values with per-sample timestamps
|
||||
|
||||
### `Common/Client/go/wshub`
|
||||
|
||||
WebSocket broadcast hub:
|
||||
- Multiple browser clients share one UDP → WebSocket relay
|
||||
- JSON-encodes signal samples and broadcasts to all connected browsers
|
||||
|
||||
### `Client/debugger`
|
||||
|
||||
Go HTTP server providing the debug web UI:
|
||||
- `main.go` — CLI entry point; starts TCP relay and HTTP server
|
||||
- `martecontrol.go` — TCP client to `DebugService`; routes commands from browser
|
||||
- `static/` — Single-page application (HTML/JS/CSS) with Chart.js plots
|
||||
|
||||
---
|
||||
|
||||
## 6. Key Design Decisions
|
||||
|
||||
| Decision | Rationale |
|
||||
|---|---|
|
||||
| Shared `UDPSProtocol.h` in `Common/UDP/` | Eliminates duplication between `UDPStreamer` and any future UDP producer; Go packages decode the same binary layout |
|
||||
| `DebugServiceI` abstract interface | Allows `DebugService` (TCP/UDP) and `WebDebugService` (HTTP/SSE) to share broker injection without coupling |
|
||||
| No STL in C++ components | MARTe2 coding convention; `Vec<T>` used instead of `std::vector` |
|
||||
| `FastPollingMutexSem` on hot path | Lowest-latency synchronisation primitive available in MARTe2; avoids OS scheduler involvement |
|
||||
| Separate `TCPLogger` component | Decoupled from `DebugService`; can be used with any MARTe2 application independently |
|
||||
| Go for clients | Minimal dependencies, single binary, easy cross-compilation; no Node/npm toolchain required |
|
||||
Reference in New Issue
Block a user