4

It's my first question on this website !

I have some trouble reading datas from a COM port, I send a complete message from another COM port, and when I receive it with Qt, it's always cut in multiple submessages.

void SerialPortReader::init()
{
    connect(m_serialPort, SIGNAL(readyRead()), this, SLOT(readData()));
}   

void SerialPortReader::readData()
{
//    m_serialPort->waitForReadyRead(200);
    QByteArray byteArray = m_serialPort->readAll();
    qDebug() << byteArray;

    if(byteArray.startsWith(SOF) && byteArray.endsWith(EOF_LS)
        && byteArray.size() >= MIN_SIZE_DATA) {
    decodeData(byteArray.constData());
    } else {
        qDebug() << "LIB SWCom : Unvalid trame !";
    }
}

the messages sent are 25 or 27 Bytes long, if I use Putty or an Hyperterminal to read them, I have no trouble. Also if I use 2 emulated serial port COM to communicate, I don't have this problem... It only occurs with Qt reading system AND with 2 physical COM port...

I think I don't get when the readyRead signal is emitted exactly...

I'm very confuse, Thank you in advance for your help !

László Papp
  • 51,870
  • 39
  • 111
  • 135
palador
  • 273
  • 2
  • 3
  • 11

3 Answers3

11

The documentation is actually quite clear about it:

void QIODevice::readyRead() [signal]

This signal is emitted once every time new data is available for reading from the device. It will only be emitted again once new data is available, such as when a new payload of network data has arrived on your network socket, or when a new block of data has been appended to your device.

readyRead() is not emitted recursively; if you reenter the event loop or call waitForReadyRead() inside a slot connected to the readyRead() signal, the signal will not be reemitted (although waitForReadyRead() may still return true).

Note for developers implementing classes derived from QIODevice: you should always emit readyRead() when new data has arrived (do not emit it only because there's data still to be read in your buffers). Do not emit readyRead() in other conditions.

This means that it is not really guaranteed how much data will be available for reading, just that some will be available.

If you wish to read more data than it is coming in one go, you may be opt for a timeout value and/or readyRead. It depends on what you are trying to achieve.

See the command line async reader example that I wrote a while ago for this operation, too:

#include "serialportreader.h"

#include <QCoreApplication>

QT_USE_NAMESPACE

SerialPortReader::SerialPortReader(QSerialPort *serialPort, QObject *parent)
    : QObject(parent)
    , m_serialPort(serialPort)
    , m_standardOutput(stdout)
{
    connect(m_serialPort, SIGNAL(readyRead()), SLOT(handleReadyRead()));
    connect(m_serialPort, SIGNAL(error(QSerialPort::SerialPortError)), SLOT(handleError(QSerialPort::SerialPortError)));
    connect(&m_timer, SIGNAL(timeout()), SLOT(handleTimeout()));

    m_timer.start(5000);
}

SerialPortReader::~SerialPortReader()
{
}

void SerialPortReader::handleReadyRead()
{
    m_readData.append(m_serialPort->readAll());

    if (!m_timer.isActive())
        m_timer.start(5000);
}

void SerialPortReader::handleTimeout()
{
    if (m_readData.isEmpty()) {
        m_standardOutput << QObject::tr("No data was currently available for reading from port %1").arg(m_serialPort->portName()) << endl;
    } else {
        m_standardOutput << QObject::tr("Data successfully received from port %1").arg(m_serialPort->portName()) << endl;
        m_standardOutput << m_readData << endl;
    }

    QCoreApplication::quit();
}

void SerialPortReader::handleError(QSerialPort::SerialPortError serialPortError)
{
    if (serialPortError == QSerialPort::ReadError) {
        m_standardOutput << QObject::tr("An I/O error occurred while reading the data from port %1, error: %2").arg(m_serialPort->portName()).arg(m_serialPort->errorString()) << endl;
        QCoreApplication::exit(1);
    }
}

In this case, the command line reader example will get any data that was passed in one shot, but it does not guarantee the length or anything.

Also, please note that the sync api that is behind your comments does not make much sense along with the async API that you are asking about. I am referring to m_serialPort->waitForReadyRead(200); in here.

Community
  • 1
  • 1
László Papp
  • 51,870
  • 39
  • 111
  • 135
  • Thank for your answer, I forgot to say that datas are continuously emitted (in one shot), every 20 ms in the final case. I knew that waitForReadyRead() prevents the signal readyRead to be re emmited, so I tried waitForReadyRead(20) before reading datas and it worked fine. But In this case I need to be guarenteed that the sender emits every 20ms and not more. – palador Oct 28 '14 at 18:19
  • @palador: as per my answer, you can use a timer for that. My example also shows the use of the timer. – László Papp Oct 28 '14 at 18:33
  • @palador: please provide some feedback when the issue is resolved. – László Papp Oct 29 '14 at 17:51
  • @palador: please do not submit clarifications as answer. Just update the question. Although you should have been clear upfront. – László Papp Oct 30 '14 at 09:49
  • Oh ok sorry, I won't forget to be clearer in my new posts. About your technique, I don't see how I can set the timer time the more accurate possible to avoid frame loss or to avoid concatenated frame... – palador Oct 30 '14 at 10:23
  • @palador: you can still delete (and should) your question that is submitted as answer, and update the question with the clarifying content. As for the solution, what is wrong about starting a timer for every 20 ms or so? If QTimer is not precise enough for you, you can use QElapsedTimer, no? – László Papp Oct 30 '14 at 10:43
1

The readyRead-signal is emitted whenever there is pending data AND the previous execution of readyRead has finished.

Documentation says:

This signal is emitted once every time new data is available for reading from the device. It will only be emitted again once new data is available, such as when a new payload of network data has arrived on your network socket, or when a new block of data has been appended to your device.

readyRead() is not emitted recursively; if you reenter the event loop or call waitForReadyRead() inside a slot connected to the readyRead() signal, the signal will not be reemitted (although waitForReadyRead() may still return true).

If this should lead to any problems in your case, you could use a while-loop where you check for bytesAvailable().

Robert
  • 1,235
  • 7
  • 17
1

Use Thread concept here , using threads you can achieve this... run your portread in a seperate thread then it will be works fine, other wise the message will be cut. other method u can use Qprocess and open a new terminal using QProcess and send the port number and connectivity details to that terminal and run it from Qt then also u can archive this

i mean we can use therad and socket communication here look at this code

    QTcpSocket *SocketTest::getSocket() {
        return socket;
    }

    void SocketTest::Connect()

    {

        socket = new QTcpSocket(this);
        socket->connectToHost("127.0.0.1",22);
        if(socket->waitForConnected(3000))
        {

            qDebug()<<"connect";
            socket->waitForBytesWritten(3000);
            socket->waitForReadyRead(1000);
         Settings::sockdata=socket->readAll();
           qDebug()<<Settingssock::sockets;
           socket->close();

        }
        else
        {
             qDebug()<<" not connect";

        }
    }


............ call socket 

 SocketTest sock;
    sock.Connect();
    QTcpSocket * socket = sock.getSocket();
       QObject *ob= new QThread;
       mythread thread1(socket);
    thread = new mythread(socket);
    thread.run();
    thread->start();
   thread->wait();

this is a simple example of telnet connection, likewise u can specify which port you want connect and connected to which ip

Arunprasanth K V
  • 20,733
  • 8
  • 41
  • 71
  • I have problems parsing this answer, I am afraid. The OP did not mention protocols, so I am not sure what port you are writing about. Also, I do not understand how QProcess/QThread is relevant in this context? Also, what does it mean "run it from Qt"? "u can achieve this" -> achieve what? – László Papp Oct 28 '14 at 16:39