map-sense.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  1. // components/map-sense.js
  2. let innerAudioContext = wx.createInnerAudioContext({
  3. useWebAudioImplement: true,
  4. });
  5. import { promisify, BeaconUtils } from "../../utils/util";
  6. let openBluetoothAdapter = promisify(wx.openBluetoothAdapter);
  7. let startBeaconDiscovery = promisify(wx.startBeaconDiscovery);
  8. let stopBeaconDiscovery = promisify(wx.stopBeaconDiscovery);
  9. let app = getApp();
  10. import {
  11. CDN_URL,
  12. CONNECT_STATUS,
  13. STATUS_TEXT,
  14. API_BASE_URL,
  15. DISTRIBUTION,
  16. AudioAddress,
  17. } from "../../config/index";
  18. const STATUS_PIC = {
  19. 0: "default",
  20. 1: "loading",
  21. 2: "success",
  22. 3: "fail",
  23. };
  24. let audioInterruptionCb = () => {};
  25. let bluetoothAdapterStateChangeCb = () => {};
  26. let isCollect = false;
  27. let hadshowModal = false
  28. let checkCollect = null
  29. let checkTimeout = null
  30. let updateingBeacon = false
  31. let updateTO = null
  32. let changeTO = null;
  33. // 选择6档
  34. const TXPOWER = -55;
  35. var result = 0,
  36. oldResult = -1;
  37. // 距离经验值(调试所得)
  38. const N = 3.3;
  39. const R = 3.4; //设定感应范围半径
  40. const RSSIVAL = 70; //rssi信号过滤临界点
  41. let AveLength = 10;
  42. let group = [
  43. ["10001", "10002"],
  44. ["10003"],
  45. ["10004"],
  46. ["10005", "10006"],
  47. ["10007"],
  48. ["10008"],
  49. ["10009"],
  50. ["10010"],
  51. ["10011"],
  52. ["10012", "10013"],
  53. ["10014"],
  54. ["10015"],
  55. ["10016"],
  56. ["10017"],
  57. ["10018"],
  58. ["10019"],
  59. ["10020", "10021"],
  60. ["10022"],
  61. ["10023"],
  62. ["10024"],
  63. ];
  64. // 1:['10001','10002'],2:['10003'],
  65. // 3:['10004'],4:['10005'],5:['10006'],
  66. // 6:['10007'],7:['10008'],8:['10009'],
  67. // 9:['10010'],10:['10011'],11:['10012','10013'],
  68. // 12:['10014'],13:['10015'],14:['10016'],
  69. // 15:['10017'],16:['10018'],17:['10019'],
  70. // 18:['10020','10021'],19:['10022'],
  71. // 20:['10023'],21:['10024']
  72. Component({
  73. /**
  74. * 组件的属性列表
  75. */
  76. properties: {},
  77. /**
  78. * 组件的初始数据
  79. */
  80. data: {
  81. status: "0",
  82. isAndroid:app.globalData.userInfo.system.toLowerCase().indexOf("android")>-1,
  83. cdn_url: CDN_URL,
  84. connect_status: CONNECT_STATUS,
  85. status_text: STATUS_TEXT,
  86. status_pic: STATUS_PIC,
  87. targetObj: {},
  88. audio_address: {},
  89. api_base_url: API_BASE_URL,
  90. // 是否是扫码播放
  91. isScanPlay: false,
  92. cdn_url: CDN_URL,
  93. classfiy: {},
  94. },
  95. /**
  96. * 组件的方法列表
  97. */
  98. methods: {
  99. openBluetooth(cb) {
  100. wx.showLoading({
  101. title: "正在打开蓝牙…",
  102. mask: true,
  103. });
  104. openBluetoothAdapter().then(
  105. (res) => {
  106. wx.hideLoading({ fail() {} });
  107. cb(res);
  108. // if (app.globalData.userInfo.system.toLowerCase().indexOf("android")>-1) {
  109. // wx.getLocation({
  110. // type: "gcj02", //返回可以用于wx.openLocation的经纬度
  111. // success: () => {
  112. // cb(res);
  113. // },
  114. // fail: (err) => {
  115. // console.log(err);
  116. // wx.showToast({
  117. // title: "请打开手机定位服务",
  118. // icon: "error",
  119. // duration: 500,
  120. // });
  121. // },
  122. // });
  123. // } else {
  124. // cb(res);
  125. // }
  126. },
  127. () => {
  128. wx.hideLoading({ fail() {} });
  129. wx.showToast({
  130. title: "请打开蓝牙",
  131. icon: "error",
  132. duration: 2000,
  133. });
  134. }
  135. );
  136. },
  137. toHandle() {
  138. if (this.data.status == "2") {
  139. this.stopBeaconDiscovery();
  140. return;
  141. }
  142. let aveArr = [];
  143. this.openBluetooth(() => {
  144. wx.showLoading({
  145. title: "正在连接…",
  146. mask: true,
  147. });
  148. startBeaconDiscovery({
  149. uuids: ["FDA50693-A4E2-4FB1-AFCF-C6EB07647825"],
  150. })
  151. .then(() => {
  152. wx.hideLoading({ fail() {} });
  153. this.setData({
  154. status: "2",
  155. });
  156. isCollect = true;
  157. updateingBeacon = false
  158. if (this.data.isAndroid) {
  159. checkCollect && clearInterval(checkCollect)
  160. checkTimeout && clearTimeout(checkTimeout)
  161. checkTimeout = setTimeout(() => {
  162. checkCollect = setInterval(() => {
  163. if (!updateingBeacon) {
  164. if (!hadshowModal) {
  165. hadshowModal = true
  166. wx.showModal({
  167. title: "请检查手机定位服务、蓝牙和网络是否保持打开状态",
  168. showCancel: false,
  169. success:()=>{
  170. this.resetPage()
  171. checkCollect && clearInterval(checkCollect)
  172. checkTimeout && clearTimeout(checkTimeout)
  173. hadshowModal = false
  174. }
  175. });
  176. }
  177. }
  178. }, 1000)
  179. }, 2000);
  180. }
  181. wx.onBeaconUpdate((data) => {
  182. console.log(data, "onBeaconUpdate");
  183. if (!isCollect) {
  184. return;
  185. }
  186. if (this.data.isAndroid) {
  187. updateTO && clearTimeout(updateTO)
  188. updateTO = setTimeout(() => {
  189. console.log('重置updateingBeacon');
  190. updateingBeacon = false
  191. }, 3000);
  192. updateingBeacon = true
  193. }
  194. console.log("还在收集…………");
  195. // 需要收集十组数据,索引号最大的组是最旧的
  196. if (!aveArr.includes(data.beacons)) {
  197. if (aveArr.length >= AveLength) {
  198. //当超过十组时,应该将索引号大的组淘汰掉
  199. aveArr.pop();
  200. }
  201. aveArr.unshift(BeaconUtils.clone(data.beacons)); //在队列前面插入
  202. }
  203. let all = []; //获取所有data.beacons数据
  204. for (let i = 0; i < aveArr.length; i++) {
  205. let ele = aveArr[i];
  206. for (let j = 0; j < ele.length; j++) {
  207. let item = ele[j];
  208. //过滤信号弱的数据
  209. if (!(item.proximity === 0 && item.rssi === 0)) {
  210. if (Math.abs(item.rssi) < RSSIVAL) {
  211. // console.log(item.proximity,item.rssi);
  212. if (!all.includes(item)) {
  213. all.push(BeaconUtils.clone(item));
  214. }
  215. }
  216. }
  217. }
  218. }
  219. let accuracyList = {};
  220. // classfiy = {10003:[{},{},···],10002:[{},{},···],10001:[{},{},···]}
  221. let classfiy = BeaconUtils.classification(all, "major");
  222. // console.log(classfiy,'classfiy');
  223. Object.keys(classfiy).forEach((key) => {
  224. //每个major的AveLength个元素数组,元素为rssi
  225. let arr = classfiy[key].map((item) => {
  226. return item.rssi;
  227. });
  228. //每个major的rssi平均值
  229. let ave = BeaconUtils.arrayAverage(arr);
  230. //计算平均差
  231. let meanDeviation = BeaconUtils.getMeanDeviation(arr, ave);
  232. //计算各rssi的高斯模糊权重
  233. let guassionArr = []; //存放权重数组
  234. for (let i = 0; i < arr.length; ++i) {
  235. guassionArr.push(
  236. BeaconUtils.getOneGuassionArray(
  237. arr.length,
  238. i,
  239. meanDeviation
  240. )
  241. );
  242. }
  243. //计算高斯模糊后的rssi值
  244. let rssiArr = []; //模糊后的rssi数组
  245. for (let i = 0; i < arr.length; ++i) {
  246. let sum = 0;
  247. for (let j = 0; j < arr.length; ++j) {
  248. if (guassionArr[i].length == 0) {
  249. sum = arr[j];
  250. break;
  251. }
  252. sum += guassionArr[i][j] * arr[j];
  253. }
  254. rssiArr.push(sum);
  255. }
  256. //时间加权后求rssi平均值
  257. let aveOnTime = BeaconUtils.arrayAverage(
  258. rssiArr
  259. .slice(0, Math.floor(rssiArr.length / 3))
  260. .concat(rssiArr)
  261. );
  262. //测距,根据时间加权后的rssi计算距离
  263. // classfiy[key].forEach((item) => {
  264. let tmp = BeaconUtils.calculateAccuracy(TXPOWER, aveOnTime, N);
  265. if (!accuracyList[key]) {
  266. //如果还没有对应的“信标号”
  267. accuracyList[key] = [tmp];
  268. } else {
  269. accuracyList[key].push(tmp);
  270. }
  271. // });
  272. });
  273. Object.keys(accuracyList).forEach((item) => {
  274. console.log(accuracyList[item] + "---------" + item);
  275. });
  276. console.log("--------分隔--------");
  277. // 筛选器,筛选出配对的一组信标
  278. let signSelect = new Array(21).fill(0); //记录各组的信标出现数量,投票选择最多的,依次是prologue,annexHall,mainHall
  279. // const prologue = ['10001','10002'] //序厅
  280. // const annexHall = ['10003','10004','10005','10006','10007'] //附厅
  281. // const mainHall = ['10008'] //主厅
  282. Object.keys(accuracyList).forEach((key) => {
  283. let aveAccuracy = BeaconUtils.arrayAverage(accuracyList[key]);
  284. if (aveAccuracy < R) {
  285. for (let i = 0; i < group.length; i++) {
  286. if (group[i].includes(key)) {
  287. signSelect[i] += 1 / (aveAccuracy + 0.01); //取距离的反比例函数值,距离越近,值越大,+0.01是为了防止除数为0
  288. }
  289. }
  290. }
  291. });
  292. console.log(signSelect);
  293. //对小于预设半径的信号进行数量投票
  294. result = BeaconUtils.maxIndex(signSelect);
  295. console.log("result", result);
  296. if (result != null && result !== oldResult) {
  297. wx.hideLoading({ fail() {} });
  298. this.playAudio(AudioAddress[result]);
  299. oldResult = result;
  300. }
  301. if (result === null && oldResult != -1) {
  302. wx.showLoading({
  303. title: "音频切换中…",
  304. mask: true,
  305. });
  306. changeTO && clearTimeout(changeTO);
  307. changeTO = setTimeout(() => {
  308. wx.hideLoading({ fail() {} });
  309. }, 4000);
  310. this.stopAndDestroyAudio();
  311. }
  312. let temp = DISTRIBUTION.find((item) => {
  313. return item.position.some((sub) => sub == result);
  314. });
  315. this.setData({
  316. classfiy,
  317. targetObj: temp || {},
  318. result,
  319. });
  320. });
  321. })
  322. .catch((err) => {
  323. console.log(err,'1111');
  324. if (err.errCode == 11002) {
  325. wx.showToast({
  326. title: "请打开手机定位服务",
  327. icon: "error",
  328. duration: 500,
  329. });
  330. } else {
  331. wx.showToast({
  332. title: "连接失败",
  333. icon: "error",
  334. duration: 500,
  335. });
  336. }
  337. });
  338. });
  339. },
  340. stopAndDestroyAudio() {
  341. oldResult = -1;
  342. if (!innerAudioContext) {
  343. return;
  344. }
  345. innerAudioContext.pause();
  346. innerAudioContext.stop();
  347. innerAudioContext.src = null;
  348. innerAudioContext.destroy();
  349. innerAudioContext = null;
  350. },
  351. playAudio(src) {
  352. if (!innerAudioContext) {
  353. innerAudioContext = wx.createInnerAudioContext({
  354. useWebAudioImplement: true,
  355. });
  356. }
  357. innerAudioContext.src = src;
  358. innerAudioContext.loop = true;
  359. innerAudioContext.onCanplay(() => {
  360. innerAudioContext.play();
  361. });
  362. // innerAudioContext.play();
  363. },
  364. stopBeaconDiscovery(notips = null) {
  365. console.log("这是取消连接");
  366. // 取消连接 停止播放音乐 目标对象置为{} 设置为第一次进入
  367. checkCollect && clearInterval(checkCollect)
  368. checkTimeout && clearTimeout(checkTimeout)
  369. updateTO && clearTimeout(updateTO)
  370. this.stopAndDestroyAudio();
  371. this.reset();
  372. isCollect = false;
  373. stopBeaconDiscovery()
  374. .then(() => {
  375. !notips &&
  376. wx.showToast({
  377. title: "取消连接成功",
  378. icon: "error",
  379. duration: 1000,
  380. });
  381. })
  382. .catch((err) => {
  383. console.log(err);
  384. });
  385. },
  386. reset() {
  387. this.setData({
  388. status: "0",
  389. targetObj: {},
  390. });
  391. },
  392. // 扫一扫
  393. toScanCode() {
  394. this.setData({
  395. isScanPlay: true,
  396. });
  397. wx.scanCode({
  398. success: (res) => {
  399. this.stopBeaconDiscovery(true);
  400. this.playAudio(res["result"]);
  401. },
  402. fail: () => {
  403. console.log("fail innerAudioContext", innerAudioContext);
  404. this.setData({
  405. isScanPlay: false,
  406. });
  407. return;
  408. },
  409. complete() {
  410. this.setData({
  411. isScanPlay: false,
  412. });
  413. },
  414. });
  415. },
  416. resetPage() {
  417. this.stopBeaconDiscovery(true);
  418. },
  419. },
  420. lifetimes: {
  421. attached: function () {
  422. audioInterruptionCb = () => {
  423. console.log(innerAudioContext);
  424. if (innerAudioContext) {
  425. this.resetPage();
  426. }
  427. };
  428. bluetoothAdapterStateChangeCb = (res) => {
  429. if (!Boolean(res.available)) {
  430. this.resetPage();
  431. }
  432. };
  433. wx.onAudioInterruptionEnd(audioInterruptionCb);
  434. wx.onBluetoothAdapterStateChange(bluetoothAdapterStateChangeCb);
  435. wx.onAppShow(audioInterruptionCb);
  436. app.events.on('network', this, (data) =>{
  437. if (!data) {
  438. this.resetPage();
  439. }
  440. })
  441. },
  442. detached: function () {
  443. this.resetPage();
  444. wx.offAudioInterruptionEnd(audioInterruptionCb);
  445. wx.offBluetoothAdapterStateChange(bluetoothAdapterStateChangeCb);
  446. wx.offAppShow(audioInterruptionCb);
  447. app.events.remove('network',this)
  448. },
  449. },
  450. });