"use strict"; /*初始化蓝牙适配器*/ //扫描时长,定时任务 let times = 5; let scanInter; // 连接失败次数 let connectTimes = 0; // 蓝牙uuid let peripheralUUID ='' // 蓝牙服务id let serviceUUID ='' // 蓝牙特征id let characteristicUUID ='' // 蓝牙待发送消息列表 let msgList = []; // 蓝牙消息发送定时任务150ms let timerMsgList = null; // 未装料状态计数,采集达到10次,发送一次停止蜂鸣指令 let bleConunt=0; // 默认不自动跳转 let allowAuto = false; // 清除消息定时任务 clearInterval(timerMsgList) //蜂鸣器最后控制时间 let lastTime = new Date().getSeconds()*1000+ new Date().getMilliseconds(); // 接收重量数据计数 let countLED = 0; //判断是否稳定记录的上次重量信息 let weight = 0; /** * 初始化蓝牙 * @param param */ function initManager(param) { ble.initManager(param, function (ret) { if (ret.state === "poweredOn") { scan({clean: true}); } else if (ret.state === 'poweredOff') { api.hideProgress(); mui.alert('检查蓝牙是否打开'); document.getElementById('text').innerHTML = '检查蓝牙是否打开'; } }); } /** * 扫描蓝牙 * @param param */ function scan(param) { if (!uuid){ //从缓存获取uuid uuid = api.getPrefs({ sync: true, key: 'uuid' }); } // 存在缓存uuid if (uuid){ // 已连接先断开 isConnected({peripheralUUID: uuid},function(ret){ if(ret){ disconnect({peripheralUUID: uuid}); } }) } //定时扫描 clearInterval(scanInter) times = 5; ble.scan(param, function (ret) { if (ret.status) { scanInter = setInterval(function (){ if (times>0){ times --; let wifiDom = document.getElementById('wifiDom'); wifiDom.innerHTML = "扫描中"+times; api.showProgress({ title: '扫描中'+times+'...', modal: true }); }else { stopScan() clearInterval(scanInter) getPeripheral(); } },1000) } }); } /** * 停止扫描 */ function stopScan(){ ble.stopScan() } /** * 获取扫描结果 */ function getPeripheral() { ble.getPeripheral(function (ret) { bleData = [] let wifiDom = document.getElementById('wifiDom'); if (ret.peripherals.length > 0) { ret.peripherals.forEach(function (p) { if (undefined !== p.name) { if (p.name.indexOf("TMR_BLE") === -1){ return false }else{ console.log(p.name); mui.toast("已扫描到" +p.name,{duration:3500,type:'div'}); wifiDom.innerHTML = p.name+'(未连接)'; document.getElementById('showBlePicker').setAttribute("class", 'btnc mui-btn-inline mui-btn-primary'); let json = { value: p.uuid, text: p.name }; bleData.push(json); //自动连接上次蓝牙 if (uuid && uuid === p.uuid){ document.getElementById('showBlePicker').innerHTML = p.name connect({peripheralUUID: uuid}); // isConnected({peripheralUUID: uuid},function(ret){ // if(ret){ // disconnect({peripheralUUID: uuid}); // connect({peripheralUUID: uuid}); // }else{ // connect({peripheralUUID: uuid}); // } // }) } } } }); if(!bleData.length>0){ mui.toast("暂未扫描到蓝牙设备",{duration:3500,type:'div'}); document.getElementById('showBlePicker').setAttribute("class", 'btnc mui-btn-inline mui-btn-primary dis'); wifiDom.innerHTML = "重新扫描"; }else{ bleInit() } } api.hideProgress(); }); } /** * 连接蓝牙 * @param param */ function connect(param) { let wifiDom = document.getElementById('wifiDom'); wifiDom.innerHTML = ""; api.showProgress({ title: '蓝牙连接中...', modal: true }); ble.connect(param, function (ret, err) { if (ret.status) { wifiDom.innerHTML = '已连接'; api.hideProgress(); connetcedStatus = true // 缓存蓝牙设备uuid方便自动连接 api.setPrefs({ key: 'uuid', value: param.peripheralUUID }); //累计连接次数 if (connectTimes>0){ addNotice(1,'#02a7f0',sbId + '主机端第'+connectTimes+'次重连成功'); }else { addNotice(1,'#02a7f0',sbId + '主机端初始化连接成功'); } //连接成功清空连接次数 connectTimes = 0; document.getElementById('text').innerHTML = '设备已连接'; text = '设备已连接'; // console.log('主机端连接成功') //设置MTU包大小 ble.setMtu({ peripheralUUID: param.peripheralUUID, mtu:150 },function(ret){ if(ret.status){ // 加载图表 initWeight(); discoverService({peripheralUUID: param.peripheralUUID}); }else{ mui.alert(ret.code) } }); }else { console.log('主机端连接失败'+JSON.stringify(err)) wifiDom.innerHTML = '连接失败'; api.hideProgress(); connetcedStatus = false //累计连接次数 if (connectTimes > 0){ if(connectTimes%10 === 0){ addNotice(4,'red',sbId + '主机端第'+connectTimes+'次重连失败'); } }else { addNotice(4,'red',sbId + '主机端连接失败,开始尝试重连'); } //连接失败一次加一次 connectTimes ++; setTimeout(function (){ if (!connetcedStatus && uuid && feedStatus){ console.log("连接失败!尝试重连" + uuid); wifiDom.innerHTML = '尝试重连'; connect({peripheralUUID: uuid}); } },3000) } }); } /*断开连接*/ function disconnect(param) { ble.disconnect(param, function (ret, err) { if (ret.status) { console.log("断开连接成功!"); } else { console.log("断开连接失败!"+JSON.stringify(err)); } }); } /*判断是否已连接*/ function isConnected(param,callback){ ble.isConnected(param, function(ret) { if (ret.status) { // console.log('已连接'); } callback(ret.status) }); } setTimeout(() => { timerMsgList = setInterval(function () { select(db, 'SELECT * FROM t_ble_msg order by id', function (ret, err) { if (!ret.status) { console.log(JSON.stringify(err)) } var data = ret.data // console.log(data.length) if (data.length > 0) { // console.log(Number(data[0].id)) // 发送报文 let msg = data[0].text; var sql = "delete from t_ble_msg where id = '" + Number(data[0].id) + "'"; execute(db, sql, function (ret, err) { if (!ret.status) { console.log(JSON.stringify(err)) } }) let param1 = { peripheralUUID: peripheralUUID, serviceUUID: serviceUUID, characteristicUUID: characteristicUUID, value: msg, writeType:'withoutResponse' }; if(connetcedStatus && peripheralUUID && serviceUUID && characteristicUUID){ writeValueForCharacteristic(param1, function (ret) { // led屏幕 var booleanLed = msg.startsWith('FF05') && msg.substr(8,4) === 'AAA5'; // 设备消息数据同步 var booleanSendWeight = msg.startsWith('FF0A0004'); if (ret.status && msg.length > 12 && (booleanLed || booleanSendWeight)) { // 同步刷新图表 // 饲喂状态刷新实时状态,否则不刷新装料信息 if(feedStatus && (allowAuto || weightBeforefix ==='1')){ renderUnload(); feedMyChart.setOption({ dataset: { source: [[1, _feedNum]] } }); } renderLoad(); weightMyChart.setOption({ dataset: { source: [[1, nowWeight]] } }); } // console.log(ret.status) }); } } }) }, 100); }, 3000); // /*蓝牙消息发送定时任务150ms*/ // timerMsgList = setInterval(function(){ // // console.log(new Date().getSeconds()+ ' ' + new Date().getMilliseconds() +' ' + msgList.length) // if (msgList.length > 0) { // // 发送报文 // let msg = msgList[0]; // msgList.splice(0, 1); // let param1 = { // peripheralUUID: peripheralUUID, // serviceUUID: serviceUUID, // characteristicUUID: characteristicUUID, // value: msg, // writeType:'withoutResponse' // }; // if(connetcedStatus && peripheralUUID && serviceUUID && characteristicUUID){ // writeValueForCharacteristic(param1, function (ret) { // // console.log(ret.status) // }); // } // } // },100) // let mss = ''; // setInterval(function(){ // // console.log(currTimeFnMs(new Date)) // let param1 = { // peripheralUUID: peripheralUUID, // serviceUUID: serviceUUID, // characteristicUUID: characteristicUUID, // value: mss, // writeType:'withoutResponse' // }; // if(connetcedStatus && peripheralUUID && serviceUUID && characteristicUUID && mss.length>0){ // writeValueForCharacteristic(param1, function (ret) { // // console.log(ret.status) // }); // } // },100) function currTimeFnMs(_date) { var _year = _date.getFullYear(); var _month = _date.getMonth() + 1 < 10 ? '0' + (_date.getMonth() + 1) : _date.getMonth() + 1; var _day = _date.getDate() < 10 ? '0' + _date.getDate() : _date.getDate(); var _hour = _date.getHours() < 10 ? '0' + _date.getHours() : _date.getHours(); var _minutes = _date.getMinutes() < 10 ? '0' + _date.getMinutes() : _date.getMinutes(); var _seconds = _date.getSeconds() < 10 ? '0' + _date.getSeconds() : _date.getSeconds(); var _ms = _date.getMilliseconds() < 10 ? '0' + _date.getMilliseconds() : _date.getMilliseconds(); return _year + "-" + _month + "-" + _day + " " + _hour + ":" + _minutes + ":" + _seconds + " "+_ms; } /*扫描蓝牙服务*/ function discoverService(param) { ble.discoverService(param, function (ret) { if (ret.services.length > 0) { // console.log("发现服务成功!"); ret.services.forEach(function (ret4) { if (ret4.indexOf("0000ffe") !== -1) { if (ret4 && ret4.length > 0) { //发现特征 discoverCharacteristics({ serviceUUID: ret4, peripheralUUID: param.peripheralUUID }, function (ret5) { if(ret5.indexOf("0000ffe2")!==-1){ return false } // console.log(ret5) peripheralUUID = param.peripheralUUID; serviceUUID =ret4; characteristicUUID =ret5; //监听消息 setNotify({ peripheralUUID: param.peripheralUUID, serviceUUID: ret4, characteristicUUID: ret5 }, function (ret6) { if (ret6 !== undefined && ret6 !=='') { if (isNaN(ret6)) { return false } //实时总重量 nowWeight = ret6; // 实时重量保存 totalList.push({ time: currTimeFnMs(new Date()), nowWeight: nowWeight }); //计算实时重量均值 const filteredValue = weightDataFilter.filter(nowWeight); //组装并发送实时重量LED屏报文 getMess({ peripheralUUID: param.peripheralUUID, serviceUUID: ret4, characteristicUUID: ret5, value: ret6 }, function (ret7) { }); // 1装料状态 // 2已校准或不需要校准 if(feedStatus && (allowAuto || weightBeforefix ==='1')){ //第一次加载 if (first) { first = false; beforeWeight = nowWeight; } if (load) { _feedNum = nowWeight - beforeWeight; // _feedNum = (filteredValue - beforeWeight).toFixed(0); document.getElementById('feedNum').innerHTML = ':' + _planSum + '/已装:' + _feedNum + ''; } else { _feedNum = beforeWeight - nowWeight; // _feedNum = (beforeWeight - filteredValue).toFixed(0); document.getElementById('feedNum').innerHTML = ':' + _planSum + '/已卸:' + _feedNum + ''; } // 显示饲料名称+计划饲喂量 text = feedName + _planSum; // 计划-饲喂 var feedErr = _planSum-_feedNum; if (isNaN(feedErr)) { return false } // console.log(beforeWeight,nowWeight) // console.log("feedErr:"+feedErr,"allow:"+allow,"_feedNum:"+_feedNum,"_planSum:"+_planSum) //蜂鸣器判断 buzzer(feedErr) // 判断重量是否稳定 const ifSta = isStable(weightDataFilter.dataBuffer, threshold); // 判断 // 1:已开启自动跳转autoJump // 2:已校准allowAuto或者不需要校准weightBeforefix // 3:已满足误差或者已装够 if (autoJump==='0' && (feedErr <= allow || _feedNum > _planSum) && (allowAuto || weightBeforefix === '1')) { // 已稳定 if(ifSta){ //稳定数据包 wendinSize.push({time:new Date().getTime(),val:nowWeight}) // 稳定时长 const staTimes = (wendinSize[wendinSize.length - 1].time - wendinSize[0].time) / 1000; console.log('数据稳定时长'+staTimes) document.getElementById('isfix').innerHTML = '稳定'+staTimes.toFixed(0)+'' if(staTimes > weightSize){ console.log("重量稳定", currTimeFn(new Date())); weightDataFilter.dataBuffer = [] wendinSize = [] remark = '自动跳转' document.getElementById('submitData').click(); } }else{ // 不稳定清空所有数据 weightDataFilter.dataBuffer = [] wendinSize = [] } } } } }); }); } } }); } else { console.log("发现服务失败!"); } }); } function buzzer(feedErr){ // 蜂鸣器校验基础值,大于500的饲料,取500为标准 let count; if (_planSum >= 500) { count = 500 } else { count = _planSum } // 计算饲料装料量与基础值的误差比例 let ration = feedErr / count; // 误差分为100%-20%(停止蜂鸣),10%-20%(间隔05),5%-10%(间隔03),2%-5%(间隔01),-100%-2%(长鸣) if (ration > 0.2) { bleConunt ++; if (statusEle || (bleConunt >= 10)) { bleConunt=0; status = ''; statusEle = false; msgList = []; control("00", "00"); setTimeout(function () { }, 500); } } else if (0.1 < ration && ration <= 0.2) { time = "05"; if (!statusEle || lastTime !== time) { lastTime = time; status = '间隔'; statusEle = true; msgList = []; console.log("开始蜂鸣" + time); control("60", time); setTimeout(function () { }, 500); } } else if (0.05 < ration && ration <= 0.1) { time = "03"; if (!statusEle || lastTime !== time) { lastTime = time; status = '间隔'; statusEle = true; msgList = []; console.log("开始蜂鸣" + time); control("60", time); setTimeout(function () { }, 500); } } else if (0.02 < ration && ration <= 0.05) { time = "01"; if (!statusEle || lastTime !== time) { lastTime = time; status = '间隔'; statusEle = true; msgList = []; console.log("开始蜂鸣" + time); control("60", time); setTimeout(function () { }, 500); } } else { if (status === '间隔' || status === '') { status = '长鸣'; statusEle = true; msgList = []; console.log("开始长鸣"); control("40", "02"); setTimeout(function () { }, 500); setTimeout(function() { console.log("开始长鸣") control("40", "02"); setTimeout(function() { }, 500) }, 500) } } } /** * 判断一组数据是否稳定在threshold误差范围内 * @param data * @param threshold * @returns {boolean} */ function isStable(data, threshold) { // 确保输入的数据是一个数组 if (!Array.isArray(data)) { throw new Error('Data must be an array'); } // 确保输入的阈值是数字 if (typeof threshold !== 'number') { throw new Error('Threshold must be a number'); } // 找到数组中的最大值和最小值 const min = Math.min(...data); const max = Math.max(...data); // 计算差值的绝对值 const difference = Math.abs(max - min); // 比较差值和阈值 return difference <= threshold; } /*扫描特征*/ function discoverCharacteristics(param, callback) { ble.discoverCharacteristics(param, function (ret) { let res = ''; if (ret.characteristics.length > 0) { // console.log("发现特征成功!"); ret.characteristics.forEach(function (s) { if (s.serviceUUID === param.serviceUUID) { callback(s.uuid); } }); } else { console.log("发现特征失败!"); callback(res); } }); } /*适配两种称数据解析*/ function StringToNum(hexx){ var hex = hexx.toString(); //force conversion var str = ''; var num = ''; for (var i = 0; i < hex.length; i += 2) { num = String.fromCharCode(parseInt(hex.substr(i, 2), 16)); if((num >= '0' && num <= '9') || (num === '-')) { str += num; } else { if(str.length !== 0) { return str } } }return str; } var weiNum = 0; /*监听蓝牙数据,转换重量数据*/ function setNotify(param, callback) { ble.setNotify(param, function (ret) { var res; var value = ret.characteristic.value; // if (ret && value.indexOf('ff01') !== -1) { // 获取设备称实时数据 if (ret && value.startsWith("ff01") && value.length > 8 && value.length < 50 ){ var wei = value.substr(8, value.length-8); weiNum++; if (shujubao >= 1 && weiNum >= (shujubao/2).toFixed(0)){ res = Math.round(Number(StringToNum(wei))); } // 发送实时重量和校准重量到其他平板 if(weiNum >= shujubao && undefined !== res && sbId && undefined !== beforeWeight){ weiNum = 0; var weiHex = decimalToHex(res>0?res:res+65536).replace(' ',''); var sbIdHex = decimalToHex(sbId).replace(' ',''); var beforeHex = decimalToHex(beforeWeight>0?beforeWeight:beforeWeight+65536).replace(' ',''); var banciHex = decimalToHex(classCode).replace(' ',''); var checiHex = decimalToHex(trainNumber).replace(' ',''); var feedInexHex = decimalToHex(feedIndex).replace(' ',''); weiHex = weiHex.length < 4?'00'+weiHex:weiHex; sbIdHex = sbIdHex.length < 4?'00'+sbIdHex:sbIdHex; beforeHex = beforeHex.length < 4?'00'+beforeHex:beforeHex; if(feedStatus){ var bleStr = 'FF0A0009'+weiHex+beforeHex+sbIdHex+banciHex+checiHex+feedInexHex; // msgList.push(bleStr); // console.log(bleStr) execute(db, "INSERT INTO t_ble_msg (id,text) VALUES ("+new Date().getTime()+",'"+bleStr+"');", function (ret, err) {}) } } } // 接收其他平板发来的校准重量和实时重量 if (feedStatus && ret && value.startsWith("ff0a") && sbId === parseInt(value.substr(8, 4),16)){ var sbId_now = parseInt(value.substr(8, 4),16); var feedIdx = parseInt(value.substr(12, 2),16); console.log(sbId) if(sbId !== sbId_now || feedIndex !== feedIdx || !load){ return false } weightDataFilter.dataBuffer = [] wendinSize = [] document.getElementById('submitData').click(); } callback(res); }); } function decimalToHex(decimal) { let hex = decimal.toString(16); if (hex.length % 2 !== 0) { hex = "0" + hex; } if (hex.length > 2) { var str = ''; for (var i = 0; i < hex.length; i += 2) { str = str + hex.substr(i, 2) + ' ' } return str.trim() } return hex; } /*发送消息到蓝牙*/ function writeValueForCharacteristic(param, callback) { if (param.value && connetcedStatus){ ble.writeValueForCharacteristic(param, function (ret) { // console.log(param.value) if(param.value.startsWith('FF0A0009')){ // console.log(JSON.stringify(ret)) } if (ret.status) { // console.log("发送成功"); } else { console.log("发送失败:"+param.value); // msgList.push("FF0A000404140414"); } callback(ret); }); } } /*组装报文并发数*/ function getMess(param, callback) { var param1 = { SF: "AA A5 ", LEN: '1A 00 ', DES: 'FF FF ', SRC: '12 34 ', TID: 'B0 A1 ', CMD1: '38 02 ', X: '00 00 ', Y: '00 00 ', WIDTH: '40 00 ', HEIGHT: '20 00 ', FORMAT: '00 00 01 00 ', CNT: '04 00 ', CRC: '00 00 ', EF: '5A 55 ', msg: '' }; var newmsg = feedName; if(!newmsg.length>0){ return false } var msglen = newmsg.getByteLen(); var ttr; if (Number(_planSum - _feedNum) >= 0) { ttr = '少'; echartsType = '少'; } else { ttr = '多'; echartsType = '多'; } if (msglen < 8) { for (var i = 0; i < 8 - msglen; i++) { newmsg = newmsg + ' '; } newmsg = newmsg + ttr; } else { newmsg = feedName + ttr; } // 不是装料状态班次饲喂完成显示 if(!feedStatus){ var newmsg = batchName+'次'; var msglen = newmsg.getByteLen(); var ttr = ''; if (msglen < 8) { for (var i = 0; i < 8 - msglen; i++) { newmsg = newmsg + ' '; } newmsg = newmsg + ttr; } else { newmsg = feedName + ttr; } param1.msg = newmsg + '饲喂完成'; }else{ // param1.msg = newmsg + Math.abs(Number(_planSum - _feedNum)).toFixed(0) + 'kg'; param1.msg = newmsg + Math.abs(Number(_planSum - _feedNum)).toFixed(0); } // param1.msg = newmsg + Math.abs(Number(param.value)).toFixed(0) + 'kg' // console.log(param1.msg) //组装字符卡LED报文 zfk.getMsg(param1, function (ret, err) { if (!ret.status){ console.log(JSON.stringify(err)) } let str = ret.sendMsg; str = str.replace(/\s+/g, ""); let num = str.length / 2; param.value = "FF0500" + num.toString(16) + str; countLED ++; // 称重量数据3包取1包 // console.log(shujubao) //LED报文 if (countLED >= shujubao){ countLED = 0 ; // msgList.push(param.value); execute(db, "INSERT INTO t_ble_msg (id,text) VALUES ("+new Date().getTime()+",'"+param.value+"');", function (ret, err) {}) } }); callback(param1.msg) } /*初始化重量图表*/ function initWeight() { api.showProgress({ title: '图表数据加装中...', modal: true }); renderUnload(); renderLoad(); feedMyChart.setOption({ dataset: { source: [[1, _feedNum]] } }); weightMyChart.setOption({ dataset: { source: [[1, nowWeight]] } }); api.hideProgress(); clearInterval(totalInterval); totalUpload = true; noticeUpload = true; totalList = []; totalInterval = setInterval(function () { totalWeightUpload(); }, 3000); document.getElementById('submitData').setAttribute("class", 'btnc mui-btn-success'); document.getElementById('text').innerHTML = 'V' + api.appVersion; api.setGlobalData({key: 'startTime', value: currTimeFn(new Date())}); }