拆包:摆动机构参数无效;相机IP配置

This commit is contained in:
yiyi 2025-11-10 00:19:20 +08:00
parent c5e321ad46
commit 6160801e22
16 changed files with 366 additions and 56 deletions

View File

@ -59,6 +59,15 @@ struct MonitoringParam
double alertThreshold = 3.0; // 报警阈值
};
/**
* @brief
*/
struct CameraParam
{
std::string name = ""; // 相机名称
std::string cameraIP = ""; // 相机IP地址
};
/**
* @brief
*/
@ -87,7 +96,8 @@ struct BeltTearingConfigResult
std::vector<ServerInfo> servers; // 服务器列表
BeltTearingAlgorithmParams algorithmParams; // 算法参数
DebugParam debugParam; // 调试参数
BeltTearingProjectType projectType; // 项目类型
BeltTearingProjectType projectType; // 项目类型
std::vector<CameraParam> cameras; // 相机列表
int serverPort = 5900; // 服务端端口
};
@ -149,6 +159,7 @@ public:
// 声明元类型以便在QVariant中使用
Q_DECLARE_METATYPE(ServerInfo)
Q_DECLARE_METATYPE(BeltTearingParam)
Q_DECLARE_METATYPE(CameraParam)
Q_DECLARE_METATYPE(BeltTearingConfigResult)
#endif // IVRBELTTEARINGCONFIG_H

View File

@ -170,6 +170,19 @@ BeltTearingConfigResult VrBeltTearingConfig::LoadConfig(const std::string& fileP
}
}
}
// 解析相机配置
else if (xml.isStartElement() && xml.name() == "Cameras") {
while (xml.readNextStartElement()) {
if (xml.name() == "Camera") {
CameraParam camera;
camera.name = xml.attributes().value("name").toString().toStdString();
camera.cameraIP = xml.attributes().value("ip").toString().toStdString();
result.cameras.push_back(camera);
xml.skipCurrentElement();
}
}
}
}
file.close();
@ -259,6 +272,16 @@ bool VrBeltTearingConfig::SaveConfig(const std::string& filePath, BeltTearingCon
xml.writeEndElement(); // ServerPort
xml.writeEndElement(); // LocalServerConfig
// 保存相机配置
xml.writeStartElement("Cameras");
for (const auto& camera : configResult.cameras) {
xml.writeStartElement("Camera");
xml.writeAttribute("name", QString::fromStdString(camera.name));
xml.writeAttribute("ip", QString::fromStdString(camera.cameraIP));
xml.writeEndElement(); // Camera
}
xml.writeEndElement(); // Cameras
xml.writeEndElement(); // BeltTearingConfig
xml.writeEndDocument();

View File

@ -12,6 +12,11 @@
<ServerPort port="5900" />
</LocalServerConfig>
<!-- 相机配置 -->
<Cameras>
<Camera name="摄像头1" ip="192.168.1.100" />
</Cameras>
<!-- 算法参数 - 基于SDK SSG_beltTearingParam结构 -->
<AlgorithmParams>
<!-- 皮带撕裂检测参数 - SDK兼容参数 -->

View File

@ -364,17 +364,25 @@ bool BeltTearingPresenter::initializeCamera()
void BeltTearingPresenter::startCamera()
{
if (!m_eyeDevice) {
if (!initializeCamera()) {
// 启动定时器持续重试初始化
LOG_WARNING("Camera initialization failed, starting retry timer...\n");
m_cameraInitTimer->start(5000); // 每5秒重试一次
return;
}
if (!initializeCamera()) {
// 启动定时器持续重试初始化
LOG_WARNING("Camera initialization failed, starting retry timer...\n");
m_cameraInitTimer->start(5000); // 每5秒重试一次
return;
}
// 准备相机IP参数使用配置中的第一个相机
const char* pCameraIP = nullptr;
if (!m_configResult.cameras.empty() && !m_configResult.cameras[0].cameraIP.empty()) {
pCameraIP = m_configResult.cameras[0].cameraIP.c_str();
LOG_INFO("Using configured camera [%s] IP: %s\n",
m_configResult.cameras[0].name.c_str(), pCameraIP);
} else {
LOG_INFO("No camera IP configured, using auto-discovery\n");
}
// 尝试打开设备
int result = m_eyeDevice->OpenDevice(nullptr, false, false, true);
int result = m_eyeDevice->OpenDevice(pCameraIP, false, false, true);
if (result != 0) {
LOG_WARNING("Failed to open camera device, error code: %d, retrying...\n", result);
@ -383,7 +391,7 @@ void BeltTearingPresenter::startCamera()
return;
}
LOG_DEBUG("Camera started successfully \n");
LOG_INFO("Camera opened successfully\n");
// 停止重试定时器
m_cameraInitTimer->stop();
@ -451,13 +459,20 @@ void BeltTearingPresenter::onCameraInitTimer()
}
}
// 准备相机IP参数使用配置中的第一个相机
const char* pCameraIP = nullptr;
if (!m_configResult.cameras.empty() && !m_configResult.cameras[0].cameraIP.empty()) {
pCameraIP = m_configResult.cameras[0].cameraIP.c_str();
}
// 尝试连接相机
int result = m_eyeDevice->OpenDevice(nullptr, false, false, true);
int result = m_eyeDevice->OpenDevice(pCameraIP, false, false, true);
if (result == 0) {
// 连接成功,开始检测
// 连接成功,停止定时器
m_cameraInitTimer->stop();
m_cameraInitialized = true;
// 开始检测
result = m_eyeDevice->StartDetect(OnPointCloudCallback, keResultDataType_Position, this);
if (result == 0) {
m_cameraDetecting = true;
@ -1097,6 +1112,8 @@ void BeltTearingPresenter::handleWriteConfig(const QByteArray& paramData)
void BeltTearingPresenter::ResetDetect()
{
LOG_INFO("Resetting detection system\n");
stopCamera();
m_hLineWorkers.clear();
@ -1116,7 +1133,46 @@ void BeltTearingPresenter::ResetDetect()
}
m_laserLineQueue.clear();
// 清空Modbus检测结果数据
if (m_pRobotProtocol) {
int ret = m_pRobotProtocol->ClearDetectionData();
if (ret != 0) {
LOG_WARNING("Failed to clear Modbus detection data during reset, error code: %d\n", ret);
} else {
LOG_INFO("Modbus detection data cleared during reset\n");
}
}
startCamera();
LOG_INFO("Detection system reset completed\n");
}
void BeltTearingPresenter::StopWork()
{
LOG_INFO("Stopping work - camera and detection\n");
// 停止相机
stopCamera();
// 设置机械臂工作状态为空闲
if (m_pRobotProtocol) {
m_pRobotProtocol->SetWorkStatus(RobotProtocol::WORK_STATUS_IDLE);
}
LOG_INFO("Work stopped successfully\n");
}
void BeltTearingPresenter::StartWork()
{
LOG_INFO("Starting work - camera and detection\n");
// 启动相机
startCamera();
// 设置机械臂工作状态为工作中在startCamera中已经设置
LOG_INFO("Work started successfully\n");
}
void BeltTearingPresenter::_AlgoDetectThread()
@ -1378,6 +1434,27 @@ int BeltTearingPresenter::InitRobotProtocol()
return this->OnRobotWorkSignal(startWork, cameraIndex);
});
// 设置系统控制回调
m_pRobotProtocol->SetSystemControlCallback([this](uint16_t command) {
switch(command) {
case 0: // 停止工作
LOG_INFO("ModbusTCP command: Stop work\n");
this->StopWork();
break;
case 1: // 开始工作
LOG_INFO("ModbusTCP command: Start work\n");
this->StartWork();
break;
case 2: // 复位检测
LOG_INFO("ModbusTCP command: Reset detect\n");
this->ResetDetect();
break;
default:
LOG_WARNING("Unknown ModbusTCP command: %d\n", command);
break;
}
});
LOG_INFO("Robot protocol initialization completed successfully\n");
return nRet;
}

View File

@ -49,6 +49,10 @@ public:
void ResetDetect();
// 系统控制方法供ModbusTCP调用
void StopWork(); // 停止工作
void StartWork(); // 开始工作
// 获取配置信息
quint16 getServerPort() const { return m_configResult.serverPort; }
const BeltTearingConfigResult& getConfig() const { return m_configResult; }
@ -82,7 +86,7 @@ private:
QString m_configFilePath;
// 相机相关
IVrEyeDevice *m_eyeDevice;
IVrEyeDevice *m_eyeDevice = nullptr;
QTimer *m_cameraInitTimer;
QString m_cameraIP;
bool m_cameraInitialized;

View File

@ -139,6 +139,11 @@ void RobotProtocol::SetWorkSignalCallback(const WorkSignalCallback& callback)
m_workSignalCallback = callback;
}
void RobotProtocol::SetSystemControlCallback(const std::function<void(uint16_t)>& callback)
{
m_systemControlCallback = callback;
}
bool RobotProtocol::IsRunning() const
{
return m_bServerRunning;
@ -171,6 +176,25 @@ int RobotProtocol::SetWorkStatus(uint16_t status)
return 0;
}
int RobotProtocol::ClearDetectionData()
{
LOG_INFO("Clearing detection result data in Modbus registers\n");
if (!m_pModbusServer) {
LOG_ERROR("ModbusTCP server not initialized\n");
return -1;
}
// 清空检测结果数据区域地址1-80共5个撕裂信息每个16个寄存器
// 总共需要清空 5 * 16 = 80 个寄存器
std::vector<uint16_t> clearData(80, 0); // 创建80个值为0的寄存器
m_pModbusServer->updateHoldingRegisters(DETECT_RESULT_ADDR, clearData);
LOG_INFO("Detection result data cleared (registers 1-80)\n");
return 0;
}
void RobotProtocol::StopModbusTCPServer()
{
LOG_DEBUG("Stop ModbusTCP server\n");
@ -192,7 +216,42 @@ IYModbusTCPServer::ErrorCode RobotProtocol::OnWriteCoils(uint8_t unitId, uint16_
IYModbusTCPServer::ErrorCode RobotProtocol::OnWriteRegisters(uint8_t unitId, uint16_t startAddress, uint16_t quantity, const uint16_t* values)
{
return IYModbusTCPServer::ErrorCode::ILLEGAL_FUNCTION;
// 只允许写入地址0系统状态寄存器
if (startAddress != STATUS_ADDR) {
LOG_WARNING("Attempt to write to read-only address: %d\n", startAddress);
return IYModbusTCPServer::ErrorCode::ILLEGAL_DATA_ADDRESS;
}
// 只允许写入单个寄存器
if (quantity != 1) {
LOG_WARNING("Invalid quantity for status register write: %d\n", quantity);
return IYModbusTCPServer::ErrorCode::ILLEGAL_DATA_VALUE;
}
if (!values) {
LOG_ERROR("Null values pointer in write registers\n");
return IYModbusTCPServer::ErrorCode::SERVER_FAILURE;
}
uint16_t command = values[0];
// 验证命令值是否有效0-停止, 1-开始, 2-复位)
if (command > 2) {
LOG_WARNING("Invalid system control command: %d\n", command);
return IYModbusTCPServer::ErrorCode::ILLEGAL_DATA_VALUE;
}
LOG_INFO("Received system control command via ModbusTCP: %d\n", command);
// 调用系统控制回调
if (m_systemControlCallback) {
m_systemControlCallback(command);
LOG_DEBUG("System control callback executed for command: %d\n", command);
} else {
LOG_WARNING("System control callback not set\n");
}
return IYModbusTCPServer::ErrorCode::SUCCESS;
}
void RobotProtocol::OnModbusTCPConnectionChanged(bool connected)

View File

@ -62,6 +62,12 @@ public:
*/
void SetWorkSignalCallback(const WorkSignalCallback& callback);
/**
* @brief
* @param callback (0-,1-,2-)
*/
void SetSystemControlCallback(const std::function<void(uint16_t)>& callback);
/**
* @brief
* @return true-false-
@ -75,6 +81,12 @@ public:
*/
int SetWorkStatus(uint16_t status);
/**
* @brief
* @return 0--
*/
int ClearDetectionData();
// 使用公共工作状态定义
static const uint16_t WORK_STATUS_IDLE = ::WORK_STATUS_IDLE;
static const uint16_t WORK_STATUS_WORKING = ::WORK_STATUS_WORKING;
@ -127,6 +139,7 @@ private:
// 回调函数
ConnectionCallback m_connectionCallback; // 连接状态回调
WorkSignalCallback m_workSignalCallback; // 工作信号回调
std::function<void(uint16_t)> m_systemControlCallback; // 系统控制回调
// Modbus地址映射
static const uint16_t STATUS_ADDR = 0; // 工作信号线圈地址 (所有相机)

View File

@ -1,8 +1,8 @@
#ifndef VERSION_H
#define VERSION_H
#define BELT_TEARING_SERVER_VERSION_STRING "2.0.1"
#define BELT_TEARING_SERVER_VERSION_BUILD "5"
#define BELT_TEARING_SERVER_VERSION_STRING "2.0.2"
#define BELT_TEARING_SERVER_VERSION_BUILD "1"
#define BELT_TEARING_SERVER_PRODUCT_NAME "BeltTearingServer"
#define BELT_TEARING_SERVER_COMPANY_NAME "VisionRobot"
#define BELT_TEARING_SERVER_COPYRIGHT "Copyright (C) 2024 VisionRobot. All rights reserved."

View File

@ -1,4 +1,9 @@
# 1.0.0 2025-9-20
# 2.0.2 2025-10-09
## build_1
1. 增加ModbusTCP协议 开关流 & 复位功能
2. 增加相机IP配置
# 2.0.1 2025-9-20
## build_3
1. 修复图像发送问题

View File

@ -69,6 +69,7 @@ int main(int argc, char *argv[])
LOG_INFO("Server is running on port %d\n", serverPort);
LOG_INFO("Press Ctrl+C to exit.\n");
#if 0
#ifdef _WIN32
LOG_INFO("Simulation thread started - sending test data every 5 seconds\n");
// 处理应用程序退出
@ -83,6 +84,7 @@ int main(int argc, char *argv[])
});
testThread.detach();
#endif
#endif
return app.exec();

View File

@ -15,14 +15,14 @@
### 3.1 保持寄存器地址分配
| 起始地址 | 名称 | 类型 | 描述 |
|---------|------|------|------|
| 0 | 系统状态 | UInt16 | 系统运行状态 |
| 1-16 | 撕裂信息1 | 结构体 | 第一个撕裂检测结果 |
| 17-32 | 撕裂信息2 | 结构体 | 第二个撕裂检测结果 |
| 33-48 | 撕裂信息3 | 结构体 | 第三个撕裂检测结果 |
| 49-64 | 撕裂信息4 | 结构体 | 第四个撕裂检测结果 |
| 65-80 | 撕裂信息5 | 结构体 | 第五个撕裂检测结果 |
| 起始地址 | 名称 | 类型 | 读/写 | 描述 |
|---------|------|------|------|------|
| 0 | 系统状态 | UInt16 | 读/写 | 系统运行状态和控制命令 |
| 1-16 | 撕裂信息1 | 结构体 | 只读 | 第一个撕裂检测结果 |
| 17-32 | 撕裂信息2 | 结构体 | 只读 | 第二个撕裂检测结果 |
| 33-48 | 撕裂信息3 | 结构体 | 只读 | 第三个撕裂检测结果 |
| 49-64 | 撕裂信息4 | 结构体 | 只读 | 第四个撕裂检测结果 |
| 65-80 | 撕裂信息5 | 结构体 | 只读 | 第五个撕裂检测结果 |
### 3.2 单个撕裂信息结构
@ -38,7 +38,27 @@
### 3.3 数据类型说明
#### 3.3.1 撕裂状态枚举
#### 3.3.1 系统状态枚举
系统状态寄存器(地址0)既可以读取也可以写入:
**读取时的状态值**:
| 值 | 状态 | 描述 |
|----|------|------|
| 0 | 系统停止 | 系统处于停止状态,不进行检测 |
| 1 | 系统运行 | 系统正常运行,正在进行撕裂检测 |
| 2 | 系统忙碌 | 系统正在处理数据 |
**写入时的控制命令**:
| 值 | 命令 | 描述 |
|----|------|------|
| 0 | 停止工作 | 停止相机检测,停止算法处理 |
| 1 | 开始工作 | 启动相机检测,开始算法处理 |
| 2 | 复位检测 | 重置检测状态,清空缓存数据,重新开始检测,**清空所有检测结果数据(地址1-80)** |
#### 3.3.2 撕裂状态枚举
| 值 | 状态 | 描述 |
|----|------|------|
@ -48,7 +68,7 @@
| 3 | keSG_tearStatus_Ended | 已结束的撕裂 |
| 4 | keSG_tearStatus_Invalid | 无效撕裂 |
#### 3.3.2 浮点数表示
#### 3.3.3 浮点数表示
浮点数使用两个连续的16位寄存器表示采用Modbus标准的大端字节序
- 高16位存储在较低地址的寄存器中
@ -63,12 +83,68 @@
| 功能码 | 描述 | 支持情况 |
|--------|------|----------|
| 0x03 | 读取保持寄存器 | 支持 |
| 0x06 | 写单个寄存器 | 支持 |
| 0x10 | 写多个寄存器 | 支持 |
| 0x06 | 写单个寄存器 | 支持(仅地址0系统状态寄存器) |
| 0x10 | 写多个寄存器 | 支持(仅地址0系统状态寄存器) |
## 5. 示例
### 5.1 读取撕裂信息
### 5.1 控制系统状态
#### 5.1.1 停止工作
```
请求:
功能码: 0x06 (写单个寄存器)
寄存器地址: 0
寄存器值: 0x0000
响应:
功能码: 0x06
寄存器地址: 0
寄存器值: 0x0000 (确认写入成功)
```
#### 5.1.2 开始工作
```
请求:
功能码: 0x06 (写单个寄存器)
寄存器地址: 0
寄存器值: 0x0001
响应:
功能码: 0x06
寄存器地址: 0
寄存器值: 0x0001 (确认写入成功)
```
#### 5.1.3 复位检测
```
请求:
功能码: 0x06 (写单个寄存器)
寄存器地址: 0
寄存器值: 0x0002
响应:
功能码: 0x06
寄存器地址: 0
寄存器值: 0x0002 (确认写入成功)
```
### 5.2 读取系统状态
```
请求:
功能码: 0x03 (读取保持寄存器)
起始地址: 0
寄存器数量: 1
响应:
数据: 0x0001 (系统运行中)
```
### 5.3 读取撕裂信息
假设要读取第一个撕裂的信息:
@ -102,8 +178,14 @@
1. 系统最多同时报告5个撕裂信息
2. 当没有撕裂时对应的寄存器区域保持为0
3. 浮点数精度为IEEE 754单精度格式
4. 所有数据均为只读,不支持外部写入
4. 撕裂信息数据为只读,仅系统状态寄存器(地址0)支持写入
5. 系统状态寄存器值为1表示系统正常运行
6. 写入系统状态寄存器时:
- 写入0停止相机检测和算法处理
- 写入1启动相机检测和算法处理
- 写入2复位检测清空缓存并重新开始**自动清空所有检测结果寄存器(地址1-80)**
7. 只有地址0(系统状态)支持写操作,写入其他地址将返回非法地址异常
8. 复位检测时系统会自动将检测结果区域的80个寄存器全部清零确保不会读取到旧的检测数据
## 8. 更新频率

View File

@ -2,6 +2,7 @@
## build_0
1. 摆动机构默认参数无效
2. 修改帧率范围
3. 相机IP配置通过配置文件config.xml 配置相机IP如果没有没有配置搜索相机进行打开
# 1.3.1 2025-10-28
## build_0

View File

@ -3,8 +3,8 @@
#define WORKPIECE_VERSION_STRING "1.0.1"
#define WORKPIECE_BUILD_STRING "1"
#define WORKPIECE_FULL_VERSION_STRING "V1.0.1.1"
#define WORKPIECE_BUILD_STRING "2"
#define WORKPIECE_FULL_VERSION_STRING "V1.0.1.2"
// 获取版本信息的便捷函数
inline const char* GetWorkpieceVersion() {

View File

@ -1,4 +1,7 @@
#1.0.1 2025-11-08
## build_2
1. 修正开机相机重连【项目结构重构】
## build_1
1. 更新算法,在应用日志中有算法版本: 1.1.0
2. 修复在没有配置文件中无法连接相机问题

View File

@ -112,16 +112,16 @@ win32:CONFIG(debug, debug|release) {
LIBS += -L../../../VrNets/release -lVrTcpServer
}else:unix:!macx {
# Unix/Linux平台库链接(包括交叉编译)
# 注意链接顺序:依赖关系从高到低
# 注意链接顺序:依赖关系从高到低(被依赖的库放在后面)
LIBS += -L../WorkpieceConfig -lWorkpieceConfig
LIBS += -L../../../AppUtils/UIUtils -lUIUtils
LIBS += -L../../../AppUtils/AppCommon -lAppCommon
LIBS += -L../../../AppUtils/CloudUtils -lCloudUtils
LIBS += -L../../../VrEyeDevice -lVrEyeDevice
LIBS += -L../../../Module/ModbusTCPServer -lModbusTCPServer
LIBS += -L../../../VrNets -lVrModbus
LIBS += -L../../../Module/ShareMem -lShareMem
LIBS += -L../../../VrNets -lVrTcpServer
LIBS += -L../../../AppUtils/UIUtils -lUIUtils
LIBS += -L../../../AppUtils/AppCommon -lAppCommon
LIBS += -L../../../AppUtils/CloudUtils -lCloudUtils
LIBS += -L../../../VrUtils -lVrUtils
# 添加系统库依赖

View File

@ -38,6 +38,31 @@ SOURCES += \
Src/PathManager.cpp \
Src/BasePresenter.cpp
# Windows平台库链接
win32:CONFIG(release, debug|release): {
LIBS += -L$$PWD/../../Module/ModbusTCPServer/release -lModbusTCPServer
LIBS += -L$$PWD/../../VrNets/release -lVrModbus
LIBS += -L$$PWD/../../VrNets/release -lVrTCPServer
LIBS += -L$$PWD/../../VrEyeDevice/release -lVrEyeDevice
LIBS += -L$$PWD/../../VrUtils/release -lVrUtils
}
else:win32:CONFIG(debug, debug|release): {
LIBS += -L$$PWD/../../Module/ModbusTCPServer/debug -lModbusTCPServer
LIBS += -L$$PWD/../../VrNets/debug -lVrModbus
LIBS += -L$$PWD/../../VrNets/debug -lVrTCPServer
LIBS += -L$$PWD/../../VrEyeDevice/debug -lVrEyeDevice
LIBS += -L$$PWD/../../VrUtils/debug -lVrUtils
}else:unix:!macx {
LIBS += -L../../Module/ModbusTCPServer -lModbusTCPServer
LIBS += -L../../VrNets -lVrModbus
LIBS += -L../../VrNets -lVrTCPServer
LIBS += -L../../VrEyeDevice -lVrEyeDevice
LIBS += -L../../VrUtils -lVrUtils
# 添加系统库依赖
LIBS += -lpthread
}
# Default rules for deployment.
unix {
target.path = /usr/lib