2026-03-29 10:48:35 +08:00

740 lines
26 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 "RodAndBarPositionPresenter.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 <cstring>
#include "Version.h"
#include "VrTimeUtils.h"
#include "VrDateUtils.h"
#include "SG_baseDataType.h"
#include "VrConvert.h"
#include "DetectPresenter.h"
#include "PathManager.h"
RodAndBarPositionPresenter::RodAndBarPositionPresenter(QObject *parent)
: BasePresenter(parent)
, m_pConfigManager(nullptr)
, m_pDetectPresenter(nullptr)
{
// 基类已经创建了相机重连定时器和检测数据缓存
}
RodAndBarPositionPresenter::~RodAndBarPositionPresenter()
{
// 基类会自动处理:相机重连定时器、算法检测线程、检测数据缓存、相机设备资源
// 释放ConfigManager析构函数会自动调用Shutdown
if (m_pConfigManager) {
delete m_pConfigManager;
m_pConfigManager = nullptr;
}
// 释放检测处理器
if(m_pDetectPresenter)
{
delete m_pDetectPresenter;
m_pDetectPresenter = nullptr;
}
}
int RodAndBarPositionPresenter::InitApp()
{
LOG_DEBUG("Start APP Version: %s\n", RODANDBARPOSITION_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<IYRodAndBarPositionStatus>()) pStatus->OnStatusUpdate("配置管理器创建失败");
return ERR_CODE(DEV_CONFIG_ERR);
}
// 初始化 ConfigManager
if (!m_pConfigManager->Initialize()) {
LOG_ERROR("Failed to initialize ConfigManager\n");
if (auto pStatus = GetStatusCallback<IYRodAndBarPositionStatus>()) pStatus->OnStatusUpdate("配置管理器初始化失败");
return ERR_CODE(DEV_CONFIG_ERR);
}
LOG_INFO("Configuration loaded successfully\n");
// 获取配置结果
ConfigResult configResult = m_pConfigManager->GetConfigResult();
// 调用基类InitCamera进行相机初始化bRGB=false, bSwing=true
InitCamera(configResult.cameraList, false, true);
LOG_INFO("Camera initialization completed. Connected cameras: %zu, default camera index: %d\n",
m_vrEyeDeviceList.size(), m_currentCameraIndex);
if (auto pStatus = GetStatusCallback<IYRodAndBarPositionStatus>()) pStatus->OnStatusUpdate("设备初始化完成");
CheckAndUpdateWorkStatus();
if (auto pStatus = GetStatusCallback<IYRodAndBarPositionStatus>()) pStatus->OnStatusUpdate("配置初始化成功");
return SUCCESS;
}
// 初始化算法参数实现BasePresenter纯虚函数
int RodAndBarPositionPresenter::InitAlgoParams()
{
LOG_DEBUG("initializing algorithm parameters\n");
QString exePath = QCoreApplication::applicationFilePath();
// 清空现有的手眼标定矩阵列表
m_clibMatrixList.clear();
// 从 ConfigManager 获取配置结果
ConfigResult configResult = m_pConfigManager->GetConfigResult();
// 从 config.xml 加载手眼标定矩阵列表(支持多相机)
for (const auto& handEyeMatrix : configResult.handEyeCalibMatrixList) {
CalibMatrix calibMatrix;
memcpy(calibMatrix.clibMatrix, handEyeMatrix.matrix, sizeof(double) * 16);
m_clibMatrixList.push_back(calibMatrix);
LOG_INFO("Loaded hand-eye calibration matrix for camera %d:\n", handEyeMatrix.cameraIndex);
QString clibMatrixStr;
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());
}
}
LOG_INFO("Total loaded %zu hand-eye calibration matrices\n", m_clibMatrixList.size());
const VrAlgorithmParams& xmlParams = configResult.algorithmParams;
LOG_INFO("Loaded XML params - Rod: rodDiameter=%.1f, rodLen=%.1f\n",
xmlParams.rodParam.rodDiameter,
xmlParams.rodParam.rodLen);
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;
}
// 获取算法版本
QString RodAndBarPositionPresenter::GetAlgoVersion() const
{
return DetectPresenter::GetAlgoVersion();
}
// 手眼标定矩阵管理方法实现
CalibMatrix RodAndBarPositionPresenter::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 RodAndBarPositionPresenter::CheckAndUpdateWorkStatus()
{
if (m_bCameraConnected) {
SetWorkStatus(WorkStatus::Ready);
} else {
SetWorkStatus(WorkStatus::Error);
}
}
// 实现BasePresenter纯虚函数执行算法检测
int RodAndBarPositionPresenter::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<IYRodAndBarPositionStatus>()){
if (auto pStatus = GetStatusCallback<IYRodAndBarPositionStatus>()) pStatus->OnStatusUpdate("扫描线数:" + std::to_string(lineNum) + ",正在算法检测...");
}
// 检查检测处理器是否已初始化
if (!m_pDetectPresenter) {
LOG_ERROR("DetectPresenter is null, cannot proceed with detection\n");
if (GetStatusCallback<IYRodAndBarPositionStatus>()) {
if (auto pStatus = GetStatusCallback<IYRodAndBarPositionStatus>()) pStatus->OnStatusUpdate("检测处理器未初始化");
}
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;
int eulerOrder = configResult.eulerOrder;
const int calibIdx = m_currentCameraIndex - 1;
if (calibIdx >= 0 && calibIdx < static_cast<int>(configResult.handEyeCalibMatrixList.size())) {
eulerOrder = configResult.handEyeCalibMatrixList[calibIdx].eulerOrder;
} else if (!configResult.handEyeCalibMatrixList.empty()) {
eulerOrder = configResult.handEyeCalibMatrixList[0].eulerOrder;
}
const int dirVectorInvert = configResult.dirVectorInvert;
LOG_INFO("[Algo Thread] Using euler order: %d, dir vector invert: %d\n", eulerOrder, dirVectorInvert);
DetectionResult detectionResult;
int nRet = m_pDetectPresenter->DetectRod(m_currentCameraIndex, detectionDataCache,
algorithmParams, debugParam, m_dataLoader,
currentClibMatrix.clibMatrix, eulerOrder, dirVectorInvert,
detectionResult);
// 根据项目类型选择处理方式
if (GetStatusCallback<IYRodAndBarPositionStatus>()) {
QString err = QString("错误:%1").arg(nRet);
if (auto pStatus = GetStatusCallback<IYRodAndBarPositionStatus>()) pStatus->OnStatusUpdate(QString("检测%1").arg(SUCCESS == nRet ? "成功": err).toStdString());
}
LOG_INFO("[Algo Thread] sx_rodPositioning detected %zu objects time : %.2f ms\n", detectionResult.positions.size(), oTimeUtils.GetElapsedTimeInMilliSec());
ERR_CODE_RETURN(nRet);
// 8. 通知UI检测结果
detectionResult.cameraIndex = m_currentCameraIndex;
if (auto pStatus = GetStatusCallback<IYRodAndBarPositionStatus>()) {
pStatus->OnDetectionResult(detectionResult);
}
// 更新状态
QString statusMsg = QString("检测完成,发现%1个棒材").arg(detectionResult.positions.size());
if (auto pStatus = GetStatusCallback<IYRodAndBarPositionStatus>()) pStatus->OnStatusUpdate(statusMsg.toStdString());
// 缓存检测结果重置索引首次发送index=0下次写10从1开始
{
std::lock_guard<std::mutex> lock(m_resultCacheMutex);
m_lastDetectionResult = detectionResult;
m_resultIndex = 1;
}
// 发送第0个结果
_SendDetectionResultToModbus(detectionResult, m_currentCameraIndex, 0);
// 9. 检测完成后,将工作状态更新为"完成"
SetWorkStatus(WorkStatus::Completed);
// 恢复到就绪状态
SetWorkStatus(WorkStatus::Ready);
return SUCCESS;
}
// 实现配置改变通知接口
void RodAndBarPositionPresenter::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<IYRodAndBarPositionStatus>()) {
if (auto pStatus = GetStatusCallback<IYRodAndBarPositionStatus>()) pStatus->OnStatusUpdate("配置已更新,算法参数重新加载成功");
}
} else {
LOG_ERROR("Failed to reload algorithm parameters after config change, error: %d\n", result);
if (GetStatusCallback<IYRodAndBarPositionStatus>()) {
if (auto pStatus = GetStatusCallback<IYRodAndBarPositionStatus>()) pStatus->OnStatusUpdate("配置更新后算法参数重新加载失败");
}
}
}
// 根据相机索引获取调平参数
SSG_planeCalibPara RodAndBarPositionPresenter::_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 RodAndBarPositionPresenter::OnCameraStatusChanged(int cameraIndex, bool isConnected)
{
LOG_INFO("Camera %d status changed: %s\n", cameraIndex, isConnected ? "connected" : "disconnected");
// 通知UI更新相机状态
if (GetStatusCallback<IYRodAndBarPositionStatus>()) {
if (cameraIndex == 1) {
if (auto pStatus = GetStatusCallback<IYRodAndBarPositionStatus>()) pStatus->OnCamera1StatusChanged(isConnected);
} else if (cameraIndex == 2) {
if (auto pStatus = GetStatusCallback<IYRodAndBarPositionStatus>()) 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<IYRodAndBarPositionStatus>()) pStatus->OnStatusUpdate(statusMsg.toStdString());
}
// 检查并更新工作状态
CheckAndUpdateWorkStatus();
}
// 实现BasePresenter虚函数工作状态变化通知
void RodAndBarPositionPresenter::OnWorkStatusChanged(WorkStatus status)
{
auto pStatus = GetStatusCallback<IYRodAndBarPositionStatus>();
if (pStatus) {
pStatus->OnWorkStatusChanged(status);
}
}
// 实现BasePresenter虚函数相机数量变化通知
void RodAndBarPositionPresenter::OnCameraCountChanged(int count)
{
auto pStatus = GetStatusCallback<IYRodAndBarPositionStatus>();
if (pStatus) {
pStatus->OnCameraCountChanged(count);
}
}
// 实现BasePresenter虚函数状态文字更新通知
void RodAndBarPositionPresenter::OnStatusUpdate(const std::string& statusMessage)
{
auto pStatus = GetStatusCallback<IYRodAndBarPositionStatus>();
if (pStatus) {
pStatus->OnStatusUpdate(statusMessage);
}
}
// ============ 实现 ICameraLevelCalculator 接口 ============
bool RodAndBarPositionPresenter::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 RodAndBarPositionPresenter::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 RodAndBarPositionPresenter::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 RodAndBarPositionPresenter::DeinitApp()
{
LOG_DEBUG("Deinitializing RodAndBarPositionPresenter\n");
// 停止检测
StopDetection();
// 释放ConfigManager析构函数会自动调用Shutdown
if (m_pConfigManager) {
delete m_pConfigManager;
m_pConfigManager = nullptr;
}
// 释放检测处理器
if (m_pDetectPresenter) {
delete m_pDetectPresenter;
m_pDetectPresenter = nullptr;
}
LOG_DEBUG("RodAndBarPositionPresenter deinitialized\n");
}
// 触发检测
bool RodAndBarPositionPresenter::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 RodAndBarPositionPresenter::LoadAndDetect(const QString& fileName)
{
LOG_INFO("Loading data from file: %s\n", fileName.toStdString().c_str());
// 使用基类的方法加载调试数据并执行检测
return LoadDebugDataAndDetect(fileName.toStdString());
}
// 重连相机
void RodAndBarPositionPresenter::ReconnectCamera()
{
LOG_INFO("Attempting to reconnect cameras\n");
TryReconnectCameras();
}
// 获取算法参数
RodAndBarPositionPresenter::AlgoParams RodAndBarPositionPresenter::GetAlgoParams() const
{
AlgoParams params;
if (m_pConfigManager) {
VrAlgorithmParams algorithmParams = m_pConfigManager->GetAlgorithmParams();
params.rodParam = algorithmParams.rodParam;
params.cornerParam = algorithmParams.cornerParam;
params.filterParam = algorithmParams.filterParam;
params.growParam = algorithmParams.growParam;
} else {
// 使用默认参数
params.rodParam.rodDiameter = 52.0;
params.rodParam.rodLen = 290.0;
}
return params;
}
// 设置算法参数
void RodAndBarPositionPresenter::SetAlgoParams(const AlgoParams& params)
{
if (!m_pConfigManager) {
LOG_WARNING("ConfigManager not initialized, cannot set algorithm params\n");
return;
}
// 获取当前配置
VrAlgorithmParams algorithmParams = m_pConfigManager->GetAlgorithmParams();
// 更新参数
algorithmParams.rodParam = params.rodParam;
algorithmParams.cornerParam = params.cornerParam;
algorithmParams.filterParam = params.filterParam;
algorithmParams.growParam = params.growParam;
// 更新到ConfigManager
m_pConfigManager->UpdateAlgorithmParams(algorithmParams);
LOG_INFO("Algorithm parameters updated\n");
}
// ============ ModbusTCP Server 相关实现 ============
void RodAndBarPositionPresenter::OnModbusWriteCallback(uint16_t startAddress, const uint16_t* data, uint16_t count)
{
// 检测触发寄存器 (地址 0):写入非零值触发检测或读取缓存结果
if (startAddress == 0 && count >= 1 && data[0] != 0) {
int cmd = data[0]; // 1=触发相机1, 2=触发相机2, 10=读取下一个缓存结果
LOG_INFO("Modbus command received: cmd=%d\n", cmd);
// 清除触发寄存器
uint16_t clearValue = 0;
WriteModbusRegisters(0, &clearValue, 1);
if (cmd == 10) {
// 读取缓存中的下一个棒材结果
_SendNextCachedResult();
} else {
// 新检测触发,索引归零
{
std::lock_guard<std::mutex> lock(m_resultCacheMutex);
m_resultIndex = 0;
}
// 设置状态为"正在检测"
uint16_t detecting = 0;
WriteModbusRegisters(1, &detecting, 1);
// 触发对应相机检测
TriggerDetection(cmd);
}
}
}
void RodAndBarPositionPresenter::OnModbusServerStatusChanged(bool isConnected)
{
LOG_INFO("Modbus server status changed: %s\n", isConnected ? "connected" : "disconnected");
if (auto pStatus = GetStatusCallback<IYRodAndBarPositionStatus>()) {
pStatus->OnRobotConnectionChanged(isConnected);
}
}
void RodAndBarPositionPresenter::_SendDetectionResultToModbus(const DetectionResult& result, int cameraIndex, int index)
{
if (!IsModbusServerRunning()) {
LOG_WARNING("Modbus server not running, skip sending result\n");
return;
}
// 从配置获取字节序
ConfigResult configResult = m_pConfigManager->GetConfigResult();
bool bigEndian = (configResult.byteOrder == 0); // 0=大端序, 1=小端序
// 地址 1状态码0=正在检测1=成功2=失败3=结果为空/无更多结果)
uint16_t statusCode = 1; // 默认成功
if (!result.success) {
statusCode = 2; // 检测失败
} else if (result.positions.empty() || index >= (int)result.positions.size()) {
statusCode = 3; // 成功但结果为空或索引越界
}
WriteModbusRegisters(1, &statusCode, 1);
// 地址 2~13指定索引棒材的 XYZ + RPY6 个 float = 12 个寄存器)
if (statusCode == 1) {
const auto& pos = result.positions[index];
float values[6] = {
(float)pos.x, (float)pos.y, (float)pos.z,
(float)pos.roll, (float)pos.pitch, (float)pos.yaw
};
uint16_t regs[12];
memcpy(regs, values, sizeof(regs));
// 大端序时交换每个float的高低字序x86小端 → Modbus大端字序
if (bigEndian) {
for (int i = 0; i < 6; i++) {
std::swap(regs[i * 2], regs[i * 2 + 1]);
}
}
WriteModbusRegisters(2, regs, 12);
}
LOG_INFO("Detection result sent via Modbus, camera: %d, index: %d, status: %d\n",
cameraIndex, index, statusCode);
}
void RodAndBarPositionPresenter::_SendNextCachedResult()
{
if (!IsModbusServerRunning()) {
LOG_WARNING("Modbus server not running, skip sending cached result\n");
return;
}
std::lock_guard<std::mutex> lock(m_resultCacheMutex);
if (m_lastDetectionResult.positions.empty() ||
m_resultIndex >= (int)m_lastDetectionResult.positions.size()) {
// 无更多结果写状态码3
uint16_t statusCode = 3;
WriteModbusRegisters(1, &statusCode, 1);
LOG_INFO("No more cached results, index=%d, total=%zu\n",
m_resultIndex, m_lastDetectionResult.positions.size());
return;
}
LOG_INFO("Sending cached result index=%d, total=%zu\n",
m_resultIndex, m_lastDetectionResult.positions.size());
_SendDetectionResultToModbus(m_lastDetectionResult,
m_lastDetectionResult.cameraIndex,
m_resultIndex);
m_resultIndex++;
}