704 lines
18 KiB
JavaScript
704 lines
18 KiB
JavaScript
/**
|
||
* 蓝牙称重设备服务模块
|
||
* 功能:
|
||
* - 自动连接已配置的称重设备
|
||
* - 主动读取重量:发送 'R' 命令,设备返回一次重量数据(readWeight方法)
|
||
* - 实时监听并解析重量数据(支持ASCII/二进制协议)
|
||
* - 发送控制命令(去皮、校准、单位切换等)
|
||
* - 连接状态管理与事件通知
|
||
*
|
||
* 使用方法:
|
||
* import scaleService from '@/utils/BluetoothScaleService.js';
|
||
* await scaleService.autoConnect();
|
||
*
|
||
* // 主动读取重量(发送一次命令,返回一次数据)
|
||
* const result = await scaleService.readWeight();
|
||
* console.log(result.raw); // 十六进制原始数据
|
||
* console.log(result.rawAscii); // ASCII原始数据
|
||
* console.log(result.value); // 解析后的重量值
|
||
*
|
||
* // 监听重量变化(设备主动推送模式)
|
||
* scaleService.onWeightChange(this.handleWeightChange);
|
||
*/
|
||
import bluetoothManager from './BluetoothManager.js';
|
||
|
||
class BluetoothScaleService {
|
||
constructor() {
|
||
this.isConnected = false;
|
||
this.deviceId = null;
|
||
this.deviceName = '';
|
||
this.serviceId = null;
|
||
this.notifyCharId = null;
|
||
this.writeCharId = null;
|
||
|
||
// 主动读取等待状态(发送读取命令后等待设备返回数据)
|
||
this.pendingRead = null; // { resolve, reject, timer }
|
||
this.readTimeout = 5000; // 读取超时时间(ms)
|
||
|
||
// 回调函数
|
||
this.callbacks = {
|
||
onWeightChange: null,
|
||
onConnectionChange: null,
|
||
onError: null,
|
||
onLog: null
|
||
};
|
||
|
||
// 当前重量数据
|
||
this.currentWeight = {
|
||
value: 0,
|
||
unit: 'kg',
|
||
stable: false,
|
||
raw: null,
|
||
timestamp: null
|
||
};
|
||
|
||
// 数据协议类型
|
||
this.protocolType = 'auto'; // auto, ascii, binary
|
||
|
||
// 预设的称重设备协议
|
||
this.protocols = {
|
||
// 通用ASCII协议(直接解析数字字符串)
|
||
ascii: {
|
||
pattern: /^([+-]?\d+\.?\d*)\s*(kg|g|lb|oz)?$/,
|
||
parser: (str) => {
|
||
const match = str.match(/([+-]?\d+\.?\d*)/);
|
||
const unitMatch = str.match(/(kg|g|lb|oz)/i);
|
||
return {
|
||
value: match ? parseFloat(match[1]) : null,
|
||
unit: unitMatch ? unitMatch[1].toLowerCase() : 'kg'
|
||
};
|
||
}
|
||
},
|
||
// 简易二进制协议(A12等常见电子秤)
|
||
A12: {
|
||
// 协议格式: 0x05 + 整数部分 + 小数部分 + 单位
|
||
parser: (buffer) => {
|
||
if (!buffer || buffer.length < 5) return null;
|
||
const data = new Uint8Array(buffer);
|
||
const sign = (data[0] & 0x80) ? -1 : 1;
|
||
const stable = (data[0] & 0x40) !== 0;
|
||
const decimal = (data[0] & 0x30) >> 4;
|
||
const weight = ((data[1] << 8) | data[2]) & 0x3FFF;
|
||
const value = sign * weight / Math.pow(10, decimal);
|
||
const units = ['kg', 'g', 'lb', 'oz'];
|
||
const unitIndex = data[3] & 0x03;
|
||
return {
|
||
value: value,
|
||
unit: units[unitIndex] || 'kg',
|
||
stable: stable
|
||
};
|
||
}
|
||
},
|
||
// 赛多利斯协议(示例)
|
||
Sartorius: {
|
||
parser: (buffer) => {
|
||
if (!buffer || buffer.length < 6) return null;
|
||
const data = new Uint8Array(buffer);
|
||
const value = ((data[2] << 16) | (data[3] << 8) | data[4]) / 1000;
|
||
return {
|
||
value: value,
|
||
unit: 'kg',
|
||
stable: data[1] === 0x01
|
||
};
|
||
}
|
||
},
|
||
// 梅特勒托利多协议(示例)
|
||
Mettler: {
|
||
parser: (buffer) => {
|
||
if (!buffer || buffer.length < 8) return null;
|
||
const data = new Uint8Array(buffer);
|
||
const value = parseFloat(String.fromCharCode.apply(null, data.slice(1, 7)));
|
||
return {
|
||
value: value,
|
||
unit: 'kg',
|
||
stable: data[7] === 0x0D
|
||
};
|
||
}
|
||
}
|
||
};
|
||
|
||
// 常用命令
|
||
this.commands = {
|
||
READ_WEIGHT: [0x52], // 'R' - 读取重量命令(ASCII 'R')
|
||
TARE: [0x1B, 0x54], // 去皮命令
|
||
CALIBRATE_1KG: [0x1B, 0x43, 0x01], // 1kg校准
|
||
CALIBRATE_2KG: [0x1B, 0x43, 0x02], // 2kg校准
|
||
CALIBRATE_5KG: [0x1B, 0x43, 0x03], // 5kg校准
|
||
UNIT_KG: [0x1B, 0x55, 0x01], // 切换kg
|
||
UNIT_G: [0x1B, 0x55, 0x02], // 切换g
|
||
UNIT_LB: [0x1B, 0x55, 0x03], // 切换lb
|
||
ZERO: [0x1B, 0x5A], // 零位校准
|
||
RESET: [0x1B, 0x52] // 复位
|
||
};
|
||
|
||
// 初始化监听器
|
||
this._initListeners();
|
||
}
|
||
|
||
/**
|
||
* 初始化蓝牙事件监听
|
||
*/
|
||
_initListeners() {
|
||
// 连接状态变化
|
||
bluetoothManager.setConnectionChangeCallback((deviceId, connected) => {
|
||
if (deviceId === this.deviceId) {
|
||
this.isConnected = connected;
|
||
if (!connected) {
|
||
this._log('设备连接断开');
|
||
}
|
||
if (this.callbacks.onConnectionChange) {
|
||
this.callbacks.onConnectionChange(connected);
|
||
}
|
||
}
|
||
});
|
||
|
||
// 数据接收
|
||
bluetoothManager.setDataReceivedCallback((deviceId, value) => {
|
||
if (deviceId === this.deviceId) {
|
||
this._handleDataReceived(value);
|
||
}
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 处理接收到的数据
|
||
*/
|
||
_handleDataReceived(buffer) {
|
||
try {
|
||
// 如果有等待中的主动读取请求,优先处理
|
||
if (this.pendingRead) {
|
||
const pending = this.pendingRead;
|
||
this.pendingRead = null;
|
||
clearTimeout(pending.timer);
|
||
|
||
const rawHex = this._bufferToHex(buffer);
|
||
const rawAscii = this._bufferToString(buffer);
|
||
this._log(`读取响应 - HEX: ${rawHex}, ASCII: ${rawAscii}`);
|
||
|
||
// 解析自定义协议
|
||
const weight = this._parseCustom(buffer);
|
||
pending.resolve({
|
||
raw: rawHex,
|
||
rawAscii: rawAscii,
|
||
...weight
|
||
});
|
||
return;
|
||
}
|
||
|
||
let weight = null;
|
||
|
||
// 根据协议类型解析数据
|
||
if (this.protocolType === 'auto') {
|
||
// 自动检测协议类型
|
||
weight = this._autoParse(buffer);
|
||
} else if (this.protocolType === 'ascii') {
|
||
weight = this._parseAscii(buffer);
|
||
} else {
|
||
// 二进制协议
|
||
weight = this._parseBinary(buffer);
|
||
}
|
||
|
||
if (weight) {
|
||
this.currentWeight = {
|
||
...weight,
|
||
raw: this._bufferToHex(buffer),
|
||
timestamp: new Date().toISOString()
|
||
};
|
||
|
||
this._log(`重量: ${weight.value} ${weight.unit} ${weight.stable ? '(稳定)' : '(变化中)'}`);
|
||
|
||
if (this.callbacks.onWeightChange) {
|
||
this.callbacks.onWeightChange(this.currentWeight);
|
||
}
|
||
}
|
||
} catch (error) {
|
||
this._log(`数据解析错误: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 自动检测并解析数据格式
|
||
*/
|
||
_autoParse(buffer) {
|
||
// 先尝试ASCII解析
|
||
const asciiResult = this._parseAscii(buffer);
|
||
if (asciiResult) {
|
||
return asciiResult;
|
||
}
|
||
|
||
// 尝试二进制协议解析
|
||
return this._parseBinary(buffer);
|
||
}
|
||
|
||
/**
|
||
* 解析ASCII格式数据
|
||
*/
|
||
_parseAscii(buffer) {
|
||
try {
|
||
const text = this._bufferToString(buffer);
|
||
const trimmed = text.trim();
|
||
|
||
// 匹配常见的重量格式
|
||
// 例如: "1.23 kg", "2.500kg", "+5.00 KG", "-0.50 kg"
|
||
const match = trimmed.match(/^([+-]?\d+\.?\d*)\s*(kg|g|lb|oz)?$/i);
|
||
if (match) {
|
||
return {
|
||
value: parseFloat(match[1]),
|
||
unit: match[2] ? match[2].toLowerCase() : 'kg',
|
||
stable: true
|
||
};
|
||
}
|
||
|
||
// 匹配带状态位的格式
|
||
// 例如: "ST,1.23kg" 或 "US,2.50kg"
|
||
const statusMatch = trimmed.match(/^(ST|US|OL),?([+-]?\d+\.?\d*)\s*(kg|g|lb|oz)?$/i);
|
||
if (statusMatch) {
|
||
return {
|
||
value: parseFloat(statusMatch[2]),
|
||
unit: statusMatch[3] ? statusMatch[3].toLowerCase() : 'kg',
|
||
stable: statusMatch[1].toUpperCase() === 'ST'
|
||
};
|
||
}
|
||
|
||
return null;
|
||
} catch (error) {
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 解析二进制格式数据
|
||
*/
|
||
_parseBinary(buffer) {
|
||
// 遍历所有预设的二进制协议进行尝试
|
||
for (const [name, protocol] of Object.entries(this.protocols)) {
|
||
if (name === 'ascii') continue;
|
||
try {
|
||
const result = protocol.parser(buffer);
|
||
if (result && typeof result.value === 'number' && !isNaN(result.value)) {
|
||
return result;
|
||
}
|
||
} catch (e) {
|
||
// 继续尝试下一个协议
|
||
}
|
||
}
|
||
|
||
// 默认二进制解析(A12格式)
|
||
return this.protocols.A12.parser(buffer);
|
||
}
|
||
|
||
/**
|
||
* 自定义协议解析(主动读取模式使用)
|
||
*
|
||
* 【重要】此方法用于 readWeight() 主动读取模式,当发送 'R' 命令后
|
||
* 设备返回数据时会调用此方法解析。
|
||
*
|
||
* 当前实现:先尝试自动检测(ASCII/二进制),如果都匹配不上则返回原始数据
|
||
* 如果你的设备有特殊协议格式,请取消下方注释并修改解析逻辑
|
||
*
|
||
* @param {ArrayBuffer} buffer 设备返回的原始数据
|
||
* @returns {{ value: number, unit: string, stable: boolean, rawHex: string, rawAscii: string }}
|
||
*/
|
||
_parseCustom(buffer) {
|
||
const data = new Uint8Array(buffer);
|
||
const hexStr = this._bufferToHex(buffer);
|
||
const asciiStr = this._bufferToString(buffer);
|
||
|
||
// ============================================================
|
||
// 【方式一】先尝试通用自动解析(支持常见ASCII和二进制协议)
|
||
// 大部分蓝牙秤可以直接用这个,不需要改下面的代码
|
||
// ============================================================
|
||
const autoResult = this._autoParse(buffer);
|
||
if (autoResult && autoResult.value !== null && !isNaN(autoResult.value)) {
|
||
this._log(`[自定义协议] 自动解析成功: ${autoResult.value} ${autoResult.unit}`);
|
||
return {
|
||
...autoResult,
|
||
rawHex: hexStr,
|
||
rawAscii: asciiStr.trim()
|
||
};
|
||
}
|
||
|
||
// ============================================================
|
||
// 【方式二】如果你的设备有特殊协议格式,在这里添加解析逻辑
|
||
// 取消注释并根据实际数据格式修改
|
||
// ============================================================
|
||
|
||
// --- 示例1: 十六进制帧格式 ---
|
||
// 假设设备返回 6 字节: [帧头0xAA][命令0x52][数据高位][数据低位][小数位][校验]
|
||
// 例: AA 52 00 7B 02 D1 → 重量 = 0x007B / 10^2 = 1.23 kg
|
||
//
|
||
// if (data.length >= 6 && data[0] === 0xAA && data[1] === 0x52) {
|
||
// const weightRaw = (data[2] << 8) | data[3];
|
||
// const decimal = data[4]; // 小数位数
|
||
// const value = weightRaw / Math.pow(10, decimal);
|
||
// this._log(`[自定义协议] 十六进制解析成功: ${value}`);
|
||
// return { value, unit: 'kg', stable: true, rawHex: hexStr, rawAscii: asciiStr.trim() };
|
||
// }
|
||
|
||
// --- 示例2: ASCII带前缀格式 ---
|
||
// 假设设备返回: "W:1.23kg\r\n" 或 "+00123g\r\n"
|
||
//
|
||
// const match = asciiStr.trim().match(/W:([+-]?\d+\.?\d*)\s*(kg|g|lb|oz)?/i);
|
||
// if (match) {
|
||
// this._log(`[自定义协议] ASCII解析成功: ${match[1]}`);
|
||
// return { value: parseFloat(match[1]), unit: (match[2] || 'kg').toLowerCase(), stable: true, rawHex: hexStr, rawAscii: asciiStr.trim() };
|
||
// }
|
||
|
||
// ============================================================
|
||
// 以上都没有匹配到 → 返回原始数据(value 为 null)
|
||
// 页面拿到后可以显示原始数据方便调试
|
||
// ============================================================
|
||
this._log(`[自定义协议] 未识别的格式 - HEX: ${hexStr}, ASCII: ${asciiStr.trim()}`);
|
||
return {
|
||
value: null,
|
||
unit: 'kg',
|
||
stable: false,
|
||
rawHex: hexStr,
|
||
rawAscii: asciiStr.trim()
|
||
};
|
||
}
|
||
|
||
/**
|
||
* Buffer转16进制字符串
|
||
*/
|
||
_bufferToHex(buffer) {
|
||
const data = new Uint8Array(buffer);
|
||
return Array.from(data).map(b => b.toString(16).padStart(2, '0')).join(' ');
|
||
}
|
||
|
||
/**
|
||
* Buffer转字符串
|
||
*/
|
||
_bufferToString(buffer) {
|
||
const data = new Uint8Array(buffer);
|
||
return String.fromCharCode.apply(null, data);
|
||
}
|
||
|
||
/**
|
||
* 设置协议类型
|
||
*/
|
||
setProtocol(protocol) {
|
||
if (protocol === 'auto' || protocol === 'ascii' || this.protocols[protocol]) {
|
||
this.protocolType = protocol;
|
||
this._log(`协议已切换: ${protocol}`);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取当前配置
|
||
*/
|
||
getConfig() {
|
||
return {
|
||
deviceId: this.deviceId,
|
||
deviceName: this.deviceName,
|
||
serviceId: this.serviceId,
|
||
notifyCharId: this.notifyCharId,
|
||
writeCharId: this.writeCharId,
|
||
protocol: this.protocolType,
|
||
isConnected: this.isConnected
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 从存储加载配置
|
||
*/
|
||
loadConfig() {
|
||
const scaleConfig = uni.getStorageSync('scaleConfig');
|
||
if (scaleConfig) {
|
||
this.deviceId = scaleConfig.deviceId || null;
|
||
this.deviceName = scaleConfig.deviceName || '';
|
||
this.serviceId = scaleConfig.serviceId || null;
|
||
this.notifyCharId = scaleConfig.characteristicId || null;
|
||
this.writeCharId = scaleConfig.writeCharacteristicId || null;
|
||
this._log('配置已加载');
|
||
}
|
||
return this.getConfig();
|
||
}
|
||
|
||
/**
|
||
* 自动连接已配置的设备
|
||
*/
|
||
async autoConnect() {
|
||
this.loadConfig();
|
||
|
||
if (!this.deviceId || !this.serviceId) {
|
||
throw new Error('未配置称重设备,请先在设置中配置');
|
||
}
|
||
|
||
return this.connect();
|
||
}
|
||
|
||
/**
|
||
* 连接指定设备
|
||
*/
|
||
async connect() {
|
||
if (!this.deviceId) {
|
||
throw new Error('缺少设备ID');
|
||
}
|
||
|
||
try {
|
||
this._log('正在初始化蓝牙...');
|
||
await bluetoothManager.init();
|
||
|
||
this._log(`正在连接设备: ${this.deviceName || this.deviceId}`);
|
||
const result = await bluetoothManager.connectKnownDevice(
|
||
this.deviceId,
|
||
this.serviceId,
|
||
this.writeCharId,
|
||
this.notifyCharId
|
||
);
|
||
|
||
// 更新配置
|
||
this.serviceId = result.serviceId;
|
||
this.notifyCharId = result.notifyCharId;
|
||
this.writeCharId = result.writeCharId;
|
||
this.isConnected = true;
|
||
|
||
this._log('连接成功');
|
||
|
||
// 通知连接状态
|
||
if (this.callbacks.onConnectionChange) {
|
||
this.callbacks.onConnectionChange(true);
|
||
}
|
||
|
||
return true;
|
||
} catch (error) {
|
||
this._log(`连接失败: ${error.message}`, 'error');
|
||
this.isConnected = false;
|
||
|
||
if (this.callbacks.onError) {
|
||
this.callbacks.onError(error);
|
||
}
|
||
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 断开连接
|
||
*/
|
||
async disconnect() {
|
||
if (this.deviceId) {
|
||
await bluetoothManager.disconnectDevice(this.deviceId);
|
||
}
|
||
this.isConnected = false;
|
||
this._log('已断开连接');
|
||
|
||
if (this.callbacks.onConnectionChange) {
|
||
this.callbacks.onConnectionChange(false);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 发送命令
|
||
*/
|
||
async sendCommand(command) {
|
||
if (!this.isConnected) {
|
||
throw new Error('设备未连接');
|
||
}
|
||
|
||
let cmdData;
|
||
if (Array.isArray(command)) {
|
||
cmdData = new Uint8Array(command);
|
||
} else if (this.commands[command]) {
|
||
cmdData = new Uint8Array(this.commands[command]);
|
||
} else {
|
||
throw new Error('未知的命令');
|
||
}
|
||
|
||
this._log(`发送命令: ${Array.from(cmdData).map(b => b.toString(16).padStart(2, '0')).join(' ')}`);
|
||
|
||
try {
|
||
await bluetoothManager.writeData(this.deviceId, cmdData.buffer);
|
||
return true;
|
||
} catch (error) {
|
||
this._log(`命令发送失败: ${error.message}`, 'error');
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 主动读取重量
|
||
* 向设备发送 'R' 命令,等待设备返回重量数据
|
||
* 一次发送一次返回,不会持续读取
|
||
*
|
||
* @param {number} [timeout=5000] 等待设备响应超时时间(ms)
|
||
* @returns {Promise<{raw: string, rawAscii: string, value: number|null, unit: string, stable: boolean}>}
|
||
*
|
||
* 返回值说明:
|
||
* - raw: 设备返回的十六进制字符串,例如 "aa 52 00 7b 02 d1"
|
||
* - rawAscii: 设备返回的ASCII字符串,例如 "1.23kg"
|
||
* - value: 解析后的重量数值(需在 _parseCustom 中实现解析逻辑,默认为 null)
|
||
* - unit: 单位(默认 'kg')
|
||
* - stable: 是否稳定(默认 false)
|
||
*
|
||
* 使用示例:
|
||
* const result = await scaleService.readWeight();
|
||
* console.log(result.raw); // "aa 52 00 7b 02 d1"
|
||
* console.log(result.rawAscii); // "1.23kg"
|
||
* console.log(result.value); // null(需实现 _parseCustom 后才有值)
|
||
*/
|
||
async readWeight(timeout) {
|
||
if (!this.isConnected) {
|
||
throw new Error('设备未连接');
|
||
}
|
||
|
||
if (this.pendingRead) {
|
||
throw new Error('已有读取请求等待中,请勿重复发送');
|
||
}
|
||
|
||
const actualTimeout = timeout || this.readTimeout;
|
||
|
||
return new Promise(async (resolve, reject) => {
|
||
// 设置超时定时器
|
||
const timer = setTimeout(() => {
|
||
this.pendingRead = null;
|
||
reject(new Error(`读取重量超时(${actualTimeout}ms),设备未返回数据`));
|
||
}, actualTimeout);
|
||
|
||
// 注册等待回调
|
||
this.pendingRead = { resolve, reject, timer };
|
||
|
||
try {
|
||
// 发送读取命令 'R'
|
||
await this.sendCommand('READ_WEIGHT');
|
||
this._log('已发送读取重量命令,等待设备响应...');
|
||
} catch (error) {
|
||
// 发送失败,清除等待状态
|
||
clearTimeout(timer);
|
||
this.pendingRead = null;
|
||
reject(error);
|
||
}
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 去皮(归零)
|
||
*/
|
||
async tare() {
|
||
return this.sendCommand('TARE');
|
||
}
|
||
|
||
/**
|
||
* 砝码校准
|
||
* @param {number} weight 校准重量(默认1kg)
|
||
*/
|
||
async calibrate(weight = 1) {
|
||
let command;
|
||
if (weight <= 1) {
|
||
command = 'CALIBRATE_1KG';
|
||
} else if (weight <= 2) {
|
||
command = 'CALIBRATE_2KG';
|
||
} else {
|
||
command = 'CALIBRATE_5KG';
|
||
}
|
||
return this.sendCommand(command);
|
||
}
|
||
|
||
/**
|
||
* 切换单位
|
||
* @param {string} unit kg, g, lb
|
||
*/
|
||
async changeUnit(unit) {
|
||
const unitMap = {
|
||
'kg': 'UNIT_KG',
|
||
'g': 'UNIT_G',
|
||
'lb': 'UNIT_LB'
|
||
};
|
||
const command = unitMap[unit];
|
||
if (!command) {
|
||
throw new Error('不支持的单位');
|
||
}
|
||
return this.sendCommand(command);
|
||
}
|
||
|
||
/**
|
||
* 获取当前重量
|
||
*/
|
||
getCurrentWeight() {
|
||
return { ...this.currentWeight };
|
||
}
|
||
|
||
/**
|
||
* 获取实时重量(带稳定性等待)
|
||
*/
|
||
async getStableWeight(timeout = 10000) {
|
||
return new Promise((resolve, reject) => {
|
||
const startTime = Date.now();
|
||
|
||
const checkWeight = () => {
|
||
if (this.currentWeight.stable) {
|
||
resolve(this.currentWeight);
|
||
} else if (Date.now() - startTime > timeout) {
|
||
reject(new Error('等待重量稳定超时'));
|
||
} else {
|
||
setTimeout(checkWeight, 100);
|
||
}
|
||
};
|
||
|
||
checkWeight();
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 发送自定义命令
|
||
*/
|
||
async sendRawCommand(commandArray) {
|
||
return this.sendCommand(commandArray);
|
||
}
|
||
|
||
/**
|
||
* 注册重量变化回调
|
||
*/
|
||
onWeightChange(callback) {
|
||
this.callbacks.onWeightChange = callback;
|
||
}
|
||
|
||
/**
|
||
* 取消重量变化回调
|
||
*/
|
||
offWeightChange() {
|
||
this.callbacks.onWeightChange = null;
|
||
}
|
||
|
||
/**
|
||
* 注册连接状态回调
|
||
*/
|
||
onConnectionChange(callback) {
|
||
this.callbacks.onConnectionChange = callback;
|
||
}
|
||
|
||
/**
|
||
* 取消连接状态回调
|
||
*/
|
||
offConnectionChange() {
|
||
this.callbacks.onConnectionChange = null;
|
||
}
|
||
|
||
/**
|
||
* 注册错误回调
|
||
*/
|
||
onError(callback) {
|
||
this.callbacks.onError = callback;
|
||
}
|
||
|
||
/**
|
||
* 取消错误回调
|
||
*/
|
||
offError() {
|
||
this.callbacks.onError = null;
|
||
}
|
||
|
||
/**
|
||
* 记录日志
|
||
*/
|
||
_log(message, level = 'info') {
|
||
console[level === 'error' ? 'error' : 'log'](`[称重服务] ${message}`);
|
||
if (this.callbacks.onLog) {
|
||
this.callbacks.onLog(message, level);
|
||
}
|
||
}
|
||
}
|
||
|
||
export default new BluetoothScaleService();
|