QSerialPort with Thread miss same data



  • Hi everyone,
    I'm using QSerialPort with a thread to avoid missing data, but I didn't. Actually, while the program is idle, so it is on the screen and PC is not performing any other process, it doesn't miss any data. However, during the program is maximized/minimized or the window is resized, it always misses some data, although using with thread. I didn't solve this problem. You can see the codes below.

    I have two classes, one is a serial port and other is mainwindow for an application. The serial port extends from QThread and this thread is started in main class' constructor.

    The MainWindow Constructor:

        SerialPort *serial= new SerialPort();
        serial->moveToThread(&cThread);
    
        connect(this , &MainWindow::finished, &cThread, &QThread::quit);
        connect(this , &MainWindow::destroyed, this, &SerialPort::deleteLater);
        connect(serial, &SerialPort::getData, this, &MainWindow::displayData);
    
        cThread.start();
    

    The Serial Port read thread:

    void SerialPort::newData()
    {
         mutex.lock();
         bArray.insert(bArray.length(),serial->readAll());
         mutex.unlock();
    
         emit getData(&bArray,&mutex);
    }
    

    The SerialPort Constructor:

        serial = new QSerialPort();
        connect(serial, &QSerialPort::readyRead, this,  &SerialPort::newData);
    
        bArray.clear();
    
        fill_serial_ports();
        portName = find_stm32_port();
    
        serial->setReadBufferSize(20*1024*1024); // it's a huge buffer.
        bArray.reserve(20*1024*1024);  // it's a huge buffer.
    
        open_serial_port();
    


  • Hi, in order to manage your stm32 microcontroller using QThread you shouldn't use the events approach but the functions

    waitForReadyRead
    readAll

    Please have a look at the way that the stm32 microcontroller is sending data. Are you managing the buffers?



  • @mrdebug How should I do this? We had done this reading periodically, but someone said that it's wrong method, and they said that you should do like in my previous message.



  • Each one has a different way to write code.
    After hundreds of big installations managing a very lot of different microcontrollers with different protocols I suggest you to use events (connect) where you don't use QThread and viceversa.
    My installations work perfecly, without missing data, h24, without to stop for months.

    At this point please do it how you prefer. My idea was only to send an advise.

    Regards.



  • @mrdebug I also tried without QThread only using readyRead event, then it missed data too.
    I guess to say again, actually while it's idle, not missing, while only GUI's window is being resized or minimized/maximized.It looks as if the serial port is close. Please, you try this condition, even you could hold left mouse button on the windows' border and not leave, then you will see the same problem.



  • I have been struggling with this problem for two weeks, I wrote a bug report about this condition, Assignee said that QSerialPort never loses data, furthermore my code is wrong. I'm very confused, I can't find any solution, don't know where to make mistakes.
    https://bugreports.qt.io/browse/QTBUG-61233



  • It is not so easy to help you without to have the full source and without to know what your microcontroller does.


  • Moderators

    @Gokhan You should post you code without QThread. Multi threading isn't needed actually as you can use signals/slots (not events) and just makes you code more complex.



  • Please try to download this source of an application Qt based that uses QSerialPort
    https://www.linux-apps.com/content/show.php/QtComPort?content=142378
    It uses event approach. Please have a look that if it can help you.
    The application can put in bridge two serial ports in order to monitor what is passing through.



  • @jsulm and @mrdebug I just now tried the terminal program in examples and added only a few lines code in order to start the MCU. It too missed data.
    If USB_Can converter's red led is on, it means that there is a missing transfer. However, while Hercules that is a serial port terminal program using, there is no missing data.
    You can see this in below video.
    Link : https://drive.google.com/file/d/0B7WvtYBLRY0YVFFmRzZvWWZKVFk/view



  • I suggest you to use QtComPort and watch the output in hex format.
    After that please post the output.
    Which kind of protocol are you using? Is there a header, a footer, a crc...???



  • @Gokhan I would say that is expected behaviour. By "resizing" the gui your main thread is blocked/busy with that.

    But the statement is still true, QSerialport does not loose data.
    Your device probably runs in an timeout because it gets no answer from your serialport, because the old data was not collected.

    Seems like Threading your SerialPort-communictation is the only option in this particular case.



  • @J.Hilk However, when I periodically read the serial port buffer via a thread for 100 ms, it returns the null data, so the serial port buffer is seeming empty and the device is giving the error again.



  • @Gokhan
    I downloaded your code,

    take a look here and here and create your QThread, how it should be done ;-)

    And don't use the blocking waitForXxx functions, use signal slots with connects.



  • @J.Hilk thank you, actually, first I wrote it like in my first post, and after that, I saw that it's not working, started to try all possibilities. I guess that there is a bug in Qt, because the assignee wrote a bug about this subject.
    https://bugreports.qt.io/browse/QTBUG-61237

    @mrdebug I have gotten lots of errors with this Project QtComPort, I didn't compile it, guess that it needs a Linux PC.


  • Qt Champions 2016

    Hi.
    It is not in Qt. its a windows/platform thing.
    It is a known thing with windows.
    It applies to all apps. Also those not written in Qt.
    https://stackoverflow.com/questions/18041622/how-do-i-stop-windows-from-blocking-the-program-during-a-window-drag-or-menu-but



  • @mrjj However, another program is working well like you can see my video. Is this big problem for application? e.g my application must immediately give an error in this condition and stop the communication.
    What do you suggest me in order to solve this problem? I had used the LabVIEW before and developed like this application, it had worked flawlessly.


  • Qt Champions 2016

    @Gokhan
    The normal solution is to use a thread.
    Then it wont be interrupted when dragging/resizing.



  • @Gokhan,

    I have added for you an example "qsp-no-freeze-workaround-windows" which demonstrates how it should work with threads!



  • @kuzulis Thank you very much, it's really working well. My first code that is in my first post here looks like this, but there is one different between them, it is inheritance. Is this a problem?

    Also, how should I use qmutex between two classes in order to syncrhronşze? It's not declared the static and global variable is not recommended for it.


  • Qt Champions 2016

    @Gokhan said in QSerialPort with Thread miss same data:

    how should I use qmutex between two classes in order to syncrhronşze?

    What do you need to synchronize ?
    You could use signals to tell other class about new data.



  • @mrjj I must convert the received data to be meaningful, then will show these in tableview. I thought these data periodically will be converted and shown in two different thread for 100milisec.


  • Qt Champions 2016

    @Gokhan
    Hi, you cannot use any widget in any other thread than the main thread.
    But using a signal to tell the some other class about the formatted data will work super.
    https://stackoverflow.com/questions/15276628/send-data-from-worker-thread-to-gui-thread-in-qt



  • @mrjj Don't I need a mutex if use a signal to tell other class? By the way, thread appends the raw array and after slot received a signal, it will convert the raw array and then clear it. Doesn't it need a mutex to do this process?


  • Qt Champions 2016

    @Gokhan
    The signal will contain a copy of the data so in most cases you do not need to protect it.
    But depends on your design. If its possible that array is being written to , while you clear it then, yes
    you need concurrency control.



  • @mrjj Sometimes the array can be huge size, e.g 100K and conversion can take a long time furthermore it will show these data in tableview. Do not you think it takes a long time? Then which method do you suggest me to use?


  • Qt Champions 2016

    @Gokhan
    Ok, if that big, then a processing/convert thread seems a good idea.



  • @mrjj I'm thinking of using total three thread, one serial port that is receiving data, second converting the raw data to be meaningful data and the last will show these data in a table. Well, should I use mutex? If yes, How? (a global or another method)



  • To synchronize I used a global mutex, while running the convert class, everything is normal and it has converted successfully.
    However, another class to show data has a problem, it's giving an error about visual c++. Also while holding left button on the bar it's not running.
    When rowData<< new QStandardItem(strTableCount); and tableView->scrollToBottom(); is actived, it gives immediately an error. Where do I make mistake again?

    class DisplayData : public QThread
    {
    public:
        DisplayData () {}
    
        QTableView *tableView;
        uint32_t tableCount=0;
        uint32_t bufferSize = 100;
        QStandardItemModel *model;
    
        void init(QTableView *table){
    
            tableView = table;
            model = new QStandardItemModel();
            tableView->setModel(model);
            model->setHorizontalHeaderLabels(QString("Number;Time(sec);ID;Ext/Std;Rtr/Data;Lenght;D0;D1;D2;D3;D4;D5;D6;D7").split(";"));
            QHeaderView *verticalHeader = tableView->verticalHeader();
            verticalHeader->setVisible(false);
            verticalHeader->setSectionResizeMode(QHeaderView::Fixed);
            verticalHeader->setDefaultSectionSize(20);
    
            tableView->setColumnWidth(0,60);
            tableView->setColumnWidth(1,70);
            tableView->setColumnWidth(2,70);
            tableView->setColumnWidth(3,50);
            tableView->setColumnWidth(4,60);
            tableView->setColumnWidth(5,50);
            tableView->setColumnWidth(6,40);
            tableView->setColumnWidth(7,40);
            tableView->setColumnWidth(8,40);
            tableView->setColumnWidth(9,40);
            tableView->setColumnWidth(10,40);
            tableView->setColumnWidth(11,40);
            tableView->setColumnWidth(12,40);
            tableView->setColumnWidth(13,40);
        }
    
        void run() override{
            QString str;
            if(tableView == NULL)
                return;
    
            while(1){
                mutexCanbusData.lock();  // it's global
                int dataLength = canbus_data.length();
                mutexCanbusData.unlock();
                if(dataLength)
                {
                    for(int i=0;i<dataLength;i++){
                        tableCount++;
    
                        const QMutexLocker locker(&mutexCanbusData);
                        QString strTableCount=QString::number(tableCount);
                        QString strTime=str.sprintf("%.03f",canbus_data.at(i).time).toUpper();
    
                        QList<QStandardItem *> rowData;
    //                    rowData<< new QStandardItem(strTableCount);
    //                   rowData<< new QStandardItem(QString::number(tableCount));
    //                    //                    rowData<< new QStandardItem(str.sprintf("%.03f",shadowSerial->canbus_data.at(i).time).toUpper());
    //                    //                    rowData<< new QStandardItem(str.sprintf("%08x",shadowSerial->canbus_data.at(i).id.all).toUpper());
    //                    //                    rowData<< new QStandardItem((shadowSerial->canbus_data.at(i).attribute.att.id==1) ? "Ext":"Std");
    //                    //                    rowData<< new QStandardItem((shadowSerial->canbus_data.at(i).attribute.att.rtr==1) ? "Rtr":"Data");
    //                    //                    rowData<< new QStandardItem(QString::number(shadowSerial->canbus_data.at(i).attribute.att.number_of_bytes));
    //                    //                    rowData<< new QStandardItem(str.sprintf("%02x",shadowSerial->canbus_data.at(i).data[0]).toUpper());
    //                    //                    rowData<< new QStandardItem(str.sprintf("%02x",shadowSerial->canbus_data.at(i).data[1]).toUpper());
    //                    //                    rowData<< new QStandardItem(str.sprintf("%02x",shadowSerial->canbus_data.at(i).data[2]).toUpper());
    //                    //                    rowData<< new QStandardItem(str.sprintf("%02x",shadowSerial->canbus_data.at(i).data[3]).toUpper());
    //                    //                    rowData<< new QStandardItem(str.sprintf("%02x",shadowSerial->canbus_data.at(i).data[4]).toUpper());
    //                    //                    rowData<< new QStandardItem(str.sprintf("%02x",shadowSerial->canbus_data.at(i).data[5]).toUpper());
    //                    //                    rowData<< new QStandardItem(str.sprintf("%02x",shadowSerial->canbus_data.at(i).data[6]).toUpper());
    //                    //                    rowData<< new QStandardItem(str.sprintf("%02x",shadowSerial->canbus_data.at(i).data[7]).toUpper());
    
    //                    //                    for(uint8_t i=0;i<14;i++)
    //                    //                        rowData[i]->setTextAlignment(Qt::AlignCenter);
                        model->appendRow(rowData);
    
                        if(tableCount>bufferSize){
                            model->removeRow(0);
                        }
                    }
                    mutexCanbusData.lock();
                    canbus_data.clear();
                    mutexCanbusData.unlock();
                }
                //tableView->scrollToBottom(); 
                QThread::msleep(100);
            }
        }
    };```
    

  • Qt Champions 2016



  • @mrjj I got it, thank you very much for your interest. In my opinion, this topic will help the developers who are newbie in Qt like me :)


  • Qt Champions 2016

    @Gokhan
    Yes it will also help people new to using threads etc.
    And how to use serial comm in thread.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.