From 9e957f29feafb7d535fb012e8b3a43dd60a647c6 Mon Sep 17 00:00:00 2001 From: Yury Shuvakin Date: Mon, 22 Jan 2024 18:45:09 +0900 Subject: [PATCH] Added first project implementation --- .gitignore | 2 + CMakeLists.txt | 52 ++++++++++ DataTypes.cpp | 120 ++++++++++++++++++++++ DataTypes.h | 77 ++++++++++++++ InteractiveChartView.cpp | 22 ++++ InteractiveChartView.h | 19 ++++ LogParser.cpp | 112 +++++++++++++++++++++ LogParser.h | 33 ++++++ MainWindow.cpp | 211 +++++++++++++++++++++++++++++++++++++++ MainWindow.h | 50 ++++++++++ MainWindow.ui | 184 ++++++++++++++++++++++++++++++++++ main.cpp | 11 ++ 12 files changed, 893 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 DataTypes.cpp create mode 100644 DataTypes.h create mode 100644 InteractiveChartView.cpp create mode 100644 InteractiveChartView.h create mode 100644 LogParser.cpp create mode 100644 LogParser.h create mode 100644 MainWindow.cpp create mode 100644 MainWindow.h create mode 100644 MainWindow.ui create mode 100644 main.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2677a5b --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ + +CMakeLists.txt.user diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..d1aa8af --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,52 @@ +cmake_minimum_required(VERSION 3.5) + +project(M1300LogViewer VERSION 0.1 LANGUAGES CXX) + +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +find_package(Qt5 REQUIRED COMPONENTS Widgets Charts) + +set(PROJECT_SOURCES + main.cpp + + MainWindow.cpp + MainWindow.h + MainWindow.ui + + DataTypes.h + DataTypes.cpp + + LogParser.h + LogParser.cpp + + InteractiveChartView.h + InteractiveChartView.cpp +) + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) + +add_executable(M1300LogViewer + ${PROJECT_SOURCES} +) + +target_link_libraries(M1300LogViewer PRIVATE Qt5::Widgets Qt5::Charts) + +set_target_properties(M1300LogViewer PROPERTIES + MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com + MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION} + MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} + MACOSX_BUNDLE TRUE + WIN32_EXECUTABLE TRUE +) + +set(CMAKE_INSTALL_PREFIX ../M1300LogViewerDeploy) + +# deploy commands +install(CODE "execute_process(COMMAND ${CMAKE_PREFIX_PATH}/bin/windeployqt.exe --dir ${CMAKE_INSTALL_PREFIX} --no-quick-import --no-translations ${CMAKE_BINARY_DIR}/M1300LogViewer.exe WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})") +install(CODE "file(COPY ${CMAKE_BINARY_DIR}/M1300LogViewer.exe DESTINATION ${CMAKE_INSTALL_PREFIX})") +install(CODE "message(\"Installation finished!\")") diff --git a/DataTypes.cpp b/DataTypes.cpp new file mode 100644 index 0000000..7e665c8 --- /dev/null +++ b/DataTypes.cpp @@ -0,0 +1,120 @@ +#include "DataTypes.h" + +#include + +QString DataTypes::messageString(MessageType type) +{ + switch (type) + { + case MessageType::LTM_REASON_NONE: + return QObject::tr("No reason", "DataTypes"); + case MessageType::LTM_REASON_AFTER_POWER_ON: + return QObject::tr("M1300 Enabling", "DataTypes"); + case MessageType::LTM_REASON_NO_CANBUS_WITH_CONTROL_BOARD: + return QObject::tr("No CAN Bus link with PWR", "DataTypes"); + case MessageType::LTM_REASON_NO_CANBUS: + return QObject::tr("CAN Bus error with PWR", "DataTypes"); + case MessageType::LTM_REASON_NO_INPUT_LONG_TIME: + return QObject::tr("No input voltage long time", "DataTypes"); + case MessageType::LTM_REASON_MAS_SHUTDOWN: + return QObject::tr("On any M1300 disabling", "DataTypes"); + case MessageType::LTM_REASON_ANY_UNDERVOLTAGE: + return QObject::tr("On any under-voltage", "DataTypes"); + case MessageType::LTM_REASON_ANY_OVERVOLTAGE: + return QObject::tr("On any over-voltage", "DataTypes"); + case MessageType::LTM_REASON_NOT_ALLOWED_TO_START_INPUT_VOLTAGE_OUT_OF_RANGE: + return QObject::tr("Voltage out of range", "DataTypes"); + case MessageType::LTM_REASON_FIRST_START: + return QObject::tr("M1300 First start", "DataTypes"); + case MessageType::LTM_REASON_CHECK_BE_K1: + return QObject::tr("K1 Feedback error", "DataTypes"); + case MessageType::LTM_REASON_K1_NO_CONTROL_FEEDBACK: + return QObject::tr("K1 Feedback exists without control", "DataTypes"); + case MessageType::LTM_REASON_K1_NO_FEEDBACK: + return QObject::tr("K1 Feedback error", "DataTypes"); + case MessageType::LTM_REASON_TURN_OFF_DUE_K1_VS_DC_LINK: + return QObject::tr("K1 Enabled, no DC-Link", "DataTypes"); + case MessageType::LTM_REASON_K2_WRONG_WAY_WORK: + return QObject::tr("K2 enabled if K1 enabled", "DataTypes"); + case MessageType::LTM_REASON_CHECK_BE_K2: + return QObject::tr("K2 Feedback error", "DataTypes"); + case MessageType::LTM_REASON_CHECK_TR_FEEDBACK: + return QObject::tr("Protection Block feedback error", "DataTypes"); + case MessageType::LTM_REASON_TR_NO_FEEDBACK: + return QObject::tr("Protection Block no feedback", "DataTypes"); + case MessageType::LTM_REASON_TR_TEST_FAULT: + return QObject::tr("Protection Block test error", "DataTypes"); + case MessageType::LTM_REASON_LOCKED_DUE_TR: + return QObject::tr("Protection Block error", "DataTypes"); + case MessageType::LTM_REASON_TURN_OFF_DUE_TR: + return QObject::tr("Protection Block error", "DataTypes"); + case MessageType::LTM_REASON_TR_CONNECT_RESISTORS_DUE_LOW_VOLTAGE: + return QObject::tr("Charge resistors enabling on under-voltage", "DataTypes"); + case MessageType::LTM_REASON_TURN_OFF_DUE_HIGH_INPUT_VOLTAGE_LONG_TIME: + return QObject::tr("System disabling on over-voltage long time", "DataTypes"); + case MessageType::LTM_REASON_TR_CONNECT_RESISTORS_DUE_HIGH_INPUT_VOLTAGE_FAST: + return QObject::tr("Charge resistors enabling on fast over-voltage", "DataTypes"); + case MessageType::LTM_REASON_TR_CONNECT_RESISTORS_DUE_HIGH_INPUT_VOLTAGE: + return QObject::tr("Charge resistors enabling on over-voltage", "DataTypes"); + case MessageType::LTM_REASON_TURN_OFF_DUE_RESISTORS_ENERGY: + return QObject::tr("System disabling on Charge Resistors over-energy", "DataTypes"); + case MessageType::LTM_REASON_TR_CONNECT_RESISTORS_DUE_HIGH_INPUT_CURRENT: + return QObject::tr("Charge resistors enabling on over-current", "DataTypes"); + case MessageType::LTM_REASON_TURN_OFF_DUE_INPUT_CURRENT: + return QObject::tr("System disabling on over-current", "DataTypes"); + case MessageType::LTM_REASON_HUR_NO_CONNECTION: + return QObject::tr("No CAN Bus link with HUR", "DataTypes"); + case MessageType::LTM_REASON_HBU_LOCK_CAUSE_HUR: + return QObject::tr("LTM_REASON_NONE", "DataTypes"); + case MessageType::LTM_REASON_HURS_TEMP_HIGH: + return QObject::tr("HUR Slave over-temperature", "DataTypes"); + case MessageType::LTM_REASON_HURS_TEMP_DEFEKT: + return QObject::tr("HUR Slave temp sensor fault", "DataTypes"); + case MessageType::LTM_REASON_HURM_TEMP_HIGH: + return QObject::tr("HUR Master over-temperature", "DataTypes"); + case MessageType::LTM_REASON_HURM_TEMP_DEFEKT: + return QObject::tr("HUR Master temp sensor fault", "DataTypes"); + case MessageType::LTM_REASON_HURM_HSS_DEFEKT: + return QObject::tr("HUR Master HSS fault", "DataTypes"); + case MessageType::LTM_REASON_HURM_WR_DEFEKT: + return QObject::tr("HUR Master WR fault", "DataTypes"); + case MessageType::LTM_REASON_HURS_HSS_DEFEKT: + return QObject::tr("HUR Slave HSS fault", "DataTypes"); + case MessageType::LTM_REASON_HURS_WR_DEFEKT: + return QObject::tr("HUR Slave WR fault", "DataTypes"); + case MessageType::LTM_REASON_HBU_LOCK_CAUSE_PWR: + return QObject::tr("PWR Fault", "DataTypes"); + case MessageType::LTM_REASON_PWR_OVERHEATING: + return QObject::tr("PWR overheat", "DataTypes"); + case MessageType::LTM_REASON_PWR_OVERCURRENT_FAST: + return QObject::tr("PWR fast over-current", "DataTypes"); + case MessageType::LTM_REASON_PWR_OVERCURRENT_LONG_TIME: + return QObject::tr("PWR long over-current", "DataTypes"); + case MessageType::LTM_REASON_PWR_FAULT_1: + return QObject::tr("PWR Phase A Fault", "DataTypes"); + case MessageType::LTM_REASON_PWR_FAULT_2: + return QObject::tr("PWR Phase B Fault", "DataTypes"); + case MessageType::LTM_REASON_PWR_FAULT_3: + return QObject::tr("PWR Phase C Fault", "DataTypes"); + case MessageType::LTM_REASON_PWR_LOCKED: + return QObject::tr("PWR Locked", "DataTypes"); + case MessageType::LTM_PARAMS_MSG: + return QObject::tr("Params Msg Status", "DataTypes"); + case MessageType::LTM_PARAMS_OUT: + return QObject::tr("Params Msg Outs", "DataTypes"); + case MessageType::LTM_PARAMS_ADC: + return QObject::tr("Params Msg ADC", "DataTypes"); + default: + break; + } + + return {}; +} + +bool DataTypes::isErrorMessage(MessageType type) +{ + return type != MessageType::LTM_REASON_NONE && + type != MessageType::LTM_PARAMS_MSG && + type != MessageType::LTM_PARAMS_OUT && + type != MessageType::LTM_PARAMS_ADC; +} diff --git a/DataTypes.h b/DataTypes.h new file mode 100644 index 0000000..d2f0e46 --- /dev/null +++ b/DataTypes.h @@ -0,0 +1,77 @@ +#ifndef DATATYPES_H +#define DATATYPES_H + +#include + +namespace DataTypes +{ + enum class MessageType + { + LTM_REASON_NONE = 0, ///< No reason + LTM_REASON_AFTER_POWER_ON = 1, ///< [Unused] M1300 Enabling + LTM_REASON_NO_CANBUS_WITH_CONTROL_BOARD = 2, ///< No CAN Bus link with PWR + LTM_REASON_NO_CANBUS = 3, ///< CAN Bus error with PWR + LTM_REASON_NO_INPUT_LONG_TIME = 4, ///< No input voltage long time + LTM_REASON_MAS_SHUTDOWN = 5, ///< On any M1300 disabling + LTM_REASON_ANY_UNDERVOLTAGE = 6, ///< On any under-voltage + LTM_REASON_ANY_OVERVOLTAGE = 7, ///< On any over-voltage + LTM_REASON_NOT_ALLOWED_TO_START_INPUT_VOLTAGE_OUT_OF_RANGE = 8, ///< Voltage out of range + LTM_REASON_FIRST_START = 9, ///< M1300 First start + LTM_REASON_CHECK_BE_K1 = 10, ///< K1 Feedback error + LTM_REASON_K1_NO_CONTROL_FEEDBACK = 11, ///< K1 Feedback exists without control + LTM_REASON_K1_NO_FEEDBACK = 12, ///< K1 Feedback error + LTM_REASON_TURN_OFF_DUE_K1_VS_DC_LINK = 13, ///< K1 Enabled, no DC-Link + LTM_REASON_K2_WRONG_WAY_WORK = 14, ///< K2 enabled if K1 enabled + LTM_REASON_CHECK_BE_K2 = 15, ///< K2 Feedback error + LTM_REASON_CHECK_TR_FEEDBACK = 16, ///< Protection Block feedback error + LTM_REASON_TR_NO_FEEDBACK = 17, ///< Protection Block no feedback + LTM_REASON_TR_TEST_FAULT = 18, ///< Protection Block test error + LTM_REASON_LOCKED_DUE_TR = 19, ///< [Unused] Protection Block error + LTM_REASON_TURN_OFF_DUE_TR = 20, ///< Protection Block error + LTM_REASON_TR_CONNECT_RESISTORS_DUE_LOW_VOLTAGE = 21, ///< Charge resistors enabling on under-voltage + LTM_REASON_TURN_OFF_DUE_HIGH_INPUT_VOLTAGE_LONG_TIME = 22, ///< System disabling on over-voltage long time + LTM_REASON_TR_CONNECT_RESISTORS_DUE_HIGH_INPUT_VOLTAGE_FAST = 23, ///< Charge resistors enabling on fast over-voltage + LTM_REASON_TR_CONNECT_RESISTORS_DUE_HIGH_INPUT_VOLTAGE = 24, ///< Charge resistors enabling on over-voltage + LTM_REASON_TURN_OFF_DUE_RESISTORS_ENERGY = 25, ///< System disabling on Charge Resistors over-energy + LTM_REASON_TR_CONNECT_RESISTORS_DUE_HIGH_INPUT_CURRENT = 26, ///< Charge resistors enabling on over-current + LTM_REASON_TURN_OFF_DUE_INPUT_CURRENT = 27, ///< System disabling on over-current + LTM_REASON_HUR_NO_CONNECTION = 28, ///< No CAN Bus link with HUR + LTM_REASON_HBU_LOCK_CAUSE_HUR = 29, ///< HUR fault + LTM_REASON_HURS_TEMP_HIGH = 30, ///< HUR Slave over-temperature + LTM_REASON_HURS_TEMP_DEFEKT = 31, ///< HUR Slave temp sensor fault + LTM_REASON_HURM_TEMP_HIGH = 32, ///< HUR Master over-temperature + LTM_REASON_HURM_TEMP_DEFEKT = 33, ///< HUR Master temp sensor fault + LTM_REASON_HURM_HSS_DEFEKT = 34, ///< HUR Master HSS fault + LTM_REASON_HURM_WR_DEFEKT = 35, ///< HUR Master WR fault + LTM_REASON_HURS_HSS_DEFEKT = 36, ///< HUR Slave HSS fault + LTM_REASON_HURS_WR_DEFEKT = 37, ///< HUR Slave WR fault + LTM_REASON_HBU_LOCK_CAUSE_PWR = 38, ///< PWR Fault + LTM_REASON_PWR_OVERHEATING = 39, ///< PWR overheat + LTM_REASON_PWR_OVERCURRENT_FAST = 40, ///< PWR fast over-current + LTM_REASON_PWR_OVERCURRENT_LONG_TIME = 41, ///< PWR long over-current + LTM_REASON_PWR_FAULT_1 = 42, ///< PWR Phase A Fault + LTM_REASON_PWR_FAULT_2 = 43, ///< PWR Phase B Fault + LTM_REASON_PWR_FAULT_3 = 44, ///< PWR Phase C Fault + LTM_REASON_PWR_LOCKED = 45, ///< PWR Locked + + LTM_PARAMS_MSG = 0xFF-3, // Params Msg Status + LTM_PARAMS_OUT = 0xFF-2, // Params Msg Outs + LTM_PARAMS_ADC = 0xFF-1, // Params Msg ADC + }; + + struct LogMessage + { + QDateTime dateTime; + quint64 startupTime; + MessageType type; + qint64 f1; + qint64 f2; + qint64 f3; + qint64 f4; + }; + + QString messageString(MessageType type); + bool isErrorMessage(MessageType type); +} + +#endif // DATATYPES_H diff --git a/InteractiveChartView.cpp b/InteractiveChartView.cpp new file mode 100644 index 0000000..ebfa2f8 --- /dev/null +++ b/InteractiveChartView.cpp @@ -0,0 +1,22 @@ +#include "InteractiveChartView.h" + +void InteractiveChartView::wheelEvent(QWheelEvent* event) +{ + qreal factor; + if (event->delta() > 0) + factor = 2.0; + else + factor = 0.5; + + QRectF r = QRectF(chart()->plotArea().left(), + chart()->plotArea().top(), + chart()->plotArea().width() / factor, + chart()->plotArea().height() / factor); + QPointF mousePos = mapFromGlobal(QCursor::pos()); + r.moveCenter(mousePos); + chart()->zoomIn(r); + QPointF delta = chart()->plotArea().center() - mousePos; + chart()->scroll(delta.x(), -delta.y()); + + QChartView::wheelEvent(event); +} diff --git a/InteractiveChartView.h b/InteractiveChartView.h new file mode 100644 index 0000000..93b983a --- /dev/null +++ b/InteractiveChartView.h @@ -0,0 +1,19 @@ +#ifndef INTERACTIVECHARTVIEW_H +#define INTERACTIVECHARTVIEW_H + +#include + +class InteractiveChartView : public QtCharts::QChartView +{ + Q_OBJECT +public: + using QChartView::QChartView; + +protected: + void wheelEvent(QWheelEvent* event) override; + +private: + qreal factor = 1.0; +}; + +#endif // INTERACTIVECHARTVIEW_H diff --git a/LogParser.cpp b/LogParser.cpp new file mode 100644 index 0000000..5cf94a8 --- /dev/null +++ b/LogParser.cpp @@ -0,0 +1,112 @@ +#include "LogParser.h" + +#include +#include + +#include + +LogParser::LogParser(QObject* parent) + : QObject{parent} +{ + logFolder = "C:/Users/Yury/Documents/M1300LogFolder"; +} + +LogParser::~LogParser() +{ +} + +void LogParser::setLogFolder(const QString& folder) +{ + logFolder = folder; +} + +void LogParser::setStartDateTime(const QDateTime& dateTime) +{ + startDateTime = dateTime; +} + +void LogParser::setEndDateTime(const QDateTime& dateTime) +{ + endDateTime = dateTime; +} + +QString LogParser::getLogFolder() const +{ + return logFolder; +} + +QDateTime LogParser::getStartDateTime() const +{ + return startDateTime; +} + +QDateTime LogParser::getEndDateTime() const +{ + return endDateTime; +} + +QStringList LogParser::logFilesByDateTime() const +{ + QStringList files; + for (const auto& fileInfo: QDir(logFolder).entryInfoList()) + { + const auto fileTime = fileInfo.baseName(); + auto date = QDateTime::fromString(fileTime, "yyMMdd_hhmmss"); + if (date.date().year() < 2000) + { + date = date.addYears(100); + } + + if (date.isValid() && date >= startDateTime && date <= endDateTime) + { + files.append(fileInfo.absoluteFilePath()); + } + } + + return files; +} + +QList LogParser::logMessagesByDateTime() const +{ + QList logMessages; + + const auto logFiles = logFilesByDateTime(); + for (const auto& logFile: logFiles) + { + QFile file(logFile); + if (!file.open(QFile::ReadOnly)) + { + qWarning() << "Can't open" << logFile; + continue; + } + + while (!file.atEnd()) + { + const auto line = QString::fromUtf8(file.readLine()); + if (line.startsWith("RTC")) + { + continue; + } + + const auto parameters = line.split(';'); + if (parameters.size() < 7) + { + continue; + } + + DataTypes::LogMessage message; + message.dateTime = QDateTime::fromSecsSinceEpoch(parameters.at(0).toULongLong()); + message.startupTime = parameters.at(1).toULongLong(); + message.type = static_cast(parameters.at(2).toUInt()); + message.f1 = parameters.at(3).toLongLong(); + message.f2 = parameters.at(4).toLongLong(); + message.f3 = parameters.at(5).toLongLong(); + message.f4 = parameters.at(6).toLongLong(); + +// qDebug() << message.dateTime << message.startupTime << int(message.type); + logMessages.append(message); + } + } + + return logMessages; +} diff --git a/LogParser.h b/LogParser.h new file mode 100644 index 0000000..65c25c7 --- /dev/null +++ b/LogParser.h @@ -0,0 +1,33 @@ +#ifndef LOGPARSER_H +#define LOGPARSER_H + +#include +#include "DataTypes.h" + +class LogParser : public QObject +{ + Q_OBJECT +public: + explicit LogParser(QObject* parent = nullptr); + ~LogParser(); + +public slots: + void setLogFolder(const QString& folder); + void setStartDateTime(const QDateTime& dateTime); + void setEndDateTime(const QDateTime& dateTime); + +public: + QString getLogFolder() const; + QDateTime getStartDateTime() const; + QDateTime getEndDateTime() const; + + QStringList logFilesByDateTime() const; + QList logMessagesByDateTime() const; + +private: + QString logFolder; + QDateTime startDateTime; + QDateTime endDateTime; +}; + +#endif // LOGPARSER_H diff --git a/MainWindow.cpp b/MainWindow.cpp new file mode 100644 index 0000000..f8e6633 --- /dev/null +++ b/MainWindow.cpp @@ -0,0 +1,211 @@ +#include "MainWindow.h" +#include "./ui_MainWindow.h" + +#include "LogParser.h" + +#include +#include +#include + +#include +#include + +#include + +MainWindow::MainWindow(QWidget* parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) + , logParser(new LogParser) + , logItemModel(new QStandardItemModel) + , inputChart(new QtCharts::QChart) + , outputChart(new QtCharts::QChart) + , adcChart(new QtCharts::QChart) +{ + ui->setupUi(this); + + for (const auto& volume: QStorageInfo::mountedVolumes()) + { + if (volume.displayName().contains("Drive")) + { + logParser->setLogFolder(volume.rootPath()); + } + + qDebug() << volume.device() << volume.displayName() << volume.fileSystemType() + << volume.name() << volume.rootPath() << volume.subvolume(); + } + + if (logParser->getLogFolder().isEmpty()) + { + logParser->setLogFolder(QDir::homePath()); + } + + ui->startDateTimeEdit->setDateTime(QDateTime::currentDateTime().addMonths(-3)); + ui->endDateTimeEdit->setDateTime(QDateTime::currentDateTime()); + + logParser->setStartDateTime(ui->startDateTimeEdit->dateTime()); + logParser->setEndDateTime(ui->endDateTimeEdit->dateTime()); + + const QStringList labels = + { + tr("Date"), tr("Startup time"), tr("Type"), tr("F1"), tr("F2"), tr("F3"), tr("F4"), tr("Description") + }; + logItemModel->setHorizontalHeaderLabels(labels); + + ui->logTableView->setModel(logItemModel.get()); + ui->logTableView->setEditTriggers(QAbstractItemView::NoEditTriggers); + ui->logTableView->setSelectionBehavior(QAbstractItemView::SelectRows); + ui->logTableView->setSelectionMode(QAbstractItemView::SingleSelection); + + ui->inputChartView->setRenderHint(QPainter::Antialiasing); + ui->inputChartView->setChart(inputChart.data()); + ui->inputChartView->setRubberBand(QtCharts::QChartView::RectangleRubberBand); + + ui->outputChartView->setRenderHint(QPainter::Antialiasing); + ui->outputChartView->setChart(outputChart.data()); + ui->outputChartView->setRubberBand(QtCharts::QChartView::RectangleRubberBand); + + ui->adcChartView->setRenderHint(QPainter::Antialiasing); + ui->adcChartView->setChart(adcChart.data()); + ui->adcChartView->setRubberBand(QtCharts::QChartView::RectangleRubberBand); + + ui->pathLineEdit->setText(logParser->getLogFolder()); + ui->pathLineEdit->setReadOnly(true); + + connect(ui->applyDateTimeButton, &QPushButton::clicked, this, &MainWindow::applyDateTimeFilter); + connect(ui->browseButton, &QPushButton::clicked, this, &MainWindow::changePath); + + updateLogMessages(); +} + +MainWindow::~MainWindow() +{ +} + +void MainWindow::changePath() +{ + const auto directory = QFileDialog::getExistingDirectory(this, tr("Choose log folder")); + if (directory.isEmpty()) + { + return; + } + + ui->pathLineEdit->setText(directory); + logParser->setLogFolder(directory); + + updateLogMessages(); +} + +void MainWindow::applyDateTimeFilter() +{ + logParser->setStartDateTime(ui->startDateTimeEdit->dateTime()); + logParser->setEndDateTime(ui->endDateTimeEdit->dateTime()); + + updateLogMessages(); +} + +void MainWindow::updateLogMessages() +{ + logMessages = logParser->logMessagesByDateTime(); + updateLogTable(); + updateCharts(); +} + +void MainWindow::updateLogTable() +{ + logItemModel->removeRows(0, logItemModel->rowCount()); + + for (const auto& message: qAsConst(logMessages)) + { + if (!DataTypes::isErrorMessage(message.type)) + { + continue; + } + + auto dateItem = new QStandardItem(); + dateItem->setData(message.dateTime, Qt::DisplayRole); + + auto startupItem = new QStandardItem(); + startupItem->setData(message.startupTime, Qt::DisplayRole); + + auto typeItem = new QStandardItem(); + typeItem->setData(static_cast(message.type), Qt::DisplayRole); + + auto f1Item = new QStandardItem(); + f1Item->setData(message.f1, Qt::DisplayRole); + + auto f2Item = new QStandardItem(); + f2Item->setData(message.f2, Qt::DisplayRole); + + auto f3Item = new QStandardItem(); + f3Item->setData(message.f3, Qt::DisplayRole); + + auto f4Item = new QStandardItem(); + f4Item->setData(message.f4, Qt::DisplayRole); + + auto descriptionItem = new QStandardItem(); + descriptionItem->setData(DataTypes::messageString(message.type), Qt::DisplayRole); + + logItemModel->appendRow({dateItem, startupItem, typeItem, f1Item, f2Item, f3Item, f4Item, descriptionItem}); + } + + ui->logTableView->resizeColumnsToContents(); +} + +void MainWindow::updateCharts() +{ + updateChart(inputChart.get(), [](const DataTypes::LogMessage& message){ return message.type == DataTypes::MessageType::LTM_PARAMS_MSG; }); + updateChart(outputChart.get(), [](const DataTypes::LogMessage& message){ return message.type == DataTypes::MessageType::LTM_PARAMS_OUT; }); + updateChart(adcChart.get(), [](const DataTypes::LogMessage& message){ return message.type == DataTypes::MessageType::LTM_PARAMS_ADC; }); +} + +void MainWindow::updateChart(QtCharts::QChart* chart, std::function condition) +{ + QList voltagePoints; + QList currentPoints; + + for (const auto& message: qAsConst(logMessages)) + { + if (condition(message)) + { + voltagePoints.append({static_cast(message.startupTime), static_cast(message.f1)}); + currentPoints.append({static_cast(message.startupTime), static_cast(message.f2)}); + } + } + + QPen yellowPen(Qt::yellow); + yellowPen.setWidth(3); + + QPen redPen(Qt::red); + redPen.setWidth(3); + + auto voltageSeries = new QtCharts::QLineSeries(); + voltageSeries->setPen(yellowPen); + voltageSeries->append(voltagePoints); + voltageSeries->setName(tr("Voltage")); + + auto currentSeries = new QtCharts::QLineSeries(); + currentSeries->setPen(redPen); + currentSeries->append(currentPoints); + currentSeries->setName(tr("Current")); + + clearChart(chart); + + chart->addSeries(voltageSeries); + chart->addSeries(currentSeries); + chart->createDefaultAxes(); +} + +void MainWindow::clearChart(QtCharts::QChart* chart) +{ + for (auto series: chart->series()) + { + chart->removeSeries(series); + delete series; + } + + for (auto axis: chart->axes()) + { + chart->removeAxis(axis); + delete axis; + } +} diff --git a/MainWindow.h b/MainWindow.h new file mode 100644 index 0000000..184d77a --- /dev/null +++ b/MainWindow.h @@ -0,0 +1,50 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "DataTypes.h" + +class QStandardItemModel; +class LogParser; + +namespace QtCharts +{ + class QChart; +} + +namespace Ui +{ + class MainWindow; +} + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +public slots: + void changePath(); + void applyDateTimeFilter(); + + void updateLogMessages(); + void updateLogTable(); + void updateCharts(); + +private: + void updateChart(QtCharts::QChart* chart, std::function condition); + void clearChart(QtCharts::QChart* chart); + + QScopedPointer ui; + + QScopedPointer logParser; + QScopedPointer logItemModel; + QList logMessages; + + QScopedPointer inputChart; + QScopedPointer outputChart; + QScopedPointer adcChart; +}; +#endif // MAINWINDOW_H diff --git a/MainWindow.ui b/MainWindow.ui new file mode 100644 index 0000000..83afcfa --- /dev/null +++ b/MainWindow.ui @@ -0,0 +1,184 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + M1300 Log Viewer + + + + + 10 + + + + + + + Date and time range + + + + 10 + + + + + true + + + + + + + End: + + + + + + + Apply + + + + + + + Start: + + + + + + + true + + + + + + + + + + Log location + + + + 10 + + + + + + 0 + 0 + + + + + + + + Browse + + + + + + + Path: + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + 2 + + + + Input parameters + + + + + + + + + + Output parameters + + + + + + + + + + ADC data + + + + + + + + + + + + + + + 0 + 0 + 800 + 22 + + + + + + + + InteractiveChartView + QGraphicsView +
InteractiveChartView.h
+
+
+ + +
diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..0a0916f --- /dev/null +++ b/main.cpp @@ -0,0 +1,11 @@ +#include "MainWindow.h" + +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindow w; + w.show(); + return a.exec(); +}