Implemented new C++ logic

This commit is contained in:
Martino Ferrari
2026-06-12 15:25:13 +02:00
parent 617b5bd712
commit f25bd7f08e
220 changed files with 39185 additions and 850 deletions
+236
View File
@@ -0,0 +1,236 @@
/**
* @file StreamHub.h
* @brief Top-level orchestrator: N UDP sources, M WebSocket clients, push loop, trigger.
*
* StreamHub owns:
* - Up to kMaxSessions UDPSourceSessions (one per MARTe2 UDPStreamer source)
* - WSServer (multi-client WebSocket server)
* - TriggerEngine (continuous / armed / triggered FSM)
*
* The push loop runs at pushRateHz Hz and:
* 1. For each configured session, reads ring buffers, applies LTTB decimation,
* serializes a binary data frame, broadcasts to all WS clients.
* 2. At statsRateHz intervals, serializes a JSON stats frame and broadcasts.
*
* Commands are received as JSON text frames from any connected WS client via the
* WSCommandCallback interface.
*/
#ifndef STREAMHUB_H_
#define STREAMHUB_H_
#include "CompilerTypes.h"
#include "FastPollingMutexSem.h"
#include "StreamString.h"
#include "StructuredDataI.h"
#include "Threads.h"
#include "UDPSourceSession.h"
#include "WSServer.h"
#include "TriggerEngine.h"
namespace StreamHub {
using MARTe::uint8;
using MARTe::uint16;
using MARTe::uint32;
using MARTe::uint64;
using MARTe::float64;
using MARTe::StreamString;
using MARTe::FastPollingMutexSem;
using MARTe::StructuredDataI;
/** Maximum number of simultaneously connected UDPStreamer sources. */
static const uint32 kMaxSessions = 32u;
/**
* @brief Top-level StreamHub orchestrator.
*
* Usage:
* StreamHub hub;
* hub.Initialise(cfg); // parse WSPort, MaxPoints, PushRate, Sources
* hub.Run(); // blocking push loop; returns on Stop()
*/
class StreamHub : public WSCommandCallback {
public:
StreamHub();
virtual ~StreamHub();
/**
* @brief Configure the hub from a MARTe2 StructuredDataI.
*
* Expected keys:
* WSPort (uint32, default 8090)
* MaxPoints (uint32, default 20000) — ring buffer capacity per signal
* PushRate (uint32, default 30) — push loop rate in Hz
* MaxPushPoints (uint32, default 500) — LTTB threshold for live push
* StatsRate (uint32, default 1) — stats broadcast rate in Hz
* +Sources { +<id> { Label=...; Addr=...; Port=... } }
*
* @return true on success.
*/
bool Initialise(StructuredDataI &cfg);
/**
* @brief Start the WebSocket server and run the push loop (blocking).
* Returns when Stop() is called (e.g. from SIGINT handler).
*/
bool Run();
/**
* @brief Signal the push loop to exit. Thread-safe.
*/
void Stop();
/* ---- WSCommandCallback (called from WS client read threads) ---------- */
virtual void OnWSCommand(const char *json, uint32 len, uint32 slotIdx);
virtual void OnWSClientConnected();
virtual void OnWSClientDisconnected();
private:
/* ---- Push loop helpers ----------------------------------------------- */
/** One push tick: for each session emit a binary frame; handle trigger. */
void PushData();
/** Serialize one binary data frame for session idx into pushBuf_. */
uint32 SerializeBinaryFrame(uint32 sessionIdx, uint8 *buf, uint32 bufSize);
/** Broadcast JSON stats for all sessions. */
void PushStats();
/** Broadcast {"type":"sources"} listing. */
void BroadcastSources();
/** Broadcast {"type":"config","sourceId":...} for one session. */
void BroadcastConfig(uint32 sessionIdx);
/* ---- Trigger (push loop side) ----------------------------------------- */
/**
* @brief Per-tick trigger servicing: finalize captures, auto-rearm,
* broadcast state transitions.
*/
void TriggerTick(float64 wallNowS);
/** Broadcast {"type":"triggerState",...} reflecting the current FSM. */
void BroadcastTriggerState();
/**
* @brief Build and broadcast the version=2 binary capture frame:
* [u8 2][f64 trigTime][f64 preSec][f64 postSec][u32 nSig]
* {[u16 keyLen][fullKey][u32 N][t f64×N][v f64×N]}
*/
void BroadcastTriggerCapture(float64 trigTime, float64 preSec,
float64 postSec);
/* ---- Command handlers (called from OnWSCommand) ---------------------- */
void HandleAddSource(const char *json);
void HandleRemoveSource(const char *json);
void HandleSaveSources();
void HandleGetSources();
void HandleGetConfig(const char *json);
void HandleGetStats();
void HandleArm();
void HandleDisarm();
void HandleRearm();
void HandleTrigStop(const char *json);
void HandleSetTrigger(const char *json);
void HandleZoom(const char *json, uint32 slotIdx);
void HandleSetMaxPoints(const char *json);
void HandlePing(uint32 slotIdx);
/* ---- Sources persistence (Go SourceConfig schema) --------------------- */
/**
* @brief Start a new dynamic session from "host:port" + options.
* Generates the session id ("s1", "s2", ...).
* @return true if the session was started.
*/
bool AddSourceInternal(const char *label, const char *addrPort,
const char *mcGroup, uint16 dataPort);
/**
* @brief Load sources from sourcesFile_ (JSON array of
* {"label","addr","multicastGroup","dataPort"}) and start them.
*/
void LoadSourcesFile();
/* ---- Tiny JSON helpers ----------------------------------------------- */
/**
* @brief Extract a string field from flat JSON: "key":"value".
* @return true if found; value written into out (up to outSize-1 chars).
*/
static bool JsonGetString(const char *json, const char *key,
char *out, uint32 outSize);
/**
* @brief Extract a numeric field from flat JSON: "key":number.
* @return true if found.
*/
static bool JsonGetFloat(const char *json, const char *key, float64 &out);
static bool JsonGetUint32(const char *json, const char *key, uint32 &out);
static bool JsonGetBool(const char *json, const char *key, bool &out);
/* ---- Members --------------------------------------------------------- */
UDPSourceSession sessions_[kMaxSessions];
uint32 numSessions_; ///< Count of active sessions (not an upper slot bound)
bool sessionActive_[kMaxSessions]; ///< Per-slot in-use flag (slots are not compacted on removal)
bool configBroadcast_[kMaxSessions]; ///< True once BroadcastConfig was sent for this session
FastPollingMutexSem sessionsMutex_; ///< Serializes add/remove (WS threads); push thread reads flags lock-free
WSServer wsServer_;
TriggerEngine trigger_;
/* Configuration */
uint16 wsPort_;
uint32 maxPoints_;
uint32 pushRateHz_;
uint32 maxPushPoints_;
uint32 statsRateHz_;
uint32 ringTemporal_; ///< Ring capacity for multi-element (waveform) signals
uint32 ringScalar_; ///< Ring capacity for scalar signals
StreamString sourcesFile_; ///< Persistent dynamic source list (JSON)
uint32 nextSourceId_; ///< Counter for generated session ids ("sN")
/* Push loop state */
volatile bool running_;
uint32 tickCount_; ///< incremented each push tick
/* Deferred setMaxPoints (applied by the push loop, not the WS thread) */
volatile bool pendingMaxPointsSet_;
uint32 pendingMaxPoints_;
/* Per-push scratch buffer (~8 MiB): one binary frame at a time */
static const uint32 kPushBufSize = 8u * 1024u * 1024u;
uint8 *pushBuf_;
/* Decimated output scratch (LTTB): maxPushPoints × 2 arrays per signal */
float64 *lttbT_;
float64 *lttbV_;
/* Ring-buffer read scratch for the push loop (preallocated in Initialise) */
static const uint32 kPushScratchPts = 262144u; ///< Max new pts/signal/tick
float64 *pushT_;
float64 *pushV_;
/* Per-(session, signal) monotonic read cursors for ReadSignalSince.
* Only new samples since the last tick are pushed (fixes re-LTTB
* corruption of overlapping windows). */
uint64 pushCursor_[kMaxSessions][UDPSS_MAX_SIGNALS];
/* Trigger servicing state (push thread only) */
static const uint32 kTrigCapturePts = 20000u; ///< LTTB cap per captured signal
TrigState lastTrigState_; ///< Last broadcast FSM state
bool rearmPending_; ///< Normal-mode auto-rearm scheduled
float64 rearmAtWallS_; ///< Wall time of the scheduled auto-rearm
};
} /* namespace StreamHub */
#endif /* STREAMHUB_H_ */