mirror of
https://github.com/Serial-Studio/Serial-Studio.git
synced 2025-01-15 05:22:53 +08:00
Add new frame identification system to address #35
This commit is contained in:
parent
56d9ce101a
commit
4a42742956
@ -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 \
|
||||
|
@ -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<QDateTime, QJsonObject>(time, json);
|
||||
m_jsonList.append(pair);
|
||||
if (JFI_Valid(info))
|
||||
m_jsonList.append(info);
|
||||
}
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include <QVariant>
|
||||
#include <QTextStream>
|
||||
#include <QJsonObject>
|
||||
#include <JSON/FrameInfo.h>
|
||||
|
||||
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<QPair<QDateTime, QJsonObject>> m_jsonList;
|
||||
QList<JFI_Object> m_jsonList;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
80
src/JSON/FrameInfo.cpp
Normal file
80
src/JSON/FrameInfo.cpp
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (c) 2020-2021 Alex Spataru <https://github.com/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<JFI_Object> *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;
|
||||
}
|
83
src/JSON/FrameInfo.h
Normal file
83
src/JSON/FrameInfo.h
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (c) 2020-2021 Alex Spataru <https://github.com/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 <QList>
|
||||
#include <QDateTime>
|
||||
#include <QJsonDocument>
|
||||
|
||||
/**
|
||||
* 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<JFI_Object> *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
|
@ -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()));
|
||||
|
@ -23,6 +23,7 @@
|
||||
#ifndef JSON_GENERATOR_H
|
||||
#define JSON_GENERATOR_H
|
||||
|
||||
#include <QPair>
|
||||
#include <QFile>
|
||||
#include <QTimer>
|
||||
#include <QObject>
|
||||
@ -37,6 +38,7 @@
|
||||
#include <QJsonDocument>
|
||||
|
||||
#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;
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include <JSON/Group.h>
|
||||
#include <JSON/Dataset.h>
|
||||
#include <JSON/Generator.h>
|
||||
#include <JSON/FrameInfo.h>
|
||||
|
||||
#include <IO/Manager.h>
|
||||
#include <IO/Console.h>
|
||||
@ -103,7 +104,7 @@ void ModuleManager::configureUpdater()
|
||||
void ModuleManager::registerQmlTypes()
|
||||
{
|
||||
LOG_TRACE() << "Registering QML types...";
|
||||
qRegisterMetaType<JSON::Frame>("Frame");
|
||||
qRegisterMetaType<JFI_Object>("JFI_Object");
|
||||
qmlRegisterType<JSON::Group>("SerialStudio", 1, 0, "Group");
|
||||
qmlRegisterType<JSON::Dataset>("SerialStudio", 1, 0, "Dataset");
|
||||
qmlRegisterType<UI::QmlPlainTextEdit>("SerialStudio", 1, 0, "QmlPlainTextEdit");
|
||||
|
@ -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, QJsonObject>(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<QDateTime, QJsonObject>(time, json);
|
||||
m_latestJsonFrame = pair;
|
||||
if (currFrameCount < frameCount)
|
||||
{
|
||||
if (JFI_Valid(frameInfo))
|
||||
m_latestJsonFrame = frameInfo;
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
#include <QObject>
|
||||
#include <JSON/Frame.h>
|
||||
#include <JSON/FrameInfo.h>
|
||||
|
||||
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<QDateTime, QJsonObject> m_latestJsonFrame;
|
||||
JFI_Object m_latestJsonFrame;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include <CSV/Player.h>
|
||||
#include <IO/Manager.h>
|
||||
#include <IO/Console.h>
|
||||
#include <JSON/Generator.h>
|
||||
#include <ConsoleAppender.h>
|
||||
#include <Misc/TimerEvents.h>
|
||||
|
||||
@ -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<double>;
|
||||
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<double>;
|
||||
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);
|
||||
}
|
||||
|
@ -31,6 +31,7 @@
|
||||
|
||||
#include <JSON/Frame.h>
|
||||
#include <JSON/Dataset.h>
|
||||
#include <JSON/FrameInfo.h>
|
||||
|
||||
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<double> m_minimumValues;
|
||||
QVector<QVector<double> *> m_points;
|
||||
QVector<JSON::Dataset *> m_datasets;
|
||||
QList<JFI_Object> m_jsonList;
|
||||
};
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user