diff --git a/.github/workflows/cppcmake.yml b/.github/workflows/cppcmake.yml index dc682dd..35ef562 100644 --- a/.github/workflows/cppcmake.yml +++ b/.github/workflows/cppcmake.yml @@ -55,16 +55,6 @@ jobs: with: version: ${{ matrix.config.qt_ver }} arch: ${{ matrix.config.qt_arch }} - - - name: Install boost - uses: MarkusJx/install-boost@v2.4.1 - id: install-boost - with: - # REQUIRED: Specify the required boost version - # A list of supported versions can be found here: - # https://github.com/Markus - boost_version: 1.80.0 - toolset: msvc - name: Configure CMake # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. @@ -77,8 +67,6 @@ jobs: -DCPACK_PACKAGE_DIRECTORY=${PWD}/package \ -DLSL_INSTALL_ROOT=${PWD}/LSL/ \ ${{ matrix.config.cmake_extra }} - env: - BOOST_ROOT: ${{ steps.install-boost.outputs.BOOST_ROOT }} - name: make run: cmake --build build --config ${{env.BUILD_TYPE}} -j --target install @@ -91,6 +79,7 @@ jobs: - name: Upload Artifacts uses: actions/upload-artifact@v3 + if: ${{ ! startsWith(github.ref, 'refs/tags/v') }} with: name: pkg-${{ matrix.config.name }} path: package diff --git a/CMakeLists.txt b/CMakeLists.txt index 3fdc019..75e72e4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.12) -project(SerialPortLSL +project(SerialPort DESCRIPTION "Read byte stream from COM port and emit as an LSL stream" HOMEPAGE_URL "https://github.com/labstreaminglayer/App-SerialPort/" LANGUAGES CXX @@ -30,9 +30,6 @@ set(CMAKE_AUTOUIC ON) find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core) find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core Widgets) -## Boost -find_package(Boost REQUIRED) - ## Threads find_package(Threads REQUIRED) @@ -44,7 +41,7 @@ add_executable(${PROJECT_NAME}) target_sources(${PROJECT_NAME} PRIVATE main.cpp mainwindow.cpp - mainwindow.h + mainwindow.hpp mainwindow.ui ) @@ -52,7 +49,6 @@ target_link_libraries(${PROJECT_NAME} PRIVATE Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Widgets - Boost::headers Threads::Threads LSL::lsl ) diff --git a/README.md b/README.md index 11a4d85..746eba6 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ The program reads a byte stream from a given COM port and emits it as an LSL str # Usage * Start the SerialPort app. You should see a window like the following. - ![serialport.png](serialport.png) +> ![serialport.png](serialport.png) * Make sure that your device is plugged in and that you know its COM port (you can usually check this in the Device Manager). @@ -14,4 +14,4 @@ The program reads a byte stream from a given COM port and emits it as an LSL str * Click the "Link" button. If all goes well you should now have a stream on your lab network that has the name that you entered under Stream Name and type "Raw". Note that you cannot close the app while it is linked. - * For subsequent uses you can save the settings in the GUI via File / Save Configuration. If the app is frequently used with different settings you might can also make a shortcut on the desktop that points to the app and appends to the Target field the snippet `-c name_of_config.cfg`. + * For subsequent uses you can save the settings in the GUI via File / Save Configuration. If the app is frequently used with different settings you might can also make a shortcut on the desktop that points to the app and appends to the Target field the snippet `name_of_config.cfg`. diff --git a/SerialPort.cfg b/SerialPort.cfg new file mode 100644 index 0000000..19beb35 --- /dev/null +++ b/SerialPort.cfg @@ -0,0 +1,2 @@ + +157600032SerialPort4005005010 \ No newline at end of file diff --git a/SerialPortLSL.cfg b/SerialPortLSL.cfg deleted file mode 100644 index 27209e3..0000000 --- a/SerialPortLSL.cfg +++ /dev/null @@ -1,20 +0,0 @@ - - - 1 - 57600 - - - 0 - 32 - SerialPort - - - 4 - 0 - 0 - - - 500 - 50 - 10 - diff --git a/main.cpp b/main.cpp index bf9ed41..d730ed4 100644 --- a/main.cpp +++ b/main.cpp @@ -1,20 +1,9 @@ +#include "mainwindow.hpp" #include -#include "mainwindow.h" -#include - - -int main(int argc, char *argv[]) -{ - // determine the startup config file... - std::string config_file = "SerialPortLSL.cfg"; - for (int k=1;k 1 ? argv[1] : nullptr); w.show(); - - return a.exec(); -} +} \ No newline at end of file diff --git a/mainwindow.cpp b/mainwindow.cpp index 795954f..38850ab 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -1,221 +1,247 @@ -#include "mainwindow.h" +#include "mainwindow.hpp" #include "ui_mainwindow.h" -#include -#include - - -MainWindow::MainWindow(QWidget *parent, const std::string &config_file) : -QMainWindow(parent), -ui(new Ui::MainWindow) -{ - ui->setupUi(this); - - // parse startup config file - load_config(config_file); - - // make GUI connections - QObject::connect(ui->actionQuit, SIGNAL(triggered()), this, SLOT(close())); - QObject::connect(ui->linkButton, SIGNAL(clicked()), this, SLOT(on_link())); - QObject::connect(ui->actionLoad_Configuration, SIGNAL(triggered()), this, SLOT(load_config_dialog())); - QObject::connect(ui->actionSave_Configuration, SIGNAL(triggered()), this, SLOT(save_config_dialog())); +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +MainWindow::MainWindow(QWidget *parent, const char *config_file) + : QMainWindow(parent), ui(new Ui::MainWindow) { + ui->setupUi(this); + connect(ui->actionLoad_Configuration, &QAction::triggered, [this]() { + load_config(QFileDialog::getOpenFileName( + this, "Load Configuration File", "", "Configuration Files (*.cfg)")); + }); + connect(ui->actionSave_Configuration, &QAction::triggered, [this]() { + save_config(QFileDialog::getSaveFileName( + this, "Save Configuration File", "", "Configuration Files (*.cfg)")); + }); + connect(ui->actionQuit, &QAction::triggered, this, &MainWindow::close); + connect(ui->actionAbout, &QAction::triggered, [this]() { + QString infostr = QStringLiteral("LSL library version: ") + + QString::number(lsl::library_version()) + + "\nLSL library info:" + lsl::library_info(); + QMessageBox::about(this, "About this app", infostr); + }); + connect(ui->linkButton, &QPushButton::clicked, this, &MainWindow::toggleRecording); + + + //: At the end of the constructor, we load the supplied config file or find it + //: in one of the default paths + QString cfgfilepath = find_config_file(config_file); + load_config(cfgfilepath); } - -void MainWindow::load_config_dialog() { - QString sel = QFileDialog::getOpenFileName(this,"Load Configuration File","","Configuration Files (*.cfg)"); - if (!sel.isEmpty()) - load_config(sel.toStdString()); +void MainWindow::load_config(const QString &filename) { + QSettings settings(filename, QSettings::Format::IniFormat); + ui->comPort->setValue(settings.value("coresettings.comport", 1).toInt()); + ui->baudRate->setValue(settings.value("coresettings.baudrate", 57600).toInt()); + ui->samplingRate->setValue(settings.value("streamsettings.samplingrate", 0).toInt()); + ui->chunkSize->setValue(settings.value("streamsettings.chunksize", 32).toInt()); + ui->input_name->setText(settings.value("streamsettings.streamname", "SerialPort").toString()); + ui->dataBits->setCurrentIndex(settings.value("miscsettings.databits", 4).toInt()); + ui->parity->setCurrentIndex(settings.value("miscsettings.parity", 0).toInt()); + ui->stopBits->setCurrentIndex(settings.value("miscsettings.stopbits", 0).toInt()); + ui->readIntervalTimeout->setValue( + settings.value("timeoutsettings.readintervaltimeout", 500).toInt()); + ui->readTotalTimeoutConstant->setValue( + settings.value("timeoutsettings.readtotaltimeoutconstant", 50).toInt()); + ui->readTotalTimeoutMultiplier->setValue( + settings.value("timeoutsettings.readtotaltimeoutmultiplier", 10).toInt()); } -void MainWindow::save_config_dialog() { - QString sel = QFileDialog::getSaveFileName(this,"Save Configuration File","","Configuration Files (*.cfg)"); - if (!sel.isEmpty()) - save_config(sel.toStdString()); +//: Save function, same as above +void MainWindow::save_config(const QString &filename) { + QSettings settings(filename, QSettings::Format::IniFormat); + settings.beginGroup("coresettings"); + settings.setValue("comport", ui->comPort->value()); + settings.setValue("baudrate", ui->baudRate->value()); + settings.endGroup(); + settings.beginGroup("streamsettings"); + settings.setValue("samplingrate", ui->samplingRate->value()); + settings.setValue("chunksize", ui->chunkSize->value()); + settings.setValue("streamname", ui->input_name->text()); + settings.endGroup(); + settings.beginGroup("miscsettings"); + settings.setValue("databits", ui->dataBits->currentIndex()); + settings.setValue("parity", ui->parity->currentIndex()); + settings.setValue("stopbits", ui->stopBits->currentIndex()); + settings.endGroup(); + settings.beginGroup("timeoutsettings"); + settings.setValue("readintervaltimeout", ui->readIntervalTimeout->value()); + settings.setValue("readtotaltimeoutconstant", ui->readTotalTimeoutConstant->value()); + settings.setValue("readtotaltimeoutmultiplier", ui->readTotalTimeoutMultiplier->value()); + settings.sync(); } void MainWindow::closeEvent(QCloseEvent *ev) { - if (reader_thread_) - ev->ignore(); + if (reader) { + QMessageBox::warning(this, "Recording still running", "Can't quit while recording"); + ev->ignore(); + } } -void MainWindow::load_config(const std::string &filename) { - using boost::property_tree::ptree; - ptree pt; - - // parse file - try { - read_xml(filename, pt); - } catch(std::exception &e) { - QMessageBox::information(this,"Error",(std::string("Cannot read config file: ")+= e.what()).c_str(),QMessageBox::Ok); - return; - } - - // get config values - try { - ui->comPort->setValue(pt.get("coresettings.comport",1)); - ui->baudRate->setValue(pt.get("coresettings.baudrate",57600)); - ui->samplingRate->setValue(pt.get("streamsettings.samplingrate",0)); - ui->chunkSize->setValue(pt.get("streamsettings.chunksize",32)); - ui->streamName->setText(pt.get("streamsettings.streamname","SerialPort").c_str()); - ui->dataBits->setCurrentIndex(pt.get("miscsettings.databits",4)); - ui->parity->setCurrentIndex(pt.get("miscsettings.parity",0)); - ui->stopBits->setCurrentIndex(pt.get("miscsettings.stopbits",0)); - ui->readIntervalTimeout->setValue(pt.get("timeoutsettings.readintervaltimeout",500)); - ui->readTotalTimeoutConstant->setValue(pt.get("timeoutsettings.readtotaltimeoutconstant",50)); - ui->readTotalTimeoutMultiplier->setValue(pt.get("timeoutsettings.readtotaltimeoutmultiplier",10)); - } catch(std::exception &) { - QMessageBox::information(this,"Error in Config File","Could not read out config parameters.",QMessageBox::Ok); - return; - } -} -void MainWindow::save_config(const std::string &filename) { - using boost::property_tree::ptree; - ptree pt; - - // transfer UI content into property tree - try { - pt.put("coresettings.comport",ui->comPort->value()); - pt.put("coresettings.baudrate",ui->baudRate->value()); - pt.put("streamsettings.samplingrate",ui->samplingRate->value()); - pt.put("streamsettings.chunksize",ui->chunkSize->value()); - pt.put("streamsettings.streamname",ui->streamName->text().toStdString()); - pt.put("miscsettings.databits",ui->dataBits->currentIndex()); - pt.put("miscsettings.parity",ui->parity->currentIndex()); - pt.put("miscsettings.stopbits",ui->stopBits->currentIndex()); - pt.put("timeoutsettings.readintervaltimeout",ui->readIntervalTimeout->value()); - pt.put("timeoutsettings.readtotaltimeoutconstant",ui->readTotalTimeoutConstant->value()); - pt.put("timeoutsettings.readtotaltimeoutmultiplier",ui->readTotalTimeoutMultiplier->value()); - } catch(std::exception &e) { - QMessageBox::critical(this,"Error",(std::string("Could not prepare settings for saving: ")+=e.what()).c_str(),QMessageBox::Ok); - } - - // write to disk - try { - write_xml(filename, pt); - } catch(std::exception &e) { - QMessageBox::critical(this,"Error",(std::string("Could not write to config file: ")+=e.what()).c_str(),QMessageBox::Ok); - } +// background data reader thread +void read_thread(HANDLE hPort, int comPort, int baudRate, int samplingRate, int chunkSize, + const std::string streamName, std::atomic &shutdown) { + try { + + // create streaminfo + lsl::stream_info info(streamName,"Raw",1,samplingRate,lsl::cf_int16,std::string("SerialPort_") + streamName); + // append some meta-data + lsl::xml_element channels = info.desc().append_child("channels"); + channels.append_child("channel") + .append_child_value("label","Channel1") + .append_child_value("type","Raw") + .append_child_value("unit","integer"); + info.desc().append_child("acquisition") + .append_child("hardware") + .append_child_value("com_port", std::to_string(comPort)) + .append_child_value("baud_rate",std::to_string(baudRate)); + + // make a new outlet + lsl::stream_outlet outlet(info,chunkSize); + + // enter transmission loop + unsigned char byte; + short sample; + unsigned long bytes_read; + while (!shutdown) { + // get a sample + ReadFile(hPort,&byte,1,&bytes_read,NULL); sample = byte; + // transmit it + if (bytes_read) + outlet.push_sample(&sample); + } + } + catch(std::exception &e) { + // any other error +// QMessageBox::critical(this,"Error",(std::string("Error during processing: ")+=e.what()).c_str(),QMessageBox::Ok); + } + CloseHandle(hPort); } - -// start/stop the cognionics connection -void MainWindow::on_link() { - if (reader_thread_) { - // === perform unlink action === - try { - shutdown_ = true; - reader_thread_->join(); - reader_thread_.reset(); - } catch(std::exception &e) { - QMessageBox::critical(this,"Error",(std::string("Could not stop the background processing: ")+=e.what()).c_str(),QMessageBox::Ok); - return; - } - - // indicate that we are now successfully unlinked - ui->linkButton->setText("Link"); - } else { - // === perform link action === - HANDLE hPort = NULL; - - try { - // get the UI parameters... - int comPort = ui->comPort->value(); - int baudRate = ui->baudRate->value(); - int samplingRate = ui->samplingRate->value(); - int chunkSize = ui->chunkSize->value(); - std::string streamName = ui->streamName->text().toStdString(); - int dataBits = ui->dataBits->currentIndex()+4; - int parity = ui->parity->currentIndex(); - int stopBits = ui->stopBits->currentIndex(); - int readIntervalTimeout = ui->readIntervalTimeout->value(); - int readTotalTimeoutConstant = ui->readTotalTimeoutConstant->value(); - int readTotalTimeoutMultiplier = ui->readTotalTimeoutMultiplier->value(); - - // try to open the serial port - std::string fname = "\\\\.\\COM" + std::to_string(comPort); - hPort = CreateFileA(fname.c_str(),GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0); - if (hPort == INVALID_HANDLE_VALUE) - throw std::runtime_error("Could not open serial port. Please make sure that you are using the right COM port and that the device is ready."); - - // try to set up serial port parameters - DCB dcbSerialParams = {0}; - if (!GetCommState(hPort, &dcbSerialParams)) - QMessageBox::critical(this,"Error","Could not get COM port state.",QMessageBox::Ok); - dcbSerialParams.BaudRate=baudRate; - dcbSerialParams.ByteSize=dataBits; - dcbSerialParams.StopBits=stopBits; - dcbSerialParams.Parity=parity; - if(!SetCommState(hPort, &dcbSerialParams)) - QMessageBox::critical(this,"Error","Could not set baud rate.",QMessageBox::Ok); - - // try to set timeouts - COMMTIMEOUTS timeouts = {0}; - if (!GetCommTimeouts(hPort,&timeouts)) - QMessageBox::critical(this,"Error","Could not get COM port timeouts.",QMessageBox::Ok); - timeouts.ReadIntervalTimeout = readIntervalTimeout; - timeouts.ReadTotalTimeoutConstant = readTotalTimeoutConstant; - timeouts.ReadTotalTimeoutMultiplier = readTotalTimeoutMultiplier; - if (!SetCommTimeouts(hPort,&timeouts)) - QMessageBox::critical(this,"Error","Could not set COM port timeouts.",QMessageBox::Ok); - - // start reading - shutdown_ = false; - reader_thread_ = std::make_unique(&MainWindow::read_thread, this, hPort, comPort, baudRate, samplingRate, chunkSize, streamName); - - } - catch(std::exception &e) { - if (hPort != INVALID_HANDLE_VALUE) - CloseHandle(hPort); - QMessageBox::critical(this,"Error",(std::string("Error during initialization: ")+=e.what()).c_str(),QMessageBox::Ok); - return; - } - - // done, all successful - ui->linkButton->setText("Unlink"); - } +//: ## Toggling the recording state +//: Our record button has two functions: start a recording and +//: stop it if a recording is running already. +void MainWindow::toggleRecording() { + /*: the `std::unique_ptr` evaluates to false if it doesn't point to an object, + * so we need to start a recording. + * First, we load the configuration from the UI fields, set the shutdown flag + * to false so the recording thread doesn't quit immediately and create the + * recording thread. */ + if (!reader) { + HANDLE hPort = NULL; + try { + + // get the UI parameters... + int comPort = ui->comPort->value(); + int baudRate = ui->baudRate->value(); + int samplingRate = ui->samplingRate->value(); + int chunkSize = ui->chunkSize->value(); + std::string streamName = ui->input_name->text().toStdString(); + int dataBits = ui->dataBits->currentIndex() + 4; + int parity = ui->parity->currentIndex(); + int stopBits = ui->stopBits->currentIndex(); + int readIntervalTimeout = ui->readIntervalTimeout->value(); + int readTotalTimeoutConstant = ui->readTotalTimeoutConstant->value(); + int readTotalTimeoutMultiplier = ui->readTotalTimeoutMultiplier->value(); + + // try to open the serial port + std::string fname = "\\\\.\\COM" + std::to_string(comPort); + hPort = CreateFileA(fname.c_str(), GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, 0); + if (hPort == INVALID_HANDLE_VALUE) + throw std::runtime_error( + "Could not open serial port. Please make sure that you are using the right COM " + "port and that the device is ready."); + + // try to set up serial port parameters + DCB dcbSerialParams = {0}; + if (!GetCommState(hPort, &dcbSerialParams)) + QMessageBox::critical( + this, "Error", "Could not get COM port state.", QMessageBox::Ok); + dcbSerialParams.BaudRate = baudRate; + dcbSerialParams.ByteSize = dataBits; + dcbSerialParams.StopBits = stopBits; + dcbSerialParams.Parity = parity; + if (!SetCommState(hPort, &dcbSerialParams)) + QMessageBox::critical(this, "Error", "Could not set baud rate.", QMessageBox::Ok); + + // try to set timeouts + COMMTIMEOUTS timeouts = {0}; + if (!GetCommTimeouts(hPort, &timeouts)) + QMessageBox::critical( + this, "Error", "Could not get COM port timeouts.", QMessageBox::Ok); + timeouts.ReadIntervalTimeout = readIntervalTimeout; + timeouts.ReadTotalTimeoutConstant = readTotalTimeoutConstant; + timeouts.ReadTotalTimeoutMultiplier = readTotalTimeoutMultiplier; + if (!SetCommTimeouts(hPort, &timeouts)) + QMessageBox::critical( + this, "Error", "Could not set COM port timeouts.", QMessageBox::Ok); + reader = std::make_unique([&]() { + read_thread( + hPort, comPort, baudRate, samplingRate, chunkSize, streamName, shutdown); + }); + + } catch (std::exception &e) { + if (hPort != INVALID_HANDLE_VALUE) CloseHandle(hPort); + QMessageBox::critical(this, "Error", + QStringLiteral("Error during initialization: ") + e.what(), QMessageBox::Ok); + return; + } + ui->linkButton->setText("Unlink"); + } else { + shutdown = true; + reader->join(); + reader.reset(); + ui->linkButton->setText("Link"); + } } -// background data reader thread -void MainWindow::read_thread(HANDLE hPort, int comPort, int baudRate, int samplingRate, int chunkSize, const std::string &streamName) { - try { - - // create streaminfo - lsl::stream_info info(streamName,"Raw",1,samplingRate,lsl::cf_int16,std::string("SerialPort_") + streamName); - // append some meta-data - lsl::xml_element channels = info.desc().append_child("channels"); - channels.append_child("channel") - .append_child_value("label","Channel1") - .append_child_value("type","Raw") - .append_child_value("unit","integer"); - info.desc().append_child("acquisition") - .append_child("hardware") - .append_child_value("com_port", std::to_string(comPort)) - .append_child_value("baud_rate",std::to_string(baudRate)); - - // make a new outlet - lsl::stream_outlet outlet(info,chunkSize); - - // enter transmission loop - unsigned char byte; - short sample; - unsigned long bytes_read; - while (!shutdown_) { - // get a sample - ReadFile(hPort,&byte,1,&bytes_read,NULL); sample = byte; - // transmit it - if (bytes_read) - outlet.push_sample(&sample); - } - } - catch(std::exception &e) { - // any other error - QMessageBox::critical(this,"Error",(std::string("Error during processing: ")+=e.what()).c_str(),QMessageBox::Ok); - } - CloseHandle(hPort); +/** + * Find a config file to load. This is (in descending order or preference): + * - a file supplied on the command line + * - [executablename].cfg in one the the following folders: + * - the current working directory + * - the default config folder, e.g. '~/Library/Preferences' on OS X + * - the executable folder + * @param filename Optional file name supplied e.g. as command line parameter + * @return Path to a found config file + */ +QString MainWindow::find_config_file(const char *filename) { + if (filename) { + QString qfilename(filename); + if (!QFileInfo::exists(qfilename)) + QMessageBox(QMessageBox::Warning, "Config file not found", + QStringLiteral("The file '%1' doesn't exist").arg(qfilename), QMessageBox::Ok, + this); + else + return qfilename; + } + QFileInfo exeInfo(QCoreApplication::applicationFilePath()); + QString defaultCfgFilename(exeInfo.completeBaseName() + ".cfg"); + QStringList cfgpaths; + cfgpaths << QDir::currentPath() + << QStandardPaths::standardLocations(QStandardPaths::ConfigLocation) << exeInfo.path(); + for (auto path : cfgpaths) { + QString cfgfilepath = path + QDir::separator() + defaultCfgFilename; + if (QFileInfo::exists(cfgfilepath)) return cfgfilepath; + } + QMessageBox(QMessageBox::Warning, "No config file not found", + QStringLiteral("No default config file could be found"), QMessageBox::Ok, this); + return ""; } -MainWindow::~MainWindow() { - delete ui; -} + +//: Tell the compiler to put the default destructor in this object file +MainWindow::~MainWindow() noexcept = default; \ No newline at end of file diff --git a/mainwindow.h b/mainwindow.h deleted file mode 100644 index a8e6161..0000000 --- a/mainwindow.h +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef MAINWINDOW_H -#define MAINWINDOW_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// LSL API -#include - -#define WIN32_LEAN_AND_MEAN -#include "windows.h" - -namespace Ui { -class MainWindow; -} - -class MainWindow : public QMainWindow -{ - Q_OBJECT - -public: - explicit MainWindow(QWidget *parent, const std::string &config_file); - ~MainWindow(); - -private slots: - // config file dialog ops (from main menu) - void load_config_dialog(); - void save_config_dialog(); - - // start the cognionics connection - void on_link(); - - // close event (potentially disabled) - void closeEvent(QCloseEvent *ev); -private: - // background data reader thread - void read_thread(HANDLE hPort, int comPort, int baudRate, int samplingRate, int chunkSize, const std::string &streamName); - - // raw config file IO - void load_config(const std::string &filename); - void save_config(const std::string &filename); - - std::unique_ptr reader_thread_{nullptr}; // our reader thread - std::atomic shutdown_{false}; // flag indicating whether the streaming thread should quit - - Ui::MainWindow *ui; -}; - -#endif // MAINWINDOW_H diff --git a/mainwindow.hpp b/mainwindow.hpp new file mode 100644 index 0000000..d455f49 --- /dev/null +++ b/mainwindow.hpp @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include +#include //for std::unique_ptr +#include + + +#define WIN32_LEAN_AND_MEAN +#include "windows.h" + +namespace Ui { + class MainWindow; +} + +class MainWindow : public QMainWindow { +Q_OBJECT +public: + explicit MainWindow(QWidget *parent, const char *config_file); + ~MainWindow() noexcept override; + +private slots: + void closeEvent(QCloseEvent *ev) override; + void toggleRecording(); + +private: + QString find_config_file(const char *filename); + void load_config(const QString &filename); + void save_config(const QString &filename); + std::unique_ptr reader{nullptr}; + + std::unique_ptr ui; // window pointer + std::atomic shutdown{false}; // flag indicating whether the recording thread should quit +}; + +#endif // MAINWINDOW_H \ No newline at end of file diff --git a/mainwindow.ui b/mainwindow.ui index a09e1a9..5d8d014 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -9,8 +9,8 @@ 0 0 - 415 - 333 + 502 + 433 @@ -26,9 +26,6 @@ Core Port Settings - - QFormLayout::AllNonFixedFieldsGrow - @@ -78,9 +75,6 @@ Stream Settings - - QFormLayout::AllNonFixedFieldsGrow - @@ -93,6 +87,9 @@ Set this to a non-zero value if you know the sampling rate. + + Hz + 0 @@ -112,7 +109,7 @@ - + The name of the stream within LSL. @@ -294,8 +291,8 @@ - - Maximum time that may pass between two bytes + + ms 10000000 @@ -394,44 +391,60 @@ 0 0 - 415 - 18 + 502 + 30 - File + &File - + + + + + Help + + + - Load Configuration + &Load Configuration + + + Ctrl+L - Save Configuration + &Save Configuration + + + Ctrl+S - Quit + &Quit + + + Ctrl+Q - + - Quit + &About - + \ No newline at end of file