node-red-zelecproto 0.1.0 → 0.1.2
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/645.js +25 -100
- package/698.js +89 -129
- package/package.json +1 -1
- package/AGENTS.md +0 -133
package/645.js
CHANGED
|
@@ -433,6 +433,20 @@ function decode645(_msg) {
|
|
|
433
433
|
return arr.includes(di)
|
|
434
434
|
}
|
|
435
435
|
|
|
436
|
+
function expandBits16(v) {
|
|
437
|
+
const bits = {};
|
|
438
|
+
for (let i = 0; i < 16; i++) {
|
|
439
|
+
bits[`bit${i}`] = (v >> i) & 0x1;
|
|
440
|
+
}
|
|
441
|
+
return bits;
|
|
442
|
+
}
|
|
443
|
+
function expandBits32(v) {
|
|
444
|
+
const bits = {};
|
|
445
|
+
for (let i = 0; i < 32; i++) {
|
|
446
|
+
bits[`bit${i}`] = (v >>> i) & 0x1;
|
|
447
|
+
}
|
|
448
|
+
return bits;
|
|
449
|
+
}
|
|
436
450
|
|
|
437
451
|
// —— 常用分支(保留你的原分支,增加越界保护)——
|
|
438
452
|
function buildDays(prefix) {
|
|
@@ -520,66 +534,22 @@ function decode645(_msg) {
|
|
|
520
534
|
rawValue: w1,
|
|
521
535
|
rawBlockHex: statusBlockHex,
|
|
522
536
|
binary: bin16(w1),
|
|
523
|
-
|
|
524
|
-
'停电抄表电池欠压': !!bit(w1, 3),
|
|
525
|
-
'时钟电池欠压': !!bit(w1, 2),
|
|
526
|
-
'有功功率方向反向': !!bit(w1, 4),
|
|
527
|
-
'无功功率方向反向': !!bit(w1, 5),
|
|
528
|
-
'控制回路错误': !!bit(w1, 8),
|
|
529
|
-
'ESAM错误': !!bit(w1, 9),
|
|
530
|
-
'内部程序错误': !!bit(w1, 12),
|
|
531
|
-
'存储器故障或损坏': !!bit(w1, 13),
|
|
532
|
-
'透支状态': !!bit(w1, 14),
|
|
533
|
-
'时钟故障': !!bit(w1, 15)
|
|
534
|
-
}
|
|
537
|
+
bits: expandBits16(w1)
|
|
535
538
|
};
|
|
536
539
|
|
|
537
|
-
//
|
|
540
|
+
// 状态字2/3只保留原始数值和binary,具体位义由上层业务或文档表解释。
|
|
538
541
|
const w2 = readU16LE();
|
|
539
|
-
const dir = (b) => (b ? '反向' : '正向');
|
|
540
542
|
const word2 = (w2 === null) ? null : {
|
|
541
543
|
rawValue: w2,
|
|
542
544
|
binary: bin16(w2),
|
|
543
|
-
|
|
544
|
-
'A相有功功率方向': dir(bit(w2, 0)),
|
|
545
|
-
'B相有功功率方向': dir(bit(w2, 1)),
|
|
546
|
-
'C相有功功率方向': dir(bit(w2, 2)),
|
|
547
|
-
'A相无功功率方向': dir(bit(w2, 4)),
|
|
548
|
-
'B相无功功率方向': dir(bit(w2, 5)),
|
|
549
|
-
'C相无功功率方向': dir(bit(w2, 6))
|
|
550
|
-
}
|
|
545
|
+
bits: expandBits16(w2)
|
|
551
546
|
};
|
|
552
547
|
|
|
553
|
-
// —— 状态字3(与你现有的 04000503 保持一致)——
|
|
554
548
|
const w3 = readU16LE();
|
|
555
|
-
const supplyBits = (w3 === null) ? 0 : ((w3 >> 1) & 0b11);
|
|
556
|
-
const supplyMode = (
|
|
557
|
-
supplyBits === 0 ? '主电源' :
|
|
558
|
-
supplyBits === 1 ? '辅助电源' :
|
|
559
|
-
supplyBits === 2 ? '电池供电' : '保留'
|
|
560
|
-
);
|
|
561
|
-
const meterTypeBits = (w3 === null) ? 0 : ((w3 >> 8) & 0b11);
|
|
562
|
-
const meterType = (
|
|
563
|
-
meterTypeBits === 0 ? '非预付费表' :
|
|
564
|
-
meterTypeBits === 1 ? '电量型预付费表' :
|
|
565
|
-
meterTypeBits === 2 ? '电费型预付费表' : '保留'
|
|
566
|
-
);
|
|
567
549
|
const word3 = (w3 === null) ? null : {
|
|
568
550
|
rawValue: w3,
|
|
569
551
|
binary: bin16(w3),
|
|
570
|
-
|
|
571
|
-
当前运行时段套数: (bit(w3, 0) ? '第二套' : '第一套'),
|
|
572
|
-
供电方式: supplyMode,
|
|
573
|
-
编程允许状态: (bit(w3, 3) ? '有效' : '失效'),
|
|
574
|
-
继电器状态: (bit(w3, 4) ? '断' : '通'),
|
|
575
|
-
当前运行时区套数: (bit(w3, 5) ? '第二套' : '第一套'),
|
|
576
|
-
继电器命令状态: (bit(w3, 6) ? '断' : '通'),
|
|
577
|
-
预跳闸报警状态: (bit(w3, 7) ? '有' : '无'),
|
|
578
|
-
电能表类型: meterType,
|
|
579
|
-
当前运行分时费率套数: (bit(w3, 10) ? '第二套' : '第一套'),
|
|
580
|
-
当前阶梯套数: (bit(w3, 11) ? '第二套' : '第一套'),
|
|
581
|
-
保电状态: (bit(w3, 12) ? '保电' : '非保电')
|
|
582
|
-
}
|
|
552
|
+
bits: expandBits16(w3)
|
|
583
553
|
};
|
|
584
554
|
|
|
585
555
|
// —— 密钥状态字(与你现有的 04000508 保持一致,32位)——
|
|
@@ -588,6 +558,7 @@ function decode645(_msg) {
|
|
|
588
558
|
rawValue: w8,
|
|
589
559
|
hexValue: w8.toString(16).toUpperCase().padStart(8, '0'),
|
|
590
560
|
binary: bin32(w8),
|
|
561
|
+
bits: expandBits32(w8),
|
|
591
562
|
keys: {
|
|
592
563
|
'主控密钥有效': !!bit(w8, 0),
|
|
593
564
|
'身份认证密钥有效': !!bit(w8, 1),
|
|
@@ -616,78 +587,31 @@ function decode645(_msg) {
|
|
|
616
587
|
|
|
617
588
|
value = {
|
|
618
589
|
rawValue: v, binary: bin,
|
|
619
|
-
|
|
620
|
-
'停电抄表电池欠压': !!(v & (1 << 3)), // bit3: 0=正常, 1=欠压
|
|
621
|
-
'时钟电池欠压': !!(v & (1 << 2)), // bit2: 0=正常, 1=欠压
|
|
622
|
-
'有功功率方向反向': !!(v & (1 << 4)), // bit4: 0=正向, 1=反向
|
|
623
|
-
'无功功率方向反向': !!(v & (1 << 5)), // bit5: 0=正向, 1=反向
|
|
624
|
-
'控制回路错误': !!(v & (1 << 8)), // bit8: 0=正常, 1=错误
|
|
625
|
-
'ESAM错误': !!(v & (1 << 9)), // bit9: 0=正常, 1=错误
|
|
626
|
-
'内部程序错误': !!(v & (1 << 12)), // bit12: 0=正常, 1=错误
|
|
627
|
-
'存储器故障或损坏': !!(v & (1 << 13)), // bit13: 0=正常, 1=故障
|
|
628
|
-
'透支状态': !!(v & (1 << 14)), // bit14: 0=正常, 1=透支
|
|
629
|
-
'时钟故障': !!(v & (1 << 15)) // bit15: 0=正常, 1=故障
|
|
630
|
-
}
|
|
590
|
+
bits: expandBits16(v)
|
|
631
591
|
};
|
|
632
592
|
} else if (di === '04000502' && arrPush.length >= 6) {
|
|
633
|
-
//
|
|
593
|
+
// 状态字2只返回原始数值,binary为bit15..bit0。
|
|
634
594
|
const lo = arrPush[4] & 0xFF;
|
|
635
595
|
const hi = arrPush[5] & 0xFF;
|
|
636
596
|
const v = (hi << 8) | lo;
|
|
637
597
|
const bin = v.toString(2).padStart(16, '0');
|
|
638
598
|
|
|
639
|
-
const bit = (n) => ((v >> n) & 0x1);
|
|
640
|
-
const dir = (b) => (b ? '反向' : '正向');
|
|
641
|
-
|
|
642
599
|
value = {
|
|
643
600
|
rawValue: v,
|
|
644
601
|
binary: bin,
|
|
645
|
-
|
|
646
|
-
'A相有功功率方向': dir(bit(0)),
|
|
647
|
-
'B相有功功率方向': dir(bit(1)),
|
|
648
|
-
'C相有功功率方向': dir(bit(2)),
|
|
649
|
-
'A相无功功率方向': dir(bit(4)),
|
|
650
|
-
'B相无功功率方向': dir(bit(5)),
|
|
651
|
-
'C相无功功率方向': dir(bit(6))
|
|
652
|
-
}
|
|
602
|
+
bits: expandBits16(v)
|
|
653
603
|
};
|
|
654
604
|
} else if (di === '04000503' && arrPush.length >= 6) {
|
|
655
|
-
//
|
|
605
|
+
// 状态字3只返回原始数值,binary为bit15..bit0。
|
|
656
606
|
const lo = arrPush[4] & 0xFF;
|
|
657
607
|
const hi = arrPush[5] & 0xFF;
|
|
658
608
|
const v = (hi << 8) | lo;
|
|
659
609
|
const bin = v.toString(2).padStart(16, '0');
|
|
660
610
|
|
|
661
|
-
const supplyBits = (v >> 1) & 0b11; // bit2-bit1
|
|
662
|
-
const supplyMode = (
|
|
663
|
-
supplyBits === 0 ? '主电源' :
|
|
664
|
-
supplyBits === 1 ? '辅助电源' :
|
|
665
|
-
supplyBits === 2 ? '电池供电' : '保留'
|
|
666
|
-
);
|
|
667
|
-
|
|
668
|
-
const meterTypeBits = (v >> 8) & 0b11; // bit9-bit8
|
|
669
|
-
const meterType = (
|
|
670
|
-
meterTypeBits === 0 ? '非预付费表' :
|
|
671
|
-
meterTypeBits === 1 ? '电量型预付费表' :
|
|
672
|
-
meterTypeBits === 2 ? '电费型预付费表' : '保留'
|
|
673
|
-
);
|
|
674
|
-
|
|
675
611
|
value = {
|
|
676
612
|
rawValue: v,
|
|
677
613
|
binary: bin,
|
|
678
|
-
|
|
679
|
-
当前运行时段套数: (v & 0x1) ? '第二套' : '第一套', // bit0
|
|
680
|
-
供电方式: supplyMode, // bit2-bit1
|
|
681
|
-
编程允许状态: (v & (1 << 3)) ? '有效' : '失效', // bit3
|
|
682
|
-
继电器状态: (v & (1 << 4)) ? '断' : '通', // bit4(线路实际工作状态)
|
|
683
|
-
当前运行时区套数: (v & (1 << 5)) ? '第二套' : '第一套', // bit5
|
|
684
|
-
继电器命令状态: (v & (1 << 6)) ? '断' : '通', // bit6(远程拉闸命令)
|
|
685
|
-
预跳闸报警状态: (v & (1 << 7)) ? '有' : '无', // bit7
|
|
686
|
-
电能表类型: meterType, // bit9-bit8
|
|
687
|
-
当前运行分时费率套数: (v & (1 << 10)) ? '第二套' : '第一套', // bit10
|
|
688
|
-
当前阶梯套数: (v & (1 << 11)) ? '第二套' : '第一套', // bit11
|
|
689
|
-
保电状态: (v & (1 << 12)) ? '保电' : '非保电' // bit12
|
|
690
|
-
}
|
|
614
|
+
bits: expandBits16(v)
|
|
691
615
|
};
|
|
692
616
|
}
|
|
693
617
|
// else if (['0000FF00', '0001FF00', '0002FF00'].includes(di) && arrPush.length > 4) {
|
|
@@ -702,6 +626,7 @@ function decode645(_msg) {
|
|
|
702
626
|
rawValue: v,
|
|
703
627
|
hexValue: v.toString(16).toUpperCase().padStart(8, '0'),
|
|
704
628
|
binary: v.toString(2).padStart(32, '0'),
|
|
629
|
+
bits: expandBits32(v),
|
|
705
630
|
keys: {
|
|
706
631
|
'主控密钥有效': !!(v & (1 << 0)), '身份认证密钥有效': !!(v & (1 << 1)), '密钥协商密钥有效': !!(v & (1 << 2)),
|
|
707
632
|
'密钥更新密钥有效': !!(v & (1 << 3)), '传输密钥有效': !!(v & (1 << 4)), '保护密钥有效': !!(v & (1 << 5)),
|
package/698.js
CHANGED
|
@@ -870,182 +870,142 @@ function parseStatusWordFromAxdrBitString(buffer) {
|
|
|
870
870
|
const bytes = buffer.slice(start, end);
|
|
871
871
|
if (bytes.length < 2) return null;
|
|
872
872
|
const bits = [];
|
|
873
|
+
// A-XDR bit-string按每字节高位到低位展开。
|
|
873
874
|
for (const byte of bytes.slice(0, 2)) {
|
|
874
875
|
for (let bit = 7; bit >= 0; bit--) {
|
|
875
876
|
bits.push((byte >> bit) & 0x01);
|
|
876
877
|
}
|
|
877
878
|
}
|
|
878
879
|
let statusWord = 0;
|
|
880
|
+
// 文档表F.x中的bit编号按展开后的bit-string下标映射,后续binary再按645一致的数值形式输出。
|
|
879
881
|
for (let i = 0; i < 16; i++) {
|
|
880
882
|
statusWord |= bits[i] << i;
|
|
881
883
|
}
|
|
882
884
|
return {
|
|
883
885
|
statusWord,
|
|
884
|
-
binary: bits.slice(0, 16).join('')
|
|
886
|
+
binary: bits.slice(0, 16).join(''),
|
|
887
|
+
consumed: end
|
|
885
888
|
};
|
|
886
889
|
}
|
|
887
890
|
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
const result = createStandardResult("电表状态", oad, dataBuffer);
|
|
896
|
-
try {
|
|
897
|
-
// 1. 读取原始状态字(小端)
|
|
898
|
-
const statusWordLe = parseMeterStatusOptimized(dataBuffer);
|
|
899
|
-
|
|
900
|
-
// 2. 转换为大端,用于大端逻辑判断
|
|
901
|
-
const statusWordBe = toBigEndian16(statusWordLe);
|
|
902
|
-
|
|
903
|
-
if (statusWordBe !== null) {
|
|
904
|
-
const valTmp = statusWordBe & 0xFFFF;
|
|
905
|
-
const bin = valTmp.toString(2).padStart(16, '0'); // 最高位bit15在最左
|
|
891
|
+
function expandBits16(v) {
|
|
892
|
+
const bits = {};
|
|
893
|
+
for (let i = 0; i < 16; i++) {
|
|
894
|
+
bits[`bit${i}`] = (v >> i) & 0x1;
|
|
895
|
+
}
|
|
896
|
+
return bits;
|
|
897
|
+
}
|
|
906
898
|
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
'bit9 ESAM错误': bin[6] === '1',
|
|
919
|
-
'bit8 控制回路错误': bin[7] === '1',
|
|
920
|
-
'bit7 保留': bin[8] === '1',
|
|
921
|
-
'bit6 保留': bin[9] === '1',
|
|
922
|
-
'bit5 无功功率方向反向': bin[10] === '1',
|
|
923
|
-
'bit4 有功功率方向反向': bin[11] === '1',
|
|
924
|
-
'bit3 停电抄表电池欠压': bin[12] === '1',
|
|
925
|
-
'bit2 时钟电池欠压': bin[13] === '1',
|
|
926
|
-
'bit1 需量积算方式': bin[14] === '1',
|
|
927
|
-
'bit0 保留': bin[15] === '1'
|
|
928
|
-
}
|
|
929
|
-
};
|
|
899
|
+
function format698StatusWord(statusWord, bitStringStatus = null, index = null) {
|
|
900
|
+
const val16 = statusWord & 0xFFFF;
|
|
901
|
+
return {
|
|
902
|
+
...(index == null ? {} : { index }),
|
|
903
|
+
rawValue: val16,
|
|
904
|
+
statusWordHex: val16.toString(16).padStart(4, '0').toUpperCase(),
|
|
905
|
+
// binary按原始bit-string显示,便于与645页面展示口径保持一致。
|
|
906
|
+
binary: bitStringStatus?.binary || val16.toString(2).padStart(16, '0'),
|
|
907
|
+
bits: expandBits16(val16)
|
|
908
|
+
};
|
|
909
|
+
}
|
|
930
910
|
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
911
|
+
function parseStatusWordsFromAxdrArray(buffer) {
|
|
912
|
+
if (!Buffer.isBuffer(buffer) || buffer.length < 2 || buffer[0] !== 0x01) return [];
|
|
913
|
+
const L = readAxdrLength(buffer, 1);
|
|
914
|
+
const count = L.len;
|
|
915
|
+
let offset = 1 + L.size;
|
|
916
|
+
const words = [];
|
|
917
|
+
|
|
918
|
+
for (let i = 0; i < count && offset < buffer.length; i++) {
|
|
919
|
+
if (buffer[offset] === 0x04) {
|
|
920
|
+
const word = parseStatusWordFromAxdrBitString(buffer.slice(offset));
|
|
921
|
+
if (!word) break;
|
|
922
|
+
words.push(word);
|
|
923
|
+
offset += word.consumed;
|
|
937
924
|
} else {
|
|
938
|
-
|
|
925
|
+
const { consumed } = enhancedParseData(buffer.slice(offset), '2014', '02');
|
|
926
|
+
if (!consumed) break;
|
|
927
|
+
offset += consumed;
|
|
939
928
|
}
|
|
940
|
-
|
|
941
|
-
} catch (e) {
|
|
942
|
-
console.error('parseMeterStatus错误:', e.message);
|
|
943
|
-
setErrorResult(result, e.message);
|
|
944
929
|
}
|
|
945
930
|
|
|
946
|
-
return
|
|
931
|
+
return words;
|
|
947
932
|
}
|
|
948
933
|
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
* 解析电表运行状态字3(20140203)- 按操作类位义输出字段
|
|
952
|
-
*/
|
|
953
|
-
function parseMeterStatusWord3(dataBuffer) {
|
|
954
|
-
const oad = '20140203';
|
|
955
|
-
const result = createStandardResult("电表运行状态字3", oad, dataBuffer);
|
|
934
|
+
function parseMeterStatusWord698(dataBuffer, oad, dataType) {
|
|
935
|
+
const result = createStandardResult(dataType, oad, dataBuffer);
|
|
956
936
|
try {
|
|
957
937
|
const bitStringStatus = parseStatusWordFromAxdrBitString(dataBuffer);
|
|
958
938
|
const statusWord = bitStringStatus?.statusWord ?? parseMeterStatusOptimized(dataBuffer);
|
|
959
939
|
if (statusWord === null) throw new Error('无法解析状态字');
|
|
960
940
|
|
|
961
|
-
const
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
const supplyBits = (val16 >> 1) & 0b11; // bit2-bit1
|
|
965
|
-
const supplyMode = (
|
|
966
|
-
supplyBits === 0 ? '主电源' :
|
|
967
|
-
supplyBits === 1 ? '辅助电源' :
|
|
968
|
-
supplyBits === 2 ? '电池供电' : '保留'
|
|
969
|
-
);
|
|
970
|
-
|
|
971
|
-
const meterTypeBits = (val16 >> 8) & 0b11; // bit9-bit8
|
|
972
|
-
const meterType = (
|
|
973
|
-
meterTypeBits === 0 ? '非预付费表' :
|
|
974
|
-
meterTypeBits === 1 ? '电量型预付费表' :
|
|
975
|
-
meterTypeBits === 2 ? '电费型预付费表' : '保留'
|
|
976
|
-
);
|
|
977
|
-
|
|
978
|
-
result.value = {
|
|
979
|
-
rawValue: val16,
|
|
980
|
-
binary: bin,
|
|
981
|
-
fields: {
|
|
982
|
-
当前运行时段套数: (val16 & 0x1) ? '第二套' : '第一套',
|
|
983
|
-
供电方式: supplyMode, // bit2-bit1
|
|
984
|
-
编程允许状态: (val16 & (1 << 3)) ? '有效' : '失效', // bit3
|
|
985
|
-
继电器状态: (val16 & (1 << 4)) ? '断' : '通', // bit4(线路实际工作状态)
|
|
986
|
-
当前运行时区套数: (val16 & (1 << 5)) ? '第二套' : '第一套',
|
|
987
|
-
继电器命令状态: (val16 & (1 << 6)) ? '断' : '通', // bit6(远程拉闸命令)
|
|
988
|
-
预跳闸报警状态: (val16 & (1 << 7)) ? '有' : '无', // bit7
|
|
989
|
-
电能表类型: meterType, // bit9-bit8
|
|
990
|
-
当前运行分时费率套数: (val16 & (1 << 10)) ? '第二套' : '第一套',
|
|
991
|
-
当前阶梯套数: (val16 & (1 << 11)) ? '第二套' : '第一套',
|
|
992
|
-
保电状态: (val16 & (1 << 12)) ? '保电' : '非保电'
|
|
993
|
-
}
|
|
994
|
-
};
|
|
941
|
+
const value = format698StatusWord(statusWord, bitStringStatus);
|
|
942
|
+
result.value = value;
|
|
995
943
|
|
|
996
944
|
setSuccessResult(result, {
|
|
997
|
-
statusWord:
|
|
998
|
-
statusWordHex:
|
|
999
|
-
statusBits: decodeMeterStatusBits(
|
|
945
|
+
statusWord: value.rawValue,
|
|
946
|
+
statusWordHex: value.statusWordHex,
|
|
947
|
+
statusBits: decodeMeterStatusBits(value.rawValue)
|
|
1000
948
|
}, { generic: { dataType: 'bit-string' } });
|
|
1001
949
|
} catch (e) {
|
|
1002
950
|
setErrorResult(result, e.message);
|
|
1003
951
|
}
|
|
952
|
+
|
|
1004
953
|
return result;
|
|
1005
954
|
}
|
|
1006
955
|
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
function parseMeterStatusWord2(dataBuffer) {
|
|
1011
|
-
const oad = '20140202';
|
|
1012
|
-
const result = createStandardResult("电表运行状态字2", oad, dataBuffer);
|
|
956
|
+
function parseMeterStatusArray698(dataBuffer) {
|
|
957
|
+
const oad = '20140200';
|
|
958
|
+
const result = createStandardResult("电表状态", oad, dataBuffer);
|
|
1013
959
|
try {
|
|
1014
|
-
const
|
|
1015
|
-
|
|
1016
|
-
if (statusWord === null) throw new Error('无法解析状态字');
|
|
1017
|
-
|
|
1018
|
-
const val16 = statusWord & 0xFFFF;
|
|
1019
|
-
const bin = bitStringStatus?.binary ?? val16.toString(2).padStart(16, '0');
|
|
1020
|
-
|
|
1021
|
-
const bit = (n) => ((val16 >> n) & 0x1);
|
|
1022
|
-
const dir = (b) => (b ? '反向' : '正向'); // 0=正向, 1=反向
|
|
1023
|
-
|
|
1024
|
-
// 按表F.2位义生成具名字段
|
|
1025
|
-
const fields = {
|
|
1026
|
-
'A相有功功率方向': dir(bit(0)), // bit0
|
|
1027
|
-
'B相有功功率方向': dir(bit(1)), // bit1
|
|
1028
|
-
'C相有功功率方向': dir(bit(2)), // bit2
|
|
1029
|
-
'A相无功功率方向': dir(bit(4)), // bit4
|
|
1030
|
-
'B相无功功率方向': dir(bit(5)), // bit5
|
|
1031
|
-
'C相无功功率方向': dir(bit(6)) // bit6
|
|
1032
|
-
// 其余bit3、bit7、bit8~bit15为保留
|
|
1033
|
-
};
|
|
960
|
+
const parsedWords = parseStatusWordsFromAxdrArray(dataBuffer);
|
|
961
|
+
if (!parsedWords.length) throw new Error('无法解析状态字数组');
|
|
1034
962
|
|
|
963
|
+
const words = parsedWords.map((word, index) => format698StatusWord(word.statusWord, word, index + 1));
|
|
1035
964
|
result.value = {
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
965
|
+
word1: words[0] || null,
|
|
966
|
+
word2: words[1] || null,
|
|
967
|
+
word3: words[2] || null,
|
|
968
|
+
words
|
|
1040
969
|
};
|
|
1041
970
|
|
|
1042
|
-
setSuccessResult(result, {
|
|
971
|
+
setSuccessResult(result, {
|
|
972
|
+
statusWords: words,
|
|
973
|
+
statusWord: words[0]?.rawValue ?? null,
|
|
974
|
+
statusWordHex: words[0]?.statusWordHex ?? null,
|
|
975
|
+
statusBits: words[0] ? decodeMeterStatusBits(words[0].rawValue) : []
|
|
976
|
+
}, { generic: { dataType: 'array' } });
|
|
1043
977
|
} catch (e) {
|
|
1044
978
|
setErrorResult(result, e.message);
|
|
1045
979
|
}
|
|
980
|
+
|
|
1046
981
|
return result;
|
|
1047
982
|
}
|
|
1048
983
|
|
|
984
|
+
/**
|
|
985
|
+
* 解析电表运行状态字1或状态字数组
|
|
986
|
+
* @param {Buffer} dataBuffer - 数据缓冲区
|
|
987
|
+
* @returns {Object} 解析结果
|
|
988
|
+
*/
|
|
989
|
+
|
|
990
|
+
function parseMeterStatus(dataBuffer, oad = '20140201') {
|
|
991
|
+
if (oad === '20140200') return parseMeterStatusArray698(dataBuffer);
|
|
992
|
+
return parseMeterStatusWord698(dataBuffer, oad, "电表状态");
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
/**
|
|
996
|
+
* 解析电表运行状态字3(20140203)
|
|
997
|
+
*/
|
|
998
|
+
function parseMeterStatusWord3(dataBuffer) {
|
|
999
|
+
return parseMeterStatusWord698(dataBuffer, '20140203', "电表运行状态字3");
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
/**
|
|
1003
|
+
* 解析电表运行状态字2(20140202)
|
|
1004
|
+
*/
|
|
1005
|
+
function parseMeterStatusWord2(dataBuffer) {
|
|
1006
|
+
return parseMeterStatusWord698(dataBuffer, '20140202', "电表运行状态字2");
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1049
1009
|
/**
|
|
1050
1010
|
* 优化的状态字解析策略
|
|
1051
1011
|
* @param {Buffer} dataBuffer - 数据缓冲区
|
package/package.json
CHANGED
package/AGENTS.md
DELETED
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
# AGENTS.md
|
|
2
|
-
|
|
3
|
-
This file contains guidelines and commands for agentic coding agents working in the node-red-zelecproto repository.
|
|
4
|
-
|
|
5
|
-
## Global Settings
|
|
6
|
-
- **Proxy**: Execute `proxy` command before running any network-related commands to enable proxy connection
|
|
7
|
-
|
|
8
|
-
## Build/Test/Lint Commands
|
|
9
|
-
|
|
10
|
-
### Current Commands
|
|
11
|
-
- `npm test` - Currently shows "Error: no test specified" (placeholder)
|
|
12
|
-
- No build, lint, or typecheck commands are currently configured
|
|
13
|
-
|
|
14
|
-
### Testing
|
|
15
|
-
- Use `node test.js` to run the manual test file
|
|
16
|
-
- Test file located at: `test.js`
|
|
17
|
-
- For single test execution, run `node -e "require('./test.js')"`
|
|
18
|
-
|
|
19
|
-
### Development
|
|
20
|
-
- No build process required (Node-RED nodes load directly)
|
|
21
|
-
- Manual testing via Node-RED flow editor recommended
|
|
22
|
-
|
|
23
|
-
## Code Style Guidelines
|
|
24
|
-
|
|
25
|
-
### File Structure and Naming
|
|
26
|
-
- Main node files: `zelecproto.js`, `zbatchproto.js`, `zeleble.js`
|
|
27
|
-
- Protocol implementations: `645.js`, `698.js`, `ble.js`
|
|
28
|
-
- HTML definitions: `zelecproto.html`, `zbatchproto.html`, `zeleble.html`
|
|
29
|
-
- Icons in: `icons/` directory (SVG format)
|
|
30
|
-
- Use kebab-case for file names
|
|
31
|
-
|
|
32
|
-
### Module Pattern
|
|
33
|
-
All Node-RED nodes must follow this pattern:
|
|
34
|
-
```javascript
|
|
35
|
-
module.exports = function (RED) {
|
|
36
|
-
"use strict";
|
|
37
|
-
|
|
38
|
-
// Require dependencies
|
|
39
|
-
var proto645 = require("./645");
|
|
40
|
-
|
|
41
|
-
function NodeName(n) {
|
|
42
|
-
RED.nodes.createNode(this, n);
|
|
43
|
-
var node = this;
|
|
44
|
-
|
|
45
|
-
this.on("input", function (msg, send, done) {
|
|
46
|
-
// Process message
|
|
47
|
-
send(msg);
|
|
48
|
-
done();
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
this.on('close', () => {
|
|
52
|
-
// Cleanup if needed
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
RED.nodes.registerType("nodename", NodeName);
|
|
57
|
-
}
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
### Import/Require Style
|
|
61
|
-
- Use `var` for requires (consistent with existing codebase)
|
|
62
|
-
- Local dependencies first: `var proto645 = require("./645");`
|
|
63
|
-
- Group requires at top of module function
|
|
64
|
-
- Use relative paths for local files
|
|
65
|
-
|
|
66
|
-
### Code Formatting
|
|
67
|
-
- Use strict mode: `"use strict";` at top of module function
|
|
68
|
-
- Indentation: 4 spaces (consistent with existing files)
|
|
69
|
-
- Semicolons required
|
|
70
|
-
- String quotes: single quotes preferred
|
|
71
|
-
- Object property access: dot notation for known properties, bracket notation for dynamic
|
|
72
|
-
|
|
73
|
-
### Naming Conventions
|
|
74
|
-
- Node functions: PascalCase (e.g., `zelecproto`, `zbatchproto`)
|
|
75
|
-
- Variables: camelCase (e.g., `msg`, `node`, `addrBytes`)
|
|
76
|
-
- Constants: UPPER_SNAKE_CASE (e.g., `START`, `END`, `OAD`)
|
|
77
|
-
- File names: kebab-case (e.g., `zelecproto.js`, `645.js`)
|
|
78
|
-
|
|
79
|
-
### Error Handling
|
|
80
|
-
- Use `throw new Error()` for validation errors
|
|
81
|
-
- Include descriptive error messages
|
|
82
|
-
- Validate input parameters before processing
|
|
83
|
-
- Example: `if (addrRaw.length !== 12) throw new Error('com_exec_addr 必须是 6 字节(12个HEX字符)');`
|
|
84
|
-
|
|
85
|
-
### Message Processing
|
|
86
|
-
- Always use the three-parameter input handler: `function (msg, send, done)`
|
|
87
|
-
- Call `send(msg)` before `done()`
|
|
88
|
-
- Clean up temporary properties: `delete msg._proto`
|
|
89
|
-
- Preserve original message structure when possible
|
|
90
|
-
|
|
91
|
-
### Protocol Implementation
|
|
92
|
-
- 645 Protocol: Focus on DL/T 645 frame building/parsing
|
|
93
|
-
- 698 Protocol: DL/T 698.45 encode/decode with CRC-16/X-25
|
|
94
|
-
- BLE Protocol: 国网多芯物联表蓝牙通信协议
|
|
95
|
-
- Each protocol file should export a function that takes `msg` and returns modified `msg`
|
|
96
|
-
|
|
97
|
-
### HTML Node Definitions
|
|
98
|
-
- Use data-template-name attribute matching node type
|
|
99
|
-
- Include name field in all nodes
|
|
100
|
-
- Set appropriate category, color, and icon
|
|
101
|
-
- Category: 'zutils' for all nodes
|
|
102
|
-
- Icons: reference SVG files in icons/ directory
|
|
103
|
-
|
|
104
|
-
### Comments and Documentation
|
|
105
|
-
- Use Chinese comments for protocol-specific explanations (consistent with existing code)
|
|
106
|
-
- Use JSDoc-style comments for functions
|
|
107
|
-
- Include protocol references and frame format descriptions
|
|
108
|
-
- Example: `// 国网多芯物联表《蓝牙通信及脉冲检定说明手册(0224)》协议`
|
|
109
|
-
|
|
110
|
-
### Buffer/Hex Handling
|
|
111
|
-
- Use `Buffer.from()` for creating buffers
|
|
112
|
-
- Hex strings should be uppercase without spaces for final output
|
|
113
|
-
- Use helper functions for hex/bytes conversion
|
|
114
|
-
- Maintain consistent hex formatting across protocols
|
|
115
|
-
|
|
116
|
-
### Node-RED Integration
|
|
117
|
-
- Register nodes in package.json under `node-red.nodes`
|
|
118
|
-
- Minimum Node.js version: >=15
|
|
119
|
-
- Minimum Node-RED version: >=1.3
|
|
120
|
-
- No external dependencies currently required
|
|
121
|
-
|
|
122
|
-
## Development Workflow
|
|
123
|
-
1. Modify protocol implementation files as needed
|
|
124
|
-
2. Test with `node test.js` or via Node-RED flow editor
|
|
125
|
-
3. Ensure all nodes follow the standard module pattern
|
|
126
|
-
4. Verify HTML definitions match JavaScript node registrations
|
|
127
|
-
5. Test message processing with sample data
|
|
128
|
-
|
|
129
|
-
## Protocol-Specific Notes
|
|
130
|
-
- 645: Handle address reversal and OAD encryption
|
|
131
|
-
- 698: Implement CRC-16/X-25 checksum validation
|
|
132
|
-
- BLE: Follow frame format with Start/End markers
|
|
133
|
-
- All protocols should support both encode and decode operations
|