增加串口接收线程支持,重构串口数据处理逻辑

This commit is contained in:
杰仔 2025-07-15 00:45:05 +08:00
parent 204b1653da
commit ba586bf2ee
11 changed files with 268 additions and 111 deletions

View File

@ -142,6 +142,15 @@ win32 {
LIBS += Advapi32.lib LIBS += Advapi32.lib
} }
# Default rules for deployment.
unix {
target.path = /usr/lib
# Link real-time library for POSIX shared memory functions
LIBS += -lrt
}
!isEmpty(target.path): INSTALLS += target
# Default rules for deployment. # Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin else: unix:!android: target.path = /opt/$${TARGET}/bin

View File

@ -6,6 +6,12 @@
#include <string> #include <string>
#include <QTimer> #include <QTimer>
#include <QObject> #include <QObject>
#include <QThread>
#include <QMutex>
#include <QMutexLocker>
#include <atomic>
#include <thread>
#include <mutex>
#include "ProtocolCommon.h" #include "ProtocolCommon.h"
#include <QSerialPort> #include <QSerialPort>
@ -114,11 +120,36 @@ private:
*/ */
std::string BuildDataProtocol(const MultiTargetData& multiTargetData, uint16_t cameraId); std::string BuildDataProtocol(const MultiTargetData& multiTargetData, uint16_t cameraId);
/**
* @brief 线
*/
void SerialReceiveThreadFunction();
/**
* @brief 线
*/
void StartReceiveThread();
/**
* @brief 线
*/
void StopReceiveThread();
/**
* @brief 线
*/
void ProcessReceivedData();
private: private:
QSerialPort* m_pSerialPort; // 串口对象 QSerialPort* m_pSerialPort; // 串口对象
SerialStatus m_serialStatus; // 串口状态 SerialStatus m_serialStatus; // 串口状态
QString m_receiveBuffer; // 接收缓冲区 QString m_receiveBuffer; // 接收缓冲区
// 线程相关
std::thread m_receiveThread; // 接收线程
std::atomic<bool> m_threadRunning; // 线程运行标志
std::mutex m_bufferMutex; // 缓冲区互斥锁
// 回调函数 // 回调函数
ConnectionCallback m_connectionCallback; // 连接状态回调 ConnectionCallback m_connectionCallback; // 连接状态回调
WorkSignalCallback m_workSignalCallback; // 工作信号回调 WorkSignalCallback m_workSignalCallback; // 工作信号回调

View File

@ -228,7 +228,7 @@ int GrabBagPresenter::Init()
m_pStatus->OnStatusUpdate("设备初始化完成"); m_pStatus->OnStatusUpdate("设备初始化完成");
CheckAndUpdateWorkStatus(); CheckAndUpdateWorkStatus();
#if 0
// 初始化配置管理器 // 初始化配置管理器
m_pConfigManager = std::make_unique<ConfigManager>(); m_pConfigManager = std::make_unique<ConfigManager>();
if (!m_pConfigManager->Initialize(configPath.toStdString())) { if (!m_pConfigManager->Initialize(configPath.toStdString())) {
@ -239,7 +239,7 @@ int GrabBagPresenter::Init()
// 注册为配置变化监听器 // 注册为配置变化监听器
m_pConfigManager->AddConfigChangeListener(std::shared_ptr<IConfigChangeListener>(this, [](IConfigChangeListener*){})); m_pConfigManager->AddConfigChangeListener(std::shared_ptr<IConfigChangeListener>(this, [](IConfigChangeListener*){}));
#endif
m_pStatus->OnStatusUpdate("配置管理器初始化成功"); m_pStatus->OnStatusUpdate("配置管理器初始化成功");
LOG_INFO("ConfigManager initialized successfully\n"); LOG_INFO("ConfigManager initialized successfully\n");
@ -947,9 +947,8 @@ void GrabBagPresenter::_SendDetectionResultToRobot(const DetectionResult& detect
if (m_pStatus) { if (m_pStatus) {
m_pStatus->OnStatusUpdate("没有检测到目标,发送空的多目标数据"); m_pStatus->OnStatusUpdate("没有检测到目标,发送空的多目标数据");
} }
return; // 即使检测结果为0也要发送空数据所以不return继续执行后面的发送逻辑
} } else {
// 获取检测到的目标位置(已经是机械臂坐标系) // 获取检测到的目标位置(已经是机械臂坐标系)
const auto& positions = detectionResult.positions; const auto& positions = detectionResult.positions;
multiTargetData.count = static_cast<uint16_t>(positions.size()); multiTargetData.count = static_cast<uint16_t>(positions.size());
@ -968,6 +967,7 @@ void GrabBagPresenter::_SendDetectionResultToRobot(const DetectionResult& detect
// 添加到多目标数据 // 添加到多目标数据
multiTargetData.targets.push_back(robotTarget); multiTargetData.targets.push_back(robotTarget);
} }
}
// 发送到机械臂网络ModbusTCP // 发送到机械臂网络ModbusTCP
bool robotSent = false; bool robotSent = false;

View File

@ -6,6 +6,8 @@
#include <QThread> #include <QThread>
#include <sstream> #include <sstream>
#include <iomanip> #include <iomanip>
#include <chrono>
#include <thread>
#include <QSerialPortInfo> #include <QSerialPortInfo>
@ -19,16 +21,20 @@ const QString SerialProtocol::SEPARATOR = ",";
SerialProtocol::SerialProtocol(QObject* parent) SerialProtocol::SerialProtocol(QObject* parent)
: QObject(parent) : QObject(parent)
, m_serialStatus(STATUS_DISCONNECTED) , m_serialStatus(STATUS_DISCONNECTED)
, m_threadRunning(false)
{ {
m_pSerialPort = new QSerialPort(this); m_pSerialPort = new QSerialPort(this);
// 连接信号和槽 // 连接信号和槽(保留原有的信号槽机制作为备用)
connect(m_pSerialPort, &QSerialPort::readyRead, this, &SerialProtocol::OnSerialDataReceived); connect(m_pSerialPort, &QSerialPort::readyRead, this, &SerialProtocol::OnSerialDataReceived);
connect(m_pSerialPort, &QSerialPort::errorOccurred, this, &SerialProtocol::OnSerialError); connect(m_pSerialPort, &QSerialPort::errorOccurred, this, &SerialProtocol::OnSerialError);
} }
SerialProtocol::~SerialProtocol() SerialProtocol::~SerialProtocol()
{ {
// 先停止接收线程
StopReceiveThread();
// 再关闭串口
CloseSerial(); CloseSerial();
} }
@ -96,6 +102,9 @@ int SerialProtocol::OpenSerial(const std::string& portName, int baudRate,
m_serialStatus = STATUS_CONNECTED; m_serialStatus = STATUS_CONNECTED;
LOG_INFO("Serial port opened successfully: %s\n", portName.c_str()); LOG_INFO("Serial port opened successfully: %s\n", portName.c_str());
// 启动接收线程
StartReceiveThread();
// 触发连接状态回调 // 触发连接状态回调
if (m_connectionCallback) { if (m_connectionCallback) {
m_connectionCallback(true); m_connectionCallback(true);
@ -108,6 +117,11 @@ void SerialProtocol::CloseSerial()
{ {
if (m_pSerialPort && m_pSerialPort->isOpen()) { if (m_pSerialPort && m_pSerialPort->isOpen()) {
LOG_INFO("Closing serial port: %s\n", m_pSerialPort->portName().toStdString().c_str()); LOG_INFO("Closing serial port: %s\n", m_pSerialPort->portName().toStdString().c_str());
// 先停止接收线程
StopReceiveThread();
// 再关闭串口
m_pSerialPort->close(); m_pSerialPort->close();
m_serialStatus = STATUS_DISCONNECTED; m_serialStatus = STATUS_DISCONNECTED;
@ -163,29 +177,32 @@ bool SerialProtocol::IsOpen() const
void SerialProtocol::OnSerialDataReceived() void SerialProtocol::OnSerialDataReceived()
{ {
LOG_DEBUG("OnSerialDataReceived() called\n"); // 注意这个方法现在主要作为Qt信号槽的备用机制
LOG_DEBUG("OnSerialDataReceived() called\n"); // 主要的数据接收由独立线程处理
LOG_DEBUG("OnSerialDataReceived() called\n"); LOG_DEBUG("[Signal] OnSerialDataReceived() called (backup mechanism)\n");
LOG_DEBUG("OnSerialDataReceived() called\n");
LOG_DEBUG("OnSerialDataReceived() called\n");
// 如果线程正在运行,优先使用线程处理
if (m_threadRunning.load()) {
LOG_DEBUG("[Signal] Thread is running, skipping signal-based processing\n");
return;
}
if (!m_pSerialPort) { if (!m_pSerialPort) {
LOG_ERROR("Serial port is null in OnSerialDataReceived\n"); LOG_ERROR("[Signal] Serial port is null in OnSerialDataReceived\n");
return; return;
} }
if (!m_pSerialPort->isOpen()) { if (!m_pSerialPort->isOpen()) {
LOG_ERROR("Serial port is not open in OnSerialDataReceived\n"); LOG_ERROR("[Signal] Serial port is not open in OnSerialDataReceived\n");
return; return;
} }
// 检查可用字节数 // 检查可用字节数
qint64 bytesAvailable = m_pSerialPort->bytesAvailable(); qint64 bytesAvailable = m_pSerialPort->bytesAvailable();
LOG_INFO("Bytes available to read: %lld\n", bytesAvailable); LOG_INFO("[Signal] Bytes available to read: %lld\n", bytesAvailable);
if (bytesAvailable <= 0) { if (bytesAvailable <= 0) {
LOG_WARNING("No bytes available but readyRead signal was triggered\n"); LOG_WARNING("[Signal] No bytes available but readyRead signal was triggered\n");
return; return;
} }
@ -193,18 +210,21 @@ void SerialProtocol::OnSerialDataReceived()
QByteArray data = m_pSerialPort->readAll(); QByteArray data = m_pSerialPort->readAll();
QString receivedData = QString::fromUtf8(data); QString receivedData = QString::fromUtf8(data);
LOG_INFO("Actually read %d bytes from serial port\n", data.size()); LOG_INFO("[Signal] Actually read %d bytes from serial port\n", data.size());
if (data.isEmpty()) { if (data.isEmpty()) {
LOG_WARNING("readAll() returned empty data\n"); LOG_WARNING("[Signal] readAll() returned empty data\n");
return; return;
} }
// 添加到接收缓冲区 // 使用互斥锁保护缓冲区
{
std::lock_guard<std::mutex> lock(m_bufferMutex);
m_receiveBuffer += receivedData; m_receiveBuffer += receivedData;
}
// 打印接收到的数据 // 打印接收到的数据
LOG_INFO("Received [%d bytes]: \"%s\"\n", data.size(), receivedData.toStdString().c_str()); LOG_INFO("[Signal] Received [%d bytes]: \"%s\"\n", data.size(), receivedData.toStdString().c_str());
// 如果包含不可打印字符额外打印ASCII码 // 如果包含不可打印字符额外打印ASCII码
bool hasNonPrintable = false; bool hasNonPrintable = false;
@ -224,34 +244,11 @@ void SerialProtocol::OnSerialDataReceived()
asciiInfo += QString("[%1] ").arg((unsigned char)c); asciiInfo += QString("[%1] ").arg((unsigned char)c);
} }
} }
LOG_INFO("%s\n", asciiInfo.toStdString().c_str()); LOG_INFO("[Signal] %s\n", asciiInfo.toStdString().c_str());
} }
// 查找完整的协议帧 // 处理接收到的数据
int startIndex = -1; ProcessReceivedData();
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);
// 解析协议数据
ParseProtocolData(protocolFrame);
// 移除已处理的数据
m_receiveBuffer.remove(0, endIndex + 1);
} else {
// 没有找到结束标识,等待更多数据
break;
}
}
// 如果缓冲区太大,清空一部分(防止内存溢出)
if (m_receiveBuffer.length() > 1024) {
m_receiveBuffer.clear();
}
} }
void SerialProtocol::OnSerialError(QSerialPort::SerialPortError error) void SerialProtocol::OnSerialError(QSerialPort::SerialPortError error)
@ -331,22 +328,32 @@ void SerialProtocol::ParseProtocolData(const QString& data)
// 按分隔符分割数据 // 按分隔符分割数据
QStringList parts = cleanData.split(SEPARATOR); QStringList parts = cleanData.split(SEPARATOR);
if (parts.isEmpty()) { // 移除空的部分
LOG_WARNING("Empty protocol data received\n"); 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; return;
} }
QString command = parts[0]; QString command = validParts[0];
LOG_DEBUG("Extracted command: '%s'\n", command.toStdString().c_str());
if (command == CMD_START) { if (command == CMD_START) {
// 处理开始命令:$,START,# // 处理开始命令:$,START,# 或 $,START,cameraId,#
LOG_INFO("Received START command\n"); LOG_INFO("Received START command\n");
// 默认相机ID为1如果有更多参数可以解析 // 默认相机ID为1如果有更多参数可以解析
int cameraId = 1; int cameraId = 1;
if (parts.size() > 1) { if (validParts.size() > 1) {
bool ok; bool ok;
cameraId = parts[1].toInt(&ok); cameraId = validParts[1].toInt(&ok);
if (!ok) { if (!ok) {
cameraId = 1; cameraId = 1;
} }
@ -355,8 +362,7 @@ void SerialProtocol::ParseProtocolData(const QString& data)
// 触发工作信号回调 // 触发工作信号回调
if (m_workSignalCallback) { if (m_workSignalCallback) {
bool result = m_workSignalCallback(true, cameraId); bool result = m_workSignalCallback(true, cameraId);
LOG_INFO("Work signal callback result: %s, camera ID: %d\n", LOG_INFO("Work signal callback result: %s, camera ID: %d\n", result ? "success" : "failed", cameraId);
result ? "success" : "failed", cameraId);
} }
m_serialStatus = STATUS_WORKING; m_serialStatus = STATUS_WORKING;
@ -400,11 +406,6 @@ int SerialProtocol::SendData(const std::string& data)
// 立即刷新缓冲区 // 立即刷新缓冲区
m_pSerialPort->flush(); m_pSerialPort->flush();
// 等待数据发送完成
if (!m_pSerialPort->waitForBytesWritten(3000)) {
LOG_ERROR("Timeout waiting for data to be written\n");
return -1;
}
LOG_DEBUG("Sent %lld bytes to serial port\n", bytesWritten); LOG_DEBUG("Sent %lld bytes to serial port\n", bytesWritten);
return SUCCESS; return SUCCESS;
@ -432,3 +433,118 @@ std::string SerialProtocol::BuildDataProtocol(const MultiTargetData& multiTarget
return ss.str(); 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();
}
}

View File

@ -3,8 +3,8 @@
#define GRABBAG_VERSION_STRING "1.0.1" #define GRABBAG_VERSION_STRING "1.0.1"
#define GRABBAG_BUILD_STRING "1" #define GRABBAG_BUILD_STRING "3"
#define GRABBAG_FULL_VERSION_STRING "V1.0.1_1" #define GRABBAG_FULL_VERSION_STRING "V1.0.1_3"
// 获取版本信息的便捷函数 // 获取版本信息的便捷函数
inline const char* GetGrabBagVersion() { inline const char* GetGrabBagVersion() {

View File

@ -1,8 +0,0 @@
# 1.0.1
```
1. 增加串口通信
```
# 1.0.0
```
初始版本
```

View File

@ -1,3 +1,7 @@
# 1.0.1 2025-07-14
1. 增加串口通信
# 1.0.0.7 2025-06-29 # 1.0.0.7 2025-06-29
1. 增加双相机界面: 通过配置文件config.xml 配置双相机参数,如果没有没有配置,搜索相机进行打开 1. 增加双相机界面: 通过配置文件config.xml 配置双相机参数,如果没有没有配置,搜索相机进行打开
@ -12,22 +16,22 @@
3. 修改log, config的目录。 存储在用户目录下->应用名下 3. 修改log, config的目录。 存储在用户目录下->应用名下
4. 增加应用只能启动一个 4. 增加应用只能启动一个
5. 手眼标定矩阵 5. 手眼标定矩阵
``` ```
[clib] [clib]
clib_0=1 clib_0=1
clib_1=0 clib_1=0
clib_2=0 clib_2=0
clib_3=0 clib_3=0
clib_4=0 clib_4=0
clib_5=1 clib_5=1
clib_6=0 clib_6=0
clib_7=0 clib_7=0
clib_8=0 clib_8=0
clib_9=0 clib_9=0
clib_10=1 clib_10=1
clib_11=0 clib_11=0
clib_12=0 clib_12=0
clib_13=0 clib_13=0
clib_14=0 clib_14=0
clib_15=1 clib_15=1
``` ```

View File

@ -8,7 +8,7 @@
* @brief * @brief
* *
*/ */
class VRSHAREMEM_EXPORT IVrShareMem class IVrShareMem
{ {
public: public:
virtual ~IVrShareMem() = default; virtual ~IVrShareMem() = default;
@ -94,12 +94,12 @@ public:
* @brief * @brief
* @return * @return
*/ */
VRSHAREMEM_EXPORT IVrShareMem* CreateShareMemInstance(); IVrShareMem* CreateShareMemInstance();
/** /**
* @brief * @brief
* @param instance * @param instance
*/ */
VRSHAREMEM_EXPORT void DestroyShareMemInstance(IVrShareMem* instance); void DestroyShareMemInstance(IVrShareMem* instance);
#endif // IVRSHAREMEM_H #endif // IVRSHAREMEM_H

View File

@ -1,6 +1,7 @@
#CONFIG -= qt #CONFIG -= qt
TEMPLATE = lib TEMPLATE = lib
CONFIG += staticlib
DEFINES += VRSHAREMEM_LIBRARY DEFINES += VRSHAREMEM_LIBRARY
CONFIG += c++11 CONFIG += c++11

View File

@ -300,12 +300,12 @@ bool VrShareMem::CheckBounds(size_t offset, size_t size) const
} }
// 导出函数实现 // 导出函数实现
VRSHAREMEM_EXPORT IVrShareMem* CreateShareMemInstance() IVrShareMem* CreateShareMemInstance()
{ {
return new VrShareMem(); return new VrShareMem();
} }
VRSHAREMEM_EXPORT void DestroyShareMemInstance(IVrShareMem* instance) void DestroyShareMemInstance(IVrShareMem* instance)
{ {
delete instance; delete instance;
} }

View File

@ -17,7 +17,11 @@ struct DeviceInfo
*/ */
struct SerialConfig struct SerialConfig
{ {
#ifdef _WIN32
std::string portName = "COM8"; // 串口名称
#else
std::string portName = "/dev/ttyS3"; // 串口名称 std::string portName = "/dev/ttyS3"; // 串口名称
#endif
int baudRate = 115200; // 波特率 int baudRate = 115200; // 波特率
int dataBits = 8; // 数据位 int dataBits = 8; // 数据位
int stopBits = 1; // 停止位 int stopBits = 1; // 停止位