diff --git a/SkywardHub.pro b/SkywardHub.pro index c5f3472aabce1eb94f5af97510dfac0dbd3c196f..bb8bef9b122c16d782de33cee6f9aee3dd6eee0f 100644 --- a/SkywardHub.pro +++ b/SkywardHub.pro @@ -19,6 +19,7 @@ INCLUDEPATH += \ libs/mavlink-skyward-lib SOURCES += \ + src/shared/Modules/CsvLogger/CsvLogger.cpp \ src/shared/Modules/Empty/EmptyModule.cpp \ src/shared/Modules/ValuesConverterViewer/ValuesViewerConfigPanel.cpp \ src/shared/Modules/ValuesConverterViewer/ValueElement.cpp \ @@ -75,6 +76,7 @@ SOURCES += \ src/entrypoints/groundstation/main.cpp HEADERS += \ + src/shared/Modules/CsvLogger/CsvLogger.h \ src/shared/Modules/Empty/EmptyModule.h \ src/shared/Modules/ValuesConverterViewer/ValueElement.h \ src/shared/Modules/ValuesConverterViewer/ValuesViewerConfigPanel.h \ diff --git a/src/shared/Components/FilterSelector/FilterSelector.cpp b/src/shared/Components/FilterSelector/FilterSelector.cpp index 47fdea567c12b5e36ac71c28a10c33548f5eefce..962ffcc213d5eb8d6f5d915951a034cad38be092 100644 --- a/src/shared/Components/FilterSelector/FilterSelector.cpp +++ b/src/shared/Components/FilterSelector/FilterSelector.cpp @@ -15,7 +15,7 @@ void FilterSelector::setupUi() topic = new QComboBox; topic->setSizeAdjustPolicy(QComboBox::AdjustToContents); topic->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); - topic->addItems(messages.keys()); + topic->addItems(listOfMessages().keys()); layout->addWidget(topic); fieldsWidget = new QListWidget; @@ -32,14 +32,14 @@ void FilterSelector::setupUi() if (filter.getTopic().toString() != Topic().toString()) currentMessage = filter.getTopic().toString(); else - currentMessage = messages.keys().at(0); + currentMessage = listOfMessages().keys().at(0); topic->setCurrentText(currentMessage); setTopic(currentMessage); connect(topic, &QComboBox::currentTextChanged, this, [=](QString key) { - if (messages.contains(key)) + if (listOfMessages().contains(key)) { filter = getFilter(); setTopic(key); @@ -74,18 +74,22 @@ void FilterSelector::selectFilter(const Filter &filter, FilterSelector::FilterSelector() { - parseMessagesList(); setupUi(); } FilterSelector::FilterSelector(const Filter &filter) : filter(filter) { - parseMessagesList(); setupUi(); } -void FilterSelector::parseMessagesList() +QMap<QString, QList<QString>> FilterSelector::listOfMessages(){ + static auto messages = parseMessagesList(); + return messages; +} + +QMap<QString, QList<QString>> FilterSelector::parseMessagesList() { + QMap<QString, QList<QString>> out; mavlink_message_info_t messagesList[256] = MAVLINK_MESSAGE_INFO; for (unsigned int i = 0; i < 255; i++) @@ -98,9 +102,10 @@ void FilterSelector::parseMessagesList() for (unsigned int ii = 0; ii < messagesList[i].num_fields; ii++) fields.append(messagesList[i].fields[ii].name); - messages[QString("Mav/") + messagesList[i].name] = fields; + out[QString("Mav/") + messagesList[i].name] = fields; } } + return out; } void FilterSelector::setTopic(QString topic) @@ -109,7 +114,7 @@ void FilterSelector::setTopic(QString topic) fieldsWidget->clear(); // Add the new topic fields - fieldsWidget->addItems(messages[topic]); + fieldsWidget->addItems(listOfMessages()[topic]); for (int i = 0; i < fieldsWidget->count(); i++) { auto item = fieldsWidget->item(i); @@ -137,4 +142,4 @@ Filter FilterSelector::getFilter() } return filter; -} \ No newline at end of file +} diff --git a/src/shared/Components/FilterSelector/FilterSelector.h b/src/shared/Components/FilterSelector/FilterSelector.h index 422a4954b726a50c2b2bfd86388fbda200080482..5d462bfa8196db0fe3f163bb3e78ced3d1ec0281 100644 --- a/src/shared/Components/FilterSelector/FilterSelector.h +++ b/src/shared/Components/FilterSelector/FilterSelector.h @@ -20,11 +20,13 @@ public: static void selectFilter(const Filter& filter, Handler handler); Filter getFilter(); + static QMap<QString, QList<QString>> listOfMessages(); + private: FilterSelector(); explicit FilterSelector(const Filter& filter); - void parseMessagesList(); + static QMap<QString, QList<QString>> parseMessagesList(); void setupUi(); void setTopic(QString topic); @@ -35,8 +37,6 @@ private: QListWidget* fieldsWidget; QComboBox* topic; - QMap<QString, QList<QString>> messages; - signals: void filterSelected(const Filter&); }; diff --git a/src/shared/Modules/CsvLogger/CsvLogger.cpp b/src/shared/Modules/CsvLogger/CsvLogger.cpp new file mode 100644 index 0000000000000000000000000000000000000000..64b02d3065bf00b5e1aefed53e4e2fac8b669a04 --- /dev/null +++ b/src/shared/Modules/CsvLogger/CsvLogger.cpp @@ -0,0 +1,94 @@ +#include "CsvLogger.h" + +#include <Components/FilterSelector/FilterSelector.h> + +CsvLogger::CsvLogger(QWidget* parent) : DefaultModule(parent), started(false), file(nullptr) { + defaultContextMenuSetup(); + setupUi(); +} + +CsvLogger::~CsvLogger() { } + +QWidget* CsvLogger::toWidget(){ + return this; +} + +XmlObject CsvLogger::toXmlObject(){ + XmlObject obj(getName(ModuleId::CSV_LOGGER)); + return obj; +} + +void CsvLogger::fromXmlObject(const XmlObject& obj){ + Q_UNUSED(obj); +} + +void CsvLogger::setupUi(){ + QVBoxLayout* layout = new QVBoxLayout; + + QComboBox* combo = new QComboBox; + combo->addItems(FilterSelector::listOfMessages().keys()); + layout->addWidget(combo); + + QHBoxLayout* bottomLayout = new QHBoxLayout; + + QLineEdit* filenameEdit = new QLineEdit; + filenameEdit->setPlaceholderText("Type output file name here."); + bottomLayout->addWidget(filenameEdit); + + QPushButton* startStopLogging = new QPushButton("Start logging"); + bottomLayout->addWidget(startStopLogging); + + QObject::connect(startStopLogging, &QPushButton::clicked, [this, combo, filenameEdit, startStopLogging](){ + if(!started){ + if(file){ + file->close(); + file->deleteLater(); + } + + file = new QFile(filenameEdit->text()); + if(!file->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append)){ + file->deleteLater(); + file = nullptr; + + error("I/O Error", QString("Unable to open file \"") + filenameEdit->text() + "\"."); + return; + } + + const auto& properties = FilterSelector::listOfMessages()[combo->currentText()]; + for(int i = 0; i < properties.size(); i++){ + file->write((properties[i] + (i < properties.size()+1 ? "," : "\n")).toUtf8()); + } + + getCore()->getMessageBroker()->unsubscribe(lastSub, this); + lastSub = Filter::fromString(combo->currentText()); + getCore()->getMessageBroker()->subscribe( + lastSub, this, + [this](const Message& message, const Filter& filter) + { received(message); }); + started = true; + } else { + if(file){ + file->close(); + file->deleteLater(); + file = nullptr; + } + started = false; + getCore()->getMessageBroker()->unsubscribe(lastSub, this); + } + + startStopLogging->setText(started ? "Stop logging" : "Start logging"); + }); + + layout->addLayout(bottomLayout); + setLayout(layout); +} + +void CsvLogger::received(const Message& msg){ + if(!file) + return; + + const auto& properties = FilterSelector::listOfMessages()[lastSub.getTopic().toString()]; + for(int i = 0; i < properties.size(); i++){ + file->write((msg.getField(properties[i]).toString() + (i < properties.size()+1 ? "," : "\n")).toUtf8()); + } +} diff --git a/src/shared/Modules/CsvLogger/CsvLogger.h b/src/shared/Modules/CsvLogger/CsvLogger.h new file mode 100644 index 0000000000000000000000000000000000000000..f0fb1b94c753fa774c4153d3b865f6851a35dffd --- /dev/null +++ b/src/shared/Modules/CsvLogger/CsvLogger.h @@ -0,0 +1,31 @@ +#ifndef CSVLOGGER_H +#define CSVLOGGER_H + +#include <Core/Message/Message.h> +#include <Modules/DefaultModule/DefaultModule.h> + +class CsvLogger : public DefaultModule +{ + Q_OBJECT + +public: + explicit CsvLogger(QWidget* parent = nullptr); + ~CsvLogger(); + + QWidget* toWidget() override; + + XmlObject toXmlObject() override; + void fromXmlObject(const XmlObject& obj) override; + +private: + bool started; + QFile* file; + Filter lastSub; + + void setupUi(); + +private slots: + void received(const Message& msg); +}; + +#endif // CSVLOGGER_H diff --git a/src/shared/Modules/ModuleInfo.h b/src/shared/Modules/ModuleInfo.h index de76b3e92d2e1df726f478c22cbfc9ffa3e2f3b5..1d26c421bc49532a8165585efdd1497aff0f07c4 100644 --- a/src/shared/Modules/ModuleInfo.h +++ b/src/shared/Modules/ModuleInfo.h @@ -11,6 +11,7 @@ enum ModuleId SKYWARDHUB, COMMANDPAD, COMPACT_COMMAND_PAD, + CSV_LOGGER, BROKERTEST, GRAPH, OUTCOMINGMESSAGEVIEWER, diff --git a/src/shared/Modules/ModulesList.cpp b/src/shared/Modules/ModulesList.cpp index 2ac1ea1f7ad333c1d7ff73ad9e301fc6344389e2..0aed0068f943ee4bae614f2a47fe95e0c0fd5ec5 100644 --- a/src/shared/Modules/ModulesList.cpp +++ b/src/shared/Modules/ModulesList.cpp @@ -4,6 +4,7 @@ #include <Components/ModulesPicker/ModulesPicker.h> #include <Modules/CommandPad/CommandPad.h> #include <Modules/CompactCommandPad/CompactCommandPad.h> +#include <Modules/CsvLogger/CsvLogger.h> #include <Modules/Empty/EmptyModule.h> #include <Modules/FileStream/FileStreamModule.h> #include <Modules/Graph/Graph.h> @@ -53,6 +54,13 @@ void ModulesList::createModuleList() compactCommandPad.addModuleSourceFiles("Modules/CompactCommandPad/"); addModuleInfo(compactCommandPad); + ModuleInfo csvLogger(ModuleId::CSV_LOGGER, + "CsvLogger", + ModuleCategory::DATASOURCE); + csvLogger.setFactory([]() { return new CsvLogger(); }); + csvLogger.addModuleSourceFiles("Modules/CsvLogger/"); + addModuleInfo(csvLogger); + ModuleInfo splitter(ModuleId::SPLITTER, "Splitter"); splitter.setFactory([]() { return new Splitter(); }); splitter.addModuleSourceFiles("Modules/Splitter/");