SMT上料防错的数据结构怎么设计?站位表、飞达、料盘三层绑定建表实践

SMT上料防错要靠数据结构兜底,而不是靠操作员和班长用眼睛对料号:把“程序里的站位表”“飞达(Feeder)实物”“料盘(Reel)批次”拆成独立的主表,再用两张带生效区间的绑定表把它们串起来,扫码时由数据库约束和一条校验SQL拦截错料。本文把这套建模叫作“三层绑定、装即绑、卸即断”:上料核对、接料换盘、质量追溯共用同一份数据,不会出现上料记录、换料台账和追溯报表三份数据互相对不上的情况。下面给出表结构、建表SQL、校验SQL和上线验收项,示例基于 MySQL 8,PostgreSQL 的差异在文中单独说明。

上料防错的数据结构指什么?

上料防错的数据结构,是指用“站位表版本—飞达—料盘”三层实体,加上“站位-飞达”“飞达-料盘”两张时间区间绑定表,来回答“某一时刻、某台贴片机、某个站位上装的是哪个飞达,飞达上挂的是哪一盘料”的数据模型。

三层实体要拆开,是因为它们的变化节奏完全不同:站位表随产品和程序版本变化,一个版本可能用几个月;飞达是设备资产,会在几台贴片机之间来回流转,还有校准周期;料盘是消耗品,一盘几千颗,用完就换,接料时新旧两盘还会短时间并存。把三者塞进一张“机台|站位|料号|料盘码”的宽表,每次换飞达或接料都只能覆盖更新,历史就丢了,事后追溯某块板用了哪一盘料时无从查起。

需要建哪几张表?

核心是五张表加一张日志表,表名和字段可以按自己的命名规范改,但拆分方式建议保持。

表名 关键字段 用途
smt_slot_plan plan_id、program_name、product_code、machine_id、side、version、status 站位表头,一个程序版本一行,status 区分草稿/生效/停用
smt_slot_plan_item plan_id、slot_no、sub_slot、part_no、alt_group、feeder_type 站位明细,每个站位应上什么料、用什么规格的飞达
feeder feeder_sn、feeder_type、width_mm、status、calib_due_date 飞达主档,状态和校准到期日用于上料前拦截
reel reel_id、part_no、lot_no、date_code、vendor_code、qty_init、qty_remain、msl_level、open_time 料盘档案,来自来料贴标或供应商条码解析
slot_feeder_bind machine_id、slot_no、sub_slot、feeder_sn、plan_id、mount_time、unmount_time 站位-飞达绑定,装上写 mount_time,卸下写 unmount_time
feeder_reel_bind feeder_sn、reel_id、bind_type、bind_time、unbind_time 飞达-料盘绑定,bind_type 区分上料 LOAD 和接料 SPLICE
load_check_log scan_time、operator_id、machine_id、slot_no、feeder_sn、reel_id、result、reason 每次扫码校验的结果,含失败原因,用于审计

替代料单独用 alt_group 表达,组成员放在一张小表 part_alt(alt_group、part_no)里:同一 alt_group 下的料号在该站位可以互换,校验时按组匹配,不要把替代料写成站位明细的多行,否则会把“一个站位只能放一种料”的约束打破。

建表SQL怎么写?

关键点只有一个:同一站位在同一时刻只能有一条“未卸下”的绑定。MySQL 不支持带条件的部分索引,可以用生成列把“未卸下”转换成 1、已卸下转换成 NULL,再建 UNIQUE 约束,因为 UNIQUE 索引允许多个 NULL。

CREATE TABLE smt_slot_plan_item (
  plan_id      BIGINT       NOT NULL,
  slot_no      VARCHAR(16)  NOT NULL,
  sub_slot     VARCHAR(4)   NOT NULL DEFAULT '0',
  part_no      VARCHAR(40)  NOT NULL,
  alt_group    VARCHAR(40)  NULL,
  feeder_type  VARCHAR(20)  NOT NULL,
  PRIMARY KEY (plan_id, slot_no, sub_slot)
);

CREATE TABLE slot_feeder_bind (
  id           BIGINT AUTO_INCREMENT PRIMARY KEY,
  machine_id   VARCHAR(20)  NOT NULL,
  slot_no      VARCHAR(16)  NOT NULL,
  sub_slot     VARCHAR(4)   NOT NULL DEFAULT '0',
  feeder_sn    VARCHAR(40)  NOT NULL,
  plan_id      BIGINT       NOT NULL,
  mount_time   DATETIME(3)  NOT NULL,
  unmount_time DATETIME(3)  NULL,
  is_open      TINYINT GENERATED ALWAYS AS (IF(unmount_time IS NULL, 1, NULL)) STORED,
  UNIQUE KEY uk_slot_open (machine_id, slot_no, sub_slot, is_open),
  UNIQUE KEY uk_feeder_open (feeder_sn, is_open),
  KEY idx_feeder_time (feeder_sn, mount_time)
);

CREATE TABLE feeder_reel_bind (
  id           BIGINT AUTO_INCREMENT PRIMARY KEY,
  feeder_sn    VARCHAR(40)  NOT NULL,
  reel_id      VARCHAR(60)  NOT NULL,
  bind_type    ENUM('LOAD','SPLICE') NOT NULL,
  bind_time    DATETIME(3)  NOT NULL,
  unbind_time  DATETIME(3)  NULL,
  KEY idx_feeder_open (feeder_sn, unbind_time),
  KEY idx_reel (reel_id, bind_time)
);

uk_feeder_open 保证一个飞达不会同时出现在两个站位。feeder_reel_bind 没有加同样的约束,是因为接料(SPLICE)期间新旧两盘会并存一段时间,旧盘在用完后才写 unbind_time。PostgreSQL 可以直接写 CREATE UNIQUE INDEX ... WHERE unmount_time IS NULL,不需要生成列。所有时间字段由数据库服务器写入,不信任扫码枪或平板的本地时间。

绑定为什么用生效区间,而不是覆盖更新?

覆盖更新只能回答“现在装的是什么”,生效区间才能回答“当时装的是什么”,而追溯要问的恰恰是当时。三种常见写法的取舍如下:

写法 能否追溯历史 并发安全 反查复杂度 适用情况
宽表覆盖更新 不能,历史被覆盖 依赖程序加锁 低 只做上料提示、不做追溯
绑定表+生效区间 能,按时间区间重叠查询 由 UNIQUE 约束兜底 中 需要上料防错和单板追溯的产线
纯事件流水 能,但要回放事件 高 高,需要先重建状态 已有消息总线、事件量很大的工厂

多数电子厂选第二种就够了:它把“当前状态”和“历史”放在同一张表里,当前状态就是 unmount_time 为空的那一行。

上料扫码时的校验SQL怎么写?

一次上料扫码的顺序是“站位码→飞达码→料盘码”,校验在一个事务里完成:先确认飞达可用且规格匹配,再确认料盘料号属于该站位的应上料号或替代组,通过后写两张绑定表和日志。

SELECT i.part_no AS plan_part, r.part_no AS reel_part,
       f.status, f.calib_due_date, i.feeder_type, f.feeder_type AS real_type
FROM smt_slot_plan p
JOIN smt_slot_plan_item i ON i.plan_id = p.plan_id
JOIN feeder f ON f.feeder_sn = :feeder_sn
JOIN reel r   ON r.reel_id   = :reel_id
WHERE p.machine_id = :machine_id
  AND p.status = 'ACTIVE'
  AND i.slot_no = :slot_no AND i.sub_slot = :sub_slot
  AND (r.part_no = i.part_no
       OR (i.alt_group IS NOT NULL AND r.part_no IN
           (SELECT part_no FROM part_alt WHERE alt_group = i.alt_group)));

返回空行就是料号不符;有行再逐项判断飞达状态、校准日期、飞达规格和料盘的湿敏开封时间。每一种失败都写进 load_check_log.reason,用固定的原因代码(如 PART_MISMATCH、FEEDER_CALIB_EXPIRED、MSL_EXPIRED),方便按原因统计。追溯反查时,用单板过站时间去和两张绑定表做区间匹配:mount_time <= :t AND (unmount_time IS NULL OR unmount_time > :t),就能得到这块板在这台机上每个站位用的料盘。

落地步骤怎么排?

  1. 统一站位号格式:不同品牌贴片机导出的站位号写法不同(如“1-L-12”和“12L”),先定一个内部格式和转换规则,再导入站位表。
  2. 先建飞达主档和料盘贴标:飞达贴耐磨标签,料盘在来料检验时生成 reel_id,解析供应商条码时保留原始条码字符串。
  3. 导入站位表并做版本管理:每次程序改动生成新版本,切换版本时在同一事务里关闭旧版本的所有未卸下绑定。
  4. 上线扫码校验:先在一条线试运行,只记录不拦截,核对一周的失败原因,修正替代料组和站位映射后再开启拦截。
  5. 接通追溯反查:用过站数据和绑定区间做一次真实批次的反查演练,确认能从单板查到料盘、从料盘批次查到受影响的单板。

常见坑有哪些?

  • 替代料没建组:操作员被迫“先上再说”,系统里出现大量人工放行记录,防错形同虚设。
  • 双轨飞达没有 sub_slot:8mm 双轨飞达一个站位挂两种料,没有子站位字段就只能存一种。
  • 接料时没有记录 SPLICE:新盘直接覆盖旧盘,旧盘余下的那段料对应的单板追不到。
  • 换版本不关旧绑定:旧版本的绑定一直“未卸下”,新版本上料时被 UNIQUE 约束拦住,现场只好让管理员手工删数据。
  • 料盘余量只做理论扣减:抛料和试打不计入,余量越算越不准,预警换料的时间点跟着失真。
  • 时间取终端本地时间:几台平板时钟不同步,区间匹配出现重叠或空档,反查结果时多时少。
  • 失败原因只写“校验失败”:没有原因代码就无法统计,问题到底出在料号、飞达还是湿敏管控说不清。

车间里是什么场景?

一个典型场景:夜班换线,贴片机从 A 产品切到 B 产品,B 产品程序里 12 号站位是 0402 电容,替代料有两个供应商。操作员先在线边仓领料,扫站位码、飞达码、料盘码,系统发现飞达校准已过期,提示换一个同规格飞达;换好后再扫,料盘是替代组里的另一供应商料号,校验通过并写入绑定。凌晨这盘料快用完,操作员接料,系统写一条 SPLICE 绑定,旧盘在打完后写解绑时间。第二天品质发现某个料批次有问题,用料盘批次查 feeder_reel_bind,再按时间区间关联过站记录,就能圈出受影响的单板范围。

上线怎么验收?

  • 错料拦截:故意扫一盘非本站位料号的料,系统拒绝并记录 PART_MISMATCH。
  • 并发约束:两个终端同时把同一飞达绑到不同站位,只有一个成功。
  • 版本切换:切换程序版本后,旧版本没有残留的未卸下绑定。
  • 接料追溯:对一次接料前后生产的单板,反查结果能分别对应到新旧两盘。
  • 反向反查:任选一个已生产完的真实料盘批次,能查出全部受影响单板,并记录查询耗时作为基线。

FAQ

问:站位表能直接用贴片机程序导出的文件吗?
答:可以作为来源,但要经过格式转换和人工确认再生效,导出文件里通常没有替代料组和飞达规格。

问:飞达和料盘要不要合成一个码?
答:不建议。飞达是资产、料盘是消耗品,合码后换盘就要重贴标签,追溯也分不清是飞达问题还是来料问题。

问:没有 MES 时能先做这几张表吗?
答:可以。这几张表本身不依赖某个系统,先用扫码终端加数据库落地,后续接入 MES 时保持表结构和时间口径不变即可。

问:绑定表数据量大了怎么办?
答:按月分区或把已卸下超过一定时间的记录归档到历史表,反查时按时间范围路由;当前状态只查 unmount_time 为空的行,量不会大。

问:手工放行要不要允许?
答:建议保留但必须记录放行人、原因代码和时间,并单独统计放行次数,放行多说明替代料组或站位表需要修正。

更新于2026年11月

posted @ 2026-11-05 14:32  智能制造MES笔记  阅读(9)  评论(0)    收藏  举报