cmvr-es/cmvr-es/service/README.md

11 KiB
Raw Permalink Blame History

Service 模块开发指南

service/ 实现边缘端对外协议和设备抽象之间的适配。Service 负责解析请求、查找设备、转换 DTO 和返回结果,不负责创建具体设备后端。

返回项目总览

当前结构

目录 职责
grpc/ 入站设备控制、状态查询和兼容流式接口
quic_edge/ 边缘端主动连接平台的 QUIC client、控制状态机和媒体 packetizer
quic_edge/tests/ 已登记到 CTest 的 QUIC 协议测试

两个遗留 gRPC client test 位于 grpc/src/*_client_test.cpp,当前没有通过 add_test() 登记。

gRPC 和 QUIC 的职责边界:

  • 机械臂、AGV 等可靠控制继续使用 gRPC
  • 节点注册、心跳和 IP 上报使用 QUIC reliable stream
  • 实时音视频使用 QUIC DATAGRAM
  • quic_edge/ 不是平台 Gateway也不是浏览器服务器。

SystemService 设备清单

SystemService/GetDeviceList 返回 DeviceManager 的当前只读快照,只包含 enabled=true 的设备。启用但创建、初始化、启动或健康检查失败的设备仍会返回, 并通过 manager_statehealthhas_errorerror_message 描述异常。 接口同时返回稳定的 device_type 和仅用于展示/诊断的具体 type_name;调用方 不得使用 type_name 做设备类别判断。

该 RPC 不修改配置、不动态注册设备,也不触发设备生命周期操作。启用 reflection 后可直接查询:

grpcurl -plaintext \
  -d '{}' \
  127.0.0.1:50052 \
  cmvr.api.SystemService/GetDeviceList

MotorService

MotorService 将 gRPC 电机命令适配到已经由 DeviceManager 创建的 MotorManagerAbstractMotor,不直接持有现场总线或厂商驱动。

关键文件:

服务按单电机仲裁。同步 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 都不具备功能安全等级。

AUBO 控制柜 IO 不经过 MotorService,由 ArmService/ExecuteJsonCommand 转发到目标 RobotArm。厂商命令和安全约束见 AUBO 控制柜 IO。 旧的 SystemService/ExecuteJsonCommand 已移除;相机 PTZ 应使用类型化的 CameraService/ControlPtz

新增 gRPC Service

当前没有动态 service registry必须完成以下全部步骤。

1. 定义 Proto

../../protos/cmvr/api/ 增加或扩展:

  • <domain>_command.proto
  • <domain>_service.proto

import 路径必须相对于 protos/。兼容规则见 ../../protos/README.md

2. 实现 Service

目录约定:

service/grpc/
├── include/grpc_example_service.h
└── src/grpc_example_service.cpp

实现类继承生成的:

cmvr::api::ExampleService::Service

通过已初始化的 DeviceManager 获取抽象设备。不要在 service 中创建厂商 SDK 对象,不要绕过设备 factory。

3. 加入 service target

将实现 .cpp 加入 CMakeLists.txtservice library并声明最小依赖。

4. 注册到 GrpcServerTask

还必须修改:

完成:

  1. 增加 service owner
  2. 在 start 中构造;
  3. 调用 builder.RegisterService(...)
  4. clearServices() 中 reset。

漏掉该步骤时项目可能编译成功,但服务不会出现在 reflection 或运行时。

5. 测试

  • 直接测试 service handler 或启动临时 gRPC server
  • 覆盖设备不存在、类型不匹配、设备错误和取消;
  • 使用 grpcurl/reflection 验证服务全名;
  • 流式 RPC 覆盖客户端断开和慢消费者;
  • 在 CMake 中使用 if(BUILD_TESTING) 包裹测试目标,并通过 add_test() 登记。

grpc_arm_client_testgrpc_hlc_client_test 是未登记到 CTest 的历史可执行文件,不能代表默认自动覆盖。

gRPC 实现约束

错误语义

当前历史服务存在两种风格:

  • gRPC status 返回 OK业务失败写入 Feedback header
  • 使用非 OK gRPC status 表达 transport/API 失败。

扩展已有服务时保持其兼容语义。新增服务必须在设计时明确:

  • 哪些错误使用 gRPC status
  • 哪些错误使用业务 Feedback
  • 是否允许部分成功;
  • deadline/cancellation 如何映射;
  • 不得同时返回互相矛盾的 transport 和业务状态。

流式 RPC

  • 检查 context->IsCancelled()
  • 检查 Read() / Write() 返回;
  • 使用 RAII 或 MediaSourceHub Subscription 释放 producer lease
  • 不持有设备状态锁进行网络写;
  • 为 wait/read 使用有限 timeout
  • 慢客户端不能阻塞设备生产线程;
  • H.264/H.265 丢帧后等待关键帧恢复;
  • gRPC RGB 流在积压超过 camera_stream_max_pending_frames 或帧龄超过 camera_stream_max_frame_age_ms 时主动丢弃旧帧,请求 IDR并从下一个关键帧恢复。

当前仅 gRPC RGB 和麦克风流使用 MediaSourceHubDepth/RGBD 仍直接读取设备帧。

gRPC 相机实时流默认最多保留 2 帧积压、最大允许 250 ms 帧龄。两个配置项填 0 时使用上述默认值。该策略以低延迟为目标,不保证每个视频帧都到达客户端;控制命令 仍由普通 gRPC RPC 承担。

FrameData 附带 capture_utc_nssource_sequenceptsdtssource_fpssource_timestampsource_frame_number。平台端可用采集时间 与接收时间的差值区分设备、网络、服务端写阻塞和客户端解码/渲染队列延迟。新增字段 保持 protobuf wire compatibility旧客户端可以继续连接但需要重新生成代码后才能 读取这些诊断字段。

当前安全状态

GrpcServerTask 使用同步 grpc::ServerBuildergrpc::InsecureServerCredentials()。reflection 由配置控制。当前没有 gRPC TLS、认证、授权或标准 health service。

QUIC 配置中的 grpc_endpoint_tls 只是上报字段,不会启用 gRPC TLS。

扩展 QUIC Edge

关键层次:

主要文件
控制流 framing quic_edge/src/control_framing.cpp
DATAGRAM 固定头和分片 quic_edge/src/datagram_packetizer.cpp
会话和状态机 quic_edge/src/quic_edge_service.cpp
传输抽象 quic_edge/include/quic_transport.h
MsQuic 后端 quic_edge/src/msquic_transport.cpp
设备媒体适配 quic_edge/src/quic_edge_device_adapter.cpp
DeviceManager 心跳适配 quic_edge/src/quic_edge_device_adapter.cpp

线协议见 ../../protos/cmvr/quic_edge/v1/README.md

新增 Transport 后端

  1. 实现 QuicTransport 完整接口;
  2. 明确 callback 所在线程;
  3. stop/close 后不得再访问已销毁 service
  4. QUEUED 表示 transport 接管待发送数据;
  5. WOULD_BLOCKERROR 不得接管任何字节;
  6. DATAGRAM batch 本地准入必须原子;
  7. native send 部分失败时关闭连接并清理 session
  8. 添加 fake transport 故障注入测试;
  9. 在默认 transport factory 中显式选择后端。

新增控制消息

不能只修改 Proto还要同步

  • envelope 构造与发送;
  • 入站 dispatch
  • 合法状态和消息时序;
  • message sequence 校验;
  • session ID 和 heartbeat sequence 校验;
  • reconnect 后状态清理;
  • Java Gateway 对端;
  • framing、状态机和 fake transport 测试。

当前 Edge 入站只接受:

  • NodeRegisterResponse
  • NodeHeartbeatAck
  • ProtocolError

虽然 Proto 定义了 MediaSessionClose,本版本 Edge 收到它仍会判为 unexpected不应将其描述为已实现的双向控制能力。

DeviceManager 心跳快照

QuicEdgeService 通过可注入的 DeviceSnapshotProvider 获取协议无关的纯值 快照。生产构造绑定已经初始化的 DeviceManagerfake transport 测试则注入 合成快照,因此协议状态机不需要创建硬件对象或依赖 DeviceManager 单例。 DeviceManager 的本地快照继续保留禁用设备QUIC wire 映射层仅序列化 enabled=true 的设备。已启用但创建、初始化或启动失败的设备不会被过滤。

心跳线程只读取 Manager 维护的内存状态,不能在这里同步访问厂商 SDK、网络或 设备总线。新增设备健康探针必须实现 AbstractDevice::healthSnapshot() 的 线程安全、无阻塞 I/O 契约;未实现时上报 UNSPECIFIED,不得伪造为健康。 设备异常字符串会限长,整条消息仍受 maximum_control_frame_bytes 约束。

跨 QUIC 通道顺序

Edge 会先调用可靠流发送 session/descriptor再调用 DATAGRAM 发送媒体,但 QUIC stream 与 DATAGRAM 没有跨通道到达顺序保证。

Gateway 必须容忍 DATAGRAM 先到,对未知 session epoch 或 codec generation 的数据有界暂存或丢弃。

QUIC 测试要求

参考 quic_edge/CMakeLists.txtquic_edge_protocol_test,至少覆盖:

  • 控制消息拆包、粘包和超限;
  • 重复或倒退 sequence
  • 注册、ACK 超时和重连;
  • DeviceManager 已启用设备过滤、类型/状态映射和 provider 失败隔离;
  • Gateway 返回零心跳周期时采用本地 heartbeat_interval_ms
  • session epoch 清理;
  • DATAGRAM header 字节序;
  • 分片边界和超大帧;
  • 原子队列准入和背压;
  • 丢帧、generation 和关键帧恢复;
  • stop 与 callback 并发。
cmake --build build --target quic_edge_protocol_test
ctest \
  --test-dir build \
  -R '^quic_edge_protocol_test$' \
  --output-on-failure

提交检查

  • service 不创建具体硬件后端
  • Proto、实现、CMake 和 GrpcServerTask 注册均已更新
  • 错误语义与已有服务兼容
  • deadline、cancel、Read/Write 失败均处理
  • 流式资源通过 RAII 释放
  • gRPC 安全能力没有被配置字段误描述
  • QUIC 状态机与 Gateway 同步更新
  • 协议测试已登记到 CTest