@jiabaida/tools 1.0.8 → 1.1.0
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.
- package/dist/cjs/core/BleApiManager.js +1 -1
- package/dist/cjs/core/BleCmdAnalysis/BaseParamProtocol.js +1 -1
- package/dist/cjs/core/BleCmdAnalysis/BleCmdAnalysis.js +1 -1
- package/dist/cjs/core/BleCmdAnalysis/BleCmdDD.js +1 -1
- package/dist/cjs/core/BleCmdAnalysis/BleCmdDDA4.js +1 -1
- package/dist/cjs/core/BleCmdAnalysis/BleCmdFFAA.js +1 -1
- package/dist/cjs/core/BleCmdAnalysis/readAndSetParam.js +1 -1
- package/dist/cjs/core/BleDataProcess.js +1 -1
- package/dist/cjs/core/OtaUpgrade.js +1 -1
- package/dist/cjs/core/mqttServer.js +1 -1
- package/dist/cjs/core/tcpServer.js +1 -1
- package/dist/cjs/index.js +1 -1
- package/dist/esm/core/BleApiManager.js +1 -1
- package/dist/esm/core/BleCmdAnalysis/BaseParamProtocol.js +1 -1
- package/dist/esm/core/BleCmdAnalysis/BleCmdAnalysis.js +1 -1
- package/dist/esm/core/BleCmdAnalysis/BleCmdDD.js +1 -1
- package/dist/esm/core/BleCmdAnalysis/BleCmdDDA4.js +1 -1
- package/dist/esm/core/BleCmdAnalysis/BleCmdFFAA.js +1 -1
- package/dist/esm/core/BleCmdAnalysis/BleCmdHVES.js +1 -1
- package/dist/esm/core/BleCmdAnalysis/readAndSetParam.js +1 -1
- package/dist/esm/core/BleDataProcess.js +1 -1
- package/dist/esm/core/OtaUpgrade.js +1 -1
- package/dist/esm/core/mqttServer.js +1 -1
- package/dist/esm/core/tcpServer.js +1 -1
- package/dist/esm/index.js +1 -1
- package/package.json +1 -1
- package/src/core/BleApiManager.js +33 -15
- package/src/core/BleCmdAnalysis/BaseParamProtocol.js +59 -34
- package/src/core/BleCmdAnalysis/BleCmdAnalysis.js +58 -2
- package/src/core/BleCmdAnalysis/BleCmdDD.js +16 -6
- package/src/core/BleCmdAnalysis/BleCmdDDA4.js +154 -227
- package/src/core/BleCmdAnalysis/BleCmdFFAA.js +1 -0
- package/src/core/BleCmdAnalysis/readAndSetParam.js +45 -17
- package/src/core/BleDataProcess.js +15 -11
- package/src/core/OtaUpgrade.js +356 -356
- package/src/core/mqttServer.js +58 -7
- package/src/core/tcpServer.js +76 -38
|
@@ -20,7 +20,8 @@ export const getDDA420Async = async (deviceId) => {
|
|
|
20
20
|
return { verified: c1 == _c1 && c2 == _c2 };
|
|
21
21
|
},
|
|
22
22
|
},
|
|
23
|
-
'DDA420'
|
|
23
|
+
'DDA420',
|
|
24
|
+
1500
|
|
24
25
|
);
|
|
25
26
|
}
|
|
26
27
|
|
|
@@ -85,10 +86,11 @@ export const getDDA420Async = async (deviceId) => {
|
|
|
85
86
|
);
|
|
86
87
|
|
|
87
88
|
// 6. 充电器温度(2字节)
|
|
88
|
-
const
|
|
89
|
+
const tempChargeTemperature = hexToDecimal(
|
|
89
90
|
decimalToHex(data[n++]) +
|
|
90
91
|
decimalToHex(data[n++])
|
|
91
|
-
)
|
|
92
|
+
);
|
|
93
|
+
const chargeTemperature = tempChargeTemperature > 32768 ? (tempChargeTemperature - 65536) / 10 : tempChargeTemperature / 10;
|
|
92
94
|
|
|
93
95
|
// 7. 已充电量(4字节)
|
|
94
96
|
const chargeCapacity = hexToDecimal(
|
|
@@ -231,7 +233,7 @@ export const getDDA420Async = async (deviceId) => {
|
|
|
231
233
|
const checks = generateCrcCheckSum(_command);
|
|
232
234
|
const command = ['0xdd', '0xa4', ..._command, ...checks, '0x77'];
|
|
233
235
|
|
|
234
|
-
return
|
|
236
|
+
return getData(
|
|
235
237
|
deviceId,
|
|
236
238
|
{
|
|
237
239
|
command,
|
|
@@ -243,7 +245,8 @@ export const getDDA420Async = async (deviceId) => {
|
|
|
243
245
|
return { verified: c1 == _c1 && c2 == _c2 };
|
|
244
246
|
},
|
|
245
247
|
},
|
|
246
|
-
'DDA421'
|
|
248
|
+
'DDA421',
|
|
249
|
+
1500
|
|
247
250
|
);
|
|
248
251
|
}
|
|
249
252
|
/** 解析充电电池信息21指令
|
|
@@ -271,7 +274,8 @@ export const getDDA420Async = async (deviceId) => {
|
|
|
271
274
|
const batterySOC = data[18]; // %
|
|
272
275
|
// 电池温度 2BYTE [19,20]
|
|
273
276
|
const batteryTempHex = decimalToHex(data[19]) + decimalToHex(data[20]);
|
|
274
|
-
const
|
|
277
|
+
const tempBatteryTemperature = hexToDecimal(batteryTempHex);
|
|
278
|
+
const batteryTemperature = tempBatteryTemperature > 32768 ? (tempBatteryTemperature - 65536) / 10 : tempBatteryTemperature / 10; // 0.1摄氏度
|
|
275
279
|
// const batteryTemperature = [data[19] ?? 0, [data[20] ?? 0]] // 摄氏度,保留一位小数
|
|
276
280
|
// 加热状态 1BYTE [21]
|
|
277
281
|
const heatingStatus = data[21]; // 0未加热 1加热中
|
|
@@ -332,7 +336,8 @@ export const getDDA420Async = async (deviceId) => {
|
|
|
332
336
|
return { verified: true };
|
|
333
337
|
},
|
|
334
338
|
},
|
|
335
|
-
'DDA425'
|
|
339
|
+
'DDA425',
|
|
340
|
+
1500
|
|
336
341
|
);
|
|
337
342
|
}
|
|
338
343
|
export const resolveDDA425 = (data) => {
|
|
@@ -377,7 +382,8 @@ export const getDDA420Async = async (deviceId) => {
|
|
|
377
382
|
return { verified: c1 == _c1 && c2 == _c2 };
|
|
378
383
|
},
|
|
379
384
|
},
|
|
380
|
-
'DDA422'
|
|
385
|
+
'DDA422',
|
|
386
|
+
3000
|
|
381
387
|
);
|
|
382
388
|
}
|
|
383
389
|
/** 解析充电器参数22指令响应
|
|
@@ -406,46 +412,66 @@ export const getDDA420Async = async (deviceId) => {
|
|
|
406
412
|
* 设置充电器参数23指令
|
|
407
413
|
*/
|
|
408
414
|
export const getDDA423Async = async (deviceId, startReg, endReg, values, valuesLength) => {
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
415
|
+
try {
|
|
416
|
+
// 起始/结束地址 2 字节大端
|
|
417
|
+
const startRegHex = decimalToHex(startReg, 4);
|
|
418
|
+
const startRegArr = [
|
|
419
|
+
parseInt(startRegHex.slice(0, 2), 16),
|
|
420
|
+
parseInt(startRegHex.slice(2, 4), 16)
|
|
421
|
+
];
|
|
422
|
+
const endRegHex = decimalToHex(endReg, 4);
|
|
423
|
+
const endRegArr = [
|
|
424
|
+
parseInt(endRegHex.slice(0, 2), 16),
|
|
425
|
+
parseInt(endRegHex.slice(2, 4), 16)
|
|
426
|
+
];
|
|
427
|
+
|
|
428
|
+
let valueArr = [];
|
|
429
|
+
// 批量模式:传入数组
|
|
430
|
+
if (Array.isArray(values)) {
|
|
431
|
+
valueArr = values.map(v => (Number(v) || 0) & 0xFF);
|
|
432
|
+
const expectLen = endReg - startReg + 1;
|
|
433
|
+
if (valueArr.length !== expectLen) {
|
|
434
|
+
if (valueArr.length > expectLen) {
|
|
435
|
+
valueArr = valueArr.slice(0, expectLen);
|
|
436
|
+
} else {
|
|
437
|
+
while (valueArr.length < expectLen) valueArr.push(0);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
valuesLength = valueArr.length;
|
|
441
|
+
} else {
|
|
442
|
+
// 单值模式:保持兼容旧逻辑
|
|
443
|
+
const len = valuesLength || (endReg - startReg + 1);
|
|
444
|
+
const valueRegHex = decimalToHex(values, len * 2);
|
|
445
|
+
for (let i = 0; i < len; i++) {
|
|
446
|
+
const hex = valueRegHex.slice(i * 2, i * 2 + 2);
|
|
447
|
+
valueArr.push(parseInt(hex, 16));
|
|
448
|
+
}
|
|
449
|
+
valuesLength = len;
|
|
450
|
+
}
|
|
431
451
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
452
|
+
const lengthHex = 4 + valuesLength;
|
|
453
|
+
const _command = ['0x23', lengthHex, ...startRegArr, ...endRegArr, ...valueArr];
|
|
454
|
+
const checks = generateCrcCheckSum(_command);
|
|
455
|
+
const command = ['0xDD', '0xA4', ..._command, ...checks, '0x77'];
|
|
456
|
+
return getData(
|
|
457
|
+
deviceId,
|
|
458
|
+
{
|
|
459
|
+
command,
|
|
460
|
+
commandVerifyHandler: (hexArr) => ({ verified: hexArr[0] == 0xdd && hexArr[1] == 0x23, pkgLen: hexArr[3] + 7 }),
|
|
461
|
+
pkgVerifyHandler: (pkg) => {
|
|
462
|
+
const len = pkg.length;
|
|
463
|
+
const [c1, c2] = generateCrcCheckSum(pkg.slice(2, len - 3));
|
|
464
|
+
const [_c1, _c2] = [pkg[len - 3], pkg[len - 2]];
|
|
465
|
+
return { verified: c1 == _c1 && c2 == _c2 };
|
|
466
|
+
},
|
|
445
467
|
},
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
468
|
+
'DDA423',
|
|
469
|
+
3000
|
|
470
|
+
);
|
|
471
|
+
} catch (e) {
|
|
472
|
+
console.warn('getDDA423Async build frame error', e);
|
|
473
|
+
return Promise.reject(e);
|
|
474
|
+
}
|
|
449
475
|
}
|
|
450
476
|
export const resolveDDA423 = (data) => {
|
|
451
477
|
if (!data) return null;
|
|
@@ -475,14 +501,14 @@ export const getDDA420Async = async (deviceId) => {
|
|
|
475
501
|
|
|
476
502
|
// 转 4 字节 (大端) 十六进制 -> 数组
|
|
477
503
|
const to4Bytes = (val) => {
|
|
478
|
-
const hex =
|
|
504
|
+
const hex = decimalToHex(val, 8); // 8位十六进制字符串
|
|
479
505
|
return [0, 2, 4, 6].map(i => parseInt(hex.slice(i, i + 2), 16));
|
|
480
506
|
};
|
|
481
507
|
const voltageArr = to4Bytes(outputVoltage);
|
|
482
508
|
const currentArr = to4Bytes(outputCurrent);
|
|
483
509
|
|
|
484
510
|
// 长度 = 1(模式) +4(电压)+4(电流) = 9
|
|
485
|
-
const lengthHex =
|
|
511
|
+
const lengthHex = decimalToHex(9, 2); // '09'
|
|
486
512
|
const _command = ['0x26', `0x${lengthHex}`, testMode & 0xFF, ...voltageArr, ...currentArr].map(o => typeof o === 'number' ? `0x${('00' + o.toString(16)).slice(-2)}` : o);
|
|
487
513
|
// 去掉 0x 前缀转成数值数组生成 CRC
|
|
488
514
|
const _commandNums = _command.map(h => parseInt(h, 16));
|
|
@@ -615,49 +641,31 @@ export const getDDA420Async = async (deviceId) => {
|
|
|
615
641
|
* @param {*} deviceId
|
|
616
642
|
* @returns
|
|
617
643
|
*/
|
|
618
|
-
export const getDDA429Async = async (deviceId, id) => {
|
|
619
|
-
//
|
|
644
|
+
export const getDDA429Async = async (deviceId, id = 1, seq = 0) => {
|
|
645
|
+
// ID 与 SEQ 均为 4 字节大端
|
|
620
646
|
const idHex = decimalToHex(id, 8);
|
|
621
|
-
|
|
647
|
+
const seqHex = decimalToHex(seq, 8);
|
|
622
648
|
const idArr = [
|
|
623
649
|
parseInt(idHex.slice(0, 2), 16),
|
|
624
650
|
parseInt(idHex.slice(2, 4), 16),
|
|
625
651
|
parseInt(idHex.slice(4, 6), 16),
|
|
626
|
-
parseInt(idHex.slice(4, 6), 16),
|
|
627
652
|
parseInt(idHex.slice(6, 8), 16)
|
|
628
653
|
];
|
|
629
|
-
const
|
|
654
|
+
const seqArr = [
|
|
655
|
+
parseInt(seqHex.slice(0, 2), 16),
|
|
656
|
+
parseInt(seqHex.slice(2, 4), 16),
|
|
657
|
+
parseInt(seqHex.slice(4, 6), 16),
|
|
658
|
+
parseInt(seqHex.slice(6, 8), 16)
|
|
659
|
+
];
|
|
660
|
+
// 长度 = 8 (ID+SEQ)
|
|
661
|
+
const _command = ['0x29', '0x08', ...idArr, ...seqArr];
|
|
630
662
|
const checks = generateCrcCheckSum(_command);
|
|
631
|
-
const command = ['
|
|
632
|
-
|
|
633
|
-
|
|
663
|
+
const command = ['0xDD', '0xA4', ..._command, ...checks, '0x77'];
|
|
634
664
|
return getData(
|
|
635
665
|
deviceId,
|
|
636
666
|
{
|
|
637
667
|
command,
|
|
638
|
-
commandVerifyHandler: (hexArr) => {
|
|
639
|
-
// 基本头部验证
|
|
640
|
-
if (hexArr[0] != 0xdd || hexArr[1] != 0x29) return { verified: false, pkgLen: null };
|
|
641
|
-
|
|
642
|
-
// 验证数据包长度是否合理
|
|
643
|
-
if (hexArr.length < 8) return { verified: false, pkgLen: null };
|
|
644
|
-
|
|
645
|
-
// 根据resolveDDA429的解析结构,ID在响应偏移32位置
|
|
646
|
-
// 实际协议中可能有所不同,需要根据协议规范调整
|
|
647
|
-
if (hexArr.length >= 36) {
|
|
648
|
-
// 从resolveDDA429可知,ID位于n=32处的4个字节
|
|
649
|
-
const respIdHex = decimalToHex(hexArr[32]) +
|
|
650
|
-
decimalToHex(hexArr[33]) +
|
|
651
|
-
decimalToHex(hexArr[34]) +
|
|
652
|
-
decimalToHex(hexArr[35]);
|
|
653
|
-
const respId = hexToDecimal(respIdHex);
|
|
654
|
-
|
|
655
|
-
// 如果ID不匹配,则拒绝该数据包
|
|
656
|
-
if (respId !== id) return { verified: false, pkgLen: null };
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
return { verified: true, pkgLen: hexArr[3] + 7 };
|
|
660
|
-
},
|
|
668
|
+
commandVerifyHandler: (hexArr) => ({ verified: hexArr[0] == 0xdd && hexArr[1] == 0x29, pkgLen: hexArr[3] + 7 }),
|
|
661
669
|
pkgVerifyHandler: (pkg) => {
|
|
662
670
|
const len = pkg.length;
|
|
663
671
|
const [c1, c2] = generateCrcCheckSum(pkg.slice(2, len - 3));
|
|
@@ -665,161 +673,80 @@ export const getDDA420Async = async (deviceId) => {
|
|
|
665
673
|
return { verified: c1 == _c1 && c2 == _c2 };
|
|
666
674
|
},
|
|
667
675
|
},
|
|
668
|
-
'DDA429'
|
|
676
|
+
'DDA429',
|
|
677
|
+
3000
|
|
669
678
|
);
|
|
670
679
|
}
|
|
671
680
|
export const resolveDDA429 = (data) => {
|
|
672
681
|
if (!data) return null;
|
|
682
|
+
const status = data[2];
|
|
683
|
+
const content = data.slice(4, data.length - 3);
|
|
673
684
|
const dataStr = hexArr2string(data);
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
const
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
const chargeTime = hexToDecimal(
|
|
720
|
-
decimalToHex(data[n++]) +
|
|
721
|
-
decimalToHex(data[n++])
|
|
722
|
-
);
|
|
723
|
-
|
|
724
|
-
// 9. 充电剩余时间(2字节)
|
|
725
|
-
const chargeRemainTime = hexToDecimal(
|
|
726
|
-
decimalToHex(data[n++]) +
|
|
727
|
-
decimalToHex(data[n++])
|
|
728
|
-
);
|
|
729
|
-
|
|
730
|
-
// 10. 充电完成时长(2字节)
|
|
731
|
-
const chargeCompleteTime = hexToDecimal(
|
|
732
|
-
decimalToHex(data[n++]) +
|
|
733
|
-
decimalToHex(data[n++])
|
|
734
|
-
);
|
|
735
|
-
|
|
736
|
-
// 输出电压占空比
|
|
737
|
-
const outputVoltagePercent = data[n++];
|
|
738
|
-
|
|
739
|
-
// 输出电流占空比
|
|
740
|
-
const outputElectricityPercent = data[n++];
|
|
741
|
-
|
|
742
|
-
// id(4字节)
|
|
743
|
-
const id1 = hexToDecimal(
|
|
744
|
-
decimalToHex(data[n++]) +
|
|
745
|
-
decimalToHex(data[n++]) +
|
|
746
|
-
decimalToHex(data[n++]) +
|
|
747
|
-
decimalToHex(data[n++])
|
|
748
|
-
);
|
|
749
|
-
|
|
750
|
-
// 检测输出电流(4字节)
|
|
751
|
-
const outputElectricity = hexToDecimal(
|
|
752
|
-
decimalToHex(data[n++]) +
|
|
753
|
-
decimalToHex(data[n++]) +
|
|
754
|
-
decimalToHex(data[n++]) +
|
|
755
|
-
decimalToHex(data[n++])
|
|
756
|
-
);
|
|
757
|
-
|
|
758
|
-
// 检测电池电压(4字节)
|
|
759
|
-
const id = hexToDecimal(
|
|
760
|
-
decimalToHex(data[n++]) +
|
|
761
|
-
decimalToHex(data[n++]) +
|
|
762
|
-
decimalToHex(data[n++]) +
|
|
763
|
-
decimalToHex(data[n++])
|
|
764
|
-
);
|
|
765
|
-
|
|
766
|
-
// 开关状态(2字节)
|
|
767
|
-
const switchStatus = hexToDecimal(
|
|
768
|
-
decimalToHex(data[n++]) +
|
|
769
|
-
decimalToHex(data[n++])
|
|
770
|
-
);
|
|
771
|
-
const swtichStatusArr = switchStatus.toString(2).padStart(16, '0').split('').reverse();
|
|
772
|
-
const chargeSwitch = swtichStatusArr[0] == '1';
|
|
773
|
-
const preChargeSwitch = swtichStatusArr[1] == '1';
|
|
774
|
-
const fanSwitch = swtichStatusArr[2] == '1';
|
|
775
|
-
|
|
776
|
-
// 保护状态(4字节)
|
|
777
|
-
const protectStatus = hexToDecimal(
|
|
778
|
-
decimalToHex(data[n++]) +
|
|
779
|
-
decimalToHex(data[n++]) +
|
|
780
|
-
decimalToHex(data[n++]) +
|
|
781
|
-
decimalToHex(data[n++])
|
|
782
|
-
);
|
|
783
|
-
const protectStatusArr = protectStatus.toString(2).padStart(32, '0').split('').reverse();
|
|
784
|
-
let protectStatusIndexs = [];
|
|
785
|
-
for (let i = 0; i < protectStatusArr.length; i++) {
|
|
786
|
-
if (protectStatusArr[i] == '1') protectStatusIndexs.push(i);
|
|
685
|
+
if (content.length < 8) return { dataStr, status, error: 'content too short' };
|
|
686
|
+
|
|
687
|
+
const id = hexToDecimal(decimalToHex(content[0]) + decimalToHex(content[1]) + decimalToHex(content[2]) + decimalToHex(content[3]));
|
|
688
|
+
const seq = hexToDecimal(decimalToHex(content[4]) + decimalToHex(content[5]) + decimalToHex(content[6]) + decimalToHex(content[7]));
|
|
689
|
+
const body = content.slice(8);
|
|
690
|
+
|
|
691
|
+
// ID=1 解析:末尾 57 字节固定含义,其余视作工作信息原始区
|
|
692
|
+
if (id === 1) {
|
|
693
|
+
const peerTailLen = 57;
|
|
694
|
+
let workInfo = [];
|
|
695
|
+
let peerSection = [];
|
|
696
|
+
if (body.length >= peerTailLen) {
|
|
697
|
+
workInfo = body.slice(0, body.length - peerTailLen);
|
|
698
|
+
peerSection = body.slice(body.length - peerTailLen);
|
|
699
|
+
} else {
|
|
700
|
+
peerSection = body.slice();
|
|
701
|
+
}
|
|
702
|
+
const workinfoField = {
|
|
703
|
+
state: workInfo[0] || 0,
|
|
704
|
+
ipwm: hexToDecimal(decimalToHex(workInfo[24]) + decimalToHex(workInfo[25]) + decimalToHex(workInfo[26]) + decimalToHex(workInfo[27])),
|
|
705
|
+
vpwm: hexToDecimal(decimalToHex(workInfo[28]) + decimalToHex(workInfo[29]) + decimalToHex(workInfo[30]) + decimalToHex(workInfo[31])),
|
|
706
|
+
vout: hexToDecimal(decimalToHex(workInfo[32]) + decimalToHex(workInfo[33]) + decimalToHex(workInfo[34]) + decimalToHex(workInfo[35])),
|
|
707
|
+
iout: hexToDecimal(decimalToHex(workInfo[36]) + decimalToHex(workInfo[37]) + decimalToHex(workInfo[38]) + decimalToHex(workInfo[39])),
|
|
708
|
+
vbat: hexToDecimal(decimalToHex(workInfo[40]) + decimalToHex(workInfo[41]) + decimalToHex(workInfo[42]) + decimalToHex(workInfo[43])),
|
|
709
|
+
port: String(workInfo[54]) || 0
|
|
710
|
+
};
|
|
711
|
+
const fieldNames = ['peer_curr', 'peer_num', 'ble_state', 'ble_init', 'ble_release', 'release_cnt', 'scan_start', 'scan_end', 'scan_res', 'scan_resp', 'conn_start', 'conn_resp', 'conn_end', 'conn_idx', 'conn_res', 'cmp_resp', 'handshake_resp', 'qr_chg', 'blacklist', 'ext_resp', 'ext_num'];
|
|
712
|
+
const peerMap = {};
|
|
713
|
+
peerSection.slice(0, 21).forEach((b, i) => { peerMap[fieldNames[i] || (`extra_${i}`)] = b; });
|
|
714
|
+
|
|
715
|
+
const extraBytes = peerSection.slice(21);
|
|
716
|
+
let i = 0;
|
|
717
|
+
let j = 0;
|
|
718
|
+
const fieldNamesExtra = ['run_ts', 'scan_ts', 'scan_end_ts', 'get_ts', 'get_end_ts', 'conn_ts', 'conn_end_ts', 'cmp_ts', 'cmp_end_ts'];
|
|
719
|
+
while (i < extraBytes.length) {
|
|
720
|
+
peerMap[fieldNamesExtra[j] || (`extra_${j}`)] = hexToDecimal(
|
|
721
|
+
decimalToHex(extraBytes[i] || 0) +
|
|
722
|
+
decimalToHex(extraBytes[i + 1] || 0) +
|
|
723
|
+
decimalToHex(extraBytes[i + 2] || 0) +
|
|
724
|
+
decimalToHex(extraBytes[i + 3] || 0)
|
|
725
|
+
);
|
|
726
|
+
i += 4;
|
|
727
|
+
j += 1;
|
|
728
|
+
}
|
|
729
|
+
return { dataStr, status, id, seq, type: 'ID1', ...workinfoField, ...peerMap };
|
|
787
730
|
}
|
|
788
731
|
|
|
789
|
-
//
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
732
|
+
// ID=2/3 解析:按 16 字节一组 peer_info
|
|
733
|
+
if (id === 2 || id === 3) {
|
|
734
|
+
const peerInfos = [];
|
|
735
|
+
for (let i = 0; i + 16 <= body.length; i += 16) {
|
|
736
|
+
const seg = body.slice(i, i + 16);
|
|
737
|
+
const macBytes = seg.slice(0, 6).map(b => decimalToHex(b));
|
|
738
|
+
const mac = macBytes.map(m => m.toUpperCase()).join(':');
|
|
739
|
+
const state = seg[6] === 255 ? -1 : seg[6];
|
|
740
|
+
const cmp = seg[7] === 255 ? -1 : seg[7];
|
|
741
|
+
const voltage = hexToDecimal(decimalToHex(seg[8]) + decimalToHex(seg[9]));
|
|
742
|
+
const b_voltage = hexToDecimal(decimalToHex(seg[10]) + decimalToHex(seg[11]));
|
|
743
|
+
const current = hexToDecimal(decimalToHex(seg[12]) + decimalToHex(seg[13]));
|
|
744
|
+
const b_current = hexToDecimal(decimalToHex(seg[14]) + decimalToHex(seg[15]));
|
|
745
|
+
peerInfos.push({ mac, state, cmp, voltage, b_voltage, current, b_current });
|
|
746
|
+
}
|
|
747
|
+
return { dataStr, status, id, seq, type: `ID${id}`, peerInfos: peerInfos.map((item, i) => ({ id: i, ...item })) };
|
|
800
748
|
}
|
|
801
749
|
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
workStatus,
|
|
805
|
-
chargeStatus,
|
|
806
|
-
chargeVoltage,
|
|
807
|
-
chargeElectricity,
|
|
808
|
-
chargePower,
|
|
809
|
-
chargeTemperature,
|
|
810
|
-
chargeCapacity,
|
|
811
|
-
chargeTime,
|
|
812
|
-
chargeRemainTime,
|
|
813
|
-
chargeCompleteTime,
|
|
814
|
-
outputVoltagePercent,
|
|
815
|
-
outputElectricityPercent,
|
|
816
|
-
id,
|
|
817
|
-
outputElectricity,
|
|
818
|
-
id1,
|
|
819
|
-
chargeSwitch,
|
|
820
|
-
preChargeSwitch,
|
|
821
|
-
fanSwitch,
|
|
822
|
-
protectStatusIndexs,
|
|
823
|
-
alarmStatusIndexs,
|
|
824
|
-
};
|
|
750
|
+
// 未知 ID
|
|
751
|
+
return { dataStr, status, id, seq, raw: body };
|
|
825
752
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { decimalToHex, decimalToTwoByteHexArray } from '../BleDataProcess.js';
|
|
1
|
+
import { decimalToHex, decimalToTwoByteHexArray, sleep, decimalToHex0x } from '../BleDataProcess.js';
|
|
2
2
|
import { enterFactory, existFactory, getDD5AE1Async, getDD5AFBAsync, getDDA5FAAsync, getDDA5OldAsync, readExistFactory, resolveBaseDD, setDDA5FAAsync, setDDA5OldAsync } from './BleCmdDD.js';
|
|
3
3
|
|
|
4
4
|
// #region 发指令读取参数
|
|
@@ -20,7 +20,7 @@ export const readParamCmd = (chipType, deviceId, paramInfo, needFactoryMode = tr
|
|
|
20
20
|
/** 地址转换为16进制 */
|
|
21
21
|
const hexAddress = decimalToTwoByteHexArray(address);
|
|
22
22
|
// 长度转换为16进制
|
|
23
|
-
const hexLength =
|
|
23
|
+
const hexLength = decimalToHex0x(length);
|
|
24
24
|
const handleRead = async (readFunction, index) => {
|
|
25
25
|
try {
|
|
26
26
|
const hex = await readFunction();
|
|
@@ -68,7 +68,7 @@ export const readParamCmd = (chipType, deviceId, paramInfo, needFactoryMode = tr
|
|
|
68
68
|
*/
|
|
69
69
|
export const getSysParamCmd = async (chipType, deviceId, paramInfo, needFactoryMode = true) => {
|
|
70
70
|
try {
|
|
71
|
-
console.
|
|
71
|
+
console.warn('paramInfo:参数信息', paramInfo);
|
|
72
72
|
const paramObj = {
|
|
73
73
|
address: paramInfo.paramNo,
|
|
74
74
|
length: paramInfo.paramLength / 2,
|
|
@@ -104,7 +104,7 @@ export const getSysParamCmd = async (chipType, deviceId, paramInfo, needFactoryM
|
|
|
104
104
|
* @param {*} paramInfoArray[].divisor 缩放系数(十进制)如 10
|
|
105
105
|
* @param {*} paramInfoArray[].isTemp 是否是温度参数
|
|
106
106
|
*/
|
|
107
|
-
export const setSysParamCmd = async (chipType, deviceId, paramInfoArray
|
|
107
|
+
export const setSysParamCmd = async (chipType, deviceId, paramInfoArray) => {
|
|
108
108
|
try {
|
|
109
109
|
console.log('paramInfoArray: ', paramInfoArray);
|
|
110
110
|
|
|
@@ -118,15 +118,13 @@ export const setSysParamCmd = async (chipType, deviceId, paramInfoArray, macAddr
|
|
|
118
118
|
// 根据系数处理参数值
|
|
119
119
|
let value = Number(paramInfo?.paramValue || paramInfo.newValue) / Number(paramInfo?.divisor || 1);
|
|
120
120
|
console.log('value: ', value);
|
|
121
|
-
if (paramInfo.isTemp
|
|
121
|
+
if (paramInfo.isTemp) {
|
|
122
122
|
value = 2731 + (Number(paramInfo.newValue) * 10)
|
|
123
123
|
}
|
|
124
124
|
value = Math.round(value);
|
|
125
125
|
let valuesHexs = [];
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
console.log('valuesHexs: ', valuesHexs);
|
|
129
|
-
}
|
|
126
|
+
valuesHexs = decimalToTwoByteHexArray(value);
|
|
127
|
+
console.log('valuesHexs: ', valuesHexs);
|
|
130
128
|
|
|
131
129
|
// 将处理后的参数对象添加到数组中
|
|
132
130
|
paramObjs.push({
|
|
@@ -319,17 +317,17 @@ export const setCapacityParamCmd = (chipType, deviceId, paramInfo) => {
|
|
|
319
317
|
const { values } = paramInfo;
|
|
320
318
|
// 循环 循环是标称的80%
|
|
321
319
|
const circular = {
|
|
322
|
-
|
|
323
|
-
|
|
320
|
+
paramNo: 1,
|
|
321
|
+
oldParamNo: "0x11",
|
|
324
322
|
values: values * 0.8,
|
|
325
|
-
|
|
323
|
+
paramLength: 2,
|
|
326
324
|
};
|
|
327
325
|
// 满充 满充等于标称 旧协议没有满充
|
|
328
326
|
const full = {
|
|
329
|
-
|
|
330
|
-
|
|
327
|
+
paramNo: 112,
|
|
328
|
+
oldParamNo: "0x10",
|
|
331
329
|
values: values,
|
|
332
|
-
|
|
330
|
+
paramLength: 2,
|
|
333
331
|
};
|
|
334
332
|
|
|
335
333
|
const handleSet = async (setFunction, index) => {
|
|
@@ -361,12 +359,42 @@ export const setCapacityParamCmd = (chipType, deviceId, paramInfo) => {
|
|
|
361
359
|
return await handleSet(() => setDDA5OldAsync(deviceId, oldParamNo, hexValues), 4);
|
|
362
360
|
}
|
|
363
361
|
};
|
|
362
|
+
|
|
363
|
+
// 写入容量参数的高低位(标称容量、满充容量)
|
|
364
|
+
const handleCapacityParam = async (paramObj, lowParamNo, highParamNo) => {
|
|
365
|
+
const value = Math.round(Number(paramObj.values) / paramInfo.divisor);
|
|
366
|
+
console.log(`容量参数value (paramNo ${lowParamNo}):`, value);
|
|
367
|
+
|
|
368
|
+
// 拆分成高低16位
|
|
369
|
+
const lowValue = value & 0xFFFF; // 低16位
|
|
370
|
+
const highValue = (value >> 16) & 0xFFFF; // 高16位
|
|
371
|
+
|
|
372
|
+
// 写入低16位
|
|
373
|
+
const lowBytes = decimalToTwoByteHexArray(lowValue);
|
|
374
|
+
const lowAddress = decimalToTwoByteHexArray(lowParamNo);
|
|
375
|
+
const hexLength = decimalToHex(1);
|
|
376
|
+
const lowHex = await handleSet(async () => await setDDA5FAAsync(deviceId, lowAddress, hexLength, lowBytes), 7);
|
|
377
|
+
|
|
378
|
+
// 写入高16位
|
|
379
|
+
const highBytes = decimalToTwoByteHexArray(highValue);
|
|
380
|
+
const highAddress = decimalToTwoByteHexArray(highParamNo);
|
|
381
|
+
const highHex = await handleSet(async () => await setDDA5FAAsync(deviceId, highAddress, hexLength, highBytes), 7);
|
|
382
|
+
|
|
383
|
+
return lowHex && highHex;
|
|
384
|
+
};
|
|
364
385
|
try {
|
|
365
386
|
await enterFactory(deviceId);
|
|
366
|
-
|
|
387
|
+
let paramInfoHex;
|
|
388
|
+
if (chipType) {
|
|
389
|
+
// 新协议:标称容量需要写入两个参数(paramNo 0和155)
|
|
390
|
+
paramInfoHex = await handleCapacityParam(paramInfo, 0, 155);
|
|
391
|
+
} else {
|
|
392
|
+
// 旧协议:直接写入
|
|
393
|
+
paramInfoHex = await handleParam(paramInfo);
|
|
394
|
+
}
|
|
367
395
|
const circularHex = await handleParam(circular);
|
|
368
396
|
if (chipType) {
|
|
369
|
-
const fullHex = await
|
|
397
|
+
const fullHex = await handleCapacityParam(full, 112, 156);
|
|
370
398
|
}
|
|
371
399
|
await existFactory(deviceId);
|
|
372
400
|
if (paramInfoHex) {
|
|
@@ -312,22 +312,26 @@
|
|
|
312
312
|
return new Promise((r) => setTimeout(() => r(true), n));
|
|
313
313
|
}
|
|
314
314
|
/**
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
315
|
+
* 将一个十进制数转换为两个字节的十六进制数组。
|
|
316
|
+
* @param {number} decimal - 要转换的十进制数。
|
|
317
|
+
* @returns {Array<string>} 包含两个十六进制字符串的数组,每个字符串表示一个字节。
|
|
318
|
+
*/
|
|
319
319
|
export const decimalToTwoByteHexArray = (decimal) => {
|
|
320
|
-
//
|
|
321
|
-
if (
|
|
322
|
-
throw new Error('
|
|
320
|
+
// 范围检查(支持有符号和无符号)
|
|
321
|
+
if (decimal < -32768 || decimal > 65535) {
|
|
322
|
+
throw new Error('输入超出范围(-32768 到 65535)');
|
|
323
323
|
}
|
|
324
|
-
|
|
325
|
-
|
|
324
|
+
|
|
325
|
+
// 负数转为无符号 16 位表示
|
|
326
|
+
if (decimal < 0) {
|
|
327
|
+
decimal = decimal & 0xFFFF;
|
|
326
328
|
}
|
|
327
|
-
|
|
328
|
-
let
|
|
329
|
+
|
|
330
|
+
let f = `00${(decimal >> 8).toString(16).toUpperCase()}`.slice(-2);
|
|
331
|
+
let e = `00${(decimal & 0xff).toString(16).toUpperCase()}`.slice(-2);
|
|
329
332
|
return [`0x${f}`, `0x${e}`];
|
|
330
333
|
}
|
|
334
|
+
|
|
331
335
|
|
|
332
336
|
/**
|
|
333
337
|
* 将十六进制字符串转换为两个十六进制的数组
|