Implemented history writer
This commit is contained in:
@@ -503,6 +503,8 @@ void App::handleJSON(const std::string& json) {
|
||||
else if (type == "stats") { onStats(json); }
|
||||
else if (type == "triggerState") { onTriggerState(json); }
|
||||
else if (type == "zoom") { onZoom(json); }
|
||||
else if (type == "historyZoom") { onHistoryZoom(json); }
|
||||
else if (type == "historyInfo") { onHistoryInfo(json); }
|
||||
else if (type == "maxPointsUpdated") { onMaxPointsUpdated(json); }
|
||||
/* pong: ignore */
|
||||
}
|
||||
@@ -705,4 +707,40 @@ void App::onMaxPointsUpdated(const std::string& json) {
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- History ------------------------------------------------------------ */
|
||||
|
||||
void App::onHistoryZoom(const std::string& json) {
|
||||
ZoomResponse resp;
|
||||
if (!ParseZoom(json, resp)) { return; }
|
||||
|
||||
/* Route the reply to the plot that requested it (via histZoomCache_) */
|
||||
for (int i = 0; i < kMaxPlotSlots; i++) {
|
||||
auto& hc = histZoomCache_[i];
|
||||
if (hc.pending && hc.reqId == resp.reqId) {
|
||||
hc.signals = std::move(resp.signals);
|
||||
hc.t0 = hc.reqT0;
|
||||
hc.t1 = hc.reqT1;
|
||||
hc.valid = true;
|
||||
hc.pending = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void App::onHistoryInfo(const std::string& json) {
|
||||
ParseHistoryInfo(json, historyInfo_);
|
||||
}
|
||||
|
||||
void App::requestHistoryZoom(int plotIdx, double t0, double t1,
|
||||
const std::string& signalsCsv) {
|
||||
if (plotIdx < 0 || plotIdx >= kMaxPlotSlots) { return; }
|
||||
if (!ws_.isConnected() || signalsCsv.empty()) { return; }
|
||||
auto& hc = histZoomCache_[plotIdx];
|
||||
hc.reqId = nextZoomReqId_++;
|
||||
hc.reqT0 = t0;
|
||||
hc.reqT1 = t1;
|
||||
hc.pending = true;
|
||||
ws_.sendText(BuildHistoryZoom(hc.reqId, t0, t1, 2400, signalsCsv));
|
||||
}
|
||||
|
||||
} /* namespace StreamHubClient */
|
||||
|
||||
@@ -232,6 +232,18 @@ public:
|
||||
/** @brief Full key "src:sig" for an assignment (empty if invalid). */
|
||||
std::string slotKey(const PlotAssignment& a) const;
|
||||
|
||||
/* ---- History ------------------------------------------------------------ */
|
||||
|
||||
/** @brief History info received from the hub. */
|
||||
const HistoryInfoMsg& historyInfo() const { return historyInfo_; }
|
||||
|
||||
/** @brief Send a historyZoom request. */
|
||||
void requestHistoryZoom(int plotIdx, double t0, double t1,
|
||||
const std::string& signalsCsv);
|
||||
|
||||
/** @brief Hi-res history zoom cache (per plot, parallel to zoomCache_). */
|
||||
PlotZoomCache& histZoomCache(int i) { return histZoomCache_[i]; }
|
||||
|
||||
/** @brief True if the stats panel is open. */
|
||||
bool& showStats() { return showStats_; }
|
||||
bool& showAddSrc() { return showAddSrc_; }
|
||||
@@ -253,6 +265,8 @@ private:
|
||||
void onStats(const std::string& json);
|
||||
void onTriggerState(const std::string& json);
|
||||
void onZoom(const std::string& json);
|
||||
void onHistoryZoom(const std::string& json);
|
||||
void onHistoryInfo(const std::string& json);
|
||||
void onMaxPointsUpdated(const std::string& json);
|
||||
|
||||
/* ---- Rendering ------------------------------------------------------ */
|
||||
@@ -296,8 +310,12 @@ private:
|
||||
/* Zoom history + hi-res zoom cache */
|
||||
std::vector<std::pair<double,double>> zoomHist_[kMaxPlotSlots];
|
||||
PlotZoomCache zoomCache_[kMaxPlotSlots];
|
||||
PlotZoomCache histZoomCache_[kMaxPlotSlots];
|
||||
uint32_t nextZoomReqId_ = 1;
|
||||
|
||||
/* History info from the hub */
|
||||
HistoryInfoMsg historyInfo_;
|
||||
|
||||
/* UI state */
|
||||
bool showStats_ = false;
|
||||
bool showAddSrc_ = false;
|
||||
|
||||
@@ -30,5 +30,6 @@
|
||||
#define ICON_FA_CHEVRON_RIGHT ">"
|
||||
#define ICON_FA_ARROW_TREND_UP "/\\"
|
||||
#define ICON_FA_ARROW_TREND_DOWN "\\/"
|
||||
#define ICON_FA_ARROWS_UP_DOWN "/\\\\/"
|
||||
#define ICON_FA_ARROWS_UP_DOWN "/\\\\/"
|
||||
#define ICON_FA_CLOCK_ROTATE_LEFT "<-"
|
||||
#endif
|
||||
|
||||
@@ -204,6 +204,13 @@ void RenderPlotPanel(App& app, int plotIdx, bool& paused) {
|
||||
zc.t0 <= app.plotXMin(plotIdx) + 1e-9 &&
|
||||
zc.t1 >= app.plotXMax(plotIdx) - 1e-9));
|
||||
|
||||
/* History zoom cache (disk-backed, for ranges beyond in-memory ring) */
|
||||
auto& hc = app.histZoomCache(plotIdx);
|
||||
const bool useHistData = !trigView && !live && !useZoomData &&
|
||||
hc.valid &&
|
||||
hc.t0 <= app.plotXMin(plotIdx) + 1e-9 &&
|
||||
hc.t1 >= app.plotXMax(plotIdx) - 1e-9;
|
||||
|
||||
/* Fill tStore/vStore[si] from the frozen view (paused) or the live ring */
|
||||
auto readBase = [&](size_t si, const Signal& sig, const std::string& key) {
|
||||
if (paused && snap.valid) {
|
||||
@@ -249,6 +256,18 @@ void RenderPlotPanel(App& app, int plotIdx, bool& paused) {
|
||||
if (!found) {
|
||||
readBase(si, sig, key);
|
||||
}
|
||||
} else if (useHistData) {
|
||||
bool found = false;
|
||||
for (const auto& hs : hc.signals) {
|
||||
if (hs.name != key) continue;
|
||||
tStore[si] = hs.t;
|
||||
vStore[si] = hs.v;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
if (!found) {
|
||||
readBase(si, sig, key);
|
||||
}
|
||||
} else {
|
||||
readBase(si, sig, key);
|
||||
}
|
||||
@@ -375,6 +394,13 @@ void RenderPlotPanel(App& app, int plotIdx, bool& paused) {
|
||||
}
|
||||
}
|
||||
|
||||
/* History indicator */
|
||||
if (useHistData) {
|
||||
ImGui::SameLine();
|
||||
ImGui::TextColored(ImVec4(0.980f,0.702f,0.529f,1.f),
|
||||
ICON_FA_CLOCK_ROTATE_LEFT " HIST");
|
||||
}
|
||||
|
||||
/* Norm mode + cursors toggles */
|
||||
ImGui::SameLine();
|
||||
{
|
||||
@@ -659,7 +685,9 @@ void RenderPlotPanel(App& app, int plotIdx, bool& paused) {
|
||||
}
|
||||
} else if (!live) {
|
||||
/* Non-live: debounced — every zoom/pan settles into one
|
||||
* request, so each zoom level gets fresh resolution. */
|
||||
* request, so each zoom level gets fresh resolution.
|
||||
* If the range is covered by the in-memory ring, use regular
|
||||
* zoom; otherwise try historyZoom (disk-backed). */
|
||||
static double rangeChangedAt[kPlotSlotsMax] = {};
|
||||
static double lastT0[kPlotSlotsMax] = {}, lastT1[kPlotSlotsMax] = {};
|
||||
const double now = ImGui::GetTime();
|
||||
@@ -670,9 +698,17 @@ void RenderPlotPanel(App& app, int plotIdx, bool& paused) {
|
||||
rangeChangedAt[plotIdx] = now;
|
||||
} else if (rangeChangedAt[plotIdx] > 0.0 &&
|
||||
now - rangeChangedAt[plotIdx] > 0.35 &&
|
||||
!zc.pending &&
|
||||
!(zc.valid && zc.t0 == t0 && zc.t1 == t1)) {
|
||||
if (!csv.empty()) { app.requestZoom(plotIdx, t0, t1, csv); }
|
||||
!csv.empty()) {
|
||||
/* Try regular zoom first */
|
||||
if (!zc.pending && !(zc.valid && zc.t0 == t0 && zc.t1 == t1)) {
|
||||
app.requestZoom(plotIdx, t0, t1, csv);
|
||||
}
|
||||
/* Also try history zoom if history is available */
|
||||
const auto& hi = app.historyInfo();
|
||||
if (hi.enabled && !hc.pending &&
|
||||
!(hc.valid && hc.t0 == t0 && hc.t1 == t1)) {
|
||||
app.requestHistoryZoom(plotIdx, t0, t1, csv);
|
||||
}
|
||||
rangeChangedAt[plotIdx] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,6 +253,23 @@ std::string BuildZoom(uint32_t reqId, double t0, double t1, int n,
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string BuildHistoryZoom(uint32_t reqId, double t0, double t1, int n,
|
||||
const std::string& signalsCsv) {
|
||||
char head[256];
|
||||
snprintf(head, sizeof(head),
|
||||
"{\"type\":\"historyZoom\",\"reqId\":%u,"
|
||||
"\"t0\":%.17g,\"t1\":%.17g,\"n\":%d,\"signals\":\"",
|
||||
static_cast<unsigned>(reqId), t0, t1, n);
|
||||
std::string out(head);
|
||||
out += signalsCsv;
|
||||
out += "\"}";
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string BuildHistoryInfo() {
|
||||
return "{\"type\":\"historyInfo\"}";
|
||||
}
|
||||
|
||||
std::string BuildSetMaxPoints(uint32_t n) {
|
||||
char buf[128];
|
||||
snprintf(buf, sizeof(buf), "{\"type\":\"setMaxPoints\",\"maxPoints\":%u}",
|
||||
@@ -530,4 +547,60 @@ bool ParseMaxPointsUpdated(const std::string& json, uint32_t& maxPoints) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ---- historyInfo -------------------------------------------------------- */
|
||||
|
||||
bool ParseHistoryInfo(const std::string& json, HistoryInfoMsg& out) {
|
||||
out.signals.clear();
|
||||
out.enabled = false;
|
||||
|
||||
char tmp[16] = "";
|
||||
jsonGetStr(json.c_str(), "enabled", tmp, sizeof(tmp));
|
||||
out.enabled = (strcmp(tmp, "true") == 0);
|
||||
|
||||
double df = 0.0;
|
||||
jsonGetDouble(json.c_str(), "durationHours", df);
|
||||
out.durationHours = df;
|
||||
|
||||
double decF = 0.0;
|
||||
jsonGetDouble(json.c_str(), "decimation", decF);
|
||||
out.decimation = static_cast<uint32_t>(decF);
|
||||
|
||||
/* Parse "signals":{...} block — same key iteration as zoom */
|
||||
const char *sig = strstr(json.c_str(), "\"signals\":{");
|
||||
if (!sig) { return out.enabled; }
|
||||
sig += 11; /* past '{"signals":{' */
|
||||
|
||||
while (*sig) {
|
||||
/* Find next "key":{ */
|
||||
const char *q = strchr(sig, '"');
|
||||
if (!q) { break; }
|
||||
q++;
|
||||
const char *qEnd = strchr(q, '"');
|
||||
if (!qEnd) { break; }
|
||||
|
||||
HistorySignalRange hr;
|
||||
hr.key = std::string(q, qEnd);
|
||||
|
||||
/* Find the nested object */
|
||||
const char *brace = strchr(qEnd, '{');
|
||||
if (!brace) { break; }
|
||||
|
||||
jsonGetDouble(brace, "t0", hr.t0);
|
||||
jsonGetDouble(brace, "t1", hr.t1);
|
||||
double cntF = 0.0, capF = 0.0;
|
||||
jsonGetDouble(brace, "count", cntF);
|
||||
jsonGetDouble(brace, "capacity", capF);
|
||||
hr.count = static_cast<uint32_t>(cntF);
|
||||
hr.capacity = static_cast<uint32_t>(capF);
|
||||
|
||||
out.signals.push_back(hr);
|
||||
|
||||
/* Advance past the closing brace */
|
||||
const char *end = strchr(brace, '}');
|
||||
if (!end) { break; }
|
||||
sig = end + 1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} /* namespace StreamHubClient */
|
||||
|
||||
@@ -161,6 +161,10 @@ std::string BuildSetTrigger(const std::string& signalKey, const std::string& edg
|
||||
/** 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);
|
||||
/** History zoom: same shape as zoom but reads from disk history. */
|
||||
std::string BuildHistoryZoom(uint32_t reqId, double t0, double t1, int n,
|
||||
const std::string& signalsCsv);
|
||||
std::string BuildHistoryInfo();
|
||||
std::string BuildSetMaxPoints(uint32_t n);
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
@@ -187,6 +191,22 @@ bool ParseTriggerState(const std::string& json, TriggerStateMsg& out);
|
||||
/** @brief Parse a "zoom" response. */
|
||||
bool ParseZoom(const std::string& json, ZoomResponse& out);
|
||||
|
||||
/** @brief Parse a "historyInfo" event. */
|
||||
struct HistorySignalRange {
|
||||
std::string key;
|
||||
double t0 = 0.0, t1 = 0.0;
|
||||
uint32_t count = 0, capacity = 0;
|
||||
};
|
||||
|
||||
struct HistoryInfoMsg {
|
||||
bool enabled = false;
|
||||
double durationHours = 0.0;
|
||||
uint32_t decimation = 0;
|
||||
std::vector<HistorySignalRange> signals;
|
||||
};
|
||||
|
||||
bool ParseHistoryInfo(const std::string& json, HistoryInfoMsg& out);
|
||||
|
||||
/** @brief Parse a "maxPointsUpdated" event. */
|
||||
bool ParseMaxPointsUpdated(const std::string& json, uint32_t& maxPoints);
|
||||
|
||||
|
||||
@@ -209,7 +209,7 @@ StreamHubClient: CMakeFiles/StreamHubClient.dir/StatsPanel.cpp.o
|
||||
StreamHubClient: CMakeFiles/StreamHubClient.dir/build.make
|
||||
StreamHubClient: CMakeFiles/StreamHubClient.dir/compiler_depend.ts
|
||||
StreamHubClient: libimgui_lib.a
|
||||
StreamHubClient: /usr/lib/libSDL2-2.0.so.0.3200.68
|
||||
StreamHubClient: /usr/lib/libSDL2-2.0.so.0.3200.70
|
||||
StreamHubClient: /usr/lib/libGLX.so
|
||||
StreamHubClient: /usr/lib/libOpenGL.so
|
||||
StreamHubClient: CMakeFiles/StreamHubClient.dir/link.txt
|
||||
|
||||
@@ -2451,7 +2451,7 @@ StreamHubClient
|
||||
/usr/lib/libGLX.so
|
||||
/usr/lib/libGLdispatch.so.0
|
||||
/usr/lib/libOpenGL.so
|
||||
/usr/lib/libSDL2-2.0.so.0.3200.68
|
||||
/usr/lib/libSDL2-2.0.so.0.3200.70
|
||||
/usr/lib/libX11.so.6
|
||||
/usr/lib/libXau.so.6
|
||||
/usr/lib/libXdmcp.so.6
|
||||
|
||||
@@ -2442,7 +2442,7 @@ StreamHubClient: /usr/lib/Scrt1.o \
|
||||
/usr/lib/libGLX.so \
|
||||
/usr/lib/libGLdispatch.so.0 \
|
||||
/usr/lib/libOpenGL.so \
|
||||
/usr/lib/libSDL2-2.0.so.0.3200.68 \
|
||||
/usr/lib/libSDL2-2.0.so.0.3200.70 \
|
||||
/usr/lib/libX11.so.6 \
|
||||
/usr/lib/libXau.so.6 \
|
||||
/usr/lib/libXdmcp.so.6 \
|
||||
@@ -2487,7 +2487,7 @@ CMakeFiles/StreamHubClient.dir/StatsPanel.cpp.o:
|
||||
|
||||
/usr/lib/libXau.so.6:
|
||||
|
||||
/usr/lib/libSDL2-2.0.so.0.3200.68:
|
||||
/usr/lib/libSDL2-2.0.so.0.3200.70:
|
||||
|
||||
/usr/lib/libGLdispatch.so.0:
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
/usr/bin/c++ -O3 -DNDEBUG -Wl,--dependency-file=CMakeFiles/StreamHubClient.dir/link.d CMakeFiles/StreamHubClient.dir/main.cpp.o CMakeFiles/StreamHubClient.dir/App.cpp.o CMakeFiles/StreamHubClient.dir/WSClient.cpp.o CMakeFiles/StreamHubClient.dir/Protocol.cpp.o CMakeFiles/StreamHubClient.dir/SourcePanel.cpp.o CMakeFiles/StreamHubClient.dir/PlotPanel.cpp.o CMakeFiles/StreamHubClient.dir/TriggerPanel.cpp.o CMakeFiles/StreamHubClient.dir/StatsPanel.cpp.o -o StreamHubClient libimgui_lib.a /usr/lib/libSDL2-2.0.so.0.3200.68 -lpthread /usr/lib/libGLX.so /usr/lib/libOpenGL.so
|
||||
/usr/bin/c++ -O3 -DNDEBUG -Wl,--dependency-file=CMakeFiles/StreamHubClient.dir/link.d CMakeFiles/StreamHubClient.dir/main.cpp.o CMakeFiles/StreamHubClient.dir/App.cpp.o CMakeFiles/StreamHubClient.dir/WSClient.cpp.o CMakeFiles/StreamHubClient.dir/Protocol.cpp.o CMakeFiles/StreamHubClient.dir/SourcePanel.cpp.o CMakeFiles/StreamHubClient.dir/PlotPanel.cpp.o CMakeFiles/StreamHubClient.dir/TriggerPanel.cpp.o CMakeFiles/StreamHubClient.dir/StatsPanel.cpp.o -o StreamHubClient libimgui_lib.a /usr/lib/libSDL2-2.0.so.0.3200.70 -lpthread /usr/lib/libGLX.so /usr/lib/libOpenGL.so
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user