Skip to content

小白盒 - 扫码出餐实现&优化

之前在华建那栋楼 3 楼大家经常听到滴滴滴、蓝牙已连接、蓝牙已断开的烦人声音就是在做这个需求。最近已经听不到这个声音,因为终于!这个功能是算趋于稳定了。没有特别大的问题了。下面跟大家分享一下这个需求前前后后遇到的一些问题。

做这个需求可以说是难度和复杂度都是我做过算是比较高的需求了,涉及硬件和很多不稳定的因为。也是踩了比较多的坑和学习到对我来说很新颖的技术。跟大家简单的分享一下。希望我分享的内容对各位能有些许帮助。

以下记录小白盒开发过程中遇到的代表性问题和解决的思路。

需求

为公司战略发展提供技术支持,让收银系统支持有线和无线两种方式进行餐品出餐。

实现方案

有线和无线、方案实现思路有所不同。

门店使用的 pos 设备分为 ipad 和 传统收银机

有线方案

有线方案是针对门店收银机(点餐机),利用有线的扫码盒(usb)。

扫码盒图示

这设备是怎么和软件通信呢?

在没有真正接触它之前,我觉得这个东西可能特别复杂,涉及 usb 链接等等之类。还没做之前就在担心这块可能会出现之后的坑。

真正接触之后顿感其实没有那么复杂,甚至还挺简单的。这种有线的设备其实跟我们键盘一样,都是一种输入设备,就像我们插电脑上的键盘。只不过它强大的点在于能够识别二维码。它能将二维码真正的内容识别出来,再像键盘一样输到我们系统中。

实现逻辑

有了前面的知识点和内容之后,实现起来其实就简单了。只需要监听系统的键盘事件即可。

店员扫了出餐码之后,将二维码/一维码的内容识别出来之后我们系统捕获到这些值。调用后端的出餐接口即可。大致实现如下:

export function useScanOrder(){
  useEffect(() => {
    document.addEventListener('keyup', (e) => {
      // 出餐操作
    })
  }, [])
}
export function useScanOrder(){
  useEffect(() => {
    document.addEventListener('keyup', (e) => {
      // 出餐操作
    })
  }, [])
}

存在的问题

当应用失去焦点之后,此时的document.addEventListener 失效,无论如何扫码,都没有办法将值传递到我们系统中。

而门店经常会将软件最小化,或点到其他软件页面(美团、饿了么)等。

这种情况捕获不到数据本质上正常的,就比如我们用手机微信扫码时,不会将这个值传到支付宝去。但是门店觉得这是不好用的。所以只能继续探索。

探索思路

windows 系统监听到键盘输入,将值传递给我们应用(区别于之前是应用页面监听键盘事件)

方案

通过 node 调用 node-gyp 调用 python 调用 C++,将 C++打包成一个.node 文件,我们就可以调用这个 node 文件。

我写了个简单的 demo,感兴趣的小伙伴可以看下这个demo

C++是底层语言,能够捕获到 windows 级别的一些键盘事件。

不是此次分享重点,这块监听键盘事件的 C++代码是宇哥写的,且需要 C++功底才能看懂这块代码。后续领悟了有机会再进行分享。

other

借助 node-gyp 可以让我们前端(js)完成一些本身不能实现的事情。分享一个库:node-sleep

js 本身不存在 sleep 这个函数,我们可以自己封装 sleep 函数,如:

ts
function sleep(time) {
	return new Promise(resolve => {
		setTimeout(() => {
			resolve()
		}, time)
	})
}
function fnA() {
	console.log('A')
}
async function sleepTest() {
	fnA() // 输出 A
	await sleep(1000) // 睡眠 1 秒
	console.log('E') // 输出 E
}
sleepTest()
function sleep(time) {
	return new Promise(resolve => {
		setTimeout(() => {
			resolve()
		}, time)
	})
}
function fnA() {
	console.log('A')
}
async function sleepTest() {
	fnA() // 输出 A
	await sleep(1000) // 睡眠 1 秒
	console.log('E') // 输出 E
}
sleepTest()

这是我们比较可能会写出的 sleep 逻辑,但是这里其实也是异步的,借助了 promise。用起来是没有后端代码那种直接可用的 sleep 好用的。用这个库就可以直接使用!

ts
var sleep = require('sleep')

sleep.sleep(1)
console.log('hahaha')
var sleep = require('sleep')

sleep.sleep(1)
console.log('hahaha')

官方单测

取得成果

有线方案的问题比较少,且都解决了,因为有线连接的性能,相对稳定。

但因为后期门店会逐步去掉这种点餐 POS 机器,会转向轻 POS,也就是 ipad,所以重点是要让无线方案做到稳定和好用 💪🏻

无线方案

什么设备能与我们软件进行通信呢?

我们采用的是蓝牙方案,因为蓝牙是无线的。需要找到一款蓝牙的扫码盒。再让设备与蓝牙连接就好了。

实现逻辑

连接示意图

核心工具库:

react-native-ble-plx 这个蓝牙库在 github 上拥有 2.7k 的 ⭐️。但同时也有一堆的 issue 😓

实现思路上就是在软件内使用这个库去连接支持 ble 模式的 扫码盒。连接成功之后即可监听蓝牙传输数据。当扫码盒扫到值后会被我们系统捕获,此时去调用出餐接口。

连接视频

存在的问题

没想到无线方案存在着一堆的问题,且范围涉及之广波及前后端。以下列举几个比较具有代表性和恶心的视频,大家可以猜测下大致是什么原因导致的。

所以的问题门店测给到的反馈都是:扫不了,出餐不了。其实没有什么营养价值,所以那段时间我和后端和测试小伙伴就用了一个最笨也是最无奈的方法:在十二时辰店蹲点开发。店员每扫一单都观察,看看具体会出现什么问题。果然这样以后就查到了一些端倪。

  • 盒子莫名其妙关机

    故障视频

    设备质量问题,第一款是淘宝买的,后续换了另外一款设备。

  • 打印机不同,导致部分一维码无法识别

    使用飞蛾打印机打出的部分小票小白盒无法识别,使用芯烨打印机则没有这个问题。

    第一想法是那让门店用芯烨打印机打印小票就好了。不过据说全国大部分门店都采用的是飞蛾打印机......

  • 扫了第一个小票之后出餐成、5 秒后扫第二个小票,小白盒无反应。

    视频 1

    视频 2

    两个视频前后手拍的,其中一个就是无法识别,大家觉得可能是什么原因导致的呢 🤔?


    我的第一想法是盒子出现故障,死机了。后续十二时辰门店也陆续反馈了这个问题。最终定位到是生成的一维码的问题。

    生成的一维码有概率会无法识别。盒子本身主要是用来扫屏幕码,小票属于纸质码。换个角度也可以说是盒子本身的问题。之后采购部购买了一款 ¥500 元左右的红外线扫码机器,就可以扫描到所有的码了。

    但是这个方案门店大概率会拒绝,主要是价格是原来的两三倍。

    最终解决方案是将一维码调整成二维码

    二维码比较大,好识别,且一维码本身能够显示的数据就非常有限。使用二维码解决了这个问题

  • 软件切到后台后无法接收数据

    有线的方案还可以通过 node-gyp 来做一些骚操作实现,但是 ios 端就不行了,苹果应用只要挂到后台之后程序只有一个极短的保活时间,保活时间一过我们的程序就假死状态了,所有监听都不会生效。

    这也就是为什么苹果引以为傲的省电大法。因为不让程序活了。

    目前只能跟店员说,保持软件在前台

  • 屏幕息屏之后。程序也”失活“

    当软件在前台时,保持软件常亮。

  • 设备长期未关机,造成设备死机。

  • 稳定性问题

    门店会间歇性的反馈小白盒断连的问题。这一块有原先程序设计不足的问题。经过几个迭代后断连问题减少了非常多。但是因为蓝牙连接本身就是一种不稳定性,且与软件存在强绑定。所以只能是尽量做一些边缘稳定操作。

    • 断线重连
    • 异步连接

取得成果

经过几次迭代,出餐率就相对比较稳定。没有出现大规模一大片没有出餐的数据。

成果图片

后续

转向云扫码。云扫码的实现思路就杜绝上诉的大部分问题。原理是扫码盒自己有能力独自传输信息给后端服务器(前台是在有网的环境)这样就能够将断连的概率降低非常多。

最理想的情况下就是,只要店里网络不断,就能一直保持工作

总结

总结下来这个需求在做的时候自己犯下了一些毛病。导致自己徒增自己的工作量。主要是下面几点吧。不知道大家有没有和我一样的时候。

  • 比较急躁

    硬件设备不好调试,且没接触过,心理层面上就比较怕它了。导致一些代码写的比较急,被小问题能卡很久。

  • 前期写了比较多的屎山 💩⛰

    最初的迭代是只能连接一个设备,但是在开发时已经预料到,后面产品肯定是会提出要能够同时连接多台设备的,但是那时候图快,图舒服。没有往多设备兼容那块去考虑,写的一手的面向过程的代码。等到兼容多设备需求出来的时候,直接寄了。

    其实当初第一个迭代的时候想着,时间催的这么急,能跑就行了,后面优化就交给下一个接受的倒霉蛋吧。结果这个类型的需求直接全部都是我自己跟了,等于有点自食其果,后面用面向对象的方式又重构了一版本。

  • 比较无力,也很无奈

    当初这个需求领导层面也非常关注,别的部分也非常关注,等于全国在等我们研发这边出这个功能,最严重的时候知道自己这个没做好就要领 s0 事故了。但是就是非常无奈,硬件有时候断连,我也没有办法。

总结下来就是还是要有信息吧。所谓兵来将挡水来土掩,我现在其实也不知道怎么做着做着就好起来了。凡事尽力就好。