How to implement reading serial data with ANSI color codes and printing out to textbox in color
-
@lukutis222 said in How to implement reading serial data with ANSI color codes and printing out to textbox in color:
I cannot wrap my head around how does \r appear in the serial data then...
Please output the QByteArray data, not the one converted to QString (for whatever reason binary data should be converted to a QString though).
wrote on 16 Sept 2022, 06:30 last edited by lukutis222QByteArray data = serial_local->serial_connection.readAll(); //read all data QString DataAsString = QString(data); // covert qbytearray to string uint16_t received_data_length = DataAsString.length(); for(int i = 0; i <received_data_length;i++){ qDebug("%u\n",DataAsString[i]); } qDebug("****RAW DATA***** \n"); for(int i = 0; i <data.length();i++){ qDebug("%u\n",data[i]); }
Both identical.
Does that mean that my remote device "secretly" slips in another \r without me even wanting ? -
QByteArray data = serial_local->serial_connection.readAll(); //read all data QString DataAsString = QString(data); // covert qbytearray to string uint16_t received_data_length = DataAsString.length(); for(int i = 0; i <received_data_length;i++){ qDebug("%u\n",DataAsString[i]); } qDebug("****RAW DATA***** \n"); for(int i = 0; i <data.length();i++){ qDebug("%u\n",data[i]); }
Both identical.
Does that mean that my remote device "secretly" slips in another \r without me even wanting ?@lukutis222 said in How to implement reading serial data with ANSI color codes and printing out to textbox in color:
Does that mean that my remote device "secretly" slips in another \r without me even wanting ?
Yes
-
wrote on 16 Sept 2022, 09:34 last edited by lukutis222
I think I managed to achieve what I want (didint test 100% but I believe I am getting very close). I want to share the current logic, perhaps you can advise on how to improve it.
void MainWindow::readData() { // save the scrollbar position QScrollBar *scrollbar = ui->Console_read_2->verticalScrollBar(); bool scrollbarAtBottom = (scrollbar->value() >= (scrollbar->maximum() - 4)); int scrollbarPrevValue = scrollbar->value(); // this moves the cursor to the bottom to avoid writing data in the middle of the console QTextCursor cursor = ui->Console_read_2->textCursor(); cursor.clearSelection(); cursor.movePosition(QTextCursor::End); ui->Console_read_2->setTextCursor(cursor); QByteArray data = serial_local->serial_connection.readAll(); //read all data QString DataAsString = QString(data); // covert qbytearray to string static QString incomplete_line = nullptr; // hold information about the last incomplete line uint16_t beggining_pointer = 0; // save the beggining pointer DataAsString.replace("\r", "\n"); // replace all \r with \n DataAsString.replace("\n\n", "\n"); // this ensures that all double \n\n (if exist) will be replaces with just a single \n int index = DataAsString.indexOf('\n'); while (index != -1) { QString complete_line = DataAsString.mid(beggining_pointer, index-(beggining_pointer)); // index is where the \n was found in the string. complete_line = incomplete_line+complete_line; // append beggining of incomplete line to complete line (initially incomplete line will be empty) incomplete_line = nullptr; // reset the incomplete line after each append because its no longer relevant Format_and_insert(complete_line); // insert data to console beggining_pointer = index+1; // increment beggining pointer to the location where the previous \n was found because this will now be our beggining pointer index = DataAsString.indexOf('\n', index+1); } incomplete_line = DataAsString.mid(beggining_pointer, DataAsString.length()); // after going through every line, take the last beggining pointer and read till the end of string. if (scrollbarAtBottom) { ui->Console_read_2->ensureCursorVisible(); } else { ui->Console_read_2->verticalScrollBar()->setValue(scrollbarPrevValue); } }
I am reading line by line and when I find an uncomplete line, I save it in the
incomplete_line
buffer and use it on the next iteration. -
And now you're again simulating readLine()/canReadLine()...
-
And now you're again simulating readLine()/canReadLine()...
wrote on 16 Sept 2022, 09:48 last edited by JonB@Christian-Ehrlicher
Which is what OP is wanting to do. His problem is that he wishes to treat\r
, without requiring a following\n
, as a line end. I have verified that Qt code for all ofcanReadLine
/readLine
/readLineData()
have\n
hard-coded in their logic. It is not possible to use these existing ones if you want to treat a bare\r
as an acceptable line-ender. (In fact, he wishes to treat any/all of\n
,\r
or\r\n
as line terminators, which complicates things.) While the proposed code above is "rough and ready", and has some flaws, and I personally was not prepared to write the full code of what he wants, if he is happy with this "approximation" it would seem that it would give him what he wants.Of course, if he did not insist on accepting
\r
as a line terminator it would be a whole lot simpler.... -
@Christian-Ehrlicher
Which is what OP is wanting to do. His problem is that he wishes to treat\r
, without requiring a following\n
, as a line end. I have verified that Qt code for all ofcanReadLine
/readLine
/readLineData()
have\n
hard-coded in their logic. It is not possible to use these existing ones if you want to treat a bare\r
as an acceptable line-ender. (In fact, he wishes to treat any/all of\n
,\r
or\r\n
as line terminators, which complicates things.) While the proposed code above is "rough and ready", and has some flaws, and I personally was not prepared to write the full code of what he wants, if he is happy with this "approximation" it would seem that it would give him what he wants.Of course, if he did not insist on accepting
\r
as a line terminator it would be a whole lot simpler....Lifetime Qt Championwrote on 16 Sept 2022, 09:54 last edited by Christian Ehrlicher@JonB Overread this.
Still the code looks ugly and over-complicated
void parseBuffer() { auto findNextSeparator = [](const QByteArray &ba, int startOfs) { for (int i = startOfs; i < ba.size(); ++i) { auto c = ba[i]; if (c == '\n' || c == '\r') { return i; } } return -1; }; auto fromIdx = 0; idx = findNextSeparator(m_buffer, fromIdx ); while (idx >= 0) { auto line = m_buffer.mid(fromIdx , idx - fromIdx); if (!line.isEmpty()) emit newLineReceived(line); fromIdx = idx + 1; } m_buffer = m_buffer.mid(fromIdx); }
Did not check if '\r' or '\n' is in the 'line' buffer - maybe this needs a +/-1 adjustment.
-
@JonB Overread this.
Still the code looks ugly and over-complicated
void parseBuffer() { auto findNextSeparator = [](const QByteArray &ba, int startOfs) { for (int i = startOfs; i < ba.size(); ++i) { auto c = ba[i]; if (c == '\n' || c == '\r') { return i; } } return -1; }; auto fromIdx = 0; idx = findNextSeparator(m_buffer, fromIdx ); while (idx >= 0) { auto line = m_buffer.mid(fromIdx , idx - fromIdx); if (!line.isEmpty()) emit newLineReceived(line); fromIdx = idx + 1; } m_buffer = m_buffer.mid(fromIdx); }
Did not check if '\r' or '\n' is in the 'line' buffer - maybe this needs a +/-1 adjustment.
wrote on 16 Sept 2022, 10:09 last edited by JonB@Christian-Ehrlicher
Please do not take this wrong, but your code is a touch simplistic. I don't blame you, as I was not prepared to write a comprehensive implementation!Per the user's requirements, for example it fails to distinguish between incoming
\r\n
--- one line --- versus\n\n
--- a line followed by a genuine blank line. It also does not deal with end-of-file at the end of a final line which has no\r
or\n
. It does not tell the OP when he needs to callreadAll()
to fetch more characters for the buffer, and address what to do if that returns eof. It does not deal well with the (unlikely but possible?) case where areadAll()
returns a line ending in\r
because that is all that has been received so far, when in fact the next character received in the future will be a\n
after the\r
. I am not saying his code deals with all of these cases either!All of which is what I thought about when deciding whether I wished to offer the full, robust code, and decided not to... :)
Nonetheless, perhaps the OP will adapt your code to whatever situations he wants to cover.
-
I did not wrote the readAll() stuff because it does not belong to the parsing - it's a separate thing which does basically
onReadyRead() { m_buffer += m_socket->readAll(); parseBuffer(); }
So the only thing left is the double empty line.
-
I did not wrote the readAll() stuff because it does not belong to the parsing - it's a separate thing which does basically
onReadyRead() { m_buffer += m_socket->readAll(); parseBuffer(); }
So the only thing left is the double empty line.
wrote on 16 Sept 2022, 10:56 last edited by@Christian-Ehrlicher
Point taken. I was looking at the existing implementation of (woboq)readLineData()
, which does a mixture of actualread()
s and "parsing" (looking for\n
), and how that interacts withreadLine()
&canReadLine()
, and got a bit caught up in that.@lukutis222
If it's all a bit complex probably best ignore my discussion with @Christian-Ehrlicher and think about whether you want to change your code over to his suggested approach. -
@Christian-Ehrlicher
Point taken. I was looking at the existing implementation of (woboq)readLineData()
, which does a mixture of actualread()
s and "parsing" (looking for\n
), and how that interacts withreadLine()
&canReadLine()
, and got a bit caught up in that.@lukutis222
If it's all a bit complex probably best ignore my discussion with @Christian-Ehrlicher and think about whether you want to change your code over to his suggested approach.@JonB said in How to implement reading serial data with ANSI color codes and printing out to textbox in color:
I was looking at the existing implementation of (woboq) readLineData(), which does a mixture of actual read()s and "parsing" (looking for \n), and how that interacts with readLine() & canReadLine(), and got a bit caught up in that.
My approach needs some miore CPU time but is easier to understand :)
-
wrote on 19 Sept 2022, 04:32 last edited by
@JonB @Christian-Ehrlicher
Your discussion is very helpful :) Thank you very much
41/51