分类归档 IT小常识

通过孟繁永

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

其实之前参考过这篇文章

https://www.printnode.com/en/docs/what-is-raw-printing

但是他们提供的api没有命令式的,只能打文件,现在回头来看,打印机确实可以这么连接,直接安装为raw的方式就可以用程序打印了,并不需要找驱动。

当然,为了简化cups的操作,对普通用户来说,提供一个tsc的驱动在传统的打印机界面上安装会更简单一些。

在cups中添加的raw打印机只能在cups中可见,打印机与扫描仪管理中是看不见的,当然,node-printer中可以调用到这个打印机。

梳理一下基本概念:

标签打印机:重点当然在标签,每次打印一小截,标签一般是固定大小,指令如TSPL

票据打印机:重点当然是票据,按需打印长度,然后撕掉。指令如EOP。

面单打印机:快递单那种,应该是跟标签打印机差不多,只不过更大。

其实对于打印机厂商来说,很多都已经兼容多种指令了,当然不包括得力这种OEM还不提供开发文档的。

要做一个跨平台的连接器,需要满足几个条件:

(1)能够构建跨平台的发布,所以范围就锁定在electron、xamarin这样的方案上,js和c#虽然都熟悉,但最近用的多的还是js,所以有限选electron,但是其实对node不够熟悉,所以造成了上一篇关于buffer类型的困惑。

(2)搞清楚打印机在不同的操作系统如何连接,目前还没尝试usb直驱这种,在mac上还是通过cups来连接,windows上都有相应的驱动,可以不单独讨论。

(3)指令集兼容性,目前只尝试了TSPL这一种,后续还需要其他的ZPL之类的测试一下,还需要在各种指令之间转换,方便原来用某个指令集编写的程序直接对接。

(4)接入方式的兼容性,至少应该支持socket和http。

(5)统一的api,尤其是友好的RestFul模式的api,便于开发对接。

通过孟繁永

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

接下来就想为什么汉字是乱码呢?是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);
    },
  });

打印成功。

通过孟繁永

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

后来我找到了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);
    },
  });

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

通过孟繁永

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

中间走了一段弯路,因为一直无法直接用程序驱动打印机打出东西来,无法验证以前的思路是否可行,所以,想着是否应该给打印机找一找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);
    },
  });

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

通过孟繁永

OBS导播台的虚拟摄像头插件

OBS的官网上有win版的虚拟摄像头插件,可是我没有找到mac版的,在github上搜到了https://github.com/johnboiles/obs-mac-virtualcam

安装成功,但是,腾讯的微信和腾讯会议均不支持,无法选择摄像头。钉钉可以,而且默认就是虚拟摄像头。

通过孟繁永

[Jenkins]Error:403 No valid crumb was included in the request

最近在更新job的配置时经常出现以上的报错,开始还误以为是百度效率云的icode出了问题。跟百度效率云的团队反馈,对方一直没有响应,不知道是解散了(开个玩笑),还是觉得这不是个问题。

检索这个问题,发现很多文章都说取消全局安全配置中的CSRF Protection的勾选即可,但是我的jenkins里没有这个勾选框了。

然后又检索到https://www.cnblogs.com/kazihuo/p/12937071.html

Jenkins版本自2.204.6以来的重大变更有:删除禁用 CSRF 保护的功能。 从较旧版本的Jenkins 升级的实例将启用 CSRF 保护和设置默认的发行者,如果之前被禁用。

这篇文章给的办法是在启动jenkins时添加一个配置

-Dhudson.security.csrf.GlobalCrumbIssuerConfiguration.DISABLE_CSRF_PROTECTION=true

来实现这个关闭。

但是这样很不优雅,尤其是我有两个jenkins,一个基本上是最新版,一个是为了兼容百度效率云的插件而保留的一个很老的版本。最新版是直接在服务器上跑的jar版本,老版本是用的docker。以上的方法就不好玩了。

所以受到https://stackoverflow.com/questions/49888756/how-do-you-disable-jenkins-csrf-with-script的启发,我安装了一个Strict Crumb Issuer插件,然后在跨站请求伪造保护这里,选择这个插件来工作,配置默认。保存就好了。

通过孟繁永

将自建jenkins注册到百度效率云的ipipe

对应百度效率云的这个文档:https://cloud.baidu.com/doc/XLY/s/Fjwvy8aq6#jenkins-%E6%8F%92%E4%BB%B6%E7%94%A8%E6%88%B7%E6%8C%87%E5%8D%97

由于官方的文档用起来很多误解,这里做一下简单的说明,方便大家使用。

首先是jenkins的安装。由于百度效率云目前的jenkins插件比较老,用最新版的jenkins无法配置成功,所有需要为百度效率云部署一个专用的jenkins,这里我称之为中继jenkins。最初,我是按百度的文档所提到的方式,下载jenkis.war,并使用

nohup java -Dfile.encoding=utf-8 -Dhudson.model.ParametersAction.keepUndefinedParameters=true -jar jenkins.war –httpPort=8888 &

这个命令来部署,同时我的业务jenkins是用docker的方式部署。后来,我将业务jenkins改为yum install jenkins直接安装在宿主机了,于是需要把这个效率云jenkins改为docker,避免冲突。

部署中继jenkins,我使用的是以下命令:

docker run -d -u root –restart=always -p 8888:8080  -v /jenkins_xly:/var/jenkins_home –name jenkins_xly -idt jenkins:2.19.1

运行后打开IP:8888,即是通常的账户密码初始化过程,接下来是插件,我取消了所有插件的安装,因为这个jenkins实例只用来转发百度效率云的构建推送。

下载百度效率云的jenkins插件

ipipe-agent.hpi,地址在文章开头那个链接中,通过百度网盘下载。在系统管理-插件管理-高级那里上传这个插件。

回到系统管理,找到iPipe agent management,按照提示填写几个字段。

  • ipipe Url:效率云:https://xly.bce.baidu.com/${企业名}/${项目名}/ipipe,可以打开效率云的ipipe页面,复制上面的url,将多余的参数去掉即可。其中企业名好理解,但项目名不好理解,因为效率云上实际上可能已经有多个项目,这里选择你要用的主要的项目,一般也是英文字母。
  • enterprise:就是上面这个url中的企业名,实际上是英文字母。
  • Jenkins Name:给当前的jenkins取个名字,如果是重复设置(像我是之前部署过重新部署的),最好跟之前的不同,否则在效率云看到的是一样的job,无法区分。在效率云的流水线设置时,下拉框显示为:Jenkins Name+ Jenkins Job Name(这个JobName就是当前这个jenkins中配置的job的项目名称。)
  • Username:是当前的jenkins实例的用户,比如用你登录jenkins的这个用户名。

点击Save,正常的情况下就直接跳转了,没有报错,如果有问题,会有报错,比如用最新版的jenkins,可以装上这个插件,但在这里提交时会报错,问题出在有一个JenkinsUUID的参数,在最新版的jenkins上是空的,我跟百度效率云团队提过这个问题,但至今尚未修复。所以只好多此一举,继续用jenkins2.19.1来中继。

创建jenkins的job

item name即项目名称,尽量用英文字母写清楚,前面提到了,这个名字会出现在效率云中作为选择job的依据。

因为我前面部署这个中继时没有安装其他插件,所以我这里只需要选择构建一个自由风格的软件项目即可。而实际上,这个job需要做的也只是转发一下请求。

在job的General配置中,找到iPipe配置,只需要填写项目标识,这个项目标识跟ipipeUrl中那个项目名是一回事儿,但实际上效率云在相应的项目中显示相关job时应该是以这个job中配置的项目名为准,而前面ipipeUrl中那个项目名应该是插件的历史遗留问题,早期只考虑到一个项目的注册方式。

然后在构建Build中添加一个执行shell,只添加一行命令:

curl {你的业务jenkins的某个job的webhook地址}

job的配置到此结束。

接下来,我们先去效率云验证下这个job能否被触发。

按照百度效率云的文档配置流水线即可,需要注意的是我开始有一个误区,就是代码触发时要选merge,而不是change。否则提交代码到代码库时不会触发流水线。

最后,简单提一下我们的业务jenkins需要做什么配置来响应中继jenkins的curl请求,这个未必是最好的方式,因为不能传递参数了,也许应该用触发远程构建之类的实现,我对jenkins还不够了解。

假定按curl的话,我们需要在业务jenkins安装一个generic-webhook-trigger插件,然后在业务jenkins的job中启用这个插件的配置,填写token这个参数,这个token只要在当前的jenkins的jobs中不重复即可,但为了方便识别,我一般会加上项目标识,比如itemname-rR74nddfdsfs23mm

然后上面curl后面的网址就有了,格式是

http://JENKINS_URL/generic-webhook-trigger/invoke?token={token}

通过孟繁永

如何修理不平衡的平衡車

女儿的小伙伴有一个平衡车,她很快就学会了怎么玩,也想拥有一个,我看了看,没舍得买个新的,果断下手一个100元的故障机,就当自己练手,修好了就赚了。

结果买的时候也没通电,拿回来开机根本站不住,比视频里跑的快多了,一直滴滴响。

于是拆机检查,发现螺丝不全,电池固定扣已经损坏,壳里面有沙子,是否进水不好说,至少是打开修过。心一下子就凉了。

接下来用万用表反复测试,观察板子上的电压,逐步搞清楚哪个板子管姿态,哪个板子是主控,按照pcb上的一些字样查到是A8这样一个比较典型的早期型号。

买一套新的主板需要两百多,老婆不同意,三百块可以买个新的了。

于是抱定决心一定要修好,然后开始继续研究。

通过观察姿态板不同角度下的反应发现有一个角度似乎可以让平衡车稳定下来,但又很难保持,初步怀疑是陀螺仪坏了,这东西在玩无人机的时候接触过,要是坏了肯定炸鸡。但如何确认是陀螺仪坏了挺麻烦,光看电压看不出来。

万用表不够用就需要示波器了,那个玩意不便宜,接下来两天就研究示波器,选型,看教程,然后发现,如果是数字信号还要用逻辑分析仪,那玩意也不便宜。两个都买个要一两千,还是基础版。

没办法,回头补基础电路知识,看看能否在其他方面找找线索。然后就看了好几天电感和电容的东西,顺便说台湾的视频讲得比较好。

其实买平衡车的时候就抱着一个念头,重置一下平衡就好了,但是拿到以后试了一下重置并不成功。现在看了一圈下来,还是觉得基本可以排除物理故障,正好收到阿尔郎官网一个重置办法,按开关20秒。然后,就修好了。

还有一种方法,如果带遥控的话,按一个组合键就行了。我买的这个不带遥控板。

通过孟繁永

jenkins2.220下的dingtalk2.0.0

这一版dingtalk插件发生了重大升级,以至于升级之后我一直以为不兼容,无法工作了,考虑降级测试,后来一直点到dingtalk的源代码地址,才发现是配置确实变了。怎么就不能在里面提示一下呢?

dingtalk插件的地址:https://plugins.jenkins.io/dingding-notifications/

github的地址:https://github.com/jenkinsci/dingding-notifications-plugin

注意源代码那里的使用说明:

1.在 系统管理 > 系统设置 > 钉钉配置 中添加机器人 全局配置 -> 测试
经测试,钉钉机器人的安全策略配置的选项属于与 关系,即如果勾选了多个策略,则需要同时满足。 所以,插件的配置最好与钉钉机器人上保持一致。

2.在项目配置页面中勾选机器人 项目配置 测试

其中需要设置name,在具体的job任务中可以看见这个名字,就可以勾选了。

id空着,保存的时候由插件自己生成。

webhook就是钉钉中机器人的地址。

下面就是安全设置,这个不清楚为什么还有需要,暂时按照钉钉里机器人的配置一样填的。

然后点test成功,钉钉群里收到消息。

通过孟繁永

用jenkins获取git change log推送给钉钉机器人

背景:为了将代码提交的记录推送到钉钉群,我们可以使用钉钉自定义机器人这个功能,关于钉钉机器人这里不解释,请查阅官方文档。本来用aliyun code管理代码时可以使用webhook快捷设置钉钉机器人,但迁移到百度效率云的icode之后,暂时失去了webhook这个功能。

以下介绍解决办法:(以下内容来自https://twiceyuan.com/2017/02/21/jenkins-changelog/

Jenkins 每次构建都会根据 git 的提交记录生成一个 web 页面来显示自上次构建之后的提交记录列表。在配置 CI 工作流时,很多情况需要获取这个提交记录的 String 值,但是 Jenkins 并没有提供这个功能。

Jenkins 官方反馈中有人也提出了这一需求:https://issues.jenkins-ci.org/browse/JENKINS-12032 。下面有人给出了一个插件来解决,插件地址:https://github.com/daniel-beck/changelog-environment-plugin ,不过作者没有编译上传到 jenkins 的插件中心,也没有文档说明怎么使用,这里简单介绍一下。

首先项目拉到本地,在项目根目录执行 mvn verify 就可以编译生成我们需要的 hpi 插件文件了(编译需要很多依赖,第一次可能会比较漫长)。之后在 Jenkins 中管理插件的高级(Advanced)中,选择上传 hpi 文件,就可以安装成功了。你也可以直接下载我编译好的

点击下载

安装成功以后,在项目配置的 Build Environment 环节,会多出一个选项:Add Changelog Information to Environment。下面有三个编辑框,分别是:Entry Format、File Item Format 和 Date Format。第一个就是填写提交日志输出格式的地方,采用的是 Java String.format 占位符的形式。其中可以使用四个参数,分别是:

  1. 提交的作者
  2. 提交的 ID
  3. 提交信息
  4. 提交时间(通过 Date Format 控制格式)

例如,我在 Entry Format 输入 %3$s (at %4$s via %1$s)\n,然后有一条在 2017-02-10 的提交记录,提交信息为「fix bug」,提交者为 twiceYuan,那么输出到环境变量的字符串就是 “fix bug (at 2017-02-10 via twiceYuan)\n” (后面的 \n 是为了多层转义,视使用情况请自行调整),同样时间格式编辑框填写的是:yyyy-MM-dd。

通过设置之后,在构建时就可以通过 shell 中来获得 SCM_CHANGELOG 变量来取到更新日志了。比如自动上传更新信息到内测平台。

(内容引用完毕)

shell中的命令如下:

curl ‘https://oapi.dingtalk.com/robot/send?access_token=87980355c9069b2d0eba095480dcead5’ \
-H ‘Content-Type: application/json’ \
-d “{‘msgtype’: ‘text’, ‘text’: {‘content’: \”${SCM_CHANGELOG}\”}}”

请留意以上命令中的引号可能被我的博客编辑器给换掉,请对照图片检查。