40 KiB
完整工单流转手册(信号级)
本文档按 信号 → 执行单元 → 状态变化 的粒度,完整描述一个工单从建单到 全部工件下线的全过程:每一步"发生了什么、执行哪个最小单元、结果状态是什么 (job 在哪/什么步骤、槽位谁占用、锁谁持有)"。
对应实现:
internal/line/executor.go(任务→PLC 动作映射)、internal/plcactions/executor.go(信号序列)、internal/device/*.go(各设备模块状态推进)、internal/robot/(任务队列/优先级仲裁/预约制)、internal/line/priority.go(优先级配置加载)、doc/signals.sql(信号表)。
一、产线拓扑与资源
| 设备 | 类型码 | 槽位 | 说明 |
|---|---|---|---|
| 接驳台 ×2 | DOCK | 各 42 槽 | 毛坯入口/成品出口;工件绑定取料槽位 |
| 磨床 | GRINDER | 1 槽 | 配油盘,M3600 加工完成 |
| 换向机构 | TURN_TABLE | 1 槽 | 方形件换向(PLC 自动旋转) |
| 气吹清洗 | AIR_BLOW | 1 槽 | 持料模型:机器人夹持吹洗,吹洗位不持工件 |
| 全检台 | FULL_CHECK | 6 槽 | 人工检验,批次检测(M2098 广播) |
| 缓存台 | CACHE | 1 槽 | 全检满时暂存待检件 |
| 机器人 | TEMP_SLOT(夹爪) | — | 全厂唯一共享资源,持料锁 |
资源锁(resource_lock):
robot:任务派发时持有、完成时释放(单在途任务互斥)temp_slot(夹爪):取料完成后归取料 job;放料完成释放;换料交接转移给前任
二、PLC 信号体系(M 区地址)
约定:PC→PLC 为软件触发信号(置 1 后 PLC 自复位);PLC→PC 为完成/状态通知 (信号轮询器每 1 秒读一遍全部状态信号、上升沿触发,speedFactor 加速时最快 50ms)。 每组操作前必须先写 M2022 工件类型。
地址均为当前 DB signal 表种子配置(
doc/signals.sql),程序启动时加载, 代码只写信号名、不写地址(详见 4.0)。
接驳台(M2000-M2019)
| 地址 | 名称 | 方向 | 含义 |
|---|---|---|---|
| M2000 | DockFetchReq | PC→PLC | 取料请求(byte,触发) |
| M2001 | DockFetchPos | PC→PLC | 取位置号(1-42) |
| M2002 | DockFetchTray | PC→PLC | 取料台号(1/2) |
| M2009 | DockFetchDone | PLC→PC | 取料完成 |
| M2010 | DockPlaceReq | PC→PLC | 放料请求(byte,触发) |
| M2011 | DockPlacePos | PC→PLC | 放位置号(1-42) |
| M2012 | DockPlaceTray | PC→PLC | 放料台号(1/2) |
| M2019 | DockPlaceDone | PLC→PC | 放料完成 |
相机(M2020-M2029)
| 地址 | 名称 | 方向 | 含义 |
|---|---|---|---|
| M2022 | CameraWorkType | PC→PLC | 工件类型(每组操作前必写) |
| M2027 | CameraShootResult | PLC→PC | 拍照结果(1=有料,0=无料) |
| M2029 | CameraShootDone | PLC→PC | 拍照完成 |
磨床(M2030-M2059)
| 地址 | 名称 | 方向 | 含义 |
|---|---|---|---|
| M2030 | GrinderFetchReq | PC→PLC | 取料请求 |
| M2039 | GrinderFetchDone | PLC→PC | 取料完成 |
| M2040 | GrinderPlaceReq | PC→PLC | 放料请求 |
| M2049 | GrinderPlaceDone | PLC→PC | 放料完成 |
| M2050 | GrinderExchangeReq | PC→PLC | 换料请求 |
| M2059 | GrinderExchangeDone | PLC→PC | 换料完成 |
换向机构(M2060-M2079)
| 地址 | 名称 | 方向 | 含义 |
|---|---|---|---|
| M2060 | TurnTableFetchReq | PC→PLC | 取料请求 |
| M2069 | TurnTableFetchDone | PLC→PC | 取料完成 |
| M2070 | TurnTablePlaceReq | PC→PLC | 放料请求 |
| M2079 | TurnTablePlaceDone | PLC→PC | 放料完成 |
| M3601.0/1 | TurnTableClamped/Released | PLC→PC | 夹紧/松开到位 |
全检换料(M2080-M2089)
| 地址 | 名称 | 方向 | 含义 |
|---|---|---|---|
| M2081 | FullCheckExchangeFetchPos | PC→PLC | 换料取位置号 |
| M2082 | FullCheckExchangePlacePos | PC→PLC | 换料放位置号 |
| M2080 | FullCheckExchangeReq | PC→PLC | 换料请求 |
| M2089 | FullCheckExchangeDone | PLC→PC | 换料完成 |
全检台(M2090-M2109)
| 地址 | 名称 | 方向 | 含义 |
|---|---|---|---|
| M2091 | FullCheckFetchPos | PC→PLC | 取位置号 |
| M2090 | FullCheckFetchReq | PC→PLC | 取料请求 |
| M2099 | FullCheckFetchDone | PLC→PC | 取料完成 |
| M2101 | FullCheckPlacePos | PC→PLC | 放位置号 |
| M2100 | FullCheckPlaceReq | PC→PLC | 放料请求 |
| M2109 | FullCheckPlaceDone | PLC→PC | 放料完成 |
| M2102 | FullCheckPlaceDoneRequestInspect | PC→PLC | 放料完成请求检验(纯脉冲不等) |
| M2098 | FullCheckInspectDone | PLC→PC | 检测完成(批次广播,全槽转 DONE) |
| M2108 | FullCheckDetecting | PLC→PC | 人工检验中 |
缓存台(M2110-M2129)
| 地址 | 名称 | 方向 | 含义 |
|---|---|---|---|
| M2111 | CacheFetchPos | PC→PLC | 取料位置号 |
| M2110 | CacheFetchReq | PC→PLC | 取料请求 |
| M2119 | CacheFetchDone | PLC→PC | 取料完成 |
| M2121 | CachePlacePos | PC→PLC | 放料位置号 |
| M2120 | CachePlaceReq | PC→PLC | 放料请求 |
| M2129 | CachePlaceDone | PLC→PC | 放料完成 |
清洗(M2130-M2139)
| 地址 | 名称 | 方向 | 含义 |
|---|---|---|---|
| M2130 | WashStartReq | PC→PLC | 清洗请求(写 1 触发) |
| M2139 | AirBlowCompleteDone | PLC→PC | 清洗完成(byte 型) |
设备状态位(M3600.x)
| 地址 | 名称 | 方向 | 含义 |
|---|---|---|---|
| M3600.0 | GrinderMachiningDone | PLC→PC | 磨床加工完成 |
| M3600.1 | GrinderAllowEntry | PLC→PC | 磨床允许进入 |
| M3604.0/1 | Dock1Present/Dock2Present | PLC→PC | 接驳台在位 |
三、状态机总览
job 状态(status)
CREATED(建单) → IN_HANDLING/PROCESSING/WAITING_UNLOAD/WAITING_DECISION(流转中)
→ COMPLETED(收尾);SCRAPPED(报废)/SUSPENDED(暂停) 为终态/挂起。
job 步骤(current_step_id,圆形件主线)
OP10_LOAD(接驳台取料) → OP15_DECIDE(持毛坯等磨床) → OP20_LOAD/EXCHANGE(上磨床)
→ OP20_WAIT(等M3600) → OP30_LOAD(成品入夹爪待清洗) → OP35_DECIDE(清洗完成持料)
→ OP40_LOAD/EXCHANGE(放全检/换料) → OP40_BATCH_DECIDE(批次决策) → OP40_REQ(已发M2102)
→ OP40_WAIT(待检) → OP40_UNLOAD(检测完成待取) → OP60_LOAD(下料接驳台) → COMPLETED
方形件在 OP10 后先走 OP25_TURN_LOAD → OP25_TURN_UNLOAD(换向)再进磨床。
槽位状态(equipment_slot.status)
EMPTY →(预约)→ PROCESSING →(落料完成)→ OCCUPIED →(M3600/M2098)→ DONE
→(取走)→ EMPTY。换料任务预约 DONE 槽位(取成品放毛坯一步完成)。
夹爪锁(temp_slot)
free →(取料完成 acquire)→ held_by_job →(放料完成 release / 换料交接 handover)→ ...
四、最小执行单元全集(信号级)
4.0 通用骨架:一次 PLC 握手到底发生了什么
机器人单爪串行作业:一个最小执行单元 = 一个搬运任务 = "从 A 点取件(装载)→ 机器人物理搬运(代码不参与)→ 放到 B 点(卸载)", 或换料(交换式):到 A 点把设备里的加工完成品取走、同时把夹爪里 持着的工件放进 A 点——一个 Req 同时完成取+放(U11 磨床 / U12 全检 / 二次换料)。注意夹爪里的工件不一定是毛坯:磨床换料时是毛坯,全检 换料与二次换料时是成品(待检成品/缓存暂存成品),链式连续换料见 4.2。 代码只负责两端与 PLC 的信号握手,中间的物理搬运由机器人自行完成, 软件以 done 信号到达作为任务完成标志。
一次完整握手的六步(代码 signalActionWithParams,约束见 agent约束 6.4.1),
以 U1 接驳台取料为例逐步拆解:
| 步 | 代码动作 | U1 实例 | 为什么这么做 |
|---|---|---|---|
| 1 | 写工件类型 | 写 CameraWorkType = 产品配方 ID |
PLC 按工件类型切换抓手/程序参数,每组动作前必须告知 |
| 2 | 连续写参数 | 写 DockFetchPos=槽号、DockFetchTray=台号 |
两个值是给同一个取料动作用的数据,参数之间不停顿、连续写 |
| 3 | 空 1 秒 | sleep(1s) |
给 PLC 一个扫描周期消化参数(S7 通信异步),否则 Req 到了参数还没生效 |
| 4 | 写 Req=1 | 写 DockFetchReq=1 |
触发 PLC 执行取料。只写 1 不写 0:动作完成后 PLC 自行复位 |
| 5 | 空 1 秒 | sleep(1s) |
防止紧接着的下一次写操作覆盖本次写入 |
| 6 | 等 done | 等 DockFetchDone 上升沿 |
见下方"done 怎么等" |
done 怎么等(不是轮询):派车时先把 done 信号名登记进 doneWaits 表
(在发 Req 之前登记——因为第 5 步的 1 秒间隔里 PLC 往往已干完并回了
done,后登记会错过上升沿导致任务永久等待)。此后完全事件驱动:
PLC done 信号上升沿 → 信号轮询器 → onSignal → 匹配 doneWaits →
OnTaskDone 广播任务完成。没有 300ms 轮询。
M 地址从哪来?配置粒度分两层,如实说明:
- 地址层(M2000/M2009…)已配置化:地址存在 DB signal 表(种子数据
doc/signals.sql,id=1 行即 DockFetchReq → M2000)。程序启动时 preload 全量加载进内存,执行时按信号名查地址,查不到直接报错"信号地址未配置, 请检查DB signal表"。改地址只改 DB,不用改代码。 - 名称层(
DockFetchReq…)仍是代码硬编码:所有信号名集中定义在constants包Sig*常量(一处定义,执行器全部引用常量,不再散落 魔法字符串)。改信号名仍需同步改 constants + DB——"只改 DB"只对 地址层成立。 - 动作→信号组映射(如 DockFetch 动作 = Req+Done+Pos+Tray 一组)也
硬编码在
plcactions方法内;如需现场按动作配置信号组,需给 signal 表 增加动作维度(架构级改造,未做)。
下文每个单元的序列按 信号名(当前配置地址) 书写:括号内地址是当前种子
配置值,现场可调(改 DB);信号名对应 constants 常量(改代码)。
4.1 前置条件:生成 vs 预约(两层,缺一不可)
每个单元的"前置"分两层,全部满足任务才会真正派车执行:
第一层 · 生成条件:设备模块周期巡检(Evaluate)时,按槽位状态 +
job 步骤判断"现在该干什么",生成任务入队。此时槽位状态没有任何变化。
第二层 · 预约通过:机器人仲裁器从队头取任务,派车前调用 line.reserve:
- 目的=夹爪(一切取料类任务):查
temp_slot锁。锁被其他 job 持有 → 预约失败,任务留队等下一轮(单爪防覆盖);free 或本人持 → 通过。 - 目的=设备槽位(放料/换料/清洗类):开事务
SELECT ... FOR UPDATE锁住 槽位行(防并发抢占)→ 校验槽位状态符合预期(换料预约 DONE,其余 预约 EMPTY)→ 通过则写 EMPTY/DONE → PROCESSING +current_job_id=本 job → 提交。状态不符 → 预约失败留队。 槽位归属就是这一步写下的,任务 done 时再由模块推进到 OCCUPIED。 - job 已终态(COMPLETED/SCRAPPED/CANCELLED)→ 任务直接作废丢弃。
重点:取料类任务不会把源槽位预约为 PROCESSING——源槽位只是任务
生成的依据(OCCUPIED/DONE),任务 done 时由模块直接写回 EMPTY。
下文每个单元按 生成:…;预约:… 两行书写前置。
4.2 换料与连续换料链(U11 / U12 / U15 的关系)
一次换料 = 取成品 + 放夹爪工件同步完成:一个 Req(磨床 M2050 / 全检 M2080)让 PLC 同时完成"取出设备里的加工完成品 + 把夹爪工件放进 空出的位置",夹爪工件随即变成设备里取出的成品(temp_slot 交接给成品 job)。三种换料的夹爪持件不同:
| 换料 | 夹爪持 | 设备槽 | 完成效果 |
|---|---|---|---|
| U11 磨床换料 | 毛坯(本 job) | 磨床 DONE 成品 | 毛坯开磨;成品入夹爪(交接给前任) |
| U12 全检换料 | 待检成品(本 job,清洗完) | 全检 DONE 成品 | 待检件落全检;done 成品入夹爪(交接给前任) |
| 二次换料(U12 同序列) | 缓存暂存成品(代做链 job) | 全检 DONE 成品 | 缓存件落全检;done 成品入夹爪(交接给 ProxyJobID 成品 job) |
连续换料链(continuous_exchange):换料任务把 done 槽的成品带进夹爪 后,链 job 不是直接放全检,而是代下料 → 回链决策点 → 再换——每轮把 一件成品从全检 done 槽(或缓存)"换"出并下料,磨床不停机、全检不空闲:
链首 job(B):U12 换料(B 待检件→全检槽,A 成品入夹爪)
→ U9 代下料 A → 链决策点 OP61_DECIDE
→ 缓存有件:U15 代取缓存成品 C → 二次换料(C 落全检槽,D 成品入夹爪)
→ U9 代下料 D → 回 OP61_DECIDE,循环
→ 缓存无件 或 全检已满 2 件:链结束 → 批次决策(M2102)
全检模块每轮评估的决策优先级(evaluateTx,从上到下命中即止):
- done 槽 + 持待检件 → U12 换料(最高优先,成品 carry 下料)
- done 槽 + 二次换料等待 job(proxy 标记)→ 补发二次换料
- 代做链 OP61_DECIDE:全检已满 2 件 → 批次决策;done 槽 + 缓存有件 → U15 代取;缓存无件 → 链结束回批次决策;无 done → 等待(幂等重评), 但本工单最后一件除外:回批次决策触发 M2102 收尾(2026-08-17 修复: 最后一件无后续换料/取料事件可等,原逻辑待检件永久等待)
- done 槽 + 无换料链活跃 → U4 自取(链活跃时抑制自取,防止 done 槽 被抢走导致二次换料链断裂;链 job 离开换料步骤后恢复自取)
- 空位 → U7 放料;全满 → U10 缓存暂存
4.3 任务队列与优先级仲裁(谁先派车)
队列模型:全部设备模块(接驳台/磨床/转台/全检/缓存)生成的任务进入同一个
机器人任务队列(robot.Queue),仲裁器(robot.Arbiter)从队头取任务 →
预约目的地成功 → 派车。机器人单爪,同一时刻至多一个在途任务;预约失败的
任务留队下一轮、让位队尾(EnqueueBack),保证每轮扫描覆盖全队不饿死新任务。
优先级规则(robot_task_priority 表,数据驱动;数值越小越优先,改表重启即生效):
组合优先级(2026-08-17 定稿):优先级语义为「任务类型 + 搬运源设备 + 目标设备」
三要素组合——一条搬运路径(如换向台→磨床链)是否优先由组合决定,任何设备组合
可单独配置,无需发明新任务类型。配置行 from_type/to_type 为 NULL 表示通配;
查找顺序(最具体优先):精确 (kind,from,to) → (kind,from) → (kind,to) → kind-only → 内置默认。
| 组合键(kind, from, to) | 默认优先级 | 典型任务 | 业务含义 |
|---|---|---|---|
| EXCHANGE | 0 | U11 磨床换料 / U12 全检换料 | 磨床不停机、全检链不断,最高 |
| (UNLOAD, TURN_TABLE) | 5 | U3 换向取料 | 换向机构与磨床同链:换向件优先取走供磨床,磨床空时优先供料(组合优先) |
| LOAD | 10 | U6 磨床放料 / U7 全检放料 / U8 转台放料 / U9 接驳台放料 | 上料类 |
| UNLOAD | 10 | U1 接驳台取料 / U2 磨床取料 / U4 全检取料 / U5 缓存取料 | 卸料类 |
| WASH | 10 | U13 清洗 | 持料模型 |
| RELOAD | 20 | U14 缓存回流 | 回流 |
| TRANSFER | 30 | U15 代取缓存件 | 普通转运 |
同优先级 FIFO:相同优先级的任务按入队时间先进先出(先入队先派车)。
计数老化防饿死(确定性,非墙钟时间):任务每预约失败让位一次(YieldCount +1),
有效优先级提升一级(下限 0 与最高优先级平齐)——让位次数是事件序而非时间:
同样的状态序列必然产生同样的让位次数,调度结果可复现(正向状态机语义),
同时低优先级任务最终必然被调度,不会无限排队。
持料隐式优先级:夹爪被某 job 持有时,其他取料类任务(目的=夹爪)预约失败 → 让位队尾;持料 job 的下一步任务(放料/换料/清洗)反而畅通——持料模型天然 把机器人让给"已持有工件"的 job 优先走完,这是优先级之外的隐式排序。
换向取料组合优先级(业务硬规则,2026-08-17 修复):
- 换向台有换向件(转台槽 DONE)时,必须先取走换向件供磨床
(组合行
('UNLOAD','TURN_TABLE',NULL)=5),不能先派接驳台取毛坯 (UNLOAD 通配=10)——换向机构与磨床同属磨床链,磨床空转等待会拖慢整线节拍。 - 历史缺陷:换向取料原与接驳台取料同为 UNLOAD(10),FIFO 下换向件排队滞留 转台、磨床空等(实测:TURN-FETCH 入队排第 6 位,DOCK-FETCH 先执行)。
- 修复演进:先新增 TURN_FETCH 任务类型(优先级 5)应急 → 被否(组合语义压扁成 单模块语义,无法覆盖"换向+其他设备"组合)→ 定稿为组合优先级:UNLOAD 类型 不变,换向取料优先由组合行表达,任意设备组合可配,不再发明新类型。
U1 接驳台取料(DOCK→夹爪,毛坯上线)
- 生成:job 停在首道工序 OP10_LOAD(StepDockFetch)且已绑定接驳台槽位 (AGV/人工放毛坯后槽位 OCCUPIED);未绑槽则静默跳过等送料
- 预约:目的=夹爪 →
temp_slot必须 free(或本 job 已持); 源槽位不预约(仍是 OCCUPIED,不是 PROCESSING) - 序列:
CameraWorkType(M2022)=配方ID →DockFetchPos(M2001)=槽号 →DockFetchTray(M2002)=台号 → 1s →DockFetchReq(M2000)=1 → 1s → 等DockFetchDone(M2009) - 结果:接驳台槽→EMPTY;夹爪→持本 job 毛坯(temp_slot 上锁);job→ OP15_DECIDE(圆形件,持毛坯等磨床)/ OP25_TURN_LOAD(方形件,先去换向)
U2 磨床取料(GRINDER→夹爪,断料收尾专用)
- 生成(磨床模块三条同时成立):
- 磨床槽 DONE——M3600.0 上升沿(磨削完成)时模块把槽 OCCUPIED→DONE, 成品仍挂在原 job 名下(current_job_id),job 停在 OP20_WAIT;
- 没有待磨件(无 job 停在 OP15_DECIDE 持毛坯等磨床);
- 没有在途磨床任务(防重复评估重复生成)。 含义:磨完这一件后没有下一件毛坯可换,成品自己取走去清洗(断料收尾)。
- 预约:目的=夹爪 →
temp_slot必须 free - 序列:
CameraWorkType(M2022) →GrinderFetchReq(M2030)=1 → 1s → 等GrinderFetchDone(M2039) - 结果:磨床槽→EMPTY;夹爪→持本 job 成品;job→OP30_LOAD(持成品待清洗)
- 对比:有毛坯待磨时磨床 DONE 不走本单元,走 U11 换料(毛坯进、 成品出、磨床不停机)
U3 转台取料(TURN_TABLE→夹爪,方形件换向后取回)
- 生成:转台槽 DONE 且 job 在 OP25_TURN_UNLOAD——放料任务 done 时 槽位直接 OCCUPIED→DONE(TurnTablePlaceDone 到达即放料+换向完成), 模块随即生成取料任务
- 任务类型:UNLOAD(组合优先级
(UNLOAD, TURN_TABLE)=5,配置表驱动)—— 换向机构与磨床同链,换向件优先取走供磨床,高于普通卸料(UNLOAD 通配=10), 规则详见 4.3 - 预约:目的=夹爪 →
temp_slot必须 free - 序列:
CameraWorkType(M2022) →TurnTableFetchReq(M2060)=1 → 1s → 等TurnTableFetchDone(M2069) - 结果:转台槽→EMPTY;夹爪→持本 job 毛坯;job→OP15_DECIDE(继续去磨床)
U4 全检取料(FULL_CHECK→夹爪,成品出全检)
- 生成:全检槽 DONE(M2098 批次检测完成时 OCCUPIED→DONE)且 job 在 OP40_UNLOAD;且无换料链活跃(有链 job 时成品由换料带走,不单独取料)
- 预约:目的=夹爪 →
temp_slot必须 free - 序列:
CameraWorkType(M2022) →FullCheckFetchPos(M2091)=槽号 → 1s →FullCheckFetchReq(M2090)=1 → 1s → 等FullCheckFetchDone(M2099) - 结果:全检槽→EMPTY;夹爪→持本 job 成品;job→OP60_LOAD(待下料)
U5 缓存取料(CACHE→夹爪,回流第一步)
- 生成:缓存槽 OCCUPIED(全检满时暂存的待检件)且 job 在 OP50_UNLOAD (待回流)
- 预约:目的=夹爪 →
temp_slot必须 free - 序列:
CameraWorkType(M2022) →CacheFetchPos(M2111)=槽号 → 1s →CacheFetchReq(M2110)=1 → 1s → 等CacheFetchDone(M2119) - 结果:缓存槽→EMPTY;夹爪→持本 job 待检件;job→OP40_LOAD(待放全检)
U6 磨床放料(夹爪→GRINDER,毛坯上磨床)
- 生成:磨床槽 EMPTY 且 job 持毛坯在 OP15_DECIDE
- 预约:目的=磨床槽 → FOR UPDATE 锁行 → EMPTY→PROCESSING(owner=job)
- 序列:
CameraWorkType(M2022) →GrinderPlaceReq(M2040)=1 → 1s → 等GrinderPlaceDone(M2049) - 结果:磨床槽→OCCUPIED(owner=job,磨削开始);夹爪释放;job→OP20_WAIT (等 M3600)
U7 全检放料(夹爪→FULL_CHECK,成品送检)
- 生成:全检有空槽 EMPTY 且 job 持成品在 OP35_DECIDE(清洗完持料等容量)
- 预约:目的=全检空槽 → EMPTY→PROCESSING(owner=job)
- 序列:
CameraWorkType(M2022) →FullCheckPlacePos(M2101)=槽号 → 1s →FullCheckPlaceReq(M2100)=1 → 1s → 等FullCheckPlaceDone(M2109) - 结果:全检槽→OCCUPIED(owner=job);夹爪释放;job→OP40_BATCH_DECIDE:
批次已满 2 件或工单最后一件 → 立即发
FullCheckPlaceDoneRequestInspect(M2102)=1 脉冲请求检验 → job→OP40_REQ;否则→OP40_WAIT 等下一件凑批
U8 转台放料(夹爪→TURN_TABLE,方形件换向)
- 生成:转台槽 EMPTY 且 job 持毛坯在 OP25_TURN_LOAD
- 预约:目的=转台槽 → EMPTY→PROCESSING(owner=job)
- 序列:
CameraWorkType(M2022) →TurnTablePlaceReq(M2070)=1 → 1s → 等TurnTablePlaceDone(M2079) - 结果:转台槽→DONE(放料协议含 PLC 自动旋转,done 即换向完成); 夹爪释放;job→OP25_TURN_UNLOAD(随后立即生成 U3 取回)
U9 接驳台放料(夹爪→DOCK,成品下线)
- 生成:接驳台有 EMPTY 槽且 job 持成品在 OP60_LOAD
- 预约:目的=接驳台槽 → EMPTY→PROCESSING;代下料场景槽位归属 PrevJobID(成品是谁的就归谁),自行下料归本人
- 序列:
CameraWorkType(M2022) →DockPlacePos(M2011)=槽号 →DockPlaceTray(M2012)=台号 → 1s →DockPlaceReq(M2010)=1 → 1s → 等DockPlaceDone(M2019) - 结果:接驳台槽→OCCUPIED;夹爪释放;job→COMPLETED (FinishJob:finished_num+1,全部完成则工单 COMPLETED)
U10 缓存放料(夹爪→CACHE,全检满暂存)
- 生成:全检 6 槽全满且缓存有空槽;job 持成品在 OP35_DECIDE
- 预约:目的=缓存槽 → EMPTY→PROCESSING(owner=job)
- 序列:
CameraWorkType(M2022) →CachePlacePos(M2121)=槽号 → 1s →CachePlaceReq(M2120)=1 → 1s → 等CachePlaceDone(M2129) - 结果:缓存槽→OCCUPIED;
job.temp_slot_no记录暂存槽;夹爪释放; job→OP50_UNLOAD(待回流,全检出空后走 U5+U7 回流)
U11 磨床换料(夹爪毛坯 ↔ GRINDER 成品,磨床不停机)
- 生成:磨床槽 DONE 且有待磨件(job 持毛坯在 OP15_DECIDE) ——与 U2 互斥:有待磨件走换料,无待磨件走取料
- 预约:目的=磨床槽 → FOR UPDATE 锁行 → DONE→PROCESSING(owner=本 job);派车时校正 PrevJobID=槽位原主人(成品 job)
- 序列:
CameraWorkType(M2022) →GrinderExchangeReq(M2050)=1 → 1s → 等GrinderExchangeDone(M2059) - 结果:磨床槽→OCCUPIED(owner=本 job,新毛坯开磨);夹爪交接:本 job 毛坯已放进磨床,夹爪转为持 PrevJobID 成品(temp_slot 锁转移);本 job→ OP20_WAIT;PrevJobID→OP30_LOAD(持成品待清洗)
U12 全检换料(夹爪待检件 ↔ FULL_CHECK 成品)
- 生成:全检有 done 槽,且存在换料链 job——持成品在 OP35_DECIDE 的 job 被判定为链首,done 槽归属链前任(成品 job)
- 预约:目的=全检 done 槽 → DONE→PROCESSING(owner=本 job)
- 序列:
CameraWorkType(M2022) →FullCheckExchangeFetchPos(M2081)=槽号 → 1s →FullCheckExchangePlacePos(M2082)=槽号 → 1s →FullCheckExchangeReq(M2080)=1 → 1s → 等FullCheckExchangeDone(M2089) (注:三条写信号之间必须各空 1 秒,与配方 wait_after_ms 一致, 缺间隔生产 PLC 会信号未生效即写下一条) - 结果:done 槽→OCCUPIED(owner=本 job);成品入夹爪(PrevJobID 持有)→ OP60_EXCHANGE_LOAD(链 job 代下料);本 job→OP40_BATCH_DECIDE(待检件 已落全检,走批次决策)
U13 清洗(夹爪持成品 → AIR_BLOW,持料模型)
- 生成:吹洗位可用且 job 持成品在 OP30_LOAD
- 预约:目的=吹洗位 → EMPTY→PROCESSING(清洗位物理不持工件,洗完 仍夹在夹爪上)
- 序列:
CameraWorkType(M2022) →WashStartReq(M2130)=1 → 1s → 等AirBlowCompleteDone(M2139)(信号名是 AirBlowCompleteDone, 旧名 WashCompleteDone 已不在信号表中,byte 型) - 结果:吹洗位→EMPTY;夹爪不释放(持料模型);job→OP35_DECIDE (持成品等全检容量决策)
U14 缓存回流(CACHE → FULL_CHECK,两步串行)
- 生成:缓存槽 OCCUPIED(owner=job) 且全检有空槽;job 在 OP50_UNLOAD
- 预约:目的=全检空槽 → EMPTY→PROCESSING(owner=job)(取缓存这半程 的夹爪锁由任务本身保证)
- 序列:U5 序列(
CacheFetchPos(M2111)→CacheFetchReq(M2110)→等CacheFetchDone(M2119))→ 立刻 U7 序列(FullCheckPlacePos(M2101)→FullCheckPlaceReq(M2100)→等FullCheckPlaceDone(M2109))→FullCheckPlaceDoneRequestInspect(M2102) 脉冲(完成信号=放料完成) - 结果:缓存槽→EMPTY;全检槽→OCCUPIED(owner=job);夹爪释放;job→ OP40_BATCH_DECIDE
U15 代取缓存件(CACHE→夹爪,换料链代做)
- 生成:全检二次换料链需要缓存里的成品(缓存槽 OCCUPIED(owner= ProxyJobID)),由链 job 持夹爪代取
- 预约:目的=夹爪 →
temp_slot已由链 job 持有(本人持→通过) - 序列:
CameraWorkType(M2022) →CacheFetchPos(M2111)=槽号 → 1s →CacheFetchReq(M2110)=1 → 1s → 等CacheFetchDone(M2119) - 结果:缓存槽→EMPTY;夹爪→链 job 持缓存成品;job→OP40_EXCHANGE_2ND (持缓存件去做二次换料)
五、完整工单流转(示例:11 件圆形件,1 磨床 / 6 槽全检 / 1 槽缓存 / 1 机器人)
阶段 0:建单与放料
| 步骤 | 发生 | 结果状态 |
|---|---|---|
| 0.1 | 新建工单,11 件圆形件(product_type=1) | 11 个 job=CREATED;工单=CREATED |
| 0.2 | 接驳台放毛坯:AGV/人工把 11 件放接驳台各槽 | 接驳台槽1-11=OCCUPIED(各绑定 job1-11);磨床=EMPTY;全检 6 槽=EMPTY;缓存=EMPTY;temp_slot=free;robot=idle |
阶段 1:首件上线(接驳台→磨床)
| 步骤 | 发生 | 结果状态 |
|---|---|---|
| 1.1 | 启动工单,接驳台模块评估:job1 绑槽1 → 生成 DOCK-FETCH 任务(U1) | 任务入队(源槽位不预约,派车前才查夹爪锁) |
| 1.2 | 派发 U1:M2022 → M2001=1 → M2002=1 → 1s → M2000 → 等 M2009 | robot=held |
| 1.3 | M2009 到达:取料完成 | 接驳台槽1→EMPTY;temp_slot→job1(毛坯入夹爪);job1→OP10_LOAD→OP15_DECIDE;robot=idle |
| 1.4 | 磨床模块评估:磨床 EMPTY + job1 持毛坯 → 生成 PLACE 任务(U6) | 派车前预约磨床槽 EMPTY→PROCESSING(owner=job1) |
| 1.5 | 派发 U6:M2022 → M2040 → 等 M2049 | robot=held |
| 1.6 | M2049 到达:放料完成 | 磨床槽→OCCUPIED(owner=job1);temp_slot 释放;job1→OP20_WAIT;robot=idle |
| 1.7 | 磨床开始磨削 job1 | job1=PROCESSING;磨床槽=OCCUPIED(job1) |
结果:job1 在磨床加工中;夹爪空;接驳台槽1 空(余 10 件)。
阶段 2:首件完成 M3600 与换料(磨床优先)
| 步骤 | 发生 | 结果状态 |
|---|---|---|
| 2.1 | PLC M3600.0 置位(磨削完成)→ 上升沿 → 磨床模块处理 | 磨床槽 OCCUPIED→DONE(owner=job1);job1 仍 OP20_WAIT(成品在磨床等出路) |
| 2.2 | 评估出路:job2 持毛坯在夹爪(OP15_DECIDE)→ 生成 EXCHANGE 任务(job2 换 job1) | 派车前预约磨床 DONE 槽→PROCESSING(owner=job2),PrevJobID 取槽位原主人 job1 |
| 2.3 | 派发 U11:M2022 → M2050 → 等 M2059 | robot=held |
| 2.4 | M2059 到达:换料完成 | 磨床槽→OCCUPIED(owner=job2);job2→OP20_WAIT(开始磨);temp_slot 交接 job2→job1(成品入夹爪);job1→OP30_LOAD;robot=idle |
结果:磨床磨 job2;夹爪持 job1 成品;磨床不停机(换料即取即放)。
阶段 3:清洗(持料模型)与放全检
| 步骤 | 发生 | 结果状态 |
|---|---|---|
| 3.1 | 清洗模块评估:job1 持成品 → 生成 WASH 任务(U13) | 预约吹洗位 |
| 3.2 | 派发 U13:M2022 → M2130 → 等 M2139 | robot=held;job1 持料贯穿(temp_slot 不释放) |
| 3.3 | M2139 到达:清洗完成 | 吹洗位→EMPTY;temp_slot 仍归 job1(持料模型);job1→OP35_DECIDE;robot=idle |
| 3.4 | 全检模块评估:全检有空槽 + job1 持成品 → 生成 PLACE 任务(U7) | 派车前预约全检空槽→PROCESSING(owner=job1) |
| 3.5 | 派发 U7:M2022 → M2101=槽号 → 1s → M2100 → 等 M2109 → M2102 请求检验(脉冲) | robot=held |
| 3.6 | M2109 到达:放料完成 | 全检槽→OCCUPIED(owner=job1);temp_slot 释放;job1→OP40_BATCH_DECIDE;robot=idle |
| 3.7 | 批次决策:槽1 已 OCCUPIED 但未满 2 件且非末件 → 不发 M2102,job1→OP40_WAIT | job1 在全检台待检;夹爪空 |
结果:job1 待检;夹爪空(持料在放全检后释放);磨床磨 job2。
阶段 4:批量流转(第 2-10 件循环)
| 步骤 | 发生 | 结果状态 |
|---|---|---|
| 4.1 | job2 M3600 → 换料给 job3(重复阶段 2);job2 清洗 → 放全检槽2(重复阶段 3) | 全检槽1=job1、槽2=job2 均 OCCUPIED;job3 磨床加工 |
| 4.2 | 全检槽1+槽2 均待检 → 发 M2102 请求检验(满 2 件批次规则) | job1/job2→OP40_REQ;人工检验开始 |
| 4.3 | PLC M2098 置位(批次检测完成广播)→ 上升沿 | 全检槽1/2 OCCUPIED→DONE;job1/job2→OP40_UNLOAD(待取料) |
| 4.4 | 评估:job3 持成品在 OP35_DECIDE(清洗完)→ 生成全检换料任务(U12:job3 换 job1) | 任务入队;派车前预约槽1(DONE)→PROCESSING(owner=job3),PrevJobID=job1 |
| 4.5 | 派发 U12:M2081=槽1 → 1s → M2082=槽1 → 1s → M2080 → 等 M2089 | robot=held |
| 4.6 | M2089 到达:全检换料完成 | 槽1→OCCUPIED(owner=job3)(待检件落全检);成品 job1 入夹爪(temp_slot→job1);job1→OP60_EXCHANGE_LOAD;job3→OP40_BATCH_DECIDE |
| 4.7 | 全检评估:槽2 DONE 无换料者 → 生成全检取料任务(U4:job2 自取) | 任务入队;派车前查夹爪锁(源槽不预约) |
| 4.8 | 派发 U4:M2022 → M2091=槽2 → 1s → M2090 → 等 M2099 | robot=held |
| 4.9 | M2099 到达:取料完成 | 槽2→EMPTY;temp_slot→job2;job2→OP60_LOAD(持成品待下料) |
| 4.10 | 评估:job2 持成品 → 下料任务(U9,自行下料回自己取料槽位) | 派车前预约接驳台槽2(EMPTY→PROCESSING,owner=job2) |
| 4.11 | 派发 U9:M2022 → M2011=槽2 → M2012=台号 → 1s → M2010 → 等 M2019 | robot=held |
| 4.12 | M2019 到达:下料完成 | 接驳台槽2→OCCUPIED(owner=job2);temp_slot 释放;job2→COMPLETED(工单 finished_num=1) |
| 4.13 | job1 换料下料(job1 持成品,U9)→ COMPLETED | 接驳台槽1→OCCUPIED(owner=job1);finished_num=2 |
| 4.14 | 循环:job4-10 重复阶段 2-4(磨床换料 → 清洗 → 全检换料/放料 → 批次 → 下料) | 每 2 件一批 M2102/M2098;finished_num 递增 |
阶段 5:缓存暂存与回流(全检 6 槽满时)
| 步骤 | 发生 | 结果状态 |
|---|---|---|
| 5.1 | 全检 6 槽全 OCCUPIED(待检)且 job7 清洗完成持料 | job7=OP35_DECIDE(持成品)等全检空位 |
| 5.2 | 全检模块评估:无空槽 → 生成缓存暂存任务(U10) | 派车前预约缓存槽(EMPTY→PROCESSING) |
| 5.3 | 派发 U10:M2121 → 1s → M2120 → 等 M2129 | job7 持成品去缓存 |
| 5.4 | M2129 到达:暂存完成 | 缓存槽→OCCUPIED(owner=job7);job7.temp_slot_no=缓存槽;temp_slot 释放;job7→OP50_UNLOAD(待回流) |
| 5.5 | 全检批次 M2098 → 槽1-6 DONE → 逐步取走/换料 → 出现空槽 | 全检有空槽 |
| 5.6 | 缓存回流评估:缓存槽 OCCUPIED + 全检空槽 → 生成回流任务(U14) | 派车前预约全检空槽(EMPTY→PROCESSING) |
| 5.7 | 派发 U14:U5(M2111→M2110→等 M2119)→ U7(M2101→M2100→等 M2109 + M2102) | robot=held |
| 5.8 | M2129 之后 M2109 到达:回流完成 | 缓存槽→EMPTY;全检槽→OCCUPIED(owner=job7);temp_slot 释放;job7→OP40_BATCH_DECIDE(继续正常批次) |
阶段 6:全检+缓存全满的背压(no_space 场景:全检 2 槽+缓存 1 槽,15 件)
| 步骤 | 发生 | 结果状态 |
|---|---|---|
| 6.1 | job1/job2 磨完→清洗→放全检槽1/槽2 | 全检满(2 槽 OCCUPIED)→ 满 2 件即发 M2102 → 检验中 |
| 6.2 | job3 磨完→清洗→全检满→缓存暂存(U10) | 缓存槽→OCCUPIED(owner=job3);job3→OP50_UNLOAD |
| 6.3 | job4 磨完→清洗→全检满+缓存满→无出路 | job4=OP35_DECIDE 持成品;temp_slot=job4(夹爪被占) |
| 6.4 | job5 毛坯取料任务(U1)预约夹爪失败(被 job4 持)→ 留队老化 | 接驳台不再上料;没有新 job 进入 OP15_DECIDE |
| 6.5 | 磨床磨完 job5 → M3600 → DONE,但无待磨件(无 OP15_DECIDE)→ 断料收尾生成 U2 取料任务 | U2 预约夹爪仍失败(被 job4 持)→ 留队;磨床停在 DONE 等出路 |
| 6.6 | 全线背压:磨床 DONE、夹爪被 job4 持、全检/缓存全满;唯一出路是 PLC M2098(批次检测完成) | 全检槽1/2→DONE;job1/job2→OP40_UNLOAD |
| 6.7 | job4 持成品 → 全检换料链(U12:job4 换 job1)→ job1 成品入夹爪→代下料(U9)→ job1 COMPLETED | 槽1→OCCUPIED(owner=job4);夹爪释放后按优先级恢复:上料/断料取料/缓存回流依次解锁 |
背压断言:暂停期间不崩溃、不空转、无死循环;恢复后槽位归属守恒。
阶段 7:最后一件收尾(last_piece)
| 步骤 | 发生 | 结果状态 |
|---|---|---|
| 7.1 | job11 M3600(磨削完成)→ 评估:无后继待磨件 → 不换料,生成断料取料任务(U2) | 任务入队;派车前查夹爪锁;无 EXCHANGE 任务 |
| 7.2 | 取料完成:磨床槽→EMPTY;temp_slot→job11;job11→OP30_LOAD | 磨床空(收尾完成) |
| 7.3 | 清洗 → 放全检(U13→U7)→ 批次决策:工单最后一件(IsLastPieceOfOrder)→ 必发 M2102 | job11→OP40_REQ;即使不满 2 件也收尾批次 |
| 7.4 | M2098 → job11→OP40_UNLOAD → 取料(U4)→ 下料(U9) | 接驳台槽11→OCCUPIED(owner=job11);job11→COMPLETED;finished_num=11 |
| 7.5 | FinishJob 检查:工单下全部 job 终态 → 工单→COMPLETED | 工单完成;全部 11 件在接驳台槽位;temp_slot=free;robot=idle;磨床/全检/缓存全 EMPTY |
六、done 信号先后交替场景(done_cross)
同一时刻可能有多条 done 信号在不同设备上先后/交叉到达,验证系统不会错配、错序、漏消费:
| 子场景 | 注入方式 | 系统行为 | 预期结果 |
|---|---|---|---|
| a. 磨床 done 迟到 | M3600.0 置位延迟 10s(mock 注入延迟) | 期间其他 job 正常流转(清洗/放全检不受影响);磨床 done 迟到达后仍被正确消费 | job 消费 M3600 恰好 1 次,不因迟到丢失、不重复推进 |
| b. 全检 done 提前(陈旧) | 启动 ~5s 注入一次 M2098=1(此时无 job 处于 OP40_REQ) | 全检模块陈旧信号守卫:jobsAtStep(OP40_REQ) 为空 → 忽略(不广播、不推进) |
陈旧 M2098 被过滤,不误唤醒任何 job |
| c. 两信号交叉到达 | M3600(迟到 10s)与真实 M2098 到达时间重叠 | doneWaits 按信号名匹配(单机器人同一时刻至多一个在途任务);M2098 仅在发过 M2102 后消费(批次广播与触发请求严格配对) | 各 job 消费各自的 done;M2098 消费不早于本批 M2102 发送;无错配 |
时序图(子场景 a+c 示意):
job5 磨削中 ──────── M3600(迟到10s) ──► 磨床 DONE → 换料
job6 放全检 ── M2109 ──► M2102 请求检验 ──► M2098 批次广播 ──► 全检槽 DONE
(两信号在 ~10s 窗口内交叉到达,互不干扰)
关键机制:
- 上升沿检测:信号从 0→1 才算一次事件(持续为 1 不重复触发)
- doneWaits 登记先行:派发前先登记 done 等待,防完成信号先于登记到达(约束 6.4.1)
- 陈旧守卫:M2098 只在有 job 处于 OP40_REQ(已发 M2102)时消费;M3600 只在有 OCCUPIED 槽时消费
- 单在途任务:机器人同一时刻只有一个在途任务,done 信号天然串行匹配
七、关键交互场景状态流转
7.1 磨床换料链(continuous_exchange,4 件 A/B/C/D 预置)
A(持毛坯,OP15) ──换B──► 磨床 OCCUPIED(A) [B 成品入夹爪]
B(持成品) ──清洗──► 全检换料 ──► B 成品下料(OP60_EXCHANGE_LOAD)
B ──OP61_DECIDE 决策:缓存有 C → 代取 C(OP50_EXCHANGE_TAKE)
B(持 C) ──二次换料(OP40_EXCHANGE_2ND, proxy C)──► 全检 done 槽换主人 → D 成品入夹爪
D 下料(OP60_LOAD_2ND) ──► D COMPLETED;B 完成链 → 批次决策 OP40_BATCH_DECIDE
- ProxyJobID:代做链中缓存件 C 的槽位归属(预约/落位都按 ProxyJobID)
- 断言:M2102 批次请求恰好 1 次;换料链 done 槽位不被自取任务抢走 (hasActiveExchangeChain 保护)
7.2 持料模型(airblow_hold)
- 清洗期间 robot 被本 job 持有(temp_slot 不释放)→ 其他 job 的取料任务预约失败留队
- 唯一释放点:放全检(U7 完成,M2109 到达后 temp_slot release)
- 断言:temp_slot 释放审计事件仅落料点 1 次、清洗完成 0 次;其他 job 清洗晚于本 job 放全检
7.3 槽位归还(dock_slot_return,6 件跨 2 接驳台)
- 自行下料:成品回到自己取料槽位(dock_equipment_id 绑定)
- 代下料(换料收尾件):回到 PrevJobID 绑定槽位(成品从哪来回哪去)
- 断言:接驳台 OCCUPIED 槽数守恒(每件成品恰好一个落位)
7.4 缓存回流(cache_reflow)
- 全检满 → 缓存暂存(U10,temp_slot_no 记录)
- 全检空 → 回流(U14:取缓存+放全检连续动作,一次任务完成)
- 断言:OP50_LOAD 暂存 → OP50_UNLOAD 回流步骤序列正确;temp_slot_no 清除
八、执行方式与验收标准
# 全量 14 场景回归(每场景独立 DB 环境)
go run ./cmd/mockrun/ -scenario <round|single_job|square|exchange|cache_reflow|
no_material|no_space|last_piece|mom|cancel_restart|continuous_exchange|
airblow_hold|dock_slot_return|done_cross> [-jobs N] [-timeout T] [-verify]
验收标准:每场景 验证结果: N 通过, 0 失败、exit=0、无 level=ERROR 日志。
场景与验证项见 14个场景.md;本文档为信号级流转依据。