软件版本:
阅读坑记需谨慎,版本要看仔细。
"node":"v14.16.0",
"electron": "^13.0.0",
"vue": "^2.6.14",
"electron-edge-js": "^14.16.1",
几年前,直接使用IE 调用相关的插件执行打印,可,IE在win11已经找不到入口了,需要单独设置edge浏览器,且使用的人,经常“不小心”重置网络的安全,导致检查需要远程帮他们开启,故此需要个桌面软件,一次解决,永不烦人。
遇到的问题
经过查看tsc打印机官网,找到了SDK下载到入口tsc官方SDK下载地址(有些人很贱,从官网下载了资料,上传到其他收费平台上去“赚钱”);找到了nodejs相关的样例。 nodejs的样例,需要用到edge-js ,但经过多日的入坑,找到的资料是使用:electron-edge-js ; 用electron-edge-js 参考官方的edge-js 样例,确实可以打印,但是:
如果循环打印大量数据,会卡死。
卡死的调试场景
- 渲染进程的打印页面(组件)循环 执行(调用)
tsclibnet.dll 打印方法;结果:页面卡白了。 - 我把对
tsclibnet.dll 是调用、循环打印,新开启一个窗口来执行;结果,该新开的窗口,如果打印数量超过3条同样还是卡死了。 - 放在“主进程”来执行:通过渲染进程(页面)向主进程发送数据信息,主进程执行数据的循环实施打印,结果还是卡死。
以上是我用electron-edge-js 调用tsclibnet.dll 遇到的问题,我不确定是不是因为我执行打印代码写的太垃圾,而导致卡死,或者我对electron 了解太少,垃圾代码贴出,便于交流: (此代码不要直接全部复制粘贴使用,我有删改,不保证可执行)
var openport = edge.func({
assemblyFile: './tsc_dll/tsclibnet.dll',
typeName: 'TSCSDK.node_driver',
methodName: 'openport'
});
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\""
}
]
];
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'){
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
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') {
}
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;
width = width * 10;
height = height * 10;
spacing = spacing * 10;
let totalWidth = width * column + spacing * (column-1);
const conf = {
width: Math.ceil(totalWidth).toString(),
height: Math.ceil(height).toString(),
speed: speed.toString(),
density: consistence.toString(),
sensor: sensor.toString(),
vertical: spacing.toString(),
offset: offsetDistance.toString(),
};
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) {
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) 只是之前 通过发送命令调用 exe ,这次也借助 node 采用web服务的方式,Python 也同样开启一个服务,这样通信我自己比较容易理解些。
打印数据结构和循环体的逻辑,和最早代码的逻辑一致: 特别注意:Python 的 TSCLIB.dll 动态库,和nodejs的不一致,去前文给到的官方地址下载Python 的例子,另外Python样例的动态库参数数据类型 没有描述,参照 java 样例的代码可以知道参数类型。
我遇到的问题,都写在代码注释中:(我的TSCLIB.dll 库和.py 在同级)
from flask import Flask,request,json,jsonify
from ctypes import *
import logging
import os
import datetime
from time import strftime
app = Flask(__name__)
'''
是否是开发
True-开发
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)
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']
try:
path = os.getcwd()
dllPath = path+"\\TSCLIB.dll"
tsclibrary = CDLL(dllPath)
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.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.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.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)
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}`);
}
后记
记录我的坑,便于后来者。希望我遇到的问题您能避免,且把您的解决方案留言告诉我们有共同问题的人。
|