GrabBag/AppUtils/AppCommon/Inc/BasePresenter.h

593 lines
18 KiB
C
Raw Normal View History

2026-03-28 10:49:55 +08:00
#ifndef BASEPRESENTER_H
#define BASEPRESENTER_H
#include <atomic>
#include <condition_variable>
#include <thread>
#include <mutex>
#include <vector>
#include <memory>
#include <string>
#include <QObject>
#include <QTimer>
#include "IVrEyeDevice.h"
#include "VZNL_Types.h"
#include "VrCommonConfig.h" // 包含公共配置结构体
#include "LaserDataLoader.h" // 激光数据加载器
#include "IVisionApplicationStatus.h" // 工作状态枚举
#include "DebugDataSaver.h" // 调试数据异步存储
2026-03-28 10:49:55 +08:00
// 前向声明
class IYModbusTCPServer;
/**
* @brief 基础Presenter类
*
* 提取各应用Presenter的共同功能:
* - 相机设备管理(InitCamera)
* - 算法参数初始化(InitAlgoParams - 纯虚函数)
* - 检测线程管理(DetectTask)
* - 检测数据缓存
* - 相机重连机制
*
* 子类需要实现的纯虚函数:
* - InitAlgoParams(): 初始化算法参数
* - ProcessAlgoDetection(): 执行具体的算法检测
* - OpenDevice(): 打开相机设备
* - TryReconnectCameras(): 尝试重连相机
* - OnCameraStatusChanged(): 相机状态变化通知
*
* 用法示例:
* @code
* class GrabBagPresenter : public BasePresenter {
* protected:
* int InitAlgoParams() override { ... }
* int ProcessAlgoDetection() override { ... }
* int OpenDevice(int cameraIndex, ...) override { ... }
* };
* @endcode
*/
class BasePresenter : public QObject
{
Q_OBJECT
public:
explicit BasePresenter(QObject *parent = nullptr);
virtual ~BasePresenter();
/**
* @brief 公用的初始化
* @return
*/
int Init();
/**
* @brief 开始检测
* @param cameraIndex 相机索引(-1表示所有相机)
* @param isAuto 是否自动模式
* @return 0: 成功, 其他: 错误码
*/
virtual int StartDetection(int cameraIndex = -1, bool isAuto = true);
/**
* @brief 停止检测
* @return 0: 成功, 其他: 错误码
*/
virtual int StopDetection();
/**
* @brief 获取相机列表
* @return 相机列表(IP地址和设备指针的pair)
*/
std::vector<std::pair<std::string, IVrEyeDevice*>> GetCameraList() const {
return m_vrEyeDeviceList;
}
/**
* @brief 设置默认相机索引
* @param cameraIndex 相机索引
*/
void SetDefaultCameraIndex(int cameraIndex) {
m_currentCameraIndex = cameraIndex;
}
/**
* @brief 获取默认相机索引
* @return 相机索引
*/
int GetDetectIndex() const {
return m_currentCameraIndex;
}
/**
* @brief 获取检测数据缓存大小
* @return 缓存中的数据个数
*/
int GetDetectionDataCacheSize() const;
/**
* @brief 保存当前缓存的检测数据到文件(便捷方法)
*
* 直接保存内部缓存的数据,无需传入参数
*
* @param filePath 文件路径
* @return 0: 成功, 其他: 错误码
*/
virtual int SaveDetectionDataToFile(const std::string& filePath);
/**
* @brief 加载调试数据并执行检测(virtual,子类可重写添加状态更新)
*
* 从文件加载激光扫描数据到缓存,然后执行算法检测
*
* @param filePath 文件路径
* @return 0: 成功, 其他: 错误码
*/
virtual int LoadDebugDataAndDetect(const std::string& filePath);
/**
* @brief 为所有相机设置状态回调
*
* 遍历所有相机设备,为每个设备设置状态回调函数
*
* @param fNotify 状态回调函数指针
* @param param 回调函数的用户参数
*/
void SetCameraStatusCallback(VzNL_OnNotifyStatusCBEx fNotify, void* param);
/**
* @brief 获取当前工作状态
* @return 当前工作状态
*/
WorkStatus GetCurrentWorkStatus() const {
return m_currentWorkStatus;
}
/**
* @brief 设置工作状态(会触发状态回调通知)
* @param status 新的工作状态
*/
void SetWorkStatus(WorkStatus status);
// ============ ModbusTCP 服务相关 ============
/**
* @brief 检查ModbusTCP服务是否运行
* @return true: 运行中, false: 未运行
*/
bool IsModbusServerRunning() const;
/**
* @brief 写入Modbus保持寄存器(发送数据)
* @param startAddress 起始地址
* @param data 数据指针
* @param count 寄存器数量
* @return 0: 成功, 其他: 错误码
*/
int WriteModbusRegisters(uint16_t startAddress, const uint16_t* data, uint16_t count);
/**
* @brief 设置状态回调接口(模板方法)
*
* 存储状态回调接口指针,用于通知UI状态变化
* 子类可以直接调用此方法设置自己的状态回调接口
*
* @tparam StatusCallbackType 状态回调接口类型
* @param statusCallback 状态回调接口指针
*/
template<typename StatusCallbackType>
void SetStatusCallback(StatusCallbackType* statusCallback) {
m_pStatusCallback = static_cast<void*>(statusCallback);
}
/**
* @brief 获取状态回调接口(模板方法)
*
* 获取之前设置的状态回调接口指针
*
* @tparam StatusCallbackType 状态回调接口类型
* @return 状态回调接口指针
*/
template<typename StatusCallbackType>
StatusCallbackType* GetStatusCallback() const {
return static_cast<StatusCallbackType*>(m_pStatusCallback);
}
/**
* @brief 通用的静态检测数据回调函数
*
* 此静态函数可被所有子类使用,将检测数据添加到缓存
* 子类的 GetDetectionCallback() 可以直接返回此函数
*
* @param eDataType 数据类型
* @param pLaserLinePoint 激光线数据指针
* @param pUserData 用户数据(BasePresenter实例指针)
*/
static void _StaticDetectionCallback(EVzResultDataType eDataType, SVzLaserLineData* pLaserLinePoint, void* pUserData);
/**
* @brief 通用的静态相机状态回调函数
*
* 此静态函数可被所有子类使用,处理相机状态变化
* 子类的 GetCameraStatusCallback() 可以直接返回此函数
*
* @param eStatus 相机工作状态
* @param pExtData 扩展数据
* @param nDataLength 数据长度
* @param pInfoParam 参数(BasePresenter实例指针)
*/
static void _StaticCameraStatusCallback(EVzDeviceWorkStatus eStatus, void* pExtData, unsigned int nDataLength, void* pInfoParam);
protected:
// ============ 纯虚函数 - 子类必须实现 ============
/**
* @brief 私有初始化子类应该重写此方法
* @return 0: 成功, 其他: 错误码
*/
virtual int InitApp() = 0;
/**
* @brief 初始化算法参数(纯虚函数)
*
* 子类需要在此方法中:
* - 加载算法参数配置
* - 初始化算法库
* - 加载标定参数
*
* @return 0: 成功, 其他: 错误码
*/
virtual int InitAlgoParams() = 0;
/**
* @brief 执行算法检测(纯虚函数)
*
* 子类需要在此方法中:
* - 使用传入的点云数据进行算法检测
* - 调用具体的算法库进行检测
* - 返回检测结果
*
* @param detectionDataCache 检测数据缓存的引用
* @return 0: 成功, 其他: 错误码
*/
virtual int ProcessAlgoDetection(std::vector<std::pair<EVzResultDataType, SVzLaserLineData>>& detectionDataCache) = 0;
/**
* @brief 获取相机状态回调函数(虚函数,提供默认实现)
*
* 默认返回 BasePresenter 的通用静态回调函数
* 子类如果需要自定义相机状态处理,可以重写此方法
*
* @return 相机状态回调函数指针
*/
virtual VzNL_OnNotifyStatusCBEx GetCameraStatusCallback() {
return &BasePresenter::_StaticCameraStatusCallback;
}
/**
* @brief 获取检测数据回调函数(虚函数,提供默认实现)
*
* 默认返回 BasePresenter 的通用静态回调函数
* 子类如果需要自定义检测数据处理,可以重写此方法
*
* @return 检测数据回调函数指针
*/
virtual VzNL_AutoOutputLaserLineExCB GetDetectionCallback() {
return &BasePresenter::_StaticDetectionCallback;
}
/**
* @brief 获取检测数据类型(纯虚函数)
*
* 子类需要返回项目使用的数据类型,例如:
* - keResultDataType_Position (只要XYZ位置)
* - keResultDataType_PointXYZRGBA (需要RGBA颜色)
*
* @return 数据类型
*/
virtual EVzResultDataType GetDetectionDataType() = 0;
/**
* @brief 创建设备对象(虚函数,子类可重写)
*
* 默认创建VzNLSDK设备。子类如果需要使用其他设备(如GlLineLaserDevice),
* 可以重写此方法来创建对应的设备对象。
*
* @param ppDevice 输出设备指针
* @return 0: 成功, 其他: 错误码
*/
virtual int CreateDevice(IVrEyeDevice** ppDevice);
/**
* @brief 尝试重连相机(虚函数,提供默认实现)
*
* 基类提供通用的重连逻辑:
* - 遍历所有配置的相机
* - 检查是否已连接
* - 尝试调用 OpenDevice 重连
* - 调用 OnCameraStatusChanged 通知状态变化
* - 更新相机连接状态和默认相机索引
*
* 子类通常不需要重写此方法,除非有特殊的重连逻辑
*
* @return true: 所有相机已连接, false: 仍有相机未连接
*/
virtual bool TryReconnectCameras();
/**
* @brief 相机状态变化通知(纯虚函数)
*
* @param cameraIndex 相机索引
* @param isConnected 是否连接
*/
virtual void OnCameraStatusChanged(int cameraIndex, bool isConnected) = 0;
/**
* @brief 工作状态变化通知(虚函数,子类可选择重写)
*
* 当工作状态改变时,BasePresenter 会调用此方法通知子类
* 子类应在此方法中调用状态回调接口的 OnWorkStatusChanged
*
* @param status 新的工作状态
*/
virtual void OnWorkStatusChanged(WorkStatus status) {
// 默认空实现,子类可以重写以调用UI回调
}
/**
* @brief 相机数量变化通知(虚函数,子类可选择重写)
*
* 当相机初始化时,BasePresenter 会调用此方法通知子类相机数量
* 子类应在此方法中调用状态回调接口的 OnCameraCountChanged
*
* @param count 相机数量
*/
virtual void OnCameraCountChanged(int count) {
// 默认空实现,子类可以重写以调用UI回调
}
/**
* @brief 状态文字更新通知(虚函数,子类可选择重写)
*
* 当需要更新状态文字时,BasePresenter 会调用此方法
* 子类应在此方法中调用状态回调接口的 OnStatusUpdate
*
* @param statusMessage 状态消息
*/
virtual void OnStatusUpdate(const std::string& statusMessage) {
// 默认空实现,子类可以重写以调用UI回调
}
/**
* @brief ModbusTCP服务器状态变化通知(虚函数,子类可选择重写)
*
* 当ModbusTCP客户端连接或断开时,BasePresenter 会调用此方法
* 子类应在此方法中调用状态回调接口的 OnRobotConnectionChanged
*
* @param isConnected true=客户端已连接,false=客户端已断开
*/
virtual void OnModbusServerStatusChanged(bool isConnected) {
// 默认空实现,子类可以重写以调用UI回调
}
/**
* @brief Modbus写寄存器回调(虚函数,子类可选择重写)
*
* 当收到Modbus写寄存器命令时调用,子类可以重写以实现特定控制逻辑
*
* @param startAddress 起始寄存器地址
* @param data 写入的数据
* @param count 寄存器数量
*/
virtual void OnModbusWriteCallback(uint16_t startAddress, const uint16_t* data, uint16_t count) {
// 默认空实现,子类可以重写以处理写寄存器命令
}
/**
* @brief 获取调试参数(虚函数,子类可选择重写)
*
* 子类可以重写此方法,返回从配置文件加载的调试参数
* 基类提供默认的调试参数
*
* @return 调试参数
*/
virtual VrDebugParam GetDebugParam() const {
return m_debugParam;
}
/**
* @brief 是否应该启动ModbusTCP Server(虚函数,子类可选择重写)
*
* 默认返回true,子类可以重写为false以跳过Server启动,
* 例如需要使用ModbusTCP Client模式的应用。
*
* @return true: 启动Server, false: 跳过Server启动
*/
virtual bool ShouldStartModbusServer() const { return true; }
/**
* @brief 设置调试参数
*
* 子类在初始化时调用此方法设置调试参数
*
* @param debugParam 调试参数
*/
void SetDebugParam(const VrDebugParam& debugParam) {
m_debugParam = debugParam;
}
// ============ 公共实现方法 ============
/**
* @brief 初始化相机设备(公共实现)
*
* 此方法实现了通用的相机初始化逻辑:
* - 保存相机配置
* - 初始化相机列表
* - 调用 OpenDevice 打开每个相机
* - 启动重连定时器(如果需要)
*
* @param cameraList 相机配置列表
* @param bRGB 是否启用RGB
* @param bSwing 是否启用摆动
* @return 0: 成功, 其他: 错误码
*/
virtual int InitCamera(std::vector<DeviceInfo>& cameraList, bool bRGB, bool bSwing);
/**
* @brief 清空检测数据缓存(virtual允许子类重写以添加特殊清理逻辑)
*/
virtual void ClearDetectionDataCache();
/**
* @brief 添加检测数据到缓存
* @param dataType 数据类型
* @param laserData 激光数据
*/
void AddDetectionDataToCache(EVzResultDataType dataType, const SVzLaserLineData& laserData);
/**
* @brief 获取检测数据缓存的引用(用于批量加载数据)
*
* 提供线程安全的方式访问缓存进行批量操作
* 调用者需要在调用此方法的作用域内完成所有操作
*
* @param func 操作函数,接收缓存引用作为参数,返回操作结果
* @return func 的返回值
*/
template<typename Func>
int AccessDetectionDataCache(Func func) {
std::lock_guard<std::mutex> lock(m_detectionDataMutex);
return func(m_detectionDataCache);
}
private:
/**
* @brief 打开相机设备(公共实现)
*
* 此方法实现了通用的相机打开逻辑:
* - 创建 IVrEyeDevice 对象
* - 初始化设备
* - 设置状态回调(调用 GetCameraStatusCallback)
* - 打开相机(使用 GetOpenDeviceMode)
* - 存储到设备列表
*
* @param cameraIndex 相机索引(从1开始)
* @param cameraName 相机名称
* @param cameraIp 相机IP地址
* @return 0: 成功, 其他: 错误码
*/
int OpenDevice(int cameraIndex, const char* cameraName, const char* cameraIp, bool bRGB, bool bSwing);
/**
* @brief 算法检测线程函数
*
* 实现了通用的检测线程循环:
* - 等待检测触发
* - 调用 ProcessAlgoDetection 执行算法
* - 处理检测结果
*/
void AlgoDetectThreadFunc();
/**
* @brief 检测任务主流程
*
* 实现了通用的检测任务流程:
* - 验证数据缓存
* - 调用 ProcessAlgoDetection 执行算法
* - 更新工作状态
*
* @return 0: 成功, 其他: 错误码
*/
int DetectTask();
/**
* @brief 启动算法检测线程
*/
void StartAlgoDetectThread();
/**
* @brief 停止算法检测线程
*/
void StopAlgoDetectThread();
/**
* @brief 启动相机重连定时器
*/
void StartCameraReconnectTimer();
/**
* @brief 停止相机重连定时器
*/
void StopCameraReconnectTimer();
private slots:
/**
* @brief 相机重连定时器触发
*/
void OnCameraReconnectTimer();
protected:
// 相机设备列表
std::vector<std::pair<std::string, IVrEyeDevice*>> m_vrEyeDeviceList;
// 当前相机索引
int m_currentCameraIndex = 0;
// 连接状态标志
bool m_bCameraConnected = false;
// 算法检测线程相关
std::atomic<bool> m_bAlgoDetectThreadRunning;
std::mutex m_algoDetectMutex;
std::condition_variable m_algoDetectCondition;
std::thread m_algoDetectThread;
// 检测数据缓存
std::mutex m_detectionDataMutex;
std::vector<std::pair<EVzResultDataType, SVzLaserLineData>> m_detectionDataCache;
// 激光数据加载器
LaserDataLoader m_dataLoader;
// 相机重连定时器
QTimer* m_pCameraReconnectTimer;
std::vector<DeviceInfo> m_expectedList;
bool m_bRGB{false};
bool m_bSwing{true};
// 工作状态管理
WorkStatus m_currentWorkStatus = WorkStatus::InitIng;
// 状态回调接口(使用void*存储,避免模板化整个类)
void* m_pStatusCallback = nullptr;
// ModbusTCP服务器
IYModbusTCPServer* m_modbusServer = nullptr;
// 初始化线程
std::thread m_initThread;
// 调试参数
VrDebugParam m_debugParam;
// 调试数据异步存储
DebugDataSaver m_debugDataSaver;
2026-03-28 10:49:55 +08:00
private:
// 启动ModbusTCP服务(在Init中调用)
int StartModbusServer(int port = 5020);
// 停止ModbusTCP服务
void StopModbusServer();
// Modbus写寄存器回调处理(返回0表示成功)
int OnModbusWriteRegisters(uint8_t unitId, uint16_t startAddress,
uint16_t quantity, const uint16_t* values);
};
#endif // BASEPRESENTER_H