Update frame parsing code (thanks @jpnorair)

This commit is contained in:
Alex Spataru 2021-09-23 19:40:42 -05:00
parent ed68f649a2
commit f754c25058
4 changed files with 137 additions and 82 deletions

View File

@ -22,13 +22,13 @@
#include "Checksum.h"
quint8 IO::crc8(const QByteArray &data)
uint8_t IO::crc8(const char *data, const int length)
{
quint8 crc = 0xff;
for (auto i = 0; i < data.length(); i++)
uint8_t crc = 0xff;
for (int i = 0; i < length; i++)
{
crc ^= data.at(i);
for (auto j = 0; j < 8; j++)
crc ^= data[i];
for (int j = 0; j < 8; j++)
{
if ((crc & 0x80) != 0)
crc = (uint8_t)((crc << 1) ^ 0x31);
@ -40,29 +40,29 @@ quint8 IO::crc8(const QByteArray &data)
return crc;
}
quint16 IO::crc16(const QByteArray &data)
uint16_t IO::crc16(const char *data, const int length)
{
quint8 x;
quint16 crc = 0xFFFF;
uint8_t x;
uint16_t crc = 0xFFFF;
for (auto i = 0; i < data.length(); ++i)
for (int i = 0; i < length; ++i)
{
x = crc >> 8 ^ data.at(i);
x = crc >> 8 ^ data[i];
x ^= x >> 4;
crc = (crc << 8) ^ ((quint16)(x << 12)) ^ ((quint16)(x << 5)) ^ ((quint16)x);
crc = (crc << 8) ^ ((uint16_t)(x << 12)) ^ ((uint16_t)(x << 5)) ^ ((uint16_t)x);
}
return crc;
}
quint32 IO::crc32(const QByteArray &data)
uint32_t IO::crc32(const char *data, const int length)
{
quint32 mask;
quint32 crc = 0xFFFFFFFF;
for (auto i = 0; i < data.length(); ++i)
uint32_t mask;
uint32_t crc = 0xFFFFFFFF;
for (int i = 0; i < length; ++i)
{
crc = crc ^ data.at(i);
for (auto j = 8; j >= 0; j--)
crc = crc ^ data[i];
for (int j = 8; j >= 0; j--)
{
mask = -(crc & 1);
crc = (crc >> 1) ^ (0xEDB88320 & mask);

View File

@ -23,13 +23,14 @@
#ifndef IO_CHECKSUM_H
#define IO_CHECKSUM_H
#include <QByteArray>
#include <cstdint>
#include <cstdlib>
namespace IO
{
quint8 crc8(const QByteArray &data);
quint16 crc16(const QByteArray &data);
quint32 crc32(const QByteArray &data);
uint8_t crc8(const char *data, const int length);
uint16_t crc16(const char *data, const int length);
uint32_t crc32(const char *data, const int length);
}
#endif

View File

@ -1,5 +1,6 @@
/*
* Copyright (c) 2020-2021 Alex Spataru <https://github.com/alex-spataru>
* Copyright (c) 2021 JP Norair <https://github.com/jpnorair>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -61,7 +62,8 @@ static QString ADD_ESCAPE_SEQUENCES(const QString &str)
* Constructor function
*/
Manager::Manager()
: m_writeEnabled(true)
: m_enableCrc(false)
, m_writeEnabled(true)
, m_maxBuzzerSize(1024 * 1024)
, m_device(nullptr)
, m_dataSource(DataSource::Serial)
@ -388,6 +390,9 @@ void Manager::disconnectDevice()
// Log changes
LOG_INFO() << "Device disconnected";
}
// Disable CRC checking
m_enableCrc = false;
}
/**
@ -508,6 +513,8 @@ void Manager::setWatchdogInterval(const int interval)
*
* This function also checks that the buffer size does not exceed specified size
* limitations.
*
* Implemementation credits: @jpnorair and @alex-spataru
*/
void Manager::readFrames()
{
@ -516,81 +523,127 @@ void Manager::readFrames()
return;
// Read until start/finish combinations are not found
auto bytes = 0;
auto prevBytes = 0;
auto cursor = m_dataBuffer;
auto start = startSequence().toUtf8();
auto finish = finishSequence().toUtf8();
while (m_dataBuffer.contains(start) && m_dataBuffer.contains(finish))
while (cursor.contains(start) && cursor.contains(finish))
{
// Begin reading from start sequence index
auto buffer = m_dataBuffer;
auto sIndex = m_dataBuffer.indexOf(start);
buffer.remove(0, sIndex + start.length());
// Remove the part of the buffer prior to, and including, the start sequence.
auto sIndex = cursor.indexOf(start);
cursor = cursor.mid(sIndex + start.length(), -1);
bytes += sIndex + start.length();
// Check that new buffer contains finish sequence
if (!buffer.contains(finish))
break;
// Copy a sub-buffer that goes until the finish sequence
auto fIndex = cursor.indexOf(finish);
auto frame = cursor.left(fIndex);
// Remove bytes outside start/end sequence range
auto fIndex = buffer.indexOf(finish);
buffer.remove(fIndex, buffer.length() - fIndex);
// Remove the data including the finish sequence from the master buffer
cursor = cursor.mid(fIndex + finish.length(), -1);
bytes += fIndex + finish.length();
// Buffer is not empty, notify app & remove read data from buffer
if (!buffer.isEmpty())
// Check CRC-8
if (cursor.startsWith("crc8:"))
{
// Remove frame from buffer
auto index = sIndex + fIndex + finish.length();
m_dataBuffer.remove(0, index);
// Enable the CRC flag
m_enableCrc = true;
// Check CRC-8
if (m_dataBuffer.startsWith("crc8:"))
// Check if we have enough data in the buffer
if (cursor.length() >= 6)
{
if (m_dataBuffer.length() >= 5)
{
quint8 crc = m_dataBuffer.at(4);
if (crc8(buffer) == crc)
emit frameReceived(buffer);
}
// Increment the number of bytes to remove from master buffer
bytes += 6;
// Get 8-bit checksum
quint8 crc = cursor.at(5);
// Compare checksums
if (crc8(frame.data(), frame.length()) == crc)
emit frameReceived(frame);
}
// Check CRC-16
else if (m_dataBuffer.startsWith("crc16:"))
{
if (m_dataBuffer.length() >= 7)
{
quint8 crcA = m_dataBuffer.at(5);
quint8 crcB = m_dataBuffer.at(6);
quint16 crc = (crcA << 8) | (crcB & 0xff);
if (crc16(buffer) == crc)
emit frameReceived(buffer);
}
}
// Check CRC-32
else if (m_dataBuffer.startsWith("crc32:"))
{
if (m_dataBuffer.length() >= 9)
{
quint8 crcA = m_dataBuffer.at(5);
quint8 crcB = m_dataBuffer.at(6);
quint8 crcD = m_dataBuffer.at(7);
quint8 crcC = m_dataBuffer.at(8);
quint32 crc
= (crcA << 24) | (crcB << 16) | (crcC << 8) | (crcD & 0xff);
if (crc32(buffer) == crc)
emit frameReceived(buffer);
}
}
// Buffer does not contain CRC code
// Not enough data, check next time...
else
emit frameReceived(buffer);
{
bytes = prevBytes;
break;
}
}
// Check CRC-16
else if (cursor.startsWith("crc16:"))
{
// Enable the CRC flag
m_enableCrc = true;
// Check if we have enough data in the buffer
if (cursor.length() >= 8)
{
// Increment the number of bytes to remove from master buffer
bytes += 8;
// Get 16-bit checksum
quint8 a = cursor.at(6);
quint8 b = cursor.at(7);
quint16 crc = (a << 8) | (b & 0xff);
// Compare checksums
if (crc16(frame.data(), frame.length()) == crc)
emit frameReceived(frame);
}
// Not enough data, check next time...
else
{
bytes = prevBytes;
break;
}
}
// Check CRC-32
else if (cursor.startsWith("crc32:"))
{
// Enable the CRC flag
m_enableCrc = true;
// Check if we have enough data in the buffer
if (cursor.length() >= 10)
{
// Increment the number of bytes to remove from master buffer
bytes += 10;
// Get 32-bit checksum
quint8 a = cursor.at(6);
quint8 b = cursor.at(7);
quint8 c = cursor.at(8);
quint8 d = cursor.at(9);
quint32 crc = (a << 24) | (b << 16) | (c << 8) | (d & 0xff);
// Compare checksums
if (crc32(frame.data(), frame.length()) == crc)
emit frameReceived(frame);
}
// Not enough data, check next time...
else
{
bytes = prevBytes;
break;
}
}
// Buffer does not contain CRC code
else if (!m_enableCrc)
emit frameReceived(frame);
// Frame read successfully, save the number of bytes to chop.
// This is used to manage incomplete frames with checksums
prevBytes = bytes;
}
// Clear temp. buffer (e.g. device sends a lot of invalid data)
if (m_dataBuffer.size() > maxBufferSize())
clearTempBuffer();
// Remove parsed data from master buffer
m_dataBuffer.remove(0, bytes);
}
/**

View File

@ -146,6 +146,7 @@ private:
private:
QTimer m_watchdog;
bool m_enableCrc;
bool m_writeEnabled;
int m_maxBuzzerSize;
QIODevice *m_device;