简介:面向创维电视等智能终端的工厂产线场景,这份串码写入与校验工具包专注解决SN码、MAC地址等关键信息的批量写入、读取与校验问题,覆盖扫码、写码、打标到工位联调的完整流程,并针对批量产线操作设计了简洁的命令交互方式。压缩包共18个文件,仅151KB,以cfg、txt、bat、html、xls等配置、脚本与表格为主,集中存放指令格式、厂商ID、供应商数据、高安检测参数等产线调试要素,还包含条码模板与页面预览文件。工具包内置主程序与服务模块,可实现设备识别与数据交互,配合条码打印组件和BarTender模板直接驱动标签输出,支持通过标准指令对接自动化检测工位;注册批处理脚本负责梳理条码组件依赖,整体结构紧凑、便于快速部署参考。已有37人学习下载,适合产线工艺、测试开发或设备维护人员据此梳理智能终端量产阶段的串码管理与校验逻辑。
1. 创维产线专用串码写入与校验工具包:到底解决产线上哪个痛点
我见过太多产线在"写码"这个环节翻车:SN 写重了、MAC 和 SN 对不上、标签打印内容和实际写入不一致,这类问题当时不暴露,等整机铺到渠道和用户手里才开始集中爆发。创维产线专用串码写入与校验工具包,正是把"串码写入、读回校验、条码打印、工位检测"四件事串成一条闭环:扫码枪一扫,工具自动生成 SN 并写入设备,写完后读回比对并落库,合格才打标签、过工位检测放行。这套方案适合整机厂、方案商和代工厂的产线工程、测试开发、MES 对接人员参考,不要求设备端已经装了专用软件,也不需要产线有复杂的 MES 基座,一台工控机加 USB 和网口就能跑起来。
2. 串码写入与校验的底层设计:数据规范、写码通道与校验闭环
2.1 SN 串码结构设计:把型号、批次、流水号编进一串字符
写码工具第一步不是写代码,而是定 SN 规则。很多产线的历史包袱就是 SN 没规则,工厂代码、日期、流水号混在一起,长度不统一,扫码枪读出来也没法解析。我在做这套工具包时,把 SN 固定为 16 位,前 14 位承载业务信息,后 2 位是校验字符。这样打印标签、MES 上报、售后追溯都共用同一套解析逻辑。
在实际产线里,SN 的典型分段如下表所示:
| 段位 | 长度 | 示例 | 含义 |
|---|---|---|---|
| 1-2 位 | 2 | TV / BK | 产品线代码,电视、板卡、配件 |
| 3-4 位 | 2 | 25 | 年份后两位,2025 年 |
| 5-6 位 | 2 | 04 | 月份 |
| 7-8 位 | 2 | A1 | 工厂加产线编号 |
| 9-14 位 | 6 | 003721 | 当日流水号,从 000001 递增 |
| 15-16 位 | 2 | X7 | 由前 14 位算出的校验符 |
SN 生成后要一次性保证当天产线内不重复。流水号建议由工具包统一分配,不要靠设备端自增,否则多工位并发容易撞号。校验字符我用的是"加权取模"算法,字符集去掉易混的 0/O、1/I、Z/2,避免人工抄写和扫码识别时出错。
生成 SN 和校验码的核心逻辑如下:
# 字符集:去掉 0/O、1/I、Z/2 这类易混字符 CHARS = "0123456789ABCDEFGHJKMNPQRSTUVWXYZ" def sn_checksum(raw: str) -> str: table = {c: i for i, c in enumerate(CHARS)} total = 0 for pos, ch in enumerate(raw): total += table[ch] * (pos + 1) total %= len(CHARS) * len(CHARS) return CHARS[total // len(CHARS)] + CHARS[total % len(CHARS)] def build_sn(product: str, year: str, month: str, factory: str, seq: int) -> str: raw = f"{product}{year}{month}{factory}{seq:06d}" return raw + sn_checksum(raw)这段代码里,sn_checksum把前 14 位每个字符映射成整数,乘以位置权重后求和,再对 36×36 取模,得到两个校验字符。build_sn负责拼接完整 SN。参数说明:product是 2 位产品线代码,year和month是 2 位数字字符串,factory是 2 位工厂产线编号,seq是当日流水号的整数,格式化时用06d补足 6 位。
这里有个容易忽略的细节:校验字符不要让现有的 MES 去算,因为 MES 算的规则可能和打印工具不一致。工具包自己生成就自己校验,两边算法保持同一份代码,减少黑匣子。
2.2 写码通道怎么选:ADB、串口与网络写号的分工
写码通道决定工具包怎么和设备通信。整机产线最常见的是 ADB,Android 智能整机在工厂测试模式开启后,用 USB 线连工控机就能执行写号命令。串口则多用于 Linux 方案板、单片机主板,UART 115200 波特率,稳定性比 ADB 高,但速度慢。网络方式适合设备已经联网、或产线设备不方便拖 USB 线的场景,工具通过设备的网络调试接口执行写号。
三种通道的取舍可以按下面这张表判断:
| 通道 | 接线方式 | 适用设备 | 写码速度 | 需要注意的点 |
|---|---|---|---|---|
| ADB | USB 数据线 | Android 整机、电视、机顶盒 | 快,单条命令百毫秒级 | 多工位共用 PC 时容易串设备 |
| 串口 | 串口线/TTL/RS232 | Linux 板卡、单片机主板 | 中,受波特率限制 | 命令交互要处理好回显和换行 |
| 网络 | 网线/Wi-Fi | 已联网的智能设备 | 快,受网络延迟影响 | 需要固定 IP 或 MAC 绑定 |
我一般建议优先用 ADB,因为整机产线的设备端系统大多是 Android,ADB 自带 shell,可以同时完成写 SN、写 MAC、读版本号、检测开机状态。串口则要对接设备端串口协议,每条命令的回显格式都不同,维护成本高。网络方式适合那种"设备已经上电联网、但工位不方便插 USB"的流水线,前提是网络环境隔离干净,不能把测试网和生产网混在一起。
2.3 校验方案:读回比对之外,为什么还要加 CRC 与防重
写码工具最核心的可靠机制是校验,但不能只做"写完后读一遍"。我在这套工具包里做了三层校验:第一层是读回比对,写入后重新读取设备里的 SN,和期望值逐字符比较;第二层是 CRC 校验,把生成的 SN 做一次加权校验,确保 SN 本身没有算错,也能在扫码枪读到标签时快速验证标签内容合法性;第三层是防重校验,每写入一台都在本地 SQLite 记录,再和 MES 侧已注册 SN 做二次查重,防止同一条 SN 被写到两台设备上。
防重查重的关键代码:
import sqlite3 def check_duplicate(sn: str) -> bool: conn = sqlite3.connect("station.db") cursor = conn.execute( "SELECT 1 FROM write_log WHERE sn = ? AND result = 'PASS'", (sn,) ) dup = cursor.fetchone() is not None conn.close() return dup这段逻辑先查本地库,result = 'PASS'表示该 SN 之前已经成功写入过。如果本地没有记录,再调用 MES 接口查全厂范围的重码。参数说明:station.db是工位本地数据库,建议每个工位一个文件,避免多工位并发写库导致锁冲突。
三层校验完成才算"写码合格",之后才允许打印标签和过工位检测。这个顺序不能反,一旦先打印后校验失败,标签就作废了。
3. 用 Python 把写码校验流程跑通:主流程代码与关键参数
3.1 主流程拆解:从扫码枪到放行的状态迁移
工具包的主流程可以理解成一条状态机:扫码触发 → 生成 SN → 查重 → 写码 → 读回校验 → 打印标签 → 工位检测 → 放行。每一步失败都要停下来报警,不允许带着 FAIL 结果继续跑。
我在实现时把每个环节定义成独立状态,方便单独调试和加超时控制:
# 主流程状态定义 STATE_SCAN = "scan" # 等待扫码枪输入或手工键入订单号 STATE_GEN = "gen" # 生成 SN 并做本地防重 STATE_WRITE = "write" # 把 SN/MAC 写入设备 STATE_VERIFY = "verify" # 读回比对 + CRC 校验 STATE_PRINT = "print" # 打印条码标签 STATE_CHECK = "workstation" # 工位检测 STATE_PASS = "pass" # 全部通过,放行 def run_station(event: dict) -> tuple: state = event["state"] sn = event.get("sn", "") if state == STATE_SCAN: return STATE_GEN, gen_and_dedupe(event["order"]) if state == STATE_GEN: return STATE_WRITE, write_sn(event["device_id"], sn) if state == STATE_WRITE: return STATE_VERIFY, verify_sn(event["device_id"], sn) if state == STATE_VERIFY: return STATE_PRINT, print_label(sn, event["mac"], event["printer_ip"]) if state == STATE_PRINT: return STATE_CHECK, workstation_check(event["device_id"]) if state == STATE_CHECK: return STATE_PASS, {"sn": sn, "result": "PASS"} return STATE_PASS, {}这段代码的核心是让每个状态只做一件事,返回值是"下一个状态"和"该状态要执行的动作"。event字典里携带order、device_id、mac、printer_ip等上下文。这样做的好处是,哪个环节超时或失败,日志里能直接看到卡在哪个状态,不用从头排查。
3.2 写码与读回校验核心代码
写码动作本身不复杂,复杂的是写完后设备什么时候生效。我用的方案是"写码 → sync → 短等待 → 读回 → 比对",必要时再重启设备二次读回。
import subprocess, time # 每条产线绑定固定设备标识,防止多工位串线 def adb(device_id: str, *args: str, timeout: float = 10): cmd = ["adb", "-s", device_id] + list(args) return subprocess.run(cmd, capture_output=True, text=True, timeout=timeout) def write_sn(device_id: str, sn: str) -> bool: # 写号目录依厂商工厂接口而定,常见的是 /data/factory/ 下的节点 ret = adb(device_id, "shell", f"echo {sn} > /data/factory/sn") adb(device_id, "shell", "sync") time.sleep(0.5) return ret.returncode == 0 def verify_sn(device_id: str, sn: str) -> bool: # 先读回原始内容,去掉换行和空格再比较 out = adb(device_id, "shell", "cat /data/factory/sn") got = out.stdout.strip() if got.upper() != sn.upper(): return False return sn_checksum(sn[:14]) == sn[14:]write_sn里的sync是强制把缓存落盘,避免设备突然断电导致 SN 丢失,这是血泪经验换来的。time.sleep(0.5)是给设备文件系统一点刷新时间,太短会导致读回旧值。verify_sn先做大小写归一化比对,再校验后两位 CRC。参数说明:device_id在 ADB 场景下是设备的transport_id或序列号,建议用adb devices -l查到的完整标识,不要用空字符串代表唯一设备。
3.3 工位配置与参数调整
每个产线的设备型号、写号目录、超时策略都不同,所以工具包把参数全部外置到 YAML 配置里,改参数不用改代码:
# station.yaml 工位配置示例 station: id: "A1" product: "TV" factory: "A1" device: channel: "adb" # adb / serial / network serial_id: "192.168.10.21:5337" write_node: "/data/factory/sn" mac_node: "/data/factory/mac" timing: write_wait: 0.5 # 写码后等待秒数 boot_wait: 15 # 开机检测最长等待秒数 retry_times: 2 # 校验失败重试次数 retry_interval: 0.5 # 重试间隔秒数 printer: ip: "192.168.10.200" port: 9100 template: "label.zpl" mes: url: "http://192.168.20.10:8080/api/records" enabled: true配置里最重要的三个参数是write_wait、boot_wait和retry_times。write_wait太短会读到旧值,太长拖节拍;boot_wait要按机型最慢的开机时间设定,不能按正常样机的时间设;retry_times建议设成 2,设备瞬时卡顿属于正常现象,重试一次能减少误判,但超过 3 次基本就是设备真的有问题。参数调整后要由产线工程确认,不要放给作业员随意改,否则售后溯源时参数无法对齐。
4. 条码打印与工位检测:ZPL 直发、防重防漏与 MES 对接
4.1 ZPL 标签模板与打印:不装驱动直接发指令
工具包打印条码没有走 Windows 打印机驱动,而是直接向打印机的 9100 端口发送 ZPL 指令。这样省掉了驱动安装和驱动版本不一致的问题,也更适合工控机上多工位快速部署。
ZPL 模板我通常设计成"一维码 + 二维码"双形式,一维码给产线扫码枪扫,二维码给售后扫:
^XA ^FO50,40^GB700,120,3^FS ^FO80,70^A0N,36,36^FD{SN}^FS ^FO80,130^BQN,2,6^FDQA,{SN};{MAC}^FS ^FO80,220^A0N,24,24^FD{PRODUCT} {DATE}^FS ^XZ{SN}、{MAC}、{PRODUCT}、{DATE}是工具在发送前替换的变量。^BQN,2,6是二维码指令,QA表示 DataMatrix 编码。打印代码直接用 socket 发文本:
import socket def print_label(printer_ip: str, port: int, zpl_text: str): with socket.create_connection((printer_ip, port), timeout=5) as s: s.sendall(zpl_text.encode("utf-8"))这段代码的逻辑很直接:建立 TCP 连接后把 ZPL 文本发给打印机,打印机自动解析执行。参数说明:port固定用 9100,这是打印机的网络打印端口;timeout=5是防止打印机离线时工位死等。建议每次打完后等打印机的回执状态,如果打印机不支持状态回执,至少要在打完后扫码确认一次标签内容。
4.2 工位检测功能的实现:开机检测、版本匹配与结果判定
条码打印完成后,设备还要过工位检测这一关。这里的"工位检测"包含三件事:设备是否能正常开机、软件版本是否为当前产线要求的版本、写入的 SN 是否已生效。只有这三项全部通过,工位才放行。
CHECKS = [ {"name": "boot", "cmd": "getprop sys.boot_completed", "expect": "1"}, {"name": "sw_ver", "cmd": "getprop ro.build.version.incremental", "expect": "V20241130"}, {"name": "sn_ready", "cmd": "cat /data/factory/sn", "expect": None}, # 由外部传入期望SN ] def workstation_check(device_id: str, expect_sn: str) -> list: fails = [] for item in CHECKS: expect = expect_sn if item["expect"] is None else item["expect"] out = adb(device_id, "shell", item["cmd"]) if expect not in out.stdout.strip(): fails.append({"check": item["name"], "got": out.stdout.strip()}) return failsworkstation_check返回空列表代表全部通过,返回非空列表则工位锁定并亮红灯。关键点是expect的匹配不要用全等比较,而要用"包含",因为 Android 的getprop输出有时带空格或回显符号,全等匹配会把好件误判成坏件。工位检测失败时,工具包会弹出对话框提示作业员扫码确认或呼叫线长处理。
4.3 记录本地落库与 MES 上报接口约定
工具包所有写码和检测记录都会落到本地 SQLite,同时按需上报 MES。本地落库的意义在于:即使 MES 断网,产线也能继续生产,断网期间的记录后续自动补传。
CREATE TABLE write_log ( id INTEGER PRIMARY KEY AUTOINCREMENT, sn TEXT UNIQUE NOT NULL, mac TEXT, station TEXT, operator TEXT, result TEXT, printed INTEGER DEFAULT 0, created_at TEXT DEFAULT (datetime('now', 'localtime')) ); CREATE INDEX idx_write_log_sn ON write_log(sn);表结构里sn加了唯一约束,这是防止本地重码的最后一道保险。printed字段标记标签是否已打印,避免校验失败却打了标签。上报 MES 用 HTTP JSON:
import requests def report_to_mes(sn: str, mac: str, result: str, station: str): payload = { "sn": sn, "mac": mac, "result": result, "station": station, } try: resp = requests.post("http://192.168.20.10:8080/api/records", json=payload, timeout=5) resp.raise_for_status() except Exception as e: # 上报失败先落本地,等待定时重传 save_pending_upload(payload)上报失败时不要阻塞产线,把数据存到pending_upload表,由后台线程定时补传。这是产线工具的基本素养:设备可以断网,产线不能停。参数说明:timeout=5是 MES 响应超时,超过 5 秒立刻转本地暂存,避免工位卡顿。
5. 产线落地绕不开的 5 个坑:写入失败、误判与串工位排查
5.1 写入成功但校验读不回来:重启时序导致的误判
现象:写码命令返回正常,但紧接着读回时发现内容是旧 SN 或空文件,把好设备误判成写码失败。
原因:很多设备的写号接口不是立即落盘,命令执行完只是写了 cache,需要sync之后延迟几毫秒才能读到;另一些设备要求重启后才把新 SN 加载到系统属性里。
解决:写码后强制sync,再等待write_wait配置的 0.5 秒,然后才读回。如果读回还是旧值,不要直接判 FAIL,重试一次,重试时先读一次确认设备是否真的写入失败还是时序问题。我吃过这个亏,第一版工具刚上线时误判率接近 15%,全是时序问题,不是设备问题。
5.2 多工位同时写码导致串设备:ADB 连接被互相打断
现象:两条产线同时运行工具包,A 工位的 SN 被写到了 B 工位的设备上,而且不是偶发,是高频出现。
原因:多个 Python 进程共用同一个 ADB server,adb shell不带-s参数时默认连接当前唯一设备,两个工位同时枚举就会出现"把命令发到另一台设备"的情况。
解决:所有 ADB 命令必须带-s指定transport_id,并且每个工位用独立的 ADB server 端口:
export ADB_SERVER_SOCKET_ADDRESS=127.0.0.1:5037 # 工位1 export ADB_SERVER_SOCKET_ADDRESS=127.0.0.1:5038 # 工位2这个配置写在工位机的启动脚本里,工具包代码里不再使用无-s的裸adb命令。这样每个工位连接自己的设备,互不干扰。
5.3 标签打出来中文乱码:ZPL 编码翻车现场
现象:标签上的品名、物料描述变成方块或者乱码,但 SN 和二维码正常。
原因:ZPL 的默认编码不是 UTF-8,中文字符直接塞进 ZPL 指令会被打印机按未知编码处理,最终显示乱码。
解决:ZPL 中文字段用^FH开启十六进制转义,再把 UTF-8 字符转成十六进制:
def zpl_hex(text: str) -> str: return "".join(f"{b:02X}" for b in text.encode("utf-8"))模板里写^FH^FD{HEX_TEXT}^FS,变量替换时对中文内容先调zpl_hex。SN、MAC 这类纯 ASCII 内容不需要转,只有中文品名和厂商信息需要。
5.4 扫码枪串工位:标签被旁边工位扫走
现象:A 工位刚打印出标签,B 工位的扫码枪把标签扫走了,B 工位系统误报"重码"并停机。
原因:无线扫码枪的光束范围太大,或者工位之间没有挡板,标签在传送带上经过 B 工位扫码范围时被误触发。
解决:扫码枪设置为手动触发模式,作业员按一下扫一下,不要用连续扫描;标签上的二维码里嵌入工位代码,工具包解析到二维码里的工位代码和当前工位不一致时直接忽略;产线布局上在工位之间加物理挡板。最管用的还是二维码里带工位标识,软件层面规避物理干扰。
5.5 好件被误判为坏件:超时参数与重试策略
现象:设备明明正常开机,工位检测却报boot超时或sw_ver读取失败,一天下来误判几十台。
原因:boot_wait设的是 8 秒,但某批设备首次开机要 12 秒才完成初始化;版本号读取命令输出带回车符,期望值用的是精确匹配,比对不上。
解决:超时参数按机型分开维护,每换一个机型就重新标定;所有getprop输出先strip()再比对,用"包含"代替"全等";检测失败时给 2 次重试机会,间隔 0.5 秒,排除设备瞬时卡顿。另外在界面上留一个人工 PASS 通道,由线长输入工号确认,避免因为工具误判把产线堵死。误判是最消耗产线信任度的,宁可多测一次,不要漏测。
6. 把单台节拍从 5 秒压到 2 秒:并行、复用与验证习惯
6.1 先别急着优化:把每台设备耗时记录成基线
优化前先给每台设备打点计时,把"扫码 → 生成 → 写码 → 校验 → 打印 → 工位检测"每一步的耗时写进 CSV。我看过不少产线项目,优化全靠感觉,改完不知道是快了还是慢了。先跑 50 台,统计每一步的均值和中位数,找出真正的大头。常见的大头是开机等待和打印,不是写码本身。
6.2 三处常见优化:并行打印、常驻连接与快速校验
第一处是打印和写码并行。SN 在扫码后已经生成,标签内容不再依赖写码结果,完全可以先打印标签再做写码校验。把打印时间从主流程里挪出去,单台能省 0.3 到 0.8 秒。第二处是复用 ADB 连接,不要每条命令都重新adb connect,设备上线后保持连接状态,写码、读回、检测都在同一会话里执行。第三处是校验加速,SN 比对直接读回后strip()比较,CRC 放在后台验证,不必等 CRC 结果才放行,CRC 失败在下一工位拦截。
6.3 收尾
我做过一次比较激进的优化,把boot_wait从 15 秒砍到 5 秒,结果误判率飙到 30%,最后只能回退参数重新标定。那之后我养成了一个习惯:任何参数改动,先跑 50 台对比基线,确认误判率和节拍都达标再切量产。工具包本身不难,难的是产线环境里那些不写在文档里的时序和习惯。希望帮到你。
本文还有配套的精品资源,点击获取