You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
5.3 KiB
5.3 KiB
任务驱动的任务调度方案
一、核心思路
不是一次性下发全部步骤,也不是等待接驳位信号
任务创建后 (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<int, SemaphoreSlim> |
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 |
即时 | — |
提升机并发控制
// 从 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,启动RunLoopAsyncStop()→ 取消 token,等待当前线程结束后标记停止