feat: 添加 @react-native-async-storage/async-storage 依赖,重构任务上下文以支持动态参数配置,更新任务表单和任务列表组件以使用新功能,移除 mock 数据文件
This commit is contained in:
parent
0e0cef6eba
commit
087fc9e0aa
709
config.json
Normal file
709
config.json
Normal 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
34
package-lock.json
generated
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
);
|
||||
|
@ -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: '', // 库位初始值为空
|
||||
},
|
||||
};
|
||||
});
|
@ -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} />
|
||||
|
@ -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',
|
||||
},
|
||||
});
|
||||
|
@ -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',
|
||||
},
|
||||
});
|
||||
|
175
src/services/configService.ts
Normal file
175
src/services/configService.ts
Normal 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
36
src/types/config.ts
Normal 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;
|
||||
}
|
@ -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; // 支持扩展参数
|
||||
}
|
||||
|
||||
// 核心任务对象
|
||||
|
Loading…
x
Reference in New Issue
Block a user