面向 CR101 现有三端的正式设计基线

轻量、持久、可恢复的机器人任务编排

基础能力节点 表达机器人可以做什么,以 组合任务模板 表达送药、宣教和心理评测,以 MySQL 保存设备 FIFO 队列真相,以现有 WebSocket 通道完成实时下发与进度回传。路径规划继续由边缘端 SLAM/ROS2 负责。

单机器人 FIFO不抢占单任务单节点area_id 必填map_id 冻结事件长期留存断线可恢复

0. 决策摘要

任务分两层

基础任务是原子节点;送药、宣教、心理评测是按顺序展开的组合任务。

队列真相在数据库

h_robot_task_queue 控制每台机器人;robot_id + queue_seq 决定任务顺序。

一次只推进一个节点

节点成功才递增 current_node_seq,不需要中断恢复复杂上下文。

安全返航是受控阻断

持续低电量时冻结队列,终止当前可停止节点,导航至本任务地图的 HP。

最终推荐:一期不引入 DAG、MQTT、Redis、分布式锁和后端路径算法。先用 MySQL 事务锁 + Outbox + 单进程调度循环形成稳定闭环;多实例和多机器人调度通过预留租约字段平滑扩展。

0.1 不可破坏的约束

  • 每个任务必须有 area_id;需要移动的任务和节点必须有明确 map_id
  • h_map.area_id 是区域到地图的一对多单向关系,不在 h_area 回填 map_id,不新增弱价值关系表。
  • 同一区域可以有多张地图,但移动任务只在机器人已确认加载的同一 map_id 内执行;一期不跨地图自动切换或规划。
  • 新任务只追加到队尾;正常业务任务不抢占正在运行的任务。
  • 导航只下发目标点位和坐标快照,服务端不计算路径、不模拟避障。
  • 任务、节点、命令、事件、投递结果均不物理删除;终态长期留存。
  • HTTP/5000 回执只代表“命令已受理”,最终成败必须由 6000 事实事件确认。

1. CR101 现状与升级边界

以下结论来自当前工程与 2026-09-14 数据库快照,明确区分“已经存在”和“本方案新增”。

能力当前 CR101升级后变更性质
任务数据task + task_result,旧写入口默认冻结h_robot_task + node + item + command + event + outbox新增并迁移只读历史
后台页面旧任务页只读;送药任务页为占位创建向导、设备队列、任务驾驶舱、异常处置替换任务域页面
机器人 Pad当前任务、药箱、返程、历史入口为占位当前节点、患者核验、内容互动、人工确认与异常提示填补业务页
药站 Pad尚无任务域闭环装药校验、交接机器人、发车确认、回收与补偿新增业务闭环
WebSocketrobotMap/controlMap/pending/pendingCmdById 已存在保留连接层,增加持久化命令、事件和任务投影复用并增强
设备事件可靠性边缘有界队列、6001 ACK、断线重连;服务端去重主要在内存ACK 前持久化 Inbox/Event,数据库唯一键幂等关键补强
边缘任务执行start_task/stop_task 有实现文件及 Mock;真机装配仍受理即拒先以细粒度 function 编排,或经专项验收后接通 RobotTask上线硬门禁
导航go_target_point/cancel_go_target_point 真机已接线,ROS2 自主规划作为 MOVE_A2B 基础节点执行器直接复用
地图h_map.area_id 已有;当前唯一键限制一区一张发布图一区多张可用图,任务冻结 map_id,运行时核对当前图解除过度约束
药箱h_med_box/h_med_box_slot/h_medicine_pack/h_med_box_fill_log 已投入现役继续作为权威源,任务只保存不可变投递快照复用,不建重复主数据
地图一对多迁移:当前 h_map.current_area_key + uk_h_map_current_area 会限制同一区域只能有一张 status=1 的地图。进入任务系统开发前,须先完成引用代码排查和测试库演练,再删除该唯一索引;现有 idx_h_map_area_status(area_id,status) 可继续承担查询。current_area_key 生成列待无引用后再单独删除,不在同一变更中冒险处理。绝不在 h_area 增加 map_id

1.1 对 Opus5 示例的取舍

采纳
分层架构、基础任务与组合任务、seq 顺序、编排画布、动画执行、实时日志、能力注册。
不照搬
MQTT、gRPC、任意 DAG、多机时空冲突规划、后端模拟路径、低电量直接抢占。它们与 CR101 当前目标或能力边界不符。

2. 总体架构

CR101 任务编排总体架构 三类操作端通过 REST 和 WebSocket 连接 Node.js 服务,服务内部由任务编译、FIFO 调度、节点执行、命令和事件账本组成,机器人边缘端负责 ROS2 驱动和自主导航。 操作与展示端 hb_admin创建/监控 robot_app现场交互 site_app装药/交接 REST 命令入口 + /ws 实时订阅 hb_server · Node.js Express Task API + Compiler校验 area/map/箱格模板展开为 node_seq FIFO Scheduler按 robot_id + queue_seq租约锁定队头 Node Executor一次只执行一个节点状态迁移 + 补偿 Command + Outbox命令持久化 / 重试4000 → 5000 Event Inbox6000 持久化去重落库后 6001 ACK Runtime Projection任务/节点/电量快照101x 推送操作端 MySQL 持久化真相 QueueCursor · Task · Node · DeliveryItem · Command · Event · Outbox · Template hb_edge · 机器人边缘端 WebSocket Client注册/重连/有界队列 Function Dispatcher幂等令牌/能力门禁 Node Adapter导航/药箱/Pad/语音 ROS2 / HardwareSLAM/避障/执行器 Safety急停/断流自停 API /device WS
图 2-1 数据库是可恢复的业务真相;WebSocket Map 是实时连接设施;边缘端是运动与硬件事实源。

2.1 模块职责

模块只负责明确不负责
Task Compiler业务校验、模板展开、快照冻结、写入队尾不连接机器人、不等待任务执行
FIFO Scheduler锁定机器人 QueueCursor,选择最小待执行 queue_seq,持有设备租约不跳过队头、不按普通优先级抢占
Node Executor解释单个基础节点、生成命令、消费事实事件不直接修改后续节点输入
WS Hub连接、鉴权、收发、广播、短时关联不作为队列和任务状态的最终真相
hb_edgeROS2 调用、自主导航、硬件执行、安全停车、事实上报不决定云端任务顺序,不自行领取下一任务

2.2 CR101 核心三端协同边界

工程端任务编排职责状态真相本次扩展重点
hb_admin创建、预校验、查看队列与进度,发起暂停/继续/召回/变更REST 重拉的数据库投影;WS 只负责增量刷新新增任务向导、驾驶舱、异常处置和审计视图
hb_server模板编译、FIFO 游标、节点状态机、命令/事件账本、恢复对账MySQL 是任务唯一持久真相,内存 Map 只保存在线连接新增任务域表、Scheduler、Executor、Inbox/Outbox
hb_edge执行白名单基础能力,调用 ROS2/硬件,实施本地安全并上报事实机器人运动、门态和硬件结果的事实源补齐任务上下文、幂等键、事件序号、断线恢复与围栏校验

hb_robot_apphb_site_app 若作为机器人 Pad、药站 Pad 投入使用,应复用 hb_server 的同一 REST/WS 契约;它们是伴随操作界面,不另建任务队列或边缘调度器。

3. 统一任务模型

3.1 基础任务:可执行的最小能力

这是目标能力目录,不代表全部已经接通:当前真机可复用 go_target_point / cancel_go_target_pointemotion_controlrotate_pill_box_ctrlrobot_self_test 及已接线的上肢/颈部函数;RETURN_HOMEWAIT_CONFIRM 由服务端编排;语音播放/互动、身份核验、Pad 内容会话仍需三端实现并通过契约与真机验收后,才能在能力注册表标记为 ENABLED。

MOVE_A2B导航到目标点,可带有序途经点;适配 go_target_point。输入必须包含 area/map/point 快照。
ACTION机械臂、灵巧手、颈部或自定义姿态动作;动作参数按 capability schema 校验。
EXPRESSION表情切换;适配 emotion_control。失败默认不影响移动安全,可由模板决定是否阻断。
VOICE_PLAY播放固定语音或 TTS;属于有限时长动作,必须上报开始和完成。
VOICE_INTERACT语音问答或交互流程;需超时、重试、取消和隐私边界。
VERIFY_IDENTITY扫码/腕带/人工确认;输出结构化核验结果,禁止仅返回自由文本。
DISPLAY_CONTENT机器人 Pad 展示 URL 页面;仅允许发布到白名单内容域并冻结内容版本。
BOX_SLOT_CONTROL旋转药箱到格子并开/关仓;适配 rotate_pill_box_ctrl,必须与箱格快照绑定。
SELF_CHECK全身、导航、药箱、箱格、传感器或网络自检;适配 robot_self_test 并扩展分项结果。
WAIT_CONFIRM等待患者或工作人员确认;有截止时间和明确超时策略。
RETURN_HOME语义节点,编译为当前任务 map_id 中的 HP 点位导航。
CUSTOM未来能力扩展口。必须先注册 code、schema、执行器和补偿策略,不能直接透传任意命令。
动作白名单原则:编排系统不能让管理端输入任意 function 名或任意 JSON。所有基础能力先登记 action_code + schema_version + handler + timeout + retry_policy,通过校验后才可进入模板。

3.2 组合任务:基础节点的有序模板

01 MOVE_A2B 到床旁02 EXPRESSION 问候03 VERIFY_IDENTITY04 BOX_SLOT_CONTROL 开格05 VOICE_PLAY 取药提示06 WAIT_CONFIRM07 关格/表情08 RETURN_HOME

身份核验失败不得开格。一次任务可包含多个患者投递段;每个投递段引用独立 DeliveryItem,已投递格与未投递格分别留存结果。

3.3 模板不是运行实例

模板用于复用编排;提交任务时,模板当前版本会展开为不可变的 h_robot_task_node 行。模板以后升级,不影响已经排队或完成的任务。任务变更时递增主表 revision:旧修订尚未执行的 Node/DeliveryItem 标记 CANCELED,新修订另插新行;已完成节点、投递证据和事件绝不改写。迟到事件必须匹配当前 task_revision + node_id + lease_epoch 才能推进。

4. 单机器人 FIFO 任务队列

每台机器人拥有一条逻辑队列。queue_seq 是该机器人内单调递增序号;node_seq 是单任务内从 1 开始的执行顺序。所谓“掏出执行”是状态迁移,不是删除记录。

队列已装载,等待调度
机器人 CR101-01 · area_id=82 · map_id=5
尚未锁定队头0%
scheduler: LOCK QueueCursor(robot_id=6) → active_task=#101
交互图 4-1 点击“下一步”观察队头锁定、节点推进、终态留存和下一任务接棒。
① QueueCursor:每机器人 1 行

next_queue_seq=105 · active_task_id=NULL · queue_status=READY
它是并发互斥门和恢复锚点。

② Task:队列项长期保留

#101 运行中,#102/#103/#104 排队。完成、失败、召回都只改状态,不删除、不重排。

③ Node:当前任务逐行推进

只允许 QueueCursor 指向的任务拥有一个活动节点;成功后原子切换到下一 node_seq

4.1 数据库里实际如何排队

任务和节点的两级顺序机器人队列按 queue_seq 排列,任务内部按 node_seq 排列,当前任务和节点由状态字段指示但不会删除。 h_robot_task:robot_id=6 queue_seq=101 · 送药status=RUNNINGcurrent_node_seq=3 queue_seq=102 · A2Bstatus=QUEUEDcurrent_node_seq=NULL queue_seq=103 · 宣教status=QUEUEDcurrent_node_seq=NULL queue_seq=104 · 自检status=QUEUED新任务只能追加 只展开当前任务的节点 h_robot_task_node:task queue_seq=101 node_seq=1MOVE · SUCCEEDED node_seq=2EXPRESSION · OK node_seq=3VERIFY · RUNNING node_seq=4OPEN_SLOT · READY node_seq=5CONFIRM · READY node_seq=6RETURN · READY
图 4-2 两级 seq:queue_seq 控制任务先后,node_seq 控制任务内部动作先后。

4.2 入队事务

  1. 锁定目标机器人的 h_robot_task_queue 行,读取并保留本次 next_queue_seq;游标行缺失则失败关闭,不在业务请求中临时补建。
  2. 校验设备、area_id、任务权限和能力清单;队列即使正在执行或因电量冻结,仍可按策略允许追加,但绝不改变活动任务。
  3. 对送药任务锁定药箱、格子、药包和装药流水,校验药箱确实挂载到该机器人。
  4. 确定 map_id,校验地图属于任务 area_id、文件就绪、所需 HP 和目标点存在。
  5. 使用刚才保留的序号创建 Task(或把既有 DRAFT 转为 QUEUED)、Node 和 DeliveryItem 快照,再把游标的 next_queue_seq 加 1;不执行 MAX(queue_seq)+1
  6. 同一事务写入 TASK_QUEUED 事件和 Outbox 后提交;任何一步失败均整体回滚,HTTP 成功时才返回 202 Accepted

4.3 队头消费算法

BEGIN;
SELECT robot_id, active_task_id, active_node_id, queue_status, lease_expire_at
FROM h_robot_task_queue
WHERE robot_id = :robotId
FOR UPDATE;

// 应用层守卫:active_task_id 必须为空,queue_status 必须为 READY;
// 含移动节点时,reported_map_id 还必须等于任务 map_id 且上报未过期,否则阻断。
SELECT id, queue_seq
FROM h_robot_task
WHERE robot_id = :robotId AND status = 'QUEUED' AND deleted = 0
ORDER BY queue_seq ASC
LIMIT 1 FOR UPDATE;

UPDATE h_robot_task
SET status='DISPATCHING', version=version+1
WHERE id=:taskId AND status='QUEUED';

UPDATE h_robot_task_queue
SET active_task_id=:taskId, queue_status='EXECUTING',
    lease_owner=:instanceId, lease_expire_at=DATE_ADD(NOW(3), INTERVAL 30 SECOND),
    lease_epoch=lease_epoch+1, version=version+1
WHERE robot_id=:robotId AND active_task_id IS NULL AND queue_status='READY';
COMMIT;

// 提交后只执行 active_task 的首个 READY 节点;节点推进也先锁同一 QueueCursor 行。

该算法兼容 CR101 当前 MySQL 5.7,不使用 SKIP LOCKED。同一机器人的所有入队、领取、节点推进、终态释放和安全冻结都先锁同一游标行,因此数据库层能保证单活动任务;租约过期只允许新调度器先对账接管,绝不直接重发副作用命令。

5. 数据结构与存储

5.1 现有权威表,不复制主数据

现有表任务系统用途关键事实
h_device机器人和药站身份、区域、在线落档任务引用机器人主键;实时在线仍以 robotMap 为准
h_area任务权限和活动范围所有新任务必须写入 area_id
h_map导航地图元数据和文件就绪状态一条地图属于一个区域;一个区域允许多张地图
h_map_pointHP、CP、房门、床旁和普通点运行任务冻结点位名称与坐标,不在事件返回后重新查坐标
h_med_box流动药箱、挂载设备、物理格数和业务态slot_count 是规格,业务不得写死 15
h_med_box_slot当前格口、占用、门态和药包关联它是当前态,不承担历史任务结果
h_med_box_fill_log药包进入/离开格子的生命周期创建任务时用于核对当前装载事实
h_medicine_pack药包与住院流水、医嘱和配送状态现有 task_id 指向旧 task.id,新版不得混写;由 DeliveryItem 关联新任务并冻结快照
h_patient_admission / h_bed / h_room收件人住院身份和目的地床位变更后,排队任务必须走受控变更,不可静默漂移

5.2 建议新增表总览

h_robot_task_queue每机器人一行的队列游标、互斥门、安全阻断和调度租约。
h_robot_task任务主表,也是每台机器人的 FIFO 队列真相。
h_robot_task_node运行实例的有序基础节点,按 node_seq 推进。
h_robot_task_delivery_item送药对象、箱格、患者、点位的不可变快照。
h_robot_task_command每次下行命令及 5000 受理回执账本。
h_robot_task_event机器人事实事件 Inbox,持久化去重和审计。
h_robot_task_outbox事务提交后的可靠下发和前端广播待办。
h_robot_task_template组合任务模板版本,发布后不可原地修改。

5.3 核心 DDL 与字段字典

数据库兼容基线:当前快照标明服务器为 MySQL 5.7.44-log。以下 DDL 不使用 SKIP LOCKED、窗口函数、函数索引或 CHECK 约束,JSON 可空列也不声明默认值。DDL 只是开发基线,不是本次执行脚本;上线前仍需在同版本测试库执行迁移、数据量评估与回滚演练。

表 5-1 h_robot_task_queue:机器人队列游标 / 并发互斥表

用途:每台机器人固定一行,原子分配 queue_seq,并保存当前活动任务、活动节点、队列阻断态和调度租约。它不保存任务内容,而是保证任何进程都不能让同一机器人同时推进两个任务。

CREATE TABLE `h_robot_task_queue` (
  `robot_id` BIGINT NOT NULL COMMENT '机器人ID,逻辑FK→h_device.id;每机器人唯一一行',
  `next_queue_seq` BIGINT NOT NULL DEFAULT 1 COMMENT '下一次入队分配的序号;事务内读取并递增',
  `active_task_id` BIGINT DEFAULT NULL COMMENT '当前活动任务ID;空表示没有任务占用执行权',
  `active_node_id` BIGINT DEFAULT NULL COMMENT '当前活动节点ID;仅用于快速定位和恢复对账',
  `reported_map_id` BIGINT DEFAULT NULL COMMENT '边缘端最近确认已加载的地图ID;移动任务派发门禁',
  `map_reported_at` DATETIME(3) DEFAULT NULL COMMENT '已加载地图事实的最近上报时间;过期时禁止派发移动',
  `queue_status` VARCHAR(24) NOT NULL DEFAULT 'READY' COMMENT 'READY/EXECUTING/PAUSED/OFFLINE_BLOCKED/ENERGY_BLOCKED/FAULT_BLOCKED',
  `block_code` VARCHAR(64) DEFAULT NULL COMMENT '电量、门态、急停、离线或人工阻断的稳定码',
  `block_message` VARCHAR(512) DEFAULT NULL COMMENT '面向运维的阻断说明,不保存敏感详情',
  `lease_owner` VARCHAR(64) DEFAULT NULL COMMENT '当前调度实例标识;单实例阶段同样写入',
  `lease_expire_at` DATETIME(3) DEFAULT NULL COMMENT '设备队列租约到期时间;过期后必须先对账',
  `lease_epoch` BIGINT NOT NULL DEFAULT 0 COMMENT '每次接管递增的围栏代次,防止旧执行器继续推进',
  `version` BIGINT NOT NULL DEFAULT 0 COMMENT '乐观锁版本;游标状态更新必须带旧version',
  `create_time` DATETIME(3) NOT NULL COMMENT '创建时间;设备纳入任务系统时初始化',
  `update_time` DATETIME(3) NOT NULL COMMENT '最后更新时间',
  PRIMARY KEY (`robot_id`),
  UNIQUE KEY `uk_queue_active_task` (`active_task_id`),
  KEY `idx_queue_lease` (`queue_status`,`lease_expire_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='机器人任务队列游标及单活动任务互斥门';
字段组字段含义与使用规则
序号next_queue_seq所有创建入口先锁本行再取号;提交后递增,回滚时任务和序号一起回滚。
活动指针active_task_id / active_node_id快速判断执行权归属;最终真相仍由对应 Task/Node 行和事件共同校验。
地图事实reported_map_id / map_reported_at来自已鉴权边缘连接;移动节点下发前必须与任务冻结 map_id 一致且未过期。
调度门禁queue_status / block_*READY 才可领取队头;暂停、离线、电量或故障只冻结领取,不改变既有 queue_seq。
接管lease_owner / lease_expire_at / lease_epoch服务重启或未来多实例接管时先抢租约、递增代次、再和机器人对账。
并发version防止迟到线程覆盖较新游标;受影响行数不是 1 时必须放弃推进。
表 5-2 h_robot_task:任务主表 / 设备 FIFO 队列表

用途:一行代表一个可审计任务;robot_id + queue_seq 唯一决定该机器人队列顺序。任务进入终态后仍保留原序号,不删除、不复用。

CREATE TABLE `h_robot_task` (
  `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '任务主键',
  `task_no` VARCHAR(40) NOT NULL COMMENT '业务任务号,全局唯一,如 RT202609150001',
  `robot_id` BIGINT NOT NULL COMMENT '执行机器人,逻辑FK→h_device.id(type=ROBOT)',
  `area_id` BIGINT NOT NULL COMMENT '任务所属区域,逻辑FK→h_area.id;所有任务必填',
  `map_id` BIGINT DEFAULT NULL COMMENT '任务使用地图,逻辑FK→h_map.id;含移动节点时必填',
  `queue_seq` BIGINT DEFAULT NULL COMMENT '机器人内单调递增队列序号;DRAFT为空,正式提交时分配',
  `task_type` VARCHAR(32) NOT NULL COMMENT '队列展示类型:MOVE_A2B/DELIVERY/EDUCATION/ASSESSMENT/SELF_CHECK/ACTION/EXPRESSION/VOICE_PLAY/VOICE_INTERACT/VERIFY_IDENTITY/DISPLAY_CONTENT/CUSTOM',
  `template_code` VARCHAR(64) DEFAULT NULL COMMENT '来源模板编码;单基础任务可为空',
  `template_version` INT DEFAULT NULL COMMENT '创建时采用的模板版本',
  `status` VARCHAR(24) NOT NULL COMMENT 'DRAFT/QUEUED/DISPATCHING/RUNNING/PAUSE_REQUESTED/PAUSED/RETURNING/SUCCEEDED/PARTIAL_SUCCESS/FAILED/CANCELED/BLOCKED',
  `current_node_seq` INT DEFAULT NULL COMMENT '当前revision内的节点序号;未开始为空',
  `progress` DECIMAL(5,2) NOT NULL DEFAULT 0 COMMENT '任务展示进度0.00~100.00,由节点权重计算',
  `source_type` VARCHAR(24) NOT NULL COMMENT '来源:ADMIN/ROBOT_PAD/SITE_PAD/SYSTEM/API',
  `source_ref` VARCHAR(64) DEFAULT NULL COMMENT '外部业务幂等键或来源记录号',
  `payload_snapshot` JSON NULL COMMENT '提交时业务参数快照,不作为节点执行真相',
  `return_policy` VARCHAR(24) NOT NULL DEFAULT 'RETURN_HOME' COMMENT '结束策略:RETURN_HOME/STAY/NONE',
  `energy_policy` VARCHAR(24) NOT NULL DEFAULT 'SAFE_RETURN' COMMENT '电量策略:SAFE_RETURN/BLOCK_CREATE',
  `parent_task_id` BIGINT DEFAULT NULL COMMENT '补偿/重试/变更来源任务ID',
  `revision` INT NOT NULL DEFAULT 1 COMMENT '任务修订号;变更排队任务时递增',
  `version` INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本,所有状态更新必须带旧version',
  `failure_code` VARCHAR(64) DEFAULT NULL COMMENT '稳定失败码',
  `failure_message` VARCHAR(512) DEFAULT NULL COMMENT '面向运维的失败说明,不放敏感详情',
  `queued_at` DATETIME(3) DEFAULT NULL COMMENT '正式进入队尾时间',
  `started_at` DATETIME(3) DEFAULT NULL COMMENT '首节点开始时间',
  `finished_at` DATETIME(3) DEFAULT NULL COMMENT '进入终态时间',
  `create_by` BIGINT DEFAULT NULL COMMENT '创建人',
  `create_time` DATETIME(3) NOT NULL COMMENT '创建时间',
  `update_by` BIGINT DEFAULT NULL COMMENT '最后修改人',
  `update_time` DATETIME(3) NOT NULL COMMENT '最后修改时间',
  `deleted` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '软删除;终态任务原则上也不删除',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_robot_task_no` (`task_no`),
  UNIQUE KEY `uk_robot_queue_seq` (`robot_id`,`queue_seq`),
  UNIQUE KEY `uk_robot_source_ref` (`robot_id`,`source_type`,`task_type`,`source_ref`),
  KEY `idx_robot_queue_pick` (`robot_id`,`status`,`queue_seq`),
  KEY `idx_area_time` (`area_id`,`create_time`),
  KEY `idx_parent_task` (`parent_task_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='机器人任务主表兼设备FIFO队列';
字段组字段含义与使用规则
身份id / task_no内部主键与对外稳定任务号;协议优先传 task_no,数据库关联用 id。
归属robot_id / area_id / map_id固定机器人和业务区域;移动任务 map_id 必填且必须属于 area_id。
队列queue_seqDRAFT 为空;正式提交后每机器人单调递增,不因取消、失败或删除而重排。
类型task_type / template_*前台显示任务类型,并追溯由哪个模板版本展开。
运行status / current_node_seq / progress任务实时投影;状态只允许按状态机迁移,禁止任意 UPDATE。
幂等source_type / source_refsource_ref 使用来源命名空间内稳定请求号;同类型重复提交返回原任务,空字符串必须规范化为 NULL。
策略return_policy / energy_policy正常结束与能量异常行为;不能由前端自由注入动作。
并发version防止迟到请求覆盖较新任务状态;设备级租约和接管信息存放在 QueueCursor。
审计parent_task_id / revision / failure_*补偿、重试和变更均保留来源,失败使用稳定码。
表 5-3 h_robot_task_node:任务节点实例表

用途:一行代表一次基础能力执行。组合任务在入队时展开成多行;同任务只允许一个节点处于 RUNNING。

CREATE TABLE `h_robot_task_node` (
  `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '节点主键',
  `task_id` BIGINT NOT NULL COMMENT '所属任务,逻辑FK→h_robot_task.id',
  `task_revision` INT NOT NULL COMMENT '节点所属任务修订号,必须等于生成该节点时的task.revision',
  `node_seq` INT NOT NULL COMMENT '任务内顺序,从1开始,成功后只向后推进',
  `node_type` VARCHAR(32) NOT NULL COMMENT 'MOVE/ACTION/VERIFY/DISPLAY/INTERACT/BOX/SELF_CHECK/WAIT',
  `action_code` VARCHAR(64) NOT NULL COMMENT '能力白名单编码,如 MOVE_A2B/VERIFY_IDENTITY',
  `area_id` BIGINT NOT NULL COMMENT '节点区域快照,必须等于任务area_id',
  `map_id` BIGINT DEFAULT NULL COMMENT '导航节点地图快照,逻辑FK→h_map.id',
  `target_point_id` BIGINT DEFAULT NULL COMMENT '目标点ID快照来源,逻辑FK→h_map_point.id',
  `target_snapshot` JSON NULL COMMENT '点位名称、类型、坐标及途经点不可变快照',
  `input_snapshot` JSON NULL COMMENT '经schema校验后的动作输入快照',
  `status` VARCHAR(20) NOT NULL DEFAULT 'READY' COMMENT 'READY/DISPATCHING/RUNNING/PAUSED/SUCCEEDED/FAILED/SKIPPED/CANCELED',
  `weight` DECIMAL(6,3) NOT NULL DEFAULT 1 COMMENT '计算任务进度的权重',
  `attempt_no` INT NOT NULL DEFAULT 0 COMMENT '已发起执行次数',
  `max_attempts` INT NOT NULL DEFAULT 1 COMMENT '最多尝试次数;物理副作用默认1',
  `timeout_sec` INT NOT NULL COMMENT '节点执行超时秒数',
  `idempotency_key` VARCHAR(96) NOT NULL COMMENT '节点副作用幂等键,重发保持不变',
  `started_at` DATETIME(3) DEFAULT NULL COMMENT '首次开始时间',
  `finished_at` DATETIME(3) DEFAULT NULL COMMENT '节点结束时间',
  `result_code` VARCHAR(64) DEFAULT NULL COMMENT '节点结果码',
  `result_message` VARCHAR(512) DEFAULT NULL COMMENT '节点结果说明',
  `result_snapshot` JSON NULL COMMENT '结构化结果;敏感数据只存引用/摘要',
  `create_time` DATETIME(3) NOT NULL COMMENT '创建时间',
  `update_time` DATETIME(3) NOT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_task_revision_node_seq` (`task_id`,`task_revision`,`node_seq`),
  UNIQUE KEY `uk_node_idempotency` (`idempotency_key`),
  KEY `idx_task_node_pick` (`task_id`,`status`,`node_seq`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='机器人任务有序节点实例表';
字段代表含义关键约束
task_revision / node_seq某次任务修订内的步骤顺序同修订从 1 连续递增;旧修订尾部只取消不覆盖,不在已越过的位置插入。
action_code基础能力编码必须在能力注册表存在且机器人声明支持。
target_snapshot目标点和途经点快照实际执行使用快照;原点位变化不改变在途任务。
input_snapshot动作参数已经按 schema 过滤;禁止任意 function 透传。
status节点状态运行时同一 task 只能一行 RUNNING,由应用事务和守卫共同保证。
attempt_no / max_attempts尝试次数开仓等不可安全重复副作用默认不自动重试,先对账。
idempotency_key副作用唯一键服务重发时不变;边缘端需维护近期已执行键结果缓存。
result_snapshot输出结果核验、自检、互动结果结构化保存;敏感明细放专属业务表。
表 5-4 h_robot_task_delivery_item:送药投递快照表

用途:冻结某次送药所需的箱格、药包、患者住院身份、床位和目标点。后续患者转床、格子清空或点位编辑不会篡改历史。

CREATE TABLE `h_robot_task_delivery_item` (
  `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '投递明细主键',
  `task_id` BIGINT NOT NULL COMMENT '送药任务ID',
  `task_revision` INT NOT NULL COMMENT '投递明细所属任务修订号',
  `item_seq` INT NOT NULL COMMENT '任务内投递顺序,从1开始',
  `delivery_node_seq` INT NOT NULL COMMENT '所属投递段起始节点序号',
  `box_id` BIGINT NOT NULL COMMENT '药箱ID快照来源→h_med_box.id',
  `box_code` VARCHAR(32) NOT NULL COMMENT '药箱硬件编码快照',
  `slot_id` BIGINT NOT NULL COMMENT '格子行ID快照来源→h_med_box_slot.id',
  `slot_no` INT NOT NULL COMMENT '物理格号快照,下发开格使用',
  `pack_id` BIGINT NOT NULL COMMENT '药包ID快照来源→h_medicine_pack.id',
  `pack_snapshot` JSON NOT NULL COMMENT '医嘱编码、药品、剂量等最小必要快照',
  `admission_id` BIGINT NOT NULL COMMENT '住院流水ID快照来源',
  `patient_id` BIGINT NOT NULL COMMENT '患者ID快照来源',
  `patient_snapshot` JSON NOT NULL COMMENT '姓名/住院号/腕带核验摘要等快照',
  `bed_id` BIGINT NOT NULL COMMENT '床位ID快照来源',
  `room_id` BIGINT DEFAULT NULL COMMENT '房间ID快照来源',
  `map_point_id` BIGINT NOT NULL COMMENT '床旁目标点ID快照来源',
  `destination_snapshot` JSON NOT NULL COMMENT '区域/地图/房间/床位/点位及坐标快照',
  `verify_mode` VARCHAR(24) NOT NULL COMMENT 'WRISTBAND_QR/MANUAL/OTHER',
  `status` VARCHAR(20) NOT NULL DEFAULT 'PENDING' COMMENT 'PENDING/VERIFYING/OPENED/DELIVERED/FAILED/RETURNED/CANCELED',
  `verified_at` DATETIME(3) DEFAULT NULL COMMENT '身份核验成功时间',
  `delivered_at` DATETIME(3) DEFAULT NULL COMMENT '确认取药完成时间',
  `failure_code` VARCHAR(64) DEFAULT NULL COMMENT '本投递项失败码',
  `active_pack_key` BIGINT GENERATED ALWAYS AS ((CASE WHEN `status` IN ('PENDING','VERIFYING','OPENED') THEN `pack_id` ELSE NULL END)) STORED COMMENT '未终结投递的药包互斥键',
  `active_slot_key` BIGINT GENERATED ALWAYS AS ((CASE WHEN `status` IN ('PENDING','VERIFYING','OPENED') THEN `slot_id` ELSE NULL END)) STORED COMMENT '未终结投递的格子互斥键',
  `create_time` DATETIME(3) NOT NULL COMMENT '创建时间',
  `update_time` DATETIME(3) NOT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_task_revision_item_seq` (`task_id`,`task_revision`,`item_seq`),
  UNIQUE KEY `uk_task_revision_pack` (`task_id`,`task_revision`,`pack_id`),
  UNIQUE KEY `uk_delivery_active_pack` (`active_pack_key`),
  UNIQUE KEY `uk_delivery_active_slot` (`active_slot_key`),
  KEY `idx_slot_active` (`slot_id`,`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='送药任务投递对象不可变快照';
字段组用途为什么必须快照
box / slot知道开哪台箱的哪个物理格格子当前态会在取药后清空。
pack关联药包和医嘱药包状态会从待派送变为配送中、已送达或失败。
patient / admission核验本次住院身份患者姓名、住院流水和腕带属于本次交付证据。
destination记录实际采用的床位、点位和坐标患者可能转床,地图点位也可能后续重标。
status / timestamps每格独立结果支持一项失败、其他项成功以及精准补偿。
active_*_key未终结药包和格子的数据库互斥MySQL 5.7 生成列配合唯一键,阻止并发请求把同一药包或格子占入两个活动任务。
表 5-5 h_robot_task_command:下行命令账本

用途:记录节点产生的每一次 4000 命令、发送尝试、5000 受理回执和最终关联事件。它解决进程重启后 pending Map 丢失的问题。

CREATE TABLE `h_robot_task_command` (
  `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '命令主键',
  `command_id` VARCHAR(64) NOT NULL COMMENT '稳定命令ID,重发保持不变',
  `task_id` BIGINT NOT NULL COMMENT '所属任务ID',
  `task_revision` INT NOT NULL COMMENT '命令创建时的任务修订号',
  `node_id` BIGINT NOT NULL COMMENT '所属节点ID',
  `robot_id` BIGINT NOT NULL COMMENT '目标机器人ID',
  `lease_epoch` BIGINT NOT NULL COMMENT '下发时的机器人队列围栏代次',
  `command_seq` INT NOT NULL COMMENT '节点内命令顺序',
  `function_name` VARCHAR(64) NOT NULL COMMENT 'CR101 function白名单名称',
  `payload` JSON NOT NULL COMMENT '实际下发参数快照,含idempotency_key',
  `status` VARCHAR(20) NOT NULL COMMENT 'CREATED/SENDING/ACCEPTED/REJECTED/TIMEOUT/CONFIRMED/UNKNOWN',
  `attempt_no` INT NOT NULL DEFAULT 0 COMMENT '发送尝试次数',
  `message_id` VARCHAR(64) DEFAULT NULL COMMENT '最近一次4000/5000关联ID',
  `response_code` INT DEFAULT NULL COMMENT '5000受理回执code',
  `response_payload` JSON NULL COMMENT '5000回执内容',
  `sent_at` DATETIME(3) DEFAULT NULL COMMENT '最近发送时间',
  `accepted_at` DATETIME(3) DEFAULT NULL COMMENT '设备受理时间',
  `confirmed_at` DATETIME(3) DEFAULT NULL COMMENT '事实事件确认完成时间',
  `last_error` VARCHAR(512) DEFAULT NULL COMMENT '最近发送错误',
  `create_time` DATETIME(3) NOT NULL COMMENT '创建时间',
  `update_time` DATETIME(3) NOT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_command_id` (`command_id`),
  UNIQUE KEY `uk_node_command_seq` (`node_id`,`command_seq`),
  KEY `idx_command_recovery` (`robot_id`,`status`,`update_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='机器人下行命令及受理回执账本';
表 5-6 h_robot_task_event:设备事实事件 Inbox

用途:先持久化、后 ACK、再投影任务状态。相同设备重传相同 message_id 时命中唯一键,只补发 ACK,不重复推进节点。

CREATE TABLE `h_robot_task_event` (
  `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '事件账本主键',
  `robot_id` BIGINT NOT NULL COMMENT '上报机器人ID',
  `device_code` VARCHAR(64) NOT NULL COMMENT '上报连接设备编码快照',
  `message_id` VARCHAR(64) NOT NULL COMMENT '6000帧messageId;设备内幂等键',
  `event_name` VARCHAR(64) NOT NULL COMMENT '事件名,如 nav_status/node_finished',
  `task_id` BIGINT DEFAULT NULL COMMENT '解析并验证后的任务ID',
  `task_revision` INT DEFAULT NULL COMMENT '事件声明的任务修订号;任务事件必须有值',
  `node_id` BIGINT DEFAULT NULL COMMENT '解析并验证后的节点ID',
  `lease_epoch` BIGINT NOT NULL COMMENT '设备执行该节点时接受的队列围栏代次',
  `event_seq` BIGINT NOT NULL COMMENT '设备会话内单调序号,供乱序检测',
  `device_session_id` VARCHAR(64) NOT NULL COMMENT '设备本次注册会话ID',
  `device_time` DATETIME(3) DEFAULT NULL COMMENT '设备产生事件时间',
  `received_at` DATETIME(3) NOT NULL COMMENT '服务端接收时间',
  `payload` JSON NOT NULL COMMENT '原始事件数据,审计与重放使用',
  `process_status` VARCHAR(20) NOT NULL DEFAULT 'RECEIVED' COMMENT 'RECEIVED/APPLIED/IGNORED/REJECTED/ERROR',
  `process_note` VARCHAR(512) DEFAULT NULL COMMENT '忽略、拒绝或处理错误说明',
  `applied_at` DATETIME(3) DEFAULT NULL COMMENT '成功投影时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_device_message` (`device_code`,`message_id`),
  UNIQUE KEY `uk_device_event_seq` (`device_code`,`device_session_id`,`event_seq`),
  KEY `idx_task_event` (`task_id`,`id`),
  KEY `idx_node_event` (`node_id`,`id`),
  KEY `idx_event_replay` (`process_status`,`received_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='机器人任务事实事件Inbox与审计账本';
表 5-7 h_robot_task_outbox:事务后可靠发送表

用途:任务事务只负责写“应该发送什么”,独立发送器在提交后发送。服务进程崩溃不会造成“数据库已提交但命令永远没发”。

CREATE TABLE `h_robot_task_outbox` (
  `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT 'Outbox主键',
  `aggregate_type` VARCHAR(24) NOT NULL COMMENT 'TASK/NODE/COMMAND/EVENT',
  `aggregate_id` BIGINT NOT NULL COMMENT '关联聚合主键',
  `outbox_type` VARCHAR(32) NOT NULL COMMENT 'DEVICE_COMMAND/FRONTEND_PUSH/SCHEDULER_WAKEUP',
  `dedup_key` VARCHAR(96) NOT NULL COMMENT '发送幂等键',
  `payload` JSON NOT NULL COMMENT '待发送内容',
  `status` VARCHAR(16) NOT NULL DEFAULT 'PENDING' COMMENT 'PENDING/SENDING/SENT/FAILED/DEAD',
  `retry_count` INT NOT NULL DEFAULT 0 COMMENT '已重试次数',
  `next_retry_at` DATETIME(3) DEFAULT NULL COMMENT '下一次可重试时间',
  `locked_by` VARCHAR(64) DEFAULT NULL COMMENT '发送实例租约持有者',
  `locked_until` DATETIME(3) DEFAULT NULL COMMENT '发送租约到期时间',
  `sent_at` DATETIME(3) DEFAULT NULL COMMENT '成功交付时间',
  `last_error` VARCHAR(512) DEFAULT NULL COMMENT '最近错误',
  `create_time` DATETIME(3) NOT NULL COMMENT '创建时间',
  `update_time` DATETIME(3) NOT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_outbox_dedup` (`dedup_key`),
  KEY `idx_outbox_pick` (`status`,`next_retry_at`,`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='任务事务Outbox可靠发送表';
表 5-8 h_robot_task_template:组合任务模板版本表

用途:保存经过审核的组合任务定义。template_code + version 唯一;发布版本不可覆盖,只能新增版本。

CREATE TABLE `h_robot_task_template` (
  `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '模板版本主键',
  `template_code` VARCHAR(64) NOT NULL COMMENT '稳定模板编码,如 MEDICINE_DELIVERY',
  `version` INT NOT NULL COMMENT '模板版本,从1递增',
  `name` VARCHAR(128) NOT NULL COMMENT '模板名称',
  `task_type` VARCHAR(32) NOT NULL COMMENT '生成的组合任务类型',
  `definition` JSON NOT NULL COMMENT '按node_seq排列的节点定义与参数schema引用',
  `status` VARCHAR(16) NOT NULL COMMENT 'DRAFT/PUBLISHED/RETIRED',
  `checksum` VARCHAR(64) NOT NULL COMMENT 'definition规范化后的SHA-256',
  `create_by` BIGINT DEFAULT NULL COMMENT '创建人',
  `create_time` DATETIME(3) NOT NULL COMMENT '创建时间',
  `published_at` DATETIME(3) DEFAULT NULL COMMENT '发布时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_template_version` (`template_code`,`version`),
  KEY `idx_template_status` (`template_code`,`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='机器人组合任务模板版本表';

5.4 数据保留与归档

  • 任务主表和节点表长期在线保留;大体量事件按月分区或按时间归档到历史表,但不物理丢失。
  • 任务列表只查询投影列,不扫描 JSON;详情页再按 task_id 拉节点、投递项和事件。
  • JSON 只保存不可频繁检索的快照,不把状态、排序、关联主键塞进 JSON。
  • 患者敏感信息只保存完成任务所需最小集,日志和错误文本禁止打印腕带原文、身份证和完整答案。

6. 送药任务完整数据链

药箱格子到送药任务快照药站装入药包形成格子当前态和装药流水,创建任务时关联患者床位地图点并冻结投递快照,随后展开送药节点入机器人队列。 装药事实 h_med_box箱体 / 挂载设备 / 区域 h_med_box_slotslot_no / pack_id / 当前态 h_med_box_fill_log装入时间 / 扫码 / 生命周期 h_medicine_pack药包 / 住院流水 / 医嘱 目的地事实 h_patient_admission患者 / 当前病区 / 当前床位 h_bed + h_room床位与房间归属 h_map_pointRBP床旁点 / RP房门点 / HP h_mapmap_id / area_id / 文件就绪 入队事务校验与冻结 1. 箱体挂在目标机器人2. 格子有药且门态安全3. area_id 全链一致4. map_id 明确且文件就绪5. 床旁点和 HP 均存在6. 药包未被活动任务占用7. 冻结当时全部最小快照 DeliveryItem一包 / 一格 / 一患者独立状态与结果 Task + Nodes按目的地点展开节点按 queue_seq 入队 失败即整单拒绝不产生半任务
图 6-1 现有表提供当前业务事实;DeliveryItem 保存本次任务证据;二者职责不能混用。

6.1 多患者、多格子的节点展开

一期以稳定性优先:按后台确认的投递顺序生成“患者投递段”。每段内部固定为导航、问候、核验、开格、提示、取药确认、关格;最后只有一个 RETURN_HOME。多个格子属于同一患者且同一目的地点时,可在同一投递段内依次开格,但每格仍有独立 DeliveryItem。

不在一期自动优化路线:自动按距离重排会改变医护确认过的顺序,还需要跨地图距离模型。可在后续加入“提交前建议排序”,由操作员确认后再冻结,运行中不再重排。

6.2 送达与清格事务

创建送药任务时,锁定箱体、格子和药包当前行,写入 DeliveryItem 活动互斥键,并把 h_medicine_pack.status 从待派送转为配送中。现有 h_medicine_pack.task_id 仍属于旧任务表,迁移评审前不得写入新版任务 ID;新关联只查询 DeliveryItem。

  1. 患者身份核验成功事件落库,DeliveryItem 进入 VERIFYING/可开格状态。
  2. 开格命令使用 box_code + slot_no + idempotency_key;5000 只记 ACCEPTED。
  3. 边缘端上报门已开、患者确认取药、门已关三个事实;缺任一事实均不判定已送达。
  4. 在同一数据库事务中把 DeliveryItem 标记 DELIVERED、更新药包状态、清空格子当前占用、补齐 fill_log.emptied_at,并写事件/Outbox。
  5. 若只完成部分格子,任务可终态为 PARTIAL_SUCCESS;失败格创建引用原任务的补偿任务,不重写原记录。

7. 状态机与运行中控制

7.1 任务状态机

任务状态机任务从草稿进入排队,队头被调度后运行,可暂停、返航、成功、部分成功、失败或取消,终态不可回退。 DRAFT QUEUED DISPATCHING RUNNING PAUSE_REQUESTED PAUSED RETURNING SUCCEEDED PARTIAL_SUCCESS FAILED CANCELED BLOCKED 提交轮到队头受理全成部分 暂停请求安全停稳继续召回/低电量返航失败取消
图 7-1 终态 SUCCEEDED / PARTIAL_SUCCESS / FAILED / CANCELED 不允许回退;重试创建新任务。

7.2 暂停、继续、返航、取消、变更

操作允许条件执行语义不能做的事
暂停RUNNING 且当前节点声明可暂停先写 PAUSE_REQUESTED,再命令边缘安全停稳,事实确认后 PAUSED不能收到 HTTP 请求就直接写 PAUSED
继续PAUSED、上下文仍有效、地图/设备/箱格复核通过从当前节点幂等恢复;无法恢复则重新执行该节点不能跳到下一个节点假装当前节点完成
返航RUNNING 或 PAUSED,操作员二次确认当前节点安全收口,任务转 RETURNING,导航到本任务 map_id 的 HP不是把 RETURN_HOME 插入原队列队尾
取消QUEUED 可直接取消;运行中需安全停止排队任务终态 CANCELED;运行任务先停止再确认终态不删除任务,不释放未核实的药包占用
任务变更QUEUED 可编辑;RUNNING 必须先安全暂停为 PAUSED,且只改同机器人/area/map 下尚未执行的白名单字段旧修订尾部标记 CANCELED,递增 revision 后插入新 Node/DeliveryItem,写 TASK_REVISED 事件已完成证据永不改写;机器人、area_id 或 map_id 变化必须取消后重新入队

7.3 节点失败策略

  • 可安全重试:语音播放、表情、只读自检、URL 打开失败,可按有限次数指数退避。
  • 先对账再重试:导航命令超时、开格命令超时、互动提交超时。先查询/等待设备当前事实,不能盲目补发。
  • 禁止自动重试:可能导致重复开仓、重复提交评测、重复清空格子的副作用操作。
  • 允许人工跳过:仅模板明确标为可选节点;身份核验和关格等安全节点不可跳过。

8. WebSocket、同步异步与一致性

8.1 现有通道保持不变

通道现有编号新版用途
服务端 → 设备4000 Function Call下发单节点命令,携带 command_id、task_no、node_seq、idempotency_key
设备 → 服务端5000 Function Response仅表示受理/拒绝,不作为节点最终成功
设备 → 服务端6000 Event节点开始、进度、到达、核验、开关格、完成、失败等事实
服务端 → 设备6001 Event ACK事件已被持久化或已幂等存在后回复
设备 → 服务端7000 Property电量、底盘、药箱、机器人状态的实时快照
设备生命周期8001~8004注册鉴权和心跳,不承载业务任务
服务端 → 操作端1010~1024 + 新任务消息保留现有设备状态;建议新增独立 1030~1034 任务投影编号

兼容原则:不改变 4000/5000/6000/6001 的外层信封。新增的是 data 字段、任务事件种类和前端任务投影编号。lease_epoch 是队列租约围栏:边缘端记录已接受的最大代次并拒绝更旧命令,服务端投影事件时也必须匹配当前代次。协议变更需服务端与边缘端契约测试同时通过。

8.2 推荐命令信封

{
  "msgType": 4000,
  "messageId": "本次发送唯一UUID",
  "deviceId": "CR101-01",
  "function": "go_target_point",
  "params": {
    "command_id": "CMD-20260915-000081",
    "idempotency_key": "TASK-810:NODE-3:MOVE_A2B:v1",
    "lease_epoch": 12,
    "task_no": "RT202609150081",
    "task_revision": 1,
    "node_seq": 3,
    "map_id": 5,
    "point_id": "32",
    "goal_pose": { "...": "由h_map_point.coordinate快照转换" }
  }
}

8.3 推荐事实事件

{
  "msgType": 6000,
  "messageId": "设备生成并在重传时保持不变",
  "deviceId": "CR101-01",
  "event": "task_node_status",
  "data": {
    "device_session_id": "注册会话ID",
    "event_seq": 1842,
    "lease_epoch": 12,
    "task_no": "RT202609150081",
    "task_revision": 1,
    "node_seq": 3,
    "command_id": "CMD-20260915-000081",
    "status": "SUCCEEDED",
    "progress": 100,
    "result_code": "ARRIVED",
    "device_time": "2026-09-15T10:22:31.442+08:00"
  }
}

8.4 同步与异步边界

动作同步部分异步部分客户端看到什么
创建任务校验和数据库入队事务调度、WS 下发和执行HTTP 202 + task_no + queue_seq
节点下发4000 与 5000 受理关联,最多短时等待真正执行和结果事件先 DISPATCHING,再 RUNNING/终态
暂停/返航校验并记录操作意图机器人安全停止或到达 HP先 REQUESTED,不假装已完成
进度查询REST 读取数据库投影WebSocket 推送变化刷新不丢状态,在线时实时更新

8.5 ACK 顺序必须调整

当前服务端 6000 事件处理是先 ACK、后业务处理,新任务域必须改为:校验连接身份 → 插入 Event Inbox(或命中唯一键)→ 提交 → 回复 6001 → 异步投影。否则服务端在 ACK 后、落库前崩溃会永久丢事件。

8.6 hb_edge 最小执行账本

新增,不是现状:在边缘端使用 Python 标准库 SQLite 建立有界 edge_command_journal。它只负责设备侧幂等和重启恢复,不保存 FIFO 队列,不取代服务端 MySQL。

字段用途安全规则
idempotency_key(PK)/ command_id识别服务端重发的同一副作用命令命中 SUCCEEDED/FAILED 时返回既有结果,不再次驱动硬件
lease_epoch / task_no / task_revision / node_seq绑定当前队列所有权和任务上下文小于设备已接受最大 epoch 的命令直接拒绝
action_code / input_digest确认相同幂等键没有携带不同参数键相同但摘要不同视为协议冲突并告警
state / result / updated_atRECEIVED/RUNNING/SUCCEEDED/FAILED/UNKNOWN 及结果摘要驱动硬件前先事务写 RECEIVED;断电留下 RUNNING 时先查硬件事实,不盲目重做

账本按容量和时间双边界清理,但未收到服务端完成确认、仍属活动任务或涉及开/关格的记录不得淘汰。SQLite 文件采用最小权限,患者身份原文和互动答案不得写入。

9. 电量告警、安全阻断与返航

低电量不作为普通“高优先级任务”插队,因为那会破坏已确定的 FIFO 和药物交付安全。它是设备级安全状态,控制调度门禁。

低电量处置流程电量先告警,持续或达到更低阈值时阻断领取新任务,安全收口当前节点并返航 Home 点,到达后队列保持冻结等待充电和人工恢复。 WARN低于预警阈值提示后台与Pad 持续确认N帧 / T秒去抖避免瞬时波动 ENERGY_BLOCKED不再领取下一任务当前节点安全收口任务转 RETURNING RETURN_HOME同 map_id 查唯一 HP自主导航返航 到达并充电队列仍冻结达恢复阈值后放行 硬急停优先级最高:急停/碰撞/底盘故障直接由边缘端本地安全控制,不等待云端任务状态机。 阈值配置:设备型号级配置 + 电池健康校准;本文不虚构固定百分比,上线前用真机续航测试确定。
图 9-1 先告警、再去抖、后阻断;安全事件不改变队列顺序。

9.1 当前任务如何结算

  • 尚未开格:当前任务标记 BLOCKED/RETURNING,保留未完成 DeliveryItem,返航后由人工决定恢复或生成补偿任务。
  • 格门已开:先完成关格和药品状态对账,再返航;任何情况下不能带着未知门态直接移动。
  • 正在展示内容:安全结束会话并保存阶段性结果,再返航;不把中断评测误标为完成。
  • 无法返航:任务 FAILED,设备进入故障态并告警;后续队列不自动推进。

10. 操作端交互与后台界面原型

CR101 管理后台WebSocket 已连接 · CR101-01
设备管理
任务编排
地图管理
药箱管理
告警与审计
机器人任务驾驶舱
区域 82 · 地图 5 · 当前点 301 房门
设备状态在线 · 任务中
当前任务送药 RT0081
任务进度3 / 8 节点
队列共 4 项

FIFO 任务队列

#101送药 · 3位患者执行中节点 3/8
#102移动 A2B · 会议室排队前方1项
#103宣教 · 301-02床排队前方2项
#104全身模块自检待执行前方3项

当前节点与事件

10:18 任务进入队头
10:18 导航命令已受理
10:20 已到达 301-01床
10:20 等待腕带身份核验
后续:开 3 号格

10.1 后台操作完整链

用户动作界面即时反馈后台事实变化成功/失败展示
点击新建任务选择机器人后锁定其 area;只展示该 area 的地图和业务对象无写入,直到保存草稿或提交缺地图、HP、能力或权限立即阻止下一步
选择送药列出目标机器人所挂药箱及已装药格只读现有 h_* 表异常格显示原因,不允许勾选
确认投递顺序显示患者段和将生成的 node_seq仍未入队跨 area/map 或无床旁点时标红阻止
提交返回 task_no、queue_seq、前方任务数一个事务创建任务、节点、快照、事件、Outbox成功显示 QUEUED;事务失败不产生残缺任务
机器人执行任务、节点、每格结果和原始事件时间线实时更新6000 事件驱动投影离线不清空页面,显示“等待恢复/需对账”
中途召回二次确认未送达对象和影响记录 RETURN_REQUESTED,安全收口后 RETURNING到 HP 后显示“已召回,N项未完成”
全部完成100%,显示每位患者/每格结果任务 SUCCEEDED,原子清空 QueueCursor 活动指针并唤醒调度下一任务自动进入 DISPATCHING

11. API 与实时消息建议

11.1 REST

方法路径用途返回要点
POST/api/robot-tasks/validate零写入预校验和节点预览errors、warnings、compiledNodes
POST/api/robot-tasks幂等创建并加入队尾202、taskNo、queueSeq、aheadCount
GET/api/robots/:id/task-queue设备当前队列运行项 + 等待项 + 阻断原因
GET/api/robot-tasks/:id任务详情投影Task、Nodes、DeliveryItems、controls
GET/api/robot-tasks/:id/events分页事件审计按 event id 顺序,不用设备时间排序推进
POST/api/robot-tasks/:id/actions暂停/继续/召回/取消/重试节点202、operationId、requestedState
PATCH/api/robot-tasks/:id受控修改 QUEUED/PAUSED 任务新 revision、旧尾部取消、新编译尾部
POST/api/robot-task-templates/:code/publish发布不可变模板版本version、checksum

11.2 前端实时消息

建议编号名称最小内容
1030TASK_QUEUE_CHANGEDrobotId、队列摘要、变更 taskNo
1031TASK_STATUS_CHANGEDtaskNo、status、progress、currentNodeSeq、version
1032TASK_NODE_CHANGEDtaskNo、nodeSeq、actionCode、status、progress
1033DELIVERY_ITEM_CHANGEDtaskNo、itemSeq、slotNo、status、resultCode
1034TASK_OPERATION_CHANGEDoperationId、action、requested/result、message

编号需在正式协议评审中锁定。任务详情仍以 REST 数据库投影为基准,WS 消息只通知变化;断线重连后重新拉详情即可收敛。

12. 恢复、对账、安全与可观测

12.1 服务重启后的恢复算法

  1. 扫描 h_robot_task_queue 中租约过期且 active_task_id 非空的游标,再关联 DISPATCHING/RUNNING/PAUSE_REQUESTED/RETURNING 任务。
  2. 若机器人离线:保持原状态,标记 WAITING_DEVICE,不推进队列。
  3. 新实例取得游标租约并递增 lease_epoch;机器人重连后要求上报 device_session_id + current task_no/node_seq/command_id/state/lease_epoch
  4. 数据库与机器人一致:续租并继续等待事件;机器人报告已完成:补录事实并推进;状态不一致:游标和任务进入 BLOCKED,等待人工处置。
  5. 对账完成前绝不自动重发有副作用命令,也不领取下一任务。

12.2 关键异常矩阵

异常系统反应队列反应人工可见信息
4000 发送失败Command 保持 CREATED/UNKNOWN,Outbox 重试队头不释放发送次数、最后错误、下次重试
5000 超时先查边缘事实,不能直接判失败当前节点保持 DISPATCHING“受理状态未知,正在对账”
6000 重复唯一键命中,回复 ACK,不重复投影无变化事件标记 duplicate/ignored
6000 乱序按任务 revision、node_seq、合法迁移校验禁止状态回退原事件保留并标记 IGNORED
机器人断线进入 grace,超时后 OFFLINE;任务不自动失败冻结该设备队列断线时间、当前节点、最后事实
服务进程崩溃数据库租约过期后新实例对账接管不依赖内存 Map 恢复恢复批次和对账结论
患者已转床排队任务阻断并提示修订;在途任务按策略人工确认不静默更换目的地原快照与当前数据差异
格门状态未知禁止移动和再次开格,进入 BLOCKED后续任务不推进箱体、格号、最后门态时间

12.3 权限与审计

  • 创建、暂停、继续、召回、取消、跳过、补偿分别配置权限;急停保持独立最高安全通道。
  • 服务端从登录身份计算可访问 area/device,忽略客户端自报角色和 area。
  • 所有人工操作写操作审计:操作者、原因、旧状态、新状态、请求 ID、来源 IP。
  • 展示 URL 采用 HTTPS、域名白名单、内容版本和校验值;禁止任务参数注入任意脚本地址。
  • 命令、事件和日志中的患者信息最小化;后台按区域和岗位做数据范围授权。
  • 腕带原文、身份凭据和互动答案不进入通用任务 JSON;只保存业务表引用、不可逆摘要或专属加密存储引用,数据库备份同样加密并审计访问。

12.4 最小监控指标

队列健康

每机器人排队数、队头等待时长、任务/节点耗时、BLOCKED 数。

通信健康

在线率、5000 延迟、6000 重传率、Outbox 堆积、未匹配回执。

业务健康

送达率、身份核验失败、格门异常、部分成功、补偿闭环时长。

13. 分阶段落地与验收

阶段范围上线门禁回滚方式
P0 契约固化状态机、能力码、WS data、错误码、地图多图策略三端契约测试和真机能力盘点通过纯文档/测试,无生产行为变化
P1 数据与队列新表、模板编译、FIFO、Outbox/Event Inbox、只读页面并发入队无重复 seq;重启可恢复;旧任务仍只读关闭新系统开关,新表保留审计
P2 A2B 与自检先接无药物副作用任务,验证节点执行器和边缘 SQLite 执行账本1000 次 Mock 队列、断线、重启、乱序测试;真机封闭区验证停止领取新任务,安全结束当前节点
P3 送药闭环药箱/格子快照、身份核验、开关格、每格结果真机门态、幂等、患者转床、部分失败、召回全覆盖入口关闭,已在途任务人工收口
P4 宣教/评测Pad URL、语音互动、内容结果和隐私域名白名单、断网恢复、会话取消、结果幂等模板退役,不影响送药与 A2B
P5 多实例/多机外部连接路由、共享租约、设备选择器故障切换和双实例竞争测试保持单活部署,不改变数据模型

13.1 必测场景

  • 20 个并发创建请求仍为同机器人生成唯一、递增、无空洞要求之外的 queue_seq。
  • 运行任务期间新增任务只进入队尾,不触发任何 stop/cancel 命令。
  • 服务端在“事务提交后、WS 发送前”崩溃,Outbox 恢复后仍能发送。
  • 服务端在“事件落库后、6001 ACK 前”崩溃,重传事件只应用一次。
  • 开格 5000 超时但物理门已开时,系统通过对账收敛,不重复开格。
  • 任务运行中机器人断线、重连、服务端重启,队列顺序和当前节点不丢失。
  • 患者转床、地图归档、点位删除、药箱更换、药包被清空均能在正确阶段被阻断。
  • 低电量去抖、返航、无法返航、门态未知四种路径均不造成下一任务误启动。
  • 身份核验失败时任何开格 function 都不会产生。
  • 任务终态后迟到/重复事件不能把状态从终态回退。
  • 任务修订后收到旧 revision 的迟到事件,只记 IGNORED,不能推进新节点或打开新格子。
  • 边缘端在副作用命令 RECEIVED/RUNNING 后断电,重启必须先对账,不能重复开格或重复提交。

13.2 建议开发顺序

数据库迁移与领域状态机 → 纯 Mock 调度器 → A2B 基础节点 → Event Inbox/Outbox → 后台驾驶舱 → 真机任务能力验收 → 送药节点 → 机器人/药站 Pad → 宣教与评测。

上线红线:真机 start_task/stop_task 当前仍是受理即拒桩。在决定使用“大任务下发”之前必须完成边缘端装配、ROS2 Action Server 联调、取消语义和恢复测试。若未通过,一期应由云端 Node Executor 使用已经接通的细粒度 function 逐节点编排。

13.3 最终不动点

QueueCursor / Task / Node / DeliveryItem / Command / Event / Outbox 七类运行对象,Template 作为配置对象;queue_seq + node_seq 两级顺序;单机器人 FIFO 不抢占;area_id 必填、移动任务 map_id 冻结;WebSocket 受理与事实完成分离;任务和事件长期留存。未来扩展多机时只替换“选择哪台机器人”和“如何取得设备租约”,不推翻任务模型。

设计依据:CR101 核心三端 hb_admin、hb_server、hb_edge,伴随界面 hb_robot_app、hb_site_app,以及 hb100_db 2026-09-14 快照;版本日期 2026-09-15。