GrabBag/AppUtils/AppCommon/Src/BasePresenter.cpp

1213 lines
42 KiB
C++
Raw Normal View History

#include "BasePresenter.h"
#include "VrLog.h"
#include "VrError.h"
#include <fstream>
#include <QFileInfo>
#include <QDir>
#include <QDateTime>
#include <QString>
#include <QCoreApplication>
#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();
2026-05-17 00:38:31 +08:00
// 清理相机回调上下文
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)
{
2026-05-17 00:38:31 +08:00
LOG_INFO("[BasePresenter] StartDetection - cameraIndex=%d, isAuto=%d, simultaneousCount=%d\n",
cameraIndex, isAuto, m_scanConfig.simultaneousCount);
2026-05-17 00:38:31 +08:00
// ===== 分支1: cameraIndex > 0单相机模式 =====
if (cameraIndex > 0) {
m_currentCameraIndex = cameraIndex;
2026-05-17 00:38:31 +08:00
int currentCamera = m_currentCameraIndex;
2026-05-17 00:38:31 +08:00
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<int>(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;
}
2026-05-17 00:38:31 +08:00
// ===== 分支2: cameraIndex <= 0 且为单相机模式,使用默认相机 =====
if (m_scanConfig.simultaneousCount == 1) {
cameraIndex = m_currentCameraIndex;
if (cameraIndex <= 0) cameraIndex = 1;
m_currentCameraIndex = cameraIndex;
int currentCamera = m_currentCameraIndex;
2026-05-17 00:38:31 +08:00
if (m_vrEyeDeviceList.empty()) {
LOG_ERROR("[BasePresenter] No camera device found\n");
return ERR_CODE(DEV_NOT_FIND);
}
2026-05-17 00:38:31 +08:00
ClearDetectionDataCache();
2026-05-17 00:38:31 +08:00
int arrayIndex = currentCamera - 1;
if (arrayIndex < 0 || arrayIndex >= static_cast<int>(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;
}
2026-05-17 00:38:31 +08:00
// ===== 分支3: 多相机同时/分批扫描 =====
LOG_INFO("[BasePresenter] 进入多相机分批扫描模式\n");
2026-05-17 00:38:31 +08:00
// 停止当前正在进行的检测
2026-05-17 19:00:47 +08:00
if (m_batchInProgress.load() || m_bAlgoDetectThreadRunning) {
2026-05-17 00:38:31 +08:00
StopDetection();
}
2026-05-17 00:38:31 +08:00
// 收集本轮所有已连接的相机
m_batchCameraList.clear();
for (int i = 0; i < static_cast<int>(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);
}
2026-05-17 00:38:31 +08:00
// 计算每批大小
int totalCameras = static_cast<int>(m_batchCameraList.size());
if (m_scanConfig.simultaneousCount == 0) {
m_batchSize = totalCameras;
} else {
m_batchSize = std::min(m_scanConfig.simultaneousCount, totalCameras);
}
2026-05-17 00:38:31 +08:00
// 初始化分批状态
m_batchStartIndex = 0;
m_batchFinishedCount = 0;
m_batchInProgress = true;
// 清空所有 per-camera 缓存
ClearAllCameraDataCaches();
2026-05-17 00:38:31 +08:00
SetWorkStatus(WorkStatus::Working);
2026-05-17 00:38:31 +08:00
// 启动算法检测线程
if (!m_bAlgoDetectThreadRunning) {
StartAlgoDetectThread();
}
2026-05-17 00:38:31 +08:00
// 启动第一批相机扫描
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();
2026-05-17 00:38:31 +08:00
// 清理多相机分批扫描状态
{
std::lock_guard<std::mutex> lock(m_batchStateMutex);
m_batchInProgress = false;
m_batchFinishedCount = 0;
m_batchCameraList.clear();
}
ClearAllCameraDataCaches();
return SUCCESS;
}
int BasePresenter::GetDetectionDataCacheSize() const
{
std::lock_guard<std::mutex> lock(const_cast<std::mutex&>(m_detectionDataMutex));
return static_cast<int>(m_detectionDataCache.size());
}
int BasePresenter::SaveDetectionDataToFile(const std::string& filePath)
{ std::lock_guard<std::mutex> lock(m_detectionDataMutex);
if(m_detectionDataCache.empty()){
LOG_WARNING("[BasePresenter] 检测数据缓存为空,无数据可保存\n");
return ERR_CODE(DATA_ERR_INVALID);
}
int lineNum = static_cast<int>(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<std::mutex> 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<DeviceInfo>& 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<int>(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 {
2026-05-17 00:38:31 +08:00
// 释放旧的回调上下文(重连场景)
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();
2026-05-17 00:38:31 +08:00
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<size_t>(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<std::mutex> lock(m_algoDetectMutex);
2026-05-17 00:38:31 +08:00
// 等待检测触发
m_algoDetectCondition.wait(lock);
if(!m_bAlgoDetectThreadRunning){
break;
}
2026-05-17 19:00:47 +08:00
if (m_batchInProgress.load()) {
2026-05-17 00:38:31 +08:00
// 多相机分批模式:检查批次是否完成并处理
ProcessBatchIfReady();
} else {
2026-05-17 00:38:31 +08:00
// 单相机模式:直接执行检测任务
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<std::mutex> 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<int>(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<std::mutex> 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);
2026-05-17 00:38:31 +08:00
// 批量模式下不在这里设置Completed由ProcessBatchIfReady统一设置
{
std::lock_guard<std::mutex> lock(m_batchStateMutex);
2026-05-17 19:00:47 +08:00
if (!m_batchInProgress.load()) {
2026-05-17 00:38:31 +08:00
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<std::mutex> lock(m_detectionDataMutex);
// 释放加载的数据
m_dataLoader.FreeLaserScanData(m_detectionDataCache);
LOG_DEBUG("[BasePresenter] 检测数据缓存已清空\n");
}
void BasePresenter::AddDetectionDataToCache(EVzResultDataType dataType, const SVzLaserLineData& laserData)
{
std::lock_guard<std::mutex> 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;
}
2026-05-17 00:38:31 +08:00
// 提取回调上下文(多相机模式下携带相机索引)
CameraCallbackContext* ctx = static_cast<CameraCallbackContext*>(pUserData);
if (!ctx || !ctx->presenter) {
LOG_ERROR("[BasePresenter Detection Callback] invalid context\n");
return;
}
2026-05-17 00:38:31 +08:00
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;
2026-05-17 00:38:31 +08:00
// 根据扫描模式分流存储
2026-05-17 19:00:47 +08:00
if (pThis->m_batchInProgress.load()) {
// 多相机分批扫描模式:存入该相机的独立缓存
2026-05-17 00:38:31 +08:00
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);
2026-05-17 00:38:31 +08:00
// 提取回调上下文(携带相机索引)
CameraCallbackContext* ctx = static_cast<CameraCallbackContext*>(pInfoParam);
if (!ctx || !ctx->presenter) {
LOG_ERROR("[BasePresenter Camera Status Callback] invalid context\n");
return;
}
2026-05-17 00:38:31 +08:00
BasePresenter* pThis = ctx->presenter;
int cameraIndex = ctx->cameraIndex;
switch (eStatus) {
case EVzDeviceWorkStatus::keDeviceWorkStatus_Offline:
{
2026-05-17 00:38:31 +08:00
LOG_WARNING("[BasePresenter Camera Status Callback] Camera %d offline/disconnected\n", cameraIndex);
pThis->m_bCameraConnected = false;
2026-05-17 00:38:31 +08:00
pThis->OnCameraStatusChanged(cameraIndex, false);
break;
}
case EVzDeviceWorkStatus::keDeviceWorkStatus_Eye_Reconnect:
{
2026-05-17 00:38:31 +08:00
LOG_INFO("[BasePresenter Camera Status Callback] Camera %d online/connected\n", cameraIndex);
pThis->m_bCameraConnected = true;
2026-05-17 00:38:31 +08:00
pThis->OnCameraStatusChanged(cameraIndex, true);
break;
}
case EVzDeviceWorkStatus::keDeviceWorkStatus_Device_Swing_Finish:
{
2026-05-17 00:38:31 +08:00
LOG_INFO("[BasePresenter Camera Status Callback] Camera %d scan finished\n", cameraIndex);
2026-05-17 19:00:47 +08:00
// 分批模式下记录批次完成
if (pThis->m_batchInProgress.load()) {
2026-05-17 00:38:31 +08:00
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<int>(m_expectedList.size()); i++) {
// 检查该位置的相机是否已连接
if (i < static_cast<int>(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<int>(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<uint16_t> values(data, data + count);
m_modbusServer->updateHoldingRegisters(startAddress, values);
2026-04-19 12:28:59 +08:00
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;
}
2026-05-17 00:38:31 +08:00
// ============ 多相机同时扫描方法实现 ============
void BasePresenter::AddDetectionDataToCameraCache(int cameraIndex,
EVzResultDataType dataType, const SVzLaserLineData& laserData)
{
std::lock_guard<std::mutex> lock(m_perCameraDataMutex);
m_perCameraDataCache[cameraIndex].push_back(std::make_pair(dataType, laserData));
}
void BasePresenter::ClearAllCameraDataCaches()
{
std::lock_guard<std::mutex> lock(m_perCameraDataMutex);
for (auto& pair : m_perCameraDataCache) {
m_dataLoader.FreeLaserScanData(pair.second);
}
m_perCameraDataCache.clear();
}
void BasePresenter::OnCameraScanFinished(int cameraIndex)
{
std::lock_guard<std::mutex> 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<int>(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<int>(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<std::mutex> 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<std::mutex> lock(m_batchStateMutex);
2026-05-17 19:00:47 +08:00
if (!m_batchInProgress.load()) return;
2026-05-17 00:38:31 +08:00
batchComplete = (m_batchFinishedCount >= m_batchSize);
}
if (!batchComplete) {
// 批次未完成可能是被提前唤醒Offline 等事件)
return;
}
int batchEnd = std::min(m_batchStartIndex + m_batchSize, static_cast<int>(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<std::mutex> dataLock(m_perCameraDataMutex);
auto it = m_perCameraDataCache.find(cameraIndex);
if (it != m_perCameraDataCache.end() && !it->second.empty()) {
std::lock_guard<std::mutex> 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<std::mutex> lock(m_batchStateMutex);
m_batchStartIndex = batchEnd;
m_batchFinishedCount = 0;
int remaining = static_cast<int>(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<int>(m_batchCameraList.size()));
{
std::lock_guard<std::mutex> lock(m_batchStateMutex);
m_batchInProgress = false;
}
SetWorkStatus(WorkStatus::Completed);
}
}