# 换机器人接口文档

本文档描述了VWED系统换机器人功能的API接口，用于在任务执行过程中动态更换执行机器人。

## 基础信息

- 基础路径：`/api/vwed-task-record`
- 接口标签：`VWED任务运行记录`
- 版本：v1.0

## 功能概述

换机器人功能允许在任务执行过程中更换执行机器人，主要用于以下场景：

1. **机器人故障**：当前执行机器人出现硬件或软件故障
2. **电量不足**：当前机器人电量过低，需要更换到电量充足的机器人
3. **性能优化**：根据实际情况选择更合适的机器人执行任务
4. **负载均衡**：优化机器人资源分配，提高系统整体效率

该功能支持两种场景：
- **未载货场景**：机器人尚未携带货物，从原任务中自动提取参数
- **已载货场景**：机器人已经携带货物，需要指定目标库位和执行动作

## 接口清单

| 序号 | 接口名称 | 接口路径 | 请求方式 | 接口描述 |
| --- | --- | --- | --- | --- |
| 1 | 换机器人 | `/switch-robot/{task_record_id}` | POST | 动态更换任务执行机器人 |

---

## 接口详情

### 1. 换机器人

#### 接口说明

在任务执行过程中动态更换执行机器人。系统会终止当前任务的所有待执行块，动态添加新的任务块（选择机器人 → 获取库位 → 执行通用动作），并通过调度器实时生效，保持任务连续运行。

**核心特性**：
- ✅ 支持未载货和已载货两种场景
- ✅ 智能参数提取（未载货时从原任务自动提取）
- ✅ 实时生效（无需重启任务）
- ✅ 保持任务ID不变
- ✅ 完整的操作日志记录

#### 请求路径

```
POST /api/vwed-task-record/switch-robot/{task_record_id}
```

#### 路径参数

| 参数名 | 类型 | 是否必须 | 描述 |
| --- | --- | --- | --- |
| task_record_id | string | 是 | 任务记录ID，必须是正在运行中的任务 |

#### 请求头

| 参数名 | 类型 | 是否必须 | 描述 |
| --- | --- | --- | --- |
| Content-Type | string | 是 | application/json |
| x-access-token | string | 否 | 系统任务令牌，用于主系统认证 |

#### 请求体参数

| 参数名 | 类型 | 是否必须 | 描述 |
| --- | --- | --- | --- |
| robot_name | string | 否 | 机器人名称（如：AGV_001） |
| robot_group | string | 否 | 机器人组（如：group_A） |
| robot_label_group | string | 否 | 机器人标签组（如：label_1） |
| switch_reason | string | 是 | 更换原因（如：机器人电量不足、机器人故障等） |
| has_cargo | boolean | 是 | 是否载货，true-已载货，false-未载货 |
| site_id | string | 条件必填 | 库位ID（已载货时必填，如：AP540） |
| action | string | 条件必填 | 执行动作（已载货时必填，如：pick、drop） |

**参数说明**：
- `robot_name`、`robot_group`、`robot_label_group` 至少提供一个，用于选择新的机器人
- 当 `has_cargo=true` 时，`site_id` 和 `action` 为必填项
- 当 `has_cargo=false` 时，系统会从原任务定义中自动提取 `site_id` 和 `action`

#### 响应结果

**成功响应**：

```json
{
    "code": 200,
    "message": "换机器人成功(未载货场景)",
    "data": {
        "task_record_id": "550e8400-e29b-41d4-a716-446655440000",
        "switch_reason": "机器人AGV_001电量不足",
        "has_cargo": false,
        "robot_name": "AGV_002",
        "robot_group": "group_A",
        "robot_label_group": "label_1",
        "site_id": "AP154",
        "action": "pick",
        "key_route": "LM532",
        "added_blocks": 1
    }
}
```

**失败响应**：

1. 任务记录不存在：
```json
{
    "code": 404,
    "message": "未找到任务记录 {task_record_id}"
}
```

2. 任务状态错误：
```json
{
    "code": 400,
    "message": "任务状态为 {status},只能对运行中的任务换机器人"
}
```

3. 任务定义格式错误：
```json
{
    "code": 400,
    "message": "任务定义格式不正确"
}
```

4. 已载货场景参数不足：
```json
{
    "code": 400,
    "message": "已载货场景需要提供库位ID和执行动作"
}
```

5. 系统异常：
```json
{
    "code": 500,
    "message": "换机器人失败: {错误详情}"
}
```

#### 响应字段说明

| 字段名 | 类型 | 描述 |
| --- | --- | --- |
| task_record_id | string | 任务记录ID |
| switch_reason | string | 更换原因 |
| has_cargo | boolean | 是否载货 |
| robot_name | string | 新机器人名称 |
| robot_group | string | 新机器人组 |
| robot_label_group | string | 新机器人标签组 |
| site_id | string | 目标库位ID |
| action | string | 执行动作（pick/drop/其他） |
| key_route | string | 关键路径（从原任务提取或使用site_id） |
| added_blocks | integer | 添加的新任务块数量（固定为1，包含3个子块） |

---

## 使用场景和示例

### 场景1：未载货 - 机器人电量不足

**业务场景**：
机器人AGV_001正在前往取货点的途中，尚未取货，系统检测到其电量不足，需要更换为电量充足的AGV_002继续执行任务。

**请求示例**：

```bash
curl -X POST "http://localhost:8000/api/vwed-task-record/switch-robot/550e8400-e29b-41d4-a716-446655440000" \
  -H "Content-Type: application/json" \
  -H "x-access-token: your-token-here" \
  -d '{
    "robot_name": "AGV_002",
    "robot_group": "group_A",
    "robot_label_group": "label_1",
    "switch_reason": "机器人AGV_001电量不足，更换为AGV_002",
    "has_cargo": false
  }'
```

**响应示例**：

```json
{
    "code": 200,
    "message": "换机器人成功(未载货场景)",
    "data": {
        "task_record_id": "550e8400-e29b-41d4-a716-446655440000",
        "switch_reason": "机器人AGV_001电量不足，更换为AGV_002",
        "has_cargo": false,
        "robot_name": "AGV_002",
        "robot_group": "group_A",
        "robot_label_group": "label_1",
        "site_id": "AP154",
        "action": "pick",
        "key_route": "LM532",
        "added_blocks": 1
    }
}
```

**系统行为**：
1. 终止当前任务的所有待执行块
2. 从原任务定义中自动提取：
   - `site_id`: "AP154"（目标取货点）
   - `action`: "pick"（取货动作）
   - `key_route`: "LM532"（关键路径）
3. 添加3个新任务块：
   - 选择机器人块：选择AGV_002
   - 获取库位块：获取AP154库位信息
   - 执行通用动作块：执行pick动作
4. 更新任务定义并通过调度器实时生效
5. AGV_002继续执行原任务的剩余步骤

---

### 场景2：已载货 - 机器人故障需要转运

**业务场景**：
机器人AGV_001已经在AP154取货，正在前往目标放货点AP540的途中发生故障。需要将货物转运到临时库位AP300，然后更换为AGV_003继续执行任务。

**请求示例**：

```bash
curl -X POST "http://localhost:8000/api/vwed-task-record/switch-robot/550e8400-e29b-41d4-a716-446655440000" \
  -H "Content-Type: application/json" \
  -H "x-access-token: your-token-here" \
  -d '{
    "robot_name": "AGV_003",
    "robot_group": "group_B",
    "switch_reason": "机器人AGV_001故障，货物转运至AP300后更换为AGV_003",
    "has_cargo": true,
    "site_id": "AP300",
    "action": "drop"
  }'
```

**响应示例**：

```json
{
    "code": 200,
    "message": "换机器人成功(已载货场景)",
    "data": {
        "task_record_id": "550e8400-e29b-41d4-a716-446655440000",
        "switch_reason": "机器人AGV_001故障，货物转运至AP300后更换为AGV_003",
        "has_cargo": true,
        "robot_name": "AGV_003",
        "robot_group": "group_B",
        "robot_label_group": null,
        "site_id": "AP300",
        "action": "drop",
        "key_route": "LM532",
        "added_blocks": 1
    }
}
```

**系统行为**：
1. 终止当前任务的所有待执行块
2. 使用请求参数：
   - `site_id`: "AP300"（临时转运点）
   - `action`: "drop"（放货动作）
3. 添加3个新任务块：
   - 选择机器人块：选择AGV_003
   - 获取库位块：获取AP300库位信息
   - 执行通用动作块：执行drop动作
4. 更新任务定义并通过调度器实时生效
5. AGV_001将货物放到AP300后，AGV_003接管任务继续执行

---

## 技术实现说明

### 处理流程

```
1. 接收换机器人请求
   ↓
2. 验证任务状态（必须是RUNNING）
   ↓
3. 解析任务定义
   ↓
4. 终止调度器中的当前任务
   ↓
5. 终止所有待执行的任务块
   ↓
6. 提取或使用参数
   ├─ 未载货：从原任务提取 keyRoute, targetSiteLabel, action
   └─ 已载货：使用请求参数 site_id, action
   ↓
7. 构建3个新任务块
   ├─ CSelectAgvBp：选择机器人块
   ├─ GetIdleSiteBp：获取库位块
   └─ CAgvOperationBp：执行通用动作块
   ↓
8. 更新任务定义（追加新块到rootBlock）
   ↓
9. 更新数据库（task_def_detail, ended_reason）
   ↓
10. 通知调度器更新任务定义（实时生效）
   ↓
11. 返回换机器人结果
```

### 核心数据结构

**添加的任务块结构**：

```json
{
    "id": "new_block_id_1",
    "name": "switch_robot_xxx",
    "blockType": "CSelectAgvBp",
    "label": "选择执行机器人(换机器人)",
    "description": "换机器人: {switch_reason}",
    "inputParams": {
        "vehicle": {"value": "AGV_002", "type": "Simple", "required": false},
        "group": {"value": "group_A", "type": "Simple", "required": false},
        "label_group": {"value": "label_1", "type": "Simple", "required": false},
        "keyRoute": {"value": "LM532", "type": "Simple", "required": true}
    },
    "children": {
        "default": [
            {
                "id": "new_block_id_2",
                "name": "get_site_xxx",
                "blockType": "GetIdleSiteBp",
                "label": "获取库位(换机器人)",
                "inputParams": {
                    "siteId": {"value": "AP154", "type": "Simple", "required": false},
                    "locked": {"value": false, "type": "Simple", "required": true},
                    "lock": {"value": true, "type": "Simple", "required": false}
                },
                "children": {}
            },
            {
                "id": "new_block_id_3",
                "name": "action_xxx",
                "blockType": "CAgvOperationBp",
                "label": "机器人通用动作(换机器人)",
                "inputParams": {
                    "targetSiteLabel": {"value": "AP154", "type": "Simple", "required": true},
                    "task": {"value": "pick", "type": "Simple", "required": false},
                    "spin": {"value": "", "type": "Simple", "required": false}
                },
                "children": {}
            }
        ]
    }
}
```

### 数据库变更

**任务记录表（vwed_taskrecord）**：
- `task_def_detail`：更新为包含新任务块的定义
- `ended_reason`：记录换机器人原因，格式：`换机器人(未载货/已载货): {switch_reason}`

**任务块记录表（vwed_blockrecord）**：
- 所有未完成的块：
  - `status`：更新为 2002（CANCEL）
  - `ended_on`：记录终止时间
  - `ended_reason`：记录终止原因，格式：`换机器人终止: {switch_reason}`

---

## 注意事项和最佳实践

### 使用限制

1. **任务状态要求**：
   - 只能对状态为 `RUNNING`（1001）的任务执行换机器人操作
   - 已完成、失败、取消的任务无法换机器人

2. **参数要求**：
   - 至少提供一个机器人选择参数（robot_name、robot_group、robot_label_group）
   - 已载货场景必须提供 site_id 和 action

3. **并发控制**：
   - 同一任务不应同时执行多次换机器人操作
   - 建议在调用前检查任务当前状态

### 最佳实践

1. **参数选择**：
   - 优先使用 `robot_name` 指定具体机器人
   - 使用 `robot_group` 让系统从组中自动选择
   - 使用 `robot_label_group` 基于标签筛选机器人

2. **未载货场景**：
   - 系统会自动从原任务中提取参数，无需手动指定 site_id 和 action
   - 适用于机器人尚未开始实际操作的情况

3. **已载货场景**：
   - 必须明确指定目标库位和动作
   - 建议先将货物放到临时库位，再更换机器人继续后续任务
   - action 常用值：`pick`（取货）、`drop`（放货）

4. **错误处理**：
   - 换机器人失败时，原任务会保持原状态
   - 建议实现重试机制
   - 记录详细的换机器人日志用于问题追溯

5. **监控和日志**：
   - 通过 `ended_reason` 字段追踪换机器人历史
   - 通过任务块记录查看被终止的块
   - 建议在业务系统中记录换机器人操作日志

---

## 错误码说明

| 错误码 | 错误信息 | 说明 | 解决方案 |
| --- | --- | --- | --- |
| 404 | 未找到任务记录 | 任务记录ID不存在 | 检查任务记录ID是否正确 |
| 400 | 只能对运行中的任务换机器人 | 任务状态不是RUNNING | 确认任务正在运行中 |
| 400 | 任务定义解析失败 | 任务定义JSON格式错误 | 检查任务定义数据完整性 |
| 400 | 任务定义格式不正确 | 缺少必要的定义字段 | 确保任务定义包含detail和rootBlock |
| 400 | 已载货场景需要提供库位ID和执行动作 | 已载货时缺少site_id或action | 补充完整的请求参数 |
| 500 | 换机器人失败 | 系统内部错误 | 查看详细错误日志，联系技术支持 |

---

## 相关接口

- [获取任务记录详情](./任务记录接口文档.md#5-获取任务记录详情)：查看换机器人后的任务状态
- [获取任务块运行记录](./任务记录接口文档.md#1-获取任务块运行记录)：查看被终止的块和新添加的块
- [获取任务执行结果](./任务记录接口文档.md#4-获取任务执行结果)：查看完整的任务执行历史
- [终止任务](./任务记录接口文档.md#3-终止任务)：如果换机器人失败，可以终止任务

---

## 更新日志

| 版本 | 日期 | 更新内容 | 作者 |
| --- | --- | --- | --- |
| v1.0 | 2025-10-09 | 初始版本，支持未载货和已载货两种场景 | Claude |

---

## 技术支持

如有问题，请联系：
- 技术文档：[VWED任务模块接口文档](./README.md)
- 问题反馈：提交Issue到项目仓库
