diff --git a/lib b/lib index 9411b419..987b8cf3 160000 --- a/lib +++ b/lib @@ -1 +1 @@ -Subproject commit 9411b4192b5f6ff6c243486c32f65d3a906fef8a +Subproject commit 987b8cf3afdc83b02626904f18451b639d0e52a4 diff --git a/src/ngscopeclient/AddInstrumentDialog.cpp b/src/ngscopeclient/AddInstrumentDialog.cpp index 6b938787..89020064 100644 --- a/src/ngscopeclient/AddInstrumentDialog.cpp +++ b/src/ngscopeclient/AddInstrumentDialog.cpp @@ -2,7 +2,7 @@ * * * ngscopeclient * * * -* Copyright (c) 2012-2024 Andrew D. Zonenberg * +* Copyright (c) 2012-2026 Andrew D. Zonenberg * * All rights reserved. * * * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the * @@ -45,16 +45,49 @@ AddInstrumentDialog::AddInstrumentDialog( const string& title, const string& nickname, Session& session, - const string& driverType) + const string& driverType, + const std::string& driver, + const std::string& transport, + const std::string& path) : Dialog(title, string("AddInstrument") + to_string_hex(reinterpret_cast(this)), ImVec2(600, 150)) , m_session(session) , m_nickname(nickname) , m_selectedDriver(0) , m_selectedTransport(0) + , m_path(path) { SCPITransport::EnumTransports(m_transports); m_drivers = session.GetDriverNamesForType(driverType); + + if(!driver.empty()) + { + int i = 0; + for(auto driverName: m_drivers) + { + if(driverName == driver) + { + m_selectedDriver = i; + break; + } + i++; + } + } + + + if(!transport.empty()) + { + int i = 0; + for(auto transportName: m_transports) + { + if(transportName == transport) + { + m_selectedTransport = i; + break; + } + i++; + } + } } AddInstrumentDialog::~AddInstrumentDialog() @@ -170,6 +203,5 @@ SCPITransport* AddInstrumentDialog::MakeTransport() bool AddInstrumentDialog::DoConnect(SCPITransport* transport) { - m_session.CreateAndAddInstrument(m_drivers[m_selectedDriver], transport, m_nickname); - return true; + return m_session.CreateAndAddInstrument(m_drivers[m_selectedDriver], transport, m_nickname); } diff --git a/src/ngscopeclient/AddInstrumentDialog.h b/src/ngscopeclient/AddInstrumentDialog.h index 22e86290..2edd0102 100644 --- a/src/ngscopeclient/AddInstrumentDialog.h +++ b/src/ngscopeclient/AddInstrumentDialog.h @@ -2,7 +2,7 @@ * * * ngscopeclient * * * -* Copyright (c) 2012-2024 Andrew D. Zonenberg * +* Copyright (c) 2012-2026 Andrew D. Zonenberg * * All rights reserved. * * * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the * @@ -45,7 +45,10 @@ class AddInstrumentDialog : public Dialog const std::string& title, const std::string& nickname, Session& session, - const std::string& driverType); + const std::string& driverType, + const std::string& driver = "", + const std::string& transport = "", + const std::string& path = ""); virtual ~AddInstrumentDialog(); virtual bool DoRender(); diff --git a/src/ngscopeclient/Dialog.cpp b/src/ngscopeclient/Dialog.cpp index 4de5aba4..026af0eb 100644 --- a/src/ngscopeclient/Dialog.cpp +++ b/src/ngscopeclient/Dialog.cpp @@ -237,6 +237,11 @@ bool Dialog::TextInputWithImplicitApply(const string& label, string& currentValu return false; } +bool Dialog::TextInputWithExplicitApply(const string& label, string& currentValue, string& committedValue) +{ + return renderEditablePropertyWithExplicitApply(-1,label,currentValue,committedValue,Unit()/*not used for string*/); +} + bool Dialog::IntInputWithImplicitApply(const string& label, int& currentValue, int& committedValue) { bool dirty = currentValue != committedValue; @@ -488,7 +493,7 @@ template */ bool Dialog::renderEditableProperty(float width, const std::string& label, std::string& currentValue, T& committedValue, Unit unit, const char* tooltip, std::optional optcolor, bool allow7SegmentDisplay, bool explicitApply) { - static_assert(std::is_same_v || std::is_same_v || std::is_same_v,"renderEditableProperty only supports int64_t, float or double"); + static_assert(std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v,"renderEditableProperty only supports string, int64_t, float or double"); bool use7Segment = false; bool changeFont = false; int64_t displayType = NumericValueDisplay::NUMERIC_DISPLAY_DEFAULT_FONT; @@ -528,6 +533,8 @@ bool Dialog::renderEditableProperty(float width, const std::string& label, std:: } if constexpr (std::is_same_v) dirty = unit.PrettyPrintInt64(committedValue) != currentValue; + else if constexpr (std::is_same_v) + dirty = committedValue != currentValue; else dirty = unit.PrettyPrint(committedValue) != currentValue; string editLabel = label+"##Edit"; @@ -674,6 +681,10 @@ bool Dialog::renderEditableProperty(float width, const std::string& label, std:: currentValue = unit.PrettyPrintInt64(committedValue); } + else if constexpr (std::is_same_v) + { + committedValue = currentValue; + } else { committedValue = static_cast(unit.ParseString(currentValue)); @@ -689,6 +700,8 @@ bool Dialog::renderEditableProperty(float width, const std::string& label, std:: { // Restore value if constexpr (std::is_same_v) currentValue = unit.PrettyPrintInt64(committedValue); + else if constexpr (std::is_same_v) + currentValue = committedValue; else currentValue = unit.PrettyPrint(committedValue); if(m_editedItemId == editId) @@ -707,6 +720,7 @@ bool Dialog::renderEditableProperty(float width, const std::string& label, std:: template bool Dialog::renderEditableProperty(float width, const std::string& label, std::string& currentValue, float& committedValue, Unit unit, const char* tooltip, std::optional optcolor, bool allow7SegmentDisplay, bool explicitApply); template bool Dialog::renderEditableProperty(float width, const std::string& label, std::string& currentValue, double& committedValue, Unit unit, const char* tooltip, std::optional optcolor, bool allow7SegmentDisplay, bool explicitApply); template bool Dialog::renderEditableProperty(float width, const std::string& label, std::string& currentValue, int64_t& committedValue, Unit unit, const char* tooltip, std::optional optcolor, bool allow7SegmentDisplay, bool explicitApply); +template bool Dialog::renderEditableProperty(float width, const std::string& label, std::string& currentValue, std::string& committedValue, Unit unit, const char* tooltip, std::optional optcolor, bool allow7SegmentDisplay, bool explicitApply); template /** @@ -731,7 +745,24 @@ bool Dialog::renderEditablePropertyWithExplicitApply(float width, const std::str template bool Dialog::renderEditablePropertyWithExplicitApply(float width, const std::string& label, std::string& currentValue, float& committedValue, Unit unit, const char* tooltip, std::optional optcolor, bool allow7SegmentDisplay); template bool Dialog::renderEditablePropertyWithExplicitApply(float width, const std::string& label, std::string& currentValue, double& committedValue, Unit unit, const char* tooltip, std::optional optcolor, bool allow7SegmentDisplay); template bool Dialog::renderEditablePropertyWithExplicitApply(float width, const std::string& label, std::string& currentValue, int64_t& committedValue, Unit unit, const char* tooltip, std::optional optcolor, bool allow7SegmentDisplay); +template bool Dialog::renderEditablePropertyWithExplicitApply(float width, const std::string& label, std::string& currentValue, std::string& committedValue, Unit unit, const char* tooltip, std::optional optcolor, bool allow7SegmentDisplay); +/** + @brief Render a badge with text inside + @param width the width of the badge + @param color the badge color + @param label the text of the badge +*/ +void Dialog::renderBadge(float width, ImVec4 color, const string& label) +{ + float fontSize = ImGui::GetFontSize(); + if(width <= 0) width = 6*fontSize; + ImGui::PushStyleColor(ImGuiCol_ChildBg, color); + ImGui::BeginChild("##badge", ImVec2(width, ImGui::GetFontSize()),false,ImGuiWindowFlags_None); + ImGui::TextUnformatted(label.c_str()); + ImGui::EndChild(); + ImGui::PopStyleColor(); +} /** @brief Segment on/off state for each of the 10 digits + "L" (needed for OL / Overload) diff --git a/src/ngscopeclient/Dialog.h b/src/ngscopeclient/Dialog.h index 84b1008b..fb6cce7f 100644 --- a/src/ngscopeclient/Dialog.h +++ b/src/ngscopeclient/Dialog.h @@ -81,6 +81,7 @@ class Dialog std::string& committedValue); protected: + bool TextInputWithExplicitApply(const std::string& label,std::string& currentValue,std::string& committedValue); bool IntInputWithImplicitApply(const std::string& label, int& currentValue, int& committedValue); bool UnitInputWithExplicitApply( const std::string& label, @@ -93,6 +94,7 @@ class Dialog bool renderEditableProperty(float width, const std::string& label, std::string& currentValue, T& committedValue, Unit unit, const char* tooltip = nullptr, std::optional optcolor = std::nullopt, bool allow7SegmentDisplay = false, bool explicitApply = false); template bool renderEditablePropertyWithExplicitApply(float width, const std::string& label, std::string& currentValue, T& committedValue, Unit unit, const char* tooltip = nullptr, std::optional optcolor = std::nullopt, bool allow7SegmentDisplay = false); + void renderBadge(float width, ImVec4 color, const std::string& label); public: static void Tooltip(const std::string& str, bool allowDisabled = false); diff --git a/src/ngscopeclient/MainWindow.cpp b/src/ngscopeclient/MainWindow.cpp index c13ded6e..e92a9ec7 100644 --- a/src/ngscopeclient/MainWindow.cpp +++ b/src/ngscopeclient/MainWindow.cpp @@ -1567,10 +1567,14 @@ void MainWindow::SaveRecentInstrumentList() continue; //Make a node for the instrument - YAML::Node inode; - inode["path"] = it.first; - inode["timestamp"] = static_cast(it.second); - node[nick] = inode; + int64_t timestamp = static_cast(it.second); + if(!node[nick] || (node[nick]["timestamp"].as() < timestamp)) + { // Only add node if not already present or if other timestamp is older + YAML::Node inode; + inode["path"] = it.first; + inode["timestamp"] = timestamp; + node[nick] = inode; + } } //Write the generated YAML to disk @@ -1631,6 +1635,64 @@ void MainWindow::AddToRecentInstrumentList(shared_ptr inst) SaveRecentInstrumentList(); } +void MainWindow::RenameRecentInstrument(std::shared_ptr inst, const std::string& oldName) +{ + if(inst == nullptr) + return; + + LogTrace("Renaming instrument \"%s\" with name \"%s\" in recent instrument list (had %zu)\n", + oldName.c_str(), inst->m_nickname.c_str(), m_recentInstruments.size()); + + auto oldConnectionString = + oldName + ":" + + inst->GetDriverName() + ":" + + inst->GetTransportName() + ":" + + inst->GetTransportConnectionString(); + auto it = m_recentInstruments.find(oldConnectionString); + if (it != m_recentInstruments.end()) + { + auto now = time(NULL); + auto newConnectionString = + inst->m_nickname + ":" + + inst->GetDriverName() + ":" + + inst->GetTransportName() + ":" + + inst->GetTransportConnectionString(); + LogTrace("Replaced connection string %s by %s\n", oldConnectionString.c_str(),newConnectionString.c_str()); + m_recentInstruments.erase(it); + m_recentInstruments.emplace(newConnectionString, now); + SaveRecentInstrumentList(); + } +} + +void MainWindow::RepathRecentInstrument(std::shared_ptr inst, const std::string& oldPath) +{ + if(inst == nullptr) + return; + + LogTrace("Changing path for instrument \"%s\" with path \"%s\" in recent instrument list (had %zu)\n", + oldPath.c_str(), inst->GetTransportConnectionString().c_str(), m_recentInstruments.size()); + + auto oldConnectionString = + inst->m_nickname + ":" + + inst->GetDriverName() + ":" + + inst->GetTransportName() + ":" + + oldPath; + auto it = m_recentInstruments.find(oldConnectionString); + if (it != m_recentInstruments.end()) + { + auto now = time(NULL); + auto newConnectionString = + inst->m_nickname + ":" + + inst->GetDriverName() + ":" + + inst->GetTransportName() + ":" + + inst->GetTransportConnectionString(); + LogTrace("Replaced connection string %s by %s\n", oldConnectionString.c_str(),newConnectionString.c_str()); + m_recentInstruments.erase(it); + m_recentInstruments.emplace(newConnectionString, now); + SaveRecentInstrumentList(); + } +} + /** @brief Helper function for creating a transport and printing an error if the connection is unsuccessful */ @@ -1777,6 +1839,9 @@ ImGui::MarkdownConfig MainWindow::GetMarkdownConfig() */ void MainWindow::RenderLoadWarningPopup() { + if(!m_errorPopupTitle.empty()) + return; // Already showing Error popup, skip Warning for now + static ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | diff --git a/src/ngscopeclient/MainWindow.h b/src/ngscopeclient/MainWindow.h index c06bea16..8fdfb21a 100644 --- a/src/ngscopeclient/MainWindow.h +++ b/src/ngscopeclient/MainWindow.h @@ -506,6 +506,8 @@ class MainWindow : public VulkanWindow public: void AddToRecentInstrumentList(std::shared_ptr inst); + void RenameRecentInstrument(std::shared_ptr inst, const std::string& oldName); + void RepathRecentInstrument(std::shared_ptr inst, const std::string& oldPath); protected: diff --git a/src/ngscopeclient/MainWindow_Menus.cpp b/src/ngscopeclient/MainWindow_Menus.cpp index 1521e155..6dbe6553 100644 --- a/src/ngscopeclient/MainWindow_Menus.cpp +++ b/src/ngscopeclient/MainWindow_Menus.cpp @@ -2,7 +2,7 @@ * * * ngscopeclient * * * -* Copyright (c) 2012-2025 Andrew D. Zonenberg and contributors * +* Copyright (c) 2012-2026 Andrew D. Zonenberg and contributors * * All rights reserved. * * * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the * @@ -340,9 +340,30 @@ void MainWindow::DoAddSubMenu( path = path + ":" + fields[j]; } + bool success = true; auto transport = MakeTransport(transname, path); if(transport != nullptr) - m_session.CreateAndAddInstrument(drivername, transport, nick); + { + if(!m_session.CreateAndAddInstrument(drivername, transport, nick)) + { + success = false; + } + } + else + { + success = false; + } + if(!success) + { // Spawn an AddInstrument dialog here, prefilled with intrument informations, to allow changing connection path + m_dialogs.emplace(make_shared( + string("Update ") + typePretty, + nick, + m_session, + typeInternal, + drivername, + transname, + path)); + } } } } diff --git a/src/ngscopeclient/ManageInstrumentsDialog.cpp b/src/ngscopeclient/ManageInstrumentsDialog.cpp index 626e2ba6..47003e09 100644 --- a/src/ngscopeclient/ManageInstrumentsDialog.cpp +++ b/src/ngscopeclient/ManageInstrumentsDialog.cpp @@ -44,10 +44,10 @@ using namespace std; // Construction / destruction ManageInstrumentsDialog::ManageInstrumentsDialog(Session& session, MainWindow* parent) - : Dialog("Manage Instruments", "Manage Instruments", ImVec2(1000, 300)) + : Dialog("Manage Instruments", "Manage Instruments", ImVec2(1024, 300),&session,parent) , m_session(session) , m_parent(parent) - , m_selection(nullptr) + //, m_selection(nullptr) { } @@ -97,7 +97,7 @@ bool ManageInstrumentsDialog::DoRender() if(ImGui::CollapsingHeader("All Instruments", ImGuiTreeNodeFlags_DefaultOpen)) { - if(ImGui::BeginTable("alltable", 7, flags)) + if(ImGui::BeginTable("alltable", 8, flags)) { AllInstrumentsTable(); ImGui::EndTable(); @@ -393,45 +393,117 @@ void ManageInstrumentsDialog::RowForNewGroup() void ManageInstrumentsDialog::AllInstrumentsTable() { + auto& prefs = m_session.GetPreferences(); auto insts = m_session.GetInstruments(); float width = ImGui::GetFontSize(); ImGui::TableSetupScrollFreeze(0, 1); //Header row does not scroll - ImGui::TableSetupColumn("Nickname", ImGuiTableColumnFlags_WidthFixed, 6*width); + ImGui::TableSetupColumn("Nickname", ImGuiTableColumnFlags_WidthFixed, 12*width); ImGui::TableSetupColumn("Make", ImGuiTableColumnFlags_WidthFixed, 9*width); - ImGui::TableSetupColumn("Model", ImGuiTableColumnFlags_WidthFixed, 15*width); + ImGui::TableSetupColumn("Model", ImGuiTableColumnFlags_WidthFixed, 12*width); ImGui::TableSetupColumn("Transport", ImGuiTableColumnFlags_WidthFixed, 4*width); - ImGui::TableSetupColumn("Path", ImGuiTableColumnFlags_WidthFixed, 25*width); - ImGui::TableSetupColumn("Serial", ImGuiTableColumnFlags_WidthFixed, 8*width); + ImGui::TableSetupColumn("Path", ImGuiTableColumnFlags_WidthFixed, 15*width); + ImGui::TableSetupColumn("Status", ImGuiTableColumnFlags_WidthFixed, 5*width); + ImGui::TableSetupColumn("Serial", ImGuiTableColumnFlags_WidthFixed, 7*width); ImGui::TableSetupColumn("Features", ImGuiTableColumnFlags_WidthFixed, 10*width); ImGui::TableHeadersRow(); + size_t instNumber = insts.size(); + size_t instIndex = 0; + if(instNumber != m_instrumentCurrentNames.size()) + { // Instrument list has changed, clear cache + m_instrumentCurrentNames.clear(); + m_instrumentCommittedNames.clear(); + m_instrumentCurrentPaths.clear(); + m_instrumentCommittedPaths.clear(); + m_instrumentCurrentNames.resize(instNumber); + m_instrumentCommittedNames.resize(instNumber); + m_instrumentCurrentPaths.resize(instNumber); + m_instrumentCommittedPaths.resize(instNumber); + } + for(auto inst : insts) { auto itype = inst->GetInstrumentTypes(); - bool rowIsSelected = (m_selection == inst); + //bool rowIsSelected = (m_selection == inst); ImGui::PushID(inst.get()); ImGui::TableNextRow(ImGuiTableRowFlags_None); - ImGui::TableSetColumnIndex(0); + if(ImGui::TableSetColumnIndex(0)) + { + if(m_instrumentCommittedNames[instIndex].empty()) m_instrumentCommittedNames[instIndex]=inst->m_nickname; + if(m_instrumentCurrentNames[instIndex].empty()) m_instrumentCurrentNames[instIndex]=inst->m_nickname; + ImGui::SetNextItemWidth(12*width); + if(TextInputWithExplicitApply("###nickname",m_instrumentCurrentNames[instIndex],m_instrumentCommittedNames[instIndex])) + { + string oldName = inst->m_nickname; + inst->m_nickname = m_instrumentCommittedNames[instIndex]; + auto si = dynamic_pointer_cast(inst); + if(si) + m_parent->RenameRecentInstrument(si,oldName); + } + } + if(ImGui::TableSetColumnIndex(1)) + { + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted(inst->GetVendor().c_str()); + } + /*ImGui::TableSetColumnIndex(1); + ImGui::AlignTextToFramePadding(); if(ImGui::Selectable( - inst->m_nickname.c_str(), + inst->GetVendor().c_str(), rowIsSelected, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap, ImVec2(0, 0))) { m_selection = dynamic_pointer_cast(inst); rowIsSelected = true; - } - if(ImGui::TableSetColumnIndex(1)) - ImGui::TextUnformatted(inst->GetVendor().c_str()); + }*/ if(ImGui::TableSetColumnIndex(2)) + { + ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted(inst->GetName().c_str()); + } if(ImGui::TableSetColumnIndex(3)) + { + ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted(inst->GetTransportName().c_str()); + } if(ImGui::TableSetColumnIndex(4)) - ImGui::TextUnformatted(inst->GetTransportConnectionString().c_str()); + { + if(inst->IsOffline()) + { // Only allow changing path if instrument is Offline + if(m_instrumentCommittedPaths[instIndex].empty()) m_instrumentCommittedPaths[instIndex]=inst->GetTransportConnectionString(); + if(m_instrumentCurrentPaths[instIndex].empty()) m_instrumentCurrentPaths[instIndex]=inst->GetTransportConnectionString(); + ImGui::SetNextItemWidth(12*width); + if(TextInputWithExplicitApply("###path",m_instrumentCurrentPaths[instIndex],m_instrumentCommittedPaths[instIndex])) + { + string oldPath = inst->GetTransportConnectionString(); + auto mi = dynamic_pointer_cast(inst); + if(mi) + mi->SetTransportConnectionString(m_instrumentCommittedPaths[instIndex]); + auto si = dynamic_pointer_cast(inst); + if(si) + m_parent->RepathRecentInstrument(si,oldPath); + } + } + else + { + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted(inst->GetTransportConnectionString().c_str()); + } + } if(ImGui::TableSetColumnIndex(5)) - ImGui::TextUnformatted(inst->GetSerial().c_str()); + { + ImGui::AlignTextToFramePadding(); + renderBadge(0, + inst->IsOffline() ? ImGui::ColorConvertU32ToFloat4(prefs.GetColor("Appearance.Stream Browser.instrument_offline_badge_color")) : ImGui::ColorConvertU32ToFloat4(prefs.GetColor("Appearance.Stream Browser.instrument_on_badge_color")), + inst->IsOffline() ? "Offline" : "Online"); + } if(ImGui::TableSetColumnIndex(6)) + { + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted(inst->GetSerial().c_str()); + } + if(ImGui::TableSetColumnIndex(7)) { string types = ""; @@ -453,8 +525,10 @@ void ManageInstrumentsDialog::AllInstrumentsTable() if(itype & Instrument::INST_BERT) types += "bert "; + ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted(types.c_str()); } ImGui::PopID(); + instIndex++; } } diff --git a/src/ngscopeclient/ManageInstrumentsDialog.h b/src/ngscopeclient/ManageInstrumentsDialog.h index 9ce9d82e..8af2d1c0 100644 --- a/src/ngscopeclient/ManageInstrumentsDialog.h +++ b/src/ngscopeclient/ManageInstrumentsDialog.h @@ -54,8 +54,12 @@ class ManageInstrumentsDialog : public Dialog Session& m_session; MainWindow* m_parent; + std::vector m_instrumentCommittedNames; + std::vector m_instrumentCurrentNames; + std::vector m_instrumentCommittedPaths; + std::vector m_instrumentCurrentPaths; - std::shared_ptr m_selection; + //std::shared_ptr m_selection; }; class TriggerGroupDragDescriptor diff --git a/src/ngscopeclient/Session.cpp b/src/ngscopeclient/Session.cpp index 9a1b5b23..0cfc36b2 100644 --- a/src/ngscopeclient/Session.cpp +++ b/src/ngscopeclient/Session.cpp @@ -47,6 +47,7 @@ #include "../scopehal/SiglentSCPIOscilloscope.h" #include "../scopehal/RigolOscilloscope.h" #include "../scopehal/MockOscilloscope.h" +#include "../scopehal/MockPowerSupply.h" #include "../scopeprotocols/EyePattern.h" #include @@ -1240,6 +1241,9 @@ bool Session::PreLoadOscilloscope(int version, const YAML::Node& node, bool onli //Make any config settings to the instrument from our preference settings ApplyPreferences(scope); + //Run the preload + scope->PreLoadConfiguration(version, node, m_idtable, m_warnings); + //All good. Add to our list of scopes etc AddInstrument(scope, false); m_idtable.emplace(node["id"].as(), (Instrument*)scope.get()); @@ -1248,9 +1252,6 @@ bool Session::PreLoadOscilloscope(int version, const YAML::Node& node, bool onli if(node["triggerdeskew"]) m_scopeDeskewCal[scope] = node["triggerdeskew"].as(); - //Run the preload - scope->PreLoadConfiguration(version, node, m_idtable, m_warnings); - return true; } @@ -1311,13 +1312,13 @@ bool Session::PreLoadVNA(int version, const YAML::Node& node, bool online) //Make any config settings to the instrument from our preference settings ApplyPreferences(scope); + //Run the preload + scope->PreLoadConfiguration(version, node, m_idtable, m_warnings); + //All good. Add to our list of scopes etc AddInstrument(scope, false); m_idtable.emplace(node["id"].as(), (Instrument*)scope.get()); - //Run the preload - scope->PreLoadConfiguration(version, node, m_idtable, m_warnings); - return true; } @@ -1797,9 +1798,8 @@ bool Session::PreLoadPowerSupply(int version, const YAML::Node& node, bool onlin if(!psu) { - /* - //Create the mock scope - scope = new MockOscilloscope( + //Create the mock PSU + psu = make_shared( node["name"].as(), node["vendor"].as(), node["serial"].as(), @@ -1807,21 +1807,18 @@ bool Session::PreLoadPowerSupply(int version, const YAML::Node& node, bool onlin driver, node["args"].as() ); - */ - LogError("offline loading of power supplies not implemented yet\n"); - return true; } //Make any config settings to the instrument from our preference settings //ApplyPreferences(psu); + //Run the preload + psu->PreLoadConfiguration(version, node, m_idtable, m_warnings); + //All good. Add to our list of scopes etc AddInstrument(psu, false); m_idtable.emplace(node["id"].as(), (Instrument*)psu.get()); - //Run the preload - psu->PreLoadConfiguration(version, node, m_idtable, m_warnings); - return true; } @@ -2899,8 +2896,9 @@ void Session::StartWaveformThreadIfNeeded() /** @brief Creates a new instrument and adds it to the session + @return Returns false if creation failed */ -void Session::CreateAndAddInstrument(const string& driver, SCPITransport* transport, const string& nickname) +bool Session::CreateAndAddInstrument(const string& driver, SCPITransport* transport, const string& nickname) { shared_ptr inst = nullptr; @@ -2953,7 +2951,7 @@ void Session::CreateAndAddInstrument(const string& driver, SCPITransport* transp "Driver error", "Failed to create instrument driver instance of type \"" + driver + "\""); delete transport; - return; + return false; } //Apply preference settings, if any, here @@ -2963,6 +2961,7 @@ void Session::CreateAndAddInstrument(const string& driver, SCPITransport* transp inst->m_nickname = nickname; AddInstrument(inst); + return true; } /** diff --git a/src/ngscopeclient/Session.h b/src/ngscopeclient/Session.h index 22e8c5df..94a95dea 100644 --- a/src/ngscopeclient/Session.h +++ b/src/ngscopeclient/Session.h @@ -621,7 +621,7 @@ class Session const std::vector& GetDriverNamesForType(const std::string& type) { return m_driverNamesByType[type]; } - void CreateAndAddInstrument( + bool CreateAndAddInstrument( const std::string& driver, SCPITransport* transport, const std::string& nickname); diff --git a/src/ngscopeclient/StreamBrowserDialog.cpp b/src/ngscopeclient/StreamBrowserDialog.cpp index 9e003280..6a283862 100644 --- a/src/ngscopeclient/StreamBrowserDialog.cpp +++ b/src/ngscopeclient/StreamBrowserDialog.cpp @@ -1035,49 +1035,54 @@ void StreamBrowserDialog::renderInstrumentNode(shared_ptr instrument auto psu = dynamic_pointer_cast(instrument); if (psu) { - //Get the state - auto psustate = m_session->GetPSUState(psu); - - bool allOn = false; - bool someOn = false; - if(psu->SupportsMasterOutputSwitching()) - allOn = psustate->m_masterEnable; - else - { - allOn = true; - for(size_t i = 0 ; i < channelCount ; i++) - { - if(psustate->m_channelOn[i]) - someOn = true; - else - allOn = false; - } - } - bool result; - if(allOn || someOn) - { - result = true; - renderToggle( - "###psuon", - true, - allOn ? - ImGui::ColorConvertU32ToFloat4(prefs.GetColor("Appearance.Stream Browser.instrument_on_badge_color")) : - ImGui::ColorConvertU32ToFloat4(prefs.GetColor("Appearance.Stream Browser.instrument_partial_badge_color")), result); - } + if (psu->IsOffline()) + renderBadge(ImGui::ColorConvertU32ToFloat4(prefs.GetColor("Appearance.Stream Browser.instrument_offline_badge_color")), "OFFLINE", "OFFL", NULL); else { - result = false; - renderOnOffToggle("###psuon", true, result); - } - if(result != allOn) - { + //Get the state + auto psustate = m_session->GetPSUState(psu); + + bool allOn = false; + bool someOn = false; if(psu->SupportsMasterOutputSwitching()) - psu->SetMasterPowerEnable(result); + allOn = psustate->m_masterEnable; else { + allOn = true; for(size_t i = 0 ; i < channelCount ; i++) { - psu->SetPowerChannelActive(i,result); + if(psustate->m_channelOn[i]) + someOn = true; + else + allOn = false; + } + } + bool result; + if(allOn || someOn) + { + result = true; + renderToggle( + "###psuon", + true, + allOn ? + ImGui::ColorConvertU32ToFloat4(prefs.GetColor("Appearance.Stream Browser.instrument_on_badge_color")) : + ImGui::ColorConvertU32ToFloat4(prefs.GetColor("Appearance.Stream Browser.instrument_partial_badge_color")), result); + } + else + { + result = false; + renderOnOffToggle("###psuon", true, result); + } + if(result != allOn) + { + if(psu->SupportsMasterOutputSwitching()) + psu->SetMasterPowerEnable(result); + else + { + for(size_t i = 0 ; i < channelCount ; i++) + { + psu->SetPowerChannelActive(i,result); + } } } } @@ -1132,19 +1137,27 @@ void StreamBrowserDialog::renderInstrumentNode(shared_ptr instrument int bankNumber = 1; for(auto bank : digitalBanks) { // Iterate on digital banks - string nodeName = "Digital Bank " + to_string(bankNumber); - if(ImGui::TreeNodeEx(nodeName.c_str())) - { - ImGui::Unindent(ImGui::GetTreeNodeToLabelSpacing()); - for(auto channel : bank) - { // Iterate on bank's channel - size_t i = channel->GetIndex(); - renderChannelNode(instrument,i,(i == lastEnabledChannelIndex)); + if(bank.size() > 1) + { // Only show Digital Bank node if there is more than on channel in the bank + string nodeName = "Digital Bank " + to_string(bankNumber); + if(ImGui::TreeNodeEx(nodeName.c_str())) + { + ImGui::Unindent(ImGui::GetTreeNodeToLabelSpacing()); + for(auto channel : bank) + { // Iterate on bank's channel + size_t i = channel->GetIndex(); + renderChannelNode(instrument,i,(i == lastEnabledChannelIndex)); + } + ImGui::Indent(ImGui::GetTreeNodeToLabelSpacing()); + ImGui::TreePop(); } - ImGui::Indent(ImGui::GetTreeNodeToLabelSpacing()); - ImGui::TreePop(); + bankNumber++; + } + else if(bank.size() == 1) + { // Only one channel in the bank, render it directly + size_t i = bank[0]->GetIndex(); + renderChannelNode(instrument,i,(i == lastEnabledChannelIndex)); } - bankNumber++; } for(size_t i : otherChannels) { // Finally iterate on other channels