一、核心代码
core.js
c
import {
showModal,
stringToHex,
sleep,
uniqueArr,
arrayBufferToHexString,
hexStringToArrayBuffer,
compareVersion
} from './tool.js'
//buffer分包,根据MTU的长度进行分包,蓝牙分发官方建议20个字节一包,但是随着硬件的发展,可设置一次传输的最大单元
//根据MTU的大小,把所发数据进行分包处理
const getBufferArrayByMtu = (data) => {
let writePacketLen = bluetoothCore.mtu;
let num = 0;
if (typeof data == "undefined") return [];
let allBuffer = hexStringToArrayBuffer(data); //16进制字符串转ArrayBuffer
let bufferLen = allBuffer.byteLength;
let bufferArray = [];
while (bufferLen > 0) {
let buffer;
if (bufferLen > writePacketLen) {
buffer = allBuffer.slice(num, num + writePacketLen);
num = num + writePacketLen;
bufferLen -= writePacketLen;
bufferArray.push(buffer);
} else {
buffer = allBuffer.slice(num, num + bufferLen);
num += bufferLen;
bufferLen -= bufferLen;
bufferArray.push(buffer);
}
}
return bufferArray;
}
// 3位数是自己定义的,4位数是蓝牙功能确定的,因为页面有交互,故定义一些字段进行交互提示
export const bluetoothStatus = {
'000': '正在搜索蓝牙设备',
'001': '正在连接',
'002': '连接成功',
'003': '正在通讯',
'004': '通讯完成',
'005': '请检查设备是否开启蓝牙!',
'006': '蓝牙已断开',
"-1": "已经连接",
"0": "正常",
"10000": "开启蓝牙后,才可连接设备",
"10001": "开启蓝牙后,才可连接设备",
"10002": "没有找到指定设备",
"10003": "连接失败",
"10004": "没有找到指定服务",
"10005": "没有找到指定特征值",
"10006": "当前连接已断开",
"10007": "当前特征值不支持此操作",
"10008": "其余所有系统上报的异常",
"10009": "Android 系统特有,系统版本低于 4.3 不支持 BLE",
"10012": "操作超时",
"10013": "连接 deviceId 为空或者是格式不正确"
}
//核心的重要的bluetoothCore对象
let bluetoothCore = {
bluetoothStatus: {}, //有时候需要自己定义通讯的提示文字
errorCallback: () => {}, //通讯过程中出现错误的回调处理方法
isErrorShowodal: true, //错误消息是否用showModal展示,因为有些错误不需要展示,或者已经展示在了其他的地方
tip: '', //展示在页面上的信息,包括正式进行的提示和蓝牙反馈的错误提示
status: '', //需要像页面展示的蓝牙状态
SERVICEID: "0000FEE7-0000-2222-8000-00805F9B34FB",
NOTIFYID: "0000FEC1-0000-2222-8000-00805F9B34FB",
WRITEID: "0000FEC1-0000-2222-8000-00805F9B34FB",
STARTPACKET: '54424F582C435143542C', //报文回复的开始标志
ENDPACKET: '2C54424F58454E44', //报文回复的结束标志
planConnectBluetoothDevice: {}, //计划去连接的蓝牙设备
connectedBluetoothDevice: {}, //已经连接上的蓝牙设备
bluetoothList: [], //展示的蓝牙列表
isDiscovering: true, //0为未在搜索,1为正在搜索
isImmediateConnect: true, //是否直接连接
isImmediateWrite: true, //是否立即写数据
filterProp: 'name', // 蓝牙过滤的属性名称
filterPattern: /^\d{10}$/, // 过滤蓝牙列表的正则表达式
isConnected: false, //设备是否连接,实际的连接状态
sendPacket: '', //当前发送的报文
respondPacket: '', //监听到的组合报文
isInit: false, //是否进行了初始化,只进行初始化一次就可以了
mtu: 20,
setTip: {}, //哪一步不需要设置提示 {'004':false}
discoveryTimer: null, // 蓝牙搜索停止的计时器
bluetoothDevicesDiscoveryTimeout: 10000, //多少秒后停止搜索蓝牙
isStopExe: false, //蓝牙断开后,是否停止执行
isCancelOparate: false, //是否中途取消操作,取消操作后,在异步方法中停止执行
isReceivePacketFinished: false, //是否此次接报文完毕,如果接收报文完毕,其他接收的就是日志,此字段表示,在日志开始的时候,可以发送指令,指令正常回复有开始和结束标志,但是日志报文没有
communicationStatus: 0, //每次通讯前设置为0,通讯成功为1,通讯成功之前就失败为-1,有可能在此次通讯中断开了蓝牙,要把有些状态还原,比如开关状态
parsePacketCallback: (result) => {}, //报文解析完后的回调
//解析报文的方式,每种蓝牙设备的协议方式不一样,故解析也不一样
parsePacketMethod: (value) => {
},
//执行蓝牙的取消操作,由于蓝牙异步操作,在以下几个重要地方做拦截操作,搜索监听中、下发指令中、解析指令中
cancelOparate: () => {
bluetoothCore.isCancelOparate = true;
bluetoothCore.status = '000';
bluetoothCore.tip = '正在搜索蓝牙设备';
},
//解析日志报文不需要判断首帧和尾帧,直接解析收到的字符串
parseLogPacketMethod: () => {
},
//蓝牙连接后的回调
bluetoothConnectedCallback: (connected) => {
},
//下发指令,调用这个方法
writePacket: (params) => {
if (bluetoothCore.isCancelOparate) return;
bluetoothCore.setBlueTip('003'); //正在通讯
bluetoothCore.writeDataToDeviceBySubPackage();
},
//蓝牙已断开后,不应该在提示其他异步的操作
setBlueTip: (status, isError = false) => {
if (!bluetoothCore.isStopExe && !bluetoothCore.isCancelOparate) {
bluetoothCore.status = status;
let newBluetoothStatus = Object.assign({}, bluetoothStatus, bluetoothCore
.bluetoothStatus); // 有时候要自己定义通讯的提示文字
let isSetTip = bluetoothCore.setTip[status] + ''; // 如果设置了false,则不显示,默认为显示
if (isSetTip !== 'false') { //有些状态下,不需要告知通讯完成
bluetoothCore.tip = newBluetoothStatus[status];
}
//如果再调用蓝牙API的时候,发生了错误,则看是否提示用户
if (isError) {
if (bluetoothCore.isErrorShowodal) {
showModal(bluetoothCore.tip);
}
//如果再通讯成功之前就失败了,则认为此次通讯是不成功的
if (bluetoothCore.communicationStatus == 0) {
bluetoothCore.communicationStatus = -1;
}
bluetoothCore.errorCallback();
}
if (status == '006') { // 如果状态等于006,则停止执行,停止显示后面的内容
bluetoothCore.isStopExe = true;
}
}
},
//获取在蓝牙模块生效期间所有已发现的蓝牙设备,包括已经和本机处于连接状态的设备
getBluetoothDevices: () => {
return new Promise((resolve) => {
uni.getBluetoothDevices({
success(res) {
let devicesList = bluetoothCore.filterDevices(res.devices);
resolve(devicesList);
}
})
})
},
//解析报文,判断解析的是否是日志
parsePacket(val, isLog = false) {
if (bluetoothCore.isCancelOparate) return;
bluetoothCore.communicationStatus = 1; //通讯成功标志
bluetoothCore.setBlueTip('004'); // 通讯完成
if (!isLog) {
console.log(`正常通讯回复的报文数据-`, val);
let result = bluetoothCore.parsePacketMethod(val);
bluetoothCore.parsePacketCallback(result)
} else {
bluetoothCore.parseLogPacketMethod(val)
}
},
async clickCreateBLEConnection() {
let devicesList = await bluetoothCore.getBluetoothDevices();
if (devicesList.length) { //判断已搜索的设备里是否有目标设备,如果有,则直接连接,如果没有,则先搜索在连接
bluetoothCore.planConnectBluetoothDevice = Object.assign({}, bluetoothCore
.planConnectBluetoothDevice, devicesList[0])
bluetoothCore.createBLEConnection();
} else {
bluetoothCore.startBluetoothDevicesDiscovery();
}
},
searchBluetooth(params) {
bluetoothCore = Object.assign(bluetoothCore, params);
if (!bluetoothCore.isInit) { // 如果没有进行初始化,则先进行初始化
bluetoothCore.init();
} else { //如果进行了初始化,则直接进行搜索
bluetoothCore.startBluetoothDevicesDiscovery();
}
},
//点击设备连接操作
operateBluetooth(params) {
console.log('params==', params)
bluetoothCore.isStopExe = false;
bluetoothCore.isCancelOparate = false;
bluetoothCore = Object.assign(bluetoothCore, params);
//先生成一个报文
if (!bluetoothCore.isInit) { // 如果没有进行初始化,则先进行初始化
bluetoothCore.init();
} else { // 判断是否连接
if (bluetoothCore.isConnected) { // 判断当前连接的车辆是否是现在的目标车辆
if (bluetoothCore.connectedBluetoothDevice[bluetoothCore.filterProp] == bluetoothCore
.planConnectBluetoothDevice[bluetoothCore.filterProp]) {
//如果当前连接的设备是目标设备,则直接写入数据
bluetoothCore.writePacket();
} else { // 断开蓝牙,再连接目标设备
bluetoothCore.closeBLEConnection(() => {
bluetoothCore.clickCreateBLEConnection();
})
}
} else {
bluetoothCore.clickCreateBLEConnection();
}
}
},
// 初始化蓝牙模块
init() {
// 初始化蓝牙对象
uni.openBluetoothAdapter({
success(res) {
bluetoothCore.isInit = true;
bluetoothCore.openBluetoothAdapterCallback();
console.log("打开蓝牙设配器成功", res)
},
fail(res) {
console.log("打开蓝牙设配器失败", res)
bluetoothCore.setBlueTip(res.errCode, true);
},
complete() {
}
})
},
openBluetoothAdapterCallback() {
// 监听蓝牙适配器的变化
uni.onBluetoothAdapterStateChange((res) => {
console.log('蓝牙适配器状态变化', res);
});
// 监听搜索新设备
uni.onBluetoothDeviceFound((res) => {
//console.log('搜索到新设备', res);
//1、有可能展示列表,有可能直接连接
if (bluetoothCore.isCancelOparate) return;
bluetoothCore.bluetoothDeviceFoundCallback(res.devices);
});
// 监听蓝牙连接状态
uni.onBLEConnectionStateChange((res) => {
console.log('蓝牙连接状态变化', res);
bluetoothCore.isConnected = res.connected;
bluetoothCore.bluetoothConnectedCallback(res.connected);
if (!res.connected) {
bluetoothCore.setBlueTip('006', true); //蓝牙已断开
}
});
// 监听MTU的变化
uni.onBLEMTUChange((res) => {
console.log('MTU变化', res);
//获取到的mtu为517,但是设置的时候只能为最大512
bluetoothCore.mtu = res.mtu - 10;
});
bluetoothCore.startBluetoothDevicesDiscovery();
},
//过滤搜索到的设备,如果搜索了立马连接,则返回指定设备,否则就只是简单的搜索列表
filterDevices: (devices) => {
let targetDevices = [];
targetDevices = devices.filter((item) => {
let name = item.name;
//console.log("搜索到的设备名称:",name)
if (bluetoothCore.isImmediateConnect) { //返回指定的那一个
return name.indexOf(bluetoothCore.planConnectBluetoothDevice[bluetoothCore
.filterProp]) != -1;
} else { //返回列表
return name != "" && bluetoothCore.filterPattern.test(name);
}
});
return targetDevices;
},
//监听搜索到的设备
bluetoothDeviceFoundCallback(devices) {
let targetDevices = bluetoothCore.filterDevices(devices);
console.log('搜索到的目标设备', targetDevices)
if (targetDevices.length) {
if (bluetoothCore.isImmediateConnect) {
//搜索到蓝牙设备后,则停止蓝牙搜索
//清除蓝牙搜索的定时器
clearTimeout(bluetoothCore.discoveryTimer);
bluetoothCore.planConnectBluetoothDevice = Object.assign({}, bluetoothCore
.planConnectBluetoothDevice, targetDevices[0]); //合并参数
bluetoothCore.bluetoothList = targetDevices;
bluetoothCore.stopBluetoothDevicesDiscovery(() => {
bluetoothCore.createBLEConnection();
})
} else {
bluetoothCore.bluetoothList = uniqueArr([...bluetoothCore.bluetoothList, ...targetDevices]);
}
}
},
createBLEConnection() {
bluetoothCore.setBlueTip('001');
console.log('蓝牙连接')
uni.createBLEConnection({
deviceId: bluetoothCore.planConnectBluetoothDevice.deviceId,
success(res) {
bluetoothCore.setBlueTip('002'); //连接成功
console.log(`与蓝牙设备${bluetoothCore.planConnectBluetoothDevice.name}创建连接成功`, res);
bluetoothCore.isConnected = true;
bluetoothCore.connectedBluetoothDevice = Object.assign({}, bluetoothCore
.planConnectBluetoothDevice);
//如果是安卓手机并且安卓系统 5.1 以上版本有效
uni.getSystemInfo({
success: function(res) {
// res.platform 可以获取手机的操作系统类型,如 "android", "ios"
// res.system 可以获取手机的操作系统版本,如 "Android 5.1.1"
console.log("操作系统类型", res.platform);
console.log("操作系统版本", res.system);
if (res.platform === 'android' && compareVersion(res.system.split(' ')[
1]
.toString(), '5.1.0') >=
0) {
// 在安卓手机且系统版本大于等于 5.1.0 时执行相应的逻辑
uni.setBLEMTU({
deviceId: bluetoothCore.connectedBluetoothDevice
.deviceId,
mtu: bluetoothCore.mtu,
success(res) {
console.log('设置mtu成功', res)
},
fail(res) {
console.log('设置mtu失败', res)
},
complete() {
bluetoothCore.getBLEDeviceServices();
}
});
} else {
console.log('设置mtu不符合条件');
bluetoothCore.getBLEDeviceServices();
}
}
});
},
fail(res) {
console.log('蓝牙连接失败', res);
bluetoothCore.setBlueTip(res.errCode, true);
}
})
},
//ios必须要执行
getBLEDeviceServices() {
uni.getBLEDeviceServices({
deviceId: bluetoothCore.connectedBluetoothDevice.deviceId,
success(res) {
console.log(`获取${bluetoothCore.connectedBluetoothDevice.deviceId}服务成功`, res);
bluetoothCore.getBLEDeviceCharacteristics();
}
})
},
//ios必须要执行
getBLEDeviceCharacteristics() {
uni.getBLEDeviceCharacteristics({
deviceId: bluetoothCore.connectedBluetoothDevice.deviceId,
serviceId: bluetoothCore.SERVICEID,
success: function(res) {
console.log(`获取${bluetoothCore.connectedBluetoothDevice.deviceId}特征值成功`,
res);
bluetoothCore.notifyAndOnBLECharacteristicValueChange();
},
fail(res) {
console.log(`获取${bluetoothCore.connectedBluetoothDevice.deviceId}特征值失败`,
res);
}
})
},
//判断报文,如果字符串中含有帧头,则是命令回复,为了防止有分包的情况,设置isReceivePacketFinished为标识字段,否则为日志标志
notifyValueCallback(val) {
console.log('设备监听返回的报文', val)
let start = val.substring(0, bluetoothCore.STARTPACKET.length);
if (start == bluetoothCore.STARTPACKET) { //如果有报文开始帧,重置接收的报文
bluetoothCore.respondPacket = "";
bluetoothCore.isReceivePacketFinished = false;
}
bluetoothCore.respondPacket = bluetoothCore.respondPacket + val;
let end = bluetoothCore.respondPacket.substring(bluetoothCore.respondPacket
.length -
bluetoothCore.ENDPACKET.length);
if (bluetoothCore.isReceivePacketFinished) { //此段标志为日志报文
bluetoothCore.parsePacket(val, true)
}
if (end == bluetoothCore.ENDPACKET) {//如果有报文结束帧,则重置接收的报文,把带有帧头帧尾的报文进行解析
bluetoothCore.isReceivePacketFinished = true;
let respondPacket = bluetoothCore.respondPacket;
bluetoothCore.respondPacket = '';
bluetoothCore.parsePacket(respondPacket, false);
}
},
notifyAndOnBLECharacteristicValueChange() {
//防止没有更换状态
uni.notifyBLECharacteristicValueChange({
state: true,
deviceId: bluetoothCore.connectedBluetoothDevice.deviceId,
serviceId: bluetoothCore.SERVICEID,
characteristicId: bluetoothCore.NOTIFYID,
success(res) {
if (bluetoothCore.isImmediateWrite) {//如果notify监听成功,则直接写报文
bluetoothCore.writePacket();
}
console.log("开启notify通知模式成功", res);
uni.onBLECharacteristicValueChange((res) => {
let val = arrayBufferToHexString(res.value);
bluetoothCore.notifyValueCallback(val);
})
},
fail(res) {}
})
},
writeBLECharacteristicValue(value, isLastPackage = true) {
console.log('写入的数据', arrayBufferToHexString(value))
uni.writeBLECharacteristicValue({
deviceId: bluetoothCore.connectedBluetoothDevice.deviceId,
serviceId: bluetoothCore.SERVICEID,
characteristicId: bluetoothCore.WRITEID,
value,
success(res) {
console.log("写入数据成功", res);
//判断是否是最后一包,如果是最后一包,则开启通讯计时器
},
fail(res) {
console.log("写入数据失败", res);
bluetoothCore.setBlueTip(res.errCode, true);
}
})
},
//不分包发送数据
writeDataToDevice() {
let allBuffer = hexStringToArrayBuffer(bluetoothCore.sendPacket);
bluetoothCore.writeBLECharacteristicValue(allBuffer);
},
//分包发送数据
async writeDataToDeviceBySubPackage() {
bluetoothCore.respondPacket = '';
let bufferArray = getBufferArrayByMtu(bluetoothCore.sendPacket);
for (let i = 0; i < bufferArray.length; i++) {
await sleep(10); //同步延迟1ms
bluetoothCore.writeBLECharacteristicValue(bufferArray[i], i == bufferArray.length - 1);
}
},
startBluetoothDevicesDiscovery() {
bluetoothCore.isDiscovering = true;
bluetoothCore.setBlueTip('000'); //正在搜索蓝牙设备
uni.startBluetoothDevicesDiscovery({
services: [], //添加了SERVICEID搜索不到蓝牙
allowDuplicatesKey: false, //是否允许重复上报同一设备。如果允许重复上报,则 uni.onBlueToothDeviceFound 方法会多次上报同一设备,但是 RSSI 值会有不同。
powerLevel: 'high',
success() {
console.log("开始蓝牙搜索成功");
bluetoothCore.discoveryTimer = setTimeout(() => {
bluetoothCore.stopBluetoothDevicesDiscovery();
}, bluetoothCore.bluetoothDevicesDiscoveryTimeout)
},
fail(res) {
console.log("开始蓝牙搜索失败:", res);
}
})
},
stopBluetoothDevicesDiscovery(callback = () => {}) {
bluetoothCore.isDiscovering = false;
//如果停止了,都没有搜索到蓝牙设备,则认为没有搜索到蓝牙设备
if (!bluetoothCore.bluetoothList.length) {
bluetoothCore.setBlueTip('005', true); //请检查设备是否开启蓝牙
}
uni.stopBluetoothDevicesDiscovery({
success() {
callback();
console.log("停止蓝牙搜索成功");
},
fail(res) {
console.log("停止蓝牙搜索失败:", res);
},
})
},
closeBLEConnection(callback = () => {}) {
//关闭蓝牙连接
uni.closeBLEConnection({
deviceId: bluetoothCore.connectedBluetoothDevice.deviceId,
})
},
// 销毁蓝牙模块
destroy() {
bluetoothCore.isInit = false;
// 取消搜索监听
uni.offBluetoothDeviceFound();
// 取消蓝牙连接状态的监听
uni.offBLEConnectionStateChange();
// 取消蓝牙适配器变化的监听
uni.offBluetoothAdapterStateChange();
// 取消MTU的监听
uni.offBLEMTUChange();
// 取消特征值变化的监听
uni.offBLECharacteristicValueChange()
//关闭蓝牙连接
bluetoothCore.closeBLEConnection();
//关闭蓝牙模块
uni.closeBluetoothAdapter()
}
}
export default bluetoothCore;
二、工具代码
tool.js
c
const iconv = require('iconv-lite');
//十六进制转字符串
export const hexToStr = function(hex, encoding) {
var trimedStr = hex.trim();
var rawStr = trimedStr.substr(0, 2).toLowerCase() === "0x" ? trimedStr.substr(2) : trimedStr;
var len = rawStr.length;
var curCharCode;
var resultStr = [];
for (var i = 0; i < len; i = i + 2) {
curCharCode = parseInt(rawStr.substr(i, 2), 16);
resultStr.push(curCharCode);
}
var val = "";
var arr = resultStr;
for (let i = 0; i < arr.length; i++) {
val += String.fromCharCode(arr[i]);
}
return val;
}
//CRC16计算函数
export const calculateCRC16 = (data) => {
// 初始化CRC16校验值
let crc = 0xFFFF;
// 遍历输入数据
for (let i = 0; i < data.length; i++) {
crc ^= (data.charCodeAt(i) & 0xFF);
for (let j = 0; j < 8; j++) {
if ((crc & 0x0001) !== 0) {
crc >>= 1;
crc ^= 0xA001;
} else {
crc >>= 1;
}
}
}
// 返回计算后的CRC16校验值
return crc.toString(16).toUpperCase();
}
//十进制转十六进制
export const tenToHex = (num, digit = 4) => {
const hex = num.toString(16);
return hex.padStart(digit, 0);
}
export const showModal = (content, sureCallback = function() {}, showCancel = false,
confirmText =
"确定", cancelText = "取消", cancelCallback = function() {}) => {
uni.showModal({
title: '提示',
content: content,
showCancel: showCancel,
confirmText: confirmText,
cancelText: cancelText,
success(res) {
if (res.confirm) {
sureCallback();
} else if (res.cancel) {
cancelCallback();
}
}
});
}
//字符串转16进制
export const stringToHex = (str) =>{
var val = "";
for (var i = 0; i < str.length; i++) {
if (val == "") {
val = str.charCodeAt(i).toString(16); //获取字符的Unicode码然后转16进制
} else {
val += str.charCodeAt(i).toString(16); //获取字符的Unicode码然后转16进制再拼接,中间用逗号隔开
}
}
return val;
}
export const sleep = (time) => {
return new Promise(resolve => setTimeout(resolve, time));
}
//根据name过滤数据
export const uniqueArr = (arr) => {
// 过滤掉重复的设备名称
let uniqueArr = arr.filter((device, index, array) => {
return array.findIndex(d => d.name === device.name) === index;
})
return uniqueArr;
}
export const arrayBufferToHexString = (buffer) => {
const hexArr = Array.prototype.map.call(
new Uint8Array(buffer),
function(bit) {
return ('00' + bit.toString(16)).slice(-2)
}
)
return hexArr.join('').toUpperCase();
}
export let hexStringToArrayBuffer = (str) => {
//十六进制转ArrayBuffer
return new Uint8Array(str.match(/[\da-f]{2}/gi).map(function(h) {
return parseInt(h, 16)
})).buffer
}
//匹配版本号
export let compareVersion = (version1, version2) => {
const v1 = version1.split('.').map(Number);
const v2 = version2.split('.').map(Number);
const len = Math.max(v1.length, v2.length);
for (let i = 0; i < len; i++) {
const num1 = i < v1.length ? v1[i] : 0;
const num2 = i < v2.length ? v2[i] : 0;
if (num1 > num2) {
return 1;
} else if (num1 < num2) {
return -1;
}
}
return 0;
}
//16进制转GB2312编码,可以转为汉字,编码可自己切换
export let hexToGb2312 = (hexString)=> {
const buffer = Buffer.from(hexString, 'hex');
const decodedString = iconv.decode(buffer, 'GB2312');
return decodedString;
}
三、通讯逻辑代码
util.js
c
import vm from '@/main.js'
import {
tenToHex,
calculateCRC16,
hexToStr,
hexToGb2312
} from './tool.js'
let packetNo = 0;
//根据状态的自定义描述
export let getBluetoothStatusDesc = (meterEcuid)=> {
return {
'000': '请靠近设备',
'001': meterEcuid,
'002': meterEcuid,
'003': meterEcuid,
'004': meterEcuid,
'005': '搜索失败',
'006': meterEcuid,
"-1": meterEcuid,
"0": "正常",
"10000": "手机蓝牙未打开",
"10001": "手机蓝牙未打开",
"10002": meterEcuid,
"10003": meterEcuid,
"10004": meterEcuid,
"10005": meterEcuid,
"10006": meterEcuid,
"10007": meterEcuid,
"10008": meterEcuid,
"10009": meterEcuid,
"10012": meterEcuid,
"10013": meterEcuid
}
}
//根据状态的图片展示
export let bluetoothStatusLoadingImagesIndex = {
'000': 0,//'正在搜索蓝牙设备',
'001': 0,//'正在连接',
'002': 1,//'连接成功',
'003': 3,//'正在通讯',
'004':1,// '通讯完成',
'005':1,// '请检查设备是否开启蓝牙!',
'006':2,// '蓝牙已断开',
"-1": 2,//"已经连接",
"0":0, //"正常",
"10000": 2,//"开启蓝牙后,才可连接设备",
"10001":2, //"当前蓝牙适配器不可用",
"10002":2,// "没有找到指定设备",
"10003":2,// "连接失败",
"10004": 2,//"没有找到指定服务",
"10005":2,// "没有找到指定特征值",
"10006":2,// "当前连接已断开",
"10007":2, //"当前特征值不支持此操作",
"10008":2,// "其余所有系统上报的异常",
"10009": 2,//"Android 系统特有,系统版本低于 4.3 不支持 BLE",
"10012": 2,//"操作超时",
"10013": 2,//"连接 deviceId 为空或者是格式不正确"
}
//获取下发的基本的报文信息
const getBaseCommandPacket = (deviceType, deviceNo, data) => {
packetNo = packetNo + 1;
let startHex = 'FAC5'; //帧头
packetNo = packetNo > 32767 ? 1 : packetNo;
let packetNoHex = tenToHex(packetNo); //数据包编号 //最大1-32767
let deviceTypeHex = deviceType == 1 ? 'FF' : 'FF'; //设备类型 FF 表示小程序,01表示无限远传摄像水表
let deviceNoBcd = deviceNo; //设备编号
let dataLenHex = tenToHex(data.length / 2); //数据域长度 算长度-转16进制-补0成2位字节
let dataHex = data; //数据域
let validData = startHex + packetNoHex + deviceTypeHex + deviceNoBcd + dataLenHex + dataHex;
//let CRC16Hex = calculateCRC16(validData); //校验
let CRC16Hex = 'FFFF'; //校验
let endHex = 'FBC6'; //帧尾
let resultHex = validData + CRC16Hex + endHex
return resultHex;
}
/*
*/
//获取数据域的报文
export const getDataCommandPacket = (deviceType = 1, deviceNo, commandCode, params) => {
let data = '';
if (commandCode == 'read-up-report-cycle') { //读上报周期
data = '0604';
} else if (commandCode == 'take-a-picture') { //拍照
data = '10' + tenToHex(20, 2) + tenToHex(0, 2) + tenToHex(0) + tenToHex(240, 2) + tenToHex(320) + tenToHex(
1, 2) + tenToHex(0, 2);
} else if (commandCode == 'get-picture-packetnumber') { //获取照片数据包数
data = '11';
} else if (commandCode == 'get-picture-data') { //获取照片数据,需要传递参数
data = '12' + tenToHex(params.picturePacketCount, 2) + tenToHex(params.picturePacketNo, 2);
} else if (commandCode == 'open-debug') { //打开调试
data = '4355';
} else if (commandCode == 'start-network') { //开始联网
data = '40';
} else if (commandCode == 'stop-network') { //结束联网
data = '41';
} else if (commandCode == 'start-read-meterAmount') { //开始抄表
data = '42';
} else if (commandCode == 'close-debug') { //关闭调试
data = '43AA';
} else if (commandCode == 'read-meter') { //获取设备信息
data = '01';
} else if (commandCode == 'read-meter-domain-port') { //读域名和端口
data = '0602';
} else if (commandCode == 'set-meter-domain-port') { // 设置域名和端口 47.115.13.97: 24226
data = '0502' + stringToHex(params.domainPort);
}
return getBaseCommandPacket(deviceType, deviceNo, data)
}
//解析报文工具方法
export const parsePacketMethod = (value) => {
let len = value.length;
let result = '';
if (true) { //数据包编号和校验数据
result = value.substring(24, len - 8);
console.log('数据域的结果', result)
return result;
} else {
uni.showModal({
content: '校验不通过',
});
return null
}
}
//不同命令的不同交互显示
const messageList = {
'take-a-picture': '正在拍照',
'get-picture-packetnumber': '获取图片数据',
'get-picture-data': '获取图片数据',
'open-debug': '打开调试',
'close-debug': '关闭调试',
'start-network': '开始联网',
'stop-network': '结束联网',
'start-read-meterAmount': '开始抄表'
}
let setSetTip = () => {
// webview的堆栈
const pages = getCurrentPages()
// 当前页面
const page = pages[pages.length - 1];
console.log('当前页面',page);
let fullPath = page.$page.fullPath;
if (fullPath.indexOf('meterDetail') != -1) {
return {
'004': false
};
} else {
return {}
}
}
//下发命令 表号、命令编码,命名接收完毕后的回调,下发指令的参数,执行蓝牙的方法
export const sendCommand = (userOption) => {
let defaultOption = {
meterEcuid: '', //表号
commandCode: '', //命令编码
parsePacketCallback: (result) => {}, //命名接收完毕后的回调
commandParams: {}, //下发指令的参数
bluetoothConnectedCallback:(connected)=>{},
errorCallback:()=>{},
communicationStatus:0, // 通讯状态
operateBluetooth: (params) => {} //执行蓝牙的方法
}
let option = Object.assign({}, defaultOption, userOption)
console.log('执行的命令-', option.commandCode);
console.log('命令执行参数', option);
let sendPacket = getDataCommandPacket(3, option.meterEcuid, option.commandCode, option.commandParams);
console.log('生成的报文', sendPacket)
let params = {
planConnectBluetoothDevice: {
meterEcuid: option.meterEcuid
},
respondPacket: '',
isImmediateConnect: true,
isImmediateWrite: true,
sendPacket: sendPacket,
isErrorShowodal: false,
setTip: setSetTip(),
bluetoothConnectedCallback:(connected)=>{
option.bluetoothConnectedCallback(connected)
},
errorCallback:()=>{
option.errorCallback()
},
communicationStatus:0, // 通讯状态
bluetoothStatus: {
'003': messageList[option.commandCode] ? messageList[option.commandCode] : '正在通讯'
},
parsePacketCallback: (result) => {
option.parsePacketCallback(result)
},
}; //初始化的参数
option.operateBluetooth(params);
}
//解析日志报文
export const parseLogPacketMethod = (val) => {
let result = hexToGb2312(val) + '\n';
console.log('打开调试后解析的字符串', result);
getApp().globalData.logAllText += result; //处理所有日志
if (result.indexOf('LogStart') != -1) {
getApp().globalData.isStartLog = true;
} else if (result.indexOf('LogEnd') != -1) {
getApp().globalData.isStartLog = false;
}
if (getApp().globalData.isStartLog && result.indexOf('LogStart') == -1 && result.indexOf(
'LogEnd') == -1) { //处理中文展示
let logChina = vm.$store.state.logChina;
logChina += result;
vm.$store.commit('setLogChina', logChina)
}
}
四、全局注册
main.js
c
//默认蓝牙设备数据
Object.assign(bluetoothCore,{
filterPattern: /^Y3[567].*/, //Y35主机、Y36分机、Y37一体式
filterProp: 'meterEcuid',
STARTPACKET:'FAC5',
ENDPACKET:'FBC6',
bluetoothDevicesDiscoveryTimeout: 5000, //多少秒后停止搜索蓝牙
parsePacketMethod:parsePacketMethod,
parseLogPacketMethod:parseLogPacketMethod,
SERVICEID: "0000FFC0-0000-xxxx-8000-00805F9B34FB",
NOTIFYID: "0000FFC1-0000-xxxx-00805F9B34FB",
WRITEID: "0000FFC2-0000-xxxx-8000-00805F9B34FB",
})
五、使用
c
sendCommandFun('open-debug')
//像蓝牙发送指令
sendCommandFun(commandCode) {
this.commandCode = commandCode;
//this.setTitle(commandCode);
//如果蓝牙未连接,则弹出连接状态弹窗 并且调试未开启
if (!bluetoothCore.isConnected) {
this.isShowModal = true;
}
sendCommand({
meterEcuid: this.meterEcuid,
commandCode: commandCode,
bluetoothConnectedCallback: (connected) => { //监听到蓝牙连接上,则关闭弹窗
console.log('蓝牙连接上的回调', connected)
if (connected) {
this.isShowModal = false;
}
},
operateBluetooth: (params) => {
bluetoothCore.operateBluetooth(params)
},
errorCallback: () => {
if ((commandCode == 'open-debug' || commandCode == 'close-debug') && bluetoothCore
.communicationStatus == -1) {
this.$store.commit('setIsStartLog', !this.isStartLog)
}
}
})
},