0

I know this question has already been asked many times and I have studied and also found in what part of my code is giving this error. I am asking this to get the alternative for that solution. I am dealing with a situation where I am reciving a large packet serially. I have connected a signal to read the serial data like below:

connect(&Serial, SIGNAL(readyRead()), this, SLOT(SerialRead()));

So SerialRead() is called everytime when the new serial data is available to read. I am trying to read a packet whoose length is not fixed. Now whats happening is SerialRead() reads few bytes of the data and then again goes for reading. As the packet length is big, it cannot read the complete packet in one run & I cannot move further in my code till I have full packet. Luckily, I studied the packet and found out that the 4th byte of the packet signifies the length of the packet. So now I have the length, that means I can just check if I have received the complete bytes as per the 4th byte and then move further. To do this, I made the following code:

QString serialData,numberOfBytes;
QStringList serialPacket;

void MainWindow::SerialRead()
{

  serialData.append(Serial.readAll());    //reading all the serial data
  serialPacket = serialData.split(" ");
  numberOfBytes = Convert.ToDec(serialPacket[3]);   //getting the length of the packer
  if(serialPacket.count() >= (numberOfBytes.toInt()))  //checking if all the bytes (full packet) is received or not
  {
      DisplayMessage(serialPacket);
  }
}

So when I receive the full packet, I go to my DisplayMessage function to process the packet. Now when the ASSERT Failure error came, I commented out the line numberOfBytes = Convert.ToDec(serialPacket[3]); and its remaining code and then there was no error. So I think the error tries to say that what if serialPacket[3] contains no data then it is giving error. If this is true then what is the replacement of it.? Can anyone tell me a good way to check if full packet is received or not.? Thanks

EDIT:

void MainWindow::SerialRead()
{
  serialData.append(Serial.readAll()); //Reading the data
  if(!serialData.isEmpty()) //Checking if data is empty or not
  {
    serialPacket = serialData.split(" ",QString::SkipEmptyParts);
    if(serialPacket.contains("CC")) //Checking if it contains "CC"
    {
    int index = serialPacket.indexOf("CC");
    numberOfBytes = Convert.ToDec(serialPacket[index+2]);
    if(serialPacket.count() >= ((numberOfBytes.toInt())+2))
    {
        DisplayMessage(serialPacket);
    }
    }
  }
}
S Andrew
  • 5,592
  • 27
  • 115
  • 237
  • You said 4th byte is the length of the packet. Why are you splitting the message with a `" "`? Just check the value of the 4th byte, when there are 4 bytes or more available. – thuga Apr 05 '17 at 10:41
  • @thuga actually the packet is something like this `0F A0 C0..` contains space between them. – S Andrew Apr 05 '17 at 10:43
  • If the first or the second byte in `SerialData` happens to be a space, what do you think happens when you split it? How many characters to you expect to wind up in `serialPacket` in this case? What do you expect `serialPacket[3]` to do, in that case? – Sam Varshavchik Apr 05 '17 at 10:46
  • @SamVarshavchik If that is the case, then 1st and 2nd byte will be space and serialPacket[3] will contain some other info from the packet. Then how can I handle it – S Andrew Apr 05 '17 at 10:50
  • No. That's not what's going to happen, and that's your bug. Reread the documentation for `split()` again, then stare at your code until you see your bug. If you don't see your bug, then add additional statements that dump the values of all variables. Or use a debugger to break at that point, and examine the contents of all variables. – Sam Varshavchik Apr 05 '17 at 11:02
  • @SamVarshavchik Actually while debugging this error is not coming. It debugs fine and show output correctly. I even removed all the breakpoints and then debug it , it ran smoothly. This error is only coming when I am running it without debugging. – S Andrew Apr 05 '17 at 11:05
  • 1
    You are assuming that you will always have enough data available to split it with a space and get the value in `serialPacket[3]`. Don't make such assumptions. Check first that you have enough data available. – thuga Apr 05 '17 at 11:07
  • @thuga Please have a look at the edit in my question. In it, I am first checking if serialData has some data or not. Then I am checking if the data contains byte "CC". This is fixed for every packet. If "CC" exits, then get its index number and add 2 to get the length byte. Is this correct because it still gives me error. – S Andrew Apr 05 '17 at 12:31
  • 1
    No. You're not checking if the data length you have is enough for `serialPacket[index+2]`. What if `serialPacket.size()` is `1`? – thuga Apr 05 '17 at 12:52
  • Are the packets line-delimited? – Kuba hasn't forgotten Monica Apr 05 '17 at 14:23

2 Answers2

1

The decoder will perform better if you make the parsing more explicit in the formalism of a parser. A two-state machine will do the job. Below is a full test case:

// https://github.com/KubaO/stackoverflown/tree/master/questions/packet-read-43228728
#include <QtTest>
#include <private/qringbuffer_p.h>

// See http://stackoverflow.com/a/32317276/1329652
/// A simple point-to-point intra-process pipe. The other endpoint can live in any
/// thread.
class AppPipe : public QIODevice {
   //...
};

class Decoder : public QObject {
   Q_OBJECT
   QPointer<QIODevice> m_device;
   QByteArray m_data;
   char m_first;
   bool m_isFirst = true;
   static constexpr char fromHex(char c) {
      return
            (c >= '0' && c <= '9') ? (c - '0') :
            (c >= 'A' && c <= 'F') ? (c - 'A' + 10) :
            (c >= 'a' && c <= 'f') ? (c - 'a' + 10) :
            -1;
   }
   void decode(const QByteArray & src) {
      for (auto c : src) {
         auto val = fromHex(c);
         if (val < 0) continue;
         if (m_isFirst)
             m_first = val << 4;
         else
             m_data.append(m_first | val);
         m_isFirst = !m_isFirst;
      }
   }
   void onReadyRead() {
      // The data has the format "XX XX XX" where X are hex digits.
      // Spaces and invalid digits are skipped
      decode(m_device->readAll());
      if (m_data.size() >= 4) {
         auto length = 4 + m_data[3];
         if (m_data.size() >= length) {
            emit hasMessage(m_data.left(length));
            m_data.remove(0, length);
         }
      }
   }
public:
   Decoder(QIODevice * dev, QObject * parent = {}) : QObject{parent}, m_device{dev} {
      connect(dev, &QIODevice::readyRead, this, &Decoder::onReadyRead);
   }
   Q_SIGNAL void hasMessage(const QByteArray &);
};

class DecoderTest : public QObject {
   Q_OBJECT
   AppPipe src{nullptr, QIODevice::ReadWrite};
   AppPipe dst{&src, QIODevice::ReadWrite};
   Q_SLOT void initTestCase() {
      src.addOther(&dst);
   }
   Q_SLOT void test1() {
      Decoder dec(&dst, this);
      QSignalSpy spy(&dec, &Decoder::hasMessage);

      src.write("0"); // send a partial header
      QCOMPARE(spy.size(), 0);
      src.write("0 00 00 03 "); // send rest of the header
      QCOMPARE(spy.size(), 0);
      src.write("0A 0B "); // send partial data
      QCOMPARE(spy.size(), 0);
      src.write("0C "); // send rest of data
      QCOMPARE(spy.size(), 1);

      QCOMPARE(dst.bytesAvailable(), 0); // ensure all data has been read

      const QByteArray packet{"\x00\x00\x00\x03\x0A\x0B\x0C", 4+3};
      QCOMPARE(spy.first().size(), 1);
      QCOMPARE(spy.first().first(), {packet});
   }
   Q_SLOT void test2() {
      Decoder dec(&dst, this);
      QSignalSpy spy(&dec, &Decoder::hasMessage);

      src.write("BABE0004 C001 DA7E\n0FAB33"); // send a packet and part of another
      QCOMPARE(spy.size(), 1);
      src.write("01 AB\n");
      QCOMPARE(spy.size(), 2);

      QCOMPARE(spy.at(0).size(), 1);
      QCOMPARE(spy.at(1).size(), 1);
      const QByteArray packet1{"\xBA\xBE\x00\x04\xC0\x01\xDA\x7E", 4+4};
      const QByteArray packet2{"\x0F\xAB\x33\x01\xAB", 4+1};
      QCOMPARE(spy.at(0).first(), {packet1});
      QCOMPARE(spy.at(1).first(), {packet2});
   }
};

QTEST_GUILESS_MAIN(DecoderTest)
#include "main.moc"
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
1

As mentioned in comments by other users, you have to make sure that the packet you are trying to receive has some data at 4th index. So that you can further process it to calculate length. As you said that the 4th index of the serialPacket will contain the length, you will have to put some condition that size of the serial packet contains atleast 4 or 5 bytes. Something like:

    if(serialPacket.size()>=5)
    {
        numberOfBytes = Convert.ToDec(serialPacket[3]);
    }
    else
    {
        QMessageBox MessageBox;
        MessageBox.setText("Error receving packets");
    }
Aircraft
  • 275
  • 2
  • 5
  • 12