> ## Documentation Index
> Fetch the complete documentation index at: https://all.mimedal.cn/llms.txt
> Use this file to discover all available pages before exploring further.

# 任务状态与异常恢复

> 说明任务何时算成功、怎样取消，以及断线或重启后该怎么处理。

## 怎样判断任务进行到了哪一步

`operation.status` 告诉调用方这次操作的结果。异步操作还会返回 `system.task.status`，用于继续查询任务。两者的对应关系如下。

`system.device_state` 单独表示设备的实际状态。例如，任务已经排队，但电机还没开始转，不能因此说设备正在运动。

| 任务状态                   | 操作状态                   | 含义                              |
| ---------------------- | ---------------------- | ------------------------------- |
| 无任务                    | `rejected`             | 请求被拒绝，没有为它下发动作                  |
| `queued`               | `accepted`             | 请求已受理，正在排队，还没开始执行               |
| `running`              | `running`              | 已开始执行，还没确认完成                    |
| `waiting_confirmation` | `waiting_confirmation` | 正在等待允许的人工检查或外部测量，需要说明在等什么       |
| `succeeded`            | `succeeded`            | 操作定义中要求的完成条件全部满足                |
| `failed`               | `failed`               | 执行已结束，但没有达到要求；可能已经做了一部分         |
| `cancelled`            | `cancelled`            | 已确认任务未开始，或设备已经安全停止；已经发生的动作不会被撤销 |
| `unknown`              | `unknown`              | 不清楚结果，或不清楚设备是否停止，需要继续核实         |

同步操作可以直接返回最终结果，不必对外创建任务。如果等待超时，但设备仍在执行或结果不清楚，必须返回一个可继续查询的任务编号或执行编号。超时只表示没有及时收到结果，不等于设备执行失败。

## 状态可以怎样变化

| 原状态                              | 可以变为                                                              | 判断依据                                        |
| -------------------------------- | ----------------------------------------------------------------- | ------------------------------------------- |
| `queued`                         | `running`、`waiting_confirmation`、`failed`、`cancelled`、`unknown`   | 启动前重新检查；如果因为检查失败而结束，必须确认没有下发动作              |
| `running`                        | `waiting_confirmation`、`succeeded`、`failed`、`cancelled`、`unknown` | 根据实际结果判断，不能只看驱动有没有报错                        |
| `waiting_confirmation`           | `running`、`succeeded`、`failed`、`cancelled`、`unknown`              | 启动前的检查通过后可以执行，完成检查通过后可以结束；确认被拒绝不代表设备已停止     |
| `unknown`                        | `running`、`waiting_confirmation`、`succeeded`、`failed`、`cancelled` | 查到新结果后更新原任务；变回 running 只表示发现设备仍在运行，不能重新下发动作 |
| `succeeded`、`failed`、`cancelled` | 不再直接修改                                                            | 保留结束记录；后来发现记录有误，应另加更正记录并更新当前状态，不能覆盖历史       |

重复查询可以得到相同状态。短暂的中间状态可以不逐条推送，但必须按顺序保留记录。

`phase` 说明当前阶段：`preconditions` 是启动前检查，`execution` 是执行中，`completion` 是检查完成结果，`reconciliation` 是核实不确定的结果，`finished` 是已结束。人工确认必须说明针对哪个阶段，不能拿启动前的确认当成完成确认。

## 受理任务时怎样占用设备和容器

受理会改变设备或样品状态的请求前，必须检查调用权限、参数、操作和设备定义版本、对象与系统版本，以及所需设备和容器是否被占用。

创建任务、保存幂等键和占用资源必须一起成功，或者能在故障后恢复到一致状态。不能只创建了任务，却没有记录它占用了哪些设备和容器。

设备开始动作前，应在已经锁定相关资源的情况下再检查一次现场状态。从检查到启动这段时间，不能让另一个冲突任务插进来。多个进程或服务共同管理设备时，也必须遵守同一套占用规则。

版本比较以受理前的状态为准。任务自己加锁也可能改变系统版本，服务端要记录这个变化。启动前再次检查时，不能把自己的加锁误判成外部冲突，也不能忽略其他任务或设备造成的变化。

任务结果未知，或还在等待完成确认时，相关设备和容器必须继续保留给原任务，或者暂停使用。锁过期、程序退出、网络断开都不能证明设备已经停止。任务结束并释放资源后，下一个任务仍要检查设备是否可用。

## 怎样避免重试造成重复动作

改变状态的请求必须带 `system.idempotency_key`，称为幂等键。它用于识别“这是同一次操作的重试”，避免再次加液、搬运或启动设备。实际接口使用其他字段时，必须说明两者的对应关系。

实现必须说明：哪些调用方和设备共用一组键、键保存多久、怎样比较两个请求，以及重启后怎样找回记录。

1. 同一调用方对同一设备发送相同键、相同内容的请求，应返回原任务和最新结果，不再下发动作。
2. 相同键却带了不同内容，应返回 `idempotency_conflict`。比较内容包括目标设备、操作和设备定义版本、对象及其预期版本、参数、执行模式和安全限制；不包括每次通信可能变化的跟踪 ID。
3. 找到原任务后，先检查调用方是否有权读取，再返回结果。任务自己改变了状态版本，不应导致这次查询被拒绝，更不能因此重新执行。
4. 原任务即使失败、取消或结果未知，相同键的请求也不能创建新任务。
5. 原任务还没结束或还在核实结果时，不能删除对应的键。保存期限已过、无法继续去重时，必须告知调用方，先查清旧操作的结果，再决定是否发起新操作。

幂等键不能解决所有设备端问题。例如，命令已发出但回执丢失，服务端仍可能不知道设备做没做。这时必须核实结果，不能直接重发；`retryable: true` 也不是重发设备动作的许可。

## 取消任务不等于设备已经停止

收到取消请求，只能说明调用方想停止任务。只有确认任务尚未开始，或者取得操作定义要求的安全停止证据后，才能返回 `cancelled`。

等待停止时，保留当前任务状态，并标记 `cancel_requested: true`。无法确认停止时，改为 `unknown`。

远程停止功能必须说明谁能调用、哪些阶段能用，以及怎样确认已经停止。可以设置专门的停止通道，避免正在运行的任务持有锁时无法停止设备；但其他普通动作不能借用这个通道绕过锁。

硬件急停是独立的保护措施。远程停止成功或任务取消成功，不能证明设备已具备合格的硬件急停功能。

重复取消时返回原任务的当前状态。如果取消请求到达前任务已经完成，应返回完成结果，不能改成已取消。当前阶段不能安全取消时，返回 `unsafe_cancellation`，同时保留原任务的真实状态。

## 怎样使用人工检查和外部测量

只有操作定义允许时，人工检查、视觉识别或外部传感器结果才能用来判断是否可以启动、是否已经完成。

每条记录必须写清：记录编号、谁或哪个传感器提供的结果、设备、任务、容器、测量字段、值、单位、时间、有效期、任务阶段，以及依据的状态版本。

服务端必须检查提交者权限、结果是否属于当前任务和阶段、有没有过期、版本是否对应。不能把另一个任务的确认拿来使用。模型猜测、静态 Skill 说明或一句没有依据的“已完成”，都不能当成设备实际结果。

如果外部检查说“已停止”，但设备反馈仍在运动，应保留两份记录和冲突说明，暂停使用相关资源，不能直接采用“已停止”。

设备已停止，但不知道样品处理到了什么程度时，要分别记录这两件事。按操作定义决定是报告失败，还是继续等待测量；依赖未知样品状态的后续动作仍然不能执行。

## 断线或重启后怎么恢复

执行服务必须保存任务编号、用于比较请求的摘要、幂等键、命令下发记录、资源占用情况、事件和结果证据。使用哪种数据库或线程模型，由实现决定。

恢复后分两种情况处理：

* 能证明命令还没下发：重新检查条件，通过后可以执行。
* 命令可能已经下发，但没有可靠结果：任务设为 `unknown`，先查询设备和样品状态，不能自动重发。

无法确认设备由谁控制时，禁止启动新的冲突任务。恢复、人工核实和解除占用都要记录原因及操作人或服务。

查询结果必须保留实际观察时间和有效期。读取缓存或刷新页面不能把旧测量时间改成当前时间。进度百分比、预计耗时和“执行中”等文字，也不能用来证明操作完成。
