635 lines
24 KiB
Markdown
635 lines
24 KiB
Markdown
# 硬件视觉调度时序图
|
||
|
||
## 概述
|
||
|
||
本文档描述了硬件视觉调度系统的完整时序流程,展示了从调度车托料完成到最终调度至立锯的全过程。
|
||
|
||
### 系统架构
|
||
|
||
| 项目 | 技术栈 | 角色 | 说明 |
|
||
| ---- | ------ | ---- | ---- |
|
||
| `go-auto` | Go | TCP客户端(上位机) | 硬件控制中枢,通过PLC控制下位机,通过TCP与视觉算法通信 |
|
||
| `LogMax3D` | C# | TCP服务端(视觉算法) | 视觉算法服务,监听9000端口,处理3D扫描、建模、切割优化 |
|
||
|
||
### 通信协议
|
||
|
||
- **协议**: TCP长连接,JSON行协议(`\n`分隔)
|
||
- **连接方向**: Go客户端 → C#服务端(上位机主动连接视觉算法)
|
||
- **消息格式**: 每行一个完整JSON对象,支持单行和多行格式化JSON自动重组
|
||
- **连接确认**: 连接后C#立即推送 `connection_ack` 确认
|
||
- **错误标识**: 使用 `error_id` 字段标识错误,空字符串表示成功
|
||
|
||
## 上位机控制时序图-视觉部分
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
autonumber
|
||
participant 调度车
|
||
participant 上位机Go
|
||
participant 下位机PLC
|
||
participant 视觉算法C#
|
||
|
||
box rgb(173, 216, 230) 硬件调度系统
|
||
participant 调度车
|
||
end
|
||
box rgb(255, 218, 185) 控制系统
|
||
participant 上位机Go
|
||
participant 下位机PLC
|
||
end
|
||
box rgb(152, 251, 152) 算法系统
|
||
participant 视觉算法C#
|
||
end
|
||
|
||
Note over 调度车,视觉算法C#: 系统初始化阶段
|
||
调度车->>上位机Go: 托料完成(M42上升沿)
|
||
上位机Go->>上位机Go: 生成WoodID、推送LED面板
|
||
|
||
Note over 调度车,视觉算法C#: 视觉定位阶段(对中拍照)
|
||
上位机Go->>下位机PLC: M809触发 + D2130=2630验证
|
||
上位机Go->>上位机Go: 打开红外测距仪、等待读数稳定
|
||
上位机Go->>下位机PLC: 写入D2152/D2154调整坐标、M119调整拍照位
|
||
上位机Go->>下位机PLC: M810就位 → 关闭红外、M130触发坐标调整
|
||
上位机Go->>上位机Go: M120就位 → 监控激光传感器读数
|
||
上位机Go->>视觉算法C#: center_photo_taking(wood_id+左右相机坐标)
|
||
视觉算法C#-->>上位机Go: ACK: center_scanning
|
||
视觉算法C#->>视觉算法C#: 异步拍照+端面中心计算
|
||
视觉算法C#->>上位机Go: center_photo_result(顶针目标坐标+直径)
|
||
上位机Go->>上位机Go: 设置原木直径、触发托料装置升高
|
||
上位机Go->>下位机PLC: 写入D2024/D2026/D2028/D2030顶针坐标
|
||
上位机Go->>下位机PLC: 写入D2168/D2170轨道目标坐标
|
||
上位机Go->>下位机PLC: M49启动顶料
|
||
|
||
Note over 调度车,视觉算法C#: 视觉扫描阶段
|
||
上位机Go->>上位机Go: M50上升沿 + D2130<1600 + D2042>=2300
|
||
上位机Go->>视觉算法C#: start_vision_algorithm(wood_id)
|
||
视觉算法C#-->>上位机Go: ACK: vision_scanning
|
||
视觉算法C#->>视觉算法C#: 异步启动相机采集
|
||
视觉算法C#->>上位机Go: capture_ready(status=ready)
|
||
上位机Go->>下位机PLC: 写入M103启动扫描轨道
|
||
上位机Go->>上位机Go: 启动M104监控(等待工单推送触发)
|
||
下位机PLC->>下位机PLC: 扫描轨道运行、编码器触发采集
|
||
|
||
Note over 调度车,视觉算法C#: 工单推送与算法处理阶段
|
||
上位机Go->>上位机Go: M104上升沿 + M42=true
|
||
上位机Go->>上位机Go: 读取D2134计算原木长度
|
||
上位机Go->>视觉算法C#: order_info(工单信息+规格+长度)
|
||
视觉算法C#-->>上位机Go: ACK: algorithm_coordinates_run(status=processing)
|
||
视觉算法C#->>视觉算法C#: 停止采集→建模→顶针裁剪→多规格切割优化
|
||
视觉算法C#->>上位机Go: algorithm_coordinates(切割坐标+旋转角度+材积)
|
||
视觉算法C#->>上位机Go: board_split_yield_result(板材拆分出材率)
|
||
|
||
Note over 调度车,视觉算法C#: 旋转与调度执行阶段
|
||
上位机Go->>上位机Go: 保存视觉数据到数据库+推送LED面板
|
||
上位机Go->>下位机PLC: 写入D2038旋转角度
|
||
上位机Go->>下位机PLC: M45执行旋转
|
||
上位机Go->>下位机PLC: 写入D2136原木直径
|
||
上位机Go->>上位机Go: M46上升沿检测(旋转完成)
|
||
上位机Go->>上位机Go: 异步计算A/B组夹臂坐标(D2106-D2110/D2162-D2166)
|
||
上位机Go->>上位机Go: 触发立锯调度(M807=true)
|
||
调度车->>调度车: 取料并调度至立锯
|
||
```
|
||
|
||
---
|
||
|
||
## 流程说明
|
||
|
||
### 1. 初始阶段
|
||
- **调度车托料完成**: M42上升沿检测,生成WoodID,推送当天原木数量至LED面板
|
||
|
||
### 2. 视觉定位阶段(对中拍照)
|
||
- **M809触发**: 升降车D2130在2630±15mm拍照位时,启动红外测距仪
|
||
- **红外测距调整**: 等待红外读数稳定,计算调整坐标写入D2152/D2154,M119调整拍照位
|
||
- **M810就位信号**: 关闭红外测距仪,写入M130触发坐标调整
|
||
- **M120就位信号**: 监控激光传感器读数,左右均在(850,1000]mm范围内时,推送`center_photo_taking`
|
||
- **异步对中计算**: C#端异步执行图漾深度相机拍照和端面中心计算
|
||
- **推送对中结果**: C#推送`center_photo_result`,包含视觉顶针机构目标坐标(mm)和直径(mm)
|
||
- **写入顶针坐标**: Go端将坐标写入D2024/D2026/D2028/D2030,轨道坐标写入D2168/D2170,M49启动顶料
|
||
|
||
### 3. 视觉扫描阶段
|
||
- **M50上升沿检测**: 同时验证D2130<1600(升降车安全位)且D2042>=2300(调度车就位)
|
||
- **D2138方向判断**: D2138>-2250为左扫描(left),<=-2250为右扫描(right)
|
||
- **推送扫描标识**: Go发送`start_vision_algorithm`,C#异步启动4台相机持续采集
|
||
- **capture_ready信号**: C#相机就绪后推送就绪信号,Go收到后写入M103启动扫描轨道
|
||
|
||
### 4. 工单推送与算法处理阶段
|
||
- **M104上升沿**: 扫描结束触发,同时验证M42=true
|
||
- **读取原木长度**: 从D2134寄存器读取长度值,减去315mm得到实际原木长度
|
||
- **推送工单信息**: Go发送`order_info`,包含工单规格、副规格、锯路厚度等参数
|
||
- **异步算法处理**: C#端依次执行:停止采集→等待帧就绪→点云融合建模→Y轴归一化→顶针裁剪→多规格切割优化
|
||
- **推送算法结果**: C#推送`algorithm_coordinates`(切割坐标+旋转角度+材积+出材率)
|
||
- **推送板材拆分**: C#异步推送`board_split_yield_result`(板材拆分出材率)
|
||
|
||
### 5. 旋转与调度执行阶段
|
||
- **数据持久化**: Go保存视觉算法数据到数据库(坐标JSON、旋转角度、材积、径级),推送出材率到LED面板
|
||
- **写入旋转角度**: D2038写入算法角度值(回读验证,最多重试5次)
|
||
- **执行旋转**: M45写入true执行物理旋转
|
||
- **写入原木直径**: D2136写入原木直径值
|
||
- **旋转完成检测**: M46上升沿检测(旋转完成),并行计算A/B组夹臂伺服坐标
|
||
- **触发立锯调度**: M807=true时触发立锯调度逻辑
|
||
|
||
## 关键特性
|
||
|
||
- **异步推送机制**: C#端收到请求后立即返回ACK,算法计算完成后异步推送结果(`center_photo_result`/`algorithm_coordinates`)
|
||
- **capture_ready握手**: C#端相机就绪后推送就绪信号,Go端收到后才写入M103启动扫描轨道,避免时序错误
|
||
- **多规格对比优化**: 算法对比主规格与副规格,选出材率最高的切割方案
|
||
- **顶针裁剪**: 根据扫描方向(left/right)从点云两端裁剪固定长度,移除机械顶针和过渡区点云
|
||
- **Y轴归一化**: scan_start="right"时翻转Y轴,确保Y=0始终对应同一物理端
|
||
- **寄存器回读验证**: 关键寄存器(D2038角度值、D2024-D2030顶针坐标)写入后立即回读验证
|
||
- **材积基准统一**: TotalVolume使用middle Y截面R计算,与AvailableVolume同源,消除出材率偏高
|
||
|
||
## 注意事项
|
||
|
||
1. 每个阶段的执行都依赖于前一阶段的成功完成
|
||
2. C#端TCP服务监听 `0.0.0.0:9000`,支持多客户端连接
|
||
3. 视觉算法的准确性直接影响整个调度流程的效果
|
||
4. Go与C#之间的TCP长连接支持自动重连(默认10秒间隔)
|
||
5. 红外测距仪读数稳定判断:连续10次相邻读数差值<0.5mm
|
||
6. 激光传感器有效范围:左/右均在(850, 1000]mm
|
||
7. 原木长度 = D2134寄存器值 - 315mm(固定偏移)
|
||
8. 顶针裁剪量根据扫描方向不同:left扫描(低Y端215mm+高Y端172mm),right扫描(低Y端170mm+高Y端209mm)
|
||
|
||
## TCP视觉算法数据接口
|
||
|
||
### 接口概述
|
||
|
||
上位机(go-auto)作为TCP客户端,连接视觉算法服务端(LogMax3D, 监听9000端口),通过JSON行协议(`\n`分隔)进行双向通信。
|
||
|
||
**通信模型**:
|
||
- **Go → C#**: 上位机发送请求(`center_photo_taking`、`start_vision_algorithm`、`order_info`)
|
||
- **C# → Go**: 算法推送异步结果(`center_photo_result`、`capture_ready`、`algorithm_coordinates`、`board_split_yield_result`)
|
||
|
||
### 通用消息格式
|
||
|
||
#### 请求格式(Go → C#)
|
||
|
||
```json
|
||
{
|
||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||
"action": "order_info",
|
||
"data": { ... },
|
||
"timestamp": 1762158534297
|
||
}
|
||
```
|
||
|
||
| 参数 | 类型 | 必填 | 说明 |
|
||
| ---- | ---- | ---- | ---- |
|
||
| id | string | 是 | 请求唯一标识符(UUID) |
|
||
| action | string | 是 | 动作类型 |
|
||
| data | object | 否 | 请求数据,根据不同action有不同结构 |
|
||
| timestamp | number | 是 | 请求时间戳(毫秒) |
|
||
|
||
#### 响应格式(C# → Go)
|
||
|
||
```json
|
||
{
|
||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||
"action": "algorithm_coordinates",
|
||
"data": { ... },
|
||
"timestamp": 1762308230194,
|
||
"error_id": ""
|
||
}
|
||
```
|
||
|
||
| 参数 | 类型 | 必填 | 说明 |
|
||
| ---- | ---- | ---- | ---- |
|
||
| id | string | 是 | 对应请求的ID |
|
||
| action | string | 是 | 动作类型 |
|
||
| data | object | 否 | 响应数据 |
|
||
| timestamp | number | 是 | 时间戳(毫秒) |
|
||
| error_id | string | 否 | 错误码:空字符串=成功,非空=错误标识 |
|
||
|
||
---
|
||
|
||
### 支持的动作类型
|
||
|
||
#### 1. center_photo_taking - 视觉取中拍照命令(Go → C#)
|
||
|
||
**功能说明:** 上位机下发视觉取中拍照命令,触发C#端图漾深度相机拍照并计算原木端面中心坐标。
|
||
|
||
**触发条件:** M120上升沿 && 激光传感器读数左/右均在(850,1000]mm
|
||
|
||
**调用方法:** `visionClient.SendRequestWithRetry(request)` → C# `HandleCenterPhotoTaking()`
|
||
|
||
**请求示例:**
|
||
```json
|
||
{
|
||
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
|
||
"action": "center_photo_taking",
|
||
"data": {
|
||
"wood_id": "WOOD_20251103_162602_004_2ace914e",
|
||
"left": {
|
||
"x": 55050,
|
||
"y": 32849,
|
||
"z": 0,
|
||
"d": 910.5
|
||
},
|
||
"right": {
|
||
"x": 55050,
|
||
"y": 32849,
|
||
"z": 0,
|
||
"d": 912.3
|
||
}
|
||
},
|
||
"timestamp": 1762158362326
|
||
}
|
||
```
|
||
|
||
**data参数说明:**
|
||
|
||
| 参数 | 类型 | 必填 | 说明 |
|
||
| ---- | ---- | ---- | ---- |
|
||
| wood_id | string | 是 | 原木唯一标识ID |
|
||
| left | object | 是 | 左相机在机械坐标系下的绝对位置 |
|
||
| left.x | number | 是 | 左相机X坐标(PLC D2016) |
|
||
| left.y | number | 是 | 左相机Y坐标(PLC D2018) |
|
||
| left.z | number | 是 | 左相机Z坐标(PLC D2126) |
|
||
| left.d | number | 否 | 左侧激光传感器读数(mm) |
|
||
| right | object | 是 | 右相机在机械坐标系下的绝对位置 |
|
||
| right.x | number | 是 | 右相机X坐标(PLC D2020) |
|
||
| right.y | number | 是 | 右相机Y坐标(PLC D2022) |
|
||
| right.z | number | 是 | 右相机Z坐标(PLC D2128) |
|
||
| right.d | number | 否 | 右侧激光传感器读数(mm) |
|
||
|
||
**即时响应(ACK):**
|
||
```json
|
||
{
|
||
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
|
||
"action": "center_scanning",
|
||
"data": {},
|
||
"timestamp": 1762158362400,
|
||
"error_id": ""
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### 2. center_photo_result - 视觉对中结果推送(C# → Go)
|
||
|
||
**功能说明:** C#端异步完成拍照和端面中心计算后,主动推送视觉顶针机构目标坐标给上位机。
|
||
|
||
**调用方法:** C# `SendCenteringResult()` → Go `handleCenterPhotoResult()`
|
||
|
||
**请求示例:**
|
||
```json
|
||
{
|
||
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
|
||
"action": "center_photo_result",
|
||
"data": {
|
||
"left": {
|
||
"x": 509.34,
|
||
"y": 1330.28,
|
||
"z": 450.0,
|
||
"diameter": 345.6
|
||
},
|
||
"right": {
|
||
"x": 512.15,
|
||
"y": 1325.40,
|
||
"z": 455.0,
|
||
"diameter": 340.2
|
||
},
|
||
"x_diameter": 348.5,
|
||
"y_diameter": 342.1,
|
||
"wood_id": "WOOD_20251103_162602_004_2ace914e",
|
||
"reg_timestamp": 1762158362326,
|
||
"recv_timestamp": 1762308226907,
|
||
"send_timestamp": 1762308230194
|
||
},
|
||
"timestamp": 1762308230194,
|
||
"error_id": ""
|
||
}
|
||
```
|
||
|
||
**data参数说明:**
|
||
|
||
| 参数 | 类型 | 必填 | 说明 |
|
||
| ---- | ---- | ---- | ---- |
|
||
| left | object | 是 | 左端面中心点结果 |
|
||
| left.x | number | 是 | 左视觉顶针机构目标X坐标(mm) = 相机位置X + RT变换偏移X |
|
||
| left.y | number | 是 | 左视觉顶针机构目标Y坐标(mm) = 相机位置Y + RT变换偏移Y |
|
||
| left.z | number | 是 | 相机到原木端面距离(mm) |
|
||
| left.diameter | number | 否 | 左端面直径(mm) = (X径+Y径)/2 |
|
||
| right | object | 是 | 右端面中心点结果 |
|
||
| right.x | number | 是 | 右视觉顶针机构目标X坐标(mm) |
|
||
| right.y | number | 是 | 右视觉顶针机构目标Y坐标(mm) |
|
||
| right.z | number | 是 | 相机到原木端面距离(mm) |
|
||
| right.diameter | number | 否 | 右端面直径(mm) |
|
||
| x_diameter | number | 否 | X方向直径(mm) |
|
||
| y_diameter | number | 否 | Y方向直径(mm) |
|
||
| wood_id | string | 是 | 原木标识 |
|
||
| reg_timestamp | number | 否 | 请求时间戳(来自上位机原始请求) |
|
||
| recv_timestamp | number | 否 | 视觉系统接收时间戳 |
|
||
| send_timestamp | number | 否 | 视觉系统发送结果时间戳 |
|
||
|
||
**Go端处理逻辑:**
|
||
1. 直径单位转换:`diameter / 10`(mm → cm)
|
||
2. 设置全局原木直径、X轴直径、Y轴直径
|
||
3. 触发托料装置升高
|
||
4. 调用`WriteVisionPinCoordinate()`写入顶针坐标至D2024/D2026/D2028/D2030
|
||
5. 计算轨道目标坐标写入D2168/D2170
|
||
6. 写入M49启动顶料
|
||
|
||
---
|
||
|
||
#### 3. start_vision_algorithm - 开始视觉扫描(Go → C#)
|
||
|
||
**功能说明:** 上位机通知视觉算法开始3D扫描,C#端异步启动4台相机进入持续采集模式。
|
||
|
||
**触发条件:** M50上升沿 && D2130<1600 && D2042>=2300
|
||
|
||
**调用方法:** `visionClient.SendRequestWithRetry(request)` → C# `HandleStartVision()`
|
||
|
||
**请求示例:**
|
||
```json
|
||
{
|
||
"id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
|
||
"action": "start_vision_algorithm",
|
||
"data": {
|
||
"wood_id": "WOOD_20251103_162602_004_2ace914e"
|
||
},
|
||
"timestamp": 1762158400000
|
||
}
|
||
```
|
||
|
||
**data参数说明:**
|
||
|
||
| 参数 | 类型 | 必填 | 说明 |
|
||
| ---- | ---- | ---- | ---- |
|
||
| wood_id | string | 是 | 原木唯一标识ID |
|
||
|
||
**即时响应(ACK):**
|
||
```json
|
||
{
|
||
"id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
|
||
"action": "vision_scanning",
|
||
"data": {
|
||
"status": "scanning"
|
||
},
|
||
"timestamp": 1762158400100,
|
||
"error_id": ""
|
||
}
|
||
```
|
||
|
||
**异步响应(相机就绪后推送):**
|
||
```json
|
||
{
|
||
"id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
|
||
"action": "capture_ready",
|
||
"data": {
|
||
"status": "ready"
|
||
},
|
||
"timestamp": 1762158401500,
|
||
"error_id": ""
|
||
}
|
||
```
|
||
|
||
> **说明:** `capture_ready`的`status`字段可能为`ready`(成功)或`error`(失败)。Go端收到`ready`后写入M103启动扫描轨道,收到`error`则不写入M103。
|
||
|
||
**Go端capture_ready处理逻辑:**
|
||
1. 检查`error_id`和`status`字段
|
||
2. 读取`pendingScanStart`(D2138方向判断结果)
|
||
3. 写入M103=true启动扫描轨道
|
||
4. 启动M104监控goroutine,等待工单推送触发
|
||
|
||
---
|
||
|
||
#### 4. order_info - 工单信息下发(Go → C#)
|
||
|
||
**功能说明:** 上位机下发工单信息,代表扫描结束。C#端停止采集并执行建模+切割优化算法。
|
||
|
||
**触发条件:** M104上升沿 && M42=true
|
||
|
||
**调用方法:** `visionClient.SendRequestWithRetry(request)` → C# `HandleOrderInfo()`
|
||
|
||
**请求示例:**
|
||
```json
|
||
{
|
||
"id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
|
||
"action": "order_info",
|
||
"data": {
|
||
"wood_id": "WOOD_20251103_162602_004_2ace914e",
|
||
"order_sn": "SO20251101000002",
|
||
"order_spec": "38*89*3.98",
|
||
"sub_spec": ["38*89*3.98", "37*66*4.1"],
|
||
"has_bark": true,
|
||
"grade": "等级A",
|
||
"start_distance": 115,
|
||
"end_distance": 115,
|
||
"length": 3785.0,
|
||
"saw_thickness": 1.8,
|
||
"scan_start": "left"
|
||
},
|
||
"timestamp": 1762158534297
|
||
}
|
||
```
|
||
|
||
**data参数说明:**
|
||
|
||
| 参数 | 类型 | 必填 | 说明 |
|
||
| ---- | ---- | ---- | ---- |
|
||
| wood_id | string | 是 | 原木唯一标识ID |
|
||
| order_sn | string | 是 | 工单编号 |
|
||
| order_spec | string | 是 | 工单主规格(格式:厚*宽*长),如"38*89*3.98" |
|
||
| sub_spec | []string | 否 | 副规格列表,算法对比主规格与各副规格选出材率最高方案 |
|
||
| has_bark | bool | 否 | 是否带树皮(默认true) |
|
||
| grade | string | 否 | 原木等级(如"等级A") |
|
||
| start_distance | number | 否 | 起始距离(mm):侧面相机至原木端面距离(常量115mm) |
|
||
| end_distance | number | 否 | 结束距离(mm):侧面相机至原木端面距离(常量115mm) |
|
||
| length | number | 否 | 原木长度(mm):D2134寄存器值 - 315 |
|
||
| saw_thickness | number | 否 | 锯片厚度(mm),默认1.8 |
|
||
| scan_start | string | 否 | 初始扫描方向:"left"或"right"(基于D2138判断) |
|
||
|
||
**即时响应(ACK):**
|
||
```json
|
||
{
|
||
"id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
|
||
"action": "algorithm_coordinates_run",
|
||
"data": {
|
||
"status": "processing"
|
||
},
|
||
"timestamp": 1762158534400,
|
||
"error_id": ""
|
||
}
|
||
```
|
||
|
||
> **说明:** `order_info`代表扫描结束,C#收到后立即响应ACK,然后异步执行:停止采集→等待帧就绪→点云融合→建模→顶针裁剪→多规格切割优化→推送结果。
|
||
|
||
---
|
||
|
||
#### 5. algorithm_coordinates - 算法切割坐标结果(C# → Go)
|
||
|
||
**功能说明:** C#端切割优化算法计算完成后,主动推送最优切割方案给上位机。
|
||
|
||
**调用方法:** C# `TcpServer.SendToClientAsync()` → Go `handleAlgorithmCoordinates()`
|
||
|
||
**请求示例:**
|
||
```json
|
||
{
|
||
"id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
|
||
"action": "algorithm_coordinates",
|
||
"data": {
|
||
"left": [120.5, 80.3, 40.1],
|
||
"right": [15.2, 55.8, 96.4, 137.0],
|
||
"rotation_angle": 15.5,
|
||
"available_volume": 0.85,
|
||
"total_volume": 1.205,
|
||
"yield_rate": 70.5,
|
||
"wood_id": "WOOD_20251103_162602_004_2ace914e"
|
||
},
|
||
"timestamp": 1762158540000,
|
||
"error_id": ""
|
||
}
|
||
```
|
||
|
||
**data参数说明:**
|
||
|
||
| 参数 | 类型 | 必填 | 说明 |
|
||
| ---- | ---- | ---- | ---- |
|
||
| left | []number | 是 | 左侧锯片坐标数组(mm),距中心距离,1-15个元素 |
|
||
| right | []number | 是 | 右侧锯片坐标数组(mm),距中心距离,1-15个元素 |
|
||
| rotation_angle | number | 是 | 旋转角度(度) |
|
||
| available_volume | number | 是 | 可用材积(m³) |
|
||
| total_volume | number | 否 | 整根原木材积(m³),基于middle Y截面R计算 |
|
||
| yield_rate | number | 否 | 出材率(%),如70.5表示70.5% |
|
||
| wood_id | string | 否 | 原木ID |
|
||
|
||
**Go端处理逻辑:**
|
||
1. 验证`error_id`为空
|
||
2. 解析`left`/`right`数组(长度1-15)并转换为float32
|
||
3. 保存视觉数据到数据库(坐标JSON、旋转角度、材积、径级)
|
||
4. 推送出材率到LED面板
|
||
5. 写入D2038旋转角度(回读验证,最多重试5次,容差0.01度)
|
||
6. 写入M45=true执行旋转
|
||
7. 写入D2136原木直径
|
||
8. 启动M46上升沿监控(旋转完成后触发立锯调度)
|
||
9. 异步计算A/B组夹臂伺服坐标
|
||
|
||
---
|
||
|
||
#### 6. board_split_yield_result - 板材拆分出材率(C# → Go)
|
||
|
||
**功能说明:** C#端异步完成板材拆分计算后,推送出材率结果给上位机。此消息在`algorithm_coordinates`之后异步推送,不阻塞主流程。
|
||
|
||
**调用方法:** C# `BoardSplitYieldService.RunAsync()` → Go `handleBoardSplitYieldResult()`
|
||
|
||
**请求示例:**
|
||
```json
|
||
{
|
||
"id": "",
|
||
"action": "board_split_yield_result",
|
||
"data": {
|
||
"status": "completed",
|
||
"wood_id": "WOOD_20251103_162602_004_2ace914e",
|
||
"yield_rate": 68.5,
|
||
"n_boards": 6,
|
||
"n_blocks": 42,
|
||
"block_volume": 0.583,
|
||
"available_volume": 0.85,
|
||
"ply_file": "data/modeling/WOOD_20251103_162602_004_2ace914e/board_split.ply",
|
||
"png_file": "data/modeling/WOOD_20251103_162602_004_2ace914e/board_split.png"
|
||
},
|
||
"timestamp": 1762158545000,
|
||
"error_id": ""
|
||
}
|
||
```
|
||
|
||
**data参数说明:**
|
||
|
||
| 参数 | 类型 | 必填 | 说明 |
|
||
| ---- | ---- | ---- | ---- |
|
||
| status | string | 是 | 计算状态:"completed"=完成,"error"=错误 |
|
||
| wood_id | string | 是 | 原木唯一标识ID |
|
||
| yield_rate | number | 是 | 板材拆分出材率(%) |
|
||
| n_boards | number | 是 | 板材数量 |
|
||
| n_blocks | number | 是 | 木方数量 |
|
||
| block_volume | number | 是 | 木方总体积(m³) |
|
||
| available_volume | number | 是 | 可用材积(m³) |
|
||
| ply_file | string | 否 | PLY点云可视化文件路径 |
|
||
| png_file | string | 否 | PNG截面图文件路径 |
|
||
|
||
---
|
||
|
||
#### 7. connection_ack - 连接确认(C# → Go)
|
||
|
||
**功能说明:** TCP连接建立后,C#端立即推送连接确认消息。
|
||
|
||
**请求示例:**
|
||
```json
|
||
{
|
||
"id": "",
|
||
"action": "connection_ack",
|
||
"data": null,
|
||
"timestamp": 1762158000000,
|
||
"error_id": ""
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 错误处理
|
||
|
||
#### 错误响应格式
|
||
|
||
当请求处理失败时,C#端通过`error_id`字段标识错误:
|
||
|
||
```json
|
||
{
|
||
"id": "req001",
|
||
"action": "algorithm_coordinates",
|
||
"data": {},
|
||
"timestamp": 1705123456789,
|
||
"error_id": "err_algorithm"
|
||
}
|
||
```
|
||
|
||
#### 常见错误码
|
||
|
||
| error_id | 说明 | 触发场景 |
|
||
| -------- | ---- | -------- |
|
||
| `""` (空字符串) | 成功 | 正常处理 |
|
||
| `err_unknown_action` | 未知动作类型 | action不在支持列表中 |
|
||
| `err_empty_data` | 请求data为空 | order_info缺少data |
|
||
| `err_parse_order` | 解析OrderData失败 | JSON格式错误 |
|
||
| `err_algorithm` | 算法计算异常 | 切割优化过程异常 |
|
||
| `err_no_data` | 未采集到有效数据 | 相机未采集到帧数据 |
|
||
| `err_no_model` | 无原木3D模型 | 未收到start_vision_algorithm |
|
||
| `err_start_capture` | 启动采集失败 | capture_ready推送error状态 |
|
||
| `err003` | 对中计算异常 | CenteringService计算过程异常 |
|
||
| `err004` | 对中服务未初始化 | 图漾相机未连接或未启用 |
|
||
|
||
### 完整调用链路时序
|
||
|
||
```
|
||
Go(上位机) C#(视觉算法)
|
||
| |
|
||
| ── center_photo_taking ──────────────────► | 对中拍照请求
|
||
| ◄── center_scanning (ACK) ─────────────── | 立即响应
|
||
| | 异步: 拍照+计算
|
||
| ◄── center_photo_result ────────────────── | 推送对中坐标
|
||
| |
|
||
| ── start_vision_algorithm ───────────────► | 开始扫描请求
|
||
| ◄── vision_scanning (ACK) ─────────────── | 立即响应
|
||
| | 异步: 启动相机采集
|
||
| ◄── capture_ready ──────────────────────── | 相机就绪信号
|
||
| (写入M103启动扫描轨道) |
|
||
| |
|
||
| ── order_info ───────────────────────────► | 工单信息(扫描结束)
|
||
| ◄── algorithm_coordinates_run (ACK) ────── | 立即响应
|
||
| | 异步: 停止采集→建模→切割优化
|
||
| ◄── algorithm_coordinates ──────────────── | 推送切割坐标+材积
|
||
| ◄── board_split_yield_result ───────────── | 推送板材拆分出材率
|
||
| |
|
||
```
|
||
|
||
### 接口使用注意事项
|
||
|
||
1. **数据格式**:所有请求为JSON行协议,每条消息以`\n`结尾
|
||
2. **坐标精度**:坐标值支持float64精度,Go端内部转换为float32写入PLC
|
||
3. **TCP长连接**:Go端使用带自动重连的TCP客户端,断线自动重连(默认10秒间隔)
|
||
4. **异步推送**:C#端收到请求后立即返回ACK,计算完成后异步推送结果,Go端通过注册的`asyncHandler`处理
|
||
5. **请求ID**:使用UUID生成唯一请求ID,便于追踪和调试
|
||
6. **超时处理**:Go端`SendRequestWithRetry`支持重试机制(可配置重试次数和间隔)
|
||
7. **线程安全**:C#端使用SemaphoreSlim保证StreamWriter写入线程安全
|
||
8. **连接清理**:C#端每60秒自动清理已断开的客户端连接,防止僵尸连接积累
|
||
9. **直径单位**:C#推送的直径单位为mm,Go端接收后除以10转换为cm存储
|
||
10. **扫描方向**:scan_start由D2138寄存器判断,>-2250为"left",<=-2250为"right"
|