|
@@ -0,0 +1,489 @@
|
|
|
+// 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)
|
|
|
+ }
|
|
|
+ },
|
|
|
+});
|