From 663894e9adce563af62573944c32f2893f0126fa Mon Sep 17 00:00:00 2001 From: "Ivan F. Valerio" Date: Sat, 1 Mar 2025 22:22:11 -0500 Subject: [PATCH 01/12] Fixed bug where loading workspace crashes due to incorrect indexing of plugin saves --- src/main_window.cpp | 30 ++++++++++++++++++++++-------- src/main_window.hpp | 10 +++++++--- src/widgets.hpp | 1 + 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/main_window.cpp b/src/main_window.cpp index 293db33d..671bf880 100644 --- a/src/main_window.cpp +++ b/src/main_window.cpp @@ -437,7 +437,8 @@ void MainWindow::loadPeriodSettings(QSettings& userprefs) this->event_manager->postEvent(&event); } -void MainWindow::saveDAQSettings(QSettings& userprefs) +void MainWindow::saveDAQSettings( + QSettings& userprefs) { userprefs.beginGroup("DAQs"); @@ -534,7 +535,7 @@ void MainWindow::saveDAQSettings(QSettings& userprefs) } void MainWindow::loadDAQSettings( - QSettings& userprefs, std::unordered_map block_cache) + QSettings& userprefs, std::unordered_map& block_cache) { userprefs.beginGroup("DAQs"); Event::Object get_devices_event(Event::Type::DAQ_DEVICE_QUERY_EVENT); @@ -657,16 +658,24 @@ void MainWindow::loadDAQSettings( userprefs.endGroup(); // DAQ } -void MainWindow::saveWidgetSettings(QSettings& userprefs) +void MainWindow::saveWidgetSettings( + QSettings& userprefs) { userprefs.beginGroup("Widgets"); Event::Object loaded_plugins_query(Event::Type::PLUGIN_LIST_QUERY_EVENT); this->event_manager->postEvent(&loaded_plugins_query); const auto plugin_list = std::any_cast>( loaded_plugins_query.getParam("plugins")); - int widget_count = 0; + int non_component_plugin_id = -1; + int plugin_id = 0; for (const auto& entry : plugin_list) { - userprefs.beginGroup(QString::number(widget_count++)); + // We don't expect the number of blocks in the system to exceed the max positive value of int + // In 2025 you would run out of memory first before that happens. + // Additionally we are saving negative values to indicate to the workspace loading system + // that the negative valued plugins do not contain a component and therefore should be left + // out of the connection loading step. + plugin_id = entry->hasComponent() ? static_cast(entry->getID()) : non_component_plugin_id--; + userprefs.beginGroup(QString::number(plugin_id)); userprefs.setValue("library", QString::fromStdString(entry->getLibrary())); userprefs.beginGroup("standardParams"); entry->saveParameterSettings(userprefs); @@ -680,7 +689,7 @@ void MainWindow::saveWidgetSettings(QSettings& userprefs) } void MainWindow::loadWidgetSettings( - QSettings& userprefs, std::unordered_map block_cache) + QSettings& userprefs, std::unordered_map& block_cache) { userprefs.beginGroup("Widgets"); QString plugin_name; @@ -703,7 +712,8 @@ void MainWindow::loadWidgetSettings( userprefs.endGroup(); // Widgets } -void MainWindow::saveConnectionSettings(QSettings& userprefs) +void MainWindow::saveConnectionSettings( + QSettings& userprefs) { Event::Object all_connections_event( Event::Type::IO_ALL_CONNECTIONS_QUERY_EVENT); @@ -722,6 +732,8 @@ void MainWindow::saveConnectionSettings(QSettings& userprefs) { continue; } + // We need to match the block id with the id already stored in the + // settings file when saveWidgets was called. id_connection.src_id = conn.src->getID(); id_connection.src_direction = conn.src_port_type; id_connection.src_port = conn.src_port; @@ -734,7 +746,7 @@ void MainWindow::saveConnectionSettings(QSettings& userprefs) } void MainWindow::loadConnectionSettings( - QSettings& userprefs, std::unordered_map block_cache) + QSettings& userprefs, std::unordered_map& block_cache) { ///////////////////// Load connections ///////////////////////// RT::block_connection_t connection; @@ -837,6 +849,8 @@ void MainWindow::saveSettings() this->saveWidgetSettings(workspaceprefs); + this->saveConnectionSettings(workspaceprefs); + userprefs.endGroup(); // Workspaces } diff --git a/src/main_window.hpp b/src/main_window.hpp index fc3fff5f..f5a1ce05 100644 --- a/src/main_window.hpp +++ b/src/main_window.hpp @@ -23,6 +23,7 @@ #include #include +#include #include "event.hpp" @@ -178,13 +179,16 @@ private slots: inline void loadPeriodSettings(QSettings& userprefs); inline void saveDAQSettings(QSettings& userprefs); inline void loadDAQSettings( - QSettings& userprefs, std::unordered_map block_cache); + QSettings& userprefs, + std::unordered_map& block_cache); inline void saveWidgetSettings(QSettings& userprefs); inline void loadWidgetSettings( - QSettings& userprefs, std::unordered_map block_cache); + QSettings& userprefs, + std::unordered_map& block_cache); inline void saveConnectionSettings(QSettings& userprefs); inline void loadConnectionSettings( - QSettings& userprefs, std::unordered_map block_cache); + QSettings& userprefs, + std::unordered_map& block_cache); Event::Manager* event_manager; QMdiArea* mdiArea = nullptr; QList subWindows; diff --git a/src/widgets.hpp b/src/widgets.hpp index 9e5e8e4f..6d956533 100644 --- a/src/widgets.hpp +++ b/src/widgets.hpp @@ -719,6 +719,7 @@ class Plugin : public Event::Handler * \return IO::Block pointer to the internal structure */ IO::Block* getBlock() { return plugin_component.get(); } + const IO::Block* getBlock() const { return plugin_component.get(); } protected: Widgets::Component* getComponent(); From f2dfc19bb053ddd774b7f40782d54a518fc9dff9 Mon Sep 17 00:00:00 2001 From: "Ivan F. Valerio" Date: Sun, 23 Mar 2025 14:39:45 -0400 Subject: [PATCH 02/12] Changed IO block behavior to check bounds when reading and writing to ports --- src/io.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/io.cpp b/src/io.cpp index a7d37159..2489d70e 100644 --- a/src/io.cpp +++ b/src/io.cpp @@ -35,7 +35,7 @@ IO::Block::Block(std::string blockname, port.value = 0.0; port.buff_value = 0.0; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index) - ports[channel.flags].push_back(port); + ports.at(channel.flags).push_back(port); port = {}; } } @@ -43,44 +43,44 @@ IO::Block::Block(std::string blockname, size_t IO::Block::getCount(IO::flags_t type) const { // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index) - return this->ports[type].size(); + return this->ports.at(type).size(); } std::string IO::Block::getChannelName(IO::flags_t type, size_t index) const { // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index) - return this->ports[type][index].channel_info.name; + return this->ports.at(type).at(index).channel_info.name; } std::string IO::Block::getChannelDescription(IO::flags_t type, size_t index) const { // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index) - return this->ports[type][index].channel_info.description; + return this->ports.at(type).at(index).channel_info.description; } void IO::Block::writeinput(size_t index, const double& data) { - this->ports[IO::INPUT][index].buff_value += data; + this->ports.at(IO::INPUT).at(index).buff_value += data; } double& IO::Block::readinput(size_t index) { // We must reset input values to zero so that the next cycle doesn't use these // values - this->ports[IO::INPUT][index].value = - this->ports[IO::INPUT][index].buff_value; - this->ports[IO::INPUT][index].buff_value = 0.0; - return this->ports[IO::INPUT][index].value; + this->ports.at(IO::INPUT).at(index).value = + this->ports.at(IO::INPUT).at(index).buff_value; + this->ports.at(IO::INPUT).at(index).buff_value = 0.0; + return this->ports.at(IO::INPUT).at(index).value; } void IO::Block::writeoutput(size_t index, const double& data) { - this->ports[IO::OUTPUT][index].value = data; + this->ports.at(IO::OUTPUT).at(index).value = data; } const double& IO::Block::readPort(IO::flags_t direction, size_t index) { - return this->ports[direction][index].value; + return this->ports.at(direction).at(index).value; } // NOLINTEND(cppcoreguidelines-pro-bounds-constant-array-index) From 489c39a3b291cc64e549369379dce02cee10161b Mon Sep 17 00:00:00 2001 From: "Ivan F. Valerio" Date: Sun, 23 Mar 2025 14:41:18 -0400 Subject: [PATCH 03/12] Adding a new fake driver to rtxi testing facilities to make device related testing a lot easier for developers without hardware access --- src/workspace.cpp | 11 +++ test/fakeDriver.cpp | 165 ++++++++++++++++++++++++++++++++++++++++++++ test/fakeDriver.hpp | 0 3 files changed, 176 insertions(+) create mode 100644 test/fakeDriver.cpp create mode 100644 test/fakeDriver.hpp diff --git a/src/workspace.cpp b/src/workspace.cpp index b551d65d..27d25307 100644 --- a/src/workspace.cpp +++ b/src/workspace.cpp @@ -75,6 +75,17 @@ Workspace::Manager::Manager(Event::Manager* ev_manager) ERROR_MSG("Unable to load GSC aio168 rtxi driver"); } } + #ifdef DEBUG_DRIVERS + const std::string fake_driver_name = "librtxifakedriver.so"; + if (bin_dir.exists(QString::fromStdString(fake_driver_name))) { + try { + this->registerDriver(bin_dir.path().toStdString() + std::string("/") + + fake_driver_name); + } catch (std::runtime_error& excepttion) { + ERROR_MSG("Unable to load fake rtxi driver"); + } + } + #endif } Workspace::Manager::~Manager() diff --git a/test/fakeDriver.cpp b/test/fakeDriver.cpp new file mode 100644 index 00000000..adbbd427 --- /dev/null +++ b/test/fakeDriver.cpp @@ -0,0 +1,165 @@ +/* +Copyright (C) 2015 Georgia Institute of Technology + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +Created by Ivan F. Valerio + +The face interface driver + +This is used for testing purposes in machines that do not contain +supported hardware. It is meant to emulate a DAQ interface. No need +to use on actual installation. +*/ + +#include "daq.hpp" + +enum physical_channel_t : uint8_t +{ + AI = 0, + AO, + DI, + DO +}; + +class Device final : public DAQ::Device +{ +public: + Device(const Device&) = default; + Device(Device&&) = delete; + Device& operator=(const Device&) = delete; + Device& operator=(Device&&) = delete; + Device(const std::string& dev_name, + const std::vector& channels, + std::string internal_name); + ~Device() final; + + size_t getChannelCount(DAQ::ChannelType::type_t type) const final; + bool getChannelActive(DAQ::ChannelType::type_t type, + DAQ::index_t index) const final; + int setChannelActive(DAQ::ChannelType::type_t type, + DAQ::index_t index, + bool state) final; + size_t getAnalogRangeCount(DAQ::index_t index) const final; + size_t getAnalogReferenceCount(DAQ::index_t index) const final; + size_t getAnalogUnitsCount(DAQ::index_t index) const final; + size_t getAnalogDownsample(DAQ::ChannelType::type_t type, + DAQ::index_t index) const final; + std::string getAnalogRangeString(DAQ::ChannelType::type_t type, + DAQ::index_t index, + DAQ::index_t range) const final; + std::string getAnalogReferenceString(DAQ::ChannelType::type_t type, + DAQ::index_t index, + DAQ::index_t reference) const final; + std::string getAnalogUnitsString(DAQ::ChannelType::type_t type, + DAQ::index_t index, + DAQ::index_t units) const final; + double getAnalogGain(DAQ::ChannelType::type_t type, + DAQ::index_t index) const final; + double getAnalogZeroOffset(DAQ::ChannelType::type_t type, + DAQ::index_t index) const final; + DAQ::index_t getAnalogRange(DAQ::ChannelType::type_t type, + DAQ::index_t index) const final; + DAQ::index_t getAnalogReference(DAQ::ChannelType::type_t type, + DAQ::index_t index) const final; + DAQ::index_t getAnalogUnits(DAQ::ChannelType::type_t type, + DAQ::index_t index) const final; + DAQ::index_t getAnalogOffsetUnits(DAQ::ChannelType::type_t type, + DAQ::index_t index) const final; + int setAnalogGain(DAQ::ChannelType::type_t type, + DAQ::index_t index, + double gain) final; + int setAnalogRange(DAQ::ChannelType::type_t type, + DAQ::index_t index, + DAQ::index_t range) final; + int setAnalogZeroOffset(DAQ::ChannelType::type_t type, + DAQ::index_t index, + double offset) final; + int setAnalogReference(DAQ::ChannelType::type_t type, + DAQ::index_t index, + DAQ::index_t reference) final; + int setAnalogUnits(DAQ::ChannelType::type_t type, + DAQ::index_t index, + DAQ::index_t units) final; + int setAnalogOffsetUnits(DAQ::ChannelType::type_t type, + DAQ::index_t index, + DAQ::index_t units) final; + int setAnalogDownsample(DAQ::ChannelType::type_t type, + DAQ::index_t index, + size_t downsample) final; + int setAnalogCounter(DAQ::ChannelType::type_t type, DAQ::index_t index) final; + int setAnalogCalibrationValue(DAQ::ChannelType::type_t type, + DAQ::index_t index, + double value) final; + double getAnalogCalibrationValue(DAQ::ChannelType::type_t type, + DAQ::index_t index) const final; + int setAnalogCalibrationActive(DAQ::ChannelType::type_t type, + DAQ::index_t index, + bool state) final; + bool getAnalogCalibrationActive(DAQ::ChannelType::type_t type, + DAQ::index_t index) const final; + bool getAnalogCalibrationState(DAQ::ChannelType::type_t type, + DAQ::index_t index) const final; + int setDigitalDirection(DAQ::index_t index, DAQ::direction_t direction) final; + + void read() final; + void write() final; + +private: + std::array, DAQ::ChannelType::UNKNOWN> + active_channels; + + std::array, 4> physical_channels_registry; + std::array default_ranges = DAQ::get_default_ranges(); + std::array default_units = DAQ::get_default_units(); + + // Used a tuple here because we want buffers to be in one place, and since not + // all IO is float, that meant we could not use a plain vector of vectors. + // Maybe we can just split them out to their own variables of same type for + // speed. + std::tuple, + std::vector, + std::vector, + std::vector> + buffer_arrays; +}; + +class Driver : public DAQ::Driver +{ +public: + static DAQ::Driver* getInstance(); + void loadDevices() final; + void unloadDevices() final; + std::vector getDevices() final; + +private: + Driver(); + std::vector nidaq_devices; +}; + +DAQ::Driver* Driver::getInstance() +{ + static Driver instance; + return &instance; +} + +extern "C" +{ +DAQ::Driver* getRTXIDAQDriver() +{ + return Driver::getInstance(); +} + +void deleteRTXIDAQDriver() {} +} diff --git a/test/fakeDriver.hpp b/test/fakeDriver.hpp new file mode 100644 index 00000000..e69de29b From 05fa854ed871667cfc70f02a480734ede80a713f Mon Sep 17 00:00:00 2001 From: "Ivan F. Valerio" Date: Sun, 23 Mar 2025 14:42:24 -0400 Subject: [PATCH 04/12] Added fake driver files to CMakeLists --- test/CMakeLists.txt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 121605e0..dc047bf4 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -81,6 +81,18 @@ target_link_libraries(fakePlugin PRIVATE GTest::gmock GTest::gmock_main ) +add_library(rtxifakedriver MODULE fakeDriver.cpp) + +target_include_directories(rtxifakedriver PRIVATE ${CMAKE_SOURCE_DIR}) + +target_link_libraries(rtxifakedriver PRIVATE rtxi rtxipal) + +install( + TARGETS rtxifakedriver + DESTINATION ${CMAKE_INSTALL_BINDIR} + EXPORT rtxiLibraryTargets +) + add_test(NAME rtxiTests COMMAND rtxiTests) add_folders(Test) From 638c6becc32c3ccc3caa64cc85b11225badf6cf7 Mon Sep 17 00:00:00 2001 From: "Ivan F. Valerio" Date: Wed, 9 Apr 2025 21:19:39 -0400 Subject: [PATCH 05/12] Added support for beter error messages when nidaqdriver fails to perform a task. Proper handling of current vs voltage settings --- src/nidaq_driver.cpp | 50 ++++++++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/src/nidaq_driver.cpp b/src/nidaq_driver.cpp index 6fd544b6..a7e216b7 100644 --- a/src/nidaq_driver.cpp +++ b/src/nidaq_driver.cpp @@ -184,6 +184,27 @@ inline void printError(int32_t status) ERROR_MSG("Message : {}", std::string(err_buff.data())); } +inline void printExtendedError(int32_t status) +{ + if (status == 0) { + return; + } + ERROR_MSG("NIDAQ ERROR : code {}", status); + const int32_t error_size = DAQmxGetExtendedErrorInfo(nullptr, 0); + if (error_size < 0) { + ERROR_MSG("Unable to get code message"); + return; + } + std::vector err_buff(static_cast(error_size)); + const int32_t errcode = DAQmxGetExtendedErrorInfo( + err_buff.data(), static_cast(error_size)); + if (errcode < 0) { + ERROR_MSG("Unable to parse message"); + return; + } + ERROR_MSG("Message : {}", std::string(err_buff.data())); +} + inline std::string physical_card_name(const std::string& device_name) { std::array buffer {}; @@ -218,6 +239,8 @@ struct physical_channel_t int32_t physical_channel_t::addToTask(TaskHandle task_handle) const { int32_t err = 0; + int32_t shunt_resistance_location = 0; + int32_t shunt_resisntance_value = 0; const std::string units = DAQ::get_default_units().at(units_index); auto [min, max] = DAQ::get_default_ranges().at(range_index); switch (type) { @@ -232,20 +255,21 @@ int32_t physical_channel_t::addToTask(TaskHandle task_handle) const DAQmx_Val_Volts, nullptr); } else if (units == "amps") { + DAQmxGetAICurrentShuntLoc(task_handle, name.c_str(), &shunt_resistance_location); err = DAQmxCreateAICurrentChan(task_handle, name.c_str(), nullptr, - reference, - min, - max, + DAQmx_Val_Diff, + min/1000.0, + max/1000.0, DAQmx_Val_Amps, - DAQmx_Val_Default, - 0, + shunt_resistance_location, + 249.0, nullptr); } else { ERROR_MSG("NIDAQ : Virtual Channel Creation : Unknown units {}", units); } - printError(err); + printExtendedError(err); break; case DAQ::ChannelType::AO: if (units == "volts") { @@ -267,17 +291,17 @@ int32_t physical_channel_t::addToTask(TaskHandle task_handle) const } else { ERROR_MSG("NIDAQ : Virtual Channel Creation : Unknown units {}", units); } - printError(err); + printExtendedError(err); break; case DAQ::ChannelType::DI: err = DAQmxCreateDIChan( task_handle, name.c_str(), nullptr, DAQmx_Val_ChanPerLine); - printError(err); + printExtendedError(err); break; case DAQ::ChannelType::DO: err = DAQmxCreateDOChan( task_handle, name.c_str(), nullptr, DAQmx_Val_ChanPerLine); - printError(err); + printExtendedError(err); break; default: ERROR_MSG("NIDAQ_DRIVER : Channel Type Unknown"); @@ -516,7 +540,7 @@ int Device::setChannelActive(DAQ::ChannelType::type_t type, DAQmxClearTask(task); err = DAQmxCreateTask(DAQ::ChannelType::type2string(type).c_str(), &task); if (err != 0) { - printError(err); + printExtendedError(err); for (auto& channel : physical_channels_registry.at(type)) { channel.active = false; } @@ -554,9 +578,9 @@ int Device::setChannelActive(DAQ::ChannelType::type_t type, } } if (DAQmxGetTaskChannels(task, nullptr, 0) > 0) { - printError(DAQmxSetSampTimingType(task, DAQmx_Val_OnDemand)); - printError(DAQmxTaskControl(task, DAQmx_Val_Task_Commit)); - printError(DAQmxStartTask(task)); + printExtendedError(DAQmxSetSampTimingType(task, DAQmx_Val_OnDemand)); + printExtendedError(DAQmxTaskControl(task, DAQmx_Val_Task_Commit)); + printExtendedError(DAQmxStartTask(task)); } return err; } From 20b39d8652eda9c1a5a0a8c3e40568dfbf7acf93 Mon Sep 17 00:00:00 2001 From: "Ivan F. Valerio" Date: Wed, 9 Apr 2025 22:29:42 -0400 Subject: [PATCH 06/12] Fixed bug where rtxi crashes with nidaq driver when there are no install daq cards in the computer --- src/nidaq_driver.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/nidaq_driver.cpp b/src/nidaq_driver.cpp index a7e216b7..911229a6 100644 --- a/src/nidaq_driver.cpp +++ b/src/nidaq_driver.cpp @@ -1017,6 +1017,9 @@ void Driver::loadDevices() printError(device_names_buffer_size); return; } + if (device_names_buffer_size == 0) { + return; + } const std::string alpha = "abcdefghijklmnopqrstuvwxyz"; std::vector buffer(static_cast(device_names_buffer_size)); DAQmxGetSysDevNames(buffer.data(), static_cast(buffer.size())); From a380b0cfa3776714dfb2c930b4cc2ca827f99af8 Mon Sep 17 00:00:00 2001 From: "Ivan F. Valerio" Date: Wed, 9 Apr 2025 22:43:07 -0400 Subject: [PATCH 07/12] Relying on fake drivers provided by vendors instead --- test/fakeDriver.cpp | 165 -------------------------------------------- test/fakeDriver.hpp | 0 2 files changed, 165 deletions(-) delete mode 100644 test/fakeDriver.cpp delete mode 100644 test/fakeDriver.hpp diff --git a/test/fakeDriver.cpp b/test/fakeDriver.cpp deleted file mode 100644 index adbbd427..00000000 --- a/test/fakeDriver.cpp +++ /dev/null @@ -1,165 +0,0 @@ -/* -Copyright (C) 2015 Georgia Institute of Technology - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . - -Created by Ivan F. Valerio - -The face interface driver - -This is used for testing purposes in machines that do not contain -supported hardware. It is meant to emulate a DAQ interface. No need -to use on actual installation. -*/ - -#include "daq.hpp" - -enum physical_channel_t : uint8_t -{ - AI = 0, - AO, - DI, - DO -}; - -class Device final : public DAQ::Device -{ -public: - Device(const Device&) = default; - Device(Device&&) = delete; - Device& operator=(const Device&) = delete; - Device& operator=(Device&&) = delete; - Device(const std::string& dev_name, - const std::vector& channels, - std::string internal_name); - ~Device() final; - - size_t getChannelCount(DAQ::ChannelType::type_t type) const final; - bool getChannelActive(DAQ::ChannelType::type_t type, - DAQ::index_t index) const final; - int setChannelActive(DAQ::ChannelType::type_t type, - DAQ::index_t index, - bool state) final; - size_t getAnalogRangeCount(DAQ::index_t index) const final; - size_t getAnalogReferenceCount(DAQ::index_t index) const final; - size_t getAnalogUnitsCount(DAQ::index_t index) const final; - size_t getAnalogDownsample(DAQ::ChannelType::type_t type, - DAQ::index_t index) const final; - std::string getAnalogRangeString(DAQ::ChannelType::type_t type, - DAQ::index_t index, - DAQ::index_t range) const final; - std::string getAnalogReferenceString(DAQ::ChannelType::type_t type, - DAQ::index_t index, - DAQ::index_t reference) const final; - std::string getAnalogUnitsString(DAQ::ChannelType::type_t type, - DAQ::index_t index, - DAQ::index_t units) const final; - double getAnalogGain(DAQ::ChannelType::type_t type, - DAQ::index_t index) const final; - double getAnalogZeroOffset(DAQ::ChannelType::type_t type, - DAQ::index_t index) const final; - DAQ::index_t getAnalogRange(DAQ::ChannelType::type_t type, - DAQ::index_t index) const final; - DAQ::index_t getAnalogReference(DAQ::ChannelType::type_t type, - DAQ::index_t index) const final; - DAQ::index_t getAnalogUnits(DAQ::ChannelType::type_t type, - DAQ::index_t index) const final; - DAQ::index_t getAnalogOffsetUnits(DAQ::ChannelType::type_t type, - DAQ::index_t index) const final; - int setAnalogGain(DAQ::ChannelType::type_t type, - DAQ::index_t index, - double gain) final; - int setAnalogRange(DAQ::ChannelType::type_t type, - DAQ::index_t index, - DAQ::index_t range) final; - int setAnalogZeroOffset(DAQ::ChannelType::type_t type, - DAQ::index_t index, - double offset) final; - int setAnalogReference(DAQ::ChannelType::type_t type, - DAQ::index_t index, - DAQ::index_t reference) final; - int setAnalogUnits(DAQ::ChannelType::type_t type, - DAQ::index_t index, - DAQ::index_t units) final; - int setAnalogOffsetUnits(DAQ::ChannelType::type_t type, - DAQ::index_t index, - DAQ::index_t units) final; - int setAnalogDownsample(DAQ::ChannelType::type_t type, - DAQ::index_t index, - size_t downsample) final; - int setAnalogCounter(DAQ::ChannelType::type_t type, DAQ::index_t index) final; - int setAnalogCalibrationValue(DAQ::ChannelType::type_t type, - DAQ::index_t index, - double value) final; - double getAnalogCalibrationValue(DAQ::ChannelType::type_t type, - DAQ::index_t index) const final; - int setAnalogCalibrationActive(DAQ::ChannelType::type_t type, - DAQ::index_t index, - bool state) final; - bool getAnalogCalibrationActive(DAQ::ChannelType::type_t type, - DAQ::index_t index) const final; - bool getAnalogCalibrationState(DAQ::ChannelType::type_t type, - DAQ::index_t index) const final; - int setDigitalDirection(DAQ::index_t index, DAQ::direction_t direction) final; - - void read() final; - void write() final; - -private: - std::array, DAQ::ChannelType::UNKNOWN> - active_channels; - - std::array, 4> physical_channels_registry; - std::array default_ranges = DAQ::get_default_ranges(); - std::array default_units = DAQ::get_default_units(); - - // Used a tuple here because we want buffers to be in one place, and since not - // all IO is float, that meant we could not use a plain vector of vectors. - // Maybe we can just split them out to their own variables of same type for - // speed. - std::tuple, - std::vector, - std::vector, - std::vector> - buffer_arrays; -}; - -class Driver : public DAQ::Driver -{ -public: - static DAQ::Driver* getInstance(); - void loadDevices() final; - void unloadDevices() final; - std::vector getDevices() final; - -private: - Driver(); - std::vector nidaq_devices; -}; - -DAQ::Driver* Driver::getInstance() -{ - static Driver instance; - return &instance; -} - -extern "C" -{ -DAQ::Driver* getRTXIDAQDriver() -{ - return Driver::getInstance(); -} - -void deleteRTXIDAQDriver() {} -} diff --git a/test/fakeDriver.hpp b/test/fakeDriver.hpp deleted file mode 100644 index e69de29b..00000000 From 3a1403c1bd6f9b50fa28e19095f2c7401c3ba7fe Mon Sep 17 00:00:00 2001 From: "Ivan F. Valerio" Date: Wed, 9 Apr 2025 22:45:23 -0400 Subject: [PATCH 08/12] Removed fakeDriver code from compilation --- test/CMakeLists.txt | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index dc047bf4..121605e0 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -81,18 +81,6 @@ target_link_libraries(fakePlugin PRIVATE GTest::gmock GTest::gmock_main ) -add_library(rtxifakedriver MODULE fakeDriver.cpp) - -target_include_directories(rtxifakedriver PRIVATE ${CMAKE_SOURCE_DIR}) - -target_link_libraries(rtxifakedriver PRIVATE rtxi rtxipal) - -install( - TARGETS rtxifakedriver - DESTINATION ${CMAKE_INSTALL_BINDIR} - EXPORT rtxiLibraryTargets -) - add_test(NAME rtxiTests COMMAND rtxiTests) add_folders(Test) From 9c161e914a62b73283b984dee7f842a117bb31bf Mon Sep 17 00:00:00 2001 From: "Ivan F. Valerio" Date: Sun, 13 Apr 2025 10:26:42 -0400 Subject: [PATCH 09/12] Changed pause button updates to only set down the button without emitting additional events --- src/widgets.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widgets.cpp b/src/widgets.cpp index e9f9fe63..4c5b3c5a 100644 --- a/src/widgets.cpp +++ b/src/widgets.cpp @@ -304,7 +304,7 @@ void Widgets::Panel::updatePauseButton() return; } const bool paused = state == RT::State::PAUSE; - pauseButton->setChecked(paused); + pauseButton->setDown(paused); } void Widgets::Panel::update_state(RT::State::state_t flag) From eae4ee4c9e2a789f0367c95bcf216924e9243a5c Mon Sep 17 00:00:00 2001 From: "Ivan F. Valerio" Date: Sat, 3 May 2025 16:02:21 -0400 Subject: [PATCH 10/12] Modified settings logic to load connections without crashing --- src/main_window.cpp | 59 ++++++++++++++++++++++----------------------- src/main_window.hpp | 6 ++--- 2 files changed, 32 insertions(+), 33 deletions(-) diff --git a/src/main_window.cpp b/src/main_window.cpp index 671bf880..389538f0 100644 --- a/src/main_window.cpp +++ b/src/main_window.cpp @@ -61,11 +61,11 @@ // deserialize it back when loading settings. struct plugin_connection { - quint64 src_id; - quint64 src_direction; - quint64 src_port; - quint64 dest_id; - quint64 dest_port; + qint32 src_id; + qint32 src_direction; + qint32 src_port; + qint32 dest_id; + qint32 dest_port; }; QDataStream& operator<<(QDataStream& out, const plugin_connection& conn) @@ -437,8 +437,7 @@ void MainWindow::loadPeriodSettings(QSettings& userprefs) this->event_manager->postEvent(&event); } -void MainWindow::saveDAQSettings( - QSettings& userprefs) +void MainWindow::saveDAQSettings(QSettings& userprefs) { userprefs.beginGroup("DAQs"); @@ -535,7 +534,7 @@ void MainWindow::saveDAQSettings( } void MainWindow::loadDAQSettings( - QSettings& userprefs, std::unordered_map& block_cache) + QSettings& userprefs, std::unordered_map& block_cache) { userprefs.beginGroup("DAQs"); Event::Object get_devices_event(Event::Type::DAQ_DEVICE_QUERY_EVENT); @@ -653,13 +652,12 @@ void MainWindow::loadDAQSettings( } userprefs.endGroup(); // Digital Output userprefs.endGroup(); // Device name - block_cache[device_id.toUInt()] = tmp_device; + block_cache[device_id.toInt()] = tmp_device; } userprefs.endGroup(); // DAQ } -void MainWindow::saveWidgetSettings( - QSettings& userprefs) +void MainWindow::saveWidgetSettings(QSettings& userprefs) { userprefs.beginGroup("Widgets"); Event::Object loaded_plugins_query(Event::Type::PLUGIN_LIST_QUERY_EVENT); @@ -669,12 +667,14 @@ void MainWindow::saveWidgetSettings( int non_component_plugin_id = -1; int plugin_id = 0; for (const auto& entry : plugin_list) { - // We don't expect the number of blocks in the system to exceed the max positive value of int - // In 2025 you would run out of memory first before that happens. - // Additionally we are saving negative values to indicate to the workspace loading system - // that the negative valued plugins do not contain a component and therefore should be left - // out of the connection loading step. - plugin_id = entry->hasComponent() ? static_cast(entry->getID()) : non_component_plugin_id--; + // We don't expect the number of blocks in the system to exceed the max + // positive value of int In 2025 you would run out of memory first before + // that happens. Additionally we are saving negative values to indicate to + // the workspace loading system that the negative valued plugins do not + // contain a component and therefore should be left out of the connection + // loading step. + plugin_id = entry->hasComponent() ? static_cast(entry->getID()) + : non_component_plugin_id--; userprefs.beginGroup(QString::number(plugin_id)); userprefs.setValue("library", QString::fromStdString(entry->getLibrary())); userprefs.beginGroup("standardParams"); @@ -689,7 +689,7 @@ void MainWindow::saveWidgetSettings( } void MainWindow::loadWidgetSettings( - QSettings& userprefs, std::unordered_map& block_cache) + QSettings& userprefs, std::unordered_map& block_cache) { userprefs.beginGroup("Widgets"); QString plugin_name; @@ -707,13 +707,12 @@ void MainWindow::loadWidgetSettings( plugin_ptr->loadCustomParameterSettings(userprefs); userprefs.endGroup(); // customParams userprefs.endGroup(); // plugin_instance_id - block_cache[plugin_instance_id.toUInt()] = plugin_ptr->getBlock(); + block_cache[plugin_instance_id.toInt()] = plugin_ptr->getBlock(); } userprefs.endGroup(); // Widgets } -void MainWindow::saveConnectionSettings( - QSettings& userprefs) +void MainWindow::saveConnectionSettings(QSettings& userprefs) { Event::Object all_connections_event( Event::Type::IO_ALL_CONNECTIONS_QUERY_EVENT); @@ -734,11 +733,11 @@ void MainWindow::saveConnectionSettings( } // We need to match the block id with the id already stored in the // settings file when saveWidgets was called. - id_connection.src_id = conn.src->getID(); - id_connection.src_direction = conn.src_port_type; - id_connection.src_port = conn.src_port; - id_connection.dest_id = conn.dest->getID(); - id_connection.dest_port = conn.dest_port; + id_connection.src_id = static_cast(conn.src->getID()); + id_connection.src_direction = static_cast(conn.src_port_type); + id_connection.src_port = static_cast(conn.src_port); + id_connection.dest_id = static_cast(conn.dest->getID()); + id_connection.dest_port = static_cast(conn.dest_port); userprefs.setValue(QString::number(connection_count++), QVariant::fromValue(id_connection)); } @@ -746,7 +745,7 @@ void MainWindow::saveConnectionSettings( } void MainWindow::loadConnectionSettings( - QSettings& userprefs, std::unordered_map& block_cache) + QSettings& userprefs, std::unordered_map& block_cache) { ///////////////////// Load connections ///////////////////////// RT::block_connection_t connection; @@ -766,9 +765,9 @@ void MainWindow::loadConnectionSettings( connection.src = block_cache[id_connection.src_id]; connection.src_port_type = static_cast(id_connection.src_direction); - connection.src_port = id_connection.src_port; + connection.src_port = static_cast(id_connection.src_port); connection.dest = block_cache[id_connection.dest_id]; - connection.dest_port = id_connection.dest_port; + connection.dest_port = static_cast(id_connection.dest_port); connection_events.emplace_back(Event::Type::IO_LINK_INSERT_EVENT); connection_events.back().setParam("connection", std::any(connection)); } @@ -797,7 +796,7 @@ void MainWindow::loadSettings() mdiArea->closeAllSubWindows(); const auto workspace_filename = userprefs.value(profile).toString(); QSettings workspaceprefs(workspace_filename, QSettings::IniFormat); - std::unordered_map blocks; + std::unordered_map blocks; this->loadPeriodSettings(workspaceprefs); this->loadDAQSettings(workspaceprefs, blocks); diff --git a/src/main_window.hpp b/src/main_window.hpp index f5a1ce05..c8eaf29a 100644 --- a/src/main_window.hpp +++ b/src/main_window.hpp @@ -180,15 +180,15 @@ private slots: inline void saveDAQSettings(QSettings& userprefs); inline void loadDAQSettings( QSettings& userprefs, - std::unordered_map& block_cache); + std::unordered_map& block_cache); inline void saveWidgetSettings(QSettings& userprefs); inline void loadWidgetSettings( QSettings& userprefs, - std::unordered_map& block_cache); + std::unordered_map& block_cache); inline void saveConnectionSettings(QSettings& userprefs); inline void loadConnectionSettings( QSettings& userprefs, - std::unordered_map& block_cache); + std::unordered_map& block_cache); Event::Manager* event_manager; QMdiArea* mdiArea = nullptr; QList subWindows; From 7972846822994025431ff8a2a0eb6d486060b018 Mon Sep 17 00:00:00 2001 From: "Ivan F. Valerio" Date: Tue, 6 May 2025 00:23:04 -0400 Subject: [PATCH 11/12] Format-fix pass --- src/main_window.hpp | 11 ++++------- src/nidaq_driver.cpp | 7 ++++--- src/widgets.hpp | 2 +- src/workspace.cpp | 4 ++-- 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/main_window.hpp b/src/main_window.hpp index c8eaf29a..16ccd3ce 100644 --- a/src/main_window.hpp +++ b/src/main_window.hpp @@ -178,17 +178,14 @@ private slots: inline void savePeriodSettings(QSettings& userprefs); inline void loadPeriodSettings(QSettings& userprefs); inline void saveDAQSettings(QSettings& userprefs); - inline void loadDAQSettings( - QSettings& userprefs, - std::unordered_map& block_cache); + inline void loadDAQSettings(QSettings& userprefs, + std::unordered_map& block_cache); inline void saveWidgetSettings(QSettings& userprefs); inline void loadWidgetSettings( - QSettings& userprefs, - std::unordered_map& block_cache); + QSettings& userprefs, std::unordered_map& block_cache); inline void saveConnectionSettings(QSettings& userprefs); inline void loadConnectionSettings( - QSettings& userprefs, - std::unordered_map& block_cache); + QSettings& userprefs, std::unordered_map& block_cache); Event::Manager* event_manager; QMdiArea* mdiArea = nullptr; QList subWindows; diff --git a/src/nidaq_driver.cpp b/src/nidaq_driver.cpp index 911229a6..d138642f 100644 --- a/src/nidaq_driver.cpp +++ b/src/nidaq_driver.cpp @@ -255,13 +255,14 @@ int32_t physical_channel_t::addToTask(TaskHandle task_handle) const DAQmx_Val_Volts, nullptr); } else if (units == "amps") { - DAQmxGetAICurrentShuntLoc(task_handle, name.c_str(), &shunt_resistance_location); + DAQmxGetAICurrentShuntLoc( + task_handle, name.c_str(), &shunt_resistance_location); err = DAQmxCreateAICurrentChan(task_handle, name.c_str(), nullptr, DAQmx_Val_Diff, - min/1000.0, - max/1000.0, + min / 1000.0, + max / 1000.0, DAQmx_Val_Amps, shunt_resistance_location, 249.0, diff --git a/src/widgets.hpp b/src/widgets.hpp index 6d956533..6f055cc6 100644 --- a/src/widgets.hpp +++ b/src/widgets.hpp @@ -719,7 +719,7 @@ class Plugin : public Event::Handler * \return IO::Block pointer to the internal structure */ IO::Block* getBlock() { return plugin_component.get(); } - const IO::Block* getBlock() const { return plugin_component.get(); } + const IO::Block* getBlock() const { return plugin_component.get(); } protected: Widgets::Component* getComponent(); diff --git a/src/workspace.cpp b/src/workspace.cpp index 27d25307..097c6b5d 100644 --- a/src/workspace.cpp +++ b/src/workspace.cpp @@ -75,7 +75,7 @@ Workspace::Manager::Manager(Event::Manager* ev_manager) ERROR_MSG("Unable to load GSC aio168 rtxi driver"); } } - #ifdef DEBUG_DRIVERS +#ifdef DEBUG_DRIVERS const std::string fake_driver_name = "librtxifakedriver.so"; if (bin_dir.exists(QString::fromStdString(fake_driver_name))) { try { @@ -85,7 +85,7 @@ Workspace::Manager::Manager(Event::Manager* ev_manager) ERROR_MSG("Unable to load fake rtxi driver"); } } - #endif +#endif } Workspace::Manager::~Manager() From 749154410f66a24e32f2e0ab7577c5f1e1a80fb1 Mon Sep 17 00:00:00 2001 From: "Ivan F. Valerio" Date: Tue, 6 May 2025 00:47:23 -0400 Subject: [PATCH 12/12] Spell-fix pass --- .codespellrc | 4 ++-- docs/Doxyfile | 2 +- docs/Doxyfile.in | 2 +- src/rtos.hpp | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.codespellrc b/.codespellrc index f8b4cc8b..15e1566d 100644 --- a/.codespellrc +++ b/.codespellrc @@ -2,6 +2,6 @@ builtin = clear,rare,en-GB_to_en-US,names,informal,code check-filenames = check-hidden = -ignore-words-list = weill,sinc,mut,numer,uint,stdio,ws -skip = */.git,*/build,*/prefix,./scripts/all_plugins,./libs +ignore-words-list = weill,sinc,mut,numer,uint,stdio,ws,indx +skip = */.git,*/build,*/prefix,./scripts/all_plugins,./libs,.venv quiet-level = 2 diff --git a/docs/Doxyfile b/docs/Doxyfile index 0659c10b..e0316a80 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -559,7 +559,7 @@ INTERNAL_DOCS = NO # names in lower-case letters. If set to YES, upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows -# (including Cygwin) ands Mac users are advised to set this option to NO. +# (including Cygwin) and Mac users are advised to set this option to NO. # The default value is: system dependent. CASE_SENSE_NAMES = NO diff --git a/docs/Doxyfile.in b/docs/Doxyfile.in index d1314280..c123e67e 100644 --- a/docs/Doxyfile.in +++ b/docs/Doxyfile.in @@ -559,7 +559,7 @@ INTERNAL_DOCS = NO # names in lower-case letters. If set to YES, upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows -# (including Cygwin) ands Mac users are advised to set this option to NO. +# (including Cygwin) and Mac users are advised to set this option to NO. # The default value is: system dependent. CASE_SENSE_NAMES = NO diff --git a/src/rtos.hpp b/src/rtos.hpp index bbce0416..c2d45cdc 100644 --- a/src/rtos.hpp +++ b/src/rtos.hpp @@ -82,7 +82,7 @@ int setPeriod(Task* task, int64_t period); * real-time loop. Available inside execute(). * * \returns Period of the realtime thread the calling function is - * runninng from, -1 otherwise. + * running from, -1 otherwise. * * \sa RT::OS::sleepTimestep */