完成主要交互、高性能组件、国际化和A型传感器数据包接收
This commit is contained in:
241
src/serial/piezoresistive_a_protocol.cpp
Normal file
241
src/serial/piezoresistive_a_protocol.cpp
Normal file
@@ -0,0 +1,241 @@
|
||||
#include "piezoresistive_a_protocol.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <qcontainerfwd.h>
|
||||
#include <qtypes.h>
|
||||
|
||||
namespace {
|
||||
constexpr quint8 kReplyStart0 = 0x55; // 0x55AA little-endian
|
||||
constexpr quint8 kReplyStart1 = 0xAA;
|
||||
constexpr quint8 kReplyStartAlt0 = 0xAA;
|
||||
constexpr quint8 kReplyStartAlt1 = 0x55;
|
||||
constexpr quint8 kRequestStart0 = 0x55; // 0x55AA little-endian
|
||||
constexpr quint8 kRequestStart1 = 0xAA;
|
||||
|
||||
quint16 readLe16(const QByteArray& data, int offset) {
|
||||
const quint8 b0 = static_cast<quint8>(data[offset]);
|
||||
const quint8 b1 = static_cast<quint8>(data[offset + 1]);
|
||||
return static_cast<quint16>(b0 | (b1 << 8));
|
||||
}
|
||||
|
||||
quint32 readLe32(const QByteArray& data, int offset) {
|
||||
const quint8 b0 = static_cast<quint8>(data[offset]);
|
||||
const quint8 b1 = static_cast<quint8>(data[offset + 1]);
|
||||
const quint8 b2 = static_cast<quint8>(data[offset + 2]);
|
||||
const quint8 b3 = static_cast<quint8>(data[offset + 3]);
|
||||
return static_cast<quint32>(b0 | (b1 << 8) | (b2 << 16) | (b3 << 24));
|
||||
}
|
||||
|
||||
void appendLe16(QByteArray& data, quint16 v) {
|
||||
data.append(static_cast<char>(v & 0xFF));
|
||||
data.append(static_cast<char>((v >> 8) & 0xFF));
|
||||
}
|
||||
|
||||
void appendLe32(QByteArray& data, quint32 v) {
|
||||
data.append(static_cast<char>(v & 0xFF));
|
||||
data.append(static_cast<char>((v >> 8) & 0xFF));
|
||||
data.append(static_cast<char>((v >> 16) & 0xFF));
|
||||
data.append(static_cast<char>((v >> 24) & 0xFF));
|
||||
}
|
||||
}
|
||||
|
||||
quint8 PiezoresistiveAFormat::crc8ITU(const QByteArray& data, int length) {
|
||||
quint8 crc = 0x00;
|
||||
const int limit = qMin(length, data.size());
|
||||
for (int i = 0; i < limit; ++i) {
|
||||
crc ^= static_cast<quint8>(data[i]);
|
||||
for (int bit = 0; bit < 8; ++bit) {
|
||||
if (crc & 0x80)
|
||||
crc = static_cast<quint8>((crc << 1) ^ 0x07);
|
||||
else
|
||||
crc = static_cast<quint8>(crc << 1);
|
||||
}
|
||||
}
|
||||
return static_cast<quint8>(crc ^ 0x55);
|
||||
}
|
||||
|
||||
ISerialFormat::ParseResult PiezoresistiveAFormat::tryParse(QByteArray* buffer, QByteArray* packet, QString* error) {
|
||||
if (!buffer || buffer->isEmpty())
|
||||
return ParseResult::NeedMore;
|
||||
|
||||
int startIndex = -1;
|
||||
for (int i = 0; i + 1 < buffer->size(); ++i) {
|
||||
if (static_cast<quint8>((*buffer)[i]) == kReplyStart0 &&
|
||||
static_cast<quint8>((*buffer)[i + 1]) == kReplyStart1) {
|
||||
startIndex = i;
|
||||
break;
|
||||
}
|
||||
if (static_cast<quint8>((*buffer)[i]) == kReplyStartAlt0 &&
|
||||
static_cast<quint8>((*buffer)[i + 1]) == kReplyStartAlt1) {
|
||||
startIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (startIndex < 0) {
|
||||
const quint8 tail = static_cast<quint8>(buffer->back());
|
||||
buffer->clear();
|
||||
if (tail == kReplyStart0 || tail == kReplyStart1)
|
||||
buffer->append(static_cast<char>(tail));
|
||||
return ParseResult::NeedMore;
|
||||
}
|
||||
|
||||
if (startIndex > 0)
|
||||
buffer->remove(0, startIndex);
|
||||
|
||||
if (buffer->size() < 5)
|
||||
return ParseResult::NeedMore;
|
||||
|
||||
const quint16 payloadLen = readLe16(*buffer, 2);
|
||||
const int totalLen = 4 + payloadLen + 1;
|
||||
if (buffer->size() < totalLen)
|
||||
return ParseResult::NeedMore;
|
||||
|
||||
QByteArray candidate = buffer->left(totalLen);
|
||||
buffer->remove(0, totalLen);
|
||||
|
||||
const quint8 crc = static_cast<quint8>(candidate.at(candidate.size() - 1));
|
||||
const quint8 calc = crc8ITU(candidate, candidate.size() - 1);
|
||||
if (crc != calc) {
|
||||
if (error)
|
||||
*error = QStringLiteral("CRC mismatch");
|
||||
return ParseResult::Invalid;
|
||||
}
|
||||
|
||||
if (packet)
|
||||
*packet = candidate;
|
||||
if (error)
|
||||
error->clear();
|
||||
return ParseResult::Ok;
|
||||
}
|
||||
|
||||
QByteArray PiezoresistiveACodec::buildRequest(const SerialConfig& config, const SensorRequest& request) {
|
||||
QByteArray packet;
|
||||
packet.reserve(15);
|
||||
|
||||
packet.append(static_cast<char>(kRequestStart0));
|
||||
packet.append(static_cast<char>(kRequestStart1));
|
||||
|
||||
const quint16 payloadLen = 9;
|
||||
appendLe16(packet, payloadLen);
|
||||
|
||||
packet.append(static_cast<char>(config.deviceAddress));
|
||||
packet.append(static_cast<char>(0x00));
|
||||
packet.append(static_cast<char>(0x80 | request.functionCode));
|
||||
|
||||
appendLe32(packet, request.startAddress);
|
||||
appendLe16(packet, request.dataLength);
|
||||
|
||||
const quint8 crc = PiezoresistiveAFormat::crc8ITU(packet, packet.size());
|
||||
packet.append(static_cast<char>(crc));
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
QByteArray PiezoresistiveACodec::buildGetVersionRequest(const SerialConfig& config) {
|
||||
// TODO:待实现内容(压阻 A 型版本号查询请求帧)
|
||||
Q_UNUSED(config)
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
QByteArray PiezoresistiveACodec::buildGetSpecRequest(const SerialConfig& config) {
|
||||
// TODO:待实现内容(压阻 A 型规格查询请求帧)
|
||||
Q_UNUSED(config)
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
bool PiezoresistiveADecoder::decodeFrame(const QByteArray& packet, DataFrame* frame, QString* error) {
|
||||
if (!frame) {
|
||||
if (error)
|
||||
*error = QStringLiteral("Null frame output");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (packet.size() < 15) {
|
||||
if (error)
|
||||
*error = QStringLiteral("Packet too short");
|
||||
return false;
|
||||
}
|
||||
|
||||
const quint8 start0 = static_cast<quint8>(packet[0]);
|
||||
const quint8 start1 = static_cast<quint8>(packet[1]);
|
||||
const bool startOk = (start0 == kReplyStart0 && start1 == kReplyStart1) ||
|
||||
(start0 == kReplyStartAlt0 && start1 == kReplyStartAlt1);
|
||||
if (!startOk) {
|
||||
if (error)
|
||||
*error = QStringLiteral("Bad start bytes");
|
||||
return false;
|
||||
}
|
||||
|
||||
const quint16 payloadLen = readLe16(packet, 2);
|
||||
const int totalLen = 4 + payloadLen + 1;
|
||||
if (packet.size() != totalLen) {
|
||||
if (error)
|
||||
*error = QStringLiteral("Length mismatch");
|
||||
return false;
|
||||
}
|
||||
|
||||
const quint8 crc = static_cast<quint8>(packet.at(packet.size() - 1));
|
||||
const quint8 calc = PiezoresistiveAFormat::crc8ITU(packet, packet.size() - 1);
|
||||
if (crc != calc) {
|
||||
if (error)
|
||||
*error = QStringLiteral("CRC mismatch");
|
||||
return false;
|
||||
}
|
||||
|
||||
const quint8 funcRaw = static_cast<quint8>(packet[6]);
|
||||
const quint16 dataLen = readLe16(packet, 11);
|
||||
const quint8 status = static_cast<quint8>(packet[13]);
|
||||
|
||||
if (payloadLen != static_cast<quint16>(10 + dataLen)) {
|
||||
if (error)
|
||||
*error = QStringLiteral("Payload length mismatch");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (status != 0) {
|
||||
if (error)
|
||||
*error = QStringLiteral("Device status error");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (packet.size() < 15 + dataLen) {
|
||||
if (error)
|
||||
*error = QStringLiteral("Data length mismatch");
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((dataLen % 2) != 0) {
|
||||
if (error)
|
||||
*error = QStringLiteral("Odd data length");
|
||||
return false;
|
||||
}
|
||||
|
||||
const int sampleCount = dataLen / 2;
|
||||
QVector<float> values;
|
||||
values.reserve(sampleCount);
|
||||
|
||||
const int dataOffset = 14;
|
||||
for (int i = 0; i < sampleCount; ++i) {
|
||||
const int offset = dataOffset + i * 2;
|
||||
const quint16 raw = readLe16(packet, offset);
|
||||
values.push_back(static_cast<float>(raw));
|
||||
}
|
||||
|
||||
frame->pts = DataFrame::makePts(QDateTime::currentDateTime());
|
||||
frame->functionCode = (funcRaw >= 0x80) ? static_cast<quint8>(funcRaw - 0x80) : funcRaw;
|
||||
frame->data = values;
|
||||
|
||||
if (error)
|
||||
error->clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PiezoresistiveADecoder::decodeSpec(const QByteArray& packet, SensorSpec* spec, QString* error) {
|
||||
// TODO:待实现内容(解析压阻 A 型规格回复并写入 SensorSpec)
|
||||
Q_UNUSED(packet)
|
||||
Q_UNUSED(spec)
|
||||
if (error)
|
||||
*error = QStringLiteral("Not implemented");
|
||||
return false;
|
||||
}
|
||||
Reference in New Issue
Block a user