121 lines
3.9 KiB
C++
121 lines
3.9 KiB
C++
/**
|
||
* @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 <QColor>
|
||
#include <string>
|
||
#include <vector>
|
||
#include <cstdint>
|
||
|
||
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<SignalView> 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 */
|