技术栈
Tauri + Element-Plus
Vue + Rust
什么是 Tauri
Tauri 是一款应用构建工具包,可以帮助开发者为主要桌面平台制作应用程序(如 mac,windows,linux 等)。几乎支持现有的任何前端框架(如 react, vue, vite 等),其核心是使用 Rust 编写的。Rust 负责系统通信,应用构建,上层应用开发只需要专注于 webview 中的网页编写(可以使用自己喜欢的任何前端语言)。
技术选型对比
主要是和 Flutter 以及 Electron 的对比。
和 Flutter 的对比:
我参考了这篇文章:zhuanlan.zhihu.com/p/520770477
其中最关键的一点其实还是在硬件层面,我们所要连接的读卡器的 dll 动态链接库文件只有32位的,所以我们一定要能打包出32位的桌面应用程序,所以不选择 Flutter。
和 Electron 的对比:
其实我并没有太调研这个框架,也没有深入的使用过,只是当时发现 Electron 打包生成的安装程序会比较大,并且 Tauri 技术较新,所以就选择 Tauri。结果后来发现如果为了兼容 Windows7,Tauri 需要离线将 WebView2 打包进安装程序,出来的安装程序也挺大的,所以这个原因就不攻自破了。
这边我还是先留白吧,等我有空拿 Electron 试着重写一下这个项目,再来补充吧,Electron 使用的是 Node.js 运行的,不知道对硬件支持的程度怎么样,不过肯定要比我从头开始学习 Rust 并且编写连接硬件的逻辑要好上不少,期待。
难点
业务流程上没什么问题,前端页面上也没有什么问题,主要困难点在于底层和硬件的交互。由于 Tauri 底层使用的是 Rust,所以我不得不自学 Rust,并编写和硬件连接的方法,由于 Rust 和 C++ 以及 C# 很像,对于我这种一直使用 JavaScript 的人来说,真是一段痛苦的经历。
硬件
一台收银机,主要是由 一台一体机电脑,一台扫码枪,一个电子秤,一个小票打印机组成,牵扯到开发连接层面的其实主要只有三个部分,分别是电子秤,小票打印机和一体机电脑上的客显屏。扫码枪之所以不牵扯开发层面,是因为扫码枪本质上就是一个格式化的自动输入设备,只要在前端监听键盘输入做对应的处理就行了,而其他三个才牵扯到驱动,串口,数据发送读取这种需要使用 Rust 编写底层逻辑。
硬件型号
虽然我在项目的 README 里面把接入的所有设备型号都单独写了出来,并且都说明如果有别的型号设备支持需求,需要二次调整代码逻辑,但是后来我在整理的时候发现,好像只有读卡器和小票打印机需要特殊对待。
读卡器:明华 URF-R330 读写卡器
读卡器之所以要说明型号,是因为读卡器这个设备,我们使用了外部的 dll 动态链接库文件来进行连接,这个部分是同事帮我去实现的解决方案,我自己还没有深入尝试只使用 Rust 代码来进行连接,并且通过发指令的方式,据我同事所说该读卡器的指令集都是封装起来的,并没有通用的解决方法,所以使用了设备厂商提供的 dll 动态链接库,在 Rust 中使用 libloading 这个库进行连接调用。就是因为这个文件只有32位的,所以我们不得不整个项目最后生成的安装程序也是32位的。
打印机:佳博 GP-58MBIII 热敏票据打印机
这种小票打印机,基本上都是兼容 ESC/POS 指令集的,所以在 Rust 寻找相关的连接库还算顺利。并且这种打印机基本上都是通过 USB 进行连接的,由于使用了 Rust 里的 libusb 库进行连接,在 mac 上只需要知道该设备的 PID 和 VID 即可,而在 Windows 上由于有打印驱动的存在,所以需要一些特殊的前置准备工作,这个我在下面的内容里面再补充,但不变的是,在代码里面我仍然需要知道该设备的 PID 和 VID。
打印机补充
你的 USB 打印机设备,在连接电脑上的时候,Windows 给你安装的默认驱动一般是 Usbprint.sys。由于我们的 Rust 代码使用的是 libusb 这个库来进行连接的,这个驱动会导致我们无法连接,所以我们需要修改该打印机的驱动。
准备工具
- zadig.exe 用来修改打印机的驱动
- libusbK-3.1.0.0-setup.exe 用来给 libusb-win32 驱动提供支持文件
参考文档
步骤
- 启动 zadig.exe 程序,找到 gp-58 打印机设备,将设备的驱动更换为 WinUSB
- 如果在步骤 1 中,更换驱动后,设备管理器中的设备显示无法正常运行,有黄色感叹号显示,则再使用 zadig.exe 程序,将打印机设备的驱动更换为 libusb-win32
- 如果打印机设备的驱动为 WinUSB 并且设备能正常使用,就不用再进行后续操作,如果驱动为 libusb-win32 则执行步骤 4
- 启动 libusbK-3.1.0.0-setup.exe 安装 libusb-win32 驱动的支持文件
ESC/POS Rust 库
我一开始选择的是 escpos-rs 这个库,后来发现里面有些指令写的有问题,还有一些功能没提供,而且也不进行维护了,于是我就在此基础上做了自己的扩展,在这里推销一波,如果发现有什么问题,或者想要扩展什么新功能,都欢迎和我交流。
客显和电子秤
这两个设备其实很简单,因为他们都是使用串口进行通信的,所以使用 serialport 这个库进行就好。
其中客显设备是一个只需要输入数据的8位LED数码顾客显示屏,他使用一套专门的客显通用 ESC/POS 指令集,并且波特率一般是 2400。
而电子秤是一个只需要读取数据的设备,我们直接打开串口后间隔从串口获取数据即可,因为他会不断的向串口发送数据,波特率一般是 9600。
打包
一开始我这边是只有 mac 环境的,所以 Windows 程序的打包,是借助的 GITHUB Action 来进行的。
// release.yml
# 可选,将显示在 GitHub 存储库的“操作”选项卡中的工作流名称
name: Release CI
# 指定此工作流的触发器
on:
push:
# 匹配特定标签 (refs/tags)
tags:
- 'v*' # 推送事件匹配 v*, 例如 v1.0,v20.15.10 等来触发工作流
# 需要运行的作业组合
jobs:
# 任务:创建 release 版本
create-release:
runs-on: windows-latest
outputs:
RELEASE_UPLOAD_ID: ${{ steps.create_release.outputs.id }}
steps:
- uses: actions/checkout@v2
# 查询版本号(tag)
- name: Query version number
id: get_version
shell: bash
run: |
echo "using version tag ${GITHUB_REF:10}"
echo ::set-output name=version::"${GITHUB_REF:10}"
# 根据查询到的版本号创建 release
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: '${{ steps.get_version.outputs.VERSION }}'
release_name: 'cashier-web ${{ steps.get_version.outputs.VERSION }}'
body: 'See the assets to download this version and install.'
# 编译 Tauri
build-tauri:
needs: create-release
strategy:
fail-fast: false
matrix:
platform: [macos-latest, windows-latest]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v2
# 安装 Node.js
- name: Setup node
uses: actions/setup-node@v1
with:
node-version: 16
# 安装 Rust
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: stable
# 使用 Rust 缓存,加快安装速度
- uses: Swatinem/rust-cache@v1
# 获取 yarn 缓存路径
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn config get cacheFolder)"
# 使用 yarn 缓存
- name: Yarn cache
uses: actions/cache@v2
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
# 安装依赖执行构建,以及推送 github release
- name: Install app dependencies and build it
# 这里的pubhome要修改为你package.json里面配置的编译命令
run: yarn && yarn install
- uses: tauri-apps/tauri-action@dev
if: matrix.platform == 'macos-latest'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
releaseId: ${{ needs.create-release.outputs.RELEASE_UPLOAD_ID }}
- uses: tauri-apps/tauri-action@dev
if: matrix.platform == 'windows-latest'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
releaseId: ${{ needs.create-release.outputs.RELEASE_UPLOAD_ID }}
args: --target i686-pc-windows-msvc
includeDebug: true
由于我的程序一定要使用32位的程序,所以我必须要进行32位的打包。这里面值得注意的一点是 tauri-action 的正式发行版,之前是 0.3,现在好像已经更新到 0.4 了,我没试。在 0.3 这个正式版里面,是不支持你在后面加 --target i686-pc-windows-msvc 这个参数来进行32位程序的打包的,因为他就没有这个逻辑,所以我们需要修改使用的 tauri-action 的版本,改成 dev 版就行了,也就是 tauri-apps/tauri-action@dev。
再来说说本地的打包,如果你执行命令 npm run tauri build -t i686-pc-windows-msvc 来进行32位安装程序的打包,是会报错的,会告诉你他不认识这个 -t i686-pc-windows-msvc 这个后缀参数。
error: unexpected arguments 'i686-pc-windows-msvc' found
如果使用的是 npm run tauri build -t=i686-pc-windows-msvc,后面的参数就跟没加一样,只能打出当前开发环境下的安装程序。
我也暂时没搞清楚这个到底是为什么,后来尝试发现,使用 yarn run tauri build -t i686-pc-windows-msvc 就能正常在64位的环境下打包出32位的安装程序了。
结尾
暂时就先写这么多,后续还会继续补充,其实主要就是和硬件的交互十分困难,而一个收银台前端,还是用 Vue 写的,实在是没什么难点。里面有些内容我确实是有点记不得了,可能在不停的后续迭代,bug修复就又想起来了再来补充吧,欢迎大家一起讨论。