/** * @file Model.h * @brief Domain types for the Qt StreamHub client. * * Mirrors the ImGui client's App.h domain model, but uses QColor instead of * ImVec4 and adds no Qt widget dependencies (pure data). */ #pragma once #include "Protocol.h" // SignalMeta, SourceStats (reused, framework-free) #include "SignalBuffer.h" // SignalBuffer, LTTBDecimate (reused) #include #include #include #include namespace shq { using StreamHubClient::SignalMeta; using StreamHubClient::SourceStats; using StreamHubClient::SignalBuffer; /** One signal as known to the client. */ struct SignalView { SignalMeta meta; SignalBuffer buf{1000000}; QColor color{137, 180, 250}; /* Catppuccin blue */ double lineWidth = 1.5; int marker = -1; /* -1 = none, else MarkerStyle index */ bool visible = true; }; /** One connected UDPStreamer source. */ struct Source { std::string id; std::string label; std::string addr; std::string state = "disconnected"; uint32_t port = 0; bool configured = false; int publishMode = 0; std::vector signals; SourceStats stats; }; /** Trigger configuration + live state (hub-side trigger semantics). */ struct TriggerCfgState { /* Config (sent to hub via setTrigger) */ std::string signalKey; /* full key "src:sig" or "src:sig[i]" */ int edge = 0; /* 0=rising 1=falling 2=both */ double threshold = 0.0; double windowSec = 0.1; /* 100 µs .. 10 s */ double prePercent = 20.0; bool single = false; /* false=normal true=single */ /* Live (driven by hub triggerState broadcasts) */ std::string status = "idle"; /* idle|armed|collecting|triggered */ bool stopped = false; bool hasTrigTime = false; double trigTime = 0.0; }; /** Per-signal vertical scale state (oscilloscope style). */ struct VScale { int mode = 0; /* 0=auto 1=range 2=manual */ double divValue = 1.0; /* units per division (manual) */ double offset = 0.0; /* raw value at center (manual) */ double screenPos = 0.0; /* position offset in divisions from center */ bool digitalInMixed = false; /* Resolved each frame: */ double resolvedDiv = 1.0; double resolvedOffset = 0.0; }; /** Assignment of one signal to a plot panel. */ struct PlotAssignment { int sourceIdx = -1; int signalIdx = -1; VScale vs; }; /*---------------------------------------------------------------------------*/ /* Plot layouts (mirror the web UI: cols×rows) */ /*---------------------------------------------------------------------------*/ enum class PlotLayout { k1x1 = 0, k2x1, k1x2, k3x1, k1x3, k2x2, k4x1, k1x4, kCount }; inline const char* const* layoutNames() { static const char* const names[] = { "1\u00d71", "2\u00d71", "1\u00d72", "3\u00d71", "1\u00d73", "2\u00d72", "4\u00d71", "1\u00d74" }; return names; } inline void layoutDims(PlotLayout l, int& cols, int& rows) { switch (l) { case PlotLayout::k1x1: cols = 1; rows = 1; break; case PlotLayout::k2x1: cols = 2; rows = 1; break; case PlotLayout::k1x2: cols = 1; rows = 2; break; case PlotLayout::k3x1: cols = 3; rows = 1; break; case PlotLayout::k1x3: cols = 1; rows = 3; break; case PlotLayout::k2x2: cols = 2; rows = 2; break; case PlotLayout::k4x1: cols = 4; rows = 1; break; case PlotLayout::k1x4: cols = 1; rows = 4; break; default: cols = 1; rows = 1; break; } } inline int layoutSlots(PlotLayout l) { int c, r; layoutDims(l, c, r); return c * r; } static const int kMaxPlotSlots = 8; } /* namespace shq */