From edb01463ff8aea869ca39778ea4f006ba674cd44 Mon Sep 17 00:00:00 2001 From: xtkuang <87661715@qq.com> Date: Fri, 31 Jul 2026 09:39:44 +0800 Subject: [PATCH] docs: move hardware guides into component directories Keep the root README concise while documenting MotorService, the Modbus TCP PLC runtime, the motor stack, and AUBO cabinet IO next to their owning code. --- README.md | 123 ++--------------- cmvr-es/devices/README.md | 4 +- cmvr-es/devices/arm/aubo_arm/README.md | 124 ++++++++++++++++++ cmvr-es/devices/motor/README.md | 82 ++++++++++++ .../motor/bus_runtime/modbus_tcp/README.md | 93 +++++++++++++ cmvr-es/service/README.md | 36 +++++ 6 files changed, 347 insertions(+), 115 deletions(-) create mode 100644 cmvr-es/devices/arm/aubo_arm/README.md create mode 100644 cmvr-es/devices/motor/README.md create mode 100644 cmvr-es/devices/motor/bus_runtime/modbus_tcp/README.md diff --git a/README.md b/README.md index 5d0468e2..9da1699d 100644 --- a/README.md +++ b/README.md @@ -139,119 +139,16 @@ sudo script/ethercat/stop_ethercat.sh eno1 sudo script/ethercat/stop_ethercat.sh eno1 --restore-network ``` -## MotorService 与 Modbus TCP PLC +## 组件文档 -工程包含从 gRPC `MotorService`、`MotorManager`、`AbstractMotor` 到 -`ModbusTcpMotorBusRuntime` 的 CMVR PLC v1 电机控制链,x86-64 的 libmodbus -3.1.11 已放在 `dependency/x86/third_party/modbus/3.1.11`。 +具体能力、配置、协议和安全边界由对应代码目录下的 README 维护: -PLC 对接时特别注意: +- [MotorService gRPC 接口](cmvr-es/service/README.md#motorservice) +- [电机设备模块](cmvr-es/devices/motor/README.md) +- [Modbus TCP PLC runtime](cmvr-es/devices/motor/bus_runtime/modbus_tcp/README.md) +- [MotorService 与 CMVR PLC v1 完整协议](docs/motor_service_modbus_tcp.md) +- [AUBO 控制柜 Standard 数字 IO](cmvr-es/devices/arm/aubo_arm/README.md) +- [配置与部署规则](cmvr-es/config/README.md) -- `host` 必须配置为 IPv4 字面量,PLC boot ID 必须非零且每次重启变化; -- owner 决策、命令 ACK 都必须回显对应 session,重连不得执行旧 mailbox; -- PLC 在进程启动时可以离线;连接 supervisor 会继续退避重试,离线期间状态 - 返回不可用且运动命令不会写 mailbox; -- 状态区按 odd/even seqlock 发布,上位机使用 - sequence-before → 64-word block → sequence-after 三段读取验证; -- 每次 `OpenCyclicPosition/Velocity` 创建新 stream epoch,PLC 必须原子清零 - `last_applied_cyclic_sequence`、旧样本去重状态和 cyclic watchdog,确认 - `StreamActive` 与正确 mode 后才 ACK;重开后的首样本序列从 `1` 开始并必须 - 重新应用; -- 活动 cyclic 流跨 `connection_epoch` 后不会自动重开;旧流的当前和后续 - setpoint 均被拒绝并在 Quick Stop 后终止,客户端必须新建 gRPC 流。断链前 - 或断链期间 pending 的 setpoint 不会应用到新 session; -- 任何清理 Quick Stop 未确认时,MotorService 都会 fail-closed 锁存,并在 - 成功执行 `setEnabled(true)` 前拒绝新的运动命令; -- Modbus Quick Stop 只是功能性停止,不能替代硬接线急停或驱动器 STO。 -- 当前 Modbus 后端只提供 x86-64 的 libmodbus 3.1.11;`dependency/arm` - 尚无对应库,因此 ARM 构建暂不支持该后端。 - -完整 gRPC 语义、配置样例、寄存器表、TIA Portal 要求、构建测试和安全边界见 -[`docs/motor_service_modbus_tcp.md`](docs/motor_service_modbus_tcp.md)。 - -## AUBO 控制柜 IO - -`AuboArm` 通过通用的 `executeJsonCommand` 接口提供控制柜 Standard 数字 IO -读写。第一版支持以下命令: - -| `operation` | 说明 | 必填字段 | -| --- | --- | --- | -| `get_di` | 读取控制柜数字输入 | `index` | -| `get_do` | 读取控制柜数字输出及其 runstate | `index` | -| `set_do` | 设置控制柜数字输出 | `index`、`value` | - -JSON 命令示例: - -```json -{"command":"cabinet_io","operation":"get_di","index":0} -{"command":"cabinet_io","operation":"get_do","index":0} -{"command":"cabinet_io","operation":"set_do","index":0,"value":true} -``` - -其中 `index` 从 `0` 开始,运行时会根据控制器返回的 IO 数量检查范围。 -`set_do.value` 必须是 JSON 布尔值 `true` 或 `false`,不接受 `0/1` 或字符串。 -`set_do` 成功响应中的 `requested_value` 表示 SDK 已接受的请求值;需要确认控制器 -当前输出状态时,再调用一次 `get_do` 读取实际值。 - -成功响应示例: - -```json -{ - "success": true, - "command": "cabinet_io", - "operation": "get_di", - "index": 0, - "count": 16, - "value": false -} -``` - -### 通过 gRPC 调用 - -该功能复用 `cmvr.api.SystemService/ExecuteJsonCommand`。默认配置中的 gRPC -端口是 `50052`,读取 DI0: - -```shell -grpcurl -plaintext \ - -d '{ - "header":{"deviceId":"aubo_arm"}, - "requestJson":"{\"command\":\"cabinet_io\",\"operation\":\"get_di\",\"index\":0}" - }' \ - 127.0.0.1:50052 \ - cmvr.api.SystemService/ExecuteJsonCommand -``` - -设置 DO0 为高电平: - -```shell -grpcurl -plaintext \ - -d '{ - "header":{"deviceId":"aubo_arm"}, - "requestJson":"{\"command\":\"cabinet_io\",\"operation\":\"set_do\",\"index\":0,\"value\":true}" - }' \ - 127.0.0.1:50052 \ - cmvr.api.SystemService/ExecuteJsonCommand -``` - -使用源码树默认配置时,先在 `cmvr-es/config/manager/device_manager.pb.txt` 中把 -`aubo_arm` 的 `enable` 改为 `true`,并在 -`cmvr-es/config/devices/arm/aubo_arm.pb.txt` 中配置正确的控制器地址和登录信息, -然后重新安装配置并启动安装产物: - -```shell -cmake --install build -./output/bin/cmvr_es -``` - -`output/bin/cmvr_es` 读取的是 `output/bin/config/`;如果进程使用显式配置路径, -应修改该配置根下的对应文件。 -设备未启用或初始化失败时,gRPC 会返回 `Device not found: aubo_arm`。 - -### 安全约束 - -- 该接口只访问控制柜 Standard 数字 IO,不操作工具端 IO、可配置 IO 或安全 IO。 -- `set_do` 不会修改控制器的输出 runstate。只有目标通道的 runstate 为 - `StandardOutputRunState::None` 时才允许写入,否则返回 - `output_managed_by_runstate`。 -- 接口不会调用会重置全部输出配置的 `setDigitalOutputRunstateDefault()`。 -- 模拟量 IO 涉及 domain、单位和量程,第一版暂不通过该 JSON 接口开放。 +Modbus Quick Stop 只是功能性停止,不能替代硬接线急停或驱动器 STO。AUBO +JSON 接口只访问控制柜 Standard 数字 IO,不访问安全 IO。 diff --git a/cmvr-es/devices/README.md b/cmvr-es/devices/README.md index aaf43066..1fa6ac7c 100644 --- a/cmvr-es/devices/README.md +++ b/cmvr-es/devices/README.md @@ -37,12 +37,12 @@ config/cmvr_es.pb.txt | --- | --- | --- | --- | | Camera | [`camera/abstract_camera.h`](camera/abstract_camera.h) | [`camera/camera_factory.h`](camera/camera_factory.h) | UVC、RealSense、Hikvision | | AGV | [`agv/abstract_agv.h`](agv/abstract_agv.h) | [`agv/agv_factory.h`](agv/agv_factory.h) | MyAgv、SRC1100 | -| RobotArm | [`arm/robot_arm.h`](arm/robot_arm.h) | [`arm/robot_arm_factory.h`](arm/robot_arm_factory.h) | MotorRobotArm、AUBO、Huayan | +| RobotArm | [`arm/robot_arm.h`](arm/robot_arm.h) | [`arm/robot_arm_factory.h`](arm/robot_arm_factory.h) | MotorRobotArm、[AUBO](arm/aubo_arm/README.md)、Huayan、UME | | DexHand | [`dexhand/abstract_dexhand.h`](dexhand/abstract_dexhand.h) | [`dexhand/dexhand_factory.h`](dexhand/dexhand_factory.h) | RH56DFTP、PX6AXGen3 | | Microphone | [`microphone/abstract_microphone.h`](microphone/abstract_microphone.h) | [`microphone/microphone_factory.h`](microphone/microphone_factory.h) | FFmpeg | | Speaker | [`speaker/abstract_speaker.h`](speaker/abstract_speaker.h) | [`speaker/speaker_factory.h`](speaker/speaker_factory.h) | FFmpeg | | BioHead | [`biohead/abstract_biohead.h`](biohead/abstract_biohead.h) | DeviceFactory 直接创建 | BioHeadRobot | -| MotorSystem | `motor/motor_system/` | DeviceFactory 直接创建 | CAN/MuJoCo motor group | +| MotorSystem | [`motor/`](motor/README.md) | DeviceFactory 直接创建 | CAN、MuJoCo、EtherCAT、Modbus TCP PLC | 代码目录存在不等于已经接入配置创建链: diff --git a/cmvr-es/devices/arm/aubo_arm/README.md b/cmvr-es/devices/arm/aubo_arm/README.md new file mode 100644 index 00000000..6d63e6b7 --- /dev/null +++ b/cmvr-es/devices/arm/aubo_arm/README.md @@ -0,0 +1,124 @@ +# AUBO RobotArm 与控制柜 IO + +`AuboArm` 是 AUBO SDK v0.27.1 的 `RobotArm` 后端。控制柜 Standard 数字 IO +通过设备通用的 `executeJsonCommand` 接口访问,远程调用复用 +`cmvr.api.SystemService/ExecuteJsonCommand`,不经过 `ArmService` 或 +`MotorService`。 + +返回 [Devices 模块指南](../../README.md) 或 [项目总览](../../../../README.md)。 + +## 代码与配置 + +- 实现:[`aubo_arm.h`](aubo_arm.h)、[`aubo_arm.cpp`](aubo_arm.cpp) +- 测试:[`tests/aubo_arm_json_command_test.cpp`](tests/aubo_arm_json_command_test.cpp) +- 设备配置:[`../../../config/devices/arm/aubo_arm.pb.txt`](../../../config/devices/arm/aubo_arm.pb.txt) +- DeviceManager 配置: + [`../../../config/manager/device_manager.pb.txt`](../../../config/manager/device_manager.pb.txt) +- SystemService 实现: + [`../../../service/grpc/src/grpc_system_service.cpp`](../../../service/grpc/src/grpc_system_service.cpp) +- Proto:[`../../../../protos/cmvr/api/system_service.proto`](../../../../protos/cmvr/api/system_service.proto) + +仓库配置使用 SDK RPC 端口 `30004`。现场部署必须填写真实控制器地址和凭据, +不要把生产密码提交到默认配置。 + +## 控制柜 Standard 数字 IO + +当前支持: + +| `operation` | 说明 | 必填字段 | +| --- | --- | --- | +| `get_di` | 读取控制柜数字输入 | `index` | +| `get_do` | 读取控制柜数字输出及其 runstate | `index` | +| `set_do` | 设置控制柜数字输出 | `index`、`value` | + +JSON 命令: + +```json +{"command":"cabinet_io","operation":"get_di","index":0} +{"command":"cabinet_io","operation":"get_do","index":0} +{"command":"cabinet_io","operation":"set_do","index":0,"value":true} +``` + +`index` 从 `0` 开始,运行时根据控制器返回的 IO 数量检查范围。 +`set_do.value` 必须是 JSON 布尔值 `true` 或 `false`,不接受 `0/1` 或字符串。 +`set_do` 成功响应中的 `requested_value` 只表示 SDK 已接受请求;确认实际输出时 +必须再调用 `get_do`。 + +读取成功响应示例: + +```json +{ + "success": true, + "command": "cabinet_io", + "operation": "get_di", + "index": 0, + "count": 16, + "value": false +} +``` + +## 通过 gRPC 调用 + +默认 gRPC 端口为 `50052`。读取 DI0: + +```shell +grpcurl -plaintext \ + -d '{ + "header":{"deviceId":"aubo_arm"}, + "requestJson":"{\"command\":\"cabinet_io\",\"operation\":\"get_di\",\"index\":0}" + }' \ + 127.0.0.1:50052 \ + cmvr.api.SystemService/ExecuteJsonCommand +``` + +设置 DO0 为高电平: + +```shell +grpcurl -plaintext \ + -d '{ + "header":{"deviceId":"aubo_arm"}, + "requestJson":"{\"command\":\"cabinet_io\",\"operation\":\"set_do\",\"index\":0,\"value\":true}" + }' \ + 127.0.0.1:50052 \ + cmvr.api.SystemService/ExecuteJsonCommand +``` + +使用源码默认配置时: + +1. 在 `cmvr-es/config/devices/arm/aubo_arm.pb.txt` 填写正确地址和登录信息; +2. 在 `cmvr-es/config/manager/device_manager.pb.txt` 将 `aubo_arm.enable` + 改为 `true`; +3. 重新安装配置并启动安装产物。 + +```shell +cmake --install build +./output/bin/cmvr_es +``` + +`output/bin/cmvr_es` 默认读取 `output/bin/config/`。使用 `--config` 时,应修改 +对应外部配置根。设备未启用或初始化失败时,gRPC 返回 +`Device not found: aubo_arm`。 + +## 安全与语义边界 + +- 只访问控制柜 Standard 数字 IO,不访问工具端 IO、可配置 IO 或安全 IO; +- `set_do` 不修改输出 runstate; +- 只有 `StandardOutputRunState::None` 的通道允许写入,否则返回 + `output_managed_by_runstate`; +- 普通访问不会调用会重置全部输出配置的 + `setDigitalOutputRunstateDefault()`; +- 模拟量 IO 涉及 domain、单位和量程,当前 JSON 接口不开放; +- gRPC/JSON 返回成功不代表目标 IO 具备功能安全等级; +- 真实写测试前应确认通道用途、负载、电气隔离、默认电平和控制器程序所有权。 + +## 测试 + +```bash +cmake --build build --target aubo_arm_json_command_test -j4 +ctest --test-dir build \ + -R '^aubo_arm_json_command_test$' \ + --output-on-failure +``` + +该测试覆盖 JSON 校验和无硬件错误路径,不代表已在真实 AUBO 控制柜完成 DI/DO +读取、写入或 runstate 拒绝验证。 diff --git a/cmvr-es/devices/motor/README.md b/cmvr-es/devices/motor/README.md new file mode 100644 index 00000000..a10ba7fc --- /dev/null +++ b/cmvr-es/devices/motor/README.md @@ -0,0 +1,82 @@ +# Motor 设备模块 + +`devices/motor/` 提供电机管理、协议适配、总线 runtime 和厂商驱动。Service、 +RobotArm 和业务 Task 只依赖 `MotorManager`/`AbstractMotor` 的稳定接口,不应 +直接访问 libmodbus、CAN、EtherCAT 或厂商 SDK。 + +返回 [Devices 模块指南](../README.md) 或 [项目总览](../../../README.md)。 + +## 目录职责 + +| 目录 | 职责 | +| --- | --- | +| `manager/` | 创建 MotorGroup,按 `motor_id`/`joint_name` 暴露 `AbstractMotor` | +| `bus_runtime/` | 连接、收发、重连、watchdog 和总线生命周期 | +| `drivers/` | CANopen、EtherCAT、MuJoCo、Modbus PLC 等具体后端 | +| `drivers/modbus_plc_motor/` | 关节限位、SI 单位和 CMVR PLC v1 命令映射 | + +PLC 电机链路为: + +```text +gRPC MotorService + | + v +MotorManager -> AbstractMotor + | + v +CmvrPlcMotorProtocol + | + v +ModbusTcpMotorBusRuntime + | + v +libmodbus -> PLC -> 驱动器/电机 +``` + +各层边界: + +- `MotorService` 负责 API 校验、单电机控制权、deadline/cancellation 和 + fail-closed Quick Stop; +- `MotorManager` 负责电机查找与统一抽象; +- `CmvrPlcMotorProtocol` 负责关节限位、SI 单位和 CMVR PLC v1 命令映射; +- `ModbusTcpMotorBusRuntime` 负责 PLC session、mailbox、ACK、状态和重连; +- PLC/驱动器必须独立实现通信 watchdog、周期 watchdog 和硬件安全动作。 + +## Modbus TCP PLC + +实现、配置、依赖和测试入口见: + +- [Modbus TCP runtime README](bus_runtime/modbus_tcp/README.md) +- [完整 MotorService/CMVR PLC v1 协议](../../../docs/motor_service_modbus_tcp.md) +- [MotorService 文档](../../service/README.md#motorservice) +- [`plc_motors.pb.txt`](../../config/devices/motor/plc_motors.pb.txt) + +当前 Modbus 后端只提供 x86-64 的 libmodbus 3.1.11。ARM 目录没有对应库, +不能把 x86 ELF 复制到 ARM 设备使用。 + +## 安全边界 + +- MotorService 是单轴 API,不提供多轴同扫描周期的原子 commit; +- PLC/Modbus 的 Profile 或 cyclic 能力不能直接当成毫秒级机械臂组伺服; +- 软件 `emergencyStop`、Quick Stop 和普通 PLC 输出都不是安全急停; +- 真实设备必须具有独立的硬接线急停、安全继电器或 F-CPU/F-I/O,以及驱动器 + STO 等经风险评估确定的安全链; +- 新硬件配置保持 `enable: false`,完成方向、限位、watchdog 和故障注入验证后 + 才能启用。 + +## 测试 + +```bash +cmake --build build --target \ + modbus_tcp_motor_bus_runtime_test \ + grpc_motor_service_test \ + grpc_motor_service_modbus_e2e_test \ + -j4 + +ctest --test-dir build \ + -R '^(modbus_tcp_motor_bus_runtime_test|grpc_motor_service_test|grpc_motor_service_modbus_e2e_test)$' \ + --output-on-failure +``` + +端到端 fake PLC 测试需要本地 TCP bind/listen 权限。软件测试不能代替真实 +S7-1215C、驱动器、STO 和断网故障台架。 diff --git a/cmvr-es/devices/motor/bus_runtime/modbus_tcp/README.md b/cmvr-es/devices/motor/bus_runtime/modbus_tcp/README.md new file mode 100644 index 00000000..f1ac4f18 --- /dev/null +++ b/cmvr-es/devices/motor/bus_runtime/modbus_tcp/README.md @@ -0,0 +1,93 @@ +# Modbus TCP PLC Runtime + +本目录实现 CMVR PLC v1 的 Modbus TCP 总线 runtime。它负责 PLC 连接、身份 +握手、session、心跳、重连、命令 mailbox、ACK 轮询和状态快照,不负责 gRPC +请求解析,也不提供功能安全急停。 + +返回 [Motor 设备模块](../../README.md) 或 [Devices 模块指南](../../../README.md)。 + +## 代码与配置 + +- [`include/modbus_tcp_client.h`](include/modbus_tcp_client.h):有界超时的 + libmodbus client +- [`include/modbus_tcp_motor_bus_runtime.h`](include/modbus_tcp_motor_bus_runtime.h): + 连接 supervisor、命令和状态 runtime +- [`include/cmvr_plc_register_map.h`](include/cmvr_plc_register_map.h): + CMVR PLC v1 寄存器常量 +- [`src/modbus_tcp_client.cpp`](src/modbus_tcp_client.cpp) +- [`src/modbus_tcp_motor_bus_runtime.cpp`](src/modbus_tcp_motor_bus_runtime.cpp) +- [`tests/modbus_tcp_motor_bus_runtime_test.cpp`](tests/modbus_tcp_motor_bus_runtime_test.cpp) +- [`../../../../config/devices/motor/plc_motors.pb.txt`](../../../../config/devices/motor/plc_motors.pb.txt) + +完整寄存器表、TIA Portal 数据块、gRPC 语义和台架步骤由 +[MotorService 与 CMVR PLC v1 完整协议](../../../../../docs/motor_service_modbus_tcp.md) +统一维护。 + +## 连接与协议约束 + +- `host` 必须是 IPv4 字面量,避免 DNS 让建连或停止出现无界等待; +- PLC boot ID 必须非零且每次 PLC 重启变化; +- owner 决策和命令 ACK 必须回显当前 session,旧 session 的 mailbox 不得执行; +- runtime `start()` 启动连接 supervisor,PLC 可以在进程启动时离线; +- 离线期间状态不可用,运动命令必须在写 mailbox 前失败; +- 重连必须重新完成身份、版本、boot ID 和 session 握手,不得重放旧命令; +- 状态区按 odd/even seqlock 发布,CMVR 使用 + sequence-before → 64-word block → sequence-after 三段读取; +- payload 必须先完整写入,commit sequence 最后写入,PLC 只原子消费新的 + commit; +- Quick Stop、Disable、故障和通信 watchdog 的状态必须通过当前 session + 的 ACK/状态确认,不能把本地写成功解释为驱动器已经安全停止。 + +## Cyclic stream + +每次 `OpenCyclicPosition/Velocity` 都创建新的轴级 stream epoch。PLC 必须在 +同一个原子状态事务中: + +1. 清零 `last_applied_cyclic_sequence`; +2. 清理旧样本去重状态; +3. 重置 cyclic watchdog; +4. 设置正确的 CSP/CSV mode; +5. 发布 `StreamActive=1` 后再 ACK。 + +重开后的首个样本序列从 `1` 开始,必须重新应用。活动 cyclic 流跨 +`connection_epoch` 后不会自动重开;旧流的当前和后续 setpoint 都被拒绝, +Quick Stop 后客户端必须建立新的 gRPC 流。断链前或断链期间 pending 的 +setpoint 不得进入新 session。 + +## 依赖与平台 + +x86-64 的 libmodbus 3.1.11 位于: + +```text +dependency/x86/third_party/modbus/3.1.11 +``` + +`dependency/arm/third_party/` 当前没有对应 libmodbus,因此 ARM 构建不支持 +该后端。支持 ARM 前必须为目标 ABI 单独编译并验证库,不能复用 x86 二进制。 + +## 安全边界 + +标准 S7-1215C DC/DC/DC 不是 failsafe PLC。Modbus Quick Stop、 +MotorService `emergencyStop`、普通 OB/FB 和普通数字输出都只是功能性控制, +不能替代: + +- 硬接线急停; +- 安全继电器或 F-CPU/F-I/O; +- 驱动器双通道 STO; +- 接触器、抱闸反馈与必要的 EDM。 + +PLC 侧通信 watchdog 和 cyclic watchdog 必须在没有 CMVR 进程参与时独立停止 +危险运动。真实启用前必须在禁能或脱载轴上验证寄存器、方向、限位、断网、PLC +重启、交换机故障和 Quick Stop 失败。 + +## 测试 + +```bash +cmake --build build --target modbus_tcp_motor_bus_runtime_test -j4 +ctest --test-dir build \ + -R '^modbus_tcp_motor_bus_runtime_test$' \ + --output-on-failure +``` + +fake PLC 测试需要本地 TCP bind/listen 权限。测试通过只证明软件协议和故障注入 +路径,不代表真实 PLC、驱动器或硬件安全链已经验收。 diff --git a/cmvr-es/service/README.md b/cmvr-es/service/README.md index 0ed71894..59959741 100644 --- a/cmvr-es/service/README.md +++ b/cmvr-es/service/README.md @@ -21,6 +21,42 @@ gRPC 和 QUIC 的职责边界: - 实时音视频使用 QUIC DATAGRAM; - `quic_edge/` 不是平台 Gateway,也不是浏览器服务器。 +## MotorService + +`MotorService` 将 gRPC 电机命令适配到已经由 `DeviceManager` 创建的 +`MotorManager` 和 `AbstractMotor`,不直接持有 PLC、现场总线或厂商驱动。 + +关键文件: + +- Proto:[`../../protos/cmvr/api/motor_service.proto`](../../protos/cmvr/api/motor_service.proto) + 和 [`../../protos/cmvr/api/motor_command.proto`](../../protos/cmvr/api/motor_command.proto) +- 实现:[`grpc/include/grpc_motor_service.h`](grpc/include/grpc_motor_service.h) + 和 [`grpc/src/grpc_motor_service.cpp`](grpc/src/grpc_motor_service.cpp) +- 注册:[`../task/grpc_server_task/src/grpc_server_task.cpp`](../task/grpc_server_task/src/grpc_server_task.cpp) +- 单元测试:[`grpc/tests/grpc_motor_service_test.cpp`](grpc/tests/grpc_motor_service_test.cpp) +- gRPC–Modbus 端到端测试: + [`grpc/tests/grpc_motor_service_modbus_e2e_test.cpp`](grpc/tests/grpc_motor_service_modbus_e2e_test.cpp) + +服务按单电机仲裁。同步 Profile 命令、Cyclic Position/Velocity 双向流、 +`setEnabled`、状态读取和软件 `emergencyStop` 共用同一控制权状态: + +- 同一电机已有 owner 时拒绝新的控制调用; +- cyclic 流首帧必须是 `open`,后续 setpoint sequence 必须严格递增; +- reader 使用 latest-wins 邮箱,客户端必须持续并发读取反馈; +- 取消、deadline、watchdog、非法帧、后端拒绝或写失败都会触发 Quick Stop; +- 任何清理 Quick Stop 未确认时,服务进入 fail-closed 锁存; +- 只有成功执行 `setEnabled(true)` 才解除服务内软件急停锁存; +- 服务层 Quick Stop 和 `emergencyStop` 都不具备功能安全等级。 + +PLC 后端的连接 epoch、stream epoch、寄存器、ACK 和 TIA Portal 要求见: + +- [Modbus TCP PLC runtime](../devices/motor/bus_runtime/modbus_tcp/README.md) +- [MotorService 与 CMVR PLC v1 完整协议](../../docs/motor_service_modbus_tcp.md) + +AUBO 控制柜 IO 不经过 `MotorService` 或 `ArmService`,而是复用 +`SystemService/ExecuteJsonCommand`。厂商命令和安全约束见 +[AUBO 控制柜 IO](../devices/arm/aubo_arm/README.md)。 + ## 新增 gRPC Service 当前没有动态 service registry,必须完成以下全部步骤。