Implemented new C++ logic
This commit is contained in:
@@ -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_ */
|
||||
Reference in New Issue
Block a user