From cba8bdd268e87b14f09ac95edd02495a741d8ede Mon Sep 17 00:00:00 2001 From: Igor Misic Date: Mon, 18 Apr 2022 20:19:47 +0200 Subject: [PATCH] serial_port: refactoring serial read to use readyRead signal (#37) --- flasher.cpp | 42 +++++++++++++++++++++--------------------- serial_port.cpp | 46 +++++++++++++++++++++++++++++++++++++++++++--- serial_port.h | 21 +++++++++++++++++++++ 3 files changed, 85 insertions(+), 24 deletions(-) diff --git a/flasher.cpp b/flasher.cpp index 53eb8fd..3645a47 100755 --- a/flasher.cpp +++ b/flasher.cpp @@ -542,7 +542,8 @@ FlashingInfo Flasher::CheckSignature() bool Flasher::CheckAck() { bool success = false; - const auto data = serial_port_.readAll(); + QByteArray data; + serial_port_.ReadData(data); if (data.size() >= 2) { if (0 == QString::compare("OK", data, Qt::CaseInsensitive)) { qInfo() << "ACK"; @@ -563,7 +564,8 @@ bool Flasher::CheckTrue() { //TODO: better handling needed. For error false is returned bool success = false; - const auto data = serial_port_.readAll(); + QByteArray data; + serial_port_.ReadData(data); if (0 == QString::compare("TRUE", data, Qt::CaseInsensitive)) { qInfo() << "TRUE"; @@ -699,8 +701,10 @@ FlashingInfo Flasher::Erase() void Flasher::GetVersion() { serial_port_.write(kVersionCmd, sizeof(kVersionCmd)); - serial_port_.waitForReadyRead(kSerialTimeoutInMs); - emit ShowTextInBrowser(serial_port_.readAll()); + serial_port_.WaitForReadyRead(kSerialTimeoutInMs); + QByteArray data; + serial_port_.ReadData(data); + emit ShowTextInBrowser(data); } bool Flasher::GetVersionJson(QJsonObject& out_json_object) @@ -734,7 +738,7 @@ bool Flasher::IsFirmwareProtected() { qInfo() << "Send is firmware protected command"; serial_port_.write(kIsFwProtectedCmd, sizeof(kIsFwProtectedCmd)); - serial_port_.waitForReadyRead(kSerialTimeoutInMs); + serial_port_.WaitForReadyRead(kSerialTimeoutInMs); return CheckTrue(); } @@ -795,7 +799,7 @@ FlashingInfo Flasher::SendFileSize() bool Flasher::SendMessage(const char *data, qint64 length, int timeout_ms) { serial_port_.write(data, length); - serial_port_.waitForReadyRead(timeout_ms); + serial_port_.WaitForReadyRead(timeout_ms); return CheckAck(); } @@ -823,26 +827,22 @@ bool Flasher::ReadMessageWithCrc(const char *in_data, qint64 length, int timeout serial_port_.write(in_data, length); QByteArray data; - while (!timer.hasExpired(timeout_ms)) { - serial_port_.waitForReadyRead(kSerialTimeoutInMs); - data.append(serial_port_.readAll()); - QByteArray data_crc = data.right(kCrc32Size); + serial_port_.WaitForReadyRead(timeout_ms); + serial_port_.ReadData(data); + QByteArray data_crc = data.right(kCrc32Size); - if (data.size() > kCrc32Size) { + if (data.size() > kCrc32Size) { - uint32_t out_data_size = data.size() - kCrc32Size; - uint32_t crc = Deserialize32(reinterpret_cast(data_crc.data())); - uint32_t calc_crc = crc::CalculateCrc32(reinterpret_cast(data.data()), out_data_size, false, false); + uint32_t out_data_size = data.size() - kCrc32Size; + uint32_t crc = Deserialize32(reinterpret_cast(data_crc.data())); + uint32_t calc_crc = crc::CalculateCrc32(reinterpret_cast(data.data()), out_data_size, false, false); - if (calc_crc == crc) { - out_data = data.left(out_data_size); - success = true; - break; - } + if (calc_crc == crc) { + out_data = data.left(out_data_size); + success = true; } } - } return success; @@ -858,7 +858,7 @@ void Flasher::SendFlashCommand() { qInfo() << "Send flash command"; serial_port_.write(kFlashFwCmd, sizeof(kFlashFwCmd)); - serial_port_.waitForReadyRead(kSerialTimeoutInMs); + serial_port_.WaitForReadyRead(kSerialTimeoutInMs); // Check ack } diff --git a/serial_port.cpp b/serial_port.cpp index 43ffcbd..c2b92bc 100755 --- a/serial_port.cpp +++ b/serial_port.cpp @@ -37,10 +37,12 @@ #include #include #include +#include namespace communication { namespace { +constexpr int kMaxNoDataPeriod {10}; //!< Max time in [ms] while waiting for new serial data. 10 ms = 1 kHz for sender minimal task frequency. constexpr int kSerialTimeoutInMs {100}; constexpr char kSoftwareTypeCmd[] = "software_type"; constexpr char kSwTypeImBoot[] = "IMBootloader"; @@ -48,9 +50,45 @@ constexpr char kSwTypeImApp[] = "IMApplication"; } // namespace -SerialPort::SerialPort() = default; +SerialPort::SerialPort() +{ + connect(this, &communication::SerialPort::readyRead, this, &communication::SerialPort::ReadyRead); +} SerialPort::~SerialPort() = default; +void SerialPort::ReadyRead() +{ + serial_rx_data_.append(readAll()); +} + +void SerialPort::ReadData(QByteArray& data_out) +{ + data_out = serial_rx_data_; + serial_rx_data_.clear(); + previous_rx_data_size_ = 0; +} + +void SerialPort::WaitForReadyRead(int timeout) +{ + QElapsedTimer timer; + timer.start(); + + while (!timer.hasExpired(timeout)) { + + QObject().thread()->msleep(kMaxNoDataPeriod); // Give some time to the sender to send the data + waitForReadyRead(1); //QSerial known workaround for triggering readyRead signal(https://bugreports.qt.io/browse/QTBUG-78086) + + int current_rx_data_size = serial_rx_data_.size(); + if ((current_rx_data_size == previous_rx_data_size_) && (current_rx_data_size != 0)) { + // No new data. Ready to read, exit the loop. + break; + } + + previous_rx_data_size_ = current_rx_data_size; + } +} + + void SerialPort::CloseConn() { if (isOpen()) { @@ -62,9 +100,11 @@ bool SerialPort::DetectBoard(bool& is_bootloader) { bool is_board_detected; write(kSoftwareTypeCmd, sizeof(kSoftwareTypeCmd)); - waitForReadyRead(kSerialTimeoutInMs); - QString software_type = readAll(); + WaitForReadyRead(kSerialTimeoutInMs); + QByteArray data_out; + ReadData(data_out); + QString software_type = data_out; if (software_type == kSwTypeImApp) { is_bootloader = false; is_board_detected = true; diff --git a/serial_port.h b/serial_port.h index 85ce9d0..535030a 100644 --- a/serial_port.h +++ b/serial_port.h @@ -49,9 +49,30 @@ class SerialPort : public QSerialPort void CloseConn(); bool TryOpenPort(bool& is_bootloader); + /** + * Wait until RX data is ready. The function has a predefined wait value with no data on the serial line, + * so it is able to receive data in chunks. + * + * @param[in] timeout Function timeout value + */ + void WaitForReadyRead(int timeout); + + /** + * Function for copying data to a given reference. After data is copied internal buffer will be cleared. + * + * @param[out] data_out Reference to where data will be copied + */ + void ReadData(QByteArray& data_out); + + public slots: + void ReadyRead(); + private: bool DetectBoard(bool& is_bootloader); bool OpenConnection(const QString& port_name); + + QByteArray serial_rx_data_; //!< Byte Array work as an RX buffer. + int previous_rx_data_size_{0}; }; } // namespace communication