QSerialPort - wating for whole data from sender - c++

I'm working with a serial device.
The QSerialPort is in a separate thread.
Thread is created this way:
QThread* serialthread = new QThread;
Serial* serial = new Serial();
serial->moveToThread(serialthread);
When Data is available this signal in my thread worker is emited:
void Serial::process()
{
serialport = new QSerialPort();
connect(this->serialport,SIGNAL(readyRead()),this,SLOT(readyToRead()));
}
void Serial::readyToRead()
{
emit SIG_dataAvailable(this->read());
}
This is the function that reads the data and checks if the data is correct - the second byte on my serial device says how long the rest of the packet is...
QByteArray Serial::read() const
{
QByteArray receivedData;
int length;
receivedData = serialport->readAll();
length = receivedData[1];
if(length != receivedData.length() - 1)
{
qDebug() << "protocol error.";
return NULL;
}
return receivedData;
}
My problem is that the signal QSerialPort::readyRead is emited before the data from the serial device is complete in the buffer. Any idea how to solve this problem?

There is absolutely NO guarantee that you'll get whole data at ONCE. You can solve this problem in some ways.
1) If you have fixed size package you can do something like this:
void foo::onSerialRead()
{
//! Is there whole datagram appears?
if (m_serial->bytesAvailable() < ::package_size) {
//! If not, waiting for other bytes
return;
}
//! Read fixed size datagram.
QByteArray package = m_serial->read(::package_size);
//! And notify about it.
emit packageReady(package);
}
2) If your package size may vary. Then you have to include "hader" in to your package. This header should contain at least "start" byte and data size (Its second byte in your case). And header shuld be fixed size. Then you can do something like this:
void foo::onSerialRead()
{
static QByteArray package;
static bool isHeaderRead = false;
static quint8 startByte = 0;
static quint8 dataSize = 0;
//! Is there whole header appears?
if (m_serial->bytesAvailable() < ::header_size) {
//! If not, waiting for other bytes
return;
}
if (!isHeaderRead) {
//! Read fixed size header.
package.append(m_serial->read(::header_size));
QDataStream out(&package);
out >> startByte;
//! Check is it actually beginning of our package?
if (Q_UNLIKELY(startByte != ::protocol_start_byte)) {
return;
}
out >> dataSize;
isHeaderRead = true;
}
//! Check is there whole package available?
if (Q_LIKELY(dataSize > m_serial->bytesAvailable())) {
//! If not, waiting for other bytes.
return;
}
//! Read rest.
package.append(m_serial->read(dataSize));
//! And notify about it.
emit packageReady(package);
package.clear();
isHeaderRead = false;
}
And there is absolutely no point in putting your QSerial in to different thread.

Related

Sending images over TCP from labVIEW to QT

I am trying to capture images taken from a camera connected to a myRIO and send them over a TCP/IP connection from labVIEW to a QT GUI application.
My problem is that QT keeps throwing a heap pointer exception and crashing when I read the data.
Expression: is_block_type_valid(header->_block_use)
I believe this could be because the data being sent is over 35k bytes, so I tried to read the data in separate chunks, but alas am still getting the error.
Below is my function that gets called on readyRead() being emitted:
void TCPHandler::onRead() {
QByteArray byteArray;
QByteArray buffer;
QByteArray dataSize = mainSocket->read(5); //read the expected amount of bytes incoming (about 35000)
while (buffer.size() < dataSize.toInt()) {
int bytesLeft = dataSize.toInt() - buffer.size();
if (bytesLeft < 1024) {
byteArray = mainSocket->read(bytesLeft);
}
else {
byteArray = mainSocket->read(1024);
}
buffer.append(byteArray);
}
QBuffer imageBuffer(&buffer);
imageBuffer.open(QIODevice::ReadOnly);
QImageReader reader(&imageBuffer, "JPEG");
QImage image;
if(reader.canRead())
image = reader.read();
else {
emit read("Cannot read image data");
}
if (!image.isNull())
{
image.save("C:/temp");
}
else
{
emit read(reader.errorString());
}}
In the LabVIEW code I send the size of the bytes being sent first, then the raw image data:
EDIT: Connect for the slot. Also should have mentioned this is running in a separate thread to the Main GUI.
TCPHandler::TCPHandler(QObject *parent)
: QObject(parent),
bytesExpected(0)
{
mainSocket = new QTcpSocket(this);
connect(mainSocket, SIGNAL(readyRead()), this, SLOT(onRead()));
connect(mainSocket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), this, &TCPHandler::displayError);
}
You are sending your length as a decimal string. Then followed by the string.
I would expect that the length would be binary value. So instead of an 'I32 to String' function use a typecast with a string as the type.

QSerialPort readyread() SIGNAL

I have a problem when receiving bytes from RS232 in QByteArray. I connected readyread() signal to call my serialport method and inside it I am reading bytes with readAll() to an QByteArray. Whenever data is available it rewrites QByteArray, but I want to receive it all, and then use data, but now I cannot because it is in parts. What to do?
Simply append to the array. You'll also need some criterion to determine when you've received all the data you wished. This can be, e.g. a given number of bytes:
class Communicator {
int expect;
QSerialPort port;
QByteArray reply;
void processReply() {
...
}
public:
Communicator() {
QObject::connect(&port, &QIODevice::readyRead, [this]{
reply += port.readAll();
if (expect && reply.size() >= expect) {
processReply();
reply.clear();
expect = 0;
}
});
...
};

Reading Variable length messages in Qtcp readyRead()

The following code is intended to display an Image sent over network. I sent a header of 16 bytes which I use to calculate the size of image that follows and then read that many bytes and display the image.
I used the concept at this link Tcp packets using QTcpSocket
void socket::readyRead()
{
while(socket->bytesAvailable() > 0) {
quint8 Data[16];
socket->read((char *)&Data,16);
img_size = (((quint8)Data[1]<<8)+ (quint8)Data[0]) * (((quint8)Data[3]<<8)+ (quint8)Data[2]) * 1;
QByteArray buffer = socket->read(img_size);
while(buffer.size() < (img_size))
{
// qDebug() << buffer.size();
socket->waitForReadyRead();
buffer.append(socket->read((img_size)-(buffer.size()) ));
}
unsigned char* imgdatara = (unsigned char*)&buffer.data()[0];
if( !image )
image = new QImage(imgdatara,32,640,QImage::Format_Grayscale8);
else
{
delete image;
image = new QImage(imgdatara,32,640,QImage::Format_Grayscale8);
}
emit msg(image);
}
}
My GUI says "not responding". How should I solve this?
Thanks
This is 100% working code from the book of Max Schlee's "Qt 4.8 Professional programming". This is not a simple question, because on the readyRead() signal you can receive:
1. A complete block
2. Only a part of block
3. Several blocks together
void MyClass::onReceive()
{
QDataStream in(m_pClient);
in.setVersion(QDataStream::Qt_4_6); // Your version. Not necessary.
for(;;)
{
if(m_nextBlockSize == 0)
{
if(m_pClient->bytesAvailable() < sizeof(m_nextBlockSize))
{
break;
}
else
{
in >> m_nextBlockSize;
}
}
if(m_pClient->bytesAvailable() < m_nextBlockSize)
{
break;
}
// Here you have each complete block
processYourBlockHere(); // <=====
m_nextBlockSize = 0;
}
}
Update: useful links for you: Serializing Qt Data Types and QDataStream

How can I use appropriate the lock mutex function, for three threads in C++?

I have a question about threads but I think that is difficult to explain, so be patient.
I have two pthreads in a QT/C++ program and one signal, Signal fills a buffer, One thread copies the buffer and one to process the buffer's data.
fill buffer1 ----Copy buffer1 to buffer2----process the buffer's 2 data
Signal's function:
void MainWindow::TcpData()
{
if(socket->bytesAvailable()>(DATA_LEN)) {
QByteArray array = socket ->readAll();
if(pthread_mutex_trylock(&data_mutex)==0)
{
if((p+array.size())<(MAX_TCP_BUFFER_SIZE+100))
{
memcpy(BUFFER+p,array.data(),array.size());
p+=array.size();
}
else {
p=0;
memcpy(BUFFER,array.data(),array.size());
p+=array.size();
}
pthread_mutex_unlock(&data_mutex);
}
}
}
Thread 1:
void *MainWindow::copyTCPdata() {
pthread_mutex_lock(&data_mutex);
while(1) {
if(data_ready) {
pthread_cond_wait(&data_cond,&data_mutex);
continue;
}
/* Move the last part of the previous buffer, that was not processed,
* on the start of the new buffer. */
memcpy(data, data+DATA_LEN, (FULL_LEN-1)*4);
/* Read the new data. */
memcpy(data+(FULL_LEN-1)*4, BUFFER,DATA_LEN);
memcpy(BUFFER,BUFFER+DATA_LEN,p);
if(p>DATA_LEN) p=p-DATA_LEN;
data_ready = 1;
pthread_cond_signal(&data_cond);
pthread_mutex_unlock(&data_mutex);
} }
Thread 2:
void *MainWindow::processData {
while(1) {
if(!data_ready) {
pthread_cond_wait(&data_cond,&data_mutex);
continue;
}
data_ready = 0;
pthread_cond_signal(&data_cond);
pthread_mutex_unlock(&data_mutex);
detectSignal(data);
pthread_mutex_lock(&data_mutex);
}
}
I think am loosing data with this way, but the program is more stable, Can someone suggest me a better solution?

Network send problem

I try to send data over the network, but the server I've programmed doesen't get the data.
This code worked befor:
void MainWindow::send()
{
QByteArray qbarr;
QDataStream qdstrm(&qbarr, QIODevice::WriteOnly);
int iCount = qlist->count();
QProgressDialog qprogrsdSend(QString("Sending..."), QString("Cancel"), 0, iCount, this);
qdstrm.setVersion(QDataStream::Qt_4_6);
qprogrsdSend.setWindowModality(Qt::WindowModal);
for(int i = 0; i < iCount; i++)
{
if(qprogrsdSend.wasCanceled())
break;
qdstrm << (quint16)0;
qdstrm << (*qlist)[i].data();
qdstrm.device()->seek(0);
qdstrm << (quint16)(qbarr.size() - sizeof(quint16));
qprogrsdSend.setValue(i);
qtcpsoClient->write(qbarr);
qtcpsoClient->flush();
qtcpsoClient->waitForBytesWritten();
qbarr.clear();
}
qlblStatus2->setText("File is send.");
}
But it Takes to many time to send each elemt from qlist. Now I tried to modify the methode, so that first all elements from qlist has been saved in qbarr. And than I send the File. This is the code that doesnt work:
void MainWindow::send()
{
QByteArray qbarr;
QDataStream qdstrm(&qbarr, QIODevice::WriteOnly);
int iCount = qlist->count();
QProgressDialog qprogrsdSend(QString("Sending..."), QString("Cancel"), 0, iCount, this);
qdstrm.setVersion(QDataStream::Qt_4_6);
qprogrsdSend.setWindowModality(Qt::WindowModal);
qdstrm << (quint16)0;
for(int i = 0; i < iCount; i++)
{
if(qprogrsdSend.wasCanceled())
break;
qdstrm << (*qlist)[i].data();
qprogrsdSend.setValue(i);
}
qdstrm.device()->seek(0);
qdstrm << (quint16)(qbarr.size() - sizeof(quint16));
qtcpsoClient->write(qbarr);
qtcpsoClient->flush();
qtcpsoClient->waitForBytesWritten();
qbarr.clear();
qlblStatus2->setText("File is send.");
}
And here is the methode I use to read the data:
void QServerThread::onReadyRead(void)
{
if(read == false)
{
read = true;
emit reading(true);
}
while(!qtcpsoClient->atEnd())
{
QDataStream qdstrmIn(qtcpsoClient);
QDataStream qdstrmOut(qfile);
QByteArray qbarrData;
quint16 qui16BlockSize = 0;
int iVersion = qdstrmIn.version();
qdstrmIn.setVersion(iVersion);
qdstrmOut.setVersion(iVersion);
if(qtcpsoClient->bytesAvailable() < (int)sizeof(quint16))
break;
qdstrmIn >> qui16BlockSize;
if(qtcpsoClient->bytesAvailable() < qui16BlockSize)
break;
qdstrmIn >> qbarrData;
qdstrmOut << qbarrData.data();
qfile->flush();
}
read = false;
emit reading(false);
}
I hope somebody can help me.
Thanks
Paul
Couldn't the problem be in your server? I suppose you connect your onReadyRead to readyRead singal of the socket. That signal is emitted once per a chunk of data received. So if you send all your data at once, it is possible the signal gets emitted only once. I suppose qtcpsoClient is the socket. Now, I can see this happening:
You ask bytesAvailable() < somesize before that much data arrived yet. In that case you read a size, but break out right after that and on next read you already lost your size information and read garbage.
This might not be a problem before as you send multiple short messages, and every message managed to fully arrive before you asked for the data size. The bug was still there though.
On a sidenote. In your original client code - why did you flush() and waitForBytesWritten() after every write? This may be the reason why it was so slow.
[edit: corrected based on Sergey Tachenov's comment]