You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

BleUtilForQT.js 15KB

1 year ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608
  1. const sm4 = require('./sm4.js');
  2. const common_vendor = require("../../../common/vendor.js");
  3. var wx = common_vendor.index
  4. var OBUNo = '';
  5. const key = '1A70B8BE26C994DA88F4712F5E60C738';
  6. var phoneRandom = '';
  7. var encrypDeviceRandom = '';
  8. var myDeviceId;
  9. // 蓝牙连接相关参数
  10. var Service, notifyCharacteristicsId, writeCharacteristicsId, readCharacteristicsId;
  11. //回调函数
  12. let CallBackInfo = null;
  13. let CallBackToQT = null;
  14. let CallBackInfoForconnected = null;
  15. var framNo = 0;
  16. var outDevices = new Array();
  17. //当前是第几条命令
  18. var currentCmdId = 0;
  19. //需要执行的命令总数
  20. var cmdTotal;
  21. //返回的命令数组;
  22. var outCmdArr = new Array();
  23. //输入的命令数组
  24. var inCmdArr = new Array();
  25. //命令类型
  26. var globalCmdType = '01';
  27. //复位状态:0未复位 1已复位
  28. var resetState = 0;
  29. var _res = {};
  30. //打开蓝牙适配器
  31. function openBle(filter, timeout, callback) {
  32. outDevices.splice(0, outDevices.length);
  33. uni.openBluetoothAdapter({
  34. success: function (res) {
  35. uni.startBluetoothDevicesDiscovery({
  36. services: ['FEE7'],
  37. success: function () {
  38. setTimeout(function () {
  39. uni.stopBluetoothDevicesDiscovery();
  40. uni.getBluetoothDevices({
  41. success: function (res) {
  42. res.devices.forEach((device) => {
  43. // 根据filter进行过滤
  44. if (device.name.indexOf(filter) == 0) {
  45. outDevices.push(device);
  46. }
  47. })
  48. callback(0, outDevices, "扫描成功");
  49. },
  50. fail: function (error) {
  51. callback(1, null, "扫描失败:" + error.errMsg);
  52. }
  53. })
  54. }, timeout)
  55. },
  56. fail: function (error) {
  57. callback(1, null, "扫描失败:" + error.errMsg);
  58. }
  59. })
  60. },
  61. fail: function (error) {
  62. callback(1, null, "扫描失败:" + error.errMsg);
  63. }
  64. })
  65. }
  66. function connectDevice(device, callback, callback2) {
  67. CallBackInfo = callback;
  68. CallBackInfoForconnected = callback2;
  69. myDeviceId = device.deviceId;
  70. uni.createBLEConnection({
  71. deviceId: myDeviceId,
  72. timeout: 3000,
  73. success: function (res) {
  74. uni.getBLEDeviceServices({
  75. deviceId: myDeviceId,
  76. success: function (res) {
  77. for (var a = 0; a < res.services.length; a++) {
  78. if ('0000FF00' == res.services[a].uuid.substr(0, 8)) {
  79. Service = res.services[a].uuid;
  80. }
  81. }
  82. uni.getBLEDeviceCharacteristics({
  83. deviceId: myDeviceId,
  84. serviceId: Service,
  85. success: function (res) {
  86. for (var i = 0; i < res.characteristics.length; i++) {
  87. let item = res.characteristics[i]
  88. if (item.properties.notify || item.properties.indicate) {
  89. if ('0000FF02' == item.uuid.substr(0, 8)) {
  90. notifyCharacteristicsId = item.uuid;
  91. }
  92. }
  93. if (item.properties.write) {
  94. if ('0000FF01' == item.uuid.substr(0, 8)) {
  95. writeCharacteristicsId = item.uuid;
  96. }
  97. } else if (item.properties.read) {
  98. if ('0000FF03' == item.uuid.substr(0, 8)) {
  99. readCharacteristicsId = item.uuid;
  100. }
  101. }
  102. }
  103. uni.notifyBLECharacteristicValueChange({
  104. state: true,
  105. deviceId: myDeviceId,
  106. serviceId: Service,
  107. characteristicId: notifyCharacteristicsId,
  108. success: function (res) {
  109. sendData(ass2001(), function (code) {
  110. if (code != 0) {
  111. _res.code = 1;
  112. _res.data = null;
  113. _res.msg = "连接失败:认证失败"
  114. CallBackInfo(_res);
  115. }
  116. })
  117. },
  118. fail: function (error) {
  119. _res.code = 1;
  120. _res.data = null;
  121. _res.msg = "连接失败:" + error.errMsg
  122. CallBackInfo(_res);
  123. }
  124. })
  125. },
  126. fail: function (error) {
  127. _res.code = 1;
  128. _res.data = null;
  129. _res.msg = "连接失败:" + error.errMsg
  130. CallBackInfo(_res);
  131. },
  132. })
  133. },
  134. fail: function (error) {
  135. _res.code = 1;
  136. _res.data = null;
  137. _res.msg = "连接失败:" + error.errMsg
  138. CallBackInfo(_res);
  139. }
  140. })
  141. },
  142. fail: function (error) {
  143. _res.code = 1;
  144. _res.data = null;
  145. _res.msg = "连接失败:" + error.errMsg
  146. CallBackInfo(_res);
  147. }
  148. })
  149. // 必须在这里的回调才能获取
  150. uni.onBLECharacteristicValueChange(function (characteristic) {
  151. var hex = Array.prototype.map.call(new Uint8Array(characteristic.value), x => ('00' + x.toString(16)).slice(-2)).join('');
  152. console.log('接收信息:' + hex);
  153. checkAndDistribution(hex);
  154. })
  155. uni.onBLEConnectionStateChange(function (res) {
  156. console.log('监听连接状态变化:');
  157. if (res.connected) {
  158. CallBackInfoForconnected(0, null, "监听连接成功");
  159. } else {
  160. resetState = 0;
  161. CallBackInfoForconnected(1, null, "监听到断开连接");
  162. }
  163. })
  164. }
  165. //断开与蓝牙外设的连接
  166. function disconnectDevice(callback) {
  167. resetState = 0;
  168. uni.closeBLEConnection({
  169. deviceId: myDeviceId,
  170. success: function (res) {
  171. uni.closeBluetoothAdapter({
  172. success: function (res) {
  173. var _res = {};
  174. _res.code = 0;
  175. _res.data = null;
  176. _res.msg = "断开成功";
  177. callback(_res);
  178. },
  179. fail: function (res) {
  180. var _res = {};
  181. _res.code = 1;
  182. _res.data = null;
  183. _res.msg = "断开失败:" + res.errMsg;
  184. callback(_res);
  185. }
  186. })
  187. },
  188. fail: function (res) {
  189. var _res = {};
  190. _res.code = 1;
  191. _res.data = null;
  192. _res.msg = "断开失败:" + res.errMsg;
  193. callback(_res);
  194. }
  195. })
  196. }
  197. ///////////////////////下面是util/////////////////////////
  198. /**
  199. * 发送数据
  200. */
  201. function sendData(cmd, callback) {
  202. console.log('发送信息:' + cmd);
  203. uni.writeBLECharacteristicValue({
  204. deviceId: myDeviceId,
  205. serviceId: Service,
  206. characteristicId: writeCharacteristicsId,
  207. value: strToBuff(cmd),
  208. success: function (res) {
  209. console.log('数据发送成功');
  210. callback(0);
  211. },
  212. fail: function (res) {
  213. console.log('数据发送失败');
  214. console.log(res);
  215. callback(1);
  216. }
  217. })
  218. }
  219. function hex2int(hex) {
  220. var len = hex.length,
  221. a = new Array(len),
  222. code;
  223. for (var i = 0; i < len; i++) {
  224. code = hex.charCodeAt(i);
  225. if (48 <= code && code < 58) {
  226. code -= 48;
  227. } else {
  228. code = (code & 0xdf) - 65 + 10;
  229. }
  230. a[i] = code;
  231. }
  232. return a.reduce(function (acc, c) {
  233. acc = 16 * acc + c;
  234. return acc;
  235. }, 0);
  236. }
  237. //命令转buffer
  238. function strToBuff(cmd) {
  239. let buffer = new ArrayBuffer(cmd.length / 2);
  240. let dataView = new Uint8Array(buffer);
  241. for (var a = 0; a < cmd.length / 2; a++) {
  242. dataView[a] = '0x' + cmd.substring(2 * a, 2 * a + 2);
  243. }
  244. return buffer
  245. }
  246. function ass2001() {
  247. OBUNo = '1122334455667788';
  248. phoneRandom = getRandom();
  249. console.log("phoneRandom:" + phoneRandom);
  250. var cmd = OBUNo + phoneRandom;
  251. return assbCmd("2001", cmd);
  252. }
  253. function ass2002() {
  254. var cmd = OBUNo + encrypDeviceRandom;
  255. return assbCmd("2002", cmd);
  256. }
  257. function ass2005() {
  258. var cmd = OBUNo;
  259. return assbCmd("2005", cmd);
  260. }
  261. function ass2006(option) {
  262. var cmd = OBUNo + option;
  263. return assbCmd("2006", cmd);
  264. }
  265. function channel(channelType, cmd, callback) {
  266. CallBackInfo = callback;
  267. sendData(ass200A(channelType, cmd), function (code) {
  268. if (code == 0) {} else {
  269. callback(1);
  270. }
  271. })
  272. }
  273. function transCmd(cmd, cmdtype, callback) {
  274. if (cmdtype == "10") {
  275. cmdtype = "01"
  276. }
  277. if (cmdtype == "20") {
  278. cmdtype = "02"
  279. }
  280. CallBackToQT = callback;
  281. currentCmdId = 0;
  282. cmdTotal = cmd.length;
  283. inCmdArr = cmd;
  284. outCmdArr.splice(0, outCmdArr.length);
  285. console.log("resetState" + resetState);
  286. if (resetState == 0) {
  287. globalCmdType = "03";
  288. channel("03", "", function (code, data, msg) {
  289. if (code == 0) {
  290. resetState = 1;
  291. globalCmdType = cmdtype;
  292. channel(cmdtype, cmd[0], function (code, data, msg) {
  293. });
  294. }
  295. });
  296. } else {
  297. globalCmdType = cmdtype;
  298. channel(cmdtype, cmd[0], function (code, data, msg) {
  299. });
  300. }
  301. }
  302. function channel2005(callback) {
  303. CallBackInfo = callback;
  304. sendData(ass2005(), function (code) {
  305. if (code == 0) {
  306. console.log(code);
  307. } else {
  308. console.log(code);
  309. callback(1);
  310. }
  311. })
  312. }
  313. function channel2006(cmd, callback) {
  314. CallBackInfo = callback;
  315. sendData(ass2006(cmd), function (code) {
  316. if (code == 0) {
  317. console.log(code);
  318. } else {
  319. console.log(code);
  320. callback(1);
  321. }
  322. })
  323. }
  324. function channelTLV(channelType, cmds, callback) {
  325. sendData(ass200A(channelType, cmd), function (code) {
  326. if (code == 0) {
  327. console.log(code);
  328. callback(0);
  329. } else {
  330. console.log(code);
  331. callback(1);
  332. }
  333. })
  334. }
  335. function ass200A(channelType, cmd) {
  336. var cmd = channelType + tenToHex(cmd.length / 2, 2) + cmd;
  337. return assbCmd("200A", cmd);
  338. }
  339. //获取16字节随机数
  340. function getRandom() {
  341. var result = '';
  342. for (var i = 0; i < 16; i++) {
  343. var tmp = Math.ceil((Math.random() * 255));
  344. tmp = (tmp.toString(16));
  345. if (tmp.length == 1) {
  346. tmp = "0" + tmp;
  347. }
  348. result += tmp;
  349. }
  350. return result.toUpperCase();
  351. }
  352. //组装命令
  353. function assbCmd(msgType, cmd) {
  354. var CmdId = 'AA55AA55';
  355. var nLength = tenToHex(8 + cmd.length / 2, 4);
  356. var data = CmdId + msgType + nLength + getFramNo() + '01' + cmd + getDate();
  357. var check = crc16(data, true);
  358. return data + check;
  359. }
  360. function getDate() {
  361. var date = new Date();
  362. return date.getFullYear().toString().substring(2, 4) + pad2(date.getMonth() + 1) + pad2(date.getDate()) + pad2(date.getHours()) + pad2(date.getMinutes()) + pad2(date.getSeconds());
  363. }
  364. function pad2(n) {
  365. return n < 10 ? '0' + n : n
  366. }
  367. function getFramNo() {
  368. if (framNo == 2147483647) {
  369. framNo = 1;
  370. } else {
  371. framNo++;
  372. }
  373. return tenToHex(framNo, 8);
  374. }
  375. /**
  376. * 10to16
  377. * num 数字
  378. * len 想要的16进制长度
  379. */
  380. function tenToHex(num, len) {
  381. var hex = num.toString(16);
  382. var zero = '';
  383. for (var a = 0; a < len - hex.length; a++) {
  384. zero += '0';
  385. }
  386. return zero + hex
  387. }
  388. function strToByte(str) {
  389. var arr = [];
  390. for (var i = 0; i < str.length; i += 2) {
  391. arr.push(parseInt('0x' + str.substring(i, i + 2)));
  392. }
  393. return arr;
  394. };
  395. function bytesToHexString(bytes) {
  396. var hexStr = '';
  397. for (var i = 0; i < bytes.length; i++) {
  398. var hex = bytes[i].toString(16);
  399. if (hex.length == 1) {
  400. hex = "0" + hex;
  401. }
  402. hexStr += hex;
  403. }
  404. return hexStr.toUpperCase();
  405. }
  406. //crc16校验,传入要校验的字符串,返回计算的crc16结果
  407. function crc16(str) {
  408. var data = strToByte(str);
  409. var crc = 0x0000;
  410. for (var i = 0; i < data.length; i++) {
  411. crc = crc ^ (data[i] << 8);
  412. for (var j = 0; j < 8; j++) {
  413. if ((crc & 0x8000) != 0)
  414. crc = (crc << 1) ^ 0x8005;
  415. else
  416. crc = (crc << 1);
  417. }
  418. }
  419. crc = ((Math.pow(2, 32) + crc).toString(16));
  420. crc = crc.substring(crc.length - 4);
  421. return crc.toUpperCase();
  422. }
  423. function checkAndDistribution(data) {
  424. var cmd = data.substring(0, data.length - 4);
  425. var crc = data.substring(data.length - 4);
  426. if (crc.toUpperCase() == crc16(cmd)) {
  427. var head = data.substring(0, 8);
  428. if (head == "aa55aa55") {
  429. var type = data.substring(8, 12);
  430. switch (type) {
  431. case "3001":
  432. analysis3001(data);
  433. break;
  434. case "3002":
  435. analysis3002(data);
  436. break;
  437. case "3005":
  438. analysis3005(data);
  439. break;
  440. case "3006":
  441. analysis3006(data);
  442. break;
  443. case "300a":
  444. analysis300A(data);
  445. break;
  446. default:
  447. }
  448. } else {
  449. console.log("信息有误");
  450. }
  451. } else {
  452. console.log("信息CRC校验不通过");
  453. }
  454. }
  455. function analysis3001(data) {
  456. var length = hex2int(data.substring(12, 16));
  457. var cmd = data.substring(26, 26 + length * 2);
  458. var cmd1 = cmd.substring(0, 16);
  459. var cmd2 = cmd.substring(16, 48);
  460. var cmd3 = cmd.substring(48, 80);
  461. console.log("cmd2" + cmd2);
  462. OBUNo = cmd1;
  463. if (authCheck(phoneRandom, cmd2)) {
  464. // 认证通过
  465. encrypDeviceRandom = encryption(cmd3);
  466. sendData(ass2002(), function (code) {
  467. if (code != 0) {
  468. _res.code = 1;
  469. _res.data = null;
  470. _res.msg = "连接失败:2001认证失败"
  471. CallBackInfo(_res);
  472. }
  473. })
  474. } else {
  475. // 认证不通过
  476. _res.code = 1;
  477. _res.data = null;
  478. _res.msg = "连接失败:3001认证失败"
  479. CallBackInfo(_res);
  480. }
  481. }
  482. function analysis3002(data) {
  483. var length = hex2int(data.substring(12, 16));
  484. var cmd = data.substring(26, 26 + length * 2);
  485. var cmd1 = cmd.substring(0, 16);
  486. var cmd2 = cmd.substring(16, 18);
  487. if (cmd2 == "00") {
  488. _res.code = 0;
  489. _res.data = null;
  490. _res.msg = "连接成功"
  491. CallBackInfo(_res);
  492. } else {
  493. _res.code = 1;
  494. _res.data = null;
  495. _res.msg = "连接失败:认证失败"
  496. CallBackInfo(_res);
  497. }
  498. }
  499. function authCheck(random, data) {
  500. console.log("random" + random);
  501. console.log("data" + data);
  502. let decryptData = sm4.decrypt(strToByte(data), strToByte(key), {
  503. padding: 'none',
  504. output: 'array'
  505. }) // 解密,不使用 padding
  506. console.log("bytesToHexString(decryptData)" + bytesToHexString(decryptData));
  507. return bytesToHexString(decryptData) == random;
  508. }
  509. function encryption(data) {
  510. // 可以为 16 进制串或字节数组,要求为 128 比特
  511. let encryptData = sm4.encrypt(strToByte(data), strToByte(key), {
  512. padding: 'none'
  513. }) // 加密,默认输出 16 进制字符串
  514. return encryptData;
  515. }
  516. function analysis300A(data) {
  517. var length = hex2int(data.substring(12, 16));
  518. var cmd = data.substring(26, 26 + length * 2);
  519. var cmd1 = cmd.substring(0, 2);
  520. var cmd2 = cmd.substring(2, 4);
  521. var cmd3 = cmd.substring(4, 6);
  522. var cmd4 = cmd.substring(6, 6 + hex2int(cmd3) * 2);
  523. if (globalCmdType == '03') {
  524. CallBackInfo(0, cmd4, cmd4);
  525. } else {
  526. if (cmd4.substr(0, 2).toUpperCase() == "6C") {
  527. var cmd = inCmdArr[currentCmdId];
  528. channel(globalCmdType, cmd.substr(0, cmd.length - 2) + cmd4.substr(cmd4.length - 2), function (code, data, msg) {
  529. });
  530. } else {
  531. outCmdArr.push(cmd4);
  532. currentCmdId++;
  533. if (currentCmdId == cmdTotal) {
  534. var _res = {};
  535. _res.code = 0;
  536. _res.data = outCmdArr;
  537. _res.msg = "成功";
  538. CallBackToQT(_res);
  539. } else {
  540. channel(globalCmdType, inCmdArr[currentCmdId], function (code, data, msg) {});
  541. }
  542. }
  543. }
  544. }
  545. function analysis3005(data) {
  546. var length = hex2int(data.substring(12, 16));
  547. var cmd = data.substring(26, 26 + length * 2);
  548. CallBackInfo(0, cmd, cmd);
  549. }
  550. function analysis3006(data) {
  551. var length = hex2int(data.substring(12, 16));
  552. var cmd = data.substring(26, 26 + length * 2);
  553. CallBackInfo(0, cmd, cmd);
  554. }
  555. ///////////
  556. /**
  557. * 将方法暴露出来
  558. */
  559. module.exports = {
  560. openBle: openBle,
  561. disconnectDevice: disconnectDevice,
  562. connectDevice: connectDevice,
  563. transCmd: transCmd
  564. }