使用 Python 字段脚本实现标签打印自动化
标签几乎每次都是对的。日期过期了,序列号丢了前导零,总数需要校验位才能让条码扫描通过。于是有人打开设计器里的标签模板,改三个字段,点击打印,然后第二天早上重复完全相同的事情。
这种编辑不应该放在模板里,而应该放在打印那一刻运行的代码里。RSJ LPSNG 允许你将 Python 脚本块直接附加到标签字段上,因此转换发生在模板内部——没有预处理步骤,不需要在你的 ERP 和打印机之间搞"先修好 CSV"的仪式。
本文讨论的是字段级自动化:什么是字段脚本、如何一次性配置好、如何通过 Web 服务 API 驱动它,以及如何防止它悄悄破坏你的生产线。
另外两个相邻主题在其他文章中有介绍:随订单变化的内容见在标签打印 API 之上构建订单专属标签,来自电子表格的数据驱动标签见如何从 Excel 数据文件和查找表创建标签。本文只聚焦于字段。
为什么用 Python 字段脚本实现标签打印自动化?
字段脚本运行在标签模板内部。Python 块附加到字段上,可以访问该字段的当前值,并在标签渲染之前对其进行修改。这一个设计决策改变了整个流程:
- 无需外部预处理。 你不需要一个在提交数据之前重写输入数据的脚本。数据原样传入;标签负责修正需要修正的内容。
- 跨客户端一致性。 脚本随标签一起走。无论作业来自 Web UI、Web 服务 API 还是 LPSNG Player,同一个字段都得到相同的处理。
- 逻辑可审查。 它就在所影响字段的旁边,而不是在某人笔记本电脑上的 cron 任务里。
值得这样做的任务都是些枯燥重复的事情:
| 任务 | 手动方式 | 字段脚本 |
|---|---|---|
| 打印时间戳 | 在字段中手动输入今天的日期 | 在渲染时格式化值 |
| 序列号 | 手动补零,漏掉一个,重新打印 | 规范化为固定宽度 |
| 校验位 | 在电子表格中计算 | 从字段值计算 |
| 派生标签 | 从另一个字段复制值 | 引用源字段 |
这些都不难。这正是重点——它们小到不值得专门做一个服务,又频繁到不能一直手动做。
一次性设置:准备标签和脚本
设置是每个字段的一次性工作。你需要标签工作室,而不是本地 Python 环境。
- 打开要自动化的标签,在 LPSNG 标签工作室中选择该字段。选择一个映射到文本或条码元素的字段。
- 附加 Python 脚本块,通过字段脚本 API 将其附加到该字段。脚本由平台与标签一起存储和管理——打印客户端上无需安装解释器。
- 使用脚本上下文。 该块可以看到字段的当前值,并在打印前返回修改后的值。确切的上下文变量在 Python API 参考中有定义——读一遍,然后复制该模式。
- 保存标签。 从此以后,每个渲染此标签的作业都会运行该脚本。
一个规范化序列号的最小块:
# Field: serial
# Runs before rendering. Input: the value passed in the print job.
raw = str(value).strip()
if raw.isdigit():
value = f"SN-{int(raw):06d}"
else:
value = raw.upper()
还有一个是盖上打印时间戳,而不是信任数据:
# Field: printed_at
from datetime import datetime
value = datetime.now().strftime("%Y-%m-%d %H:%M")
在托管平台而不是自己的代码中做这件事的价值在于,脚本是标签的一部分:它与标签一起版本化,跨作业复用,并且不会与模板失去同步。
复用才是真正的回报。 一旦脚本正确,就把同一个块附加到每个需要相同转换的字段上——跨标签、跨打印机。十个都以相同方式打印序列号的标签模板,就是十个以相同方式失败的模板,而修复只需在一个地方。
构建自动化流程:从数据到打印
脚本附加好之后,流程就是:认证、提交打印作业、让标签完成工作。
认证一次
LPSNG 的 Web 服务接口使用 OAuth2,采用简化的注册协议,通过单个 URL 连接外部系统。注册一次,将令牌存储在密钥库中,并使其远离你的代码仓库:
export LPSNG_BASE_URL="https://lpsng.rsj.de" # host of your edition
export LPSNG_TOKEN="<token from the OAuth2 registration>"
提交作业
Web 服务 API 接受打印作业,将单个标签渲染为 PDF 或 PNG,并支持直接打印。确切的端点路径和负载模式来自 Web 服务接口文档——下面的代码片段展示了调用的形态,不能替代参考文档:
import os
import requests
BASE = os.environ["LPSNG_BASE_URL"]
TOKEN = os.environ["LPSNG_TOKEN"]
job = {
# Payload keys are illustrative — use the schema from the
# web service interface documentation.
"label": "shipping-label",
"printer": "warehouse-zebra-01",
"data": {
"order_id": "A-10442",
"serial": "42", # the field script turns this into SN-000042
"printed_at": "", # left empty on purpose
},
}
resp = requests.post(
f"{BASE}/<print-endpoint-from-the-docs>",
headers={"Authorization": f"Bearer {TOKEN}"},
json=job,
timeout=30,
)
resp.raise_for_status()
print(resp.json()) # the response gives you the reference to poll
注意调用方不做什么:它不格式化序列号,也不填写 printed_at。这些现在是标签的工作。
有趣的情况:计算值
最快收回成本的转换是校验位,因为校验位错误意味着扫描器在包装站拒绝该标签。脚本从作业传入的值计算它:
# Field: ean
# Compute the mod-10 check digit for a 12-digit payload.
digits = [int(c) for c in str(value) if c.isdigit()]
if len(digits) == 12:
checksum = (10 - (sum(digits[::2]) + 3 * sum(digits[1::2])) % 10) % 10
value = "".join(map(str, digits)) + str(checksum)
else:
# Defensive: don't render an invalid barcode silently.
value = "INVALID"
因为脚本在条码渲染之前修改字段值,条码内容反映计算出的结果。你不需要单独的条码生成步骤。
对比:困难的方式
替代方案是在上游计算这些值:一个预处理器读取你的订单数据,重写每个字段,然后才调用 API。它能工作,直到预处理器和标签模板产生分歧——一个补到六位,另一个补到八位——而没人注意到,直到一托盘货带着不可读的序列号发出去。将逻辑保留在模板内部意味着每个字段只有恰好一个"正确"的定义。
如果你需要在没有浏览器的环境中运行相同的标签逻辑,LPSNG Player 是一个独立的命令行打印引擎,接受一个包加数据,并生成 PDF、PNG、JSON、直接打印或 ESL 更新。相同的标签,相同的脚本,不同的执行上下文。
自动化打印中的监控和故障处理
一个静默失败的自动化流程比手动流程更糟糕,因为没人在看标签。
监控队列。 Web UI 有一个作业队列,Web 服务 API 让你检查作业状态。轮询提交后得到的引用,使用退避策略,并在作业在同一状态停留过久时告警:
import time
def wait_for_job(session, base, job_ref, timeout=120, interval=5):
deadline = time.monotonic() + timeout
while time.monotonic() < deadline:
status = session.get(
f"{base}/<job-status-endpoint-from-the-docs>/{job_ref}",
timeout=15,
).json()
if status.get("state") in ("done", "failed"):
return status
time.sleep(interval)
raise TimeoutError(f"job {job_ref} did not finish in {timeout}s")
已发布的接口文档记录了作业提交、单标签渲染、直接打印和状态——它没有描述 webhook,所以轮询是应该基于的模式。如果你需要推送通知,请将其视为需要在你这边解决的缺口。
在脚本内部防御性地失败。 在事情发生之前决定坏输入应该怎么做。一个在条码字段中渲染 INVALID 的脚本,比一个抛出异常并导致整个作业失败的脚本——或者一个抛出异常、被吞掉、然后打印未格式化值的脚本——对你更有帮助。查看字段脚本 API 文档了解平台如何报告失败的块,并编写脚本使失败模式是你选择的那个。
用渲染输出做试运行。 Web 服务 API 可以将单个标签渲染为 PDF 或 PNG。打印前先渲染。这是你拥有的最便宜的冒烟测试:给脚本喂一个已知输入,查看输出,然后才将流程指向打印机。
Python 字段脚本的最佳实践
- 每个脚本一个转换。 一个既格式化日期又查找客户名称又重新计算总数的块,是没人会在凌晨两点调试的块。
- 保持幂等。 重新打印会发生——卡住的标签、被拒的纸箱、按了两次打印的操作员。如果
SN-000042再次经过脚本,它应该输出SN-000042,而不是SN-SN-000042。在转换之前检查值是否已经具有预期的形态。 - 在 LPSNG 外部对脚本进行版本控制。 将标签定义及其脚本块保存在 Git 中,并附上每个脚本存在原因的简短说明。平台存储并运行它们;它不能替代代码审查。
- 永远不要硬编码数据。 没有固定的客户编号,没有固定的日期,没有打印机特定的分支逻辑。一切都应该来自字段值或打印作业中传入的数据。
- 保持轻量。 避免长时间运行的操作。字段脚本在打印路径中运行,慢脚本就是慢标签。
- 用真实的异常情况测试。 空字符串、前导零、已经格式化的值、过长的值。这些正是字段脚本出问题的地方。
FAQ
Python 字段脚本可以访问外部 API 或数据库吗?
字段脚本 API 设计用于在标签上下文中访问和修改字段值。对于外部数据,你通常会在打印作业数据中传入。查看 LPSNG 文档了解对外部调用的任何限制。
字段脚本中可用的 Python 版本和库是什么?
确切的 Python 环境在 LPSNG 字段脚本 API 文档中有说明。建议使用标准库功能并保持脚本轻量。
字段脚本如何与条码配合工作?
脚本可以在条码渲染之前修改字段值,允许你计算校验位、格式化数据或应用影响条码内容的转换。
脚本执行时间有限制吗?
字段脚本应该高效。虽然所提供的信息中没有详细说明具体限制,但最佳实践是避免长时间运行的操作。参考 LPSNG 文档了解任何约束。
结论
模式很简单,这就是它经得起考验的原因:将转换一次性附加到字段上,认证一次,永远提交作业。手动编辑消失了,正确标签的定义恰好存在于一个地方——模板——而不是取决于今天早上碰巧在打印机旁的那个人。
从一个字段开始。选择你的团队最常编辑的那个,写十行 Python,渲染一个 PDF,并与已知良好的标签进行比较。然后做下一个。
Next Generation Label Printing System 是运行这些脚本的托管平台——标签工作室、OAuth2 Web 服务接口、作业队列和 Player——无需维护本地 Python 环境。你可以在 lpsng.rsj.de 看到全貌。
