ecBLEApp.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952
  1. const logEnable = false
  2. let isAndroid = false
  3. let ecBluetoothAdapterStateChangeCallback = () => {}
  4. let ecBluetoothDeviceFoundCallback = () => {}
  5. let ecBLEConnectionStateChangeCallback = () => {}
  6. let ecBLECharacteristicValueChangeCallback = () => {}
  7. let ecDeviceId = ''
  8. let ecGattServerUUID = ''
  9. const ecGattServerUUIDOption1 = '0000FFF0-0000-1000-8000-00805F9B34FB'
  10. const ecGattServerUUIDOption2 = 'FFF0'
  11. const yiyuanUUIDOption1 = '0000F536-0000-1000-8000-00805F9B34FB';
  12. const yiyuanUUIDOption2 = 'F536';
  13. let ecGattCharacteristicWriteUUID = ''
  14. const ecGattCharacteristicWriteUUIDOption1 = '0000FFF2-0000-1000-8000-00805F9B34FB'
  15. const ecGattCharacteristicWriteUUIDOption2 = 'FFF2'
  16. const yiyuancharacterID1 = '0000FF15-0000-1000-8000-00805F9B34FB'
  17. const yiyuancharacterID2 = 'FF15'
  18. import i18 from '@/utils/i18.js'
  19. const log = data => {
  20. if (logEnable) {
  21. console.log('[eciot]:' + JSON.stringify(data))
  22. }
  23. }
  24. const onBluetoothAdapterStateChange = cb => {
  25. ecBluetoothAdapterStateChangeCallback = cb
  26. }
  27. const _openBluetoothAdapter = () => {
  28. return new Promise(function(resolve, reject) {
  29. uni.openBluetoothAdapter({
  30. success(res) {
  31. log(res)
  32. resolve({
  33. ok: true,
  34. errCode: 0,
  35. errMsg: ''
  36. })
  37. },
  38. fail(res) {
  39. log(res)
  40. // {"errMsg":"openBluetoothAdapter:fail not available","code":10001}
  41. resolve({
  42. ok: false,
  43. errCode: res.code,
  44. errMsg: res.errMsg,
  45. })
  46. },
  47. })
  48. })
  49. }
  50. uni.onBluetoothAdapterStateChange(res => {
  51. log(res)
  52. // {"discovering":true,"available":true}
  53. if (!res.available) {
  54. ecBluetoothAdapterStateChangeCallback({
  55. ok: false,
  56. errCode: 30005,
  57. errMsg: i18('蓝牙适配器不可用'),
  58. })
  59. }
  60. })
  61. const openBluetoothAdapter = async () => {
  62. await _openBluetoothAdapter()
  63. const systemInfo = uni.getSystemInfoSync()
  64. log(systemInfo)
  65. if (systemInfo.platform.toLowerCase() === 'android') {
  66. isAndroid = true
  67. }
  68. const systemSetting = uni.getSystemSetting()
  69. log(systemSetting)
  70. const appAuthorizeSetting = uni.getAppAuthorizeSetting()
  71. log(appAuthorizeSetting)
  72. if (!systemSetting.bluetoothEnabled) {
  73. ecBluetoothAdapterStateChangeCallback({
  74. ok: false,
  75. errCode: 30001,
  76. errMsg: i18('请打开系统蓝牙开关'),
  77. })
  78. return
  79. }
  80. if (isAndroid && !systemSetting.locationEnabled) {
  81. ecBluetoothAdapterStateChangeCallback({
  82. ok: false,
  83. errCode: 30002,
  84. errMsg: i18('请打开系统定位开关'),
  85. })
  86. return
  87. }
  88. if (isAndroid && (appAuthorizeSetting.locationAuthorized!=='authorized')) {
  89. ecBluetoothAdapterStateChangeCallback({
  90. ok: false,
  91. errCode: 30003,
  92. errMsg: i18('请打开应用定位权限,允许应用使用您的位置信息'),
  93. })
  94. return
  95. }
  96. const openRes = await _openBluetoothAdapter()
  97. ecBluetoothAdapterStateChangeCallback(openRes)
  98. }
  99. uni.onBluetoothDeviceFound(res => {
  100. // log(res)
  101. const device = res.devices[0]
  102. const name = device.name ? device.name : device.localName
  103. if (!name) {
  104. return
  105. }
  106. let id = device.deviceId
  107. let rssi = device.RSSI
  108. ecBluetoothDeviceFoundCallback({
  109. id,
  110. name,
  111. rssi
  112. })
  113. })
  114. const onBluetoothDeviceFound = cb => {
  115. ecBluetoothDeviceFoundCallback = cb
  116. }
  117. const startBluetoothDevicesDiscovery = () => {
  118. uni.startBluetoothDevicesDiscovery({
  119. //services: [ecServerId],
  120. allowDuplicatesKey: true,
  121. // powerLevel: 'high',
  122. complete(res) {
  123. log(res)
  124. },
  125. })
  126. }
  127. const stopBluetoothDevicesDiscovery = () => {
  128. uni.stopBluetoothDevicesDiscovery({
  129. complete(res) {
  130. log(res)
  131. },
  132. })
  133. }
  134. const onBLEConnectionStateChange = cb => {
  135. ecBLEConnectionStateChangeCallback = cb
  136. }
  137. const _createBLEConnection = () => {
  138. return new Promise(function(resolve, reject) {
  139. uni.createBLEConnection({
  140. deviceId: ecDeviceId,
  141. success(res) {
  142. log(res)
  143. resolve({
  144. ok: true,
  145. errCode: 0,
  146. errMsg: ''
  147. })
  148. },
  149. fail(res) {
  150. log(res)
  151. resolve({
  152. ok: false,
  153. errCode: res.code,
  154. errMsg: res.errMsg,
  155. })
  156. },
  157. })
  158. })
  159. }
  160. const getBLEDeviceServices = () => {
  161. return new Promise(function(resolve, reject) {
  162. setTimeout(()=>{
  163. uni.getBLEDeviceServices({
  164. deviceId: ecDeviceId,
  165. success(res) {
  166. log(res)
  167. resolve({
  168. ok: true,
  169. errCode: 0,
  170. errMsg: '',
  171. services: res.services,
  172. })
  173. },
  174. fail(res) {
  175. log(res)
  176. resolve({
  177. ok: false,
  178. errCode: res.code,
  179. errMsg: res.errMsg
  180. })
  181. },
  182. })
  183. },1200)//之前是800ms,要休眠到1200ms
  184. })
  185. }
  186. const getBLEDeviceCharacteristics = serviceId => {
  187. return new Promise(function(resolve, reject) {
  188. uni.getBLEDeviceCharacteristics({
  189. deviceId: ecDeviceId,
  190. serviceId,
  191. success(res) {
  192. log(res)
  193. resolve({
  194. ok: true,
  195. errCode: 0,
  196. errMsg: '',
  197. characteristics: res.characteristics,
  198. })
  199. },
  200. fail(res) {
  201. log(res)
  202. resolve({
  203. ok: false,
  204. errCode: res.code,
  205. errMsg: res.errMsg
  206. })
  207. },
  208. })
  209. })
  210. }
  211. const notifyBLECharacteristicValueChange = (serviceId, characteristicId) => {
  212. return new Promise(function(resolve, reject) {
  213. uni.notifyBLECharacteristicValueChange({
  214. state: true,
  215. deviceId: ecDeviceId,
  216. serviceId,
  217. characteristicId,
  218. success(res) {
  219. log(res)
  220. resolve({
  221. ok: true,
  222. errCode: 0,
  223. errMsg: ''
  224. })
  225. },
  226. fail(res) {
  227. log(res)
  228. resolve({
  229. ok: false,
  230. errCode: res.code,
  231. errMsg: res.errMsg
  232. })
  233. },
  234. })
  235. })
  236. }
  237. const setBLEMTU = mtu => {
  238. return new Promise(function(resolve, reject) {
  239. setTimeout(()=>{
  240. uni.setBLEMTU({
  241. deviceId: ecDeviceId,
  242. mtu,
  243. success(res) {
  244. log(res)
  245. resolve({
  246. ok: true,
  247. errCode: 0,
  248. errMsg: ''
  249. })
  250. },
  251. fail(res) {
  252. log(res)
  253. resolve({
  254. ok: false,
  255. errCode: res.code,
  256. errMsg: res.errMsg
  257. })
  258. },
  259. })
  260. },500)
  261. })
  262. }
  263. uni.onBLEConnectionStateChange(async res => {
  264. log(res)
  265. if (res.connected) {
  266. console.log("=== 设备连接成功 ===")
  267. const servicesResult = await getBLEDeviceServices()
  268. console.log("服务发现结果:", servicesResult)
  269. if (!servicesResult.ok) {
  270. ecBLEConnectionStateChangeCallback(servicesResult)
  271. closeBLEConnection()
  272. return
  273. }
  274. console.log("设备提供的所有服务:", servicesResult.services)
  275. // 优先查找的服务和特征值UUID(与微信端保持一致)
  276. const targetServiceUUIDs = [
  277. '0000FFE0-0000-1000-8000-00805F9B34FB'.toUpperCase(),
  278. 'FFE0'.toUpperCase(),
  279. // 保留原来的选项
  280. '0000FFF0-0000-1000-8000-00805F9B34FB'.toUpperCase(),
  281. 'FFF0'.toUpperCase(),
  282. '0000F536-0000-1000-8000-00805F9B34FB'.toUpperCase(),
  283. 'F536'.toUpperCase()
  284. ];
  285. const targetCharacteristicUUIDs = [
  286. '0000FFE1-0000-1000-8000-00805F9B34FB'.toUpperCase(),
  287. 'FFE1'.toUpperCase(),
  288. // 保留原来的选项
  289. '0000FFF2-0000-1000-8000-00805F9B34FB'.toUpperCase(),
  290. 'FFF2'.toUpperCase(),
  291. '0000FF15-0000-1000-8000-00805F9B34FB'.toUpperCase(),
  292. 'FF15'.toUpperCase()
  293. ];
  294. let serviceFound = false;
  295. let notifyEnabled = false; // 新增:跟踪是否成功启用通知
  296. let writeCharacteristicFound = false; // 跟踪是否找到写入特征值
  297. // 首先按优先级查找
  298. serviceLoop: for (const service of servicesResult.services) {
  299. const serviceUUID = service.uuid.toUpperCase();
  300. console.log("正在检查服务:", serviceUUID);
  301. // 检查是否是目标服务
  302. if (targetServiceUUIDs.includes(serviceUUID)) {
  303. console.log("✅ 找到目标服务:", serviceUUID);
  304. // 获取该服务下的所有特征值
  305. const characteristicsResult = await getBLEDeviceCharacteristics(service.uuid)
  306. console.log(`服务 ${service.uuid} 的特征值获取结果:`, characteristicsResult);
  307. if (!characteristicsResult.ok) {
  308. console.log(`获取服务 ${service.uuid} 的特征值失败`)
  309. continue
  310. }
  311. console.log(`服务 ${service.uuid} 下的特征值:`, characteristicsResult.characteristics)
  312. // 分别查找通知特征值和写入特征值
  313. let notifyCharacteristic = null;
  314. let writeCharacteristic = null;
  315. // 遍历所有特征值
  316. for (const characteristic of characteristicsResult.characteristics) {
  317. const charUUID = characteristic.uuid.toUpperCase();
  318. console.log("检查特征值:", charUUID, "属性:", characteristic.properties);
  319. // 查找通知特征值
  320. if (characteristic.properties && (characteristic.properties.notify || characteristic.properties.indicate)) {
  321. notifyCharacteristic = characteristic;
  322. console.log("✅ 找到通知特征值:", charUUID);
  323. // 尝试启用通知
  324. const notifyResult = await notifyBLECharacteristicValueChange(
  325. service.uuid,
  326. characteristic.uuid
  327. )
  328. if (notifyResult.ok) {
  329. console.log("✅ 通知已启用:", characteristic.uuid);
  330. notifyEnabled = true;
  331. } else {
  332. console.log("⚠️ 启用通知失败:", characteristic.uuid, notifyResult);
  333. }
  334. }
  335. // 查找写入特征值
  336. if (targetCharacteristicUUIDs.includes(charUUID) &&
  337. characteristic.properties &&
  338. (characteristic.properties.write || characteristic.properties.writeNoResponse)) {
  339. writeCharacteristic = characteristic;
  340. console.log("✅ 找到写入特征值:", charUUID);
  341. }
  342. }
  343. // 如果找到了写入特征值,设置全局变量
  344. if (writeCharacteristic) {
  345. ecGattServerUUID = service.uuid;
  346. ecGattCharacteristicWriteUUID = writeCharacteristic.uuid;
  347. console.log("服务 UUID:", ecGattServerUUID);
  348. console.log("写入特征值 UUID:", ecGattCharacteristicWriteUUID);
  349. serviceFound = true;
  350. writeCharacteristicFound = true;
  351. break serviceLoop;
  352. }
  353. }
  354. }
  355. // 如果没找到目标服务/特征值,则使用动态查找
  356. if (!serviceFound) {
  357. console.log("未找到预定义的服务/特征值,使用动态查找...")
  358. serviceLoop2: for (const service of servicesResult.services) {
  359. console.log("正在检查服务:", service.uuid)
  360. // 获取该服务下的所有特征值
  361. const characteristicsResult = await getBLEDeviceCharacteristics(service.uuid)
  362. console.log(`服务 ${service.uuid} 的特征值获取结果:`, characteristicsResult);
  363. if (!characteristicsResult.ok) {
  364. console.log(`获取服务 ${service.uuid} 的特征值失败`)
  365. continue
  366. }
  367. console.log(`服务 ${service.uuid} 下的特征值:`, characteristicsResult.characteristics)
  368. // 分别查找通知特征值和写入特征值
  369. let notifyCharacteristic = null;
  370. // 遍历所有特征值
  371. for (const characteristic of characteristicsResult.characteristics) {
  372. console.log("动态查找 - 检查特征值:", characteristic.uuid, "属性:", characteristic.properties);
  373. // 查找通知特征值
  374. if (characteristic.properties && (characteristic.properties.notify || characteristic.properties.indicate)) {
  375. notifyCharacteristic = characteristic;
  376. console.log("✅ 找到通知特征值:", characteristic.uuid);
  377. // 尝试启用通知
  378. const notifyResult = await notifyBLECharacteristicValueChange(
  379. service.uuid,
  380. characteristic.uuid
  381. )
  382. if (notifyResult.ok) {
  383. console.log("✅ 通知已启用:", characteristic.uuid);
  384. notifyEnabled = true;
  385. } else {
  386. console.log("⚠️ 启用通知失败:", characteristic.uuid, notifyResult);
  387. }
  388. }
  389. // 查找写入特征值
  390. if (characteristic.properties &&
  391. (characteristic.properties.write || characteristic.properties.writeNoResponse)) {
  392. // 找到支持写入的特征值,设置全局变量
  393. ecGattServerUUID = service.uuid
  394. ecGattCharacteristicWriteUUID = characteristic.uuid
  395. console.log("✅ 找到可写特征值")
  396. console.log("服务 UUID:", ecGattServerUUID)
  397. console.log("特征值 UUID:", ecGattCharacteristicWriteUUID)
  398. serviceFound = true
  399. writeCharacteristicFound = true;
  400. break serviceLoop2; // 使用标签跳出外层循环
  401. }
  402. }
  403. }
  404. }
  405. // 如果没有找到可写特征值
  406. if (!writeCharacteristicFound) {
  407. console.error("❌ 未找到支持写入的特征值")
  408. console.log("所有服务和特征值信息:", servicesResult.services);
  409. ecBLEConnectionStateChangeCallback({
  410. ok: false,
  411. errCode: 30000,
  412. errMsg: '未找到支持写入的特征值',
  413. })
  414. closeBLEConnection()
  415. return
  416. }
  417. // 检查是否成功启用了通知
  418. if (!notifyEnabled) {
  419. console.warn("⚠️ 警告:未成功启用任何特征值的通知功能");
  420. console.log("⚠️ 设备可能使用轮询方式而非通知方式通信");
  421. // 继续连接,因为有些设备可能不使用通知机制
  422. } else {
  423. console.log("✅ 通知功能已成功启用");
  424. }
  425. // 设置MTU(仅安卓)
  426. if (isAndroid) {
  427. try {
  428. await setBLEMTU(247)
  429. console.log("MTU 设置成功")
  430. } catch (error) {
  431. console.log("MTU 设置失败:", error)
  432. }
  433. }
  434. // 连接成功回调
  435. console.log("=== 准备发送连接成功回调 ===");
  436. console.log("服务UUID:", ecGattServerUUID);
  437. console.log("写入特征值UUID:", ecGattCharacteristicWriteUUID);
  438. ecBLEConnectionStateChangeCallback({
  439. ok: true,
  440. errCode: 0,
  441. errMsg: '',
  442. })
  443. } else {
  444. console.log("=== 设备断开连接 ===");
  445. ecBLEConnectionStateChangeCallback({
  446. ok: false,
  447. errCode: 0,
  448. errMsg: 'disconnect',
  449. })
  450. }
  451. })
  452. const createBLEConnection = async id => {
  453. ecDeviceId = id
  454. const res = await _createBLEConnection()
  455. if (!res.ok) {
  456. ecBLEConnectionStateChangeCallback(res)
  457. }
  458. }
  459. const closeBLEConnection = () => {
  460. uni.closeBLEConnection({
  461. deviceId: ecDeviceId,
  462. complete(res) {
  463. log(res);
  464. uni.showToast({
  465. title: '操作执行成功!',
  466. icon: 'success',
  467. duration: 2000
  468. });
  469. },
  470. })
  471. }
  472. uni.onBLECharacteristicValueChange(res => {
  473. log(res)
  474. let x = new Uint8Array(res.value)
  475. log(x)
  476. let str = utf8BytesToStr(x)
  477. let strHex = ''
  478. for (let i = 0; i < x.length; i++) {
  479. strHex = strHex + x[i].toString(16).padStart(2, '0').toUpperCase()
  480. }
  481. log(str)
  482. log(strHex)
  483. ecBLECharacteristicValueChangeCallback(str, strHex)
  484. })
  485. const onBLECharacteristicValueChange = cb => {
  486. ecBLECharacteristicValueChangeCallback = cb
  487. }
  488. const _writeBLECharacteristicValue = buffer => {
  489. return new Promise(function(resolve, reject) {
  490. uni.writeBLECharacteristicValue({
  491. deviceId: ecDeviceId,
  492. serviceId: ecGattServerUUID,
  493. characteristicId: ecGattCharacteristicWriteUUID,
  494. value: buffer,
  495. writeType: 'writeNoResponse',
  496. success(res) {
  497. log(res)
  498. resolve({
  499. ok: true,
  500. errCode: 0,
  501. errMsg: ''
  502. })
  503. },
  504. fail(res) {
  505. console.log("写入失败");
  506. console.log(res);
  507. log(res)
  508. resolve({
  509. ok: false,
  510. errCode: res.code,
  511. errMsg: res.errMsg
  512. })
  513. },
  514. })
  515. })
  516. }
  517. const writeBLECharacteristicValue = async (str, isHex) => {
  518. if (str.length === 0)
  519. return {
  520. ok: false,
  521. errCode: 30000,
  522. errMsg: 'data is null'
  523. }
  524. let buffer
  525. if (isHex) {
  526. buffer = new ArrayBuffer(str.length / 2)
  527. let x = new Uint8Array(buffer)
  528. for (let i = 0; i < x.length; i++) {
  529. x[i] = parseInt(str.substr(2 * i, 2), 16)
  530. }
  531. } else {
  532. // AT指令模式 - 处理ASCII字符串
  533. // 转换为UTF-8字节数组
  534. const encoder = new TextEncoder();
  535. const utf8Bytes = encoder.encode(str);
  536. buffer = utf8Bytes.buffer;
  537. // 显示ASCII格式的日志
  538. console.log("TX: " + str); // 发送数据日志
  539. const writeData = "RX: " + str;
  540. saveWriteDataToLocal(writeData, 'rx');
  541. }
  542. if (isHex){
  543. // 显示十六进制格式的日志(类似 RX: + 的格式)
  544. const hexArray = [...new Uint8Array(buffer)]
  545. .map(b => b.toString(16).padStart(2, '0').toUpperCase());
  546. const hexString = hexArray.join(' ');
  547. console.log("RX: " + hexString); // 发送数据日志,格式如 "RX: 01 03 00 01 00 01"
  548. // 保存写入数据到本地存储
  549. const writeData = "RX: " + hexString;
  550. saveWriteDataToLocal(writeData,'rx');
  551. }else {
  552. const writeData = "RX: " + str;
  553. saveWriteDataToLocal(writeData,'rx');
  554. }
  555. return await _writeBLECharacteristicValue(buffer)
  556. }
  557. /**
  558. * 向蓝牙特征值写入数据
  559. * @param {string} str - 要发送的字符串数据
  560. * @param {boolean} isHex - 数据格式标志。
  561. * false: str 包含十六进制字符串 (e.g., "01030001004695F8")
  562. * true: str 包含 ASCII 字符串 (e.g., "Hello")
  563. * @returns {Promise<Object>} 写入操作的结果
  564. */
  565. const writeBLECharacteristicValueTwo = async (str, isHex) => {
  566. if (str.length === 0)
  567. return {
  568. ok: false,
  569. errCode: 30000,
  570. errMsg: 'data is null'
  571. }
  572. let buffer;
  573. // isHex === false 时,发送 ASCII 字符串
  574. if (!isHex) {
  575. // AT指令模式 - 处理ASCII字符串
  576. // 转换为UTF-8字节数组
  577. const encoder = new TextEncoder();
  578. const utf8Bytes = encoder.encode(str);
  579. buffer = utf8Bytes.buffer;
  580. // 显示ASCII格式的日志
  581. console.log("TX (ASCII): " + str); // 发送数据日志
  582. const writeData = "TX (ASCII): " + str;
  583. saveWriteDataToLocal(writeData, 'tx');
  584. }
  585. // isHex === true 时,发送十六进制字符串
  586. else {
  587. // 移除所有空格,确保是连续的十六进制字符串
  588. const hexString = str.replace(/\s/g, '');
  589. // 检查长度是否为偶数
  590. if (hexString.length % 2 !== 0) {
  591. console.error("Invalid hex string length");
  592. return {
  593. ok: false,
  594. errCode: 30001,
  595. errMsg: 'Invalid hex string length'
  596. };
  597. }
  598. // 检查是否包含非十六进制字符
  599. if (!/^[0-9A-Fa-f]*$/.test(hexString)) {
  600. console.error("Invalid hex characters");
  601. return {
  602. ok: false,
  603. errCode: 30002,
  604. errMsg: 'Invalid hex characters'
  605. };
  606. }
  607. // 创建 ArrayBuffer 并填充数据
  608. buffer = new ArrayBuffer(hexString.length / 2);
  609. let x = new Uint8Array(buffer);
  610. for (let i = 0; i < x.length; i++) {
  611. x[i] = parseInt(hexString.substr(2 * i, 2), 16);
  612. }
  613. // 显示十六进制格式的日志
  614. const formattedHex = hexString.match(/.{1,2}/g)?.join(' ') || '';
  615. console.log("TX (HEX): " + formattedHex); // 发送数据日志
  616. const writeData = "TX (HEX): " + formattedHex;
  617. saveWriteDataToLocal(writeData, 'tx');
  618. }
  619. console.log("Buffer to send:", new Uint8Array(buffer));
  620. return await _writeBLECharacteristicValue(buffer);
  621. }
  622. // 新增:模拟 QString::toLatin1() 的行为
  623. function stringToLatin1Bytes(str) {
  624. const bytes = [];
  625. for (let i = 0; i < str.length; i++) {
  626. const code = str.charCodeAt(i); // 获取 UTF-16 code unit
  627. // 检查是否在 Latin-1 范围内 (0-255)
  628. if (code >= 0 && code <= 255) {
  629. bytes.push(code); // 直接作为字节值
  630. } else {
  631. // C++ QString::toLatin1() 对于超出范围的字符通常会变成 0 或 '?' (0x3F)
  632. // 这里我们选择 0x3F ('?')
  633. // 你需要根据 C++ 实际行为调整,可能是 0x00
  634. bytes.push(0x3F); // 或者 bytes.push(0x00);
  635. }
  636. }
  637. return new Uint8Array(bytes);
  638. }
  639. const utf8BytesToStr = utf8Bytes => {
  640. let unicodeStr = ''
  641. for (let pos = 0; pos < utf8Bytes.length;) {
  642. let flag = utf8Bytes[pos]
  643. let unicode = 0
  644. if (flag >>> 7 === 0) {
  645. unicodeStr += String.fromCharCode(utf8Bytes[pos])
  646. pos += 1
  647. }
  648. // else if ((flag & 0xFC) === 0xFC) {
  649. // unicode = (utf8Bytes[pos] & 0x3) << 30
  650. // unicode |= (utf8Bytes[pos + 1] & 0x3F) << 24
  651. // unicode |= (utf8Bytes[pos + 2] & 0x3F) << 18
  652. // unicode |= (utf8Bytes[pos + 3] & 0x3F) << 12
  653. // unicode |= (utf8Bytes[pos + 4] & 0x3F) << 6
  654. // unicode |= (utf8Bytes[pos + 5] & 0x3F)
  655. // unicodeStr += String.fromCharCode(unicode)
  656. // pos += 6
  657. // }
  658. // else if ((flag & 0xF8) === 0xF8) {
  659. // unicode = (utf8Bytes[pos] & 0x7) << 24
  660. // unicode |= (utf8Bytes[pos + 1] & 0x3F) << 18
  661. // unicode |= (utf8Bytes[pos + 2] & 0x3F) << 12
  662. // unicode |= (utf8Bytes[pos + 3] & 0x3F) << 6
  663. // unicode |= (utf8Bytes[pos + 4] & 0x3F)
  664. // unicodeStr += String.fromCharCode(unicode)
  665. // pos += 5
  666. // }
  667. else if ((flag & 0xf0) === 0xf0) {
  668. unicode = (utf8Bytes[pos] & 0xf) << 18
  669. unicode |= (utf8Bytes[pos + 1] & 0x3f) << 12
  670. unicode |= (utf8Bytes[pos + 2] & 0x3f) << 6
  671. unicode |= utf8Bytes[pos + 3] & 0x3f
  672. unicodeStr += String.fromCharCode(unicode)
  673. pos += 4
  674. } else if ((flag & 0xe0) === 0xe0) {
  675. unicode = (utf8Bytes[pos] & 0x1f) << 12
  676. unicode |= (utf8Bytes[pos + 1] & 0x3f) << 6
  677. unicode |= utf8Bytes[pos + 2] & 0x3f
  678. unicodeStr += String.fromCharCode(unicode)
  679. pos += 3
  680. } else if ((flag & 0xc0) === 0xc0) {
  681. //110
  682. unicode = (utf8Bytes[pos] & 0x3f) << 6
  683. unicode |= utf8Bytes[pos + 1] & 0x3f
  684. unicodeStr += String.fromCharCode(unicode)
  685. pos += 2
  686. } else {
  687. unicodeStr += String.fromCharCode(utf8Bytes[pos])
  688. pos += 1
  689. }
  690. }
  691. return unicodeStr
  692. }
  693. const strToUtf8Bytes = str => {
  694. let bytes = []
  695. for (let i = 0; i < str.length; ++i) {
  696. let code = str.charCodeAt(i)
  697. if (code >= 0x10000 && code <= 0x10ffff) {
  698. bytes.push((code >> 18) | 0xf0) // 第一个字节
  699. bytes.push(((code >> 12) & 0x3f) | 0x80)
  700. bytes.push(((code >> 6) & 0x3f) | 0x80)
  701. bytes.push((code & 0x3f) | 0x80)
  702. } else if (code >= 0x800 && code <= 0xffff) {
  703. bytes.push((code >> 12) | 0xe0)
  704. bytes.push(((code >> 6) & 0x3f) | 0x80)
  705. bytes.push((code & 0x3f) | 0x80)
  706. } else if (code >= 0x80 && code <= 0x7ff) {
  707. bytes.push((code >> 6) | 0xc0)
  708. bytes.push((code & 0x3f) | 0x80)
  709. } else {
  710. bytes.push(code)
  711. }
  712. }
  713. return bytes
  714. }
  715. // 在文件顶部添加全局变量
  716. let globalWriteLogs = []
  717. let logListeners = []
  718. // 保存数据到全局变量的函数(移除本地存储)
  719. const saveWriteDataToLocal = (data, type) => {
  720. try {
  721. const now = new Date();
  722. const year = now.getFullYear();
  723. const month = String(now.getMonth() + 1).padStart(2, '0');
  724. const day = String(now.getDate()).padStart(2, '0');
  725. const hours = String(now.getHours()).padStart(2, '0');
  726. const minutes = String(now.getMinutes()).padStart(2, '0');
  727. const seconds = String(now.getSeconds()).padStart(2, '0');
  728. // 创建日志条目
  729. const logEntry = {
  730. time: `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`,
  731. data: data,
  732. type: type
  733. }
  734. // 添加到全局变量
  735. globalWriteLogs.push(logEntry)
  736. // 限制全局日志数量为最近100条
  737. if (globalWriteLogs.length > 100) {
  738. globalWriteLogs = globalWriteLogs.slice(-100)
  739. }
  740. // 通知所有监听器
  741. notifyLogListeners()
  742. } catch (e) {
  743. }
  744. }
  745. // 添加获取全局日志的方法
  746. const getGlobalWriteLogs = () => {
  747. return globalWriteLogs
  748. }
  749. // 添加清空全局日志的方法
  750. const clearGlobalWriteLogs = () => {
  751. globalWriteLogs = []
  752. notifyLogListeners()
  753. }
  754. // 添加监听器管理方法
  755. const addLogListener = (callback) => {
  756. logListeners.push(callback)
  757. }
  758. const removeLogListener = (callback) => {
  759. const index = logListeners.indexOf(callback)
  760. if (index > -1) {
  761. logListeners.splice(index, 1)
  762. }
  763. }
  764. // 通知监听器
  765. const notifyLogListeners = () => {
  766. logListeners.forEach(callback => {
  767. callback(globalWriteLogs)
  768. })
  769. }
  770. // 根据类型删除日志的函数(仅操作全局变量)
  771. const removeWriteDataByType = (type) => {
  772. try {
  773. const originalLength = globalWriteLogs.length;
  774. // 过滤掉指定类型的日志
  775. globalWriteLogs = globalWriteLogs.filter(log => log.type !== type)
  776. const deletedCount = originalLength - globalWriteLogs.length;
  777. // 通知所有监听器更新
  778. notifyLogListeners()
  779. console.log(`已删除类型为 '${type}' 的日志,共删除 ${deletedCount} 条`);
  780. return {
  781. success: true,
  782. deletedCount: deletedCount
  783. }
  784. } catch (e) {
  785. console.error('删除写入数据失败:', e)
  786. return {
  787. success: false,
  788. error: e
  789. }
  790. }
  791. }
  792. // 清空所有日志的函数(仅操作全局变量)
  793. const clearAllWriteData = () => {
  794. try {
  795. globalWriteLogs = []
  796. notifyLogListeners()
  797. console.log("已清空所有日志");
  798. return {
  799. success: true
  800. }
  801. } catch (e) {
  802. console.error('清空所有日志失败:', e)
  803. return {
  804. success: false,
  805. error: e
  806. }
  807. }
  808. }
  809. // 获取特定类型的日志
  810. const getWriteDataByType = (type) => {
  811. try {
  812. const filteredLogs = globalWriteLogs.filter(log => log.type === type)
  813. return filteredLogs
  814. } catch (e) {
  815. console.error('获取特定类型日志失败:', e)
  816. return []
  817. }
  818. }
  819. // 获取所有日志(按时间倒序排列)
  820. const getAllWriteDataSorted = () => {
  821. try {
  822. // 创建副本避免修改原数组
  823. let writeLogs = [...globalWriteLogs]
  824. // 按时间倒序排列(最新的在前面)
  825. writeLogs.sort((a, b) => {
  826. return new Date(b.time) - new Date(a.time);
  827. });
  828. return writeLogs
  829. } catch (e) {
  830. console.error('获取排序日志失败:', e)
  831. return []
  832. }
  833. }
  834. // 导出新增的方法
  835. export default {
  836. onBluetoothAdapterStateChange,
  837. openBluetoothAdapter,
  838. onBluetoothDeviceFound,
  839. startBluetoothDevicesDiscovery,
  840. stopBluetoothDevicesDiscovery,
  841. onBLEConnectionStateChange,
  842. createBLEConnection,
  843. closeBLEConnection,
  844. onBLECharacteristicValueChange,
  845. writeBLECharacteristicValue,
  846. saveWriteDataToLocal,
  847. getGlobalWriteLogs,
  848. clearGlobalWriteLogs,
  849. addLogListener,
  850. removeLogListener,
  851. removeWriteDataByType, // 新增
  852. clearAllWriteData, // 新增
  853. getWriteDataByType, // 新增
  854. getAllWriteDataSorted, // 新增
  855. writeBLECharacteristicValueTwo
  856. }