map-sense.js 14 KB

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