Files
MARTe-Integrated-Components/ARCHITECTURE.md
T
Martino Ferrari 617b5bd712 Initial release
2026-05-29 13:29:59 +02:00

11 KiB
Raw Blame History

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.

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