GrabBag/GrabBagApp/Presenter/Src/SerialProtocol.cpp

551 lines
18 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "SerialProtocol.h"
#include "VrLog.h"
#include "VrError.h"
#include <QStringList>
#include <QDebug>
#include <QThread>
#include <sstream>
#include <iomanip>
#include <chrono>
#include <thread>
#include <QSerialPortInfo>
// 协议常量定义
const QString SerialProtocol::PROTOCOL_START = "$";
const QString SerialProtocol::PROTOCOL_END = "#";
const QString SerialProtocol::CMD_START = "START";
const QString SerialProtocol::CMD_DATA = "DATA";
const QString SerialProtocol::SEPARATOR = ",";
SerialProtocol::SerialProtocol(QObject* parent)
: QObject(parent)
, m_serialStatus(STATUS_DISCONNECTED)
, m_threadRunning(false)
{
m_pSerialPort = new QSerialPort(this);
// 连接信号和槽(保留原有的信号槽机制作为备用)
connect(m_pSerialPort, &QSerialPort::readyRead, this, &SerialProtocol::OnSerialDataReceived);
connect(m_pSerialPort, &QSerialPort::errorOccurred, this, &SerialProtocol::OnSerialError);
}
SerialProtocol::~SerialProtocol()
{
// 先停止接收线程
StopReceiveThread();
// 再关闭串口
CloseSerial();
}
int SerialProtocol::OpenSerial(const std::string& portName, int baudRate,
int dataBits, int stopBits, int parity, int flowControl)
{
if (m_pSerialPort->isOpen()) {
LOG_WARNING("Serial port is already open: %s\n", portName.c_str());
return SUCCESS;
}
LOG_INFO("Opening serial port: %s, baudRate: %d, dataBits: %d, stopBits: %d, parity: %d, flowControl: %d\n",
portName.c_str(), baudRate, dataBits, stopBits, parity, flowControl);
// 设置串口参数
m_pSerialPort->setPortName(QString::fromStdString(portName));
m_pSerialPort->setBaudRate(baudRate);
// 设置数据位
switch (dataBits) {
case 5: m_pSerialPort->setDataBits(QSerialPort::Data5); break;
case 6: m_pSerialPort->setDataBits(QSerialPort::Data6); break;
case 7: m_pSerialPort->setDataBits(QSerialPort::Data7); break;
case 8: m_pSerialPort->setDataBits(QSerialPort::Data8); break;
default: m_pSerialPort->setDataBits(QSerialPort::Data8); break;
}
// 设置停止位
switch (stopBits) {
case 1: m_pSerialPort->setStopBits(QSerialPort::OneStop); break;
case 2: m_pSerialPort->setStopBits(QSerialPort::TwoStop); break;
default: m_pSerialPort->setStopBits(QSerialPort::OneStop); break;
}
// 设置校验位
switch (parity) {
case 0: m_pSerialPort->setParity(QSerialPort::NoParity); break;
case 1: m_pSerialPort->setParity(QSerialPort::OddParity); break;
case 2: m_pSerialPort->setParity(QSerialPort::EvenParity); break;
default: m_pSerialPort->setParity(QSerialPort::NoParity); break;
}
// 设置流控制
switch (flowControl) {
case 0: m_pSerialPort->setFlowControl(QSerialPort::NoFlowControl); break;
case 1: m_pSerialPort->setFlowControl(QSerialPort::HardwareControl); break;
case 2: m_pSerialPort->setFlowControl(QSerialPort::SoftwareControl); break;
default: m_pSerialPort->setFlowControl(QSerialPort::NoFlowControl); break;
}
// 尝试打开串口
if (!m_pSerialPort->open(QIODevice::ReadWrite)) {
LOG_ERROR("Failed to open serial port: %s, error: %s\n", portName.c_str(), m_pSerialPort->errorString().toStdString().c_str());
m_serialStatus = STATUS_ERROR;
return ERR_CODE(DEV_OPEN_ERR);
}
// 清空接收缓冲区
m_receiveBuffer.clear();
m_pSerialPort->clear();
// 设置读取超时和缓冲区大小
m_pSerialPort->setReadBufferSize(1024);
m_serialStatus = STATUS_CONNECTED;
LOG_INFO("Serial port opened successfully: %s\n", portName.c_str());
// 启动接收线程
StartReceiveThread();
// 触发连接状态回调
if (m_connectionCallback) {
m_connectionCallback(true);
}
return SUCCESS;
}
void SerialProtocol::CloseSerial()
{
if (m_pSerialPort && m_pSerialPort->isOpen()) {
LOG_INFO("Closing serial port: %s\n", m_pSerialPort->portName().toStdString().c_str());
// 先停止接收线程
StopReceiveThread();
// 再关闭串口
m_pSerialPort->close();
m_serialStatus = STATUS_DISCONNECTED;
// 触发连接状态回调
if (m_connectionCallback) {
m_connectionCallback(false);
}
}
}
int SerialProtocol::SendMultiTargetData(const MultiTargetData& multiTargetData, uint16_t cameraId)
{
if (!m_pSerialPort || !m_pSerialPort->isOpen()) {
LOG_ERROR("Serial port is not open, cannot send data\n");
return -1;
}
LOG_DEBUG("Sending multi-target data, count: %d, camera ID: %d\n", multiTargetData.count, cameraId);
// 构造协议数据
std::string protocolData = BuildDataProtocol(multiTargetData, cameraId);
// 发送数据
int result = SendData(protocolData);
if (result == SUCCESS) {
LOG_INFO("Multi-target data sent successfully: %s\n", protocolData.c_str());
} else {
LOG_ERROR("Failed to send multi-target data\n");
}
return result;
}
SerialProtocol::SerialStatus SerialProtocol::GetSerialStatus() const
{
return m_serialStatus;
}
void SerialProtocol::SetConnectionCallback(const ConnectionCallback& callback)
{
m_connectionCallback = callback;
}
void SerialProtocol::SetWorkSignalCallback(const WorkSignalCallback& callback)
{
m_workSignalCallback = callback;
}
bool SerialProtocol::IsOpen() const
{
return m_pSerialPort && m_pSerialPort->isOpen();
}
void SerialProtocol::OnSerialDataReceived()
{
// 注意这个方法现在主要作为Qt信号槽的备用机制
// 主要的数据接收由独立线程处理
LOG_DEBUG("[Signal] OnSerialDataReceived() called (backup mechanism)\n");
// 如果线程正在运行,优先使用线程处理
if (m_threadRunning.load()) {
LOG_DEBUG("[Signal] Thread is running, skipping signal-based processing\n");
return;
}
if (!m_pSerialPort) {
LOG_ERROR("[Signal] Serial port is null in OnSerialDataReceived\n");
return;
}
if (!m_pSerialPort->isOpen()) {
LOG_ERROR("[Signal] Serial port is not open in OnSerialDataReceived\n");
return;
}
// 检查可用字节数
qint64 bytesAvailable = m_pSerialPort->bytesAvailable();
LOG_INFO("[Signal] Bytes available to read: %lld\n", bytesAvailable);
if (bytesAvailable <= 0) {
LOG_WARNING("[Signal] No bytes available but readyRead signal was triggered\n");
return;
}
// 读取所有可用数据
QByteArray data = m_pSerialPort->readAll();
QString receivedData = QString::fromUtf8(data);
LOG_INFO("[Signal] Actually read %d bytes from serial port\n", data.size());
if (data.isEmpty()) {
LOG_WARNING("[Signal] readAll() returned empty data\n");
return;
}
// 使用互斥锁保护缓冲区
{
std::lock_guard<std::mutex> lock(m_bufferMutex);
m_receiveBuffer += receivedData;
}
// 打印接收到的数据
LOG_INFO("[Signal] Received [%d bytes]: \"%s\"\n", data.size(), receivedData.toStdString().c_str());
// 如果包含不可打印字符额外打印ASCII码
bool hasNonPrintable = false;
for (char c : data) {
if (c < 32 || c > 126) {
hasNonPrintable = true;
break;
}
}
if (hasNonPrintable) {
QString asciiInfo = "ASCII: ";
for (char c : data) {
if (c >= 32 && c <= 126) {
asciiInfo += QString("'%1' ").arg(c);
} else {
asciiInfo += QString("[%1] ").arg((unsigned char)c);
}
}
LOG_INFO("[Signal] %s\n", asciiInfo.toStdString().c_str());
}
// 处理接收到的数据
ProcessReceivedData();
}
void SerialProtocol::OnSerialError(QSerialPort::SerialPortError error)
{
if (error == QSerialPort::NoError) {
return;
}
QString errorString = m_pSerialPort ? m_pSerialPort->errorString() : "Unknown error";
LOG_ERROR("=== SERIAL PORT ERROR ===\n");
LOG_ERROR("Error type: %d\n", (int)error);
LOG_ERROR("Error message: %s\n", errorString.toStdString().c_str());
LOG_ERROR("Port open status: %s\n", (m_pSerialPort && m_pSerialPort->isOpen()) ? "OPEN" : "CLOSED");
// 详细的错误类型说明
switch (error) {
case QSerialPort::DeviceNotFoundError:
LOG_ERROR("Device not found error - port may be disconnected\n");
break;
case QSerialPort::PermissionError:
LOG_ERROR("Permission error - port may be in use by another application\n");
break;
case QSerialPort::OpenError:
LOG_ERROR("Open error - failed to open the port\n");
break;
case QSerialPort::WriteError:
LOG_ERROR("Write error - failed to write data\n");
break;
case QSerialPort::ReadError:
LOG_ERROR("Read error - failed to read data\n");
break;
case QSerialPort::ResourceError:
LOG_ERROR("Resource error - device disappeared or became unavailable\n");
break;
case QSerialPort::UnsupportedOperationError:
LOG_ERROR("Unsupported operation error\n");
break;
case QSerialPort::TimeoutError:
LOG_ERROR("Timeout error\n");
break;
default:
LOG_ERROR("Unknown error type: %d\n", (int)error);
break;
}
// 设置错误状态
m_serialStatus = STATUS_ERROR;
// 根据错误类型处理
switch (error) {
case QSerialPort::ResourceError:
case QSerialPort::DeviceNotFoundError:
case QSerialPort::PermissionError:
LOG_ERROR("Critical error - notifying connection callback\n");
// 连接断开错误
if (m_connectionCallback) {
m_connectionCallback(false);
}
break;
default:
LOG_WARNING("Non-critical error - continuing operation\n");
break;
}
LOG_ERROR("=== END SERIAL ERROR ===\n");
}
void SerialProtocol::ParseProtocolData(const QString& data)
{
LOG_DEBUG("Parsing protocol data: %s\n", data.toStdString().c_str());
// 移除协议开始和结束标识
QString cleanData = data;
cleanData.remove(PROTOCOL_START);
cleanData.remove(PROTOCOL_END);
// 按分隔符分割数据
QStringList parts = cleanData.split(SEPARATOR);
// 移除空的部分
QStringList validParts;
for (const QString& part : parts) {
if (!part.isEmpty()) {
validParts.append(part);
}
}
if (validParts.isEmpty()) {
LOG_WARNING("No valid parts found in protocol data\n");
return;
}
QString command = validParts[0];
LOG_DEBUG("Extracted command: '%s'\n", command.toStdString().c_str());
if (command == CMD_START) {
// 处理开始命令:$,START,# 或 $,START,cameraId,#
LOG_INFO("Received START command\n");
// 默认相机ID为1如果有更多参数可以解析
int cameraId = 1;
if (validParts.size() > 1) {
bool ok;
cameraId = validParts[1].toInt(&ok);
if (!ok) {
cameraId = 1;
}
}
// 触发工作信号回调
if (m_workSignalCallback) {
bool result = m_workSignalCallback(true, cameraId);
LOG_INFO("Work signal callback result: %s, camera ID: %d\n", result ? "success" : "failed", cameraId);
}
m_serialStatus = STATUS_WORKING;
} else {
LOG_WARNING("Unknown command received: %s\n", command.toStdString().c_str());
}
}
int SerialProtocol::SendData(const std::string& data)
{
if (!m_pSerialPort || !m_pSerialPort->isOpen()) {
LOG_ERROR("Serial port is not open, cannot send data\n");
return -1;
}
// 检查串口是否可写
if (!m_pSerialPort->isWritable()) {
LOG_ERROR("Serial port is not writable\n");
return -1;
}
// 检查待写入的字节数
qint64 bytesToWrite = m_pSerialPort->bytesToWrite();
LOG_INFO("Bytes pending write before send: %lld\n", bytesToWrite);
QByteArray dataToSend = data.c_str();
qint64 bytesWritten = m_pSerialPort->write(dataToSend);
LOG_INFO("Write operation result: %lld bytes (expected: %d)\n", bytesWritten, dataToSend.size());
if (bytesWritten == -1) {
LOG_ERROR("Failed to write data to serial port: %s\n", m_pSerialPort->errorString().toStdString().c_str());
m_serialStatus = STATUS_ERROR;
return -1;
}
if (bytesWritten != dataToSend.size()) {
LOG_WARNING("PARTIAL WRITE: %lld of %d bytes written\n", bytesWritten, dataToSend.size());
}
// 立即刷新缓冲区
m_pSerialPort->flush();
LOG_DEBUG("Sent %lld bytes to serial port\n", bytesWritten);
return SUCCESS;
}
std::string SerialProtocol::BuildDataProtocol(const MultiTargetData& multiTargetData, uint16_t cameraId)
{
std::stringstream ss;
// 协议格式:$,DATA,NUM,posX,posY,posZ,posC,posX,posY,posZ,posC...,#
ss << PROTOCOL_START.toStdString() << SEPARATOR.toStdString()
<< CMD_DATA.toStdString() << SEPARATOR.toStdString()
<< multiTargetData.count;
// 添加每个目标的坐标数据
for (const auto& target : multiTargetData.targets) {
ss << SEPARATOR.toStdString()
<< std::fixed << std::setprecision(2) << target.x << SEPARATOR.toStdString()
<< std::fixed << std::setprecision(2) << target.y << SEPARATOR.toStdString()
<< std::fixed << std::setprecision(2) << target.z << SEPARATOR.toStdString()
<< std::fixed << std::setprecision(2) << target.rz;
}
ss << SEPARATOR.toStdString() << PROTOCOL_END.toStdString();
return ss.str();
}
// 线程方法实现
void SerialProtocol::StartReceiveThread()
{
if (m_threadRunning.load()) {
LOG_WARNING("Receive thread is already running\n");
return;
}
LOG_INFO("Starting serial receive thread\n");
m_threadRunning.store(true);
// 启动接收线程
m_receiveThread = std::thread(&SerialProtocol::SerialReceiveThreadFunction, this);
}
void SerialProtocol::StopReceiveThread()
{
if (!m_threadRunning.load()) {
LOG_DEBUG("Receive thread is not running\n");
return;
}
LOG_INFO("Stopping serial receive thread\n");
m_threadRunning.store(false);
// 等待线程结束
if (m_receiveThread.joinable()) {
m_receiveThread.join();
LOG_INFO("Serial receive thread stopped\n");
}
}
void SerialProtocol::SerialReceiveThreadFunction()
{
LOG_INFO("Serial receive thread started\n");
while (m_threadRunning.load()) {
// 检查串口是否打开
if (!m_pSerialPort || !m_pSerialPort->isOpen()) {
// 串口未打开,短暂休眠后继续检查
std::this_thread::sleep_for(std::chrono::milliseconds(100));
continue;
}
try {
// 使用waitForReadyRead进行阻塞式等待超时时间100ms
if (m_pSerialPort->waitForReadyRead(100)) {
// 有数据可读
qint64 bytesAvailable = m_pSerialPort->bytesAvailable();
if (bytesAvailable > 0) {
LOG_INFO("[Thread] Bytes available to read: %lld\n", bytesAvailable);
// 读取所有可用数据
QByteArray data = m_pSerialPort->readAll();
if (!data.isEmpty()) {
QString receivedData = QString::fromUtf8(data);
LOG_INFO("[Thread] Actually read %d bytes from serial port\n", data.size());
LOG_INFO("[Thread] Received [%d bytes]: \"%s\"\n", data.size(), receivedData.toStdString().c_str());
// 使用互斥锁保护缓冲区
{
std::lock_guard<std::mutex> lock(m_bufferMutex);
m_receiveBuffer += receivedData;
}
// 处理接收到的数据
ProcessReceivedData();
}
}
}
} catch (const std::exception& e) {
LOG_ERROR("[Thread] Exception in receive thread: %s\n", e.what());
} catch (...) {
LOG_ERROR("[Thread] Unknown exception in receive thread\n");
}
}
LOG_INFO("Serial receive thread finished\n");
}
void SerialProtocol::ProcessReceivedData()
{
std::lock_guard<std::mutex> lock(m_bufferMutex);
// 查找完整的协议帧
int startIndex = -1;
int endIndex = -1;
while ((startIndex = m_receiveBuffer.indexOf(PROTOCOL_START)) != -1) {
endIndex = m_receiveBuffer.indexOf(PROTOCOL_END, startIndex);
if (endIndex != -1) {
// 提取完整的协议帧
QString protocolFrame = m_receiveBuffer.mid(startIndex, endIndex - startIndex + 1);
LOG_INFO("[Thread] Found complete protocol frame: %s\n", protocolFrame.toStdString().c_str());
// 解析协议数据
ParseProtocolData(protocolFrame);
// 移除已处理的数据
m_receiveBuffer.remove(0, endIndex + 1);
} else {
// 没有找到结束标识,等待更多数据
break;
}
}
// 如果缓冲区太大,清空一部分(防止内存溢出)
if (m_receiveBuffer.length() > 1024) {
LOG_WARNING("[Thread] Receive buffer too large (%d bytes), clearing\n", m_receiveBuffer.length());
m_receiveBuffer.clear();
}
}