// components/map-sense.js let innerAudioContext = wx.createInnerAudioContext({ useWebAudioImplement: true }); import http from "../../utils/http"; import { promisify, BeaconUtils } from "../../utils/util"; let openBluetoothAdapter = promisify(wx.openBluetoothAdapter); let closeBluetoothAdapter = promisify(wx.closeBluetoothAdapter); let startBeaconDiscovery = promisify(wx.startBeaconDiscovery); let stopBeaconDiscovery = promisify(wx.stopBeaconDiscovery); import { CDN_URL, CONNECT_STATUS, STATUS_TEXT, API_BASE_URL, DISTRIBUTION, } from "../../config/index"; const STATUS_PIC = { 0: "default", 1: "loading", 2: "success", 3: "fail", }; let audioInterruptionCb = ()=>{} let bluetoothAdapterStateChangeCb = ()=>{} let isCollect = false let changeTO = null // 选择6档 const TXPOWER = -55; var result = 0, oldResult = -1; // 距离经验值(调试所得) const N = 3.3; const R = 3.4; //设定感应范围半径 const RSSIVAL = 70; //rssi信号过滤临界点 let AveLength = 10; let group = [ ["10001", "10002"], ["10003"], ["10004"], ["10005"], ["10006"], ["10007"], ["10008"], ["10009"], ["10010"], ["10011"], ["10012", "10013"], ["10014"], ["10015"], ["10016"], ["10017"], ["10018"], ["10019"], ["10020", "10021"], ["10022"], ["10023"], ["10024"], ]; // 1:['10001','10002'],2:['10003'], // 3:['10004'],4:['10005'],5:['10006'], // 6:['10007'],7:['10008'],8:['10009'], // 9:['10010'],10:['10011'],11:['10012','10013'], // 12:['10014'],13:['10015'],14:['10016'], // 15:['10017'],16:['10018'],17:['10019'], // 18:['10020','10021'],19:['10022'], // 20:['10023'],21:['10024'] let AudioAddress = { 0: "https://culture.4dage.com/demo/audio/1.2.mp3", 1: "https://culture.4dage.com/demo/audio/3.mp3", 2: "https://culture.4dage.com/demo/audio/4.mp3", 3: "https://culture.4dage.com/demo/audio/5.mp3", 4: "https://culture.4dage.com/demo/audio/6.mp3", 5: "https://culture.4dage.com/demo/audio/7.mp3", 6: "https://culture.4dage.com/demo/audio/8.mp3", 7: "https://culture.4dage.com/demo/audio/9.mp3", 8: "https://culture.4dage.com/demo/audio/10.mp3", 9: "https://culture.4dage.com/demo/audio/11.mp3", 10: "https://culture.4dage.com/demo/audio/12.13.mp3", 11: "https://culture.4dage.com/demo/audio/14.mp3", 12: "https://culture.4dage.com/demo/audio/15.mp3", 13: "https://culture.4dage.com/demo/audio/16.mp3", 14: "https://culture.4dage.com/demo/audio/17.mp3", 15: "https://culture.4dage.com/demo/audio/18.mp3", 16: "https://culture.4dage.com/demo/audio/19.mp3", 17: "https://culture.4dage.com/demo/audio/20.21.mp3", 18: "https://culture.4dage.com/demo/audio/22.mp3", 19: "https://culture.4dage.com/demo/audio/23.mp3", 20: "https://culture.4dage.com/demo/audio/24.mp3", }; Component({ /** * 组件的属性列表 */ properties: {}, /** * 组件的初始数据 */ data: { status: "0", cdn_url: CDN_URL, connect_status: CONNECT_STATUS, status_text: STATUS_TEXT, status_pic: STATUS_PIC, targetObj: {}, audio_address: {}, api_base_url: API_BASE_URL, // 是否是扫码播放 isScanPlay: false, cdn_url: CDN_URL, classfiy: {}, }, /** * 组件的方法列表 */ methods: { openBluetooth(cb) { wx.showLoading({ title: "正在打开蓝牙…", mask: true, }); openBluetoothAdapter().then( (res) => { wx.hideLoading({fail(){}}); cb(res); }, () => { wx.hideLoading({fail(){}}); wx.showToast({ title: "请打开蓝牙", icon: "error", duration: 2000, }); } ); }, toHandle() { if (this.data.status == "2") { this.stopBeaconDiscovery(); return; } let aveArr = []; this.openBluetooth(() => { wx.showLoading({ title: "正在连接…", mask: true, }); startBeaconDiscovery({ uuids: ["FDA50693-A4E2-4FB1-AFCF-C6EB07647825"], }) .then(() => { wx.hideLoading({fail(){}}); this.setData({ status: "2", }); isCollect = true wx.onBeaconUpdate((data) => { if (!isCollect) { return } console.log('还在收集…………'); // 需要收集十组数据,索引号最大的组是最旧的 if (!aveArr.includes(data.beacons)) { if (aveArr.length >= AveLength) { //当超过十组时,应该将索引号大的组淘汰掉 aveArr.pop() } aveArr.unshift(BeaconUtils.clone(data.beacons)); //在队列前面插入 } let all = []; //获取所有data.beacons数据 for (let i = 0; i < aveArr.length; i++) { let ele = aveArr[i]; for (let j = 0; j < ele.length; j++) { let item = ele[j]; //过滤信号弱的数据 if (!(item.proximity === 0 && item.rssi === 0)) { if (Math.abs(item.rssi) < RSSIVAL) { // console.log(item.proximity,item.rssi); if (!all.includes(item)) { all.push(BeaconUtils.clone(item)) } } } } } let accuracyList = {}; // classfiy = {10003:[{},{},···],10002:[{},{},···],10001:[{},{},···]} let classfiy = BeaconUtils.classification(all, "major"); // console.log(classfiy,'classfiy'); Object.keys(classfiy).forEach((key) => { //每个major的AveLength个元素数组,元素为rssi let arr = classfiy[key].map((item) => { return item.rssi; }); //每个major的rssi平均值 let ave = BeaconUtils.arrayAverage(arr); //计算平均差 let meanDeviation = BeaconUtils.getMeanDeviation(arr, ave); //计算各rssi的高斯模糊权重 let guassionArr = []; //存放权重数组 for (let i = 0; i < arr.length; ++i) { guassionArr.push( BeaconUtils.getOneGuassionArray( arr.length, i, meanDeviation ) ); } //计算高斯模糊后的rssi值 let rssiArr = []; //模糊后的rssi数组 for (let i = 0; i < arr.length; ++i) { let sum = 0; for (let j = 0; j < arr.length; ++j) { if (guassionArr[i].length == 0) { sum = arr[j]; break; } sum += guassionArr[i][j] * arr[j]; } rssiArr.push(sum); } //时间加权后求rssi平均值 let aveOnTime = BeaconUtils.arrayAverage( rssiArr .slice(0, Math.floor(rssiArr.length / 3)) .concat(rssiArr) ); //测距,根据时间加权后的rssi计算距离 // classfiy[key].forEach((item) => { let tmp = BeaconUtils.calculateAccuracy( TXPOWER, aveOnTime, N ); if (!accuracyList[key]) { //如果还没有对应的“信标号” accuracyList[key] = [tmp]; }else{ accuracyList[key].push(tmp); } // }); }); // Object.keys(accuracyList).forEach(item=>{ // console.log(accuracyList[item]+'---------'+item); // }) // console.log('--------分隔--------'); // 筛选器,筛选出配对的一组信标 let signSelect = new Array(21).fill(0); //记录各组的信标出现数量,投票选择最多的,依次是prologue,annexHall,mainHall // const prologue = ['10001','10002'] //序厅 // const annexHall = ['10003','10004','10005','10006','10007'] //附厅 // const mainHall = ['10008'] //主厅 Object.keys(accuracyList).forEach((key) => { let aveAccuracy = BeaconUtils.arrayAverage(accuracyList[key]); if (aveAccuracy < R) { for (let i = 0; i < group.length; i++) { if (group[i].includes(key)) { signSelect[i] += 1/(aveAccuracy+0.01); //取距离的反比例函数值,距离越近,值越大,+0.01是为了防止除数为0 } } } }); // console.log(signSelect); //对小于预设半径的信号进行数量投票 result = BeaconUtils.maxIndex(signSelect); // console.log("result", result); if (result != null && result !== oldResult) { wx.hideLoading({fail(){}}) this.playAudio(AudioAddress[result]) oldResult = result; } if (result === null && oldResult!=-1) { wx.showLoading({ title: "音频切换中…", mask: true, }); changeTO && clearTimeout(changeTO) changeTO = setTimeout(() => { wx.hideLoading({fail(){}}) }, 4000); this.stopAndDestroyAudio() } let temp = DISTRIBUTION.find((item) => { return item.position.some((sub) => sub == result); }); this.setData({ classfiy, targetObj: temp || {}, result }); }); }) .catch(() => { wx.showToast({ title: "连接失败", icon: "error", duration: 500, }); }); }); }, stopAndDestroyAudio() { console.log(innerAudioContext,'innerAudioContext'); if (!innerAudioContext) { return } innerAudioContext.pause() innerAudioContext.stop(); innerAudioContext.src = null innerAudioContext.destroy(); innerAudioContext = null oldResult = -1 }, playAudio(src) { if (!innerAudioContext) { innerAudioContext = wx.createInnerAudioContext({ useWebAudioImplement: true }) } innerAudioContext.src = src; innerAudioContext.loop = true; innerAudioContext.onCanplay(()=>{ innerAudioContext.play(); }) // innerAudioContext.play(); }, stopBeaconDiscovery(notips=null) { console.log("这是取消连接"); // 取消连接 停止播放音乐 目标对象置为{} 设置为第一次进入 this.stopAndDestroyAudio(); this.reset(); isCollect = false stopBeaconDiscovery().then(() => { !notips && wx.showToast({ title: "取消连接成功", icon: "error", duration: 1000, }); }) .catch((err) => { console.log(err); }); }, reset(){ this.setData({ status: "0", targetObj: {}, }); }, // 扫一扫 toScanCode() { this.setData({ isScanPlay: true, }); wx.scanCode({ success: res=> { this.stopBeaconDiscovery(true) this.playAudio(res["result"]) }, fail: ()=> { console.log("fail innerAudioContext", innerAudioContext); this.setData({ isScanPlay: false, }); return; }, complete() { this.setData({ isScanPlay: false, }); }, }); }, resetPage(){ this.stopBeaconDiscovery(true); }, getAudios() { http.get("/api/web/getAudioIndex").then((res) => { let { data } = res, target = {}; data.forEach((item) => { switch (item.type) { case 1: target["10001"] = `${this.data.api_base_url}/data/${item.audio}`; break; case 2: target["10002"] = `${this.data.api_base_url}/data/${item.audio}`; break; case 3: target["10003"] = `${this.data.api_base_url}/data/${item.audio}`; break; default: break; } }); this.setData({ audio_address: target, }); }); }, }, lifetimes: { attached: function () { audioInterruptionCb = ()=>{ console.log(innerAudioContext); if (innerAudioContext) { this.resetPage() } } bluetoothAdapterStateChangeCb=(res)=>{ if (!Boolean(res.available)) { this.resetPage() } } wx.onAudioInterruptionEnd(audioInterruptionCb) wx.onBluetoothAdapterStateChange(bluetoothAdapterStateChangeCb) wx.onAppShow(audioInterruptionCb) }, detached: function () { this.resetPage() wx.offAudioInterruptionEnd(audioInterruptionCb) wx.offBluetoothAdapterStateChange(bluetoothAdapterStateChangeCb) wx.offAppShow(audioInterruptionCb) } }, });