feat: 添加 @react-native-async-storage/async-storage 依赖,重构任务上下文以支持动态参数配置,更新任务表单和任务列表组件以使用新功能,移除 mock 数据文件

This commit is contained in:
xudan 2025-07-22 15:10:57 +08:00
parent 0e0cef6eba
commit 087fc9e0aa
13 changed files with 1664 additions and 119 deletions

709
config.json Normal file
View File

@ -0,0 +1,709 @@
{
"version": "1.0.0",
"locations": [
{ "label": "AP-1", "value": "AP-1" },
{ "label": "AP-2", "value": "AP-2" },
{ "label": "AP-3", "value": "AP-3" },
{ "label": "AP-4", "value": "AP-4" },
{ "label": "AP-5", "value": "AP-5" },
{ "label": "炉前缓存区", "value": "FURNACE_BUFFER" },
{ "label": "热处理上料交接区", "value": "HEAT_TREATMENT_LOADING" },
{ "label": "成品存储区", "value": "FINISHED_STORAGE" },
{ "label": "原料仓库", "value": "RAW_MATERIAL_WAREHOUSE" },
{ "label": "质检区", "value": "QUALITY_INSPECTION" }
],
"locationsBays": [
{ "label": "AS2_2_001", "value": "AS2_2_001" },
{ "label": "AS2_2_002", "value": "AS2_2_002" },
{ "label": "AS2_2_003", "value": "AS2_2_003" },
{ "label": "AS2_2_004", "value": "AS2_2_004" },
{ "label": "AS2_2_005", "value": "AS2_2_005" }
],
"payloads": [
{ "label": "满料架-A1", "value": "满料架-A1" },
{ "label": "满料架-A2", "value": "满料架-A2" },
{ "label": "空料架-B1", "value": "空料架-B1" },
{ "label": "空料架-B2", "value": "空料架-B2" },
{ "label": "空车", "value": "空车" },
{ "label": "钢材-Q235", "value": "钢材-Q235" },
{ "label": "铝合金-6061", "value": "铝合金-6061" }
],
"robotActions": [
{ "label": "运输", "value": "TRANSPORT" },
{ "label": "取货", "value": "PICKUP" },
{ "label": "卸货", "value": "DROPOFF" },
{ "label": "等待", "value": "WAIT" },
{ "label": "充电", "value": "CHARGE" },
{ "label": "清洁", "value": "CLEAN" }
],
"tasks": [
{
"id": "task-001",
"name": "炉前缓存区到热处理上料交接区运输",
"status": "IDLE",
"createdAt": "2024-01-15T08:30:00.000Z",
"parameters": {
"startLocation": {
"label": "起点",
"type": "Select",
"value": "FURNACE_BUFFER",
"required": true,
"options": [
{ "label": "AP-1", "value": "AP-1" },
{ "label": "AP-2", "value": "AP-2" },
{ "label": "AP-3", "value": "AP-3" },
{ "label": "AP-4", "value": "AP-4" },
{ "label": "AP-5", "value": "AP-5" },
{ "label": "炉前缓存区", "value": "FURNACE_BUFFER" },
{ "label": "热处理上料交接区", "value": "HEAT_TREATMENT_LOADING" },
{ "label": "成品存储区", "value": "FINISHED_STORAGE" },
{ "label": "原料仓库", "value": "RAW_MATERIAL_WAREHOUSE" },
{ "label": "质检区", "value": "QUALITY_INSPECTION" }
]
},
"endLocation": {
"label": "终点",
"type": "Select",
"value": "HEAT_TREATMENT_LOADING",
"required": true,
"options": [
{ "label": "AP-1", "value": "AP-1" },
{ "label": "AP-2", "value": "AP-2" },
{ "label": "AP-3", "value": "AP-3" },
{ "label": "AP-4", "value": "AP-4" },
{ "label": "AP-5", "value": "AP-5" },
{ "label": "炉前缓存区", "value": "FURNACE_BUFFER" },
{ "label": "热处理上料交接区", "value": "HEAT_TREATMENT_LOADING" },
{ "label": "成品存储区", "value": "FINISHED_STORAGE" },
{ "label": "原料仓库", "value": "RAW_MATERIAL_WAREHOUSE" },
{ "label": "质检区", "value": "QUALITY_INSPECTION" }
]
},
"robotAction": {
"label": "机器人动作",
"type": "Select",
"value": "TRANSPORT",
"required": true,
"options": [
{ "label": "运输", "value": "TRANSPORT" },
{ "label": "取货", "value": "PICKUP" },
{ "label": "卸货", "value": "DROPOFF" },
{ "label": "等待", "value": "WAIT" },
{ "label": "充电", "value": "CHARGE" },
{ "label": "清洁", "value": "CLEAN" }
]
},
"payload": {
"label": "载荷",
"type": "Select",
"value": "满料架-A1",
"required": false,
"options": [
{ "label": "满料架-A1", "value": "满料架-A1" },
{ "label": "满料架-A2", "value": "满料架-A2" },
{ "label": "空料架-B1", "value": "空料架-B1" },
{ "label": "空料架-B2", "value": "空料架-B2" },
{ "label": "空车", "value": "空车" },
{ "label": "钢材-Q235", "value": "钢材-Q235" },
{ "label": "铝合金-6061", "value": "铝合金-6061" }
]
},
"locationBay": {
"label": "库位",
"type": "Select",
"value": "AS2_2_001",
"required": false,
"options": [
{ "label": "AS2_2_001", "value": "AS2_2_001" },
{ "label": "AS2_2_002", "value": "AS2_2_002" },
{ "label": "AS2_2_003", "value": "AS2_2_003" },
{ "label": "AS2_2_004", "value": "AS2_2_004" },
{ "label": "AS2_2_005", "value": "AS2_2_005" }
]
}
}
},
{
"id": "task-002",
"name": "原料仓库到质检区取货",
"status": "IDLE",
"createdAt": "2024-01-15T09:15:00.000Z",
"parameters": {
"startLocation": {
"label": "起点",
"type": "Select",
"value": "RAW_MATERIAL_WAREHOUSE",
"required": true,
"options": [
{ "label": "AP-1", "value": "AP-1" },
{ "label": "AP-2", "value": "AP-2" },
{ "label": "AP-3", "value": "AP-3" },
{ "label": "AP-4", "value": "AP-4" },
{ "label": "AP-5", "value": "AP-5" },
{ "label": "炉前缓存区", "value": "FURNACE_BUFFER" },
{ "label": "热处理上料交接区", "value": "HEAT_TREATMENT_LOADING" },
{ "label": "成品存储区", "value": "FINISHED_STORAGE" },
{ "label": "原料仓库", "value": "RAW_MATERIAL_WAREHOUSE" },
{ "label": "质检区", "value": "QUALITY_INSPECTION" }
]
},
"endLocation": {
"label": "终点",
"type": "Select",
"value": "QUALITY_INSPECTION",
"required": true,
"options": [
{ "label": "AP-1", "value": "AP-1" },
{ "label": "AP-2", "value": "AP-2" },
{ "label": "AP-3", "value": "AP-3" },
{ "label": "AP-4", "value": "AP-4" },
{ "label": "AP-5", "value": "AP-5" },
{ "label": "炉前缓存区", "value": "FURNACE_BUFFER" },
{ "label": "热处理上料交接区", "value": "HEAT_TREATMENT_LOADING" },
{ "label": "成品存储区", "value": "FINISHED_STORAGE" },
{ "label": "原料仓库", "value": "RAW_MATERIAL_WAREHOUSE" },
{ "label": "质检区", "value": "QUALITY_INSPECTION" }
]
},
"robotAction": {
"label": "机器人动作",
"type": "Select",
"value": "PICKUP",
"required": true,
"options": [
{ "label": "运输", "value": "TRANSPORT" },
{ "label": "取货", "value": "PICKUP" },
{ "label": "卸货", "value": "DROPOFF" },
{ "label": "等待", "value": "WAIT" },
{ "label": "充电", "value": "CHARGE" },
{ "label": "清洁", "value": "CLEAN" }
]
},
"payload": {
"label": "载荷",
"type": "Select",
"value": "钢材-Q235",
"required": false,
"options": [
{ "label": "满料架-A1", "value": "满料架-A1" },
{ "label": "满料架-A2", "value": "满料架-A2" },
{ "label": "空料架-B1", "value": "空料架-B1" },
{ "label": "空料架-B2", "value": "空料架-B2" },
{ "label": "空车", "value": "空车" },
{ "label": "钢材-Q235", "value": "钢材-Q235" },
{ "label": "铝合金-6061", "value": "铝合金-6061" }
]
},
"locationBay": {
"label": "库位",
"type": "Select",
"value": "AS2_2_002",
"required": false,
"options": [
{ "label": "AS2_2_001", "value": "AS2_2_001" },
{ "label": "AS2_2_002", "value": "AS2_2_002" },
{ "label": "AS2_2_003", "value": "AS2_2_003" },
{ "label": "AS2_2_004", "value": "AS2_2_004" },
{ "label": "AS2_2_005", "value": "AS2_2_005" }
]
}
}
},
{
"id": "task-003",
"name": "质检区到成品存储区卸货",
"status": "COMPLETED",
"createdAt": "2024-01-15T10:00:00.000Z",
"parameters": {
"startLocation": {
"label": "起点",
"type": "Select",
"value": "QUALITY_INSPECTION",
"required": true,
"options": [
{ "label": "AP-1", "value": "AP-1" },
{ "label": "AP-2", "value": "AP-2" },
{ "label": "AP-3", "value": "AP-3" },
{ "label": "AP-4", "value": "AP-4" },
{ "label": "AP-5", "value": "AP-5" },
{ "label": "炉前缓存区", "value": "FURNACE_BUFFER" },
{ "label": "热处理上料交接区", "value": "HEAT_TREATMENT_LOADING" },
{ "label": "成品存储区", "value": "FINISHED_STORAGE" },
{ "label": "原料仓库", "value": "RAW_MATERIAL_WAREHOUSE" },
{ "label": "质检区", "value": "QUALITY_INSPECTION" }
]
},
"endLocation": {
"label": "终点",
"type": "Select",
"value": "FINISHED_STORAGE",
"required": true,
"options": [
{ "label": "AP-1", "value": "AP-1" },
{ "label": "AP-2", "value": "AP-2" },
{ "label": "AP-3", "value": "AP-3" },
{ "label": "AP-4", "value": "AP-4" },
{ "label": "AP-5", "value": "AP-5" },
{ "label": "炉前缓存区", "value": "FURNACE_BUFFER" },
{ "label": "热处理上料交接区", "value": "HEAT_TREATMENT_LOADING" },
{ "label": "成品存储区", "value": "FINISHED_STORAGE" },
{ "label": "原料仓库", "value": "RAW_MATERIAL_WAREHOUSE" },
{ "label": "质检区", "value": "QUALITY_INSPECTION" }
]
},
"robotAction": {
"label": "机器人动作",
"type": "Select",
"value": "DROPOFF",
"required": true,
"options": [
{ "label": "运输", "value": "TRANSPORT" },
{ "label": "取货", "value": "PICKUP" },
{ "label": "卸货", "value": "DROPOFF" },
{ "label": "等待", "value": "WAIT" },
{ "label": "充电", "value": "CHARGE" },
{ "label": "清洁", "value": "CLEAN" }
]
},
"payload": {
"label": "载荷",
"type": "Select",
"value": "铝合金-6061",
"required": false,
"options": [
{ "label": "满料架-A1", "value": "满料架-A1" },
{ "label": "满料架-A2", "value": "满料架-A2" },
{ "label": "空料架-B1", "value": "空料架-B1" },
{ "label": "空料架-B2", "value": "空料架-B2" },
{ "label": "空车", "value": "空车" },
{ "label": "钢材-Q235", "value": "钢材-Q235" },
{ "label": "铝合金-6061", "value": "铝合金-6061" }
]
},
"locationBay": {
"label": "库位",
"type": "Select",
"value": "AS2_2_003",
"required": false,
"options": [
{ "label": "AS2_2_001", "value": "AS2_2_001" },
{ "label": "AS2_2_002", "value": "AS2_2_002" },
{ "label": "AS2_2_003", "value": "AS2_2_003" },
{ "label": "AS2_2_004", "value": "AS2_2_004" },
{ "label": "AS2_2_005", "value": "AS2_2_005" }
]
}
}
},
{
"id": "task-004",
"name": "AP-1到AP-2空车运输",
"status": "IDLE",
"createdAt": "2024-01-15T11:30:00.000Z",
"parameters": {
"startLocation": {
"label": "起点",
"type": "Select",
"value": "AP-1",
"required": true,
"options": [
{ "label": "AP-1", "value": "AP-1" },
{ "label": "AP-2", "value": "AP-2" },
{ "label": "AP-3", "value": "AP-3" },
{ "label": "AP-4", "value": "AP-4" },
{ "label": "AP-5", "value": "AP-5" },
{ "label": "炉前缓存区", "value": "FURNACE_BUFFER" },
{ "label": "热处理上料交接区", "value": "HEAT_TREATMENT_LOADING" },
{ "label": "成品存储区", "value": "FINISHED_STORAGE" },
{ "label": "原料仓库", "value": "RAW_MATERIAL_WAREHOUSE" },
{ "label": "质检区", "value": "QUALITY_INSPECTION" }
]
},
"endLocation": {
"label": "终点",
"type": "Select",
"value": "AP-2",
"required": true,
"options": [
{ "label": "AP-1", "value": "AP-1" },
{ "label": "AP-2", "value": "AP-2" },
{ "label": "AP-3", "value": "AP-3" },
{ "label": "AP-4", "value": "AP-4" },
{ "label": "AP-5", "value": "AP-5" },
{ "label": "炉前缓存区", "value": "FURNACE_BUFFER" },
{ "label": "热处理上料交接区", "value": "HEAT_TREATMENT_LOADING" },
{ "label": "成品存储区", "value": "FINISHED_STORAGE" },
{ "label": "原料仓库", "value": "RAW_MATERIAL_WAREHOUSE" },
{ "label": "质检区", "value": "QUALITY_INSPECTION" }
]
},
"robotAction": {
"label": "机器人动作",
"type": "Select",
"value": "TRANSPORT",
"required": true,
"options": [
{ "label": "运输", "value": "TRANSPORT" },
{ "label": "取货", "value": "PICKUP" },
{ "label": "卸货", "value": "DROPOFF" },
{ "label": "等待", "value": "WAIT" },
{ "label": "充电", "value": "CHARGE" },
{ "label": "清洁", "value": "CLEAN" }
]
},
"payload": {
"label": "载荷",
"type": "Select",
"value": "空车",
"required": false,
"options": [
{ "label": "满料架-A1", "value": "满料架-A1" },
{ "label": "满料架-A2", "value": "满料架-A2" },
{ "label": "空料架-B1", "value": "空料架-B1" },
{ "label": "空料架-B2", "value": "空料架-B2" },
{ "label": "空车", "value": "空车" },
{ "label": "钢材-Q235", "value": "钢材-Q235" },
{ "label": "铝合金-6061", "value": "铝合金-6061" }
]
},
"locationBay": {
"label": "库位",
"type": "Select",
"value": "AS2_2_004",
"required": false,
"options": [
{ "label": "AS2_2_001", "value": "AS2_2_001" },
{ "label": "AS2_2_002", "value": "AS2_2_002" },
{ "label": "AS2_2_003", "value": "AS2_2_003" },
{ "label": "AS2_2_004", "value": "AS2_2_004" },
{ "label": "AS2_2_005", "value": "AS2_2_005" }
]
}
}
},
{
"id": "task-005",
"name": "机器人充电任务",
"status": "IDLE",
"createdAt": "2024-01-15T12:00:00.000Z",
"parameters": {
"startLocation": {
"label": "起点",
"type": "Select",
"value": "AP-3",
"required": true,
"options": [
{ "label": "AP-1", "value": "AP-1" },
{ "label": "AP-2", "value": "AP-2" },
{ "label": "AP-3", "value": "AP-3" },
{ "label": "AP-4", "value": "AP-4" },
{ "label": "AP-5", "value": "AP-5" },
{ "label": "炉前缓存区", "value": "FURNACE_BUFFER" },
{ "label": "热处理上料交接区", "value": "HEAT_TREATMENT_LOADING" },
{ "label": "成品存储区", "value": "FINISHED_STORAGE" },
{ "label": "原料仓库", "value": "RAW_MATERIAL_WAREHOUSE" },
{ "label": "质检区", "value": "QUALITY_INSPECTION" }
]
},
"endLocation": {
"label": "终点",
"type": "Select",
"value": "AP-3",
"required": true,
"options": [
{ "label": "AP-1", "value": "AP-1" },
{ "label": "AP-2", "value": "AP-2" },
{ "label": "AP-3", "value": "AP-3" },
{ "label": "AP-4", "value": "AP-4" },
{ "label": "AP-5", "value": "AP-5" },
{ "label": "炉前缓存区", "value": "FURNACE_BUFFER" },
{ "label": "热处理上料交接区", "value": "HEAT_TREATMENT_LOADING" },
{ "label": "成品存储区", "value": "FINISHED_STORAGE" },
{ "label": "原料仓库", "value": "RAW_MATERIAL_WAREHOUSE" },
{ "label": "质检区", "value": "QUALITY_INSPECTION" }
]
},
"robotAction": {
"label": "机器人动作",
"type": "Select",
"value": "CHARGE",
"required": true,
"options": [
{ "label": "运输", "value": "TRANSPORT" },
{ "label": "取货", "value": "PICKUP" },
{ "label": "卸货", "value": "DROPOFF" },
{ "label": "等待", "value": "WAIT" },
{ "label": "充电", "value": "CHARGE" },
{ "label": "清洁", "value": "CLEAN" }
]
},
"payload": {
"label": "载荷",
"type": "Select",
"value": "空车",
"required": false,
"options": [
{ "label": "满料架-A1", "value": "满料架-A1" },
{ "label": "满料架-A2", "value": "满料架-A2" },
{ "label": "空料架-B1", "value": "空料架-B1" },
{ "label": "空料架-B2", "value": "空料架-B2" },
{ "label": "空车", "value": "空车" },
{ "label": "钢材-Q235", "value": "钢材-Q235" },
{ "label": "铝合金-6061", "value": "铝合金-6061" }
]
}
}
},
{
"id": "task-006",
"name": "成品存储区清洁任务",
"status": "RUNNING",
"createdAt": "2024-01-15T13:15:00.000Z",
"parameters": {
"startLocation": {
"label": "起点",
"type": "Select",
"value": "FINISHED_STORAGE",
"required": true,
"options": [
{ "label": "AP-1", "value": "AP-1" },
{ "label": "AP-2", "value": "AP-2" },
{ "label": "AP-3", "value": "AP-3" },
{ "label": "AP-4", "value": "AP-4" },
{ "label": "AP-5", "value": "AP-5" },
{ "label": "炉前缓存区", "value": "FURNACE_BUFFER" },
{ "label": "热处理上料交接区", "value": "HEAT_TREATMENT_LOADING" },
{ "label": "成品存储区", "value": "FINISHED_STORAGE" },
{ "label": "原料仓库", "value": "RAW_MATERIAL_WAREHOUSE" },
{ "label": "质检区", "value": "QUALITY_INSPECTION" }
]
},
"endLocation": {
"label": "终点",
"type": "Select",
"value": "FINISHED_STORAGE",
"required": true,
"options": [
{ "label": "AP-1", "value": "AP-1" },
{ "label": "AP-2", "value": "AP-2" },
{ "label": "AP-3", "value": "AP-3" },
{ "label": "AP-4", "value": "AP-4" },
{ "label": "AP-5", "value": "AP-5" },
{ "label": "炉前缓存区", "value": "FURNACE_BUFFER" },
{ "label": "热处理上料交接区", "value": "HEAT_TREATMENT_LOADING" },
{ "label": "成品存储区", "value": "FINISHED_STORAGE" },
{ "label": "原料仓库", "value": "RAW_MATERIAL_WAREHOUSE" },
{ "label": "质检区", "value": "QUALITY_INSPECTION" }
]
},
"robotAction": {
"label": "机器人动作",
"type": "Select",
"value": "CLEAN",
"required": true,
"options": [
{ "label": "运输", "value": "TRANSPORT" },
{ "label": "取货", "value": "PICKUP" },
{ "label": "卸货", "value": "DROPOFF" },
{ "label": "等待", "value": "WAIT" },
{ "label": "充电", "value": "CHARGE" },
{ "label": "清洁", "value": "CLEAN" }
]
},
"payload": {
"label": "载荷",
"type": "Select",
"value": "空车",
"required": false,
"options": [
{ "label": "满料架-A1", "value": "满料架-A1" },
{ "label": "满料架-A2", "value": "满料架-A2" },
{ "label": "空料架-B1", "value": "空料架-B1" },
{ "label": "空料架-B2", "value": "空料架-B2" },
{ "label": "空车", "value": "空车" },
{ "label": "钢材-Q235", "value": "钢材-Q235" },
{ "label": "铝合金-6061", "value": "铝合金-6061" }
]
}
}
},
{
"id": "task-007",
"name": "满料架A2从原料仓库到AP-4运输",
"status": "IDLE",
"createdAt": "2024-01-15T14:20:00.000Z",
"parameters": {
"startLocation": {
"label": "起点",
"type": "Select",
"value": "RAW_MATERIAL_WAREHOUSE",
"required": true,
"options": [
{ "label": "AP-1", "value": "AP-1" },
{ "label": "AP-2", "value": "AP-2" },
{ "label": "AP-3", "value": "AP-3" },
{ "label": "AP-4", "value": "AP-4" },
{ "label": "AP-5", "value": "AP-5" },
{ "label": "炉前缓存区", "value": "FURNACE_BUFFER" },
{ "label": "热处理上料交接区", "value": "HEAT_TREATMENT_LOADING" },
{ "label": "成品存储区", "value": "FINISHED_STORAGE" },
{ "label": "原料仓库", "value": "RAW_MATERIAL_WAREHOUSE" },
{ "label": "质检区", "value": "QUALITY_INSPECTION" }
]
},
"endLocation": {
"label": "终点",
"type": "Select",
"value": "AP-4",
"required": true,
"options": [
{ "label": "AP-1", "value": "AP-1" },
{ "label": "AP-2", "value": "AP-2" },
{ "label": "AP-3", "value": "AP-3" },
{ "label": "AP-4", "value": "AP-4" },
{ "label": "AP-5", "value": "AP-5" },
{ "label": "炉前缓存区", "value": "FURNACE_BUFFER" },
{ "label": "热处理上料交接区", "value": "HEAT_TREATMENT_LOADING" },
{ "label": "成品存储区", "value": "FINISHED_STORAGE" },
{ "label": "原料仓库", "value": "RAW_MATERIAL_WAREHOUSE" },
{ "label": "质检区", "value": "QUALITY_INSPECTION" }
]
},
"waypoint": {
"label": "途经点",
"type": "Select",
"value": "QUALITY_INSPECTION",
"required": false,
"options": [
{ "label": "AP-1", "value": "AP-1" },
{ "label": "AP-2", "value": "AP-2" },
{ "label": "AP-3", "value": "AP-3" },
{ "label": "AP-4", "value": "AP-4" },
{ "label": "AP-5", "value": "AP-5" },
{ "label": "炉前缓存区", "value": "FURNACE_BUFFER" },
{ "label": "热处理上料交接区", "value": "HEAT_TREATMENT_LOADING" },
{ "label": "成品存储区", "value": "FINISHED_STORAGE" },
{ "label": "原料仓库", "value": "RAW_MATERIAL_WAREHOUSE" },
{ "label": "质检区", "value": "QUALITY_INSPECTION" }
]
},
"robotAction": {
"label": "机器人动作",
"type": "Select",
"value": "TRANSPORT",
"required": true,
"options": [
{ "label": "运输", "value": "TRANSPORT" },
{ "label": "取货", "value": "PICKUP" },
{ "label": "卸货", "value": "DROPOFF" },
{ "label": "等待", "value": "WAIT" },
{ "label": "充电", "value": "CHARGE" },
{ "label": "清洁", "value": "CLEAN" }
]
},
"payload": {
"label": "载荷",
"type": "Select",
"value": "满料架-A2",
"required": false,
"options": [
{ "label": "满料架-A1", "value": "满料架-A1" },
{ "label": "满料架-A2", "value": "满料架-A2" },
{ "label": "空料架-B1", "value": "空料架-B1" },
{ "label": "空料架-B2", "value": "空料架-B2" },
{ "label": "空车", "value": "空车" },
{ "label": "钢材-Q235", "value": "钢材-Q235" },
{ "label": "铝合金-6061", "value": "铝合金-6061" }
]
},
"locationBay": {
"label": "库位",
"type": "Select",
"value": "AS2_2_005",
"required": false,
"options": [
{ "label": "AS2_2_001", "value": "AS2_2_001" },
{ "label": "AS2_2_002", "value": "AS2_2_002" },
{ "label": "AS2_2_003", "value": "AS2_2_003" },
{ "label": "AS2_2_004", "value": "AS2_2_004" },
{ "label": "AS2_2_005", "value": "AS2_2_005" }
]
}
}
},
{
"id": "task-008",
"name": "AP-5等待任务",
"status": "ERROR",
"createdAt": "2024-01-15T15:45:00.000Z",
"parameters": {
"startLocation": {
"label": "起点",
"type": "Select",
"value": "AP-5",
"required": true,
"options": [
{ "label": "AP-1", "value": "AP-1" },
{ "label": "AP-2", "value": "AP-2" },
{ "label": "AP-3", "value": "AP-3" },
{ "label": "AP-4", "value": "AP-4" },
{ "label": "AP-5", "value": "AP-5" },
{ "label": "炉前缓存区", "value": "FURNACE_BUFFER" },
{ "label": "热处理上料交接区", "value": "HEAT_TREATMENT_LOADING" },
{ "label": "成品存储区", "value": "FINISHED_STORAGE" },
{ "label": "原料仓库", "value": "RAW_MATERIAL_WAREHOUSE" },
{ "label": "质检区", "value": "QUALITY_INSPECTION" }
]
},
"endLocation": {
"label": "终点",
"type": "Select",
"value": "AP-5",
"required": true,
"options": [
{ "label": "AP-1", "value": "AP-1" },
{ "label": "AP-2", "value": "AP-2" },
{ "label": "AP-3", "value": "AP-3" },
{ "label": "AP-4", "value": "AP-4" },
{ "label": "AP-5", "value": "AP-5" },
{ "label": "炉前缓存区", "value": "FURNACE_BUFFER" },
{ "label": "热处理上料交接区", "value": "HEAT_TREATMENT_LOADING" },
{ "label": "成品存储区", "value": "FINISHED_STORAGE" },
{ "label": "原料仓库", "value": "RAW_MATERIAL_WAREHOUSE" },
{ "label": "质检区", "value": "QUALITY_INSPECTION" }
]
},
"robotAction": {
"label": "机器人动作",
"type": "Select",
"value": "WAIT",
"required": true,
"options": [
{ "label": "运输", "value": "TRANSPORT" },
{ "label": "取货", "value": "PICKUP" },
{ "label": "卸货", "value": "DROPOFF" },
{ "label": "等待", "value": "WAIT" },
{ "label": "充电", "value": "CHARGE" },
{ "label": "清洁", "value": "CLEAN" }
]
},
"payload": {
"label": "载荷",
"type": "Select",
"value": "空料架-B1",
"required": false,
"options": [
{ "label": "满料架-A1", "value": "满料架-A1" },
{ "label": "满料架-A2", "value": "满料架-A2" },
{ "label": "空料架-B1", "value": "空料架-B1" },
{ "label": "空料架-B2", "value": "空料架-B2" },
{ "label": "空车", "value": "空车" },
{ "label": "钢材-Q235", "value": "钢材-Q235" },
{ "label": "铝合金-6061", "value": "铝合金-6061" }
]
}
}
}
],
"serverUrl": "http://localhost:3000/api"
}

34
package-lock.json generated
View File

@ -8,6 +8,7 @@
"name": "MyReactNativeApp",
"version": "0.0.1",
"dependencies": {
"@react-native-async-storage/async-storage": "^2.2.0",
"@react-native/new-app-screen": "0.80.1",
"@react-navigation/bottom-tabs": "^7.4.2",
"@react-navigation/native": "^7.1.14",
@ -2742,6 +2743,18 @@
"node": ">= 8"
}
},
"node_modules/@react-native-async-storage/async-storage": {
"version": "2.2.0",
"resolved": "https://registry.npmmirror.com/@react-native-async-storage/async-storage/-/async-storage-2.2.0.tgz",
"integrity": "sha512-gvRvjR5JAaUZF8tv2Kcq/Gbt3JHwbKFYfmb445rhOj6NUMx3qPLixmDx5pZAyb9at1bYvJ4/eTUipU5aki45xw==",
"license": "MIT",
"dependencies": {
"merge-options": "^3.0.4"
},
"peerDependencies": {
"react-native": "^0.0.0-0 || >=0.65 <1.0"
}
},
"node_modules/@react-native-community/cli": {
"version": "19.0.0",
"resolved": "https://registry.npmmirror.com/@react-native-community/cli/-/cli-19.0.0.tgz",
@ -7677,6 +7690,15 @@
"node": ">=8"
}
},
"node_modules/is-plain-obj": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
"integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/is-regex": {
"version": "1.2.1",
"resolved": "https://registry.npmmirror.com/is-regex/-/is-regex-1.2.1.tgz",
@ -9368,6 +9390,18 @@
"integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==",
"license": "MIT"
},
"node_modules/merge-options": {
"version": "3.0.4",
"resolved": "https://registry.npmmirror.com/merge-options/-/merge-options-3.0.4.tgz",
"integrity": "sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==",
"license": "MIT",
"dependencies": {
"is-plain-obj": "^2.1.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/merge-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/merge-stream/-/merge-stream-2.0.0.tgz",

View File

@ -10,6 +10,7 @@
"test": "jest"
},
"dependencies": {
"@react-native-async-storage/async-storage": "^2.2.0",
"@react-native/new-app-screen": "0.80.1",
"@react-navigation/bottom-tabs": "^7.4.2",
"@react-navigation/native": "^7.1.14",

View File

@ -1,5 +1,5 @@
import React from 'react';
import { StyleSheet, TouchableOpacity } from 'react-native';
import { StyleSheet, TouchableOpacity, Dimensions } from 'react-native';
import { Card, Chip } from '@rneui/themed';
import { Task, TaskStatus } from '../types/task';
@ -15,8 +15,10 @@ const statusColors: Record<TaskStatus, string> = {
ERROR: 'red',
};
const TaskCard: React.FC<TaskCardProps> = ({ task, onPress }) => {
const { width } = Dimensions.get('window');
const cardWidth = (width - 32) / 2; // 减去padding除以2得到每个卡片的宽度
const TaskCard: React.FC<TaskCardProps> = ({ task, onPress }) => {
return (
<TouchableOpacity onPress={() => onPress(task)} style={styles.touchable}>
<Card containerStyle={styles.card}>
@ -42,12 +44,13 @@ const TaskCard: React.FC<TaskCardProps> = ({ task, onPress }) => {
const styles = StyleSheet.create({
touchable: {
flex: 1,
width: '50%',
width: cardWidth,
marginBottom: 8,
},
card: {
margin: 4,
borderRadius: 8,
width: cardWidth - 8, // 减去margin
},
title: {
marginBottom: 12,

View File

@ -2,7 +2,7 @@ import React, { useState, useRef, useEffect } from 'react';
import { StyleSheet, ScrollView, TouchableOpacity, View } from 'react-native';
import { Input, BottomSheet, ListItem, Button } from '@rneui/themed';
import { Task, RobotAction } from '../types/task';
import { LOCATIONS, ROBOT_ACTIONS, PAYLOADS } from '../data/mockData';
import { useTasks } from '../context/TasksContext';
interface TaskFormProps {
task: Task;
@ -10,6 +10,7 @@ interface TaskFormProps {
}
const TaskForm: React.FC<TaskFormProps> = ({ task, onTaskChange }) => {
const { locations, payloads, robotActions } = useTasks();
const [isVisible, setIsVisible] = useState(false);
const [currentField, setCurrentField] = useState('');
const [currentItems, setCurrentItems] = useState<
@ -232,13 +233,13 @@ const TaskForm: React.FC<TaskFormProps> = ({ task, onTaskChange }) => {
'startLocation',
'起点',
task.parameters.startLocation,
LOCATIONS,
locations,
)}
{renderDropdown(
'endLocation',
'终点',
task.parameters.endLocation,
LOCATIONS,
locations,
)}
<Input
@ -252,9 +253,9 @@ const TaskForm: React.FC<TaskFormProps> = ({ task, onTaskChange }) => {
'robotAction',
'机器人动作',
task.parameters.robotAction,
ROBOT_ACTIONS,
robotActions,
)}
{renderDropdown('payload', '载荷', task.parameters.payload, PAYLOADS)}
{renderDropdown('payload', '载荷', task.parameters.payload, payloads)}
{/* 库位字段 */}
<Input

View File

@ -1,18 +1,119 @@
import React, { createContext, useState, useContext, ReactNode } from 'react';
import React, {
createContext,
useState,
useContext,
ReactNode,
useEffect,
} from 'react';
import { Task } from '../types/task';
import { MOCK_TASKS } from '../data/mockData';
import {
AppConfig,
LocationOption,
PayloadOption,
RobotActionOption,
} from '../types/config';
import {
getConfig,
getSettings,
executeTask,
clearCachedConfig,
} from '../services/configService';
interface TasksContextData {
tasks: Task[];
locations: LocationOption[];
locationsBays: LocationOption[];
payloads: PayloadOption[];
robotActions: RobotActionOption[];
getTaskById: (id: string) => Task | undefined;
updateTask: (updatedTask: Task) => void;
runTask: (id: string) => void;
refreshConfig: () => Promise<void>;
isConfigLoaded: boolean;
}
const TasksContext = createContext<TasksContextData>({} as TasksContextData);
export const TasksProvider: React.FC<{children: ReactNode}> = ({ children }) => {
const [tasks, setTasks] = useState<Task[]>(MOCK_TASKS);
export const TasksProvider: React.FC<{ children: ReactNode }> = ({
children,
}) => {
const [tasks, setTasks] = useState<Task[]>([]);
const [locations, setLocations] = useState<LocationOption[]>([]);
const [locationsBays, setLocationsBays] = useState<LocationOption[]>([]);
const [payloads, setPayloads] = useState<PayloadOption[]>([]);
const [robotActions, setRobotActions] = useState<RobotActionOption[]>([]);
const [isConfigLoaded, setIsConfigLoaded] = useState(false);
// 组件初始化时加载配置
useEffect(() => {
const loadConfig = async () => {
try {
// 清除缓存以确保加载最新的配置
await clearCachedConfig();
const config = await getConfig();
if (config) {
applyConfig(config);
setIsConfigLoaded(true);
console.log('成功加载配置,任务数量:', config.tasks?.length || 0);
} else {
console.log('没有找到配置文件,使用空数据');
// 使用空数据而不是mock数据
applyConfig({
version: '0.0.0',
locations: [],
locationsBays: [],
payloads: [],
robotActions: [],
tasks: [],
});
setIsConfigLoaded(false);
}
} catch (error) {
console.error('加载配置失败:', error);
// 发生错误时也使用空数据
applyConfig({
version: '0.0.0',
locations: [],
locationsBays: [],
payloads: [],
robotActions: [],
tasks: [],
});
setIsConfigLoaded(false);
}
};
loadConfig();
}, []);
const applyConfig = (config: AppConfig) => {
setTasks(config.tasks || []);
setLocations(config.locations || []);
setLocationsBays(config.locationsBays || []);
setPayloads(config.payloads || []);
setRobotActions(config.robotActions || []);
};
const refreshConfig = async () => {
try {
// 刷新时也清除缓存
await clearCachedConfig();
const config = await getConfig();
if (config) {
applyConfig(config);
setIsConfigLoaded(true);
console.log('成功刷新配置,任务数量:', config.tasks?.length || 0);
} else {
console.log('刷新配置时没有找到配置文件');
setIsConfigLoaded(false);
}
} catch (error) {
console.error('刷新配置失败:', error);
setIsConfigLoaded(false);
}
};
const getTaskById = (id: string) => {
return tasks.find(task => task.id === id);
@ -20,28 +121,59 @@ export const TasksProvider: React.FC<{children: ReactNode}> = ({ children }) =>
const updateTask = (updatedTask: Task) => {
setTasks(prevTasks =>
prevTasks.map(task => (task.id === updatedTask.id ? updatedTask : task))
prevTasks.map(task => (task.id === updatedTask.id ? updatedTask : task)),
);
};
const runTask = (id: string) => {
const runTask = async (id: string) => {
const task = getTaskById(id);
if (!task) return;
// 更新任务状态为运行中
setTasks(prevTasks =>
prevTasks.map(task =>
task.id === id ? { ...task, status: 'RUNNING' } : task
)
prevTasks.map(t => (t.id === id ? { ...t, status: 'RUNNING' } : t)),
);
// 模拟任务完成
setTimeout(() => {
try {
// 获取服务器设置并发送任务执行请求
const settings = await getSettings();
if (settings.serverUrl) {
await executeTask(settings.serverUrl, task.id, {
name: task.name,
parameters: task.parameters,
});
}
// 模拟任务完成实际项目中应该通过WebSocket或轮询获取任务状态
setTimeout(() => {
setTasks(prevTasks =>
prevTasks.map(task =>
task.id === id ? { ...task, status: 'COMPLETED' } : task
)
);
}, 5000);
prevTasks.map(t => (t.id === id ? { ...t, status: 'COMPLETED' } : t)),
);
}, 5000);
} catch (error) {
console.error('任务执行失败:', error);
// 任务执行失败,更新状态为错误
setTasks(prevTasks =>
prevTasks.map(t => (t.id === id ? { ...t, status: 'ERROR' } : t)),
);
}
};
return (
<TasksContext.Provider value={{ tasks, getTaskById, updateTask, runTask }}>
<TasksContext.Provider
value={{
tasks,
locations,
locationsBays,
payloads,
robotActions,
getTaskById,
updateTask,
runTask,
refreshConfig,
isConfigLoaded,
}}
>
{children}
</TasksContext.Provider>
);

View File

@ -1,63 +0,0 @@
import { Task, RobotAction } from '../types/task';
import { v4 as uuidv4 } from 'uuid';
export const LOCATIONS = Array.from({ length: 100 }, (_, i) => ({
label: `AP-${i + 1}`,
value: `AP-${i + 1}`,
}));
export const ROBOT_ACTIONS: { label: string; value: RobotAction }[] = [
{ label: '运输', value: 'TRANSPORT' },
{ label: '取货', value: 'PICKUP' },
{ label: '卸货', value: 'DROPOFF' },
{ label: '等待', value: 'WAIT' },
{ label: '充电', value: 'CHARGE' },
{ label: '清洁', value: 'CLEAN' },
];
export const LOCATIONS_BAYS = Array.from({ length: 100 }, (_, i) => ({
label: `AS2_2_${String(i + 1).padStart(3, '0')}`,
value: `AS2_2_${String(i + 1).padStart(3, '0')}`,
}));
export const PAYLOADS = [
{ label: '满料架-A1', value: '满料架-A1' },
{ label: '空料架-B2', value: '空料架-B2' },
{ label: '空料架-C3', value: '空料架-C3' },
{ label: '空车', value: '空车' },
];
const getRandomElement = <T>(arr: T[]): T =>
arr[Math.floor(Math.random() * arr.length)];
export const MOCK_TASKS: Task[] = Array.from({ length: 15 }, (_, i) => {
const startLocation = getRandomElement(LOCATIONS);
const endLocation = getRandomElement(
LOCATIONS.filter(l => l.value !== startLocation.value),
);
const robotAction = getRandomElement(ROBOT_ACTIONS);
const payload = getRandomElement(PAYLOADS);
const statusOptions: Task['status'][] = [
'IDLE',
'RUNNING',
'COMPLETED',
'ERROR',
];
const status = getRandomElement(statusOptions);
return {
id: uuidv4(),
name: `任务 ${i + 1}: 从 ${startLocation.label}${endLocation.label}`,
status: status,
createdAt: new Date(
new Date().getTime() - Math.random() * 1000 * 60 * 60 * 24,
).toISOString(),
parameters: {
startLocation: startLocation.value,
endLocation: endLocation.value,
robotAction: robotAction.value,
payload: payload.value,
locationBay: '', // 库位初始值为空
},
};
});

View File

@ -1,6 +1,7 @@
import React from 'react';
import { createStackNavigator } from '@react-navigation/stack';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
import TaskListScreen from '../screens/TaskListScreen';
import TaskEditScreen from '../screens/TaskEditScreen';
import RunScreen from '../screens/RunScreen';
@ -13,16 +14,50 @@ const Tab = createBottomTabNavigator();
function HomeStackNavigator() {
return (
<HomeStack.Navigator>
<HomeStack.Screen name="TaskList" component={TaskListScreen} options={{ title: '任务列表' }} />
<HomeStack.Screen name="TaskEdit" component={TaskEditScreen} options={{ title: '编辑任务' }} />
<HomeStack.Screen
name="TaskList"
component={TaskListScreen}
options={{ title: '任务列表' }}
/>
<HomeStack.Screen
name="TaskEdit"
component={TaskEditScreen}
options={{ title: '编辑任务' }}
/>
</HomeStack.Navigator>
);
}
export default function AppNavigator() {
return (
<Tab.Navigator>
<Tab.Screen name="主页" component={HomeStackNavigator} options={{ headerShown: false }} />
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ color, size }) => {
let iconName: string;
if (route.name === '主页') {
iconName = 'home';
} else if (route.name === '运行') {
iconName = 'play-arrow';
} else if (route.name === '编辑') {
iconName = 'edit';
} else if (route.name === '设置') {
iconName = 'settings';
} else {
iconName = 'help';
}
return <MaterialIcons name={iconName} size={size} color={color} />;
},
tabBarActiveTintColor: '#2196F3',
tabBarInactiveTintColor: 'gray',
})}
>
<Tab.Screen
name="主页"
component={HomeStackNavigator}
options={{ headerShown: false }}
/>
<Tab.Screen name="运行" component={RunScreen} />
<Tab.Screen name="编辑" component={EditScreen} />
<Tab.Screen name="设置" component={SettingsScreen} />

View File

@ -1,18 +1,467 @@
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import React, { useState, useEffect } from 'react';
import {
StyleSheet,
Text,
View,
TextInput,
TouchableOpacity,
Alert,
ActivityIndicator,
ScrollView,
} from 'react-native';
import { AppSettings } from '../types/config';
import {
getSettings,
saveSettings,
downloadConfig,
getConfig,
clearCachedConfig,
} from '../services/configService';
import { useTasks } from '../context/TasksContext';
export default function SettingsScreen() {
const { refreshConfig } = useTasks();
const [settings, setSettings] = useState<AppSettings>({
configFileName: '',
serverUrl: '',
});
const [loading, setLoading] = useState(false);
const [configStatus, setConfigStatus] = useState<string>('未加载');
// 组件加载时获取设置
useEffect(() => {
loadSettings();
checkConfigStatus();
}, []);
const loadSettings = async () => {
try {
const currentSettings = await getSettings();
setSettings(currentSettings);
} catch (error) {
Alert.alert('错误', '加载设置失败');
}
};
const checkConfigStatus = async () => {
const config = await getConfig();
if (config) {
setConfigStatus(
`已加载 (版本: ${config.version}, 任务数: ${
config.tasks?.length || 0
})`,
);
} else {
setConfigStatus('未加载');
}
};
const handleSaveSettings = async () => {
if (!settings.configFileName.trim()) {
Alert.alert('错误', '请输入配置文件名');
return;
}
if (!settings.serverUrl.trim()) {
Alert.alert('错误', '请输入服务器地址');
return;
}
try {
await saveSettings(settings);
Alert.alert('成功', '设置已保存');
} catch (error) {
Alert.alert('错误', '保存设置失败');
}
};
const handleRefreshLocalConfig = async () => {
setLoading(true);
try {
// 清除缓存并重新加载本地配置
await clearCachedConfig();
await refreshConfig();
await checkConfigStatus();
Alert.alert('成功', '本地配置已刷新!');
} catch (error) {
console.error('刷新本地配置失败:', error);
Alert.alert('错误', '刷新本地配置失败');
} finally {
setLoading(false);
}
};
const handleDownloadConfig = async () => {
if (!settings.configFileName.trim()) {
Alert.alert('错误', '请先输入配置文件名');
return;
}
if (!settings.serverUrl.trim()) {
Alert.alert('错误', '请先输入服务器地址');
return;
}
setLoading(true);
try {
// 先保存设置
await saveSettings(settings);
// 下载配置文件
const config = await downloadConfig(
settings.serverUrl,
settings.configFileName,
);
Alert.alert(
'成功',
`配置文件下载成功!\n版本: ${config.version}\n任务数量: ${
config.tasks?.length || 0
}`,
[
{
text: '确定',
onPress: async () => {
await refreshConfig();
checkConfigStatus();
},
},
],
);
} catch (error) {
Alert.alert(
'下载失败',
error instanceof Error ? error.message : '未知错误',
);
} finally {
setLoading(false);
}
};
const handleClearCache = async () => {
Alert.alert(
'确认清除',
'确定要清除缓存的配置文件吗?系统将重新加载本地 config.json 文件。',
[
{ text: '取消', style: 'cancel' },
{
text: '确定',
onPress: async () => {
setLoading(true);
try {
await clearCachedConfig();
await refreshConfig();
checkConfigStatus();
Alert.alert('成功', '缓存已清除,已重新加载配置');
} catch (error) {
Alert.alert('错误', '清除缓存失败');
} finally {
setLoading(false);
}
},
},
],
);
};
const handleTestConnection = async () => {
if (!settings.serverUrl.trim()) {
Alert.alert('错误', '请先输入服务器地址');
return;
}
setLoading(true);
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000);
const response = await fetch(`${settings.serverUrl}/health`, {
method: 'GET',
signal: controller.signal,
});
clearTimeout(timeoutId);
if (response.ok) {
Alert.alert('连接成功', '服务器连接正常');
} else {
Alert.alert('连接失败', `HTTP ${response.status}`);
}
} catch (error) {
Alert.alert('连接失败', '无法连接到服务器');
} finally {
setLoading(false);
}
};
return (
<View style={styles.screenContainer}>
<Text>!</Text>
</View>
<ScrollView style={styles.container}>
<View style={styles.content}>
{loading && (
<View style={styles.loadingOverlay}>
<ActivityIndicator size="large" color="#007AFF" />
<Text style={styles.loadingText}>...</Text>
</View>
)}
{/* 配置状态 */}
<View style={styles.section}>
<Text style={styles.sectionTitle}></Text>
<Text style={styles.statusText}>{configStatus}</Text>
</View>
{/* 配置文件设置 */}
<View style={styles.section}>
<Text style={styles.sectionTitle}></Text>
<View style={styles.inputGroup}>
<Text style={styles.label}></Text>
<TextInput
style={styles.textInput}
value={settings.configFileName}
onChangeText={text =>
setSettings(prev => ({ ...prev, configFileName: text }))
}
placeholder="config"
placeholderTextColor="#999"
autoCapitalize="none"
/>
</View>
<View style={styles.buttonRow}>
<TouchableOpacity
style={[styles.button, styles.downloadButton]}
onPress={handleDownloadConfig}
disabled={loading}
>
<Text style={styles.buttonText}></Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.button, styles.refreshButton]}
onPress={handleRefreshLocalConfig}
disabled={loading}
>
<Text style={styles.buttonText}></Text>
</TouchableOpacity>
</View>
<View style={styles.buttonRow}>
<TouchableOpacity
style={[styles.button, styles.clearButton]}
onPress={handleClearCache}
disabled={loading}
>
<Text style={styles.buttonText}></Text>
</TouchableOpacity>
</View>
</View>
{/* 服务器设置 */}
<View style={styles.section}>
<Text style={styles.sectionTitle}></Text>
<View style={styles.inputGroup}>
<Text style={styles.label}></Text>
<TextInput
style={styles.textInput}
value={settings.serverUrl}
onChangeText={text =>
setSettings(prev => ({ ...prev, serverUrl: text }))
}
placeholder="http://localhost:3000/api"
placeholderTextColor="#999"
autoCapitalize="none"
keyboardType="url"
/>
</View>
<View style={styles.buttonRow}>
<TouchableOpacity
style={[styles.button, styles.testButton]}
onPress={handleTestConnection}
disabled={loading}
>
<Text style={styles.buttonText}></Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.button, styles.saveButton]}
onPress={handleSaveSettings}
disabled={loading}
>
<Text style={styles.buttonText}></Text>
</TouchableOpacity>
</View>
</View>
{/* 说明信息 */}
<View style={styles.infoSection}>
<Text style={styles.infoTitle}>使</Text>
<Text style={styles.infoText}></Text>
<Text style={styles.infoText}>
1.
</Text>
<Text style={styles.infoText}>2. config.json </Text>
<Text style={styles.infoText}>3. </Text>
<Text style={styles.infoText}>
"下载配置文件"
</Text>
<Text style={styles.infoText}>
"清除缓存"
</Text>
</View>
</View>
</ScrollView>
);
}
const styles = StyleSheet.create({
screenContainer: {
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#f5f5f5',
},
content: {
padding: 20,
},
title: {
fontSize: 24,
fontWeight: 'bold',
textAlign: 'center',
marginBottom: 30,
color: '#333',
},
section: {
backgroundColor: 'white',
borderRadius: 10,
padding: 20,
marginBottom: 20,
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.1,
shadowRadius: 3.84,
elevation: 5,
},
sectionTitle: {
fontSize: 18,
fontWeight: 'bold',
marginBottom: 15,
color: '#333',
},
inputGroup: {
marginBottom: 15,
},
label: {
fontSize: 16,
marginBottom: 8,
color: '#333',
fontWeight: '500',
},
inputRow: {
flexDirection: 'row',
alignItems: 'center',
},
textInput: {
flex: 1,
borderWidth: 1,
borderColor: '#ddd',
borderRadius: 8,
padding: 12,
fontSize: 16,
backgroundColor: '#fafafa',
},
extension: {
marginLeft: 8,
fontSize: 16,
color: '#666',
fontWeight: '500',
},
statusRow: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 15,
},
statusLabel: {
fontSize: 16,
color: '#333',
fontWeight: '500',
},
statusText: {
fontSize: 16,
color: '#007AFF',
marginLeft: 8,
},
button: {
borderRadius: 8,
padding: 12,
alignItems: 'center',
minHeight: 48,
justifyContent: 'center',
},
downloadButton: {
backgroundColor: '#007AFF',
flex: 1,
marginRight: 10,
},
refreshButton: {
backgroundColor: '#32D74B',
flex: 1,
},
testButton: {
backgroundColor: '#34C759',
flex: 1,
marginRight: 10,
},
saveButton: {
backgroundColor: '#FF9500',
flex: 1,
},
buttonText: {
color: 'white',
fontSize: 16,
fontWeight: '600',
},
buttonRow: {
flexDirection: 'row',
marginTop: 10,
},
infoSection: {
backgroundColor: 'white',
borderRadius: 10,
padding: 20,
marginBottom: 20,
},
infoTitle: {
fontSize: 18,
fontWeight: 'bold',
marginBottom: 15,
color: '#333',
},
infoText: {
fontSize: 14,
lineHeight: 20,
color: '#666',
marginBottom: 5,
},
loadingOverlay: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: 'rgba(255, 255, 255, 0.8)',
justifyContent: 'center',
alignItems: 'center',
zIndex: 1,
},
loadingText: {
marginTop: 10,
fontSize: 16,
color: '#007AFF',
},
clearButton: {
backgroundColor: '#FF3B30',
},
});

View File

@ -1,16 +1,20 @@
import React from 'react';
import { View, FlatList, StyleSheet } from 'react-native';
import { View, ScrollView, StyleSheet } from 'react-native';
import { useNavigation } from '@react-navigation/native';
import { StackNavigationProp } from '@react-navigation/stack';
import { useTasks } from '../context/TasksContext';
import TaskCard from '../components/TaskCard';
import { Task } from '../types/task';
type RootStackParamList = {
TaskList: undefined;
TaskEdit: { task: Task };
};
type TaskListNavigationProp = StackNavigationProp<RootStackParamList, 'TaskList'>;
type TaskListNavigationProp = StackNavigationProp<
RootStackParamList,
'TaskList'
>;
export default function TaskListScreen() {
const { tasks } = useTasks();
@ -22,13 +26,17 @@ export default function TaskListScreen() {
return (
<View style={styles.container}>
<FlatList
data={tasks}
renderItem={({ item }) => <TaskCard task={item} onPress={() => handlePressTask(item)} />}
keyExtractor={item => item.id}
numColumns={2}
contentContainerStyle={styles.list}
/>
<ScrollView contentContainerStyle={styles.scrollContainer}>
<View style={styles.tasksContainer}>
{tasks.map(task => (
<TaskCard
key={task.id}
task={task}
onPress={() => handlePressTask(task)}
/>
))}
</View>
</ScrollView>
</View>
);
}
@ -37,6 +45,12 @@ const styles = StyleSheet.create({
container: {
flex: 1,
},
list: {
paddingHorizontal: 8,
}});
scrollContainer: {
padding: 16,
},
tasksContainer: {
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: 'space-between',
},
});

View File

@ -0,0 +1,175 @@
import AsyncStorage from '@react-native-async-storage/async-storage';
import { AppConfig, AppSettings } from '../types/config';
const SETTINGS_KEY = 'app_settings';
const CONFIG_CACHE_KEY = 'cached_config';
// 默认设置
const DEFAULT_SETTINGS: AppSettings = {
configFileName: 'config',
serverUrl: 'http://localhost:3000/api',
};
// 获取设置
export const getSettings = async (): Promise<AppSettings> => {
try {
const stored = await AsyncStorage.getItem(SETTINGS_KEY);
if (stored) {
return { ...DEFAULT_SETTINGS, ...JSON.parse(stored) };
}
return DEFAULT_SETTINGS;
} catch (error) {
console.error('获取设置失败:', error);
return DEFAULT_SETTINGS;
}
};
// 保存设置
export const saveSettings = async (settings: AppSettings): Promise<void> => {
try {
await AsyncStorage.setItem(SETTINGS_KEY, JSON.stringify(settings));
} catch (error) {
console.error('保存设置失败:', error);
throw error;
}
};
// 加载本地配置文件
export const loadLocalConfig = async (): Promise<AppConfig | null> => {
try {
// 尝试加载项目根目录下的 config.json 文件
const localConfig = require('../../config.json');
console.log('成功加载本地配置文件:', localConfig);
return localConfig as AppConfig;
} catch (error) {
console.log('本地配置文件不存在或加载失败:', error);
return null;
}
};
// 从服务器下载配置文件
export const downloadConfig = async (
serverUrl: string,
configFileName: string,
): Promise<AppConfig> => {
try {
const url = `${serverUrl.replace(/\/$/, '')}/${configFileName}.json`;
console.log('下载配置文件:', url);
const response = await fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const config: AppConfig = await response.json();
// 缓存服务器配置文件(标记为服务器来源)
const configWithSource = { ...config, _source: 'server' };
await AsyncStorage.setItem(
CONFIG_CACHE_KEY,
JSON.stringify(configWithSource),
);
return config;
} catch (error) {
console.error('下载配置文件失败:', error);
throw error;
}
};
// 获取缓存的配置文件
export const getCachedConfig = async (): Promise<AppConfig | null> => {
try {
const cached = await AsyncStorage.getItem(CONFIG_CACHE_KEY);
if (cached) {
const config = JSON.parse(cached);
// 移除内部标记字段
delete config._source;
return config;
}
return null;
} catch (error) {
console.error('获取缓存配置失败:', error);
return null;
}
};
// 获取配置文件(优先级:缓存的服务器配置 > 本地config.json > 空数据)
export const getConfig = async (): Promise<AppConfig | null> => {
try {
// 1. 先尝试获取缓存的配置(通常是从服务器下载的)
const cachedConfig = await getCachedConfig();
if (cachedConfig) {
console.log('使用缓存的配置文件');
return cachedConfig;
}
// 2. 如果没有缓存,尝试加载本地配置文件
const localConfig = await loadLocalConfig();
if (localConfig) {
console.log('使用本地 config.json 文件');
// 将本地配置也缓存起来,但标记为本地来源
const configWithSource = { ...localConfig, _source: 'local' };
await AsyncStorage.setItem(
CONFIG_CACHE_KEY,
JSON.stringify(configWithSource),
);
return localConfig;
}
console.log('没有找到任何配置文件');
return null;
} catch (error) {
console.error('获取配置文件失败:', error);
return null;
}
};
// 清除缓存配置(用于强制重新加载)
export const clearCachedConfig = async (): Promise<void> => {
try {
await AsyncStorage.removeItem(CONFIG_CACHE_KEY);
console.log('已清除缓存配置');
} catch (error) {
console.error('清除缓存配置失败:', error);
}
};
// 向服务器发送任务执行请求
export const executeTask = async (
serverUrl: string,
taskId: string,
taskData: any,
): Promise<any> => {
try {
const url = `${serverUrl.replace(/\/$/, '')}/execute-task`;
console.log('执行任务请求:', url, taskData);
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
taskId,
...taskData,
}),
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return await response.json();
} catch (error) {
console.error('执行任务失败:', error);
throw error;
}
};

36
src/types/config.ts Normal file
View File

@ -0,0 +1,36 @@
import { Task, RobotAction } from './task';
// 配置文件中的位置选项
export interface LocationOption {
label: string;
value: string;
}
// 配置文件中的载荷选项
export interface PayloadOption {
label: string;
value: string;
}
// 配置文件中的机器人动作选项
export interface RobotActionOption {
label: string;
value: RobotAction;
}
// 完整的配置文件结构
export interface AppConfig {
version: string;
locations: LocationOption[];
locationsBays: LocationOption[];
payloads: PayloadOption[];
robotActions: RobotActionOption[];
tasks: Task[];
serverUrl?: string; // 服务器地址
}
// 设置存储接口
export interface AppSettings {
configFileName: string;
serverUrl: string;
}

View File

@ -10,14 +10,33 @@ export type RobotAction =
// 任务的状态
export type TaskStatus = 'IDLE' | 'RUNNING' | 'COMPLETED' | 'ERROR';
// 任务参数
// 参数选项接口
export interface ParameterOption {
label: string;
value: string;
}
// 动态参数配置接口
export interface DynamicParameter {
label: string;
type: 'Simple' | 'Select' | 'MultiSelect' | 'Text' | 'Number';
value: string | string[] | number;
required: boolean;
options?: ParameterOption[];
placeholder?: string;
min?: number;
max?: number;
}
// 任务参数 - 支持动态配置
export interface TaskParameters {
startLocation: string; // 起点
endLocation: string; // 终点
waypoint?: string; // 途经点 (可选)
robotAction: RobotAction; // 机器人动作
payload: string; // 载荷,比如 '空料架' 或具体的物料ID
locationBay?: string; // 库位 (可选)
startLocation: DynamicParameter; // 起点
endLocation: DynamicParameter; // 终点
waypoint?: DynamicParameter; // 途经点 (可选)
robotAction: DynamicParameter; // 机器人动作
payload: DynamicParameter; // 载荷,比如 '空料架' 或具体的物料ID
locationBay?: DynamicParameter; // 库位 (可选)
[key: string]: DynamicParameter | undefined; // 支持扩展参数
}
// 核心任务对象