2026-03-26 08:30:31 +08:00

436 lines
17 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "HolePitPositionPresenter.h"
#include "VrError.h"
#include "VrLog.h"
#include <QtCore/QCoreApplication>
#include <QtCore/QFileInfo>
#include <QtCore/QDir>
#include <QtCore/QString>
#include <QtCore/QStandardPaths>
#include <QtCore/QFile>
#include <QtCore/QDateTime>
#include <cmath>
#include <algorithm>
#include <cstdio>
#include <QImage>
#include <QThread>
#include <atomic>
#include "Version.h"
#include "VrTimeUtils.h"
#include "VrDateUtils.h"
#include "VrConvert.h"
#include "TCPServerProtocol.h"
#include "DetectPresenter.h"
#include "PathManager.h"
// 配置变化监听器代理实现
void ConfigChangeListenerProxy::OnSystemConfigChanged(const SystemConfig& config)
{
if (m_presenter) {
LOG_INFO("ConfigChangeListenerProxy: config changed, reloading algorithm parameters\n");
m_presenter->InitAlgoParams();
}
}
HolePitPositionPresenter::HolePitPositionPresenter(QObject *parent)
: BasePresenter(parent)
, m_pConfigManager(nullptr)
, m_pDetectPresenter(nullptr)
, m_pTCPServer(nullptr)
, m_bTCPConnected(false)
{
// 基类已经创建了相机重连定时器和检测数据缓存
}
HolePitPositionPresenter::~HolePitPositionPresenter()
{
// 基类会自动处理:相机重连定时器、算法检测线程、检测数据缓存、相机设备资源
// 释放ConfigManager
if (m_pConfigManager) {
m_pConfigManager->Shutdown();
delete m_pConfigManager;
m_pConfigManager = nullptr;
}
// 释放TCP服务器
if (m_pTCPServer) {
m_pTCPServer->Deinitialize();
delete m_pTCPServer;
m_pTCPServer = nullptr;
}
// 释放检测处理器
if (m_pDetectPresenter) {
delete m_pDetectPresenter;
m_pDetectPresenter = nullptr;
}
}
int HolePitPositionPresenter::InitApp()
{
LOG_DEBUG("Start APP Version: %s\n", HOLEPITPOSITION_FULL_VERSION_STRING);
fprintf(stderr, "[DIAG] InitApp started, version: %s\n", HOLEPITPOSITION_FULL_VERSION_STRING);
fflush(stderr);
// 初始化连接状态
SetWorkStatus(WorkStatus::InitIng);
fprintf(stderr, "[DIAG] Creating DetectPresenter...\n"); fflush(stderr);
m_pDetectPresenter = new DetectPresenter();
fprintf(stderr, "[DIAG] DetectPresenter created OK\n"); fflush(stderr);
int nRet = SUCCESS;
// 创建 ConfigManager 实例
fprintf(stderr, "[DIAG] Creating ConfigManager...\n"); fflush(stderr);
m_pConfigManager = new ConfigManager();
if (!m_pConfigManager) {
LOG_ERROR("Failed to create ConfigManager instance\n");
if (auto pStatus = GetStatusCallback<IYHolePitPositionStatus>()) pStatus->OnStatusUpdate("配置管理器创建失败");
return ERR_CODE(DEV_CONFIG_ERR);
}
fprintf(stderr, "[DIAG] ConfigManager created OK\n"); fflush(stderr);
// 初始化 ConfigManager
fprintf(stderr, "[DIAG] Initializing ConfigManager...\n"); fflush(stderr);
if (!m_pConfigManager->Initialize()) {
LOG_ERROR("Failed to initialize ConfigManager\n");
if (auto pStatus = GetStatusCallback<IYHolePitPositionStatus>()) pStatus->OnStatusUpdate("配置管理器初始化失败");
return ERR_CODE(DEV_CONFIG_ERR);
}
fprintf(stderr, "[DIAG] ConfigManager initialized OK\n"); fflush(stderr);
// 注册配置变化监听器
m_pConfigListener = std::make_shared<ConfigChangeListenerProxy>(this);
m_pConfigManager->AddConfigChangeListener(m_pConfigListener);
LOG_INFO("Configuration loaded successfully\n");
fprintf(stderr, "[DIAG] Configuration loaded OK\n"); fflush(stderr);
// 获取配置结果
ConfigResult configResult = m_pConfigManager->GetConfigResult();
fprintf(stderr, "[DIAG] Camera count from config: %zu\n", configResult.cameraList.size()); fflush(stderr);
// 调用基类InitCamera进行相机初始化bRGB=false, bSwing=true
fprintf(stderr, "[DIAG] Calling InitCamera...\n"); fflush(stderr);
InitCamera(configResult.cameraList, false, true);
fprintf(stderr, "[DIAG] InitCamera completed OK\n"); fflush(stderr);
LOG_INFO("Camera initialization completed. Connected cameras: %zu, default camera index: %d\n",
m_vrEyeDeviceList.size(), m_currentCameraIndex);
// 初始化TCP服务器
fprintf(stderr, "[DIAG] Initializing TCP server...\n"); fflush(stderr);
nRet = InitTCPServer();
if (nRet != 0) {
if (auto pStatus = GetStatusCallback<IYHolePitPositionStatus>()) pStatus->OnStatusUpdate("TCP服务器初始化失败");
m_bTCPConnected = false;
} else {
if (auto pStatus = GetStatusCallback<IYHolePitPositionStatus>()) pStatus->OnStatusUpdate("TCP服务器初始化成功");
}
fprintf(stderr, "[DIAG] TCP server init result: %d\n", nRet); fflush(stderr);
if (auto pStatus = GetStatusCallback<IYHolePitPositionStatus>()) pStatus->OnStatusUpdate("设备初始化完成");
CheckAndUpdateWorkStatus();
if (auto pStatus = GetStatusCallback<IYHolePitPositionStatus>()) pStatus->OnStatusUpdate("配置初始化成功");
fprintf(stderr, "[DIAG] InitApp completed successfully\n"); fflush(stderr);
return SUCCESS;
}
// 初始化算法参数实现BasePresenter纯虚函数
int HolePitPositionPresenter::InitAlgoParams()
{
fprintf(stderr, "[DIAG] InitAlgoParams started\n"); fflush(stderr);
LOG_DEBUG("initializing algorithm parameters\n");
QString exePath = QCoreApplication::applicationFilePath();
// 清空现有的手眼标定矩阵列表
m_clibMatrixList.clear();
// 从 ConfigManager 获取配置结果(包含多相机手眼标定矩阵列表)
ConfigResult configResult = m_pConfigManager->GetConfigResult();
// 从 config.xml 加载所有相机的手眼标定矩阵
for (size_t i = 0; i < configResult.handEyeCalibMatrixList.size(); i++) {
CalibMatrix calibMatrix;
memcpy(calibMatrix.clibMatrix, configResult.handEyeCalibMatrixList[i].matrix, sizeof(double) * 16);
m_clibMatrixList.push_back(calibMatrix);
LOG_INFO("Loaded hand-eye calibration matrix for camera %zu from config.xml:\n", i + 1);
QString clibMatrixStr;
for (int r = 0; r < 4; ++r) {
clibMatrixStr.clear();
for (int c = 0; c < 4; ++c) {
clibMatrixStr += QString::asprintf("%8.4f ", calibMatrix.clibMatrix[r * 4 + c]);
}
LOG_INFO(" %s\n", clibMatrixStr.toStdString().c_str());
}
}
// 如果没有配置任何矩阵,添加一个默认单位矩阵
if (m_clibMatrixList.empty()) {
CalibMatrix defaultMatrix;
double initClibMatrix[16] = {
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0
};
memcpy(defaultMatrix.clibMatrix, initClibMatrix, sizeof(initClibMatrix));
m_clibMatrixList.push_back(defaultMatrix);
LOG_WARNING("No hand-eye calibration matrices found, using identity matrix\n");
}
LOG_INFO("Total loaded %zu hand-eye calibration matrices\n", m_clibMatrixList.size());
const VrAlgorithmParams& xmlParams = configResult.algorithmParams;
// 打印线段参数
LOG_INFO("Loaded XML params - LineSeg: distScale=%.1f, segGapTh_y=%.1f, segGapTh_z=%.1f\n",
xmlParams.lineSegParam.distScale,
xmlParams.lineSegParam.segGapTh_y,
xmlParams.lineSegParam.segGapTh_z);
// 打印角点参数
LOG_INFO("Loaded XML params - Corner: cornerTh=%.1f, scale=%.1f, minEndingGap=%.1f, minEndingGap_z=%.1f, jumpCornerTh_1=%.1f, jumpCornerTh_2=%.1f\n",
xmlParams.cornerParam.cornerTh,
xmlParams.cornerParam.scale,
xmlParams.cornerParam.minEndingGap,
xmlParams.cornerParam.minEndingGap_z,
xmlParams.cornerParam.jumpCornerTh_1,
xmlParams.cornerParam.jumpCornerTh_2);
// 打印噪声滤除参数
LOG_INFO("Loaded XML params - OutlierFilter: continuityTh=%.1f, outlierTh=%.1f\n",
xmlParams.outlierFilterParam.continuityTh,
xmlParams.outlierFilterParam.outlierTh);
// 打印树生长参数
LOG_INFO("Loaded XML params - TreeGrow: maxLineSkipNum=%d, yDeviation_max=%.1f, maxSkipDistance=%.1f, zDeviation_max=%.1f, minLTypeTreeLen=%.1f, minVTypeTreeLen=%.1f\n",
xmlParams.treeGrowParam.maxLineSkipNum,
xmlParams.treeGrowParam.yDeviation_max,
xmlParams.treeGrowParam.maxSkipDistance,
xmlParams.treeGrowParam.zDeviation_max,
xmlParams.treeGrowParam.minLTypeTreeLen,
xmlParams.treeGrowParam.minVTypeTreeLen);
LOG_INFO("Algorithm parameters initialized successfully\n");
fprintf(stderr, "[DIAG] InitAlgoParams completed OK\n"); fflush(stderr);
return SUCCESS;
}
// 手眼标定矩阵管理方法实现
CalibMatrix HolePitPositionPresenter::GetClibMatrix(int index) const
{
CalibMatrix clibMatrix;
double initClibMatrix[16] = {
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0
};
memcpy(clibMatrix.clibMatrix, initClibMatrix, sizeof(initClibMatrix));
if (index >= 0 && index < static_cast<int>(m_clibMatrixList.size())) {
clibMatrix = m_clibMatrixList[index];
memcpy(clibMatrix.clibMatrix, m_clibMatrixList[index].clibMatrix, sizeof(initClibMatrix));
} else {
LOG_WARNING("Invalid hand-eye calibration matrix\n");
}
return clibMatrix;
}
std::string HolePitPositionPresenter::GetAlgoVersion() const
{
if (m_pDetectPresenter) {
return m_pDetectPresenter->GetAlgoVersion();
}
return "unknown";
}
void HolePitPositionPresenter::CheckAndUpdateWorkStatus()
{
if (m_bCameraConnected) {
SetWorkStatus(WorkStatus::Ready);
} else {
SetWorkStatus(WorkStatus::Error);
}
}
// 实现BasePresenter纯虚函数执行算法检测
int HolePitPositionPresenter::ProcessAlgoDetection(std::vector<std::pair<EVzResultDataType, SVzLaserLineData>>& detectionDataCache)
{
LOG_INFO("[Algo Thread] Start real detection task using algorithm\n");
// 1. 获取缓存的点云数据(已由基类验证非空)
unsigned int lineNum = detectionDataCache.size();
if (auto pStatus = GetStatusCallback<IYHolePitPositionStatus>()) {
pStatus->OnStatusUpdate("扫描线数:" + std::to_string(lineNum) + ",正在算法检测...");
}
// 检查检测处理器是否已初始化
if (!m_pDetectPresenter) {
LOG_ERROR("DetectPresenter is null, cannot proceed with detection\n");
if (auto pStatus = GetStatusCallback<IYHolePitPositionStatus>()) {
pStatus->OnStatusUpdate("检测处理器未初始化");
}
return ERR_CODE(DEV_NOT_FIND);
}
CVrTimeUtils oTimeUtils;
// 获取当前使用的手眼标定矩阵
const CalibMatrix currentClibMatrix = GetClibMatrix(m_currentCameraIndex - 1);
// 打印手眼标定矩阵
LOG_INFO("[Algo Thread] Hand-Eye Calibration Matrix (Camera %d):\n", m_currentCameraIndex);
LOG_INFO(" [%.4f, %.4f, %.4f, %.4f]\n", currentClibMatrix.clibMatrix[0], currentClibMatrix.clibMatrix[1], currentClibMatrix.clibMatrix[2], currentClibMatrix.clibMatrix[3]);
LOG_INFO(" [%.4f, %.4f, %.4f, %.4f]\n", currentClibMatrix.clibMatrix[4], currentClibMatrix.clibMatrix[5], currentClibMatrix.clibMatrix[6], currentClibMatrix.clibMatrix[7]);
LOG_INFO(" [%.4f, %.4f, %.4f, %.4f]\n", currentClibMatrix.clibMatrix[8], currentClibMatrix.clibMatrix[9], currentClibMatrix.clibMatrix[10], currentClibMatrix.clibMatrix[11]);
LOG_INFO(" [%.4f, %.4f, %.4f, %.4f]\n", currentClibMatrix.clibMatrix[12], currentClibMatrix.clibMatrix[13], currentClibMatrix.clibMatrix[14], currentClibMatrix.clibMatrix[15]);
// 从 ConfigManager 获取算法参数和调试参数
VrAlgorithmParams algorithmParams = m_pConfigManager->GetAlgorithmParams();
ConfigResult configResult = m_pConfigManager->GetConfigResult();
VrDebugParam debugParam = configResult.debugParam;
// 获取旋转顺序配置(从对应相机的标定矩阵中获取)
int eulerOrder = 11; // 默认外旋ZYX
int calibIdx = m_currentCameraIndex - 1;
if (calibIdx >= 0 && calibIdx < static_cast<int>(configResult.handEyeCalibMatrixList.size())) {
eulerOrder = configResult.handEyeCalibMatrixList[calibIdx].eulerOrder;
}
LOG_INFO("[Algo Thread] Using euler order: %d (camera %d)\n", eulerOrder, m_currentCameraIndex);
// 获取方向向量反向配置
int dirVectorInvert = configResult.plcRobotServerConfig.dirVectorInvert;
LOG_INFO("[Algo Thread] Using dir vector invert: %d\n", dirVectorInvert);
HoleDetectionResult detectionResult;
int nRet = m_pDetectPresenter->DetectHoles(m_currentCameraIndex, detectionDataCache,
algorithmParams, debugParam, m_dataLoader,
currentClibMatrix.clibMatrix, eulerOrder, dirVectorInvert,
m_currentRobotPose, detectionResult);
// 根据项目类型选择处理方式
if (auto pStatus = GetStatusCallback<IYHolePitPositionStatus>()) {
QString err = QString("错误:%1").arg(nRet);
pStatus->OnStatusUpdate(QString("检测%1").arg(SUCCESS == nRet ? "成功": err).toStdString());
}
LOG_INFO("[Algo Thread] DetectHoles detected %zu holes (candidates=%d, filtered=%d) time : %.2f ms\n",
detectionResult.positions.size(), detectionResult.totalCandidates, detectionResult.filteredCount,
oTimeUtils.GetElapsedTimeInMilliSec());
ERR_CODE_RETURN(nRet);
// 8. 通知UI检测结果
detectionResult.cameraIndex = m_currentCameraIndex;
if (auto pStatus = GetStatusCallback<IYHolePitPositionStatus>()) {
pStatus->OnDetectionResult(detectionResult);
}
// 更新状态
QString statusMsg = QString("检测完成,发现%1个孔").arg(detectionResult.positions.size());
if (auto pStatus = GetStatusCallback<IYHolePitPositionStatus>()) pStatus->OnStatusUpdate(statusMsg.toStdString());
// 发送检测结果给TCP客户端
_SendDetectionResultToTCP(detectionResult, m_currentCameraIndex);
// 9. 检测完成后,将工作状态更新为"完成"
SetWorkStatus(WorkStatus::Completed);
// 恢复到就绪状态
SetWorkStatus(WorkStatus::Ready);
return SUCCESS;
}
// 实现配置改变通知接口
void HolePitPositionPresenter::OnConfigChanged(const ConfigResult& configResult)
{
LOG_INFO("Configuration changed notification received, reloading algorithm parameters\n");
// 重新初始化算法参数
int result = InitAlgoParams();
if (result == SUCCESS) {
LOG_INFO("Algorithm parameters reloaded successfully after config change\n");
if (auto pStatus = GetStatusCallback<IYHolePitPositionStatus>()) {
pStatus->OnStatusUpdate("配置已更新,算法参数重新加载成功");
}
} else {
LOG_ERROR("Failed to reload algorithm parameters after config change, error: %d\n", result);
if (auto pStatus = GetStatusCallback<IYHolePitPositionStatus>()) {
pStatus->OnStatusUpdate("配置更新后算法参数重新加载失败");
}
}
}
// 实现BasePresenter纯虚函数相机状态变化通知
void HolePitPositionPresenter::OnCameraStatusChanged(int cameraIndex, bool isConnected)
{
LOG_INFO("Camera %d status changed: %s\n", cameraIndex, isConnected ? "connected" : "disconnected");
// 通知UI更新相机状态
if (GetStatusCallback<IYHolePitPositionStatus>()) {
if (cameraIndex == 1) {
if (auto pStatus = GetStatusCallback<IYHolePitPositionStatus>()) pStatus->OnCamera1StatusChanged(isConnected);
} else if (cameraIndex == 2) {
if (auto pStatus = GetStatusCallback<IYHolePitPositionStatus>()) pStatus->OnCamera2StatusChanged(isConnected);
}
// 获取相机名称用于状态消息
QString cameraName;
int arrayIndex = cameraIndex - 1;
if (arrayIndex >= 0 && arrayIndex < static_cast<int>(m_vrEyeDeviceList.size())) {
cameraName = QString::fromStdString(m_vrEyeDeviceList[arrayIndex].first);
} else {
cameraName = QString("相机%1").arg(cameraIndex);
}
QString statusMsg = QString("%1%2").arg(cameraName).arg(isConnected ? "已连接" : "已断开");
if (auto pStatus = GetStatusCallback<IYHolePitPositionStatus>()) pStatus->OnStatusUpdate(statusMsg.toStdString());
}
// 检查并更新工作状态
CheckAndUpdateWorkStatus();
}
// 实现BasePresenter虚函数工作状态变化通知
void HolePitPositionPresenter::OnWorkStatusChanged(WorkStatus status)
{
auto pStatus = GetStatusCallback<IYHolePitPositionStatus>();
if (pStatus) {
pStatus->OnWorkStatusChanged(status);
}
}
// 实现BasePresenter虚函数相机数量变化通知
void HolePitPositionPresenter::OnCameraCountChanged(int count)
{
auto pStatus = GetStatusCallback<IYHolePitPositionStatus>();
if (pStatus) {
pStatus->OnCameraCountChanged(count);
}
}
// 实现BasePresenter虚函数状态文字更新通知
void HolePitPositionPresenter::OnStatusUpdate(const std::string& statusMessage)
{
auto pStatus = GetStatusCallback<IYHolePitPositionStatus>();
if (pStatus) {
pStatus->OnStatusUpdate(statusMessage);
}
}