#include "BasePresenter.h" #include "VrLog.h" #include "VrError.h" #include #include #include #include #include #include #include "PathManager.h" #include "IYModbusTCPServer.h" BasePresenter::BasePresenter(QObject *parent) : QObject(parent) , m_currentCameraIndex(0) , m_bCameraConnected(false) , m_bAlgoDetectThreadRunning(false) , m_pCameraReconnectTimer(nullptr) { // 创建相机重连定时器 m_pCameraReconnectTimer = new QTimer(this); m_pCameraReconnectTimer->setInterval(2000); // 默认2秒 connect(m_pCameraReconnectTimer, &QTimer::timeout, this, &BasePresenter::OnCameraReconnectTimer); } BasePresenter::~BasePresenter() { // 清除状态回调指针,防止后续回调访问 m_pStatusCallback = nullptr; // 等待初始化线程完成 if (m_initThread.joinable()) { m_initThread.join(); } // 处理待处理的 Qt 事件,确保 QueuedConnection 的回调执行时检查到 m_pStatusCallback 为空 QCoreApplication::processEvents(); // 停止检测线程 StopAlgoDetectThread(); // 停止调试数据异步存储 m_debugDataSaver.Stop(); // 释放检测数据缓存中的动态内存 ClearDetectionDataCache(); // 停止重连定时器 StopCameraReconnectTimer(); // 停止ModbusTCP服务器 StopModbusServer(); // 清理相机设备 for (size_t i = 0; i < m_vrEyeDeviceList.size(); ++i) { auto& camera = m_vrEyeDeviceList[i]; if (camera.second) { camera.second->CloseDevice(); delete camera.second; camera.second = nullptr; } } m_vrEyeDeviceList.clear(); // 清理相机回调上下文 for (auto& pair : m_cameraContexts) { delete pair.second; } m_cameraContexts.clear(); // 清理各相机独立数据缓存 ClearAllCameraDataCaches(); LOG_INFO("BasePresenter destructor finished\n"); } int BasePresenter::Init() { LOG_INFO("BasePresenter::Init()\n"); // 在后台线程中执行初始化 m_initThread = std::thread([this]() { int nRet = InitApp(); if (nRet != SUCCESS) { LOG_ERROR("InitApp failed: %d\n", nRet); return; } // 初始化算法参数 nRet = InitAlgoParams(); LOG_INFO("Algorithm parameters initialization result: %d\n", nRet); // 启动ModbusTCP服务(子类可通过重写 ShouldStartModbusServer() 跳过) if (ShouldStartModbusServer()) { int modbusRet = StartModbusServer(5020); if (modbusRet == SUCCESS) { LOG_INFO("ModbusTCP server started on port 5020\n"); } else { LOG_WARNING("Failed to start ModbusTCP server\n"); } } SetWorkStatus(WorkStatus::Ready); }); return SUCCESS; } int BasePresenter::StartDetection(int cameraIndex, bool isAuto) { LOG_INFO("[BasePresenter] StartDetection - cameraIndex=%d, isAuto=%d, simultaneousCount=%d\n", cameraIndex, isAuto, m_scanConfig.simultaneousCount); // ===== 分支1: cameraIndex > 0,单相机模式 ===== if (cameraIndex > 0) { m_currentCameraIndex = cameraIndex; int currentCamera = m_currentCameraIndex; if (m_vrEyeDeviceList.empty()) { LOG_ERROR("[BasePresenter] No camera device found\n"); return ERR_CODE(DEV_NOT_FIND); } ClearDetectionDataCache(); int arrayIndex = currentCamera - 1; if (arrayIndex < 0 || arrayIndex >= static_cast(m_vrEyeDeviceList.size()) || m_vrEyeDeviceList[arrayIndex].second == nullptr) { LOG_ERROR("[BasePresenter] Camera %d is not connected or invalid\n", currentCamera); return ERR_CODE(DEV_NOT_FIND); } SetWorkStatus(WorkStatus::Working); IVrEyeDevice* pDevice = m_vrEyeDeviceList[arrayIndex].second; EVzResultDataType eDataType = GetDetectionDataType(); VzNL_OnNotifyStatusCBEx statusCallback = GetCameraStatusCallback(); VzNL_AutoOutputLaserLineExCB detectCallback = GetDetectionCallback(); // 使用相机专属的回调上下文 CameraCallbackContext* ctx = m_cameraContexts[currentCamera]; pDevice->SetStatusCallback(statusCallback, ctx); int nRet = pDevice->StartDetect(detectCallback, eDataType, ctx); LOG_INFO("[BasePresenter] Camera %d start detection result: %d\n", currentCamera, nRet); if (nRet == SUCCESS) { StartAlgoDetectThread(); } LOG_INFO("[BasePresenter] StartDetection finish\n"); return nRet; } // ===== 分支2: cameraIndex <= 0 且为单相机模式,使用默认相机 ===== if (m_scanConfig.simultaneousCount == 1) { cameraIndex = m_currentCameraIndex; if (cameraIndex <= 0) cameraIndex = 1; m_currentCameraIndex = cameraIndex; int currentCamera = m_currentCameraIndex; if (m_vrEyeDeviceList.empty()) { LOG_ERROR("[BasePresenter] No camera device found\n"); return ERR_CODE(DEV_NOT_FIND); } ClearDetectionDataCache(); int arrayIndex = currentCamera - 1; if (arrayIndex < 0 || arrayIndex >= static_cast(m_vrEyeDeviceList.size()) || m_vrEyeDeviceList[arrayIndex].second == nullptr) { LOG_ERROR("[BasePresenter] Camera %d is not connected or invalid\n", currentCamera); return ERR_CODE(DEV_NOT_FIND); } SetWorkStatus(WorkStatus::Working); IVrEyeDevice* pDevice = m_vrEyeDeviceList[arrayIndex].second; EVzResultDataType eDataType = GetDetectionDataType(); VzNL_OnNotifyStatusCBEx statusCallback = GetCameraStatusCallback(); VzNL_AutoOutputLaserLineExCB detectCallback = GetDetectionCallback(); CameraCallbackContext* ctx = m_cameraContexts[currentCamera]; pDevice->SetStatusCallback(statusCallback, ctx); int nRet = pDevice->StartDetect(detectCallback, eDataType, ctx); LOG_INFO("[BasePresenter] Camera %d start detection result: %d\n", currentCamera, nRet); if (nRet == SUCCESS) { StartAlgoDetectThread(); } LOG_INFO("[BasePresenter] StartDetection finish\n"); return nRet; } // ===== 分支3: 多相机同时/分批扫描 ===== LOG_INFO("[BasePresenter] 进入多相机分批扫描模式\n"); // 停止当前正在进行的检测 if (m_batchInProgress.load() || m_bAlgoDetectThreadRunning) { StopDetection(); } // 收集本轮所有已连接的相机 m_batchCameraList.clear(); for (int i = 0; i < static_cast(m_vrEyeDeviceList.size()); i++) { if (m_vrEyeDeviceList[i].second != nullptr) { m_batchCameraList.push_back(i + 1); // 1-based index } } if (m_batchCameraList.empty()) { LOG_ERROR("[BasePresenter] No connected cameras for batch scan\n"); return ERR_CODE(DEV_NOT_FIND); } // 计算每批大小 int totalCameras = static_cast(m_batchCameraList.size()); if (m_scanConfig.simultaneousCount == 0) { m_batchSize = totalCameras; } else { m_batchSize = std::min(m_scanConfig.simultaneousCount, totalCameras); } // 初始化分批状态 m_batchStartIndex = 0; m_batchFinishedCount = 0; m_batchInProgress = true; // 清空所有 per-camera 缓存 ClearAllCameraDataCaches(); SetWorkStatus(WorkStatus::Working); // 启动算法检测线程 if (!m_bAlgoDetectThreadRunning) { StartAlgoDetectThread(); } // 启动第一批相机扫描 StartBatchScan(); LOG_INFO("[BasePresenter] 分批扫描已启动: total=%d, batchSize=%d\n", totalCameras, m_batchSize); return SUCCESS; } int BasePresenter::StopDetection() { LOG_INFO("[BasePresenter] StopDetection\n"); // 停止所有相机的检测 for (size_t i = 0; i < m_vrEyeDeviceList.size(); ++i) { IVrEyeDevice* pDevice = m_vrEyeDeviceList[i].second; if (pDevice) { int ret = pDevice->StopDetect(); if (ret == 0) { LOG_INFO("[BasePresenter] Camera %zu stop detection successfully\n", i + 1); } else { LOG_WARNING("[BasePresenter] Camera %zu stop detection failed, error code: %d\n", i + 1, ret); } } } // 停止算法检测线程 StopAlgoDetectThread(); // 清理多相机分批扫描状态 { std::lock_guard lock(m_batchStateMutex); m_batchInProgress = false; m_batchFinishedCount = 0; m_batchCameraList.clear(); } ClearAllCameraDataCaches(); return SUCCESS; } int BasePresenter::GetDetectionDataCacheSize() const { std::lock_guard lock(const_cast(m_detectionDataMutex)); return static_cast(m_detectionDataCache.size()); } int BasePresenter::SaveDetectionDataToFile(const std::string& filePath) { std::lock_guard lock(m_detectionDataMutex); if(m_detectionDataCache.empty()){ LOG_WARNING("[BasePresenter] 检测数据缓存为空,无数据可保存\n"); return ERR_CODE(DATA_ERR_INVALID); } int lineNum = static_cast(m_detectionDataCache.size()); float scanSpeed = 0.0f; int maxTimeStamp = 0; int clockPerSecond = 0; int result = m_dataLoader.SaveLaserScanData(filePath, m_detectionDataCache, lineNum, scanSpeed, maxTimeStamp, clockPerSecond); if (result == SUCCESS) { LOG_INFO("[BasePresenter] 成功保存 %d 行检测数据到文件: %s\n", lineNum, filePath.c_str()); } else { LOG_ERROR("[BasePresenter] 保存检测数据失败,错误: %s\n", m_dataLoader.GetLastError().c_str()); } return result; } int BasePresenter::LoadDebugDataAndDetect(const std::string& filePath) { SetWorkStatus(WorkStatus::Working); LOG_INFO("[BasePresenter] Loading debug data from file: %s\n", filePath.c_str()); int lineNum = 0; float scanSpeed = 0.0f; int maxTimeStamp = 0; int clockPerSecond = 0; int result = SUCCESS; // 1. 清空现有的检测数据缓存 ClearDetectionDataCache(); std::string fileName = QFileInfo(QString::fromStdString(filePath)).fileName().toStdString(); OnStatusUpdate(QString("加载文件:%1").arg(fileName.c_str()).toStdString()); // 2. 加载数据到缓存 { std::lock_guard lock(m_detectionDataMutex); result = m_dataLoader.LoadLaserScanData(filePath, m_detectionDataCache, lineNum, scanSpeed, maxTimeStamp, clockPerSecond); } if (result != SUCCESS) { LOG_ERROR("[BasePresenter] 加载调试数据失败: %s\n", m_dataLoader.GetLastError().c_str()); OnStatusUpdate("调试数据加载失败"); return result; } OnStatusUpdate(QString("成功加载 %1 行调试数据").arg(lineNum).toStdString()); LOG_INFO("[BasePresenter] 成功加载 %d 行调试数据\n", lineNum); // 3. 执行检测任务 result = DetectTask(); return result; } void BasePresenter::SetCameraStatusCallback(VzNL_OnNotifyStatusCBEx fNotify, void* param) { for (size_t i = 0; i < m_vrEyeDeviceList.size(); i++) { IVrEyeDevice* pDevice = m_vrEyeDeviceList[i].second; if (pDevice) { pDevice->SetStatusCallback(fNotify, param); LOG_DEBUG("[BasePresenter] Status callback set for camera %zu\n", i + 1); } } } void BasePresenter::SetWorkStatus(WorkStatus status) { if (m_currentWorkStatus != status) { m_currentWorkStatus = status; LOG_INFO("[BasePresenter] Work status changed to: %s\n", WorkStatusToString(status).c_str()); // 调用虚函数通知子类,子类可以在此调用UI回调 OnWorkStatusChanged(status); } } // ============ InitCamera 完整实现 ============ int BasePresenter::InitCamera(std::vector& cameraList, bool bRGB, bool bSwing) { LOG_INFO("[BasePresenter] InitCamera\n"); m_bRGB = bRGB; m_bSwing = bSwing; // 保存相机配置信息,用于重连尝试 m_expectedList = cameraList; // 通知UI相机个数 int cameraCount = cameraList.size(); OnCameraCountChanged(cameraCount); LOG_INFO("[BasePresenter] init eyedevice list\n"); // 初始化相机列表,预分配空间 m_vrEyeDeviceList.resize(cameraCount, std::make_pair("", nullptr)); LOG_INFO("[BasePresenter] camera count : %d\n", cameraCount); for(int i = 0; i < cameraCount; i++) { m_vrEyeDeviceList[i] = std::make_pair(cameraList[i].name, nullptr); LOG_INFO("[BasePresenter] camera %d name : %s, ip : %s\n", i, cameraList[i].name.c_str(), cameraList[i].ip.c_str()); } // 尝试初始化所有相机 bool allCamerasConnected = true; if(cameraCount > 0){ // 循环打开所有配置的相机 for (int i = 0; i < cameraCount; i++) { int cameraIndex = i + 1; // 相机索引从1开始 int nRet = OpenDevice(cameraIndex, cameraList[i].name.c_str(), cameraList[i].ip.c_str(), bRGB, bSwing); bool isConnected = (nRet == SUCCESS); // 通知相机状态变化 OnCameraStatusChanged(cameraIndex, isConnected); if (!isConnected) { allCamerasConnected = false; LOG_WARNING("[BasePresenter] 相机%d (%s) 连接失败\n", cameraIndex, cameraList[i].name.c_str()); } else { LOG_INFO("[BasePresenter] 相机%d (%s) 连接成功\n", cameraIndex, cameraList[i].name.c_str()); } } } else { // 没有配置相机,创建一个默认项 m_vrEyeDeviceList.resize(1, std::make_pair("", nullptr)); DeviceInfo devInfo; devInfo.index = 1; devInfo.ip = ""; devInfo.name = "相机"; m_expectedList.push_back(devInfo); int nRet = OpenDevice(1, "相机", nullptr, bRGB, bSwing); if (nRet != SUCCESS) { allCamerasConnected = false; } // 通知相机状态变化 OnCameraStatusChanged(1, SUCCESS == nRet); } // 检查连接状态 int connectedCount = 0; for (const auto& device : m_vrEyeDeviceList) { if (device.second != nullptr) { connectedCount++; } } m_bCameraConnected = (connectedCount > 0); // 至少有一个相机连接成功 // 设置默认相机索引为第一个连接的相机 m_currentCameraIndex = 1; // 默认从1开始 for (int i = 0; i < static_cast(m_vrEyeDeviceList.size()); i++) { if (m_vrEyeDeviceList[i].second != nullptr) { m_currentCameraIndex = i + 1; // 找到第一个连接的相机 break; } } LOG_INFO("[BasePresenter] 相机初始化完成: %d/%d 台相机连接成功, 默认相机索引: %d\n", connectedCount, m_expectedList.size(), m_currentCameraIndex); // 如果不是所有期望的相机都连接成功,启动重连定时器 if (!allCamerasConnected && !m_expectedList.empty()) { LOG_INFO("[BasePresenter] 部分相机未连接 (%d/%d),启动重连定时器\n", connectedCount, m_expectedList.size()); StartCameraReconnectTimer(); } else if (allCamerasConnected) { LOG_INFO("[BasePresenter] 所有相机连接成功\n"); // 确保定时器停止 StopCameraReconnectTimer(); } else { LOG_WARNING("[BasePresenter] 没有配置相机 (expectedCount=%d)\n", m_expectedList.size()); } return SUCCESS; } // ============ CreateDevice 默认实现 ============ int BasePresenter::CreateDevice(IVrEyeDevice** ppDevice) { if (!ppDevice) { return ERR_CODE(DEV_ARG_INVAILD); } // 默认创建VzNLSDK设备 IVrEyeDevice::CreateObject(ppDevice); if (*ppDevice) { LOG_INFO("[BasePresenter] Created VzNL SDK device (default)\n"); return SUCCESS; } LOG_ERROR("[BasePresenter] Failed to create VzNL SDK device\n"); return ERR_CODE(DEV_OPEN_ERR); } // ============ OpenDevice 完整实现 ============ int BasePresenter::OpenDevice(int cameraIndex, const char* cameraName, const char* cameraIp, bool bRGB, bool bSwing) { LOG_INFO("[BasePresenter] OpenDevice - index %d (%s, %s)\n", cameraIndex, cameraName, cameraIp ? cameraIp : "NULL"); // 1. 通过虚函数创建设备对象(子类可重写以创建不同类型的设备) IVrEyeDevice* pDevice = nullptr; int nCreateRet = CreateDevice(&pDevice); if (nCreateRet != SUCCESS || !pDevice) { LOG_ERROR("[BasePresenter] Failed to create device object, result: %d\n", nCreateRet); return ERR_CODE(DEV_OPEN_ERR); } // 2. 初始化设备 int nRet = pDevice->InitDevice(); if(nRet != SUCCESS){ delete pDevice; LOG_ERROR("[BasePresenter] InitDevice failed, error code: %d\n", nRet); } ERR_CODE_RETURN(nRet); // 3. 打开相机设备 nRet = pDevice->OpenDevice(cameraIp, bRGB, bSwing); LOG_INFO("[BasePresenter] OpenDevice camera %d (%s/%s) result: %d \n", cameraIndex, bRGB ? "RGB" : "Normal", bSwing ? "Swing" : "Normal", nRet); // 4. 处理打开结果 bool cameraConnected = (SUCCESS == nRet); if(!cameraConnected){ delete pDevice; // 释放失败的设备 pDevice = nullptr; } else { // 释放旧的回调上下文(重连场景) auto oldCtx = m_cameraContexts.find(cameraIndex); if (oldCtx != m_cameraContexts.end()) { delete oldCtx->second; } // 创建新的回调上下文,携带相机索引 CameraCallbackContext* ctx = new CameraCallbackContext{this, cameraIndex}; m_cameraContexts[cameraIndex] = ctx; // 设置状态回调(使用带相机索引的上下文) VzNL_OnNotifyStatusCBEx callback = GetCameraStatusCallback(); nRet = pDevice->SetStatusCallback(callback, ctx); LOG_DEBUG("[BasePresenter] SetStatusCallback result: %d\n", nRet); if (nRet != SUCCESS) { delete pDevice; pDevice = nullptr; } } LOG_DEBUG("[BasePresenter] Camera %d (%s) connected %s\n", cameraIndex, cameraName, cameraConnected ? "success" : "failed"); // 6. 存储到设备列表 int arrIdx = cameraIndex - 1; if(m_vrEyeDeviceList.size() > static_cast(arrIdx)){ m_vrEyeDeviceList[arrIdx] = std::make_pair(cameraName, pDevice); } else { LOG_WARNING("[BasePresenter] Camera index %d out of range, list size: %zu\n", cameraIndex, m_vrEyeDeviceList.size()); } return nRet; } // ============ AlgoDetectThreadFunc 实现 ============ void BasePresenter::AlgoDetectThreadFunc() { LOG_INFO("[BasePresenter] 算法检测线程启动\n"); while(m_bAlgoDetectThreadRunning) { std::unique_lock lock(m_algoDetectMutex); // 等待检测触发 m_algoDetectCondition.wait(lock); if(!m_bAlgoDetectThreadRunning){ break; } if (m_batchInProgress.load()) { // 多相机分批模式:检查批次是否完成并处理 ProcessBatchIfReady(); } else { // 单相机模式:直接执行检测任务 LOG_INFO("[BasePresenter] 检测线程被唤醒,开始执行检测任务\n"); int nRet = DetectTask(); if(nRet != SUCCESS){ LOG_ERROR("[BasePresenter] 检测任务执行失败,错误码: %d\n", nRet); } else { LOG_INFO("[BasePresenter] 检测任务执行成功\n"); } } } LOG_INFO("[BasePresenter] 算法检测线程退出\n"); } // ============ DetectTask 实现 ============ int BasePresenter::DetectTask() { LOG_INFO("[BasePresenter] DetectTask - 开始执行检测任务\n"); // 获取调试参数 VrDebugParam debugParam = GetDebugParam(); // 详细日志模式 if (debugParam.enableDebug && debugParam.printDetailLog) { LOG_INFO("[BasePresenter] 调试模式已启用\n"); LOG_INFO("[BasePresenter] - savePointCloud: %s\n", debugParam.savePointCloud ? "true" : "false"); LOG_INFO("[BasePresenter] - saveDebugImage: %s\n", debugParam.saveDebugImage ? "true" : "false"); LOG_INFO("[BasePresenter] - debugOutputPath: %s\n", debugParam.debugOutputPath.c_str()); } // 1. 验证检测数据缓存 { std::lock_guard lock(m_detectionDataMutex); if (m_detectionDataCache.empty()) { LOG_WARNING("[BasePresenter] 检测数据缓存为空\n"); SetWorkStatus(WorkStatus::Error); return ERR_CODE(DEV_DATA_INVALID); } LOG_INFO("[BasePresenter] 检测数据缓存大小: %zu\n", m_detectionDataCache.size()); } // 2. 调试模式 - 异步保存点云数据 if (debugParam.enableDebug && debugParam.savePointCloud) { // 确定输出路径 QString outputPath; if (debugParam.debugOutputPath.empty()) { outputPath = QCoreApplication::applicationDirPath() + "/debug"; } else { outputPath = QString::fromStdString(debugParam.debugOutputPath); } // 确保输出根目录存在 QDir dir(outputPath); if (!dir.exists()) { dir.mkpath("."); } // 在检测时刻生成时间戳,确保文件名反映实际检测时间 std::string timestamp = QDateTime::currentDateTime().toString("yyyyMMdd_HHmmss").toStdString(); // 获取当前相机名称,用于生成更直观的文件名 std::string deviceName; int arrayIndex = m_currentCameraIndex - 1; LOG_INFO("[BasePresenter] currentCameraIndex: %d, arrayIndex: %d\n", m_currentCameraIndex, arrayIndex); if (arrayIndex >= 0 && arrayIndex < static_cast(m_vrEyeDeviceList.size())) { deviceName = m_vrEyeDeviceList[arrayIndex].first; } if (debugParam.printDetailLog) { LOG_INFO("[BasePresenter] 提交点云数据异步保存任务,路径: %s,时间: %s,设备: %s\n", outputPath.toStdString().c_str(), timestamp.c_str(), deviceName.c_str()); } // 深拷贝数据后异步保存,不阻塞检测流程 { std::lock_guard lock(m_detectionDataMutex); m_debugDataSaver.SaveAsync(outputPath.toStdString(), timestamp, m_detectionDataCache, deviceName); } } // 3. 调用子类实现的算法检测,传入缓存数据引用 LOG_INFO("[BasePresenter] ProcessAlgoDetection 执行算法检测\n"); int nRet = ProcessAlgoDetection(m_detectionDataCache); LOG_INFO("[BasePresenter] ProcessAlgoDetection 执行结果: %d\n", nRet); // 批量模式下不在这里设置Completed,由ProcessBatchIfReady统一设置 { std::lock_guard lock(m_batchStateMutex); if (!m_batchInProgress.load()) { SetWorkStatus(WorkStatus::Completed); } } LOG_INFO("[BasePresenter] DetectTask - 检测任务执行成功\n"); return nRet; } void BasePresenter::StartAlgoDetectThread() { if (m_bAlgoDetectThreadRunning) { LOG_WARNING("[BasePresenter] 算法检测线程已经在运行\n"); return; } m_bAlgoDetectThreadRunning = true; // 启动检测线程(不再detach,使用joinable线程) m_algoDetectThread = std::thread(&BasePresenter::AlgoDetectThreadFunc, this); LOG_INFO("[BasePresenter] 算法检测线程已启动\n"); } void BasePresenter::StopAlgoDetectThread() { if (!m_bAlgoDetectThreadRunning) { return; } LOG_INFO("[BasePresenter] 正在停止算法检测线程...\n"); m_bAlgoDetectThreadRunning = false; // 唤醒可能在等待的线程 m_algoDetectCondition.notify_all(); // 等待线程退出 if (m_algoDetectThread.joinable()) { m_algoDetectThread.join(); } LOG_INFO("[BasePresenter] 算法检测线程已停止\n"); } void BasePresenter::ClearDetectionDataCache() { std::lock_guard lock(m_detectionDataMutex); // 释放加载的数据 m_dataLoader.FreeLaserScanData(m_detectionDataCache); LOG_DEBUG("[BasePresenter] 检测数据缓存已清空\n"); } void BasePresenter::AddDetectionDataToCache(EVzResultDataType dataType, const SVzLaserLineData& laserData) { std::lock_guard lock(m_detectionDataMutex); m_detectionDataCache.push_back(std::make_pair(dataType, laserData)); } // 通用的静态检测数据回调函数实现 void BasePresenter::_StaticDetectionCallback(EVzResultDataType eDataType, SVzLaserLineData* pLaserLinePoint, void* pUserData) { // 验证输入参数 if (!pLaserLinePoint) { LOG_WARNING("[BasePresenter Detection Callback] pLaserLinePoint is null\n"); return; } if (pLaserLinePoint->nPointCount <= 0) { LOG_WARNING("[BasePresenter Detection Callback] Point count is zero or negative: %d\n", pLaserLinePoint->nPointCount); return; } if (!pLaserLinePoint->p3DPoint) { LOG_WARNING("[BasePresenter Detection Callback] p3DPoint is null\n"); return; } // 提取回调上下文(多相机模式下携带相机索引) CameraCallbackContext* ctx = static_cast(pUserData); if (!ctx || !ctx->presenter) { LOG_ERROR("[BasePresenter Detection Callback] invalid context\n"); return; } BasePresenter* pThis = ctx->presenter; int cameraIndex = ctx->cameraIndex; // 创建 SVzLaserLineData 副本 SVzLaserLineData lineData; memset(&lineData, 0, sizeof(SVzLaserLineData)); // 根据数据类型分配和复制点云数据 if (eDataType == keResultDataType_Position) { // 复制 SVzNL3DPosition 数据 if (pLaserLinePoint->p3DPoint && pLaserLinePoint->nPointCount > 0) { lineData.p3DPoint = new SVzNL3DPosition[pLaserLinePoint->nPointCount]; if (lineData.p3DPoint) { if(pLaserLinePoint->p3DPoint){ memcpy(lineData.p3DPoint, pLaserLinePoint->p3DPoint, sizeof(SVzNL3DPosition) * pLaserLinePoint->nPointCount); } else { memset(lineData.p3DPoint, 0, sizeof(SVzNL3DPosition) * pLaserLinePoint->nPointCount); } } lineData.p2DPoint = new SVzNL2DPosition[pLaserLinePoint->nPointCount]; if (lineData.p2DPoint){ if(pLaserLinePoint->p2DPoint) { memcpy(lineData.p2DPoint, pLaserLinePoint->p2DPoint, sizeof(SVzNL2DPosition) * pLaserLinePoint->nPointCount); } else { memset(lineData.p2DPoint, 0, sizeof(SVzNL2DPosition) * pLaserLinePoint->nPointCount); } } } } else if (eDataType == keResultDataType_PointXYZRGBA) { // 复制 SVzNLPointXYZRGBA 数据 if (pLaserLinePoint->p3DPoint && pLaserLinePoint->nPointCount > 0) { lineData.p3DPoint = new SVzNLPointXYZRGBA[pLaserLinePoint->nPointCount]; if (lineData.p3DPoint) { if(pLaserLinePoint->p3DPoint){ memcpy(lineData.p3DPoint, pLaserLinePoint->p3DPoint, sizeof(SVzNLPointXYZRGBA) * pLaserLinePoint->nPointCount); } else { memset(lineData.p3DPoint, 0, sizeof(SVzNLPointXYZRGBA) * pLaserLinePoint->nPointCount); } } lineData.p2DPoint = new SVzNL2DLRPoint[pLaserLinePoint->nPointCount]; if (lineData.p2DPoint) { if(pLaserLinePoint->p2DPoint) { memcpy(lineData.p2DPoint, pLaserLinePoint->p2DPoint, sizeof(SVzNL2DLRPoint) * pLaserLinePoint->nPointCount); } else { memset(lineData.p2DPoint, 0, sizeof(SVzNL2DLRPoint) * pLaserLinePoint->nPointCount); } } } } // 复制其他字段 lineData.nPointCount = pLaserLinePoint->nPointCount; lineData.llTimeStamp = pLaserLinePoint->llTimeStamp; lineData.llFrameIdx = pLaserLinePoint->llFrameIdx; lineData.nEncodeNo = pLaserLinePoint->nEncodeNo; lineData.fSwingAngle = pLaserLinePoint->fSwingAngle; lineData.bEndOnceScan = pLaserLinePoint->bEndOnceScan; // 根据扫描模式分流存储 if (pThis->m_batchInProgress.load()) { // 多相机分批扫描模式:存入该相机的独立缓存 pThis->AddDetectionDataToCameraCache(cameraIndex, eDataType, lineData); } else { // 单相机模式:存入共享缓存 pThis->AddDetectionDataToCache(eDataType, lineData); } } // 通用的静态相机状态回调函数实现 void BasePresenter::_StaticCameraStatusCallback(EVzDeviceWorkStatus eStatus, void* pExtData, unsigned int nDataLength, void* pInfoParam) { LOG_DEBUG("[BasePresenter Camera Status Callback] received: status=%d\n", (int)eStatus); // 提取回调上下文(携带相机索引) CameraCallbackContext* ctx = static_cast(pInfoParam); if (!ctx || !ctx->presenter) { LOG_ERROR("[BasePresenter Camera Status Callback] invalid context\n"); return; } BasePresenter* pThis = ctx->presenter; int cameraIndex = ctx->cameraIndex; switch (eStatus) { case EVzDeviceWorkStatus::keDeviceWorkStatus_Offline: { LOG_WARNING("[BasePresenter Camera Status Callback] Camera %d offline/disconnected\n", cameraIndex); pThis->m_bCameraConnected = false; pThis->OnCameraStatusChanged(cameraIndex, false); break; } case EVzDeviceWorkStatus::keDeviceWorkStatus_Eye_Reconnect: { LOG_INFO("[BasePresenter Camera Status Callback] Camera %d online/connected\n", cameraIndex); pThis->m_bCameraConnected = true; pThis->OnCameraStatusChanged(cameraIndex, true); break; } case EVzDeviceWorkStatus::keDeviceWorkStatus_Device_Swing_Finish: { LOG_INFO("[BasePresenter Camera Status Callback] Camera %d scan finished\n", cameraIndex); // 分批模式下记录批次完成 if (pThis->m_batchInProgress.load()) { pThis->OnCameraScanFinished(cameraIndex); } // 唤醒算法检测线程 pThis->m_algoDetectCondition.notify_one(); break; } default: break; } } // 相机一直重联 void BasePresenter::StartCameraReconnectTimer() { LOG_DEBUG("[BasePresenter] StartCameraReconnectTimer called\n"); // 使用QMetaObject::invokeMethod确保在正确的线程中操作定时器 QMetaObject::invokeMethod(this, [this]() { if (!m_pCameraReconnectTimer) { return; } if (m_pCameraReconnectTimer->isActive()) { return; } m_pCameraReconnectTimer->start(); }, Qt::QueuedConnection); } void BasePresenter::StopCameraReconnectTimer() { // 直接停止定时器(析构时需要立即停止,不能用QueuedConnection) if (m_pCameraReconnectTimer) { m_pCameraReconnectTimer->stop(); } } // ============ OnCameraReconnectTimer 实现 ============ void BasePresenter::OnCameraReconnectTimer() { #ifdef _WIN32 return; #endif // 调用子类实现的重连逻辑 bool allConnected = TryReconnectCameras(); if (allConnected) { LOG_INFO("[BasePresenter] 所有相机重连成功,停止定时器\n"); StopCameraReconnectTimer(); } } // ============ TryReconnectCameras 默认实现 ============ bool BasePresenter::TryReconnectCameras() { LOG_DEBUG("[BasePresenter] TryReconnectCameras all %zd \n", m_expectedList.size()); bool allConnected = true; int connectedCount = 0; // 遍历所有配置的相机,尝试重连失败的相机 for (int i = 0; i < static_cast(m_expectedList.size()); i++) { // 检查该位置的相机是否已连接 if (i < static_cast(m_vrEyeDeviceList.size()) && m_vrEyeDeviceList[i].second != nullptr) { // 相机已连接,跳过 connectedCount++; continue; } // 尝试重连相机 int cameraIndex = i + 1; // 相机索引从1开始 const DeviceInfo& cameraInfo = m_expectedList[i]; LOG_DEBUG("[BasePresenter] 尝试重连相机 %d (%s, %s)\n", cameraIndex, cameraInfo.name.c_str(), cameraInfo.ip.c_str()); // 调用 OpenDevice 重连(使用初始化时的 RGB/Swing 参数) int nRet = OpenDevice(cameraIndex, cameraInfo.name.c_str(), cameraInfo.ip.c_str(), m_bRGB, m_bSwing); OnCameraStatusChanged(cameraIndex, SUCCESS == nRet); if (nRet == SUCCESS) { LOG_INFO("[BasePresenter] 相机 %d (%s) 重连成功\n", cameraIndex, cameraInfo.name.c_str()); connectedCount++; } else { LOG_DEBUG("[BasePresenter] 相机 %d (%s) 重连失败,错误码: %d\n", cameraIndex, cameraInfo.name.c_str(), nRet); allConnected = false; } } // 更新相机连接状态 m_bCameraConnected = (connectedCount > 0); // 更新默认相机索引为第一个连接的相机 for (int i = 0; i < static_cast(m_vrEyeDeviceList.size()); i++) { if (m_vrEyeDeviceList[i].second != nullptr) { m_currentCameraIndex = i + 1; break; } } LOG_INFO("[BasePresenter] 相机重连尝试完成: %d/%d 台相机已连接\n", connectedCount, m_expectedList.size()); return (connectedCount == m_expectedList.size() && allConnected); } // ============ ModbusTCP 服务实现 ============ int BasePresenter::StartModbusServer(int port) { LOG_INFO("[BasePresenter] 启动ModbusTCP服务器,端口: %d\n", port); // 如果已经运行,先停止 if (m_modbusServer) { StopModbusServer(); } // 创建ModbusTCP服务器实例 if (!IYModbusTCPServer::CreateInstance(&m_modbusServer)) { LOG_ERROR("[BasePresenter] 创建ModbusTCP服务器实例失败\n"); return ERR_CODE(DEV_OPEN_ERR); } // 设置写寄存器回调 m_modbusServer->setWriteRegistersCallback( [this](uint8_t unitId, uint16_t startAddress, uint16_t quantity, const uint16_t* values) -> IYModbusTCPServer::ErrorCode { int ret = this->OnModbusWriteRegisters(unitId, startAddress, quantity, values); return (ret == 0) ? IYModbusTCPServer::ErrorCode::SUCCESS : IYModbusTCPServer::ErrorCode::SERVER_FAILURE; } ); // 设置连接状态回调 m_modbusServer->setConnectionStatusCallback( [this](bool isConnected) { LOG_INFO("[BasePresenter] Modbus客户端%s\n", isConnected ? "已连接" : "已断开"); // 通知子类 ModbusTCP 连接状态变化 this->OnModbusServerStatusChanged(isConnected); } ); // 启动服务器 int ret = m_modbusServer->start(port, 5); if (ret != 0) { LOG_ERROR("[BasePresenter] 启动ModbusTCP服务器失败,错误码: %d\n", ret); delete m_modbusServer; m_modbusServer = nullptr; return ERR_CODE(DEV_OPEN_ERR); } LOG_INFO("[BasePresenter] ModbusTCP服务器启动成功\n"); return SUCCESS; } void BasePresenter::StopModbusServer() { if (m_modbusServer) { m_modbusServer->stop(); delete m_modbusServer; m_modbusServer = nullptr; } } bool BasePresenter::IsModbusServerRunning() const { return m_modbusServer != nullptr; } int BasePresenter::WriteModbusRegisters(uint16_t startAddress, const uint16_t* data, uint16_t count) { if (!m_modbusServer) { LOG_WARNING("[BasePresenter] ModbusTCP服务器未运行\n"); return ERR_CODE(DEV_NOT_FIND); } if (!data || count == 0) { LOG_WARNING("[BasePresenter] 无效的Modbus写入参数\n"); return ERR_CODE(DEV_DATA_INVALID); } // 转换为vector并写入 std::vector values(data, data + count); m_modbusServer->updateHoldingRegisters(startAddress, values); return SUCCESS; } int BasePresenter::OnModbusWriteRegisters(uint8_t unitId, uint16_t startAddress, uint16_t quantity, const uint16_t* values) { LOG_DEBUG("[BasePresenter] Modbus收到写寄存器: unitId=%d, 地址=%d, 数量=%d\n", unitId, startAddress, quantity); // 调用虚函数让子类处理 OnModbusWriteCallback(startAddress, values, quantity); return 0; } // ============ 多相机同时扫描方法实现 ============ void BasePresenter::AddDetectionDataToCameraCache(int cameraIndex, EVzResultDataType dataType, const SVzLaserLineData& laserData) { std::lock_guard lock(m_perCameraDataMutex); m_perCameraDataCache[cameraIndex].push_back(std::make_pair(dataType, laserData)); } void BasePresenter::ClearAllCameraDataCaches() { std::lock_guard lock(m_perCameraDataMutex); for (auto& pair : m_perCameraDataCache) { m_dataLoader.FreeLaserScanData(pair.second); } m_perCameraDataCache.clear(); } void BasePresenter::OnCameraScanFinished(int cameraIndex) { std::lock_guard lock(m_batchStateMutex); m_batchFinishedCount++; LOG_INFO("[BasePresenter] Camera %d finished, batch progress: %d/%d\n", cameraIndex, m_batchFinishedCount, m_batchSize); } void BasePresenter::StartBatchScan() { int batchEnd = std::min(m_batchStartIndex + m_batchSize, static_cast(m_batchCameraList.size())); int batchCount = batchEnd - m_batchStartIndex; LOG_INFO("[BasePresenter] StartBatchScan: cameras [%d, %d), count=%d\n", m_batchStartIndex, batchEnd, batchCount); if (batchCount <= 0) { LOG_WARNING("[BasePresenter] StartBatchScan: empty batch\n"); return; } EVzResultDataType eDataType = GetDetectionDataType(); VzNL_AutoOutputLaserLineExCB detectCallback = GetDetectionCallback(); VzNL_OnNotifyStatusCBEx statusCallback = GetCameraStatusCallback(); for (int i = m_batchStartIndex; i < batchEnd; i++) { int cameraIndex = m_batchCameraList[i]; int arrIndex = cameraIndex - 1; if (arrIndex < 0 || arrIndex >= static_cast(m_vrEyeDeviceList.size())) { LOG_WARNING("[BasePresenter] Camera %d index out of range, skipping\n", cameraIndex); continue; } IVrEyeDevice* pDevice = m_vrEyeDeviceList[arrIndex].second; if (!pDevice) { LOG_WARNING("[BasePresenter] Camera %d device is null, skipping\n", cameraIndex); continue; } // 清空该相机的独立缓存 { std::lock_guard lock(m_perCameraDataMutex); auto it = m_perCameraDataCache.find(cameraIndex); if (it != m_perCameraDataCache.end()) { m_dataLoader.FreeLaserScanData(it->second); m_perCameraDataCache.erase(it); } } // 使用相机专属的回调上下文 CameraCallbackContext* ctx = m_cameraContexts[cameraIndex]; pDevice->SetStatusCallback(statusCallback, ctx); int nRet = pDevice->StartDetect(detectCallback, eDataType, ctx); LOG_INFO("[BasePresenter] Camera %d start detect: %d\n", cameraIndex, nRet); } LOG_INFO("[BasePresenter] Batch scan started: %d cameras\n", batchCount); } void BasePresenter::ProcessBatchIfReady() { // 检查当前批次是否全部完成 bool batchComplete = false; { std::lock_guard lock(m_batchStateMutex); if (!m_batchInProgress.load()) return; batchComplete = (m_batchFinishedCount >= m_batchSize); } if (!batchComplete) { // 批次未完成,可能是被提前唤醒(Offline 等事件) return; } int batchEnd = std::min(m_batchStartIndex + m_batchSize, static_cast(m_batchCameraList.size())); LOG_INFO("[BasePresenter] 批次完成,处理相机 [%d, %d)\n", m_batchStartIndex, batchEnd); // 逐个处理批次内每个相机的数据 for (int i = m_batchStartIndex; i < batchEnd; i++) { int cameraIndex = m_batchCameraList[i]; // 将相机数据从 per-camera 缓存移到 m_detectionDataCache { std::lock_guard dataLock(m_perCameraDataMutex); auto it = m_perCameraDataCache.find(cameraIndex); if (it != m_perCameraDataCache.end() && !it->second.empty()) { std::lock_guard detLock(m_detectionDataMutex); m_dataLoader.FreeLaserScanData(m_detectionDataCache); m_detectionDataCache.clear(); m_detectionDataCache = std::move(it->second); m_perCameraDataCache.erase(it); } else { LOG_WARNING("[BasePresenter] 相机%d 无扫描数据,跳过\n", cameraIndex); continue; } } m_currentCameraIndex = cameraIndex; LOG_INFO("[BasePresenter] 处理相机 %d (%d/%d)\n", cameraIndex, i - m_batchStartIndex + 1, m_batchSize); int nRet = DetectTask(); if (nRet != SUCCESS) { LOG_ERROR("[BasePresenter] 相机%d 检测失败: %d\n", cameraIndex, nRet); } } // 推进到下一批 bool hasMore = false; { std::lock_guard lock(m_batchStateMutex); m_batchStartIndex = batchEnd; m_batchFinishedCount = 0; int remaining = static_cast(m_batchCameraList.size()) - m_batchStartIndex; if (remaining > 0) { if (m_scanConfig.simultaneousCount == 0) { m_batchSize = remaining; } else { m_batchSize = std::min(m_scanConfig.simultaneousCount, remaining); } hasMore = true; } } if (hasMore) { LOG_INFO("[BasePresenter] 启动下一批扫描: start=%d, size=%d\n", m_batchStartIndex, m_batchSize); // 在主线程中启动下一批扫描 QMetaObject::invokeMethod(this, [this]() { StartBatchScan(); }, Qt::QueuedConnection); } else { LOG_INFO("[BasePresenter] 所有批次扫描完成,共 %d 个相机\n", static_cast(m_batchCameraList.size())); { std::lock_guard lock(m_batchStateMutex); m_batchInProgress = false; } SetWorkStatus(WorkStatus::Completed); } }