Add Auto publishing mode to UDPStreamer; optimise WebUI hub

UDPStreamer:
- Add PublishingMode = "Strict" | "Auto" config parameter
- Add MinRefreshRate (Hz) for Auto mode; uses HRT phase-locked tick
  counting to rate-limit sends without accumulation buffers
- Fix high-frequency integration test configs to use newline-separated
  key-value pairs (MARTe2 StandardParser does not treat ';' as delimiter)
- Add 3 new unit tests (AutoMode_Valid, AutoMode_MissingRefreshRate,
  UnknownPublishingMode); all 33 tests passing

WebUI hub:
- Skip ring-buffer LTTB writes when zoom has not been accessed in 10 s,
  reducing idle CPU usage
- Use unsafe float64→bytes reinterpretation to eliminate per-element
  encoding overhead in the hot broadcast path

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Martino Ferrari
2026-05-26 22:29:40 +02:00
parent b465dd680c
commit f85ab8652c
8 changed files with 549 additions and 127 deletions
+92 -15
View File
@@ -4,14 +4,14 @@
# Usage:
# ./run.sh # run MARTe2 app only
# ./run.sh --webui # also start the WebUI Go client in the background
# ./run.sh --nativeui # also start the NativeUI ImGui client in the background
# ./run.sh --qtui # also start the NativeUI Qt client in the background
# ./run.sh --help # show this message
#
# The WebUI is started with three sources:
# The clients are started with three sources:
# Streamer @ 127.0.0.1:44500 (scalar signals, PacketTime, 1 kHz)
# FastStreamer @ 127.0.0.1:44501 (packed arrays, FirstSample/LastSample, 5 kHz)
# FullArrStreamer @ 127.0.0.1:44502 (packed arrays, FullArray, 5 kHz)
# Additional sources and a persistent source list file (--sources-file) can
# be configured directly in the WebUI or by editing this script.
#
# Prerequisites:
# - marte_env.sh must be present in the repo root (sets MARTe2_DIR, etc.)
@@ -27,11 +27,15 @@ REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
# ── Parse arguments ──────────────────────────────────────────────────────────
START_WEBUI=0
START_NATIVEUI=0
START_QTUI=0
for arg in "$@"; do
case "$arg" in
--webui) START_WEBUI=1 ;;
--webui) START_WEBUI=1 ;;
--nativeui) START_NATIVEUI=1 ;;
--qtui) START_QTUI=1 ;;
--help|-h)
sed -n '2,15p' "$0"
sed -n '2,21p' "$0"
exit 0
;;
esac
@@ -84,10 +88,55 @@ WEBUI_DIR="${REPO_ROOT}/Client/WebUI"
WEBUI_BIN="${WEBUI_DIR}/udpstreamer-webui"
if [ "${START_WEBUI}" -eq 1 ] && [ ! -x "${WEBUI_BIN}" ]; then
echo "==> Building WebUI..."
(cd "${WEBUI_DIR}" && go build -o udpstreamer-webui ./...)
(cd "${WEBUI_DIR}" && go build -o udpstreamer-webui ./...)
echo "==> WebUI build done."
fi
# ── Build NativeUI (ImGui) binary (if requested and not already built) ───────
NATIVEUI_DIR="${REPO_ROOT}/Client/NativeUI"
NATIVEUI_BUILD="${NATIVEUI_DIR}/build"
NATIVEUI_BIN="${NATIVEUI_BUILD}/daq_viewer"
if [ "${START_NATIVEUI}" -eq 1 ]; then
if [ ! -x "${NATIVEUI_BIN}" ]; then
echo "==> Building NativeUI/ImGui (cmake)..."
cmake -S "${NATIVEUI_DIR}" -B "${NATIVEUI_BUILD}" -DCMAKE_BUILD_TYPE=Release -Wno-dev
cmake --build "${NATIVEUI_BUILD}" --parallel "$(nproc)"
echo "==> NativeUI/ImGui build done."
else
# Rebuild if sources are newer than the binary
if find "${NATIVEUI_DIR}/src" -name '*.cpp' -o -name '*.h' \
| xargs ls -t 2>/dev/null | head -1 \
| xargs -I{} test {} -nt "${NATIVEUI_BIN}" 2>/dev/null; then
echo "==> Sources changed — rebuilding NativeUI/ImGui..."
cmake --build "${NATIVEUI_BUILD}" --parallel "$(nproc)"
echo "==> NativeUI/ImGui rebuild done."
fi
fi
fi
# ── Build NativeUI-Qt binary (if requested and not already built) ─────────────
QTUI_DIR="${REPO_ROOT}/Client/NativeUI-Qt"
QTUI_BUILD="${QTUI_DIR}/build"
QTUI_BIN="${QTUI_BUILD}/daq_viewer"
if [ "${START_QTUI}" -eq 1 ]; then
if [ ! -x "${QTUI_BIN}" ]; then
echo "==> Building NativeUI/Qt (cmake)..."
cmake -S "${QTUI_DIR}" -B "${QTUI_BUILD}" -DCMAKE_BUILD_TYPE=Release -Wno-dev
cmake --build "${QTUI_BUILD}" --parallel "$(nproc)"
echo "==> NativeUI/Qt build done."
else
# Rebuild if sources are newer than the binary
if find "${QTUI_DIR}/src" "${REPO_ROOT}/Client/NativeUI/src" \
\( -name '*.cpp' -o -name '*.h' \) \
| xargs ls -t 2>/dev/null | head -1 \
| xargs -I{} test {} -nt "${QTUI_BIN}" 2>/dev/null; then
echo "==> Sources changed — rebuilding NativeUI/Qt..."
cmake --build "${QTUI_BUILD}" --parallel "$(nproc)"
echo "==> NativeUI/Qt rebuild done."
fi
fi
fi
# ── Set LD_LIBRARY_PATH ───────────────────────────────────────────────────────
COMP="${MARTe2_Components_DIR}/Build/${TARGET}/Components"
@@ -105,10 +154,6 @@ ${LD_LIBRARY_PATH}"
echo "==> LD_LIBRARY_PATH=${LD_LIBRARY_PATH}"
# ── Create class-name symlinks for MARTe2 auto-loader ────────────────────────
# MARTe2 looks for <ClassName>.so when a class is not yet registered.
# Some components bundle multiple classes into one .so (e.g. WaveformGAM.so
# contains WaveformSin, WaveformChirp, WaveformPointsDef). We need symlinks
# so dlopen("<ClassName>.so") succeeds.
WAVEFORM_DIR="${COMP}/GAMs/WaveformGAM"
for cls in WaveformSin WaveformChirp WaveformPointsDef; do
target="${WAVEFORM_DIR}/${cls}.so"
@@ -117,10 +162,10 @@ for cls in WaveformSin WaveformChirp WaveformPointsDef; do
fi
done
# ── Optionally start WebUI ────────────────────────────────────────────────────
WEBUI_PID=""
if [ "${START_WEBUI}" -eq 1 ]; then
echo "==> Starting WebUI on http://localhost:8080 (source: 127.0.0.1:44500)..."
echo "==> Starting WebUI on http://localhost:8080..."
"${WEBUI_BIN}" \
--source "Streamer@127.0.0.1:44500" \
--source "FastStreamer@127.0.0.1:44501" \
@@ -130,6 +175,38 @@ if [ "${START_WEBUI}" -eq 1 ]; then
echo "==> WebUI PID ${WEBUI_PID}"
fi
# ── Optionally start NativeUI (ImGui) ────────────────────────────────────────
NATIVEUI_PID=""
if [ "${START_NATIVEUI}" -eq 1 ]; then
if [ ! -x "${NATIVEUI_BIN}" ]; then
echo "ERROR: NativeUI/ImGui binary not found at ${NATIVEUI_BIN}" >&2
exit 1
fi
echo "==> Starting NativeUI/ImGui..."
"${NATIVEUI_BIN}" \
"Streamer@127.0.0.1:44500" \
"FastStreamer@127.0.0.1:44501" \
"FullArrStreamer@127.0.0.1:44502" &
NATIVEUI_PID=$!
echo "==> NativeUI/ImGui PID ${NATIVEUI_PID}"
fi
# ── Optionally start NativeUI-Qt ──────────────────────────────────────────────
QTUI_PID=""
if [ "${START_QTUI}" -eq 1 ]; then
if [ ! -x "${QTUI_BIN}" ]; then
echo "ERROR: NativeUI/Qt binary not found at ${QTUI_BIN}" >&2
exit 1
fi
echo "==> Starting NativeUI/Qt..."
"${QTUI_BIN}" \
"Streamer@127.0.0.1:44500" \
"FastStreamer@127.0.0.1:44501" \
"FullArrStreamer@127.0.0.1:44502" &
QTUI_PID=$!
echo "==> NativeUI/Qt PID ${QTUI_PID}"
fi
# ── Launch MARTe2 application ─────────────────────────────────────────────────
MARTE_APP="${MARTe2_DIR}/Build/${TARGET}/App/MARTeApp.ex"
CFG="${SCRIPT_DIR}/TestApp.cfg"
@@ -149,9 +226,9 @@ echo ""
cleanup() {
echo ""
echo "==> Stopping..."
if [ "${START_WEBUI}" -eq 1 ] && kill -0 "${WEBUI_PID}" 2>/dev/null; then
kill "${WEBUI_PID}"
fi
[ -n "${WEBUI_PID}" ] && kill -0 "${WEBUI_PID}" 2>/dev/null && kill "${WEBUI_PID}"
[ -n "${NATIVEUI_PID}" ] && kill -0 "${NATIVEUI_PID}" 2>/dev/null && kill "${NATIVEUI_PID}"
[ -n "${QTUI_PID}" ] && kill -0 "${QTUI_PID}" 2>/dev/null && kill "${QTUI_PID}"
}
trap cleanup EXIT INT TERM