diff --git a/Serial-Studio.pro b/Serial-Studio.pro index 29c81d72..f9ac2ebb 100644 --- a/Serial-Studio.pro +++ b/Serial-Studio.pro @@ -147,6 +147,7 @@ HEADERS += \ src/IO/Manager.h \ src/JSON/Dataset.h \ src/JSON/Frame.h \ + src/JSON/FrameInfo.h \ src/JSON/Generator.h \ src/JSON/Group.h \ src/Misc/ModuleManager.h \ @@ -167,6 +168,7 @@ SOURCES += \ src/IO/Manager.cpp \ src/JSON/Dataset.cpp \ src/JSON/Frame.cpp \ + src/JSON/FrameInfo.cpp \ src/JSON/Generator.cpp \ src/JSON/Group.cpp \ src/Misc/ModuleManager.cpp \ diff --git a/src/CSV/Export.cpp b/src/CSV/Export.cpp index 0765af8a..e6d2c119 100644 --- a/src/CSV/Export.cpp +++ b/src/CSV/Export.cpp @@ -51,11 +51,11 @@ Export::Export() : m_exportEnabled(true) { auto io = IO::Manager::getInstance(); - auto jp = JSON::Generator::getInstance(); + auto ge = JSON::Generator::getInstance(); auto te = Misc::TimerEvents::getInstance(); connect(io, &IO::Manager::connectedChanged, this, &Export::closeFile); - connect(jp, &JSON::Generator::jsonChanged, this, &Export::updateValues); connect(te, &Misc::TimerEvents::timeout1Hz, this, &Export::writeValues); + connect(ge, &JSON::Generator::jsonChanged, this, &Export::registerFrame); LOG_TRACE() << "Class initialized"; } @@ -142,25 +142,15 @@ void Export::closeFile() */ void Export::writeValues() { - // Bubble-sort the JSON frames so that they are ordered from earliest to most recent - for (int i = 0; i < m_jsonList.count() - 1; ++i) - { - for (int j = 0; j < m_jsonList.count() - i - 1; ++j) - { - auto dateTimeA = m_jsonList.at(j + 0).first; - auto dateTimeB = m_jsonList.at(j + 1).first; - - if (dateTimeA > dateTimeB) - m_jsonList.swapItemsAt(j, j + 1); - } - } + // Sort JSON frames so that they are ordered from least-recent to most-recent + JFI_SortList(&m_jsonList); // Export JSON frames for (int k = 0; k < m_jsonList.count(); ++k) { // Get project title & cell values - auto json = m_jsonList.first().second; - auto dateTime = m_jsonList.first().first; + auto dateTime = m_jsonList.first().rxDateTime; + auto json = m_jsonList.first().jsonDocument.object(); auto projectTitle = json.value("t").toVariant().toString(); // Validate JSON & title @@ -282,10 +272,10 @@ void Export::writeValues() } /** - * Obtains the latest JSON dataframe & appends it to the JSON list - * (which is later read & written to the CSV file). + * Obtains the latest JSON dataframe & appends it to the JSON list, which is later read, + * sorted and written to the CSV file by the @c writeValues() function. */ -void Export::updateValues(const QJsonDocument &document, const QDateTime &time) +void Export::registerFrame(const JFI_Object &info) { // Ignore if device is not connected (we don't want to generate a CSV file when we // are reading another CSV file don't we?) @@ -296,12 +286,7 @@ void Export::updateValues(const QJsonDocument &document, const QDateTime &time) if (!exportEnabled()) return; - // Get & validate JSON document - auto json = document.object(); - if (json.isEmpty()) - return; - // Update JSON list - auto pair = qMakePair(time, json); - m_jsonList.append(pair); + if (JFI_Valid(info)) + m_jsonList.append(info); } diff --git a/src/CSV/Export.h b/src/CSV/Export.h index b8d727cb..a18d28b5 100644 --- a/src/CSV/Export.h +++ b/src/CSV/Export.h @@ -29,6 +29,7 @@ #include #include #include +#include namespace CSV { @@ -66,13 +67,13 @@ public slots: private slots: void writeValues(); - void updateValues(const QJsonDocument &document, const QDateTime &time); + void registerFrame(const JFI_Object &info); private: QFile m_csvFile; bool m_exportEnabled; QTextStream m_textStream; - QList> m_jsonList; + QList m_jsonList; }; } diff --git a/src/CSV/Player.cpp b/src/CSV/Player.cpp index 321feb04..d839c56f 100644 --- a/src/CSV/Player.cpp +++ b/src/CSV/Player.cpp @@ -379,7 +379,7 @@ void Player::updateData() // input source for the QML bridge auto json = getJsonFrame(framePosition() + 1); if (!json.isEmpty()) - JSON::Generator::getInstance()->setJsonDocument(json); + JSON::Generator::getInstance()->loadJSON(json); // If the user wants to 'play' the CSV, get time difference between this // frame and the next frame & schedule an automated update diff --git a/src/JSON/FrameInfo.cpp b/src/JSON/FrameInfo.cpp new file mode 100644 index 00000000..381a187a --- /dev/null +++ b/src/JSON/FrameInfo.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2020-2021 Alex Spataru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "FrameInfo.h" + +/** + * Returns @c true if the given JFI @info structure has a non-empty JSON document and a + * valid frame number. + */ +bool JFI_Valid(const JFI_Object &info) +{ + return info.frameNumber >= 0 && !info.jsonDocument.isEmpty(); +} + +/** + * Orders the given JFI @c list from least recent (first item) to most recent (last item) + * using a simple Bubble-Sort algorithm. + */ +void JFI_SortList(QList *list) +{ + Q_ASSERT(list); + + for (int i = 0; i < list->count() - 1; ++i) + { + for (int j = 0; j < list->count() - i - 1; ++j) + { + auto frameA = list->at(j + 0).frameNumber; + auto frameB = list->at(j + 1).frameNumber; + + if (frameA >= frameB) + list->swapItemsAt(j, j + 1); + } + } +} + +/** + * Creates an empty JFI structure with the current system date/time and frame number @c n + */ +JFI_Object JFI_Empty(const quint64 n) +{ + JFI_Object info; + info.frameNumber = n; + info.rxDateTime = QDateTime::currentDateTime(); + return info; +} + +/** + * Creates a new JFI structure with the given information + * + * @param n frame number + * @param t date/time + * @param d JSON document + */ +JFI_Object JFI_CreateNew(const quint64 n, const QDateTime &t, const QJsonDocument &d) +{ + JFI_Object info; + info.rxDateTime = t; + info.frameNumber = n; + info.jsonDocument = d; + return info; +} diff --git a/src/JSON/FrameInfo.h b/src/JSON/FrameInfo.h new file mode 100644 index 00000000..c45d4240 --- /dev/null +++ b/src/JSON/FrameInfo.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2020-2021 Alex Spataru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef JSON_FRAME_INFO_H +#define JSON_FRAME_INFO_H + +#include +#include +#include + +/** + * Defines a JSON frame information structure. We need to use this in order to be able + * to correctly process all received frames in a thread-worker manner. + * + * Every time we receive a frame from the serial/network device, we generate the JSON + * in another thread in order to avoid putting to much pressure in the main (UI) thread. + * + * This results in a perceived increase of application performance. However, since each + * JSON frame is generated in a different worker thread, the main thread may not receive + * JSON data in the correct order. + * + * To mitigate this, we create this structure, which contains the following information: + * - Frame number (assigned by the JSON::Generator class when raw frame data is + * received, and before the JSON object is genereated/parsed). + * - RX date/time (assigned in the same manner as the frame number). + * - JSON document/object (which contains all the frame information + what we should + * do with it). + * + * We need to register the frame number because (in some cases), the RX date/time will be + * the same between two or more frames (e.g. if you have a very high baud rate, there is + * a chance that frames will be registered with the same rx date/time down to the + * millisecond, this was the root cause of bug #35). + * + * To mitigate this, we simply increment the frame number each time that we receive a raw + * frame. Frame numbers are registered as a uint64_t for this very reason (it would take + * about 585 years to overflow this variable, if you had a computer that was able to + * process a frame every nanosecond). + * + * Frame number is reset when the device connection state is changed. + */ +typedef struct +{ + quint64 frameNumber; + QDateTime rxDateTime; + QJsonDocument jsonDocument; +} JFI_Object; + +//---------------------------------------------------------------------------------------- +// Convenience functions +//---------------------------------------------------------------------------------------- + +extern bool JFI_Valid(const JFI_Object &info); +extern void JFI_SortList(QList *list); + +extern JFI_Object JFI_Empty(const quint64 n = 0); +extern JFI_Object JFI_CreateNew(const quint64 n, const QDateTime &t, + const QJsonDocument &d); + +/* + * Important magic to be able to use JFI structures in Qt signals/slots + */ +Q_DECLARE_METATYPE(JFI_Object) + +#endif diff --git a/src/JSON/Generator.cpp b/src/JSON/Generator.cpp index a8aa3dda..4343aa82 100644 --- a/src/JSON/Generator.cpp +++ b/src/JSON/Generator.cpp @@ -48,7 +48,8 @@ static const QRegExp UNMATCHED_VALUES_REGEX("(%\b([0-9]|[1-9][0-9])\b)"); * Initializes the JSON Parser class and connects appropiate SIGNALS/SLOTS */ Generator::Generator() - : m_opMode(kAutomatic) + : m_frameCount(0) + , m_opMode(kAutomatic) { auto io = IO::Manager::getInstance(); auto cp = CSV::Player::getInstance(); @@ -79,14 +80,6 @@ QString Generator::jsonMapData() const return m_jsonMapData; } -/** - * Returns the parsed JSON document from the received packet - */ -QJsonDocument Generator::document() const -{ - return m_document; -} - /** * Returns the file name (e.g. "JsonMap.json") of the loaded JSON map file */ @@ -247,22 +240,20 @@ void Generator::writeSettings(const QString &path) } /** - * Changes the JSON document to be used to generate the user interface. - * This function is set to public in order to allow the CSV-replay feature to - * work by replacing the data/json input source. + * Notifies the rest of the application that a new JSON frame has been received. The JFI + * also contains RX date/time and frame number. + * + * Read the "FrameInfo.h" file for more information. */ -void Generator::setJsonDocument(const QJsonDocument &document, const QDateTime &time) +void Generator::loadJFI(const JFI_Object &info) { - if (document.object().isEmpty()) - return; - bool csvOpen = CSV::Player::getInstance()->isOpen(); bool devOpen = IO::Manager::getInstance()->connected(); if (csvOpen || devOpen) { - m_document = document; - emit jsonChanged(document, time); + if (JFI_Valid(info)) + emit jsonChanged(info); } else @@ -270,13 +261,22 @@ void Generator::setJsonDocument(const QJsonDocument &document, const QDateTime & } /** - * Resets all the statistics related to the current serial port device and - * the JSON map file + * Create a new JFI event with the given @a JSON document and increment the frame count + */ +void Generator::loadJSON(const QJsonDocument &json) +{ + auto jfi = JFI_CreateNew(m_frameCount, QDateTime::currentDateTime(), json); + m_frameCount++; + loadJFI(jfi); +} + +/** + * Resets all the statistics related to the current device and the JSON map file */ void Generator::reset() { - m_document = QJsonDocument::fromJson(QByteArray("{}")); - emit jsonChanged(document(), QDateTime::currentDateTime()); + m_frameCount = 0; + emit jsonChanged(JFI_Empty()); } /** @@ -302,15 +302,18 @@ void Generator::readData(const QByteArray &data) if (data.isEmpty()) return; + // Increment received frames + m_frameCount++; + // Create new worker thread to read JSON data QThread *thread = new QThread; - JSONWorker *worker = new JSONWorker(data, QDateTime::currentDateTime()); + JSONWorker *worker = new JSONWorker(data, m_frameCount, QDateTime::currentDateTime()); worker->moveToThread(thread); connect(thread, SIGNAL(started()), worker, SLOT(process())); connect(worker, SIGNAL(finished()), thread, SLOT(quit())); connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater())); connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); - connect(worker, &JSONWorker::jsonReady, this, &Generator::setJsonDocument); + connect(worker, &JSONWorker::jsonReady, this, &Generator::loadJFI); thread->start(); } @@ -322,9 +325,10 @@ void Generator::readData(const QByteArray &data) * Constructor function, stores received frame data & the date/time that the frame data * was received. */ -JSONWorker::JSONWorker(const QByteArray &data, const QDateTime &time) +JSONWorker::JSONWorker(const QByteArray &data, const quint64 frame, const QDateTime &time) : m_time(time) , m_data(data) + , m_frame(frame) , m_engine(nullptr) { } @@ -436,7 +440,7 @@ void JSONWorker::process() // No parse error, update UI & reset error counter if (error.error == QJsonParseError::NoError) - emit jsonReady(document, m_time); + emit jsonReady(JFI_CreateNew(m_frame, m_time, document)); // Delete object in 500 ms QTimer::singleShot(500, this, SIGNAL(finished())); diff --git a/src/JSON/Generator.h b/src/JSON/Generator.h index edd871c7..319ae790 100644 --- a/src/JSON/Generator.h +++ b/src/JSON/Generator.h @@ -23,6 +23,7 @@ #ifndef JSON_GENERATOR_H #define JSON_GENERATOR_H +#include #include #include #include @@ -37,6 +38,7 @@ #include #include "Frame.h" +#include "FrameInfo.h" namespace JSON { @@ -46,10 +48,10 @@ class JSONWorker : public QObject signals: void finished(); - void jsonReady(const QJsonDocument &document, const QDateTime &time); + void jsonReady(const JFI_Object &info); public: - JSONWorker(const QByteArray &data, const QDateTime &time); + JSONWorker(const QByteArray &data, const quint64 frame, const QDateTime &time); public slots: void process(); @@ -57,6 +59,7 @@ public slots: private: QDateTime m_time; QByteArray m_data; + quint64 m_frame; QJSEngine *m_engine; }; @@ -79,7 +82,7 @@ class Generator : public QObject signals: void jsonFileMapChanged(); void operationModeChanged(); - void jsonChanged(const QJsonDocument &document, const QDateTime &time); + void jsonChanged(const JFI_Object &info); public: enum OperationMode @@ -93,7 +96,6 @@ public: static Generator *getInstance(); QString jsonMapData() const; - QJsonDocument document() const; QString jsonMapFilename() const; QString jsonMapFilepath() const; OperationMode operationMode() const; @@ -108,21 +110,20 @@ private: public slots: void readSettings(); + void loadJFI(const JFI_Object &info); void writeSettings(const QString &path); - void setJsonDocument(const QJsonDocument &document, - const QDateTime &time = QDateTime::currentDateTime()); + void loadJSON(const QJsonDocument &json); private slots: void reset(); void readData(const QByteArray &data); private: - Frame m_frame; QFile m_jsonMap; + quint64 m_frameCount; QSettings m_settings; QString m_jsonMapData; OperationMode m_opMode; - QJsonDocument m_document; QThread m_workerThread; JSONWorker *m_jsonWorker; diff --git a/src/Misc/ModuleManager.cpp b/src/Misc/ModuleManager.cpp index 9b4ae7c3..9031f14f 100644 --- a/src/Misc/ModuleManager.cpp +++ b/src/Misc/ModuleManager.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -103,7 +104,7 @@ void ModuleManager::configureUpdater() void ModuleManager::registerQmlTypes() { LOG_TRACE() << "Registering QML types..."; - qRegisterMetaType("Frame"); + qRegisterMetaType("JFI_Object"); qmlRegisterType("SerialStudio", 1, 0, "Group"); qmlRegisterType("SerialStudio", 1, 0, "Dataset"); qmlRegisterType("SerialStudio", 1, 0, "QmlPlainTextEdit"); diff --git a/src/UI/DataProvider.cpp b/src/UI/DataProvider.cpp index 7f8460cf..1761275f 100644 --- a/src/UI/DataProvider.cpp +++ b/src/UI/DataProvider.cpp @@ -42,6 +42,7 @@ static DataProvider *INSTANCE = nullptr; */ DataProvider::DataProvider() { + m_latestJsonFrame = JFI_Empty(); auto cp = CSV::Player::getInstance(); auto io = IO::Manager::getInstance(); auto ge = JSON::Generator::getInstance(); @@ -118,8 +119,7 @@ void DataProvider::resetData() return; // Make latest frame invalid - m_latestJsonFrame - = qMakePair(QDateTime::currentDateTime(), QJsonObject()); + m_latestJsonFrame = JFI_Empty(); // Update UI emit updated(); @@ -131,7 +131,7 @@ void DataProvider::resetData() */ void DataProvider::updateData() { - if (m_latestFrame.read(m_latestJsonFrame.second)) + if (m_latestFrame.read(m_latestJsonFrame.jsonDocument.object())) emit updated(); } @@ -139,15 +139,14 @@ void DataProvider::updateData() * Ensures that only the most recent JSON document will be displayed on the user * interface. */ -void DataProvider::selectLatestJSON(const QJsonDocument &document, const QDateTime &time) +void DataProvider::selectLatestJSON(const JFI_Object &frameInfo) { - if (m_latestJsonFrame.first < time) - { - auto json = document.object(); - if (json.isEmpty()) - return; + auto frameCount = frameInfo.frameNumber; + auto currFrameCount = m_latestJsonFrame.frameNumber; - auto pair = qMakePair(time, json); - m_latestJsonFrame = pair; + if (currFrameCount < frameCount) + { + if (JFI_Valid(frameInfo)) + m_latestJsonFrame = frameInfo; } } diff --git a/src/UI/DataProvider.h b/src/UI/DataProvider.h index e96e946c..5abfbb1a 100644 --- a/src/UI/DataProvider.h +++ b/src/UI/DataProvider.h @@ -25,6 +25,7 @@ #include #include +#include namespace UI { @@ -61,11 +62,11 @@ private: private slots: void resetData(); void updateData(); - void selectLatestJSON(const QJsonDocument &document, const QDateTime &time); + void selectLatestJSON(const JFI_Object &frameInfo); private: JSON::Frame m_latestFrame; - QPair m_latestJsonFrame; + JFI_Object m_latestJsonFrame; }; } diff --git a/src/UI/GraphProvider.cpp b/src/UI/GraphProvider.cpp index d0e5b303..0e92019e 100644 --- a/src/UI/GraphProvider.cpp +++ b/src/UI/GraphProvider.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -67,10 +68,12 @@ GraphProvider::GraphProvider() // Module signals/slots auto cp = CSV::Player::getInstance(); auto io = IO::Manager::getInstance(); + auto ge = JSON::Generator::getInstance(); auto te = Misc::TimerEvents::getInstance(); connect(cp, SIGNAL(openChanged()), this, SLOT(resetData())); connect(te, SIGNAL(timeout42Hz()), this, SLOT(drawGraphs())); connect(io, SIGNAL(connectedChanged()), this, SLOT(resetData())); + connect(ge, &JSON::Generator::jsonChanged, this, &GraphProvider::registerFrame); // Avoid issues when CSV player goes backwards connect(CSV::Player::getInstance(), SIGNAL(timestampChanged()), @@ -243,59 +246,69 @@ void GraphProvider::resetData() */ void GraphProvider::drawGraphs() { - // Clear dataset & latest values list - m_datasets.clear(); + // Sort JSON frames so that they are ordered from least-recent to most-recent + JFI_SortList(&m_jsonList); - // Get frame, abort if frame is invalid - auto frame = DataProvider::getInstance()->latestFrame(); - if (!frame->isValid()) - return; - - // Create list with datasets that need to be graphed - for (int i = 0; i < frame->groupCount(); ++i) + // Graph each frame + for (int f = 0; f < m_jsonList.count(); ++f) { - auto group = frame->groups().at(i); - for (int j = 0; j < group->datasetCount(); ++j) + // Clear dataset & latest values list + m_datasets.clear(); + + // Get frame, abort if frame is invalid + JSON::Frame frame; + if (!frame.read(m_jsonList.at(f).jsonDocument.object())) + continue; + + // Create list with datasets that need to be graphed + for (int i = 0; i < frame.groupCount(); ++i) { - auto dataset = group->datasets().at(j); - if (dataset->graph()) - m_datasets.append(dataset); + auto group = frame.groups().at(i); + for (int j = 0; j < group->datasetCount(); ++j) + { + auto dataset = group->datasets().at(j); + if (dataset->graph()) + m_datasets.append(dataset); + } + } + + // Create list with dataset values (converted to double) + for (int i = 0; i < graphCount(); ++i) + { + // Register dataset for this graph + if (m_points.count() < (i + 1)) + { + auto vector = new QVector; + m_points.append(vector); + } + + // Register min. values list + if (m_minimumValues.count() < (i + 1)) + m_minimumValues.append(getValue(i)); + + // Register max. values list + if (m_maximumValues.count() < (i + 1)) + m_maximumValues.append(getValue(i)); + + // Update minimum value + if (minimumValue(i) > getValue(i)) + m_minimumValues.replace(i, getValue(i)); + + // Update minimum value + if (maximumValue(i) < getValue(i)) + m_maximumValues.replace(i, getValue(i)); + + // Remove older items + if (m_points.at(i)->count() >= displayedPoints()) + m_points.at(i)->remove(0, m_points.at(i)->count() - displayedPoints()); + + // Add values + m_points.at(i)->append(getValue(i)); } } - // Create list with dataset values (converted to double) - for (int i = 0; i < graphCount(); ++i) - { - // Register dataset for this graph - if (m_points.count() < (i + 1)) - { - auto vector = new QVector; - m_points.append(vector); - } - - // Register min. values list - if (m_minimumValues.count() < (i + 1)) - m_minimumValues.append(getValue(i)); - - // Register max. values list - if (m_maximumValues.count() < (i + 1)) - m_maximumValues.append(getValue(i)); - - // Update minimum value - if (minimumValue(i) > getValue(i)) - m_minimumValues.replace(i, getValue(i)); - - // Update minimum value - if (maximumValue(i) < getValue(i)) - m_maximumValues.replace(i, getValue(i)); - - // Remove older items - if (m_points.at(i)->count() >= displayedPoints()) - m_points.at(i)->remove(0, m_points.at(i)->count() - displayedPoints()); - - // Add values - m_points.at(i)->append(getValue(i)); - } + // Clear frame list + m_jsonList.clear(); // Update UI emit dataUpdated(); @@ -351,3 +364,13 @@ void GraphProvider::updateGraph(QAbstractSeries *series, const int index) } } } + +/** + * Obtains the latest JSON dataframe & appends it to the JSON list, which is later read, + * sorted & graphed by the @c drawGraph() function. + */ +void GraphProvider::registerFrame(const JFI_Object &frameInfo) +{ + if (JFI_Valid(frameInfo)) + m_jsonList.append(frameInfo); +} diff --git a/src/UI/GraphProvider.h b/src/UI/GraphProvider.h index d336d268..f9509eb7 100644 --- a/src/UI/GraphProvider.h +++ b/src/UI/GraphProvider.h @@ -31,6 +31,7 @@ #include #include +#include QT_CHARTS_USE_NAMESPACE @@ -79,6 +80,7 @@ private slots: void resetData(); void drawGraphs(); void csvPlayerFixes(); + void registerFrame(const JFI_Object &frameInfo); private: int m_prevFramePos; @@ -87,6 +89,7 @@ private: QVector m_minimumValues; QVector *> m_points; QVector m_datasets; + QList m_jsonList; }; }