I am trying to repeatedly write and read to/from a QBuffer object via QTextStream. First I construct both objects:
QBuffer b;
b.open(QIODevice::ReadWrite);
QTextStream s(&b);
// Setup text stream here
Then I write three different portions of information and read them back:
s << "Test" << 666 << endl << flush;
s.seek(0);
qDebug() << s.readAll();
s << "X" << endl << flush;
s.seek(0);
qDebug() << s.readAll();
s << "Test" << 777 << endl << flush;
s.seek(0);
qDebug() << s.readAll();
Of course I do not get the data portion I wrote immediately before, but the cumulated data:
"Test666\n"
"Test666\nX\n"
"Test666\nX\nTest777\n"
I could do adaptive seek calls to get the correct data but I do not want the QBuffer to grow infinitely.
I tried a s.reset() call between writes but the result is the same. Calling reset() or open()/close() directly on the buffer gives a crippled result (which is expected since the stream is bypassed):
"Test666\n"
"X\nst666\n"
"Test777\n"
I could probably build a new buffer for every cycle, open it and attach it to the stream but that is slow.
Is there a proper and fast solution for this use case?
You can access QBuffer's internal QByteArray storage directly with QBuffer::buffer() and then delete everything with QByteArray::clear(). Then manually seek() back to the start.
QBuffer b;
b.open(QIODevice::ReadWrite);
QTextStream s(&b);
s << "Test" << 666 << endl << flush;
s.seek(0);
qDebug() << s.readAll();
b.buffer().clear();
s.seek(0);
s << "X" << endl << flush;
s.seek(0);
qDebug() << s.readAll();
b.buffer().clear();
s.seek(0);
s << "Test" << 777 << endl << flush;
s.seek(0);
qDebug() << s.readAll();
"Test666\n"
"X\n"
"Test777\n"
QTextStream also has a constructor which takes a QByteArray directly and creates the QBuffer automatically, which may save a little code in this case.
Related
This question is in reference to: How to read data from AVRO file using C++ interface?
int main(int argc, char**argv)
{
std::cout << "AVRO Test\n" << std::endl;
if (argc < 2)
{
std::cerr << BOLD << RED << "ERROR: " << ENDC << "please provide an "
<< "input file\n" << std::endl;
return -1;
}
avro::DataFileReader<avro::GenericDatum> reader(argv[1]);
auto dataSchema = reader.dataSchema();
// Write out data schema in JSON for grins
std::ofstream output("data_schema.json");
dataSchema.toJson(output);
output.close();
avro::GenericDatum datum(dataSchema);
while (reader.read(datum))
{
std::cout << "Type: " << datum.type() << std::endl;
if (datum.type() == avro::AVRO_RECORD)
{
const avro::GenericRecord& r = datum.value<avro::GenericRecord>();
std::cout << "Field-count: " << r.fieldCount() << std::endl;
// TODO: pull out each field
}
}
return 0;
}
I used this code, but keep getting a seg fault at the while loop. I have a very large schema and a large amount of data. Decoding the data piece by piece as the Avro examples gives in its "cpx" example is not practical, I need a generic way of reading. I get the seg fault the 3rd time through (consistently) with no error code returned from the read(). Open to any and all suggestions and ideas about reading large schemas in Avro.
As it turns out there is an open ticket/issue on the Avro page for this exact issue. https://issues.apache.org/jira/browse/AVRO-3194
I am writing a structure into a file using the following line:
std::fstream snif::fileHandler;
fileHandler.write(reinterpret_cast<char*>(rawData), sizeof(rawDataStruct));
where rawdataStruct is:
typedef struct _rawData rawDataStruct;
now after writing the structures into the file, I am reading the structure from the beginning of the binary file using:
std::cout << "going for print data read from file\n";
snif::fileHandler.seekg(0); //, std::ios::beg);
snif::fileHandler.read(reinterpret_cast<char*>(rawData), sizeof(rawDataStruct));
if (snif::fileHandler.fail()) {
std::cerr << "reading error\n";
exit(0);
}
std::cout << "PSH flag = " << rawData->tcpFlag.PSH << std::endl
<< "source port " << rawData->sourcePort << std::endl
<< "destination port " << rawData->destinationPort << std::endl
<< " sequence number " << rawData->sequenceNumber << std::endl
<< " Acknowledge number " << rawData->acknowledgeNumber << std::endl
<< " acknowledge flag " << rawData->tcpFlag.ACK << std::endl
<< " SYN flag " << rawData->tcpFlag.SYN << std::endl
<< "FIN flag " << rawData->tcpFlag.FIN << std::endl;
but if I check my standard output, the last line geting printed is:
"going for print data read from file";
There is no code showing it, but what mode is the file opened? Hopefully it is configured for binary. To see the available options, review std::basic_fstream and std::ios_base::openmode. I suggest to make sure that the following open modes are set:
ios::binary | ios::out | ios::in | ios::trunc
Depending on what purpose is happening, ios::trunc (truncate) may have to be replaced by ios::app (append).
While doing some basic testing, it has been discovered on my C++11 compliant compiler that the
fileHandler.write(reinterpret_cast<char*>(rawData), sizeof(rawDataStruct));
has a potential problem that is easily solved by adding the & operator in front of the rawData like this:
fileHandler.write(reinterpret_cast<char*>(&rawData), sizeof(rawDataStruct));
The compiler should have given a warning, but that is contingent on the compiler version, and whether the -Wall option or better is used. This may explain how the screen output seemingly stops at the
"going for print data read from file"
message. The read function also needs the & operator in front of rawData:
snif::fileHandler.read(reinterpret_cast<char*>(&rawData), sizeof(rawDataStruct));
Perhaps a run-time exception from the reinterpret_cast<> operator is being thrown that is not being caught. It is difficult to know until the system and compiler are documented.
Additionally, if rawData is declared as a pointer, then a better variable name is pRawData, as well as posting more of the code. For example, if the pRawData is never pointing to a valid instance of the rawDataStruct, then unpredictable things will occur.
I'm trying to connect a micro-controller with my desktop PC via USB-serial cable.
The OS of my desktop PC is Windows 8.1, and USB-serial cable is TTL-232R-3V3. (FTDI)
(Qt version: 5.2.0 beta1, QtCreator Version: 3.0, Compiler: MSVC2012)
Now I'm trying read/write loop-back tests, and that's why RX/TX pin of USB-serial cable are connected with each other.
Here is my code.
#include <QtCore/QCoreApplication>
#include <QtSerialPort/QSerialPort>
#include <QtSerialPort/QSerialPortInfo>
#include <QtCore/QDebug>
#define PORT_NAME "COM3"
#define BAUDRATE 19600
#define TIMEOUT_MS 1000
QT_USE_NAMESPACE
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QSerialPort pSerial(PORT_NAME);
const char strMsg[] = "#1:Send data line \n #2:Send data line\n #3:Send data line end\n";
char strBuf[256];
qint64 nByte;
if(pSerial.open(QIODevice::ReadWrite)){
pSerial.setBaudRate(BAUDRATE);
qDebug() << "OPEN PASS";
pSerial.write(strMsg);
pSerial.flush();
if(pSerial.waitForBytesWritten(TIMEOUT_MS)){
qDebug() << "WRITE PASS";
}
pSerial.waitForReadyRead(TIMEOUT_MS);
while(true){
if( pSerial.canReadLine()){
qDebug() << "CAN READ LINE";
nByte = pSerial.readLine(strBuf,sizeof(strBuf));
qDebug() << "Length: " << nByte;
qDebug() << "Read data: " << strBuf;
}
}
pSerial.close();
} else {
qDebug() << "OPEN FAIL\n";
}
return a.exec();
}
When the program starts to run, the result is different than I expected.
Only first line of sent data can be received. So, "Read data: #1 Send data line" is printed
on console. But the rest of sent data will never be received. Does anyone know why?
Any help would be appreciated.
Thanks in advance.
EDIT: I revised my code according to Papp's comment.Then it works as I expected.
All sent message has been received.
Does it mean I misunderstand the usage about readLine() or canReadLine()?
// while(true){
// if( pSerial.canReadLine()){
// qDebug() << "CAN READ LINE";
// nByte = pSerial.readLine(strBuf,sizeof(strBuf));
// qDebug() << "Length: " << nByte;
// qDebug() << "Read data: " << strBuf;
// }
// }
pSerial.waitForReadyRead(TIMEOUT_MS);
QByteArray readData = pSerial.readAll();
while (pSerial.waitForReadyRead(TIMEOUT_MS)) {
readData.append(pSerial.readAll());
}
qDebug() << "Read data: " << readData;
EDIT 2nd time : Following code also works for me.
while(true){
if( pSerial.waitForReadyRead(TIMEOUT_MS) && pSerial.canReadLine()){ // I revised this line
qDebug() << "CAN READ LINE";
nByte = pSerial.readLine(strBuf,sizeof(strBuf));
qDebug() << "Length: " << nByte;
qDebug() << "Read data: " << strBuf;
qDebug() << "Error Message: " << pSerial.errorString();
}
}
That is because you need to read in a loop like this:
QByteArray readData = serialPort.readAll();
while (serialPort.waitForReadyRead(5000))
readData.append(serialPort.readAll());
Please see the creadersync example for the details what I added to 5.2. You can also check the creaderasync example for non-blocking operation.
To be fair, we have not tested readLine that much, but it works for me on Unix, so does it on Windows for someone else.
The mistake that you've made is expecting to receive all the sent data when waitForReadyRead returns. When waitForReadyRead finishes, all you're guaranteed is some data being available to be read. It may be as little as one character, not necessarily a whole line.
The loop from your last modification is the almost correct way to do it. You should nest reading of the lines in a separate loop. The following code is how it should be done, and agrees with the semantics of QIODevice:
while (pSerial.waitForReadyRead(TIMEOUT_MS)) {
while (pSerial.canReadLine()) {
qDebug() << "NEW LINE";
QByteArray line = pSerial.readLine();
qDebug() << "Length: " << line.size();
qDebug() << "Read data: " << line;
qDebug() << "Error Message: " << pSerial.errorString();
}
}
qDebug << "TIMED OUT";
Note that none of this code should even run in the GUI thread. Ideally you should move it to a QObject, use the signals emitted by QIODevice (and thus QSerialPort), and move that object to a separate thread.
The GUI thread can sometimes block for long periods of time, it's not normally desirable to have it disturb the timeliness of your device communication. Similarly, you don't want device timeouts to block the GUI thread. Both are equally bad and are a very common source of bad user experience. Qt makes multithreading very easy - leverage it for your user's sake, and do it properly.
On Linux I have to do it this way to receive ASCII text ending with '\n'
QByteArray readData = pSerial.readAll();
while (readData[readData.length() - 1] != '\n') {
pSerial.waitForReadyRead(5000);
readData.append(pSerial.readAll());
}
QString result(readData);
QSerialPort::readLine() doesn't work for me either
I cannot seem to figure out why, during the while loop at the bottom,
std::cout << line;
does not print anything.
I believe that the test.txt file is not actually being written to because when I open test.txt in my folder, its empty. Any thoughts?
void Ticket::WriteTicket()
{
std::string ticketInput;
std::ofstream ticketFile("test.txt");
ticketFile.open("test.txt");
std::cout << "Please Enter Ticket Information: " << std::endl;
getline(std::cin, ticketInput);
std::cout << ticketInput << std::endl; //does print out the line
ticketFile << ticketInput;
ticketFile.close();
//here for testing only
std::string line;
std::ifstream ticketRead("test.txt");
while(getline(ticketRead, line));
{
std::cout << "something here?: " << line; // there is nothing here when it outputs
}
}
EDIT (SOLUTION):
After using some of the information that was given above, mainly from Basile Starynkevitch (I put this here because I cannot upvote yet), I was able to get the code to work!
I also did some research in my book and copied a similar program's style. Aka where to put what part of the code, and then the input worked. I continued on with the output and the key part was the std::ifstream::in in the opening of the file for output.
void Ticket::WriteTicket()
{
std::string ticketInput;
std::cout << "Please Enter Ticket Information: " << std::endl;
getline(std::cin, ticketInput);
std::ofstream ticketFile("Ticket.txt");
ticketFile << ticketInput << std::endl;
ticketFile.close();
//here for testing
std::ifstream ticketRead;
ticketRead.open("Ticket.txt", std::ifstream::in);
std::string line;
while(getline(ticketRead, line))
{
std::cout << line << std::endl;
}
}
Thank you for the help everyone!
You need to flush the output buffer.
ticketFile << ticketInput;
should be
ticketFile << ticketInput << std::endl;
std::endl flushes the output buffer.See std::flush if you don't want the new line.
C++ I/O is buffered. At least code
std::cout << "something here?: " << line << std::flush;
but in your case
std::cout << "something here?: " << line << std::endl;
would be better.
Also
std::ofstream ticketFile("test.txt")
should probably be
std::ofstream ticketFile("test.txt", std::ios_base::out);
I strongly suggest taking some hours to read more about C++ libraries before coding. Check every function or class that you are using. Of course, you also need to std::flush on ticketFile.
Maybe the file need to be openned in write mode.
Try this
std::ofstream ticketFile("test.txt","w");
I am attempting to read from/write to an RS-232 capable device. This works without issue on Linux. The device is connected via a Digitus USB/Serial Adapter.
The device shows up in Device Manager as COM4.
void PayLife::run() {
this->sendingData = 0;
this->running = true;
qDebug() << "Starting PayLife Thread";
this->port = new AbstractSerial();
this->port->setDeviceName(this->addy);
QByteArray ba;
if (port->open(AbstractSerial::ReadWrite| AbstractSerial::Unbuffered)) {
if (!port->setBaudRate(AbstractSerial::BaudRate19200)) {
qDebug() << "Set baud rate " << AbstractSerial::BaudRate19200 << " error.";
goto end_thread;
};
if (!port->setDataBits(AbstractSerial::DataBits7)) {
qDebug() << "Set data bits " << AbstractSerial::DataBits7 << " error.";
goto end_thread;
}
if (!port->setParity(AbstractSerial::ParityEven)) {
qDebug() << "Set parity " << AbstractSerial::ParityEven << " error.";
goto end_thread;
}
if (!port->setStopBits(AbstractSerial::StopBits1)) {
qDebug() << "Set stop bits " << AbstractSerial::StopBits1 << " error.";
goto end_thread;
}
if (!port->setFlowControl(AbstractSerial::FlowControlOff)) {
qDebug() << "Set flow " << AbstractSerial::FlowControlOff << " error.";
goto end_thread;
}
while(this->running) {
if ((port->bytesAvailable() > 0) || port->waitForReadyRead(900)) {
ba.clear();
ba = port->read(1024);
qDebug() << "Readed is : " << ba.size() << " bytes";
}
else {
qDebug() << "Timeout read data in time : " << QTime::currentTime();
}
}
}
end_thread:
this->running = false;
}
On Linux, I don't use QSerialDevice, just regular serial reading/writing.
No matter what, I always get:
Starting PayLife Thread
Readed is : 0 bytes
Timeout read data in time : QTime("16:27:43")
Timeout read data in time : QTime("16:27:44")
Timeout read data in time : QTime("16:27:45")
Timeout read data in time : QTime("16:27:46")
I am not exactly sure why.
Note, I tried first to use regular Windows API reading and writing with the same results, i.e. it just doesn't ready any data from the device.
I am 100% sure that there is always something to read from the device, as it spams ENQ across the connection.
You should generate the doxygen documentation of QSerialDevice if you haven't already done so. The problem seems to be explained there.
On Windows in unbuffered mode:
Necessary to avoid the values of CharIntervalTimeout and
TotalReadConstantTimeout equal to 0. In theory, it was planned that at
zero values of timeouts method AbstractSerial::read() will read the
data which are in the buffer device driver (not to be confused with
the buffer AbstractSerial!) and return them immediately. But for
unknown reasons, this reading always returns 0, not depending on
whether or not a ready-made data in the buffer.
Because read waits for the data in unbuffered mode, I guess waitForReadyReady doesn't do anything useful in that mode.