3 Commits
31 changed files with 3382 additions and 2196 deletions
+198 -270
View File
@@ -1,313 +1,241 @@
# AGENTS.md
# Repository Guidelines
Guide for agents working in the MARTe2 Integrated Components repository. Read
`ARCHITECTURE.md` and `CLAUDE.md` for deeper detail; this file focuses on
non-obvious knowledge, commands, and conventions that are not self-evident from
a single file read.
Guide for AI assistants working in the MARTe2 Integrated Components repository.
Focuses on non-obvious facts: commands, conventions, cross-module contracts, and
gotchas that are not self-evident from a single file read.
---
## Project Overview
## Repository at a glance
MARTe2 component library with **two independent real-time data paths** sharing
one binary wire protocol (`Common/UDP/UDPSProtocol.h`):
MARTe2 component library with two independent real-time data paths:
1. **Streaming path**`UDPStreamer` DataSource (serialises signals to UDPS
binary packets on UDP 44500) → `StreamHub` (headless C++ hub: ring buffers,
LTTB decimation, trigger FSM, history writer) → WebSocket 8090 → clients
(browser SPA, native ImGui, native Qt).
2. **Debug path**`DebugService` Interface patches `ClassRegistryDatabase` at
1. **Streaming path**`UDPStreamer` DataSource serialises DDB signals into UDPS
binary packets on UDP → `StreamHub` (headless C++ hub: ring buffers, LTTB
decimation, trigger FSM, history writer, binary recorder) → WebSocket 8090 →
clients (browser SPA, native ImGui, native Qt).
2. **Debug path**`DebugService` patches `ClassRegistryDatabase` at
`Initialise()` so `ConfigureApplication()` wraps all `MemoryMap*Broker` types
with `DebugBrokerWrapper<T>`. Zero application code changes. Exposes TCP 8080
(text commands), UDP 8081 (trace telemetry), TCP 8082 (`TcpLogger`).
with `DebugBrokerWrapper<T>`**zero application code changes**. Exposes
TCP 8080 (text commands), UDP 8081 (UDPS trace telemetry), TCP 8082
(`TcpLogger` log forward).
Key shared artefact: `Common/UDP/UDPSProtocol.h` — the UDPS binary wire format
(17-byte packed header, 136-byte signal descriptors, little-endian). It is
deliberately MARTe2-free and is mirrored across **four** codebases that must stay
in sync: C++ producers (`UDPStreamer`, `DebugService`), C++ consumer
(`Source/Components/Interfaces/UDPStream/UDPSClient`), Go decoder
(`Common/Client/go/udpsprotocol`), and the JS client parsers. Any protocol
change must be mirrored in all of them.
## Architecture & Data Flow
StreamHub WebSocket protocol: JSON text frames for commands/events, binary
frames for data pushes (spec in `ARCHITECTURE.md` §6). The Go hub
(`Client/udpstreamer`) and the C++ StreamHub implement the identical protocol;
browser JS, ImGui, and Qt clients must stay compatible with both.
```
[SineArrayGAM/TimeArrayGAM] → DDB → UDPStreamer (UDPS over UDP)
├─→ UDPStreamerClient (input DS back into a MARTe2 RT app, round-trip)
└─→ StreamHub: UDPSourceSession (receive thread → SignalRingBuffer)
→ push loop @30Hz: LTTB decimate temporal sigs → WS binary frames → clients
DebugService: patches broker builders at Initialise(); TCP 8080 commands,
UDP 8081 telemetry, TcpLogger 8082 (REPORT_ERROR → "LOG <LEVEL> <desc>" lines)
```
---
- **Wire protocol**: `Common/UDP/UDPSProtocol.h` is the canonical spec (17-byte
packed header, magic `0x53504455` 'UDPS', 136-byte signal descriptors,
CONFIG/DATA/ACK/CONNECT/DISCONNECT packet types, quant/time/publish modes).
Deliberately MARTe2-free so Go clients reuse it. **Mirrored across four
codebases that must stay in sync**: C++ producers (UDPStreamer, DebugService),
C++ consumer (`Source/Components/Interfaces/UDPStream/UDPSClient`), Go decoder
(`Common/Client/go/udpsprotocol/protocol.go`), and JS parsers
(`Client/udpstreamer/static/`, `Client/debugger/static/`). Any protocol change
must be mirrored in all of them.
- **WS protocol** has two implementations — Go hub (`Common/Client/go/wshub`) and
C++ StreamHub — that must behave identically; every client (SPA, ImGui, Qt)
must satisfy both. JSON text frames for commands/events (`addSource`,
`removeSource`, `setTrigger`, `arm`, `zoom`, `historyZoom`, `recStart`…), binary
frames for data pushes (live v1 + trigger capture v2).
- **Threading model**: RT threads only spinlock+memcpy (`FastPollingMutexSem`);
all socket I/O, fragmentation, and reassembly lives on background
`SingleThreadService` threads. StreamHub: per-session UDPSClient receive
threads + WS accept/read threads + one push loop.
- **DebugService patching**: `PatchRegistry()` replaces the ObjectBuilder for 11
`MemoryMap*Broker` classes; runs only when `ControlPort > 0`; static guard
against double-patching; wrappers persist for process lifetime.
## Environment setup (do this first, always)
## Key Directories
`source env.sh` is **required** before building *or* running anything. It sets:
| Path | Purpose |
|---|---|
| `Source/Components/DataSources/UDPStreamer/` | Output DataSource; UDP I/O on bg thread, RT thread only spinlock+memcpy in `Synchronise()` |
| `Source/Components/DataSources/UDPStreamerClient/` | Input DataSource (shared `UDPSClient`), double-buffered ready/scratch |
| `Source/Components/GAMs/` | `SineArrayGAM` (float32 sine, continuous phase), `TimeArrayGAM` (us-timer → per-sample timestamp array) |
| `Source/Components/Interfaces/DebugService/` | Registry patching, `DebugBrokerWrapper.h`, TCP/UDP services |
| `Source/Components/Interfaces/TCPLogger/` | `LoggerConsumerI` forwarding `REPORT_ERROR` to ≤8 TCP clients |
| `Source/Components/Interfaces/UDPStream/` | Plain-C++ helpers (not MARTe2 Objects): `UDPSClient` (auto-reconnect + fragment reassembly), `UDPSServer` (not thread-safe — owner's Execute thread only) |
| `Source/Applications/StreamHub/` | Standalone app (links MARTe2 core): `StreamHub`, `UDPSourceSession`, `WSServer`, `TriggerEngine`, `HistoryWriter`, `BinaryRecorder`, `LTTB`, `SignalRingBuffer` |
| `Common/UDP/` | Canonical wire protocol (header-only, MARTe2-free) |
| `Common/Client/go/` | Go mirror: `udpsprotocol` (decoder), `wshub` (WS hub client) |
| `Client/udpstreamer/` | Go legacy direct-UDP oscilloscope web UI (connects straight to UDPStreamer, no StreamHub) |
| `Client/webui/` | Go thin static server; SPA talks WS directly to C++ StreamHub (discovers via `GET /hub`) |
| `Client/debugger/` | Go debug web UI for DebugService |
| `Client/streamhub/` | Native ImGui+SDL2+OpenGL oscilloscope (C++17, no MARTe2) |
| `Client/streamhub-qt/` | Native Qt Widgets oscilloscope (Qt6 preferred, Qt5 fallback) |
| `Test/` | GTest, legacy Integration tests, Configurations (.cfg), E2E suite |
| `Docs/` | Per-component reference: `Protocol.md`, `UDPStreamer.md`, `StreamHub-{API,UserGuide,Developer}.md`, `DebugService.md`, `WebUI.md`, `Tutorial.md`, `E2E-Suite.md` |
- `MARTe2_DIR` (default `~/workspace/MARTe2`) and `MARTe2_Components_DIR`
(default `~/workspace/MARTe2-components`) — external dependencies, not in
this repo. Edit `env.sh` if your MARTe2 install lives elsewhere.
- `TARGET=x86-linux`
- `LD_LIBRARY_PATH` — prepends MARTe2 Core, MARTe2-components (LinuxTimer,
FileDataSource), and this repo's built `.so`s (UDPStreamer, SineArrayGAM,
TimeArrayGAM, DebugService, TCPLogger).
## Development Commands
Without sourcing `env.sh`, builds fail (can't find `MakeDefaults`) and binaries
fail to load shared libs. The E2E scripts source it themselves, but a bare
`make` or `./MainGTest.ex` from a fresh shell will not.
---
## Build commands
All C++ builds use the MARTe2 `Makefile.gcc` wrapper system. There is one
top-level `Makefile.gcc` and one per component directory. `TARGET` defaults to
`x86-linux`; pass `TARGET=x86-linux` explicitly when invoking `make -C` from
elsewhere.
`source env.sh` is **mandatory** before any MARTe2 build or run (sets
`MARTe2_DIR`, `MARTe2_Components_DIR`, `TARGET=x86-linux`, `LD_LIBRARY_PATH`).
The E2E scripts source it themselves; a bare `make` from a fresh shell will not
work. `run_streamhub.sh` hard-errors if `MARTe2_DIR` is unset.
```bash
source env.sh
make -f Makefile.gcc core # all C++ MARTe2 components (UDPStreamer, GAMs, DebugService, TCPLogger, UDPStream)
make -f Makefile.gcc apps # StreamHub standalone app
make -f Makefile.gcc test # build GTest + Integration test binaries
make -f Makefile.gcc core # 7 components (UDPStream interface FIRST, then UDPStreamer, UDPStreamerClient, GAMs, TCPLogger, DebugService)
make -f Makefile.gcc apps # StreamHub standalone app → Build/x86-linux/StreamHub/StreamHub.ex
make -f Makefile.gcc test # GTest + Integration test binaries + component test libs
make -f Makefile.gcc all # core + apps + test
make -f Makefile.gcc clean
make -f Makefile.gcc # = all: core apps test
# Build a single component (each component dir has its own Makefile.gcc):
make -C Source/Components/DataSources/UDPStreamer -f Makefile.gcc
make -C Source/Applications/StreamHub -f Makefile.gcc
# Single component:
make -C Source/Components/GAMs/SineArrayGAM -f Makefile.gcc
```
Build output goes to `Build/x86-linux/` — shared libs under
`Build/x86-linux/Components/<...>/`, the StreamHub executable at
`Build/x86-linux/StreamHub/StreamHub.ex`, test binaries under
`Build/x86-linux/GTest/` and `Build/x86-linux/Test/Integration/`.
Build output `Build/x86-linux/` mirroring `PACKAGE` paths (both `libX.so` and
`X.so` are produced). `compile_commands.json` (repo root, gitignored) feeds
LSP/clangd; CMake clients export their own into `Client/*/build/`.
`compile_commands.json` is generated at the repo root (gitignored) for LSP /
clangd. The CMake-based clients (ImGui, Qt) also export it into their `build/`.
### Non-MARTe2 clients (separate build systems, no env.sh needed)
### Non-MARTe2 clients (no env.sh needed)
```bash
# Go clients (UDPS web client, debug client, E2E chain-client)
cd Common/Client/go && go build ./...
cd Client/debugger && go build ./...
cd Client/udpstreamer && go build ./... # or: cd Client/webui && go build
cd Test/E2E/suite/client && go build ./... # chain-client (E2E driver) + its tests
# ImGui desktop client (needs SDL2; fetches Dear ImGui + ImPlot via FetchContent)
cd Client/streamhub && cmake -B build -DCMAKE_BUILD_TYPE=Release && cmake --build build
# Qt desktop client (Qt5 or Qt6 Widgets + WebSockets; autodetects, prefers Qt6)
cd Client/streamhub-qt && cmake -B build && cmake --build build
```
---
### Key scripts
## Run / test commands
### Unit & integration tests (C++)
```bash
./Build/x86-linux/GTest/MainGTest.ex # UDPStreamer unit tests
./Build/x86-linux/GTest/MainGTest.ex --gtest_filter='Name*' # single test
./Build/x86-linux/Test/Integration/Integration/IntegrationTests.ex # DebugService integration tests
```
### Go tests
```bash
cd Test/E2E/suite/client && go test ./...
```
### Python framework tests (E2E framework logic, no live stack needed)
```bash
cd Test/E2E/suite && python3 -m unittest tests_py
```
### E2E / demo scripts (build + launch the full stack)
| Script | What it does |
| Script | Purpose |
|---|---|
| `./run_streamhub.sh` | Launch MARTe2 app (UDPStreamer) + StreamHub, optionally web UI (`-w`) and ImGui client (`-g`). Ports documented in its header. |
| `./Test/E2E/suite/run_e2e.sh` | **Unified E2E suite**: per-scenario generates data + cfgs, runs MARTe2+StreamHub, drives the Go `chain-client` (live/zoom/window/trigger) for `chain` scenarios plus `direct`/`recorder`/`debug`/`tcplogger` scenario kinds, runs a stress matrix, unit suites + coverage, builds a Typst PDF report. Flags: `--skip-build`, `--only <id>`, `--pdf-only`, `--cpp-coverage`, `--skip-coverage`, `--skip-stress`, `--skip-datasources`, `--skip-recorder`, `--skip-debug`, `--skip-tcplogger`. |
| `./Test/E2E/suite/run_stress.sh` | **Capacity/stress harness**: sweeps one load axis at a time (signal size/count, subscriber fan-out, source count, WS-client count, zoom rate), gates on survival+liveness (hard) and RSS+zoom-p95 latency (soft). Flags: `--skip-build`, `--only <id>`, `--axis <axis>`. |
| `./run_streamhub.sh` | Demo stack: build + launch MARTe2 app + StreamHub, optional web (`-w`) / ImGui (`-g`) clients. Flags `-m/-c` MARTe2 dirs, `-b TARGET`, `-p WS_PORT`, `-n MAX_POINTS` (actual default 1000000, header says 10000), `-s` skip build. Generates temp hub cfg with `+History`/`+Recorder` blocks in `/tmp`. Ctrl-C kills all. |
| `./Test/E2E/suite/run_e2e.sh` | Full E2E: 57-scenario matrix + stress + unit suites + gcov coverage + Typst PDF report. Flags: `--skip-build`, `--only <id>`, `--pdf-only`, `--skip-coverage`, `--skip-stress`, `--skip-datasources`, `--skip-recorder`, `--skip-debug`, `--skip-tcplogger` |
| `./Test/E2E/suite/run_stress.sh` | Capacity harness: sweeps one load axis at a time (`--axis`), hard gates survival+liveness, soft gates RSS+zoom-p95 |
The E2E suite produces `Build/x86-linux/E2E/chain/` artifacts: `report_data.json`,
`history.jsonl` (per-run headline metrics for trend/regression tracking),
`trend_*.png` plots, and `E2E_Report.pdf` (compiled from `E2E_Report.typ` via
`typst`). `--cpp-coverage` triggers an instrumented `--coverage` rebuild, captures
with `lcov` (restricted to `Source/*` + `Test/*`), then restores a clean build.
## Code Conventions & Common Patterns
---
- **No STL in `Source/Components/**` (and StreamHub)**: use `StreamString` (not
`std::string`), `FastPollingMutexSem`/`EventSem` (not `std::mutex`/threads),
fixed arrays / MARTe2 `Vector<T>` (not `std::vector`), `REPORT_ERROR` /
`REPORT_ERROR_STATIC` macros (no exceptions). C stdlib is fine. Heap
`new`/`delete[]` is normal. STL/C++17 is fine in `Client/streamhub/` and
`Client/streamhub-qt/`.
- **RT hot-path rule**: `FastPollingMutexSem` on real-time hot paths, never OS
mutexes; RT cycle must not block on the scheduler.
- **Class registration**: `CLASS_REGISTER_DECLARATION()` in the class `public:`
section of the header; `CLASS_REGISTER(Name, "1.0")` at the end of the `.cpp`
inside `namespace MARTe`. Every component `.cpp` ends with it.
- **EUPL v1.1 license headers** on all C++ sources and `Makefile.inc` — preserve
on new files.
- **Per-component build**: each dir has one-line `Makefile.gcc` wrapper
(`include Makefile.inc`) + `Makefile.inc` declaring `OBJSX`, `PACKAGE`,
`ROOT_DIR`, `INCLUDES` (re-declared per file, ~12 MARTe2 layer dirs),
`LIBRARIES`, including `MakeStdLibDefs.$(TARGET)` then
`MakeStdLibRules.$(TARGET)`. Generated `depends.x86-linux` (gcc -MM) is
committed but **never hand-edited** — delete to regenerate.
- **Qt client**: `QT_NO_KEYWORDS` is required (reused `Protocol.h` structs have
members named `signals`); Qt classes use `Q_SIGNALS`/`Q_SLOTS`/`Q_EMIT`. Run
with long options: `--host HOST --port 8090` (single-dash misparsed). Single
GUI thread, 60 Hz QTimer repaint.
- **StreamHub config** is *not* a MARTe2 `RealTimeApplication`: `Hub = { WSPort
MaxPoints PushRate MaxPushPoints RingTemporal RingScalar +Recorder{...}
Sources={id={Label Addr Port}} }`. `+History` keys: `Directory` (required),
`DurationHours` (1), `Decimation` (1), `FlushIntervalSec` (5),
`MinDiskFreeMB` (500). `.shist` files: 64-byte header ('SHR1') + circular
(t,v) float64 pairs.
- **UDPStreamer config**: `Port` (44500; multicast data = `DataPort`, default
`Port+1`), `MaxPayloadSize` (1400), `PublishingMode` `Strict`/`Accumulate`,
per-signal `Signals={Name={Type,Unit,NumberOfDimensions,NumberOfElements,
TimeMode}}` with `TimeMode` `PacketTime`/`FirstSample`/`LastSample`/`FullArray`;
multicast needs `MulticastGroup` + `Interface`.
## Code organization
## Important Files
```
Common/
UDP/UDPSProtocol.h # shared UDPS binary protocol (header-only, MARTe2-free)
Client/go/ # Go packages: udpsprotocol (decoder), wshub (WS hub client)
Source/
Components/ # MARTe2 components — NO STL allowed here
DataSources/UDPStreamer/ # real-time UDP signal streaming DataSource
DataSources/UDPStreamerClient/ # (MARTe2-side UDPS client DataSource)
GAMs/SineArrayGAM/ # sine-wave array generator GAM
GAMs/TimeArrayGAM/ # time-reference array GAM
Interfaces/DebugService/ # tracing/forcing/breakpoint Interface (registry-patched brokers)
Interfaces/TCPLogger/ # REPORT_ERROR → TCP forwarder (LoggerConsumerI)
Interfaces/UDPStream/ # UDPSClient/UDPSServer (C++ consumer/producer of UDPS)
Applications/StreamHub/ # headless C++ hub app (links MARTe2 core but follows MARTe2 style)
Client/
debugger/ # Go web client for DebugService (browser UI in static/)
udpstreamer/ # Go web server + static SPA for UDPStreamer (legacy direct-UDP path)
webui/ # Go StreamHub WebSocket web UI (newer, hub-based)
streamhub/ # native ImGui desktop client (SDL2 + OpenGL + ImPlot, C++17, no MARTe2)
streamhub-qt/ # native Qt Widgets desktop client (C++17, Qt5/Qt6, no MARTe2)
Test/
GTest/ # GTest harness (MainGTest.cpp) for UDPStreamer unit tests
Integration/ # DebugService integration tests (link against DebugService .so)
Components/DataSources/UDPStreamer/ # UDPStreamer unit test sources
Configurations/ # MARTe2 .cfg files for tests and demos
E2E/
chain/ # streaming-chain E2E + stress suite (Python orchestrator + Go client)
datasources/ recorder/ streamhub/ # older per-component E2E scripts
Docs/ # per-component reference docs (Protocol, UDPStreamer, StreamHub-*, DebugService, ...)
docs/superpowers/{specs,plans}/ # design specs + implementation plans (dated)
ARCHITECTURE.md # full architecture (data flow diagrams, protocol tables, WS protocol)
- `env.sh` — environment; source first, always.
- `Makefile.gcc` / `Makefile.inc` (root) — build orchestration.
- `Common/UDP/UDPSProtocol.h` — canonical wire format; changing it triggers the
4-way mirror checklist above.
- `Source/Applications/StreamHub/main.cpp` — hub entry (`[-cfg file.cfg]
[-port N] [-maxPoints N]`); hub **must be heap-allocated** (~128 MB, exceeds
the 8 MB stack).
- `Test/Configurations/*.cfg` — MARTe2 app configs (`$App = { Class =
RealTimeApplication }` with `+Functions`, `+DataSources`, `+States`, `+Timings`
blocks); `streamhub_demo.cfg` and `TestApp.cfg` are good templates.
- `Test/E2E/suite/{scenarios,gen_data,gen_cfg,validate_waveform,stress}.py` —
declarative scenario matrix and generators consumed identically by the Go
chain-client and validators.
- `Client/debugger/main.go` — `-addr :7777` default, `-enable-dangerous-commands`
safety gate (CR-4) for FORCE/PAUSE/RESUME/STEP/BREAK/MSG.
## Runtime/Tooling Preferences
- **OS**: Linux x86_64 (`TARGET=x86-linux`). External deps live outside this
repo: `MARTe2_DIR` (default `~/workspace/MARTe2`) and
`MARTe2_Components_DIR` (default `~/workspace/MARTe2-components`) — edit
`env.sh` if they differ. `env.sh`'s `LD_LIBRARY_PATH` does **not** cover
UDPStreamerClient/UDPStream lib dirs.
- **C++**: MARTe2 `Makefile.gcc` wrapper system, gtest-1.7.0 for tests.
- **Go**: `go 1.21`; modules use `replace marte2/common => ../../Common/Client/go`
(`gorilla/websocket` v1.5.1). Go binaries are gitignored.
- **ImGui client**: needs SDL2; CMake FetchContent pins Dear ImGui **v1.91.8** +
ImPlot **v0.17** (`implot_items.cpp` is a slow -O3 TU, ~2 min rebuild).
- **Qt client**: Qt6 preferred, Qt5 fallback, Widgets + WebSockets, custom
QPainter plotting (no QtCharts).
- **E2E report**: `typst compile E2E_Report.typ`; Python 3 + numpy for the suite.
- Remove `vgore.*` core dumps when you see them; they are not gitignored.
## Testing & QA
Four test layers; `env.sh` + built stack required for all but the standalone
ones. Only `tests_py.py`, Go tests, and the built C++ test binaries run
standalone.
```bash
./Build/x86-linux/GTest/MainGTest.ex --gtest_filter='Name*' # C++ GTest
./Build/x86-linux/Test/Integration/Integration/IntegrationTests.ex # legacy DebugService runtime tests
cd Test/E2E/suite/client && go test ./... # Go chain-client unit tests
cd Test/E2E/suite && python3 -m unittest tests_py # framework logic, standalone
```
### Component layout convention
- **GTest**: `MainGTest.ex` currently holds only `DebugServiceGTest`
(TraceRingBuffer SPSC, DebugSignalInfo, BreakOp). Component GTests
(`UDPStreamerGTest.cpp` ~46 cases, `StreamHubTest.a`, `UDPStreamerClientTest.a`)
compile **as libraries only — no standalone executable**.
- **Legacy IntegrationTests.ex**: 9 printf-narrated DebugService runtime tests,
always returns 0; `collect.py` parses stdout blocks.
- **E2E suite** (`run_e2e.sh`): 57 curated scenarios (s01s57) across kinds
`chain`/`direct`/`recorder`/`debug`/`debug_pause_resume`/`tcplogger`, driven
against live MARTeApp.ex + StreamHub.ex + Go chain-client. `scenarios.py` is a
curated covering set: **every configurable UDPStreamer option value appears in
≥1 scenario** — add a scenario when adding an option.
- **Oracle gates** (`validate_waveform.py`): **fidelity** (every received value
within `tol` of ground truth; 0 for un-quantised ints, float epsilon for
un-quantised floats, `quant_step/2 + 1e-6·range` for quantised) is the
**correctness gate**. **Shape** is a *gross* sanity gate + tracked metric
(`corr >= 0.5`, `nRMSE <= 0.30` relaxed by quant step, frequency searched
±5% band); a correct sinusoid yields corr ~0.820.98, wrong frequency
collapses to ~0.00. Do **not** tighten shape into a correctness gate —
timestamp calibration (Phase-A) is pending.
- **Stress** (`run_stress.sh`): 7 axes (signal size/count/fan-out/sources/WS
clients/zoom rate), hard gates survival+liveness, soft gates RSS+zoom-p95.
- **Coverage**: `--cpp-coverage` rebuilds with gcov, captures via `lcov`
restricted to `Source/*` + `Test/*`, then restores a clean build.
- Artifacts → `Build/x86-linux/E2E/chain/`: `results.json` (XFAIL/XPASS for
`known_issue` markers), `report_data.json`, `history.jsonl`, `trend_*.png`,
`E2E_Report.pdf`; stress → `stress/stress_results.json`.
Every MARTe2 component directory contains:
- `Makefile.gcc` — thin wrapper (`TARGET = x86-linux` then `include Makefile.inc`).
- `Makefile.inc` — real build definition: `OBJSX`, `PACKAGE`, `INCLUDES`,
`LIBRARIES`, includes `$(MARTe2_DIR)/MakeDefaults/MakeStdLibDefs.$(TARGET)`
and `MakeStdLibRules.$(TARGET)`, and `depends.$(TARGET)`.
- `depends.x86-linux` / `dependsRaw.x86-linux` — generated header dependency
files (committed; regenerated by the build). Do not hand-edit.
- `<ClassName>.{h,cpp}` — the implementation, with `CLASS_REGISTER(ClassName,
"1.0")` at the bottom of the `.cpp` (MARTe2 class-registration macro).
To add a new component, copy an existing one and adapt `Makefile.inc`'s
`OBJSX` / `PACKAGE` / `INCLUDES` / `LIBRARIES`.
---
## Conventions and gotchas
### No STL in MARTe2 components
`Source/Components/**` must not use the C++ standard library. Use MARTe2 types:
`StreamString` (not `std::string`), `FastPollingMutexSem` (not `std::mutex`),
fixed arrays / `StaticList` (not `std::vector`), MARTe2 error macros
(`REPORT_ERROR`, `REPORT_ERROR_STATIC`) instead of exceptions. Includes come
from `$(MARTe2_DIR)/Source/Core/...` (BareMetal, Scheduler, FileSystem).
`Source/Applications/StreamHub/` links MARTe2 core and follows the same
no-STL-on-RT-paths style, but is a standalone app (not a loadable component).
STL / C++17 is **fine** in `Client/streamhub/` and `Client/streamhub-qt/`
(framework-free C++17, no MARTe2 dependency).
### RT hot-path mutex rule
Use `FastPollingMutexSem` on real-time hot paths — never OS mutexes
(`pthread_mutex`, `std::mutex`). The RT cycle must not block on the scheduler.
### Protocol change checklist
Changing `Common/UDP/UDPSProtocol.h` (or the Go mirror) requires mirroring
across all four consumers listed in §1, plus the JS client parsers in
`Client/udpstreamer/static/`. The Go decoder lives in
`Common/Client/go/udpsprotocol/protocol.go` and re-declares the constants
(`HeaderSize`, `SigDescSize`, packet types, quant types, time modes) — it does
**not** include the C header.
### Qt client: `QT_NO_KEYWORDS`
`Client/streamhub-qt/` reuses `../streamhub/Protocol.{h,cpp}` and
`SignalBuffer.h` verbatim. Those structs have members named `signals`, which
collide with Qt's `signals`/`slots`/`emit` macros. The Qt build sets
`QT_NO_KEYWORDS` and all Qt classes use `Q_SIGNALS:`/`Q_SLOTS:`/`Q_EMIT`. Do
not remove that define, and do not use the lowercase Qt keywords in Qt-client
code. Single GUI thread — `QWebSocket` signals arrive on the GUI thread, no
locks; a 60 Hz `QTimer` drives repaint. Run with long options:
`./build/StreamHubQtClient --host HOST --port 8090` (single-dash `-host` is
misparsed as clustered short flags).
### StreamHub history
`HistoryWriter` (`Source/Applications/StreamHub/HistoryWriter.{h,cpp}`) does
disk-backed circular storage: per-signal `.shist` files with a 64-byte header
and a pre-allocated circular region of `(float64 time, float64 value)` pairs.
Configured via a `+History` block (`Directory` required; `DurationHours`
default 1, `Decimation` default 1, `FlushIntervalSec` default 5,
`MinDiskFreeMB` default 500). WS commands: `historyInfo`, `historyZoom`.
### E2E scenario / stress matrix
`Test/E2E/suite/scenarios.py` is a *curated covering set*: every configurable
UDPStreamer option value appears in at least one scenario, plus high-risk
interactions. `stress.py` is the capacity sibling — it sweeps one load axis at
a time and records survival/liveness (hard gates) and RSS/zoom-p95 latency
(soft gates). Both feed the same `gen_data.py` / `gen_cfg.py` generators. When
adding a new UDPStreamer option, add a scenario that exercises it and update
`validate_scenario` / the stress matrix accordingly.
`validate_waveform.py` has two gates: **fidelity** (every received value within
`tol` of some ground-truth value; `tol` is 0 for un-quantised ints, a float
epsilon for un-quantised floats, `quant_step/2 + 1e-6·range` for quantised
floats) — this is the **correctness** gate. **Shape** (least-squares sine fit,
correlation ≥ 0.99) is a gross-sanity gate + tracked metric, pending Phase-A
timestamp calibration. Do not tighten the shape gate to a correctness gate.
### Gitignored build artefacts
`Build/`, `compile_commands.json`, `*.gcov`/`*.gcda`/`*.gcno` (coverage), Go
binaries (`Client/*/marte2debugger`, `udpstreamer-webui`, `streamhub-e2e`,
`Client/*/bin/`), and the `udp_standalone_webui/` scratch dir are all
gitignored. `vgore.*` core dumps (e.g. `vgcore.26968`) are not — remove them.
---
## Documentation map
- `ARCHITECTURE.md` — full architecture: data-flow diagrams, UDPS packet/header
tables, CONFIG payload, StreamHub WebSocket protocol (commands, events,
binary frames), threading model.
- `Docs/` — per-component reference docs (`Protocol.md`, `UDPStreamer.md`,
`SineArrayGAM.md`, `DebugService.md`, `StreamHub-{UserGuide,API,Developer}.md`,
`Tutorial.md`, `WebUI.md`).
- `docs/superpowers/specs/` — design specs (dated, e.g.
`2026-06-26-stress-suite-report-integration-design.md`).
- `docs/superpowers/plans/` — implementation plans paired with specs.
- `.superpowers/sdd/` — task briefs, reports, and a `progress.md` ledger for
in-flight feature work (not always present; per-branch).
- `README.md` — high-level overview + quick-start config snippets.
- `CLAUDE.md` — condensed agent guidance (overlaps with this file).
---
## Ports reference (defaults)
## Ports Reference (defaults)
| Port | Protocol | Component | Purpose |
|------|----------|-----------|---------|
| 44500 | UDP | UDPStreamer | scalar signals (unicast control) |
| 44501 | UDP | UDPStreamer | packed arrays (FirstSample/LastSample) |
| 44502 | UDP | UDPStreamer | packed arrays (FullArray) |
|---|---|---|---|
| 44500 | UDP | UDPStreamer | scalar signals (unicast control + data) |
| 44501/44502 | UDP | UDPStreamer | packed arrays (FirstSample/LastSample, FullArray) |
| 44503 | UDP | UDPStreamer | multicast data (group 239.0.0.1) |
| 8080 | TCP | DebugService | text command channel |
| 8081 | UDP | DebugService | trace telemetry stream |
| 8080 | TCP | DebugService | text command channel (one client at a time, newline-terminated) |
| 8081 | UDP | DebugService | trace telemetry (UDPS format) |
| 8082 | TCP | TcpLogger | REPORT_ERROR log forward |
| 8090 | TCP/WS | StreamHub | WebSocket (commands + binary data) |
| 8080 | TCP | udpstreamer-webui | web UI listen (legacy direct-UDP path) |
| 9090 | TCP | debugger | debug web UI listen |
Note the 8080 collision: DebugService control vs. the legacy web UI listen port.
In combined demos that run both, the scripts adjust one of them — check the
script header before assuming.
---
## License
EUPL v1.1 — license headers are present on all C++ sources. Preserve them on
new files; do not relicense.
| 7777 | TCP | Client/debugger | debug web UI (older docs say 9090; current flag is `-addr`) |
| 8080 | TCP | Client/udpstreamer, Client/webui | web UI listen (collides with DebugService in combined demos — scripts adjust) |
+32 -1
View File
@@ -39,7 +39,38 @@ cd Client/streamhub-qt && cmake -B build && cmake --build build
End-to-end demo script (build + launch full stack, see header for ports/options): `./run_streamhub.sh`.
**Streaming-chain E2E suite** (`Test/E2E/suite/`): `./run_e2e.sh [--skip-build] [--only <id>] [--cpp-coverage]` drives the full chain per scenario (`scenarios.py`) — generates typed/shaped input + both cfgs, runs MARTe2+StreamHub, records via the Go `chain-client` (live/zoom/window/trigger), and validates the recorded waveform against an analytic/fed oracle (`validate_waveform.py`: fidelity gates correctness, sine shape-fit is a gross-sanity gate + tracked metric pending Phase-A timestamp calibration). It then runs the unit suites + coverage (`collect.py`: C++ GTest, Go, Python; `--cpp-coverage` does an instrumented `--coverage` rebuild, captures with lcov restricted to `Source/*` (the `Test/` harness itself is excluded — it executes every line by construction and would just inflate the number), then restores the clean build), consolidates everything into `report_data.json` with per-field progression/regression vs the previous run and trend plots (`report_build.py`, history in `Build/x86-linux/E2E/chain/history.jsonl`), and compiles a Typst PDF (`E2E_Report.typ`). Python framework unit tests: `python3 -m unittest tests_py` (in `Test/E2E/suite/`).
**Streaming-chain E2E suite** (`Test/E2E/suite/`):
```bash
./Test/E2E/suite/run_e2e.sh [flags]
```
Flags:
| Flag | Effect |
|---|---|
| `--skip-build` | Skip C++ component rebuild |
| `--only <id>` | Run a single scenario by ID |
| `--pdf-only` | Just compile the Typst PDF report |
| `--cpp-coverage` | Instrumented gcov rebuild + lcov capture (on by default) |
| `--skip-coverage` | Disable the coverage pass |
| `--skip-stress` | Skip the stress matrix |
| `--skip-datasources` | Skip `direct` scenarios |
| `--skip-recorder` | Skip `recorder` scenarios |
| `--skip-debug` | Skip `debug` and `debug_pause_resume` scenarios |
| `--skip-tcplogger` | Skip `tcplogger` scenarios |
Scenario kinds (defined in `scenarios.py`):
- **chain** — full streaming pipeline: MARTe2 → UDPStreamer → StreamHub → Go `chain-client` (live/zoom/window/trigger). Validates recorded waveform against analytic/fed oracle (`validate_waveform.py`: fidelity gates correctness, sine shape-fit is a gross-sanity gate + tracked metric).
- **direct** — MARTe2 FileReader → FileWriter round-trip, validates binary output.
- **recorder** — MARTe2 → StreamHub with history recorder, validates recorded `.bin` file.
- **debug / debug_pause_resume** — DebugService scenarios via the Go `debugclient`.
- **tcplogger** — TcpLogger scenarios via the Go `debugclient`.
After scenarios, the suite runs unit tests + coverage (`collect.py`: C++ GTest, Go, Python; coverage uses lcov restricted to `Source/*` — the `Test/` harness is excluded), consolidates everything into `report_data.json` with per-field progression/regression vs the previous run and trend plots (`report_build.py`, history in `Build/x86-linux/E2E/chain/history.jsonl`), and compiles a Typst PDF (`E2E_Report.typ`). Artifacts go to `Build/x86-linux/E2E/chain/` (report, logs, PDF) and `/tmp/chain_e2e/` (scratch). Results are aggregated into `results.json` with XFAIL/XPASS handling for known issues.
Python framework unit tests: `python3 -m unittest tests_py` (in `Test/E2E/suite/`).
Build output goes to `Build/x86-linux/` (shared libs per component, `.ex` executables).
+14
View File
@@ -99,6 +99,20 @@ func BuildDisconnectPacket() []byte {
})
}
// BuildAckPacket returns a 17-byte ACK datagram. Unicast clients send it
// periodically as a keepalive: UDPSServer refreshes the client's last-seen
// without re-sending CONFIG (which a repeated CONNECT would trigger).
func BuildAckPacket() []byte {
return buildHeader(PacketHeader{
Magic: MagicUDPS,
Type: PktACK,
Counter: 0,
FragmentIdx: 0,
TotalFragments: 1,
PayloadBytes: 0,
})
}
// ─── Signal descriptor (136 bytes) ───────────────────────────────────────────
// SignalInfo holds the parsed metadata for one signal.
+69
View File
@@ -0,0 +1,69 @@
package wshub
import (
"net"
"testing"
"time"
"marte2/common/udpsprotocol"
)
// TestUDPClientSendsPeriodicKeepAliveAcks verifies that a unicast UDPClient
// re-sends ACK datagrams from the SAME socket at keepAliveInterval. The
// UDPSServer refreshes a unicast client's last-seen only on client->server
// traffic; without this keepalive it evicts the client after ClientTimeout
// (default 30 s) and the stream dies.
func TestUDPClientSendsPeriodicKeepAliveAcks(t *testing.T) {
srv, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.ParseIP("127.0.0.1")})
if err != nil {
t.Fatal(err)
}
defer srv.Close()
c := NewUDPClient(srv.LocalAddr().String(), "ka1", NewHub(), "", 0)
c.keepAliveInterval = 150 * time.Millisecond
go c.Run()
defer c.Stop()
buf := make([]byte, 512)
// 1) CONNECT from the client's ephemeral socket.
srv.SetReadDeadline(time.Now().Add(3 * time.Second))
n, clientAddr, err := srv.ReadFromUDP(buf)
if err != nil {
t.Fatalf("expected CONNECT: %v", err)
}
hdr, err := udpsprotocol.ParseHeader(buf[:n])
if err != nil {
t.Fatalf("parse CONNECT: %v", err)
}
if hdr.Type != udpsprotocol.PktConnect {
t.Fatalf("first packet type = %d, want CONNECT (%d)", hdr.Type, udpsprotocol.PktConnect)
}
// 2) Collect ACKs for ~1 s: must be periodic and from the SAME socket
// (a new ephemeral socket would be registered as a new client).
deadline := time.Now().Add(time.Second)
acks := 0
for time.Now().Before(deadline) {
srv.SetReadDeadline(deadline)
n, addr, err := srv.ReadFromUDP(buf)
if err != nil {
break
}
hdr, err := udpsprotocol.ParseHeader(buf[:n])
if err != nil {
continue
}
if hdr.Type != udpsprotocol.PktACK {
t.Fatalf("unexpected packet type %d from %s", hdr.Type, addr)
}
if addr.String() != clientAddr.String() {
t.Fatalf("ACK from %s, want same socket as CONNECT (%s)", addr, clientAddr)
}
acks++
}
if acks < 3 {
t.Fatalf("expected >= 3 keepalive ACKs in 1 s, got %d", acks)
}
}
+46 -1
View File
@@ -172,6 +172,11 @@ const (
reconnectDelay = 2 * time.Second
readBufSize = 65536
udpRcvBufSize = 8 * 1024 * 1024
// keepAliveInterval is the unicast keepalive period. The UDPStreamer
// server evicts silent unicast clients after its ClientTimeout (default
// 30 s); an ACK from the same socket refreshes its last-seen without
// triggering a CONFIG resend (a CONNECT would).
keepAliveInterval = 15 * time.Second
)
// UDPClient manages the connection to one MARTe2 streamer source.
@@ -181,6 +186,7 @@ type UDPClient struct {
hub *Hub
multicastGroup string
dataPort int
keepAliveInterval time.Duration
stopCh chan struct{}
}
@@ -192,6 +198,7 @@ func NewUDPClient(serverAddr, sourceID string, hub *Hub, multicastGroup string,
hub: hub,
multicastGroup: multicastGroup,
dataPort: dataPort,
keepAliveInterval: keepAliveInterval,
stopCh: make(chan struct{}),
}
}
@@ -253,6 +260,20 @@ func (u *UDPClient) runSession() error {
return err
}
log.Printf("[%s] udp: sent CONNECT", u.sourceID)
lastData := time.Now()
lastKeepAlive := time.Now()
// sendKeepAliveIfDue sends an ACK if the keepalive interval has elapsed.
// ACK refreshes the server's last-seen without re-sending CONFIG (which a
// repeated CONNECT would trigger).
sendKeepAliveIfDue := func() error {
if u.keepAliveInterval > 0 && time.Since(lastKeepAlive) >= u.keepAliveInterval {
if _, err := conn.WriteToUDP(udpsprotocol.BuildAckPacket(), serverAddr); err != nil {
return err
}
lastKeepAlive = time.Now()
}
return nil
}
reassembler := udpsprotocol.NewReassembler(2 * time.Second)
buf := make([]byte, readBufSize)
@@ -260,14 +281,34 @@ func (u *UDPClient) runSession() error {
var currentPublishMode uint8
for {
conn.SetReadDeadline(time.Now().Add(silenceTimeout))
// Wake up at least every keepalive interval so ACKs are sent even
// when the server is idle; the read deadline also doubles as the
// silence detector (no data for silenceTimeout = server gone).
wakeup := silenceTimeout
if u.keepAliveInterval > 0 && u.keepAliveInterval < wakeup {
wakeup = u.keepAliveInterval
}
conn.SetReadDeadline(time.Now().Add(wakeup))
n, _, err := conn.ReadFromUDP(buf)
arrivalTime := time.Now()
if err != nil {
if ne, ok := err.(net.Error); ok && ne.Timeout() {
if time.Since(lastData) >= silenceTimeout {
// True silence: stream is dead — Run() reconnects.
conn.WriteToUDP(udpsprotocol.BuildDisconnectPacket(), serverAddr)
return err
}
// Short wakeup: keepalive if due, then keep waiting.
if kaErr := sendKeepAliveIfDue(); kaErr != nil {
return kaErr
}
continue
}
conn.WriteToUDP(udpsprotocol.BuildDisconnectPacket(), serverAddr)
return err
}
lastData = arrivalTime
if n < udpsprotocol.HeaderSize {
log.Printf("[%s] udp: short datagram (%d bytes), skipping", u.sourceID, n)
@@ -334,6 +375,10 @@ func (u *UDPClient) runSession() error {
return nil
default:
}
if kaErr := sendKeepAliveIfDue(); kaErr != nil {
return kaErr
}
}
}
+282
View File
@@ -0,0 +1,282 @@
# E2E Test Suite
The streaming-chain end-to-end suite (`Test/E2E/suite/`) validates the full data path from
MARTe2 real-time application through the UDPS wire protocol to StreamHub and client consumers.
It also covers the debug/trace path (DebugService, TCPLogger) and the direct
UDPStreamer-to-UDPStreamerClient round-trip.
## Overview
The suite is driven by a single orchestrator script:
```bash
source env.sh
./Test/E2E/suite/run_e2e.sh [flags]
```
For each scenario defined in `scenarios.py`, the orchestrator:
1. **Generates input data** (`gen_data.py`) — deterministic typed/shaped binary in MARTe2
FileReader format, plus a ground-truth dict for the validator.
2. **Generates configs** (`gen_cfg.py`) — MARTe2 app config (LinuxTimer + FileReader + IOGAM +
UDPStreamer) and StreamHub config, per scenario.
3. **Launches the server stack** — MARTe2 app + StreamHub (for chain/recorder scenarios) or
MARTe2 app alone (for direct/debug scenarios).
4. **Drives mock clients** — the Go `chain-client` (chain scenarios) or `debugclient`
(debug/tcplogger scenarios) connects, records data, and runs behavioural checks.
5. **Validates** (`validate_waveform.py`) — compares the recorded stream against the analytic
ground truth and/or the fed-reference tap file.
6. **Renders plots** (`plots.py`) — waveform, trigger, and zoom overlay PNGs per scenario.
7. **Runs unit tests + coverage** (`collect.py`) — C++ GTest, Go, and Python suites with
optional lcov C++ line coverage.
8. **Runs stress matrix** (`stress_run.py` / `stress.py`) — capacity sweeps (signal size,
count, fan-out, zoom rate) with survival/liveness/RSS/latency gates.
9. **Builds the report** (`report_build.py`) — consolidates everything into
`report_data.json` with regression tracking against the previous run, trend plots, and a
Typst PDF (`E2E_Report.typ`).
---
## Flags
| Flag | Effect |
| -------------------- | -------------------------------------------------------- |
| `--skip-build` | Skip C++ component rebuild |
| `--only <id>` | Run a single scenario by ID |
| `--pdf-only` | Just compile the Typst PDF report (no tests) |
| `--cpp-coverage` | Instrumented gcov rebuild + lcov capture (on by default) |
| `--skip-coverage` | Disable the coverage pass |
| `--skip-stress` | Skip the stress matrix |
| `--skip-datasources` | Skip `direct` scenarios |
| `--skip-recorder` | Skip `recorder` scenarios |
| `--skip-debug` | Skip `debug` and `debug_pause_resume` scenarios |
| `--skip-tcplogger` | Skip `tcplogger` scenarios |
---
## Scenario Kinds
### chain
Full streaming pipeline: MARTe2 (FileReader -> IOGAM -> UDPStreamer) -> StreamHub -> Go
`chain-client`. The client records the live binary stream and runs behavioural checks
(live, zoom, window, trigger). The validator compares the recording against the analytic
ground truth (fidelity, sine shape fit, continuity) and optionally a fed-reference tap.
### direct
MARTe2 FileReader -> UDPStreamer -> UDPStreamerClient -> FileWriter round-trip. Validates that
the written binary matches the input binary (bit-exact for each signal type).
### recorder
MARTe2 -> UDPStreamer -> StreamHub with BinaryRecorder enabled. Validates the `.bin` file
written to disk by the recorder against the original input.
### debug / debug_pause_resume
DebugService scenarios exercising FORCE, TRACE, and BREAK commands over TCP (port 8080) with
trace telemetry on UDP (port 8081). The Go `debugclient` scripts a fixed command sequence and
verifies real acknowledgements. The `debug_pause_resume` variant additionally verifies that
PAUSE halts the RT loop and RESUME restarts it via live VALUE polling.
### tcplogger
TCPLogger delivery: verifies that a triggered DebugService event produces a log line on the
TCPLogger TCP port (8082/9090).
---
## Validation Oracles
Each chain scenario specifies an `oracle` mode:
- **analytic** — ground truth is reconstructed from `gen_data.py`'s deterministic formulas
(sine, ramp, counter, time_us, time_ns). No reference file needed.
- **fed** — a second IOGAM branch in the MARTe config taps the same signals into a FileWriter
("tap file"). The validator compares recordings against this tap.
- **both** — both oracles are applied.
Per-signal checks (`validate_waveform.py`):
| Check | Description |
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Fidelity** | Every received value within tolerance of some ground-truth value. Tolerance is 0 for raw integers, float epsilon for raw floats, `quant_step/2 + 1e-6*range` for quantised floats. |
| **Shape** | Sine signals (>= 8 points): least-squares fit of `a*sin(wt)+b*cos(wt)+c`. Requires correlation >= 0.99 and low normalised RMSE (relaxed by quant step). |
| **Fed reference** | When `--tap` is given, each received value must also match the tap. |
| **Continuity** | Flags inter-sample gaps > 10x median spacing. Fails when summed gap duration exceeds 5% of capture span. |
---
## Client Checks
The Go `chain-client` (`Test/E2E/suite/client/`) performs behavioural checks specified per
scenario in `client_checks`:
| Check | What it verifies |
| --------- | ----------------------------------------------------------------------------------------------- |
| `live` | WebSocket connection succeeds and live binary pushes arrive with monotonic timestamps. |
| `zoom` | A `zoom` WS command returns a valid binary response covering the requested time range. |
| `window` | A `window` WS command returns data within the specified time bounds. |
| `trigger` | A `trigger` WS command on the specified signal fires and returns data around the trigger point. |
---
## Stress Matrix
The stress module (`stress.py` + `stress_run.py`) exercises capacity by sweeping one load axis
at a time:
| Axis | What is scaled |
| ------------------ | ------------------------------------------------------------ |
| Signal size | Bytes per packet (array element count) |
| Signal count | Number of signals per source |
| Subscriber fan-out | Number of StreamHub instances subscribing to one UDPStreamer |
| WS client count | Parallel WebSocket clients on one StreamHub |
| Zoom request rate | Concurrent zoom queries per second per client |
Gates:
- **Survival** (hard) — neither server crashed or hung.
- **Liveness** (hard) — every client received monotonic, timestamped pushes.
- **Peak RSS** (soft) — MARTe and StreamHub memory stayed under case ceilings.
- **Zoom p95 latency** (soft) — round-trip zoom query latency under load.
Results are written to `stress_results.json` with axis/level for scaling-curve plots.
---
## Artifacts
| Path | Content |
| -------------------------------------------- | ------------------------------------------------------------------- |
| `Build/x86-linux/E2E/chain/results.json` | Per-scenario status (PASS/FAIL/SKIP/XFAIL/XPASS) + waveform metrics |
| `Build/x86-linux/E2E/chain/report_data.json` | Full report data including regression diffs |
| `Build/x86-linux/E2E/chain/history.jsonl` | One-line-per-run headline metrics for trend tracking |
| `Build/x86-linux/E2E/chain/trend_*.png` | Pass-rate / coverage / fidelity / memory trend plots |
| `Build/x86-linux/E2E/chain/E2E_Report.pdf` | Compiled Typst PDF report |
| `Build/x86-linux/E2E/chain/unit_tests.json` | Per-suite test results (GTest, Go, Python) |
| `Build/x86-linux/E2E/chain/coverage.json` | Per-language coverage percentages |
| `Build/x86-linux/E2E/chain/stress/` | Stress matrix results |
| `Build/x86-linux/E2E/chain/hub_<id>.log` | StreamHub stdout/stderr per scenario |
| `Build/x86-linux/E2E/chain/marte_<id>.log` | MARTe2 app stdout/stderr per scenario |
| `Build/x86-linux/E2E/chain/client_<id>.log` | Client stdout/stderr per scenario |
| `/tmp/chain_e2e/` | Scratch: input binaries, configs, recordings, metrics, plots |
---
## XFAIL / XPASS Handling
Scenarios may carry a `known_issue` marker (a human-readable string describing a documented,
not-yet-fixed chain gap). When present:
- A raw **FAIL** is reclassified as **XFAIL** (expected failure) — does not break the green
baseline.
- A raw **PASS** becomes **XPASS** (unexpectedly fixed) — surfaced as a failure to prompt
removal of the stale marker.
Overall status is PASS when there are no hard FAILs and no XPASSes.
---
## Framework Files
| File | Role |
| ---------------------- | --------------------------------------------------------------- |
| `run_e2e.sh` | Top-level orchestrator (build, run scenarios, coverage, report) |
| `scenarios.py` | Declarative scenario matrix + validation |
| `gen_data.py` | Deterministic input binary generator |
| `gen_cfg.py` | MARTe2 + StreamHub config generator |
| `validate_waveform.py` | Waveform comparison (fidelity, shape, continuity) |
| `plots.py` | Per-scenario PNG figure renderer |
| `collect.py` | Unit test runner + coverage collector (GTest, Go, Python, lcov) |
| `report_build.py` | Report data consolidator + trend plots + history |
| `stress.py` | Declarative stress case matrix |
| `stress_run.py` | Stress matrix orchestrator |
| `proc_perf.py` | Live-process CPU/RSS snapshot from `/proc` |
| `E2E_Report.typ` | Typst template for the PDF report |
| `tests_py.py` | Python framework unit tests (`python3 -m unittest tests_py`) |
| `client/main.go` | Go chain-client (live record + zoom/window/trigger checks) |
| `debugclient/main.go` | Go debug/tcplogger client (command scripting + verification) |
---
## Scenario Matrix
| ID | Kind | Description |
| ----------------------------- | ------------------ | ---------------------------------------------------------------------------------------- |
| `s01_scalar_uint32` | chain | Single uint32 scalar counter, Strict unicast (type fidelity) |
| `s02_array_float32_fullarray` | chain | 100-elem float32 array, FullArray time mode, uint64 ns time array |
| `s03_quant_uint16` | chain | float32 scalar quantised to uint16 over [-5,5], Strict unicast |
| `s04_int8_scalar` | chain | int8 scalar counter, type fidelity |
| `s05_uint8_scalar` | chain | uint8 scalar counter, type fidelity |
| `s06_int16_scalar` | chain | int16 scalar ramp, type fidelity |
| `s07_uint16_scalar` | chain | uint16 scalar ramp, type fidelity |
| `s08_int32_scalar` | chain | int32 scalar counter, type fidelity |
| `s09_int64_scalar` | chain | int64 scalar counter, type fidelity |
| `s10_uint64_scalar` | chain | uint64 scalar counter, type fidelity |
| `s11_float64_scalar` | chain | float64 scalar sine 5 Hz (double-precision path) |
| `s12_f32_arr8` | chain | float32 8-elem array sine 5 Hz |
| `s13_f32_arr32` | chain | float32 32-elem array sine 10 Hz |
| `s14_f64_arr64` | chain | float64 64-elem array ramp |
| `s15_i16_arr16` | chain | int16 16-elem array counter |
| `s16_f32_arr256` | chain | float32 256-elem array sine 5 Hz (large frame) |
| `s17_lastsample` | chain | float32 8-elem LastSample, uint64 ns scalar anchor |
| `s18_firstsample` | chain | float32 8-elem FirstSample, uint32 us scalar anchor |
| `s19_fullarray_f64` | chain | float64 50-elem FullArray sine 5 Hz, uint64 ns time |
| `s20_quant_uint8` | chain | float32 scalar quant uint8 [-1,1] sine 5 Hz |
| `s21_quant_int8` | chain | float32 scalar quant int8 [-10,10] sine 5 Hz |
| `s22_quant_int16` | chain | float32 scalar quant int16 [-100,100] ramp |
| `s23_quant_f64_arr` | chain | float64 16-elem quant uint16 [-2,2] sine 5 Hz |
| `s24_accumulate` | chain | float32 scalar sine 5 Hz, Accumulate @50 Hz refresh |
| `s25_decimate4` | chain | float32 scalar sine 5 Hz, Decimate ratio 4 |
| `s26_decimate10_arr` | chain | float32 8-elem counter, Decimate ratio 10 |
| `s27_frag_f64_128` | chain | float64 128-elem ramp, MaxPayload 512 (fragmented) |
| `s28_frag_f32_100` | chain | float32 100-elem sine 5 Hz, MaxPayload 256 (fragmented) |
| `s29_mcast_scalar` | chain | multicast float32 scalar sine 5 Hz |
| `s30_mcast_arr_fullarray` | chain | multicast float32 32-elem FullArray sine 5 Hz |
| `s31_two_src` | chain | two unicast sources: float32 sine + uint32 counter |
| `s32_three_src` | chain | three unicast sources: int16 ramp / float64 sine / uint8 counter |
| `s33_dec_arr_quant` | chain | Decimate 2 + 16-elem quant uint16 sine 5 Hz |
| `s34_acc_fullarray` | chain | Accumulate @100 Hz: accumulated scalar + 32-elem FullArray sine passenger |
| `s35_mcast_decimate` | chain | multicast + Decimate ratio 5, float32 scalar sine 5 Hz |
| `s36_big_frag_dec` | chain | float64 64-elem ramp, MaxPayload 256 + Decimate 4 |
| `s37_trig_ramp_i32` | chain | trigger on int32 ramp scalar |
| `s38_trig_f64_sine` | chain | trigger on float64 sine 5 Hz scalar |
| `s39_uint8_arr32` | chain | uint8 32-elem array counter (wrap fidelity) |
| `s40_int8_arr16` | chain | int8 16-elem array counter (wrap fidelity) |
| `s41_f32_unit` | chain | float32 scalar ramp with Unit=V |
| `s42_f64_counter` | chain | float64 scalar counter (large integer values) |
| `s43_fullarray_quant` | chain | float32 16-elem FullArray quant uint16 sine 5 Hz |
| `s44_window_check` | chain | float32 sine 5 Hz scalar, window time-range check |
| `s45_decimate_multisig` | chain | Decimate ratio 2 over a 2-signal source |
| `s46_accumulate_arr` | chain | Accumulate @200 Hz: accumulated scalar sine + 16-elem array passenger |
| `s47_mcast_multisrc` | chain | multicast, two sources (scalar each) |
| `s48_f64_arr_big_payload` | chain | float64 100-elem ramp, MaxPayload 65490 (single frame) |
| `s49_mixed_quant_raw` | chain | one source: quant uint8 sine + raw float32 sine |
| `s50_trig_quant` | chain | trigger on quantised uint16 sine 10 Hz |
| `s51_8x1msps_100hz` | chain | 8x float32 10k-elem arrays @1 MSps, FirstSample, 100 Hz packets (~32 MB/s) |
| `s52_direct_unicast` | direct | Direct UDPStreamer->UDPStreamerClient round-trip, unicast |
| `s53_direct_multicast` | direct | Direct UDPStreamer->UDPStreamerClient round-trip, multicast |
| `s54_recorder` | recorder | StreamHub BinaryRecorder disk-output round-trip |
| `s55_debug_force_trace_break` | debug | DebugService FORCE/TRACE/BREAK over real TCP 8080 + UDP 8081 |
| `s56_tcplogger_delivery` | tcplogger | TCPLogger delivers a log line for a triggered DebugService event |
| `s57_debug_pause_resume` | debug_pause_resume | DebugService PAUSE/RESUME halts and resumes the RT loop, verified via live VALUE polling |
---
## Coverage Goals
The chain scenario matrix is a curated covering set: every configurable UDPStreamer option
value appears in at least one scenario:
- **All 10 MARTe2 types**: int8, uint8, int16, uint16, int32, uint32, int64, uint64, float32, float64
- **Scalar and array shapes**: elements 1, 8, 16, 32, 50, 64, 100, 128, 256, 1000, 10000
- **All four TimeModes**: PacketTime, FullArray, FirstSample, LastSample
- **All five QuantizedTypes**: none, uint8, int8, uint16, int16
- **All three PublishingModes**: Strict, Accumulate, Decimate
- **Both network modes**: unicast and multicast
- **Fragmentation**: small MaxPayloadSize forcing multi-fragment datagrams
- **Multi-source**: 1, 2, and 3 independent UDPStreamer feeds into one StreamHub
- **High-risk interactions**: decimate+quant+array, accumulate+fullarray, multicast+decimate,
fragmentation+decimate, mixed quant+raw signals
+124 -10
View File
@@ -8,7 +8,9 @@ thread.
## Key Features
- **Zero-copy RT path** — `Synchronise()` only locks, copies signal memory, and posts a semaphore.
- **Single-client model** — one client at a time; a new CONNECT replaces the previous session.
- **Unicast and multicast** — unicast (default): single client at a time, new CONNECT replaces
the previous session. Multicast: multiple clients receive data simultaneously by joining
a multicast group; control traffic uses a TCP listener.
- **Packet fragmentation** — large payloads are split into ≤ `MaxPayloadSize`-byte datagrams,
each with a header carrying fragment index and total count so the client can reassemble them.
- **Signal quantization** — `float32`/`float64` signals can be linearly quantized to
@@ -16,6 +18,8 @@ thread.
- **Temporal arrays** — signals with `NumberOfElements > 1` can carry per-sample time
metadata via `TimeMode` and `TimeSignal`, enabling high-frequency burst transmission
(e.g. 1 000 samples per RT cycle at 1 MSps).
- **Publishing modes** — `Strict` (one packet per RT cycle), `Accumulate` (batch N snapshots
then flush on size or time limit), `Decimate` (send every Nth cycle).
---
@@ -26,10 +30,22 @@ thread.
Class = UDPStreamer
// Network
Port = 44500 // UDP port the server listens on (default: 44500)
Port = 44500 // UDP port (unicast) or TCP control port (multicast)
MaxPayloadSize = 1400 // Maximum bytes per UDP datagram (default: 1400)
// Must be > 17 (header size). Tune for MTU.
// Multicast (optional — omit for unicast mode)
MulticastGroup = "239.0.0.1" // IPv4 multicast address (224.0.0.0/4)
Interface = "eth0" // Multicast-bound interface (mandatory when MulticastGroup is set)
DataPort = 44501 // UDP port for multicast DATA (default: Port+1)
// Publishing mode (optional)
PublishingMode = "Strict" // Strict | Accumulate | Decimate
// For Accumulate mode:
MinRefreshRate = 120.0 // Flush frequency in Hz (required for Accumulate)
// For Decimate mode:
Ratio = 10 // Send 1 packet every N RT cycles (required for Decimate)
// Background thread (optional)
CPUMask = 0x2 // CPU affinity mask for the network thread
StackSize = 1048576 // Stack size in bytes (default: 1 MiB)
@@ -67,16 +83,22 @@ thread.
### Top-level Parameters
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `Port` | uint16 | 44500 | UDP server port |
| ---------------- | ------ | ---------------- | --------------------------------------------------------------------------- |
| `Port` | uint16 | 44500 | UDP server port (unicast) or TCP control port (multicast). Values ≤ 1024 produce a warning. |
| `MaxPayloadSize` | uint32 | 1400 | Max payload bytes per UDP datagram (min 18) |
| `CPUMask` | uint32 | 0 (any) | Background thread CPU affinity |
| `StackSize` | uint32 | 1 048 576 | Background thread stack size in bytes |
| `MulticastGroup` | string | *(absent)* | IPv4 multicast address (e.g. `"239.0.0.1"`). Must be in 224.0.0.0/4. Absent or empty = unicast mode. |
| `Interface` | string | *(absent)* | Network interface for multicast binding (e.g. `"eth0"`). **Mandatory** when `MulticastGroup` is set. |
| `DataPort` | uint16 | Port+1 | UDP port for multicast DATA datagrams. Ignored in unicast mode. |
| `PublishingMode` | string | Strict | `Strict`: send every RT cycle. `Accumulate`: batch until size/time limit. `Decimate`: send every Nth cycle. |
| `MinRefreshRate` | float64| — | Flush frequency in Hz. **Required** when `PublishingMode` = `Accumulate`. |
| `Ratio` | uint32 | — | Send 1 packet every `Ratio` RT cycles. **Required** when `PublishingMode` = `Decimate`. |
| `CPUMask` | uint32 | 0xFFFFFFFF (any) | Background thread CPU affinity bitmask |
| `StackSize` | uint32 | MARTe2 default | Background thread stack size in bytes |
### Per-signal Parameters
| Parameter | Type | Default | Applies to |
|-----------|------|---------|------------|
| --------------- | ------- | ------------ | -------------------------------------------------------- |
| `Unit` | string | `""` | Any type — informational, forwarded to client in CONFIG |
| `RangeMin` | float64 | 0.0 | float32/float64 with `QuantizedType` |
| `RangeMax` | float64 | 1.0 | float32/float64 with `QuantizedType` |
@@ -88,7 +110,7 @@ thread.
### Quantization Types
| Value | Wire type | Bit depth | Notes |
|-------|-----------|-----------|-------|
| -------- | -------------- | --------- | ------------------------------------------------- |
| `none` | same as source | — | Raw copy, no quantization |
| `uint8` | uint8 | 8-bit | Maps `[RangeMin, RangeMax]``[0, 255]` |
| `int8` | int8 | 8-bit | Maps `[RangeMin, RangeMax]``[-127, 127]` |
@@ -105,7 +127,7 @@ wire_value = (uint16)(normalized × 65535)
### Time Modes
| Value | Meaning | Requirements |
|-------|---------|--------------|
| ------------- | ------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------- |
| `PacketTime` | The HRT counter captured at `Synchronise()` time is used as the packet timestamp. No per-signal time metadata. | — |
| `FullArray` | `TimeSignal` carries one timestamp per element (same `NumberOfElements`). | `TimeSignal` must have the same `NumberOfElements`. |
| `FirstSample` | `TimeSignal` is a scalar giving the timestamp of element `[0]`. Elements `[1..N-1]` are inferred at `1/SamplingRate` intervals. | Scalar `TimeSignal`; `SamplingRate > 0`. |
@@ -113,6 +135,62 @@ wire_value = (uint16)(normalized × 65535)
---
## Network Modes
### Unicast (default)
The server opens a single UDP socket on `Port`. The client initiates the session by sending a
CONNECT packet to that port. The server replies with a CONFIG packet on the same socket and
subsequently sends DATA packets directly to the client's address. One client at a time; a new
CONNECT evicts the previous client.
### Multicast
Enabled by setting `MulticastGroup` to a valid IPv4 multicast address (224.0.0.0/4).
The `Interface` parameter is **mandatory** and specifies the network interface to bind.
The server opens a TCP listener on `Port` for control traffic and a UDP socket aimed at
`MulticastGroup:DataPort` for data traffic. The client:
1. Connects to `Port` via TCP and sends a CONNECT packet.
2. Receives the CONFIG packet over TCP.
3. Joins the multicast group (`MulticastGroup:DataPort`) to receive DATA packets.
Multiple clients may receive data simultaneously by joining the same group.
---
## Publishing Modes
### Strict (default)
Sends one DATA packet for every `Synchronise()` call (every RT cycle). Simplest and lowest
latency.
### Accumulate
Batches multiple RT-cycle snapshots into a single DATA packet. All signals (scalars and arrays)
are accumulated: one full snapshot per RT cycle. The batch is flushed when either:
- **Size condition**: adding one more sample would exceed `MaxPayloadSize`.
- **Time condition**: `1/MinRefreshRate` seconds have elapsed since the last flush.
The maximum batch count is computed automatically from `MaxPayloadSize` and the total wire size
of all signals. Scalar signals with `Unit="us"` or `"ns"` are auto-promoted as the per-sample
FullArray time reference for all other scalars.
Requires `MinRefreshRate` (Hz) to be set.
### Decimate
Sends one DATA packet every `Ratio` RT cycles, dropping intermediate cycles. Only the most
recent snapshot at the Nth cycle is sent.
Requires `Ratio` (≥ 1) to be set. `Ratio = 1` is equivalent to `Strict` mode (a warning is
logged).
---
## Broker
UDPStreamer uses `MemoryMapSynchronisedOutputBroker` for output signals. This broker is
@@ -151,7 +229,7 @@ PrepareNextState() ← opens UDP server socket, starts background threa
---
## Example: minimal scalar streaming
## Example: minimal scalar streaming (unicast)
```
+Data = {
@@ -168,6 +246,26 @@ PrepareNextState() ← opens UDP server socket, starts background threa
}
```
## Example: multicast with accumulation
```
+Streamer = {
Class = UDPStreamer
Port = 44500 // TCP control port
MulticastGroup = "239.0.0.1" // Enables multicast mode
Interface = "eth0" // Mandatory for multicast
DataPort = 44501 // UDP data port (default: Port+1)
MaxPayloadSize = 1400
PublishingMode = "Accumulate"
MinRefreshRate = 60.0 // Flush at least 60 times/s
Signals = {
Time = { Type = uint32; Unit = "us" }
Voltage = { Type = float32; Unit = "V"; RangeMin = -10.0; RangeMax = 10.0; QuantizedType = uint16 }
}
}
```
## Example: high-frequency burst
```
@@ -198,3 +296,19 @@ With `MaxPayloadSize = 1400`, a single 1000-element float32 signal produces:
payload = 8 B (HRT timestamp) + 4 B (T0/uint32) + 4000 B (float32×1000) = 4012 B
fragments = ceil(4012 / 1383) = 3
```
## Example: decimated output
```
+Streamer = {
Class = UDPStreamer
Port = 44500
PublishingMode = "Decimate"
Ratio = 10 // Send 1 packet every 10 RT cycles
Signals = {
Time = { Type = uint32; Unit = "us" }
Position = { Type = float64; Unit = "mm" }
}
}
```
+3 -2
View File
@@ -10,7 +10,7 @@ for control applications built with [MARTe2](https://vcis.f4e.europa.eu/marte2-d
This repository integrates two complementary capabilities:
| Capability | Component | Purpose |
|---|---|---|
| --------------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------- |
| **Signal streaming** | `UDPStreamer` DataSource | Continuously stream selected signals to a browser-based oscilloscope over UDP |
| **Signal debugging** | `DebugService` Interface | On-demand signal tracing, value forcing, and conditional breakpoints — zero application code changes required |
| **Sine generation** | `SineArrayGAM` | Generate continuous sine-wave arrays for testing and simulation |
@@ -81,6 +81,7 @@ Instruments a running MARTe2 application **without modifying its source code**.
afterward the application transparently uses the wrapped brokers.
Capabilities accessible over TCP (port 8080 by default):
- `DISCOVER` — enumerate all signals with type and alias metadata
- `TRACE` — enable/disable high-speed UDP telemetry per signal (with decimation)
- `FORCE` / `UNFORCE` — inject persistent values into signals on the RT path
@@ -225,7 +226,7 @@ Open `http://localhost:9090`, explore the object tree, trace signals, force valu
## Documentation
| Document | Contents |
|---|---|
| ----------------------------- | -------------------------------------------------------------- |
| `Docs/Protocol.md` | UDPS binary wire protocol specification |
| `Docs/UDPStreamer.md` | UDPStreamer DataSource configuration reference |
| `Docs/SineArrayGAM.md` | SineArrayGAM configuration reference |
@@ -21,6 +21,8 @@
* methods, such as those inline could be defined on the header file, instead.
*/
#include "ErrorType.h"
#include "StreamString.h"
#define DLL_API
/*---------------------------------------------------------------------------*/
@@ -37,10 +39,7 @@
#include "EmbeddedThreadI.h"
#include "GlobalObjectsDatabase.h"
#include "HighResolutionTimer.h"
#include "MemoryMapSynchronisedOutputBroker.h"
#include "MemoryOperationsHelper.h"
#include "Sleep.h"
#include "Threads.h"
#include "UDPStreamer.h"
/*---------------------------------------------------------------------------*/
@@ -52,7 +51,8 @@ namespace MARTe {
/** Default port used when none is specified. */
static const uint16 UDPS_DEFAULT_PORT = 44500u;
/** Default data port offset: dataPort = port + this value when DataPort is not specified. */
/** Default data port offset: dataPort = port + this value when DataPort is not
* specified. */
static const uint16 UDPS_DEFAULT_DATA_PORT_OFFSET = 1u;
/** Maximum pending TCP connections on the listener backlog. */
@@ -80,10 +80,8 @@ static const uint32 UDPS_TIMESTAMP_BYTES = 8u;
/* Method definitions */
/*---------------------------------------------------------------------------*/
UDPStreamer::UDPStreamer() :
MemoryDataSourceI(),
EmbeddedServiceMethodBinderI(),
executor(*this) {
UDPStreamer::UDPStreamer()
: MemoryDataSourceI(), EmbeddedServiceMethodBinderI(), executor(*this) {
port = UDPS_DEFAULT_PORT;
maxPayloadSize = UDPS_DEFAULT_MAX_PAYLOAD;
cpuMask = 0xFFFFFFFFu;
@@ -231,15 +229,13 @@ bool UDPStreamer::Initialise(StructuredDataI &data) {
(void)data.Read("PublishingMode", publishStr);
if ((publishStr.Size() == 0u) || (publishStr == "Strict")) {
publishMode = UDPStreamerPublishStrict;
}
else if (publishStr == "Accumulate") {
} else if (publishStr == "Accumulate") {
publishMode = UDPStreamerPublishAccumulate;
}
else if (publishStr == "Decimate") {
} else if (publishStr == "Decimate") {
publishMode = UDPStreamerPublishDecimate;
}
else {
REPORT_ERROR(ErrorManagement::ParametersError,
} else {
REPORT_ERROR(
ErrorManagement::ParametersError,
"Unknown PublishingMode '%s'. Allowed: Strict|Accumulate|Decimate.",
publishStr.Buffer());
ok = false;
@@ -250,18 +246,19 @@ bool UDPStreamer::Initialise(StructuredDataI &data) {
/* MinRefreshRate controls the time-based flush: flush when
* (now - lastPublishTs) >= flushPeriodTicks, or when adding one more
* sample would overflow MaxPayloadSize. Whichever fires first. */
if (!data.Read("MinRefreshRate", minRefreshRate) || (minRefreshRate <= 0.0)) {
REPORT_ERROR(ErrorManagement::ParametersError,
if (!data.Read("MinRefreshRate", minRefreshRate) ||
(minRefreshRate <= 0.0)) {
REPORT_ERROR(
ErrorManagement::ParametersError,
"MinRefreshRate > 0 is required when PublishingMode = Accumulate.");
ok = false;
}
else {
} else {
float64 hrtFreq = static_cast<float64>(HighResolutionTimer::Frequency());
flushPeriodTicks = static_cast<uint64>(hrtFreq / minRefreshRate);
REPORT_ERROR(ErrorManagement::Information,
REPORT_ERROR(
ErrorManagement::Information,
"Accumulate mode: MinRefreshRate=%.1f Hz, flushPeriodTicks=%llu.",
minRefreshRate,
static_cast<unsigned long long>(flushPeriodTicks));
minRefreshRate, static_cast<unsigned long long>(flushPeriodTicks));
}
}
@@ -272,11 +269,11 @@ bool UDPStreamer::Initialise(StructuredDataI &data) {
REPORT_ERROR(ErrorManagement::ParametersError,
"Ratio >= 1 is required when PublishingMode = Decimate.");
ok = false;
}
else {
} else {
decimateRatio = ratio;
if (decimateRatio == 1u) {
REPORT_ERROR(ErrorManagement::Warning,
REPORT_ERROR(
ErrorManagement::Warning,
"Decimate mode with Ratio=1 is equivalent to Strict mode.");
}
REPORT_ERROR(ErrorManagement::Information,
@@ -299,6 +296,14 @@ bool UDPStreamer::Initialise(StructuredDataI &data) {
if (data.Read("DataPort", dp)) {
(void)serverCfg.Write("DataPort", dp);
}
StreamString iface;
if (data.Read("Interface", iface)) {
(void)serverCfg.Write("Interface", iface);
} else {
ok = false;
REPORT_ERROR(ErrorManagement::InitialisationError,
"Missing mandatory interface for multicasting");
}
}
uint32 clientTimeout = 0u;
if (data.Read("ClientTimeout", clientTimeout)) {
@@ -407,20 +412,15 @@ bool UDPStreamer::SetConfiguredDatabase(StructuredDataI &data) {
if (signalsDatabase.Read("QuantizedType", quantStr)) {
if (quantStr == "uint8") {
signalInfos[i].quantType = UDPStreamerQuantUint8;
}
else if (quantStr == "int8") {
} else if (quantStr == "int8") {
signalInfos[i].quantType = UDPStreamerQuantInt8;
}
else if (quantStr == "uint16") {
} else if (quantStr == "uint16") {
signalInfos[i].quantType = UDPStreamerQuantUint16;
}
else if (quantStr == "int16") {
} else if (quantStr == "int16") {
signalInfos[i].quantType = UDPStreamerQuantInt16;
}
else if (quantStr == "none") {
} else if (quantStr == "none") {
signalInfos[i].quantType = UDPStreamerQuantNone;
}
else {
} else {
REPORT_ERROR(ErrorManagement::ParametersError,
"Signal %s: unknown QuantizedType '%s'. "
"Allowed: none|uint8|int8|uint16|int16.",
@@ -449,17 +449,13 @@ bool UDPStreamer::SetConfiguredDatabase(StructuredDataI &data) {
}
if (timeModeStr == "PacketTime") {
signalInfos[i].timeMode = UDPStreamerTimePacket;
}
else if (timeModeStr == "FullArray") {
} else if (timeModeStr == "FullArray") {
signalInfos[i].timeMode = UDPStreamerTimeFullArray;
}
else if (timeModeStr == "FirstSample") {
} else if (timeModeStr == "FirstSample") {
signalInfos[i].timeMode = UDPStreamerTimeFirstSample;
}
else if (timeModeStr == "LastSample") {
} else if (timeModeStr == "LastSample") {
signalInfos[i].timeMode = UDPStreamerTimeLastSample;
}
else {
} else {
REPORT_ERROR(ErrorManagement::ParametersError,
"Signal %s: unknown TimeMode '%s'. "
"Allowed: PacketTime|FullArray|FirstSample|LastSample.",
@@ -477,8 +473,7 @@ bool UDPStreamer::SetConfiguredDatabase(StructuredDataI &data) {
"TimeMode != PacketTime.",
signalInfos[i].name.Buffer());
ok = false;
}
else {
} else {
timeSignalNames[i] = tsName;
/* Index resolved in pass 3 */
signalInfos[i].timeSignalIdx = UDPS_NO_TIME_SIGNAL;
@@ -520,10 +515,10 @@ bool UDPStreamer::SetConfiguredDatabase(StructuredDataI &data) {
}
}
if (!found) {
REPORT_ERROR(ErrorManagement::ParametersError,
REPORT_ERROR(
ErrorManagement::ParametersError,
"Signal %s: TimeSignal '%s' not found among declared signals.",
signalInfos[i].name.Buffer(),
timeSignalNames[i].Buffer());
signalInfos[i].name.Buffer(), timeSignalNames[i].Buffer());
ok = false;
}
}
@@ -567,12 +562,11 @@ bool UDPStreamer::SetConfiguredDatabase(StructuredDataI &data) {
"Signal %s: FullArray TimeMode requires TimeSignal "
"%s to have the same NumberOfElements (%u vs %u).",
signalInfos[i].name.Buffer(),
signalInfos[tsIdx].name.Buffer(),
tsElems, signalInfos[i].numElements);
signalInfos[tsIdx].name.Buffer(), tsElems,
signalInfos[i].numElements);
ok = false;
}
}
else if ((signalInfos[i].timeMode == UDPStreamerTimeFirstSample) ||
} else if ((signalInfos[i].timeMode == UDPStreamerTimeFirstSample) ||
(signalInfos[i].timeMode == UDPStreamerTimeLastSample)) {
if (tsElems != 1u) {
REPORT_ERROR(ErrorManagement::ParametersError,
@@ -605,9 +599,11 @@ bool UDPStreamer::SetConfiguredDatabase(StructuredDataI &data) {
*/
if (ok && (publishMode == UDPStreamerPublishAccumulate)) {
/* Find primary time signal: prefer Unit="us"/"ns", fall back to first integer scalar */
/* Find primary time signal: prefer Unit="us"/"ns", fall back to first
* integer scalar */
uint32 primaryTsIdx = UDPS_NO_TIME_SIGNAL;
for (uint32 i = 0u; i < numSigs && (primaryTsIdx == UDPS_NO_TIME_SIGNAL); i++) {
for (uint32 i = 0u; i < numSigs && (primaryTsIdx == UDPS_NO_TIME_SIGNAL);
i++) {
if (signalInfos[i].numElements == 1u) {
if ((signalInfos[i].unit == "us") || (signalInfos[i].unit == "ns")) {
primaryTsIdx = i;
@@ -615,7 +611,8 @@ bool UDPStreamer::SetConfiguredDatabase(StructuredDataI &data) {
}
}
if (primaryTsIdx == UDPS_NO_TIME_SIGNAL) {
for (uint32 i = 0u; i < numSigs && (primaryTsIdx == UDPS_NO_TIME_SIGNAL); i++) {
for (uint32 i = 0u; i < numSigs && (primaryTsIdx == UDPS_NO_TIME_SIGNAL);
i++) {
if (signalInfos[i].numElements == 1u) {
TypeDescriptor td = signalInfos[i].type;
if ((td == UnsignedInteger32Bit) || (td == UnsignedInteger64Bit) ||
@@ -639,8 +636,8 @@ bool UDPStreamer::SetConfiguredDatabase(StructuredDataI &data) {
signalInfos[i].accumulated = true;
singleCycleWireBytes += signalInfos[i].wireByteSize;
/* Auto-assign time reference for non-primary, non-time scalars */
if ((signalInfos[i].numElements == 1u) &&
(i != primaryTsIdx) && (primaryTsIdx != UDPS_NO_TIME_SIGNAL) &&
if ((signalInfos[i].numElements == 1u) && (i != primaryTsIdx) &&
(primaryTsIdx != UDPS_NO_TIME_SIGNAL) &&
(signalInfos[i].timeMode == UDPStreamerTimePacket)) {
signalInfos[i].timeMode = UDPStreamerTimeFullArray;
signalInfos[i].timeSignalIdx = primaryTsIdx;
@@ -655,13 +652,13 @@ bool UDPStreamer::SetConfiguredDatabase(StructuredDataI &data) {
if (ok) {
/* DATA payload: [8 HRT][4 numSamples][numSamples × singleCycle] */
static const uint32 ACCUM_HEADER = UDPS_TIMESTAMP_BYTES + 4u; /* 12 bytes */
static const uint32 ACCUM_HEADER =
UDPS_TIMESTAMP_BYTES + 4u; /* 12 bytes */
if ((ACCUM_HEADER + singleCycleWireBytes) > maxPayloadSize) {
REPORT_ERROR(ErrorManagement::ParametersError,
"Accumulate mode: even a single sample (%u B) exceeds "
"MaxPayloadSize (%u B).",
ACCUM_HEADER + singleCycleWireBytes,
maxPayloadSize);
ACCUM_HEADER + singleCycleWireBytes, maxPayloadSize);
ok = false;
}
}
@@ -674,8 +671,8 @@ bool UDPStreamer::SetConfiguredDatabase(StructuredDataI &data) {
REPORT_ERROR(ErrorManagement::Information,
"Accumulate mode: singleCycleWireBytes=%u, "
"maxBatchCount=%u, maxPayloadSize=%u, totalWireBytes=%u.",
singleCycleWireBytes,
maxBatchCount, maxPayloadSize, totalWireBytes);
singleCycleWireBytes, maxBatchCount, maxPayloadSize,
totalWireBytes);
}
}
@@ -695,15 +692,18 @@ bool UDPStreamer::AllocateMemory() {
HeapI *heap = GlobalObjectsDatabase::Instance()->GetStandardHeap();
/* In Accumulate mode, readyBuffer / scratchBuffer hold maxBatchCount consecutive
* snapshots instead of a single one. */
/* HI-3: use 64-bit arithmetic to prevent overflow in maxBatchCount * totalSrcBytes */
/* In Accumulate mode, readyBuffer / scratchBuffer hold maxBatchCount
* consecutive snapshots instead of a single one. */
/* HI-3: use 64-bit arithmetic to prevent overflow in maxBatchCount *
* totalSrcBytes */
uint64 readyBufSize64 = (maxBatchCount > 0u)
? (static_cast<uint64>(maxBatchCount) * static_cast<uint64>(totalSrcBytes))
? (static_cast<uint64>(maxBatchCount) *
static_cast<uint64>(totalSrcBytes))
: static_cast<uint64>(totalSrcBytes);
if (readyBufSize64 > 0xFFFFFFFFu) {
REPORT_ERROR(ErrorManagement::FatalError,
"Accumulate buffer size overflow (maxBatchCount=%u * totalSrcBytes=%u).",
"Accumulate buffer size overflow (maxBatchCount=%u * "
"totalSrcBytes=%u).",
maxBatchCount, totalSrcBytes);
return false;
}
@@ -712,7 +712,8 @@ bool UDPStreamer::AllocateMemory() {
/* readyBuffer: copy of signal memory shared with background thread */
readyBuffer = reinterpret_cast<uint8 *>(heap->Malloc(readyBufSize));
if (readyBuffer == NULL_PTR(uint8 *)) {
REPORT_ERROR(ErrorManagement::FatalError, "Could not allocate readyBuffer.");
REPORT_ERROR(ErrorManagement::FatalError,
"Could not allocate readyBuffer.");
return false;
}
(void)MemoryOperationsHelper::Set(readyBuffer, 0, readyBufSize);
@@ -720,7 +721,8 @@ bool UDPStreamer::AllocateMemory() {
/* scratchBuffer: background-thread-private copy for serialization */
scratchBuffer = reinterpret_cast<uint8 *>(heap->Malloc(readyBufSize));
if (scratchBuffer == NULL_PTR(uint8 *)) {
REPORT_ERROR(ErrorManagement::FatalError, "Could not allocate scratchBuffer.");
REPORT_ERROR(ErrorManagement::FatalError,
"Could not allocate scratchBuffer.");
return false;
}
(void)MemoryOperationsHelper::Set(scratchBuffer, 0, readyBufSize);
@@ -744,11 +746,13 @@ bool UDPStreamer::AllocateMemory() {
/* --- Accumulate-mode extra buffers --- */
if (maxBatchCount > 0u) {
/* Linear fill buffer: RT thread writes one snapshot per slot (0..maxBatchCount-1) */
/* Linear fill buffer: RT thread writes one snapshot per slot
* (0..maxBatchCount-1) */
uint32 accumBufSize = maxBatchCount * totalSrcBytes;
accumBuffer = reinterpret_cast<uint8 *>(heap->Malloc(accumBufSize));
if (accumBuffer == NULL_PTR(uint8 *)) {
REPORT_ERROR(ErrorManagement::FatalError, "Could not allocate accumBuffer.");
REPORT_ERROR(ErrorManagement::FatalError,
"Could not allocate accumBuffer.");
return false;
}
(void)MemoryOperationsHelper::Set(accumBuffer, 0, accumBufSize);
@@ -776,7 +780,8 @@ bool UDPStreamer::AllocateMemory() {
readyFill = 0u;
REPORT_ERROR(ErrorManagement::Information,
"Accumulate buffers: maxBatchCount=%u, accumBufSize=%u B, readyBufSize=%u B.",
"Accumulate buffers: maxBatchCount=%u, accumBufSize=%u B, "
"readyBufSize=%u B.",
maxBatchCount, accumBufSize, readyBufSize);
}
@@ -799,7 +804,8 @@ bool UDPStreamer::PrepareNextState(const char8 *const currentStateName,
ok = server.Start();
/* Build the CONFIG payload and cache it in the server so any CONNECT client
* receives it immediately. The config is static for the lifetime of this state. */
* receives it immediately. The config is static for the lifetime of this
* state. */
if (ok) {
uint32 configBufSize = 4u + (numSigs * UDPS_SIGNAL_DESC_SIZE) + 32u + 1u;
HeapI *heap = GlobalObjectsDatabase::Instance()->GetStandardHeap();
@@ -808,14 +814,12 @@ bool UDPStreamer::PrepareNextState(const char8 *const currentStateName,
uint32 cfgPayloadSize = 0u;
if (BuildConfigPayload(cfgBuf, configBufSize, cfgPayloadSize)) {
(void)server.SendConfig(cfgBuf, cfgPayloadSize);
}
else {
} else {
REPORT_ERROR(ErrorManagement::Warning,
"Could not build initial CONFIG payload.");
}
heap->Free(reinterpret_cast<void *&>(cfgBuf));
}
else {
} else {
REPORT_ERROR(ErrorManagement::Warning,
"Could not allocate CONFIG buffer.");
}
@@ -867,8 +871,8 @@ bool UDPStreamer::Synchronise() {
/* HI-3: if accumFill reached maxBatchCount, force-flush before writing */
if (accumFill >= maxBatchCount) {
uint32 filled = accumFill;
(void) MemoryOperationsHelper::Copy(
readyBuffer, accumBuffer, filled * totalSrcBytes);
(void)MemoryOperationsHelper::Copy(readyBuffer, accumBuffer,
filled * totalSrcBytes);
(void)MemoryOperationsHelper::Copy(
reinterpret_cast<uint8 *>(readyTimestamps),
reinterpret_cast<const uint8 *>(accumTimestamps),
@@ -887,7 +891,8 @@ bool UDPStreamer::Synchronise() {
uint32 filled = accumFill;
bufMutex.FastUnLock();
/* Check flush conditions (volatile read of lastPublishTs is safe on x86). */
/* Check flush conditions (volatile read of lastPublishTs is safe on x86).
*/
static const uint32 ACCUM_HEADER = UDPS_TIMESTAMP_BYTES + 4u; /* 12 bytes */
uint32 curPayload = ACCUM_HEADER + filled * singleCycleWireBytes;
uint32 nextPayload = curPayload + singleCycleWireBytes;
@@ -896,8 +901,8 @@ bool UDPStreamer::Synchronise() {
if (sizeCondition || timeCondition) {
bufMutex.FastLock(TTInfiniteWait);
(void) MemoryOperationsHelper::Copy(
readyBuffer, accumBuffer, filled * totalSrcBytes);
(void)MemoryOperationsHelper::Copy(readyBuffer, accumBuffer,
filled * totalSrcBytes);
(void)MemoryOperationsHelper::Copy(
reinterpret_cast<uint8 *>(readyTimestamps),
reinterpret_cast<const uint8 *>(accumTimestamps),
@@ -910,8 +915,7 @@ bool UDPStreamer::Synchronise() {
lastPublishTs = ts;
(void)dataSem.Post();
}
}
else if (publishMode == UDPStreamerPublishDecimate) {
} else if (publishMode == UDPStreamerPublishDecimate) {
/* --- Decimate path ---
* Post dataSem only every decimateRatio calls. */
decimateCounter++;
@@ -923,8 +927,7 @@ bool UDPStreamer::Synchronise() {
bufMutex.FastUnLock();
(void)dataSem.Post();
}
}
else {
} else {
/* --- Strict path: post every call --- */
bufMutex.FastLock(TTInfiniteWait);
(void)MemoryOperationsHelper::Copy(readyBuffer, memory, totalSrcBytes);
@@ -941,8 +944,11 @@ ErrorManagement::ErrorType UDPStreamer::Execute(ExecutionInfo &info) {
if (info.GetStage() == ExecutionInfo::StartupStage) {
const char8 *modeStr = "Strict";
if (publishMode == UDPStreamerPublishAccumulate) { modeStr = "Accumulate"; }
else if (publishMode == UDPStreamerPublishDecimate) { modeStr = "Decimate"; }
if (publishMode == UDPStreamerPublishAccumulate) {
modeStr = "Accumulate";
} else if (publishMode == UDPStreamerPublishDecimate) {
modeStr = "Decimate";
}
REPORT_ERROR(ErrorManagement::Information,
"UDPStreamer background thread started (port %u, mode %s).",
static_cast<uint32>(port), modeStr);
@@ -960,7 +966,8 @@ ErrorManagement::ErrorType UDPStreamer::Execute(ExecutionInfo &info) {
dataSem.ResetWait(TimeoutType(UDPS_DATA_WAIT_MS));
bool dataReady = (waitErr == ErrorManagement::NoError);
/* --- Poll for incoming control commands (CONNECT / DISCONNECT / ACK) --- */
/* --- Poll for incoming control commands (CONNECT / DISCONNECT / ACK) ---
*/
server.ServiceClients();
if (dataReady && server.HasClients()) {
@@ -973,8 +980,8 @@ ErrorManagement::ErrorType UDPStreamer::Execute(ExecutionInfo &info) {
bufMutex.FastLock(TTInfiniteWait);
fill = readyFill;
if (fill > 0u) {
(void) MemoryOperationsHelper::Copy(
scratchBuffer, readyBuffer, fill * totalSrcBytes);
(void)MemoryOperationsHelper::Copy(scratchBuffer, readyBuffer,
fill * totalSrcBytes);
(void)MemoryOperationsHelper::Copy(
reinterpret_cast<uint8 *>(scratchTimestamps),
reinterpret_cast<const uint8 *>(readyTimestamps),
@@ -984,8 +991,8 @@ ErrorManagement::ErrorType UDPStreamer::Execute(ExecutionInfo &info) {
if (fill > 0u) {
SerializeAccumulated(scratchBuffer, scratchTimestamps, fill);
uint32 sendBytes = UDPS_TIMESTAMP_BYTES + 4u +
fill * singleCycleWireBytes;
uint32 sendBytes =
UDPS_TIMESTAMP_BYTES + 4u + fill * singleCycleWireBytes;
packetCounter++;
if (!server.SendData(packetCounter, wireBuffer, sendBytes)) {
REPORT_ERROR(ErrorManagement::Warning,
@@ -993,13 +1000,12 @@ ErrorManagement::ErrorType UDPStreamer::Execute(ExecutionInfo &info) {
packetCounter);
}
}
}
else {
} else {
/* --- Single-snapshot send (Strict or Decimate) --- */
uint64 ts = 0u;
bufMutex.FastLock(TTInfiniteWait);
(void) MemoryOperationsHelper::Copy(
scratchBuffer, readyBuffer, totalSrcBytes);
(void)MemoryOperationsHelper::Copy(scratchBuffer, readyBuffer,
totalSrcBytes);
ts = syncTimestamp;
bufMutex.FastUnLock();
@@ -1036,8 +1042,10 @@ void UDPStreamer::SerializeAccumulated(const uint8 *src,
*/
uint8 *dst = wireBuffer;
/* 8-byte packet-level HRT timestamp = timestamp of the first (oldest) sample */
(void) MemoryOperationsHelper::Copy(dst, &timestamps[0u], UDPS_TIMESTAMP_BYTES);
/* 8-byte packet-level HRT timestamp = timestamp of the first (oldest) sample
*/
(void)MemoryOperationsHelper::Copy(dst, &timestamps[0u],
UDPS_TIMESTAMP_BYTES);
dst += UDPS_TIMESTAMP_BYTES;
/* 4-byte sample count */
@@ -1049,17 +1057,20 @@ void UDPStreamer::SerializeAccumulated(const uint8 *src,
const bool isSrcFloat32 = (signalInfos[i].type == Float32Bit);
const float64 rMin = signalInfos[i].rangeMin;
float64 rRange = signalInfos[i].rangeMax - rMin;
if (rRange == 0.0) { rRange = 1.0; }
if (rRange == 0.0) {
rRange = 1.0;
}
/* Pack one snapshot (all elements) from each slot, in order */
for (uint32 k = 0u; k < numSamples; k++) {
const uint8 *slotSrc = src + (k * totalSrcBytes) + signalInfos[i].bufferOffset;
const uint8 *slotSrc =
src + (k * totalSrcBytes) + signalInfos[i].bufferOffset;
if (signalInfos[i].quantType == UDPStreamerQuantNone) {
(void) MemoryOperationsHelper::Copy(dst, slotSrc, signalInfos[i].srcByteSize);
(void)MemoryOperationsHelper::Copy(dst, slotSrc,
signalInfos[i].srcByteSize);
dst += signalInfos[i].srcByteSize;
}
else {
} else {
const uint8 *s = slotSrc;
for (uint32 e = 0u; e < nelems; e++) {
float64 rawVal = 0.0;
@@ -1068,18 +1079,22 @@ void UDPStreamer::SerializeAccumulated(const uint8 *src,
(void)MemoryOperationsHelper::Copy(&f32, s, 4u);
rawVal = static_cast<float64>(f32);
s += 4u;
}
else {
} else {
(void)MemoryOperationsHelper::Copy(&rawVal, s, 8u);
s += 8u;
}
float64 norm = (rawVal - rMin) / rRange;
if (norm < 0.0) { norm = 0.0; }
if (norm > 1.0) { norm = 1.0; }
if (norm < 0.0) {
norm = 0.0;
}
if (norm > 1.0) {
norm = 1.0;
}
switch (signalInfos[i].quantType) {
case UDPStreamerQuantUint8: {
uint8 q = static_cast<uint8>(norm * 255.0);
*dst = q; dst += 1u;
*dst = q;
dst += 1u;
break;
}
case UDPStreamerQuantInt8: {
@@ -1109,9 +1124,7 @@ void UDPStreamer::SerializeAccumulated(const uint8 *src,
}
}
bool UDPStreamer::BuildConfigPayload(uint8 *buf,
uint32 bufSize,
bool UDPStreamer::BuildConfigPayload(uint8 *buf, uint32 bufSize,
uint32 &payloadSize) {
payloadSize = 0u;
@@ -1135,7 +1148,8 @@ bool UDPStreamer::BuildConfigPayload(uint8 *buf,
if (nameLen >= UDPS_MAX_SIGNAL_NAME) {
nameLen = UDPS_MAX_SIGNAL_NAME - 1u;
}
(void) MemoryOperationsHelper::Copy(p, signalInfos[i].name.Buffer(), nameLen);
(void)MemoryOperationsHelper::Copy(p, signalInfos[i].name.Buffer(),
nameLen);
p += UDPS_MAX_SIGNAL_NAME;
/* Type code: 1 byte */
@@ -1184,7 +1198,8 @@ bool UDPStreamer::BuildConfigPayload(uint8 *buf,
if (unitLen >= UDPS_MAX_UNIT_LEN) {
unitLen = UDPS_MAX_UNIT_LEN - 1u;
}
(void) MemoryOperationsHelper::Copy(p, signalInfos[i].unit.Buffer(), unitLen);
(void)MemoryOperationsHelper::Copy(p, signalInfos[i].unit.Buffer(),
unitLen);
p += UDPS_MAX_UNIT_LEN;
payloadSize += UDPS_SIGNAL_DESC_SIZE;
@@ -1214,8 +1229,7 @@ void UDPStreamer::QuantizeAndSerialize(const uint8 *srcBuf, uint64 timestamp) {
/* Raw copy */
(void)MemoryOperationsHelper::Copy(dst, src, signalInfos[i].srcByteSize);
dst += signalInfos[i].srcByteSize;
}
else {
} else {
float64 rMin = signalInfos[i].rangeMin;
float64 rRange = signalInfos[i].rangeMax - rMin;
if (rRange == 0.0) {
@@ -1232,16 +1246,19 @@ void UDPStreamer::QuantizeAndSerialize(const uint8 *srcBuf, uint64 timestamp) {
(void)MemoryOperationsHelper::Copy(&f32, s, 4u);
rawVal = static_cast<float64>(f32);
s += 4u;
}
else {
} else {
(void)MemoryOperationsHelper::Copy(&rawVal, s, 8u);
s += 8u;
}
/* Normalize and clamp to [0.0, 1.0] */
float64 norm = (rawVal - rMin) / rRange;
if (norm < 0.0) { norm = 0.0; }
if (norm > 1.0) { norm = 1.0; }
if (norm < 0.0) {
norm = 0.0;
}
if (norm > 1.0) {
norm = 1.0;
}
switch (signalInfos[i].quantType) {
case UDPStreamerQuantUint8: {
@@ -1278,34 +1295,37 @@ void UDPStreamer::QuantizeAndSerialize(const uint8 *srcBuf, uint64 timestamp) {
uint8 UDPStreamer::TypeDescriptorToCode(TypeDescriptor td) {
uint8 code = UDPS_TYPECODE_UNKNOWN;
if (td == UnsignedInteger8Bit) { code = UDPS_TYPECODE_UINT8; }
else if (td == SignedInteger8Bit) { code = UDPS_TYPECODE_INT8; }
else if (td == UnsignedInteger16Bit) { code = UDPS_TYPECODE_UINT16; }
else if (td == SignedInteger16Bit) { code = UDPS_TYPECODE_INT16; }
else if (td == UnsignedInteger32Bit) { code = UDPS_TYPECODE_UINT32; }
else if (td == SignedInteger32Bit) { code = UDPS_TYPECODE_INT32; }
else if (td == UnsignedInteger64Bit) { code = UDPS_TYPECODE_UINT64; }
else if (td == SignedInteger64Bit) { code = UDPS_TYPECODE_INT64; }
else if (td == Float32Bit) { code = UDPS_TYPECODE_FLOAT32; }
else if (td == Float64Bit) { code = UDPS_TYPECODE_FLOAT64; }
if (td == UnsignedInteger8Bit) {
code = UDPS_TYPECODE_UINT8;
} else if (td == SignedInteger8Bit) {
code = UDPS_TYPECODE_INT8;
} else if (td == UnsignedInteger16Bit) {
code = UDPS_TYPECODE_UINT16;
} else if (td == SignedInteger16Bit) {
code = UDPS_TYPECODE_INT16;
} else if (td == UnsignedInteger32Bit) {
code = UDPS_TYPECODE_UINT32;
} else if (td == SignedInteger32Bit) {
code = UDPS_TYPECODE_INT32;
} else if (td == UnsignedInteger64Bit) {
code = UDPS_TYPECODE_UINT64;
} else if (td == SignedInteger64Bit) {
code = UDPS_TYPECODE_INT64;
} else if (td == Float32Bit) {
code = UDPS_TYPECODE_FLOAT32;
} else if (td == Float64Bit) {
code = UDPS_TYPECODE_FLOAT64;
}
return code;
}
uint16 UDPStreamer::GetPort() const {
return port;
}
uint16 UDPStreamer::GetPort() const { return port; }
uint32 UDPStreamer::GetMaxPayloadSize() const {
return maxPayloadSize;
}
uint32 UDPStreamer::GetMaxPayloadSize() const { return maxPayloadSize; }
bool UDPStreamer::IsClientConnected() const {
return server.HasClients();
}
bool UDPStreamer::IsClientConnected() const { return server.HasClients(); }
bool UDPStreamer::IsMulticast() const {
return server.IsMulticast();
}
bool UDPStreamer::IsMulticast() const { return server.IsMulticast(); }
CLASS_REGISTER(UDPStreamer, "1.0")
@@ -141,9 +141,10 @@ struct UDPStreamerSignalInfo {
*
* @par Top-level configuration parameters
* | Parameter | Type | Default | Description |
* |-----------------|---------|---------|-------------|
* |-----------------|---------|------------------|-------------|
* | Port | uint16 | 44500 | TCP control port (multicast) or UDP server port (unicast). Values ≤ 1024 produce a warning. |
* | MulticastGroup | string | *(absent)* | **Enables multicast mode.** IPv4 multicast address, e.g. `"239.0.0.1"`. Must be in 224.0.0.0/4. Absent or empty = unicast. |
* | Interface | string | *(absent)* | Multicast binded interface **ONLY FOR MULTICAST** |
* | DataPort | uint16 | Port+1 | UDP port for multicast DATA datagrams. Ignored in unicast mode. Must be non-zero and differ from Port. |
* | MaxPayloadSize | uint32 | 1400 | Maximum bytes of signal payload per UDP datagram (excluding the 17-byte header). Larger signals are fragmented. |
* | PublishingMode | string | Strict | `Strict`: send one packet every Synchronise() call. `Auto`: rate-limited; flush only when MinRefreshRate interval has elapsed. |
@@ -1,48 +1,48 @@
../../../..//Build/x86-linux/Components/DataSources/UDPStreamer/UDPStreamer.o: UDPStreamer.cpp \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/AdvancedErrorManagement.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ClassProperties.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/TypeCharacteristics.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/ErrorType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/CompilerTypes.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Architecture/x86_gcc/CompilerTypes.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/FractionalInteger.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/TypeCharacteristics.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitBoolean.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/TemplateParametersVerificator.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/CompilerTypes.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitRange.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/TypeCharacteristics.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/StreamString.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/TypeCharacteristics.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HeapManager.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HeapI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/ErrorManagement.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/ErrorInformation.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/ErrorType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitBoolean.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitRange.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StreamI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TimeoutType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HighResolutionTimer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TimeStamp.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Architecture/x86_gcc/HighResolutionTimerA.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/CompilerTypes.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/HighResolutionTimer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../../HighResolutionTimer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/HighResolutionTimerCalibrator.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../../GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TimeStamp.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/StreamMemoryReference.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/CharBuffer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitBoolean.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/ErrorManagement.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/BufferedStreamI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TimeoutType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/AnyType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ClassProperties.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/FractionalInteger.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ClassRegistryDatabase.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/GlobalObjectsDatabase.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/GlobalObjectI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/StandardHeap.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../../HeapI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../../ErrorManagement.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../../GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../Generic/StandardHeap_Generic.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/FastPollingMutexSem.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Atomic.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Architecture/x86_gcc/AtomicA.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/ErrorManagement.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Sleep.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ClassRegistryItem.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/CString.h \
@@ -53,7 +53,6 @@
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BasicType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/FractionalInteger.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitRange.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitBoolean.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/ZeroTerminatedArray.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/LinkedListable.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/LinkedListHolderT.h \
@@ -70,17 +69,18 @@
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StaticListHolder.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Matrix.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Vector.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HeapManager.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HeapI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/MemoryOperationsHelper.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/FormatDescriptor.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/IOBuffer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/CharBuffer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StreamI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StringHelper.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/StreamStringIOBuffer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/AdvancedErrorManagement.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ClassProperties.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/StreamMemoryReference.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Configuration/ConfigurationDatabase.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Configuration/AnyObject.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/Object.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StringHelper.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/StructuredDataI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/AnyType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/CLASSREGISTER.h \
@@ -104,8 +104,6 @@
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/StructuredDataI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Configuration/TypeConversion.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Vector.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/StreamString.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/StreamStringIOBuffer.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/EmbeddedThreadI.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/EmbeddedServiceMethodBinderI.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/ExecutionInfo.h \
@@ -116,18 +114,12 @@
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitSet.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/EmbeddedServiceMethodBinderT.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HighResolutionTimer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/MemoryMapSynchronisedOutputBroker.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/MemoryMapOutputBroker.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/MemoryMapBroker.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/BrokerI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/DataSourceI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/StatefulI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/ExecutableI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Sleep.h \
UDPStreamer.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/EmbeddedServiceMethodBinderI.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L1Portability/EventSem.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/MemoryDataSourceI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/DataSourceI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/StatefulI.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/SingleThreadService.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/EmbeddedServiceI.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/EmbeddedThreadI.h \
@@ -141,5 +133,6 @@
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HandleI.h \
/home/martino/workspace/MARTe2/Source/Core/FileSystem/L1Portability/Environment/Linux/SocketCore.h \
/home/martino/workspace/MARTe2/Source/Core/FileSystem/L1Portability/BasicUDPSocket.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Sleep.h \
/home/martino/workspace/MARTe2/Source/Core/FileSystem/L1Portability/InternetHost.h \
../../../..//Common/UDP/UDPSProtocol.h
@@ -1,48 +1,48 @@
UDPStreamer.o: UDPStreamer.cpp \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/AdvancedErrorManagement.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ClassProperties.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/TypeCharacteristics.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/ErrorType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/CompilerTypes.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Architecture/x86_gcc/CompilerTypes.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/FractionalInteger.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/TypeCharacteristics.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitBoolean.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/TemplateParametersVerificator.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/CompilerTypes.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitRange.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/TypeCharacteristics.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/StreamString.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/TypeCharacteristics.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HeapManager.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HeapI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/ErrorManagement.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/ErrorInformation.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/ErrorType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitBoolean.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitRange.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StreamI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TimeoutType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HighResolutionTimer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TimeStamp.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Architecture/x86_gcc/HighResolutionTimerA.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/CompilerTypes.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/HighResolutionTimer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../../HighResolutionTimer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/HighResolutionTimerCalibrator.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../../GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TimeStamp.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/StreamMemoryReference.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/CharBuffer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitBoolean.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/ErrorManagement.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/BufferedStreamI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TimeoutType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/AnyType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ClassProperties.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/FractionalInteger.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ClassRegistryDatabase.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/GlobalObjectsDatabase.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/GlobalObjectI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/StandardHeap.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../../HeapI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../../ErrorManagement.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../../GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../Generic/StandardHeap_Generic.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/FastPollingMutexSem.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Atomic.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Architecture/x86_gcc/AtomicA.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/ErrorManagement.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Sleep.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ClassRegistryItem.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/CString.h \
@@ -53,7 +53,6 @@ UDPStreamer.o: UDPStreamer.cpp \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BasicType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/FractionalInteger.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitRange.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitBoolean.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/ZeroTerminatedArray.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/LinkedListable.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/LinkedListHolderT.h \
@@ -70,17 +69,18 @@ UDPStreamer.o: UDPStreamer.cpp \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StaticListHolder.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Matrix.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Vector.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HeapManager.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HeapI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/MemoryOperationsHelper.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/FormatDescriptor.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/IOBuffer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/CharBuffer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StreamI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StringHelper.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/StreamStringIOBuffer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/AdvancedErrorManagement.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ClassProperties.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/StreamMemoryReference.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Configuration/ConfigurationDatabase.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Configuration/AnyObject.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/Object.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StringHelper.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/StructuredDataI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/AnyType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/CLASSREGISTER.h \
@@ -104,8 +104,6 @@ UDPStreamer.o: UDPStreamer.cpp \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/StructuredDataI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Configuration/TypeConversion.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Vector.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/StreamString.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/StreamStringIOBuffer.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/EmbeddedThreadI.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/EmbeddedServiceMethodBinderI.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/ExecutionInfo.h \
@@ -116,18 +114,12 @@ UDPStreamer.o: UDPStreamer.cpp \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitSet.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/EmbeddedServiceMethodBinderT.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HighResolutionTimer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/MemoryMapSynchronisedOutputBroker.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/MemoryMapOutputBroker.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/MemoryMapBroker.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/BrokerI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/DataSourceI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/StatefulI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/ExecutableI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Sleep.h \
UDPStreamer.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/EmbeddedServiceMethodBinderI.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L1Portability/EventSem.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/MemoryDataSourceI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/DataSourceI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L5GAMs/StatefulI.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/SingleThreadService.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/EmbeddedServiceI.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/EmbeddedThreadI.h \
@@ -141,5 +133,6 @@ UDPStreamer.o: UDPStreamer.cpp \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HandleI.h \
/home/martino/workspace/MARTe2/Source/Core/FileSystem/L1Portability/Environment/Linux/SocketCore.h \
/home/martino/workspace/MARTe2/Source/Core/FileSystem/L1Portability/BasicUDPSocket.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Sleep.h \
/home/martino/workspace/MARTe2/Source/Core/FileSystem/L1Portability/InternetHost.h \
../../../..//Common/UDP/UDPSProtocol.h
@@ -55,6 +55,9 @@ static const uint16 UDPS_CLIENT_DEFAULT_DP_OFFSET = 1u;
/** Default max payload per UDP datagram (bytes). */
static const uint32 UDPS_CLIENT_DEFAULT_MAX_PAYLOAD = 1400u;
/** Default unicast keepalive interval (seconds); 0 disables. */
static const uint32 UDPS_CLIENT_DEFAULT_KEEPALIVE_INTERVAL_S = 15u;
/** Bytes prepended to each DATA payload for the HRT packet timestamp. */
static const uint32 UDPS_CLIENT_TIMESTAMP_BYTES = 8u;
@@ -129,6 +132,7 @@ UDPStreamerClient::UDPStreamerClient() :
serverAddress = UDPS_CLIENT_DEFAULT_ADDR;
port = UDPS_CLIENT_DEFAULT_PORT;
maxPayloadSize = UDPS_CLIENT_DEFAULT_MAX_PAYLOAD;
keepAliveInterval = UDPS_CLIENT_DEFAULT_KEEPALIVE_INTERVAL_S;
cpuMask = 0xFFFFFFFFu;
stackSize = THREADS_DEFAULT_STACKSIZE;
dataPort = UDPS_CLIENT_DEFAULT_PORT + UDPS_CLIENT_DEFAULT_DP_OFFSET;
@@ -201,6 +205,12 @@ bool UDPStreamerClient::Initialise(StructuredDataI &data) {
}
}
if (ok) {
if (!data.Read("KeepAliveInterval", keepAliveInterval)) {
keepAliveInterval = UDPS_CLIENT_DEFAULT_KEEPALIVE_INTERVAL_S;
}
}
if (ok) {
if (!data.Read("CPUMask", cpuMask)) {
cpuMask = 0xFFFFFFFFu;
@@ -245,6 +255,7 @@ bool UDPStreamerClient::Initialise(StructuredDataI &data) {
if (ok) { ok = cdb.Write("DataPort", static_cast<uint32>(dataPort)); }
}
if (ok) { ok = cdb.Write("MaxPayloadSize", maxPayloadSize); }
if (ok) { ok = cdb.Write("KeepAliveInterval", keepAliveInterval); }
if (ok) { ok = cdb.Write("CPUMask", cpuMask); }
if (ok) { ok = cdb.Write("StackSize", stackSize); }
if (ok) { ok = cdb.MoveToRoot(); }
@@ -174,6 +174,7 @@ private:
StreamString serverAddress; /**< Server IP address. */
uint16 port; /**< Server port. */
uint32 maxPayloadSize; /**< Max payload bytes per datagram. */
uint32 keepAliveInterval; /**< Seconds between unicast keepalive ACKs (0 disables). */
uint32 cpuMask; /**< Background thread CPU affinity. */
uint32 stackSize; /**< Background thread stack size. */
StreamString multicastGroup; /**< Multicast group IP; empty = unicast. */
@@ -24,6 +24,7 @@ UDPSClient::UDPSClient()
useMulticast(false),
silenceTimeoutTicks(0u),
reconnectDelayTicks(0u),
keepAliveIntervalTicks(0u),
maxPayloadSize(UDPS_CLIENT_DEFAULT_MAX_PAYLOAD),
cpuMask(0xFFFFFFFFu),
stackSize(65536u),
@@ -33,6 +34,7 @@ UDPSClient::UDPSClient()
connected(false),
lastDataTicks(0u),
disconnectTick(0u),
lastKeepAliveTicks(0u),
localPort(0u),
lastGcTicks(0u) {
@@ -93,6 +95,10 @@ bool UDPSClient::Initialise(StructuredDataI &data) {
(void) data.Read("ReconnectDelay", reconnectS);
reconnectDelayTicks = static_cast<uint64>(reconnectS) * HighResolutionTimer::Frequency();
uint32 keepAliveS = UDPS_CLIENT_DEFAULT_KEEPALIVE_INTERVAL_S;
(void) data.Read("KeepAliveInterval", keepAliveS);
keepAliveIntervalTicks = static_cast<uint64>(keepAliveS) * HighResolutionTimer::Frequency();
uint32 mps = UDPS_CLIENT_DEFAULT_MAX_PAYLOAD;
(void) data.Read("MaxPayloadSize", mps);
maxPayloadSize = mps;
@@ -185,6 +191,18 @@ ErrorManagement::ErrorType UDPSClient::Execute(ExecutionInfo &info) {
}
}
// Unicast keepalive: UDPSServer evicts silent unicast clients after its
// ClientTimeout (default 30 s). Re-sending CONNECT would also re-trigger
// a CONFIG resend; an ACK refreshes the server's last-seen with no side
// effects, so it is the keepalive packet of choice. Multicast clients
// hold a persistent TCP control connection and need no keepalive.
if (!useMulticast && (keepAliveIntervalTicks > 0u)) {
if ((now - lastKeepAliveTicks) >= keepAliveIntervalTicks) {
SendKeepAlive();
lastKeepAliveTicks = now;
}
}
// Periodic GC of stale reassembly slots (~every 1 s)
uint64 gcFreq = HighResolutionTimer::Frequency();
if ((now - lastGcTicks) >= gcFreq) {
@@ -204,6 +222,7 @@ bool UDPSClient::Connect() {
if (ok) {
connected = true;
lastDataTicks = HighResolutionTimer::Counter();
lastKeepAliveTicks = lastDataTicks;
if (listener != NULL_PTR(UDPSClientListener *)) {
listener->OnUDPSConnected();
}
@@ -378,6 +397,25 @@ void UDPSClient::Disconnect() {
}
}
// ---------------------------------------------------------------------------
// Private: SendKeepAlive
// ---------------------------------------------------------------------------
void UDPSClient::SendKeepAlive() {
if (useMulticast || !recvSocket.IsValid()) {
return;
}
uint8 ackPkt[UDPS_HEADER_SIZE];
UDPSBuildHeader(ackPkt, UDPS_TYPE_ACK, 0u, 0u, 1u, 0u);
InternetHost serverDest(serverPort, serverAddr.Buffer());
(void) recvSocket.SetDestination(serverDest);
uint32 sendSize = UDPS_HEADER_SIZE;
if (!recvSocket.Write(reinterpret_cast<const char8 *>(ackPkt), sendSize)) {
/* Non-fatal: if the server is truly gone, the silence timeout
* triggers the usual disconnect + reconnect. */
}
}
// ---------------------------------------------------------------------------
// Private: ReceiveAndProcess
// ---------------------------------------------------------------------------
@@ -92,6 +92,11 @@ public:
/** Default delay between reconnect attempts (seconds). */
static const uint32 UDPS_CLIENT_DEFAULT_RECONNECT_DELAY_S = 2u;
/** Default unicast keepalive interval (seconds). UDPSServer evicts silent
* unicast clients after its ClientTimeout (default 30 s); the client
* re-sends an ACK on this interval to stay registered. 0 disables. */
static const uint32 UDPS_CLIENT_DEFAULT_KEEPALIVE_INTERVAL_S = 15u;
/** Default maximum payload size (bytes, excluding 17-byte header). */
static const uint32 UDPS_CLIENT_DEFAULT_MAX_PAYLOAD = 1400u;
@@ -111,9 +116,11 @@ public:
* - ServerAddr (char*) Server IPv4 address. Required.
* - Port (uint16) Server UDP port (unicast) or TCP listen port (multicast). Required.
* - MulticastGroup (char*) IPv4 multicast address; presence enables multicast mode.
* - Interface (char*) Network interface for multicast join (e.g. "lo"). Required when MulticastGroup is set.
* - DataPort (uint16) UDP multicast data port (defaults to Port+1).
* - SilenceTimeout (uint32) Seconds of no data before reconnect. Default 5.
* - ReconnectDelay (uint32) Seconds to wait between reconnect attempts. Default 2.
* - KeepAliveInterval (uint32) Seconds between unicast keepalive ACKs. Default 15. 0 disables.
* - MaxPayloadSize (uint32) Max payload bytes per datagram, excluding header. Default 1400.
* - CPUMask (uint32) CPU affinity mask for the receive thread. Default 0xFFFFFFFF.
* - StackSize (uint32) Stack size for the receive thread. Default 65536.
@@ -167,6 +174,8 @@ private:
// -------------------------------------------------------------------------
bool Connect();
void Disconnect();
/** Send a keepalive ACK to the server (unicast only, same socket). */
void SendKeepAlive();
bool ReceiveAndProcess();
bool ConnectUnicast();
@@ -195,6 +204,7 @@ private:
bool useMulticast;
uint64 silenceTimeoutTicks;
uint64 reconnectDelayTicks;
uint64 keepAliveIntervalTicks; ///< 0 = keepalive disabled
uint32 maxPayloadSize;
uint32 cpuMask;
uint32 stackSize;
@@ -208,6 +218,7 @@ private:
bool connected;
uint64 lastDataTicks; ///< Ticks at last received DATA/CONFIG
uint64 disconnectTick; ///< Ticks when we disconnected (for delay)
uint64 lastKeepAliveTicks; ///< Ticks at last keepalive ACK sent
// Unicast
BasicUDPSocket recvSocket; ///< Bound to ephemeral port; receives DATA
@@ -6,12 +6,15 @@
#include "UDPSServer.h"
#include "AdvancedErrorManagement.h"
#include "ErrorType.h"
#include "HighResolutionTimer.h"
#include "MemoryOperationsHelper.h"
#include "StreamString.h"
#include <sys/select.h>
#include <arpa/inet.h>
#include <poll.h>
#include <sys/select.h>
#include <sys/socket.h>
namespace MARTe {
@@ -20,18 +23,10 @@ namespace MARTe {
// ---------------------------------------------------------------------------
UDPSServer::UDPSServer()
: port(0u),
maxPayloadSize(UDPS_SERVER_DEFAULT_MAX_PAYLOAD),
dataPort(0u),
useMulticast(false),
clientTimeoutTicks(0u),
numUnicastClients(0u),
numTCPClients(0u),
cachedConfig(NULL_PTR(uint8 *)),
cachedConfigSize(0u),
sendBuf(NULL_PTR(uint8 *)),
sendBufCapacity(0u),
configCounter(0u),
: port(0u), maxPayloadSize(UDPS_SERVER_DEFAULT_MAX_PAYLOAD), dataPort(0u),
useMulticast(false), clientTimeoutTicks(0u), numUnicastClients(0u),
numTCPClients(0u), cachedConfig(NULL_PTR(uint8 *)), cachedConfigSize(0u),
sendBuf(NULL_PTR(uint8 *)), sendBufCapacity(0u), configCounter(0u),
started(false) {
for (uint32 i = 0u; i < UDPS_SERVER_MAX_UNICAST_CLIENTS; i++) {
@@ -47,9 +42,7 @@ UDPSServer::UDPSServer()
}
}
UDPSServer::~UDPSServer() {
(void) Stop();
}
UDPSServer::~UDPSServer() { (void)Stop(); }
// ---------------------------------------------------------------------------
// Initialise
@@ -59,14 +52,14 @@ bool UDPSServer::Initialise(StructuredDataI &data) {
uint32 portU32 = 0u;
if (data.Read("Port", portU32)) {
port = static_cast<uint16>(portU32);
}
else {
} else {
REPORT_ERROR_STATIC(ErrorManagement::ParametersError,
"UDPSServer: Port not specified.");
return false;
}
/* port == 0 is valid: unicast push-only mode (no serverSocket bind,
* no CONNECT/DISCONNECT/ACK reception). Clients added via AddStaticClient(). */
* no CONNECT/DISCONNECT/ACK reception). Clients added via AddStaticClient().
*/
StreamString mcGroup;
if (data.Read("MulticastGroup", mcGroup) && (mcGroup.Size() > 0u)) {
@@ -79,11 +72,18 @@ bool UDPSServer::Initialise(StructuredDataI &data) {
(void)data.Read("DataPort", dpU32);
dataPort = static_cast<uint16>(dpU32);
if (dataPort == port) {
REPORT_ERROR_STATIC(ErrorManagement::ParametersError,
REPORT_ERROR_STATIC(
ErrorManagement::ParametersError,
"UDPSServer: DataPort (%u) must differ from Port (%u).",
static_cast<uint32>(dataPort), static_cast<uint32>(port));
return false;
}
if (!data.Read("Interface", interface)) {
REPORT_ERROR_STATIC(
ErrorManagement::ParametersError,
"Missing mandatory `Interface` field for multicast operations");
return false;
}
}
uint32 mps = UDPS_SERVER_DEFAULT_MAX_PAYLOAD;
@@ -92,8 +92,8 @@ bool UDPSServer::Initialise(StructuredDataI &data) {
uint32 timeoutSecs = UDPS_SERVER_DEFAULT_CLIENT_TIMEOUT_S;
(void)data.Read("ClientTimeout", timeoutSecs);
clientTimeoutTicks = (timeoutSecs > 0u)
? (static_cast<uint64>(timeoutSecs) * HighResolutionTimer::Frequency())
clientTimeoutTicks = (timeoutSecs > 0u) ? (static_cast<uint64>(timeoutSecs) *
HighResolutionTimer::Frequency())
: 0u;
return true;
@@ -127,20 +127,25 @@ bool UDPSServer::Start() {
if (ok) {
tcpListener.SetBlocking(false);
}
// UDP data socket connected to multicast group
// UDP data socket connected to multicast group.
// Set IP_MULTICAST_IF so outgoing datagrams leave on the specified
// interface rather than whichever the kernel routing table picks.
ok &= dataSocket.Open();
if (ok) {
ok = dataSocket.Open();
}
if (ok) {
ok = dataSocket.Connect(multicastGroup.Buffer(), dataPort);
struct in_addr localIf;
localIf.s_addr = inet_addr(interface.Buffer());
int fd = static_cast<int>(dataSocket.GetWriteHandle());
ok = (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
&localIf, static_cast<socklen_t>(sizeof(localIf))) == 0);
}
ok &= dataSocket.Connect(multicastGroup.Buffer(), dataPort);
if (!ok) {
REPORT_ERROR_STATIC(ErrorManagement::FatalError,
REPORT_ERROR_STATIC(
ErrorManagement::FatalError,
"UDPSServer: Failed to open multicast sockets on port %u.",
static_cast<uint32>(port));
}
}
else {
} else {
// Unicast send socket (unconnected; SetDestination per Write)
ok = uniSendSocket.Open();
if (!ok) {
@@ -155,7 +160,8 @@ bool UDPSServer::Start() {
ok = serverSocket.Listen(port); // UDP "listen" = bind
}
if (!ok) {
REPORT_ERROR_STATIC(ErrorManagement::FatalError,
REPORT_ERROR_STATIC(
ErrorManagement::FatalError,
"UDPSServer: Failed to bind receive socket on port %u.",
static_cast<uint32>(port));
}
@@ -235,11 +241,9 @@ void UDPSServer::ServiceClients() {
pfd.fd = static_cast<int>(tcpListener.GetReadHandle());
pfd.events = POLLIN;
pfd.revents = 0;
bool pending = (::poll(&pfd, 1u, 0) > 0) &&
((pfd.revents & POLLIN) != 0);
BasicTCPSocket *newConn = pending
? tcpListener.WaitConnection(0u)
: NULL_PTR(BasicTCPSocket *);
bool pending = (::poll(&pfd, 1u, 0) > 0) && ((pfd.revents & POLLIN) != 0);
BasicTCPSocket *newConn =
pending ? tcpListener.WaitConnection(0u) : NULL_PTR(BasicTCPSocket *);
if (newConn != NULL_PTR(BasicTCPSocket *)) {
// Find free TCP client slot
uint32 freeSlot = UDPS_SERVER_MAX_TCP_CLIENTS;
@@ -251,9 +255,9 @@ void UDPSServer::ServiceClients() {
}
if (freeSlot < UDPS_SERVER_MAX_TCP_CLIENTS) {
HandleMulticastTCPConnect(newConn, freeSlot);
}
else {
REPORT_ERROR_STATIC(ErrorManagement::Warning,
} else {
REPORT_ERROR_STATIC(
ErrorManagement::Warning,
"UDPSServer: TCP client table full, rejecting new connection.");
(void)newConn->Close();
delete newConn;
@@ -268,20 +272,25 @@ void UDPSServer::ServiceClients() {
}
// Non-blocking peek
int fd = tcpClients[i]->GetReadHandle();
if (fd < 0 || fd >= FD_SETSIZE) { continue; /* HI-6: skip FDs outside select range */ }
if (fd < 0 || fd >= FD_SETSIZE) {
continue; /* HI-6: skip FDs outside select range */
}
fd_set rset;
FD_ZERO(&rset);
FD_SET(fd, &rset);
struct timeval tv;
tv.tv_sec = 0; tv.tv_usec = 0;
tv.tv_sec = 0;
tv.tv_usec = 0;
int nready = select(fd + 1, &rset, NULL, NULL, &tv);
if (nready > 0) {
uint8 pktBuf[UDPS_HEADER_SIZE];
uint32 recvSize = UDPS_HEADER_SIZE;
bool recvOk = tcpClients[i]->Read(reinterpret_cast<char8 *>(pktBuf), recvSize);
bool recvOk =
tcpClients[i]->Read(reinterpret_cast<char8 *>(pktBuf), recvSize);
if (!recvOk || (recvSize == 0u)) {
REPORT_ERROR_STATIC(ErrorManagement::Information,
"UDPSServer: TCP client disconnected (slot %u).", i);
"UDPSServer: TCP client disconnected (slot %u).",
i);
EvictTCPClient(i);
continue;
}
@@ -290,21 +299,23 @@ void UDPSServer::ServiceClients() {
reinterpret_cast<const UDPSPacketHeader *>(pktBuf);
if ((hdr->magic == UDPS_MAGIC) &&
(hdr->type == UDPS_TYPE_DISCONNECT)) {
REPORT_ERROR_STATIC(ErrorManagement::Information,
REPORT_ERROR_STATIC(
ErrorManagement::Information,
"UDPSServer: TCP client sent DISCONNECT (slot %u).", i);
EvictTCPClient(i);
}
}
}
}
}
else {
} else {
// Unicast: poll serverSocket for CONNECT / DISCONNECT / ACK
if (!serverSocket.IsValid()) {
return;
}
int fd = serverSocket.GetReadHandle();
if (fd < 0 || fd >= FD_SETSIZE) { return; /* HI-6 */ }
if (fd < 0 || fd >= FD_SETSIZE) {
return; /* HI-6 */
}
fd_set rset;
FD_ZERO(&rset);
FD_SET(fd, &rset);
@@ -313,7 +324,8 @@ void UDPSServer::ServiceClients() {
while (nready > 0) {
uint8 pktBuf[UDPS_HEADER_SIZE + 4u];
uint32 recvSize = static_cast<uint32>(sizeof(pktBuf));
bool recvOk = serverSocket.Read(reinterpret_cast<char8 *>(pktBuf), recvSize);
bool recvOk =
serverSocket.Read(reinterpret_cast<char8 *>(pktBuf), recvSize);
if (!recvOk || (recvSize < UDPS_HEADER_SIZE)) {
break;
}
@@ -325,11 +337,9 @@ void UDPSServer::ServiceClients() {
InternetHost src = serverSocket.GetSource();
if (hdr->type == UDPS_TYPE_CONNECT) {
HandleUnicastConnect(src);
}
else if (hdr->type == UDPS_TYPE_DISCONNECT) {
} else if (hdr->type == UDPS_TYPE_DISCONNECT) {
HandleUnicastDisconnect(src);
}
else if (hdr->type == UDPS_TYPE_ACK) {
} else if (hdr->type == UDPS_TYPE_ACK) {
HandleUnicastAck(src);
}
@@ -373,14 +383,14 @@ bool UDPSServer::SendConfig(const uint8 *payload, uint32 payloadSize) {
bool sent = SendFragmentedTCP(*tcpClients[i], UDPS_TYPE_CONFIG,
configCounter, payload, payloadSize);
if (!sent) {
REPORT_ERROR_STATIC(ErrorManagement::Warning,
REPORT_ERROR_STATIC(
ErrorManagement::Warning,
"UDPSServer: CONFIG send failed to TCP client %u, evicting.", i);
EvictTCPClient(i);
ok = false;
}
}
}
else {
} else {
// Send CONFIG to each unicast client
for (uint32 i = 0u; i < UDPS_SERVER_MAX_UNICAST_CLIENTS; i++) {
if (!unicastClients[i].active) {
@@ -406,7 +416,8 @@ bool UDPSServer::SendConfig(const uint8 *payload, uint32 payloadSize) {
// SendData
// ---------------------------------------------------------------------------
bool UDPSServer::SendData(uint32 counter, const uint8 *payload, uint32 payloadSize) {
bool UDPSServer::SendData(uint32 counter, const uint8 *payload,
uint32 payloadSize) {
if (!started) {
return false;
}
@@ -415,15 +426,15 @@ bool UDPSServer::SendData(uint32 counter, const uint8 *payload, uint32 payloadSi
if (useMulticast) {
// Single multicast write (no dest needed — socket already connected)
bool sent = SendFragmentedUDP(dataSocket, NULL_PTR(InternetHost *),
UDPS_TYPE_DATA, counter, payload, payloadSize);
bool sent =
SendFragmentedUDP(dataSocket, NULL_PTR(InternetHost *), UDPS_TYPE_DATA,
counter, payload, payloadSize);
if (!sent) {
REPORT_ERROR_STATIC(ErrorManagement::Warning,
"UDPSServer: DATA send to multicast group failed.");
ok = false;
}
}
else {
} else {
for (uint32 i = 0u; i < UDPS_SERVER_MAX_UNICAST_CLIENTS; i++) {
if (!unicastClients[i].active) {
continue;
@@ -470,7 +481,8 @@ bool UDPSServer::AddStaticClient(const char8 *ip, uint16 port_) {
}
if (freeSlot >= UDPS_SERVER_MAX_UNICAST_CLIENTS) {
REPORT_ERROR_STATIC(ErrorManagement::Warning,
"UDPSServer: Unicast client table full, cannot add static client %s:%u.",
"UDPSServer: Unicast client table full, cannot add "
"static client %s:%u.",
ip, static_cast<uint32>(port_));
return false;
}
@@ -489,8 +501,8 @@ bool UDPSServer::AddStaticClient(const char8 *ip, uint16 port_) {
numUnicastClients++;
REPORT_ERROR_STATIC(ErrorManagement::Information,
"UDPSServer: Static client added: %s:%u.",
ip, static_cast<uint32>(port_));
"UDPSServer: Static client added: %s:%u.", ip,
static_cast<uint32>(port_));
return true;
}
@@ -513,35 +525,24 @@ uint32 UDPSServer::GetClientCount() const {
return count;
}
bool UDPSServer::HasClients() const {
return (GetClientCount() > 0u);
}
bool UDPSServer::HasClients() const { return (GetClientCount() > 0u); }
bool UDPSServer::IsMulticast() const {
return useMulticast;
}
bool UDPSServer::IsMulticast() const { return useMulticast; }
uint16 UDPSServer::GetPort() const {
return port;
}
uint16 UDPSServer::GetPort() const { return port; }
uint32 UDPSServer::GetMaxPayloadSize() const {
return maxPayloadSize;
}
uint32 UDPSServer::GetMaxPayloadSize() const { return maxPayloadSize; }
// ---------------------------------------------------------------------------
// Private: SendFragmentedUDP
// ---------------------------------------------------------------------------
bool UDPSServer::SendFragmentedUDP(BasicUDPSocket &sock,
InternetHost *dest,
uint8 type,
uint32 counter,
const uint8 *payload,
uint32 payloadSize) {
bool UDPSServer::SendFragmentedUDP(BasicUDPSocket &sock, InternetHost *dest,
uint8 type, uint32 counter,
const uint8 *payload, uint32 payloadSize) {
uint32 maxChunk = maxPayloadSize; // payload bytes per fragment (excl. header)
uint32 totalFrags = (payloadSize == 0u) ? 1u :
((payloadSize + maxChunk - 1u) / maxChunk);
uint32 totalFrags =
(payloadSize == 0u) ? 1u : ((payloadSize + maxChunk - 1u) / maxChunk);
bool ok = true;
uint32 offs = 0u;
@@ -552,15 +553,12 @@ bool UDPSServer::SendFragmentedUDP(BasicUDPSocket &sock,
chunkSize = maxChunk;
}
UDPSBuildHeader(sendBuf, type, counter,
static_cast<uint16>(f),
static_cast<uint16>(totalFrags),
chunkSize);
UDPSBuildHeader(sendBuf, type, counter, static_cast<uint16>(f),
static_cast<uint16>(totalFrags), chunkSize);
if (chunkSize > 0u) {
(void)MemoryOperationsHelper::Copy(sendBuf + UDPS_HEADER_SIZE,
payload + offs,
chunkSize);
payload + offs, chunkSize);
}
uint32 sendSize = UDPS_HEADER_SIZE + chunkSize;
@@ -586,14 +584,12 @@ bool UDPSServer::SendFragmentedUDP(BasicUDPSocket &sock,
// Private: SendFragmentedTCP
// ---------------------------------------------------------------------------
bool UDPSServer::SendFragmentedTCP(BasicTCPSocket &sock,
uint8 type,
uint32 counter,
const uint8 *payload,
bool UDPSServer::SendFragmentedTCP(BasicTCPSocket &sock, uint8 type,
uint32 counter, const uint8 *payload,
uint32 payloadSize) {
uint32 maxChunk = maxPayloadSize;
uint32 totalFrags = (payloadSize == 0u) ? 1u :
((payloadSize + maxChunk - 1u) / maxChunk);
uint32 totalFrags =
(payloadSize == 0u) ? 1u : ((payloadSize + maxChunk - 1u) / maxChunk);
bool ok = true;
uint32 offs = 0u;
@@ -604,15 +600,12 @@ bool UDPSServer::SendFragmentedTCP(BasicTCPSocket &sock,
chunkSize = maxChunk;
}
UDPSBuildHeader(sendBuf, type, counter,
static_cast<uint16>(f),
static_cast<uint16>(totalFrags),
chunkSize);
UDPSBuildHeader(sendBuf, type, counter, static_cast<uint16>(f),
static_cast<uint16>(totalFrags), chunkSize);
if (chunkSize > 0u) {
(void)MemoryOperationsHelper::Copy(sendBuf + UDPS_HEADER_SIZE,
payload + offs,
chunkSize);
payload + offs, chunkSize);
}
uint32 sendSize = UDPS_HEADER_SIZE + chunkSize;
@@ -660,8 +653,8 @@ void UDPSServer::HandleUnicastConnect(const InternetHost &src) {
uint16 srcPort = src.GetPort();
REPORT_ERROR_STATIC(ErrorManagement::Information,
"UDPSServer: CONNECT from %s:%u.",
srcAddr, static_cast<uint32>(srcPort));
"UDPSServer: CONNECT from %s:%u.", srcAddr,
static_cast<uint32>(srcPort));
// Check if this client is already known
uint32 existing = FindUnicastClient(srcAddr, srcPort);
@@ -693,9 +686,9 @@ void UDPSServer::HandleUnicastConnect(const InternetHost &src) {
unicastClients[slot].ipAddr,
static_cast<uint32>(unicastClients[slot].clientPort));
EvictUnicastClient(slot);
}
else {
REPORT_ERROR_STATIC(ErrorManagement::Warning,
} else {
REPORT_ERROR_STATIC(
ErrorManagement::Warning,
"UDPSServer: All slots occupied by static clients; rejecting %s:%u.",
srcAddr, static_cast<uint32>(srcPort));
return;
@@ -736,8 +729,8 @@ void UDPSServer::HandleUnicastDisconnect(const InternetHost &src) {
uint32 slot = FindUnicastClient(srcAddr, srcPort);
if (slot < UDPS_SERVER_MAX_UNICAST_CLIENTS) {
REPORT_ERROR_STATIC(ErrorManagement::Information,
"UDPSServer: DISCONNECT from %s:%u.",
srcAddr, static_cast<uint32>(srcPort));
"UDPSServer: DISCONNECT from %s:%u.", srcAddr,
static_cast<uint32>(srcPort));
EvictUnicastClient(slot);
}
}
@@ -826,7 +819,8 @@ void UDPSServer::EvictUnicastClient(uint32 idx) {
// Private: HandleMulticastTCPConnect
// ---------------------------------------------------------------------------
void UDPSServer::HandleMulticastTCPConnect(BasicTCPSocket *newClient, uint32 idx) {
void UDPSServer::HandleMulticastTCPConnect(BasicTCPSocket *newClient,
uint32 idx) {
if (idx >= UDPS_SERVER_MAX_TCP_CLIENTS) {
return;
}
@@ -835,16 +829,19 @@ void UDPSServer::HandleMulticastTCPConnect(BasicTCPSocket *newClient, uint32 idx
numTCPClients++;
REPORT_ERROR_STATIC(ErrorManagement::Information,
"UDPSServer: Multicast TCP client connected (slot %u).", idx);
"UDPSServer: Multicast TCP client connected (slot %u).",
idx);
// Send cached CONFIG over TCP
if (cachedConfig != NULL_PTR(uint8 *)) {
configCounter++;
bool sent = SendFragmentedTCP(*newClient, UDPS_TYPE_CONFIG,
configCounter, cachedConfig, cachedConfigSize);
bool sent = SendFragmentedTCP(*newClient, UDPS_TYPE_CONFIG, configCounter,
cachedConfig, cachedConfigSize);
if (!sent) {
REPORT_ERROR_STATIC(ErrorManagement::Warning,
"UDPSServer: Failed to send CONFIG to new TCP client (slot %u).", idx);
REPORT_ERROR_STATIC(
ErrorManagement::Warning,
"UDPSServer: Failed to send CONFIG to new TCP client (slot %u).",
idx);
EvictTCPClient(idx);
}
}
@@ -235,6 +235,7 @@ private:
uint16 port;
uint32 maxPayloadSize;
StreamString multicastGroup;
StreamString interface;
uint16 dataPort;
bool useMulticast;
uint64 clientTimeoutTicks; ///< 0 = disabled
+1
View File
@@ -424,6 +424,7 @@ $TestApp = {
Port = 44500
MulticastGroup = "239.0.0.1"
DataPort = 44503
Interface = "127.0.0.1"
MaxPayloadSize = 1400
PublishingMode = "Accumulate"
MinRefreshRate = 100
+1
View File
@@ -279,6 +279,7 @@ $App = {
Port = 44500
MulticastGroup = "239.0.0.1"
DataPort = 44503
Interface = "127.0.0.1"
MaxPayloadSize = 1400
PublishingMode = "Accumulate"
MinRefreshRate = 100
+1
View File
@@ -411,6 +411,7 @@ $App = {
Port = 44500
MulticastGroup = "239.0.0.1"
DataPort = 44503
Interface = "127.0.0.1"
MaxPayloadSize = 1400
PublishingMode = "Accumulate"
MinRefreshRate = 100
@@ -64,6 +64,7 @@ $E2EMulticastTest = {
Port = 44600
MulticastGroup = "239.0.0.1"
DataPort = 44610
Interface = "127.0.0.1"
MaxPayloadSize = 65507
PublishingMode = "Strict"
Signals = {
Binary file not shown.
Binary file not shown.
Binary file not shown.
+39
View File
@@ -17,6 +17,8 @@ expected, not an error.
"""
import argparse
import os
import re
import subprocess
import sys
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
@@ -25,6 +27,41 @@ import scenarios as S # noqa: E402
PRODUCER_HZ = 1000 # LinuxTimer frequency (Hz)
def _iface_to_ip(name):
"""Resolve a network interface name (e.g. ``"wlan0"``) to its first IPv4
address via ``ip -4 addr show <name>``. Returns ``"127.0.0.1"`` on failure."""
try:
out = subprocess.check_output(
["ip", "-4", "addr", "show", name],
stderr=subprocess.DEVNULL, text=True)
m = re.search(r"inet\s+(\d+\.\d+\.\d+\.\d+)", out)
if m:
return m.group(1)
except (subprocess.CalledProcessError, FileNotFoundError, OSError):
pass
return "127.0.0.1"
def _mcast_interface_ip(group):
"""Return the IPv4 address of the OS-selected interface for a multicast
group. MARTe2's ``BasicUDPSocket::Join`` expects an IP address (passed to
``inet_addr``), not an interface name.
Uses ``ip route get <group>`` to find the outgoing device, then resolves
its IP. Falls back to ``"127.0.0.1"`` if the route or device lookup fails.
"""
try:
out = subprocess.check_output(
["ip", "route", "get", group],
stderr=subprocess.DEVNULL, text=True)
m = re.search(r"dev\s+(\S+)", out)
if m:
return _iface_to_ip(m.group(1))
except (subprocess.CalledProcessError, FileNotFoundError, OSError):
pass
return "127.0.0.1"
def _ndims(elements):
return 0 if elements == 1 else 1
@@ -78,6 +115,8 @@ def _streamer_block(src, scenario):
if scenario["network"] == "multicast":
parts.append(f'MulticastGroup = "{src["multicast_group"]}"')
parts.append(f"DataPort = {src['data_port']}")
iface = src.get("interface") or _mcast_interface_ip(src["multicast_group"])
parts.append(f'Interface = "{iface}"')
sigs = " ".join(_streamer_sig(sig) for sig in s)
return (f" +Streamer_{src['id']} = {{ Class = UDPStreamer "
f"{' '.join(parts)} Signals = {{ {sigs} }} }}")
+2 -1
View File
@@ -46,9 +46,10 @@ INCLUDES += -I$(ROOT_DIR)/Common/UDP
INCLUDES += -I$(ROOT_DIR)/Source/Components/DataSources/UDPStreamer
INCLUDES += -I$(ROOT_DIR)/Source/Components/Interfaces/DebugService
INCLUDES += -I$(ROOT_DIR)/Source/Components/Interfaces/TCPLogger
INCLUDES += -I$(ROOT_DIR)/Source/Components/Interfaces/UDPStream
INCLUDES += -I$(ROOT_DIR)/Test/Components/DataSources/UDPStreamer
OBJSX = DebugServiceGTest.x
OBJSX = DebugServiceGTest.x UDPSClientGTest.x
all: $(BUILD_DIR)/MainGTest$(EXEEXT)
+296
View File
@@ -0,0 +1,296 @@
/**
* @file UDPSClientGTest.cpp
* @brief GTest coverage for UDPSClient unicast keepalive.
*
* UDPSServer evicts silent unicast clients after its ClientTimeout (default
* 30 s). UDPSClient must therefore re-send a keepalive ACK from the same
* socket on KeepAliveInterval so the server refreshes its last-seen without
* re-sending CONFIG. These tests drive a real UDPSClient against a local
* UDP socket acting as the server and assert the wire behaviour; a final
* pair runs the REAL UDPSServer + UDPSClient past the eviction deadline to
* lock the fix in against regression.
*
* @copyright Copyright 2015 F4E | European Joint Undertaking for ITER and
* the Development of Fusion Energy ('Fusion for Energy').
* Licensed under the EUPL, Version 1.1 or - as soon they will be approved
* by the European Commission - subsequent versions of the EUPL (the "Licence")
* You may not use this work except in compliance with the Licence.
* You may obtain a copy of the Licence at: http://ec.europa.eu/idabc/eupl
*
* @warning Unless required by applicable law or agreed to in writing,
* software distributed under the Licence is distributed on an "AS IS"
* basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the Licence permissions and limitations under the Licence.
*/
#define DLL_API
/*---------------------------------------------------------------------------*/
/* Standard header includes */
/*---------------------------------------------------------------------------*/
#include "gtest/gtest.h"
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/select.h>
#include <sys/socket.h>
/*---------------------------------------------------------------------------*/
/* Project header includes */
/*---------------------------------------------------------------------------*/
#include "BasicUDPSocket.h"
#include "ConfigurationDatabase.h"
#include "InternetHost.h"
#include "Sleep.h"
#include "UDPSClient.h"
#include "UDPSProtocol.h"
#include "UDPSServer.h"
using namespace MARTe;
namespace {
/** @return the bound local port of @p sock, or 0 on failure. */
uint16 GetBoundPort(BasicUDPSocket &sock) {
struct sockaddr_in addr;
socklen_t len = sizeof(addr);
if (getsockname(sock.GetReadHandle(),
reinterpret_cast<struct sockaddr *>(&addr), &len) != 0) {
return 0u;
}
return ntohs(addr.sin_port);
}
/**
* @brief Read one datagram from @p sock within @p timeoutMs.
* @return true and fills @p type/@p srcPort on a valid UDPS datagram; false
* on timeout or malformed packet.
*/
bool WaitDatagram(BasicUDPSocket &sock, int timeoutMs, uint8 &type,
uint16 &srcPort) {
int fd = sock.GetReadHandle();
if (fd < 0) {
return false;
}
fd_set rset;
FD_ZERO(&rset);
FD_SET(fd, &rset);
struct timeval tv;
tv.tv_sec = timeoutMs / 1000;
tv.tv_usec = (timeoutMs % 1000) * 1000;
int nready = select(fd + 1, &rset, NULL, NULL, &tv);
if (nready <= 0) {
return false;
}
uint8 buf[UDPS_HEADER_SIZE];
uint32 size = UDPS_HEADER_SIZE;
if (!sock.Read(reinterpret_cast<char8 *>(buf), size)) {
return false;
}
if (size < UDPS_HEADER_SIZE) {
return false;
}
const UDPSPacketHeader *hdr = reinterpret_cast<const UDPSPacketHeader *>(buf);
if (hdr->magic != UDPS_MAGIC) {
return false;
}
type = hdr->type;
InternetHost src = sock.GetSource();
srcPort = src.GetPort();
return true;
}
/**
* @brief Pump the UDPSServer service loop for @p durationMs, like UDPStreamer
* does from its background thread.
*/
void PumpServer(UDPSServer &server, uint32 durationMs) {
uint32 elapsed = 0u;
while (elapsed < durationMs) {
server.ServiceClients();
Sleep::MSec(20u);
elapsed += 20u;
}
}
/**
* @brief Pump the server loop until a client registers (or timeout).
* @return true if at least one client connected.
*/
bool WaitForClient(UDPSServer &server, uint32 timeoutMs) {
uint32 elapsed = 0u;
while (elapsed < timeoutMs) {
server.ServiceClients();
if (server.GetClientCount() > 0u) {
return true;
}
Sleep::MSec(20u);
elapsed += 20u;
}
return false;
}
} // namespace
/*---------------------------------------------------------------------------*/
/* Method definitions */
/*---------------------------------------------------------------------------*/
TEST(UDPSClientGTest, TestUnicastKeepAliveSendsPeriodicAck) {
/* Fake server socket (ephemeral port) */
BasicUDPSocket server;
ASSERT_TRUE(server.Open());
ASSERT_TRUE(server.Listen(0u));
uint16 serverPort = GetBoundPort(server);
ASSERT_NE(serverPort, 0u);
ConfigurationDatabase cfg;
ASSERT_TRUE(cfg.Write("ServerAddr", "127.0.0.1"));
ASSERT_TRUE(cfg.Write("Port", static_cast<uint32>(serverPort)));
ASSERT_TRUE(cfg.Write("KeepAliveInterval", 1u));
/* SilenceTimeout=0 keeps the session stable for the whole test */
ASSERT_TRUE(cfg.Write("SilenceTimeout", 0u));
UDPSClient client;
ASSERT_TRUE(client.Initialise(cfg));
ASSERT_TRUE(client.Start());
/* 1) CONNECT from the client's ephemeral socket */
uint8 type = 0xFFu;
uint16 clientPort = 0u;
ASSERT_TRUE(WaitDatagram(server, 3000, type, clientPort));
EXPECT_EQ(type, UDPS_TYPE_CONNECT);
ASSERT_NE(clientPort, 0u);
/* 2) Keepalive ACKs arrive periodically from the SAME socket */
uint32 acks = 0u;
uint32 elapsedMs = 0u;
while ((acks < 2u) && (elapsedMs < 3500u)) {
uint8 t = 0xFFu;
uint16 port = 0u;
bool got = WaitDatagram(server, 1000, t, port);
elapsedMs += 1000u;
if (!got) {
continue;
}
if ((t == UDPS_TYPE_ACK) && (port == clientPort)) {
acks++;
}
}
EXPECT_GE(acks, 2u);
client.Stop();
server.Close();
}
TEST(UDPSClientGTest, TestKeepAliveDisabledWhenIntervalZero) {
BasicUDPSocket server;
ASSERT_TRUE(server.Open());
ASSERT_TRUE(server.Listen(0u));
uint16 serverPort = GetBoundPort(server);
ASSERT_NE(serverPort, 0u);
ConfigurationDatabase cfg;
ASSERT_TRUE(cfg.Write("ServerAddr", "127.0.0.1"));
ASSERT_TRUE(cfg.Write("Port", static_cast<uint32>(serverPort)));
ASSERT_TRUE(cfg.Write("KeepAliveInterval", 0u));
ASSERT_TRUE(cfg.Write("SilenceTimeout", 0u));
UDPSClient client;
ASSERT_TRUE(client.Initialise(cfg));
ASSERT_TRUE(client.Start());
uint8 type = 0xFFu;
uint16 clientPort = 0u;
ASSERT_TRUE(WaitDatagram(server, 3000, type, clientPort));
EXPECT_EQ(type, UDPS_TYPE_CONNECT);
/* No keepalive configured: nothing else must arrive */
EXPECT_FALSE(WaitDatagram(server, 2000, type, clientPort));
client.Stop();
server.Close();
}
TEST(UDPSClientGTest, TestKeepAlivePreventsServerEviction) {
/* Regression test for the 30 s unicast disconnect: UDPSServer evicts a
* silent client after ClientTimeout; the client's periodic ACKs must
* keep it registered. Drive the REAL server + client pair, like
* UDPStreamer + StreamHub do, past the eviction deadline. */
/* Free-port probe (UDP has no TIME_WAIT) */
BasicUDPSocket probe;
ASSERT_TRUE(probe.Open());
ASSERT_TRUE(probe.Listen(0u));
uint16 serverPort = GetBoundPort(probe);
probe.Close();
ASSERT_NE(serverPort, 0u);
/* Server with a short eviction timeout so the test is fast */
ConfigurationDatabase serverCfg;
ASSERT_TRUE(serverCfg.Write("Port", static_cast<uint32>(serverPort)));
ASSERT_TRUE(serverCfg.Write("ClientTimeout", 3u));
UDPSServer server;
ASSERT_TRUE(server.Initialise(serverCfg));
ASSERT_TRUE(server.Start());
/* Client: keepalive every 1 s (< server timeout), silence disabled */
ConfigurationDatabase clientCfg;
ASSERT_TRUE(clientCfg.Write("ServerAddr", "127.0.0.1"));
ASSERT_TRUE(clientCfg.Write("Port", static_cast<uint32>(serverPort)));
ASSERT_TRUE(clientCfg.Write("KeepAliveInterval", 1u));
ASSERT_TRUE(clientCfg.Write("SilenceTimeout", 0u));
UDPSClient client;
ASSERT_TRUE(client.Initialise(clientCfg));
ASSERT_TRUE(client.Start());
ASSERT_TRUE(WaitForClient(server, 3000)); /* CONNECT registered */
EXPECT_EQ(server.GetClientCount(), 1u);
/* Pump well past ClientTimeout: keepalive ACKs must prevent eviction */
PumpServer(server, 5000);
EXPECT_EQ(server.GetClientCount(), 1u);
client.Stop();
server.Stop();
}
TEST(UDPSClientGTest, TestServerEvictsWithoutKeepAlive) {
/* Negative control: without keepalive the same harness MUST evict, which
* proves TestKeepAlivePreventsServerEviction passes because of the ACKs
* and not because eviction is broken. */
BasicUDPSocket probe;
ASSERT_TRUE(probe.Open());
ASSERT_TRUE(probe.Listen(0u));
uint16 serverPort = GetBoundPort(probe);
probe.Close();
ASSERT_NE(serverPort, 0u);
ConfigurationDatabase serverCfg;
ASSERT_TRUE(serverCfg.Write("Port", static_cast<uint32>(serverPort)));
ASSERT_TRUE(serverCfg.Write("ClientTimeout", 3u));
UDPSServer server;
ASSERT_TRUE(server.Initialise(serverCfg));
ASSERT_TRUE(server.Start());
ConfigurationDatabase clientCfg;
ASSERT_TRUE(clientCfg.Write("ServerAddr", "127.0.0.1"));
ASSERT_TRUE(clientCfg.Write("Port", static_cast<uint32>(serverPort)));
ASSERT_TRUE(clientCfg.Write("KeepAliveInterval", 0u));
ASSERT_TRUE(clientCfg.Write("SilenceTimeout", 0u));
UDPSClient client;
ASSERT_TRUE(client.Initialise(clientCfg));
ASSERT_TRUE(client.Start());
ASSERT_TRUE(WaitForClient(server, 3000)); /* CONNECT registered */
EXPECT_EQ(server.GetClientCount(), 1u);
/* No ACKs: the server must evict after ClientTimeout */
PumpServer(server, 5000);
EXPECT_EQ(server.GetClientCount(), 0u);
client.Stop();
server.Stop();
}
+148
View File
@@ -195,3 +195,151 @@
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest-test-part.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest-typed-test.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest_pred_impl.h
../../Build/x86-linux//GTest/UDPSClientGTest.o: UDPSClientGTest.cpp \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/internal/gtest-internal.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/internal/gtest-port.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest-message.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/internal/gtest-string.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/internal/gtest-filepath.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/internal/gtest-type-util.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest-death-test.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/internal/gtest-death-test-internal.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest-param-test.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/internal/gtest-param-util.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/internal/gtest-linked_ptr.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest-printers.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/internal/gtest-param-util-generated.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest_prod.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest-test-part.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest-typed-test.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest_pred_impl.h \
/home/martino/workspace/MARTe2/Source/Core/FileSystem/L1Portability/BasicUDPSocket.h \
/home/martino/workspace/MARTe2/Source/Core/FileSystem/L1Portability/BasicSocket.h \
/home/martino/workspace/MARTe2/Source/Core/FileSystem/L1Portability/InternetHost.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/StreamString.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/TypeCharacteristics.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/CompilerTypes.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Architecture/x86_gcc/CompilerTypes.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HeapManager.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HeapI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/ErrorManagement.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/ErrorInformation.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/ErrorType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitBoolean.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/TemplateParametersVerificator.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitRange.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/TypeCharacteristics.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StreamI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TimeoutType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HighResolutionTimer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TimeStamp.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Architecture/x86_gcc/HighResolutionTimerA.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/CompilerTypes.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/HighResolutionTimer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../../HighResolutionTimer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/HighResolutionTimerCalibrator.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../../GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TimeStamp.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/CharBuffer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitBoolean.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/ErrorManagement.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/BufferedStreamI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TimeoutType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/AnyType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ClassProperties.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/FractionalInteger.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ClassRegistryDatabase.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/GlobalObjectsDatabase.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/GlobalObjectI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/StandardHeap.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../../HeapI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../Generic/StandardHeap_Generic.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/FastPollingMutexSem.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Atomic.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Architecture/x86_gcc/AtomicA.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Sleep.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ClassRegistryItem.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/CString.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/ZeroTerminatedArray.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/Introspection.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/IntrospectionEntry.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TypeDescriptor.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BasicType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/FractionalInteger.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitRange.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/ZeroTerminatedArray.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/LinkedListable.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/LinkedListHolderT.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/IteratorT.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Iterator.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/LinkedListable.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/LinkedListHolder.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/SortFilter.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/SearchFilter.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/LoadableLibrary.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ObjectBuilder.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HeapI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StaticList.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StaticListHolder.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Matrix.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Vector.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/MemoryOperationsHelper.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/FormatDescriptor.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/IOBuffer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StreamI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StringHelper.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/StreamStringIOBuffer.h \
/home/martino/workspace/MARTe2/Source/Core/FileSystem/L1Portability/Environment/Linux/InternetHostCore.h \
/home/martino/workspace/MARTe2/Source/Core/FileSystem/L1Portability/Environment/Linux/InternetMulticastCore.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HandleI.h \
/home/martino/workspace/MARTe2/Source/Core/FileSystem/L1Portability/Environment/Linux/SocketCore.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Sleep.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Configuration/ConfigurationDatabase.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Configuration/AnyObject.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/Object.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/StructuredDataI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/AnyType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/CLASSREGISTER.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ClassRegistryItemT.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ObjectBuilderT.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Configuration/ConfigurationDatabaseNode.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Fnv1aHashFunction.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/HashFunction.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ReferenceContainer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/LinkedListHolder.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/Object.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/Reference.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ReferenceContainerFilter.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ReferenceContainerNode.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ReferenceT.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/UnorderedMap.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HeapManager.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StringHelper.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ReferenceContainerFilterObjectName.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ReferenceContainer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/StructuredDataI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Configuration/TypeConversion.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Vector.h \
/home/martino/workspace/MARTe2/Source/Core/FileSystem/L1Portability/InternetHost.h \
../../Source/Components/Interfaces/UDPStream/UDPSClient.h \
/home/martino/workspace/MARTe2/Source/Core/FileSystem/L1Portability/BasicTCPSocket.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/EmbeddedServiceMethodBinderI.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/ExecutionInfo.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L1Portability/Threads.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/ThreadsB.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L1Portability/ExceptionHandler.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/ProcessorType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitSet.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/ExecutionInfo.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HighResolutionTimer.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/SingleThreadService.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/EmbeddedServiceI.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/EmbeddedServiceMethodBinderI.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/EmbeddedServiceMethodBinderT.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/EmbeddedThreadI.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/EmbeddedThread.h \
../../Common/UDP/UDPSProtocol.h
+148
View File
@@ -195,3 +195,151 @@ MainGTest.o: MainGTest.cpp \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest-test-part.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest-typed-test.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest_pred_impl.h
UDPSClientGTest.o: UDPSClientGTest.cpp \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/internal/gtest-internal.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/internal/gtest-port.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest-message.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/internal/gtest-string.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/internal/gtest-filepath.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/internal/gtest-type-util.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest-death-test.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/internal/gtest-death-test-internal.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest-param-test.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/internal/gtest-param-util.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/internal/gtest-linked_ptr.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest-printers.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/internal/gtest-param-util-generated.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest_prod.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest-test-part.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest-typed-test.h \
/home/martino/workspace/MARTe2/Lib/gtest-1.7.0/include/gtest/gtest_pred_impl.h \
/home/martino/workspace/MARTe2/Source/Core/FileSystem/L1Portability/BasicUDPSocket.h \
/home/martino/workspace/MARTe2/Source/Core/FileSystem/L1Portability/BasicSocket.h \
/home/martino/workspace/MARTe2/Source/Core/FileSystem/L1Portability/InternetHost.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/StreamString.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/TypeCharacteristics.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/CompilerTypes.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Architecture/x86_gcc/CompilerTypes.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HeapManager.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HeapI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/ErrorManagement.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/ErrorInformation.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/ErrorType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitBoolean.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/TemplateParametersVerificator.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitRange.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/TypeCharacteristics.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StreamI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TimeoutType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HighResolutionTimer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TimeStamp.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Architecture/x86_gcc/HighResolutionTimerA.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/CompilerTypes.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/HighResolutionTimer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../../HighResolutionTimer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/HighResolutionTimerCalibrator.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../../GeneralDefinitions.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TimeStamp.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/CharBuffer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitBoolean.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/ErrorManagement.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/BufferedStreamI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TimeoutType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/AnyType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ClassProperties.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/FractionalInteger.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ClassRegistryDatabase.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/GlobalObjectsDatabase.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/GlobalObjectI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/StandardHeap.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../../HeapI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Environment/Linux/../Generic/StandardHeap_Generic.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/FastPollingMutexSem.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Atomic.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Architecture/x86_gcc/AtomicA.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Sleep.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ClassRegistryItem.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/CString.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/ZeroTerminatedArray.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/Introspection.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/IntrospectionEntry.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/TypeDescriptor.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BasicType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/FractionalInteger.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitRange.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/ZeroTerminatedArray.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/LinkedListable.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/LinkedListHolderT.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/IteratorT.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Iterator.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/LinkedListable.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/LinkedListHolder.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/SortFilter.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/SearchFilter.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/LoadableLibrary.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ObjectBuilder.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HeapI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StaticList.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StaticListHolder.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Matrix.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Vector.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/MemoryOperationsHelper.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/FormatDescriptor.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/IOBuffer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StreamI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StringHelper.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L3Streams/StreamStringIOBuffer.h \
/home/martino/workspace/MARTe2/Source/Core/FileSystem/L1Portability/Environment/Linux/InternetHostCore.h \
/home/martino/workspace/MARTe2/Source/Core/FileSystem/L1Portability/Environment/Linux/InternetMulticastCore.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HandleI.h \
/home/martino/workspace/MARTe2/Source/Core/FileSystem/L1Portability/Environment/Linux/SocketCore.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/Sleep.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Configuration/ConfigurationDatabase.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Configuration/AnyObject.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/Object.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/StructuredDataI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/AnyType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/CLASSREGISTER.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ClassRegistryItemT.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ObjectBuilderT.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Configuration/ConfigurationDatabaseNode.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Fnv1aHashFunction.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/HashFunction.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ReferenceContainer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/LinkedListHolder.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/Object.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/Reference.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ReferenceContainerFilter.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ReferenceContainerNode.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ReferenceT.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/UnorderedMap.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HeapManager.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/StringHelper.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ReferenceContainerFilterObjectName.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/ReferenceContainer.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L2Objects/StructuredDataI.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L4Configuration/TypeConversion.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/Vector.h \
/home/martino/workspace/MARTe2/Source/Core/FileSystem/L1Portability/InternetHost.h \
../../Source/Components/Interfaces/UDPStream/UDPSClient.h \
/home/martino/workspace/MARTe2/Source/Core/FileSystem/L1Portability/BasicTCPSocket.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/EmbeddedServiceMethodBinderI.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/ExecutionInfo.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L1Portability/Threads.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/ThreadsB.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L1Portability/ExceptionHandler.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/ProcessorType.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L0Types/BitSet.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/ExecutionInfo.h \
/home/martino/workspace/MARTe2/Source/Core/BareMetal/L1Portability/HighResolutionTimer.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/SingleThreadService.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/EmbeddedServiceI.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/EmbeddedServiceMethodBinderI.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/EmbeddedServiceMethodBinderT.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/EmbeddedThreadI.h \
/home/martino/workspace/MARTe2/Source/Core/Scheduler/L3Services/EmbeddedThread.h \
../../Common/UDP/UDPSProtocol.h
BIN
View File
Binary file not shown.