Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
347 changes: 300 additions & 47 deletions pages/index/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const LAST_CONNECTED_DEVICE = 'last_connected_device'
const PrinterJobs = require('../../printer/printerjobs')
const printerUtil = require('../../printer/printerutil')
const imageUtil = require('../../utils/imageutil')

function inArray(arr, key, val) {
for (let i = 0; i < arr.length; i++) {
Expand Down Expand Up @@ -36,7 +37,12 @@ Page({
data: {
devices: [],
connected: false,
chs: []
chs: [],
isPrinting: false,
printProgress: 0,
printStatusText: '',
selectedImage: null,
imageData: null
},
onUnload() {
this.closeBluetoothAdapter()
Expand Down Expand Up @@ -226,67 +232,314 @@ Page({
})
},
writeBLECharacteristicValue() {
let printerJobs = new PrinterJobs();
printerJobs
.print('2018年12月5日17:34')
.print(printerUtil.fillLine())
.setAlign('ct')
.setSize(2, 2)
.print('#20饿了么外卖')
.setSize(1, 1)
.print('切尔西Chelsea')
.setSize(2, 2)
.print('在线支付(已支付)')
.setSize(1, 1)
.print('订单号:5415221202244734')
.print('下单时间:2017-07-07 18:08:08')
.setAlign('lt')
.print(printerUtil.fillAround('一号口袋'))
.print(printerUtil.inline('意大利茄汁一面 * 1', '15'))
.print(printerUtil.fillAround('其他'))
.print('餐盒费:1')
.print('[赠送康师傅冰红茶] * 1')
.print(printerUtil.fillLine())
.setAlign('rt')
.print('原价:¥16')
.print('总价:¥16')
.setAlign('lt')
.print(printerUtil.fillLine())
.print('备注')
.print("无")
.print(printerUtil.fillLine())
.println();
this.startPrintJob(() => {
let printerJobs = new PrinterJobs();
printerJobs
.print('2018年12月5日17:34')
.print(printerUtil.fillLine())
.setAlign('ct')
.setSize(2, 2)
.print('#20饿了么外卖')
.setSize(1, 1)
.print('切尔西Chelsea')
.setSize(2, 2)
.print('在线支付(已支付)')
.setSize(1, 1)
.print('订单号:5415221202244734')
.print('下单时间:2017-07-07 18:08:08')
.setAlign('lt')
.print(printerUtil.fillAround('一号口袋'))
.print(printerUtil.inline('意大利茄汁一面 * 1', '15'))
.print(printerUtil.fillAround('其他'))
.print('餐盒费:1')
.print('[赠送康师傅冰红茶] * 1')
.print(printerUtil.fillLine())
.setAlign('rt')
.print('原价:¥16')
.print('总价:¥16')
.setAlign('lt')
.print(printerUtil.fillLine())
.print('备注')
.print("无")
.print(printerUtil.fillLine())
.println();

let buffer = printerJobs.buffer();
console.log('ArrayBuffer', 'length: ' + buffer.byteLength, ' hex: ' + ab2hex(buffer));
// 1.并行调用多次会存在写失败的可能性
// 2.建议每次写入不超过20字节
// 分包处理,延时调用
const maxChunk = 20;
const delay = 20;
for (let i = 0, j = 0, length = buffer.byteLength; i < length; i += maxChunk, j++) {
let subPackage = buffer.slice(i, i + maxChunk <= length ? (i + maxChunk) : length);
setTimeout(this._writeBLECharacteristicValue, j * delay, subPackage);
}
return printerJobs.buffer();
});
},
_writeBLECharacteristicValue(buffer) {
/**
* 写入BLE特征值(单包数据发送)
*
* 详细记录每个数据包的发送情况,包括:
* - 包序号和总包数
* - 数据包内容(十六进制)
* - 发送结果(成功/失败)
*
* @param {ArrayBuffer} buffer - 要发送的数据包
* @param {number} packageNumber - 当前包序号(从1开始)
* @param {number} totalPackages - 总包数
*/
_writeBLECharacteristicValue(buffer, packageNumber = 0, totalPackages = 0) {
// 详细的发送前日志
if (packageNumber > 0) {
console.log(`\n--- 发送第${packageNumber}/${totalPackages}包数据 ---`);
console.log(`数据长度: ${buffer.byteLength} 字节`);
console.log(`数据十六进制: ${ab2hex(buffer)}`);
console.log(`时间戳: ${new Date().toLocaleTimeString()}`);
}

wx.writeBLECharacteristicValue({
deviceId: this._deviceId,
serviceId: this._serviceId,
characteristicId: this._characteristicId,
value: buffer,
success(res) {
console.log('writeBLECharacteristicValue success', res)
success: (res) => {
console.log(`✅ 第${packageNumber}/${totalPackages}包发送成功`, res);

// 更新打印进度
if (packageNumber > 0) {
this.updatePrintProgress();
}
},
fail(res) {
console.log('writeBLECharacteristicValue fail', res)
fail: (res) => {
console.error(`❌ 第${packageNumber}/${totalPackages}包发送失败`, res);
console.error(`错误详情: errCode=${res.errCode}, errMsg=${res.errMsg}`);

// 可以在这里添加重试逻辑
if (packageNumber > 0 && res.errCode === 10008) {
console.warn('特征值不支持写入操作,请检查特征值属性');
}
}
})
},

/**
* 开始打印任务
*
* 打印数据分包发送管理:
* 1. 获取完整的打印数据Buffer
* 2. 计算分包数量和进度
* 3. 按顺序发送每个数据包
* 4. 实时更新发送进度和状态
*
* @param {Function} getBufferFunc - 获取打印数据的函数,返回ArrayBuffer
*
* 分包策略:
* - 每包最大20字节(微信小程序BLE限制)
* - 包间延时20ms,避免发送过快
* - 实时显示发送进度 [当前包/总包数]
*
* @example
* this.startPrintJob(() => {
* let printerJobs = new PrinterJobs();
* printerJobs.print('测试内容');
* return printerJobs.buffer();
* });
*/
startPrintJob(getBufferFunc) {
try {
const buffer = getBufferFunc();

// 详细的调试信息输出
console.log('=== 打印任务开始 ===');
console.log(`总数据长度: ${buffer.byteLength} 字节`);
console.log(`完整数据十六进制: ${ab2hex(buffer)}`);

// 设置打印状态
this.setData({
isPrinting: true,
printProgress: 0,
printStatusText: '准备打印...'
});

// 计算分包策略
const maxChunk = 20; // 微信小程序BLE单次写入最大字节数
const totalPackages = Math.ceil(buffer.byteLength / maxChunk);
this._totalPackages = totalPackages;
this._currentPackage = 0;

console.log(`分包策略: 每包${maxChunk}字节,共${totalPackages}包`);

// 分包处理,延时调用
const delay = 20; // 包间延时,避免发送过快

for (let i = 0, j = 0, length = buffer.byteLength; i < length; i += maxChunk, j++) {
// 计算当前包的起始和结束位置
const startPos = i;
const endPos = Math.min(i + maxChunk, length);
const subPackage = buffer.slice(startPos, endPos);

// 调试输出每个分包的信息
console.log(`第${j + 1}包: 位置[${startPos}-${endPos}], 长度${subPackage.byteLength}字节`);
console.log(`第${j + 1}包十六进制: ${ab2hex(subPackage)}`);

// 设置定时器发送数据包
setTimeout(this._writeBLECharacteristicValue, j * delay, subPackage, j + 1, totalPackages);
}

// 设置完成定时器(预留1秒缓冲时间)
const totalTime = totalPackages * delay + 1000;
console.log(`预计发送完成时间: ${totalTime}ms`);

setTimeout(() => {
this.completePrintJob();
}, totalTime);

} catch (error) {
console.error('打印任务失败:', error);
this.setData({
isPrinting: false,
printStatusText: '打印失败: ' + error.message
});
}
},

/**
* 更新打印进度
*
* 实时计算并显示当前打印进度:
* - 计算百分比进度
* - 更新UI状态
* - 记录进度日志
*/
updatePrintProgress() {
this._currentPackage++;
const progress = Math.round((this._currentPackage / this._totalPackages) * 100);

console.log(`📊 打印进度更新: ${this._currentPackage}/${this._totalPackages} = ${progress}%`);

this.setData({
printProgress: progress,
printStatusText: `正在传输 [${this._currentPackage}/${this._totalPackages}]`
});
},

/**
* 完成打印任务
*
* 处理打印完成状态:
* 1. 设置100%进度
* 2. 显示成功消息
* 3. 3秒后自动隐藏状态
*
* 如果所有包都成功发送,显示成功消息;
* 否则显示警告信息。
*/
completePrintJob() {
const allPackagesSent = this._currentPackage >= this._totalPackages;

if (allPackagesSent) {
console.log('✅ 打印任务完成:所有数据包发送成功');
this.setData({
printProgress: 100,
printStatusText: '打印指令发送成功'
});
} else {
console.warn(`⚠️ 打印任务完成:仅发送了 ${this._currentPackage}/${this._totalPackages} 包`);
this.setData({
printProgress: Math.round((this._currentPackage / this._totalPackages) * 100),
printStatusText: '打印数据发送不完整'
});
}

// 3秒后隐藏状态
setTimeout(() => {
console.log('打印状态UI已隐藏');
this.setData({
isPrinting: false,
printProgress: 0,
printStatusText: ''
});

// 重置计数器
this._currentPackage = 0;
this._totalPackages = 0;
}, 3000);
},
closeBluetoothAdapter() {
wx.closeBluetoothAdapter()
this._discoveryStarted = false
},
/**
* 打印二维码
*/
printQRCode() {
this.startPrintJob(() => {
let printerJobs = new PrinterJobs();
printerJobs
.setAlign('ct')
.print('扫码关注')
.qrcode('https://github.com/your-repo', 8, 'M')
.lineFeed(2);

return printerJobs.buffer();
});
},

/**
* 选择并预览图片
*/
printImage() {
imageUtil.chooseAndProcessImage()
.then(result => {
this.setData({
selectedImage: result.imagePath,
imageData: {
bitmapData: result.bitmapData,
width: result.width,
height: result.height
}
});
})
.catch(error => {
wx.showToast({
title: '图片处理失败',
icon: 'none'
});
console.error('图片处理失败:', error);
});
},

/**
* 确认打印图片
*/
confirmPrintImage() {
if (!this.data.imageData) {
wx.showToast({
title: '没有图片数据',
icon: 'none'
});
return;
}

this.startPrintJob(() => {
let printerJobs = new PrinterJobs();
printerJobs
.setAlign('ct')
.print('图片打印')
.bitmap(this.data.imageData.bitmapData, this.data.imageData.width, this.data.imageData.height)
.lineFeed(2);

return printerJobs.buffer();
});

// 清除预览
this.setData({
selectedImage: null,
imageData: null
});
},

/**
* 取消打印图片
*/
cancelPrintImage() {
this.setData({
selectedImage: null,
imageData: null
});
},

onLoad(options) {
const lastDevice = wx.getStorageSync(LAST_CONNECTED_DEVICE);
this.setData({
Expand Down
Loading