| .. | ||
| grpc_server_task | ||
| quic_edge_task | ||
| self_collision_task | ||
| touch_screen_task | ||
| ume_teleop_task | ||
| CMakeLists.txt | ||
| README.md | ||
| task_factory.h | ||
| task.h | ||
Task 模块开发指南
task/ 保存由 TaskManager 管理的可运行模块。任务分为周期调度任务和自持线程/事件循环的服务任务。
返回项目总览。
Task 契约
所有任务实现 task.h 中的接口:
id():配置和管理器使用的稳定唯一 ID;runMode():周期任务或服务任务;init():配置校验和本地资源准备;start():启动执行,必须快速返回;step(dt):周期任务的一次计算;stop():幂等停止并回收任务拥有的资源;state()和状态辅助接口:线程安全地暴露状态;detailStatusString():提供可诊断状态,不包含密钥。
析构函数应安全调用 stop()。异步状态、错误和统计必须使用 mutex/atomic 保护。
运行模式
PERIODIC_STEP
- 由 TaskManager scheduler 调用
step(); - 配置
control_period_s必须是有限正数; - 只有 task state 为
RUNNING时执行 step; - 所有周期任务共享一个 scheduler 线程;
- step 不能做阻塞网络 I/O 或无界计算;
- step 返回失败时应更新任务状态和可诊断错误;
- 不要在 step 内同步停止 TaskManager。
BLOCKING_SERVICE
- TaskManager 不调用其
step(); - start 必须创建自己的 server/worker 后快速返回;
- stop 必须关闭 server、唤醒等待并 join 线程;
- 不能把
BLOCKING_SERVICE理解为 start 可以永久阻塞。
GrpcServerTask 和 QuicEdgeTask 是当前服务任务参考实现。
新增任务的完整链路
1. 配置 Proto
在 protos/cmvr/config/ 新增:
message ExampleTaskConfig {
string id = 1;
}
message ExampleTaskRootConfig {
ExampleTaskConfig example_task = 1;
}
在 TaskConfigEntry::TaskType 中使用新的、稳定的枚举值。当前 2 和 4 已 reserved,不得复用。
2. 实现目录
task/example_task/
├── CMakeLists.txt
├── include/example_task.h
├── src/example_task.cpp
└── tests/example_task_test.cpp
实现 Task 全部接口。持续运行任务的典型状态转换为:
UNINITIALIZED -> IDLE -> RUNNING -> STOPPED
└-------> FAILED
持续周期任务必须在 start 后进入 RUNNING,否则 scheduler 不会调用 step。命令驱动任务可以在 start 后保持 IDLE,收到外部命令后再进入 RUNNING,当前 TouchScreenTask 就采用这种方式。
init、start 或 step 都可能进入 FAILED,一次性任务也可以进入 SUCCEEDED。失败后是否允许重新 init/start 必须在类注释和测试中明确。
3. Creator 与注册
creator 应:
- 校验 manager entry ID 和 config file;
- 使用
ConfigHelper加载 root config; - 校验子配置 ID 与 manager entry ID 一致;
- 只创建对象,不进行耗时外部连接;
- 返回
nullptr并记录明确错误。
提供:
void registerExampleTaskFactory();
通过 task_factory.h 的 TaskFactory::registerCreator() 注册。
registerCreator() 对相同 TaskType 会覆盖旧 creator,当前不会报错。不要重复注册。creator 在 registry mutex 持有期间执行,因此不能从 creator 递归调用 register/create。
4. main 注册
在 TaskManager::getInstance() 之前显式调用注册函数。当前位置见 ../main.cpp。
TouchScreen 是 TaskFactory 内硬编码的历史实现;新任务优先使用显式 registry 模式。
5. TaskManager 映射
在 ../manager/task_manager/src/task_manager.cpp 的 TaskType 字符串映射中增加新类型,否则计划日志会显示 unknown。
6. 默认配置
新增:
cmvr-es/config/tasks/example_task/example_task.pb.txt
并在 config/manager/task_manager.pb.txt 增加 entry。依赖硬件、网络或证书的新任务默认 enable: false。
配置 run_mode 必须和 task 的 runMode() 一致,否则 TaskManager 会跳过该任务。
7. CMake
- 为任务建立独立 library target;
- 添加项目命名空间 alias;
- 在
CMakeLists.txt或../CMakeLists.txt加入子目录; - 根
cmvr_es必须链接注册函数所在 target,避免静态库未被带入; - 测试放在
if(BUILD_TESTING); - 使用
add_test()登记到 CTest。
参考 quic_edge_task/CMakeLists.txt。
TaskManager 当前语义
- init 按配置顺序执行;
- init 失败只跳过该任务,进程继续;
- ID 重复会跳过后加入项;
- run mode 不匹配会跳过;
TASK_RUN_MODE_UNKNOWN当前会记录错误并退化为PERIODIC_STEP,配置不得依赖该历史行为;- start 阶段任一任务失败会停止此前已启动任务;
- 返回 false 的 task 必须自行清理此次 start 的部分资源;
- 任务保存在 unordered_map,不能依赖 start/stop 顺序;
- manager 处于 running 状态时,stop 先停止 scheduler,再调用每个任务 stop;
- 未启动或已经停止时,
stopRunTask()会直接返回; - TaskManager 单例已存在时传入新配置不会热更新。
任务之间有依赖时,应由一个协调 task 显式管理,或增加明确依赖模型;不能依赖配置顺序碰巧变成启动顺序。
测试最低要求
- 缺失配置、空 ID 和 ID mismatch;
- run-mode mismatch;
- init/start/stop 状态转换;
- stop 重复调用;
- start 失败后的资源清理;
- 周期 dt、调度延迟和 step 失败;
- service worker 正常 shutdown;
- stop 发生在阻塞等待期间;
- 析构时仍处于 RUNNING;
- fake 依赖下的无设备运行。
单项测试:
cmake --build build --target quic_edge_task_test
ctest \
--test-dir build \
-R '^quic_edge_task_test$' \
--output-on-failure
提交检查
- 使用新的 TaskType 枚举号
- 配置 root message、默认配置和 ID 一致
- creator 与 main 注册均已完成
- run mode 与配置一致
- start 快速返回
- stop 幂等并 join 所有线程
- 状态查询线程安全
- CMake target 被根程序链接
- 测试已登记到 CTest