产线要做批次追溯,第一步都是把扫码枪接进上位机。接法就三种:键盘楔(模拟键盘输入)、串口(RS232)、网口(TCP)。选型不复杂:单工位、就扫个码不做逻辑,键盘模式最快上线;多工位、要和工位数据绑定、要防重码漏码,老老实实走串口或 TCP。扫码本身一天就能调通,难的是后面那件事——怎么把码和这个工位的加工数据绑在一起,出了问题能正查反查。这篇从接线讲到落库。
三种接法:先想清楚要不要"程序拿到码"
扫码枪当键盘用,码值直接"打"进当前焦点的文本框,末尾自带回车。零代码,但程序无法区分"扫码"和"手工敲键盘",焦点一跑偏码就丢了。
扫码枪切串口模式,上位机开 SerialPort 监听,按结束符拼帧。码值独立通道进来,可以做格式校验、防重、绑定逻辑,是定制上位机的默认选择。
固定式工业读码器常用,扫码器做 TCP 客户端主动把码推给上位机监听端口,布线距离不受限,适合读码器装在线体上、工控机在柜子里的场景。
▲ 判断标准其实就一条:码进来之后要不要走程序逻辑。要校验、要绑定、要防重,就必须 B 或 C;只是把码填进一个输入框,A 最省事。
串口模式两个必配项:结束符和帧间隔
串口扫码枪的数据是一串字节流过来的,程序怎么知道一条码扫完了?靠两个约定:
| 配置项 | 推荐值 | 说明 |
|---|---|---|
| 结束符 | CR+LF(\r\n) | 扫码枪设置里打开"添加回车换行后缀",程序按行读取,一条码一行,边界清晰 |
| 波特率 | 9600 / 115200 | 枪与程序必须一致;改波特率后记得重新上电 |
| 帧间隔超时 | 50~100 ms | ReadTimeout 兜底:万一结束符配置丢了,靠"静默 100ms 视为一帧结束"收尾 |
| 编码 | ASCII | 条码内容都是 ASCII;遇到中文标签码再考虑 UTF-8 |
这里有个新手常踩的坑:用 ReadExisting 一把抓再自己切串。串口数据是分段到达的,一条码可能分两三次 DataReceived 事件才收齐,直接抓就会拿到半条码。稳妥做法是逐字节/逐段累积进缓冲区,见到 \r\n 才取出一整条。C# 骨架:
private readonly StringBuilder _buf = new StringBuilder(); private DateTime _lastCode = DateTime.MinValue; private void OnDataReceived(object s, SerialDataReceivedEventArgs e) { string chunk = _port.ReadExisting(); lock (_buf) _buf.Append(chunk); string all; lock (_buf) all = _buf.ToString(); int idx; while ((idx = all.IndexOf("\r\n")) >= 0) { string code = all.Substring(0, idx).Trim(); all = all.Substring(idx + 2); if (code.Length > 0) HandleCode(code); // 一条完整码 } lock (_buf) { _buf.Clear(); _buf.Append(all); } } private void HandleCode(string code) { // ① 防抖:同码 2 秒内重复到达视为连扫抖动,丢弃 if (code == _lastText && (DateTime.Now - _lastCode).TotalSeconds < 2) return; // ② 格式校验:长度、前缀、字符集,不合格直接红字提示重扫 if (!CodeRule.IsValid(code)) { RaiseBad(code); return; } _lastText = code; _lastCode = DateTime.Now; _queue.Enqueue(code); // 丢给业务线程做绑定,串口线程不做重活 }
码不是目的:扫码要和工位数据绑在一起
扫码枪调通只是开始。追溯的本质是回答两个问题:这个产品用了什么料、经过了什么过程(正查);这批料出了问题,流到了哪些产品里(反查)。所以每条扫码记录落库时,必须同时绑上当时的上下文:
落库结构不复杂,核心就是一张主表 trace_record,记每一次扫码绑定:
| 字段 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键 |
| product_code | varchar(64) | 产品码(一物一码),建索引 |
| material_batch | varchar(64) | 扫到的物料/零件批号码,反查的关键字段,建索引 |
| station_id / op_no | varchar / int | 工位与工序号 |
| work_order | varchar(32) | 工单号 |
| operator | varchar(32) | 操作员账号 |
| process_data | json/varchar | 该工位关键参数快照(扭矩、温度、测试值) |
| result / scan_time | tinyint / datetime | 合格与否 + 扫码时刻 |
▲ 反查就是一条 SQL:SELECT product_code FROM trace_record WHERE material_batch = '批次号',物料批号索引必须建,否则产线追溯时全表扫描能急死人。
现场最容易翻车的三件事
重码。操作员对着同一个件连扫两下,或者返修件二次上线,系统里出现两条记录。处理原则:同一工位同一产品码只允许一条"合格"记录,重复扫码要么拒绝并提示"已上线",要么走显式的返修流程覆盖,绝不能静默追加。
漏码。产品流到下一工位,才发现上一工位没扫码。所以第③步的"校验放行"很重要:下一工位扫码时检查上一工序记录是否存在,缺了就拦截报"跳工序"。宁可停线一分钟,不要事后补录一堆假数据。
码制混乱。今天客户用 Code128,明天换 QR,后天来一批 DataMatrix。扫码枪要在设置里把需要的码制全打开,同时上位机的格式校验规则按"前缀+长度"做白名单,陌生格式的码直接拒收并报警——收进来一个解析不了的码,比不收更麻烦。
收尾:上线前的自检清单
- 结束符配置与程序解析方式一致(都按 \r\n),断帧超时兜底已配;
- 同一码 2 秒内重复扫,系统只认一次;
- 扫错料(与工单 BOM 不符)时系统拦截并红字提示,不放行;
- 跳工序能被下一工位拦截;
- 拿一个真实批号做反查,10 秒内能列出全部流向产品;
- 扫码记录与工位参数快照在同一事务里落库,不会出现"有码无参数"。
扫码接入这件事,硬件层面半天搞定,真正的工程量在"绑定与防错"上。如果你的追溯系统现在只是把码存了个流水,建议先补一个功能:输入任意物料批号,列出它流向了哪些产品——做不出来的话,追溯其实还没开始。
