const sm4 = require('./sm4.js'); const common_vendor = require("../../../common/vendor.js"); var wx = common_vendor.index var OBUNo = ''; const key = '1A70B8BE26C994DA88F4712F5E60C738'; var phoneRandom = ''; var encrypDeviceRandom = ''; var myDeviceId; // 蓝牙连接相关参数 var Service, notifyCharacteristicsId, writeCharacteristicsId, readCharacteristicsId; //回调函数 let CallBackInfo = null; let CallBackToQT = null; let CallBackInfoForconnected = null; var framNo = 0; var outDevices = new Array(); //当前是第几条命令 var currentCmdId = 0; //需要执行的命令总数 var cmdTotal; //返回的命令数组; var outCmdArr = new Array(); //输入的命令数组 var inCmdArr = new Array(); //命令类型 var globalCmdType = '01'; //复位状态:0未复位 1已复位 var resetState = 0; var _res = {}; //打开蓝牙适配器 function openBle(filter, timeout, callback) { outDevices.splice(0, outDevices.length); uni.openBluetoothAdapter({ success: function (res) { uni.startBluetoothDevicesDiscovery({ services: ['FEE7'], success: function () { setTimeout(function () { uni.stopBluetoothDevicesDiscovery(); uni.getBluetoothDevices({ success: function (res) { res.devices.forEach((device) => { // 根据filter进行过滤 if (device.name.indexOf(filter) == 0) { outDevices.push(device); } }) callback(0, outDevices, "扫描成功"); }, fail: function (error) { callback(1, null, "扫描失败:" + error.errMsg); } }) }, timeout) }, fail: function (error) { callback(1, null, "扫描失败:" + error.errMsg); } }) }, fail: function (error) { callback(1, null, "扫描失败:" + error.errMsg); } }) } function connectDevice(device, callback, callback2) { CallBackInfo = callback; CallBackInfoForconnected = callback2; myDeviceId = device.deviceId; uni.createBLEConnection({ deviceId: myDeviceId, timeout: 3000, success: function (res) { uni.getBLEDeviceServices({ deviceId: myDeviceId, success: function (res) { for (var a = 0; a < res.services.length; a++) { if ('0000FF00' == res.services[a].uuid.substr(0, 8)) { Service = res.services[a].uuid; } } uni.getBLEDeviceCharacteristics({ deviceId: myDeviceId, serviceId: Service, success: function (res) { for (var i = 0; i < res.characteristics.length; i++) { let item = res.characteristics[i] if (item.properties.notify || item.properties.indicate) { if ('0000FF02' == item.uuid.substr(0, 8)) { notifyCharacteristicsId = item.uuid; } } if (item.properties.write) { if ('0000FF01' == item.uuid.substr(0, 8)) { writeCharacteristicsId = item.uuid; } } else if (item.properties.read) { if ('0000FF03' == item.uuid.substr(0, 8)) { readCharacteristicsId = item.uuid; } } } uni.notifyBLECharacteristicValueChange({ state: true, deviceId: myDeviceId, serviceId: Service, characteristicId: notifyCharacteristicsId, success: function (res) { sendData(ass2001(), function (code) { if (code != 0) { _res.code = 1; _res.data = null; _res.msg = "连接失败:认证失败" CallBackInfo(_res); } }) }, fail: function (error) { _res.code = 1; _res.data = null; _res.msg = "连接失败:" + error.errMsg CallBackInfo(_res); } }) }, fail: function (error) { _res.code = 1; _res.data = null; _res.msg = "连接失败:" + error.errMsg CallBackInfo(_res); }, }) }, fail: function (error) { _res.code = 1; _res.data = null; _res.msg = "连接失败:" + error.errMsg CallBackInfo(_res); } }) }, fail: function (error) { _res.code = 1; _res.data = null; _res.msg = "连接失败:" + error.errMsg CallBackInfo(_res); } }) // 必须在这里的回调才能获取 uni.onBLECharacteristicValueChange(function (characteristic) { var hex = Array.prototype.map.call(new Uint8Array(characteristic.value), x => ('00' + x.toString(16)).slice(-2)).join(''); console.log('接收信息:' + hex); checkAndDistribution(hex); }) uni.onBLEConnectionStateChange(function (res) { console.log('监听连接状态变化:'); if (res.connected) { CallBackInfoForconnected(0, null, "监听连接成功"); } else { resetState = 0; CallBackInfoForconnected(1, null, "监听到断开连接"); } }) } //断开与蓝牙外设的连接 function disconnectDevice(callback) { resetState = 0; uni.closeBLEConnection({ deviceId: myDeviceId, success: function (res) { uni.closeBluetoothAdapter({ success: function (res) { var _res = {}; _res.code = 0; _res.data = null; _res.msg = "断开成功"; callback(_res); }, fail: function (res) { var _res = {}; _res.code = 1; _res.data = null; _res.msg = "断开失败:" + res.errMsg; callback(_res); } }) }, fail: function (res) { var _res = {}; _res.code = 1; _res.data = null; _res.msg = "断开失败:" + res.errMsg; callback(_res); } }) } ///////////////////////下面是util///////////////////////// /** * 发送数据 */ function sendData(cmd, callback) { console.log('发送信息:' + cmd); uni.writeBLECharacteristicValue({ deviceId: myDeviceId, serviceId: Service, characteristicId: writeCharacteristicsId, value: strToBuff(cmd), success: function (res) { console.log('数据发送成功'); callback(0); }, fail: function (res) { console.log('数据发送失败'); console.log(res); callback(1); } }) } function hex2int(hex) { var len = hex.length, a = new Array(len), code; for (var i = 0; i < len; i++) { code = hex.charCodeAt(i); if (48 <= code && code < 58) { code -= 48; } else { code = (code & 0xdf) - 65 + 10; } a[i] = code; } return a.reduce(function (acc, c) { acc = 16 * acc + c; return acc; }, 0); } //命令转buffer function strToBuff(cmd) { let buffer = new ArrayBuffer(cmd.length / 2); let dataView = new Uint8Array(buffer); for (var a = 0; a < cmd.length / 2; a++) { dataView[a] = '0x' + cmd.substring(2 * a, 2 * a + 2); } return buffer } function ass2001() { OBUNo = '1122334455667788'; phoneRandom = getRandom(); console.log("phoneRandom:" + phoneRandom); var cmd = OBUNo + phoneRandom; return assbCmd("2001", cmd); } function ass2002() { var cmd = OBUNo + encrypDeviceRandom; return assbCmd("2002", cmd); } function ass2005() { var cmd = OBUNo; return assbCmd("2005", cmd); } function ass2006(option) { var cmd = OBUNo + option; return assbCmd("2006", cmd); } function channel(channelType, cmd, callback) { CallBackInfo = callback; sendData(ass200A(channelType, cmd), function (code) { if (code == 0) {} else { callback(1); } }) } function transCmd(cmd, cmdtype, callback) { if (cmdtype == "10") { cmdtype = "01" } if (cmdtype == "20") { cmdtype = "02" } CallBackToQT = callback; currentCmdId = 0; cmdTotal = cmd.length; inCmdArr = cmd; outCmdArr.splice(0, outCmdArr.length); console.log("resetState" + resetState); if (resetState == 0) { globalCmdType = "03"; channel("03", "", function (code, data, msg) { if (code == 0) { resetState = 1; globalCmdType = cmdtype; channel(cmdtype, cmd[0], function (code, data, msg) { }); } }); } else { globalCmdType = cmdtype; channel(cmdtype, cmd[0], function (code, data, msg) { }); } } function channel2005(callback) { CallBackInfo = callback; sendData(ass2005(), function (code) { if (code == 0) { console.log(code); } else { console.log(code); callback(1); } }) } function channel2006(cmd, callback) { CallBackInfo = callback; sendData(ass2006(cmd), function (code) { if (code == 0) { console.log(code); } else { console.log(code); callback(1); } }) } function channelTLV(channelType, cmds, callback) { sendData(ass200A(channelType, cmd), function (code) { if (code == 0) { console.log(code); callback(0); } else { console.log(code); callback(1); } }) } function ass200A(channelType, cmd) { var cmd = channelType + tenToHex(cmd.length / 2, 2) + cmd; return assbCmd("200A", cmd); } //获取16字节随机数 function getRandom() { var result = ''; for (var i = 0; i < 16; i++) { var tmp = Math.ceil((Math.random() * 255)); tmp = (tmp.toString(16)); if (tmp.length == 1) { tmp = "0" + tmp; } result += tmp; } return result.toUpperCase(); } //组装命令 function assbCmd(msgType, cmd) { var CmdId = 'AA55AA55'; var nLength = tenToHex(8 + cmd.length / 2, 4); var data = CmdId + msgType + nLength + getFramNo() + '01' + cmd + getDate(); var check = crc16(data, true); return data + check; } function getDate() { var date = new Date(); return date.getFullYear().toString().substring(2, 4) + pad2(date.getMonth() + 1) + pad2(date.getDate()) + pad2(date.getHours()) + pad2(date.getMinutes()) + pad2(date.getSeconds()); } function pad2(n) { return n < 10 ? '0' + n : n } function getFramNo() { if (framNo == 2147483647) { framNo = 1; } else { framNo++; } return tenToHex(framNo, 8); } /** * 10to16 * num 数字 * len 想要的16进制长度 */ function tenToHex(num, len) { var hex = num.toString(16); var zero = ''; for (var a = 0; a < len - hex.length; a++) { zero += '0'; } return zero + hex } function strToByte(str) { var arr = []; for (var i = 0; i < str.length; i += 2) { arr.push(parseInt('0x' + str.substring(i, i + 2))); } return arr; }; function bytesToHexString(bytes) { var hexStr = ''; for (var i = 0; i < bytes.length; i++) { var hex = bytes[i].toString(16); if (hex.length == 1) { hex = "0" + hex; } hexStr += hex; } return hexStr.toUpperCase(); } //crc16校验,传入要校验的字符串,返回计算的crc16结果 function crc16(str) { var data = strToByte(str); var crc = 0x0000; for (var i = 0; i < data.length; i++) { crc = crc ^ (data[i] << 8); for (var j = 0; j < 8; j++) { if ((crc & 0x8000) != 0) crc = (crc << 1) ^ 0x8005; else crc = (crc << 1); } } crc = ((Math.pow(2, 32) + crc).toString(16)); crc = crc.substring(crc.length - 4); return crc.toUpperCase(); } function checkAndDistribution(data) { var cmd = data.substring(0, data.length - 4); var crc = data.substring(data.length - 4); if (crc.toUpperCase() == crc16(cmd)) { var head = data.substring(0, 8); if (head == "aa55aa55") { var type = data.substring(8, 12); switch (type) { case "3001": analysis3001(data); break; case "3002": analysis3002(data); break; case "3005": analysis3005(data); break; case "3006": analysis3006(data); break; case "300a": analysis300A(data); break; default: } } else { console.log("信息有误"); } } else { console.log("信息CRC校验不通过"); } } function analysis3001(data) { var length = hex2int(data.substring(12, 16)); var cmd = data.substring(26, 26 + length * 2); var cmd1 = cmd.substring(0, 16); var cmd2 = cmd.substring(16, 48); var cmd3 = cmd.substring(48, 80); console.log("cmd2" + cmd2); OBUNo = cmd1; if (authCheck(phoneRandom, cmd2)) { // 认证通过 encrypDeviceRandom = encryption(cmd3); sendData(ass2002(), function (code) { if (code != 0) { _res.code = 1; _res.data = null; _res.msg = "连接失败:2001认证失败" CallBackInfo(_res); } }) } else { // 认证不通过 _res.code = 1; _res.data = null; _res.msg = "连接失败:3001认证失败" CallBackInfo(_res); } } function analysis3002(data) { var length = hex2int(data.substring(12, 16)); var cmd = data.substring(26, 26 + length * 2); var cmd1 = cmd.substring(0, 16); var cmd2 = cmd.substring(16, 18); if (cmd2 == "00") { _res.code = 0; _res.data = null; _res.msg = "连接成功" CallBackInfo(_res); } else { _res.code = 1; _res.data = null; _res.msg = "连接失败:认证失败" CallBackInfo(_res); } } function authCheck(random, data) { console.log("random" + random); console.log("data" + data); let decryptData = sm4.decrypt(strToByte(data), strToByte(key), { padding: 'none', output: 'array' }) // 解密,不使用 padding console.log("bytesToHexString(decryptData)" + bytesToHexString(decryptData)); return bytesToHexString(decryptData) == random; } function encryption(data) { // 可以为 16 进制串或字节数组,要求为 128 比特 let encryptData = sm4.encrypt(strToByte(data), strToByte(key), { padding: 'none' }) // 加密,默认输出 16 进制字符串 return encryptData; } function analysis300A(data) { var length = hex2int(data.substring(12, 16)); var cmd = data.substring(26, 26 + length * 2); var cmd1 = cmd.substring(0, 2); var cmd2 = cmd.substring(2, 4); var cmd3 = cmd.substring(4, 6); var cmd4 = cmd.substring(6, 6 + hex2int(cmd3) * 2); if (globalCmdType == '03') { CallBackInfo(0, cmd4, cmd4); } else { if (cmd4.substr(0, 2).toUpperCase() == "6C") { var cmd = inCmdArr[currentCmdId]; channel(globalCmdType, cmd.substr(0, cmd.length - 2) + cmd4.substr(cmd4.length - 2), function (code, data, msg) { }); } else { outCmdArr.push(cmd4); currentCmdId++; if (currentCmdId == cmdTotal) { var _res = {}; _res.code = 0; _res.data = outCmdArr; _res.msg = "成功"; CallBackToQT(_res); } else { channel(globalCmdType, inCmdArr[currentCmdId], function (code, data, msg) {}); } } } } function analysis3005(data) { var length = hex2int(data.substring(12, 16)); var cmd = data.substring(26, 26 + length * 2); CallBackInfo(0, cmd, cmd); } function analysis3006(data) { var length = hex2int(data.substring(12, 16)); var cmd = data.substring(26, 26 + length * 2); CallBackInfo(0, cmd, cmd); } /////////// /** * 将方法暴露出来 */ module.exports = { openBle: openBle, disconnectDevice: disconnectDevice, connectDevice: connectDevice, transCmd: transCmd }