Honeywell 打印机(ZPL)配置调用超详细教程 -YB
20231214 Honeywell 打印机(ZPL)配置调用超详细教程 -YB
–noted by yuanbao
在线笔记地址:https://note.youdao.com/s/9iwQsXJi
一、概述:
1.1 打印机介绍:
规格参数:
1.2 打印机设置方式:
-
网络打印机:网线插到打印机上。
-
共享打印机:电脑与打印机通过 USB 连接,电脑将打印机共享到网络中,电脑不能关机。
1.3 传动需求场景:
传动现场环境是:现场一个下线点(含多个工位)配置一台工控机和一台打印机,工控机和打印机分别通过网线接入生产网络中。通过过点信息(自动)和手动补打的方式调用打印,也就是需要做到某工位群 A 在这台打印机 P1,某工位群 B 在另一台打印机 P2。最终会有一百多台打印机,能实现在对应工位打印条码。
解决办法:将每个打印机配置成网络打印机,之后配置到 MES 应用服务器上,MES 在调用时需确定使用哪台打印机,通过维护工位和打印机的关联关系(考虑在自定义档案中),这样在一条业务数据中就能直接匹配到对应打印机了,通过网络打印完成条码打印。
1.4 核心思路:
1. 安装打印机驱动;
2. 配置打印机端口,使得电脑可直接调通打印机(控制面板中有该打印机且可打印测试页);
3. 启动打印微服务,通过传参 zpl 指令调用打印机完成打印;
4. 集成到 MES 中,MES 配置打印模板,完成参数替换后调打印服务完成打印。
1.5 打印机服务设置网页:
浏览器访问https:// 打印机 ip
二、配置打印机:
2.1 驱动安装
资源包内容:
tips:HoneyWell 官方驱动可以,Bartender 网站上下载的 HoneyWell 对应打印机型号的驱动也可以,都行。 运行 exe 会进行解压,生成文件:
运行 DriverWizard.exe 才开始安装驱动,注意选择正确打印机的型号。
根据自己的场景选择,下面演示通过网络的配置方式:选择对应的打印机型号。(如果是 USB 方式会自动匹配出打印机)
2.2 配置 ip 端口
创建端口后,再指定这个端口:
2.3 若配置的端口不对可删除端口重新配置。【非必须】
若无法删除端口,则作如下操作: 运行:services.msc 停掉 Print Spooler 服务后,运行 spool 删除 PRINTERS 文件夹后,再启动 Print Spooler 服务,再对端口删除就可以了。
三、打印操作:
3.1 打印测试页、一般文档或图片打印:
按需设置纸张规格:(这个设置的效果不一定好,可作联通性测试。纸张规格可能会影响最终打印效果,以实际为准)
至此,说明我们的电脑与打印机已是可连通状态。 如果不通,自行 ping 一下打印机 ip,是否能通,解决网络问题后再作尝试。
3.2 打印微服务:
a. 资源包:启动微服务。
b. 获取打印列表(浏览器或 Postman):
http://127.0.0.1:8181/print-proxy/
c. 打印本地图片(浏览器或 Postman):
d. ZPL 打印:(需用 Postman 使用 Post 请求,参数要求放置在 body 的 form-data 下)
另外: 在线调试网址(预览样式):(有时候不一定准确,得看打印机具体适配情况) https://labelary.com/viewer.html
3.3 BarTender 打印:
快速设计,解决临时性打印需求。
四、打印服务集成到 MES
4.1 主要页面效果
V52 的打印模板存在问题,传动项目已修复。
在传动项目中做了一个页面,来做条码打印,操作方式是:传参条码内容和选择打印机。
效果:
打印模板的 ZPL 指令内容:
^XA
^LH5,20
^CF0,135,210
^TBN,870,500
^FO50,50^FD#barcode#^FS
^XZ
4.2 核心代码:
a. 前端:
// YB-打印相关方法~~~~
// 获取打印机集合
getPrinters() {
this.$http.post("ubcPrintController!getServicePrinterList.m").then((res) => {
this.printersList = res;
this.printBarForm.printer = this.optionData(res);
});
},
// 打开打印对话框
goPrintDialog() {
// this.printBarForm.barCode = "";
// this.printBarForm.printer = ;
// 从缓存里取打印机,避免重复操作
this.printBarForm.printer = window.localStorage.getItem("printerSelected") == null ? "" : window.localStorage.getItem("printerSelected");
this.isPrintFlag = true;
},
// 关闭打印对话框
doCacel() {
this.$refs["printBarForm"].resetFields();
this.isPrintFlag = false;
},
// 提交打印
submitPrint() {
if (!this.printBarForm.barcode) {
this.$message({
message: "打印条码不能为空!",
type: "warning",
});
return false;
}
// 将需转换打印模板的变量进行封装
let zplValJson = {
"barcode": this.printBarForm.barcode,
};
let data = qs.stringify({
printer: this.printBarForm.printer,
printTemplate: "UBC_TEMPLET_REPORT-20231212-1713", // 此处固定这个打印模板
zplValJson: JSON.stringify(zplValJson),
});
this.$http.post("ubcPrintController!printBarcodeYB.m", data).then((res) => {
if (res.type == "success") {
this.$message({
message: res.data,
type: res.type,
});
// 保存选择的打印机到浏览器缓存,避免下次重复操作
window.localStorage.setItem('printerSelected', this.printBarForm.printer);
this.isPrintFlag = false;
this.doCacel();
} else {
this.$message({
message: res.data,
type: res.type,
});
}
});
},
b. 后端:
/**
* @MethodName: printBarcodeYB
* @Description: 打印zpl-调用时传参打印模板编码、打印变量(map结构,非写死,便于后期对打印模板的变量调整)、打印机名称
* @Param printTemplateCode 打印模板编码
* @Param zplValJo 打印变量jsonObject
* @Param printer 打印机名称
* @Return void
* @Author: yuanbao
* @Date: 2023/12/13
**/
public void printBarcodeYB(String printTemplateCode, JSONObject zplValJo, String printer)
{
// 1、数据预处理
JSONArray jsonArray = new JSONArray();
jsonArray.add(zplValJo);
List<UbcTempletReport> templetReportList = this.getDao().find(UbcTempletReport.class, new String[]{"code", "isDelete"}, new Object[]{printTemplateCode, UmaConstantUtil.IS_DELETE_NO});
if (CollectionUtils.isEmpty(templetReportList))
{
throw new MestarException("打印模板编码为:" + printTemplateCode + " 未找到对应打印模板,请检查!");
}
UbcTempletReport ubcTempletReport = templetReportList.get(0);
// 2、获取文本换后zplText
List<Map<String, String>> list = self.getCommonText(ubcTempletReport.getId(), jsonArray);
// 3、开始打印
for (Map<String, String> map : list)
{
String zplText = map.get("zplText");
String zpl = zplText.trim();
if (!(zpl.startsWith("^XA") || zpl.startsWith("~DG")) || !zpl.endsWith("^XZ"))
{
throw new MestarException("打印模板编码为:" + printTemplateCode + ",ZPL语句格式不正确,请检查!");
}
String returnMsg = ZplPrint(zpl, printer);
if (!"success".equals(returnMsg))
{
throw new MestarException(returnMsg);
}
}
}
/**
* @MethodName: printBarcodeYB
* @Description: 通过工位,打印zpl-调用时传参打印模板编码、打印变量(map结构,非写死,便于后期对打印模板的变量调整)、工位编码
* @Param printTemplateCode 打印模板编码
* @Param zplValJo 打印变量jsonObject
* @Param cellCode 工位编码
* @Return void
* @Author: yuanbao
* @Date: 2023/12/13
**/
public void printBarcodeByCellYB(String printTemplateCode, JSONObject zplValJo, String cellCode)
{
// 1、数据预处理
JSONArray jsonArray = new JSONArray();
jsonArray.add(zplValJo);
List<UbcTempletReport> templetReportList = this.getDao().find(UbcTempletReport.class, new String[] { "code", "isDelete" }, new Object[] { printTemplateCode, UmaConstantUtil.IS_DELETE_NO });
if (CollectionUtils.isEmpty(templetReportList))
{
throw new MestarException("打印模板编码为:" + printTemplateCode + " 未找到对应打印模板,请检查!");
}
UbcTempletReport ubcTempletReport = templetReportList.get(0);
// 2、获取文本换后zplText
List<Map<String, String>> list = self.getCommonText(ubcTempletReport.getId(), jsonArray);
// 3、由工位转换得到对应的打印机名称:通过自定义档案
Map<String, MbbUdiBO> udiMap = udiImpl.getUdiBOMap(IMOMPrjConstants.UDI_TYPE_PRINTER_ADDRESS, 2);
if (CollectionUtils.isEmpty(udiMap))
{
throw new MestarException("打印机列表未找到!自定义项类型中未维护编码为:" + IMOMPrjConstants.UDI_TYPE_PRINTER_ADDRESS + " 的类型,请维护!");
}
String hql = "select u from MbbUdi u left join u.udiType ut where ut.code=:udiTypeCode and ut.isDelete=0 and u.shortName like '%" + cellCode + "%' and u.isDelete=0";
List<MbbUdi> udiList = this.getDao().createQuery(hql).setParameter("udiTypeCode", IMOMPrjConstants.UDI_TYPE_PRINTER_ADDRESS).list();
if (CollectionUtils.isEmpty(udiList))
{
throw new MestarException("打印机未找到!自定义档案中(打印机列表的类型下),未维护工位为:" + cellCode + " 的打印机,请维护!");
} else if (udiList.size() > 1)
{
MestarLogger.error("工位为:" + cellCode + " 的打印机,找到了" + udiList.size() + "台!应注意维护准确,现随机取其中一台!");
}
MbbUdi udi = udiList.get(0);
String printer = udi.getName(); // udi的名称放打印机名称,简称放工位编码拼接
// 4、开始打印
for (Map<String, String> map : list)
{
String zplText = map.get("zplText");
String zpl = zplText.trim();
if (!(zpl.startsWith("^XA") || zpl.startsWith("~DG")) || !zpl.endsWith("^XZ"))
{
throw new MestarException("打印模板编码为:" + printTemplateCode + ",ZPL语句格式不正确,请检查!");
}
String returnMsg = ZplPrint(zpl, printer);
if (!"success".equals(returnMsg))
{
throw new MestarException(returnMsg);
}
}
}
五、待完善部分:
用自定义档案维护打印机名称和工位(多个工位编码逗号拼接,不可重叠)的关联关系。
提供方法,传参:打印模板编码、打印 json 数据(map)、工位编码。
从而准确匹配到对应网络打印机进行打印。
后已实现:
代码:
/**
* @MethodName: printBarcodeYB
* @Description: 通过工位,打印zpl-调用时传参打印模板编码、打印变量(map结构,非写死,便于后期对打印模板的变量调整)、工位编码
* @Param printTemplateCode 打印模板编码
* @Param zplValJo 打印变量jsonObject
* @Param cellCode 工位编码
* @Return void
* @Author: yuanbao
* @Date: 2023/12/13
**/
public void printBarcodeByCellYB(String printTemplateCode, JSONObject zplValJo, String cellCode)
{
// 1、数据预处理
JSONArray jsonArray = new JSONArray();
jsonArray.add(zplValJo);
List<UbcTempletReport> templetReportList = this.getDao().find(UbcTempletReport.class, new String[] { "code", "isDelete" }, new Object[] { printTemplateCode, UmaConstantUtil.IS_DELETE_NO });
if (CollectionUtils.isEmpty(templetReportList))
{
throw new MestarException("打印模板编码为:" + printTemplateCode + " 未找到对应打印模板,请检查!");
}
UbcTempletReport ubcTempletReport = templetReportList.get(0);
// 2、获取文本换后zplText
List<Map<String, String>> list = self.getCommonText(ubcTempletReport.getId(), jsonArray);
// 3、由工位转换得到对应的打印机名称:通过自定义档案
Map<String, MbbUdiBO> udiMap = udiImpl.getUdiBOMap(IMOMPrjConstants.UDI_TYPE_PRINTER_ADDRESS, 2);
if (CollectionUtils.isEmpty(udiMap))
{
throw new MestarException("打印机列表未找到!自定义项类型中未维护编码为:" + IMOMPrjConstants.UDI_TYPE_PRINTER_ADDRESS + " 的类型,请维护!");
}
String hql = "select u from MbbUdi u left join u.udiType ut where ut.code=:udiTypeCode and ut.isDelete=0 and u.shortName like '%" + cellCode + "%' and u.isDelete=0";
List<MbbUdi> udiList = this.getDao().createQuery(hql).setParameter("udiTypeCode", IMOMPrjConstants.UDI_TYPE_PRINTER_ADDRESS).list();
if (CollectionUtils.isEmpty(udiList))
{
throw new MestarException("打印机未找到!自定义档案中(打印机列表的类型下),未维护工位为:" + cellCode + " 的打印机,请维护!");
} else if (udiList.size() > 1)
{
MestarLogger.error("工位为:" + cellCode + " 的打印机,找到了" + udiList.size() + "台!应注意维护准确,现随机取其中一台!");
}
MbbUdi udi = udiList.get(0);
String printer = udi.getName(); // udi的名称放打印机名称,简称放工位编码拼接
// 4、开始打印
for (Map<String, String> map : list)
{
String zplText = map.get("zplText");
String zpl = zplText.trim();
if (!(zpl.startsWith("^XA") || zpl.startsWith("~DG")) || !zpl.endsWith("^XZ"))
{
throw new MestarException("打印模板编码为:" + printTemplateCode + ",ZPL语句格式不正确,请检查!");
}
String returnMsg = ZplPrint(zpl, printer);
if (!"success".equals(returnMsg))
{
throw new MestarException(returnMsg);
}
}
}
六、调试过程问题
6.1 ZPL 打印机通过打印微服务调用时,报错:
java.lang.IllegalArgumentException: Invalid character found in the request target.
** 问题原因:**url 中带有特殊字符,如花括号冒号 {:^~| 等等。
解决:
1、去除 url 中的特殊字符;
2、不用浏览器请求,改用 post 请求。(使用 PostMan 工具)且将参数信息不要放在 Params 里,而是放在 Body 的 form-data 里。(亲测可用)
3、其他方式:改 tomcat 的配置, 在 conf/catalina.properties 添加或修改:tomcat.util.http.parser.HttpParser.requestTargetAllow=|{} 然后重启即可
6.2 问题:自动换行后有自动补在行末尾的符号。
和厂家沟通后:可能打印机对 ZPL 指令中的 FB 不太行,换成 TB 指令。
命令块:
^XA
^LH5,20
^FO50,50
^CF0,135,210
^TBN,870,500
^FD2400010-PB5C/E-J016^FS
^XZ
效果:
6.3 问题:打印中文乱码问题
YQF- 后端代码对中文作 byte 数组转化处理。要求命令格式满足正则:
\\^FO([0-9.]*),([0-9.]*)(\\^A(.*?)([NRIB]),([0-9.]*),([0-9.]*))\\^FD(.*?)\\^FS
命令块:
^XA
^LH5,20
^CF0,135,210
^TBN,900,500
^FO50,50^A0N,5,5^FD#barcode#^FS
^XZ
这样即可打印中文了。
但又出现换行问题。只能通过排版解决。
^XA
^LH5,20
^CF0,135,210
^TBN,900,500
^FO50,50^A0N,3,5^FD艾普工华^FS
^FO50,150^A0N,7,7^FD杨超^FS
^FO50,350^A0N,3,5^FD15871739924^FS
^XZ
暂时不需要中文,未启用此模板。
另外,HoneyWell 打印机支持在线上传字体到打印机,然后在 zpl 指令中使用该字体即可:(前提先自行上传字体到打印机)
^XA
^CWA,E:simsun.ttf
^CI28
^FO50,300^AA,64,64^FD日期:2023年12月15日^FS
^XZ
七、其他
7.1 Code 128 条码
也称作条形码。
样式:
ZPL 命令块:
^XA
^FO150,150^BY3
^BCN,200,Y,N,N
^FDbarcode123-TEST^FS^PQ1
^XZ
7.2 QR Code 条码
也称作二维码。
样式:
ZPL 命令块:
^XA
^FO100,100
^BQN,2,10
^FDTEST-ABC123^FS
^XZ