From d448bbd7108310fdb4b6f2c0ea2df1b00e0b17f2 Mon Sep 17 00:00:00 2001 From: Joshua Wise Date: Thu, 26 Sep 2024 22:32:54 -0700 Subject: [PATCH 01/25] WIP: StreamBrowserDialog gets support for some rich UI on oscilloscopes --- src/ngscopeclient/StreamBrowserDialog.cpp | 52 ++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/src/ngscopeclient/StreamBrowserDialog.cpp b/src/ngscopeclient/StreamBrowserDialog.cpp index c82fe439..709f9414 100644 --- a/src/ngscopeclient/StreamBrowserDialog.cpp +++ b/src/ngscopeclient/StreamBrowserDialog.cpp @@ -69,13 +69,63 @@ bool StreamBrowserDialog::DoRender() auto insts = m_session.GetInstruments(); for(auto inst : insts) { - if(ImGui::TreeNodeEx(inst->m_nickname.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) + bool instIsOpen = ImGui::TreeNodeEx(inst->m_nickname.c_str(), ImGuiTreeNodeFlags_DefaultOpen); + + // Render ornaments for this instrument: offline, trigger status, ... + if (auto scope = std::dynamic_pointer_cast(inst)) { + if (scope->IsOffline()) { + /* XXX: refactor these "badges" into a common badge render function */ + ImGui::SameLine(ImGui::GetWindowContentRegionMax().x - 60 /* XXX: size for text */); + /* XXX: this is not really a button, and should be rendered as a rect and a text */ + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.8, 0.3, 0.3, 1.0) /* XXX: pull color from prefs */); + ImGui::SmallButton("OFFLINE"); + ImGui::PopStyleColor(); + } + } + + if(instIsOpen) { + if (auto scope = std::dynamic_pointer_cast(inst)) { + ImGui::BeginChild("sample_params", ImVec2(0, 50), ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_Border); + + auto srate_txt = Unit(Unit::UNIT_SAMPLERATE).PrettyPrint(scope->GetSampleRate()); + auto sdepth_txt = Unit(Unit::UNIT_SAMPLEDEPTH).PrettyPrint(scope->GetSampleDepth()); + + bool clicked = false; + bool hovered = false; + ImGui::Text("Sample rate: "); ImGui::SameLine(0, 0); clicked |= ImGui::TextLink(srate_txt.c_str()); hovered |= ImGui::IsItemHovered(); + ImGui::Text("Sample depth: "); ImGui::SameLine(0, 0); clicked |= ImGui::TextLink(sdepth_txt.c_str()); hovered |= ImGui::IsItemHovered(); + if (clicked) { + m_parent->ShowTimebaseProperties(); + } + if (hovered) { + m_parent->AddStatusHelp("mouse_lmb", "Open timebase properties"); + } + + ImGui::EndChild(); + } + for(size_t i=0; iGetChannelCount(); i++) { auto chan = inst->GetChannel(i); + if (chan->m_displaycolor != "") { + ImGui::PushStyleColor(ImGuiCol_Text, ColorFromString(chan->m_displaycolor)); + } bool open = ImGui::TreeNodeEx(chan->GetDisplayName().c_str(), ImGuiTreeNodeFlags_DefaultOpen); + if (chan->m_displaycolor != "") { + ImGui::PopStyleColor(); + } + + if (auto scopechan = dynamic_cast(chan)) { + if (!scopechan->IsEnabled()) { + ImGui::SameLine(ImGui::GetWindowContentRegionMax().x - 60 /* XXX: size for text */); + /* XXX: this is not really a button, and should be rendered as a rect and a text */ + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.4, 0.4, 0.4, 1.0) /* XXX: pull color from prefs */); + ImGui::SmallButton("disabled"); + ImGui::PopStyleColor(); + } + } //Single stream: drag the stream not the channel bool singleStream = chan->GetStreamCount() == 1; From 444643f2f889dfa1fef1167027409e8f3f52f20c Mon Sep 17 00:00:00 2001 From: Joshua Wise Date: Fri, 27 Sep 2024 11:35:47 -0700 Subject: [PATCH 02/25] refactor info link, to be used elsewhere soon --- src/ngscopeclient/StreamBrowserDialog.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/ngscopeclient/StreamBrowserDialog.cpp b/src/ngscopeclient/StreamBrowserDialog.cpp index 709f9414..570ab4cb 100644 --- a/src/ngscopeclient/StreamBrowserDialog.cpp +++ b/src/ngscopeclient/StreamBrowserDialog.cpp @@ -65,6 +65,18 @@ StreamBrowserDialog::~StreamBrowserDialog() */ bool StreamBrowserDialog::DoRender() { + // Some helpers for rendering widgets that appear in the StreamBrowserDialog. + + // Render a link of the "Sample rate: 4 GSa/s" type that shows up in the + // scope properties box. + auto renderInfoLink = [](const char *label, const char *linktext, bool &clicked, bool &hovered) + { + ImGui::Text("%s: ", label); + ImGui::SameLine(0, 0); + clicked |= ImGui::TextLink(linktext); + hovered |= ImGui::IsItemHovered(); + }; + //Add all instruments auto insts = m_session.GetInstruments(); for(auto inst : insts) @@ -86,15 +98,15 @@ bool StreamBrowserDialog::DoRender() if(instIsOpen) { if (auto scope = std::dynamic_pointer_cast(inst)) { - ImGui::BeginChild("sample_params", ImVec2(0, 50), ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_Border); + ImGui::BeginChild("sample_params", ImVec2(0, 0), ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_Border); auto srate_txt = Unit(Unit::UNIT_SAMPLERATE).PrettyPrint(scope->GetSampleRate()); auto sdepth_txt = Unit(Unit::UNIT_SAMPLEDEPTH).PrettyPrint(scope->GetSampleDepth()); bool clicked = false; bool hovered = false; - ImGui::Text("Sample rate: "); ImGui::SameLine(0, 0); clicked |= ImGui::TextLink(srate_txt.c_str()); hovered |= ImGui::IsItemHovered(); - ImGui::Text("Sample depth: "); ImGui::SameLine(0, 0); clicked |= ImGui::TextLink(sdepth_txt.c_str()); hovered |= ImGui::IsItemHovered(); + renderInfoLink("Sample rate", srate_txt.c_str(), clicked, hovered); + renderInfoLink("Sample depth", sdepth_txt.c_str(), clicked, hovered); if (clicked) { m_parent->ShowTimebaseProperties(); } From 51a63c9a38256164dcd2aa5683a425d5d3dca343 Mon Sep 17 00:00:00 2001 From: Joshua Wise Date: Fri, 27 Sep 2024 12:07:34 -0700 Subject: [PATCH 03/25] refactor renderBadge, and only render a badge if there is enough space, and fall back on a shorter string if there is not --- src/ngscopeclient/StreamBrowserDialog.cpp | 47 +++++++++++++++++------ 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/src/ngscopeclient/StreamBrowserDialog.cpp b/src/ngscopeclient/StreamBrowserDialog.cpp index 570ab4cb..2a121bdb 100644 --- a/src/ngscopeclient/StreamBrowserDialog.cpp +++ b/src/ngscopeclient/StreamBrowserDialog.cpp @@ -77,21 +77,49 @@ bool StreamBrowserDialog::DoRender() hovered |= ImGui::IsItemHovered(); }; + float badgeXMin; // left edge over which we must not overrun + float badgeXCur; // right edge to render the next badge against + auto startBadgeLine = [&badgeXMin, &badgeXCur]() + { + ImGuiWindow *window = ImGui::GetCurrentWindowRead(); + // roughly, what ImGui::GetCursorPosPrevLineX would be, if it existed; convert from absolute-space to window-space + badgeXMin = (window->DC.CursorPosPrevLine - window->Pos + window->Scroll).x + ImGui::GetStyle().ItemSpacing.x; + badgeXCur = ImGui::GetWindowContentRegionMax().x; + }; + auto renderBadge = [&badgeXMin, &badgeXCur](ImVec4 color, ... /* labels, ending in NULL */) + { + va_list ap; + va_start(ap, color); + + /* XXX: maybe color should be a prefs string? */ + + while (const char *label = va_arg(ap, const char *)) { + float xsz = ImGui::CalcTextSize(label).x + ImGui::GetStyle().ItemSpacing.x + ImGui::GetStyle().FramePadding.x * 2; + if ((badgeXCur - xsz) < badgeXMin) { + continue; + } + + // ok, we have enough space -- commit to it! + badgeXCur -= xsz; + ImGui::SameLine(badgeXCur); + ImGui::PushStyleColor(ImGuiCol_Button, color); + ImGui::SmallButton(label); + ImGui::PopStyleColor(); + break; + } + }; + //Add all instruments auto insts = m_session.GetInstruments(); for(auto inst : insts) { bool instIsOpen = ImGui::TreeNodeEx(inst->m_nickname.c_str(), ImGuiTreeNodeFlags_DefaultOpen); + startBadgeLine(); // Render ornaments for this instrument: offline, trigger status, ... if (auto scope = std::dynamic_pointer_cast(inst)) { if (scope->IsOffline()) { - /* XXX: refactor these "badges" into a common badge render function */ - ImGui::SameLine(ImGui::GetWindowContentRegionMax().x - 60 /* XXX: size for text */); - /* XXX: this is not really a button, and should be rendered as a rect and a text */ - ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.8, 0.3, 0.3, 1.0) /* XXX: pull color from prefs */); - ImGui::SmallButton("OFFLINE"); - ImGui::PopStyleColor(); + renderBadge(ImVec4(0.8, 0.3, 0.3, 1.0) /* XXX: pull color from prefs */, "OFFLINE", "OFFL", NULL); } } @@ -128,14 +156,11 @@ bool StreamBrowserDialog::DoRender() if (chan->m_displaycolor != "") { ImGui::PopStyleColor(); } + startBadgeLine(); if (auto scopechan = dynamic_cast(chan)) { if (!scopechan->IsEnabled()) { - ImGui::SameLine(ImGui::GetWindowContentRegionMax().x - 60 /* XXX: size for text */); - /* XXX: this is not really a button, and should be rendered as a rect and a text */ - ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.4, 0.4, 0.4, 1.0) /* XXX: pull color from prefs */); - ImGui::SmallButton("disabled"); - ImGui::PopStyleColor(); + renderBadge(ImVec4(0.4, 0.4, 0.4, 1.0) /* XXX: pull color from prefs */, "disabled", "disa", NULL); } } From deb8bd2b9c0a95fc0f2e5e5a2e1c89bfff55c0d2 Mon Sep 17 00:00:00 2001 From: Joshua Wise Date: Fri, 27 Sep 2024 12:11:35 -0700 Subject: [PATCH 04/25] oops, actually align the right side with the spacing --- src/ngscopeclient/StreamBrowserDialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ngscopeclient/StreamBrowserDialog.cpp b/src/ngscopeclient/StreamBrowserDialog.cpp index 2a121bdb..33806a46 100644 --- a/src/ngscopeclient/StreamBrowserDialog.cpp +++ b/src/ngscopeclient/StreamBrowserDialog.cpp @@ -100,7 +100,7 @@ bool StreamBrowserDialog::DoRender() } // ok, we have enough space -- commit to it! - badgeXCur -= xsz; + badgeXCur -= xsz - ImGui::GetStyle().ItemSpacing.x; ImGui::SameLine(badgeXCur); ImGui::PushStyleColor(ImGuiCol_Button, color); ImGui::SmallButton(label); From be3aa87b9ee8f49c41e051aa48e777d6c947aa30 Mon Sep 17 00:00:00 2001 From: Joshua Wise Date: Fri, 27 Sep 2024 12:32:58 -0700 Subject: [PATCH 05/25] StreamBrowserDialog: add a child box of a channel / stream for offset and range --- src/ngscopeclient/StreamBrowserDialog.cpp | 68 +++++++++++++++++------ 1 file changed, 51 insertions(+), 17 deletions(-) diff --git a/src/ngscopeclient/StreamBrowserDialog.cpp b/src/ngscopeclient/StreamBrowserDialog.cpp index 33806a46..e8995a54 100644 --- a/src/ngscopeclient/StreamBrowserDialog.cpp +++ b/src/ngscopeclient/StreamBrowserDialog.cpp @@ -83,7 +83,7 @@ bool StreamBrowserDialog::DoRender() { ImGuiWindow *window = ImGui::GetCurrentWindowRead(); // roughly, what ImGui::GetCursorPosPrevLineX would be, if it existed; convert from absolute-space to window-space - badgeXMin = (window->DC.CursorPosPrevLine - window->Pos + window->Scroll).x + ImGui::GetStyle().ItemSpacing.x; + badgeXMin = (window->DC.CursorPosPrevLine - window->Pos + window->Scroll).x; badgeXCur = ImGui::GetWindowContentRegionMax().x; }; auto renderBadge = [&badgeXMin, &badgeXCur](ImVec4 color, ... /* labels, ending in NULL */) @@ -149,10 +149,20 @@ bool StreamBrowserDialog::DoRender() { auto chan = inst->GetChannel(i); + bool singleStream = chan->GetStreamCount() == 1; + bool renderScopeProps = false; + if (auto scopechan = dynamic_cast(chan)) { + if (scopechan->IsEnabled()) { + renderScopeProps = true; + } + } + + bool hasChildren = !singleStream || renderScopeProps; + if (chan->m_displaycolor != "") { ImGui::PushStyleColor(ImGuiCol_Text, ColorFromString(chan->m_displaycolor)); } - bool open = ImGui::TreeNodeEx(chan->GetDisplayName().c_str(), ImGuiTreeNodeFlags_DefaultOpen); + bool open = ImGui::TreeNodeEx(chan->GetDisplayName().c_str(), ImGuiTreeNodeFlags_DefaultOpen | (!hasChildren ? ImGuiTreeNodeFlags_Leaf : 0)); if (chan->m_displaycolor != "") { ImGui::PopStyleColor(); } @@ -165,7 +175,6 @@ bool StreamBrowserDialog::DoRender() } //Single stream: drag the stream not the channel - bool singleStream = chan->GetStreamCount() == 1; if(singleStream) { StreamDescriptor s(chan, 0); @@ -191,26 +200,51 @@ bool StreamBrowserDialog::DoRender() ImGui::TextUnformatted(chan->GetDisplayName().c_str()); ImGui::EndDragDropSource(); } - - if(open && !singleStream) + + if(open) { for(size_t j=0; jGetStreamCount(); j++) { - ImGui::Selectable(chan->GetStreamName(j).c_str()); - - StreamDescriptor s(chan, j); - if(ImGui::BeginDragDropSource()) + if (!singleStream) { - if(s.GetType() == Stream::STREAM_TYPE_ANALOG_SCALAR) - ImGui::SetDragDropPayload("Scalar", &s, sizeof(s)); - else - ImGui::SetDragDropPayload("Stream", &s, sizeof(s)); + ImGui::Selectable(chan->GetStreamName(j).c_str()); + + StreamDescriptor s(chan, j); + if(ImGui::BeginDragDropSource()) + { + if(s.GetType() == Stream::STREAM_TYPE_ANALOG_SCALAR) + ImGui::SetDragDropPayload("Scalar", &s, sizeof(s)); + else + ImGui::SetDragDropPayload("Stream", &s, sizeof(s)); - ImGui::TextUnformatted(s.GetName().c_str()); - ImGui::EndDragDropSource(); + ImGui::TextUnformatted(s.GetName().c_str()); + ImGui::EndDragDropSource(); + } + else + DoItemHelp(); + } + if (renderScopeProps) + { + auto scopechan = dynamic_cast(chan); + ImGui::BeginChild("scope_params", ImVec2(0, 0), ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_Border); + + auto offset_txt = Unit(Unit::UNIT_VOLTS).PrettyPrint(scopechan->GetOffset(j)); + auto range_txt = Unit(Unit::UNIT_VOLTS).PrettyPrint(scopechan->GetVoltageRange(j)); + + bool clicked = false; + bool hovered = false; + renderInfoLink("Offset", offset_txt.c_str(), clicked, hovered); + renderInfoLink("Voltage range", range_txt.c_str(), clicked, hovered); + if (clicked) { + /* XXX: refactor to be more like FilterGraphEditor::HandleNodeProperties? */ + m_parent->ShowChannelProperties(scopechan); + } + if (hovered) { + m_parent->AddStatusHelp("mouse_lmb", "Open channel properties"); + } + + ImGui::EndChild(); } - else - DoItemHelp(); } } From aba9f197ef937c9521563cf2d35954525bd8aea0 Mon Sep 17 00:00:00 2001 From: Joshua Wise Date: Mon, 30 Sep 2024 13:55:59 -0700 Subject: [PATCH 06/25] cache trigger state from InstrumentThread in InstrumentConnectionState, and then read it from the StreamBrowserDialog UI later --- src/ngscopeclient/InstrumentThread.cpp | 1 + src/ngscopeclient/Session.h | 5 ++++ src/ngscopeclient/StreamBrowserDialog.cpp | 30 +++++++++++++++++++++++ 3 files changed, 36 insertions(+) diff --git a/src/ngscopeclient/InstrumentThread.cpp b/src/ngscopeclient/InstrumentThread.cpp index 81477f32..ebab82a0 100644 --- a/src/ngscopeclient/InstrumentThread.cpp +++ b/src/ngscopeclient/InstrumentThread.cpp @@ -93,6 +93,7 @@ void InstrumentThread(InstrumentThreadArgs args) else { auto stat = scope->PollTrigger(); + session->GetInstrumentConnectionState(inst)->m_lastTriggerState = stat; if(stat == Oscilloscope::TRIGGER_MODE_TRIGGERED) scope->AcquireData(); } diff --git a/src/ngscopeclient/Session.h b/src/ngscopeclient/Session.h index db08cded..a570f946 100644 --- a/src/ngscopeclient/Session.h +++ b/src/ngscopeclient/Session.h @@ -58,6 +58,7 @@ class InstrumentConnectionState m_shuttingDown = false; args.shuttingDown = &m_shuttingDown; m_thread = std::make_unique(InstrumentThread, args); + m_lastTriggerState = Oscilloscope::TRIGGER_MODE_WAIT; } ~InstrumentConnectionState() @@ -79,6 +80,9 @@ class InstrumentConnectionState ///@brief Thread for polling the instrument std::unique_ptr m_thread; + + ///@brief Cached trigger state, to reflect in the UI + Oscilloscope::TriggerMode m_lastTriggerState; }; /** @@ -132,6 +136,7 @@ class Session void AddInstrument(std::shared_ptr inst, bool createDialogs = true); void RemoveInstrument(std::shared_ptr inst); + std::shared_ptr GetInstrumentConnectionState(std::shared_ptr inst) { return m_instrumentStates[inst]; } bool IsMultiScope() { return m_multiScope; } diff --git a/src/ngscopeclient/StreamBrowserDialog.cpp b/src/ngscopeclient/StreamBrowserDialog.cpp index e8995a54..32c6f37c 100644 --- a/src/ngscopeclient/StreamBrowserDialog.cpp +++ b/src/ngscopeclient/StreamBrowserDialog.cpp @@ -120,6 +120,36 @@ bool StreamBrowserDialog::DoRender() if (auto scope = std::dynamic_pointer_cast(inst)) { if (scope->IsOffline()) { renderBadge(ImVec4(0.8, 0.3, 0.3, 1.0) /* XXX: pull color from prefs */, "OFFLINE", "OFFL", NULL); + } else { + switch (m_session.GetInstrumentConnectionState(inst)->m_lastTriggerState) { + case Oscilloscope::TRIGGER_MODE_RUN: + /* prefer language "ARMED" to "RUN": + * "RUN" could mean either "waiting + * for trigger" or "currently + * capturing samples post-trigger", + * "ARMED" is unambiguous */ + renderBadge(ImVec4(0.3, 0.8, 0.3, 1.0), "ARMED", "A", NULL); + break; + case Oscilloscope::TRIGGER_MODE_STOP: + renderBadge(ImVec4(0.8, 0.3, 0.3, 1.0), "STOPPED", "STOP", "S", NULL); + break; + case Oscilloscope::TRIGGER_MODE_TRIGGERED: + renderBadge(ImVec4(0.7, 0.7, 0.3, 1.0), "TRIGGERED", "TRIG'D", "T'D", "T", NULL); + break; + case Oscilloscope::TRIGGER_MODE_WAIT: + /* prefer language "BUSY" to "WAIT": + * "WAIT" could mean "waiting for + * trigger", "BUSY" means "I am + * doing something internally and am + * not ready for some reason" */ + renderBadge(ImVec4(0.8, 0.3, 0.3, 1.0), "BUSY", "B", NULL); + break; + case Oscilloscope::TRIGGER_MODE_AUTO: + renderBadge(ImVec4(0.3, 0.8, 0.3, 1.0), "AUTO", "A", NULL); + break; + default: + break; + } } } From 31517853c0a6c54110df448680e50ea0e5839e51 Mon Sep 17 00:00:00 2001 From: Frederic Borry Date: Wed, 2 Oct 2024 23:26:47 +0200 Subject: [PATCH 07/25] Fixed triger state update logic. --- src/ngscopeclient/InstrumentThread.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/ngscopeclient/InstrumentThread.cpp b/src/ngscopeclient/InstrumentThread.cpp index ebab82a0..90bc86c5 100644 --- a/src/ngscopeclient/InstrumentThread.cpp +++ b/src/ngscopeclient/InstrumentThread.cpp @@ -65,6 +65,8 @@ void InstrumentThread(InstrumentThreadArgs args) auto bertstate = args.bertstate; auto psustate = args.psustate; + bool triggerUpToDate = false; + while(!*args.shuttingDown) { //Flush any pending commands @@ -86,6 +88,15 @@ void InstrumentThread(InstrumentThreadArgs args) { //LogTrace("Scope isn't armed, sleeping\n"); this_thread::sleep_for(chrono::milliseconds(5)); + if(!triggerUpToDate) + { // Check for trigger state change + auto stat = scope->PollTrigger(); + session->GetInstrumentConnectionState(inst)->m_lastTriggerState = stat; + if(stat == Oscilloscope::TRIGGER_MODE_STOP) + { // Final state + triggerUpToDate = true; + } + } } //Grab data if it's ready @@ -96,6 +107,7 @@ void InstrumentThread(InstrumentThreadArgs args) session->GetInstrumentConnectionState(inst)->m_lastTriggerState = stat; if(stat == Oscilloscope::TRIGGER_MODE_TRIGGERED) scope->AcquireData(); + triggerUpToDate = false; } } From 2805ab6804ad01fc4e5fa846ffc10517405bd3f4 Mon Sep 17 00:00:00 2001 From: Frederic Borry Date: Thu, 3 Oct 2024 00:46:50 +0200 Subject: [PATCH 08/25] First step to download progress-bar on onscilloscope channels. --- src/ngscopeclient/Session.h | 24 ++++ src/ngscopeclient/StreamBrowserDialog.cpp | 166 ++++++++++++++++++++-- 2 files changed, 177 insertions(+), 13 deletions(-) diff --git a/src/ngscopeclient/Session.h b/src/ngscopeclient/Session.h index a570f946..38880b35 100644 --- a/src/ngscopeclient/Session.h +++ b/src/ngscopeclient/Session.h @@ -59,6 +59,12 @@ class InstrumentConnectionState args.shuttingDown = &m_shuttingDown; m_thread = std::make_unique(InstrumentThread, args); m_lastTriggerState = Oscilloscope::TRIGGER_MODE_WAIT; + size_t channelNumber = 0; + + // TODO : temporary => should no longer be needed once GetDownloadProgress() API is available on OscilloscopeChannel + if(args.inst) + channelNumber = args.inst->GetChannelCount(); + m_channelDownloadStates.assign(channelNumber,-1); } ~InstrumentConnectionState() @@ -75,6 +81,20 @@ class InstrumentConnectionState m_thread = nullptr; } + int GetChannelDownloadState(size_t i) const + { + if(i >= m_channelDownloadStates.size()) + return -1; + + return m_channelDownloadStates[i]; + } + + void SetChannelDownloadState(size_t i, int downloadState) + { + m_channelDownloadStates[i] = downloadState; + } + + ///@brief Termination flag for shutting down the polling thread std::atomic m_shuttingDown; @@ -83,6 +103,10 @@ class InstrumentConnectionState ///@brief Cached trigger state, to reflect in the UI Oscilloscope::TriggerMode m_lastTriggerState; + + // TODO : temporary => should no longer be needed once GetDownloadProgress() API is available on OscilloscopeChannel + ///@brief Cached download sates, to reflect in the UI + std::vector m_channelDownloadStates; }; /** diff --git a/src/ngscopeclient/StreamBrowserDialog.cpp b/src/ngscopeclient/StreamBrowserDialog.cpp index 32c6f37c..7d11ee72 100644 --- a/src/ngscopeclient/StreamBrowserDialog.cpp +++ b/src/ngscopeclient/StreamBrowserDialog.cpp @@ -37,8 +37,20 @@ #include "StreamBrowserDialog.h" #include "MainWindow.h" +/* @brief Width used to display progress bars (e.g. download progress bar)*/ +#define PROGRESS_BAR_WIDTH 90 + using namespace std; +// TODO move this to OscilloscopeChannel class along with GetDwnwloadProgress() API +enum DownloadState : int { + DOWNLOAD_PROGRESS_DISABLED = -3, + DOWNLOAD_NONE = -2, + DOWNLOAD_WAITING = -1, + DOWNLOAD_STARTED = 0, + DOWNLOAD_FINISHED = 100 +}; + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Construction / destruction @@ -108,6 +120,64 @@ bool StreamBrowserDialog::DoRender() break; } }; + auto renderDownloadProgress = [&badgeXMin, &badgeXCur](int progress) + { + static const char* const download[] = {"DOWNLOADING", "DOWNLOAD" ,"DL","D", "", NULL}; + static const char* const wait[] = {"WAITING..." , "WAITING" ,"WA","W", "", NULL}; + static const char* const stop[] = {"STOPPED" , "STOP" ,"ST","S", "", NULL}; + static const char* const ready[] = {"READY" , "RDY" ,"RY","R", "", NULL}; + static const char* const* labels; + ImVec4 color; + switch(progress) + { + case DownloadState::DOWNLOAD_NONE: + labels = stop; + color.x = 0.8 ; color.y=0.3 ; color.z=0.3; color.w=1.0; + break; + case DownloadState::DOWNLOAD_WAITING: + labels = wait; + color.x = 0.8 ; color.y=0.3 ; color.z=0.3; color.w=1.0; + break; + case DownloadState::DOWNLOAD_FINISHED: + labels = ready; + color.x = 0.3 ; color.y=0.8 ; color.z=0.3; color.w=1.0; + break; + default: + labels = download; + color.x = 0.7 ; color.y=0.7 ; color.z=0.3; color.w=1.0; + } + // Only show progress bar if an download is proceeding + bool hasProgress = (progress >= DownloadState::DOWNLOAD_WAITING) && (progress <= DownloadState::DOWNLOAD_FINISHED); + bool hasLabel; + int labelIndex = 0; + while (const char *label = labels[labelIndex]) + { + hasLabel = strlen(label)>0; + float xsz = ImGui::CalcTextSize(label).x + (hasProgress ? PROGRESS_BAR_WIDTH : 0) + (ImGui::GetStyle().ItemSpacing.x) * ((hasProgress && hasLabel ? 1 : 0)+(hasLabel ? 1 : 0)) + ImGui::GetStyle().FramePadding.x * 2; + if ((badgeXCur - xsz) < badgeXMin) { + labelIndex++; + continue; + } + + // ok, we have enough space -- commit to it! + badgeXCur -= xsz - ImGui::GetStyle().ItemSpacing.x; + ImGui::SameLine(badgeXCur); + if(hasLabel) + { + ImGui::PushStyleColor(ImGuiCol_Button, color); + ImGui::SmallButton(labels[labelIndex]); + ImGui::PopStyleColor(); + if(hasProgress) + ImGui::SameLine(); + } + if(hasProgress) + { + ImGui::ProgressBar(((float)progress)/100, ImVec2(PROGRESS_BAR_WIDTH, ImGui::GetFontSize())); + } + + break; + } + }; //Add all instruments auto insts = m_session.GetInstruments(); @@ -115,13 +185,17 @@ bool StreamBrowserDialog::DoRender() { bool instIsOpen = ImGui::TreeNodeEx(inst->m_nickname.c_str(), ImGuiTreeNodeFlags_DefaultOpen); startBadgeLine(); + + auto state = m_session.GetInstrumentConnectionState(inst); // Render ornaments for this instrument: offline, trigger status, ... - if (auto scope = std::dynamic_pointer_cast(inst)) { + auto scope = std::dynamic_pointer_cast(inst); + if (scope) { if (scope->IsOffline()) { renderBadge(ImVec4(0.8, 0.3, 0.3, 1.0) /* XXX: pull color from prefs */, "OFFLINE", "OFFL", NULL); } else { - switch (m_session.GetInstrumentConnectionState(inst)->m_lastTriggerState) { + Oscilloscope::TriggerMode mode = state ? state->m_lastTriggerState : Oscilloscope::TRIGGER_MODE_STOP; + switch (mode) { case Oscilloscope::TRIGGER_MODE_RUN: /* prefer language "ARMED" to "RUN": * "RUN" could mean either "waiting @@ -155,7 +229,7 @@ bool StreamBrowserDialog::DoRender() if(instIsOpen) { - if (auto scope = std::dynamic_pointer_cast(inst)) { + if (scope) { ImGui::BeginChild("sample_params", ImVec2(0, 0), ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_Border); auto srate_txt = Unit(Unit::UNIT_SAMPLERATE).PrettyPrint(scope->GetSampleRate()); @@ -175,7 +249,8 @@ bool StreamBrowserDialog::DoRender() ImGui::EndChild(); } - for(size_t i=0; iGetChannelCount(); i++) + size_t channelCount = inst->GetChannelCount(); + for(size_t i=0; iGetChannel(i); @@ -188,6 +263,7 @@ bool StreamBrowserDialog::DoRender() } bool hasChildren = !singleStream || renderScopeProps; + bool triggerArmed = scope ? scope->IsTriggerArmed() : false; if (chan->m_displaycolor != "") { ImGui::PushStyleColor(ImGuiCol_Text, ColorFromString(chan->m_displaycolor)); @@ -196,14 +272,7 @@ bool StreamBrowserDialog::DoRender() if (chan->m_displaycolor != "") { ImGui::PopStyleColor(); } - startBadgeLine(); - if (auto scopechan = dynamic_cast(chan)) { - if (!scopechan->IsEnabled()) { - renderBadge(ImVec4(0.4, 0.4, 0.4, 1.0) /* XXX: pull color from prefs */, "disabled", "disa", NULL); - } - } - //Single stream: drag the stream not the channel if(singleStream) { @@ -231,6 +300,77 @@ bool StreamBrowserDialog::DoRender() ImGui::EndDragDropSource(); } + // Channel decoration + startBadgeLine(); + auto scopechan = dynamic_cast(chan); + if (scopechan) { + if (!scopechan->IsEnabled()) { + renderBadge(ImVec4(0.4, 0.4, 0.4, 1.0) /* XXX: pull color from prefs */, "disabled", "disa", NULL); + } + else if(scope) { + int progress = DownloadState::DOWNLOAD_NONE; + // TODO get this out of GetDownloadState() channel API when implemented + // For now, we simulate it for demonstration purpose + uint64_t sampleDepth = scope->GetSampleDepth(); + if(sampleDepth <= 100000) + { + progress = DownloadState::DOWNLOAD_PROGRESS_DISABLED; + } + if(progress != DownloadState::DOWNLOAD_PROGRESS_DISABLED) + { + if(state) + { + progress = state->GetChannelDownloadState(i); + if(triggerArmed) + { + // Render method is called 60 times per second + // We want to simulate a download time of 1 MSample/S + uint64_t downloadIncrement = std::max(((1000000ULL*100/sampleDepth)/60),1ULL); + if(i == 0) + { // Start with first channel + if(progress<=DownloadState::DOWNLOAD_STARTED) + { // Restart all channels + for(size_t j = 0 ; j < channelCount ; j++) + { + state->SetChannelDownloadState(j,DownloadState::DOWNLOAD_WAITING); + } + } + if(progressGetChannelDownloadState(i-1)>=DOWNLOAD_FINISHED) + { + if(progress=DownloadState::DOWNLOAD_FINISHED) + { // Start over + for(size_t j = 0 ; j < channelCount ; j++) + { + state->SetChannelDownloadState(j,DownloadState::DOWNLOAD_WAITING); + } + } + else + { + if(progress>DownloadState::DOWNLOAD_FINISHED) + progress = DownloadState::DOWNLOAD_FINISHED; + state->SetChannelDownloadState(i,progress); + } + } + else if(progress != DownloadState::DOWNLOAD_NONE) + { // Set download state to non + state->SetChannelDownloadState(i,DownloadState::DOWNLOAD_NONE); + } + } + renderDownloadProgress(progress); + } + } + } + if(open) { for(size_t j=0; jGetStreamCount(); j++) @@ -253,9 +393,9 @@ bool StreamBrowserDialog::DoRender() else DoItemHelp(); } - if (renderScopeProps) + // Channel/stram properties + if (renderScopeProps && scopechan) { - auto scopechan = dynamic_cast(chan); ImGui::BeginChild("scope_params", ImVec2(0, 0), ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_Border); auto offset_txt = Unit(Unit::UNIT_VOLTS).PrettyPrint(scopechan->GetOffset(j)); From 0963828fb03985b3ca26d695da6ca5241edb5c7c Mon Sep 17 00:00:00 2001 From: fredzo Date: Sun, 6 Oct 2024 23:15:15 +0200 Subject: [PATCH 09/25] Moved to actual scopehal GetDownloadState() API. --- src/ngscopeclient/StreamBrowserDialog.cpp | 67 +++-------------------- 1 file changed, 7 insertions(+), 60 deletions(-) diff --git a/src/ngscopeclient/StreamBrowserDialog.cpp b/src/ngscopeclient/StreamBrowserDialog.cpp index 7d11ee72..d009a397 100644 --- a/src/ngscopeclient/StreamBrowserDialog.cpp +++ b/src/ngscopeclient/StreamBrowserDialog.cpp @@ -263,7 +263,6 @@ bool StreamBrowserDialog::DoRender() } bool hasChildren = !singleStream || renderScopeProps; - bool triggerArmed = scope ? scope->IsTriggerArmed() : false; if (chan->m_displaycolor != "") { ImGui::PushStyleColor(ImGuiCol_Text, ColorFromString(chan->m_displaycolor)); @@ -303,69 +302,17 @@ bool StreamBrowserDialog::DoRender() // Channel decoration startBadgeLine(); auto scopechan = dynamic_cast(chan); - if (scopechan) { - if (!scopechan->IsEnabled()) { + if (scopechan) + { + if (!scopechan->IsEnabled()) + { renderBadge(ImVec4(0.4, 0.4, 0.4, 1.0) /* XXX: pull color from prefs */, "disabled", "disa", NULL); } - else if(scope) { - int progress = DownloadState::DOWNLOAD_NONE; - // TODO get this out of GetDownloadState() channel API when implemented - // For now, we simulate it for demonstration purpose - uint64_t sampleDepth = scope->GetSampleDepth(); - if(sampleDepth <= 100000) - { - progress = DownloadState::DOWNLOAD_PROGRESS_DISABLED; - } + else + { + int progress = scopechan->GetDownloadState(); if(progress != DownloadState::DOWNLOAD_PROGRESS_DISABLED) { - if(state) - { - progress = state->GetChannelDownloadState(i); - if(triggerArmed) - { - // Render method is called 60 times per second - // We want to simulate a download time of 1 MSample/S - uint64_t downloadIncrement = std::max(((1000000ULL*100/sampleDepth)/60),1ULL); - if(i == 0) - { // Start with first channel - if(progress<=DownloadState::DOWNLOAD_STARTED) - { // Restart all channels - for(size_t j = 0 ; j < channelCount ; j++) - { - state->SetChannelDownloadState(j,DownloadState::DOWNLOAD_WAITING); - } - } - if(progressGetChannelDownloadState(i-1)>=DOWNLOAD_FINISHED) - { - if(progress=DownloadState::DOWNLOAD_FINISHED) - { // Start over - for(size_t j = 0 ; j < channelCount ; j++) - { - state->SetChannelDownloadState(j,DownloadState::DOWNLOAD_WAITING); - } - } - else - { - if(progress>DownloadState::DOWNLOAD_FINISHED) - progress = DownloadState::DOWNLOAD_FINISHED; - state->SetChannelDownloadState(i,progress); - } - } - else if(progress != DownloadState::DOWNLOAD_NONE) - { // Set download state to non - state->SetChannelDownloadState(i,DownloadState::DOWNLOAD_NONE); - } - } renderDownloadProgress(progress); } } From 5d6eb276c0bb475d8936273153e34e6ee8da1cd6 Mon Sep 17 00:00:00 2001 From: fredzo Date: Mon, 7 Oct 2024 00:32:44 +0200 Subject: [PATCH 10/25] Made digital channel nodes collapsed by default. Fixed digital channel properties. --- src/ngscopeclient/StreamBrowserDialog.cpp | 56 +++++++++++++++-------- 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/src/ngscopeclient/StreamBrowserDialog.cpp b/src/ngscopeclient/StreamBrowserDialog.cpp index d009a397..7c0c85ed 100644 --- a/src/ngscopeclient/StreamBrowserDialog.cpp +++ b/src/ngscopeclient/StreamBrowserDialog.cpp @@ -255,11 +255,13 @@ bool StreamBrowserDialog::DoRender() auto chan = inst->GetChannel(i); bool singleStream = chan->GetStreamCount() == 1; + auto scopechan = dynamic_cast(chan); bool renderScopeProps = false; - if (auto scopechan = dynamic_cast(chan)) { - if (scopechan->IsEnabled()) { - renderScopeProps = true; - } + bool isDigital = false; + if (scopechan) + { + renderScopeProps = scopechan->IsEnabled(); + isDigital = scopechan->GetType(0) == Stream::STREAM_TYPE_DIGITAL; } bool hasChildren = !singleStream || renderScopeProps; @@ -267,7 +269,7 @@ bool StreamBrowserDialog::DoRender() if (chan->m_displaycolor != "") { ImGui::PushStyleColor(ImGuiCol_Text, ColorFromString(chan->m_displaycolor)); } - bool open = ImGui::TreeNodeEx(chan->GetDisplayName().c_str(), ImGuiTreeNodeFlags_DefaultOpen | (!hasChildren ? ImGuiTreeNodeFlags_Leaf : 0)); + bool open = ImGui::TreeNodeEx(chan->GetDisplayName().c_str(), isDigital ? 0 : ImGuiTreeNodeFlags_DefaultOpen | (!hasChildren ? ImGuiTreeNodeFlags_Leaf : 0)); if (chan->m_displaycolor != "") { ImGui::PopStyleColor(); } @@ -301,7 +303,6 @@ bool StreamBrowserDialog::DoRender() // Channel decoration startBadgeLine(); - auto scopechan = dynamic_cast(chan); if (scopechan) { if (!scopechan->IsEnabled()) @@ -345,19 +346,38 @@ bool StreamBrowserDialog::DoRender() { ImGui::BeginChild("scope_params", ImVec2(0, 0), ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_Border); - auto offset_txt = Unit(Unit::UNIT_VOLTS).PrettyPrint(scopechan->GetOffset(j)); - auto range_txt = Unit(Unit::UNIT_VOLTS).PrettyPrint(scopechan->GetVoltageRange(j)); - - bool clicked = false; - bool hovered = false; - renderInfoLink("Offset", offset_txt.c_str(), clicked, hovered); - renderInfoLink("Voltage range", range_txt.c_str(), clicked, hovered); - if (clicked) { - /* XXX: refactor to be more like FilterGraphEditor::HandleNodeProperties? */ - m_parent->ShowChannelProperties(scopechan); + if(isDigital) + { + // TODO: Digital Threshold value is not cached on most scopes... uncomment the code bellow ONLY once it has been cached on all drivers + auto threshold_txt = Unit(Unit::UNIT_VOLTS).PrettyPrint(0/*scope->GetDigitalThreshold(i)*/); + + bool clicked = false; + bool hovered = false; + renderInfoLink("Threshold", threshold_txt.c_str(), clicked, hovered); + if (clicked) { + /* XXX: refactor to be more like FilterGraphEditor::HandleNodeProperties? */ + m_parent->ShowChannelProperties(scopechan); + } + if (hovered) { + m_parent->AddStatusHelp("mouse_lmb", "Open channel properties"); + } } - if (hovered) { - m_parent->AddStatusHelp("mouse_lmb", "Open channel properties"); + else + { + auto offset_txt = Unit(Unit::UNIT_VOLTS).PrettyPrint(scopechan->GetOffset(j)); + auto range_txt = Unit(Unit::UNIT_VOLTS).PrettyPrint(scopechan->GetVoltageRange(j)); + + bool clicked = false; + bool hovered = false; + renderInfoLink("Offset", offset_txt.c_str(), clicked, hovered); + renderInfoLink("Voltage range", range_txt.c_str(), clicked, hovered); + if (clicked) { + /* XXX: refactor to be more like FilterGraphEditor::HandleNodeProperties? */ + m_parent->ShowChannelProperties(scopechan); + } + if (hovered) { + m_parent->AddStatusHelp("mouse_lmb", "Open channel properties"); + } } ImGui::EndChild(); From eca259551a4973d1b602be07432b8750bbc48b1d Mon Sep 17 00:00:00 2001 From: fredzo Date: Tue, 8 Oct 2024 00:43:43 +0200 Subject: [PATCH 11/25] Restored digital threshold value read on digital channel properties (now that value has been cached on all drivers implementing it). --- src/ngscopeclient/StreamBrowserDialog.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ngscopeclient/StreamBrowserDialog.cpp b/src/ngscopeclient/StreamBrowserDialog.cpp index 7c0c85ed..845bfb5f 100644 --- a/src/ngscopeclient/StreamBrowserDialog.cpp +++ b/src/ngscopeclient/StreamBrowserDialog.cpp @@ -348,8 +348,7 @@ bool StreamBrowserDialog::DoRender() if(isDigital) { - // TODO: Digital Threshold value is not cached on most scopes... uncomment the code bellow ONLY once it has been cached on all drivers - auto threshold_txt = Unit(Unit::UNIT_VOLTS).PrettyPrint(0/*scope->GetDigitalThreshold(i)*/); + auto threshold_txt = Unit(Unit::UNIT_VOLTS).PrettyPrint(scope->GetDigitalThreshold(i)); bool clicked = false; bool hovered = false; From ed6a7bda5fbe7aa98b7f509a1e4de78c14864725 Mon Sep 17 00:00:00 2001 From: Joshua Wise Date: Mon, 7 Oct 2024 22:55:03 -0700 Subject: [PATCH 12/25] StreamBrowserDialog: use new GetDownloadProgress API (todo: put back the timeout behavior, tweak some more UI) --- src/ngscopeclient/StreamBrowserDialog.cpp | 56 ++++++++++------------- 1 file changed, 24 insertions(+), 32 deletions(-) diff --git a/src/ngscopeclient/StreamBrowserDialog.cpp b/src/ngscopeclient/StreamBrowserDialog.cpp index 845bfb5f..eed4481e 100644 --- a/src/ngscopeclient/StreamBrowserDialog.cpp +++ b/src/ngscopeclient/StreamBrowserDialog.cpp @@ -42,15 +42,6 @@ using namespace std; -// TODO move this to OscilloscopeChannel class along with GetDwnwloadProgress() API -enum DownloadState : int { - DOWNLOAD_PROGRESS_DISABLED = -3, - DOWNLOAD_NONE = -2, - DOWNLOAD_WAITING = -1, - DOWNLOAD_STARTED = 0, - DOWNLOAD_FINISHED = 100 -}; - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Construction / destruction @@ -78,7 +69,7 @@ StreamBrowserDialog::~StreamBrowserDialog() bool StreamBrowserDialog::DoRender() { // Some helpers for rendering widgets that appear in the StreamBrowserDialog. - + // Render a link of the "Sample rate: 4 GSa/s" type that shows up in the // scope properties box. auto renderInfoLink = [](const char *label, const char *linktext, bool &clicked, bool &hovered) @@ -88,7 +79,7 @@ bool StreamBrowserDialog::DoRender() clicked |= ImGui::TextLink(linktext); hovered |= ImGui::IsItemHovered(); }; - + float badgeXMin; // left edge over which we must not overrun float badgeXCur; // right edge to render the next badge against auto startBadgeLine = [&badgeXMin, &badgeXCur]() @@ -102,15 +93,15 @@ bool StreamBrowserDialog::DoRender() { va_list ap; va_start(ap, color); - + /* XXX: maybe color should be a prefs string? */ - + while (const char *label = va_arg(ap, const char *)) { float xsz = ImGui::CalcTextSize(label).x + ImGui::GetStyle().ItemSpacing.x + ImGui::GetStyle().FramePadding.x * 2; if ((badgeXCur - xsz) < badgeXMin) { continue; } - + // ok, we have enough space -- commit to it! badgeXCur -= xsz - ImGui::GetStyle().ItemSpacing.x; ImGui::SameLine(badgeXCur); @@ -120,7 +111,7 @@ bool StreamBrowserDialog::DoRender() break; } }; - auto renderDownloadProgress = [&badgeXMin, &badgeXCur](int progress) + auto renderDownloadProgress = [&badgeXMin, &badgeXCur](OscilloscopeChannel *scopechan) { static const char* const download[] = {"DOWNLOADING", "DOWNLOAD" ,"DL","D", "", NULL}; static const char* const wait[] = {"WAITING..." , "WAITING" ,"WA","W", "", NULL}; @@ -128,26 +119,31 @@ bool StreamBrowserDialog::DoRender() static const char* const ready[] = {"READY" , "RDY" ,"RY","R", "", NULL}; static const char* const* labels; ImVec4 color; - switch(progress) + bool hasProgress = false; + switch(scopechan->GetDownloadState()) { - case DownloadState::DOWNLOAD_NONE: + case InstrumentChannel::DownloadState::DOWNLOAD_NONE: labels = stop; color.x = 0.8 ; color.y=0.3 ; color.z=0.3; color.w=1.0; break; - case DownloadState::DOWNLOAD_WAITING: + case InstrumentChannel::DownloadState::DOWNLOAD_WAITING: labels = wait; color.x = 0.8 ; color.y=0.3 ; color.z=0.3; color.w=1.0; break; - case DownloadState::DOWNLOAD_FINISHED: + case InstrumentChannel::DownloadState::DOWNLOAD_IN_PROGRESS: + labels = download; + hasProgress = true; + color.x = 0.7 ; color.y=0.7 ; color.z=0.3; color.w=1.0; + break; + case InstrumentChannel::DownloadState::DOWNLOAD_FINISHED: labels = ready; + hasProgress = true; color.x = 0.3 ; color.y=0.8 ; color.z=0.3; color.w=1.0; break; default: - labels = download; - color.x = 0.7 ; color.y=0.7 ; color.z=0.3; color.w=1.0; + return; } // Only show progress bar if an download is proceeding - bool hasProgress = (progress >= DownloadState::DOWNLOAD_WAITING) && (progress <= DownloadState::DOWNLOAD_FINISHED); bool hasLabel; int labelIndex = 0; while (const char *label = labels[labelIndex]) @@ -158,7 +154,7 @@ bool StreamBrowserDialog::DoRender() labelIndex++; continue; } - + // ok, we have enough space -- commit to it! badgeXCur -= xsz - ImGui::GetStyle().ItemSpacing.x; ImGui::SameLine(badgeXCur); @@ -172,13 +168,13 @@ bool StreamBrowserDialog::DoRender() } if(hasProgress) { - ImGui::ProgressBar(((float)progress)/100, ImVec2(PROGRESS_BAR_WIDTH, ImGui::GetFontSize())); + ImGui::ProgressBar(scopechan->GetDownloadProgress(), ImVec2(PROGRESS_BAR_WIDTH, ImGui::GetFontSize())); } break; } }; - + //Add all instruments auto insts = m_session.GetInstruments(); for(auto inst : insts) @@ -187,7 +183,7 @@ bool StreamBrowserDialog::DoRender() startBadgeLine(); auto state = m_session.GetInstrumentConnectionState(inst); - + // Render ornaments for this instrument: offline, trigger status, ... auto scope = std::dynamic_pointer_cast(inst); if (scope) { @@ -226,7 +222,7 @@ bool StreamBrowserDialog::DoRender() } } } - + if(instIsOpen) { if (scope) { @@ -311,11 +307,7 @@ bool StreamBrowserDialog::DoRender() } else { - int progress = scopechan->GetDownloadState(); - if(progress != DownloadState::DOWNLOAD_PROGRESS_DISABLED) - { - renderDownloadProgress(progress); - } + renderDownloadProgress(scopechan); } } From 6f3d66692cf7356e81070aa4385d7339ab7ee8ac Mon Sep 17 00:00:00 2001 From: Joshua Wise Date: Mon, 7 Oct 2024 23:54:55 -0700 Subject: [PATCH 13/25] StreamBrowserDialog: prefer to show the status badge rather than progress if there is not enough space for both --- src/ngscopeclient/StreamBrowserDialog.cpp | 91 ++++++++++++++--------- 1 file changed, 57 insertions(+), 34 deletions(-) diff --git a/src/ngscopeclient/StreamBrowserDialog.cpp b/src/ngscopeclient/StreamBrowserDialog.cpp index eed4481e..dbc37bd3 100644 --- a/src/ngscopeclient/StreamBrowserDialog.cpp +++ b/src/ngscopeclient/StreamBrowserDialog.cpp @@ -37,9 +37,6 @@ #include "StreamBrowserDialog.h" #include "MainWindow.h" -/* @brief Width used to display progress bars (e.g. download progress bar)*/ -#define PROGRESS_BAR_WIDTH 90 - using namespace std; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -113,21 +110,41 @@ bool StreamBrowserDialog::DoRender() }; auto renderDownloadProgress = [&badgeXMin, &badgeXCur](OscilloscopeChannel *scopechan) { - static const char* const download[] = {"DOWNLOADING", "DOWNLOAD" ,"DL","D", "", NULL}; - static const char* const wait[] = {"WAITING..." , "WAITING" ,"WA","W", "", NULL}; - static const char* const stop[] = {"STOPPED" , "STOP" ,"ST","S", "", NULL}; - static const char* const ready[] = {"READY" , "RDY" ,"RY","R", "", NULL}; + static const char* const download[] = {"DOWNLOADING", "DOWNLOAD" ,"DL","D", NULL}; + + /* prefer language "PENDING" to "WAITING": "PENDING" implies + * that we are going to do it when we get through a list of + * other things, "WAITING" could mean that the channel is + * waiting for something else (trigger?) + */ + static const char* const pend[] = {"PENDING" , "PEND" ,"PE","P", NULL}; + + /* prefer language "COMPLETE" to "READY": "READY" implies + * that the channel might be ready to capture or something, + * but "COMPLETE" at least is not to be confused with that. + * ("DOWNLOADED" is more specific but is easy to confuse + * with "DOWNLOADING". If you can come up with a better + * mid-length abbreviation for "COMPLETE" than "DL OK" / + * "OK", give it a go, I guess.) + */ + static const char* const ready[] = {"COMPLETE" , "DL OK" ,"OK","C", NULL}; static const char* const* labels; ImVec4 color; bool hasProgress = false; switch(scopechan->GetDownloadState()) { case InstrumentChannel::DownloadState::DOWNLOAD_NONE: - labels = stop; - color.x = 0.8 ; color.y=0.3 ; color.z=0.3; color.w=1.0; - break; + case InstrumentChannel::DownloadState::DOWNLOAD_UNKNOWN: + /* There is nothing to say about this -- + * either there is nothing pending at all on + * the system, or this scope doesn't know + * how to report it, and in either case, we + * don't need to render a badge about it. + */ + return; case InstrumentChannel::DownloadState::DOWNLOAD_WAITING: - labels = wait; + labels = pend; + hasProgress = true; color.x = 0.8 ; color.y=0.3 ; color.z=0.3; color.w=1.0; break; case InstrumentChannel::DownloadState::DOWNLOAD_IN_PROGRESS: @@ -137,42 +154,48 @@ bool StreamBrowserDialog::DoRender() break; case InstrumentChannel::DownloadState::DOWNLOAD_FINISHED: labels = ready; - hasProgress = true; color.x = 0.3 ; color.y=0.8 ; color.z=0.3; color.w=1.0; break; default: return; } - // Only show progress bar if an download is proceeding - bool hasLabel; - int labelIndex = 0; - while (const char *label = labels[labelIndex]) + + +/// @brief Width used to display progress bars (e.g. download progress bar) +#define PROGRESS_BAR_WIDTH 80 + + // try first adding a bar, and if ther eisn't enough room + // for a bar, skip it and try just putting a label + for (int withoutBar = 0; withoutBar < 2; withoutBar++) { - hasLabel = strlen(label)>0; - float xsz = ImGui::CalcTextSize(label).x + (hasProgress ? PROGRESS_BAR_WIDTH : 0) + (ImGui::GetStyle().ItemSpacing.x) * ((hasProgress && hasLabel ? 1 : 0)+(hasLabel ? 1 : 0)) + ImGui::GetStyle().FramePadding.x * 2; - if ((badgeXCur - xsz) < badgeXMin) { - labelIndex++; - continue; - } + if (withoutBar) + hasProgress = false; - // ok, we have enough space -- commit to it! - badgeXCur -= xsz - ImGui::GetStyle().ItemSpacing.x; - ImGui::SameLine(badgeXCur); - if(hasLabel) + for (int i = 0; labels[i]; i++) { + const char *label = labels[i]; + + float xsz = ImGui::CalcTextSize(label).x + (hasProgress ? (ImGui::GetStyle().ItemSpacing.x + PROGRESS_BAR_WIDTH) : 0) + ImGui::GetStyle().FramePadding.x * 2 + ImGui::GetStyle().ItemSpacing.x; + if ((badgeXCur - xsz) < badgeXMin) + continue; + + // ok, we have enough space -- commit to it! + badgeXCur -= xsz - ImGui::GetStyle().ItemSpacing.x; + ImGui::SameLine(badgeXCur); ImGui::PushStyleColor(ImGuiCol_Button, color); - ImGui::SmallButton(labels[labelIndex]); + ImGui::SmallButton(label); ImGui::PopStyleColor(); - if(hasProgress) + if(hasProgress) + { ImGui::SameLine(); - } - if(hasProgress) - { - ImGui::ProgressBar(scopechan->GetDownloadProgress(), ImVec2(PROGRESS_BAR_WIDTH, ImGui::GetFontSize())); - } + ImGui::ProgressBar(scopechan->GetDownloadProgress(), ImVec2(PROGRESS_BAR_WIDTH, ImGui::GetFontSize())); + } - break; + return; + } } + + // well, shoot -- I guess there wasn't enough room to do *anything* useful! }; //Add all instruments From 4b04dd78ebf086bc0fc609f34e394df93081a253 Mon Sep 17 00:00:00 2001 From: Joshua Wise Date: Tue, 8 Oct 2024 00:10:20 -0700 Subject: [PATCH 14/25] StreamBrowserDialog: by default, make the dialog larger to fit more interesting properties --- src/ngscopeclient/MainWindow.cpp | 2 +- src/ngscopeclient/StreamBrowserDialog.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ngscopeclient/MainWindow.cpp b/src/ngscopeclient/MainWindow.cpp index 11246d0a..dc73770f 100644 --- a/src/ngscopeclient/MainWindow.cpp +++ b/src/ngscopeclient/MainWindow.cpp @@ -1277,7 +1277,7 @@ void MainWindow::DockingArea() rightPanelID = topNode->ChildNodes[1]->ID; } else - ImGui::DockBuilderSplitNode(topNode->ID, ImGuiDir_Left, 0.1, &leftPanelID, &rightPanelID); + ImGui::DockBuilderSplitNode(topNode->ID, ImGuiDir_Left, 0.2, &leftPanelID, &rightPanelID); ImGui::DockBuilderDockWindow(m_streamBrowser->GetTitleAndID().c_str(), leftPanelID); ImGui::DockBuilderDockWindow(m_initialWorkspaceDockRequest->GetTitleAndID().c_str(), rightPanelID); diff --git a/src/ngscopeclient/StreamBrowserDialog.cpp b/src/ngscopeclient/StreamBrowserDialog.cpp index dbc37bd3..2c505539 100644 --- a/src/ngscopeclient/StreamBrowserDialog.cpp +++ b/src/ngscopeclient/StreamBrowserDialog.cpp @@ -43,7 +43,7 @@ using namespace std; // Construction / destruction StreamBrowserDialog::StreamBrowserDialog(Session& session, MainWindow* parent) - : Dialog("Stream Browser", "Stream Browser", ImVec2(300, 400)) + : Dialog("Stream Browser", "Stream Browser", ImVec2(550, 400)) , m_session(session) , m_parent(parent) { From eb5e04539bac83f274f91436a87d148fe768015d Mon Sep 17 00:00:00 2001 From: Joshua Wise Date: Tue, 8 Oct 2024 00:13:17 -0700 Subject: [PATCH 15/25] StreamBrowserDialog: any kind of InstrumentChannel can have download progress now --- src/ngscopeclient/StreamBrowserDialog.cpp | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/ngscopeclient/StreamBrowserDialog.cpp b/src/ngscopeclient/StreamBrowserDialog.cpp index 2c505539..5112bc8d 100644 --- a/src/ngscopeclient/StreamBrowserDialog.cpp +++ b/src/ngscopeclient/StreamBrowserDialog.cpp @@ -108,7 +108,7 @@ bool StreamBrowserDialog::DoRender() break; } }; - auto renderDownloadProgress = [&badgeXMin, &badgeXCur](OscilloscopeChannel *scopechan) + auto renderDownloadProgress = [&badgeXMin, &badgeXCur](InstrumentChannel *chan) { static const char* const download[] = {"DOWNLOADING", "DOWNLOAD" ,"DL","D", NULL}; @@ -131,7 +131,7 @@ bool StreamBrowserDialog::DoRender() static const char* const* labels; ImVec4 color; bool hasProgress = false; - switch(scopechan->GetDownloadState()) + switch(chan->GetDownloadState()) { case InstrumentChannel::DownloadState::DOWNLOAD_NONE: case InstrumentChannel::DownloadState::DOWNLOAD_UNKNOWN: @@ -188,7 +188,7 @@ bool StreamBrowserDialog::DoRender() if(hasProgress) { ImGui::SameLine(); - ImGui::ProgressBar(scopechan->GetDownloadProgress(), ImVec2(PROGRESS_BAR_WIDTH, ImGui::GetFontSize())); + ImGui::ProgressBar(chan->GetDownloadProgress(), ImVec2(PROGRESS_BAR_WIDTH, ImGui::GetFontSize())); } return; @@ -322,16 +322,11 @@ bool StreamBrowserDialog::DoRender() // Channel decoration startBadgeLine(); - if (scopechan) + if (scopechan && !scopechan->IsEnabled()) { - if (!scopechan->IsEnabled()) - { - renderBadge(ImVec4(0.4, 0.4, 0.4, 1.0) /* XXX: pull color from prefs */, "disabled", "disa", NULL); - } - else - { - renderDownloadProgress(scopechan); - } + renderBadge(ImVec4(0.4, 0.4, 0.4, 1.0) /* XXX: pull color from prefs */, "disabled", "disa", NULL); + } else { + renderDownloadProgress(chan); } if(open) From ceb5be65ec2cb0e8d393a6fa6ad7d834ef64616d Mon Sep 17 00:00:00 2001 From: Joshua Wise Date: Tue, 8 Oct 2024 00:33:29 -0700 Subject: [PATCH 16/25] StreamDownloadWindow: add "download is slow" heuristic with hystersis --- src/ngscopeclient/StreamBrowserDialog.cpp | 36 +++++++++++++++++------ src/ngscopeclient/StreamBrowserDialog.h | 4 +++ 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/ngscopeclient/StreamBrowserDialog.cpp b/src/ngscopeclient/StreamBrowserDialog.cpp index 5112bc8d..8eedf5e9 100644 --- a/src/ngscopeclient/StreamBrowserDialog.cpp +++ b/src/ngscopeclient/StreamBrowserDialog.cpp @@ -108,17 +108,17 @@ bool StreamBrowserDialog::DoRender() break; } }; - auto renderDownloadProgress = [&badgeXMin, &badgeXCur](InstrumentChannel *chan) + auto renderDownloadProgress = [this, &badgeXMin, &badgeXCur](std::shared_ptr inst, InstrumentChannel *chan) { static const char* const download[] = {"DOWNLOADING", "DOWNLOAD" ,"DL","D", NULL}; - + /* prefer language "PENDING" to "WAITING": "PENDING" implies * that we are going to do it when we get through a list of * other things, "WAITING" could mean that the channel is * waiting for something else (trigger?) */ static const char* const pend[] = {"PENDING" , "PEND" ,"PE","P", NULL}; - + /* prefer language "COMPLETE" to "READY": "READY" implies * that the channel might be ready to capture or something, * but "COMPLETE" at least is not to be confused with that. @@ -129,8 +129,21 @@ bool StreamBrowserDialog::DoRender() */ static const char* const ready[] = {"COMPLETE" , "DL OK" ,"OK","C", NULL}; static const char* const* labels; + ImVec4 color; bool hasProgress = false; + double elapsed = GetTime() - chan->GetDownloadStartTime(); + + // determine what label we should apply, and while we are at + // it, determine if this channel appears to be slow enough + // to need a progress bar + +/// @brief hysteresis threshold for a channel finishing a download faster than this to be declared fast +#define CHANNEL_DOWNLOAD_THRESHOLD_FAST_SECONDS ((double)0.2) + +/// @brief hysteresis threshold for a channel finishing a still being in progress for longer than this to be declared slow +#define CHANNEL_DOWNLOAD_THRESHOLD_SLOW_SECONDS ((double)0.4) + switch(chan->GetDownloadState()) { case InstrumentChannel::DownloadState::DOWNLOAD_NONE: @@ -144,27 +157,32 @@ bool StreamBrowserDialog::DoRender() return; case InstrumentChannel::DownloadState::DOWNLOAD_WAITING: labels = pend; - hasProgress = true; + if (elapsed > CHANNEL_DOWNLOAD_THRESHOLD_SLOW_SECONDS) + m_channelDownloadIsSlow[{inst, chan}] = true; + hasProgress = m_channelDownloadIsSlow[{inst, chan}]; color.x = 0.8 ; color.y=0.3 ; color.z=0.3; color.w=1.0; break; case InstrumentChannel::DownloadState::DOWNLOAD_IN_PROGRESS: labels = download; - hasProgress = true; + if (elapsed > CHANNEL_DOWNLOAD_THRESHOLD_SLOW_SECONDS) + m_channelDownloadIsSlow[{inst, chan}] = true; + hasProgress = m_channelDownloadIsSlow[{inst, chan}]; color.x = 0.7 ; color.y=0.7 ; color.z=0.3; color.w=1.0; break; case InstrumentChannel::DownloadState::DOWNLOAD_FINISHED: labels = ready; + if (elapsed < CHANNEL_DOWNLOAD_THRESHOLD_FAST_SECONDS) + m_channelDownloadIsSlow[{inst, chan}] = false; color.x = 0.3 ; color.y=0.8 ; color.z=0.3; color.w=1.0; break; default: return; } - /// @brief Width used to display progress bars (e.g. download progress bar) #define PROGRESS_BAR_WIDTH 80 - // try first adding a bar, and if ther eisn't enough room + // try first adding a bar, and if there isn't enough room // for a bar, skip it and try just putting a label for (int withoutBar = 0; withoutBar < 2; withoutBar++) { @@ -194,7 +212,7 @@ bool StreamBrowserDialog::DoRender() return; } } - + // well, shoot -- I guess there wasn't enough room to do *anything* useful! }; @@ -326,7 +344,7 @@ bool StreamBrowserDialog::DoRender() { renderBadge(ImVec4(0.4, 0.4, 0.4, 1.0) /* XXX: pull color from prefs */, "disabled", "disa", NULL); } else { - renderDownloadProgress(chan); + renderDownloadProgress(inst, chan); } if(open) diff --git a/src/ngscopeclient/StreamBrowserDialog.h b/src/ngscopeclient/StreamBrowserDialog.h index 6af3897e..61b90431 100644 --- a/src/ngscopeclient/StreamBrowserDialog.h +++ b/src/ngscopeclient/StreamBrowserDialog.h @@ -35,6 +35,8 @@ #ifndef StreamBrowserDialog_h #define StreamBrowserDialog_h +#include + #include "Dialog.h" #include "Session.h" @@ -53,6 +55,8 @@ class StreamBrowserDialog : public Dialog Session& m_session; MainWindow* m_parent; + + std::map, InstrumentChannel *>, bool> m_channelDownloadIsSlow; }; #endif From a1000800968d728f107184e41ca34e4f0a7ebb6c Mon Sep 17 00:00:00 2001 From: fredzo Date: Tue, 8 Oct 2024 16:07:28 +0200 Subject: [PATCH 17/25] Removed no longer needed code after moving to GetChannelDowloadProgress() API. --- src/ngscopeclient/Session.h | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/src/ngscopeclient/Session.h b/src/ngscopeclient/Session.h index 38880b35..a570f946 100644 --- a/src/ngscopeclient/Session.h +++ b/src/ngscopeclient/Session.h @@ -59,12 +59,6 @@ class InstrumentConnectionState args.shuttingDown = &m_shuttingDown; m_thread = std::make_unique(InstrumentThread, args); m_lastTriggerState = Oscilloscope::TRIGGER_MODE_WAIT; - size_t channelNumber = 0; - - // TODO : temporary => should no longer be needed once GetDownloadProgress() API is available on OscilloscopeChannel - if(args.inst) - channelNumber = args.inst->GetChannelCount(); - m_channelDownloadStates.assign(channelNumber,-1); } ~InstrumentConnectionState() @@ -81,20 +75,6 @@ class InstrumentConnectionState m_thread = nullptr; } - int GetChannelDownloadState(size_t i) const - { - if(i >= m_channelDownloadStates.size()) - return -1; - - return m_channelDownloadStates[i]; - } - - void SetChannelDownloadState(size_t i, int downloadState) - { - m_channelDownloadStates[i] = downloadState; - } - - ///@brief Termination flag for shutting down the polling thread std::atomic m_shuttingDown; @@ -103,10 +83,6 @@ class InstrumentConnectionState ///@brief Cached trigger state, to reflect in the UI Oscilloscope::TriggerMode m_lastTriggerState; - - // TODO : temporary => should no longer be needed once GetDownloadProgress() API is available on OscilloscopeChannel - ///@brief Cached download sates, to reflect in the UI - std::vector m_channelDownloadStates; }; /** From b419a0ba2d868e5bfd49af740f5be5a1a93aac40 Mon Sep 17 00:00:00 2001 From: Frederic Borry Date: Wed, 9 Oct 2024 01:04:30 +0200 Subject: [PATCH 18/25] Prevents flickering for fast download. --- src/ngscopeclient/StreamBrowserDialog.cpp | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/ngscopeclient/StreamBrowserDialog.cpp b/src/ngscopeclient/StreamBrowserDialog.cpp index 8eedf5e9..f81cf2e4 100644 --- a/src/ngscopeclient/StreamBrowserDialog.cpp +++ b/src/ngscopeclient/StreamBrowserDialog.cpp @@ -127,11 +127,17 @@ bool StreamBrowserDialog::DoRender() * mid-length abbreviation for "COMPLETE" than "DL OK" / * "OK", give it a go, I guess.) */ - static const char* const ready[] = {"COMPLETE" , "DL OK" ,"OK","C", NULL}; + static const char* const ready[] = {"COMPLETE" , "DL OK" ,"OK","C", NULL}; + + /* Let's use active for fast download channels to display when data is available + */ + static const char* const active[] = {"ACTIVE" , "ACTV" ,"ACT","A", NULL}; + static const char* const* labels; ImVec4 color; bool hasProgress = false; + bool isActive = false; double elapsed = GetTime() - chan->GetDownloadStartTime(); // determine what label we should apply, and while we are at @@ -160,6 +166,7 @@ bool StreamBrowserDialog::DoRender() if (elapsed > CHANNEL_DOWNLOAD_THRESHOLD_SLOW_SECONDS) m_channelDownloadIsSlow[{inst, chan}] = true; hasProgress = m_channelDownloadIsSlow[{inst, chan}]; + isActive = !hasProgress; color.x = 0.8 ; color.y=0.3 ; color.z=0.3; color.w=1.0; break; case InstrumentChannel::DownloadState::DOWNLOAD_IN_PROGRESS: @@ -167,18 +174,26 @@ bool StreamBrowserDialog::DoRender() if (elapsed > CHANNEL_DOWNLOAD_THRESHOLD_SLOW_SECONDS) m_channelDownloadIsSlow[{inst, chan}] = true; hasProgress = m_channelDownloadIsSlow[{inst, chan}]; + isActive = !hasProgress; color.x = 0.7 ; color.y=0.7 ; color.z=0.3; color.w=1.0; break; case InstrumentChannel::DownloadState::DOWNLOAD_FINISHED: labels = ready; if (elapsed < CHANNEL_DOWNLOAD_THRESHOLD_FAST_SECONDS) m_channelDownloadIsSlow[{inst, chan}] = false; + isActive = !m_channelDownloadIsSlow[{inst, chan}]; color.x = 0.3 ; color.y=0.8 ; color.z=0.3; color.w=1.0; break; default: return; } + if(isActive) + { // For fast channels, only show a constant ACTIVE green badge + labels = active; + color.x = 0.3 ; color.y=0.8 ; color.z=0.3; color.w=1.0; + } + /// @brief Width used to display progress bars (e.g. download progress bar) #define PROGRESS_BAR_WIDTH 80 @@ -343,7 +358,9 @@ bool StreamBrowserDialog::DoRender() if (scopechan && !scopechan->IsEnabled()) { renderBadge(ImVec4(0.4, 0.4, 0.4, 1.0) /* XXX: pull color from prefs */, "disabled", "disa", NULL); - } else { + } + else if (state && state->m_lastTriggerState != Oscilloscope::TRIGGER_MODE_STOP) + { renderDownloadProgress(inst, chan); } From 1d6cd5b9fde8c7885d8e0b66c430dc95992a943d Mon Sep 17 00:00:00 2001 From: fredzo Date: Wed, 9 Oct 2024 19:42:08 +0200 Subject: [PATCH 19/25] Fixed end of download detection based on new AcquisitionStopped() helper method on Oscilloscope. Fixed looping on PollTrigger() when scope is in Run mode. --- src/ngscopeclient/InstrumentThread.cpp | 2 +- src/ngscopeclient/StreamBrowserDialog.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ngscopeclient/InstrumentThread.cpp b/src/ngscopeclient/InstrumentThread.cpp index 90bc86c5..21072c83 100644 --- a/src/ngscopeclient/InstrumentThread.cpp +++ b/src/ngscopeclient/InstrumentThread.cpp @@ -92,7 +92,7 @@ void InstrumentThread(InstrumentThreadArgs args) { // Check for trigger state change auto stat = scope->PollTrigger(); session->GetInstrumentConnectionState(inst)->m_lastTriggerState = stat; - if(stat == Oscilloscope::TRIGGER_MODE_STOP) + if(stat == Oscilloscope::TRIGGER_MODE_STOP || stat == Oscilloscope::TRIGGER_MODE_RUN || stat == Oscilloscope::TRIGGER_MODE_TRIGGERED) { // Final state triggerUpToDate = true; } diff --git a/src/ngscopeclient/StreamBrowserDialog.cpp b/src/ngscopeclient/StreamBrowserDialog.cpp index f81cf2e4..88eeb104 100644 --- a/src/ngscopeclient/StreamBrowserDialog.cpp +++ b/src/ngscopeclient/StreamBrowserDialog.cpp @@ -359,7 +359,7 @@ bool StreamBrowserDialog::DoRender() { renderBadge(ImVec4(0.4, 0.4, 0.4, 1.0) /* XXX: pull color from prefs */, "disabled", "disa", NULL); } - else if (state && state->m_lastTriggerState != Oscilloscope::TRIGGER_MODE_STOP) + else if (state) { renderDownloadProgress(inst, chan); } From 48c5651b8386448af3ea571b12e4412d52a874ba Mon Sep 17 00:00:00 2001 From: Joshua Wise Date: Wed, 9 Oct 2024 12:21:40 -0700 Subject: [PATCH 20/25] StreamBrowserDialog: make "active" behave like a hard drive indicator light --- src/ngscopeclient/StreamBrowserDialog.cpp | 31 ++++++++++++++++------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/src/ngscopeclient/StreamBrowserDialog.cpp b/src/ngscopeclient/StreamBrowserDialog.cpp index 88eeb104..29194047 100644 --- a/src/ngscopeclient/StreamBrowserDialog.cpp +++ b/src/ngscopeclient/StreamBrowserDialog.cpp @@ -136,8 +136,8 @@ bool StreamBrowserDialog::DoRender() static const char* const* labels; ImVec4 color; + bool shouldRender = true; bool hasProgress = false; - bool isActive = false; double elapsed = GetTime() - chan->GetDownloadStartTime(); // determine what label we should apply, and while we are at @@ -153,6 +153,9 @@ bool StreamBrowserDialog::DoRender() switch(chan->GetDownloadState()) { case InstrumentChannel::DownloadState::DOWNLOAD_NONE: + if (elapsed < CHANNEL_DOWNLOAD_THRESHOLD_FAST_SECONDS) + m_channelDownloadIsSlow[{inst, chan}] = false; + /* FALLTHRU */ case InstrumentChannel::DownloadState::DOWNLOAD_UNKNOWN: /* There is nothing to say about this -- * either there is nothing pending at all on @@ -160,13 +163,13 @@ bool StreamBrowserDialog::DoRender() * how to report it, and in either case, we * don't need to render a badge about it. */ - return; + shouldRender = false; + break; case InstrumentChannel::DownloadState::DOWNLOAD_WAITING: labels = pend; if (elapsed > CHANNEL_DOWNLOAD_THRESHOLD_SLOW_SECONDS) m_channelDownloadIsSlow[{inst, chan}] = true; hasProgress = m_channelDownloadIsSlow[{inst, chan}]; - isActive = !hasProgress; color.x = 0.8 ; color.y=0.3 ; color.z=0.3; color.w=1.0; break; case InstrumentChannel::DownloadState::DOWNLOAD_IN_PROGRESS: @@ -174,25 +177,35 @@ bool StreamBrowserDialog::DoRender() if (elapsed > CHANNEL_DOWNLOAD_THRESHOLD_SLOW_SECONDS) m_channelDownloadIsSlow[{inst, chan}] = true; hasProgress = m_channelDownloadIsSlow[{inst, chan}]; - isActive = !hasProgress; color.x = 0.7 ; color.y=0.7 ; color.z=0.3; color.w=1.0; break; case InstrumentChannel::DownloadState::DOWNLOAD_FINISHED: labels = ready; if (elapsed < CHANNEL_DOWNLOAD_THRESHOLD_FAST_SECONDS) m_channelDownloadIsSlow[{inst, chan}] = false; - isActive = !m_channelDownloadIsSlow[{inst, chan}]; color.x = 0.3 ; color.y=0.8 ; color.z=0.3; color.w=1.0; break; default: - return; + shouldRender = false; + break; } - if(isActive) - { // For fast channels, only show a constant ACTIVE green badge + // For fast channels, show a constant green badge when a + // download has started "recently" -- even if we're not + // downloading at this moment. This could be slightly + // misleading (i.e., after a channel goes into STOP mode, we + // will remain ACTIVE for up to THRESHOLD_SLOW time) but the + // period of time for which it is misleading is short! + if(!m_channelDownloadIsSlow[{inst, chan}] && elapsed < CHANNEL_DOWNLOAD_THRESHOLD_SLOW_SECONDS) + { labels = active; color.x = 0.3 ; color.y=0.8 ; color.z=0.3; color.w=1.0; + shouldRender = true; + hasProgress = false; } + + if (!shouldRender) + return; /// @brief Width used to display progress bars (e.g. download progress bar) #define PROGRESS_BAR_WIDTH 80 @@ -359,7 +372,7 @@ bool StreamBrowserDialog::DoRender() { renderBadge(ImVec4(0.4, 0.4, 0.4, 1.0) /* XXX: pull color from prefs */, "disabled", "disa", NULL); } - else if (state) + else { renderDownloadProgress(inst, chan); } From ff70b363b61facfa2e73882b8c8478e76c099bba Mon Sep 17 00:00:00 2001 From: fredzo Date: Wed, 9 Oct 2024 23:27:26 +0200 Subject: [PATCH 21/25] Made download speed detection global for an instrument rather then per-channel. --- src/ngscopeclient/StreamBrowserDialog.cpp | 32 ++++++++++++++--------- src/ngscopeclient/StreamBrowserDialog.h | 2 +- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/ngscopeclient/StreamBrowserDialog.cpp b/src/ngscopeclient/StreamBrowserDialog.cpp index 29194047..53a60430 100644 --- a/src/ngscopeclient/StreamBrowserDialog.cpp +++ b/src/ngscopeclient/StreamBrowserDialog.cpp @@ -108,7 +108,7 @@ bool StreamBrowserDialog::DoRender() break; } }; - auto renderDownloadProgress = [this, &badgeXMin, &badgeXCur](std::shared_ptr inst, InstrumentChannel *chan) + auto renderDownloadProgress = [this, &badgeXMin, &badgeXCur](std::shared_ptr inst, InstrumentChannel *chan, bool isLast) { static const char* const download[] = {"DOWNLOADING", "DOWNLOAD" ,"DL","D", NULL}; @@ -153,8 +153,8 @@ bool StreamBrowserDialog::DoRender() switch(chan->GetDownloadState()) { case InstrumentChannel::DownloadState::DOWNLOAD_NONE: - if (elapsed < CHANNEL_DOWNLOAD_THRESHOLD_FAST_SECONDS) - m_channelDownloadIsSlow[{inst, chan}] = false; + if (isLast && (elapsed < CHANNEL_DOWNLOAD_THRESHOLD_FAST_SECONDS)) + m_instrumentDownloadIsSlow[inst] = false; /* FALLTHRU */ case InstrumentChannel::DownloadState::DOWNLOAD_UNKNOWN: /* There is nothing to say about this -- @@ -168,21 +168,21 @@ bool StreamBrowserDialog::DoRender() case InstrumentChannel::DownloadState::DOWNLOAD_WAITING: labels = pend; if (elapsed > CHANNEL_DOWNLOAD_THRESHOLD_SLOW_SECONDS) - m_channelDownloadIsSlow[{inst, chan}] = true; - hasProgress = m_channelDownloadIsSlow[{inst, chan}]; + m_instrumentDownloadIsSlow[inst] = true; + hasProgress = m_instrumentDownloadIsSlow[inst]; color.x = 0.8 ; color.y=0.3 ; color.z=0.3; color.w=1.0; break; case InstrumentChannel::DownloadState::DOWNLOAD_IN_PROGRESS: labels = download; if (elapsed > CHANNEL_DOWNLOAD_THRESHOLD_SLOW_SECONDS) - m_channelDownloadIsSlow[{inst, chan}] = true; - hasProgress = m_channelDownloadIsSlow[{inst, chan}]; + m_instrumentDownloadIsSlow[inst] = true; + hasProgress = m_instrumentDownloadIsSlow[inst]; color.x = 0.7 ; color.y=0.7 ; color.z=0.3; color.w=1.0; break; case InstrumentChannel::DownloadState::DOWNLOAD_FINISHED: labels = ready; - if (elapsed < CHANNEL_DOWNLOAD_THRESHOLD_FAST_SECONDS) - m_channelDownloadIsSlow[{inst, chan}] = false; + if (isLast && (elapsed < CHANNEL_DOWNLOAD_THRESHOLD_FAST_SECONDS)) + m_instrumentDownloadIsSlow[inst] = false; color.x = 0.3 ; color.y=0.8 ; color.z=0.3; color.w=1.0; break; default: @@ -196,7 +196,7 @@ bool StreamBrowserDialog::DoRender() // misleading (i.e., after a channel goes into STOP mode, we // will remain ACTIVE for up to THRESHOLD_SLOW time) but the // period of time for which it is misleading is short! - if(!m_channelDownloadIsSlow[{inst, chan}] && elapsed < CHANNEL_DOWNLOAD_THRESHOLD_SLOW_SECONDS) + if(!m_instrumentDownloadIsSlow[inst] && elapsed < CHANNEL_DOWNLOAD_THRESHOLD_SLOW_SECONDS) { labels = active; color.x = 0.3 ; color.y=0.8 ; color.z=0.3; color.w=1.0; @@ -294,6 +294,8 @@ bool StreamBrowserDialog::DoRender() if(instIsOpen) { + size_t lastEnabledChannelIndex; + size_t channelCount = inst->GetChannelCount(); if (scope) { ImGui::BeginChild("sample_params", ImVec2(0, 0), ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_Border); @@ -310,11 +312,17 @@ bool StreamBrowserDialog::DoRender() if (hovered) { m_parent->AddStatusHelp("mouse_lmb", "Open timebase properties"); } + for(size_t i = 0; iIsChannelEnabled(i)) + { + lastEnabledChannelIndex = i; + } + } ImGui::EndChild(); } - size_t channelCount = inst->GetChannelCount(); for(size_t i=0; iGetChannel(i); @@ -374,7 +382,7 @@ bool StreamBrowserDialog::DoRender() } else { - renderDownloadProgress(inst, chan); + renderDownloadProgress(inst, chan, (i == lastEnabledChannelIndex)); } if(open) diff --git a/src/ngscopeclient/StreamBrowserDialog.h b/src/ngscopeclient/StreamBrowserDialog.h index 61b90431..dc497518 100644 --- a/src/ngscopeclient/StreamBrowserDialog.h +++ b/src/ngscopeclient/StreamBrowserDialog.h @@ -56,7 +56,7 @@ class StreamBrowserDialog : public Dialog Session& m_session; MainWindow* m_parent; - std::map, InstrumentChannel *>, bool> m_channelDownloadIsSlow; + std::map, bool> m_instrumentDownloadIsSlow; }; #endif From d4334619200b9a44b73dcc5d46598802110b7743 Mon Sep 17 00:00:00 2001 From: "Andrew D. Zonenberg" Date: Wed, 9 Oct 2024 20:45:15 -0700 Subject: [PATCH 22/25] StreamBrowserDialog: fixed build warnings and some formatting issues --- src/ngscopeclient/StreamBrowserDialog.cpp | 52 +++++++++++------------ 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/src/ngscopeclient/StreamBrowserDialog.cpp b/src/ngscopeclient/StreamBrowserDialog.cpp index 53a60430..df4cbe6e 100644 --- a/src/ngscopeclient/StreamBrowserDialog.cpp +++ b/src/ngscopeclient/StreamBrowserDialog.cpp @@ -121,7 +121,7 @@ bool StreamBrowserDialog::DoRender() /* prefer language "COMPLETE" to "READY": "READY" implies * that the channel might be ready to capture or something, - * but "COMPLETE" at least is not to be confused with that. + * but "COMPLETE" at least is not to be confused with that. * ("DOWNLOADED" is more specific but is easy to confuse * with "DOWNLOADING". If you can come up with a better * mid-length abbreviation for "COMPLETE" than "DL OK" / @@ -203,7 +203,7 @@ bool StreamBrowserDialog::DoRender() shouldRender = true; hasProgress = false; } - + if (!shouldRender) return; @@ -256,9 +256,10 @@ bool StreamBrowserDialog::DoRender() // Render ornaments for this instrument: offline, trigger status, ... auto scope = std::dynamic_pointer_cast(inst); if (scope) { - if (scope->IsOffline()) { + if (scope->IsOffline()) renderBadge(ImVec4(0.8, 0.3, 0.3, 1.0) /* XXX: pull color from prefs */, "OFFLINE", "OFFL", NULL); - } else { + else + { Oscilloscope::TriggerMode mode = state ? state->m_lastTriggerState : Oscilloscope::TRIGGER_MODE_STOP; switch (mode) { case Oscilloscope::TRIGGER_MODE_RUN: @@ -294,24 +295,23 @@ bool StreamBrowserDialog::DoRender() if(instIsOpen) { - size_t lastEnabledChannelIndex; + size_t lastEnabledChannelIndex = 0; size_t channelCount = inst->GetChannelCount(); - if (scope) { + if (scope) + { ImGui::BeginChild("sample_params", ImVec2(0, 0), ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_Border); - + auto srate_txt = Unit(Unit::UNIT_SAMPLERATE).PrettyPrint(scope->GetSampleRate()); auto sdepth_txt = Unit(Unit::UNIT_SAMPLEDEPTH).PrettyPrint(scope->GetSampleDepth()); - + bool clicked = false; bool hovered = false; renderInfoLink("Sample rate", srate_txt.c_str(), clicked, hovered); renderInfoLink("Sample depth", sdepth_txt.c_str(), clicked, hovered); - if (clicked) { + if (clicked) m_parent->ShowTimebaseProperties(); - } - if (hovered) { + if (hovered) m_parent->AddStatusHelp("mouse_lmb", "Open timebase properties"); - } for(size_t i = 0; iIsChannelEnabled(i)) @@ -319,10 +319,10 @@ bool StreamBrowserDialog::DoRender() lastEnabledChannelIndex = i; } } - + ImGui::EndChild(); } - + for(size_t i=0; iGetChannel(i); @@ -331,7 +331,7 @@ bool StreamBrowserDialog::DoRender() auto scopechan = dynamic_cast(chan); bool renderScopeProps = false; bool isDigital = false; - if (scopechan) + if (scopechan) { renderScopeProps = scopechan->IsEnabled(); isDigital = scopechan->GetType(0) == Stream::STREAM_TYPE_DIGITAL; @@ -339,14 +339,12 @@ bool StreamBrowserDialog::DoRender() bool hasChildren = !singleStream || renderScopeProps; - if (chan->m_displaycolor != "") { + if (chan->m_displaycolor != "") ImGui::PushStyleColor(ImGuiCol_Text, ColorFromString(chan->m_displaycolor)); - } bool open = ImGui::TreeNodeEx(chan->GetDisplayName().c_str(), isDigital ? 0 : ImGuiTreeNodeFlags_DefaultOpen | (!hasChildren ? ImGuiTreeNodeFlags_Leaf : 0)); - if (chan->m_displaycolor != "") { + if (chan->m_displaycolor != "") ImGui::PopStyleColor(); - } - + //Single stream: drag the stream not the channel if(singleStream) { @@ -373,14 +371,14 @@ bool StreamBrowserDialog::DoRender() ImGui::TextUnformatted(chan->GetDisplayName().c_str()); ImGui::EndDragDropSource(); } - + // Channel decoration startBadgeLine(); if (scopechan && !scopechan->IsEnabled()) { renderBadge(ImVec4(0.4, 0.4, 0.4, 1.0) /* XXX: pull color from prefs */, "disabled", "disa", NULL); - } - else + } + else { renderDownloadProgress(inst, chan, (i == lastEnabledChannelIndex)); } @@ -411,11 +409,11 @@ bool StreamBrowserDialog::DoRender() if (renderScopeProps && scopechan) { ImGui::BeginChild("scope_params", ImVec2(0, 0), ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_Border); - + if(isDigital) { auto threshold_txt = Unit(Unit::UNIT_VOLTS).PrettyPrint(scope->GetDigitalThreshold(i)); - + bool clicked = false; bool hovered = false; renderInfoLink("Threshold", threshold_txt.c_str(), clicked, hovered); @@ -431,7 +429,7 @@ bool StreamBrowserDialog::DoRender() { auto offset_txt = Unit(Unit::UNIT_VOLTS).PrettyPrint(scopechan->GetOffset(j)); auto range_txt = Unit(Unit::UNIT_VOLTS).PrettyPrint(scopechan->GetVoltageRange(j)); - + bool clicked = false; bool hovered = false; renderInfoLink("Offset", offset_txt.c_str(), clicked, hovered); @@ -444,7 +442,7 @@ bool StreamBrowserDialog::DoRender() m_parent->AddStatusHelp("mouse_lmb", "Open channel properties"); } } - + ImGui::EndChild(); } } From 0a3499a2ff3bfe47e439dab24bba12987bca6010 Mon Sep 17 00:00:00 2001 From: "Andrew D. Zonenberg" Date: Wed, 9 Oct 2024 21:10:58 -0700 Subject: [PATCH 23/25] Fixed bug in stream browser causing multiple streams to appear in the same window due to lack of unique IDs --- devdoc/Introduction.md | 6 ++++++ lib | 2 +- src/ngscopeclient/StreamBrowserDialog.cpp | 19 ++++++++++++------- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/devdoc/Introduction.md b/devdoc/Introduction.md index 9d5cb4c5..9267acad 100644 --- a/devdoc/Introduction.md +++ b/devdoc/Introduction.md @@ -33,6 +33,9 @@ End user documentation is located at https://www.ngscopeclient.org/manual/conten \defgroup spectrometerdrivers Spectrometer drivers \ingroup drivers +\defgroup matrixdrivers Switch matrix drivers +\ingroup drivers + \defgroup vnadrivers VNA drivers \ingroup drivers @@ -50,6 +53,9 @@ End user documentation is located at https://www.ngscopeclient.org/manual/conten \defgroup math Basic math functions \ingroup libscopeprotocols +\defgroup rf RF +\ingroup libscopeprotocols + \defgroup ngscopeclient Ngscopeclient (GUI) \defgroup dialogs Dialog boxes diff --git a/lib b/lib index 7f13107a..1aba50ef 160000 --- a/lib +++ b/lib @@ -1 +1 @@ -Subproject commit 7f13107aaed51b1540c9595e18d9b4acce3736b6 +Subproject commit 1aba50ef70e1c791521cf73456493b3cbdc94260 diff --git a/src/ngscopeclient/StreamBrowserDialog.cpp b/src/ngscopeclient/StreamBrowserDialog.cpp index df4cbe6e..334f63cc 100644 --- a/src/ngscopeclient/StreamBrowserDialog.cpp +++ b/src/ngscopeclient/StreamBrowserDialog.cpp @@ -387,6 +387,8 @@ bool StreamBrowserDialog::DoRender() { for(size_t j=0; jGetStreamCount(); j++) { + ImGui::PushID(j); + if (!singleStream) { ImGui::Selectable(chan->GetStreamName(j).c_str()); @@ -408,7 +410,8 @@ bool StreamBrowserDialog::DoRender() // Channel/stram properties if (renderScopeProps && scopechan) { - ImGui::BeginChild("scope_params", ImVec2(0, 0), ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_Border); + ImGui::BeginChild("scope_params", ImVec2(0, 0), + ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_Border); if(isDigital) { @@ -417,13 +420,13 @@ bool StreamBrowserDialog::DoRender() bool clicked = false; bool hovered = false; renderInfoLink("Threshold", threshold_txt.c_str(), clicked, hovered); - if (clicked) { + if (clicked) + { /* XXX: refactor to be more like FilterGraphEditor::HandleNodeProperties? */ m_parent->ShowChannelProperties(scopechan); } - if (hovered) { + if (hovered) m_parent->AddStatusHelp("mouse_lmb", "Open channel properties"); - } } else { @@ -434,17 +437,19 @@ bool StreamBrowserDialog::DoRender() bool hovered = false; renderInfoLink("Offset", offset_txt.c_str(), clicked, hovered); renderInfoLink("Voltage range", range_txt.c_str(), clicked, hovered); - if (clicked) { + if (clicked) + { /* XXX: refactor to be more like FilterGraphEditor::HandleNodeProperties? */ m_parent->ShowChannelProperties(scopechan); } - if (hovered) { + if (hovered) m_parent->AddStatusHelp("mouse_lmb", "Open channel properties"); - } } ImGui::EndChild(); } + + ImGui::PopID(); } } From 9923605d6f489a44f07277a1e9cfe02491f87767 Mon Sep 17 00:00:00 2001 From: "Andrew D. Zonenberg" Date: Wed, 9 Oct 2024 23:38:39 -0700 Subject: [PATCH 24/25] Initial implementation of runtime profiling for filter graph --- lib | 2 +- src/ngscopeclient/FilterGraphEditor.cpp | 56 +++++++++++++++++- src/ngscopeclient/FilterGraphEditor.h | 6 +- src/ngscopeclient/MainWindow_Icons.cpp | 2 + src/ngscopeclient/Session.cpp | 11 +++- src/ngscopeclient/Session.h | 15 ++++- .../icons/contrib/blender/24x24/time.png | Bin 0 -> 472 bytes .../icons/contrib/blender/scalable/time.svg | 5 ++ 8 files changed, 89 insertions(+), 8 deletions(-) create mode 100644 src/ngscopeclient/icons/contrib/blender/24x24/time.png create mode 100644 src/ngscopeclient/icons/contrib/blender/scalable/time.svg diff --git a/lib b/lib index 1aba50ef..cb21bd2c 160000 --- a/lib +++ b/lib @@ -1 +1 @@ -Subproject commit 1aba50ef70e1c791521cf73456493b3cbdc94260 +Subproject commit cb21bd2ce3f10488cca12fa6e31daf24b4ebbea7 diff --git a/src/ngscopeclient/FilterGraphEditor.cpp b/src/ngscopeclient/FilterGraphEditor.cpp index 7e1e1755..342bbcc6 100644 --- a/src/ngscopeclient/FilterGraphEditor.cpp +++ b/src/ngscopeclient/FilterGraphEditor.cpp @@ -494,7 +494,7 @@ bool FilterGraphEditor::DoRender() for(auto it : chans) { for(auto chan : it.second) - DoNodeForChannel(chan, it.first, multiInst); + DoNodeForChannel(chan, it.first, multiInst, 0); //TODO: acquisition time etc? } //Make a newly dragged node spawn at the mouse position @@ -518,9 +518,10 @@ bool FilterGraphEditor::DoRender() //Filters auto filters = Filter::GetAllInstances(); + auto filterperf = m_session.GetFilterGraphRuntime(); for(auto f : filters) { - DoNodeForChannel(f, nullptr, false); + DoNodeForChannel(f, nullptr, false, filterperf[f]); //Add a reference to the channel so even if we remove the last user of it this frame, it won't be deleted until we're ready f->AddRef(); @@ -1942,8 +1943,14 @@ void FilterGraphEditor::DoNodeForTrigger(Trigger* trig) TODO: this seems to fail hard if we do not have at least one input OR output on the node. Why? */ -void FilterGraphEditor::DoNodeForChannel(InstrumentChannel* channel, shared_ptr inst, bool multiInst) +void FilterGraphEditor::DoNodeForChannel( + InstrumentChannel* channel, + shared_ptr inst, + bool multiInst, + int64_t runtime) { + Unit fs(Unit::UNIT_FS); + //If the channel has no color, make it neutral gray //(this is often true for e.g. external trigger) string displaycolor = channel->m_displaycolor; @@ -2136,6 +2143,49 @@ void FilterGraphEditor::DoNodeForChannel(InstrumentChannel* channel, shared_ptr< headercolor, headerText.c_str()); + //TODO: add an option for toggling this + //TODO: add preference for colors + //Draw a bubble above the text with the runtime stats + if(runtime > 0) + { + auto runtimeText = fs.PrettyPrint(runtime); + auto runtimeSize = headerfont->CalcTextSizeA(headerfontsize, FLT_MAX, 0, runtimeText.c_str()); + + auto timebgColor = ColorFromString("#404040"); + auto timeTextColor = ColorFromString("#ffffff"); + float timespacing = 0.1 * headerheight; + float runtimeBot = pos.y - timespacing; + float bubbleHeight = runtimeSize.y + 2*ImGui::GetStyle().FramePadding.y; + + ImVec2 clockiconpos( + pos.x + ImGui::GetStyle().FramePadding.x, + runtimeBot - bubbleHeight + ImGui::GetStyle().FramePadding.y); + ImVec2 clockiconsize(runtimeSize.y, runtimeSize.y); + + ImVec2 textpos( + clockiconpos.x + clockiconsize.x + ImGui::GetStyle().ItemSpacing.x, + clockiconpos.y ); + + bgList->AddRectFilled( + ImVec2(pos.x + 1, runtimeBot - bubbleHeight), + ImVec2(textpos.x + runtimeSize.x + ImGui::GetStyle().FramePadding.y, runtimeBot), + timebgColor, + rounding, + ImDrawFlags_RoundCornersAll); + + bgList->AddImage( + m_parent->GetTextureManager()->GetTexture("time"), + clockiconpos, + clockiconpos + clockiconsize ); + + bgList->AddText( + headerfont, + headerfontsize, + textpos, + timeTextColor, + runtimeText.c_str()); + } + //Draw the force vector if(ImGui::IsKeyDown(ImGuiKey_Q)) RenderForceVector(bgList, pos, size, m_nodeForces[id]); diff --git a/src/ngscopeclient/FilterGraphEditor.h b/src/ngscopeclient/FilterGraphEditor.h index 3c7d8ab6..8c0f36fc 100644 --- a/src/ngscopeclient/FilterGraphEditor.h +++ b/src/ngscopeclient/FilterGraphEditor.h @@ -177,7 +177,11 @@ class FilterGraphEditor : public Dialog void DoInternalLinksForGroup(std::shared_ptr group); void DoNodeForGroupOutputs(std::shared_ptr group); void DoNodeForGroupInputs(std::shared_ptr group); - void DoNodeForChannel(InstrumentChannel* channel, std::shared_ptr inst, bool multiInst); + void DoNodeForChannel( + InstrumentChannel* channel, + std::shared_ptr inst, + bool multiInst, + int64_t runtime); void DoNodeForTrigger(Trigger* trig); bool HandleNodeProperties(); void HandleDoubleClicks(); diff --git a/src/ngscopeclient/MainWindow_Icons.cpp b/src/ngscopeclient/MainWindow_Icons.cpp index 2ad1d465..e82ff56b 100644 --- a/src/ngscopeclient/MainWindow_Icons.cpp +++ b/src/ngscopeclient/MainWindow_Icons.cpp @@ -171,6 +171,8 @@ void MainWindow::LoadStatusBarIcons() m_texmgr.LoadTexture("mouse_move", FindDataFile("icons/contrib/blender/24x24/mouse_move.png")); m_texmgr.LoadTexture("mouse_wheel", FindDataFile("icons/contrib/blender/24x24/mouse_wheel.png")); + + m_texmgr.LoadTexture("time", FindDataFile("icons/contrib/blender/24x24/time.png")); } /** diff --git a/src/ngscopeclient/Session.cpp b/src/ngscopeclient/Session.cpp index 88d1decf..c3821deb 100644 --- a/src/ngscopeclient/Session.cpp +++ b/src/ngscopeclient/Session.cpp @@ -85,7 +85,7 @@ Session::Session(MainWindow* wnd) , m_tPrimaryTrigger(0) , m_triggerArmed(false) , m_triggerOneShot(false) - , m_graphExecutor(/*8*/1) + , m_graphExecutor(4) , m_lastFilterGraphExecTime(0) , m_history(*this) , m_multiScope(false) @@ -3174,7 +3174,6 @@ size_t Session::GetFilterCount() return filters.size(); } - /** @brief Queues a request to refresh all filters the next time we poll stuff */ @@ -3239,6 +3238,10 @@ void Session::RefreshAllFilters() } m_lastFilterGraphExecTime = (GetTime() - tstart) * FS_PER_SECOND; + { + lock_guard lock(m_lastFilterGraphRuntimeMutex); + m_lastFilterGraphRuntimeStats = m_graphExecutor.GetRunTimes(); + } } /** @@ -3291,6 +3294,10 @@ bool Session::RefreshDirtyFilters() } m_lastFilterGraphExecTime = (GetTime() - tstart) * FS_PER_SECOND; + { + lock_guard lock(m_lastFilterGraphRuntimeMutex); + m_lastFilterGraphRuntimeStats = m_graphExecutor.GetRunTimes(); + } return true; } diff --git a/src/ngscopeclient/Session.h b/src/ngscopeclient/Session.h index a570f946..ffa7d2ea 100644 --- a/src/ngscopeclient/Session.h +++ b/src/ngscopeclient/Session.h @@ -80,7 +80,7 @@ class InstrumentConnectionState ///@brief Thread for polling the instrument std::unique_ptr m_thread; - + ///@brief Cached trigger state, to reflect in the UI Oscilloscope::TriggerMode m_lastTriggerState; }; @@ -324,6 +324,13 @@ class Session void OnMarkerChanged(); + ///@brief Return the last filter graph runtime stats + std::map GetFilterGraphRuntime() + { + std::lock_guard lock(m_lastFilterGraphRuntimeMutex); + return m_lastFilterGraphRuntimeStats; + } + protected: void UpdatePacketManagers(const std::set& nodes); @@ -445,6 +452,12 @@ class Session ///@brief Time spent on the last filter graph execution std::atomic m_lastFilterGraphExecTime; + ///@brief Mutex for controlling access to m_lastFilterGraphRuntimeStats + std::mutex m_lastFilterGraphRuntimeMutex; + + ///@brief Performance stats from last graph execution + std::map m_lastFilterGraphRuntimeStats; + ///@brief Mutex for controlling access to performance counters std::mutex m_perfClockMutex; diff --git a/src/ngscopeclient/icons/contrib/blender/24x24/time.png b/src/ngscopeclient/icons/contrib/blender/24x24/time.png new file mode 100644 index 0000000000000000000000000000000000000000..0192afda2ec5feae2a1c8a5c093e43672af08fea GIT binary patch literal 472 zcmV;}0Vn>6P)JLb;ZN-A-ZNoMYxIp?2$?)*~#G)YWNRQ)duW^(dMlC~tR+W1-0y`)}rB@k*A zpaFFGi4JfDtb{Z|;`>0`BkZCJ)W$V91O^e|7O()E8Mm6(ppuc2G#-Jn@oV<2gC|`+ zgGNOB9auN+yqP7a#|(-VNeD+Ne$$!j0;Q0_ZbZD9;vQ#kcG8;eMXLn<=r>6x*58nH zp1~W@80RJ<>C5_ylIHR(at&Ss+$Bk;*1wZWz4QsqZ{sgv-TLzw-`8F^cI)k31 z+rTcSC0%Qno)1Y4XTBw=AKHN!UY6LedIRhM4^H&Uu3lEDs1h#~X$*h^pH`^YRdehl ziQFq5Gf2`^$6r<3z$Kovq+5Y}Z*o!6rld6+Cl5|PbA=(V!LRZEAN_Bs?f<`Qj5&n> O0000 + + + + From 5711d44943697bced8ffb04900080cbf39e10bcd Mon Sep 17 00:00:00 2001 From: "Andrew D. Zonenberg" Date: Thu, 10 Oct 2024 00:17:29 -0700 Subject: [PATCH 25/25] MainWindow: fixed some bugs related to handling of null transport in in add-instrument menu. Fixes #777. --- src/ngscopeclient/MainWindow.cpp | 9 ++++++++- src/ngscopeclient/MainWindow_Menus.cpp | 21 +++++++++++++++++---- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/ngscopeclient/MainWindow.cpp b/src/ngscopeclient/MainWindow.cpp index dc73770f..4dd24686 100644 --- a/src/ngscopeclient/MainWindow.cpp +++ b/src/ngscopeclient/MainWindow.cpp @@ -1490,7 +1490,9 @@ void MainWindow::AddToRecentInstrumentList(shared_ptr inst) if(inst == nullptr) return; - LogTrace("Adding instrument \"%s\" to recent instrument list\n", inst->m_nickname.c_str()); + LogTrace("Adding instrument \"%s\" to recent instrument list (had %zu)\n", + inst->m_nickname.c_str(), m_recentInstruments.size()); + LogIndenter li; auto now = time(NULL); @@ -1499,6 +1501,7 @@ void MainWindow::AddToRecentInstrumentList(shared_ptr inst) inst->GetDriverName() + ":" + inst->GetTransportName() + ":" + inst->GetTransportConnectionString(); + LogTrace("Connection string: %s\n", connectionString.c_str()); m_recentInstruments[connectionString] = now; //Delete anything old @@ -1517,8 +1520,12 @@ void MainWindow::AddToRecentInstrumentList(shared_ptr inst) } } + LogTrace("Removing oldest instrument (%s) to make room\n", oldestPath.c_str()); m_recentInstruments.erase(oldestPath); } + + LogTrace("Added (now have %zu total recent instruments)\n", m_recentInstruments.size()); + SaveRecentInstrumentList(); } /** diff --git a/src/ngscopeclient/MainWindow_Menus.cpp b/src/ngscopeclient/MainWindow_Menus.cpp index e9811d88..c86b93f4 100644 --- a/src/ngscopeclient/MainWindow_Menus.cpp +++ b/src/ngscopeclient/MainWindow_Menus.cpp @@ -322,8 +322,17 @@ void MainWindow::DoAddSubMenu( for(auto cstring : cstrings) { auto fields = explode(cstring, ':'); + + //make sure it's well formed if(fields.size() < 4) - continue; + { + //Special case: null transport allows 3 fields + if( (fields.size() == 3) && (fields[2] == "null") ) + {} + + else + continue; + } auto nick = fields[0]; auto drivername = fields[1]; @@ -333,9 +342,13 @@ void MainWindow::DoAddSubMenu( { if(ImGui::MenuItem(nick.c_str())) { - auto path = fields[3]; - for(size_t j=4; j= 4) + { + path = fields[3]; + for(size_t j=4; j