Files
bj_power/bj_power_mes/完整工单流转.md
T

40 KiB
Raw Blame History

完整工单流转手册(信号级)

本文档按 信号 → 执行单元 → 状态变化 的粒度,完整描述一个工单从建单到 全部工件下线的全过程:每一步"发生了什么、执行哪个最小单元、结果状态是什么 (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 → 匹配 doneWaitsOnTaskDone 广播任务完成。没有 300ms 轮询

M 地址从哪来?配置粒度分两层,如实说明

  1. 地址层(M2000/M2009…)已配置化:地址存在 DB signal 表(种子数据 doc/signals.sqlid=1 行即 DockFetchReq → M2000)。程序启动时 preload 全量加载进内存,执行时按信号名查地址,查不到直接报错"信号地址未配置, 请检查DB signal表"。改地址只改 DB,不用改代码
  2. 名称层(DockFetchReq…)仍是代码硬编码:所有信号名集中定义在 constantsSig* 常量(一处定义,执行器全部引用常量,不再散落 魔法字符串)。改信号名仍需同步改 constants + DB——"只改 DB"只对 地址层成立。
  3. 动作→信号组映射(如 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,从上到下命中即止):

  1. done 槽 + 持待检件 → U12 换料(最高优先,成品 carry 下料)
  2. done 槽 + 二次换料等待 job(proxy 标记)→ 补发二次换料
  3. 代做链 OP61_DECIDE:全检已满 2 件 → 批次决策;done 槽 + 缓存有件 → U15 代取;缓存无件 → 链结束回批次决策;无 done → 等待(幂等重评), 但本工单最后一件除外:回批次决策触发 M2102 收尾2026-08-17 修复: 最后一件无后续换料/取料事件可等,原逻辑待检件永久等待)
  4. done 槽 + 无换料链活跃 → U4 自取(链活跃时抑制自取,防止 done 槽 被抢走导致二次换料链断裂;链 job 离开换料步骤后恢复自取)
  5. 空位 → 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_LOADStepDockFetch)且已绑定接驳台槽位 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→夹爪,断料收尾专用)

  • 生成(磨床模块三条同时成立):
    1. 磨床槽 DONE——M3600.0 上升沿(磨削完成)时模块把槽 OCCUPIED→DONE 成品仍挂在原 job 名下(current_job_id),job 停在 OP20_WAIT
    2. 没有待磨件(无 job 停在 OP15_DECIDE 持毛坯等磨床);
    3. 没有在途磨床任务(防重复评估重复生成)。 含义:磨完这一件后没有下一件毛坯可换,成品自己取走去清洗(断料收尾)。
  • 预约:目的=夹爪 → 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→DONETurnTablePlaceDone 到达即放料+换向完成), 模块随即生成取料任务
  • 任务类型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→夹爪,成品出全检)

  • 生成:全检槽 DONEM2098 批次检测完成时 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 FinishJobfinished_num+1,全部完成则工单 COMPLETED

U10 缓存放料(夹爪→CACHE,全检满暂存)

  • 生成:全检 6 槽全满且缓存有空槽;job 持成品在 OP35_DECIDE
  • 预约:目的=缓存槽 → EMPTY→PROCESSING(owner=job)
  • 序列CameraWorkType(M2022) → CachePlacePos(M2121)=槽号 → 1s → CachePlaceReq(M2120)=1 → 1s → 等 CachePlaceDone(M2129)
  • 结果:缓存槽→OCCUPIEDjob.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_WAITPrevJobID→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;缓存=EMPTYtemp_slot=freerobot=idle

阶段 1:首件上线(接驳台→磨床)

步骤 发生 结果状态
1.1 启动工单,接驳台模块评估:job1 绑槽1 → 生成 DOCK-FETCH 任务(U1 任务入队(源槽位不预约,派车前才查夹爪锁)
1.2 派发 U1M2022 → M2001=1 → M2002=1 → 1s → M2000 → 等 M2009 robot=held
1.3 M2009 到达:取料完成 接驳台槽1→EMPTYtemp_slot→job1(毛坯入夹爪);job1→OP10_LOAD→OP15_DECIDErobot=idle
1.4 磨床模块评估:磨床 EMPTY + job1 持毛坯 → 生成 PLACE 任务(U6) 派车前预约磨床槽 EMPTY→PROCESSING(owner=job1)
1.5 派发 U6M2022 → M2040 → 等 M2049 robot=held
1.6 M2049 到达:放料完成 磨床槽→OCCUPIED(owner=job1)temp_slot 释放;job1→OP20_WAITrobot=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 派发 U11M2022 → M2050 → 等 M2059 robot=held
2.4 M2059 到达:换料完成 磨床槽→OCCUPIED(owner=job2)job2→OP20_WAIT(开始磨);temp_slot 交接 job2→job1(成品入夹爪);job1→OP30_LOADrobot=idle

结果:磨床磨 job2;夹爪持 job1 成品;磨床不停机(换料即取即放)。

阶段 3:清洗(持料模型)与放全检

步骤 发生 结果状态
3.1 清洗模块评估:job1 持成品 → 生成 WASH 任务(U13) 预约吹洗位
3.2 派发 U13M2022 → M2130 → 等 M2139 robot=heldjob1 持料贯穿(temp_slot 不释放)
3.3 M2139 到达:清洗完成 吹洗位→EMPTYtemp_slot 仍归 job1(持料模型)job1→OP35_DECIDErobot=idle
3.4 全检模块评估:全检有空槽 + job1 持成品 → 生成 PLACE 任务(U7) 派车前预约全检空槽→PROCESSING(owner=job1)
3.5 派发 U7M2022 → M2101=槽号 → 1s → M2100 → 等 M2109 → M2102 请求检验(脉冲) robot=held
3.6 M2109 到达:放料完成 全检槽→OCCUPIED(owner=job1)temp_slot 释放job1→OP40_BATCH_DECIDErobot=idle
3.7 批次决策:槽1 已 OCCUPIED 但未满 2 件且非末件 → 不发 M2102job1→OP40_WAIT job1 在全检台待检;夹爪空

结果:job1 待检;夹爪空(持料在放全检后释放);磨床磨 job2。

阶段 4:批量流转(第 2-10 件循环)

步骤 发生 结果状态
4.1 job2 M3600 → 换料给 job3(重复阶段 2);job2 清洗 → 放全检槽2(重复阶段 3) 全检槽1=job1、槽2=job2 均 OCCUPIEDjob3 磨床加工
4.2 全检槽1+槽2 均待检 → 发 M2102 请求检验(满 2 件批次规则) job1/job2→OP40_REQ;人工检验开始
4.3 PLC M2098 置位(批次检测完成广播)→ 上升沿 全检槽1/2 OCCUPIED→DONEjob1/job2→OP40_UNLOAD(待取料)
4.4 评估:job3 持成品在 OP35_DECIDE(清洗完)→ 生成全检换料任务(U12job3 换 job1 任务入队;派车前预约槽1DONE)→PROCESSING(owner=job3)PrevJobID=job1
4.5 派发 U12M2081=槽1 → 1s → M2082=槽1 → 1s → M2080 → 等 M2089 robot=held
4.6 M2089 到达:全检换料完成 槽1→OCCUPIED(owner=job3)(待检件落全检);成品 job1 入夹爪temp_slot→job1);job1→OP60_EXCHANGE_LOADjob3→OP40_BATCH_DECIDE
4.7 全检评估:槽2 DONE 无换料者 → 生成全检取料任务(U4job2 自取) 任务入队;派车前查夹爪锁(源槽不预约)
4.8 派发 U4M2022 → M2091=槽2 → 1s → M2090 → 等 M2099 robot=held
4.9 M2099 到达:取料完成 槽2→EMPTYtemp_slot→job2job2→OP60_LOAD(持成品待下料)
4.10 评估:job2 持成品 → 下料任务(U9,自行下料回自己取料槽位) 派车前预约接驳台槽2EMPTY→PROCESSINGowner=job2
4.11 派发 U9M2022 → 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/M2098finished_num 递增

阶段 5:缓存暂存与回流(全检 6 槽满时)

步骤 发生 结果状态
5.1 全检 6 槽全 OCCUPIED(待检)且 job7 清洗完成持料 job7=OP35_DECIDE(持成品)等全检空位
5.2 全检模块评估:无空槽 → 生成缓存暂存任务(U10 派车前预约缓存槽(EMPTY→PROCESSING
5.3 派发 U10M2121 → 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 派发 U14U5M2111→M2110→等 M2119)→ U7M2101→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→DONEjob1/job2→OP40_UNLOAD
6.7 job4 持成品 → 全检换料链U12job4 换 job1)→ job1 成品入夹爪→代下料(U9)→ job1 COMPLETED 槽1→OCCUPIED(owner=job4);夹爪释放后按优先级恢复:上料/断料取料/缓存回流依次解锁

背压断言:暂停期间不崩溃、不空转、无死循环;恢复后槽位归属守恒。

阶段 7:最后一件收尾(last_piece

步骤 发生 结果状态
7.1 job11 M3600(磨削完成)→ 评估:无后继待磨件 → 不换料,生成断料取料任务(U2 任务入队;派车前查夹爪锁;无 EXCHANGE 任务
7.2 取料完成:磨床槽→EMPTYtemp_slot→job11job11→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→COMPLETEDfinished_num=11
7.5 FinishJob 检查:工单下全部 job 终态 → 工单→COMPLETED 工单完成;全部 11 件在接驳台槽位;temp_slot=freerobot=idle;磨床/全检/缓存全 EMPTY

六、done 信号先后交替场景(done_cross)

同一时刻可能有多条 done 信号在不同设备上先后/交叉到达,验证系统不会错配、错序、漏消费:

子场景 注入方式 系统行为 预期结果
a. 磨床 done 迟到 M3600.0 置位延迟 10smock 注入延迟) 期间其他 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 消费各自的 doneM2098 消费不早于本批 M2102 发送;无错配

时序图(子场景 a+c 示意)

job5 磨削中 ──────── M3600(迟到10s) ──► 磨床 DONE → 换料
job6 放全检 ── M2109 ──► M2102 请求检验 ──► M2098 批次广播 ──► 全检槽 DONE
                 (两信号在 ~10s 窗口内交叉到达,互不干扰)

关键机制

  1. 上升沿检测:信号从 0→1 才算一次事件(持续为 1 不重复触发)
  2. doneWaits 登记先行:派发前先登记 done 等待,防完成信号先于登记到达(约束 6.4.1)
  3. 陈旧守卫M2098 只在有 job 处于 OP40_REQ(已发 M2102)时消费;M3600 只在有 OCCUPIED 槽时消费
  4. 单在途任务:机器人同一时刻只有一个在途任务,done 信号天然串行匹配

七、关键交互场景状态流转

7.1 磨床换料链(continuous_exchange4 件 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 COMPLETEDB 完成链 → 批次决策 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_return6 件跨 2 接驳台)

  • 自行下料:成品回到自己取料槽位dock_equipment_id 绑定)
  • 代下料(换料收尾件):回到 PrevJobID 绑定槽位(成品从哪来回哪去)
  • 断言:接驳台 OCCUPIED 槽数守恒(每件成品恰好一个落位)

7.4 缓存回流(cache_reflow

  • 全检满 → 缓存暂存(U10temp_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;本文档为信号级流转依据。