feat(amr-api): 新增暂停和恢复AMR的专用API接口,替换原有的通用控制接口以提升语义清晰度和错误处理

This commit is contained in:
xudan 2025-12-08 10:17:23 +08:00
parent 7dba81550e
commit 18d4205b74
5 changed files with 781 additions and 13 deletions

View File

@ -0,0 +1,697 @@
# 路径导航、手动充电、小车暂停/恢复运行接口文档
## 接口概述
**接口名称:** AMR小车控制接口
**接口描述:** 提供AMR小车的暂停/恢复运行控制、手动发布充电任务和手动下发移动任务(路径导航)功能。在某个场景中选择一个小车,如果该小车正在运行中,可以下发暂停运行指令,将小车置为暂停状态;如果小车是暂停状态,可以下发恢复运行指令,使小车恢复到之前的运行状态;也可以手动为小车发布充电任务(需要小车处于空载状态);还可以手动为小车下发移动任务,指定小车移动到目标站点
---
## 目录
- [1. 暂停小车运行](#1-暂停小车运行)
- [2. 恢复小车运行](#2-恢复小车运行)
- [3. 手动发布充电任务](#3-手动发布充电任务)
- [4. 手动下发小车移动任务](#4-手动下发小车移动任务)
- [通用规范](#通用规范)
- [错误处理](#错误处理)
- [调用示例](#调用示例)
- [注意事项](#注意事项)
---
## 1. 暂停小车运行
### 接口信息
| 项目 | 内容 |
|------|------|
| **请求方法** | PUT |
| **请求路径** | `/amr/{id}/pause` |
| **接口标识** | pauseAmr |
| **权限要求** | `amr:vwed_amr:edit` |
### 接口描述
如果小车正在运行中(`navigationalState``"driving"`),可以下发暂停运行指令,将小车置为暂停状态。
### 请求参数
#### 路径参数
| 参数名 | 类型 | 必填 | 说明 | 示例值 |
|--------|------|------|------|--------|
| id | String | 是 | AMR的ID | `"amr_001"` |
#### 请求体参数
| 参数名 | 类型 | 必填 | 说明 | 示例值 |
|--------|------|------|------|--------|
| sceneId | String | 是 | 场景ID | `"scene_001"` |
### 请求示例
```json
PUT /amr/amr_001/pause
Content-Type: application/json
{
"sceneId": "scene_001"
}
```
### 响应数据
#### 成功响应
**HTTP状态码** 200
**业务状态码:** 200
```json
{
"success": true,
"message": "暂停小车运行成功!",
"code": 200,
"result": "暂停小车运行成功!",
"timestamp": 1728547200000
}
```
#### 错误响应
**参数错误AMR ID为空**
```json
{
"success": false,
"message": "AMR ID为空",
"code": 500,
"result": null,
"timestamp": 1728547200000
}
```
**参数错误场景ID为空**
```json
{
"success": false,
"message": "场景ID为空",
"code": 500,
"result": null,
"timestamp": 1728547200000
}
```
**业务错误(小车不存在、不在线、不是运行状态或指令发送失败)**
```json
{
"success": false,
"message": "暂停小车运行失败!可能是小车不存在、不在线、不是运行状态或指令发送失败,请查看日志获取详细信息。",
"code": 500,
"result": null,
"timestamp": 1728547200000
}
```
---
## 2. 恢复小车运行
### 接口信息
| 项目 | 内容 |
|------|------|
| **请求方法** | PUT |
| **请求路径** | `/amr/{id}/resume` |
| **接口标识** | resumeAmr |
| **权限要求** | `amr:vwed_amr:edit` |
### 接口描述
如果小车是暂停状态(`navigationalState``"paused"`),可以下发恢复运行指令,使小车恢复到之前的运行状态。
### 请求参数
#### 路径参数
| 参数名 | 类型 | 必填 | 说明 | 示例值 |
|--------|------|------|------|--------|
| id | String | 是 | AMR的ID | `"amr_001"` |
#### 请求体参数
| 参数名 | 类型 | 必填 | 说明 | 示例值 |
|--------|------|------|------|--------|
| sceneId | String | 是 | 场景ID | `"scene_001"` |
### 请求示例
```json
PUT /amr/amr_001/resume
Content-Type: application/json
{
"sceneId": "scene_001"
}
```
### 响应数据
#### 成功响应
**HTTP状态码** 200
**业务状态码:** 200
```json
{
"success": true,
"message": "恢复小车运行成功!",
"code": 200,
"result": "恢复小车运行成功!",
"timestamp": 1728547200000
}
```
#### 错误响应
**参数错误AMR ID为空**
```json
{
"success": false,
"message": "AMR ID为空",
"code": 500,
"result": null,
"timestamp": 1728547200000
}
```
**参数错误场景ID为空**
```json
{
"success": false,
"message": "场景ID为空",
"code": 500,
"result": null,
"timestamp": 1728547200000
}
```
**业务错误(小车不存在、不在线、不是暂停状态或指令发送失败)**
```json
{
"success": false,
"message": "恢复小车运行失败!可能是小车不存在、不在线、不是暂停状态或指令发送失败,请查看日志获取详细信息。",
"code": 500,
"result": null,
"timestamp": 1728547200000
}
```
---
## 3. 手动发布充电任务
### 接口信息
| 项目 | 内容 |
|------|------|
| **请求方法** | GET |
| **请求路径** | `/amr/manuallSetCharging/{sceneId}/{amrId}` |
| **接口标识** | manuallSetCharging |
| **权限要求** | 无特殊权限要求(或使用默认权限) |
### 接口描述
手动为指定场景中的小车发布充电任务。**注意:需要进行充电的小车必须是空载状态(`isLoading = false`**。
接口会设置小车的充电标志(`is_need_charge = true`),由调度器算法生成充电任务。
### 请求参数
#### 路径参数
| 参数名 | 类型 | 必填 | 说明 | 示例值 |
|--------|------|------|------|--------|
| sceneId | String | 是 | 场景ID | `"scene_001"` |
| amrId | String | 是 | AMR的ID | `"amr_001"` |
### 请求示例
```
GET /amr/manuallSetCharging/scene_001/amr_001
```
### 响应数据
#### 成功响应
**HTTP状态码** 200
**业务状态码:** 200
```json
{
"success": true,
"message": "手动发布充电任务成功!",
"code": 200,
"result": "手动发布充电任务成功!",
"timestamp": 1728547200000
}
```
#### 错误响应
**参数错误场景ID或AMR ID为空**
```json
{
"success": false,
"message": "场景ID或AMR id为空",
"code": 500,
"result": null,
"timestamp": 1728547200000
}
```
**业务错误(小车不存在)**
```json
{
"success": false,
"message": "小车不存在!",
"code": 500,
"result": null,
"timestamp": 1728547200000
}
```
**业务错误(小车不是空载状态)**
```json
{
"success": false,
"message": "小车当前不是空载状态,无法发送充电指令!",
"code": 500,
"result": null,
"timestamp": 1728547200000
}
```
**业务错误(场景实例不存在或小车不在场景中)**
```json
{
"success": false,
"message": "手动发布充电任务失败!可能是场景实例不存在或小车不在场景中,请查看日志获取详细信息。",
"code": 500,
"result": null,
"timestamp": 1728547200000
}
```
---
## 4. 手动下发小车移动任务
### 接口信息
| 项目 | 内容 |
|------|------|
| **请求方法** | POST |
| **请求路径** | `/task/manual/move` |
| **接口标识** | manualMoveTask |
| **权限要求** | `task:vwed_task:add` |
### 接口描述
手动为指定场景中的小车下发移动任务,使小车移动到目标站点。接口会自动创建任务、任务块和移动动作,并将任务推送到调度器队列中执行。
**注意:** 小车必须满足以下条件才能下发移动任务:
- 小车必须存在
- 小车必须在线(`isOnline = 1`
- 小车必须开启接单(`isAcceptTask = 1`
- 小车当前没有正在执行的任务(`currentTaskName``curTaskId` 为空)
- 小车状态必须为空闲(`amrStatus = 0`
- 小车必须关联到目标场景
### 请求参数
#### 请求体参数
| 参数名 | 类型 | 必填 | 说明 | 示例值 |
|--------|------|------|------|--------|
| sceneId | String | 是 | 场景ID | `"scene_001"` |
| amrId | String | 是 | AMR的ID | `"amr_001"` |
| taskName | String | 是 | 任务名称 | `"手动移动任务"` |
| targetStationName | String | 是 | 目标站点名称 | `"station_001"` |
| description | String | 否 | 任务描述 | `"手动下发的小车移动任务"` |
| operator | String | 否 | 操作人,默认为"system" | `"admin"` |
| priority | Integer | 是 | 任务优先级范围1-1000默认500 | `500` |
### 请求示例
```json
POST /task/manual/move
Content-Type: application/json
{
"sceneId": "scene_001",
"amrId": "amr_001",
"taskName": "手动移动任务",
"targetStationName": "station_001",
"description": "手动下发的小车移动任务",
"operator": "admin",
"priority": 500
}
```
### 响应数据
#### 成功响应
**HTTP状态码** 200
**业务状态码:** 200
```json
{
"success": true,
"message": "操作成功",
"code": 200,
"result": {
"taskId": "task_123456",
"taskBlockId": "block_123456",
"vwedTaskId": "vwed_task_123456",
"amrId": "amr_001",
"amrName": "AMR-001",
"currentStationName": "station_002",
"targetStationName": "station_001"
},
"timestamp": 1728547200000
}
```
**响应字段说明:**
| 字段名 | 类型 | 说明 |
|--------|------|------|
| taskId | String | 创建的任务ID |
| taskBlockId | String | 创建的任务块ID |
| vwedTaskId | String | VWED任务ID |
| amrId | String | 小车ID |
| amrName | String | 小车名称 |
| currentStationName | String | 小车当前站点名称 |
| targetStationName | String | 目标站点名称 |
#### 错误响应
**参数错误场景ID为空**
```json
{
"success": false,
"message": "场景ID不能为空",
"code": 500,
"result": null,
"timestamp": 1728547200000
}
```
**参数错误AMR ID为空**
```json
{
"success": false,
"message": "AMR id不能为空",
"code": 500,
"result": null,
"timestamp": 1728547200000
}
```
**参数错误(任务名称为空)**
```json
{
"success": false,
"message": "任务名称不能为空",
"code": 500,
"result": null,
"timestamp": 1728547200000
}
```
**参数错误(目标站点为空)**
```json
{
"success": false,
"message": "目标站点不能为空",
"code": 500,
"result": null,
"timestamp": 1728547200000
}
```
**参数错误(优先级范围错误)**
```json
{
"success": false,
"message": "优先级范围为1-1000",
"code": 500,
"result": null,
"timestamp": 1728547200000
}
```
**业务错误场景ID不存在**
```json
{
"success": false,
"message": "场景ID不存在",
"code": 500,
"result": null,
"timestamp": 1728547200000
}
```
**业务错误(小车不存在)**
```json
{
"success": false,
"message": "指定的小车不存在",
"code": 500,
"result": null,
"timestamp": 1728547200000
}
```
**业务错误(小车未上线)**
```json
{
"success": false,
"message": "小车未上线,无法下发任务",
"code": 500,
"result": null,
"timestamp": 1728547200000
}
```
**业务错误(小车未开启接单)**
```json
{
"success": false,
"message": "小车未开启接单,无法下发任务",
"code": 500,
"result": null,
"timestamp": 1728547200000
}
```
**业务错误(小车正在执行任务)**
```json
{
"success": false,
"message": "小车正在执行任务,无法重复下发",
"code": 500,
"result": null,
"timestamp": 1728547200000
}
```
**业务错误(小车状态非空闲)**
```json
{
"success": false,
"message": "小车状态非空闲,暂不能执行新任务",
"code": 500,
"result": null,
"timestamp": 1728547200000
}
```
**业务错误(小车未关联到目标场景)**
```json
{
"success": false,
"message": "小车未关联到目标场景,无法调度",
"code": 500,
"result": null,
"timestamp": 1728547200000
}
```
---
## 通用规范
### 统一返回结构
所有接口均采用统一的返回结构:
```json
{
"success": boolean, // 操作是否成功
"message": string, // 提示信息
"code": number, // 状态码200表示成功其他表示失败
"result": any, // 业务数据成功时返回具体数据失败时为null
"timestamp": number // 时间戳
}
```
### 小车状态说明
小车的导航状态(`navigationalState`)可能的值:
- `"driving"`:正在运行中
- `"paused"`:已暂停
- `"finish"`:已完成/空闲
### 操作前提条件
**暂停操作前提:**
- 小车必须存在
- 小车必须在线(`isOnline = 1`
- 小车必须处于运行状态(`navigationalState = "driving"`
**恢复操作前提:**
- 小车必须存在
- 小车必须在线(`isOnline = 1`
- 小车必须处于暂停状态(`navigationalState = "paused"`
**手动发布充电任务前提:**
- 小车必须存在
- 小车必须处于空载状态(`isLoading = false`
- 场景实例必须存在
- 小车必须在场景中
**手动下发移动任务前提:**
- 小车必须存在
- 小车必须在线(`isOnline = 1`
- 小车必须开启接单(`isAcceptTask = 1`
- 小车当前没有正在执行的任务(`currentTaskName``curTaskId` 为空)
- 小车状态必须为空闲(`amrStatus = 0`
- 小车必须关联到目标场景
---
## 错误处理
### 常见错误码
| 错误码 | 说明 | 处理建议 |
|--------|------|----------|
| 200 | 操作成功 | - |
| 500 | 操作失败 | 查看错误信息,检查小车状态和日志 |
### 错误信息说明
- **"AMR ID为空"**路径参数中的AMR ID未提供或为空
- **"场景ID为空"**请求体中的场景ID未提供或为空
- **"暂停小车运行失败!可能是小车不存在、不在线、不是运行状态或指令发送失败,请查看日志获取详细信息。"**:业务逻辑检查失败或指令发送失败
- **"恢复小车运行失败!可能是小车不存在、不在线、不是暂停状态或指令发送失败,请查看日志获取详细信息。"**:业务逻辑检查失败或指令发送失败
- **"场景ID或AMR id为空"**路径参数中的场景ID或AMR ID未提供或为空
- **"小车不存在!"**:指定的小车不存在
- **"小车当前不是空载状态,无法发送充电指令!"**:小车正在载货,无法发布充电任务
- **"手动发布充电任务失败!可能是场景实例不存在或小车不在场景中,请查看日志获取详细信息。"**:场景实例不存在或小车不在场景中
- **"场景ID不能为空"**请求体中的场景ID未提供或为空
- **"AMR id不能为空"**请求体中的AMR ID未提供或为空
- **"任务名称不能为空"**:请求体中的任务名称未提供或为空
- **"目标站点不能为空"**:请求体中的目标站点未提供或为空
- **"优先级范围为1-1000"**:请求体中的优先级不在有效范围内
- **"场景ID不存在"**指定的场景ID不存在
- **"指定的小车不存在"**:指定的小车不存在
- **"小车未上线,无法下发任务"**:小车不在线
- **"小车未开启接单,无法下发任务"**:小车未开启接单功能
- **"小车正在执行任务,无法重复下发"**:小车当前有正在执行的任务
- **"小车状态非空闲,暂不能执行新任务"**:小车状态不是空闲状态
- **"小车未关联到目标场景,无法调度"**:小车未关联到指定的场景
---
## 注意事项
1. **权限验证**
- 暂停/恢复接口需要 `amr:vwed_amr:edit` 权限
- 手动发布充电任务接口可能不需要特殊权限
- 手动下发移动任务接口需要 `task:vwed_task:add` 权限
2. **参数校验**
- `sceneId` 为必填参数,前端需要进行相应的表单验证
- AMR ID通过路径参数传递必须提供有效的AMR ID
3. **状态检查**
- 暂停操作要求小车必须处于运行状态(`navigationalState = "driving"`
- 恢复操作要求小车必须处于暂停状态(`navigationalState = "paused"`
- 小车必须在线(`isOnline = 1`)才能执行暂停/恢复操作
- 手动发布充电任务要求小车必须处于空载状态(`isLoading = false`
- 手动下发移动任务要求小车必须在线、开启接单、无在执行任务、状态为空闲,且必须关联到目标场景
4. **错误处理**:建议前端对各种错误情况进行友好的提示处理,特别是状态不匹配的情况
5. **日志记录**:接口调用会自动记录操作日志,便于问题追踪
6. **指令类型**:接口使用 VDA5050 标准的 `pause``resume` 即时动作类型,需要确保设备支持这些指令类型
7. **场景隔离**接口支持场景ID参数符合多场景架构设计
8. **异步操作**:指令发送是异步的,接口返回成功仅表示指令已发送,不代表小车状态已立即改变。建议前端在操作后适当延迟再查询小车状态
9. **手动发布充电任务特殊说明**
- 该接口不会直接发送充电指令给小车,而是设置充电标志
- 调度器算法会检测到充电标志并自动生成充电任务
- 小车会按照调度器的任务安排前往充电站进行充电
- 充电任务的执行时间取决于调度器的任务调度策略
10. **手动下发移动任务特殊说明**
- 该接口会自动创建任务、任务块和移动动作
- 任务块会自动推送到 Redis 队列中,由调度器调度执行
- 小车会按照调度器的任务安排移动到目标站点
- 任务的执行时间取决于调度器的任务调度策略和任务优先级
- 接口会返回创建的任务ID、任务块ID等信息可用于后续查询任务状态
---

View File

@ -6,6 +6,8 @@ const enum API {
= '/amr/controlAmr',
= '/amr/acceptTask',
= '/amr/manuallSetCharging',
AMR = '/amr',
AMR = '/amr',
}
// 只保留右键菜单需要的API函数
@ -85,3 +87,43 @@ export async function setAvailable(id: string): Promise<boolean> {
export async function setUnavailable(id: string): Promise<boolean> {
return await setAcceptTask(id, false);
}
/**
* AMR运行
* @param amrId AMR的IDAMR名称
* @param sceneId ID
* @returns
*/
export async function pauseAmr(amrId: string, sceneId: string): Promise<boolean> {
type B = { sceneId: string };
type D = { success: boolean; message: string; code: number; result: any; timestamp: number };
try {
const response = await http.put<D, B>(`${API.AMR}/${amrId}/pause`, { sceneId });
// response 是 D 类型,包含 success 属性
return (response as any).success;
} catch (error) {
console.error('暂停AMR失败:', error);
return false;
}
}
/**
* AMR运行
* @param amrId AMR的IDAMR名称
* @param sceneId ID
* @returns
*/
export async function resumeAmr(amrId: string, sceneId: string): Promise<boolean> {
type B = { sceneId: string };
type D = { success: boolean; message: string; code: number; result: any; timestamp: number };
try {
const response = await http.put<D, B>(`${API.AMR}/${amrId}/resume`, { sceneId });
// response 是 D 类型,包含 success 属性
return (response as any).success;
} catch (error) {
console.error('恢复AMR失败:', error);
return false;
}
}

View File

@ -172,9 +172,11 @@ const handleActionComplete = (data: { success: boolean; action: string; message?
if (data.success) {
message.success(`${actionName}操作成功`);
} else {
// 使
const errorMessage = data.message || `${actionName}操作失败`;
message.error(errorMessage);
// message HTTP
if (data.message) {
message.error(data.message);
}
// message HTTP
}
emit('actionComplete', data);

View File

@ -159,7 +159,7 @@ interface Props {
}
interface Emits {
(e: 'actionComplete', data: { action: RobotAction; robot: RobotInfo; success: boolean }): void;
(e: 'actionComplete', data: { action: RobotAction; robot: RobotInfo; success: boolean; message?: string }): void;
(e: 'customImage', data: { robotInfo: RobotInfo }): void;
}
@ -257,11 +257,12 @@ const handleRobotAction = async (action: RobotAction | 'custom_image', actionNam
console.log('机器人操作结果:', result);
//
//
emit('actionComplete', {
action,
robot: robotInfo.value,
success: result.success,
message: result.message, // message
});
} catch (error) {
console.error(`机器人${actionName}操作失败:`, error);
@ -270,6 +271,7 @@ const handleRobotAction = async (action: RobotAction | 'custom_image', actionNam
action,
robot: robotInfo.value,
success: false,
message: undefined, // message
});
}
};

View File

@ -103,15 +103,39 @@ async function executeAmrAction(
result = await AmrApi.setUnavailable(robotInfo.id);
break;
case 'pause':
// 暂停 - 有对应API
result = await AmrApi.controlAmr(robotInfo.id, 'pause');
case 'pause': {
// 暂停 - 使用新的API
const sceneId = editorStore.getEditorValue()?.getSceneId();
if (!sceneId) {
console.error('暂停AMR失败: 场景ID未提供');
return {
success: false,
message: '暂停AMR失败: 场景ID未提供',
};
}
// 注意根据接口文档这里传递的是AMR的名称而不是ID
// RobotInfo 接口中使用 label 属性表示机器人名称
const amrName = robotInfo.label || robotInfo.id;
result = await AmrApi.pauseAmr(amrName, sceneId);
break;
}
case 'resume':
// 继续 - 有对应API
result = await AmrApi.controlAmr(robotInfo.id, 'resume');
case 'resume': {
// 继续 - 使用新的API
const sceneId = editorStore.getEditorValue()?.getSceneId();
if (!sceneId) {
console.error('恢复AMR失败: 场景ID未提供');
return {
success: false,
message: '恢复AMR失败: 场景ID未提供',
};
}
// 注意根据接口文档这里传递的是AMR的名称而不是ID
// RobotInfo 接口中使用 label 属性表示机器人名称
const amrName = robotInfo.label || robotInfo.id;
result = await AmrApi.resumeAmr(amrName, sceneId);
break;
}
case 'start':
// 启动 - 有对应API
@ -195,9 +219,10 @@ async function executeAmrAction(
};
}
// API 调用失败时不需要额外的错误提示,因为 http 层已经处理了错误提示
return {
success: result,
message: result ? `${action}操作成功` : '',
message: result ? `${action}操作成功` : '', // 失败时返回空字符串,让全局错误处理显示具体错误
};
} catch (error) {