0

Well, I have created a program which picks up the input signal from serial input. I can successfully receive the data transmitted from the device through UART. I want to terminate the thread after achieving certain conditions( such as receiving more than 5 bytes, etc.) I think the problem is how to terminate the thread in Qt correctly, but I couldn't find the way. The program seems falls into deadlock after calling the exec() in the sub function. Can anyone help with that problem? Thank you very much!

Here's my header file:

#ifndef SERIALTHREAD
#define SERIALTHREAD

#include <QtSerialPort/QSerialPort>
#include <QDebug>

#include <QString>
#include <QThread>
#include <QtCore>

#include <iostream>
#include <fstream>


class SerialControlThread : public QThread
{
   Q_OBJECT
  public:

  explicit SerialControlThread(QString ComPort,QObject *parent = 0);
  ~SerialControlThread(); // Destructor

    bool openSerialPort();
    void closeSerialPort();
    void run();
    bool TelltoExit();
    void StarttoRun();


   private:

     int DataCount;

     QString ComPortNumber;
     QSerialPort *serial;

     int* VoltageStorage; // Total 3 channels, each channel takes 10 data

     unsigned int Channel_A[10]; // Channel_A is for Phase Tx s

     int DataCountIndexA; // This is how many data has been sent to the buffer;

     int SentDataCount;
     unsigned char StoreDataBuffer[2];

     unsigned char TotalDataCounter;

     std::ofstream write;

  signals:
      void BufferisFull(int*);
      void TimeToQuit();



public slots:


private slots:
    void readData();
    void handleError(QSerialPort::SerialPortError error);

};

#endif // SERIALTHREAD

This is the.cpp

#include "serialcontrol.h"
#include <iostream>

SerialControlThread::SerialControlThread(QString ComPort,QObject *parent) :
    QThread(parent),ComPortNumber(ComPort)
{

    DataCountIndexA=0;

    DataCount=0;

    serial = new QSerialPort(this);

    connect(this,SIGNAL(TimeToQuit()),this,SLOT(quit()));\
    connect(serial, SIGNAL(readyRead()), this, SLOT(readData()));
    connect(serial, SIGNAL(error(QSerialPort::SerialPortError)), this,
            SLOT(handleError(QSerialPort::SerialPortError)));

    for (int i=0;i<10;i++)
      Channel_A[i]=0;


}

SerialControlThread::~SerialControlThread()
{

    this->closeSerialPort();
    delete serial;
}

bool SerialControlThread::openSerialPort()
{

  //  std::cout << "Hey I am in serial function" << std::endl;

    serial->setPortName(ComPortNumber) ;
    serial->setBaudRate(QSerialPort::Baud9600); //This can be set through menu in the future
    serial->setDataBits(QSerialPort::Data8); // A packets contains 8 bits ( 3 for signature bits)
    serial->setParity(QSerialPort::NoParity);
    serial->setStopBits(QSerialPort::OneStop);
    serial->setFlowControl(QSerialPort::NoFlowControl);

    if (!(serial->open(QIODevice::ReadWrite))) {

    return false;  // return false when the device can't be opened
     }else
    {
     return true;} // return true when the device is avalaible

}

void SerialControlThread::closeSerialPort()
{
    if (serial->isOpen())
        serial->close();
}

void SerialControlThread::handleError(QSerialPort::SerialPortError error)
{


}

void SerialControlThread::readData()
{
    QByteArray data=serial->read(100);
    const char *TempChar=data.data();

    std::cout << TempChar << std::endl;

    DataCount++;

   if(DataCount>=4)
    {
       std::cout << "I am bigger than 4" << std::endl;
       this->quit();
     }
   }

}

void SerialControlThread::run()
{





}
bool SerialControlThread::TelltoExit()
{

}

void SerialControlThread::StarttoRun()
{

        // Sending the msp430 S to activate the following sequence
            const char *temp="S";
           serial->write(temp);
           serial->waitForBytesWritten(30000);
           this->exec();

}

This is the main.cpp

#include <QCoreApplication>
#include <QtSerialPort/QSerialPortInfo>
#include <QList>
#include <iostream>
#include <QString>
#include <QDebug>
#include <QSerialPort>

#include "serialcontrol.h"

using namespace std;


int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);



   int AvailablePorts=QSerialPortInfo::availablePorts().count();
   QList<QSerialPortInfo> SerialObject=QSerialPortInfo::availablePorts();


   cout <<  "There are total: " << SerialObject.count() <<  " available ports " << endl << endl;


   QString description;



   for (int i=0;i<AvailablePorts;i++)
   {
       cout << "The " << i+1 << " com port is :";
       qDebug() <<  SerialObject[i].portName();
       qDebug() << "Description : " <<  SerialObject[i].description();
       qDebug() << "Manufacturer: " <<  SerialObject[i].manufacturer();

       cout << endl;
   }



   SerialControlThread *RunThread=new SerialControlThread(SerialObject[0].portName(),&a);


   cout << RunThread->openSerialPort() << endl;
   RunThread->StarttoRun();




   cout << "I am out of here" << endl;


    delete RunThread;






    return a.exec();
}

I wish to close the thread( back to the main function) when the buffer has received more than 4 data, but it doesn't.

It is my output

There are total: 1 available ports

The 1 com port is :"COM8"
Description :  "MSP430 Application UART"
Manufacturer:  "Texas Instruments"

1
0
1
2
3
I am bigger than 4
4
I am bigger than 4
5
I am bigger than 4
6
I am bigger than 4
7
I am bigger than 4
8
I am bigger than 4
9
I am bigger than 4

Apparently, the program gets stuck in a loop. I have tried some solutions, but none of these worked.

  • 1
    Brief scan of this code finds: ` delete RunThread; return a.exec();` Not sure about other things but this one is clearly wrong. The main thread is spinning in `a.exec()` and the other thread supposed to be live when main thread is entering its event loop? – Alexander V Feb 15 '17 at 06:35

1 Answers1

0

StartToRun calls QThread::exec in the wrong thread: you call it in the main thread, but it's supposed to be called in the thread itself - from within run().

Alas, SerialControlThread doesn't have to be a thread. Making it a thread forces it to be used in a dedicated thread - that should be a choice left to its user. Perhaps the thread would be shared among other serial controllers, or perhaps it'll do just fine in the main thread. Thus, it should be an object that handles serial data, that has a thread-safe interface so that you can move it to another thread if you wish - but would still work fine in the main thread, and thus has to handle data asynchronously without blocking.

Considering whether one needs to control the worker thread's run status so tightly: an idle thread consumes no resources - its event loop is blocked waiting on new events, its stack eventually gets paged out if there's memory pressure. If one intends to "wake" the thread for each operation, there's no need to be explicit about it: the event loop in the thread behaves that way be default and by design: it wakes when there are new events, such as incoming data, otherwise it sleeps. One shouldn't be stopping the thread then.

The example below shows a very minimal implementation. On the whole it's not very useful other than to demonstrate brevity as a contrast to the length of code in the question - in spite of identical limited functionality. Presumably you have a more complex communications protocol that you wish to handle. You may wish to consider the use of QDataStream read transactions to make the reader code more expressive, and using a state machine to represent the protocol.

// https://github.com/KubaO/stackoverflown/tree/master/questions/serial-galore-42241570
#include <QtWidgets>
#include <QtSerialPort>

// See https://stackoverflow.com/q/40382820/1329652
template <typename Fun> void safe(QObject * obj, Fun && fun) {
   Q_ASSERT(obj->thread() || qApp && qApp->thread() == QThread::currentThread());
   if (Q_LIKELY(obj->thread() == QThread::currentThread() || !obj->thread()))
      return fun();
   struct Event : public QEvent {
      using F = typename std::decay<Fun>::type;
      F fun;
      Event(F && fun) : QEvent(QEvent::None), fun(std::move(fun)) {}
      Event(const F & fun) : QEvent(QEvent::None), fun(fun) {}
      ~Event() { fun(); }
   };
   QCoreApplication::postEvent(
            obj->thread() ? obj : qApp, new Event(std::forward<Fun>(fun)));
}

class SerialController : public QObject {
   Q_OBJECT
   QSerialPort m_port{this};
   QByteArray m_rxData;

   void onError(QSerialPort::SerialPortError error) {
      Q_UNUSED(error);
   }
   void onData(const QByteArray & data) {
      m_rxData.append(data);
      qDebug() << "Got" << m_rxData.toHex() << "(" << m_rxData.size() << ") - done.";
      emit hasReply(m_rxData);
   }
   void onData() {
      if (m_port.bytesAvailable() >= 4)
         onData(m_port.readAll());
   }
public:
   explicit SerialController(const QString & port, QObject * parent = nullptr) :
      QObject{parent}
   {
      m_port.setPortName(port);
      connect(&m_port, static_cast<void(QSerialPort::*)(QSerialPort::SerialPortError)>(&QSerialPort::error),
              this, &SerialController::onError);
   }
   ~SerialController() { qDebug() << __FUNCTION__; }
   bool open() {
      m_port.setBaudRate(QSerialPort::Baud9600);
      m_port.setDataBits(QSerialPort::Data8);
      m_port.setParity(QSerialPort::NoParity);
      m_port.setStopBits(QSerialPort::OneStop);
      m_port.setFlowControl(QSerialPort::NoFlowControl);
      return m_port.open(QIODevice::ReadWrite);
   }
   /// This method is thread-safe.
   void start() {
      safe(this, [=]{
         m_port.write("S");
         qDebug() << "Sent data";
      });
   }
   Q_SIGNAL void hasReply(const QByteArray &);
   void injectData(const QByteArray & data) {
      onData(data);
   }
};

QDebug operator<<(QDebug dbg, const QSerialPortInfo & info) {
   dbg << info.portName();
   if (!info.description().isEmpty())
       dbg << " Description: " <<  info.description();
   if (!info.manufacturer().isEmpty())
       dbg << " Manufacturer: " <<  info.manufacturer();
   return dbg;
}

// A thread that starts on construction, and is always safe to destruct.
class RunningThread : public QThread {
   Q_OBJECT
   using QThread::run; // final
public:
   RunningThread(QObject * parent = nullptr) : QThread(parent) { start(); }
   ~RunningThread() { qDebug() << __FUNCTION__; quit(); wait(); }
};

int main(int argc, char *argv[])
{
   QCoreApplication app(argc, argv);
   auto const ports = QSerialPortInfo::availablePorts();
   if (ports.isEmpty())
      qFatal("No serial ports");

   int n{};
   qDebug() << "Available ports:";
   for (auto & port : ports)
      qDebug() << "port[" << n++ << "]: " << port;

   SerialController ctl{ports.at(5).portName()};
   if (!ctl.open())
      qFatal("Open Failed");

   // Optional: the controller will work fine in the main thread.
   if (true) ctl.moveToThread(new RunningThread{&ctl}); // Owns its thread

   // Let's pretend we got a reply;
   QTimer::singleShot(1000, &ctl, [&ctl]{
      ctl.injectData("ABCD");
   });
   QObject::connect(&ctl, &SerialController::hasReply, ctl.thread(), &QThread::quit);
   QObject::connect(&ctl, &SerialController::hasReply, [&]{
      qDebug() << "The controller is done, quitting.";
      app.quit();
   });
   ctl.start();
   return app.exec();
}
#include "main.moc"
Community
  • 1
  • 1
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313