#include "ScrewPositionPresenter.h" #include "DetectPresenter.h" #include "PathManager.h" #include "ScrewPositionTCPProtocol.h" #include "Version.h" #include "VrConvert.h" #include "VrError.h" #include "VrLog.h" #include "VrTimeUtils.h" #include #include #include #include #include #include ScrewPositionPresenter::ScrewPositionPresenter(QObject *parent) : BasePresenter(parent) { } ScrewPositionPresenter::~ScrewPositionPresenter() { if (m_pConfigManager) { delete m_pConfigManager; m_pConfigManager = nullptr; } if (m_pTCPServer) { m_pTCPServer->Deinitialize(); delete m_pTCPServer; m_pTCPServer = nullptr; } if (m_pDetectPresenter) { delete m_pDetectPresenter; m_pDetectPresenter = nullptr; } } int ScrewPositionPresenter::InitApp() { LOG_DEBUG("Start APP Version: %s\n", SCREWPOSITION_FULL_VERSION_STRING); SetWorkStatus(WorkStatus::InitIng); m_pDetectPresenter = new DetectPresenter(); m_pConfigManager = new ConfigManager(); if (!m_pConfigManager) { LOG_ERROR("Failed to create ConfigManager instance\n"); if (auto pStatus = GetStatusCallback()) { pStatus->OnStatusUpdate("配置管理器创建失败"); } return ERR_CODE(DEV_CONFIG_ERR); } if (!m_pConfigManager->Initialize()) { LOG_ERROR("Failed to initialize ConfigManager\n"); if (auto pStatus = GetStatusCallback()) { pStatus->OnStatusUpdate("配置管理器初始化失败"); } return ERR_CODE(DEV_CONFIG_ERR); } const ConfigResult configResult = m_pConfigManager->GetConfigResult(); SetDebugParam(configResult.debugParam); m_modbusRegistersInitialized = false; m_modbusWorkStatus = 0; std::vector cameraList = configResult.cameraList; InitCamera(cameraList, false, true); LOG_INFO("Camera initialization completed. Connected cameras: %zu, default camera index: %d\n", m_vrEyeDeviceList.size(), m_currentCameraIndex); int nRet = InitTCPServer(); if (nRet != SUCCESS) { if (auto pStatus = GetStatusCallback()) { pStatus->OnStatusUpdate("TCP服务器初始化失败"); } m_bTCPConnected = false; } else { if (auto pStatus = GetStatusCallback()) { pStatus->OnStatusUpdate("TCP服务器初始化成功"); } } if (auto pStatus = GetStatusCallback()) { pStatus->OnStatusUpdate("设备初始化完成"); } CheckAndUpdateWorkStatus(); if (auto pStatus = GetStatusCallback()) { pStatus->OnStatusUpdate("配置初始化成功"); } return SUCCESS; } int ScrewPositionPresenter::InitAlgoParams() { LOG_DEBUG("initializing algorithm parameters\n"); m_clibMatrixList.clear(); const ConfigResult configResult = m_pConfigManager->GetConfigResult(); SetDebugParam(configResult.debugParam); const VrAlgorithmParams& xmlParams = configResult.algorithmParams; const 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 }; if (!configResult.handEyeCalibMatrixList.empty()) { int cameraCount = static_cast(configResult.cameraList.size()); for (const auto& matrixInfo : configResult.handEyeCalibMatrixList) { cameraCount = std::max(cameraCount, matrixInfo.cameraIndex); } cameraCount = std::max(cameraCount, 1); m_clibMatrixList.resize(static_cast(cameraCount)); for (auto& calibMatrix : m_clibMatrixList) { std::memcpy(calibMatrix.clibMatrix, initClibMatrix, sizeof(initClibMatrix)); } for (const auto& matrixInfo : configResult.handEyeCalibMatrixList) { if (matrixInfo.cameraIndex <= 0 || matrixInfo.cameraIndex > cameraCount) { continue; } std::memcpy(m_clibMatrixList[static_cast(matrixInfo.cameraIndex - 1)].clibMatrix, matrixInfo.matrix, sizeof(matrixInfo.matrix)); } LOG_INFO("Loaded %zu hand-eye matrices from config.xml\n", configResult.handEyeCalibMatrixList.size()); } else { const QString clibPath = PathManager::GetInstance().GetCalibrationFilePath(); LOG_INFO("Loading hand-eye matrices from legacy ini: %s\n", clibPath.toStdString().c_str()); const int nExistMatrixNum = CVrConvert::GetClibMatrixCount(clibPath.toStdString().c_str()); LOG_INFO("Found %d legacy hand-eye calibration matrices\n", nExistMatrixNum); for (int matrixIndex = 0; matrixIndex < nExistMatrixNum; ++matrixIndex) { char matrixIdent[64]; #ifdef _WIN32 sprintf_s(matrixIdent, "CalibMatrixInfo_%d", matrixIndex); #else sprintf(matrixIdent, "CalibMatrixInfo_%d", matrixIndex); #endif CalibMatrix calibMatrix; std::memcpy(calibMatrix.clibMatrix, initClibMatrix, sizeof(initClibMatrix)); if (CVrConvert::LoadClibMatrix(clibPath.toStdString().c_str(), matrixIdent, "dCalibMatrix", calibMatrix.clibMatrix)) { LOG_INFO("Successfully loaded legacy matrix %d\n", matrixIndex); } else { LOG_WARNING("Failed to load legacy matrix %d, using identity matrix\n", matrixIndex); } m_clibMatrixList.push_back(calibMatrix); } } if (m_clibMatrixList.empty()) { const int cameraCount = std::max(1, static_cast(configResult.cameraList.size())); m_clibMatrixList.resize(static_cast(cameraCount)); for (auto& calibMatrix : m_clibMatrixList) { std::memcpy(calibMatrix.clibMatrix, initClibMatrix, sizeof(initClibMatrix)); } LOG_INFO("No hand-eye matrix found, using identity matrix for %d camera(s)\n", cameraCount); } LOG_INFO("Loaded XML params - Screw: rodDiameter=%.1f, isHorizonScan=%s\n", xmlParams.screwParam.rodDiameter, xmlParams.screwParam.isHorizonScan ? "true" : "false"); LOG_INFO("Loaded XML params - Filter: continuityTh=%.1f, outlierTh=%.1f\n", xmlParams.filterParam.continuityTh, xmlParams.filterParam.outlierTh); LOG_INFO("Algorithm parameters initialized successfully\n"); return SUCCESS; } CalibMatrix ScrewPositionPresenter::GetClibMatrix(int index) const { CalibMatrix clibMatrix; const 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 }; std::memcpy(clibMatrix.clibMatrix, initClibMatrix, sizeof(initClibMatrix)); if (index >= 0 && index < static_cast(m_clibMatrixList.size())) { std::memcpy(clibMatrix.clibMatrix, m_clibMatrixList[index].clibMatrix, sizeof(initClibMatrix)); } else { LOG_WARNING("Invalid hand-eye calibration matrix\n"); } return clibMatrix; } void ScrewPositionPresenter::CheckAndUpdateWorkStatus() { SetWorkStatus(m_bCameraConnected ? WorkStatus::Ready : WorkStatus::Error); } int ScrewPositionPresenter::ProcessAlgoDetection(std::vector>& detectionDataCache) { LOG_INFO("[Algo Thread] Start real detection task using algorithm\n"); const unsigned int lineNum = detectionDataCache.size(); if (auto pStatus = GetStatusCallback()) { pStatus->OnStatusUpdate(QString("扫描线数:%1,正在算法检测...").arg(lineNum).toStdString()); } if (!m_pDetectPresenter) { LOG_ERROR("DetectPresenter is null, cannot proceed with detection\n"); if (auto pStatus = GetStatusCallback()) { pStatus->OnStatusUpdate("检测处理器未初始化"); } return ERR_CODE(DEV_NOT_FIND); } CVrTimeUtils oTimeUtils; const CalibMatrix currentClibMatrix = GetClibMatrix(m_currentCameraIndex - 1); const VrAlgorithmParams algorithmParams = m_pConfigManager->GetAlgorithmParams(); const ConfigResult configResult = m_pConfigManager->GetConfigResult(); const VrDebugParam debugParam = configResult.debugParam; const int poseOutputOrder = configResult.poseOutputOrder; // 每台相机独立:欧拉角顺序 + 绕 XYZ 三轴的补偿旋转 + 接近点偏移 + Robot 系后补偿 HandEyeExtrinsic extrinsic; for (const auto& calibMatrix : configResult.handEyeCalibMatrixList) { if (calibMatrix.cameraIndex == m_currentCameraIndex) { extrinsic.eulerOrder = calibMatrix.eulerOrder; extrinsic.rotX = calibMatrix.rotX; extrinsic.rotY = calibMatrix.rotY; extrinsic.rotZ = calibMatrix.rotZ; extrinsic.approachOffset = calibMatrix.approachOffset; extrinsic.offsetX = calibMatrix.offsetX; extrinsic.offsetY = calibMatrix.offsetY; extrinsic.offsetZ = calibMatrix.offsetZ; extrinsic.outRotX = calibMatrix.outRotX; extrinsic.outRotY = calibMatrix.outRotY; extrinsic.outRotZ = calibMatrix.outRotZ; break; } } LOG_INFO("[Algo Thread] camera=%d eulerOrder=%d poseOutputOrder=%d rot=(%.3f, %.3f, %.3f) outRot=(%.3f, %.3f, %.3f) approachOffset=%.3f offset=(%.3f, %.3f, %.3f)\n", m_currentCameraIndex, extrinsic.eulerOrder, poseOutputOrder, extrinsic.rotX, extrinsic.rotY, extrinsic.rotZ, extrinsic.outRotX, extrinsic.outRotY, extrinsic.outRotZ, extrinsic.approachOffset, extrinsic.offsetX, extrinsic.offsetY, extrinsic.offsetZ); DetectionResult detectionResult; detectionResult.cameraIndex = m_currentCameraIndex; int nRet = SUCCESS; if (m_currentDetectionType == DETECTION_TYPE_TOOL_DISK) { nRet = m_pDetectPresenter->DetectToolDisk( m_currentCameraIndex, detectionDataCache, algorithmParams, debugParam, m_dataLoader, currentClibMatrix.clibMatrix, m_currentRobotPose, extrinsic, poseOutputOrder, detectionResult); } else { nRet = m_pDetectPresenter->DetectScrew( m_currentCameraIndex, detectionDataCache, algorithmParams, debugParam, m_dataLoader, currentClibMatrix.clibMatrix, m_currentRobotPose, extrinsic, poseOutputOrder, detectionResult); } if (nRet != SUCCESS) { detectionResult.success = false; if (detectionResult.errorCode == 0) { detectionResult.errorCode = nRet; } if (detectionResult.message.isEmpty() || detectionResult.message == QStringLiteral("检测成功")) { detectionResult.message = QString("检测失败:%1").arg(nRet); } } LOG_INFO("[Algo Thread] detection type=%d objects=%zu time : %.2f ms\n", static_cast(m_currentDetectionType), detectionResult.positions.size(), oTimeUtils.GetElapsedTimeInMilliSec()); if (auto pStatus = GetStatusCallback()) { pStatus->OnDetectionResult(detectionResult); } QString statusMsg; if (!detectionResult.success) { statusMsg = detectionResult.message; } else if (m_currentDetectionType == DETECTION_TYPE_TOOL_DISK) { statusMsg = QString("工具盘检测完成,定位点数:%1").arg(detectionResult.positions.size()); } else { statusMsg = QString("检测完成,发现%1个螺杆").arg(detectionResult.positions.size()); } if (auto pStatus = GetStatusCallback()) { pStatus->OnStatusUpdate(statusMsg.toStdString()); } _SendDetectionResultToTCP(detectionResult, m_currentCameraIndex); _PublishDetectionResultToModbus(detectionResult); return nRet; } void ScrewPositionPresenter::OnConfigChanged(const ConfigResult& configResult) { LOG_INFO("Configuration changed notification received, reloading algorithm parameters\n"); SetDebugParam(configResult.debugParam); int result = InitAlgoParams(); stopServer(); const int tcpResult = InitTCPServer(); if (result == SUCCESS) { LOG_INFO("Algorithm parameters reloaded successfully after config change\n"); if (auto pStatus = GetStatusCallback()) { pStatus->OnStatusUpdate("配置已更新,算法参数重新加载成功"); } } else { LOG_ERROR("Failed to reload algorithm parameters after config change, error: %d\n", result); if (auto pStatus = GetStatusCallback()) { pStatus->OnStatusUpdate("配置更新后算法参数重新加载失败"); } } if (tcpResult != SUCCESS) { LOG_ERROR("Failed to restart TCP server after config change, error: %d\n", tcpResult); if (auto pStatus = GetStatusCallback()) { pStatus->OnStatusUpdate("TCP server restart failed"); } } } SSG_planeCalibPara ScrewPositionPresenter::_GetCameraCalibParam(int cameraIndex) { SSG_planeCalibPara calibParam; const double identityMatrix[9] = {1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0}; for (int i = 0; i < 9; ++i) { calibParam.planeCalib[i] = identityMatrix[i]; calibParam.invRMatrix[i] = identityMatrix[i]; } calibParam.planeHeight = -1.0; const VrAlgorithmParams algorithmParams = m_pConfigManager->GetAlgorithmParams(); for (const auto& cameraParam : algorithmParams.planeCalibParam.cameraCalibParams) { if (cameraParam.cameraIndex == cameraIndex && cameraParam.isCalibrated) { for (int i = 0; i < 9; ++i) { calibParam.planeCalib[i] = cameraParam.planeCalib[i]; calibParam.invRMatrix[i] = cameraParam.invRMatrix[i]; } calibParam.planeHeight = cameraParam.planeHeight; } } return calibParam; } void ScrewPositionPresenter::OnCameraStatusChanged(int cameraIndex, bool isConnected) { LOG_INFO("Camera %d status changed: %s\n", cameraIndex, isConnected ? "connected" : "disconnected"); if (auto pStatus = GetStatusCallback()) { if (cameraIndex == 1) { pStatus->OnCamera1StatusChanged(isConnected); } else if (cameraIndex == 2) { pStatus->OnCamera2StatusChanged(isConnected); } QString cameraName; const int arrayIndex = cameraIndex - 1; if (arrayIndex >= 0 && arrayIndex < static_cast(m_vrEyeDeviceList.size())) { cameraName = QString::fromStdString(m_vrEyeDeviceList[arrayIndex].first); } else { cameraName = QString("相机%1").arg(cameraIndex); } const QString statusMsg = QString("%1%2").arg(cameraName).arg(isConnected ? "已连接" : "已断开"); pStatus->OnStatusUpdate(statusMsg.toStdString()); } CheckAndUpdateWorkStatus(); } void ScrewPositionPresenter::OnWorkStatusChanged(WorkStatus status) { if (auto pStatus = GetStatusCallback()) { pStatus->OnWorkStatusChanged(status); } if (!m_modbusRegistersInitialized && status == WorkStatus::Ready) { _InitializeModbusRegisters(); } if (status == WorkStatus::Working) { _UpdateModbusWorkStatus(1); } else if (status == WorkStatus::Error) { _UpdateModbusWorkStatus(3); } } void ScrewPositionPresenter::OnCameraCountChanged(int count) { if (auto pStatus = GetStatusCallback()) { pStatus->OnCameraCountChanged(count); } } void ScrewPositionPresenter::OnStatusUpdate(const std::string& statusMessage) { if (auto pStatus = GetStatusCallback()) { pStatus->OnStatusUpdate(statusMessage); } } void ScrewPositionPresenter::OnModbusServerStatusChanged(bool isConnected) { if (auto pStatus = GetStatusCallback()) { pStatus->OnRobotConnectionChanged(isConnected); } } void ScrewPositionPresenter::OnModbusWriteCallback(uint16_t startAddress, const uint16_t* data, uint16_t count) { if (!data || count == 0) { return; } constexpr uint16_t kTriggerAddress = 0; constexpr uint16_t kRobotPoseAddress = 2; constexpr uint16_t kRobotPoseRegCount = 12; // 缓存机械臂位姿写入(地址2~13,6个float32) for (uint16_t i = 0; i < count; ++i) { uint16_t addr = startAddress + i; if (addr >= kRobotPoseAddress && addr < kRobotPoseAddress + kRobotPoseRegCount) { m_modbusRobotPoseRegs[addr - kRobotPoseAddress] = data[i]; } } // 检查触发 uint16_t triggerValue = 0; bool hasTrigger = false; for (uint16_t i = 0; i < count; ++i) { if (startAddress + i == kTriggerAddress) { triggerValue = data[i]; hasTrigger = true; break; } } if (!hasTrigger) { return; } const uint16_t resetValue = 0; WriteModbusRegisters(kTriggerAddress, &resetValue, 1); if (triggerValue == static_cast(DETECTION_TYPE_SCREW) || triggerValue == static_cast(DETECTION_TYPE_TOOL_DISK)) { // 从缓存的寄存器中解析机械臂位姿 RobotPose6D robotPose; auto regToFloat = [this](int offset) -> float { uint32_t raw = (static_cast(m_modbusRobotPoseRegs[offset]) << 16) | static_cast(m_modbusRobotPoseRegs[offset + 1]); float val = 0; std::memcpy(&val, &raw, sizeof(val)); return val; }; robotPose.x = regToFloat(0); robotPose.y = regToFloat(2); robotPose.z = regToFloat(4); robotPose.a = regToFloat(6); robotPose.b = regToFloat(8); robotPose.c = regToFloat(10); LOG_INFO("Modbus trigger: type=%u, robotPose=(%.3f, %.3f, %.3f, %.3f, %.3f, %.3f)\n", triggerValue, robotPose.x, robotPose.y, robotPose.z, robotPose.a, robotPose.b, robotPose.c); TriggerDetection(-1, static_cast(triggerValue), robotPose); return; } LOG_WARNING("Unsupported Modbus trigger value: %u\n", triggerValue); _UpdateModbusWorkStatus(3); if (auto pStatus = GetStatusCallback()) { pStatus->OnStatusUpdate(QString("Modbus触发值无效:%1").arg(triggerValue).toStdString()); } } bool ScrewPositionPresenter::CalculatePlaneCalibration( const std::vector>& scanData, double planeCalib[9], double& planeHeight, double invRMatrix[9]) { try { if (scanData.empty()) { LOG_ERROR("No scan data available for plane calibration\n"); return false; } LaserDataLoader dataLoader; std::vector> xyzData; int convertResult = dataLoader.ConvertToSVzNL3DPosition(scanData, xyzData); if (convertResult != SUCCESS || xyzData.empty()) { LOG_WARNING("Failed to convert data to XYZ format or no XYZ data available\n"); return false; } const double identity[9] = {1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0}; std::memcpy(planeCalib, identity, sizeof(double) * 9); std::memcpy(invRMatrix, identity, sizeof(double) * 9); planeHeight = -1.0; LOG_INFO("Plane calibration calculated successfully: height=%.3f\n", planeHeight); return true; } catch (const std::exception& e) { LOG_ERROR("Exception in CalculatePlaneCalibration: %s\n", e.what()); return false; } catch (...) { LOG_ERROR("Unknown exception in CalculatePlaneCalibration\n"); return false; } } bool ScrewPositionPresenter::SaveLevelingResults(double planeCalib[9], double planeHeight, double invRMatrix[9], int cameraIndex, const QString& cameraName) { try { if (!m_pConfigManager) { LOG_ERROR("ConfigManager is null, cannot save leveling results\n"); return false; } if (cameraIndex <= 0 || cameraName.isEmpty()) { LOG_ERROR("Invalid camera info when saving leveling results\n"); return false; } const QString configPath = PathManager::GetInstance().GetConfigFilePath(); SystemConfig systemConfig = m_pConfigManager->GetConfig(); VrCameraPlaneCalibParam cameraParam; cameraParam.cameraIndex = cameraIndex; cameraParam.cameraName = cameraName.toStdString(); cameraParam.planeHeight = planeHeight; cameraParam.isCalibrated = true; for (int i = 0; i < 9; ++i) { cameraParam.planeCalib[i] = planeCalib[i]; cameraParam.invRMatrix[i] = invRMatrix[i]; } systemConfig.configResult.algorithmParams.planeCalibParam.SetCameraCalibParam(cameraParam); if (!m_pConfigManager->UpdateFullConfig(systemConfig)) { LOG_ERROR("Failed to update config with leveling results\n"); return false; } if (!m_pConfigManager->SaveConfigToFile(configPath.toStdString())) { LOG_ERROR("Failed to save config file with leveling results\n"); return false; } LOG_INFO("Leveling results saved successfully for camera %d (%s)\n", cameraIndex, cameraName.toUtf8().constData()); return true; } catch (const std::exception& e) { LOG_ERROR("Exception in SaveLevelingResults: %s\n", e.what()); return false; } } bool ScrewPositionPresenter::LoadLevelingResults(int cameraIndex, const QString& cameraName, double planeCalib[9], double& planeHeight, double invRMatrix[9]) { try { if (!m_pConfigManager) { LOG_ERROR("ConfigManager is null, cannot load calibration data\n"); return false; } const ConfigResult configResult = m_pConfigManager->GetConfigResult(); VrCameraPlaneCalibParam cameraParamValue; if (!configResult.algorithmParams.planeCalibParam.GetCameraCalibParam(cameraIndex, cameraParamValue) || !cameraParamValue.isCalibrated) { LOG_INFO("No calibration data found for camera %d (%s)\n", cameraIndex, cameraName.toUtf8().constData()); return false; } for (int i = 0; i < 9; ++i) { planeCalib[i] = cameraParamValue.planeCalib[i]; invRMatrix[i] = cameraParamValue.invRMatrix[i]; } planeHeight = cameraParamValue.planeHeight; return true; } catch (const std::exception& e) { LOG_ERROR("Exception in LoadLevelingResults: %s\n", e.what()); return false; } } void ScrewPositionPresenter::DeinitApp() { LOG_DEBUG("Deinitializing ScrewPositionPresenter\n"); StopDetection(); if (m_pTCPServer) { m_pTCPServer->Deinitialize(); delete m_pTCPServer; m_pTCPServer = nullptr; } if (m_pConfigManager) { delete m_pConfigManager; m_pConfigManager = nullptr; } if (m_pDetectPresenter) { delete m_pDetectPresenter; m_pDetectPresenter = nullptr; } } bool ScrewPositionPresenter::TriggerDetection(int cameraIndex, DetectionType detectionType, const RobotPose6D& robotPose) { if (cameraIndex > 0) { SetDefaultCameraIndex(cameraIndex); } m_currentDetectionType = detectionType; m_currentRobotPose = robotPose; m_requestTimestamp = QDateTime::currentMSecsSinceEpoch(); if (!m_bCameraConnected) { LOG_WARNING("Camera not connected, cannot trigger detection\n"); _UpdateModbusWorkStatus(3); return false; } if (GetCurrentWorkStatus() == WorkStatus::Working) { LOG_WARNING("Detection is already running, skip duplicated trigger\n"); _UpdateModbusWorkStatus(1); return false; } _InitializeModbusRegisters(); _ResetModbusResultRegisters(); _UpdateModbusWorkStatus(1); int ret = StartDetection(cameraIndex, false); if (ret != SUCCESS) { LOG_ERROR("Failed to trigger detection, error: %d\n", ret); _UpdateModbusWorkStatus(3); return false; } return true; } int ScrewPositionPresenter::LoadAndDetect(const QString& fileName, DetectionType detectionType) { LOG_INFO("Loading data from file: %s, detectionType: %d\n", fileName.toStdString().c_str(), static_cast(detectionType)); m_currentDetectionType = detectionType; return LoadDebugDataAndDetect(fileName.toStdString()); } void ScrewPositionPresenter::ReconnectCamera() { LOG_INFO("Attempting to reconnect cameras\n"); TryReconnectCameras(); } ScrewPositionPresenter::AlgoParams ScrewPositionPresenter::GetAlgoParams() const { AlgoParams params; if (m_pConfigManager) { const VrAlgorithmParams algorithmParams = m_pConfigManager->GetAlgorithmParams(); params.screwParam = algorithmParams.screwParam; params.cornerParam = algorithmParams.cornerParam; params.filterParam = algorithmParams.filterParam; params.growParam = algorithmParams.growParam; } else { params.screwParam.rodDiameter = 10.0; params.screwParam.isHorizonScan = true; } return params; } QString ScrewPositionPresenter::GetAlgoVersion() const { return DetectPresenter::GetAlgoVersion(); } void ScrewPositionPresenter::SetAlgoParams(const AlgoParams& params) { if (!m_pConfigManager) { LOG_WARNING("ConfigManager not initialized, cannot set algorithm params\n"); return; } VrAlgorithmParams algorithmParams = m_pConfigManager->GetAlgorithmParams(); algorithmParams.screwParam = params.screwParam; algorithmParams.cornerParam = params.cornerParam; algorithmParams.filterParam = params.filterParam; algorithmParams.growParam = params.growParam; m_pConfigManager->UpdateAlgorithmParams(algorithmParams); LOG_INFO("Algorithm parameters updated\n"); }