StreamHub: per-signal calibration, config reload, whitespace-tolerant JSON
- Add CalibrationEntry (heap-allocated array[256], char[] fields to stay within the 133 MB struct's canonical address limit) plus calibrationMutex_ and numCalibration_. - Implement SetCalibrationEntry/ClearCalibration, BroadcastCalibration, BroadcastConfigAck, HandleSetCalibration, HandleReloadConfig. - Wire setCalibration and reloadConfig into OnWSCommand dispatch. - Broadcast calibration to each newly connected client after triggerState. - Fix JSON round-trip bug: replace JsonGetString/JsonGetBool helpers with a shared whitespace-tolerant JsonFindValue (tolerates "key" : "value" as written by HandleSaveSources); add JsonIsFinite (no <cmath>). - Extend LoadSourcesFile(bool skipActive) to also parse calibration blocks; SourceIsActive checks live sessions before starting a duplicate. - Extend HandleSaveSources to persist calibration blocks; emit configSaved ack. - Reload semantics: calibration replaced wholesale, sources added only. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
ffe7cb1cc5
commit
cdafb877a3
@@ -44,6 +44,32 @@ using MARTe::StructuredDataI;
|
||||
/** Maximum number of simultaneously connected UDPStreamer sources. */
|
||||
static const uint32 kMaxSessions = 32u;
|
||||
|
||||
/** Maximum number of stored per-signal calibration entries. */
|
||||
static const uint32 kMaxCalibration = 256u;
|
||||
|
||||
/** Maximum length of a calibration unit override (mirrors the Go maxUnitLen). */
|
||||
static const uint32 kMaxUnitLen = 16u;
|
||||
|
||||
/**
|
||||
* @brief One per-signal affine calibration: y = raw*scale + offset.
|
||||
*
|
||||
* Keyed by the source LABEL (not the runtime "sN" id, which is assigned in
|
||||
* add-order and would rebind if the source list were reordered) and by the
|
||||
* BASE signal name (no "[i]" suffix: one entry covers a whole array signal).
|
||||
*
|
||||
* Fixed-size char arrays are used deliberately: embedding 256 StreamString
|
||||
* (each of which allocates its own heap buffer) into a 133 MB struct that is
|
||||
* itself heap-allocated pushes offsets beyond the canonical x86-64 address
|
||||
* limit and causes a SIGSEGV in the constructor.
|
||||
*/
|
||||
struct CalibrationEntry {
|
||||
char source[128]; ///< Source label
|
||||
char signal[128]; ///< Base signal name (no "[i]" suffix)
|
||||
char unit[17]; ///< Unit override (max kMaxUnitLen chars + NUL)
|
||||
MARTe::float64 scale;
|
||||
MARTe::float64 offset;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Top-level StreamHub orchestrator.
|
||||
*
|
||||
@@ -108,6 +134,12 @@ private:
|
||||
/** Broadcast {"type":"config","sourceId":...} for one session. */
|
||||
void BroadcastConfig(uint32 sessionIdx);
|
||||
|
||||
/** Broadcast {"type":"calibration","cal":[...]} to all clients. */
|
||||
void BroadcastCalibration();
|
||||
|
||||
/** Broadcast {"type":"configSaved"|"configReloaded","ok":...} to all clients. */
|
||||
void BroadcastConfigAck(const char *msgType, bool ok, const char *errText);
|
||||
|
||||
/* ---- Trigger (push loop side) ----------------------------------------- */
|
||||
|
||||
/**
|
||||
@@ -146,6 +178,8 @@ private:
|
||||
void HandleHistoryInfo(uint32 slotIdx);
|
||||
void HandleSetMaxPoints(const char *json);
|
||||
void HandlePing(uint32 slotIdx);
|
||||
void HandleSetCalibration(const char *json);
|
||||
void HandleReloadConfig();
|
||||
|
||||
/* ---- Binary recorder commands --------------------------------------- */
|
||||
|
||||
@@ -172,10 +206,29 @@ private:
|
||||
const char *mcGroup, uint16 dataPort);
|
||||
|
||||
/**
|
||||
* @brief Load sources from sourcesFile_ (JSON array of
|
||||
* {"label","addr","multicastGroup","dataPort"}) and start them.
|
||||
* @brief Load sources and calibration from sourcesFile_ (a flat JSON array
|
||||
* of {"label","addr","multicastGroup","dataPort"} source blocks and
|
||||
* {"source","signal","scale","offset","unit"} calibration blocks).
|
||||
* @param skipActive when true, a source whose "host:port" is already
|
||||
* streaming is left alone instead of being started a second time.
|
||||
* @return true if the file was read.
|
||||
*/
|
||||
void LoadSourcesFile();
|
||||
bool LoadSourcesFile(bool skipActive);
|
||||
|
||||
/** @return true if a session for this "host:port" is already active. */
|
||||
bool SourceIsActive(const char *addrPort);
|
||||
|
||||
/**
|
||||
* @brief Store or replace one calibration entry. An identity entry
|
||||
* (scale 1, offset 0, empty unit) removes any stored one instead.
|
||||
* @return true if the entry was valid (and therefore stored or removed).
|
||||
*/
|
||||
bool SetCalibrationEntry(const char *source, const char *signal,
|
||||
MARTe::float64 scale, MARTe::float64 offset,
|
||||
const char *unit);
|
||||
|
||||
/** Drop every calibration entry (used by reload, which replaces wholesale). */
|
||||
void ClearCalibration();
|
||||
|
||||
/* ---- Tiny JSON helpers ----------------------------------------------- */
|
||||
|
||||
@@ -220,6 +273,10 @@ private:
|
||||
StreamString sourcesFile_; ///< Persistent dynamic source list (JSON)
|
||||
uint32 nextSourceId_; ///< Counter for generated session ids ("sN")
|
||||
|
||||
CalibrationEntry *calibration_; ///< Heap-allocated array[kMaxCalibration]
|
||||
uint32 numCalibration_;
|
||||
FastPollingMutexSem calibrationMutex_; ///< Serializes calibration reads/writes
|
||||
|
||||
/* Push loop state */
|
||||
volatile bool running_;
|
||||
uint32 tickCount_; ///< incremented each push tick
|
||||
|
||||
Reference in New Issue
Block a user