map-sense.js 14 KB

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