软件版本:
阅读坑记需谨慎,版本要看仔细。
"node":"v14.16.0",
"electron": "^13.0.0",
//当然本文问题和vue 没毛线关系
"vue": "^2.6.14",
//遇到坑的主角是这个
"electron-edge-js": "^14.16.1",
// 打包用到是该工具是 electron-builder,我想我遇到的坑应该和打包工具没有啥关系,但还是列出来,便于交流
几年前,直接使用IE 调用相关的插件执行打印,可,IE在win11已经找不到入口了,需要单独设置edge浏览器,且使用的人,经常“不小心”重置网络的安全,导致检查需要远程帮他们开启,故此需要个桌面软件,一次解决,永不烦人。
遇到的问题
经过查看tsc打印机官网,找到了SDK下载到入口tsc官方SDK下载地址(有些人很贱,从官网下载了资料,上传到其他收费平台上去“赚钱”);找到了nodejs相关的样例。
nodejs的样例,需要用到edge-js
,但经过多日的入坑,找到的资料是使用:electron-edge-js
;
用electron-edge-js
参考官方的edge-js
样例,确实可以打印,但是:
如果循环打印大量数据,会卡死。
最佳解决办法(2023-08-11)[适用于 win系统其他未试]
从TSC中国官网资料下载网址https://www.chinatsc.cn/zh-CN/downloads打开折叠项软件开发工具包(SDK)
,往下翻找,找到TSC PrintServer
下载之后是一个win下的安装包,安装之后,在安装目录下又调用案例。
是需要使用WebSocket
进行通信。官方的这个打印服务和我此文末尾的python开启打印服务
异曲同工,官方都开发好了,为什么不直接用。
不熟悉WebSocket
也没关系,找资料看看,试几次就会用了。
使用方法:(熟悉WebSocket
的跳过)结合官方案例,及下列两个代码tsc服务连接.js
、打印数据生成.js
(中文文件名仅为了此容易读[零级英文自己读着麻烦])
//一、 调用 `打印数据生成.js`里的方法生成想要的指令数据
TSCFunc.windowsfont(item.printVal.x, item.printVal.y, item.printVal.fontheight, item.printVal.rotation, item.printVal.fontstyle, item.printVal.fontunderline, szFaceName, item.printVal.content);
//二、把生成的打印指令放到一个对象的`functions_inorder`属性里
let print_cmd = {functions_inorder:TSCFunc.functions_inorder};
//三、发送打印指令
//我此处是 `tsc服务连接.js` 文件里的封装,已提前连接了打印服务;如果未连接打印服务,参考官方例子连接打印服务执行打印
TSCWS.wsSendMsg(print_cmd);
tsc ws 服务连接代码:tsc服务连接.js
export default {
/**
* 基础配置
*/
conf:{
ip: '127.0.0.1',
port: '8888'
},
/**
* 状态数据
*/
state:{
/**
* 与打印服务链接状态
*/
connectionStatus: false,
/**
* 打印机状态
*/
printerState: null,
/**
* 打印机名
*/
printerName: null,
/**
* 打印机列表
*/
printerInfoList: [],
/**
* 异常信息
*/
errmsg:{
message: null,
code: null
}
},
/**
* 实例化的 WebSocket
*/
ws: null,
/**
* 初始
*/
init(ip = null,port = null){
const self = this;
if (ip) {
this.conf.ip = ip
}
if (port) {
this.conf.port = port
}
if (!this.state.connectionStatus) {
const {ip,port} = this.conf;
let url = `ws://${ip}:${port}`;
let xws = null;
this.ws = xws = new WebSocket(url);
// xws.OPEN
xws.onopen = function (event) {
console.log('TSC ws 服务已连接',event);
self.state.connectionStatus = true;
self.getPrinter();
};
xws.onmessage = function (event) {
console.log(event);
if(event.data == "Finished"){
// console.log(event.data);
self.state.errmsg.message = '完成';
return false
}
try {
const obj = JSON.parse(event.data);
if ('usb_list' in obj ){
self.state.printerInfoList = [];
if(obj.usb_list.length > 0){
for (let i = 0; i < obj.usb_list.length; i++) {
self.state.printerInfoList.push({
path:obj.usb_list[i].USBPath,//base64
name: obj.usb_list[i].USBName
});
}
}
}else if ('Function_Failed' in obj){
self.state.errmsg.message = `Failed: ${obj.Function_Failed}`;
}else if ('printerstatus' in obj){
if(obj.printerstatus.length != 0){
self.state.printerState = obj.printerstatus.join("\r\n");
}
}else if ('printername' in obj){
self.state.printerName =obj.printername;
}
} catch (e) {
self.state.errmsg.message = `打印服务数据解析未知异常:${e.message}`;
}
};
xws.onclose = function (event){
self.state.connectionStatus = false;
self.state.errmsg.code = event.code;
// 此处的异常描述是我用软件反应官方,然后根据翻译结果结合我的理解乱整的,不要当真
if (event.code != 1000) {
if(event.code == 1001) {
self.state.errmsg.message = "打印服务连接丢失";
}else if(event.code == 1002) {
self.state.errmsg.message = "由于协议错误,打印服终止连接";
}else if(event.code == 1003) {
self.state.errmsg.message = "数据类型错误,打印服终止连接";
}else if(event.code == 1004) {
self.state.errmsg.message = "保留状态代码";
}else if(event.code == 1005) {
self.state.errmsg.message = "无状态代码";
}else if(event.code == 1006) {
self.state.errmsg.message = "连接异常关闭";
}else if(event.code == 1007) {
self.state.errmsg.message = "打印服终止连接,发送的消息与服务要求不一致(utf-8)";
}else if(event.code == 1008) {
self.state.errmsg.message = "打印服终止连接,消息可能违法规范";
}else if(event.code == 1009) {
self.state.errmsg.message = "打印服终止连接,数据太大无法处理";
}else if(event.code == 1010) {
self.state.errmsg.message = `打印服务未响应扩展,需要的扩展包括:${event.reason}`;
}else if(event.code == 1011) {
self.state.errmsg.message = "打印服务终止连接,遇到了意外阻止它完成请求";
}else if(event.code == 1015) {
self.state.errmsg.message = "打印服务终止连接,无法执行TLS握手";
}else{
self.state.errmsg.message = "未知原因";
}
}else{
self.state.errmsg.message = '';
}
};
xws.onerror = function (error){
self.state.errmsg.message = `打印服务连接未知异常:${error.message}`
};
}
},
/**
* 发送消息
*/
wsSendMsg(data){
if (typeof data != "string") {
data = JSON.stringify(data);
}
this.state.errmsg.message = '指令发送';
this.ws.send(data)
},
/**
* 关闭打印服务连接
*/
closeConn(){
if (this.ws || this.state.connectionStatus) {
this.ws.close();
this.state.errmsg.message = '关闭连接';
this.state.connectionStatus = false;
}
},
/**
* 读取打印机
*/
getPrinter(){
this.state.errmsg.message = '获取打印机列表';
this.wsSendMsg({"usb_list": []});
},
/**
* 获取连接状态
*/
getConnectionStatus(){
return this.state.connectionStatus;
},
/**
* 获取状态信息里的打印机信息
* @returns
*/
getStatePrinter(){
return this.state.printerInfoList;
}
}
生成打印数据代码(根据官方简单修改):打印数据生成.js
export default {
functions_inorder:[],
/**
* 清空数据
*/
init(){
this.functions_inorder=[];
},
/**
* 连接打印机USB
* @param {string} usbPrinter usb路徑(USBPath),若参数为空字串{"openport_usb" :""}则自动连接找到的第一台打印机
*/
openport_usb(usbPrinter){
this.functions_inorder.push({"openport_usb" : usbPrinter});
},
/**
* 连接打印机网络
* @param {String} ip: 打印机IP地址
* @param {String} port: 打印机网络端口
*/
openport_net(ip, port){
this.functions_inorder.push({"openport_net" : ip + "," + port});
},
/**
* 中断打印机连线
*/
closeport(){
this.functions_inorder.push({"closeport" : ""});
},
/**
* 清除打印机Buffer
*/
clearbuffer(){
this.functions_inorder.push({"clearbuffer" : ""});
},
/**
* 设定标签资讯
* @param {String} width_mm: 标签宽度(单位mm)
* @param {String} height_mm: 标签高度(单位mm)
*@param {String} speed: 打印速度
*@param {String} density: 打印浓度
*@param {String} sensor: 標籤種類["0":GAP, "1":BLINE]
*@param {String} gap_height: 间隙高度(单位mm)
*@param {String} gap_offset: 间隙偏移(单位mm)
*/
setup(width_mm, height_mm, speed, density, sensor, gap_height, gap_offset){
this.functions_inorder.push({"setup" : width_mm + "," + height_mm + "," +speed + "," +density + "," +sensor + "," +gap_height + "," +gap_offset});
},
/**
* 打印条码
* @param {String} x: 条码x轴座标(单位dot)
* @param {String} y: 条码y轴座标(单位dot)
* @param {String} type: 条码类型["128", "128M", "EAN128", "25", "25C", "39", "39C", "93", "EAN13", "EAN13+2", "EAN13+5", "EAN8", "EAN8+2", "EAN8+5", "CODA", "POST", "UPCA", "UPCA+2", "UPCA+5", "UPCE", "UPCE+2", "UPCE+5", "CPOST", "MSI", "MSIC", "PLESSEY", "ITF14", "EAN14", "11", "TELEPEN", "TELEPENN", "PLANET", "CODE49", "DPI", "DPL"]
* @param {String} height: 条码高度
* @param {String} readable: 条码可读性["0":无, "1":置左, "2":置中, "3":置右]
* @param {String} rotation: 条码旋转["0":无, "90":90度, "180":180度, "270":270度]
* @param {String} narrow: 条码窄元件宽度[详见TSPL手册BARCODE条目]
* @param {String} wide: 条码宽元件宽度[详见TSPL手册BARCODE条目]
* @param {String} code: 条码内容
*/
barcode(x, y, type, height, readable, rotation, narrow, wide, code){
if(code.charAt(0) == '@')
this.functions_inorder.push({"barcode" : x + "," + y + ",\"" + type + "\"," + height + "," + readable + "," + rotation + "," + narrow + "," + wide + "," + code });
else
this.functions_inorder.push({"barcode" : x + "," + y + ",\"" + type + "\"," + height + "," + readable + "," + rotation + "," + narrow + "," + wide + ",\"" + code + "\"" });
},
/**
* 打印文字
* @param {String} x: 文字x轴座标(单位dot)
* @param {String} y: 文字y轴座标(单位dot)
* @param {String} fonttype: 内建字型名称["0", "1", "2", "3", "4", "5", "6", "7", "8", "ROMAN.TTF"] 或 下载字型名称
* @param {String} rotation: 文字旋转["0":无, "90":90度, "180":180度, "270":270度]
* @param {String} xmul: 文字x轴放大率
* @param {String} ymul: 文字y轴放大率
* @param {String} text: 打印文字
*/
printerfont(x, y, fonttype, rotation, xmul, ymul, text){
if(text.charAt(0) == '@')
this.functions_inorder.push({"printerfont" : x + "," + y + ",\"" +fonttype + "\"," + rotation + "," +xmul + "," + ymul + "," + text});
else
this.functions_inorder.push({"printerfont" : x + "," + y + ",\"" +fonttype + "\"," + rotation + "," +xmul + "," + ymul + ",\"" + text + "\"" });
},
/**
* 发送指令到打印机
* @param {String} cmd: 指令
*/
sendcommand(cmd){
this.functions_inorder.push({"sendcommand" : cmd});
},
/**
* 发送指令到打印机,加上换行符号(\r\n)
* @param {String} cmd: 指令
*/
sendcommand_crlf(cmd){
this.functions_inorder.push({"sendcommand_crlf" : cmd});
},
/**
* 打印标签
* @param {String} sets: 份数
* @param {String} copies: 张数
*/
printlabel(sets, copies){
this.functions_inorder.push({"printlabel" : sets + "," + copies});
},
/**
* 下载PCX
* @param {String} file_path: 档案路径
* @param {String} image_name: 保存名称
*/
downloadpcx(file_path, image_name){
this.functions_inorder.push({"downloadpcx" : file_path + "," + image_name});
},
/**
* 跳过下一张标签
*/
formfeed(){
this.functions_inorder.push({"formfeed" : ""});
},
/**
* 关闭backfeed功能
*/
nobackfeed(){
this.functions_inorder.push({"nobackfeed" : ""});
},
/**
* 打印文字
* @param {String} x: 文字x轴座标(单位dot)
* @param {String} y: 文字y轴座标(单位dot)
* @param {String} fontheight: 文字大小
* @param {String} rotation: 文字旋转["0":无, "90":90度, "180":180度, "270":270度]
* @param {String} fontstyle: 文字样式["0":无, "1":斜体, "2":粗体, "3":斜体+粗体]
* @param {String} fontunderline: 文字底线["0":无, "1":底线]
* @param {String} szFaceName: 字型名称[例:"Arial"]
* @param {String} text: 打印文字
*/
windowsfont(x, y, fontheight, rotation, fontstyle, fontunderline, szFaceName, text) {
this.functions_inorder.push({"windowsfont" : x + "," + y + "," +fontheight + "," + rotation + "," + fontstyle + "," +fontunderline + "," + szFaceName + "," + text});
},
// /**
// * 发送位元组数据到打印机
// * @param {String} uint8_arr 指令[拷贝原文档,不知道描述类型是否正确]
// */
// sendUint8Array(uint8_arr){
// // this.functions_inorder.push({"sendUint8Array" : Base64.fromUint8Array(uint8_arr)});//官方有Base64相关的文件我用不到(不懂)此用法,所以注释为修改
// },
/**
* 返回打印机状态
*/
printerstatus(){
this.functions_inorder.push({"printerstatus" : ""});
},
/**
* 返回打印机名称
*/
printername(){
this.functions_inorder.push({"printername" : ""});
},
/**
* 返回打印机序号
*/
printerserial(){
this.functions_inorder.push({"printerserial" : ""});
}
}
卡死的调试场景
- 渲染进程的打印页面(组件)循环 执行(调用)
tsclibnet.dll
打印方法;结果:页面卡白了。 - 我把对
tsclibnet.dll
是调用、循环打印,新开启一个窗口来执行;结果,该新开的窗口,如果打印数量超过3条同样还是卡死了。 - 放在“主进程”来执行:通过渲染进程(页面)向主进程发送数据信息,主进程执行数据的循环实施打印,结果还是卡死。
以上是我用electron-edge-js
调用tsclibnet.dll
遇到的问题,我不确定是不是因为我执行打印代码写的太垃圾,而导致卡死,或者我对electron
了解太少,垃圾代码贴出,便于交流:
(此代码不要直接全部复制粘贴使用,我有删改,不保证可执行)
//关于库方法的说明,我在官方的站点中有pdf中文文档
//为了方便交流,我找到一个html版本,方便阅读的 https://www.e-learn.cn/topic/3539545 (以官方为准,此仅方便交流)
var openport = edge.func({
assemblyFile: './tsc_dll/tsclibnet.dll',
typeName: 'TSCSDK.node_driver',
methodName: 'openport'
});
//省略了一堆官方样例给到的如上动态库引入方法
// windowsfont sendcommand barcode sendcommand printlabel 都省略了,看官方样例
let data = [
[{
//文本类坐标信息
"type": "text",
"printVal": {
"x": 0,
"y": 11,
"fontheight": 40,
"rotation": 0,
"fontstyle": 0,
"fontunderline": 0,
"szFaceName": "黑体",
"content": "打印内容1"
}
}, {
"type": "qrcode",
"printVal": "QRCODE 102,364,H,5,A,0,M2,S5,\"https://www.baidu.com\""
}
//此处省略了n组如上结构数据
]
//此处也省略了n组如上数据
];
// data的解释
// data的第一纬:出纸的总张数,即循环一次完,打印机打印完成,出一次纸
// data的第二纬度:每个需要打印内容和动态库相应方法匹配,
openport('打印机名或网络地址', true);
// 打印机相关配置
setup(conf,true);
for (let i = 0; i < data.length; i++) {
clearbuffer('', true);
let reverseArr = [];
data[i].forEach(ele => {
// 文本类型处理
if (ele.type == 'text') {
windowsfont(ele.printVal, true);
} else if (ele.type == 'qrcode'){// 二维码类处理
sendcommand(ele.printVal, true);
} else if (ele.type == 'barcode'){// 条码类处理
barcode(ele.printVal, true);
} else if (ele.type == 'reverse'){// 反相打印(黑底白字)类处理
// 此处为什么不直接执行sendcommand()执行打印命令?
// 因为反相打印如果在文字之前执行命令,只会打印一个黑块,没有字;如果需要黑底白字,就要放在所有指令的最后执行
if (Array.isArray(ele.printVal) && ele.printVal.length > 0) {
reverseArr.push.apply(reverseArr, ele.printVal);
}
}
});
//
if (reverseArr.length > 0) {
for (let ri = 0; ri < reverseArr.length; ri++) {
sendcommand(reverseArr[ri], true);
}
}
let label_variable = { quantity: '1', copy: '1' };
// 此方法的参数的含义,我没理解透
printlabel(label_variable, true);
}
// 关闭打印机
closeport('', true);
以上就是我执行打印,页面卡死的代码。
解决方法尝试
思来想去,翻来覆去搜索资料,开启系统任务管理器,打印的时候CPU占用突增,是不是进程资源占用不符合系统规则,系统弄死该进程呢?如何重新开启进程?略熟悉的语言也只有js?
nodejs 好像可以开启临时web服务,我把数据发送到另外一个软件上执行,对吧。且还可以打包exe可执行文件->应该可以完美解决打印:
对,可行,没有卡死,打印流程完美按照我的想法工作;(在本地 执行 node server.js
的时候服务完全正常启动,打印正常完美执行;)
打印执行打循环体,还是上述代码;web 服务我用node的框架 express
(完全不懂,参照网络样例使用)
package.json 文件
{
"name": "print_exe",
"version": "0.0.1",
"description": "执行打印服务",
"main": "server.js",
"author": {
"name": "programmer"
},
"dependencies": {
// 重新自己安装,根据自己的版本来
"edge-js": "^18.4.0",
"express": "^4.18.1"
},
"pkg": {
"scripts": "build/**/*.js",
"assets": "views/**/*",
"targets": [
"node14-win-x64"
],
"outputPath": "dist"
}
}
server.js
// 'use strict';
const fs = require('fs');
const crypto = require('crypto');
const path = require ('path');
var edge = require('edge-js');
var express = require('express');
var bodyParser = require('body-parser')
var app = express();
var urlencodedParser = bodyParser.urlencoded({ extended: false });
var urlencodedJsonParser = bodyParser.json();
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(express.static('./'));
app.listen(5000, function () {
console.log("Server Start!!");
log("Server Start!!");
})
// 接受打印数据方法入口
app.post('/printing', urlencodedJsonParser, (req, res) =>{
// 数据合理性验证
// 节约页面,省去我的垃圾代码
res.json({code:0,message:'打印执行中请等待',data:null});
printfile(req.body);
});
var openport;
var setup;
var about;
var sendcommand;
var clearbuffer;
var printerfont;
var barcode;
var printlabel;
var closeport;
var sendcommand_utf8;
var sendcommand_binary;
var windowsfont;
try {
openport = edge.func({
assemblyFile: 'tsclibnet.dll',
typeName: 'TSCSDK.node_driver',
methodName: 'openport'
});
setup = edge.func({
assemblyFile: 'tsclibnet.dll',
typeName: 'TSCSDK.node_driver',
methodName: 'setup'
});
about = edge.func({
assemblyFile: 'tsclibnet.dll',
typeName: 'TSCSDK.node_driver',
methodName: 'about'
});
sendcommand = edge.func({
assemblyFile: 'tsclibnet.dll',
typeName: 'TSCSDK.node_driver',
methodName: 'sendcommand'
});
clearbuffer = edge.func({
assemblyFile: 'tsclibnet.dll',
typeName: 'TSCSDK.node_driver',
methodName: 'clearbuffer'
});
printerfont = edge.func({
assemblyFile: 'tsclibnet.dll',
typeName: 'TSCSDK.node_driver',
methodName: 'printerfont'
});
barcode = edge.func({
assemblyFile: 'tsclibnet.dll',
typeName: 'TSCSDK.node_driver',
methodName: 'barcode'
});
printlabel = edge.func({
assemblyFile: 'tsclibnet.dll',
typeName: 'TSCSDK.node_driver',
methodName: 'printlabel'
});
closeport = edge.func({
assemblyFile: 'tsclibnet.dll',
typeName: 'TSCSDK.node_driver',
methodName: 'closeport'
});
sendcommand_utf8 = edge.func({
assemblyFile: 'tsclibnet.dll',
typeName: 'TSCSDK.node_driver',
methodName: 'sendcommand_utf8'
});
sendcommand_binary = edge.func({
assemblyFile: 'tsclibnet.dll',
typeName: 'TSCSDK.node_driver',
methodName: 'sendcommand_binary'
});
windowsfont = edge.func({
assemblyFile: 'tsclibnet.dll',
typeName: 'TSCSDK.node_driver',
methodName: 'windowsfont'
});
}catch (error) {
log(error,'error');
}
// 日志处理方法
function log(logContent,type='info') {
//省去垃圾代码,
}
/**
* 执行打印
* @param {Object} printData 打印数据
*/
function printfile(printData){
try {
if (! printData.hasOwnProperty('print_name')) {
throw "缺少打印机";
}
if (! printData.hasOwnProperty('printer_config')) {
throw "缺少打印纸配置";
}
if (! printData.hasOwnProperty('print_data')) {
throw "缺少打印数据";
}
const {print_name,printer_config,print_data} = printData;
let {width,height,speed,consistence,sensor,spacing,offsetDistance,column} = printer_config;
// 传入的单位为cm 此处是单位处理
width = width * 10;
height = height * 10;
spacing = spacing * 10;
// 总宽 mm
let totalWidth = width * column + spacing * (column-1);
// 打印纸配置数据
const conf = {
width: Math.ceil(totalWidth).toString(),//a
height: Math.ceil(height).toString(),//b
speed: speed.toString(),//c
density: consistence.toString(),//d
sensor: sensor.toString(),//e
vertical: spacing.toString(),//f vertical
offset: offsetDistance.toString(),//g
};
for (let i = 0; i < print_data.length; i++) {
openport(print_name, true);
setup(conf,true);
clearbuffer('', true);
sendcommand('DIRECTION 1');
let reverseArr = [];
print_data[i].forEach(ele => {
log(ele.printVal);
if (ele.type == 'text') {
windowsfont(ele.printVal, true);
} else if (ele.type == 'qrcode'){
sendcommand(ele.printVal, true);
} else if (ele.type == 'barcode'){
barcode(ele.printVal, true);
} else if (ele.type == 'reverse'){
if (Array.isArray(ele.printVal) && ele.printVal.length > 0) {
reverseArr.push.apply(reverseArr, ele.printVal);
}
}
});
if (reverseArr.length > 0) {
for (let ri = 0; ri < reverseArr.length; ri++) {
sendcommand(reverseArr[ri], true);
}
}
let label_variable = { quantity: '1', copy: '1' };
printlabel(label_variable, true);
// 关闭打印机
closeport('', true);
log('打印关闭');
}
} catch (error) {
// console.log(error);
log(error,'error');
}
}
但是!但是!打包后的exe文件的启动错误问题让我无法解决:
Error: Module did not self-register: '\\?\E:\a\node_modules\edge-js\lib\native\win32\x64\14.19.3\edge_nativeclr.node'.
打包工具是 pkg
网络上查找了原因,说需要重新针对自己的node版本打包动态库!不想再去研究了,项目需要使用(向老板汇报)。
最终解决方法
之前简单了解过Python 且也尝试过Python 调用该库,(很早之前记录的踩坑记 Electron-vue开发桌面应用调用TSCLIB.dll)
只是之前 通过发送命令调用Python打包的 exe 程序;这次用Python的exe启动了一个web服务,这样用http方式发送打印数据到该服务器即可。
打印数据结构和循环体的逻辑,和最早代码的逻辑一致:
特别注意:Python 的 TSCLIB.dll
动态库,和nodejs的不一致,去前文给到的官方地址下载Python 的例子,另外Python样例的动态库参数数据类型 没有描述,参照 java 样例的代码可以知道参数类型。
我遇到的问题,都写在代码注释中:(我的TSCLIB.dll
库和.py
在同级)
# coding=utf-8
from flask import Flask,request,json,jsonify
# import ctypes
from ctypes import *
import logging
import os
import datetime
from time import strftime
# import chardet
app = Flask(__name__)
'''
是否是开发
True-开发
False-生产
'''
# develop = False
# 应用执行目录,该应用存在于调用应用下的地址
appExePath = ""
@app.route('/')
def hello_world():
# 用于应用检测是否可以正常访问
return 'Hello World'
@app.route('/printing',methods=['POST'])
def exeprint():
app.logger.info('info log')
p_ddata = request.get_data()
data = json.loads(p_ddata)
# 的数据格式完全和最开始体到代码 data 数据格式一致
print_data = data['print_data']
# 打印机名
printer_name = data['print_name']
# 打印机相关配置
printer_config = data['printer_config']
column = printer_config['column']
width = printer_config['width'] * 10
height = printer_config['height'] * 10
vertical = printer_config['spacing'] * 10
totalWidth = width * column + vertical * (column-1)
speed = printer_config['speed']
density = printer_config['consistence']
sensor = printer_config['sensor']
offset = printer_config['offsetDistance']
# print(totalWidth)
try:
path = os.getcwd()
dllPath = path+"\\TSCLIB.dll"
tsclibrary = CDLL(dllPath)
# 如果用了 tsclibrary.setup() 来设置,好像会出错,不执行,我不清楚是否是传入参数的错误,所以我用 tsclibrary.sendcommandW() 来执行我需要的设置命令
# tsclibrary.setup(str(totalWidth),str(height),str(speed),str(density),str(sensor),str(spacing),str(offset))
# setup (String width,String height,String speed,String density,String sensor,String vertical,String offset); # java 的
# tsclibrary.sendcommandW("DIRECTION 1")
for item in print_data:
tsclibrary.openportW(printer_name)
tsclibrary.sendcommandW("DIRECTION 1")
tsclibrary.sendcommandW("SIZE "+str(totalWidth)+" mm, "+str(height)+" mm")
tsclibrary.sendcommandW("GAP "+str(vertical)+" mm, 0 mm")
tsclibrary.sendcommandW("SPEED "+str(speed))
tsclibrary.sendcommandW("DENSITY "+str(density))
# tsclibrary.setup(str(round(totalWidth)),str(round(height)),str(round(speed)),str(round(density)),str(round(sensor)),str(round(vertical)),str(round(offset)))
tsclibrary.clearbuffer()
reverseList = []
for pd in item:
if pd['type'] == 'text':
x = pd['printVal']['x']
y = pd['printVal']['y']
fontheight = pd['printVal']['fontheight']
rotation = pd['printVal']['rotation']
fontstyle = pd['printVal']['fontstyle']
fontunderline = pd['printVal']['fontunderline']
szFaceName = pd['printVal']['szFaceName']
szFaceName = 'simhei'
content = pd['printVal']['content']
# 官方给的 tsclibrary.windowsfontW() 好像调用无效(打印数据好像没传到打印机)
# tsclibrary.windowsfontW 和 tsclibrary.windowsfont 有什么区别吗?
# 还是官方给到例子太老了??
# tsclibrary.windowsfontW(x,y,fontheight,rotation, fontstyle, fontunderline, szFaceName,content)#无法打印
# tsclibrary.windowsfont(x,y,fontheight,rotation, fontstyle, fontunderline, szFaceName,content)#打印乱码
tsclibrary.windowsfontUnicode( x, y, fontheight, rotation, fontstyle, fontunderline, szFaceName, content)#字体为默认宋
elif pd['type'] == 'qrcode':
tsclibrary.sendcommandW(pd['printVal'])
elif pd['type'] == 'reverse':
reverseList.extend(pd['printVal'])
for revCmd in reverseList:
tsclibrary.sendcommandW(revCmd)
# tsclibrary.sendcommand(revCmd)
# tsclibrary.printlabelW("1","1")
tsclibrary.printlabel("1","1")
tsclibrary.closeport()
except OSError as err:
app.logger.warning(err)
print(err)
return jsonify(status="success")
if __name__ == '__main__':
# 日志配置 不需要的话此处可以删
now=datetime.datetime.now()
logdate = now.strftime("%Y-%m-%d")
handler = logging.FileHandler(logdate+'.log', encoding='UTF-8')
handler.setLevel(logging.DEBUG)
logging_format = logging.Formatter('%(asctime)s - %(levelname)s - %(filename)s - %(funcName)s - %(lineno)s - %(message)s')
handler.setFormatter(logging_format)
app.logger.addHandler(handler)
# 日志配置end
app.run()
Python 打印服务程序 全部代码。
打包 exe 命令pyinstaller -F -w -i my.ico PrintServer.py
参考地址
应用程序启动打印服务(python 打包的exe)
打包后的EXE 和 TSCLIB.dll
库都和应用打包后的启动exe位于同级目录(如果要放在子目录或者其他目录 Python 动态库地址,需要再处理,否则electon 无法启动打印服务)
我的此段代码是位于electron主js文件的最后。
try {
// 启动打印服务
if (是生产) {
//注意每个人的端口号可能不一样
child_process.exec('netstat -ano|findstr "5000"',(err, stdout, stderr)=>{
if (stdout === "") {
child_process.exec(`${homeDir}\\PrintServer.exe`,(err, stdout, stderr)=>{
logger.info(`命令执行err:${err}`);
logger.info(`命令执行stdout:${stdout}`);
logger.info(`命令执行stderr:${stderr}`);
});
}else{
logger.info('5000 端口被占用,或打印服务已启动');
}
})
}
// 如果是开发,手动启动打印服务
} catch (e) {
logger.info(`命令执行异常:${e}`);
}
后记
记录我的坑,便于后来者。希望我遇到的问题您能避免,且把您的解决方案留言告诉我们有共同问题的人。