[SOLVED] QtSerialPort - switching between async/blocking mode



  • Hello All

    I've got a question about possibility to switch between blocking/async modes for QtSerialPort during reading/writing.
    The problem I'm having is in following setup:

    1. I've got basic communication up and running
    2. On connection to the device I read its configuration (it's a lot of parameters - over 100 - so during connection I use blocking mode and read every parameter - this is in order to simplify program logic)
    3. Then I "switch to asynchronous" mode by connecting to portError() and readyRead() signals and trigger device to send data on its own
    4. Everything goes smoothly until I try to switch configuration (there are up to 8 configuration slots) so I:
      • tell device to switch configuration
      • "wait" asynchronously for it to confirm
      • then I try to switch to "synchronous" mode by disconnecting signals and try to read new configuration via blocking mode

    The last point is where I'm having problems. Everything works perfectly on Linux by on Windows I get following behaviour:

    1. Device acknowledges configuration switch
    2. I disconnect signals
    3. I try to read first parameter via:
        bool ok = true;
        _port->write(cmd);
        _port->waitForBytesWritten(TMOUT);
        while (ok && ! _port->canReadLine())
            ok = _port->waitForReadyRead(TMOUT);
    

    and I always end up with error on waitForReadyRead() (write() & waitForBytesWritten() always complete successfully) and I see portError() callback being called even though I have disconnected it.
    FWIW device is replying correctly for this parameter request (I can see that when I sniff the communication) but somehow it is lost (even the "disconnected" readyRead() callback is not called) and in the portError() callback I get error=9 (ResourceError if I can count to 10 :))

    Does anybody know how I can switch between blocking/asynchronous modes on Windows?

    Best regards
    Andrzej


  • Lifetime Qt Champion

    Hi and welcome to devnet,

    Strange effect indeed, can you show your connect and disconnect code ?



  • First of all thank you for your interest, and apologies for getting back so late (I expected some mail notification that someone has replied and found your reply only now - is it possible to configure this forum to notify about replies?).

    Here's the function in question.

    void Driver::asyncMode(bool is_async)
    {
        if (is_async) {
            connect(_port, &QSerialPort::readyRead, this, &Driver::readyRead);
            connect(_port, static_cast<void(QSerialPort::*)(QSerialPort::SerialPortError)>(&QSerialPort::error),
                    this,  &Driver::portError);
        } else
            _port->disconnect();
    }
    

    Actually during searching for solution I've managed to prepare sample code that shows even problems with basic usage of waitForReadyRead() on Windows. The sample - in a nutshell - is "device" that handles three commands "START" after which it periodically sends time, "STOP" after which it cease to do it and "DATE" for which it replies with value (this is equivalent of my parameters reading in real project).
    Pay not much attention to main.cpp (it's a quick stab at simulating real code behaviour - and here I'm not properly waiting for thread to finish).

    When I'm testing this on virtual com ports on Linux (socat -d -d PTY,raw,echo=0,b115200,cs8 PTY,raw,echo=0,b115200,cs8) I get correct behaviour:


    $ ./async_test /dev/pts/28 /dev/pts/30
    Openning device port
    Openning driver port
    Synchrounous date reading ...
    Read buffer size: 0
    Asking for date ...
    After waitForReadyRead(): true
    Current date: "Thu Jun 11 2015"
    Starting device ...
    void Driver::readyRead() "OK"
    void Driver::readyRead() ":15:58:14"
    void Driver::readyRead() ":15:58:14"
    void Driver::readyRead() ":15:58:15"
    void Driver::readyRead() ":15:58:15"
    void Driver::readyRead() ":15:58:15"
    void Driver::readyRead() ":15:58:16"
    void Driver::readyRead() ":15:58:16"
    void Driver::readyRead() ":15:58:17"
    void Driver::readyRead() ":15:58:17"
    void Driver::readyRead() ":15:58:18"
    Stopping device ...
    void Driver::readyRead() "OK"
    Asking for date ...
    After waitForReadyRead(): true
    Current date: "Thu Jun 11 2015"

    On Windows though (with com0com) I get erroneous behaviour:


    Openning device port
    Openning driver port
    Synchrounous date reading ...
    Read buffer size: 0
    Asking for date ...
    void Driver::readDate() waitForBytesWritten() error: "Parametr jest niepoprawny."
    Starting device ...
    void Driver::readyRead() "Cz cze 11 2015"
    !! Wrong reply for Start: "Cz cze 11 2015"
    void Driver::readyRead() "OK"
    void Driver::readyRead() ":16:01:30"
    void Driver::readyRead() ":16:01:31"
    void Driver::readyRead() ":16:01:31"
    void Driver::readyRead() ":16:01:32"
    void Driver::readyRead() ":16:01:32"
    void Driver::readyRead() ":16:01:33"
    void Driver::readyRead() ":16:01:33"
    void Driver::readyRead() ":16:01:34"
    void Driver::readyRead() ":16:01:34"
    void Driver::readyRead() ":16:01:35"
    Stopping device ...
    void Driver::readyRead() "OK"
    Asking for date ...
    After waitForReadyRead(): false
    void Driver::readDate() waitForReadyRead() error: "Up│yn╣│ limit czasu operacji oczekiwania."

    The errors are reported in Polish and they are more or less "Invalid parameter" and "Timeout".

    Am I missing anything obvious?

    The behaviour I'm seeing is that waitForReadyRead() waits for data and then timeouts with some error (not necessarily "timeout") while the expected data are actually being read (note that after first error on Windows the expected reply for "DATE" command is fed into readyRead() function as a reply for "START" command, for which reply is obtained in next readyRead() invocation).

    If anyone could explain this or point QSerialPort developers to this I'd appreciate.
    Best regards
    Andrzej


  • Lifetime Qt Champion

    You can find the mail settings here

    @kuzulis might be able to help



  • I understood nothing, but if you provided the complete minimum example which reproduces a problem, it would be good (for example, even using the com0com).



  • Thank you kuzulis for devoting your time to look at my problem!

    I already provided sample (the output above is from this sample) and here it is again: sample.

    On Linux (with virtual pair of ports from socat) this code runs without any problem. However on Windows with com0com I see first error from waitForBytesWritten() and then later on waitForReadyRead().

    My original wish is more or less what you see in Driver::readyRead() function. In this callback when I get confirmation from "STOP" command I wanted to switch to "synchronous" mode and read configuration (in sample it is just "DATE" command but in real project this is reading of full configuration which is about 170 params). In there you'll find preprocessor conditional and if you change it then there'll be no problem with reading "DATE" (however I wanted to avoid reading of my configuration that way since it would unnecessarily complicate code).
    So there are two problems with this sample code (on Windows that is, on Linux everything is working OK):

    • why I see error on waitForBytesWritten() in first readyDate() invocation
    • how to call readDate() from readyRead() callback so that it completes without a problem

    Once again thank you for taking your time to look at this.

    Best regards
    Andrzej



  • Seems you are right.. This is a bug in QSerialPort.

    I found out the reason, please look and try (and to watch and to vote) for the patch: https://codereview.qt-project.org/#/c/115392/

    :)



  • Great! I'll take a closer look at it. I hope it will be merged soon (BTW will it be back ported to 5.4, or will it be only in 5.5+) but if voting helps I certainly will vote ... but how do I that :) ?



  • or will it be only in 5.5+)

    This will be released in future Qt 5.5.1 (but not in Qt 5.5.0).. But, in any case, you can build and install the QtSerialPort manually.

    but if voting helps I certainly will vote ... but how do I that :) ?

    Yes, please vote for it (+1 or +2) if this works for you. :)


Log in to reply
 

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