#include "serial_backend.h" #include "piezoresistive_a_protocol.h" #include "serial_qt_transport.h" #include #include #include #include #include #include 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"); auto codec = std::make_shared(); auto decoder = std::make_shared(); auto format = std::make_shared(); 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()); 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(capped)) return; m_config.deviceAddress = static_cast(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(capped)) return; m_request.functionCode = static_cast(capped); syncSendRequest_(); emit requestFunctionChanged(); } void SerialBackend::setRequestStartAddress(int addr) { const quint32 capped = static_cast(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(capped)) return; m_request.dataLength = static_cast(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 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::setSensorWidth(int w) { m_spec.cols = w; syncSendConfig_(); } void SerialBackend::setSensorHeight(int h) { m_spec.rows = h; syncSendConfig_(); } 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); }