diff --git a/plugins/data_recorder/data_recorder.cpp b/plugins/data_recorder/data_recorder.cpp index 6f19e896..5e4c9f04 100644 --- a/plugins/data_recorder/data_recorder.cpp +++ b/plugins/data_recorder/data_recorder.cpp @@ -39,6 +39,7 @@ #include "debug.hpp" #include "fifo.hpp" #include "rtos.hpp" +#include "userprefs/userprefs.hpp" DataRecorder::Panel::Panel(QMainWindow* mwindow, Event::Manager* ev_manager) : Widgets::Panel( @@ -47,6 +48,7 @@ DataRecorder::Panel::Panel(QMainWindow* mwindow, Event::Manager* ev_manager) , blockList(new QComboBox) , channelList(new QComboBox) , typeList(new QComboBox) + , timeTagType(new QComboBox()) , selectionBox(new QListWidget) , recordStatus(new QLabel) , downsampleSpin(new QSpinBox(this)) @@ -130,10 +132,20 @@ DataRecorder::Panel::Panel(QMainWindow* mwindow, Event::Manager* ev_manager) auto* stampLayout = new QHBoxLayout; // Add timestamp elements - stampLayout->addWidget(timeStampEdit); addTag = new QPushButton(tr("Tag")); stampLayout->addWidget(addTag); + + auto* timeTagLabel = new QLabel(tr("Time Tag Type:")); + stampLayout->addWidget(timeTagLabel); + timeTagType->addItem("Index", TIME_TAG_TYPE::INDEX); + timeTagType->addItem("Time Stamp", TIME_TAG_TYPE::TIME); + timeTagType->addItem("No Tag", TIME_TAG_TYPE::NONE); + stampLayout->addWidget(timeTagType); + QObject::connect(timeTagType, + QOverload::of(&QComboBox::activated), + this, + &DataRecorder::Panel::setTimeTagType); QObject::connect( addTag, &QPushButton::released, this, &DataRecorder::Panel::addNewTag); @@ -333,12 +345,13 @@ void DataRecorder::Panel::changeDataFile() fileDialog.setWindowTitle("Select Data File"); QSettings userprefs; - QSettings::setPath(QSettings::NativeFormat, - QSettings::SystemScope, - "/usr/local/share/rtxi/"); - fileDialog.setDirectory( - userprefs.value("/dirs/data", getenv("HOME")).toString()); // NOLINT + userprefs.beginGroup("settings"); + fileDialog.setDirectory(userprefs + .value(QString::fromStdString(std::string( + UserPrefs::HDF5_SAVE_LOCATION_KEY))) + .toString()); // NOLINT + userprefs.endGroup(); QStringList filterList; filterList.push_back("HDF5 files (*.h5)"); filterList.push_back("All files (*.*)"); @@ -362,9 +375,6 @@ void DataRecorder::Panel::changeDataFile() filename += ".h5"; } - // Write this directory to the user prefs as most recently used - userprefs.setValue("/dirs/data", fileDialog.directory().path()); - auto* hplugin = dynamic_cast(this->getHostPlugin()); hplugin->change_file(filename.toStdString()); this->fileNameEdit->setText(QString(hplugin->getOpenFilename().c_str())); @@ -470,6 +480,7 @@ void DataRecorder::Panel::startRecordClicked() this->starting_record_time = QTime::currentTime(); this->trialNum->setNum(hplugin->getTrialCount()); this->trialLength->setText("Recording..."); + this->timeTagType->setDisabled(true); } } @@ -487,6 +498,7 @@ void DataRecorder::Panel::stopRecordClicked() this->fileSize->setNum( static_cast(QFile(fileNameEdit->text()).size()) / (1024.0 * 1024.0)); + this->timeTagType->setDisabled(false); } } @@ -527,6 +539,17 @@ void DataRecorder::Panel::syncEnableRecordingButtons(const QString& /*unused*/) this->recordStatus->setText(ready ? "Ready" : "Not ready"); } +void DataRecorder::Panel::setTimeTagType() +{ + this->time_type = + static_cast(timeTagType->currentData().toInt()); +} + +int DataRecorder::Panel::getTimeTagType() const +{ + return static_cast(this->time_type); +} + DataRecorder::Plugin::Plugin(Event::Manager* ev_manager) : Widgets::Plugin(ev_manager, std::string(DataRecorder::MODULE_NAME)) , recording(false) @@ -592,10 +615,12 @@ void DataRecorder::Plugin::startRecording() this->append_new_trial(); const Event::Type event_type = Event::Type::RT_THREAD_UNPAUSE_EVENT; std::vector start_recording_event; + m_channel_data_counts.clear(); for (auto& rec_channel : this->m_recording_channels_list) { start_recording_event.emplace_back(event_type); start_recording_event.back().setParam( "thread", static_cast(rec_channel.component.get())); + m_channel_data_counts.push_back(0); } this->getEventManager()->postEvent(start_recording_event); this->recording.store(true); @@ -677,15 +702,32 @@ void DataRecorder::Plugin::open_trial_group() H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); - for (auto& channel : this->m_recording_channels_list) { - compression_property = H5Pcreate(H5P_DATASET_CREATE); - H5Pset_deflate(compression_property, 7); - channel.hdf5_data_handle = - H5PTcreate(this->hdf5_handles.sync_group_handle, - channel.channel.name.c_str(), - this->hdf5_handles.channel_datatype_handle, - this->m_data_chunk_size, - compression_property); + const int data_type = + dynamic_cast(this->getPanel())->getTimeTagType(); + m_channel_data_counts.clear(); + if (data_type == 2) { + for (auto& channel : this->m_recording_channels_list) { + compression_property = H5Pcreate(H5P_DATASET_CREATE); + H5Pset_deflate(compression_property, 7); + channel.hdf5_data_handle = + H5PTcreate(this->hdf5_handles.sync_group_handle, + channel.channel.name.c_str(), + H5T_IEEE_F64LE, + this->m_data_chunk_size, + compression_property); + } + } else { + for (auto& channel : this->m_recording_channels_list) { + m_channel_data_counts.push_back(0); + compression_property = H5Pcreate(H5P_DATASET_CREATE); + H5Pset_deflate(compression_property, 7); + channel.hdf5_data_handle = + H5PTcreate(this->hdf5_handles.sync_group_handle, + channel.channel.name.c_str(), + this->hdf5_handles.channel_datatype_handle, + this->m_data_chunk_size, + compression_property); + } } } @@ -887,19 +929,64 @@ void DataRecorder::Plugin::process_data_worker() return; } std::vector data_buffer(this->m_data_chunk_size); + std::vector data_buffer_doubles; + data_buffer_doubles.reserve(this->m_data_chunk_size); const size_t packet_byte_size = sizeof(DataRecorder::data_token_t); int64_t read_bytes = 0; size_t packet_count = 0; + size_t channel_id = 0; + const int time_type = + dynamic_cast(this->getPanel())->getTimeTagType(); const std::shared_lock lk(this->m_channels_list_mut); - for (auto& channel : this->m_recording_channels_list) { - while (read_bytes = channel.channel.data_source->read( - data_buffer.data(), packet_byte_size * data_buffer.size()), - read_bytes > 0) - { - packet_count = static_cast(read_bytes) / packet_byte_size; - DataRecorder::Plugin::save_data( - channel.hdf5_data_handle, data_buffer, packet_count); - } + switch (time_type) { + case 0: + + for (auto& channel : this->m_recording_channels_list) { + while (read_bytes = channel.channel.data_source->read( + data_buffer.data(), packet_byte_size * data_buffer.size()), + read_bytes > 0) + { + packet_count = static_cast(read_bytes) / packet_byte_size; + DataRecorder::Plugin::save_data( + channel.hdf5_data_handle, data_buffer, packet_count); + } + } + break; + case 1: + + for (auto& channel : this->m_recording_channels_list) { + while (read_bytes = channel.channel.data_source->read( + data_buffer.data(), packet_byte_size * data_buffer.size()), + read_bytes > 0) + { + for (auto& token : data_buffer) { + token.time = m_channel_data_counts[channel_id++]++; + } + packet_count = static_cast(read_bytes) / packet_byte_size; + DataRecorder::Plugin::save_data( + channel.hdf5_data_handle, data_buffer, packet_count); + } + } + break; + case 2: + + for (auto& channel : this->m_recording_channels_list) { + while (read_bytes = channel.channel.data_source->read( + data_buffer.data(), packet_byte_size * data_buffer.size()), + read_bytes > 0) + { + for (auto& token : data_buffer) { + data_buffer_doubles.push_back(token.value); + } + packet_count = static_cast(read_bytes) / packet_byte_size; + DataRecorder::Plugin::save_data( + channel.hdf5_data_handle, data_buffer_doubles, packet_count); + } + } + break; + default: + ERROR_MSG("DataRecorder::Plugin::process_data_worker : Bad time tagging type detected. Unable to save data to hdf5 file"); + break; } } @@ -915,6 +1002,17 @@ void DataRecorder::Plugin::save_data( } } +void DataRecorder::Plugin::save_data(hid_t data_id, + const std::vector& data, + size_t packet_count) +{ + const herr_t err = + H5PTappend(data_id, static_cast(packet_count), data.data()); + if (err < 0) { + ERROR_MSG("Unable to write data into hdf5 file!"); + } +} + DataRecorder::Component::Component(Widgets::Plugin* hplugin, const std::string& probe_name) : Widgets::Component(hplugin, diff --git a/plugins/data_recorder/data_recorder.hpp b/plugins/data_recorder/data_recorder.hpp index d9e2364d..5c02808f 100644 --- a/plugins/data_recorder/data_recorder.hpp +++ b/plugins/data_recorder/data_recorder.hpp @@ -25,9 +25,9 @@ #include +#include "fifo.hpp" #include "io.hpp" #include "widgets.hpp" -#include "fifo.hpp" class QComboBox; class QListWidget; @@ -93,6 +93,13 @@ class Panel : public Widgets::Panel { Q_OBJECT + enum TIME_TAG_TYPE + { + INDEX = 0, + TIME, + NONE + }; + public: Panel(const Panel&) = delete; Panel(Panel&&) = delete; @@ -101,6 +108,8 @@ class Panel : public Widgets::Panel Panel(QMainWindow* mwindow, Event::Manager* ev_manager); ~Panel() override = default; + int getTimeTagType() const; + signals: void updateBlockInfo(); void record_signal(bool record); @@ -121,11 +130,13 @@ private slots: void addNewTag(); void processData(); void syncEnableRecordingButtons(const QString& /*unused*/); + void setTimeTagType(); private: size_t m_buffer_size = DEFAULT_BUFFER_SIZE; size_t downsample_rate {1}; std::vector dataTags; + int time_type=TIME_TAG_TYPE::INDEX; QGroupBox* channelGroup = nullptr; QGroupBox* stampGroup = nullptr; @@ -137,6 +148,7 @@ private slots: QComboBox* blockList = nullptr; QComboBox* channelList = nullptr; QComboBox* typeList = nullptr; + QComboBox* timeTagType = nullptr; QListWidget* selectionBox = nullptr; QLabel* recordStatus = nullptr; QPushButton* addRecorderButton = nullptr; @@ -200,6 +212,9 @@ class Plugin : public Widgets::Plugin static void save_data(hid_t data_id, const std::vector& data, size_t packet_count); + static void save_data(hid_t data_id, + const std::vector& data, + size_t packet_count); hsize_t m_data_chunk_size = static_cast(1000); int m_compression_factor = 5; struct hdf5_handles @@ -231,6 +246,7 @@ class Plugin : public Widgets::Plugin int trial_count = 0; std::string hdf5_filename; std::vector m_recording_channels_list; + std::vector m_channel_data_counts; std::shared_mutex m_channels_list_mut; std::atomic open_file = false; }; // class Plugin diff --git a/plugins/performance_measurement/performance_measurement.cpp b/plugins/performance_measurement/performance_measurement.cpp index f81efe01..82baaaf8 100644 --- a/plugins/performance_measurement/performance_measurement.cpp +++ b/plugins/performance_measurement/performance_measurement.cpp @@ -105,7 +105,7 @@ PerformanceMeasurement::Panel::Panel(const std::string& mod_name, PerformanceMeasurement::Component::Component(Widgets::Plugin* hplugin) : Widgets::Component(hplugin, std::string(MODULE_NAME), - std::vector(), + PerformanceMeasurement::get_default_channels(), PerformanceMeasurement::get_default_vars()) { if (RT::OS::getFifo(this->fifo, @@ -137,6 +137,13 @@ void PerformanceMeasurement::Component::execute() switch (this->getState()) { case RT::State::EXEC: + writeoutput(0, stats.duration); + writeoutput(1, stats.timestep); + writeoutput(2, stats.latency); + writeoutput(3, stats.max_timestep); + writeoutput(4, stats.max_duration); + writeoutput(5, stats.max_latency); + writeoutput(6, stats.jitter); this->fifo->writeRT(&this->stats, sizeof(PerformanceMeasurement::performance_stats_t)); break; diff --git a/plugins/performance_measurement/performance_measurement.hpp b/plugins/performance_measurement/performance_measurement.hpp index 5d74626b..a511f573 100644 --- a/plugins/performance_measurement/performance_measurement.hpp +++ b/plugins/performance_measurement/performance_measurement.hpp @@ -24,9 +24,10 @@ #include "math/runningstat.h" #include "widgets.hpp" -namespace RT::OS{ +namespace RT::OS +{ class Fifo; -} // namespace RT::OS +} // namespace RT::OS class QLineEdit; @@ -36,22 +37,39 @@ namespace PerformanceMeasurement constexpr std::string_view MODULE_NAME = "RT Benchmarks"; inline std::vector get_default_vars() -{ - return - { - { - } - }; +{ + return {{}}; +} + +inline std::vector get_default_channels() +{ + return { + {"Duration", + "Real-Time measurement of time elapsed time (ns) for calculations each period", + IO::OUTPUT}, + {"Time Step", "Real-Time measurement of system period", IO::OUTPUT}, + {"Latency", "Time between intended wake-up and real wake-up", IO::OUTPUT}, + {"Max Duration", + "Maximum computation time measured since last reset", + IO::OUTPUT}, + {"Max Time Step", + "Maximum realtime period measured since last reset", + IO::OUTPUT}, + {"Max Latency", "Maximum latency measured since last reset", IO::OUTPUT}, + {"Jitter", + "Standard Deviation of the real-time period measured since last reset", + IO::OUTPUT}}; } -struct performance_stats_t{ - double duration=0.0; - double timestep=0.0; - double latency=0.0; - double max_duration=0.0; - double max_timestep=0.0; - double max_latency=0.0; - double jitter=0.0; +struct performance_stats_t +{ + double duration = 0.0; + double timestep = 0.0; + double latency = 0.0; + double max_duration = 0.0; + double max_timestep = 0.0; + double max_latency = 0.0; + double jitter = 0.0; }; class Plugin : public Widgets::Plugin @@ -59,28 +77,29 @@ class Plugin : public Widgets::Plugin public: explicit Plugin(Event::Manager* ev_manager); performance_stats_t getSampleStat(); + private: RT::OS::Fifo* component_fifo; }; // class Plugin class Component : public Widgets::Component { -public: - explicit Component(Widgets::Plugin* hplugin); +public: + explicit Component(Widgets::Plugin* hplugin); void setTickPointers(int64_t* s_ticks, int64_t* e_ticks); void execute() override; - RT::OS::Fifo* getFIfoPtr(){ return this->fifo.get(); } + RT::OS::Fifo* getFIfoPtr() { return this->fifo.get(); } private: performance_stats_t stats; - //RunningStat timestepStat; + // RunningStat timestepStat; RunningStat latencyStat; - int64_t *start_ticks=nullptr; // only accessed in rt - int64_t *end_ticks=nullptr; // only accessed in rt - int64_t last_start_ticks=0; + int64_t* start_ticks = nullptr; // only accessed in rt + int64_t* end_ticks = nullptr; // only accessed in rt + int64_t last_start_ticks = 0; std::unique_ptr fifo; }; @@ -89,20 +108,21 @@ class Panel : public Widgets::Panel Q_OBJECT public: - Panel(const std::string& mod_name, QMainWindow* mwindow, Event::Manager* ev_manager); + Panel(const std::string& mod_name, + QMainWindow* mwindow, + Event::Manager* ev_manager); public slots: /*! * Starts the statistics over */ void reset(); - //void resetMaxTimeStep(); + // void resetMaxTimeStep(); /*! * Updates the GUI with the latest values */ void refresh() override; - private: QLineEdit* durationEdit; QLineEdit* timestepEdit; @@ -114,10 +134,12 @@ public slots: std::unique_ptr createRTXIPlugin(Event::Manager* ev_manager); -Widgets::Panel* createRTXIPanel(QMainWindow* main_window, Event::Manager* ev_manager); +Widgets::Panel* createRTXIPanel(QMainWindow* main_window, + Event::Manager* ev_manager); -std::unique_ptr createRTXIComponent(Widgets::Plugin* host_plugin); +std::unique_ptr createRTXIComponent( + Widgets::Plugin* host_plugin); Widgets::FactoryMethods getFactories(); -} // namespace PerformanceMeasurement +} // namespace PerformanceMeasurement #endif /* PERFORMANCE_MEASUREMENT_H */ diff --git a/plugins/userprefs/userprefs.cpp b/plugins/userprefs/userprefs.cpp index cd4346ee..55b1e101 100644 --- a/plugins/userprefs/userprefs.cpp +++ b/plugins/userprefs/userprefs.cpp @@ -1,20 +1,20 @@ /* - The Real-Time eXperiment Interface (RTXI) - Copyright (C) 2011 Georgia Institute of Technology, University of Utah, - Will Cornell Medical College +The Real-Time eXperiment Interface (RTXI) +Copyright (C) 2011 Georgia Institute of Technology, University of Utah, +Will Cornell Medical College - 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 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. +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 . +You should have received a copy of the GNU General Public License +along with this program. If not, see . */ @@ -42,18 +42,12 @@ UserPrefs::Panel::Panel(QMainWindow* mwindow, Event::Manager* ev_manager) : Widgets::Panel(std::string(UserPrefs::MODULE_NAME), mwindow, ev_manager) , status(new QLabel) , dirGroup(new QGroupBox) - , HDF(new QGroupBox) , buttons(new QGroupBox) , settingsDirEdit(new QLineEdit(dirGroup)) , dataDirEdit(new QLineEdit(dirGroup)) - , HDFBufferEdit(new QLineEdit(HDF)) { // Preferences structure - const QSettings user_preferences; - QSettings::setPath(QSettings::NativeFormat, - QSettings::SystemScope, - "/usr/local/share/rtxi/"); - + userprefs.beginGroup("settings"); // Main layout auto* box_layout = new QVBoxLayout; @@ -61,9 +55,11 @@ UserPrefs::Panel::Panel(QMainWindow* mwindow, Event::Manager* ev_manager) auto* dirLayout = new QGridLayout; // Create elements for directory paths - const QString env_var = QString::fromLocal8Bit(qgetenv("HOME")); - settingsDirEdit->setText( - user_preferences.value("/dirs/setfiles", env_var).toString()); + const QString workspace_dir = + QString::fromStdString(std::string(WORKSPACE_SAVE_LOCATION_KEY)); + const QString hdf5_dir = + QString::fromStdString(std::string(HDF5_SAVE_LOCATION_KEY)); + settingsDirEdit->setText(userprefs.value(workspace_dir).toString()); dirLayout->addWidget( new QLabel(tr("Default settings directory:")), 0, 0, 1, 1); dirLayout->addWidget(settingsDirEdit, 0, 1, 1, 1); @@ -74,8 +70,7 @@ UserPrefs::Panel::Panel(QMainWindow* mwindow, Event::Manager* ev_manager) this, &UserPrefs::Panel::chooseSettingsDir); - dataDirEdit->setText( - user_preferences.value("/dirs/data", env_var).toString()); + dataDirEdit->setText(userprefs.value(hdf5_dir).toString()); dirLayout->addWidget( new QLabel(tr("Default HDF5 data directory:")), 1, 0, 1, 1); dirLayout->addWidget(dataDirEdit, 1, 1, 1, 1); @@ -89,20 +84,7 @@ UserPrefs::Panel::Panel(QMainWindow* mwindow, Event::Manager* ev_manager) // Attach layout to group dirGroup->setLayout(dirLayout); - // Create new child widget and layout - auto* hdfLayout = new QGridLayout; - - // Create elements for child widget - hdfLayout->addWidget( - new QLabel(tr("HDF Data Recorder Buffer Size (MB):")), 0, 0, 1, 1); - hdfLayout->addWidget(HDFBufferEdit, 0, 1, 1, 1); - HDFBufferEdit->setText( - QString::number(user_preferences.value("/system/HDFbuffer", 10).toInt())); - - // Attach child to parent - HDF->setLayout(hdfLayout); - - // Create new child widget + // Create new child widget auto* buttonLayout = new QHBoxLayout; // Create elements for child widget @@ -132,39 +114,38 @@ UserPrefs::Panel::Panel(QMainWindow* mwindow, Event::Manager* ev_manager) // Attach child widget to parent widget box_layout->addWidget(dirGroup); - box_layout->addWidget(HDF); box_layout->addWidget(buttons); // Attach layout to widget setLayout(box_layout); setWindowTitle(QString(this->getName().c_str())); + userprefs.endGroup(); // Set layout to Mdi this->getMdiWindow()->setFixedSize(500, this->sizeHint().height() + 50); } void UserPrefs::Panel::reset() { + userprefs.beginGroup("settings"); const QString env_var = QString::fromLocal8Bit(qgetenv("HOME")); settingsDirEdit->setText(env_var); dataDirEdit->setText(env_var); - HDFBufferEdit->setText(QString::number(10)); - userprefs.setValue("/dirs/setfiles", settingsDirEdit->text()); - userprefs.setValue("/dirs/data", dataDirEdit->text()); - bool ok = false; - const QString buffer = HDFBufferEdit->text(); - userprefs.setValue("/system/HDFbuffer", buffer.toInt(&ok)); status->setText("Preferences \nreset"); + userprefs.endGroup(); } void UserPrefs::Panel::apply() { - userprefs.setValue("/dirs/setfiles", settingsDirEdit->text()); - userprefs.setValue("/dirs/data", dataDirEdit->text()); - bool ok = false; - const QString buffer = HDFBufferEdit->text(); - userprefs.setValue("/system/HDFbuffer", buffer.toInt(&ok)); + userprefs.beginGroup("settings"); + userprefs.setValue(QString::fromStdString( + std::string(UserPrefs::WORKSPACE_SAVE_LOCATION_KEY)), + settingsDirEdit->text()); + userprefs.setValue( + QString::fromStdString(std::string(UserPrefs::HDF5_SAVE_LOCATION_KEY)), + dataDirEdit->text()); status->setText("Preferences \napplied"); + userprefs.endGroup(); } void UserPrefs::Panel::chooseSettingsDir() @@ -173,18 +154,17 @@ void UserPrefs::Panel::chooseSettingsDir() const QString dir_name = QFileDialog::getExistingDirectory( this, tr("Choose default directory for settings files"), - userprefs.value("/dirs/setfiles", env_var).toString(), + settingsDirEdit->text(), QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); settingsDirEdit->setText(dir_name); } void UserPrefs::Panel::chooseDataDir() { - const QString env_var = QString::fromLocal8Bit(qgetenv("HOME")); const QString dir_name = QFileDialog::getExistingDirectory( this, tr("Choose default directory for HDF5 data files"), - userprefs.value("/dirs/data", env_var).toString(), + dataDirEdit->text(), QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); dataDirEdit->setText(dir_name); } diff --git a/plugins/userprefs/userprefs.hpp b/plugins/userprefs/userprefs.hpp index ccaef8c0..bfd398dc 100644 --- a/plugins/userprefs/userprefs.hpp +++ b/plugins/userprefs/userprefs.hpp @@ -29,6 +29,8 @@ namespace UserPrefs { constexpr std::string_view MODULE_NAME = "User Preferences"; +constexpr std::string_view WORKSPACE_SAVE_LOCATION_KEY = "wokrspace_dir"; +constexpr std::string_view HDF5_SAVE_LOCATION_KEY = "hdf5_dir"; class Plugin : public Widgets::Plugin { @@ -56,12 +58,10 @@ public slots: QSettings userprefs; QGroupBox* dirGroup = nullptr; - QGroupBox* HDF = nullptr; QGroupBox* buttons = nullptr; QLineEdit* settingsDirEdit = nullptr; // directory for settings files QLineEdit* dataDirEdit = nullptr; // directory of most recent data file - QLineEdit* HDFBufferEdit = nullptr; // buffer size for HDF Data Recorder }; // class Panel std::unique_ptr createRTXIPlugin(Event::Manager* ev_manager); diff --git a/src/main_window.cpp b/src/main_window.cpp index 40e30332..293db33d 100644 --- a/src/main_window.cpp +++ b/src/main_window.cpp @@ -399,6 +399,22 @@ void MainWindow::loadWindow() showMaximized(); } userprefs.endGroup(); + userprefs.beginGroup("settings"); + const auto workspace_dir_key = QString::fromStdString( + std::string(UserPrefs::WORKSPACE_SAVE_LOCATION_KEY)); + const auto data_dir_key = + QString::fromStdString(std::string(UserPrefs::HDF5_SAVE_LOCATION_KEY)); + auto workspace_dir = userprefs.value(workspace_dir_key).toString(); + auto data_dir = userprefs.value(data_dir_key).toString(); + const QString env_var = QString::fromLocal8Bit(qgetenv("HOME")); + if (workspace_dir == "") { + userprefs.setValue(workspace_dir_key, env_var); + } + if (data_dir == "") { + userprefs.setValue(data_dir_key, env_var); + } + userprefs.endGroup(); + show(); } @@ -531,7 +547,7 @@ void MainWindow::loadDAQSettings( DAQ::index_t current_channel_id = 0; for (const auto& device_id : userprefs.childGroups()) { userprefs.beginGroup(device_id); - device_name = userprefs.value("name").value(); + device_name = userprefs.value("name").toString(); // NOTE: We need to make sure that the settings we are about to load are // not reloaded for other devices. Therefore we check whether the name // exists in the loaded devices registry and whether we already used @@ -547,7 +563,7 @@ void MainWindow::loadDAQSettings( block_cache.end(), [block](const auto& entry) { return entry.second == block; }) - != block_cache.end()); + == block_cache.end()); }); if (iter == devices.end()) { ERROR_MSG("Unable to find DAQ device {} from the list of loaded devices.", @@ -648,8 +664,9 @@ void MainWindow::saveWidgetSettings(QSettings& userprefs) this->event_manager->postEvent(&loaded_plugins_query); const auto plugin_list = std::any_cast>( loaded_plugins_query.getParam("plugins")); + int widget_count = 0; for (const auto& entry : plugin_list) { - userprefs.beginGroup(QString::number(entry->getID())); + userprefs.beginGroup(QString::number(widget_count++)); userprefs.setValue("library", QString::fromStdString(entry->getLibrary())); userprefs.beginGroup("standardParams"); entry->saveParameterSettings(userprefs); @@ -671,7 +688,7 @@ void MainWindow::loadWidgetSettings( std::string event_status; for (const auto& plugin_instance_id : userprefs.childGroups()) { userprefs.beginGroup(plugin_instance_id); - plugin_name = userprefs.value("library").value(); + plugin_name = userprefs.value("library").toString(); this->loadWidget(plugin_name, plugin_ptr); // Load the settings userprefs.beginGroup("standardParams"); @@ -754,7 +771,7 @@ void MainWindow::loadSettings() auto* load_settings_dialog = new QInputDialog(this); load_settings_dialog->setInputMode(QInputDialog::TextInput); load_settings_dialog->setComboBoxEditable(false); - load_settings_dialog->setComboBoxItems(userprefs.childGroups()); + load_settings_dialog->setComboBoxItems(userprefs.childKeys()); load_settings_dialog->setLabelText("Profile"); load_settings_dialog->setOkButtonText("Load"); load_settings_dialog->exec(); @@ -766,29 +783,35 @@ void MainWindow::loadSettings() const QString profile = load_settings_dialog->textValue(); mdiArea->closeAllSubWindows(); - userprefs.beginGroup(profile); - + const auto workspace_filename = userprefs.value(profile).toString(); + QSettings workspaceprefs(workspace_filename, QSettings::IniFormat); std::unordered_map blocks; - this->loadPeriodSettings(userprefs); + this->loadPeriodSettings(workspaceprefs); - this->loadDAQSettings(userprefs, blocks); + this->loadDAQSettings(workspaceprefs, blocks); - this->loadWidgetSettings(userprefs, blocks); + this->loadWidgetSettings(workspaceprefs, blocks); - this->loadConnectionSettings(userprefs, blocks); + this->loadConnectionSettings(workspaceprefs, blocks); - userprefs.endGroup(); // profile userprefs.endGroup(); // workspaces } void MainWindow::saveSettings() { QSettings userprefs; + userprefs.beginGroup("settings"); + const auto workspace_dir_loc = + userprefs + .value(QString::fromStdString( + std::string(UserPrefs::WORKSPACE_SAVE_LOCATION_KEY))) + .toString(); + userprefs.endGroup(); userprefs.beginGroup("Workspaces"); auto* save_settings_dialog = new QInputDialog(this); save_settings_dialog->setInputMode(QInputDialog::TextInput); save_settings_dialog->setComboBoxEditable(true); - save_settings_dialog->setComboBoxItems(userprefs.childGroups()); + save_settings_dialog->setComboBoxItems(userprefs.childKeys()); save_settings_dialog->setLabelText("Profile"); save_settings_dialog->setOkButtonText("Save"); save_settings_dialog->exec(); @@ -802,16 +825,18 @@ void MainWindow::saveSettings() if (userprefs.childGroups().contains(profile_name)) { userprefs.remove(profile_name); } + const QString workspace_filename = + workspace_dir_loc + "/" + profile_name + ".ws"; + userprefs.setValue(profile_name, workspace_filename); + // userprefs.beginGroup(profile_name); + QSettings workspaceprefs(workspace_filename, QSettings::IniFormat); + workspaceprefs.clear(); + this->savePeriodSettings(workspaceprefs); - userprefs.beginGroup(profile_name); - - this->savePeriodSettings(userprefs); - - this->saveDAQSettings(userprefs); + this->saveDAQSettings(workspaceprefs); - this->saveWidgetSettings(userprefs); + this->saveWidgetSettings(workspaceprefs); - userprefs.endGroup(); // profile userprefs.endGroup(); // Workspaces } diff --git a/src/widgets.cpp b/src/widgets.cpp index d3272942..e9f9fe63 100644 --- a/src/widgets.cpp +++ b/src/widgets.cpp @@ -325,6 +325,7 @@ void Widgets::Panel::exit() event.setParam("pluginPointer", std::any(static_cast(this->hostPlugin))); this->event_manager->postEvent(&event); + this->hostPlugin = nullptr; } void Widgets::Panel::refresh() diff --git a/src/workspace.cpp b/src/workspace.cpp index 0800a213..b551d65d 100644 --- a/src/workspace.cpp +++ b/src/workspace.cpp @@ -170,47 +170,48 @@ Widgets::Plugin* Workspace::Manager::loadCorePlugin(const std::string& library) this->registerFactories(library, *fact_methods); plugin = this->rtxi_factories_registry[library].createPlugin(event_manager); plugin_ptr = this->registerWidget(std::move(plugin)); + plugin_ptr->setLibrary(library); return plugin_ptr; } // TODO: extract plugin dynamic loading to another class Widgets::Plugin* Workspace::Manager::loadPlugin(const std::string& library) { - const std::string& library_loc = library; Widgets::Plugin* plugin_ptr = nullptr; // if widget factory is already registered then all we have to do is run it - if (this->rtxi_factories_registry.find(library_loc) + if (this->rtxi_factories_registry.find(library) != this->rtxi_factories_registry.end()) { std::unique_ptr plugin = - this->rtxi_factories_registry[library_loc].createPlugin( + this->rtxi_factories_registry[library].createPlugin( this->event_manager); plugin_ptr = this->registerWidget(std::move(plugin)); plugin_ptr->attachComponent( - this->rtxi_factories_registry[library_loc].createComponent(plugin_ptr)); + this->rtxi_factories_registry[library].createComponent(plugin_ptr)); + plugin_ptr->setLibrary(library); return plugin_ptr; } // If it is just a core plugin then handle that elsewhere and return - plugin_ptr = this->loadCorePlugin(library_loc); + plugin_ptr = this->loadCorePlugin(library); if (plugin_ptr != nullptr) { return plugin_ptr; } - if (this->m_plugin_loader->load(library_loc.c_str()) != 0) { - ERROR_MSG("Plugin::load : failed to load {}", library_loc.c_str()); + if (this->m_plugin_loader->load(library.c_str()) != 0) { + ERROR_MSG("Plugin::load : failed to load {}", library.c_str()); return nullptr; } auto gen_fact_methods = this->m_plugin_loader->dlsym( - library_loc.c_str(), "getFactories"); + library.c_str(), "getFactories"); if (gen_fact_methods == nullptr) { ERROR_MSG("Plugin::load : failed to retrieve getFactories symbol"); // If we got here it means we loaded the lirbary but not the symbol. // Let's just unload the library and exit before we regret it. - this->m_plugin_loader->unload(library_loc.c_str()); + this->m_plugin_loader->unload(library.c_str()); return nullptr; } @@ -218,18 +219,18 @@ Widgets::Plugin* Workspace::Manager::loadPlugin(const std::string& library) this->rtxi_factories_registry[library] = *fact_methods; std::unique_ptr plugin = fact_methods->createPlugin(this->event_manager); - plugin->setLibrary(library); if (plugin == nullptr) { ERROR_MSG("Plugin::load : failed to create plugin from library {} ", library); - this->m_plugin_loader->unload(library_loc.c_str()); + this->m_plugin_loader->unload(library.c_str()); return nullptr; } + plugin->setLibrary(library); std::unique_ptr component; try { component = fact_methods->createComponent(plugin.get()); } catch (const std::invalid_argument& e) { - this->m_plugin_loader->unload(library_loc.c_str()); + this->m_plugin_loader->unload(library.c_str()); return nullptr; } plugin->attachComponent(std::move(component));