自制热敏打印机连接器始末(7)指令如何编码,尤其是汉字的编码

接下来就想为什么汉字是乱码呢?是command.setText(50, 100, “TSS24.BF2”, 1, 1, “一二三”);里面的汉字需要转码吗?

对照jpPrinter.addCommand里面把原来的转码去掉了,如果在command.setText(50, 100, “TSS24.BF2”, 1, 1, “一二三”);中把汉字转成unico呢,试了试也不行。

把jpPrinter.addCommand恢复回去,

jpPrinter.addCommand = function(content) {
      // 将指令转成数组装起;
      var code = new encode.TextEncoder("gb18030", {
        NONSTANDARD_allowLegacyEncoding: true,
      }).encode(content);
      for (var i = 0; i < code.length; ++i) {
        command.push(code[i]);
      }
      // command = command + content;
    };

还是会报错,throw new TypeError(‘first argument must be a string or Buffer’);

然后仔细一看,原来data复制不止是string类型,还可以是buffer类型。

那就接着从BluetoothPrinter往这里扒,因为不是蓝牙,不受每次传输的长度限制,所以把分段传输的逻辑去掉。

var data = Array.from(uint8Buf);
  var buffer = new ArrayBuffer(data.length);
  
  var dataView = new DataView(buffer);
  for (var i = 0; i < data.length; ++i) {
    dataView.setUint8(i, data[i]);
  }
  console.log(
    "data type is: " + typeof data + ", is buffer: " + Buffer.isBuffer(data)
  );

但是,还是报同样地错误。

然后发现Buffer.isBuffer(data)是false,为什么类型不对呢?

于是搜到了这里http://nodejs.cn/api/buffer.html#buffer_static_method_buffer_from_arraybuffer_byteoffset_length

是nodejs里面对Buffer有不同于js的类型定义。

于是改成:

var uint8Buf = command.getData();
  var buffer=Buffer.from(uint8Buf);
  console.log(
    "buffer type is: " + typeof buffer + ", is buffer: " + Buffer.isBuffer(buffer)
  );
  var jobid = "";
  printer.printDirect({
    data: buffer, // or simple String: "some text"
    printer: "Deli_DL_888B_NEW_", // printer name, if missing then will print to default printer
    type: "RAW", // type: RAW, TEXT, PDF, JPEG, COMMAND.. depends on platform
    success: function(jobID) {
      console.log("sent to printer with ID: " + jobID);
      jobid = jobID;
    },
    error: function(err) {
      console.log(err);
    },
  });

打印成功。

自制热敏打印机连接器始末(6)验证指令可以驱动打印机输出但有乱码

后来我找到了https://github.com/qihang666/BluetoothPrinter这个项目,人家用蓝牙直接连打印机,比如我手头的DL-888AW就有蓝牙,我用usb为什么就不行呢,所以我就着手将这个项目中的代码拿过来。

尽管他里面的打印机指令集叫jprinter,但指令集实际上是TSPL,所以理论上应该可以复用。

拿过来的部分是https://github.com/qihang666/BluetoothPrinter/tree/master/components/gprint,

考虑到https://github.com/tojocky/node-printer/blob/master/examples/print_raw.js这里面提示的是data部分需要string类型,所以,我把tsc.js里面的command改成了字符串类型,把

jpPrinter.addCommand = function(content) {
      // 将指令转成数组装起;
      var code = new encode.TextEncoder("gb18030", {
        NONSTANDARD_allowLegacyEncoding: true,
      }).encode(content);
      for (var i = 0; i < code.length; ++i) {
        command.push(code[i]);
      }
    };

改成了

jpPrinter.addCommand = function(content) {
       command = command + content;
    };

然后打印的时候:

var command = tsc.jpPrinter.createNew();
  console.log(command);
  command.setSize(60, 40);
  command.setGap(2);
  command.setCls();
  command.setText(50, 10, "2", 1, 1, "Hello");
  command.setText(50, 100, "TSS24.BF2", 1, 1, "一二三");
  // command.setQR(50, 50, "L", 5, "A", "977767937@qq.com");
  command.setPagePrint();

  var data=command.getData();
  var jobid = "";
  printer.printDirect({
    data: data, // or simple String: "some text"
    printer: "Deli_DL_888B_NEW_", // printer name, if missing then will print to default printer
    type: "RAW", // type: RAW, TEXT, PDF, JPEG, COMMAND.. depends on platform
    success: function(jobID) {
      console.log("sent to printer with ID: " + jobID);
      jobid = jobID;
    },
    error: function(err) {
      console.log(err);
    },
  });

惊喜出现了,打印机工作了,但是汉字是乱码。

自制热敏打印机连接器始末(5)放弃直接驱动usb,尝试找替代的驱动程序

中间走了一段弯路,因为一直无法直接用程序驱动打印机打出东西来,无法验证以前的思路是否可行,所以,想着是否应该给打印机找一找mac版的驱动,然后尝试直接在电脑上打印一个pdf文件,来和程序做下对比。于是,按照打印机的说明书里说的TSPL这个语言,找到了TSC这个厂商,他们家的网站上资源真是全啊https://www.chinatsc.cn/

按照类似的规格找了几个型号的打印机,下载驱动,然后发现其实人家的驱动都在一起。

这个ppd文件就是所谓驱动,用vscode打开,发现就是文本文件,对比一下,发现基本上都是一样的,对于热敏打印机来说,只有几个参数有区别,主要是打印宽度、打印速度、打印方式(热敏/热转印)。

找到了驱动,就可以在系统的打印机管理中添加打印机,然后自选某个驱动文件,比如我试了TA200,Deli_DL_888B_NEW是可以打印文件的。

但是我用node-printer还是无法打印。

  printer.printDirect({
    data:"测试", // or simple String: "some text"
    printer: "Deli_DL_888B_NEW_", // printer name, if missing then will print to default printer
    type: "RAW", // type: RAW, TEXT, PDF, JPEG, COMMAND.. depends on platform
    success: function(jobID) {
      console.log("sent to printer with ID: " + jobID);
      jobid = jobID;
    },
    error: function(err) {
      console.log(err);
    },
  });

打印机有时间连动都不动。

自制热敏打印机连接器始末(4)分清楚热敏打印机的几种常用指令集

都调试起来以后,连接打印机,发现打印机根本没反应,网上找到的相关资料很少,大部分都是在windows或者linux上做的,难道是mac的osx有一些安全限制,导致打印的指令被拦截了。

于是照着这个思路查下去,终于找到了一个新的东西:CUPS。其实这个东西支持linux也支持mac,按照说明添加上打印机之后,确实能在mac的打印机列表中看到了,而且在系统打开一个文件用这个打印机打印,是有动作的,尽管是瞎走纸,没有真正的输出。但至少说明是有希望了。

那么为什么用electron-escpos还是无法打印呢,甚至依然连走纸反应都没有。

无奈又去得力的官网看了看,找到888B这个打印机的说明书,仔细瞅了一眼,竟然发现它说他的编程语言是TSPL,不兼容ESCPOS,擦,过于相信刚找到escpos这个神奇的协议的时候它所说的几乎所有的打印机都支持了。

https://blog.csdn.net/weixin_34355881/article/details/89543300这篇文章介绍了打印机主流的指令类型(ESC命令集+CPCL命令集+TSPL命令集)之间的区别。这就解释了上面我对打印机类型的模糊认识

好了,标签打印机一般用TSPL命令集,票据打印机一般用ESCPOS。而我用的得力888B(NEW)是标签打印机。

那么,就需要将原来的escpos换成tspl来写打印命令。

自制热敏打印机连接器始末(3)

接上一篇,分清楚了两种模块的规范,那么怎么解决两种规范的不同呢,似乎可以通过配置babel来实现,于是我在babel.config.js中尝试了很多办法,都没有收效,归根结底是还没有搞清楚vue和electron之间是如何各自适用这些规范的。

按照某种说法,最新的vue是遵循ES6,而electron或者说node模块是CommonJs,但是,在项目中,npm install进来的模块都放在node_modules中,怎么区分呢,如果不是babel.config.js来负责,那就应该是electron相关的,于是我找到了electronBuilder这个配置。

在vue.config.js(如果没有则在项目根目录新建)中:

module.exports = {
  configureWebpack: {
    devtool: "source-map"
  },
  pluginOptions: {
    electronBuilder: {
      // List native deps here if they don't work
      externals: ["escpos", "escpos-usb"]
      // If you are using Yarn Workspaces, you may have multiple node_modules folders
      // List them all here so that VCP Electron Builder can find them
    }
  }
  // chainWebpack: config => {
  //   config.module
  //     .rule("js")
  //     .test(/\.jsx?$/)
  //     .exclude.clear()
  //     .end()
  //     .include.add(function() {
  //       return ["./node_modules/escpos", "./node_modules/escpos-usb", "src"];
  //     })
  //     .end();
  // }
};

这个发现得益于https://medium.com/@nyorikakar/printing-with-vue-electron-and-node-f52a730e63e7这篇文章,而且我还在后面的评论中发现了https://github.com/song940/node-escpos/issues/297,最终靠这些信息搞清楚了逻辑。

(1)对于用于node的模块,需要在vue.config.js的externals那里列出,这样,vue就不会让babel啥的那些loader去处理了。

(2)负责和node打交道的代码要放在background.js中,这在前面第二个连接中可以看到,而且最关键的是在vue的代码因为是通过BrowserWindow加载的,在其初始化是webPreferences中的nodeIntegration这个属性需要配置为true,const { ipcRenderer } = window.require(“electron”);才能用。而且,这个true如果是放在.env中不能直接给process.env.ELECTRON_NODE_INTEGRATION=true,那样,调到的是字符串,不是布尔值,会出现下面的报错:

Uncaught TypeError: window.require is not a function
    at Module../node_modules/cache-loader/dist/cjs.js?!./node_modules/babel-loader/lib/index.js!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/App.vue?vue&type=script&lang=js& (App.vue:22)
    at __webpack_require__ (bootstrap:853)
    at fn (bootstrap:150)
    at Module../src/App.vue?vue&type=script&lang=js& (App.vue?c53a:1)
    at __webpack_require__ (bootstrap:853)
    at fn (bootstrap:150)
    at Module../src/App.vue (App.vue:1)
    at __webpack_require__ (bootstrap:853)
    at fn (bootstrap:150)
    at Module../src/main.js (HelloWorld.vue?adfd:1)

自制热敏打印机连接器始末(2)从跨平台方案开始

接上一篇,选择vue+electron之后,那就按electron-vue这个来。虽然能跑起来,但是代码上传到git以后,收到很多高危报警,https://github.com/futuremeng/electron-vue-escpos-bridge

尝试升级一下,结果发现因为版本已经差太多,需要花很多时间修,遂放弃。通过检索,发现有一个新的方案,https://nklayman.github.io/vue-cli-plugin-electron-builder/

于是,开启新的https://github.com/futuremeng/vue-electron-builder-escpos-printer-bridge

先用vue cli4创建一个新的项目,再vue add electron-builder。尝试运行yarn electron:serve 的时候提示Vue Devtools下载失败,需要翻墙跑成功一遍才行。

接下来分析打印机如何连接,通过检索发现escpos这个协议,据说是大部分打印机都支持的。那么是否可以用escpos直接驱动打印机呢,网上有树莓派的示范。

在寻找合适的escpos驱动的时候,发现了各种版本的,除了java、python,还有php和node,我决定选择node版的https://www.npmjs.com/package/escpos

但是,当然当我尝试引入到项目的时候,悲剧发生了。

ERROR Failed to compile with 1 errors 16:30:59

error in ./node_modules/escpos/statuses.js

Module parse failed: Unexpected token (7:7)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
|
| class DeviceStatus {

byte = ”;
| bits = [];
| bitsAsc = [];

@ ./node_modules/escpos/index.js 12:17-38
@ ./node_modules/cache-loader/dist/cjs.js??ref–12-0!./node_modules/babel-loader/lib!./node_modules/cache-loader/dist/cjs.js??ref–0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/App.vue?vue&type=script&lang=js&
@ ./src/App.vue?vue&type=script&lang=js&
@ ./src/App.vue
@ ./src/main.js
@ multi (webpack)-dev-server/client?http://192.168.3.139:8080&sockPath=/sockjs-node (webpack)/hot/dev-server.js ./src/main.js

见到是loader的问题,我就尝试配置babel-loader script-loader file-loader。都没有解决问题,实际上,我也不是很了解这些loader到底是干什么的。

在这个过程中,了解到CommonJS和ES6这些区别,还有,在网页版的vue项目和electron中还涉及到运行环境的问题,网页中没有办法直接调用node接口。

自制热敏打印机连接器始末(1)不想花钱,还想跨平台

前情回顾:做了一个网站,需要在网页上打印二维码,将热敏打印机连到电脑上,在网页上直接用print打印出来的二维码完全无法识别,应该是分辨率的问题,因为屏幕的分辨率和打印机的分辨率差太多。所以寻找解决方案,找到了c-lodop.com这个网站提供的中间件,需要付费,而且只有win版。

需求分析:现在重构网站,计划做成saas模式,在打印模块上,需要尽量摆脱第三方的束缚,所以构思如何自己实现。

网页到打印机,这中间是网页-浏览器-操作系统-usb-打印机,这样一个路径。经过确认,网页直接连打印机是不显示的,有一个比较接近的办法是通过chrome的app方案,但通用性不高,放弃,那就只剩下跟lodop一样的模式了,要么把整个网站构建成桌面程序,要么通过中间件来连打印机,网页再通过http或者socket驱动中间件。

当然,选择一个中间件是比较合适的,整个后台构建成桌面程序,没有必要,维护起来也太笨重。

接下来,就是选择跨平台的桌面程序方案来做这个中间件了。需要实现几个部分,一是http或者socket,二是打印机连接,中间的打印机连接管理。

当然,首选相对比较熟悉的vue+electron的方案。

某培训机构(服务号)开发计划方案

某培训机构(服务号)开发计划方案

 

甲方需求

 

  • 内容模块
  • 付费音视频
  1. 内容专区:分为三大版块【阅读、朗诵、科普】
  2. 呈现形式:音频、视频、音频+文本(根据需要)
  3. 付费方式:单件计费、打包计费、会员计费
  4. 其他功能:试听、留言互动区、打赏

 

  • 在线报名、测评系统
  1. 在线报名

功能:信息录入、音视频上传

  1. 在线测评

功能:在线计时答题、统计得分

  1. 信息反馈

功能:审核通过发送通知书,会员下载打印通知书

  1. 定位

功能:准确定位会员所在位置,向其推荐就近比赛地点。

 

  • 会员系统
  • 会员渠道:扫码即会员、支付即会员、注册即会员、导入即会员
  • 会员管理:会员信息、权益、互动、积分、余额、打卡、消费、参与活动、报名信息、测评结果、信息反馈……
  • 会员营销:会员标签、积分赠送、商城、推荐有奖、节日关怀、生日关怀……
  • 会员推荐:会员推荐二维码、会员推荐分享页面
  • 会员通知信息的发布

 

 

  • 数据模块
  • 消费记录、充值记录
  • 点播数据:每一节目的浏览及付费数据
  • 行为数据:会员的浏览及消费数据,分析其偏好,可做相关推荐。
  • 定位分布:分析会员的地理位置,定位分布。

 

  • 客服模块

直接链接到相关人员微信或QQ

 

 

  • 社区模块

会员信息及时反馈,会员互动,保持热度,定期开展活动,增设赛事及活动的入口。

 

 

 

 

产品设计

 

根据甲方需求,将产品总结为一个前后端分离的运营平台和微信应用。其中运营平台包括所有的资源管理和业务管理,微信应用作为面向用户的终端呈现,为用户提供在线服务。

需求基本上反映了业务需求,本方案对部分细节设定做了增减,如暂时不考虑积分规则,而建议在用户激活之后逐步设计积分规则和配套的运营活动,否则将对系统复杂度影响太大,而又不能在初期用户导入阶段发挥足够的作用。另外,用户的互动场景表达尚不清晰,考虑到用户间的交流面临审核监管的责任问题,建议单独设计这一部分的功能和管理方式,初步方案中也暂不涉及。

针对营销管理和消息推送模型做了较为细致的设计。其中营销管理的管理方式覆盖了典型的营销策略,可以直接配置上线,不需二次开发,消息管理覆盖了短信、微信、邮件多通道消息推送,及定时和触发两种模式,可以兼顾几乎所有业务场景,由运营方有效主导。

 

架构设计

 

开发计划

模块 开发周期(周)
运营管理平台 用户管理 1
分类管理 0.5
资源管理 1
课程管理 1
营销管理 3
订单管理 1
授权管理 1
知识答题 2
活动管理 1
消息管理 2
推荐管理 1
行为管理 2
客户专员 1
权限管理 0.5
微信应用 消息推送 0.5
内容浏览 1
课程清单 1
活动报名 1
知识答题 1
我的会员 1
合计 23.5

 

开发周期6个月,总报价:60万元。

 

运营支撑服务

云主机:每年2000元至5000元。

流量费用:每会员每月0.5元至2元。

运维费用:每月1万,上线后前12个月免费,含设计范围内的合理修改,不含功能变更或扩充。

响应时效:紧急需求24小时内提供解决方案,一般需求48小时内提供解决方案。

PDF版下载:

某培训机构服务号开发计划方案

期待产品原型、UI设计与开发统一版本管理工具

目前产品原型到UI设计,再到开发测试上线,是分离的,目前尚未发现有一统天下的工具和平台。

我倒是非常渴望有这么一个工具,尤其对于小团队而言。具体的需求就是将产品原型、UI设计、开发测试三者的版本管理集中在一起,每个阶段的某个版本有若干状态,如设计中/已推送/已交付/已验收。墨刀实现了产品经理与UI设计之间的协同,如果再能与开发协同一下则更棒。

对一个项目而言,有这么几个部分,页面树、工作流、页面。页面是基本元素,页面数上可以标注该页面存在的生命周期,如从v1到v4,或者从v3到v5,并且设定发布的url,可以包括测试版地址和发布版地址,工具根据url自动快照,获取当前版本,并通过图像识别对比差异,只保留变更的版本,存入版本库。而产品原型和UI设计的版本分别由产品经理和UI设计师标注和提交。查看时,可以查看最新版,也可以专门查看某一版本的执行情况。

融媒体出版物《全民经典朗读范本》

所谓融媒体,无非相对传统的图书来说,除了平面的图文,又多了音频视频和app式的用户交互。不过这些组合带来的是投资的极大增加,一本几万块钱可以搞定的书,也因此需要数百万的投入。

这种形式,适合一定类型的阅读,比如一本讲朗读艺术的书,如果只是用文字来描述,一定不如这些朗诵艺术家亲身示范来得有用,再加上用户自己可以录音并回放,甚至上传到网上参与分享和比赛,那才是一个与实践相结合的学习过程。

这本书定价118元,想买的话自己淘宝吧。