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?