一个使用 Tauri 的收银台项目整理

4,967 阅读8分钟

技术栈

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 这个库来进行连接的,这个驱动会导致我们无法连接,所以我们需要修改该打印机的驱动。

准备工具

  1. zadig.exe 用来修改打印机的驱动
  2. libusbK-3.1.0.0-setup.exe 用来给 libusb-win32 驱动提供支持文件

参考文档

github.com/libusb/libu…

步骤

  1. 启动 zadig.exe 程序,找到 gp-58 打印机设备,将设备的驱动更换为 WinUSB
  2. 如果在步骤 1 中,更换驱动后,设备管理器中的设备显示无法正常运行,有黄色感叹号显示,则再使用 zadig.exe 程序,将打印机设备的驱动更换为 libusb-win32
  3. 如果打印机设备的驱动为 WinUSB 并且设备能正常使用,就不用再进行后续操作,如果驱动为 libusb-win32 则执行步骤 4
  4. 启动 libusbK-3.1.0.0-setup.exe 安装 libusb-win32 驱动的支持文件

ESC/POS Rust 库

我一开始选择的是 escpos-rs 这个库,后来发现里面有些指令写的有问题,还有一些功能没提供,而且也不进行维护了,于是我就在此基础上做了自己的扩展,在这里推销一波,如果发现有什么问题,或者想要扩展什么新功能,都欢迎和我交流。

escpos-rust

github.com/EmiyaGm/esc…

客显和电子秤

这两个设备其实很简单,因为他们都是使用串口进行通信的,所以使用 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修复就又想起来了再来补充吧,欢迎大家一起讨论。