Skip to content

Commit a0c9ebc

Browse files
author
Vedran
authored
Merge pull request #5 from vedranMv/dev-1.0
Dev-1.0
2 parents bb86377 + 75bbe4f commit a0c9ebc

48 files changed

Lines changed: 1170 additions & 833 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,50 @@
11
[Data dashboard](https://github.com/vedranMv/dataDashboard) [![Build Status](https://ci1.vedran.ml/api/badges/vedranMv/dataDashboard/status.svg)](https://ci1.vedran.ml/vedranMv/dataDashboard) [![License](https://img.shields.io/badge/license-GPL%20v3.0-brightgreen.svg)](/License)
22
=======
3-
(QT based app for data visualization)
3+
(QT based app for real-time data visualization)
44

5-
Data dashboard is a program based on QT framework that can be used to to visualize some of the common data types that one comes in contact when working with sensors. Although it was developed as during my work with IMU, and is mostly centered around it, it should be versatile enough for other sensors and purposes as well.
5+
Data dashboard is a program based on QT framework that can be used to to visualize some of the common data types that one comes in contact when working with sensors. Although it was developed during my work with IMU, and is mostly centered around it, it should be versatile enough for other sensors and purposes as well.
66

7-
It's currently a work in progress, but once completed it should to be able to:
8-
* Read data from serial port and or network socket (real input channel)
7+
It is currently possible to:
8+
* Read data from serial port or network socket (real input channel)
9+
* Show estimated incoming data rate
910
* Define data frame (specify start/end of frame character, as well as data separator char)
1011
* Perform basic math (addition, subtraction, abs value) on multiple input channels and visualize result of the operations (virtual input channels)
1112
* Log input channels into a file
1213
* Visualize 3D orientation by taking either euler angles or quaternion as input
1314
* Visualize linear data series (flexible number of input channels)
1415
* Plot data in 3D scatter plot (or 2D by specifying one of the inputs as 0)
1516
* Create dashboard consisting of any combination of the visualizations above on runtime
17+
* Preserve settings between runs in a configuration file
18+
* Internal app log for easier troubleshooting
1619

1720
## Demo of current progress
18-
``/doc`` folder contains few screenshoots, demonstrating the functionality
21+
Few screenshots from ``/doc``, demonstrating the UI
1922

20-
Demo video: https://vedran.ml/public/projects/datadashboard/demo.mp4
23+
[<img src="doc/dashboard.PNG" width="500" />](doc/dashboard.PNG)
24+
25+
[<img src="doc/input.PNG" width="500" />](doc/input.PNG)
26+
27+
[<img src="doc/math.PNG" width="500" /></a>](doc/comparison.PNG)
28+
29+
Demo videos:
30+
* https://vedran.ml/public/projects/datadashboard/demo.mp4
31+
* https://vedran.ml/public/projects/datadashboard/demo_net.mp4
2132

2233
## How to run
2334

2435
There are several ways to run on both windows and linux.
2536

2637
### Windows
2738

28-
* Download a portable (statically linked) precompiled version from [Releases](https://github.com/vedranMv/dataDashboard/releases); _; or_
39+
* Download a portable (statically linked) precompiled version from [Releases](https://github.com/vedranMv/dataDashboard/releases)_; or_
2940
* Download QT Creator and compile the source from this repo
3041

3142
### Linux
3243
There are several dependencies for running on linux systems, regardless of which method below you go with:
3344
> ``sudo apt-get install libgl1-mesa-dev libfontconfig1 libxcb-randr0-dev libxcb-xtest0-dev libxcb-xinerama0-dev libxcb-shape0-dev libxcb-xkb-dev``
3445
3546
After that, it's possible to:
36-
* (Ubuntu) Download a precompiled (dynamically linked), version from [Releases](https://github.com/vedranMv/) and launch ``datadashboard.sh``; _; or_
47+
* (Ubuntu) Download a precompiled (dynamically linked), version from [Releases](https://github.com/vedranMv/dataDashboard/releases) and launch ``datadashboard.sh``_; or_
3748
* Install QT creator and compile from scratch _; or_
3849
* Compile the project yourself, following the guide below
3950

@@ -42,10 +53,10 @@ After that, it's possible to:
4253
1. Download libraries and compilation tools (not available on older Ubuntu 18.04/16.04)
4354
> ``sudo apt-get install qt5-default qt5-qmake libqt5datavisualization5-dev libqt5serialport5-dev libqt5core5a libqt5x11extras5-dev``
4455
45-
2. Clone this project
56+
2. Clone this project (master branch for latest release version)
4657
> ``git clone --depth=1 https://github.com/vedranMv/dataDashboard``
4758
48-
3. Make build directory inside the cloned one
59+
3. Make build directory inside the cloned repo
4960
> ``mkdir dataDashboard/build`` <br/>
5061
``cd dataDashboard/build``
5162

@@ -58,8 +69,11 @@ After that, it's possible to:
5869
5970
## Known issues
6071
* Ubuntu 16.04/18.04: Launching scatter plot crashes the program (in Ubuntu 20.04 works as expected)
72+
* 3D orientation plot sometimes freezes (workaround: just switch to another tab, then reopen _Dashboard_ tab)
73+
* High data rates unsupported (scatter plot results in biggest negative impact on performance, see video)
6174

6275
## Acknowledgments
6376
Made using:
64-
* QT framework
65-
* [CQtDeployer](https://github.com/QuasarApp/CQtDeployer) part of CI pipeline for ubuntu
77+
* QT framework & examples
78+
* [CQtDeployer](https://github.com/QuasarApp/CQtDeployer) part of CI pipeline for ubuntu
79+
* [QTCustomPlot](https://www.qcustomplot.com/index.php/introduction) line plot component

dataSources/dataSources.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#ifndef DATASOURCES_H
2+
#define DATASOURCES_H
3+
4+
#include "networkAdapter/networkadapter.h"
5+
#include "serialAdapter/serialadapter.h"
6+
7+
#endif // DATASOURCES_H
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
#include "networkadapter.h"
2+
3+
NetworkAdapter::NetworkAdapter(): _threadQuit(false), _clientSocket(nullptr),
4+
_port(0)
5+
{
6+
_tcpServer = new QTcpServer();
7+
connect(_tcpServer, &QTcpServer::newConnection,
8+
this, &NetworkAdapter::_onNewConnection);
9+
10+
_mux = DataMultiplexer::GetP();
11+
}
12+
13+
NetworkAdapter::~NetworkAdapter()
14+
{
15+
// Pass kill signal to the thread and wait for it to terminate
16+
_threadQuit = true;
17+
wait();
18+
StopListening();
19+
}
20+
21+
/**
22+
* @brief Network port setted
23+
* @param port
24+
*/
25+
void NetworkAdapter::SetNetPort(uint16_t port)
26+
{
27+
_port = port;
28+
}
29+
30+
/**
31+
* @brief Network port getter
32+
* @return
33+
*/
34+
uint16_t NetworkAdapter::GetPort()
35+
{
36+
return _port;
37+
}
38+
39+
/**
40+
* @brief Start listening on network port
41+
* Starts up TCP server on a port
42+
*/
43+
bool NetworkAdapter::StartListening()
44+
{
45+
if (!_tcpServer->isListening())
46+
{
47+
// External process use this flag to control thread, avoid
48+
// forcing its value here
49+
//_threadQuit = false;
50+
51+
if (_port == 0)
52+
{
53+
return false;
54+
emit logLine("Port cannot be 0");
55+
}
56+
57+
_tcpServer->listen(QHostAddress::Any, _port);
58+
_tcpServer->resumeAccepting();
59+
emit logLine("Waiting for new connection on port "+QString::number(_port));
60+
}
61+
62+
return true;
63+
}
64+
65+
/**
66+
* @brief Stop listening on network port
67+
* Stop TCP server and tear down a client connection if it exists
68+
*/
69+
void NetworkAdapter::StopListening()
70+
{
71+
emit logLine("Disconnecting client");
72+
73+
if (_clientSocket != nullptr)
74+
if (_clientSocket->isOpen())
75+
{
76+
disconnect(_clientSocket, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
77+
disconnect(_clientSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
78+
this, SLOT(onSocketStateChanged(QAbstractSocket::SocketState)));
79+
_clientSocket->close();
80+
delete _clientSocket;
81+
_clientSocket = nullptr;
82+
}
83+
84+
emit logLine("Stopping network thread");
85+
if (_tcpServer->isListening())
86+
_tcpServer->close();
87+
}
88+
89+
/**
90+
* @brief [Slot] Handler for new TCP connection
91+
* Called by TCP server whenever a new client is asking to be connected
92+
*/
93+
void NetworkAdapter::_onNewConnection()
94+
{
95+
_clientSocket = _tcpServer->nextPendingConnection();
96+
_tcpServer->pauseAccepting();
97+
emit logLine("Connection established, reading data");
98+
//start();
99+
100+
connect(_clientSocket, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
101+
connect(_clientSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
102+
this, SLOT(onSocketStateChanged(QAbstractSocket::SocketState)));
103+
}
104+
105+
/**
106+
* @brief [Slot] Handler for state change in sockets
107+
* Used to handle a client disconnecting from the server. Functions log
108+
* disconnect, delete client object and reset the pointer. Afterwards,
109+
* the server is enabled to receive new clients.
110+
* @param socketState socket state after a change
111+
*/
112+
void NetworkAdapter::onSocketStateChanged(QAbstractSocket::SocketState socketState)
113+
{
114+
if (socketState == QAbstractSocket::UnconnectedState)
115+
{
116+
emit logLine("Client disconnected, waiting for new connection");
117+
_clientSocket->deleteLater();
118+
_clientSocket = nullptr;
119+
_tcpServer->resumeAccepting();
120+
}
121+
}
122+
123+
/**
124+
* @brief [Slot] Handler for new incoming data on the socket
125+
* Called by the client socket when there's new data available
126+
*/
127+
void NetworkAdapter::onReadyRead()
128+
{
129+
QByteArray responseData = _clientSocket->readAll();
130+
const QString response = QString::fromUtf8(responseData);
131+
132+
_mux->ReceiveData(response);
133+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#ifndef NETWORKADAPTER_H
2+
#define NETWORKADAPTER_H
3+
4+
#include <QTcpServer>
5+
#include <QTcpSocket>
6+
#include <QThread>
7+
8+
#include "helperObjects/dataMultiplexer/datamultiplexer.h"
9+
10+
11+
class NetworkAdapter : public QThread
12+
{
13+
Q_OBJECT
14+
public:
15+
NetworkAdapter();
16+
~NetworkAdapter();
17+
18+
void SetNetPort(uint16_t port);
19+
uint16_t GetPort();
20+
bool StartListening();
21+
void StopListening();
22+
23+
signals:
24+
void logLine(const QString &s);
25+
26+
private slots:
27+
void _onNewConnection();
28+
void onSocketStateChanged(QAbstractSocket::SocketState socketState);
29+
void onReadyRead();
30+
31+
private:
32+
33+
bool _threadQuit;
34+
QTcpServer *_tcpServer;
35+
QTcpSocket *_clientSocket;
36+
37+
uint16_t _port;
38+
DataMultiplexer* _mux;
39+
};
40+
41+
#endif // NETWORKADAPTER_H
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
#include "serialadapter.h"
2+
3+
#include <QSerialPort>
4+
#include <QTime>
5+
6+
SerialAdapter::SerialAdapter(QObject *parent) :
7+
QThread(parent)
8+
{
9+
_mux = DataMultiplexer::GetP();
10+
}
11+
12+
SerialAdapter::~SerialAdapter()
13+
{
14+
_threadQuit = true;
15+
wait();
16+
}
17+
18+
/**
19+
* @brief Open serial port
20+
*/
21+
void SerialAdapter::StartListening()
22+
{
23+
// Check if the thread is already running, if not start it
24+
if (!isRunning())
25+
{
26+
_threadQuit = false;
27+
start();
28+
}
29+
}
30+
31+
/**
32+
* @brief Close serial port
33+
*/
34+
void SerialAdapter::StopListening()
35+
{
36+
_threadQuit = true;
37+
}
38+
39+
/**
40+
* @brief Update serial port parameters
41+
* @param portName
42+
* @param portBaud
43+
*/
44+
void SerialAdapter::UpdatePort(const QString &portName, const QString &portBaud)
45+
{
46+
if ((portName == _portName) && (portBaud == _portBaud))
47+
_portUpdated = false;
48+
else
49+
{
50+
_portUpdated = true;
51+
_portName = portName;
52+
_portBaud = portBaud;
53+
}
54+
}
55+
56+
/**
57+
* @brief Thread to read and publish serial data, capable of dynamically
58+
* updating port parameters
59+
*/
60+
void SerialAdapter::run()
61+
{
62+
QSerialPort serial;
63+
64+
emit logLine("Serial thread started");
65+
66+
if (_portName.isEmpty())
67+
{
68+
emit logLine(tr("No port name specified"));
69+
return;
70+
}
71+
72+
_portUpdated = true;
73+
74+
while (!_threadQuit)
75+
{
76+
// Check if port name changed while running
77+
if (_portUpdated)
78+
{
79+
serial.close();
80+
serial.setPortName(_portName);
81+
serial.setBaudRate(_portBaud.toUInt());
82+
83+
if (!serial.open(QIODevice::ReadWrite))
84+
{
85+
emit logLine(tr("Can't open %1, error code %2")
86+
.arg(_portName).arg(serial.error()));
87+
return;
88+
}
89+
_portUpdated = false;
90+
}
91+
92+
// Read response
93+
if (serial.waitForReadyRead(5))
94+
{
95+
QByteArray responseData = serial.readAll();
96+
while (serial.waitForReadyRead(1))
97+
responseData += serial.readAll();
98+
99+
const QString response = QString::fromUtf8(responseData);
100+
101+
_mux->ReceiveData(response);
102+
}
103+
}
104+
serial.close();
105+
_threadQuit = false;
106+
emit logLine("Serial thread exited");
107+
}

0 commit comments

Comments
 (0)