简介:本资源是一套面向Java开发者的TSC条码打印机二次开发实战范例,聚焦零售、物流及仓储系统中常见的标签打印集成需求,适用于具备Java基础并需对接硬件设备的中高级开发者。压缩包共47个文件,含27个HTML文档(提供API说明与调用流程图解)、4个XML与properties配置文件(支撑环境适配与参数管理)、1个核心Java源文件及对应class编译类、1个TSCLIB.dll动态库与jna.jar封装依赖,辅以build.xml构建脚本和How to call DLL in Java.doc详解文档,整体仅1.18MB,轻量易部署。已有798人学习下载,资源结构清晰,覆盖从JNI/JNA调用原理、打印模板设计、Code128/QR码生成到异常处理与跨平台兼容性验证的完整链路,附带NetBeans工程结构与Javadoc文档,可直接复用核心逻辑快速嵌入订单、出入库等业务系统。
1. TSC条码打印机-JAVA调用范例:不是“连上就能打”,而是“连对端口、选对指令、绕过固件玄学”的实操闭环
你手头有一台TSC TP系列(比如TP244、TP345、TL200)或 TS系列(TS800、TS820)条码打印机,Java后端要驱动它打印标签——但试了网上抄来的几段Socket直连代码,要么连不上,要么打出来全是乱码,要么只打第一行就卡死。这不是Java不行,是TSC的通信协议和固件行为有隐性约定:它不认标准ESC/POS,也不走通用USB打印队列;它吃的是自己家的TSPL指令集,且对串口握手、超时、缓冲区清空极其敏感。这份JAVA调用范例,就是某开发者在产线部署时踩了两周坑后,把驱动逻辑、异常分支、设备初始化顺序全拧成可复现脚本的成果包。它不讲抽象架构,只解决三件事:怎么稳定建立连接、怎么把文本/条码/图片转成TSPL指令流、怎么让每一张标签都按预期位置精准吐出。适合正在做WMS、MES、仓储系统对接的Java工程师,也适合刚接手老系统维护、发现原生驱动早已失效的运维同学。
2. TSPL指令集与Java通信层:为什么不能用PrintService,而必须手写Socket/SerialPort
TSC打印机本质是一台嵌入式设备,其核心是TSPL(TSC Printer Language)指令解释器。它不是Windows/Linux系统级打印服务能直接调度的“标准打印机”,而是一个监听特定端口(串口COMx / 网络TCP 9100)的独立终端。Java的javax.printAPI(如PrintServiceLookup)依赖操作系统CUPS或GDI层,对TSPL无感知,强行注入会触发协议不匹配错误(返回ERROR: UNKNOWN COMMAND或静默丢弃)。真实可行路径只有两条:串口直连(RS232/USB转串口)或TCP Socket网络连接。本范例统一采用Socket方案——因产线环境多为网口部署,且串口在Linux容器中权限配置复杂,Socket更易跨平台复现。
2.1 TSPL指令的核心结构与Java拼接逻辑
TSPL指令是纯ASCII文本,以<CR>(\r)结尾,不换行。一条完整指令由命令名、参数、回车组成。例如:
SIZE 100 mm, 50 mm GAP 2 mm, 0 mm DIRECTION 0 REFERENCE 0,0 CLS TEXT 10,10,"2",0,1,1,"Hello World" BARCODE 10,50,"128",100,1,0,2,2,"1234567890" PRINT 1Java中不能简单用字符串拼接,必须严格控制:
- 所有数字参数需转为字符串,禁止科学计数法;
- 中文需GB2312编码(TSC默认字符集),不能用UTF-8;
TEXT命令的字体名(如"2")对应固件内置字体索引,非任意名称;BARCODE类型必须用TSPL定义值("128"、"EAN13"、"CODE39"),不能写"Code128"。
提示:TSC官方文档《TSPL Programming Guide》第3章明确列出所有指令语法。范例中
TSPLCommandBuilder类已封装常用指令生成逻辑,避免手拼出错。
2.2 Socket连接的关键参数与超时控制
TSC默认监听TCP端口9100,但不支持长连接复用。每次打印任务必须:
- 建立新连接;
- 发送完整TSPL指令流(含
CLS清屏、PRINT n); - 主动关闭连接(
socket.close()); - 等待至少200ms再发起下一次连接。
若复用Socket,第二次发送会因缓冲区残留或状态机错位导致PRINT不执行。范例中TSCPrinterClient类强制使用try-with-resources确保连接释放:
public void printLabel(String tsplCommands) throws IOException { try (Socket socket = new Socket()) { socket.connect(new InetSocketAddress(host, port), 3000); // 连接超时3s socket.setSoTimeout(5000); // 读写超时5s try (OutputStream out = socket.getOutputStream()) { out.write(tsplCommands.getBytes(StandardCharsets.ISO_8859_1)); // 关键:ISO-8859-1而非UTF-8 out.flush(); } } }StandardCharsets.ISO_8859_1:TSPL指令本身是ASCII,用此编码可保证字节1:1映射,避免中文乱码(中文由后续GB2312编码单独处理);setSoTimeout(5000):防止网络抖动时线程永久阻塞;out.flush():必须显式调用,否则JVM缓冲区可能延迟发送。
2.3 中文标签的GB2312编码与字体预加载
TSC打印机不内置UTF-8字体。若直接传UTF-8字节,固件会将其解析为乱码字符(如æ–‡)。正确做法是:
- Java中将中文字符串转为GB2312字节数组;
- 在
TEXT指令中指定字体为"4"(TSC GB2312内置字体); - 确保打印机固件版本≥V1.0.0(旧版可能无GB2312支持)。
String chineseText = "测试标签"; byte[] gb2312Bytes = chineseText.getBytes("GB2312"); // 拼入TSPL:TEXT 10,10,"4",0,1,1,""+new String(gb2312Bytes, "ISO-8859-1")注意:new String(gb2312Bytes, "ISO-8859-1")是关键技巧——它把GB2312字节当ISO-8859-1解码,生成可在TSPL中安全传输的ASCII字符串序列(实际是字节透传)。这是TSC官方推荐的中文处理方式,非玄学。
3. 完整Java调用范例:从初始化到多标签批量打印的6步落地链
本范例提供开箱即用的TSCPrinterDemo.java,覆盖真实业务场景:单标签即时打印、多标签循环打印、带变量参数的模板化输出。所有代码基于JDK 8+,无第三方依赖(仅用JDK原生API),可直接编译运行。
3.1 环境准备与设备确认
- 硬件连接:TSC打印机通过网线接入局域网,确认IP地址(如
192.168.1.100),在打印机面板或Web管理页(http://192.168.1.100)中开启“网络打印”并确认端口为9100; - 固件检查:进入Web管理页 → “系统信息”,记录固件版本(如
V1.2.3),低于V1.0.0需升级; - 防火墙放行:服务器所在机器需允许出站TCP 9100端口(
telnet 192.168.1.100 9100应能连通)。
注意:TSC部分型号(如TL200)默认关闭网络打印,需在面板按
MENU→Network→TCP/IP→Enable手动开启。
3.2 核心类结构与职责划分
| 类名 | 职责 | 关键方法 |
|---|---|---|
TSPLCommandBuilder | TSPL指令生成器 | buildText(),buildBarcode(),buildImage()(Base64转TSPL) |
TSCPrinterClient | Socket通信客户端 | connect(),sendCommands(),printLabel() |
LabelTemplate | 标签模板引擎 | render(Map<String, Object>),支持${name}变量替换 |
TSCPrinterDemo | 入口演示类 | main(),含5个典型用例 |
所有类均位于com.tsc.printer包下,无Spring等框架耦合,可直接复制进任意Java项目。
3.3 单标签打印:Hello World级验证
这是验证链路是否打通的最小闭环。以下代码生成最简TSPL并发送:
public class TSCPrinterDemo { public static void main(String[] args) { TSCPrinterClient client = new TSCPrinterClient("192.168.1.100", 9100); // 构建最简TSPL:清屏 + 打印文字 + 打印1张 String tspl = new TSPLCommandBuilder() .cls() // 清除缓冲区 .text(10, 10, "2", 0, 1, 1, "Hello World") // 字体2,水平放大1倍 .print(1) // 打印1张 .build(); // 返回完整TSPL字符串 try { client.printLabel(tspl); System.out.println("标签打印成功"); } catch (IOException e) { System.err.println("打印失败:" + e.getMessage()); } } }cls():必须放在每条指令流开头,否则旧内容残留;text(...)参数顺序严格对应TSPL规范:X,Y,Font,Rotation,Width,Height,Content;build()返回字符串末尾已自动添加\r,无需手动拼接。
3.4 多标签循环打印:解决产线连续出标需求
产线常需连续打印100张相同标签。若逐次新建Socket连接,效率低下且易触发TSC连接数限制(默认5个)。范例采用“单连接+多PRINT”模式:
// 打印10张相同标签(共用一次Socket) String tsplTemplate = new TSPLCommandBuilder() .cls() .text(10, 10, "2", 0, 1, 1, "批次号:${batch}") .barcode(10, 50, "128", 100, 1, 0, 2, 2, "${code}") .print(10) // 关键:此处设为10,非循环10次 .build(); String finalTspl = LabelTemplate.render(tsplTemplate, Map.of("batch", "20240501-A", "code", "123456789012")); client.printLabel(finalTspl); // 一次发送,TSC自动循环打印10张PRINT 10指令让TSC内部循环执行前序所有绘图指令10次,比Java层循环10次printLabel()快3倍以上;LabelTemplate.render()对${key}做安全替换,避免SQL注入式字符串拼接。
3.5 图片标签打印:BMP转TSPL Base64的硬核转换
TSC支持BITMAP指令打印单色BMP图片。范例提供ImageToTSPLConverter工具类,将本地BMP文件转为TSPL指令:
// 将src/test/resources/logo.bmp转为TSPL指令 String bitmapCmd = ImageToTSPLConverter.convert( Paths.get("src/test/resources/logo.bmp"), 10, 10, 200, 100); // X,Y,Width,Height // 拼入TSPL流 String tspl = new TSPLCommandBuilder() .cls() .append(bitmapCmd) // 插入位图指令 .text(10, 120, "2", 0, 1, 1, "LOGO测试") .print(1) .build();- 输入必须是单色BMP(1-bit),24位彩色BMP会解析失败;
- 转换逻辑:读取BMP像素阵列 → 按TSC要求的字节序重排(每行补零至8像素倍数)→ Base64编码 → 生成
BITMAP指令; - 工具类已处理BMP文件头校验,非BMP文件抛
IllegalArgumentException。
3.6 批量变量标签:JSON模板驱动的动态生成
面对SKU繁多的电商场景,需根据JSON数据动态生成不同内容标签。范例提供JsonLabelGenerator:
// JSON数据源(可来自HTTP API或DB) String jsonData = """ [ {"sku":"A001","name":"产品A","price":"99.00"}, {"sku":"A002","name":"产品B","price":"199.00"} ] """; List<Map<String, String>> labels = JsonLabelGenerator.parse(jsonData); for (Map<String, String> label : labels) { String tspl = new TSPLCommandBuilder() .cls() .text(10, 10, "2", 0, 1, 1, label.get("name")) .text(10, 40, "4", 0, 1, 1, label.get("sku")) // 中文SKU用字体4 .barcode(10, 80, "CODE39", 80, 1, 0, 2, 2, label.get("sku")) .print(1) .build(); client.printLabel(tspl); Thread.sleep(300); // 每张间隔300ms,防TSC缓冲溢出 }JsonLabelGenerator.parse()使用JDK 8javax.json,无Jackson/Fastjson依赖;Thread.sleep(300)是血泪经验:TSC处理速度约3张/秒,过快发送会导致PRINT被忽略。
4. 避坑指南:TSC JAVA调用中5个高频翻车点与根治方案
TSC的“稳定”是相对的,其固件对指令时序、编码、缓冲区极其敏感。以下5条是某开发者在3个产线项目中反复验证的避坑清单,每条均按“现象→原因→解决”结构给出可立即执行的方案。
4.1 现象:Socket连接成功,但PRINT指令无响应,标签不吐出
原因:TSC固件在收到PRINT后需一定时间处理,若Java端未等待即关闭连接,固件来不及执行。尤其在高负载时,PRINT可能被丢弃。
解决:在sendCommands()后增加Thread.sleep(500),或改用TSCPrinterClient.waitForPrintFinish()(范例中已封装,通过发送STATUS?指令轮询READY状态,超时10秒报错)。
4.2 现象:中文显示为方块或乱码(如□□□)
原因:Java字符串用UTF-8编码传给TSC,但TSC只识别GB2312字节流;或字体名未设为"4"(GB2312字体索引)。
解决:
- 确保
TEXT指令字体参数为"4"; - 使用
new String(text.getBytes("GB2312"), "ISO-8859-1")转换字符串; - 验证:用记事本另存为GB2312编码,复制内容到TSPL指令中测试。
4.3 现象:条码无法扫描,扫描枪提示“格式错误”
原因:BARCODE指令参数顺序错误。TSPL中BARCODE x,y,type,width,height,readable,rotation,mode,data,常见错误是将readable(是否显示人眼可读文本)与mode(校验位模式)混淆。
解决:
readable=1:显示下方数字;readable=0:不显示;mode=2:启用校验位(EAN13/UPC必开);mode=0:禁用;- 范例中
buildBarcode()方法已固化正确参数顺序,禁止手动拼接。
4.4 现象:打印多张时,第二张开始位置偏移或内容错乱
原因:未在每次打印前执行CLS(Clear Screen)指令,导致上一张的绘图指令残留于TSC内存缓冲区。
解决:强制在每条TSPL指令流开头调用.cls()。范例中TSPLCommandBuilder构造函数已默认包含cls(),若手动拼接TSPL,必须首行写CLS\r。
4.5 现象:Linux服务器上打印失败,报Connection refused,但Windows正常
原因:Linux防火墙(iptables/firewalld)拦截出站9100端口,或TSC打印机IP被服务器ARP缓存污染(arp -d 192.168.1.100可清除)。
解决:
- 服务器执行:
telnet 192.168.1.100 9100,确认连通性; - 若不通,检查
iptables -L -n | grep 9100,添加规则:iptables -I OUTPUT -p tcp --dport 9100 -j ACCEPT; - 清除ARP缓存:
ip neigh flush dev eth0(eth0替换为实际网卡名)。
5. 进阶技巧:用TSPL指令实现动态定位与条件打印
真实业务中,标签内容长度不可控(如商品描述字段),若固定坐标打印,长文本会溢出或遮挡条码。TSPL虽无“自动换行”指令,但可通过TEXT指令的width/height缩放与ROTATION旋转,结合Java计算实现动态适配。以下是某仓储系统落地的两个硬核技巧。
5.1 文本自适应宽度:根据字符数动态缩放字体
TSC的TEXT指令支持width和height参数(范围1~8),用于放大字体。当字段内容超长时,可降低放大倍数保证不换行:
public String buildAdaptiveText(int x, int y, String content) { int charCount = content.length(); int width = 1, height = 1; if (charCount <= 10) { width = 2; height = 2; // 短文本放大显示 } else if (charCount <= 20) { width = 1; height = 2; // 中等长度保持高度 } else { width = 1; height = 1; // 长文本用原始大小 } return String.format("TEXT %d,%d,\"2\",0,%d,%d,\"%s\"\r", x, y, width, height, escapeTSPL(content)); }escapeTSPL(content):转义TSPL特殊字符(如"需写为\",\r需写为\\r);- 此方案避免Java层文本截断,由TSC固件完成渲染,精度更高。
5.2 条码与文本联动定位:基于条码宽度反推文本起始X坐标
条码宽度受BARCODE指令width参数影响(单位mm),而TEXT需精确放置在条码正上方。范例提供BarcodePositionCalculator:
| 条码类型 | 标准宽度(mm) | 推荐TEXT X偏移 |
|---|---|---|
| CODE39(10位) | 65 | x = barcodeX + 5 |
| EAN13(13位) | 78 | x = barcodeX + 8 |
| CODE128(12位) | 52 | x = barcodeX + 3 |
int barcodeX = 10; int barcodeWidth = BarcodePositionCalculator.getWidth("123456789012", "CODE128"); int textX = barcodeX + (barcodeWidth > 70 ? 8 : 3); // 宽条码多留空隙 String tspl = new TSPLCommandBuilder() .barcode(barcodeX, 80, "CODE128", 100, 1, 0, 2, 2, "123456789012") .text(textX, 40, "2", 0, 1, 1, "SKU-123456789012") .print(1) .build();getWidth()方法根据条码类型、数据长度、width参数查表计算,避免目测调试;- 实测误差<0.2mm,满足产线贴标精度要求。
5.3 条件打印:用TSPL的IF指令实现逻辑分支
TSPL V1.1+固件支持IF/ENDIF指令,可基于变量值决定是否打印某区域。范例中LabelTemplate扩展支持${if:condition}...${end}语法:
// 模板中写: // ${if:isPromotion==true}PROMOTION!\r${end} // ${if:isPromotion==false}REGULAR\r${end} String rendered = LabelTemplate.render(template, Map.of("isPromotion", "true")); // 输出:PROMOTION!\r- Java层解析
${if:xxx}为TSPLIF指令(如IF "true"=="true"); - 需确保TSC固件≥V1.1.0,否则
IF指令被忽略; - 此方案减少Java层if-else分支,提升模板复用率。
从那以后我每次对接新TSC设备,都强制走一遍这三步:先用telnet确认9100端口通,再发最简CLS\rTEXT 10,10,"2",0,1,1,"OK"\rPRINT 1\r验证基础链路,最后用STATUS?指令查固件版本——少走两天弯路。希望帮到你。
本文还有配套的精品资源,点击获取