完成主要交互、高性能组件、国际化和A型传感器数据包接收

This commit is contained in:
2026-01-13 16:34:28 +08:00
parent 47e6dc7244
commit 1960e6a5b9
84 changed files with 7752 additions and 332 deletions

View File

@@ -0,0 +1,358 @@
#include "serial_backend.h"
#include "piezoresistive_a_protocol.h"
#include "serial_qt_transport.h"
#include <QDebug>
#include <QMetaObject>
#include <QtGlobal>
#include <qcontainerfwd.h>
#include <qserialportinfo.h>
#include <vector>
SerialBackend::SerialBackend(QObject* parent)
: QObject(parent)
, m_packetQueue(2048)
, m_frameQueue(2048)
, m_readThread(&m_packetQueue)
, m_decodeThread(&m_packetQueue, &m_frameQueue) {
m_request.dataLength = 24;
m_spec.model = QStringLiteral("PZR-A");
m_spec.rows = 3;
m_spec.cols = 4;
auto codec = std::make_shared<PiezoresistiveACodec>();
auto decoder = std::make_shared<PiezoresistiveADecoder>();
auto format = std::make_shared<PiezoresistiveAFormat>();
m_manager.registerProtocol(codec->name(), {codec, decoder, format});
m_manager.setActiveProtocol(codec->name());
m_sendWorker = new SerialSendWorker();
m_sendWorker->moveToThread(&m_sendThread);
connect(m_sendWorker, &SerialSendWorker::bytesReceived, this, [this](const QByteArray& data) {
#if 0
if (!data.isEmpty())
qDebug().noquote() << "Serial recv bytes:" << QString::fromLatin1(data.toHex(' '));
#endif
m_readThread.enqueueBytes(data);
});
connect(m_sendWorker, &SerialSendWorker::requestBuilt, this, &SerialBackend::requestBuilt);
connect(m_sendWorker, &SerialSendWorker::writeFailed, this, [](const QString& error) {
if (!error.isEmpty())
qWarning().noquote() << "Serial write failed:" << error;
});
connect(&m_readThread, &SerialReadThread::parseError, this, [](const QString& error) {
if (!error.isEmpty())
qWarning().noquote() << "Serial packet invalid:" << error;
});
connect(&m_decodeThread, &SerialDecodeThread::decodeError, this, [](const QString& error) {
if (!error.isEmpty())
qWarning().noquote() << "Serial decode failed:" << error;
});
connect(&m_decodeThread, &SerialDecodeThread::frameAvailable, this, &SerialBackend::drainFrames_);
m_sendThread.start();
setTransport(std::make_unique<QtSerialTransport>());
updateProtocolBindings_();
syncSendConfig_();
syncSendRequest_();
refreshPorts();
}
SerialBackend::~SerialBackend() {
close();
stopPipeline_();
if (m_sendWorker && m_sendThread.isRunning()) {
QMetaObject::invokeMethod(m_sendWorker, [worker = m_sendWorker]() {
worker->closeTransport();
}, Qt::BlockingQueuedConnection);
}
if (m_sendWorker && m_sendThread.isRunning()) {
QMetaObject::invokeMethod(m_sendWorker, &QObject::deleteLater, Qt::QueuedConnection);
}
if (m_sendThread.isRunning()) {
m_sendThread.quit();
m_sendThread.wait();
}
m_sendWorker = nullptr;
}
QString SerialBackend::mode() const {
return (m_config.mode == DeviceMode::Slave) ? QStringLiteral("slave") : QStringLiteral("master");
}
QString SerialBackend::sensorGrid() const {
return QStringLiteral("%1x%2").arg(m_spec.rows).arg(m_spec.cols);
}
void SerialBackend::setPortName(const QString& name) {
if (m_config.portName == name)
return;
m_config.portName = name;
syncSendConfig_();
emit portNameChanged();
}
void SerialBackend::setBaudRate(int rate) {
if (m_config.baudRate == rate)
return;
m_config.baudRate = rate;
syncSendConfig_();
emit baudRateChanged();
}
void SerialBackend::setPollIntervalMs(int intervalMs) {
intervalMs = qMax(1, intervalMs);
if (m_config.pollIntervalMs == intervalMs)
return;
m_config.pollIntervalMs = intervalMs;
syncSendConfig_();
emit pollIntervalMsChanged();
}
void SerialBackend::setDeviceAddress(int address) {
const int capped = qBound(0, address, 255);
if (m_config.deviceAddress == static_cast<quint8>(capped))
return;
m_config.deviceAddress = static_cast<quint8>(capped);
syncSendConfig_();
emit deviceAddressChanged();
}
void SerialBackend::setMode(const QString& mode) {
const QString lower = mode.trimmed().toLower();
const DeviceMode next = (lower == QStringLiteral("master")) ? DeviceMode::Master : DeviceMode::Slave;
if (m_config.mode == next)
return;
m_config.mode = next;
syncSendConfig_();
emit modeChanged();
}
void SerialBackend::setRequestFunction(int func) {
const int capped = qBound(0, func, 255);
if (m_request.functionCode == static_cast<quint8>(capped))
return;
m_request.functionCode = static_cast<quint8>(capped);
syncSendRequest_();
emit requestFunctionChanged();
}
void SerialBackend::setRequestStartAddress(int addr) {
const quint32 capped = static_cast<quint32>(qMax(0, addr));
if (m_request.startAddress == capped)
return;
m_request.startAddress = capped;
syncSendRequest_();
emit requestStartAddressChanged();
}
void SerialBackend::setRequestLength(int len) {
const int capped = qBound(0, len, 65535);
if (m_request.dataLength == static_cast<quint16>(capped))
return;
m_request.dataLength = static_cast<quint16>(capped);
syncSendRequest_();
emit requestLengthChanged();
}
void SerialBackend::setProtocol(const QString& name) {
if (!m_manager.setActiveProtocol(name))
return;
updateProtocolBindings_();
emit protocolChanged();
}
void SerialBackend::applySensorSpec(const QString& model, int rows, int cols) {
const QString nextModel = model.trimmed();
const int nextRows = qMax(0, rows);
const int nextCols = qMax(0, cols);
bool changed = false;
if (!nextModel.isEmpty() && m_spec.model != nextModel) {
m_spec.model = nextModel;
emit sensorModelChanged();
changed = true;
}
if (m_spec.rows != nextRows || m_spec.cols != nextCols) {
m_spec.rows = nextRows;
m_spec.cols = nextCols;
emit sensorGridChanged();
changed = true;
}
if (!changed)
return;
}
void SerialBackend::setTransport(std::unique_ptr<ISerialTransport> transport) {
if (!transport || !m_sendWorker)
return;
if (!m_sendThread.isRunning())
m_sendThread.start();
if (transport->thread() != &m_sendThread)
transport->moveToThread(&m_sendThread);
QMetaObject::invokeMethod(m_sendWorker, [worker = m_sendWorker, transport = std::move(transport)]() mutable {
worker->setTransport(std::move(transport));
}, Qt::BlockingQueuedConnection);
}
void SerialBackend::refreshPorts() {
// Placeholder for real port discovery (QtSerialPort or third-party transport).
m_availablePorts.clear();
auto device_found = QSerialPortInfo::availablePorts();
for (auto item : device_found) {
m_availablePorts.append(item.portName());
}
if (m_config.portName.isEmpty() && !m_availablePorts.isEmpty()) {
m_config.portName = m_availablePorts.first();
emit portNameChanged();
}
emit availablePortsChanged();
}
bool SerialBackend::open() {
if (m_connected)
return true;
startPipeline_();
bool ok = false;
QString error;
if (m_sendWorker) {
QMetaObject::invokeMethod(m_sendWorker, [worker = m_sendWorker, config = m_config, &ok, &error]() {
ok = worker->openTransport(config, &error);
}, Qt::BlockingQueuedConnection);
}
if (!ok) {
qWarning().noquote() << "Serial open failed:" << error;
return false;
}
m_connected = true;
emit connectedChanged();
return true;
}
void SerialBackend::close() {
if (!m_connected)
return;
if (m_sendWorker && m_sendThread.isRunning()) {
QMetaObject::invokeMethod(m_sendWorker, [worker = m_sendWorker]() {
worker->closeTransport();
}, Qt::BlockingQueuedConnection);
}
stopPipeline_();
m_connected = false;
emit connectedChanged();
}
void SerialBackend::requestOnce() {
if (!m_sendWorker)
return;
QMetaObject::invokeMethod(m_sendWorker, [worker = m_sendWorker]() {
worker->requestOnce();
}, Qt::QueuedConnection);
}
void SerialBackend::feedBytes(const QByteArray& data) {
if (!m_readThread.isRunning())
startPipeline_();
m_readThread.enqueueBytes(data);
}
void SerialBackend::drainFrames_() {
if (!m_frameCallback)
return;
DataFrame frame;
while (m_frameQueue.tryPop(&frame))
m_frameCallback(frame);
}
void SerialBackend::startPipeline_() {
if (m_readThread.isRunning() || m_decodeThread.isRunning())
stopPipeline_();
m_packetQueue.reset();
m_frameQueue.reset();
m_readThread.clear();
updateProtocolBindings_();
if (!m_readThread.isRunning())
m_readThread.start();
if (!m_decodeThread.isRunning())
m_decodeThread.start();
}
void SerialBackend::stopPipeline_() {
if (m_readThread.isRunning()) {
m_readThread.stop();
m_readThread.wait();
}
if (m_decodeThread.isRunning()) {
m_decodeThread.stop();
m_decodeThread.wait();
}
m_packetQueue.reset();
m_frameQueue.reset();
m_readThread.clear();
}
void SerialBackend::updateProtocolBindings_() {
const auto bundle = m_manager.activeBundle();
SerialReadThread::ParseFunc parseFunc;
if (bundle.format) {
parseFunc = [format = bundle.format](QByteArray* buffer, QByteArray* packet, QString* error) {
return format->tryParse(buffer, packet, error);
};
}
m_readThread.setParseFunc(std::move(parseFunc));
SerialDecodeThread::DecodeFunc decodeFunc;
if (bundle.decoder) {
decodeFunc = [decoder = bundle.decoder](const QByteArray& packet, DataFrame* frame, QString* error) {
return decoder->decodeFrame(packet, frame, error);
};
}
m_decodeThread.setDecodeFunc(std::move(decodeFunc));
SerialSendWorker::BuildRequestFunc requestFunc;
if (bundle.codec) {
requestFunc = [codec = bundle.codec](const SerialConfig& config, const SensorRequest& request) {
return codec->buildRequest(config, request);
};
}
if (m_sendWorker) {
QMetaObject::invokeMethod(m_sendWorker, [worker = m_sendWorker, requestFunc]() mutable {
worker->setBuildRequestFunc(std::move(requestFunc));
}, Qt::QueuedConnection);
}
}
void SerialBackend::syncSendConfig_() {
if (!m_sendWorker)
return;
const SerialConfig config = m_config;
QMetaObject::invokeMethod(m_sendWorker, [worker = m_sendWorker, config]() {
worker->setConfig(config);
}, Qt::QueuedConnection);
}
void SerialBackend::syncSendRequest_() {
if (!m_sendWorker)
return;
const SensorRequest request = m_request;
QMetaObject::invokeMethod(m_sendWorker, [worker = m_sendWorker, request]() {
worker->setRequest(request);
}, Qt::QueuedConnection);
}