I'm trying to create my own worker thread for I/O operations. As far as I know the low-level management of the serial port is already done in a separate thread, but I want to put my code in another one to handle timeouts and other middleware stuff.
Here the complete code of my class. Please note it's my first attempt with Qt's multi-threading.
EDIT
The code is updated without sub-classing QThread
#ifndef WORKERSERIAL_H
#define WORKERSERIAL_H
#include <QObject>
#include <QThread>
#include <QQueue>
#include <QMutex>
#include <QTimer>
#include <QDebug>
#include "myserial.h"
class WorkerSerial : public QObject
{
Q_OBJECT
public:
explicit WorkerSerial(QObject *parent = 0) : QObject(parent)
{
m_serial = new MySerial(this);
connect(m_serial, &MySerial::lineReceived, this, &WorkerSerial::serial_LineReceived);
m_stop = false;
}
bool open(QString port, quint32 baudrate) { return m_serial->open(port, baudrate); }
bool isOpen() { return m_serial->serial()->isOpen(); }
QStringList ports() { return m_serial->ports(); }
private:
MySerial *m_serial;
QQueue<QString> m_queue;
QMutex m_mutex;
bool m_stop;
private slots:
void serial_LineReceived(QByteArray line)
{
emit lineReceived(line);
}
signals:
void lineReceived(QByteArray line);
public slots:
void close() { m_serial->close(); }
void send(QString data) { m_mutex.lock(); m_queue.enqueue(data); m_mutex.unlock(); }
void stop() { m_mutex.lock(); m_stop = true; m_mutex.unlock(); }
void run() {
forever {
m_mutex.lock();
if (m_stop)
{
qDebug() << "Closing...";
QTimer::singleShot(0, this, SLOT(close()));
while(m_serial->serial()->isOpen());
m_mutex.unlock();
return;
}
if (!m_queue.isEmpty())
{
QString data = m_queue.dequeue();
qDebug() << m_serial->sendMessage(data.toLocal8Bit(), true);
}
m_mutex.unlock();
}
}
};
#endif // WORKERSERIAL_H
The MySerial class it's a convenient wrapper for QSerialPort. Here the relevant functions:
bool MySerial::open(QString port, quint32 baudrate) {
m_serial->setPortName(port);
if (!m_serial->open(QIODevice::ReadWrite)) return false;
m_serial->setDataBits(QSerialPort::Data7);
m_serial->setParity(QSerialPort::EvenParity);
m_serial->setStopBits(QSerialPort::TwoStop);
m_serial->setBaudRate(baudrate);
m_serial->setFlowControl(QSerialPort::NoFlowControl);
return true;
}
QByteArray MySerial::sendMessage(QByteArray data) {
m_serial->write(data);
return data;
}
Finally, here how I start, use and close the worker:
QThread *workerThread = new QThread;
m_worker = new WorkerSerial;
m_worker->moveToThread(workerThread);
workerThread->start();
QTimer::singleShot(0, m_workerDMX, SLOT(run()));
// ...
m_worker->open("COM1", 250000));
// ...
m_worker->send("Hello World!");
// ...
m_worker->stop();
workerThread->quit();
workerThread->wait(1000);
// Here the application ends
This is the output:
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QSerialPort(0x19882160), parent's thread is QThread(0x187fe598), current thread is WorkerSerial(0x19d4c9b8)
[this error appears when it calls the SendMessage() function]
"Hello World!"
Closing...
QMutex: destroying locked mutex
QThread: Destroyed while thread is still running
[the two errors above are due the singleSlot() isn't executed and thus the serial won't close]
Invalid parameter passed to C runtime function.
Invalid parameter passed to C runtime function.
It seems clear I've messed up something!
Would you help me to understand my mistakes?
Here is (an incomplete) implementation, which should give you an idea of how to simplify your code. Some additional notes are below:
class WorkerSerial : public QObject
{
Q_OBJECT
public:
explicit WorkerSerial(QObject *parent = 0) : QObject(parent)
{
// no need for slot -- connect directly to our signal
connect(&m_serial, &MySerial::lineReceived, this, &WorkerSerial::lineReceived);
}
bool open(QString port, quint32 baudrate) { return m_serial.open(port, baudrate); }
bool isOpen() { return m_serial.serial().isOpen(); }
QStringList ports() { return m_serial.ports(); }
private:
// doesn't need to be a pointer
MySerial m_serial;
signals:
void lineReceived(const QByteArray& line);
public slots:
void close() { m_serial.close(); }
void send(const QByteArray& data)
{
// "lengthy" function call
qDebug() << m_serial.sendMessage(data, true);
}
};
class Sender : public QObject
{
Q_OBJECT
public:
void send(const QByteArray& data) { emit sig_send(data); }
signals:
void sig_send(const QByteArray& data);
}
...
WorkerSerial m_worker;
m_worker.open("COM1", 250000));
QThread m_thread;
m_worker.moveToThread(&m_thread);
m_thread.start();
Sender m_sender;
QObject::connect(&m_sender, &Sender::sig_send, &m_worker, &WorkerSerial::send, Qt::QueuedConnection);
m_sender.send("Hello World!");
m_thread.quit();
m_thread.wait();
m_worker.close();
Additional notes:
I've added a Sender class that queues calls to WorkerSerial::send() on a different thread. If you have a signal in your code, which triggers calls to WorkerSerial::send() you don't need this class.
Once you move m_worker to another thread, it is unsafe to access any of its methods, unless (a) they are guaranteed to be thread-safe; or (b) you serialize (protect) access to m_serial with a mutex.
NOTE: this is not a fully working solution (yet). But I think it's better to post it as an answer than add another piece of code in the original question.
I managed to a different approach. Perhaps it looks weird... but it should have some advantages:
it guarantees the order of the messages to be transmitted
using a QQueue I can know the queue size or other information about
the "variable shadowing" mechanism should allow to safely communicate with the thread
Here the code, below the two last issues:
#ifndef WORKERSERIAL_H
#define WORKERSERIAL_H
#include <QObject>
#include <QThread>
#include <QQueue>
#include <QMutex>
#include <QDebug>
#include "myserial.h"
class WorkerSerial : public QThread
{
Q_OBJECT
public:
explicit WorkerSerial(QObject *parent = 0, int timeout = 0) : QThread(parent), timeout(timeout)
{
quit = false;
command = None;
}
~WorkerSerial()
{
mutex.lock();
quit = true;
mutex.unlock();
wait();
}
void open(QString port, quint32 baudrate)
{
mutex.lock();
this->port = port;
this->baudrate = baudrate;
command = Open;
mutex.unlock();
}
void close()
{
mutex.lock();
command = Close;
mutex.unlock();
}
void stop()
{
mutex.lock();
command = Quit;
mutex.unlock();
}
private:
enum Commands
{
None,
Open,
Close,
Quit
};
QQueue<QString> m_queue;
QMutex mutex;
int timeout;
int command;
QString port;
int baudrate;
QString request;
bool quit;
signals:
void portStateChanged(bool isOpen);
void lineReceived(QByteArray line);
public slots:
void send(QString data)
{
mutex.lock();
m_queue.enqueue(data);
mutex.unlock();
}
void run()
{
MySerial serial;
QString _port;
int _baudrate;
int _timeout;
QString _request = "";
int _command = None;
connect(&serial, &MySerial::lineReceived, this, &WorkerSerial::lineReceived);
mutex.lock();
_timeout = timeout;
mutex.unlock();
forever {
mutex.lock();
_command = command;
command = None;
if (!m_queue.isEmpty()) _request = m_queue.dequeue();
mutex.unlock();
switch (_command) {
case Open:
mutex.lock();
_port = port;
_baudrate = baudrate;
mutex.unlock();
if (serial.open(_port, _baudrate)) emit portStateChanged(true);
else portStateChanged(false);
break;
case Close:
serial.close();
emit portStateChanged(serial.serial()->isOpen());
break;
case Quit:
serial.close();
return;
break;
default:
break;
}
if (!_request.isEmpty())
{
qDebug() << serial.sendMessage(_request.toLocal8Bit(), true);
_request = "";
}
msleep(1);
}
}
};
#endif // WORKERSERIAL_H
Here how to use it:
WorkerSerial *worker = new WorkerSerial(this);
connect(worker , &WorkerSerial::lineReceived, this, &MainWindow::lineReceived);
connect(worker , &WorkerSerial::portStateChanged, this, &MainWindow::portStateChanged);
worker->start();
// ..
worker->open("COM2", 250000);
worker->send("Hello World");
// ..
worker->stop();
worker->wait(1000);
It "works" but there are two main issues:
the serialport is opened but the data are not actually transmitted. The MySerial class is working because if I do the following, bypassing the WorkerSerial:
MySerial serial;
serial.open("COM2", 250000);
serial.sendMessage("Hello World!", true);
the data is sent! Hence there is still something wrong in my WorkerSerial.
due to the forever loop, I need to insert a small delay, otherwise the CPU will run up to 100% doing nothing.
Related
QThread documentation suggests two ways to make code run in a separate thread. If I subclass QThread and reimplement run(), then I get
QBasicTimer::start: Timers cannot be started from another thread
-
#include <QWidget>
#include <QThread>
#include <QBasicTimer>
#include <QDebug>
#include <QEvent>
#include <QCoreApplication>
class Worker : public QThread
{
Q_OBJECT
int id;
bool m_abort = false;
bool compute = false;
public:
Worker() {}
protected:
void timerEvent(QTimerEvent *event) override {
if (event->timerId() == id) {
compute = true;
} else {
QObject::timerEvent(event);
}
}
public slots:
void abort() {m_abort = true;}
void run() {
qDebug() << QThread::currentThreadId();
QBasicTimer timer;
id = timer.timerId();
timer.start(1000, this);
forever {
if (m_abort) break;
QCoreApplication::processEvents();
if (compute)
qDebug() << "computed";
compute = false;
}
}
};
class MainWidget : public QWidget
{
Q_OBJECT
QThread thread;
Worker* worker;
public:
MainWidget()
{
qDebug() << QThread::currentThreadId();
worker = new Worker;
worker->start();
}
~MainWidget(){worker->abort();}
};
1) Is the timer being started from another thread?
2) Why I don't get that warning when QBasicTimer is replaced by QTimer?
3) Why I don't get that warning when using moveToThread?
#include <QWidget>
#include <QThread>
#include <QBasicTimer>
#include <QDebug>
#include <QEvent>
#include <QCoreApplication>
class Worker : public QObject
{
Q_OBJECT
QBasicTimer* timer;
bool m_abort = false;
bool compute = false;
public:
Worker() {}
protected:
void timerEvent(QTimerEvent *event) override {
if (event->timerId() == timer->timerId()) {
compute = true;
} else {
QObject::timerEvent(event);
}
}
public slots:
void abort() {m_abort = true;}
void run() {
timer = new QBasicTimer;
timer->start(1000, this);
forever {
if (m_abort) break;
QCoreApplication::processEvents();
if (compute)
qDebug() << "computed";
compute = false;
}
}
};
class MainWidget : public QWidget
{
Q_OBJECT
QThread thread;
Worker* worker;
public:
MainWidget()
{
worker = new Worker;
worker->moveToThread(&thread);
connect(this, &MainWidget::start, worker, &Worker::run);
thread.start();
emit start();
}
~MainWidget(){worker->abort(); thread.quit(); thread.wait();}
signals:
void start();
};
Regarding the first (non-moveToThread) example...
A quick look at the Qt source for QBasicTimer::start shows the following...
void QBasicTimer::start(int msec, QObject *obj)
{
QAbstractEventDispatcher *eventDispatcher = QAbstractEventDispatcher::instance();
// ...
if (Q_UNLIKELY(obj && obj->thread() != eventDispatcher->thread())) {
qWarning("QBasicTimer::start: Timers cannot be started from another thread");
return;
}
So it expects its second argument obj to have a thread affinity equal to the current thread.
In your Worker::run implementation, however, you have...
timer.start(1000, this);
In this context the current thread is the new thread created by the QThread instance but this refers to the QWorker instance created by the MainWidget on the main GUI thread. Hence the warning.
Edit 1:
To the question...
why it works with moveToThread()?
Consider the implementation of the MainWidget ctor...
MainWidget()
{
worker = new Worker;
worker->moveToThread(&thread);
connect(this, &MainWidget::start, worker, &Worker::run);
thread.start();
emit start();
}
By the time Worker::run is called the Worker instance has been moved to the new thread. So when the line...
timer.start(1000, this);
executes, this (which refers to the Worker instance) is on the current thread and the thread affinity test in QBasicTimer::start passes without warning.
Sorry if the above is a bit convoluted but the important thing is to consider the thread affinity of the second arg to QBasicTimer::start: it must be the currently running thread.
Here: Qt multi-thread with GUI I've learned how to create a background worker (the Engine class).
In that class, I have a QSerialPort object, that runs in the main thread (see How to setup QSerialPort on a separate thread?).
I'm using the signal/slot mechanism to send/receive data. But it works in one-way only. The signals emitted from the Engine object ("worker thread") are received by the QSerialPort ("main thread"). The viceversa doesn't work: any signal emitted from QSerialPort is not received by the Engine.
engine.h
#ifndef ENGINE_H
#define ENGINE_H
#include <QObject>
#include <QTimer>
#include "myserial.h"
class Engine : public QObject
{
Q_OBJECT
public:
explicit Engine(QObject *parent = 0);
private:
QTimer m_timer;
MySerial m_serial;
signals:
void serialSendMessage(QByteArray data);
private slots:
void lineReceived(QByteArray line);
void foo();
public slots:
void run();
void open(QString port, quint32 baudrate);
void close();
};
#endif // ENGINE_H
engine.c
#include "engine.h"
#include <QDebug>
Engine::Engine(QObject *parent) : QObject(parent)
{
connect(&m_timer, &QTimer::timeout, this, &Engine::foo);
m_timer.setInterval(200);
}
// THIS IS NEVER EXECUTED!
void Engine::lineReceived(QByteArray line)
{
qDebug() << line;
}
// THIS IS RECEIVED BY QSERIALPORT
void Engine::foo()
{
emit serialSendMessage("Hello World!");
}
void Engine::run()
{
// THIS DOESN'T WORK!
connect(&m_serial, &MySerial::lineReceived, this, &Engine::lineReceived);
// THIS WORK!
connect(this, &Engine::serialSendMessage, &m_serial, &MySerial::sendMessage);
}
void Engine::open(QString port, quint32 baudrate)
{
m_serial.open(port, baudrate);
QTimer::singleShot(0, &m_timer, static_cast<void (QTimer::*)(void)>(&QTimer::start));
}
void Engine::close()
{
m_serial.close();
}
MySerial.h
#ifndef MYSERIAL_H
#define MYSERIAL_H
#include <QObject>
#include <QtSerialPort/QSerialPort>
#include <QtSerialPort/QSerialPortInfo>
class MySerial : public QSerialPort {
Q_OBJECT
public:
explicit MySerial(QObject *parent = 0);
bool open(QString port, quint32 baudrate);
using QSerialPort::open;
QByteArray sendMessage(QByteArray data, bool nmea);
signals:
void lineReceived(QByteArray line);
private slots:
void onReadyRead();
};
#endif // MYSERIAL_H
MySerial.c
#include "myserial.h"
#include <QDebug>
MySerial::MySerial(QObject *parent) : QSerialPort(parent) {
}
bool MySerial::open(QString port, quint32 baudrate)
{
disconnect(this, 0, 0, 0);
connect(this, &FemtoSerial::readyRead, this, &MySerial::onReadyRead);
setPortName(port);
if (!open(QIODevice::ReadWrite)) return false;
setDataBits(QSerialPort::Data8);
setParity(QSerialPort::NoParity);
setStopBits(QSerialPort::OneStop);
setBaudRate(baudrate);
setFlowControl(QSerialPort::NoFlowControl);
return true;
}
void MySerial::onReadyRead() {
static QList<QByteArray> lines;
static QByteArray buffer;
buffer += readAll();
int index = buffer.indexOf("\r");
while (index != -1) {
lines.append(buffer.left(index + 1));
buffer = buffer.mid(index + 1);
index = buffer.indexOf("\r");
}
// THIS SIGNAL IS EMITTED!
while (!lines.isEmpty()) emit lineReceived(lines.takeFirst());
}
QByteArray MySerial::sendMessage(QByteArray data) {
write(data);
return data;
}
EDIT
Trying to add a QEventLoop:
void Engine::run()
{
QEventLoop loop;
connect(&m_serial, &MySerial::lineReceived, this, &Engine::lineReceived);
connect(this, &Engine::serialSendMessage, &m_serial, &MySerial::sendMessage);
loop.exec();
}
The behavior is the same: the data is sent, but the receiving slot is never executed.
bool MySerial::open(QString port, quint32 baudrate)
{
//<s>disconnect(this, 0, 0, 0);</s> // <--- strikeout
connect(this, &FemtoSerial::readyRead, this, &MySerial::onReadyRead);
// ...
}
From the documentation:
Disconnect everything connected to an object's signals
disconnect(myObject, 0, 0, 0);
it means from the signals of myObject not the other way round.
That line prevents the slot in Engine to be executed because it has just disconnected from the source signal.
Engine might disconnect when the port closes, to avoid multiple connections on the next opening.
I am using QLocalSocket and QLocalServer for inter process communication on Windows 7 using VS 2010 and Qt 5.5.1.
After sending over 256 messages to the other process the destructor in CIPSocket freezes. I traced the problem to a signal-slot problem in qtbase\src\corelib\ioqwinoverlappedionotifier.cpp where the emitted signal _q_notify() in notify(DWORD numberOfBytes, DWORD errorCode, OVERLAPPED *overlapped) does not result in a call of _q_notified(). Therefore the Semaphore hSemaphore exceeds its max-count, resulting in the deadlock in the destructor.
What could be the reason for the signal-slot not working? I could not find any disconnects or block signals.
Thanks in advance.
main.cpp:
#include "main.h"
#include <QtCore/QCoreApplication>
#include <QtCore/QThread>
#include <iostream>
int main(int argc, char *argv[])
{
printf("Server (0) or Socket (1)?\n");
char c = getchar();
if (c == '0') {
QCoreApplication app(argc, argv);
CIPServer server;
app.exec();
}
else if (c == '1') {
CIPSocket socket;
for (unsigned int i = 0; i <= 256; ++i) {
socket.update(i);
QThread::msleep(10);
}
}
}
/*--------------------------------------------------------------------------
CIPSocket
----------------------------------------------------------------------------*/
CIPSocket::CIPSocket()
: m_bIsReady(false)
{
m_pSocket = new QLocalSocket(this);
m_stream.setDevice(m_pSocket);
connect(m_pSocket, SIGNAL(connected()), this, SLOT(connectionReady()));
connect(m_pSocket, SIGNAL(disconnected()), this, SLOT(connectionLost()));
m_pSocket->connectToServer("DemoServer");
}
CIPSocket::~CIPSocket()
{
delete m_pSocket;
m_pSocket = NULL;
}
void CIPSocket::update(int i)
{
if (m_bIsReady)
m_stream << i;
}
void CIPSocket::connectionReady()
{ m_bIsReady = true; }
void CIPSocket::connectionLost()
{ m_bIsReady = false; }
/*--------------------------------------------------------------------------
CIPServer
----------------------------------------------------------------------------*/
CIPServer::CIPServer(QObject* parent)
: QLocalServer(parent)
{
if (!listen("DemoServer")) {
throw ("Could not connect to 'DemoServer'");
}
connect(this, SIGNAL(newConnection()), this, SLOT(socketConnected()));
}
CIPServer::~CIPServer()
{}
void CIPServer::socketConnected()
{
qDebug() << "Connected";
m_pConnection = nextPendingConnection();
m_stream.setDevice(m_pConnection);
connect(m_pConnection, SIGNAL(disconnected()), m_pConnection, SLOT(deleteLater()));
connect(m_pConnection, SIGNAL(readyRead()), this, SLOT(update()));
}
void CIPServer::update()
{
if (m_pConnection->bytesAvailable() >= 4) {
int i;
m_stream >> i;
qDebug() << i;
}
}
main.h:
#include <QtNetwork/QLocalServer>
#include <QtNetwork/QLocalSocket>
#include <QtCore/QDataStream>
#include <QtCore/QThread>
/// \brief Creates a socket for inter-process communication
class CIPSocket
: public QObject
{
Q_OBJECT;
public:
/// Constructor
CIPSocket();
/// Destructor
virtual ~CIPSocket();
/// Send the data
void update(int i);
public slots:
/// Enables updating
void connectionReady();
/// Disables updating
void connectionLost();
private:
/// The target stream
QDataStream m_stream;
/// The socket connecting to server
QLocalSocket* m_pSocket;
/// Indicates if the socket is connected
bool m_bIsReady;
};
/// \brief Creates a server for inter-process communication
class CIPServer
: public QLocalServer
{
Q_OBJECT;
public:
/// Constructor
CIPServer(QObject* parent = NULL);
/// Destructor
virtual ~CIPServer();
/// Starts the server
void start();
private slots:
/// Connects the socket to the stream and to the update function
void socketConnected();
/// Reads the data from the stream and emits a the results
void update();
private:
/// The currently connected socket
QLocalSocket* m_pConnection;
/// The incoming stream
QDataStream m_stream;
};
demo.pro:
CONFIG += qt debug
QT += network
HEADERS += main.h
SOURCES += main.cpp
CONFIG += console
The error occurs due to the event loop not running. Starting QCoreApplication starts the event loop, but waits for the application to quit. Therefore the sending has to be done in another thread.
Attached code shows the correct usage.
main.cpp:
#include "main.h"
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
printf("Server (0) or Socket (1)?\n");
char c = getchar();
if (c == '0') {
CIPServer server;
QCoreApplication::exec();
}
else if (c == '1') {
CIPSocket socket;
CSender sender(500);
QObject::connect(&sender, SIGNAL(sendMessage(int)), &socket, SLOT(update(int)));
QObject::connect(&sender, SIGNAL(allMessagesSent()), &socket, SLOT(close()));
sender.start();
QCoreApplication::exec();
}
}
/*--------------------------------------------------------------------------
CIPSocket
----------------------------------------------------------------------------*/
CIPSocket::CIPSocket()
: m_bIsReady(false)
{
m_pSocket = new QLocalSocket(this);
m_stream.setDevice(m_pSocket);
connect(m_pSocket, SIGNAL(connected()), this, SLOT(connectionReady()));
connect(m_pSocket, SIGNAL(disconnected()), this, SLOT(connectionLost()));
m_pSocket->connectToServer("DemoServer");
}
CIPSocket::~CIPSocket()
{
delete m_pSocket;
m_pSocket = NULL;
}
void CIPSocket::update(int i)
{
if (m_bIsReady)
m_stream << i;
}
void CIPSocket::connectionReady()
{ m_bIsReady = true; }
void CIPSocket::connectionLost()
{ m_bIsReady = false; }
void CIPSocket::close()
{ QCoreApplication::exit(); }
/*--------------------------------------------------------------------------
CIPServer
----------------------------------------------------------------------------*/
CIPServer::CIPServer(QObject* parent)
: QLocalServer(parent)
{
if (!listen("DemoServer")) {
throw ("Could not connect to 'DemoServer'");
}
connect(this, SIGNAL(newConnection()), this, SLOT(socketConnected()));
}
CIPServer::~CIPServer()
{}
void CIPServer::socketConnected()
{
qDebug() << "Connected";
m_pConnection = nextPendingConnection();
m_stream.setDevice(m_pConnection);
connect(m_pConnection, SIGNAL(disconnected()), m_pConnection, SLOT(deleteLater()));
connect(m_pConnection, SIGNAL(readyRead()), this, SLOT(update()));
connect(m_pConnection, SIGNAL(disconnected()), this, SLOT(close()));
}
void CIPServer::update()
{
if (m_pConnection->bytesAvailable() >= 4) {
int i;
m_stream >> i;
qDebug() << i;
}
}
void CIPServer::close()
{ QCoreApplication::exit(); }
/*--------------------------------------------------------------------------
CSender
----------------------------------------------------------------------------*/
CSender::CSender(int iNumMessages)
: m_iNumMessages(iNumMessages)
{}
CSender::~CSender()
{}
void CSender::run()
{
while (m_iNumMessages > 0) {
emit sendMessage(m_iNumMessages);
msleep(10);
m_iNumMessages--;
}
emit allMessagesSent();
}
main.h:
#include <QtNetwork/QLocalServer>
#include <QtNetwork/QLocalSocket>
#include <QtCore/QDataStream>
#include <QtCore/QThread>
#include <QtCore/QCoreApplication>
/// \brief Creates a socket for inter-process communication
class CIPSocket
: public QObject
{
Q_OBJECT;
public:
/// Constructor
CIPSocket();
/// Destructor
virtual ~CIPSocket();
public slots:
/// Enables updating
void connectionReady();
/// Disables updating
void connectionLost();
/// Send the data
void update(int i);
/// Close the application
void close();
private:
/// The target stream
QDataStream m_stream;
/// The socket connecting to server
QLocalSocket* m_pSocket;
/// Indicates if the socket is connected
bool m_bIsReady;
};
/// \brief Creates a server for inter-process communication
class CIPServer
: public QLocalServer
{
Q_OBJECT;
public:
/// Constructor
CIPServer(QObject* parent = NULL);
/// Destructor
virtual ~CIPServer();
private slots:
/// Connects the socket to the stream and to the update function
void socketConnected();
/// Reads the data from the stream and emits a the results
void update();
/// Close the application
void close();
private:
/// The currently connected socket
QLocalSocket* m_pConnection;
/// The incoming stream
QDataStream m_stream;
};
/// \brief Sends the messages via CIPSocket
class CSender
: public QThread
{
Q_OBJECT;
public:
/// Constructor
CSender(int iNumMessages);
/// Destructor
virtual ~CSender();
/// Sends the requestet number of messages in 10 ms steps
virtual void run();
signals:
/// Sends the message via the CIPSocket
void sendMessage(int);
/// Informs about all messages being sent
void allMessagesSent();
private:
/// The number of messages to send
int m_iNumMessages;
};
The program works well as it is programmed: try the following:
start server - start client, sends data - server receives data - stop client - start client once more, sends data - server receives data ...
The program has no deadlock, and it does not freeze! The program is waiting for events in Qt eventloop in line 13:
app.exec();
The question is: what shall it do?
I assume, you like to quit the program after work, then insert:
void CIPServer::socketConnected()
{ . . .
connect(m_pConnection, SIGNAL(disconnected()), this, SLOT(theend()));
}
void CIPServer::theend()
{
QCoreApplication::quit();
}
Try these actions:
start server - start client, sends data - server receives data - stop client – server stops also
I want to communicate via the serial port in Qt (Qt 5.4.0, Ubuntu 14.04.3), and for decreasing the workload I want to shift the communication part into a second thread. Thus I created the following files:
serial_controller.cpp:
#include "serial_controller.h"
serial_controller_worker::serial_controller_worker(const QString &portname, int waitTimeout, int BaudRate)
{
this->portName = portname;
this->waitTimeout = waitTimeout;
this->baudrate = BaudRate;
this->serial.setPortName(this->portName);
this->serial.setBaudRate(this->baudrate);
if (!serial.open(QIODevice::ReadWrite))
{
emit error(tr("Can't open %1, error code %2").arg(portName).arg(serial.error()));
qDebug() << tr("Can't open %1, error code %2").arg(portName).arg(serial.error());
return;
}
else
{
emit error(tr("Opened %1").arg(portName));
qDebug() << tr("Opened %1").arg(portName);
}
}
serial_controller_worker::~serial_controller_worker()
{
this->serial.close();
}
void serial_controller_worker::process_data()
{
bool newData = false;
bool run = false;
this->mutex.lock();
newData = this->sendNewData;
run = this->recvLoop;
this->mutex.unlock();
if(run == false)
{
qDebug() << "Run is false, returning!";
return;
}
else
{
if(newData == true)
{
qDebug() << "TransAction started!";
QByteArray requestData = request.toLocal8Bit();
qDebug() << "Writing data: " << requestData;
serial.write(requestData);
qDebug() << "Data written";
if(serial.waitForBytesWritten(waitTimeout))
{
if(serial.waitForReadyRead(waitTimeout))
{
qDebug() << "Waiting for data!";
QByteArray responseData = serial.readAll();
while(serial.waitForReadyRead(10))
responseData += serial.readAll();
QString response(responseData);
QByteArray response_arr = response.toLocal8Bit();
qDebug() << "Response is: " << response_arr.toHex();
emit this->response(response);
}
else
{
qDebug() << "Wait read response timeout";
emit this->timeout(tr("Wait read response timeout %1").arg(QTime::currentTime().toString()));
}
}
else
{
qDebug() << "Wait write request timeout!";
emit this->timeout(tr("Wait write request timeout %1").arg(QTime::currentTime().toString()));
}
mutex.lock();
this->sendNewData = false;
mutex.unlock();
}
QThread::msleep(10);
this->process_data();
}
}
void serial_controller_worker::transaction(const QString &request)
{
mutex.lock();
this->sendNewData = true;
this->recvLoop = true;
this->request = request;
mutex.unlock();
this->process_data();
}
//Serial_controller functions
serial_controller::serial_controller(const QString &portName, int waitTimeout, int BaudRate)
{
serial_controller_worker *newWorker = new serial_controller_worker(portName, waitTimeout, BaudRate);
newWorker->moveToThread(&workerThread);
connect(&workerThread, &QThread::finished, newWorker, &QObject::deleteLater);
connect(this, &serial_controller::newTransaction, newWorker, &serial_controller_worker::transaction);
connect(newWorker, &serial_controller_worker::response, this, &serial_controller::response_slot);
workerThread.start();
}
serial_controller::~serial_controller()
{
workerThread.quit();
workerThread.wait();
}
void serial_controller::transaction(const QString &request)
{
emit this->newTransaction(request);
}
void serial_controller::response_slot(QString response)
{
emit this->response(response);
}
serial_controller.h:
#include <QObject>
#include <QThread>
#include <QVector>
#include <memory>
#include <QtSerialPort/QtSerialPort>
class serial_controller_worker: public QObject
{
Q_OBJECT
private:
QString portName;
QString request;
int waitTimeout;
QMutex mutex;
QWaitCondition cond;
int baudrate;
QSerialPort serial;
bool quit;
bool sendNewData = false;
bool recvLoop = false;
public slots:
void transaction(const QString &request);
signals:
void response(QString s);
void error(const QString &s);
void timeout(const QString &s);
public:
serial_controller_worker(const QString &portname, int waitTimeout, int BaudRate);
~serial_controller_worker();
void process_data(void);
};
class serial_controller: public QObject
{
Q_OBJECT
private:
QThread workerThread;
QString portName;
QString request;
int waitTimeout;
QMutex mutex;
QWaitCondition cond;
int baudrate;
QSerialPort serial;
public:
serial_controller(const QString &portName, int waitTimeout, int BaudRate);
~serial_controller();
public slots:
void transaction(const QString &request);
void response_slot(QString response);
signals:
void newTransaction(const QString &request);
void response(QString s);
void error(const QString &s);
void timeout(const QString &s);
};
Now I have the problem that
a) when calling serial_controller_worker::process_data(), I get the output
Writing data: "..."
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QSerialPort(0x2533e60), parent's thread is QThread(0x2486850), current thread is QThread(0x25341e0)
Data written
QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread
Then I get the answer, but afterwards I am not able to call serial_controller_worker::transaction() anymore, I simply get no notice that it is executed. Why? What am I doing wrong here?
Edit 1: Solution to problem 1 is:
Replacing
QSerialPort serial;
with
QSerialPort *serial;
serial = new QSerialPort(this);
Still the second problem is unsolved.
Ok, found the answer.
The solution to the first problem is:
I have to replace
QSerialPort serial;
with
QSerialPort *serial;
serial = new QSerialPort(this);
The solution to the second problem:
Remove the line
this->process_data();
from the function process_data()
Sorry for basic question. I'm trying to show json in QPlainTextWidget. I have api function which have console output and contains all needed data. Looks like that:
int iperf_run_server(struct iperf_test *test)
{
int result, s, streams_accepted;
fd_set read_set, write_set;
struct iperf_stream *sp;
struct timeval now;
struct timeval* timeout;
......
if (test->json_output)
if (iperf_json_start(test) < 0)
return -1;
if (test->json_output) {
cJSON_AddItemToObject(test->json_start, "version", cJSON_CreateString(version));
cJSON_AddItemToObject(test->json_start, "system_info", cJSON_CreateString(get_system_info()));
} else if (test->verbose) {
iprintf(test, "%s\n", version);
iprintf(test, "%s", "");
fflush(stdout);
printf("%s\n", get_system_info());
}
.....
cleanup_server(test);
if (test->json_output) {
if (iperf_json_finish(test) < 0)
return -1;
}
....
return 0;
}
For now I have first thread with my gui, and second thread, contains class which run this function on a signal. All things works normally, but i'm not fully understand, how I can "stop" iperf_run_server for "reading/buffering" output, without any changes in api.
The simplest thing to do would be to collect each message in a string, and emit a signal from the object running in the second thread. You can connect that signal to a slot in an object in the GUI thread.A zero-timeout timer is invoked each time the event loop is done processing other events - it is a useful mechanism to leverage to run things "continuously".
For example:
#include <QApplication>
#include <QPlainTextEdit>
#include <QThread>
#include <QBasicTimer>
#include <QTextStream>
//! A thread that's always safe to destruct.
class Thread : public QThread {
private:
// This is a final class.
using QThread::run;
public:
Thread(QObject * parent = 0) : QThread(parent) {}
~Thread() {
quit();
wait();
}
};
class IperfTester : public QObject {
Q_OBJECT
struct Test { int n; Test(int n_) : n(n_) {} };
QList<Test> m_tests;
QBasicTimer m_timer;
public:
IperfTester(QObject * parent = 0) : QObject(parent) {
for (int i = 0; i < 50; ++i) m_tests << Test(i+1);
}
//! Run the tests. This function is thread-safe.
Q_SLOT void runTests() {
QMetaObject::invokeMethod(this, "runTestsImpl");
}
Q_SIGNAL void message(const QString &);
private:
Q_INVOKABLE void runTestsImpl() {
m_timer.start(0, this);
}
void timerEvent(QTimerEvent * ev) {
if (ev->timerId() != m_timer.timerId()) return;
if (m_tests.isEmpty()) {
m_timer.stop();
return;
}
runTest(m_tests.first());
m_tests.removeFirst();
}
void runTest(Test & test) {
// do the work
QString msg;
QTextStream s(&msg);
s << "Version:" << "3.11" << "\n";
s << "Number:" << test.n << "\n";
emit message(msg);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QPlainTextEdit log;
// This order is important: the thread must be defined after the object
// to be moved into the thread.
IperfTester tester;
Thread thread;
tester.moveToThread(&thread);
thread.start();
log.connect(&tester, SIGNAL(message(QString)), SLOT(appendPlainText(QString)));
log.show();
tester.runTests();
return a.exec();
// Here, the thread is stopped and destructed first, following by a now threadless
// tester. It would be an error if the tester object was destructed while its
// thread existed (even if it was stopped!).
}
#include "main.moc"