Implemented new C++ logic

This commit is contained in:
Martino Ferrari
2026-06-12 15:25:13 +02:00
parent 617b5bd712
commit f25bd7f08e
220 changed files with 39185 additions and 850 deletions
+184
View File
@@ -0,0 +1,184 @@
/**
* @file TriggerPanel.cpp
* @brief Trigger configuration bar — hub-side trigger semantics.
*
* The trigger runs in the C++ StreamHub: this panel only edits the config
* (signal/edge/threshold/window/pre%/mode), sends setTrigger + arm/disarm/
* rearm/trigStop commands, and reflects the hub triggerState broadcasts.
*/
#include "TriggerPanel.h"
#include "App.h"
#include "Protocol.h"
#include "imgui.h"
#include "Icons.h"
#include <cstdio>
#include <cstring>
#include <cmath>
namespace StreamHubClient {
/* Window presets (mirrors the web UI: 100 µs .. 10 s) */
static const double kWinVals[] = {1e-4, 1e-3, 1e-2, 1e-1, 1.0, 10.0};
static const char* kWinLabels[] = {"100 µs", "1 ms", "10 ms", "100 ms", "1 s", "10 s"};
static const int kNumWins = 6;
static const char* kEdgeLabels[] = {ICON_FA_ARROW_TREND_UP " Rising",
ICON_FA_ARROW_TREND_DOWN " Falling",
ICON_FA_ARROWS_UP_DOWN " Both"};
static const char* kEdgeWire[] = {"rising", "falling", "both"};
/* Send the current config to the hub. */
static void sendTrigConfig(App& app) {
auto& trig = app.trigger();
if (trig.signalKey.empty()) { return; }
app.ws().sendText(BuildSetTrigger(
trig.signalKey, kEdgeWire[trig.edge], trig.threshold,
trig.windowSec, trig.prePercent,
trig.single ? "single" : "normal"));
}
void RenderTriggerPanel(App& app) {
auto& trig = app.trigger();
auto& sources = app.sources();
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.15f, 0.15f, 0.20f, 1.f));
ImGui::BeginChild("##trigbar", ImVec2(0.f, 90.f), true);
ImGui::PopStyleColor();
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(6.f, 4.f));
bool cfgChanged = false;
/* ── Signal selector (full keys "src:sig") ─────────────────────────── *
* Vector signals are flattened time-series on the hub: one key per
* signal; the trigger engine checks every decoded sample of it. */
ImGui::SetNextItemWidth(180.f);
if (ImGui::BeginCombo("Signal##trig", trig.signalKey.empty()
? "<select>" : trig.signalKey.c_str())) {
for (const auto& src : sources) {
for (const auto& sig : src.signals) {
char key[192];
snprintf(key, sizeof(key), "%s:%s",
src.id.c_str(), sig.meta.name.c_str());
bool sel = (trig.signalKey == key);
if (ImGui::Selectable(key, sel)) {
trig.signalKey = key;
cfgChanged = true;
}
if (sel) { ImGui::SetItemDefaultFocus(); }
}
}
ImGui::EndCombo();
}
ImGui::SameLine();
/* ── Edge ─────────────────────────────────────────────────────────── */
ImGui::SetNextItemWidth(95.f);
if (ImGui::Combo("Edge##trig", &trig.edge, kEdgeLabels, 3)) {
cfgChanged = true;
}
ImGui::SameLine();
/* ── Threshold ───────────────────────────────────────────────────── */
ImGui::SetNextItemWidth(80.f);
if (ImGui::InputDouble("Thr##trig", &trig.threshold, 0.0, 0.0, "%.4g",
ImGuiInputTextFlags_EnterReturnsTrue)) {
cfgChanged = true;
}
ImGui::SameLine();
/* ── Window ──────────────────────────────────────────────────────── */
int winIdx = 3; /* default 100 ms */
for (int w = 0; w < kNumWins; w++) {
if (std::fabs(trig.windowSec - kWinVals[w]) < kWinVals[w] * 1e-6) {
winIdx = w; break;
}
}
ImGui::SetNextItemWidth(85.f);
if (ImGui::Combo("Win##trig", &winIdx, kWinLabels, kNumWins)) {
trig.windowSec = kWinVals[winIdx];
cfgChanged = true;
}
ImGui::SameLine();
/* ── Pre-trigger % ───────────────────────────────────────────────── */
ImGui::SetNextItemWidth(100.f);
float preP = static_cast<float>(trig.prePercent);
if (ImGui::SliderFloat("Pre%##trig", &preP, 0.f, 100.f, "%.0f%%")) {
trig.prePercent = static_cast<double>(preP);
}
if (ImGui::IsItemDeactivatedAfterEdit()) { cfgChanged = true; }
ImGui::SameLine();
/* ── Mode ────────────────────────────────────────────────────────── */
ImGui::TextUnformatted("Mode:");
ImGui::SameLine();
int singleInt = trig.single ? 1 : 0;
if (ImGui::RadioButton("Normal", &singleInt, 0)) { cfgChanged = true; }
ImGui::SameLine();
if (ImGui::RadioButton("Single", &singleInt, 1)) { cfgChanged = true; }
trig.single = (singleInt != 0);
ImGui::NewLine();
/* ── Status badge ────────────────────────────────────────────────── */
ImVec4 badgeColor;
const char* badgeText;
if (trig.status == "armed") { badgeColor = ImVec4(1.f,0.8f,0.f,1.f); badgeText = "ARMED"; }
else if (trig.status == "collecting") { badgeColor = ImVec4(0.4f,0.7f,1.f,1.f); badgeText = "COLLECTING"; }
else if (trig.status == "triggered") { badgeColor = ImVec4(0.2f,0.9f,0.3f,1.f); badgeText = "TRIGGERED"; }
else { badgeColor = ImVec4(0.6f,0.6f,0.6f,1.f); badgeText = "IDLE"; }
ImGui::TextColored(badgeColor, "[%s]", badgeText);
if (trig.hasTrigTime &&
(trig.status == "collecting" || trig.status == "triggered")) {
ImGui::SameLine();
ImGui::TextDisabled("t=%.6f", trig.trigTime);
}
ImGui::SameLine();
/* ── Arm / Disarm / Rearm / Stop ─────────────────────────────────── */
bool canArm = !trig.signalKey.empty();
if (!canArm) { ImGui::BeginDisabled(); }
if (ImGui::Button(ICON_FA_BOLT " Arm")) {
sendTrigConfig(app);
app.ws().sendText(BuildArm());
}
if (!canArm) { ImGui::EndDisabled(); }
ImGui::SameLine();
if (ImGui::Button("Disarm")) {
app.ws().sendText(BuildDisarm());
}
ImGui::SameLine();
if (trig.single && trig.status == "triggered") {
if (ImGui::Button("Rearm")) {
app.ws().sendText(BuildRearm());
}
ImGui::SameLine();
}
if (!trig.single) {
if (ImGui::Button(trig.stopped ? ICON_FA_PLAY " Run" : ICON_FA_STOP " Stop")) {
trig.stopped = !trig.stopped;
app.ws().sendText(BuildTrigStop(trig.stopped));
}
ImGui::SameLine();
}
ImGui::NewLine();
/* Config edits while armed take effect immediately on the hub */
if (cfgChanged && !trig.signalKey.empty()) {
sendTrigConfig(app);
}
ImGui::PopStyleVar();
ImGui::EndChild();
ImGui::Separator();
}
} /* namespace StreamHubClient */