@jiabaida/tools 1.0.3 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,11 +4,15 @@ import { eventBus, getOS } from './commonfun'; // 导入eventBus
4
4
  const serviceId = '0000FF00-0000-1000-8000-00805F9B34FB';
5
5
  const UUID_READ = '0000FF01-0000-1000-8000-00805F9B34FB';
6
6
  const UUID_WRITE = '0000FF02-0000-1000-8000-00805F9B34FB';
7
-
7
+ const { isAndroid, isIOS } = getOS();
8
8
  const bluetoothAdapterState = ref({
9
9
  available: false,
10
10
  discovering: false,
11
11
  });
12
+
13
+
14
+ export var isInit = false;
15
+
12
16
  // #region 处理广播的设备信息
13
17
  /**
14
18
  * 处理广播的设备信息
@@ -34,7 +38,13 @@ const enhanceDevice = (device) => {
34
38
  const socHex = advHex.slice(16, 18);
35
39
  const soc = socHex || socHex == 0 ? hexToDecimal(socHex) : '';
36
40
  const productType = advHex.slice(18, 20);
37
- const protocolVersion = advHex.slice(20, 22);
41
+ let protocolVersion = advHex.slice(20, 22);
42
+ if (protocolVersion) {
43
+ const v = hexToDecimal(protocolVersion)
44
+ if (macAddr.startsWith("A5:C2") && v <= 0x23) {
45
+ protocolVersion = '01';
46
+ }
47
+ }
38
48
  const appkeyStatus = advHex.slice(22, 24);
39
49
  const tempVoltage = advHex.slice(24, 26);
40
50
  const voltage = tempVoltage || tempVoltage == 0 ? hexToDecimal(tempVoltage) : '';
@@ -46,82 +56,106 @@ const enhanceDevice = (device) => {
46
56
  }
47
57
  return device;
48
58
  }
49
- // #region 初始化蓝牙
50
- /** 初始化蓝牙 */
51
- export const initBle = async () => {
52
- const checkBluetoothStatus = async () => {
53
- return new Promise((resolve, reject) => {
54
- uni.openBluetoothAdapter({
55
- success: async (res) => {
56
- console.log('openBluetoothAdapter success', res);
57
- resolve(res);
58
- try {
59
- // await startDevicesDiscovery(false);
60
- // stopBluetoothDevicesDiscovery();
61
- onBluetoothDeviceFound();
62
- onBLECharacteristicValueChange();
63
- onBLEConnectionStateChange();
64
- onBluetoothAdapterStateChange()
65
- } catch (error) {
66
- console.log('error: ', error);
67
- reject(error);
68
- }
69
- },
70
- fail: (err) => {
71
- console.log('openBluetoothAdapter fail', err);
72
- reject(err);
73
- },
74
- });
75
- });
76
- };
77
- const checkLocationStatus = async () => {
78
- // 创建超时Promise
79
- const timeoutPromise = new Promise((resolve, reject) => {
80
- setTimeout(() => {
81
- reject(new Error('网络信号差或连接异常,请检查网络情况'));
82
- }, 10000); // 10秒超时
59
+
60
+ /**
61
+ * 状态码:
62
+ * 101:网络信号差或连接异常,请检查网络情况 --- 打开定位开关
63
+ * 102:当前手机蓝牙不可用,请检查手机蓝牙状态 --- 打开蓝牙开关 openSystemBluetoothSetting
64
+ * 103:请授权小程序位置信息权限, 并重启小程序。--- 打开小程序设置页面 openSetting
65
+ * 104:请授权微信位置信息权限, 并重启小程序。 --- 打开手机内微信权限设置页面 openAppAuthorizeSetting
66
+ * 105:请授权微信位置信息和蓝牙权限 --- 打开手机内微信权限设置页面 openAppAuthorizeSetting
67
+ * 106:请授权蓝牙和位置权限,并重启小程序 --- 打开小程序设置页面 openSetting
68
+ */
69
+ export const checkBluetoothStatus = async (successCallback = () => {}) => {
70
+ return new Promise((resolve, reject) => {
71
+ uni.openBluetoothAdapter({
72
+ success: async (res) => {
73
+ if (successCallback) {
74
+ successCallback(res);
75
+ }
76
+ resolve(res);
77
+ },
78
+ fail: (err) => {
79
+ console.log('openBluetoothAdapter fail4-----', err);
80
+ if (err && err?.errno == 3) {
81
+ reject({ code: 105, message: '请授权微信位置信息和蓝牙权限' });
82
+ }
83
+ if (err && err?.errno == 103) {
84
+ reject({ code: 106, message: '请授权蓝牙和位置权限,并重启小程序' });
85
+ }
86
+ reject({ code: 102, message: '当前手机蓝牙不可用,请检查手机蓝牙状态' });
87
+ },
83
88
  });
84
- // 创建位置获取Promise
85
- const locationPromise = new Promise((resolve, reject) => {
86
- uni.getLocation({
87
- type: 'wgs84',
88
- success: (res) => {
89
- console.log('位置信息-----------', res);
90
- resolve(res);
91
- },
92
- fail: (err) => {
93
- console.log('位置信息err', err);
94
- reject(err);
95
- },
96
- });
89
+ });
90
+ }
91
+
92
+ export const checkLocationStatus = async (successCallback = () => {}) => {
93
+ // 创建超时Promise
94
+ const timeoutPromise = new Promise((resolve, reject) => {
95
+ setTimeout(() => {
96
+ reject({ code: 101, message: '网络信号差或连接异常,请检查网络情况' });
97
+ }, 10000); // 10秒超时
98
+ });
99
+ // 创建位置获取Promise
100
+ const locationPromise = new Promise((resolve, reject) => {
101
+ uni.getLocation({
102
+ type: 'wgs84',
103
+ success: (res) => {
104
+ console.log('位置信息-----------', res);
105
+ resolve(res);
106
+ },
107
+ fail: (err) => {
108
+ console.log('位置信息err', err);
109
+ if (err && err?.errno == 103) {
110
+ reject({ code: 103, message: '请授权小程序位置信息权限, 并重启小程序。' });
111
+ }
112
+ if (err && err?.errCode == 2) {
113
+ reject({ code: 101, message: '网络信号差或连接异常,请检查网络情况' });
114
+ }
115
+ reject({ code: 104, message: '请授权微信位置信息权限, 并重启小程序。' });
116
+ },
97
117
  });
98
- // 使用Promise.race实现超时控制
99
- Promise.race([locationPromise, timeoutPromise])
100
- .then((res) => {
101
- // 成功获取位置信息
102
- checkBluetoothStatus();
103
- })
104
- .catch((err) => {
105
- console.log('位置获取失败或超时:', err);
106
- Promise.reject({
107
- code: 1,
108
- // message: '请检查手机位置是否开启',
109
- });
110
- });
111
- };
118
+ });
119
+ // 使用Promise.race实现超时控制
120
+ return Promise.race([locationPromise, timeoutPromise])
121
+ .then(async (res) => {
122
+ // 成功获取位置信息
123
+ return await checkBluetoothStatus(successCallback);
124
+ })
125
+ }
126
+
127
+ export const getBluetoothPermission = async (successCallback = () => {}) => {
112
128
  try {
113
- const { isAndroid } = getOS();
114
129
  if (isAndroid) {
115
- checkLocationStatus();
130
+ return await checkLocationStatus(successCallback);
116
131
  } else {
117
- checkBluetoothStatus();
132
+ return await checkBluetoothStatus(successCallback);
118
133
  }
119
134
  } catch (error) {
120
135
  console.error('初始化蓝牙失败:', error);
121
- Promise.reject({
122
- code: error.code || error.errCode || -1,
123
- });
136
+ throw (error);
124
137
  }
138
+ }
139
+
140
+ // #region 初始化蓝牙
141
+ /** 初始化蓝牙
142
+ * 错误码:
143
+ * 101:网络信号差或连接异常,请检查网络情况
144
+ * 102:当前手机蓝牙不可用,请检查手机蓝牙状态
145
+ */
146
+ export const initBle = async () => {
147
+ const initBluetoothSuccessCb = () => {
148
+ try {
149
+ onBluetoothDeviceFound();
150
+ onBLECharacteristicValueChange();
151
+ onBLEConnectionStateChange();
152
+ onBluetoothAdapterStateChange()
153
+ isInit = true;
154
+ } catch (error) {
155
+ throw error;
156
+ }
157
+ }
158
+ return await getBluetoothPermission(initBluetoothSuccessCb);
125
159
  };
126
160
  // #region 获取蓝牙适配器状态
127
161
  /** 获取蓝牙适配器状态 */
@@ -171,6 +205,10 @@ export const startDevicesDiscovery = async (allowDuplicatesKey = true) => {
171
205
  // #region 停止搜索蓝牙设备
172
206
  /**停止搜索蓝牙设备 */
173
207
  export const stopBluetoothDevicesDiscovery = async () => {
208
+ bluetoothAdapterState.value = {
209
+ available: bluetoothAdapterState.value.available,
210
+ discovering: false,
211
+ }
174
212
  return new Promise((resolve, reject) => {
175
213
  uni.stopBluetoothDevicesDiscovery({
176
214
  success: (res) => {
@@ -435,8 +473,6 @@ export const scanHandle = (historyConnectedList = [], connectFn = (device) => {
435
473
  isMac = true;
436
474
  }
437
475
  }
438
-
439
- const { isAndroid } = getOS();
440
476
  // 检查历史连接列表是否已包含该设备
441
477
  const matchedHistoryDevice = historyConnectedList.find((item) => item.macAddr == searchKey);
442
478
  if (matchedHistoryDevice) {
@@ -458,6 +494,8 @@ export const scanHandle = (historyConnectedList = [], connectFn = (device) => {
458
494
  }
459
495
  },
460
496
  complete: (err) => {
497
+ },
498
+ fail: (err) => {
461
499
  console.log('scanCode err', err);
462
500
  failScanFn(err)
463
501
  }
@@ -466,7 +504,7 @@ export const scanHandle = (historyConnectedList = [], connectFn = (device) => {
466
504
  // #region 广播5秒
467
505
  export const foundScanDevice = async (result, connectFn) => {
468
506
  const deviceList = [];
469
- await startDevicesDiscovery();
507
+ await startDevicesDiscovery(isIOS);
470
508
  onFoundDevice((device) => {
471
509
  deviceList.push(device)
472
510
  const findDevice = device.macAddr === result || device.name === result;
@@ -485,8 +523,6 @@ export const foundScanDevice = async (result, connectFn) => {
485
523
  try {
486
524
  // 五秒后停止搜索且关闭轮询器
487
525
  console.log('stopBluetoothDevicesDiscovery', deviceList);
488
- clearInterval(scanTimer);
489
- scanTimer = null;
490
526
  uni.stopBluetoothDevicesDiscovery();
491
527
  const findDevice = deviceList.find((device) => device.macAddr === result || device.name === result);
492
528
  connectFn(findDevice);
@@ -1,8 +1,8 @@
1
-
2
1
  import { getSysParamCmd } from './readAndSetParam'
3
2
  import { decimalToHex, hexToDecimal } from '../BleDataProcess';
4
- import { batteryInfoJson, capInfoJson, voltageInfoJson, currentInfoJson, tempInfoJson, equalizerFunJson, capVolInfoJson, funcJson, tempFuncJson, systemJson, funcAndTempFuncJson } from '../dataJson/baseParamsJson.js'
3
+ import { batteryInfoJson, capInfoJson, voltageInfoJson,currentInfoJson, tempInfoJson, equalizerFunJson, capVolInfoJson, funcJson, tempFuncJson, systemJson, funcAndTempFuncJson } from '../dataJson/baseParamsJson.js';
5
4
  import { readExistFactory, getProtectCountCmd, enterFactory } from './BleCmdDD.js'
5
+
6
6
  /**
7
7
  *
8
8
  * @param {*} value 原始十进制值字数组
@@ -111,7 +111,7 @@ export const getBaseParams = async (chipType, deviceData, index, paramObj) => {
111
111
  switch (index) {
112
112
  case 1:
113
113
  let datasInfo1 = {};
114
- await sendCmdSeq(datasInfo1, chipType, deviceId, batteryInfoJson)
114
+ await sendCmdSeq(datasInfo1, chipType, deviceId, batteryInfoJson(crsDivisor))
115
115
  dataInfo = getBaseInfo(datasInfo1)
116
116
  return dataInfo
117
117
  case 2:
@@ -188,9 +188,11 @@ export const getBaseParams = async (chipType, deviceData, index, paramObj) => {
188
188
  if (chipType) {
189
189
  let tindex = tempInfoJson.findIndex((el) => el.paramNo == 8)
190
190
  let tindex1 = tempInfoJson.findIndex((el) => el.paramNo == 44)
191
+ let tindex2 = tempInfoJson.findIndex((el) => el.paramNo == 202)
191
192
  const tempData = await getSysParamCmd(chipType, deviceId, { ...tempInfoJson[tindex], paramLength: 16 })
192
193
  const tempData1 = await getSysParamCmd(chipType, deviceId, { ...tempInfoJson[tindex1], paramLength: 8 })
193
- console.log('tempData: ', tempData);
194
+ const tempData2 = await getSysParamCmd(chipType, deviceId, { ...tempInfoJson[tindex2], paramLength: 4 })
195
+ console.log('tempData: ', tempData, tempData2);
194
196
  datasInfo5['chargeHightempProtect'] = tempData?.slice(0, 2)
195
197
  datasInfo5['chargeHightempRecover'] = tempData?.slice(2, 4)
196
198
  datasInfo5['chargeLowtempProtect'] = tempData?.slice(4, 6)
@@ -203,6 +205,8 @@ export const getBaseParams = async (chipType, deviceData, index, paramObj) => {
203
205
  datasInfo5['chargeHightempDelay'] = tempData1?.slice(2, 4)
204
206
  datasInfo5['dischargingLowtempDelay'] = tempData1?.slice(4, 6)
205
207
  datasInfo5['dischargingHightempDelay'] = tempData1?.slice(6, 8)
208
+ datasInfo5['overtempProtect'] = tempData2?.slice(0, 2)
209
+ datasInfo5['overtempRecover'] = tempData2?.slice(2, 4)
206
210
  } else {
207
211
  await sendCmdSeq(datasInfo5, chipType, deviceId, tempInfoJson)
208
212
  }
@@ -433,7 +437,7 @@ const getCurrentInfo = async (datas, chipType) => {
433
437
  console.log('datas: ', datas);
434
438
  const occhg = format(datas.occhg, 0.01, 0, 3)
435
439
  // 采用补码形式传送
436
- const dischargeOvercurrentProtect = (datas.dischargeOvercurrentProtect || datas.dischargeOvercurrentProtect == 0) && (format(datas.dischargeOvercurrentProtect, 1, 0, 3) - 0x10000) * 0.01
440
+ const dischargeOvercurrentProtect = (datas.dischargeOvercurrentProtect || datas.dischargeOvercurrentProtect == 0) && Number(((format(datas.dischargeOvercurrentProtect, 1, 0, 3) - 0x10000) * 0.01).toFixed(3))
437
441
 
438
442
  let chargeOvercurrentDelay = null
439
443
  let chargeOvercurrentRecoverDelay = null
@@ -546,6 +550,8 @@ const getTempInfo = (datas, chipType) => {
546
550
  const dischargingHightempRecover = formatTemp(datas.dischargingHightempRecover)
547
551
  const dischargingLowtempProtect = formatTemp(datas.dischargingLowtempProtect)
548
552
  const dischargingLowtempRecover = formatTemp(datas.dischargingLowtempRecover)
553
+ const overtempProtect = formatTemp(datas.overtempProtect)
554
+ const overtempRecover = formatTemp(datas.overtempRecover)
549
555
  let chargeHightempDelay = null
550
556
  let chargeLowtempDelay = null
551
557
  let dischargingHightempDelay = null
@@ -573,7 +579,9 @@ const getTempInfo = (datas, chipType) => {
573
579
  chargeHightempDelay,
574
580
  chargeLowtempDelay,
575
581
  dischargingHightempDelay,
576
- dischargingLowtempDelay
582
+ dischargingLowtempDelay,
583
+ overtempProtect,
584
+ overtempRecover
577
585
  }
578
586
  }
579
587
 
@@ -631,11 +639,26 @@ const getFuncAndTempFuncInfo = (datas) => {
631
639
  })
632
640
  return { func, tempFunc }
633
641
  }
642
+
643
+ const splitNumberTo1Byte = (combined = 0) => {
644
+ combined = Number(combined);
645
+ return {
646
+ val1: combined & 0xFF,
647
+ val2: (combined >> 8) & 0xFF
648
+ };
649
+ }
650
+
634
651
  const getSystemInfo = (datas) => {
635
652
  const serialNumber = format(datas.serialNumber, 1)
636
653
  const cycleCount = format(datas.cycleCount, 1)
637
654
  const strCount = format(datas.strCount, 1)
638
655
  const rsnsValue = format(datas.rsnsValue, crsDivisor, 0, 2)
656
+ const param1911 = splitNumberTo1Byte(format(datas.param1911)).val1
657
+ const param1912 = splitNumberTo1Byte(format(datas.param1911)).val2
658
+ const param1981 = splitNumberTo1Byte(format(datas.param1981)).val1
659
+ const param1982 = splitNumberTo1Byte(format(datas.param1981)).val2
660
+ const param1951 = splitNumberTo1Byte(format(datas.param1951)).val1
661
+ const param1952 = splitNumberTo1Byte(format(datas.param1951)).val2
639
662
  // const identifyCurrent = format(datas.identifyCurrent, 1)
640
663
  // const sleepTime = format(datas.sleepTime, 1)
641
664
  // const capacityInterval = format(datas.capacityInterval, 1)
@@ -644,6 +667,12 @@ const getSystemInfo = (datas) => {
644
667
  cycleCount,
645
668
  strCount,
646
669
  rsnsValue,
670
+ param1911,
671
+ param1912,
672
+ param1951,
673
+ param1952,
674
+ param1981,
675
+ param1982,
647
676
  // identifyCurrent,
648
677
  // sleepTime,
649
678
  // capacityInterval
@@ -1,4 +1,4 @@
1
- import { decimalToHex, generateCrc16modbusCheck, generateCrcCheckSum, hex2string, hexArr2Assic, hexArr2string, hexToDecimal, sleep, stringToTwoHexArray } from '../BleDataProcess';
1
+ import { decimalToHex, fromBCD, generateCrc16modbusCheck, generateCrcCheckSum, hex2string, hexArr2Assic, hexArr2string, hexToDecimal, isWithin30Minutes, sleep, stringToTwoHexArray, toBCD } from '../BleDataProcess';
2
2
  import { getData } from './BleCmdAnalysis.js';
3
3
  import { getFFAA80Async, resolveFFAA80 } from './BleCmdFFAA.js';
4
4
 
@@ -244,9 +244,21 @@ export const resolveDDA503 = (data) => {
244
244
 
245
245
  const binArr = data[24].toString(2).padStart(8, '0').split('').reverse();
246
246
 
247
- const balances = data.slice(16, 20).flatMap((byte) =>
248
- byte.toString(2).padStart(8, "0").split("").reverse()
249
- );
247
+
248
+ // 获取16-19索引的4个字节
249
+ const BC1 = (data[16] << 8 | data[17])
250
+ .toString(2)
251
+ .padStart(16, "0")
252
+ .split("")
253
+ .reverse();
254
+ console.log('BC1: ', BC1);
255
+ const BC2 = (data[18] << 8 | data[19])
256
+ .toString(2)
257
+ .padStart(16, "0")
258
+ .split("")
259
+ .reverse();
260
+ const BC = BC1.concat(BC2);
261
+ const balances = BC;
250
262
  console.warn("BC: ---------------", balances);
251
263
  const fet = data[24];
252
264
  /**充电开关 */
@@ -1332,4 +1344,119 @@ export const resetCapacity = (deviceId) => {
1332
1344
  },
1333
1345
  'DD5A_0A'
1334
1346
  );
1335
- }
1347
+ }
1348
+
1349
+ /**读取BMS系统时间*/
1350
+ export const getDDA506Async = async (deviceId) => {
1351
+ const command = ['0xdd', '0xa5', '0x06', '0x00', '0xff', '0xfa', '0x77'];
1352
+ const response = '';
1353
+ return getData(
1354
+ deviceId,
1355
+ {
1356
+ command,
1357
+ commandVerifyHandler: (hexArr) => ({ verified: hexArr[0] == 0xdd && hexArr[1] == 0x06, pkgLen: hexArr[3] + 7 }),
1358
+ pkgVerifyHandler: (pkg) => {
1359
+ const len = pkg.length;
1360
+ const [c1, c2] = generateCrcCheckSum(pkg.slice(2, len - 3));
1361
+ const [_c1, _c2] = [pkg[len - 3], pkg[len - 2]];
1362
+ return { verified: c1 == _c1 && c2 == _c2 };
1363
+ },
1364
+ },
1365
+ 'DDA5_06'
1366
+ );
1367
+ }
1368
+
1369
+ /**解析BMS系统时间*/
1370
+ export const resolveDDA506 = (data) => {
1371
+ if (!data) return null;
1372
+ if (data[1] == 6 && data[2] == 0 && data[3] == 6) {
1373
+ //解析各字段
1374
+ const seconds = fromBCD(data[4]);
1375
+ const minutes = fromBCD(data[5]);
1376
+ const hours = fromBCD(data[6]);
1377
+ const day = fromBCD(data[7]);
1378
+ const month = fromBCD(data[8]);
1379
+ const year = 2000 + fromBCD(data[9]);
1380
+ const timeStr = `${year}-${String(month).padStart(2, 0)}-${String(day).padStart(2, 0)} ${String(hours).padStart(2, 0)}:${String(minutes).padStart(2, 0)}:${String(seconds).padStart(2, 0)}`;
1381
+ console.log('timeStr: ', timeStr, isWithin30Minutes(timeStr));
1382
+ return isWithin30Minutes(timeStr);
1383
+ } else {
1384
+ return false;
1385
+ }
1386
+ }
1387
+
1388
+ /**设置BMS系统时间*/
1389
+ //verified: hexArr[0] ->响应指令头 hexArr[1] ->指令码 pkgLen -> 响应指令长度
1390
+ export const setDD5AE3sync = async (deviceId) => {
1391
+ const header = ['0xDD', '0x5A']
1392
+ const now = new Date();
1393
+ const year = now.getFullYear();
1394
+ const month = now.getMonth() + 1;
1395
+ const day = now.getDate();
1396
+ const hours = now.getHours();
1397
+ const minutes = now.getMinutes();
1398
+ const seconds = now.getSeconds();
1399
+
1400
+ const values = [toBCD(seconds), toBCD(minutes), toBCD(hours) | 0x80, toBCD(day), toBCD(month), toBCD(year - 2000)].map(b => '0x' + b.toString(16).padStart(2, '0'));
1401
+ const content = ['0xE3', '0x07', '0x06', ...values];
1402
+ const checks = generateCrcCheckSum(content);
1403
+ const command = [...header, ...content, ...checks, '0x77'];
1404
+ console.log('command: ', command);
1405
+ return getData(
1406
+ deviceId,
1407
+ {
1408
+ command,
1409
+ commandVerifyHandler: (hexArr) => ({ verified: hexArr[0] == 0xdd && hexArr[1] == 0xe3, pkgLen: hexArr[3] + 7 }),
1410
+ pkgVerifyHandler: (pkg) => {
1411
+ return { verified: true };
1412
+ },
1413
+ },
1414
+ 'SET_DDA5_E3'
1415
+ );
1416
+ }
1417
+
1418
+ /** 读取BMS运行时长 */
1419
+ export const getDDA4F0Async = async (deviceId) => {
1420
+ // 指令:DD A4 F0 00 FF 0B 77
1421
+ const command = ['0xDD', '0xA4', '0xF0', '0x00'];
1422
+ const checks = generateCrcCheckSum(command.slice(2)); // 只对命令码和长度做校验
1423
+ const fullCommand = [...command, ...checks, '0x77'];
1424
+
1425
+ return getData(
1426
+ deviceId,
1427
+ {
1428
+ command: fullCommand,
1429
+ commandVerifyHandler: (hexArr) => ({
1430
+ verified: hexArr[0] == 0xDD && hexArr[1] == 0xF0,
1431
+ pkgLen: hexArr[3] + 7
1432
+ }),
1433
+ pkgVerifyHandler: (pkg) => {
1434
+ const len = pkg.length;
1435
+ const [c1, c2] = generateCrcCheckSum(pkg.slice(2, len - 3));
1436
+ const [_c1, _c2] = [pkg[len - 3], pkg[len - 2]];
1437
+ return { verified: c1 == _c1 && c2 == _c2 };
1438
+ },
1439
+ },
1440
+ 'DDA4_F0'
1441
+ );
1442
+ }
1443
+
1444
+ /** 解析BMS运行时长 */
1445
+ export const resolveDDA4F0 = (data) => {
1446
+ if (!data) return null;
1447
+ // 响应格式:DD F0 状态 长度 运行时长[4字节] 校验 0x77
1448
+ if (data[1] == 0xF0 && data[3] == 4) {
1449
+ // 运行时长为4字节,单位一般为秒(具体以协议为准)
1450
+ const runTime =
1451
+ (data[4] << 24) |
1452
+ (data[5] << 16) |
1453
+ (data[6] << 8) |
1454
+ data[7];
1455
+ return {
1456
+ runTime, // 运行时长(秒)
1457
+ runTimeHour: (runTime / 3600).toFixed(2), // 转为小时
1458
+ raw: hexArr2string(data)
1459
+ };
1460
+ }
1461
+ return null;
1462
+ }
@@ -353,3 +353,43 @@
353
353
  return binaryString;
354
354
  }
355
355
 
356
+
357
+ /**转BCD*/
358
+ export const fromBCD = (bcd) => {
359
+ // 确保只取低 8 位(安全)
360
+ bcd = bcd & 0xFF;
361
+ const tens = (bcd >> 4) & 0x0F; // 高4位
362
+ const units = bcd & 0x0F; // 低4位
363
+ return tens * 10 + units;
364
+ }
365
+
366
+ export const parseDateTime = (str) => {
367
+ const reg = /^(\d{4})-(\d{1,2})-(\d{1,2})\s+(\d{1,2}):(\d{1,2}):(\d{1,2})$/;
368
+ const match = str.match(reg);
369
+ if (!match) return null;
370
+ const [, y, m, d, h, i, s] = match.map(Number);
371
+ return new Date(y, m - 1, d, h, i, s).getTime();
372
+ }
373
+
374
+ // 判断是否在30分钟内
375
+ export const isWithin30Minutes = (timeStr) => {
376
+ const now = Date.now(); // 手机当前时间(毫秒)
377
+ const targetTime = parseDateTime(timeStr);
378
+ // console.log('targetTime: ', targetTime);
379
+ if (targetTime === null) {
380
+ console.error('时间字符串格式错误:', timeStr);
381
+ return false;
382
+ }
383
+ const diff = Math.abs(now - targetTime); // 毫秒差
384
+ const thirtyMinutes = 30 * 60 * 1000; // 30分钟对应的毫秒数
385
+ return diff >= thirtyMinutes;
386
+ }
387
+
388
+ export const toBCD = (num) => {
389
+ if (num < 0 || num > 99) {
390
+ throw new Error('BCD only supports 0-99');
391
+ }
392
+ const tens = Math.floor(num / 10); // 十位(0~9)
393
+ const units = num % 10; // 个位(0~9)
394
+ return (tens << 4) | units; // 高4位 + 低4位
395
+ }
@@ -40,7 +40,9 @@ export class OTAUpgrade {
40
40
  this.macAddr = macAddr;
41
41
  this.platform = platform; // 'APP' | 'MP' | 'H5'
42
42
  // 自动判断是否Telink,入参为macAddr
43
- this.isTeLink = TelinkApi.isTeLink ? TelinkApi.isTeLink(macAddr) : false;
43
+ this.isTeLink = TelinkApi.isTeLink ? TelinkApi.isTeLink({
44
+ macAddr
45
+ }) : false;
44
46
  this.onProgress = onProgress;
45
47
  this.onSuccess = onSuccess;
46
48
  this.onError = onError;
@@ -52,6 +54,7 @@ export class OTAUpgrade {
52
54
  this.ready = true;
53
55
  this.timer = null;
54
56
  this.s_progress = 0;
57
+ this.otaStarting = false;
55
58
  this.progressType = 'download'; // 当前进度类型: 'download' | 'upgrade'
56
59
  }
57
60
 
@@ -70,6 +73,7 @@ export class OTAUpgrade {
70
73
  const downloadTask = uni.downloadFile({
71
74
  url: that.filePath,
72
75
  success: ({ tempFilePath }) => {
76
+ this.addBLECharValueChangeListener();
73
77
  that.resolve(tempFilePath).then(resolve).catch(reject);
74
78
  },
75
79
  fail: (res) => {
@@ -158,7 +162,6 @@ export class OTAUpgrade {
158
162
  normalUpgrade() {
159
163
  setTimeout(() => {
160
164
  BLE.writeATCmd(this.otaInfo, this.deviceId, this.fail.bind(this));
161
- this.addBLECharValueChangeListener();
162
165
  }, 2600);
163
166
  }
164
167
 
@@ -236,6 +239,7 @@ export class OTAUpgrade {
236
239
  }
237
240
 
238
241
  addBLECharValueChangeListener() {
242
+ console.log('添加蓝牙特征值变化监听');
239
243
  const that = this;
240
244
  uni.notifyBLECharacteristicValueChange({
241
245
  deviceId: that.deviceId,
@@ -273,13 +277,19 @@ export class OTAUpgrade {
273
277
  }
274
278
 
275
279
  async doWithResponse(intArr) {
280
+ console.log('收到设备响应: ', intArr[2]);
276
281
  if (intArr[2] == 0x80) {
277
282
  let str = String.fromCharCode(...intArr.slice(4, -1));
283
+ console.log('doWithResponse otaStart1', str);
278
284
  if (str.indexOf(this.otaInfo) > -1) {
279
285
  this.finishedTimes = 0;
280
286
  this.transferFirmwareFile();
281
287
  }
282
- if (str.indexOf(this.otaStart) > -1) {
288
+ if (str.indexOf(this.otaStart) > -1 || this.otaStarting) {
289
+ if (str.indexOf(this.otaStart) > -1) {
290
+ this.otaStarting = true;
291
+ }
292
+ console.log('doWithResponse otaStart2', str);
283
293
  if (str.toUpperCase().includes('ERROR')) {
284
294
  this.fail(str);
285
295
  return;
@@ -313,10 +323,12 @@ export class OTAUpgrade {
313
323
  if (timeFor51) {
314
324
  const pkg = [0x00];
315
325
  BLE.transferFirmwareFileCmd(this.deviceId, pkg, true, this.fail.bind(this));
326
+ console.log('[OTA]', '=== Send 51 ===', this.finishedTimes, this.totalTimes, pkg);
316
327
  } else {
317
328
  const pkg = this.fileHexArray.slice(this.finishedTimes * max, nextTime * max);
318
- const index = Transfer.decimalToTwoByteHexArray(nextTime);
329
+ const index = Transfer.decimalToTwoByteHexArray(this.finishedTimes);
319
330
  BLE.transferFirmwareFileCmd(this.deviceId, index.concat(pkg), false, this.fail.bind(this));
331
+ console.log('[OTA]', `S50-${this.finishedTimes}/${this.totalTimes}`);
320
332
  }
321
333
  this.onProgress && this.onProgress({ type: 'upgrade', percent: Math.floor((this.finishedTimes * 100) / this.totalTimes) });
322
334
  }
@@ -252,7 +252,7 @@ export const BLE = {
252
252
  readUUID: '0000FF01-0000-1000-8000-00805F9B34FB',
253
253
  TAG: '[BLE_OTA]',
254
254
  WRITE_DELAY: 25,
255
- PACKAGE_MAX_LENGTH: 240,
255
+ PACKAGE_MAX_LENGTH: 237,
256
256
  // 分包发送
257
257
  sendMsgToKey(buffer, deviceId, fail) {
258
258
  const { isAndroid, isIOS } = getOS();
@@ -34,7 +34,8 @@ export function getOS() {
34
34
  const os = uni.getSystemInfoSync()?.osName;
35
35
  return {
36
36
  isIOS: os == 'ios',
37
- isAndroid: os == 'android',
37
+ isAndroid: os == 'android' || os == 'openharmonyos',
38
+ isHarmony: os === 'harmony' || os === 'openharmonyos',
38
39
  };
39
40
  }
40
41
  export function getPlatform() {