Sending images over TCP from labVIEW to QT - c++

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.

Related

QPixmap::loadFromData() does not accept JPG data received from TCP/IP

Since a few days I try to send JPG data via TCP to a QT interface. All images are generated by another process, which makes some GPU-based image processing. The network topology is shown in the below:
EDIT
Transmitter-side
Previously, data were processed by an algorithm to BMP24 images. Afterwards BMP images are compressed/encoded to JPGs, in order to reduce the network traffic. Encoding is done by using this library which is used in the following function.
void CudaBase::encodeBmpToJpeg(unsigned char* idata, uint8_t* odata, int* p_jpeg_size, int width, int height)
{
... // init stuff
gpujpeg_encoder_input_set_image(&encoder_input, idata); // pass idata which contains interleaved BMP24 data
gpujpeg_encoder_encode(encoder, &param, &param_image, &encoder_input, &image, p_jpeg_size); // encode BMP24 to JPG, variable image holds compressed JPG data
gpujpeg_image_save_to_file(image_dir, image, *p_jpeg_size); // save JPG on harddrive
CUDA_CHECK(cudaStreamSynchronize(stream));
// Copy JPG from image to odata
odata = (uint8_t*) malloc(*p_jpeg_size);
if(odata != nullptr)
memcpy((void*)odata, (void*)image, *p_jpeg_size);
else
printf("Could not allocated memory for jpeg image\n");
CUDA_CHECK(cudaStreamDestroy(stream));
gpujpeg_image_destroy(image);
gpujpeg_encoder_destroy(encoder);
}
Now odata contains JPG data and is ready to send via TCP, which is done by the following:
socket.make_tcp_header(&header, nof_recieved_records, nof_records, ch);
socket.send(&header, sizeof(header));
socket.waitForACK();
socket.send(images[ch], algthm.getImageSize()); // images[ch] points to odata
socket.waitForACK();
This is how the transmitter sends
template <typename T>
void Socket::send(T* ptr, int count)
{
printf("Writing to server... \n");
int result = 0;
int _size = count;
do
{
result = write(sockfd, (void*)ptr, _size);
if(result == -1)
{
printf("Error sending to server: %d\n",errno);
_size = 0;
}
else
{
_size -= result;
}
}
while(_size > 0);
}
Receiver-side
On the QT client side, JPG data are written to a QByteArray and then displayed in a QLabel using the QPixmap::loadFromData() function, but loadFromData() always returns zero. So it seems that the content of QByteArray is not JPG compliant.
When data are received by the client, QT emits a signal and the following readyRead() function is called:
void ThreadSocket::readyRead()
{
static qint64 sum = 0;
while(socket->bytesAvailable())
{
sum += socket->bytesAvailable();
data->append(socket->readAll());
}
// Receive header from client
if(sum == header_size)
{
sum = 0;
setHeader(stream);
send("OK");
emit received_data(META);
}
// receive image data
else if(sum == header.img_size)
{
sum = 0;
send("OK");
emit received_data(RAW);
}
else
{
return;
}
clearBuffer();
}
EDIT ends here
The following function is a SLOT which is emitted when new (raw) data are received by the client thread.
void Controller::refresh_image()
{
tcp_header *header = socket->getHeader(); // Get header informations from threaded socket
QPixmap pix;
switch(header->format)
{
case BMP24:
{
QImage img(socket->getData(),
header->img_width,
header->img_height,
QImage::Format_RGB888);
pix = QPixmap::fromImage(img);
break;
}
case JPEG:
{
if(!(pix.loadFromData(socket->getData(), "JPG"))) // Every time return zero
qDebug() << "Not able to load pixmap from data";
break;
}
}
// Reload images in GUI
view->label_list()->at(header->current_channel)->setPixmap(pix);
view->update();
}
Note tcp_header is a structure that I have set by myself and is part of my own TCP protocol. Depending on the header, either BMP or JPG images are displayed. Displaying BMP images works without any problems.
After debugging I noticed the following:
JPG data sent by the transmitter are exactly the same as those received. In addition, metadata and BMPs are also received correctly.
When the receiver loads JPG images from the hard disk with QPixmap::load(path_to.jpg)images are correctly displayed in the GUI. (Note the JPGs were generated by the transmitter a few milliseconds before loading).
Received data are completely different from those load from the hard disk, whether both have the same source.
The size of the transferred bytes is exactly the same as on the hard disk. The data is of type uint8_t at the receiver and at the transmitter.
What could be the reason that the transferred data is different from the data loaded from the hard disk?
Does QT possibly make a conversion internally?

QSerialPort - wating for whole data from sender

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.

Can QSerialPort read more than 512 bytes of data?

I want to use QSerialPort to read data transmitted from a device. The device transmits a frame of 4000 data bytes each time. I try with the following simple code
QSerialPort *serialPort;
char receivedData[4000];
int numRead = 0;
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
/* Initialize serial port*/
serialPort = new QSerialPort(this);
QString portName = "COM6";
qint32 baudRate = 460800;
serialPort->setPortName(portName);
serialPort->setBaudRate(baudRate);
serialPort->setDataBits(QSerialPort::Data8);
serialPort->setParity(QSerialPort::NoParity);
serialPort->setStopBits(QSerialPort::OneStop);
serialPort->setFlowControl(QSerialPort::NoFlowControl);
serialPort->setReadBufferSize(4000);
if (!serialPort->open(QIODevice::ReadOnly)) {
qDebug() << "Cannot open comport";
}
connect(serialPort, SIGNAL(readyRead()), this, SLOT(serialReceived()));
}
void MainWindow::serialReceived()
{
numRead = serialPort->read(receivedData, 4000);
serialPort->flush();
}
The problem is: it always shows that only 512 data bytes are read. How can I read the whole 4000 bytes data frame? (when I'm using Matlab to read this 4000 bytes frame, it's working fine)
There's no limit, but you don't necessarily receive all data in single chunk.
You have to keep listening until you have the number of bytes you're waiting for (or a timeout).
void MainWindow::serialReceived()
{
receivedData.append(serialPort->readAll());
if(receivedData.size() >= 4000) {
// we're full
}
}
You generally have to read out the data in a loop (to ensure you get it all), here is a snippet of example code this is equivalent to your serialReceived() function, except it emits the data using emit rxDataReady(newData); to whoever is listening...
void QSerialPortReader::handleReadyRead()
{
QByteArray newData;
// Get the data
while (mp_serialPort->bytesAvailable())
{
newData.append(mp_serialPort->readAll());
}
emit rxDataReady(newData);
}
edit
Although I don't do any max size checking... but that is trivial to add if you need it (i.e. just use read(..., spaceAvail) instead of readAll and then decrement spaceAvail...

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;
}
});
...
};

QLocalSocket to QLocalServer message being corrupted during transfer

I haven't been able to find a similar issue, so here goes:
I'm sending a QString from a QLocalSocket to a QLocalServer across two applications. The receiving (QLocalServer) application does receive the message, but it seems the encoding is completely wrong.
If I send a QString = "x" from the QLocalSocket (client), I'm getting a foreign (Chinese?) symbol in the QLocalServer. My code is literally copied from the Nokia Developer website
If I printout the message via QDebug, I get "??". If I fire it in a message box, Chinese characters are printed. I've tried re-encoding the received message to UTF-8, Latin1, etc., with no luck.
Code is as follows:
//Client
int main(int argc, char *argv[])
{
QLocalSocket * m_socket = new QLocalSocket();
m_socket->connectToServer("SomeServer");
if(m_socket->waitForConnected(1000))
{
//send a message to the server
QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_7);
out << "x";
out.device()->seek(0);
m_socket->write(block);
m_socket->flush();
QMessageBox box;
box.setText("mesage has been sent");
box.exec();
...
}
//Server - this is within a QMainWindow
void MainWindow::messageReceived()
{
QLocalSocket *clientConnection = m_pServer->nextPendingConnection();
while (clientConnection->bytesAvailable() < (int)sizeof(quint32))
clientConnection->waitForReadyRead();
connect(clientConnection, SIGNAL(disconnected()),
clientConnection, SLOT(deleteLater()));
QDataStream in(clientConnection);
in.setVersion(QDataStream::Qt_4_7);
if (clientConnection->bytesAvailable() < (int)sizeof(quint16)) {
return;
}
QString message;
in >> message;
QMessageBox box;
box.setText(QString(message));
box.exec();
}
Any help is highly appreciated.
The client is serializing a const char* while the server is deserializing a QString. These aren't compatible. The former literally writes the string bytes, the latter first encodes to UTF-16. So, I guess on the server side, the raw string data "fff" is being decoded into a QString as though it were UTF-16 data... perhaps resulting in character U+6666, 晦.
Try changing the client to also serialize a QString, i.e.
// client writes a QString
out << QString::fromLatin1("fff");
// server reads a QString
QString message;
in >> message;