连接蓝牙
- 初始化蓝牙模块、找到对应连接的打印机
Bluetooth
- 连接蓝牙
BLE
- 找到支持写入数据
write
、订阅变化notify
的特征值characteristic
打印
- 根据打印机提供API,拼接打印指令
SIZE 40 mm,20 mm\r\n GAP 2 mm,0 mm\r\n CLS\r\n QRCODE 12,28,L,3,A,0,"http://172.16.0.20:15016?O22CdW14$A00002073106"\r\n TEXT 116,32,"3",0,1,1,"6233"\r\n TEXT 116,64,"3",0,1,1,"220704SK1001"\r\n TEXT 116,96,"3",0,1,1,"0"\r\n PRINT 1,1\r\n
- 一张一张标签发送打印指令
- 打印指令发送失败,记录索引下标,标签打印指令全部发送完成,弹窗提示是否重新打印失败的标签。
- 标签发送失败
failArr
记录下标,上一张标签发送指令失败(发送的指令不知在哪个位置发生截断),在数组起始位置添加换行符\r\n
,使下一张标签的指令生效(都是记录思路的伪代码!)/** * @param {Array} printData 需要打印的数据数组, 一项代表一张标签 */ async function _print (printData = []) { const number = 1, // 几张标签调用函数发送一次打印指令 failArr = []; // 记录失败的标签 let command = [], num = 0, percentage = 0; // 进度 for (let i = 0, len = printData.length; i < len; i++) { command = [...]; // ... 拼接打印指令 调用打印机API转为数组 num++; if ((number === num) || ((i === len - 1) && num > 0)) { // 发送打印指令 const arr = await handleSend(i - number + 1, i, command); // 调用函数发送打印指令 if (arr.length) { // 打印指令发送失败 记录索引下标 failArr.push(...arr); } // 重新记录打印指令 num = 0; command = []; if (arr.length) { // 上一张标签的打印指令发送失败, 在下一张标签起始位置添加换行符, 使CLS生效 command.push(...); // EOL } } percentage = Math.ceil((i + 1) / len * 100); // 更新进度 } if (failArr.length) { // 处理发送失败的标签 const arr = printData.filter((item, index) => (failArr.indexOf(index) != -1)); // 弹窗提示:以下编号的标签打印失败:8、16,是否需要重新打印? // 如果用户选择重新打印 return this._print(arr); } }
- 发送打印指令(
number
张标签) - 若打印指令发送失败,手动添加换行符
\r\n
,使CLS
生效,重试3次 - 若发送失败,返回打印指令发送失败的索引下标
async function handleSend(begin, end, command) { const retryCount = 3; // 定义重试次数 let complete = false, // 是否打印完成 tryNum = 0; // 尝试打印次数 while (!complete && (tryNum < (retryCount + 1))) { tryNum++; if (tryNum == 2) { // 指令发送失败 直接结束循环 重新发送打印指令(尝试打印的第2次) // 在数组起始位置添加换行符 (使CLS生效) command.unshift(...); // EOL } const hasFail = await send(command); if (hasFail) { console.log('--- 重新打印 --->', end); } complete = !hasFail; } const arr = []; if (!complete) { for (let j = begin; j <= end; j++) { arr.push(j); // 打印指令发送失败的标签索引 } } return arr; }
- 分包发送打印指令:若某次指令发送失败,直接结束循环,结束函数
- 通过蓝牙写入数据发送打印指令,官网建议每次写入不超过20字节
oneTimeData
async function send(buff) { const oneTimeData = 20, // 每次发送字节数 printNum = 1; // 打印份数 const loopTime = Math.ceil(buff.length / oneTimeData), // 一份发送几次 = buff / 每次发送字节数 lastData = parseInt(buff.length % oneTimeData); for (let i = 0; i < printNum; i++) { // printNum 份 // 第 i 份 for (let j = 0; j < loopTime; j++) { // 循环发送 // 分包发送的第 j 次 const size = (j == loopTime - 1 && lastData > 0) ? lastData : oneTimeData; // 是否余数 ? lastData : 每次发送字节数 const buf = new ArrayBuffer(size); const dataView = new DataView(buf); for (let k = 0; k < size; k++) { const index = j * oneTimeData + k; dataView.setUint8(k, buff[index]); } const flag = await handleSendItem(buf, j, loopTime); if (!flag) { console.warn(`fail: 发送的第${j}次, 共发送${loopTime}次 (经多次重试失败)`); // 指令发送失败 直接结束循环 不继续往后发 return true; } } } return false; }
- 问题:不确定哪次指令发送失败,直接结束循环,可能导致打印机走纸,但是没有打印内容,也可能中间几张标签丢失(没想明白原因)
- 解决方法:
handleSendItem
函数内部添加逻辑,如果某个包(20字节)发送失败,这个包重试3次。- 若仍旧失败,再走之前逻辑,问题依旧在,只是概率小
async function handleSendItem(buf, j, loopTime) { const retryCount = 3; // 定义重试次数 let complete = false, // 指令是否发送完成 tryNum = 0; // 尝试发送次数 while (!complete && (tryNum < (retryCount + 1))) { tryNum++; if (tryNum > 1) { console.log('重新发送这个包', tryNum); } // 调用 uni.writeBLECharacteristicValue 向低功耗蓝牙设备特征值中写入二进制数据 发送打印指令 const flag = await writeBLECharacteristicValue(buf); if (!flag) { console.warn(`fail: 发送的第${j}次, 共发送${loopTime}次`); } complete = flag; } return complete; }
流程图
流程图:标签打印(语雀)
问题
- 某一张标签指令发送失败,直接结束循环,重新发送指令,或直接发送下一张标签指令
- 往数组前添加换行符
EOL
使CLS
生效,可能导致空标签、几张标签漏打印
- 往数组前添加换行符
- 解决方法:
- 某个包发送失败,这个包重新发送,重试三次
- 若仍旧失败,走之前流程(结束循环,发送下一张标签指令)
- 问题依旧在,只是概率小 ~( TロT)σ
优化
handleSend
、handleSendItem
通过调用函数_try
传入参数tryNum
(重试次数)、fn
(处理函数)处理重试逻辑function isPromise(obj) { return obj instanceof Promise; // return (!!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function'); } /** * 传入参数tryNum(重试次数)、fn(处理函数)处理重试逻辑 * @param {Function} fn 处理函数 返回Boolean(是否成功); 或者返回Promise, thenable返回状态数据Boolean(是否成功) * @param {Number} retryCount 重试次数 * @returns {Promise} 是否执行成功 */ async function _try(fn, retryCount = 3) { if (retryCount < 0) { console.warn('retryCount must be greater than 0'); retryCount = 0; } let tryNum = 0, // 执行次数 complete = false; // 是否完成 while (!complete && (tryNum < (retryCount + 1))) { tryNum++; if (tryNum > 1) { console.log(`重试第${tryNum - 1}次`); } let result = fn.call(this, tryNum); if (isPromise(result)) { result = await result; } if (result) { // 执行成功 complete = true; } } return complete; }
- 优化
handleSend
async function handleSend(begin, end, command) { const complete = await _try(async (tryNum) => { // 处理函数 if (tryNum == 2) { // 指令发送失败 直接结束循环 重新发送打印指令(尝试打印的第2次) // 在数组起始位置添加换行符 (使CLS生效) command.unshift(...); // EOL } const hasFail = await send(command); return !hasFail; }, 3); // 重试3次 const arr = []; if (!complete) { for (let j = begin; j <= end; j++) { arr.push(j); // 打印指令发送失败的标签索引 } } return arr; }
- 优化
handleSendItem
async function handleSendItem(buf, j, loopTime) { const complete = await _try(() => { // 调用 uni.writeBLECharacteristicValue 向低功耗蓝牙设备特征值中写入二进制数据 发送打印指令 const flag = await writeBLECharacteristicValue(buf); if (!flag) { console.warn(`fail: 发送的第${j}次, 共发送${loopTime}次`); } return flag; }, 3); // 重试3次 return complete; }