1196 lines
44 KiB
C++
Raw Permalink Normal View History

#include "BagThreadPositionPresenter.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 <QImage>
#include <QThread>
#include <atomic>
#include <QJsonObject>
#include <QJsonArray>
2026-02-11 00:53:51 +08:00
#include <chrono>
#include "Version.h"
#include "VrTimeUtils.h"
#include "VrDateUtils.h"
#include "SG_baseDataType.h"
#include "VrConvert.h"
#include "DetectPresenter.h"
#include "PathManager.h"
#include "IGlLineLaserDevice.h" // GlLineLaserDevice接口
BagThreadPositionPresenter::BagThreadPositionPresenter(QObject *parent)
: BasePresenter(parent)
, m_pConfigManager(nullptr)
, m_pDetectPresenter(nullptr)
{
// 基类已经创建了相机重连定时器和检测数据缓存
}
BagThreadPositionPresenter::~BagThreadPositionPresenter()
{
2026-02-11 00:53:51 +08:00
// 关闭 ModbusTCP 客户端(必须在基类析构之前)
ShutdownModbusClient();
// 基类会自动处理相机重连定时器、算法检测线程、检测数据缓存、相机设备资源、ModbusTCP服务器
// 释放ConfigManager析构函数会自动调用Shutdown
if (m_pConfigManager) {
delete m_pConfigManager;
m_pConfigManager = nullptr;
}
// 释放检测处理器
if(m_pDetectPresenter)
{
delete m_pDetectPresenter;
m_pDetectPresenter = nullptr;
}
}
int BagThreadPositionPresenter::InitApp()
{
LOG_DEBUG("Start APP Version: %s\n", BAGTHREADPOSITION_FULL_VERSION_STRING);
// 初始化连接状态
SetWorkStatus(WorkStatus::InitIng);
m_pDetectPresenter = new DetectPresenter();
int nRet = SUCCESS;
// 创建 ConfigManager 实例
m_pConfigManager = new ConfigManager();
if (!m_pConfigManager) {
LOG_ERROR("Failed to create ConfigManager instance\n");
if (auto pStatus = GetStatusCallback<IYBagThreadPositionStatus>()) pStatus->OnStatusUpdate("配置管理器创建失败");
return ERR_CODE(DEV_CONFIG_ERR);
}
// 初始化 ConfigManager
if (!m_pConfigManager->Initialize()) {
LOG_ERROR("Failed to initialize ConfigManager\n");
if (auto pStatus = GetStatusCallback<IYBagThreadPositionStatus>()) pStatus->OnStatusUpdate("配置管理器初始化失败");
return ERR_CODE(DEV_CONFIG_ERR);
}
LOG_INFO("Configuration loaded successfully\n");
// 获取配置结果
ConfigResult configResult = m_pConfigManager->GetConfigResult();
2026-02-11 00:53:51 +08:00
// 保存 ModbusTCP 大小端配置
m_modbusBigEndian = configResult.modbusBigEndian;
LOG_INFO("ModbusTCP bigEndian: %s\n", m_modbusBigEndian ? "true (大端)" : "false (小端)");
// 调用基类InitCamera进行相机初始化bRGB=false, bSwing=false
// 注意BagThreadPosition使用GlLineLaserDevice通过重写CreateDevice实现
InitCamera(configResult.cameraList, false, false);
LOG_INFO("Camera initialization completed. Connected cameras: %zu, default camera index: %d\n",
m_vrEyeDeviceList.size(), m_currentCameraIndex);
2026-02-11 00:53:51 +08:00
// 初始化各相机的基准距离
for (size_t i = 0; i < configResult.cameraList.size(); i++) {
double baseDistance = configResult.cameraList[i].baseDistance;
if (baseDistance != 0.0) {
ApplyBaseDistance(configResult.cameraList[i].index, baseDistance);
}
}
2026-02-11 00:53:51 +08:00
// 初始化 ModbusTCP 客户端主动连接远端PLC
InitModbusClient(configResult.modbusIP, configResult.modbusPort, configResult.modbusPollingInterval);
if (auto pStatus = GetStatusCallback<IYBagThreadPositionStatus>()) pStatus->OnStatusUpdate("设备初始化完成");
CheckAndUpdateWorkStatus();
if (auto pStatus = GetStatusCallback<IYBagThreadPositionStatus>()) pStatus->OnStatusUpdate("配置初始化成功");
return SUCCESS;
}
// 初始化算法参数实现BasePresenter纯虚函数
int BagThreadPositionPresenter::InitAlgoParams()
{
LOG_DEBUG("initializing algorithm parameters\n");
// 清空现有的手眼标定矩阵列表
m_clibMatrixList.clear();
// 获取手眼标定文件路径并确保文件存在
QString clibPath = PathManager::GetInstance().GetCalibrationFilePath();
LOG_INFO("Loading hand-eye matrices from: %s\n", clibPath.toStdString().c_str());
// 读取存在的矩阵数量
int nExistMatrixNum = CVrConvert::GetClibMatrixCount(clibPath.toStdString().c_str());
LOG_INFO("Found %d 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;
// 初始化为单位矩阵
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 // 第四行
};
// 加载矩阵数据
bool loadSuccess = CVrConvert::LoadClibMatrix(clibPath.toStdString().c_str(), matrixIdent, "dCalibMatrix", calibMatrix.clibMatrix);
if(loadSuccess)
{
m_clibMatrixList.push_back(calibMatrix);
LOG_INFO("Successfully loaded matrix %d\n", matrixIndex);
// 输出矩阵内容
QString clibMatrixStr;
LOG_INFO("Matrix %d content:\n", matrixIndex);
for (int i = 0; i < 4; ++i) {
clibMatrixStr.clear();
for (int j = 0; j < 4; ++j) {
clibMatrixStr += QString::asprintf("%8.4f ", calibMatrix.clibMatrix[i * 4 + j]);
}
LOG_INFO(" %s\n", clibMatrixStr.toStdString().c_str());
}
}
else
{
LOG_WARNING("Failed to load matrix %d, using identity matrix\n", matrixIndex);
// 如果加载失败,使用单位矩阵
memcpy(calibMatrix.clibMatrix, initClibMatrix, sizeof(initClibMatrix));
m_clibMatrixList.push_back(calibMatrix);
}
}
LOG_INFO("Total loaded %zu hand-eye calibration matrices\n", m_clibMatrixList.size());
// 从 ConfigManager 获取配置结果
ConfigResult configResult = m_pConfigManager->GetConfigResult();
const VrAlgorithmParams& xmlParams = configResult.algorithmParams;
LOG_INFO("Loaded XML params - Thread: isHorizonScan=%s\n",
xmlParams.threadParam.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 BagThreadPositionPresenter::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;
}
void BagThreadPositionPresenter::CheckAndUpdateWorkStatus()
{
2026-02-11 00:53:51 +08:00
// 使用基类的 m_bCameraConnected 标志
if (m_bCameraConnected) {
SetWorkStatus(WorkStatus::Ready);
} else {
SetWorkStatus(WorkStatus::Error);
}
}
// 实现BasePresenter纯虚函数执行算法检测
int BagThreadPositionPresenter::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(GetStatusCallback<IYBagThreadPositionStatus>()){
if (auto pStatus = GetStatusCallback<IYBagThreadPositionStatus>()) pStatus->OnStatusUpdate("扫描线数:" + std::to_string(lineNum) + ",正在算法检测...");
}
// 检查检测处理器是否已初始化
if (!m_pDetectPresenter) {
LOG_ERROR("DetectPresenter is null, cannot proceed with detection\n");
if (GetStatusCallback<IYBagThreadPositionStatus>()) {
if (auto pStatus = GetStatusCallback<IYBagThreadPositionStatus>()) pStatus->OnStatusUpdate("检测处理器未初始化");
}
// 更新ModbusTCP寄存器地址2 = 2检测失败
2026-02-11 00:53:51 +08:00
if (IsModbusClientConnected()) {
WriteRemoteSingleRegister(2, 2); // 失败
LOG_INFO("ModbusTCP: 检测失败处理器未初始化地址2写入2\n");
}
return ERR_CODE(DEV_NOT_FIND);
}
CVrTimeUtils oTimeUtils;
// 获取当前使用的手眼标定矩阵
const CalibMatrix currentClibMatrix = GetClibMatrix(m_currentCameraIndex - 1);
// 从 ConfigManager 获取算法参数和调试参数
VrAlgorithmParams algorithmParams = m_pConfigManager->GetAlgorithmParams();
ConfigResult configResult = m_pConfigManager->GetConfigResult();
VrDebugParam debugParam = configResult.debugParam;
DetectionResult detectionResult;
int nRet = m_pDetectPresenter->DetectScrew(m_currentCameraIndex, detectionDataCache,
algorithmParams, debugParam, m_dataLoader,
currentClibMatrix.clibMatrix, detectionResult);
// 根据项目类型选择处理方式
if (GetStatusCallback<IYBagThreadPositionStatus>()) {
QString err = QString("错误:%1").arg(nRet);
if (auto pStatus = GetStatusCallback<IYBagThreadPositionStatus>()) pStatus->OnStatusUpdate(QString("检测%1").arg(SUCCESS == nRet ? "成功": err).toStdString());
}
LOG_INFO("[Algo Thread] sx_bagThreadMeasure detected %zu objects time : %.2f ms\n", detectionResult.positions.size(), oTimeUtils.GetElapsedTimeInMilliSec());
2026-02-11 00:53:51 +08:00
// 如果检测失败更新ModbusTCP状态并恢复轮询
if (nRet != SUCCESS) {
2026-02-11 00:53:51 +08:00
if (IsModbusClientConnected()) {
WriteRemoteSingleRegister(2, 2); // 失败
LOG_INFO("ModbusTCP: 检测失败地址2写入2\n");
}
2026-02-11 00:53:51 +08:00
// 检测失败也要恢复轮询,继续读取扫描请求
m_modbusPollingPaused = false;
}
ERR_CODE_RETURN(nRet);
// 8. 通知UI检测结果
detectionResult.cameraIndex = m_currentCameraIndex;
if (auto pStatus = GetStatusCallback<IYBagThreadPositionStatus>()) {
pStatus->OnDetectionResult(detectionResult);
}
// 保存检测结果用于ModbusTCP输出
{
std::lock_guard<std::mutex> lock(m_modbusResultMutex);
m_lastDetectionResult = detectionResult;
}
// 更新ModbusTCP寄存器地址2 = 1检测成功并输出xyzu数据到地址4
2026-02-11 00:53:51 +08:00
if (IsModbusClientConnected()) {
WriteRemoteSingleRegister(2, 1); // 成功
LOG_INFO("ModbusTCP: 检测成功地址2写入1\n");
// 直接输出xyzu数据到地址4
if (!detectionResult.threadInfoList.empty()) {
size_t threadCount = detectionResult.threadInfoList.size();
std::vector<uint16_t> outputData;
outputData.reserve(threadCount * 8);
LOG_INFO("ModbusTCP: 准备输出 %zu 条拆线的xyzu数据\n", threadCount);
// 将每个拆线的 x, y, z, u 转换为寄存器数据
for (const auto& thread : detectionResult.threadInfoList) {
2026-02-11 00:53:51 +08:00
uint16_t regs[2];
// x (centerX)
float x = static_cast<float>(thread.centerX);
2026-02-11 00:53:51 +08:00
_FloatToRegisters(x, regs, m_modbusBigEndian);
outputData.push_back(regs[0]);
outputData.push_back(regs[1]);
// y (centerY)
float y = static_cast<float>(thread.centerY);
2026-02-11 00:53:51 +08:00
_FloatToRegisters(y, regs, m_modbusBigEndian);
outputData.push_back(regs[0]);
outputData.push_back(regs[1]);
// z (centerZ)
float z = static_cast<float>(thread.centerZ);
2026-02-11 00:53:51 +08:00
_FloatToRegisters(z, regs, m_modbusBigEndian);
outputData.push_back(regs[0]);
outputData.push_back(regs[1]);
// u (rotateAngle)
float u = static_cast<float>(thread.rotateAngle);
2026-02-11 00:53:51 +08:00
_FloatToRegisters(u, regs, m_modbusBigEndian);
outputData.push_back(regs[0]);
outputData.push_back(regs[1]);
LOG_DEBUG("ModbusTCP: 拆线数据: x=%.3f, y=%.3f, z=%.3f, u=%.3f\n",
thread.centerX, thread.centerY, thread.centerZ, thread.rotateAngle);
}
// 从地址4开始写入数据
if (!outputData.empty()) {
2026-02-11 00:53:51 +08:00
bool ret = WriteRemoteRegisters(4, outputData);
if (ret) {
LOG_INFO("ModbusTCP: 成功输出 %zu 条拆线的xyzu数据到寄存器地址4起共%zu个寄存器\n",
threadCount, outputData.size());
} else {
2026-02-11 00:53:51 +08:00
LOG_ERROR("ModbusTCP: 输出xyzu数据失败\n");
}
}
}
}
2026-02-11 00:53:51 +08:00
// 恢复轮询(检测完成后恢复读取扫描请求)
m_modbusPollingPaused = false;
// 更新状态
QString statusMsg = QString("检测完成,发现%1条拆线").arg(detectionResult.positions.size() / 2);
if (auto pStatus = GetStatusCallback<IYBagThreadPositionStatus>()) pStatus->OnStatusUpdate(statusMsg.toStdString());
// 9. 检测完成后,将工作状态更新为"完成"
SetWorkStatus(WorkStatus::Completed);
// 恢复到就绪状态
SetWorkStatus(WorkStatus::Ready);
return SUCCESS;
}
// 实现配置改变通知接口
void BagThreadPositionPresenter::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 (GetStatusCallback<IYBagThreadPositionStatus>()) {
if (auto pStatus = GetStatusCallback<IYBagThreadPositionStatus>()) pStatus->OnStatusUpdate("配置已更新,算法参数重新加载成功");
}
} else {
LOG_ERROR("Failed to reload algorithm parameters after config change, error: %d\n", result);
if (GetStatusCallback<IYBagThreadPositionStatus>()) {
if (auto pStatus = GetStatusCallback<IYBagThreadPositionStatus>()) pStatus->OnStatusUpdate("配置更新后算法参数重新加载失败");
}
}
}
// 根据相机索引获取调平参数
SSG_planeCalibPara BagThreadPositionPresenter::_GetCameraCalibParam(int cameraIndex)
{
// 查找指定相机索引的调平参数
SSG_planeCalibPara calibParam;
// 使用单位矩阵(未校准状态)
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; // 使用默认高度
// 从 ConfigManager 获取算法参数
VrAlgorithmParams algorithmParams = m_pConfigManager->GetAlgorithmParams();
for (const auto& cameraParam : algorithmParams.planeCalibParam.cameraCalibParams) {
if (cameraParam.cameraIndex == cameraIndex) {
// 根据isCalibrated标志决定使用标定矩阵还是单位矩阵
if (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;
}
// 实现BasePresenter纯虚函数相机状态变化通知
void BagThreadPositionPresenter::OnCameraStatusChanged(int cameraIndex, bool isConnected)
{
LOG_INFO("Camera %d status changed: %s\n", cameraIndex, isConnected ? "connected" : "disconnected");
// 通知UI更新相机状态
if (GetStatusCallback<IYBagThreadPositionStatus>()) {
if (cameraIndex == 1) {
if (auto pStatus = GetStatusCallback<IYBagThreadPositionStatus>()) pStatus->OnCamera1StatusChanged(isConnected);
} else if (cameraIndex == 2) {
if (auto pStatus = GetStatusCallback<IYBagThreadPositionStatus>()) 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<IYBagThreadPositionStatus>()) pStatus->OnStatusUpdate(statusMsg.toStdString());
}
// 检查并更新工作状态
CheckAndUpdateWorkStatus();
}
// 实现BasePresenter虚函数工作状态变化通知
void BagThreadPositionPresenter::OnWorkStatusChanged(WorkStatus status)
{
auto pStatus = GetStatusCallback<IYBagThreadPositionStatus>();
if (pStatus) {
pStatus->OnWorkStatusChanged(status);
}
}
// 实现BasePresenter虚函数相机数量变化通知
void BagThreadPositionPresenter::OnCameraCountChanged(int count)
{
auto pStatus = GetStatusCallback<IYBagThreadPositionStatus>();
if (pStatus) {
pStatus->OnCameraCountChanged(count);
}
}
// 实现BasePresenter虚函数状态文字更新通知
void BagThreadPositionPresenter::OnStatusUpdate(const std::string& statusMessage)
{
auto pStatus = GetStatusCallback<IYBagThreadPositionStatus>();
if (pStatus) {
pStatus->OnStatusUpdate(statusMessage);
}
}
2026-02-11 00:53:51 +08:00
// 实现BasePresenter虚函数ModbusTCP连接状态变化通知
void BagThreadPositionPresenter::OnModbusServerStatusChanged(bool isConnected)
{
LOG_INFO("ModbusTCP Client 连接状态: %s\n", isConnected ? "已连接" : "已断开");
auto pStatus = GetStatusCallback<IYBagThreadPositionStatus>();
if (pStatus) {
// ModbusTCP连接状态关联到机械臂连接状态
pStatus->OnRobotConnectionChanged(isConnected);
// 更新状态文字显示远端IP
std::string statusMsg = isConnected ?
"ModbusTCP已连接到 " + m_modbusClientIP :
"ModbusTCP已断开" + m_modbusClientIP + "";
pStatus->OnStatusUpdate(statusMsg);
}
}
// ============ 实现 ICameraLevelCalculator 接口 ============
bool BagThreadPositionPresenter::CalculatePlaneCalibration(
const std::vector<std::pair<EVzResultDataType, SVzLaserLineData>>& 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;
}
LOG_INFO("Calculating plane calibration from %zu scan lines\n", scanData.size());
// 转换为算法需要的XYZ格式
LaserDataLoader dataLoader;
std::vector<std::vector<SVzNL3DPosition>> 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;
}
// 拆线定位项目暂时使用简单的平面拟合
// 使用默认的单位矩阵
double identity[9] = {1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0};
memcpy(planeCalib, identity, sizeof(double) * 9);
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;
}
}
// ============ 实现 ICameraLevelResultSaver 接口 ============
bool BagThreadPositionPresenter::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) {
LOG_ERROR("Invalid camera index: %d\n", cameraIndex);
return false;
}
if (cameraName.isEmpty()) {
LOG_ERROR("Camera name is empty\n");
return false;
}
// 获取当前配置
QString configPath = PathManager::GetInstance().GetConfigFilePath();
LOG_INFO("Config path: %s\n", configPath.toUtf8().constData());
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());
LOG_INFO("Plane height: %.3f\n", planeHeight);
LOG_INFO("Calibration marked as completed\n");
return true;
} catch (const std::exception& e) {
LOG_ERROR("Exception in SaveLevelingResults: %s\n", e.what());
return false;
}
}
bool BagThreadPositionPresenter::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;
}
// 从ConfigManager获取配置结果
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;
LOG_INFO("Calibration data loaded successfully for camera %d (%s)\n", cameraIndex, cameraName.toUtf8().constData());
LOG_INFO("Plane height: %.3f\n", planeHeight);
return true;
} catch (const std::exception& e) {
LOG_ERROR("Exception in LoadLevelingResults: %s\n", e.what());
return false;
}
}
// 反初始化
void BagThreadPositionPresenter::DeinitApp()
{
LOG_DEBUG("Deinitializing BagThreadPositionPresenter\n");
2026-02-11 00:53:51 +08:00
// 关闭 ModbusTCP 客户端
ShutdownModbusClient();
// 停止检测
StopDetection();
// 释放ConfigManager析构函数会自动调用Shutdown
if (m_pConfigManager) {
delete m_pConfigManager;
m_pConfigManager = nullptr;
}
// 释放检测处理器
if (m_pDetectPresenter) {
delete m_pDetectPresenter;
m_pDetectPresenter = nullptr;
}
LOG_DEBUG("BagThreadPositionPresenter deinitialized\n");
}
// 触发检测
bool BagThreadPositionPresenter::TriggerDetection(int cameraIndex)
{
// 设置相机索引
if (cameraIndex > 0) {
SetDefaultCameraIndex(cameraIndex);
}
// 检查是否已连接相机
if (!m_bCameraConnected) {
LOG_WARNING("Camera not connected, cannot trigger detection\n");
return false;
}
// 触发检测
int ret = StartDetection(cameraIndex, false);
if (ret != SUCCESS) {
LOG_ERROR("Failed to trigger detection, error: %d\n", ret);
return false;
}
return true;
}
// 加载文件并检测
int BagThreadPositionPresenter::LoadAndDetect(const QString& fileName)
{
LOG_INFO("Loading data from file: %s\n", fileName.toStdString().c_str());
// 使用基类的方法加载调试数据并执行检测
return LoadDebugDataAndDetect(fileName.toStdString());
}
// 重连相机
void BagThreadPositionPresenter::ReconnectCamera()
{
LOG_INFO("Attempting to reconnect cameras\n");
TryReconnectCameras();
}
// 获取算法参数
BagThreadPositionPresenter::AlgoParams BagThreadPositionPresenter::GetAlgoParams() const
{
AlgoParams params;
if (m_pConfigManager) {
VrAlgorithmParams algorithmParams = m_pConfigManager->GetAlgorithmParams();
params.threadParam = algorithmParams.threadParam;
params.cornerParam = algorithmParams.cornerParam;
params.filterParam = algorithmParams.filterParam;
params.growParam = algorithmParams.growParam;
} else {
// 使用默认参数
params.threadParam.isHorizonScan = true;
}
return params;
}
// 设置算法参数
void BagThreadPositionPresenter::SetAlgoParams(const AlgoParams& params)
{
if (!m_pConfigManager) {
LOG_WARNING("ConfigManager not initialized, cannot set algorithm params\n");
return;
}
// 获取当前配置
VrAlgorithmParams algorithmParams = m_pConfigManager->GetAlgorithmParams();
// 更新参数
algorithmParams.threadParam = params.threadParam;
algorithmParams.cornerParam = params.cornerParam;
algorithmParams.filterParam = params.filterParam;
algorithmParams.growParam = params.growParam;
// 更新到ConfigManager
m_pConfigManager->UpdateAlgorithmParams(algorithmParams);
2026-02-11 00:53:51 +08:00
// 保存到配置文件
QString configPath = PathManager::GetInstance().GetConfigFilePath();
if (m_pConfigManager->SaveConfigToFile(configPath.toStdString())) {
LOG_INFO("Algorithm parameters updated and saved to config file\n");
} else {
LOG_ERROR("Failed to save algorithm parameters to config file\n");
}
}
// 重写BasePresenter虚函数创建设备对象
// BagThreadPosition项目使用GlLineLaserDevice
int BagThreadPositionPresenter::CreateDevice(IVrEyeDevice** ppDevice)
{
if (!ppDevice) {
return ERR_CODE(DEV_ARG_INVAILD);
}
// 使用IGlLineLaserDevice工厂方法创建GlLineLaserDevice
IGlLineLaserDevice* pGlDevice = nullptr;
int nRet = IGlLineLaserDevice::CreateGlLineLaserObject(&pGlDevice);
if (nRet == SUCCESS && pGlDevice) {
*ppDevice = pGlDevice;
LOG_INFO("[BagThreadPositionPresenter] Created GlLineLaser device\n");
return SUCCESS;
}
LOG_ERROR("[BagThreadPositionPresenter] Failed to create GlLineLaser device, error: %d\n", nRet);
return ERR_CODE(DEV_OPEN_ERR);
}
2026-02-11 00:53:51 +08:00
// 应用基准距离到指定相机设备
void BagThreadPositionPresenter::ApplyBaseDistance(int cameraIndex, double baseDistance)
{
2026-02-11 00:53:51 +08:00
int arrayIndex = cameraIndex - 1;
if (arrayIndex < 0 || arrayIndex >= static_cast<int>(m_vrEyeDeviceList.size())) {
LOG_WARNING("[BagThreadPositionPresenter] ApplyBaseDistance: invalid cameraIndex %d\n", cameraIndex);
return;
}
2026-02-11 00:53:51 +08:00
IVrEyeDevice* pDevice = m_vrEyeDeviceList[arrayIndex].second;
IGlLineLaserDevice* pGlDevice = dynamic_cast<IGlLineLaserDevice*>(pDevice);
if (pGlDevice) {
int ret = pGlDevice->SetBaseDistance(baseDistance);
if (ret == SUCCESS) {
LOG_INFO("[BagThreadPositionPresenter] 相机%d基准距离已设置: %.1f mm\n", cameraIndex, baseDistance);
} else {
LOG_ERROR("[BagThreadPositionPresenter] 相机%d设置基准距离失败: %d\n", cameraIndex, ret);
}
} else {
LOG_WARNING("[BagThreadPositionPresenter] 相机%d不是GlLineLaser设备无法设置基准距离\n", cameraIndex);
}
}
// 应用ModbusTCP配置IP和大小端
void BagThreadPositionPresenter::ApplyModbusConfig(const std::string& ip, bool bigEndian)
{
m_modbusBigEndian = bigEndian;
LOG_INFO("[BagThreadPositionPresenter] ModbusTCP大小端已更新: %s\n", bigEndian ? "大端" : "小端");
// 如果IP发生变化重启ModbusTCP客户端
if (ip != m_modbusClientIP) {
LOG_INFO("[BagThreadPositionPresenter] ModbusTCP IP变更: %s -> %s重启客户端\n",
m_modbusClientIP.c_str(), ip.c_str());
ShutdownModbusClient();
m_modbusShutdownRequested = false;
InitModbusClient(ip, m_modbusClientPort, m_modbusPollingInterval);
}
}
// ============ 面标定辅助函数 ============
SSG_planeCalibPara BagThreadPositionPresenter::GetBaseCalibPara(
const std::vector<std::pair<EVzResultDataType, SVzLaserLineData>>& scanData)
{
SSG_planeCalibPara calibPara;
2026-02-11 00:53:51 +08:00
// 初始化为单位矩阵
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++) {
calibPara.planeCalib[i] = identityMatrix[i];
calibPara.invRMatrix[i] = identityMatrix[i];
}
calibPara.planeHeight = 2600.0;
// 转换为算法需要的XYZ格式
std::vector<std::vector<SVzNL3DPosition>> xyzData;
int convertResult = m_dataLoader.ConvertToSVzNL3DPosition(scanData, xyzData);
if (convertResult != SUCCESS || xyzData.empty()) {
LOG_WARNING("Failed to convert data to XYZ format for calibration\n");
return calibPara;
}
2026-02-11 00:53:51 +08:00
// 调用算法获取基础标定参数
calibPara = wd_bagThread_getBaseCalibPara(xyzData);
2026-02-11 00:53:51 +08:00
LOG_INFO("Base calibration parameters obtained: planeHeight=%.3f\n", calibPara.planeHeight);
2026-02-11 00:53:51 +08:00
return calibPara;
}
// ============ ModbusTCP Client 实现 ============
void BagThreadPositionPresenter::InitModbusClient(const std::string& ip, uint16_t port, int pollingInterval)
{
m_modbusClientIP = ip;
m_modbusClientPort = port;
m_modbusPollingInterval = pollingInterval;
m_modbusShutdownRequested = false;
m_modbusReconnectAttempts = 0;
m_lastScanRequestState = false;
m_lastModbusConnectedState = false;
LOG_INFO("[ModbusTCP Client] 初始化: IP=%s, 端口=%d, 轮询间隔=%dms\n",
ip.c_str(), port, pollingInterval);
// 创建客户端并尝试连接
bool connected = false;
if (!ip.empty()) {
std::lock_guard<std::mutex> lock(m_modbusClientMutex);
if (m_modbusClient) {
m_modbusClient->Disconnect();
delete m_modbusClient;
m_modbusClient = nullptr;
}
if (!IYModbusTCPClient::CreateInstance(&m_modbusClient, ip, port)) {
LOG_ERROR("[ModbusTCP Client] 创建客户端实例失败\n");
} else {
m_modbusClient->SetTimeout(1000);
auto result = m_modbusClient->Connect();
if (result != IYModbusTCPClient::SUCCESS) {
LOG_WARNING("[ModbusTCP Client] 连接 %s:%d 失败: %s\n",
ip.c_str(), port, m_modbusClient->GetLastError().c_str());
} else {
2026-02-11 00:53:51 +08:00
LOG_INFO("[ModbusTCP Client] 成功连接到 %s:%d\n", ip.c_str(), port);
connected = true;
}
}
}
2026-02-11 00:53:51 +08:00
m_lastModbusConnectedState = connected;
OnModbusServerStatusChanged(connected);
// 启动轮询线程
StartModbusPolling();
}
void BagThreadPositionPresenter::ShutdownModbusClient()
{
// 防止重复调用
bool expected = false;
if (!m_modbusShutdownRequested.compare_exchange_strong(expected, true)) {
return;
}
LOG_INFO("[ModbusTCP Client] 正在关闭...\n");
// 停止轮询线程
StopModbusPolling();
// 清理客户端
{
std::lock_guard<std::mutex> lock(m_modbusClientMutex);
if (m_modbusClient) {
m_modbusClient->Disconnect();
delete m_modbusClient;
m_modbusClient = nullptr;
}
}
LOG_INFO("[ModbusTCP Client] 已关闭\n");
}
void BagThreadPositionPresenter::StartModbusPolling()
{
if (m_modbusShutdownRequested) {
LOG_WARNING("[ModbusTCP Client] 无法启动轮询:已请求关闭\n");
return;
}
if (m_modbusPollingRunning) {
LOG_WARNING("[ModbusTCP Client] 轮询已在运行\n");
return;
}
m_modbusPollingRunning = true;
m_modbusPollingThread = std::thread(&BagThreadPositionPresenter::ModbusPollingThreadFunc, this);
LOG_INFO("[ModbusTCP Client] 轮询线程已启动,间隔: %d ms\n", m_modbusPollingInterval);
}
void BagThreadPositionPresenter::StopModbusPolling()
{
if (!m_modbusPollingRunning) {
return;
}
m_modbusPollingRunning = false;
if (m_modbusPollingThread.joinable()) {
m_modbusPollingThread.join();
}
LOG_INFO("[ModbusTCP Client] 轮询线程已停止\n");
}
void BagThreadPositionPresenter::ModbusPollingThreadFunc()
{
LOG_INFO("[ModbusTCP Client] 轮询线程启动\n");
int reconnectCounter = 0;
const int reconnectInterval = 2000; // 重连间隔 2秒
while (m_modbusPollingRunning && !m_modbusShutdownRequested) {
// 检查连接状态
bool currentConnected = CheckModbusConnection();
// 连接状态变化时通知
if (currentConnected != m_lastModbusConnectedState) {
m_lastModbusConnectedState = currentConnected;
OnModbusServerStatusChanged(currentConnected);
// 重连成功后重置状态
if (currentConnected) {
m_lastScanRequestState = false;
m_modbusReconnectAttempts = 0;
m_modbusReadFailCount = 0;
reconnectCounter = 0;
LOG_INFO("[ModbusTCP Client] 重连成功\n");
}
}
if (!currentConnected) {
// 未连接时,尝试重连
reconnectCounter++;
int reconnectThreshold = (m_modbusPollingInterval > 0) ?
(reconnectInterval / m_modbusPollingInterval) : 10;
if (reconnectCounter >= reconnectThreshold) {
reconnectCounter = 0;
m_modbusReconnectAttempts++;
LOG_INFO("[ModbusTCP Client] 尝试重连(第%d次...\n", m_modbusReconnectAttempts);
TryConnectModbus();
}
std::this_thread::sleep_for(std::chrono::milliseconds(m_modbusPollingInterval));
continue;
}
// 已连接但暂停轮询(检测期间)
if (m_modbusPollingPaused) {
std::this_thread::sleep_for(std::chrono::milliseconds(m_modbusPollingInterval));
continue;
}
// 读取扫描请求寄存器 reg[0]
int requestValue = ReadScanRequest();
if (requestValue >= 0) {
// 读取成功,重置失败计数
m_modbusReadFailCount = 0;
// 电平检测reg[0]==1 即触发扫描
if (requestValue == 1) {
LOG_INFO("[ModbusTCP Client] 检测到扫描请求reg[0]=1\n");
// 暂停轮询(检测期间不再读取请求)
m_modbusPollingPaused = true;
// 清零 reg[0]
WriteRemoteSingleRegister(0, 0);
// 清零 reg[2](检测状态)
WriteRemoteSingleRegister(2, 0);
// 触发检测
bool success = TriggerDetection(m_currentCameraIndex);
if (!success) {
LOG_ERROR("[ModbusTCP Client] 触发检测失败\n");
WriteRemoteSingleRegister(2, 2); // 写入失败状态
m_modbusPollingPaused = false; // 恢复轮询
}
}
} else {
// 读取失败,累计失败次数
m_modbusReadFailCount++;
LOG_WARNING("[ModbusTCP Client] 读取扫描请求失败(连续第%d次\n", m_modbusReadFailCount);
if (m_modbusReadFailCount >= MAX_READ_FAIL_COUNT) {
// 连续多次失败,断开连接以触发重连
LOG_WARNING("[ModbusTCP Client] 连续%d次读取失败断开连接以触发重连\n", m_modbusReadFailCount);
m_modbusReadFailCount = 0;
{
std::lock_guard<std::mutex> lock(m_modbusClientMutex);
if (m_modbusClient) {
m_modbusClient->Disconnect();
}
}
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(m_modbusPollingInterval));
}
LOG_INFO("[ModbusTCP Client] 轮询线程退出\n");
}
bool BagThreadPositionPresenter::CheckModbusConnection()
{
std::lock_guard<std::mutex> lock(m_modbusClientMutex);
return m_modbusClient && m_modbusClient->IsConnected();
}
bool BagThreadPositionPresenter::TryConnectModbus()
{
std::lock_guard<std::mutex> lock(m_modbusClientMutex);
// 销毁旧实例,重新创建以确保 libmodbus 底层 socket 状态干净
if (m_modbusClient) {
m_modbusClient->Disconnect();
delete m_modbusClient;
m_modbusClient = nullptr;
}
if (!IYModbusTCPClient::CreateInstance(&m_modbusClient, m_modbusClientIP, m_modbusClientPort)) {
LOG_ERROR("[ModbusTCP Client] 创建客户端实例失败\n");
return false;
}
m_modbusClient->SetTimeout(1000);
auto result = m_modbusClient->Connect();
if (result != IYModbusTCPClient::SUCCESS) {
LOG_WARNING("[ModbusTCP Client] 连接 %s:%d 失败: %s\n",
m_modbusClientIP.c_str(), m_modbusClientPort,
m_modbusClient->GetLastError().c_str());
return false;
}
LOG_INFO("[ModbusTCP Client] 已连接到 %s:%d\n", m_modbusClientIP.c_str(), m_modbusClientPort);
return true;
}
int BagThreadPositionPresenter::ReadScanRequest()
{
std::lock_guard<std::mutex> lock(m_modbusClientMutex);
if (!m_modbusClient || !m_modbusClient->IsConnected()) {
return -1;
}
std::vector<uint16_t> values;
auto result = m_modbusClient->ReadHoldingRegisters(0, 1, values);
if (result != IYModbusTCPClient::SUCCESS || values.empty()) {
return -1;
}
return static_cast<int>(values[0]);
}
bool BagThreadPositionPresenter::WriteRemoteRegisters(uint16_t startAddress, const std::vector<uint16_t>& values)
{
std::lock_guard<std::mutex> lock(m_modbusClientMutex);
if (!m_modbusClient || !m_modbusClient->IsConnected()) {
LOG_WARNING("[ModbusTCP Client] 未连接,无法写入寄存器\n");
return false;
}
auto result = m_modbusClient->WriteMultipleRegisters(startAddress, values);
if (result != IYModbusTCPClient::SUCCESS) {
LOG_ERROR("[ModbusTCP Client] 写入寄存器失败(地址=%d数量=%zu: %s\n",
startAddress, values.size(), m_modbusClient->GetLastError().c_str());
return false;
}
LOG_DEBUG("[ModbusTCP Client] 写入寄存器成功:地址=%d数量=%zu\n", startAddress, values.size());
return true;
}
bool BagThreadPositionPresenter::WriteRemoteSingleRegister(uint16_t address, uint16_t value)
{
std::lock_guard<std::mutex> lock(m_modbusClientMutex);
if (!m_modbusClient || !m_modbusClient->IsConnected()) {
LOG_WARNING("[ModbusTCP Client] 未连接,无法写入寄存器\n");
return false;
}
auto result = m_modbusClient->WriteSingleRegister(address, value);
if (result != IYModbusTCPClient::SUCCESS) {
LOG_ERROR("[ModbusTCP Client] 写入单寄存器失败(地址=%d: %s\n",
address, m_modbusClient->GetLastError().c_str());
return false;
}
LOG_DEBUG("[ModbusTCP Client] 写入单寄存器成功:地址=%d值=%d\n", address, value);
return true;
}
bool BagThreadPositionPresenter::IsModbusClientConnected() const
{
std::lock_guard<std::mutex> lock(m_modbusClientMutex);
return m_modbusClient && m_modbusClient->IsConnected();
}
// ============ 辅助函数 ============
/**
* @brief float
* @param value float
* @param regs 2uint16_t
* @param bigEndian true=false=
*/
void BagThreadPositionPresenter::_FloatToRegisters(float value, uint16_t* regs, bool bigEndian)
{
// 将 float 转换为 4 字节
uint32_t intValue;
memcpy(&intValue, &value, sizeof(float));
if (bigEndian) {
// 大端:高位在前
regs[0] = static_cast<uint16_t>((intValue >> 16) & 0xFFFF); // 高16位
regs[1] = static_cast<uint16_t>(intValue & 0xFFFF); // 低16位
} else {
// 小端:低位在前
regs[0] = static_cast<uint16_t>(intValue & 0xFFFF); // 低16位
regs[1] = static_cast<uint16_t>((intValue >> 16) & 0xFFFF); // 高16位
}
}