# 任务驱动的任务调度方案 ## 一、核心思路 ``` 不是一次性下发全部步骤,也不是等待接驳位信号 任务创建后 (status=1) → 执行器轮询到 → 启动线程 → 单任务内逐条串行下发明细 → 前一条完成 → 自动执行下一条 → 全部完成 → 任务结束 多任务之间并行执行,互不干扰 ``` ## 二、执行流程 ``` Start() 启动后台轮询 │ └─ RunLoopAsync (每 5 秒一轮) │ └─ ExecuteAsync │ ├─ 查询 taskType=1, taskCategory=1, taskStatus=1 │ ├─ foreach task → new Thread: │ │ │ ├─ TaskSemaphore.Wait() ← 最多 10 线程 │ │ │ └─ ProcessOneAsync(task): │ │ │ ├─ taskStatus → 2 │ │ │ ├─ foreach detail (按 objId): │ │ │ │ │ ├─ detailStatus → 2 │ │ ├─ 下发设备 │ │ │ 1 → AGV (不限并发) │ │ │ 2 → 提升机 (每栋限 2) │ │ │ 0 → 输送线 │ │ ├─ 等待执行完成 │ │ └─ detailStatus → 3 │ │ │ └─ taskStatus → 3 │ └─ Thread.Join() 等本轮全部完成 → 5 秒后下一轮 ``` ## 三、并发模型 ``` 任务 A: Detail 1 → Detail 2 → Detail 3 → ... 任务 B: Detail 1 → Detail 2 → Detail 3 → ... 三个任务同时跑 任务 C: Detail 1 → Detail 2 → Detail 3 → ... 单任务内: 串行 (前一条完成才执行下一条) 多任务间: 并行 (各自独立线程) ``` ### 限制 | 控制项 | 机制 | 值 | |--------|------|-----| | 最大并行任务数 | `SemaphoreSlim` | 10 | | 提升机并行/栋 | `ConcurrentDictionary` | 2 | | AGV 并行 | 无限制 | — | | DB 写入 | `lock (DbWriteLock)` | 串行 | ## 四、单任务处理详情 ``` ProcessOneAsync(task): Step 1 ─ 标记任务执行中 lock (DbWriteLock): task.taskStatus = 2 Update(task) Step 2 ─ 逐条处理明细 (按 objId 排序) for each detail: 跳过已完成 (status 2 or 3) lock (DbWriteLock): detail.taskStatus = 2 Update(detail) ↓ 下发设备 (锁外,可并行) deviceType: 1 → DispatchAgvAsync AGV 模拟 10s 2 → DispatchHoistAsync 提升机模拟 20s 0 → DispatchConveyorAsync 输送线 成功: lock (DbWriteLock): detail.taskStatus = 3 Update(detail) 失败: lock (DbWriteLock): detail.taskStatus = 1 回退待重试 Update(detail) return (中断任务) Step 3 ─ 标记任务完成 lock (DbWriteLock): task.taskStatus = 3 Update(task) ``` ## 五、设备下发 | 设备类型 | deviceType | 方法 | 模拟延迟 | 并发控制 | |---------|-----------|------|---------|---------| | AGV | 1 | `DispatchAgvAsync` | 10s | 无限制 | | 提升机 | 2 | `DispatchHoistAsync` | 20s | 每栋 2 台 | | 输送线 | 0 | `DispatchConveyorAsync` | 即时 | — | ### 提升机并发控制 ```csharp // 从 startPoint 提取楼栋号: "14#_L2_HOIST" → 14 var building = ExtractBuilding(detail.startPoint); // 获取该楼栋信号量,首次自动创建 SemaphoreSlim(2, 2) var semaphore = HoistSemaphores.GetOrAdd(building, _ => new SemaphoreSlim(2, 2)); await semaphore.WaitAsync(); // 等待有空闲提升机 // ... 执行 ... semaphore.Release(); // 释放 ``` ## 六、执行示例 3 个包材入库任务,每个 6 条明细: ``` T+0s [线程1] 任务A Detail1 AGV开始 [线程2] 任务B Detail1 AGV开始 [线程3] 任务C Detail1 AGV开始 T+10s [线程1] 任务A Detail1 完成 [线程2] 任务B Detail1 完成 [线程3] 任务C Detail1 完成 T+10s [线程1] 任务A Detail2 提升机开始 [线程2] 任务B Detail2 提升机开始 [线程3] 等待(13#提升机满) T+30s [线程1] 任务A Detail2 完成 [线程2] 任务B Detail2 完成 [线程3] 任务C Detail2 提升机开始 T+30s [线程1] 任务A Detail3 AGV开始 [线程2] 任务B Detail3 AGV开始 ...并行继续... ``` ## 七、状态流转 ``` LiveTaskQueue.taskStatus: 1 (待执行) → 2 (执行中) → 3 (已完成) LiveTaskDetail.taskStatus: 1 (待执行) → 2 (执行中) → 3 (已完成) ↘ 失败回退 → 1 (待重试) ``` ## 八、UI 控制 系统监控页面顶部卡片,点击启动/停止: ``` ┌──────────────────────────────────────────────────┐ │ ● 包材入库调度 运行中/已停止 [▶ 启动] [■ 停止] │ └──────────────────────────────────────────────────┘ ``` - `Start()` → 创建 `CancellationTokenSource`,启动 `RunLoopAsync` - `Stop()` → 取消 token,等待当前线程结束后标记停止