let dataTransition = require('./dataTransition.js'); let util = require('./util.js'); /** * v2.1.2 * 胡宇健 * 注:电池锁等获取设备状态的功能暂只兼容了WA-206的设备 */ function BluetoothOperate() { let _machineNO = null; //当前类保留的设备编号 let _operateType = null; //操作 let _discoveryDevicesTimer = null; let _deviceId = null; let _serviceId = 'FEF6'; //UUID let _characteristicId_notify = null; //读特征值 let _characteristicId_write = null; //写特征值 let _sequenceId = 10; //流水号 let _sendData = ''; //发送的包数据 let _dataLen = 0; //数据长度 let _systemState = ''; let _sequenceId_16 = ''; //流水号16进制 let _CRC16 = ''; let _dataContent = ''; let _connected = false; let _hasReceive = false; let _callBack = null; //回调函数 let _key = ''; //蓝牙密钥 let _sendCommandTime = 0; let _sendCommandTimer = null; //是否重新发送消息的定时器 let _listenerTime = 0; let _listenerTimer = null; let _reConnect = 1; //重新连接次数 let _logList = []; //日志包 let _machineState = []; //设备状态二进制数组 let _machineVerson = ''; //设备版本号 let _machinevoltage = ''; //设备电压 this.start = (operateType, machineNO, key, cb) => { _initVariable(); //初始化变量 //变量赋值 _operateType = operateType; _key = key; _callBack = cb; this.log(machineNO, _machineNO, 'operate:', _operateType); //对比两个设备编号 _initBluetooth(() => { //初始化 //若存在该设备编号表面与前面的操作一致,直接使用deviceId进行连接 if (machineNO == _machineNO && _deviceId && _operateType) { _startConnectDevices(); //进入直接连接步骤 } else { _machineNO = machineNO; _deviceId = null; _operate(); //进入搜索步骤 } }) } this.end = (cb) => { my.hideLoading(); my.hideToast(); if (_sendCommandTimer) { clearTimeout(_sendCommandTimer); } if (_listenerTimer) { clearTimeout(_listenerTimer); } my.disconnectBLEDevice({ deviceId: _deviceId, complete: () => { _closeBluetoothAdapter(); _stopBluetoothDevicesDiscovery(); this.log('结束蓝牙操作!'); cb && cb(); _initVariable(); } }) } this.getMachineNO = () => { return _machineNO; } this.getKey = () => { return _key; } this.getLog = () => { return _logList; } //获取电池锁状态 this.getBatteryLockState = () => { return _machineState[0]; } //判断电池锁是否开启 this.isOpenBatteryLock = () => { if (_machineState[0] == 0) return true; else return false; } //获取设备运动状态 this.getMotionState = () => { return _machineState[6]; } //判断设备是否运动中 this.isMotion = () => { if (_machineState[6] == 1) return true; else return false; } //获取设备借还车状态 this.getBorrowState = () => { return _machineState[7]; } //判断设备是否已借车 this.isBorrowed = () => { if (_machineState[7] == 0) return true; else return false; } this.getMachinevoltage = () => { return _machinevoltage / 1000; } const _initVariable = () => { _operateType = null; _discoveryDevicesTimer = null; _serviceId = 'FEF6'; _characteristicId_notify = null; _characteristicId_write = null; _sequenceId = 10; _sendData = ''; _dataLen = 0; _systemState = ''; _sequenceId_16 = ''; _CRC16 = ''; _dataContent = ''; _connected = false; _hasReceive = false; _callBack = null; _sendCommandTime = 0; _sendCommandTimer = null; _listenerTime = 0; _listenerTimer = null; _logList = []; _reConnect = 1; } const _operate = () => { //进入常规搜索流程 _deviceId = null; //一旦进入常规搜索流程初始化id,确保中断操作时下一次操作不会误使用该id _startBluetoothDevicesDiscovery(); } //开启蓝牙搜索,下一步打开监听事件 const _startBluetoothDevicesDiscovery = () => { my.startBluetoothDevicesDiscovery({ allowDuplicatesKey: false, services: ['FEF6', 'FEF5'], success: (res) => { this.log('蓝牙搜索启用成功!'); this.log(res.errMsg); if (res.isDiscovering) { _onBluetoothDeviceFound(); } else { this.log('没有开启定位服务', 'isDiscovering = false'); } }, fail: (err) => { if (err.errMsg.indexOf('not init') > -1) { _initBluetooth(() => { _startBluetoothDevicesDiscovery(); }); } else { this.end(() => { _callBack && _callBack(false); }); this.log('startBluetoothDevicesDiscovery error:', err.errMsg); } } }) } //蓝牙搜索监听事件 const _onBluetoothDeviceFound = () => { _repeatDiscoveryMachine(); my.onBluetoothDeviceFound((res) => { let device = res.devices[0]; if (device && device.advertisData && device.advertisData.byteLength != 0) { let machineNO = dataTransition.encrypt(dataTransition.ab2hex(device.advertisData).slice(4, 13)); this.log('搜索到的设备编号:' + machineNO + ",目标:" + _machineNO); if (machineNO == _machineNO) { _stopBluetoothDevicesDiscovery(); clearInterval(_discoveryDevicesTimer); _discoveryDevicesTimer = null; _deviceId = device.deviceId; this.log('deviceId:', _deviceId); // if (_operateType == 'open' || _operateType == 'close') if (_operateType != '') _startConnectDevices(); else _callBack && _callBack(true); } } }) } //定时器 const _repeatDiscoveryMachine = () => { let discoveryDevicesTime = 0; if (_discoveryDevicesTimer) { clearInterval(_discoveryDevicesTimer); _discoveryDevicesTimer = null; } _discoveryDevicesTimer = setInterval(() => { if (discoveryDevicesTime > 14) { clearInterval(_discoveryDevicesTimer); _discoveryDevicesTimer = null; discoveryDevicesTime = 0; my.hideToast(); my.hideLoading(); _stopBluetoothDevicesDiscovery(); this.end(() => { _callBack && _callBack(false); }); // my.confirm({ // title: '温馨提示', // content: '搜索设备失败,请尝试重新打开定位与蓝牙,是否重新搜索?', // success: (res) => { // if (res.confirm==false) { // this.end(() => { // _callBack && _callBack(false); // }); // } else { //蒙层和confirm // my.showToast({ // content: '请稍候', // icon: 'loading', // mask: true, // duration: 1000000 // }) // _startBluetoothDevicesDiscovery(); // } // } // }) } else { discoveryDevicesTime++; console.log('搜索时间:', discoveryDevicesTime) } }, 1000) } //通过deviceId连接蓝牙设备 const _startConnectDevices = () => { if (_deviceId.length > 0) { my.connectBLEDevice({ deviceId: _deviceId, timeout: 15000, success: (res) => { this.log('createBLEConnection success:', res.errMsg); if (res.errCode == 0) { _getService(); } }, fail: (err) => { this.log('createBLEConnection error:', err.errMsg); if (err.errCode != -1 && err.errCode != 10000) { if (err.errCode == 10006 || err.errCode == 10003 || err.errCode == 10012) { this.end(() => { _callBack && _callBack(false); }); // my.confirm({ // title: '温馨提示', // content: '连接失败,请尝试: (1)重新打开手机定位 (2)点击重试', // confirmText: '重试', // success: (res) => { // if (res.confirm == false) { // this.end(() => { // _callBack && _callBack(false); // }); // } else { // this.log('正在重连...'); // //这里加一个 // my.closeBLEConnection({ // deviceId: _deviceId, // complete: () => { // my.closeBluetoothAdapter({ // success: () => { // my.openBluetoothAdapter({ // success: () => { // this.log('重连之前,重启蓝牙适配器成功!') // if (_reConnect >= 2) { // _startBluetoothDevicesDiscovery(); // } else { // _reConnect++; // _startConnectDevices(); // } // } // }) // } // }); // }, // }) // } // } // }) } } else if (err.errMsg.indexOf('not init') > -1) { //入口之一,判断无初始化直接执行 _initBluetooth(() => { _startConnectDevices(); }); } else { this.end(() => { _callBack && _callBack(false); }); } }, complete: () => { } }); } } //获取所有服务 const _getService = () => { _reConnect = 1; // 获取蓝牙设备service值 my.getBLEDeviceServices({ deviceId: _deviceId, success: (res) => { this.log('获取的所有服务值', res.errMsg); _getCharacter(res.services); }, fail: (err) => { this.log('getBLEDeviceServices error', err.errMsg); my.showToast({ content: 'service获取失败', icon: 'none', }) this.end(() => { _callBack && _callBack(false); }); } }) } //获取特征值 const _getCharacter = (services) => { services.forEach((service, index) => { if (service.uuid.indexOf(_serviceId) > -1) { _serviceId = service.uuid; this.log("serviced", _serviceId) my.getBLEDeviceCharacteristics({ deviceId: _deviceId, serviceId: _serviceId, success: (res) => { this.log('特征值:', res.errMsg); res.characteristics.forEach((item) => { if (item.properties.write) _characteristicId_write = item.uuid; if (!item.properties.write && item.properties.notify) _characteristicId_notify = item.uuid; }) this.log('特征值读:', _characteristicId_notify, '特征值写:', _characteristicId_write); _notifyBLECharacteristicValueChange(() => { _connectCtrl(); }); }, fail: (err) => { this.log('读取特征值失败:' + err.errMsg); this.end(() => { _callBack && _callBack(false); }); util.showModal_nocancel('读取特征值失败,请重试。') } }) return; } }); } //秘钥连接指令 const _connectCtrl = () => { let secretKey = _key.toString().trim().toLowerCase(); //拼接数据头 let sequenceId_16 = dataTransition.getSequenceId(_sequenceId); _sequenceId++; let c = secretKey.toString().replace(/\s+/g, ""); let cLength = dataTransition.getSecretKeyLength(c); //发送内容 let sendValue = `02 00 01 ${cLength}`; //02 连接命令 01连接请求 cLength秘钥长度。 let allData = `${sendValue} ${secretKey}`; let header = dataTransition.header(allData, 0, '00', sequenceId_16); let data = header + allData.replace(/\s+/g, ""); this.log('发送的连接数据:', data); _sendCtrl(data); } //发送指令。判断是否分包发送数据 const _sendCtrl = (data) => { //保存一下发送的数据 _sendData = data; //如果大于20个字节则分包发送,两个字符一个字节 let dataLen = Math.ceil(data.length / 40); if (dataLen > 1) { //3 for (let i = 0; i < data.length; i += 40) { let value = dataTransition.hexStringToArrayBuffer(data.slice(i, i + 40)); this.log("分包发送的数据", data.slice(i, i + 40)) _writeBLECharacteristicValue(value); } } else { let value = dataTransition.hexStringToArrayBuffer(data); _writeBLECharacteristicValue(value); } } //发送信息 const _writeBLECharacteristicValue = (value, cb) => { setTimeout(() => { my.writeBLECharacteristicValue({ deviceId: _deviceId, serviceId: _serviceId, characteristicId: _characteristicId_write, // 这里的value是ArrayBuffer类型 value: value, success: (res) => { this.log('发送信息成功', res.errMsg); cb && cb(); }, fail: (err) => { this.log('writeBLECharacteristicValue error ', err.errMsg); let code = err.errCode; if (code == 10006 || code == 10000) { //连接断开 _startConnectDevices(); } else if (code == 10008) { this.log('重发数据', dataTransition.ab2hex(value)); _writeBLECharacteristicValue(value); } else { util.showModal_nocancel('数据发送失败,请重试。'); this.end(() => { _callBack && _callBack(false); }); } } }) }, 200); } //监听事件开启 const _notifyBLECharacteristicValueChange = (cb) => { my.notifyBLECharacteristicValueChange({ state: true, deviceId: _deviceId, serviceId: _serviceId, characteristicId: _characteristicId_notify, success: () => { if (_listenerTime != -1) { //超时安全判断 _listenerTimer = setTimeout(() => { if (_listenerTime == 0) { this.log('未接收到回复信息!自动重发一次!'); _listenerTime++; _connectCtrl(); } else { my.hideToast(); my.hideLoading(); my.confirm({ content: '未接收到回复信息!', confirmText: '重试', success: (res) => { if (res.confirm == true) { my.showToast({ content: '请稍后', icon: 'loading', mask: true, duration: 100000 }) _connectCtrl(); } else { this.end(() => { _callBack && _callBack(false); }); } } }) } }, 5000) } my.onBLECharacteristicValueChange((res) => { clearTimeout(_listenerTimer); _listenerTime = -1; let data = dataTransition.ab2hex(res.value) this.log('********notify收到的数据:', data); if (data.slice(0, 4) == 'aa10') { this.log('指令发送成功:'); } else if (data.slice(0, 4) == 'aa30') { this.log('CRC校验失败'); util.showModal_nocancel('CRC校验失败,请重试。'); this.end(() => { _callBack && _callBack(false); }); //end } else if (data.slice(0, 3) == 'aa0' && _dataLen == 0) { //设备版本号 _machineVerson = data.slice(2, 4); //16进制流水号 _sequenceId_16 = data.slice(6, 8); //0a //计算数据包长度 _dataLen = parseInt(data.slice(8, 12), 16); //003e,16=62 //计算systemState _systemState = data.slice(4, 6); //4c _analysisSystem(_systemState); //crc _CRC16 = data.slice(12, 16); //290e this.log("需要接收的字节长度", _dataLen); if (data.length > 16) { _connectData(data.slice(16)) } } else { if (_dataLen > 0) { _connectData(data) } } }) cb && cb(); }, fail: (err) => { this.log('notifyBLECharacteristicValueChange', err.errMsg); util.showModal_nocancel('特征值监听开启失败,请重试。') this.end(() => { _callBack && _callBack(false); }); } }) } //解析存储车辆状态 const _analysisSystem = (systemState) => { //16进制转换为10进制 let decimalState = parseInt(systemState, 16); //10进制转换为2进制 let binaryState = parseInt(decimalState).toString(2); if (binaryState.length < 8) { let supplyNum = 8 - binaryState.length; while (supplyNum > 0) { binaryState = '0' + binaryState; supplyNum--; } } //字符串拆分成为数组 let stateArray = Array.prototype.slice.call(binaryState); _machineState = stateArray; console.log(stateArray); } //拼接数据,判断数据并发送 const _connectData = (data) => { this.log('拼接内容:', data); _dataContent += data; this.log('内容长度', _dataContent.length, '接收到的数据长度', _dataLen, '*2'); if (_dataContent.length == _dataLen * 2) { //接收完该长度的字节和校验CRC成功之后再发送ACK let dc = _dataContent; let dcArr = []; this.log('接收的数据长度字节:', dc.length / 2); let contentArr = dataTransition.addFlagBeforeArr(dataTransition.strAverage2Arr(dc, 2)); if (parseInt(dataTransition.getCRC16(contentArr), 16) == parseInt(_CRC16, 16)) { this.log('CRC16校验成功'); let value = dataTransition.hexStringToArrayBuffer(`aa12${_systemState}${_sequenceId_16}00000000`); this.log(`返回的确认数据${dataTransition.ab2hex(value)}`); //分析数据返回的内容 _analysisBLEContent(dc, value); } else { this.log('CRC16校验失败', dataTransition.getCRC16(contentArr) + "_应为:" + _CRC16); util.showModal_nocancel('CRC16校验失败,请重试。'); this.end(() => { _callBack && _callBack(false); }); //end } _dataLen = 0; _systemState = ''; _dataContent = ''; _sequenceId_16 = ''; _CRC16 = ''; } } //解析蓝牙发送内容 const _analysisBLEContent = (content, reply) => { this.log('解析数据数据***************', content); if (content.indexOf('020101') > -1) { if (!_connected) { _connected = true; this.log('连接成功'); _writeBLECharacteristicValue(reply, () => { _ctrl(_operateType); }); _analysisVoltage(content); } } else if (content.indexOf('0300820100') > -1) { if (_sendCommandTimer) { //每次都将清楚等待任务 clearTimeout(_sendCommandTimer); _sendCommandTimer = null; } if (!_hasReceive) { _hasReceive = true; _writeBLECharacteristicValue(reply, () => { this.log('开锁成功,开始回调ctrl_cb'); this.end(() => { _callBack && _callBack(true); }); }); } } else if (content.indexOf('0300810100') > -1) { if (_sendCommandTimer) { //每次都将清楚等待任务 clearTimeout(_sendCommandTimer); _sendCommandTimer = null; } if (!_hasReceive) { _hasReceive = true; _writeBLECharacteristicValue(reply, () => { this.log('上锁成功,开始回调ctrl_cb'); this.end(() => { _callBack && _callBack(true); }); }); } } else if (content.indexOf('0300840100') > -1) { //响铃成功 if (_sendCommandTimer) { //每次都将清楚等待任务 clearTimeout(_sendCommandTimer); _sendCommandTimer = null; } if (!_hasReceive) { _hasReceive = true; _writeBLECharacteristicValue(reply, () => { this.log('响铃成功,开始回调ctrl_cb'); this.end(() => { _callBack && _callBack(true); }); }); } } else if (content.indexOf('0300870100') > -1) { //临时关锁成功 if (_sendCommandTimer) { //每次都将清楚等待任务 clearTimeout(_sendCommandTimer); _sendCommandTimer = null; } if (!_hasReceive) { _hasReceive = true; _writeBLECharacteristicValue(reply, () => { this.log('响铃成功,开始回调ctrl_cb'); this.end(() => { _callBack && _callBack(true); }); }); } } else if (content.indexOf('0300850100') > -1) { //开电池锁成功 if (_sendCommandTimer) { //每次都将清楚等待任务 clearTimeout(_sendCommandTimer); _sendCommandTimer = null; } if (!_hasReceive) { _hasReceive = true; _writeBLECharacteristicValue(reply, () => { this.log('响铃成功,开始回调ctrl_cb'); this.end(() => { _callBack && _callBack(true); }); }); } } else if (content.indexOf('04008524') > -1) { _writeBLECharacteristicValue(reply, () => { this.log('心跳包'); }); } else if (content.indexOf('020100') > -1) { if (_sendCommandTimer) { //每次都将清楚等待任务 clearTimeout(_sendCommandTimer); _sendCommandTimer = null; } this.log('鉴权失败:', _sendData); util.showModal_nocancel('鉴权失败!') this.end(() => { _callBack && _callBack(false); }); //end } else { if (_sendCommandTimer) { //每次都将清楚等待任务 clearTimeout(_sendCommandTimer); _sendCommandTimer = null; } this.end(() => { let text = content === '0300810102' ? '运动中不能上锁!' : '蓝牙操作失败,请重试!'; this.log(text) my.showToast({ content: text, mask: true, icon: 'none', duration: 5000 }) _callBack && _callBack(false); }) } } const _analysisVoltage = (content) => { //判断81 :终端电池电压 if (content.indexOf('02010181') > -1) { //这之后两位表示电池电压:81,两位表示长度:02,加上020101共十位 let startN = content.indexOf('020101') + 10; //后四位表示电压值:16进制 let voltageData = content.slice(startN, startN + 4); let voltage = parseInt(voltageData, 16); if (_machineVerson == '02') _machinevoltage = voltage * 10; else _machinevoltage = voltage; } } //指令 const _ctrl = (type) => { let sequenceId_16 = dataTransition.getSequenceId(_sequenceId); _sequenceId++; let sendData = ''; if (type === 'open') // sendData = '03 00 02 01 00'; else if (type === 'close') // sendData = '03 00 01 01 01'; else if (type === 'bell') // sendData = '03 00 04 01 01'; else if (type === 'batteryOpen') // sendData = '03 00 05 01 01'; else if (type === 'temporaryClose') // sendData = '03 00 01 01 30'; else if (type === 'temporaryOpen') // sendData = '03 00 01 01 31'; let header = dataTransition.header(sendData, 0, '00', sequenceId_16); let data = header + sendData.replace(/\s+/g, ""); this.log(`发送${type}指令`, data); _sendCtrl(data); _sendCommandTimer = setTimeout(() => { //可能出现发送消息后没有收到应答,将再次发送 if (_sendCommandTime == 0) { this.log('设备未响应,自动重发') _sendCommandTime++; _ctrl(_operateType); } else { this.log('设备未响应') my.hideLoading(); util.showModal('设备未响应,是否重新发送指令?', () => { this.log('手动重发ctrl') my.showLoading({ content: '开锁中', }) _ctrl(_operateType); }, () => { this.end(() => { _callBack && _callBack(false); }); }) } }, 5000) } const _stopBluetoothDevicesDiscovery = () => { my.stopBluetoothDevicesDiscovery({ success: (res) => { this.log('停止搜寻附近的蓝牙外围设备'); }, fail: (err) => { this.log(err.errMsg); } }) } const _closeBluetoothAdapter = () => { my.closeBluetoothAdapter({ success: (res) => { this.log('关闭蓝牙适配器') }, fail: (err) => { this.log(err.errMsg); } }) } const _initBluetooth = (cb) => { my.openBluetoothAdapter({ success: (res) => { }, fail: (err) => { this.log('initBluetooth:', err.errMsg); // my.showToast({ // content: '蓝牙初始化失败', // icon: 'none', // }) }, complete: () => { cb && cb(); } }) } this.log = (...str) => { let now = new Date(); now = util.formatTime(now) console.log(now, ...str); _logList.push(str.join(',')) } } module.exports = BluetoothOperate