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(); +}