From a9ec49afa8c5828d6eb72bebc52ce81e31b5f0b4 Mon Sep 17 00:00:00 2001 From: rstein Date: Thu, 19 Dec 2024 16:55:25 +0100 Subject: [PATCH] added Dashboard-ImPlotSink UI unit-test Signed-off-by: rstein --- cmake/DependenciesSHAs.cmake | 4 +- .../gnuradio/examples/function_generator.grc | 2 +- .../gnuradio/examples/signal_generator.grc | 2 +- src/service/main.cpp | 4 +- src/ui/Dashboard.cpp | 11 +- src/ui/Flowgraph.cpp | 96 +++++--- src/ui/Flowgraph.hpp | 46 +++- src/ui/test/CMakeLists.txt | 1 + .../examples/fg_dipole_intensity_ramp.grc | 205 ++++++++---------- .../examples/fg_dipole_intensity_ramp.yml | 14 +- src/ui/test/qa_chart_fgdipole.cpp | 37 ++-- 11 files changed, 235 insertions(+), 187 deletions(-) diff --git a/cmake/DependenciesSHAs.cmake b/cmake/DependenciesSHAs.cmake index a3184d2a..6dd83b22 100644 --- a/cmake/DependenciesSHAs.cmake +++ b/cmake/DependenciesSHAs.cmake @@ -1,7 +1,7 @@ -set(GIT_SHA_GNURADIO4 db0a2bcc4a14759c992ca89661f687b87d93e923 CACHE STRING "" FORCE) # main as of 2024-12-09 +set(GIT_SHA_GNURADIO4 0da51c551ed2317c7afafa066971d2eac8617ae1 CACHE STRING "" FORCE) # main as of 2024-12-19 set(GIT_SHA_OPENCMW_CPP bb8996babab2000a4ae3612ea146a551a96e59c4 CACHE STRING "" FORCE) # main as of 2024-10-18 set(GIT_SHA_UT v2.0.1 CACHE STRING "" FORCE) # latest version as of 2023-12-19 -set(GIT_SHA_GR_DIGITIZERS 749394c12285887eb8840ac4cd0c46f1d21b46b4 CACHE STRING "" FORCE) # dev-prototype as of 2024-11-06 +set(GIT_SHA_GR_DIGITIZERS fbbcf42cdf51513bad3415fb35363b58fd4ebb16 CACHE STRING "" FORCE) # main as of 2024-12-18 diff --git a/src/service/gnuradio/examples/function_generator.grc b/src/service/gnuradio/examples/function_generator.grc index 932c18d1..2b4002b9 100644 --- a/src/service/gnuradio/examples/function_generator.grc +++ b/src/service/gnuradio/examples/function_generator.grc @@ -1,7 +1,7 @@ blocks: - name: !!str ClockSource id: !!str gr::basic::ClockSource - template_args: !!str "float" + template_args: !!str "unsigned char" parameters: do_zero_order_hold: !!bool true n_samples_max: !!uint32 0 diff --git a/src/service/gnuradio/examples/signal_generator.grc b/src/service/gnuradio/examples/signal_generator.grc index 678365d8..ecfd43a8 100644 --- a/src/service/gnuradio/examples/signal_generator.grc +++ b/src/service/gnuradio/examples/signal_generator.grc @@ -1,7 +1,7 @@ blocks: - name: !!str ClockSource id: !!str gr::basic::ClockSource - template_args: !!str "float" + template_args: !!str "unsigned char" parameters: do_zero_order_hold: !!bool true n_samples_max: !!uint32 0 diff --git a/src/service/main.cpp b/src/service/main.cpp index a59c7046..dcabbb57 100644 --- a/src/service/main.cpp +++ b/src/service/main.cpp @@ -43,7 +43,7 @@ void registerTestBlocks(Registry& registry) { gr::registerBlock(registry); // ommitting gr::UncertainValue for now, which would also be supported by picoscope block gr::registerBlock(registry); gr::registerBlock(registry); - gr::registerBlock(registry); + gr::registerBlock(registry); gr::registerBlock(registry); fmt::print("providedBlocks:\n"); for (auto& blockName : registry.providedBlocks()) { @@ -189,4 +189,4 @@ int main(int argc, char** argv) { dashboardWorkerThread.join(); grAcqWorkerThread.join(); grFgWorkerThread.join(); -} +} \ No newline at end of file diff --git a/src/ui/Dashboard.cpp b/src/ui/Dashboard.cpp index f0368e7e..74d43db7 100644 --- a/src/ui/Dashboard.cpp +++ b/src/ui/Dashboard.cpp @@ -271,7 +271,16 @@ void Dashboard::load(const std::string& grcData, const std::string& dashboardDat localFlowGraph.parse(grcData); // Load is called after parsing the flowgraph so that we already have the list of sources doLoad(dashboardData); + } catch (const gr::exception& e) { +#ifndef NDEBUG + fmt::println(stderr, "Dashboard::load(const std::string& grcData,const std::string& dashboardData): error: {}", e); +#endif + components::Notification::error(fmt::format("Error: {}", e.what())); + App::instance().closeDashboard(); } catch (const std::exception& e) { +#ifndef NDEBUG + fmt::println(stderr, "Dashboard::load(const std::string& grcData,const std::string& dashboardData): error: {}", e.what()); +#endif components::Notification::error(fmt::format("Error: {}", e.what())); App::instance().closeDashboard(); } @@ -711,4 +720,4 @@ void DashboardDescription::load(const std::shared_ptr& source, std::shared_ptr DashboardDescription::createEmpty(const std::string& name) { return std::make_shared(DashboardDescription{.name = name, .source = unsavedSource(), .isFavorite = false, .lastUsed = std::nullopt}); } -} // namespace DigitizerUi +} // namespace DigitizerUi \ No newline at end of file diff --git a/src/ui/Flowgraph.cpp b/src/ui/Flowgraph.cpp index 14e878e4..678b4143 100644 --- a/src/ui/Flowgraph.cpp +++ b/src/ui/Flowgraph.cpp @@ -32,19 +32,28 @@ auto typeToName() { } std::string valueTypeName(auto& port) { - static const std::map mangledToName{{typeToName(), "float"s}, {typeToName(), "double"s}, - - {typeToName>(), "std::complex"s}, {typeToName>(), "std::complex"s}, - - {typeToName>(), "gr::DataSet"s}, {typeToName>(), "gr::DataSet"s}, - - {typeToName(), "std::int8_t"s}, {typeToName(), "std::int16_t"s}, {typeToName(), "std::int32_t"s}, {typeToName(), "std::int64_t"s}}; + static const std::map mangledToName{ // + {typeToName(), "float"s}, // + {typeToName(), "double"s}, // + {typeToName>(), "std::complex"s}, // + {typeToName>(), "std::complex"s}, // + {typeToName>(), "gr::DataSet"s}, // + {typeToName>(), "gr::DataSet"s}, // + {typeToName(), "std::int8_t"s}, // + {typeToName(), "std::int16_t"s}, // + {typeToName(), "std::int32_t"s}, // + {typeToName(), "std::int64_t"s}, // + {typeToName(), "std::uint8_t"s}, // + {typeToName(), "std::uint16_t"s}, // + {typeToName(), "std::uint32_t"s}, // + {typeToName(), "std::uint64_t"s}}; // if (auto it = mangledToName.find(port.defaultValue().type().name()); it != mangledToName.end()) { return it->second; - } else { - return "unknown_type"s; } + + throw gr::exception(fmt::format("valueTypeName(auto& port) - could not identify port data type '{}'", port.defaultValue().type().name())); + return "unknown_type"s; } const std::string& DataType::toString() const { @@ -137,7 +146,8 @@ void BlockRegistry::addBlockDefinitionsFromPluginLoader(gr::PluginLoader& plugin void BlockRegistry::addBlockDefinition(std::unique_ptr&& t) { _types.insert({t->name, std::move(t)}); } -Block::Block(std::string_view name, const BlockDefinition* t, gr::property_map settings) : name(name), m_type(t), m_settings(std::move(settings)) { // +Block::Block(std::string_view name, const BlockDefinition* t, gr::property_map settings) : name(name), m_type(t), m_settings(std::move(settings)) { + // setCurrentInstantiation(m_type->instantiations.cbegin()->first); } @@ -151,10 +161,11 @@ void Block::setSetting(const std::string& settingName, const pmtv::pmt& p) { App::instance().sendMessage(msg); } -void Block::updateSettings(const gr::property_map& settings) { +void Block::updateSettings(const gr::property_map& settings, const std::map>, gr::settings::PMTCompare>& stagedSettings) { for (const auto& [k, v] : settings) { m_settings[k] = v; } + _storedSettings = stagedSettings; } void Block::update() { @@ -210,16 +221,24 @@ void Block::update() { if (t == "std::complex") { return DataType::ComplexFloat32; } - // if (t == "std::complex") return DataType::ComplexInt64; - // if (t == "std::complex") return DataType::ComplexInt32; - // if (t == "std::complex") return DataType::ComplexInt16; - // if (t == "std::complex") return DataType::ComplexInt8; if (t == "double") { return dataset ? DataType::DataSetFloat64 : DataType::Float64; } if (t == "float") { return dataset ? DataType::DataSetFloat32 : DataType::Float32; } + if (t == "std::uint64_t") { + return DataType::UInt64; + } + if (t == "std::uint32_t" || t == "unsigned int") { + return DataType::UInt32; + } + if (t == "std::uint16_t" || t == "unsigned short") { + return DataType::UInt16; + } + if (t == "std::uint8_t") { + return DataType::UInt8; + } if (t == "std::int64_t") { return DataType::Int64; } @@ -232,7 +251,6 @@ void Block::update() { if (t == "std::int8_t") { return DataType::Int8; } - if (t == "gr::DataSet") { return DataType::DataSetFloat32; } @@ -246,15 +264,14 @@ void Block::update() { if (t == "bus") { return DataType::BusConnection; } - if (t == "") { + if (t.empty()) { return DataType::Wildcard; } if (t == "untyped") { return DataType::Untyped; } - fmt::print("unhandled type {}\n", t); - assert(0); + throw gr::exception(fmt::format("unhandled data type: '{}'\n", t)); return DataType::Untyped; }; auto getType = [&](const std::string& t, bool dataset) { @@ -322,28 +339,32 @@ void FlowGraph::parse(const std::filesystem::path& file) { void FlowGraph::parse(const std::string& str) { clear(); - auto grGraph = [this, &str]() { + gr::Graph grGraph = [this, &str]() -> gr::Graph { try { return gr::loadGrc(*_pluginLoader, str); + } catch (const gr::exception&) { + throw; } catch (const std::string& e) { - throw std::runtime_error(e); + throw gr::exception(e); + } catch (...) { + throw std::current_exception(); } }(); grGraph.forEachBlock([&](const auto& grBlock) { - auto typeName = grBlock.typeName(); - typeName = std::string_view(typeName.begin(), typeName.find('<')); - auto type = BlockRegistry::instance().get(typeName); + auto typeName = grBlock.typeName(); + typeName = std::string_view(typeName.begin(), typeName.find('<')); + const BlockDefinition* type = BlockRegistry::instance().get(typeName); if (!type) { auto msg = fmt::format("Block type '{}' is unknown.", typeName); components::Notification::error(msg); - throw std::runtime_error(msg); + throw gr::exception(msg); } auto block = type->createBlock(grBlock.name()); block->m_uniqueName = grBlock.uniqueName(); block->m_metaInformation = grBlock.metaInformation(); - block->updateSettings(grBlock.settings().get()); + block->updateSettings(grBlock.settings().get(), grBlock.settings().getStoredAll()); addBlock(std::move(block)); }); @@ -358,6 +379,9 @@ void FlowGraph::parse(const std::string& str) { if (definition.topLevel >= ports.size()) { auto msg = fmt::format("Cannot connect, index {} is not valid (only {} ports available)", definition.topLevel, ports.size()); components::Notification::error(msg); +#ifndef NDEBUG + throw std::runtime_error(msg); +#endif } // TODO check subIndex once we support port collections return std::pair{ports.begin() + definition.topLevel, definition.subIndex}; @@ -507,10 +531,15 @@ Connection* FlowGraph::connect(Block::Port* a, Block::Port* b) { std::swap(a, b); } - auto ain = std::size_t(a - a->owningUiBlock->outputs().data()); - auto bin = std::size_t(b - b->owningUiBlock->inputs().data()); + const auto ain = std::size_t(a - a->owningUiBlock->outputs().data()); + const auto bin = std::size_t(b - b->owningUiBlock->inputs().data()); - fmt::print("connect {}.{} to {}.{}\n", a->owningUiBlock->name, ain, b->owningUiBlock->name, bin); + if (a->rawPortType != b->rawPortType) { + fmt::println("incompatible block connection: {}.{}({}) to {}.{}({})", // + a->owningUiBlock->name, ain, a->rawPortType, b->owningUiBlock->name, bin, b->rawPortType); + } else { + fmt::println("connect {}.{}({}) to {}.{}({})", a->owningUiBlock->name, ain, a->rawPortType, b->owningUiBlock->name, bin, b->rawPortType); + } auto it = m_connections.insert(Connection(a->owningUiBlock, ain, b->owningUiBlock, bin)); a->portConnections.push_back(&(*it)); @@ -557,6 +586,13 @@ static std::unique_ptr createGRBlock(gr::PluginLoader& loader, c } grBlock->settings().set(params); + // using StoredSettingsType = std::map>, gr::settings::PMTCompare>; + for (const auto& [_, vec] : block.storedSettings()) { + for (const auto& [ctx, map] : vec) { + std::ignore = grBlock->settings().set(map, ctx); + } + } + grBlock->settings().applyStagedParameters(); return grBlock; } @@ -675,4 +711,4 @@ void FlowGraph::changeBlockDefinition(Block* block, const std::string& type) { m_graphChanged = true; } -} // namespace DigitizerUi +} // namespace DigitizerUi \ No newline at end of file diff --git a/src/ui/Flowgraph.hpp b/src/ui/Flowgraph.hpp index 44721d9a..87428043 100644 --- a/src/ui/Flowgraph.hpp +++ b/src/ui/Flowgraph.hpp @@ -38,6 +38,10 @@ struct DataType { Float32, DataSetFloat32, DataSetFloat64, + UInt64, + UInt32, + UInt16, + UInt8, Int64, Int32, Int16, @@ -143,6 +147,7 @@ struct DataType { } constexpr inline DataType() {} + constexpr inline DataType(Id id) : m_id(id) {} const std::string& toString() const; @@ -171,14 +176,18 @@ class BlockInstantiationDefinition { std::string defaultValue; }; + template struct NumberParameter { inline explicit NumberParameter(T v) : defaultValue(v) {} + T defaultValue; }; + struct StringParameter { std::string defaultValue; }; + struct Parameter { std::string id; std::string label; @@ -192,12 +201,15 @@ class BlockInstantiationDefinition { auto data_inputs() { return inputs | std::views::filter([](const PortDefinition& p) { return p.type != "message"; }); } + auto message_inputs() { return inputs | std::views::filter([](const PortDefinition& p) { return p.type == "message"; }); } + auto data_outputs() { return outputs | std::views::filter([](const PortDefinition& p) { return p.type != "message"; }); } + auto message_outputs() { return outputs | std::views::filter([](const PortDefinition& p) { return p.type == "message"; }); } @@ -256,6 +268,8 @@ struct BlockRegistry { class Block { public: + using StoredSettingsType = std::map>, gr::settings::PMTCompare>; + class Port { public: enum class Direction { @@ -263,14 +277,14 @@ class Block { Output, }; - Block* owningUiBlock; - const std::string name; - const std::string rawPortType; - bool isDataset; - const Direction portDirection; + Block* owningUiBlock = nullptr; + const std::string name{}; + const std::string rawPortType{}; + bool isDataset = false; + const Direction portDirection{}; - DataType portDataType; - std::vector portConnections; + DataType portDataType{}; + std::vector portConnections{}; }; struct EnumParameter { @@ -284,10 +298,12 @@ class Block { return *this; } }; + template struct NumberParameter { T value; }; + struct RawParameter { std::string value; }; @@ -310,21 +326,26 @@ class Block { const auto& inputs() const { return m_inputs; } const auto& outputs() const { return m_outputs; } - auto dataInputs() const { + + auto dataInputs() const { return m_inputs | std::views::filter([](const Port& p) { return p.portDataType != DataType::AsyncMessage; }); } + auto dataOutputs() const { return m_outputs | std::views::filter([](const Port& p) { return p.portDataType != DataType::AsyncMessage; }); } + auto messageInputs() const { return m_inputs | std::views::filter([](const Port& p) { return p.portDataType == DataType::AsyncMessage; }); } + auto messageOutputs() const { return m_outputs | std::views::filter([](const Port& p) { return p.portDataType == DataType::AsyncMessage; }); } - void setSetting(const std::string& name, const pmtv::pmt& par); - const auto& settings() const { return m_settings; } + void setSetting(const std::string& name, const pmtv::pmt& par); + const auto& settings() const { return m_settings; } + const StoredSettingsType& storedSettings() const { return _storedSettings; } void update(); @@ -335,13 +356,14 @@ class Block { auto& inputs() { return m_inputs; } auto& outputs() { return m_outputs; } - void updateSettings(const gr::property_map& settings); + void updateSettings(const gr::property_map& settings, const StoredSettingsType& stagedSettings = {}); const gr::property_map& metaInformation() const { return m_metaInformation; } protected: std::vector m_inputs; std::vector m_outputs; gr::property_map m_settings; + StoredSettingsType _storedSettings{}; bool m_updated = false; FlowGraph* m_flowGraph = nullptr; const BlockDefinition* m_type; @@ -446,4 +468,4 @@ class FlowGraph { // TODO add remote sources here? }; -} // namespace DigitizerUi +} // namespace DigitizerUi \ No newline at end of file diff --git a/src/ui/test/CMakeLists.txt b/src/ui/test/CMakeLists.txt index f7a0e544..e15828e2 100644 --- a/src/ui/test/CMakeLists.txt +++ b/src/ui/test/CMakeLists.txt @@ -62,6 +62,7 @@ if(ENABLE_IMGUI_TEST_ENGINE) add_imgui_test(qa_filtercomboboxes) add_imgui_test(qa_keypad) add_imgui_test(qa_chart) + add_imgui_test(qa_chart_fgdipole) add_imgui_test(qa_dashboardpage) add_imgui_test(qa_docking) endif() diff --git a/src/ui/test/examples/fg_dipole_intensity_ramp.grc b/src/ui/test/examples/fg_dipole_intensity_ramp.grc index cf1c3cc9..0df565d7 100644 --- a/src/ui/test/examples/fg_dipole_intensity_ramp.grc +++ b/src/ui/test/examples/fg_dipole_intensity_ramp.grc @@ -1,25 +1,24 @@ blocks: - name: ClockSource id: gr::basic::ClockSource - template_args: "float" + template_args: !!str "unsigned char" parameters: - chunk_size: 100 + chunk_size: !!uint32 100 do_zero_order_hold: true - n_samples_max: 100000000 - repeat_period: 2000000000 - sample_rate: 1000.000000 - verbose_console: false - tag_times: - - 10000000 # 10 ms in ns - - 100000000 # 100 ms in ns - - 300000000 # 300 ms in ns - - 350000000 # 350 ms in ns - - 550000000 # 550 ms in ns - - 560000000 # 560 ms in ns - - 650000000 # 650 ms in ns - - 800000000 # 800 ms in ns - - 850000000 # 850 ms in ns - tag_values: + n_samples_max: !!uint32 10000 + repeat_period: !!uint64 2000000000 + sample_rate: !!float32 1000.000000 + verbose_console: true + tag_times: !!uint64 + - 10000000 + - 40000000 + - 200000000 + - 400000000 + - 500000000 + - 1000000000 + - 1200000000 + - 1800000000 + tag_values: !!str - "CMD_BP_START/FAIR.SELECTOR.C=1:S=1:P=1" - "CMD_BP_START/FAIR.SELECTOR.C=1:S=1:P=2" - "CMD_BP_START/FAIR.SELECTOR.C=1:S=1:P=3" @@ -28,114 +27,102 @@ blocks: - "CMD_DIAG_TRIGGER1" - "CMD_BP_START/FAIR.SELECTOR.C=1:S=1:P=6" - "CMD_BP_START/FAIR.SELECTOR.C=1:S=1:P=7" - - "CMD_BP_START/FAIR.SELECTOR.C=1:S=1:P=8" - name: DipoleCurrentGenerator id: gr::basic::FunctionGenerator - template_args: "float" + template_args: !!str float parameters: - duration: 0.000000 - final_value: 0.000000 - impulse_time0: 0.000000 - impulse_time1: 0.000000 - round_off_time: 0.000000 - sample_rate: 1000.000000 - signal_type: Const - start_value: 0.000000 + duration: !!float32 0.000000 + final_value: !!float32 0.000000 + impulse_time0: !!float32 0.000000 + impulse_time1: !!float32 0.000000 + round_off_time: !!float32 0.000000 + sample_rate: !!float32 1000.000000 + signal_type: !!str Const + start_value: !!float32 0.000000 ctx_parameters: - - context: CMD_BP_START/FAIR.SELECTOR.C=1:S=1:P=1 - time: 0 + - context: !!str CMD_BP_START/FAIR.SELECTOR.C=1:S=1:P=1 + time: !!uint64 0 parameters: - duration: 0.000000 - final_value: 0.000000 - impulse_time0: 0.000000 - impulse_time1: 0.000000 - round_off_time: 0.000000 - sample_rate: 1000.000000 - signal_type: Const - start_value: 5.000000 - - context: CMD_BP_START/FAIR.SELECTOR.C=1:S=1:P=2 - time: 0 + duration: !!float32 0.000000 + start_value: !!float32 1.000000 + final_value: !!float32 1.000000 + impulse_time0: !!float32 0.000000 + impulse_time1: !!float32 0.000000 + round_off_time: !!float32 0.000000 + sample_rate: !!float32 1000.000000 + signal_type: !!str Const + - context: !!str CMD_BP_START/FAIR.SELECTOR.C=1:S=1:P=2 + time: !!uint64 0 parameters: - duration: 0.200000 - final_value: 30.000000 - impulse_time0: 0.000000 - impulse_time1: 0.000000 - round_off_time: 0.000000 - sample_rate: 1000.000000 - signal_type: LinearRamp - start_value: 5.000000 - - context: CMD_BP_START/FAIR.SELECTOR.C=1:S=1:P=3 - time: 0 + duration: !!float32 0.300000 + start_value: !!float32 1.000000 + final_value: !!float32 5.000000 + impulse_time0: !!float32 0.000000 + impulse_time1: !!float32 0.000000 + round_off_time: !!float32 0.050000 + sample_rate: !!float32 1000.000000 + signal_type: !!str ParabolicRamp + - context: !!str CMD_BP_START/FAIR.SELECTOR.C=1:S=1:P=3 + time: !!uint64 0 parameters: - duration: 0.000000 - final_value: 0.000000 - impulse_time0: 0.000000 - impulse_time1: 0.000000 - round_off_time: 0.000000 - sample_rate: 1000.000000 - signal_type: Const - start_value: 30.000000 - - context: CMD_BP_START/FAIR.SELECTOR.C=1:S=1:P=4 - time: 0 + duration: !!float32 0.000000 + start_value: !!float32 5.000000 + final_value: !!float32 5.000000 + impulse_time0: !!float32 0.000000 + impulse_time1: !!float32 0.000000 + round_off_time: !!float32 0.000000 + sample_rate: !!float32 1000.000000 + signal_type: !!str Const + - context: !!str CMD_BP_START/FAIR.SELECTOR.C=1:S=1:P=4 + time: !!uint64 0 parameters: - duration: 0.100000 - final_value: 20.000000 - impulse_time0: 0.000000 - impulse_time1: 0.000000 - round_off_time: 0.020000 - sample_rate: 1000.000000 - signal_type: ParabolicRamp - start_value: 30.000000 - - context: CMD_BP_START/FAIR.SELECTOR.C=1:S=1:P=5 - time: 0 + duration: !!float32 2.000000 + start_value: !!float32 5.000000 + final_value: !!float32 30.000000 + impulse_time0: !!float32 0.000000 + impulse_time1: !!float32 0.000000 + round_off_time: !!float32 0.200000 + sample_rate: !!float32 1000.000000 + signal_type: !!str ParabolicRamp + - context: !!str CMD_BP_START/FAIR.SELECTOR.C=1:S=1:P=5 + time: !!uint64 0 parameters: - duration: 0.000000 - final_value: 0.000000 - impulse_time0: 0.000000 - impulse_time1: 0.000000 - round_off_time: 0.000000 - sample_rate: 1000.000000 - signal_type: Const - start_value: 20.000000 - - context: CMD_BP_START/FAIR.SELECTOR.C=1:S=1:P=6 - time: 0 + duration: !!float32 0.000000 + start_value: !!float32 30.000000 + final_value: !!float32 30.000000 + impulse_time0: !!float32 0.000000 + impulse_time1: !!float32 0.000000 + round_off_time: !!float32 0.000000 + sample_rate: !!float32 1000.000000 + signal_type: !!str Const + - context: !!str CMD_BP_START/FAIR.SELECTOR.C=1:S=1:P=6 + time: !!uint64 0 parameters: - duration: 0.100000 - final_value: 10.000000 - impulse_time0: 0.000000 - impulse_time1: 0.000000 - round_off_time: 0.000000 - sample_rate: 1000.000000 + duration: !!float32 0.500000 + start_value: !!float32 30.000000 + final_value: !!float32 1.000000 + impulse_time0: !!float32 0.000000 + impulse_time1: !!float32 0.000000 + round_off_time: !!float32 0.000000 + sample_rate: !!float32 1000.000000 signal_type: CubicSpline - start_value: 20.000000 - - context: CMD_BP_START/FAIR.SELECTOR.C=1:S=1:P=7 - time: 0 + - context: !!str CMD_BP_START/FAIR.SELECTOR.C=1:S=1:P=7 + time: !!uint64 0 parameters: - duration: 0.000000 - final_value: 0.000000 - impulse_time0: 0.000000 - impulse_time1: 0.000000 - round_off_time: 0.000000 - sample_rate: 1000.000000 + duration: !!float32 0.000000 + start_value: !!float32 1.000000 + final_value: !!float32 1.000000 + impulse_time0: !!float32 0.000000 + impulse_time1: !!float32 0.000000 + round_off_time: !!float32 0.000000 + sample_rate: !!float32 1000.000000 signal_type: Const - start_value: 10.000000 - - context: CMD_BP_START/FAIR.SELECTOR.C=1:S=1:P=8 - time: 0 - parameters: - duration: 0.000000 - final_value: 20.000000 - impulse_time0: 0.020000 - impulse_time1: 0.060000 - round_off_time: 0.000000 - sample_rate: 1000.000000 - signal_type: ImpulseResponse - start_value: 5.000000 - name: DipoleCurrentSink id: opendigitizer::ImPlotSink - template_args: float parameters: - signal_name: dipole current - signal_unit: A + color: !!uint32 0x0000FF + signal_name: !!str dipole current + signal_unit: !!str A connections: - [ClockSource, 0, DipoleCurrentGenerator, 0] - [DipoleCurrentGenerator, 0, DipoleCurrentSink, 0] diff --git a/src/ui/test/examples/fg_dipole_intensity_ramp.yml b/src/ui/test/examples/fg_dipole_intensity_ramp.yml index f449db57..6f84e6d2 100644 --- a/src/ui/test/examples/fg_dipole_intensity_ramp.yml +++ b/src/ui/test/examples/fg_dipole_intensity_ramp.yml @@ -1,19 +1,19 @@ sources: - - name: sink 1 + - name: DipoleCurrentSink block: DipoleCurrentSink port: 0 - color: 4281816053 + color: !!uint32 0x0000ff plots: - name: Plot 1 axes: - axis: X - min: 0.1 - max: 3000.0 + min: 0.0 + max: 11000.0 - axis: Y - min: -1.991981029510498 - max: 1.9978399276733398 + min: -0.01 + max: 32.0 sources: - - sink 1 + - DipoleCurrentSink rect: - 8 - 0 diff --git a/src/ui/test/qa_chart_fgdipole.cpp b/src/ui/test/qa_chart_fgdipole.cpp index f014f914..1a6716e9 100644 --- a/src/ui/test/qa_chart_fgdipole.cpp +++ b/src/ui/test/qa_chart_fgdipole.cpp @@ -16,20 +16,18 @@ #include "gnuradio-4.0/basic/FunctionGenerator.hpp" #include "gnuradio-4.0/basic/clock_source.hpp" #include "imgui_test_engine/imgui_te_internal.h" +#include #include #include +#include #include #include #include -#include #include CMRC_DECLARE(ui_test_assets); -/// This file is a duplicate from qa_chart.cpp (but with fg_dipole_intensity_ramp.grc), as requested in #231, to be hacked on. -/// After the dust settles, we should compare the 2 and move duplicated code into shared helper classes - using namespace boost; using namespace boost::ut; @@ -38,6 +36,7 @@ template class TestScheduler : public gr::scheduler::Simple { public: explicit TestScheduler(gr::Graph&& graph) : gr::scheduler::Simple(std::move(graph)) {} + void stop() { gr::scheduler::Simple::stop(); } }; @@ -50,12 +49,12 @@ struct TestState { void startScheduler(gr::Graph&& graph) { schedulerThread = std::thread([&] { scheduler = std::make_unique>(std::move(graph)); - scheduler->runAndWait(); + auto ret = scheduler->runAndWait(); + expect(ret.has_value()) << std::format("TestScheduler returned with: {} in {}:{}", ret.error().message, ret.error().srcLoc(), ret.error().methodName()); }); } void stopScheduler() { - dashboard = {}; scheduler->stop(); schedulerThread.join(); } @@ -67,49 +66,43 @@ struct TestApp : public DigitizerUi::test::ImGuiTestApp { using DigitizerUi::test::ImGuiTestApp::ImGuiTestApp; void registerTests() override { - ImGuiTest* t = IM_REGISTER_TEST(engine(), "chart_dashboard", "more_plotting"); + ImGuiTest* t = IM_REGISTER_TEST(engine(), "chart_dashboard", "DashboardPage::drawPlot"); t->SetVarsDataType(); t->GuiFunc = [](ImGuiTestContext*) { ImGui::Begin("Test Window", nullptr, ImGuiWindowFlags_NoSavedSettings); ImGui::SetWindowPos({0, 0}); - ImGui::SetWindowSize(ImVec2(500, 500)); + ImGui::SetWindowSize(ImVec2(800, 800)); if (g_state.dashboard) { + DigitizerUi::DashboardPage page; + page.draw(*g_state.dashboard); ut::expect(!g_state.dashboard->plots().empty()); - auto plot = g_state.dashboard->plots()[0]; - - if (ImPlot::BeginPlot("Line Plot")) { - DigitizerUi::DashboardPage::drawPlot(*g_state.dashboard, plot); - ImPlot::EndPlot(); - } } ImGui::End(); }; t->TestFunc = [](ImGuiTestContext* ctx) { - "more_plotting"_test = [ctx] { + "DashboardPage::drawPlot"_test = [ctx] { ctx->SetRef("Test Window"); // For our test we stop the graph after a certain amount samples. // TODO: Once Ivan finishes his new ImPlotSink registry class we can remove these reinterpret_cast. auto execution = g_state.dashboard->localFlowGraph.createExecutionContext(); - auto blockModel = g_state.dashboard->localFlowGraph.findPlotSinkGrBlock("sink 3"); + auto blockModel = g_state.dashboard->localFlowGraph.findPlotSinkGrBlock("DipoleCurrentSink"); ut::expect(blockModel); auto plotBlockModel = reinterpret_cast>*>(blockModel); auto implotSink = reinterpret_cast*>(plotBlockModel->raw()); - const int maxSamples = 1400; - while (g_state.dashboard && implotSink->data.size() < maxSamples) { + while (gr::lifecycle::isActive(implotSink->state())) { ImGuiTestEngine_Yield(ctx->Engine); } - captureScreenshot(*ctx); - g_state.stopScheduler(); + captureScreenshot(*ctx); }; }; } @@ -139,7 +132,7 @@ int main(int argc, char* argv[]) { auto execution = g_state.dashboard->localFlowGraph.createExecutionContext(); g_state.dashboard->localFlowGraph.setPlotSinkGrBlocks(std::move(execution.plotSinkGrBlocks)); - g_state.startScheduler(std::move(execution.graph)); + g_state.startScheduler(std::move(execution.grGraph)); return app.runTests() ? 0 : 1; -} +} \ No newline at end of file