修改ModbusTCPServer的退出; 修改轮眉的协议
This commit is contained in:
parent
12a1a0e738
commit
5dda95edb9
@ -25,6 +25,7 @@
|
||||
| 18,19 | 下点X | R | FLOAT32 | 轮毂下点X坐标 (mm) |
|
||||
| 20,21 | 下点Y | R | FLOAT32 | 轮毂下点Y坐标 (mm) |
|
||||
| 22,23 | 下点Z | R | FLOAT32 | 轮毂下点Z坐标 (mm) |
|
||||
| 24,25 | 到地面高度 | R | FLOAT32 | 轮眉到地面高度 (mm) |
|
||||
|
||||
## FLOAT32 格式
|
||||
|
||||
@ -50,5 +51,5 @@
|
||||
```
|
||||
1. 写寄存器0 = 设备序号 (1-4)
|
||||
2. 读寄存器1 等待状态变为 4 (完成)
|
||||
3. 读地址2-23获取检测结果
|
||||
3. 读地址2-25获取检测结果
|
||||
```
|
||||
|
||||
@ -79,6 +79,11 @@
|
||||
<CURVE IsShow="0" />
|
||||
<VALUE Stategy="0" TimeBngrWay="0" Coef_a="0" Coef_b="0" Coef_c="0" Coef_d="0" Coef_e="0" ResetStep="0" XInvl="1000" MaxRadom="0" MinRadom="0" MaxValue="0" MinValue="0" />
|
||||
</DATA>
|
||||
<DATA Name="到地面高度" BLOCK="2" PrtcTYPE="2" Unit="mm" ShowType="0" Addr="24" Gain="1" Size="2" BitOffset="0" BitNum="32" Range="--" Color="" ByteOder="0" WordOder="0" IntervalTime="0" IsShow="1" IsBRead="1" IsBWrite="0">
|
||||
<TIMEOUT Time="2000" ResendCount="0" />
|
||||
<CURVE IsShow="0" />
|
||||
<VALUE Stategy="0" TimeBngrWay="0" Coef_a="0" Coef_b="0" Coef_c="0" Coef_d="0" Coef_e="0" ResetStep="0" XInvl="1000" MaxRadom="0" MinRadom="0" MaxValue="0" MinValue="0" />
|
||||
</DATA>
|
||||
</DATA_LIST>
|
||||
</DEVICE>
|
||||
</DEVICE_LIST>
|
||||
|
||||
@ -19,11 +19,11 @@ WheelMeasurePresenter::WheelMeasurePresenter(QObject* parent)
|
||||
{
|
||||
// 创建配置接口
|
||||
IVrWheelMeasureConfig::CreateInstance(&m_config);
|
||||
LOG_INFO("ALGO_VERSION: %s \n", wd_wheelArchHeigthMeasureVersion());
|
||||
}
|
||||
|
||||
WheelMeasurePresenter::~WheelMeasurePresenter()
|
||||
{
|
||||
// 释放配置接口
|
||||
if (m_config) {
|
||||
delete m_config;
|
||||
m_config = nullptr;
|
||||
@ -79,23 +79,27 @@ void WheelMeasurePresenter::OnCameraStatusChanged(int cameraIndex, bool isConnec
|
||||
{
|
||||
LOG_INFO("Camera[%d] status changed: %s\n", cameraIndex, isConnected ? "connected" : "disconnected");
|
||||
|
||||
if (m_statusUpdate) {
|
||||
// 从配置中获取相机名称(cameraIndex从1开始)
|
||||
QString cameraName;
|
||||
int enabledIndex = 0;
|
||||
for (const auto& cameraConfig : m_configResult.cameras) {
|
||||
if (cameraConfig.enabled) {
|
||||
enabledIndex++;
|
||||
if (enabledIndex == cameraIndex) {
|
||||
cameraName = QString::fromStdString(cameraConfig.name);
|
||||
break;
|
||||
}
|
||||
if (!m_statusUpdate) return;
|
||||
|
||||
// 从配置中获取相机名称(cameraIndex从1开始)
|
||||
QString cameraName;
|
||||
int enabledIndex = 0;
|
||||
for (const auto& cameraConfig : m_configResult.cameras) {
|
||||
if (cameraConfig.enabled) {
|
||||
enabledIndex++;
|
||||
if (enabledIndex == cameraIndex) {
|
||||
cameraName = QString::fromStdString(cameraConfig.name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (cameraName.isEmpty()) {
|
||||
cameraName = QString("Camera%1").arg(cameraIndex);
|
||||
}
|
||||
}
|
||||
if (cameraName.isEmpty()) {
|
||||
cameraName = QString("Camera%1").arg(cameraIndex);
|
||||
}
|
||||
|
||||
// 切换到主线程更新UI
|
||||
QMetaObject::invokeMethod(this, [this, cameraName, isConnected]() {
|
||||
if (!m_statusUpdate) return;
|
||||
if (isConnected) {
|
||||
m_statusUpdate->OnCameraConnected(cameraName);
|
||||
m_statusUpdate->OnDeviceStatusChanged(cameraName, static_cast<int>(DeviceStatus::Online));
|
||||
@ -103,13 +107,12 @@ void WheelMeasurePresenter::OnCameraStatusChanged(int cameraIndex, bool isConnec
|
||||
m_statusUpdate->OnCameraDisconnected(cameraName);
|
||||
m_statusUpdate->OnDeviceStatusChanged(cameraName, static_cast<int>(DeviceStatus::Offline));
|
||||
}
|
||||
}
|
||||
}, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void WheelMeasurePresenter::OnWorkStatusChanged(WorkStatus status)
|
||||
{
|
||||
// 写入Modbus工作状态 (地址1)
|
||||
// 0=空闲, 1=初始化中, 2=就绪, 3=检测中, 4=完成, 5=错误
|
||||
uint16_t statusValue = 0;
|
||||
switch (status) {
|
||||
case WorkStatus::InitIng: statusValue = 1; break;
|
||||
@ -122,32 +125,45 @@ void WheelMeasurePresenter::OnWorkStatusChanged(WorkStatus status)
|
||||
}
|
||||
WriteModbusRegisters(1, &statusValue, 1);
|
||||
|
||||
// 直接使用公用的 WorkStatus,不再需要转换
|
||||
if (m_statusUpdate) {
|
||||
m_statusUpdate->OnWorkStatusChanged(status);
|
||||
}
|
||||
// 切换到主线程更新UI
|
||||
QMetaObject::invokeMethod(this, [this, status]() {
|
||||
if (m_statusUpdate) {
|
||||
m_statusUpdate->OnWorkStatusChanged(status);
|
||||
}
|
||||
}, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void WheelMeasurePresenter::OnCameraCountChanged(int count)
|
||||
{
|
||||
if (m_statusUpdate) {
|
||||
// 使用配置中的相机名称,因为此时 m_vrEyeDeviceList 可能还未填充
|
||||
QStringList cameraNames;
|
||||
for (const auto& cameraConfig : m_configResult.cameras) {
|
||||
if (cameraConfig.enabled) {
|
||||
cameraNames.append(QString::fromStdString(cameraConfig.name));
|
||||
}
|
||||
if (!m_statusUpdate) return;
|
||||
|
||||
QStringList cameraNames;
|
||||
for (const auto& cameraConfig : m_configResult.cameras) {
|
||||
if (cameraConfig.enabled) {
|
||||
cameraNames.append(QString::fromStdString(cameraConfig.name));
|
||||
}
|
||||
LOG_INFO("OnCameraCountChanged: count=%d, cameraNames=%d\n", count, cameraNames.size());
|
||||
m_statusUpdate->OnNeedShowImageCount(cameraNames);
|
||||
}
|
||||
LOG_INFO("OnCameraCountChanged: count=%d, cameraNames=%d\n", count, cameraNames.size());
|
||||
|
||||
// 切换到主线程更新UI
|
||||
QMetaObject::invokeMethod(this, [this, cameraNames]() {
|
||||
if (m_statusUpdate) {
|
||||
m_statusUpdate->OnNeedShowImageCount(cameraNames);
|
||||
}
|
||||
}, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void WheelMeasurePresenter::OnStatusUpdate(const std::string& statusMessage)
|
||||
{
|
||||
if (m_statusUpdate) {
|
||||
m_statusUpdate->OnStatusUpdate(QString::fromStdString(statusMessage));
|
||||
}
|
||||
if (!m_statusUpdate) return;
|
||||
|
||||
QString msg = QString::fromStdString(statusMessage);
|
||||
// 切换到主线程更新UI
|
||||
QMetaObject::invokeMethod(this, [this, msg]() {
|
||||
if (m_statusUpdate) {
|
||||
m_statusUpdate->OnStatusUpdate(msg);
|
||||
}
|
||||
}, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
bool WheelMeasurePresenter::initializeConfig(const QString& configPath)
|
||||
@ -465,10 +481,15 @@ void WheelMeasurePresenter::processScanData(std::vector<std::pair<EVzResultDataT
|
||||
}
|
||||
groundCalibPara.planeHeight = calibParam->planeHeight;
|
||||
LOG_INFO("Using calibrated plane parameters for camera %d\n", m_currentCameraIndex);
|
||||
|
||||
LOG_INFO(" planeHeight: %.3f, errorCompensation: %.2f\n", calibParam->planeHeight, calibParam->errorCompensation);
|
||||
|
||||
if(calibParam){
|
||||
// 计算调平使用的地面高度(加上该相机的误差补偿)
|
||||
double adjustedPlaneHeight = groundCalibPara.planeHeight + calibParam->errorCompensation;
|
||||
LOG_INFO(" adjustedPlaneHeight (with compensation): %.3f\n", adjustedPlaneHeight);
|
||||
|
||||
for(size_t i = 0; i < scanLines.size(); i++){
|
||||
wd_horizonCamera_lineDataR(scanLines[i], calibParam->planeCalib, groundCalibPara.planeHeight);
|
||||
wd_horizonCamera_lineDataR(scanLines[i], calibParam->planeCalib, adjustedPlaneHeight);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -531,17 +552,18 @@ void WheelMeasurePresenter::processScanData(std::vector<std::pair<EVzResultDataT
|
||||
wheelArchResult.wheelDownPos.x,
|
||||
wheelArchResult.wheelDownPos.y,
|
||||
wheelArchResult.wheelDownPos.z);
|
||||
LOG_INFO("archToCenterHeigth: %.3f mm\n", wheelArchResult.archToCenterHeigth);
|
||||
LOG_INFO("archToCenterHeigth: %.3f archToGroundHeigth: %.3f mm\n", wheelArchResult.archToCenterHeigth, wheelArchResult.archToGroundHeigth);
|
||||
LOG_INFO("==================================================\n");
|
||||
|
||||
if (m_statusUpdate) {
|
||||
QString statusMsg = QString("轮眉高度: %1 mm").arg(wheelArchResult.archToCenterHeigth);
|
||||
QString statusMsg = QString("轮眉高度: %1 mm, 到地面高度: %2 mm").arg(wheelArchResult.archToCenterHeigth).arg(wheelArchResult.archToGroundHeigth);
|
||||
m_statusUpdate->OnStatusUpdate(statusMsg);
|
||||
}
|
||||
|
||||
// 填充测量结果数据
|
||||
WheelMeasureData measureData;
|
||||
measureData.archToCenterHeight = wheelArchResult.archToCenterHeigth;
|
||||
measureData.archToGroundHeight = wheelArchResult.archToGroundHeigth;
|
||||
measureData.wheelArchPosX = wheelArchResult.wheelArchPos.x;
|
||||
measureData.wheelArchPosY = wheelArchResult.wheelArchPos.y;
|
||||
measureData.wheelArchPosZ = wheelArchResult.wheelArchPos.z;
|
||||
@ -581,7 +603,7 @@ void WheelMeasurePresenter::processScanData(std::vector<std::pair<EVzResultDataT
|
||||
|
||||
result.bImageValid = !result.image.isNull();
|
||||
|
||||
// ========== 写入检测结果到 Modbus (地址2-23) ==========
|
||||
// ========== 写入检测结果到 Modbus (地址2-25) ==========
|
||||
// 辅助lambda: float转两个uint16_t (大端模式)
|
||||
auto floatToUint16 = [](float value, uint16_t& high, uint16_t& low) {
|
||||
uint32_t bits;
|
||||
@ -590,7 +612,7 @@ void WheelMeasurePresenter::processScanData(std::vector<std::pair<EVzResultDataT
|
||||
low = static_cast<uint16_t>(bits & 0xFFFF);
|
||||
};
|
||||
|
||||
uint16_t modbusData[22];
|
||||
uint16_t modbusData[24];
|
||||
memset(modbusData, 0, sizeof(modbusData));
|
||||
|
||||
// 地址2: 设备序号
|
||||
@ -599,7 +621,7 @@ void WheelMeasurePresenter::processScanData(std::vector<std::pair<EVzResultDataT
|
||||
modbusData[1] = (errCode == 0) ? 1 : 0;
|
||||
|
||||
if (errCode == 0) {
|
||||
// 地址4-5: 轮眉高度
|
||||
// 地址4-5: 轮眉高度(到中心)
|
||||
floatToUint16(static_cast<float>(wheelArchResult.archToCenterHeigth), modbusData[2], modbusData[3]);
|
||||
// 地址6-7: 轮眉X
|
||||
floatToUint16(static_cast<float>(wheelArchResult.wheelArchPos.x), modbusData[4], modbusData[5]);
|
||||
@ -619,10 +641,12 @@ void WheelMeasurePresenter::processScanData(std::vector<std::pair<EVzResultDataT
|
||||
floatToUint16(static_cast<float>(wheelArchResult.wheelDownPos.y), modbusData[18], modbusData[19]);
|
||||
// 地址22-23: 下点Z
|
||||
floatToUint16(static_cast<float>(wheelArchResult.wheelDownPos.z), modbusData[20], modbusData[21]);
|
||||
// 地址24-25: 到地面高度
|
||||
floatToUint16(static_cast<float>(wheelArchResult.archToGroundHeigth), modbusData[22], modbusData[23]);
|
||||
}
|
||||
|
||||
WriteModbusRegisters(2, modbusData, 22);
|
||||
LOG_INFO("Modbus: 写入检测结果到地址2-23, 设备=%d, 有效=%d\n", m_currentCameraIndex, modbusData[1]);
|
||||
WriteModbusRegisters(2, modbusData, 24);
|
||||
LOG_INFO("Modbus: 写入检测结果到地址2-25, 设备=%d, 有效=%d\n", m_currentCameraIndex, modbusData[1]);
|
||||
|
||||
// 检测完成后清零"检测控制"(地址0)
|
||||
uint16_t zero = 0;
|
||||
@ -638,7 +662,6 @@ void WheelMeasurePresenter::processScanData(std::vector<std::pair<EVzResultDataT
|
||||
// 如果正在进行顺序检测,继续检测下一个设备
|
||||
if (m_sequentialDetecting) {
|
||||
m_sequentialCurrentIndex++;
|
||||
// 使用 QMetaObject::invokeMethod 延迟执行,确保当前检测完全结束
|
||||
QMetaObject::invokeMethod(this, [this]() {
|
||||
continueSequentialDetection();
|
||||
}, Qt::QueuedConnection);
|
||||
|
||||
@ -2,9 +2,9 @@
|
||||
#define VERSION_H
|
||||
|
||||
|
||||
#define WHEELMEASURE_VERSION_STRING "1.0.0"
|
||||
#define WHEELMEASURE_BUILD_STRING "2"
|
||||
#define WHEELMEASURE_FULL_VERSION_STRING "V1.0.0.2"
|
||||
#define WHEELMEASURE_VERSION_STRING "1.0.1"
|
||||
#define WHEELMEASURE_BUILD_STRING "3"
|
||||
#define WHEELMEASURE_FULL_VERSION_STRING "V1.0.1.3"
|
||||
|
||||
// 获取版本信息的便捷函数
|
||||
inline const char* GetWheelMeasureVersion() {
|
||||
|
||||
@ -1,32 +1,8 @@
|
||||
# 1.0.2
|
||||
## build_3 2025-12-17
|
||||
1. 测试不同的工件
|
||||
2.
|
||||
|
||||
## build_2 2025-12-14
|
||||
1. 完善协议
|
||||
2. 显示结果精简和长度输出
|
||||
|
||||
## build_1 2025-12-11
|
||||
1. 解决ARM上页面刷新崩溃问题
|
||||
|
||||
## build_0 2025-12-11
|
||||
1. 更新算法
|
||||
|
||||
# 1.0.1 2025-11-08
|
||||
## build_2
|
||||
1. 修正开机相机重连【项目结构重构】
|
||||
|
||||
# 1.0.1 2025-12-31
|
||||
## build_1
|
||||
1. 更新算法,在应用日志中有算法版本: 1.1.0
|
||||
2. 修复在没有配置文件中无法连接相机问题
|
||||
2. 增加过滤参数
|
||||
|
||||
# 1.0.0 2025-11-02
|
||||
## build_1
|
||||
1. 修复相机打开点亮激光器
|
||||
2. 修复相机调平
|
||||
3. 相机参数配置失败提示指定的错误
|
||||
|
||||
## build_0
|
||||
1. 初始版本
|
||||
# 1.0.0 2025-12-27
|
||||
- 初始版本
|
||||
|
||||
|
||||
@ -66,6 +66,12 @@ void DialogCameraLevel::setConfig(IVrWheelMeasureConfig* config, WheelMeasureCon
|
||||
{
|
||||
m_pConfig = config;
|
||||
m_pConfigResult = configResult;
|
||||
|
||||
// 如果相机已经选择,重新加载当前相机的标定状态
|
||||
// 修复:打开页面时配置可能在相机列表之后设置,导致初始加载失败
|
||||
if (m_currentCameraIndex >= 0 && m_currentCameraIndex < static_cast<int>(m_cameraList.size())) {
|
||||
checkAndDisplayCalibrationStatus(m_currentCameraIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void DialogCameraLevel::initializeCameraCombo()
|
||||
@ -182,9 +188,7 @@ bool DialogCameraLevel::performCameraLeveling()
|
||||
// 获取选中的相机索引
|
||||
int selectedIndex = ui->combo_camera->currentIndex();
|
||||
|
||||
// 1. 设置调平状态回调
|
||||
setLevelingStatusCallback();
|
||||
|
||||
// 先检查索引有效性,再设置回调
|
||||
if (selectedIndex < 0 || selectedIndex >= static_cast<int>(m_cameraList.size())) {
|
||||
LOG_ERROR("Invalid camera index: %d\n", selectedIndex);
|
||||
return false;
|
||||
@ -192,12 +196,16 @@ bool DialogCameraLevel::performCameraLeveling()
|
||||
|
||||
LOG_INFO("Performing camera leveling with camera %d (index %d)\n", selectedIndex + 1, selectedIndex);
|
||||
|
||||
// 1. 设置调平状态回调(在索引检查之后)
|
||||
setLevelingStatusCallback();
|
||||
|
||||
// 2. 清空之前的扫描数据
|
||||
clearScanDataCache();
|
||||
|
||||
// 3. 启动相机扫描地面数据
|
||||
if (!startCameraScan(selectedIndex)) {
|
||||
LOG_ERROR("Failed to start camera scan for leveling\n");
|
||||
restorePresenterStatusCallback(); // 恢复回调
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -229,6 +237,7 @@ bool DialogCameraLevel::performCameraLeveling()
|
||||
|
||||
if (!calculatePlaneCalibration(planeCalib, planeHeight, invRMatrix)) {
|
||||
LOG_ERROR("Failed to calculate plane calibration\n");
|
||||
restorePresenterStatusCallback(); // 恢复回调
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -249,16 +258,22 @@ bool DialogCameraLevel::performCameraLeveling()
|
||||
|
||||
if (!saveLevelingResults(planeCalib, planeHeight, invRMatrix, cameraIndex, cameraName)) {
|
||||
LOG_ERROR("Failed to save leveling results\n");
|
||||
restorePresenterStatusCallback(); // 恢复回调
|
||||
return false;
|
||||
}
|
||||
|
||||
clearScanDataCache();
|
||||
|
||||
// 9. 调平完成后恢复回调
|
||||
restorePresenterStatusCallback();
|
||||
|
||||
LOG_INFO("Camera leveling completed successfully\n");
|
||||
|
||||
return true;
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR("Exception in performCameraLeveling: %s\n", e.what());
|
||||
restorePresenterStatusCallback(); // 异常时也恢复回调
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -471,12 +486,16 @@ bool DialogCameraLevel::saveLevelingResults(double planeCalib[9], double planeHe
|
||||
return false;
|
||||
}
|
||||
|
||||
// 获取UI中的误差补偿值
|
||||
double errorCompensation = ui->edit_error_compensation->text().toDouble();
|
||||
|
||||
// 创建或更新相机调平参数
|
||||
WheelCameraPlaneCalibParam cameraParam;
|
||||
cameraParam.cameraIndex = cameraIndex;
|
||||
cameraParam.cameraName = cameraName.toStdString();
|
||||
cameraParam.planeHeight = planeHeight;
|
||||
cameraParam.isCalibrated = true;
|
||||
cameraParam.errorCompensation = errorCompensation;
|
||||
|
||||
// 复制校准矩阵
|
||||
for (int i = 0; i < 9; i++) {
|
||||
@ -508,7 +527,7 @@ bool DialogCameraLevel::saveLevelingResults(double planeCalib[9], double planeHe
|
||||
|
||||
LOG_INFO("Leveling results saved successfully for camera %d (%s)\n",
|
||||
cameraIndex, cameraName.toUtf8().constData());
|
||||
LOG_INFO("Plane height: %.3f\n", planeHeight);
|
||||
LOG_INFO("Plane height: %.3f, Error compensation: %.2f\n", planeHeight, errorCompensation);
|
||||
|
||||
return true;
|
||||
|
||||
@ -536,12 +555,18 @@ bool DialogCameraLevel::loadCameraCalibrationData(int cameraIndex, const QString
|
||||
}
|
||||
planeHeight = param.planeHeight;
|
||||
|
||||
// 加载该相机的误差补偿值到UI
|
||||
ui->edit_error_compensation->setText(QString::number(param.errorCompensation, 'f', 1));
|
||||
|
||||
LOG_INFO("Calibration data loaded successfully for camera %d (%s)\n",
|
||||
cameraIndex, cameraName.toUtf8().constData());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 没有找到标定数据时,设置默认误差补偿值
|
||||
ui->edit_error_compensation->setText(QString::number(-5.0, 'f', 1));
|
||||
|
||||
LOG_INFO("No calibration data found for camera %d (%s)\n",
|
||||
cameraIndex, cameraName.toUtf8().constData());
|
||||
return false;
|
||||
@ -627,3 +652,54 @@ void DialogCameraLevel::restorePresenterStatusCallback()
|
||||
|
||||
LOG_INFO("Presenter status callback restored for all cameras\n");
|
||||
}
|
||||
|
||||
void DialogCameraLevel::on_btn_save_compensation_clicked()
|
||||
{
|
||||
// 保存误差补偿值到当前相机的调平参数
|
||||
if (!m_pConfig || !m_pConfigResult) {
|
||||
LOG_ERROR("Config is null, cannot save error compensation\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_currentCameraIndex < 0 || m_currentCameraIndex >= static_cast<int>(m_cameraList.size())) {
|
||||
LOG_WARNING("Invalid camera index: %d\n", m_currentCameraIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取UI中的误差补偿值
|
||||
double errorCompensation = ui->edit_error_compensation->text().toDouble();
|
||||
int cameraIndex = m_currentCameraIndex + 1; // 转换为1-based索引
|
||||
QString cameraName = QString::fromStdString(m_cameraList[m_currentCameraIndex].first);
|
||||
|
||||
LOG_INFO("Saving error compensation for camera %d (%s): %.2f\n",
|
||||
cameraIndex, cameraName.toUtf8().constData(), errorCompensation);
|
||||
|
||||
// 查找或创建相机调平参数
|
||||
bool found = false;
|
||||
for (auto& param : m_pConfigResult->planeCalibParams) {
|
||||
if (param.cameraIndex == cameraIndex) {
|
||||
param.errorCompensation = errorCompensation;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有找到现有记录,创建一个新的(仅包含误差补偿,其他值为默认)
|
||||
if (!found) {
|
||||
WheelCameraPlaneCalibParam newParam;
|
||||
newParam.cameraIndex = cameraIndex;
|
||||
newParam.cameraName = cameraName.toStdString();
|
||||
newParam.errorCompensation = errorCompensation;
|
||||
newParam.isCalibrated = false; // 尚未标定
|
||||
m_pConfigResult->planeCalibParams.push_back(newParam);
|
||||
}
|
||||
|
||||
// 保存配置到文件
|
||||
QString configPath = PathManager::GetInstance().GetConfigFilePath();
|
||||
bool saveResult = m_pConfig->SaveConfig(configPath.toStdString(), *m_pConfigResult);
|
||||
if (saveResult) {
|
||||
LOG_INFO("Error compensation saved successfully for camera %d: %.2f\n", cameraIndex, errorCompensation);
|
||||
} else {
|
||||
LOG_ERROR("Failed to save error compensation\n");
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,6 +48,7 @@ private slots:
|
||||
void on_btn_apply_clicked();
|
||||
void on_btn_cancel_clicked();
|
||||
void on_combo_camera_currentIndexChanged(int index);
|
||||
void on_btn_save_compensation_clicked();
|
||||
|
||||
private:
|
||||
Ui::DialogCameraLevel *ui;
|
||||
|
||||
@ -180,6 +180,111 @@ background-color: rgb(47, 48, 52);</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_error_compensation">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>16</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">color: rgb(221, 225, 233);</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>误差补偿:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="edit_error_compensation">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>35</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>16</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">color: rgb(221, 225, 233);
|
||||
background-color: rgb(47, 48, 52);</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>-5.0</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_unit">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>14</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">color: rgb(221, 225, 233);</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>mm</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_save_compensation">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>60</width>
|
||||
<height>35</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>60</width>
|
||||
<height>35</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>14</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QPushButton {
|
||||
color: rgb(221, 225, 233);
|
||||
background-color: rgb(60, 63, 65);
|
||||
border: 1px solid #3B3D47;
|
||||
border-radius: 4px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: rgb(80, 83, 85);
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: rgb(45, 48, 50);
|
||||
}</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>保存</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
|
||||
@ -78,12 +78,10 @@ MainWindow::MainWindow(QWidget *parent)
|
||||
connect(m_presenter, &WheelMeasurePresenter::configUpdated, this, &MainWindow::onConfigSaved);
|
||||
|
||||
// 连接设备点击信号到重新检测槽函数
|
||||
connect(m_deviceStatusWidget, &DeviceStatusWidget::deviceClicked,
|
||||
this, &MainWindow::onDeviceClicked);
|
||||
connect(m_deviceStatusWidget, &DeviceStatusWidget::deviceClicked, this, &MainWindow::onDeviceClicked);
|
||||
|
||||
// 连接图像右键点击信号到保存数据槽函数
|
||||
connect(m_gridView, &ImageGridWidget::tileRightClicked,
|
||||
this, &MainWindow::onTileRightClicked);
|
||||
connect(m_gridView, &ImageGridWidget::tileRightClicked, this, &MainWindow::onTileRightClicked);
|
||||
|
||||
// 设置版本信息显示
|
||||
setupVersionDisplay();
|
||||
@ -100,6 +98,11 @@ MainWindow::MainWindow(QWidget *parent)
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
// 先清除回调,防止后台线程继续调用
|
||||
if (m_presenter) {
|
||||
m_presenter->setStatusUpdate(nullptr);
|
||||
}
|
||||
|
||||
if (m_gridView) {
|
||||
delete m_gridView;
|
||||
m_gridView = nullptr;
|
||||
@ -141,6 +144,7 @@ MainWindow::~MainWindow()
|
||||
}
|
||||
|
||||
delete ui;
|
||||
LOG_DEBUG("~MainWindow finish \n");
|
||||
}
|
||||
|
||||
void MainWindow::resizeEvent(QResizeEvent* event)
|
||||
@ -353,9 +357,9 @@ void MainWindow::onDeviceClicked(const QString& deviceName)
|
||||
|
||||
void MainWindow::onConfigSaved()
|
||||
{
|
||||
if (m_presenter) {
|
||||
m_presenter->Init();
|
||||
}
|
||||
// 配置已在 WheelMeasurePresenter::OnConfigChanged 中更新
|
||||
// 算法参数和调平参数的修改不需要重新初始化相机
|
||||
LOG_INFO("Config saved, configuration updated in memory\n");
|
||||
}
|
||||
|
||||
void MainWindow::onTileRightClicked(int index, const QString& alias)
|
||||
|
||||
@ -322,9 +322,9 @@ border: none;</string>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>1340</x>
|
||||
<y>760</y>
|
||||
<y>800</y>
|
||||
<width>560</width>
|
||||
<height>230</height>
|
||||
<height>190</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
@ -347,7 +347,7 @@ border: none;</string>
|
||||
<x>1340</x>
|
||||
<y>230</y>
|
||||
<width>560</width>
|
||||
<height>520</height>
|
||||
<height>560</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
|
||||
@ -22,29 +22,29 @@ void DeviceResultCard::setupUI()
|
||||
" background-color: rgb(47, 48, 52); "
|
||||
" border: 1px solid rgb(60, 62, 68); "
|
||||
" border-radius: 8px; "
|
||||
" padding: 10px; "
|
||||
" padding: 6px; "
|
||||
"} "
|
||||
"DeviceResultCard:hover { "
|
||||
" border: 1px solid rgb(80, 82, 88); "
|
||||
"}"
|
||||
);
|
||||
|
||||
setMinimumHeight(120);
|
||||
setMaximumHeight(160);
|
||||
setMinimumHeight(130);
|
||||
setMaximumHeight(170);
|
||||
|
||||
// 主布局:水平布局(左侧名称 | 右侧数据)
|
||||
QHBoxLayout* mainLayout = new QHBoxLayout(this);
|
||||
mainLayout->setContentsMargins(12, 8, 12, 8);
|
||||
mainLayout->setSpacing(15);
|
||||
mainLayout->setContentsMargins(8, 4, 8, 4);
|
||||
mainLayout->setSpacing(10);
|
||||
|
||||
// 左侧:设备名称和状态
|
||||
QVBoxLayout* leftLayout = new QVBoxLayout();
|
||||
leftLayout->setSpacing(4);
|
||||
leftLayout->setSpacing(2);
|
||||
|
||||
m_nameLabel = new QLabel(m_deviceName, this);
|
||||
m_nameLabel->setStyleSheet("QLabel { color: rgb(221, 225, 233); font-size: 16px; background: transparent; }");
|
||||
m_nameLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
||||
m_nameLabel->setMinimumWidth(80);
|
||||
m_nameLabel->setMinimumWidth(60);
|
||||
|
||||
m_statusLabel = new QLabel(QString::fromUtf8("等待检测"), this);
|
||||
m_statusLabel->setStyleSheet("QLabel { color: rgb(150, 150, 150); font-size: 16px; background: transparent; }");
|
||||
@ -65,21 +65,25 @@ void DeviceResultCard::setupUI()
|
||||
|
||||
// 右侧:纵向显示数据
|
||||
QVBoxLayout* rightLayout = new QVBoxLayout();
|
||||
rightLayout->setSpacing(2);
|
||||
rightLayout->setSpacing(4);
|
||||
|
||||
// 拱高
|
||||
m_heightLabel = new QLabel(QString::fromUtf8("拱高: -- mm"), this);
|
||||
m_heightLabel->setStyleSheet("QLabel { color: rgb(100, 200, 100); font-size: 16px; background: transparent; }");
|
||||
|
||||
// 到地面高度
|
||||
m_groundHeightLabel = new QLabel(QString::fromUtf8("到地面: -- mm"), this);
|
||||
m_groundHeightLabel->setStyleSheet("QLabel { color: rgb(100, 180, 220); font-size: 16px; background: transparent; }");
|
||||
|
||||
// 拱点位置
|
||||
m_archPosLabel = new QLabel(QString::fromUtf8("拱点: X:-- Y:-- Z:--"), this);
|
||||
m_archPosLabel->setStyleSheet("QLabel { color: rgb(180, 180, 180); font-size: 14px; background: transparent; }");
|
||||
|
||||
// 上位置
|
||||
// 上点位置
|
||||
m_upPosLabel = new QLabel(QString::fromUtf8("上点: X:-- Y:-- Z:--"), this);
|
||||
m_upPosLabel->setStyleSheet("QLabel { color: rgb(180, 180, 180); font-size: 14px; background: transparent; }");
|
||||
|
||||
// 下位置
|
||||
// 下点位置
|
||||
m_downPosLabel = new QLabel(QString::fromUtf8("下点: X:-- Y:-- Z:--"), this);
|
||||
m_downPosLabel->setStyleSheet("QLabel { color: rgb(180, 180, 180); font-size: 14px; background: transparent; }");
|
||||
|
||||
@ -89,6 +93,7 @@ void DeviceResultCard::setupUI()
|
||||
|
||||
rightLayout->addStretch();
|
||||
rightLayout->addWidget(m_heightLabel);
|
||||
rightLayout->addWidget(m_groundHeightLabel);
|
||||
rightLayout->addWidget(m_archPosLabel);
|
||||
rightLayout->addWidget(m_upPosLabel);
|
||||
rightLayout->addWidget(m_downPosLabel);
|
||||
@ -105,6 +110,10 @@ void DeviceResultCard::updateResult(const WheelMeasureData& data, bool isValid)
|
||||
m_heightLabel->setText(QString::fromUtf8("拱高: %1 mm").arg(data.archToCenterHeight, 0, 'f', 2));
|
||||
m_heightLabel->setStyleSheet("QLabel { color: rgb(100, 200, 100); font-size: 16px; background: transparent; }");
|
||||
|
||||
// 更新到地面高度
|
||||
m_groundHeightLabel->setText(QString::fromUtf8("到地面: %1 mm").arg(data.archToGroundHeight, 0, 'f', 2));
|
||||
m_groundHeightLabel->setStyleSheet("QLabel { color: rgb(100, 180, 220); font-size: 16px; background: transparent; }");
|
||||
|
||||
// 更新拱点位置
|
||||
QString archPosText = QString::fromUtf8("拱点: X:%1 Y:%2 Z:%3")
|
||||
.arg(data.wheelArchPosX, 0, 'f', 2)
|
||||
@ -112,14 +121,14 @@ void DeviceResultCard::updateResult(const WheelMeasureData& data, bool isValid)
|
||||
.arg(data.wheelArchPosZ, 0, 'f', 2);
|
||||
m_archPosLabel->setText(archPosText);
|
||||
|
||||
// 更新上位置
|
||||
// 更新上点位置
|
||||
QString upPosText = QString::fromUtf8("上点: X:%1 Y:%2 Z:%3")
|
||||
.arg(data.wheelUpPosX, 0, 'f', 2)
|
||||
.arg(data.wheelUpPosY, 0, 'f', 2)
|
||||
.arg(data.wheelUpPosZ, 0, 'f', 2);
|
||||
m_upPosLabel->setText(upPosText);
|
||||
|
||||
// 更新下位置
|
||||
// 更新下点位置
|
||||
QString downPosText = QString::fromUtf8("下点: X:%1 Y:%2 Z:%3")
|
||||
.arg(data.wheelDownPosX, 0, 'f', 2)
|
||||
.arg(data.wheelDownPosY, 0, 'f', 2)
|
||||
@ -140,6 +149,9 @@ void DeviceResultCard::updateResult(const WheelMeasureData& data, bool isValid)
|
||||
m_heightLabel->setText(QString::fromUtf8("拱高: -- mm"));
|
||||
m_heightLabel->setStyleSheet("QLabel { color: rgb(200, 100, 100); font-size: 16px; background: transparent; }");
|
||||
|
||||
m_groundHeightLabel->setText(QString::fromUtf8("到地面: -- mm"));
|
||||
m_groundHeightLabel->setStyleSheet("QLabel { color: rgb(200, 100, 100); font-size: 16px; background: transparent; }");
|
||||
|
||||
m_archPosLabel->setText(QString::fromUtf8("拱点: X:-- Y:-- Z:--"));
|
||||
m_upPosLabel->setText(QString::fromUtf8("上点: X:-- Y:-- Z:--"));
|
||||
m_downPosLabel->setText(QString::fromUtf8("下点: X:-- Y:-- Z:--"));
|
||||
@ -156,6 +168,8 @@ void DeviceResultCard::clearResult()
|
||||
{
|
||||
m_heightLabel->setText(QString::fromUtf8("拱高: -- mm"));
|
||||
m_heightLabel->setStyleSheet("QLabel { color: rgb(150, 150, 150); font-size: 16px; background: transparent; }");
|
||||
m_groundHeightLabel->setText(QString::fromUtf8("到地面: -- mm"));
|
||||
m_groundHeightLabel->setStyleSheet("QLabel { color: rgb(150, 150, 150); font-size: 16px; background: transparent; }");
|
||||
m_archPosLabel->setText(QString::fromUtf8("拱点: X:-- Y:-- Z:--"));
|
||||
m_upPosLabel->setText(QString::fromUtf8("上点: X:-- Y:-- Z:--"));
|
||||
m_downPosLabel->setText(QString::fromUtf8("下点: X:-- Y:-- Z:--"));
|
||||
|
||||
@ -31,9 +31,10 @@ private:
|
||||
QString m_deviceName;
|
||||
QLabel* m_nameLabel = nullptr;
|
||||
QLabel* m_heightLabel = nullptr;
|
||||
QLabel* m_groundHeightLabel = nullptr; // 到地面高度
|
||||
QLabel* m_archPosLabel = nullptr; // 拱点位置
|
||||
QLabel* m_upPosLabel = nullptr; // 上位置
|
||||
QLabel* m_downPosLabel = nullptr; // 下位置
|
||||
QLabel* m_upPosLabel = nullptr; // 上点位置
|
||||
QLabel* m_downPosLabel = nullptr; // 下点位置
|
||||
QLabel* m_timestampLabel = nullptr;
|
||||
QLabel* m_statusLabel = nullptr;
|
||||
};
|
||||
|
||||
@ -60,6 +60,7 @@ struct WheelCameraPlaneCalibParam
|
||||
double planeHeight = -1; // 地面高度
|
||||
double invRMatrix[9] = {1, 0, 0, 0, 1, 0, 0, 0, 1}; // 逆旋转矩阵(回到原坐标系)
|
||||
bool isCalibrated = false; // 是否已标定
|
||||
double errorCompensation = -5.0; // 误差补偿(mm)
|
||||
};
|
||||
|
||||
/**
|
||||
@ -154,6 +155,7 @@ struct WheelMeasureData
|
||||
{
|
||||
int id = 0; // 测量ID
|
||||
double archToCenterHeight = 0.0; // 拱高到中心的高度
|
||||
double archToGroundHeight = 0.0; // 拱高到地面的高度
|
||||
double wheelArchPosX = 0.0; // 拱点X坐标
|
||||
double wheelArchPosY = 0.0; // 拱点Y坐标
|
||||
double wheelArchPosZ = 0.0; // 拱点Z坐标
|
||||
|
||||
@ -84,6 +84,12 @@ WheelMeasureConfigResult VrWheelMeasureConfig::LoadConfig(const std::string& fil
|
||||
calibParam.cameraName = xml.attributes().value("name").toString().toStdString();
|
||||
calibParam.planeHeight = xml.attributes().value("planeHeight").toDouble();
|
||||
calibParam.isCalibrated = xml.attributes().value("isCalibrated").toInt() != 0;
|
||||
// 读取误差补偿参数,默认值为-5.0
|
||||
if (xml.attributes().hasAttribute("errorCompensation")) {
|
||||
calibParam.errorCompensation = xml.attributes().value("errorCompensation").toDouble();
|
||||
} else {
|
||||
calibParam.errorCompensation = -5.0;
|
||||
}
|
||||
|
||||
// 读取planeCalib矩阵
|
||||
QString planeCalibStr = xml.attributes().value("planeCalib").toString();
|
||||
@ -321,6 +327,7 @@ bool VrWheelMeasureConfig::SaveConfig(const std::string& filePath, WheelMeasureC
|
||||
xml.writeAttribute("name", QString::fromStdString(calibParam.cameraName));
|
||||
xml.writeAttribute("planeHeight", QString::number(calibParam.planeHeight, 'f', 6));
|
||||
xml.writeAttribute("isCalibrated", QString::number(calibParam.isCalibrated ? 1 : 0));
|
||||
xml.writeAttribute("errorCompensation", QString::number(calibParam.errorCompensation, 'f', 2));
|
||||
|
||||
// 保存planeCalib矩阵
|
||||
QStringList planeCalibList;
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
<!-- 相机调平参数 -->
|
||||
<PlaneCalibParams>
|
||||
<!-- 相机1的调平参数(默认单位矩阵,未校准状态) -->
|
||||
<CameraCalib index="1" name="Camera1" planeHeight="554.0" isCalibrated="1"
|
||||
<CameraCalib index="1" name="Camera1" planeHeight="554.0" isCalibrated="1" errorCompensation="-5.00"
|
||||
planeCalib="1.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,1.0"
|
||||
invRMatrix="1.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,1.0"/>
|
||||
</PlaneCalibParams>
|
||||
|
||||
@ -15,6 +15,7 @@ typedef struct
|
||||
SVzNL3DPoint downLine[2];
|
||||
SVzNL3DPoint centerLine[2];
|
||||
double archToCenterHeigth;
|
||||
double archToGroundHeigth;
|
||||
}WD_wheelArchInfo;
|
||||
|
||||
//读版本号
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -509,6 +509,9 @@ protected:
|
||||
// ModbusTCP服务器
|
||||
IYModbusTCPServer* m_modbusServer = nullptr;
|
||||
|
||||
// 初始化线程
|
||||
std::thread m_initThread;
|
||||
|
||||
private:
|
||||
// 启动ModbusTCP服务(在Init中调用)
|
||||
int StartModbusServer(int port = 502);
|
||||
|
||||
@ -22,8 +22,10 @@ BasePresenter::BasePresenter(QObject *parent)
|
||||
|
||||
BasePresenter::~BasePresenter()
|
||||
{
|
||||
// 停止ModbusTCP服务器
|
||||
StopModbusServer();
|
||||
// 等待初始化线程完成
|
||||
if (m_initThread.joinable()) {
|
||||
m_initThread.join();
|
||||
}
|
||||
|
||||
// 停止检测线程
|
||||
StopAlgoDetectThread();
|
||||
@ -31,8 +33,12 @@ BasePresenter::~BasePresenter()
|
||||
// 停止重连定时器
|
||||
StopCameraReconnectTimer();
|
||||
|
||||
// 停止ModbusTCP服务器
|
||||
StopModbusServer();
|
||||
|
||||
// 清理相机设备
|
||||
for (auto& camera : m_vrEyeDeviceList) {
|
||||
for (size_t i = 0; i < m_vrEyeDeviceList.size(); ++i) {
|
||||
auto& camera = m_vrEyeDeviceList[i];
|
||||
if (camera.second) {
|
||||
camera.second->CloseDevice();
|
||||
delete camera.second;
|
||||
@ -46,24 +52,30 @@ int BasePresenter::Init()
|
||||
{
|
||||
LOG_INFO("BasePresenter::Init()\n");
|
||||
|
||||
int nRet = SUCCESS;
|
||||
// 在后台线程中执行初始化
|
||||
m_initThread = std::thread([this]() {
|
||||
int nRet = InitApp();
|
||||
if (nRet != SUCCESS) {
|
||||
LOG_ERROR("InitApp failed: %d\n", nRet);
|
||||
return;
|
||||
}
|
||||
|
||||
nRet = InitApp();
|
||||
ERR_CODE_RETURN(nRet);
|
||||
// 初始化算法参数
|
||||
nRet = InitAlgoParams();
|
||||
LOG_INFO("Algorithm parameters initialization result: %d\n", nRet);
|
||||
|
||||
// 初始化算法参数
|
||||
nRet = InitAlgoParams();
|
||||
LOG_INFO("Algorithm parameters initialization result : %d\n", nRet);
|
||||
// 启动ModbusTCP服务
|
||||
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");
|
||||
}
|
||||
|
||||
// 启动ModbusTCP服务
|
||||
int modbusRet = StartModbusServer(5020);
|
||||
if (modbusRet == SUCCESS) {
|
||||
LOG_INFO("ModbusTCP server started on port 502(0)\n");
|
||||
} else {
|
||||
LOG_WARNING("Failed to start ModbusTCP server\n");
|
||||
}
|
||||
SetWorkStatus(WorkStatus::Ready);
|
||||
});
|
||||
|
||||
return nRet;
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
int BasePresenter::StartDetection(int cameraIndex, bool isAuto)
|
||||
@ -462,9 +474,8 @@ void BasePresenter::StartAlgoDetectThread()
|
||||
|
||||
m_bAlgoDetectThreadRunning = true;
|
||||
|
||||
// 启动检测线程
|
||||
// 启动检测线程(不再detach,使用joinable线程)
|
||||
m_algoDetectThread = std::thread(&BasePresenter::AlgoDetectThreadFunc, this);
|
||||
m_algoDetectThread.detach(); // 分离线程,让其独立运行
|
||||
|
||||
LOG_INFO("[BasePresenter] 算法检测线程已启动\n");
|
||||
}
|
||||
@ -482,7 +493,10 @@ void BasePresenter::StopAlgoDetectThread()
|
||||
// 唤醒可能在等待的线程
|
||||
m_algoDetectCondition.notify_all();
|
||||
|
||||
// 注意:因为线程使用了 detach(),所以不需要 join()
|
||||
// 等待线程退出
|
||||
if (m_algoDetectThread.joinable()) {
|
||||
m_algoDetectThread.join();
|
||||
}
|
||||
|
||||
LOG_INFO("[BasePresenter] 算法检测线程已停止\n");
|
||||
}
|
||||
@ -628,29 +642,23 @@ void BasePresenter::StartCameraReconnectTimer()
|
||||
// 使用QMetaObject::invokeMethod确保在正确的线程中操作定时器
|
||||
QMetaObject::invokeMethod(this, [this]() {
|
||||
if (!m_pCameraReconnectTimer) {
|
||||
LOG_ERROR("[BasePresenter] m_pCameraReconnectTimer is nullptr!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_pCameraReconnectTimer->isActive()) {
|
||||
LOG_DEBUG("[BasePresenter] 相机重连定时器已在运行\n");
|
||||
return;
|
||||
}
|
||||
|
||||
m_pCameraReconnectTimer->start();
|
||||
LOG_INFO("[BasePresenter] 启动相机重连定时器(间隔: %d ms)\n", m_pCameraReconnectTimer->interval());
|
||||
}, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void BasePresenter::StopCameraReconnectTimer()
|
||||
{
|
||||
// 使用QMetaObject::invokeMethod确保在正确的线程中操作定时器
|
||||
QMetaObject::invokeMethod(this, [this]() {
|
||||
if (m_pCameraReconnectTimer && m_pCameraReconnectTimer->isActive()) {
|
||||
m_pCameraReconnectTimer->stop();
|
||||
LOG_INFO("[BasePresenter] 停止相机重连定时器\n");
|
||||
}
|
||||
}, Qt::QueuedConnection);
|
||||
// 直接停止定时器(析构时需要立即停止,不能用QueuedConnection)
|
||||
if (m_pCameraReconnectTimer) {
|
||||
m_pCameraReconnectTimer->stop();
|
||||
}
|
||||
}
|
||||
|
||||
// ============ OnCameraReconnectTimer 实现 ============
|
||||
@ -768,7 +776,6 @@ int BasePresenter::StartModbusServer(int port)
|
||||
void BasePresenter::StopModbusServer()
|
||||
{
|
||||
if (m_modbusServer) {
|
||||
LOG_INFO("[BasePresenter] 停止ModbusTCP服务器\n");
|
||||
m_modbusServer->stop();
|
||||
delete m_modbusServer;
|
||||
m_modbusServer = nullptr;
|
||||
|
||||
@ -427,7 +427,11 @@ void CVrEyeDevice::DebugThreadFunc()
|
||||
// 固定的点云文件路径
|
||||
// const std::string debugFilePath = "C:\\project\\QT\\GrabBag\\TestData\\workpiece\\new\\test\\9_LazerData_Hi229229.txt";
|
||||
// const std::string debugFilePath = "C:\\project\\QT\\GrabBag\\TestData\\bag\\LaserLine1_grid.txt";
|
||||
#ifdef _WIN32
|
||||
const std::string debugFilePath = "C:\\project\\QT\\GrabBag\\TestData\\eye_test.txt";
|
||||
#else
|
||||
const std::string debugFilePath = "/home/linaro/eye_test.txt";
|
||||
#endif
|
||||
|
||||
LaserDataLoader loader;
|
||||
std::vector<std::pair<EVzResultDataType, SVzLaserLineData>> laserLines;
|
||||
@ -438,24 +442,24 @@ void CVrEyeDevice::DebugThreadFunc()
|
||||
|
||||
// 加载点云文件
|
||||
int ret = loader.LoadLaserScanData(debugFilePath, laserLines, lineNum, scanSpeed, maxTimeStamp, clockPerSecond);
|
||||
if (ret != SUCCESS) {
|
||||
if (ret == SUCCESS) {
|
||||
LOG_DEBUG("VR_EYE_DEBUG_MODE: Failed to load laser data from %s, error: %s\n",debugFilePath.c_str(), loader.GetLastError().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// 循环读取并回调
|
||||
auto iter = laserLines.begin();
|
||||
auto iter = laserLines.begin();
|
||||
|
||||
while (iter != laserLines.end()) {
|
||||
if (m_debugCallback) {
|
||||
auto& dataPair = *iter;
|
||||
m_debugCallback(dataPair.first, &dataPair.second, m_debugCallbackParam);
|
||||
while (iter != laserLines.end()) {
|
||||
if (m_debugCallback) {
|
||||
auto& dataPair = *iter;
|
||||
m_debugCallback(dataPair.first, &dataPair.second, m_debugCallbackParam);
|
||||
}
|
||||
|
||||
iter++;
|
||||
// 模拟采集间隔,约30ms
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(2));
|
||||
}
|
||||
|
||||
iter++;
|
||||
// 模拟采集间隔,约30ms
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(2));
|
||||
}
|
||||
|
||||
if (m_debugStatusCallback) {
|
||||
m_debugStatusCallback(keDeviceWorkStatus_Device_Swing_Finish, nullptr, 0, m_debugStatusCallbackParam);
|
||||
m_debugStatusCallback(keDeviceWorkStatus_Device_Auto_Stop, nullptr, 0, m_debugStatusCallbackParam);
|
||||
|
||||
@ -95,8 +95,10 @@ void ModbusTCPServer::stop()
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INFO("[ModbusTCPServer] stop()\n");
|
||||
|
||||
m_shouldStop.store(true);
|
||||
|
||||
|
||||
// 关闭服务器socket以中断accept调用
|
||||
if (m_serverSocket != -1) {
|
||||
#ifdef _WIN32
|
||||
@ -135,57 +137,69 @@ void ModbusTCPServer::stop()
|
||||
void ModbusTCPServer::serverLoop()
|
||||
{
|
||||
fd_set readfds;
|
||||
int maxfd = m_serverSocket;
|
||||
|
||||
|
||||
while (!m_shouldStop.load()) {
|
||||
// 检查服务器socket是否有效
|
||||
int serverSock = m_serverSocket;
|
||||
if (serverSock == -1) {
|
||||
break; // socket已关闭,退出循环
|
||||
}
|
||||
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(m_serverSocket, &readfds);
|
||||
|
||||
maxfd = m_serverSocket;
|
||||
|
||||
FD_SET(serverSock, &readfds);
|
||||
|
||||
int maxfd = serverSock;
|
||||
|
||||
// 添加所有客户端socket到监听集合
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_clientsMutex);
|
||||
for (const auto& pair : m_clients) {
|
||||
int clientSocket = pair.first;
|
||||
FD_SET(clientSocket, &readfds);
|
||||
if (clientSocket > maxfd) {
|
||||
maxfd = clientSocket;
|
||||
if (clientSocket >= 0) {
|
||||
FD_SET(clientSocket, &readfds);
|
||||
if (clientSocket > maxfd) {
|
||||
maxfd = clientSocket;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 设置超时时间(1秒),避免阻塞太久
|
||||
struct timeval timeout;
|
||||
timeout.tv_sec = 1;
|
||||
timeout.tv_usec = 0;
|
||||
|
||||
|
||||
int selectResult = select(maxfd + 1, &readfds, nullptr, nullptr, &timeout);
|
||||
|
||||
|
||||
// 再次检查是否应该停止
|
||||
if (m_shouldStop.load()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (selectResult == -1) {
|
||||
if (!m_shouldStop.load()) {
|
||||
LOG_WARNING("select error: %s\n", strerror(errno));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (selectResult == 0) {
|
||||
// 超时,继续循环
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// 检查服务器socket是否有新连接
|
||||
if (FD_ISSET(m_serverSocket, &readfds)) {
|
||||
if (serverSock >= 0 && FD_ISSET(serverSock, &readfds)) {
|
||||
handleNewConnection();
|
||||
}
|
||||
|
||||
|
||||
// 检查客户端socket是否有数据
|
||||
std::vector<int> socketsToRemove;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_clientsMutex);
|
||||
for (const auto& pair : m_clients) {
|
||||
int clientSocket = pair.first;
|
||||
if (FD_ISSET(clientSocket, &readfds)) {
|
||||
if (clientSocket >= 0 && FD_ISSET(clientSocket, &readfds)) {
|
||||
try {
|
||||
handleClientData(pair.second);
|
||||
} catch (...) {
|
||||
@ -195,7 +209,7 @@ void ModbusTCPServer::serverLoop()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 删除有问题的客户端连接
|
||||
for (int socket : socketsToRemove) {
|
||||
removeClient(socket);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user