/** * @file Protocol.h * @brief StreamHub wire protocol: binary frame parser + JSON builder/parser. */ #pragma once #include "SignalBuffer.h" #include #include #include #include namespace StreamHubClient { /* ── Signal metadata (mirrors UDPSSignalDescriptor fields used by UI) ─────── */ struct SignalMeta { std::string name; std::string unit; int typeCode = 0; int quantType = 0; int timeMode = 0; int timeSignalIdx = -1; float samplingRate = 0.f; double rangeMin = 0.0; double rangeMax = 0.0; uint32_t numRows = 1u; uint32_t numCols = 1u; uint32_t numElements = 1u; /* numRows × numCols */ }; /* ── Per-source statistics (hub "stats" broadcast, Go field names) ────────── */ struct SourceStats { std::string state = "disconnected"; uint64_t totalReceived = 0; uint64_t totalLost = 0; double rateHz = 0.0; double rateStdHz = 0.0; double fragsPerCycle = 0.0; double bytesPerCycle = 0.0; double cycleAvgMs = 0.0; double cycleStdMs = 0.0; double cycleMinMs = 0.0; double cycleMaxMs = 0.0; double cycleHistMin = 0.0; double cycleHistMax = 0.0; double cycleHist[20] = {}; }; /* ── Source descriptor ────────────────────────────────────────────────────── */ struct SourceInfo { std::string id; std::string label; std::string addr; std::string state; uint32_t port = 0; }; /* ── Decoded push-frame signal data ──────────────────────────────────────── */ struct FrameSignal { std::string name; std::vector t; std::vector v; }; struct DataFrame { std::string sourceId; std::vector signals; }; /* ── Zoom response ────────────────────────────────────────────────────────── */ struct ZoomSignal { std::string name; /* full key "src:sig" */ std::vector t; std::vector v; }; struct ZoomResponse { uint32_t reqId = 0; std::vector signals; }; /* ── Trigger capture (binary frame version=2) ─────────────────────────────── */ struct CaptureSignal { std::string key; /* full key "src:sig" */ std::vector t; std::vector v; }; struct CaptureFrame { double trigTime = 0.0; double preSec = 0.0; double postSec = 0.0; std::vector signals; }; /* ── Trigger state event ──────────────────────────────────────────────────── */ struct TriggerStateMsg { std::string state; /* idle|armed|collecting|triggered */ std::string mode = "normal"; /* normal|single */ bool stopped = false; bool hasTrigTime = false; double trigTime = 0.0; }; /*---------------------------------------------------------------------------*/ /* Binary frame parser */ /*---------------------------------------------------------------------------*/ /** * @brief Parse one StreamHub binary push frame. * * Format: * [1] version | [1] idLen | [idLen] sourceId | [4] numSignals * per signal: [2] keyLen | [key] | [4] pairCount | [N×8] t | [N×8] v * * @return false if the frame is malformed. */ bool ParseBinaryFrame(const uint8_t* buf, size_t len, DataFrame& out); /** * @brief Parse a version=2 trigger-capture binary frame. * * Format: * [1] version=2 | [8] trigTime | [8] preSec | [8] postSec | [4] nSig * per signal: [2] keyLen | [key] | [4] N | [N×8] t | [N×8] v * * @return false if the frame is malformed or version != 2. */ bool ParseCaptureFrame(const uint8_t* buf, size_t len, CaptureFrame& out); /*---------------------------------------------------------------------------*/ /* JSON command builders */ /*---------------------------------------------------------------------------*/ std::string BuildPing(); std::string BuildGetSources(); std::string BuildGetConfig(const std::string& sourceId); std::string BuildGetStats(); std::string BuildAddSource(const std::string& label, const std::string& addr, const std::string& multicastGroup = std::string(), uint16_t dataPort = 0); std::string BuildRemoveSource(const std::string& id); std::string BuildArm(); std::string BuildDisarm(); std::string BuildRearm(); std::string BuildTrigStop(bool stopped); std::string BuildSaveSources(); /** edge: "rising" | "falling" | "both"; mode: "normal" | "single". */ std::string BuildSetTrigger(const std::string& signalKey, const std::string& edge, double threshold, double windowSec, double prePercent, const std::string& mode); /** signalsCsv: comma-separated full keys "src:sig,src:sig2"; n<=0 → raw. */ std::string BuildZoom(uint32_t reqId, double t0, double t1, int n, const std::string& signalsCsv); std::string BuildSetMaxPoints(uint32_t n); /*---------------------------------------------------------------------------*/ /* JSON event parsers */ /*---------------------------------------------------------------------------*/ /** @brief Extract the "type" field from a JSON text frame. */ std::string ParseType(const std::string& json); /** @brief Parse a "sources" event. */ bool ParseSources(const std::string& json, std::vector& out); /** @brief Parse a "config" event (one source). */ bool ParseConfig(const std::string& json, std::string& sourceId, int& publishMode, std::vector& signals); /** @brief Parse a "stats" event. */ bool ParseStats(const std::string& json, std::vector>& out); /** @brief Parse a "triggerState" event. */ bool ParseTriggerState(const std::string& json, TriggerStateMsg& out); /** @brief Parse a "zoom" response. */ bool ParseZoom(const std::string& json, ZoomResponse& out); /** @brief Parse a "maxPointsUpdated" event. */ bool ParseMaxPointsUpdated(const std::string& json, uint32_t& maxPoints); } /* namespace StreamHubClient */