2025-12-10 00:01:32 +08:00
|
|
|
|
#ifndef BINOCULARMARKTCPPROTOCOL_H
|
|
|
|
|
|
#define BINOCULARMARKTCPPROTOCOL_H
|
|
|
|
|
|
|
|
|
|
|
|
#include <QObject>
|
|
|
|
|
|
#include <QJsonObject>
|
|
|
|
|
|
#include <QJsonArray>
|
|
|
|
|
|
#include <QJsonDocument>
|
|
|
|
|
|
#include <QByteArray>
|
|
|
|
|
|
#include <QTimer>
|
|
|
|
|
|
#include <QMap>
|
|
|
|
|
|
#include <opencv2/opencv.hpp>
|
|
|
|
|
|
#include "IYTCPServer.h"
|
|
|
|
|
|
#include "binocularMarkCam_Export.h"
|
|
|
|
|
|
#include "SG_baseDataType.h"
|
|
|
|
|
|
|
|
|
|
|
|
// 帧格式定义
|
|
|
|
|
|
#define FRAME_HEADER "##START#" // 8字节帧头
|
|
|
|
|
|
#define FRAME_TAIL "#END" // 4字节帧尾
|
|
|
|
|
|
#define FRAME_HEADER_SIZE 8
|
|
|
|
|
|
#define FRAME_TAIL_SIZE 4
|
|
|
|
|
|
#define FRAME_LENGTH_SIZE 8 // 数据长度字段(64位整数)
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 消息类型枚举
|
|
|
|
|
|
*/
|
|
|
|
|
|
enum class MarkMessageType
|
|
|
|
|
|
{
|
2025-12-18 21:36:41 +08:00
|
|
|
|
UNKNOWN, // 未知消息类型
|
|
|
|
|
|
MARK_RESULT, // 标记检测结果上报(持续工作模式)
|
|
|
|
|
|
HEARTBEAT, // 心跳消息
|
|
|
|
|
|
HEARTBEAT_ACK, // 心跳应答
|
|
|
|
|
|
CMD_TRIGGER, // 触发检测命令(已废弃)
|
|
|
|
|
|
CMD_SINGLE_DETECTION, // 单次检测命令(返回图像和Mark)
|
|
|
|
|
|
SINGLE_DETECTION_RESULT, // 单次检测结果
|
|
|
|
|
|
CMD_SINGLE_IMAGE, // 单次图像命令
|
|
|
|
|
|
IMAGE_DATA, // 图像数据
|
|
|
|
|
|
CMD_START_WORK, // 开始持续工作命令
|
|
|
|
|
|
CMD_STOP_WORK, // 停止持续工作命令
|
|
|
|
|
|
CMD_SET_CALIBRATION, // 设置标定矩阵命令
|
|
|
|
|
|
CMD_SET_EXPOSURE_TIME, // 设置曝光时间命令
|
|
|
|
|
|
CMD_SET_GAIN, // 设置增益命令
|
|
|
|
|
|
CMD_RESPONSE = 200, // 命令应答
|
2025-12-10 00:01:32 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief BinocularMark TCP通信协议类
|
|
|
|
|
|
* 实现双目标记检测系统的TCP通信协议
|
|
|
|
|
|
* 支持粘包处理
|
|
|
|
|
|
*/
|
|
|
|
|
|
class BinocularMarkTcpProtocol : public QObject
|
|
|
|
|
|
{
|
|
|
|
|
|
Q_OBJECT
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
explicit BinocularMarkTcpProtocol(QObject *parent = nullptr);
|
|
|
|
|
|
~BinocularMarkTcpProtocol();
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 启动TCP服务器
|
|
|
|
|
|
* @param port TCP端口
|
|
|
|
|
|
* @return true-成功,false-失败
|
|
|
|
|
|
*/
|
|
|
|
|
|
bool startServer(quint16 port);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 停止TCP服务器
|
|
|
|
|
|
*/
|
|
|
|
|
|
void stopServer();
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 启动心跳定时器
|
|
|
|
|
|
* @param heartbeatInterval 心跳间隔(秒),默认30秒
|
|
|
|
|
|
*/
|
|
|
|
|
|
void startHeartbeat(int heartbeatInterval = 30);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 停止心跳定时器
|
|
|
|
|
|
*/
|
|
|
|
|
|
void stopHeartbeat();
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2025-12-18 21:36:41 +08:00
|
|
|
|
* @brief 发送标记检测结果(持续工作模式)
|
2025-12-10 00:01:32 +08:00
|
|
|
|
* @param marks 检测到的3D标记列表
|
|
|
|
|
|
* @param leftImage 左相机图像
|
|
|
|
|
|
* @param rightImage 右相机图像
|
|
|
|
|
|
* @param errorCode 错误码
|
|
|
|
|
|
*/
|
|
|
|
|
|
void sendMarkResult(const std::vector<SWD_charuco3DMark>& marks,
|
|
|
|
|
|
const cv::Mat& leftImage,
|
|
|
|
|
|
const cv::Mat& rightImage,
|
|
|
|
|
|
int errorCode);
|
|
|
|
|
|
|
2025-12-18 21:36:41 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 发送单次检测结果(包含图像和Mark)
|
|
|
|
|
|
* @param pClient 客户端指针
|
|
|
|
|
|
* @param marks 检测到的3D标记列表
|
|
|
|
|
|
* @param leftImage 左相机图像
|
|
|
|
|
|
* @param rightImage 右相机图像
|
|
|
|
|
|
* @param errorCode 错误码
|
|
|
|
|
|
*/
|
|
|
|
|
|
void sendSingleDetectionResult(const TCPClient* pClient,
|
|
|
|
|
|
const std::vector<SWD_charuco3DMark>& marks,
|
|
|
|
|
|
const cv::Mat& leftImage,
|
|
|
|
|
|
const cv::Mat& rightImage,
|
|
|
|
|
|
int errorCode);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 发送图像数据
|
|
|
|
|
|
* @param pClient 客户端指针
|
|
|
|
|
|
* @param leftImage 左相机图像
|
|
|
|
|
|
* @param rightImage 右相机图像
|
|
|
|
|
|
*/
|
|
|
|
|
|
void sendImageData(const TCPClient* pClient, const cv::Mat& leftImage, const cv::Mat& rightImage);
|
|
|
|
|
|
|
2025-12-10 00:01:32 +08:00
|
|
|
|
signals:
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 触发检测信号
|
|
|
|
|
|
*/
|
|
|
|
|
|
void triggerDetection();
|
|
|
|
|
|
|
2025-12-18 21:36:41 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 单次检测请求信号
|
|
|
|
|
|
* @param pClient 请求的客户端
|
|
|
|
|
|
*/
|
|
|
|
|
|
void singleDetectionRequested(const TCPClient* pClient);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 单次图像请求信号
|
|
|
|
|
|
* @param pClient 请求的客户端
|
|
|
|
|
|
*/
|
|
|
|
|
|
void singleImageRequested(const TCPClient* pClient);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 开始持续工作请求信号
|
|
|
|
|
|
*/
|
|
|
|
|
|
void startWorkRequested();
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 停止持续工作请求信号
|
|
|
|
|
|
*/
|
|
|
|
|
|
void stopWorkRequested();
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 设置曝光时间请求信号
|
|
|
|
|
|
* @param exposureTime 曝光时间
|
|
|
|
|
|
*/
|
|
|
|
|
|
void setExposureTimeRequested(double exposureTime);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 设置增益请求信号
|
|
|
|
|
|
* @param gain 增益
|
|
|
|
|
|
*/
|
|
|
|
|
|
void setGainRequested(double gain);
|
|
|
|
|
|
|
2025-12-10 00:01:32 +08:00
|
|
|
|
private slots:
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 心跳定时器超时处理
|
|
|
|
|
|
*/
|
|
|
|
|
|
void onHeartbeatTimeout();
|
|
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief TCP服务器接收回调
|
|
|
|
|
|
*/
|
|
|
|
|
|
static void tcpRecvCallback(const TCPClient* pClient, const char* pData, const unsigned int nLen);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief TCP服务器事件回调
|
|
|
|
|
|
*/
|
|
|
|
|
|
static void tcpEventCallback(const TCPClient* pClient, TCPServerEventType eventType);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 处理接收到的数据
|
|
|
|
|
|
* @param pClient 客户端指针
|
|
|
|
|
|
* @param pData 数据指针
|
|
|
|
|
|
* @param nLen 数据长度
|
|
|
|
|
|
*/
|
|
|
|
|
|
void handleReceivedData(const TCPClient* pClient, const char* pData, unsigned int nLen);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 构造数据帧
|
|
|
|
|
|
* @param jsonData JSON数据
|
|
|
|
|
|
* @return 完整的数据帧(帧头+长度+数据+帧尾)
|
|
|
|
|
|
*/
|
|
|
|
|
|
QByteArray buildFrame(const QByteArray& jsonData);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 解析数据帧(处理粘包)
|
|
|
|
|
|
* @param clientId 客户端ID
|
|
|
|
|
|
* @param data 接收到的数据
|
|
|
|
|
|
* @param outJsonData 输出的JSON数据数组
|
|
|
|
|
|
* @return 成功解析的帧数量
|
|
|
|
|
|
*/
|
|
|
|
|
|
int parseFrames(const QString& clientId, const QByteArray& data, QList<QByteArray>& outJsonData);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 解析消息类型
|
|
|
|
|
|
* @param msgTypeStr 消息类型字符串
|
|
|
|
|
|
* @return 消息类型枚举
|
|
|
|
|
|
*/
|
|
|
|
|
|
MarkMessageType parseMessageType(const QString& msgTypeStr);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 处理JSON消息
|
|
|
|
|
|
* @param pClient 客户端指针
|
|
|
|
|
|
* @param jsonData JSON数据
|
|
|
|
|
|
*/
|
|
|
|
|
|
void handleJsonMessage(const TCPClient* pClient, const QByteArray& jsonData);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 处理心跳消息
|
|
|
|
|
|
* @param pClient 客户端指针
|
|
|
|
|
|
* @param jsonObj JSON对象
|
|
|
|
|
|
*/
|
|
|
|
|
|
void handleHeartbeat(const TCPClient* pClient, const QJsonObject& jsonObj);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 处理触发检测命令
|
|
|
|
|
|
* @param pClient 客户端指针
|
|
|
|
|
|
* @param jsonObj JSON对象
|
|
|
|
|
|
*/
|
|
|
|
|
|
void handleTriggerCommand(const TCPClient* pClient, const QJsonObject& jsonObj);
|
|
|
|
|
|
|
2025-12-18 21:36:41 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 处理单次检测命令
|
|
|
|
|
|
* @param pClient 客户端指针
|
|
|
|
|
|
* @param jsonObj JSON对象
|
|
|
|
|
|
*/
|
|
|
|
|
|
void handleSingleDetectionCommand(const TCPClient* pClient, const QJsonObject& jsonObj);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 处理单次图像命令
|
|
|
|
|
|
* @param pClient 客户端指针
|
|
|
|
|
|
* @param jsonObj JSON对象
|
|
|
|
|
|
*/
|
|
|
|
|
|
void handleSingleImageCommand(const TCPClient* pClient, const QJsonObject& jsonObj);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 处理开始工作命令
|
|
|
|
|
|
* @param pClient 客户端指针
|
|
|
|
|
|
* @param jsonObj JSON对象
|
|
|
|
|
|
*/
|
|
|
|
|
|
void handleStartWorkCommand(const TCPClient* pClient, const QJsonObject& jsonObj);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 处理停止工作命令
|
|
|
|
|
|
* @param pClient 客户端指针
|
|
|
|
|
|
* @param jsonObj JSON对象
|
|
|
|
|
|
*/
|
|
|
|
|
|
void handleStopWorkCommand(const TCPClient* pClient, const QJsonObject& jsonObj);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 处理设置标定矩阵命令
|
|
|
|
|
|
* @param pClient 客户端指针
|
|
|
|
|
|
* @param jsonObj JSON对象
|
|
|
|
|
|
*/
|
|
|
|
|
|
void handleSetCalibrationCommand(const TCPClient* pClient, const QJsonObject& jsonObj);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 处理设置曝光时间命令
|
|
|
|
|
|
* @param pClient 客户端指针
|
|
|
|
|
|
* @param jsonObj JSON对象
|
|
|
|
|
|
*/
|
|
|
|
|
|
void handleSetExposureTimeCommand(const TCPClient* pClient, const QJsonObject& jsonObj);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 处理设置增益命令
|
|
|
|
|
|
* @param pClient 客户端指针
|
|
|
|
|
|
* @param jsonObj JSON对象
|
|
|
|
|
|
*/
|
|
|
|
|
|
void handleSetGainCommand(const TCPClient* pClient, const QJsonObject& jsonObj);
|
|
|
|
|
|
|
2025-12-10 00:01:32 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 发送心跳应答
|
|
|
|
|
|
* @param pClient 客户端指针
|
|
|
|
|
|
*/
|
|
|
|
|
|
void sendHeartbeatAck(const TCPClient* pClient);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 发送命令应答
|
|
|
|
|
|
* @param pClient 客户端指针
|
|
|
|
|
|
* @param cmdType 命令类型
|
|
|
|
|
|
* @param result 执行结果
|
|
|
|
|
|
* @param errorCode 错误码
|
|
|
|
|
|
* @param errorMsg 错误消息
|
|
|
|
|
|
*/
|
|
|
|
|
|
void sendCommandResponse(const TCPClient* pClient, const QString& cmdType,
|
|
|
|
|
|
bool result, int errorCode, const QString& errorMsg);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 图像转Base64
|
|
|
|
|
|
* @param image OpenCV图像
|
|
|
|
|
|
* @return Base64字符串
|
|
|
|
|
|
*/
|
|
|
|
|
|
QString imageToBase64(const cv::Mat& image);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 生成客户端ID
|
|
|
|
|
|
* @param pClient 客户端指针
|
|
|
|
|
|
* @return 客户端ID
|
|
|
|
|
|
*/
|
|
|
|
|
|
QString generateClientId(const TCPClient* pClient);
|
|
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
IYTCPServer* m_pTcpServer; // TCP服务器
|
|
|
|
|
|
QTimer* m_pHeartbeatTimer; // 心跳定时器
|
|
|
|
|
|
int m_nHeartbeatInterval; // 心跳间隔(秒)
|
|
|
|
|
|
quint16 m_nTcpPort; // TCP服务器端口
|
|
|
|
|
|
|
|
|
|
|
|
// 客户端数据缓冲区(用于处理粘包)
|
|
|
|
|
|
QMap<QString, QByteArray> m_clientBuffers; // 客户端ID -> 数据缓冲区
|
|
|
|
|
|
|
|
|
|
|
|
static BinocularMarkTcpProtocol* s_pInstance; // 静态实例指针
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
#endif // BINOCULARMARKTCPPROTOCOL_H
|