23 KiB
23 KiB
任务清单接口设计方案
日期: 2025-01-10 版本: v1.0 目标: 为无人车平台Mock服务增加任务清单接口
📋 需求背景
由无人车侧的平台(mock)提供一个单独的任务清单接口,可以返回无人车所有的可执行任务。然后,我们的应用请求这个接口,选择某个任务,下发给无人车去执行。
核心概念: 任务池/任务库 - 预定义的可执行任务模板清单
🎯 接口设计
接口信息
- 方法:
GET /api/v1/missions/available - 认证: Bearer Token (JWT)
- 响应:
application/json; charset=utf-8 - 功能: 返回无人车平台的所有可执行任务清单
查询参数
| 参数名 | 类型 | 必填 | 描述 | 示例 |
|---|---|---|---|---|
| missionType | string | 否 | 任务类型筛选 | PATROL_TRANSPORT, CARGO_TRANSPORT |
| priority | integer | 否 | 优先级筛选 | 1-5 |
| minBatteryLevel | integer | 否 | 最低电量要求 | 30 |
| limit | integer | 否 | 返回数量限制 | 10 |
| offset | integer | 否 | 分页偏移量 | 0 |
请求头
| Header | 必填 | 示例 | 说明 |
|---|---|---|---|
| Authorization | 是 | Bearer eyJhbGciOi... | JWT 鉴权令牌 |
| Content-Type | 是 | application/json | 固定为 JSON |
| Accept | 否 | application/json | 建议显式声明 |
📦 数据结构设计
任务对象 (Mission)
{
"missionId": "MISSION_TEMPLATE_001",
"missionName": "T1航站楼巡逻",
"missionType": "PATROL_TRANSPORT",
"description": "T1航站楼周边区域巡逻任务",
"priority": 3,
"estimatedDuration": 1800000,
"totalMileage": 2500.0,
"waypoints": [
{
"waypointId": "WP_001",
"latitude": 36.354068,
"longitude": 120.083410,
"waypointType": "START",
"name": "T1航站楼东侧",
"altitude": 10.5,
"estimatedArrivalTime": null
},
{
"waypointId": "WP_002",
"latitude": 36.355123,
"longitude": 120.084567,
"waypointType": "CHECKPOINT",
"name": "巡逻检查点A",
"altitude": 11.2,
"estimatedArrivalTime": null
},
{
"waypointId": "WP_003",
"latitude": 36.356789,
"longitude": 120.085234,
"waypointType": "END",
"name": "T1航站楼北侧",
"altitude": 10.8,
"estimatedArrivalTime": null
}
],
"requirements": {
"vehicleType": "UNMANNED",
"minBatteryLevel": 30,
"requiredSensors": ["GPS", "LIDAR", "CAMERA"],
"weatherConditions": "CLEAR | LIGHT_RAIN"
},
"metadata": {
"createdTime": 1736175000000,
"createdBy": "system",
"tags": ["patrol", "security", "routine"],
"zone": "TERMINAL_1"
}
}
字段说明
基础信息
missionId(string): 任务唯一标识missionName(string): 任务名称missionType(string): 任务类型description(string): 任务描述priority(integer): 优先级 1-5,数字越大优先级越高
时间和里程
estimatedDuration(integer): 预计执行时长(毫秒)totalMileage(number): 预计总里程(米)
路径点 (waypoints)
waypointId(string): 路径点IDlatitude(number): 纬度 (WGS84)longitude(number): 经度 (WGS84)waypointType(string): 路径点类型START: 起点CHECKPOINT: 检查点WAYPOINT: 普通路径点END: 终点
name(string): 路径点名称altitude(number, 可选): 海拔高度(米)estimatedArrivalTime(integer, 可选): 预计到达时间(毫秒,UTC)
任务要求 (requirements)
vehicleType(string): 车辆类型要求minBatteryLevel(integer): 最低电量要求 (0-100)requiredSensors(array): 必需的传感器列表weatherConditions(string): 可执行天气条件
元数据 (metadata)
createdTime(integer): 创建时间(毫秒,UTC)createdBy(string): 创建者tags(array): 标签列表zone(string): 区域标识
📡 响应格式
成功响应 (200 OK)
{
"code": 200,
"message": "success",
"timestamp": 1736175610000,
"data": {
"total": 5,
"missions": [
{
"missionId": "MISSION_TEMPLATE_001",
"missionName": "T1航站楼巡逻",
"missionType": "PATROL_TRANSPORT",
"description": "T1航站楼周边区域巡逻任务",
"priority": 3,
"estimatedDuration": 1800000,
"totalMileage": 2500.0,
"waypoints": [...],
"requirements": {...},
"metadata": {...}
},
{
"missionId": "MISSION_TEMPLATE_002",
"missionName": "货物运输-仓库A到登机口B3",
"missionType": "CARGO_TRANSPORT",
"priority": 4,
"estimatedDuration": 900000,
"totalMileage": 1200.0,
"waypoints": [...],
"requirements": {...},
"metadata": {...}
}
]
}
}
错误响应
401 未授权
{
"code": 401,
"message": "Unauthorized: Invalid or expired token",
"timestamp": 1736175610000,
"data": null
}
400 参数错误
{
"code": 400,
"message": "Invalid parameter: missionType must be one of [PATROL_TRANSPORT, CARGO_TRANSPORT, PASSENGER_TRANSPORT]",
"timestamp": 1736175610000,
"data": null
}
🗂️ 任务类型定义
标准任务类型
| 任务类型 | 代码 | 描述 |
|---|---|---|
| 巡逻运输 | PATROL_TRANSPORT | 定期巡逻和安全检查 |
| 货物运输 | CARGO_TRANSPORT | 货物运输和配送 |
| 客运服务 | PASSENGER_TRANSPORT | 旅客运输服务 |
| 应急响应 | EMERGENCY_RESPONSE | 应急事件响应 |
| 维护检查 | MAINTENANCE_INSPECTION | 设施维护和检查 |
| 清洁作业 | CLEANING_OPERATION | 清洁和环境维护 |
🔧 实现方案
1. 数据存储结构
在 mock_unmanned_vehicle.py 中定义任务池:
# 可执行任务清单(任务模板库)
AVAILABLE_MISSIONS = [
{
"missionId": "MISSION_TEMPLATE_001",
"missionName": "T1航站楼巡逻",
"missionType": "PATROL_TRANSPORT",
"description": "T1航站楼周边区域巡逻任务",
"priority": 3,
"estimatedDuration": 1800000,
"totalMileage": 2500.0,
"waypoints": [
{
"waypointId": "WP_T1_001",
"latitude": 36.354068,
"longitude": 120.083410,
"waypointType": "START",
"name": "T1航站楼东侧",
"altitude": 10.5
},
{
"waypointId": "WP_T1_002",
"latitude": 36.355123,
"longitude": 120.084567,
"waypointType": "CHECKPOINT",
"name": "巡逻检查点A",
"altitude": 11.2
},
{
"waypointId": "WP_T1_003",
"latitude": 36.356789,
"longitude": 120.085234,
"waypointType": "END",
"name": "T1航站楼北侧",
"altitude": 10.8
}
],
"requirements": {
"vehicleType": "UNMANNED",
"minBatteryLevel": 30,
"requiredSensors": ["GPS", "LIDAR", "CAMERA"],
"weatherConditions": "CLEAR | LIGHT_RAIN"
},
"metadata": {
"createdTime": 1736175000000,
"createdBy": "system",
"tags": ["patrol", "security", "routine"],
"zone": "TERMINAL_1"
}
},
{
"missionId": "MISSION_TEMPLATE_002",
"missionName": "货物运输-仓库A到登机口B3",
"missionType": "CARGO_TRANSPORT",
"description": "从货物仓库A运输至B3登机口",
"priority": 4,
"estimatedDuration": 900000,
"totalMileage": 1200.0,
"waypoints": [
{
"waypointId": "WP_CARGO_001",
"latitude": 36.352000,
"longitude": 120.082000,
"waypointType": "START",
"name": "货物仓库A",
"altitude": 9.5
},
{
"waypointId": "WP_CARGO_002",
"latitude": 36.353500,
"longitude": 120.083500,
"waypointType": "WAYPOINT",
"name": "运输通道中点",
"altitude": 10.0
},
{
"waypointId": "WP_CARGO_003",
"latitude": 36.355000,
"longitude": 120.085000,
"waypointType": "END",
"name": "B3登机口",
"altitude": 10.2
}
],
"requirements": {
"vehicleType": "UNMANNED",
"minBatteryLevel": 25,
"requiredSensors": ["GPS", "CAMERA"],
"weatherConditions": "CLEAR | LIGHT_RAIN | CLOUDY",
"maxCargoWeight": 500
},
"metadata": {
"createdTime": 1736175100000,
"createdBy": "system",
"tags": ["cargo", "logistics", "high-priority"],
"zone": "CARGO_AREA"
}
},
{
"missionId": "MISSION_TEMPLATE_003",
"missionName": "T2航站楼接驳",
"missionType": "PASSENGER_TRANSPORT",
"description": "T2航站楼旅客接驳服务",
"priority": 5,
"estimatedDuration": 600000,
"totalMileage": 800.0,
"waypoints": [
{
"waypointId": "WP_T2_001",
"latitude": 36.358000,
"longitude": 120.086000,
"waypointType": "START",
"name": "T2到达大厅",
"altitude": 11.0
},
{
"waypointId": "WP_T2_002",
"latitude": 36.359000,
"longitude": 120.087000,
"waypointType": "END",
"name": "T2出发大厅",
"altitude": 11.5
}
],
"requirements": {
"vehicleType": "UNMANNED",
"minBatteryLevel": 40,
"requiredSensors": ["GPS", "LIDAR", "CAMERA", "ULTRASONIC"],
"weatherConditions": "CLEAR | CLOUDY",
"maxPassengerCount": 4
},
"metadata": {
"createdTime": 1736175200000,
"createdBy": "system",
"tags": ["passenger", "shuttle", "high-priority"],
"zone": "TERMINAL_2"
}
},
{
"missionId": "MISSION_TEMPLATE_004",
"missionName": "跑道周边应急巡查",
"missionType": "EMERGENCY_RESPONSE",
"description": "跑道周边区域应急巡查任务",
"priority": 5,
"estimatedDuration": 1200000,
"totalMileage": 3500.0,
"waypoints": [
{
"waypointId": "WP_RUNWAY_001",
"latitude": 36.360000,
"longitude": 120.088000,
"waypointType": "START",
"name": "跑道17端",
"altitude": 8.0
},
{
"waypointId": "WP_RUNWAY_002",
"latitude": 36.365000,
"longitude": 120.090000,
"waypointType": "CHECKPOINT",
"name": "跑道中段检查点",
"altitude": 8.5
},
{
"waypointId": "WP_RUNWAY_003",
"latitude": 36.370000,
"longitude": 120.092000,
"waypointType": "END",
"name": "跑道35端",
"altitude": 9.0
}
],
"requirements": {
"vehicleType": "UNMANNED",
"minBatteryLevel": 50,
"requiredSensors": ["GPS", "LIDAR", "CAMERA", "THERMAL"],
"weatherConditions": "ANY",
"emergencyLightRequired": true
},
"metadata": {
"createdTime": 1736175300000,
"createdBy": "system",
"tags": ["emergency", "runway", "critical"],
"zone": "RUNWAY_AREA"
}
},
{
"missionId": "MISSION_TEMPLATE_005",
"missionName": "停机坪维护巡检",
"missionType": "MAINTENANCE_INSPECTION",
"description": "停机坪设施维护巡检",
"priority": 2,
"estimatedDuration": 2400000,
"totalMileage": 4200.0,
"waypoints": [
{
"waypointId": "WP_APRON_001",
"latitude": 36.351000,
"longitude": 120.081000,
"waypointType": "START",
"name": "停机坪A1",
"altitude": 10.0
},
{
"waypointId": "WP_APRON_002",
"latitude": 36.352500,
"longitude": 120.082500,
"waypointType": "CHECKPOINT",
"name": "停机坪A5",
"altitude": 10.2
},
{
"waypointId": "WP_APRON_003",
"latitude": 36.354000,
"longitude": 120.084000,
"waypointType": "CHECKPOINT",
"name": "停机坪B2",
"altitude": 10.5
},
{
"waypointId": "WP_APRON_004",
"latitude": 36.355500,
"longitude": 120.085500,
"waypointType": "END",
"name": "停机坪B8",
"altitude": 10.8
}
],
"requirements": {
"vehicleType": "UNMANNED",
"minBatteryLevel": 35,
"requiredSensors": ["GPS", "CAMERA", "INSPECTION_TOOLS"],
"weatherConditions": "CLEAR | CLOUDY"
},
"metadata": {
"createdTime": 1736175400000,
"createdBy": "system",
"tags": ["maintenance", "inspection", "routine"],
"zone": "APRON_AREA"
}
}
]
# 任务类型枚举
MISSION_TYPES = {
"PATROL_TRANSPORT": "巡逻运输",
"CARGO_TRANSPORT": "货物运输",
"PASSENGER_TRANSPORT": "客运服务",
"EMERGENCY_RESPONSE": "应急响应",
"MAINTENANCE_INSPECTION": "维护检查",
"CLEANING_OPERATION": "清洁作业"
}
2. 接口处理函数
@app.route('/api/v1/missions/available', methods=['GET', 'OPTIONS'])
def get_available_missions():
"""
获取可执行任务清单
查询参数:
- missionType: 任务类型筛选
- priority: 优先级筛选
- minBatteryLevel: 最低电量要求
- limit: 返回数量限制
- offset: 分页偏移量
"""
# 处理 CORS 预检请求
if request.method == 'OPTIONS':
return '', 200
try:
# 获取查询参数
mission_type = request.args.get('missionType')
priority = request.args.get('priority', type=int)
min_battery = request.args.get('minBatteryLevel', type=int)
limit = request.args.get('limit', type=int, default=100)
offset = request.args.get('offset', type=int, default=0)
# 筛选任务
filtered_missions = AVAILABLE_MISSIONS.copy()
# 按任务类型筛选
if mission_type:
if mission_type not in MISSION_TYPES:
return jsonify({
"code": 400,
"message": f"Invalid missionType. Must be one of {list(MISSION_TYPES.keys())}",
"timestamp": int(time.time() * 1000),
"data": None
}), 400
filtered_missions = [m for m in filtered_missions if m["missionType"] == mission_type]
# 按优先级筛选
if priority is not None:
if priority < 1 or priority > 5:
return jsonify({
"code": 400,
"message": "Invalid priority. Must be between 1 and 5",
"timestamp": int(time.time() * 1000),
"data": None
}), 400
filtered_missions = [m for m in filtered_missions if m["priority"] == priority]
# 按电量要求筛选
if min_battery is not None:
if min_battery < 0 or min_battery > 100:
return jsonify({
"code": 400,
"message": "Invalid minBatteryLevel. Must be between 0 and 100",
"timestamp": int(time.time() * 1000),
"data": None
}), 400
filtered_missions = [
m for m in filtered_missions
if m["requirements"]["minBatteryLevel"] <= min_battery
]
# 分页处理
total = len(filtered_missions)
paginated_missions = filtered_missions[offset:offset + limit]
# 构造响应
response = {
"code": 200,
"message": "success",
"timestamp": int(time.time() * 1000),
"data": {
"total": total,
"limit": limit,
"offset": offset,
"missions": paginated_missions
}
}
logging.info(f"返回 {len(paginated_missions)} 个可执行任务 (总数: {total})")
return jsonify(response), 200
except Exception as e:
logging.error(f"获取任务清单失败: {str(e)}")
return jsonify({
"code": 500,
"message": f"Internal server error: {str(e)}",
"timestamp": int(time.time() * 1000),
"data": None
}), 500
3. CORS 配置
确保在文件中已有 CORS 配置:
@app.after_request
def after_request(response):
"""添加 CORS 头"""
response.headers.add('Access-Control-Allow-Origin', '*')
response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization,Accept,X-Request-Id')
response.headers.add('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,OPTIONS')
return response
🧪 测试用例
1. 获取所有任务
curl -X GET "http://localhost:5000/api/v1/missions/available" \
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..." \
-H "Accept: application/json"
2. 按任务类型筛选
curl -X GET "http://localhost:5000/api/v1/missions/available?missionType=CARGO_TRANSPORT" \
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..." \
-H "Accept: application/json"
3. 按优先级筛选
curl -X GET "http://localhost:5000/api/v1/missions/available?priority=5" \
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..." \
-H "Accept: application/json"
4. 按电量要求筛选
curl -X GET "http://localhost:5000/api/v1/missions/available?minBatteryLevel=50" \
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..." \
-H "Accept: application/json"
5. 分页查询
curl -X GET "http://localhost:5000/api/v1/missions/available?limit=2&offset=0" \
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..." \
-H "Accept: application/json"
6. 组合筛选
curl -X GET "http://localhost:5000/api/v1/missions/available?missionType=PATROL_TRANSPORT&priority=3&limit=10" \
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..." \
-H "Accept: application/json"
📐 实施步骤
Phase 1: 基础实现 ✅
- 在
mock_unmanned_vehicle.py中添加任务池数据结构(约200行) - 实现
/api/v1/missions/available接口(约80行) - 添加基本的任务类型和优先级筛选
- 测试接口基本功能
Phase 2: 增强功能
- 添加更多样化的任务模板(不同区域、不同类型)
- 实现高级筛选(按区域、按标签、按天气条件等)
- 添加任务搜索功能(按名称、描述关键字)
- 优化分页性能
Phase 3: 集成与扩展
- 与任务下发接口集成(下一步需求)
- 添加任务状态管理(可执行、进行中、已完成)
- 实现任务动态更新机制
- 添加任务执行统计和历史记录
🔗 与现有系统的集成
与 /status 接口的关系
/status接口:返回车辆当前正在执行的任务状态/missions/available接口:返回可供选择的任务清单
数据一致性
任务模板中的字段结构应与 missionContext 保持一致:
# 当任务被分配给车辆时,从任务模板创建任务实例
def assign_mission_to_vehicle(vehicle_id, mission_template_id):
template = get_mission_template(mission_template_id)
# 创建任务实例
mission_instance = {
"missionId": f"{template['missionId']}_INSTANCE_{timestamp}",
"missionType": template["missionType"],
"startTime": int(time.time() * 1000),
"estimatedEndTime": int(time.time() * 1000) + template["estimatedDuration"],
"progress": 0.0,
"totalMileage": template["totalMileage"],
"waypoints": [
{**wp, "status": "PENDING"}
for wp in template["waypoints"]
]
}
# 更新车辆数据
vehicle_data["mission_id"] = mission_instance["missionId"]
vehicle_data["mission_type"] = mission_instance["missionType"]
vehicle_data["mission_start_time"] = mission_instance["startTime"]
return mission_instance
📊 后续扩展方向
1. 任务下发接口
POST /api/v1/vehicles/{vehicleId}/missions
用于将选中的任务分配给指定车辆。
2. 任务管理接口
GET /api/v1/missions/{missionId} # 获取任务详情
PUT /api/v1/missions/{missionId} # 更新任务
DELETE /api/v1/missions/{missionId} # 删除任务
POST /api/v1/missions # 创建新任务模板
3. 任务执行控制
POST /api/v1/vehicles/{vehicleId}/missions/{missionId}/pause # 暂停任务
POST /api/v1/vehicles/{vehicleId}/missions/{missionId}/resume # 恢复任务
POST /api/v1/vehicles/{vehicleId}/missions/{missionId}/cancel # 取消任务
4. 任务统计与分析
GET /api/v1/missions/statistics # 任务统计信息
GET /api/v1/missions/history # 任务执行历史
✅ 验收标准
-
接口可访问性
- 接口正常响应 200 状态码
- 支持 CORS 跨域请求
- JWT 认证正常工作
-
数据完整性
- 每个任务包含所有必需字段
- 路径点数据完整且符合规范
- 任务类型和状态枚举值正确
-
筛选功能
- 按任务类型筛选正常
- 按优先级筛选正常
- 按电量要求筛选正常
- 多条件组合筛选正常
-
分页功能
- limit 和 offset 参数正常工作
- total 字段返回正确的总数
- 边界情况处理正确
-
错误处理
- 无效参数返回 400 错误
- 未授权访问返回 401 错误
- 服务器错误返回 500 错误
- 错误消息清晰易懂
-
性能要求
- 响应时间 < 500ms
- 支持并发请求
- 日志记录完整
📝 附录
A. 坐标系统说明
所有坐标使用 WGS84 坐标系:
- 纬度范围: -90 ~ 90
- 经度范围: -180 ~ 180
- 青岛胶东国际机场参考中心: (120.0834104, 36.35406879)
B. 时间格式说明
所有时间戳使用 毫秒级 UTC 时间:
- 格式: Unix timestamp (milliseconds)
- 示例: 1736175610000
- 转换:
int(time.time() * 1000)
C. 参考文档
- 通用无人车运行状态API:
doc/requirement/universal_autonomous_vehicle_api_min_required.md - Mock服务主文件:
tools/mock_unmanned_vehicle.py - 项目说明文档:
CLAUDE.md
文档结束