Initial release

This commit is contained in:
Martino Ferrari
2026-05-29 13:29:59 +02:00
commit 617b5bd712
110 changed files with 29234 additions and 0 deletions
+240
View File
@@ -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 |