node-zserial 1.0.16 → 1.0.17
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/package.json +1 -1
- package/zserial.js +47 -85
package/package.json
CHANGED
package/zserial.js
CHANGED
|
@@ -651,24 +651,21 @@ module.exports = function (RED) {
|
|
|
651
651
|
// Prefer flushing data depending on split mode
|
|
652
652
|
var m = null;
|
|
653
653
|
if (spliton === "frame") {
|
|
654
|
-
// In frame mode, partial bytes are accumulated in assembleBuf
|
|
655
654
|
if (typeof assembleBuf !== "undefined" && assembleBuf && assembleBuf.length) {
|
|
656
655
|
m = Buffer.from(assembleBuf);
|
|
657
|
-
|
|
658
|
-
assembleBuf = Buffer.alloc(0);
|
|
656
|
+
assembleBuf = Buffer.alloc(0); // 清掉残留
|
|
659
657
|
}
|
|
660
658
|
} else {
|
|
661
|
-
// legacy modes use buf/i
|
|
662
659
|
if (i !== 0) {
|
|
663
660
|
m = buf.slice(0, i);
|
|
664
661
|
i = 0;
|
|
665
662
|
}
|
|
666
663
|
}
|
|
664
|
+
|
|
667
665
|
if (m) {
|
|
668
666
|
if (binoutput !== "bin") { m = m.toString(); }
|
|
669
667
|
msgout.payload = m;
|
|
670
|
-
}else {
|
|
671
|
-
// ensure payload exists for upstream logic
|
|
668
|
+
} else {
|
|
672
669
|
msgout.payload = (binoutput !== "bin") ? "" : Buffer.alloc(0);
|
|
673
670
|
}
|
|
674
671
|
msgout.status = "ERR_TIMEOUT";
|
|
@@ -773,33 +770,32 @@ module.exports = function (RED) {
|
|
|
773
770
|
|
|
774
771
|
|
|
775
772
|
/***** -------------------------------- Frame parsers for DL/T645 & DL/T698.45 (FULL) -------------------------------- *****/
|
|
776
|
-
// FE 去前导(645
|
|
773
|
+
// FE 去前导(645/部分场景)
|
|
777
774
|
function stripFE(buf) {
|
|
778
775
|
let s = 0;
|
|
779
776
|
while (s < buf.length && buf[s] === 0xFE) s++;
|
|
780
777
|
return buf.slice(s);
|
|
781
778
|
}
|
|
782
|
-
//
|
|
779
|
+
// 8bit 累加和([start,end))
|
|
783
780
|
function sum8(buf, start, end) {
|
|
784
781
|
let sum = 0;
|
|
785
782
|
for (let i = start; i < end; i++) sum = (sum + buf[i]) & 0xFF;
|
|
786
783
|
return sum;
|
|
787
784
|
}
|
|
788
|
-
// CRC-16/X25
|
|
785
|
+
// CRC-16/X25:init 0xFFFF, poly 0x1021(反射实现:0x8408),xorout 0xFFFF,帧内低字节在前
|
|
789
786
|
function crc16x25(buf, start, end) {
|
|
790
787
|
let crc = 0xFFFF;
|
|
791
788
|
for (let i = start; i < end; i++) {
|
|
792
789
|
crc ^= buf[i];
|
|
793
790
|
for (let b = 0; b < 8; b++) {
|
|
794
|
-
if (crc & 1) crc = (crc >>> 1) ^ 0x8408;
|
|
791
|
+
if (crc & 1) crc = (crc >>> 1) ^ 0x8408;
|
|
795
792
|
else crc >>>= 1;
|
|
796
793
|
}
|
|
797
794
|
}
|
|
798
795
|
crc ^= 0xFFFF;
|
|
799
|
-
// 返回 16-bit,低字节在前(与帧内 FCS 字节序一致)
|
|
800
796
|
return crc & 0xFFFF;
|
|
801
797
|
}
|
|
802
|
-
// HDLC
|
|
798
|
+
// HDLC 透明传输反转义:0x7D 0xXX -> 0xXX ^ 0x20
|
|
803
799
|
function unescapeHDLC(src) {
|
|
804
800
|
const out = [];
|
|
805
801
|
for (let i = 0; i < src.length; i++) {
|
|
@@ -814,115 +810,79 @@ module.exports = function (RED) {
|
|
|
814
810
|
return Buffer.from(out);
|
|
815
811
|
}
|
|
816
812
|
|
|
817
|
-
//
|
|
813
|
+
// 645:FE* 68 + 6 addr + 68 + ctrl + len + data + cs + 16
|
|
818
814
|
function tryParse645(input) {
|
|
819
815
|
let b = stripFE(input);
|
|
820
816
|
if (b.length < 12) return { ok: false };
|
|
821
817
|
if (b[0] !== 0x68) return { ok: false };
|
|
822
|
-
if (b.length >= 8 && b[7] !== 0x68) return { ok: false }; // 第二个 0x68
|
|
823
|
-
if (b.length < 12) return { ok: false };
|
|
824
|
-
|
|
818
|
+
if (b.length >= 8 && b[7] !== 0x68) return { ok: false }; // 第二个 0x68
|
|
825
819
|
const dataLen = b[9] >>> 0;
|
|
826
820
|
const total = 12 + dataLen;
|
|
827
821
|
if (b.length < total) return { ok: false }; // 半包
|
|
828
|
-
|
|
829
|
-
const
|
|
830
|
-
const endIdx = total - 1;
|
|
831
|
-
const csExpect = sum8(b, 8, csIdx); // 从 ctrl(索引8) 到 data 末
|
|
822
|
+
const csIdx = total - 2, endIdx = total - 1;
|
|
823
|
+
const csExpect = sum8(b, 8, csIdx);
|
|
832
824
|
const csGot = b[csIdx];
|
|
833
825
|
const endFlag = b[endIdx];
|
|
834
826
|
const frame = b.slice(0, total);
|
|
835
|
-
|
|
836
|
-
if (
|
|
837
|
-
return { ok: false, used: total, frame, err: "645_END_FLAG_16_MISSING" };
|
|
838
|
-
}
|
|
839
|
-
if (csGot !== csExpect) {
|
|
840
|
-
return { ok: false, used: total, frame, err: "645_CHECKSUM_MISMATCH" };
|
|
841
|
-
}
|
|
827
|
+
if (endFlag !== 0x16) return { ok: false, used: total, frame, err: "645_END_FLAG_16_MISSING" };
|
|
828
|
+
if (csGot !== csExpect) return { ok: false, used: total, frame, err: "645_CHECKSUM_MISMATCH" };
|
|
842
829
|
return { ok: true, used: total, frame };
|
|
843
830
|
}
|
|
844
831
|
|
|
845
|
-
//
|
|
832
|
+
// 698(68-LEN 变体):68 LL LH C ... DATA ... CS 16 ;兼容 FE* 前导
|
|
846
833
|
function tryParse698Len(input) {
|
|
847
|
-
|
|
848
|
-
const b = input;
|
|
834
|
+
const b = stripFE(input); // 关键:剥 FE 前导
|
|
849
835
|
if (b.length < 5) return { ok: false };
|
|
850
836
|
if (b[0] !== 0x68) return { ok: false };
|
|
851
|
-
|
|
852
|
-
if (b.length >= 8 && b[7] === 0x68) return { ok: false };
|
|
853
|
-
|
|
837
|
+
if (b.length >= 8 && b[7] === 0x68) return { ok: false }; // 645 让 645 解析
|
|
854
838
|
const len = (b[1] | (b[2] << 8)) >>> 0;
|
|
855
|
-
if (len <= 0 || len > 4096) {
|
|
856
|
-
|
|
857
|
-
}
|
|
858
|
-
const
|
|
859
|
-
if (b.length < total) return { ok: false }; // 半包
|
|
860
|
-
|
|
861
|
-
const csIdx = total - 2;
|
|
862
|
-
const endIdx = total - 1;
|
|
839
|
+
if (len <= 0 || len > 4096) return { ok: false, used: 1, frame: b.slice(0, 1), err: "698_BAD_LENGTH" };
|
|
840
|
+
const total = 1 + 2 + len + 2; // 68 + len2 + payload(len) + CS,16
|
|
841
|
+
if (b.length < total) return { ok: false }; // 半包
|
|
842
|
+
const csIdx = total - 2, endIdx = total - 1;
|
|
863
843
|
const endFlag = b[endIdx];
|
|
864
|
-
const csExpect = sum8(b, 3, csIdx);
|
|
844
|
+
const csExpect = sum8(b, 3, csIdx); // 从 control 起到 CS 前
|
|
865
845
|
const csGot = b[csIdx];
|
|
866
846
|
const frame = b.slice(0, total);
|
|
867
|
-
|
|
868
|
-
if (
|
|
869
|
-
return { ok: false, used: total, frame, err: "698_END_FLAG_16_MISSING" };
|
|
870
|
-
}
|
|
871
|
-
if (csGot !== csExpect) {
|
|
872
|
-
return { ok: false, used: total, frame, err: "698_CHECKSUM_MISMATCH" };
|
|
873
|
-
}
|
|
847
|
+
if (endFlag !== 0x16) return { ok: false, used: total, frame, err: "698_END_FLAG_16_MISSING" };
|
|
848
|
+
if (csGot !== csExpect) return { ok: false, used: total, frame, err: "698_CHECKSUM_MISMATCH" };
|
|
874
849
|
return { ok: true, used: total, frame };
|
|
875
850
|
}
|
|
876
851
|
|
|
877
|
-
//
|
|
852
|
+
// 698(HDLC):7E ... [FCS(lo,hi)] 7E,支持 0x7D 转义与 X.25 FCS
|
|
878
853
|
function tryParse698HDLC(input) {
|
|
879
854
|
const b = input;
|
|
880
|
-
if (b.length < 6) return { ok: false };
|
|
855
|
+
if (b.length < 6) return { ok: false };
|
|
881
856
|
if (b[0] !== 0x7E) return { ok: false };
|
|
882
|
-
|
|
883
|
-
// 找到下一枚结束 0x7E(允许中间出现其他 0x7E? 正常应视为下一帧结束)
|
|
884
857
|
let endPos = -1;
|
|
885
|
-
for (let i = 1; i < b.length; i++) {
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
const rawFrame = b.slice(0, endPos + 1); // [0 .. endPos]
|
|
891
|
-
const payloadEscaped = b.slice(1, endPos); // 去掉前后 0x7E
|
|
892
|
-
// HDLC 透明传输反转义
|
|
858
|
+
for (let i = 1; i < b.length; i++) { if (b[i] === 0x7E) { endPos = i; break; } }
|
|
859
|
+
if (endPos === -1) return { ok: false }; // 半包
|
|
860
|
+
const rawFrame = b.slice(0, endPos + 1);
|
|
861
|
+
const payloadEscaped = b.slice(1, endPos); // 去 7E
|
|
893
862
|
const payload = unescapeHDLC(payloadEscaped);
|
|
894
|
-
if (payload.length < 3) {
|
|
895
|
-
// 至少 1 字节信息 + 2 字节 FCS
|
|
896
|
-
return { ok: false, used: endPos + 1, frame: rawFrame, err: "698_HDLC_TOO_SHORT" };
|
|
897
|
-
}
|
|
898
|
-
|
|
899
|
-
// FCS:最后两字节,低字节在前
|
|
863
|
+
if (payload.length < 3) return { ok: false, used: endPos + 1, frame: rawFrame, err: "698_HDLC_TOO_SHORT" };
|
|
900
864
|
const fcsLo = payload[payload.length - 2];
|
|
901
865
|
const fcsHi = payload[payload.length - 1];
|
|
902
866
|
const fcs = (fcsHi << 8) | fcsLo;
|
|
903
867
|
const calc = crc16x25(payload, 0, payload.length - 2);
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
return { ok: false, used: endPos + 1, frame: rawFrame, err: "698_HDLC_CRC_FAIL" };
|
|
907
|
-
}
|
|
908
|
-
// 校验 OK,返回原始帧(包含 7E .. 7E),便于上层直接下发/记录
|
|
909
|
-
return { ok: true, used: endPos + 1, frame: rawFrame };
|
|
868
|
+
if (calc !== fcs) return { ok: false, used: endPos + 1, frame: rawFrame, err: "698_HDLC_CRC_FAIL" };
|
|
869
|
+
return { ok: true, used: endPos + 1, frame: rawFrame }; // 原始含 7E
|
|
910
870
|
}
|
|
911
871
|
|
|
912
|
-
//
|
|
872
|
+
// 统一喂入器:抽帧、报错、剔噪
|
|
913
873
|
let assembleBuf = Buffer.alloc(0);
|
|
914
874
|
function feedAndExtract(d, emitOk, emitErr) {
|
|
915
875
|
if (!Buffer.isBuffer(d)) d = Buffer.from(d);
|
|
916
876
|
assembleBuf = Buffer.concat([assembleBuf, d]);
|
|
917
877
|
|
|
918
|
-
//
|
|
878
|
+
// 防溢出
|
|
919
879
|
if (assembleBuf.length > bufMaxSize) {
|
|
920
880
|
emitErr(Buffer.from(assembleBuf), "BUFFER_OVERFLOW_DROP_OLD");
|
|
921
881
|
assembleBuf = Buffer.alloc(0);
|
|
922
882
|
return;
|
|
923
883
|
}
|
|
924
884
|
|
|
925
|
-
//
|
|
885
|
+
// 前导剔噪:仅保留以 FE/68/7E 开头;若以 FE 开头,剥离所有 FE 前导
|
|
926
886
|
let s = 0;
|
|
927
887
|
while (s < assembleBuf.length) {
|
|
928
888
|
const c = assembleBuf[s];
|
|
@@ -930,10 +890,14 @@ module.exports = function (RED) {
|
|
|
930
890
|
s++;
|
|
931
891
|
}
|
|
932
892
|
if (s > 0) assembleBuf = assembleBuf.slice(s);
|
|
893
|
+
if (assembleBuf.length && assembleBuf[0] === 0xFE) {
|
|
894
|
+
let k = 0; while (k < assembleBuf.length && assembleBuf[k] === 0xFE) k++;
|
|
895
|
+
assembleBuf = assembleBuf.slice(k);
|
|
896
|
+
}
|
|
933
897
|
|
|
934
|
-
//
|
|
898
|
+
// 抽帧
|
|
935
899
|
while (assembleBuf.length >= 5) {
|
|
936
|
-
// 优先 HDLC
|
|
900
|
+
// 优先 HDLC,再 645,再 698-Len(避免误判)
|
|
937
901
|
let r = tryParse698HDLC(assembleBuf);
|
|
938
902
|
if (!r.ok) r = tryParse645(assembleBuf);
|
|
939
903
|
if (!r.ok) r = tryParse698Len(assembleBuf);
|
|
@@ -944,17 +908,15 @@ module.exports = function (RED) {
|
|
|
944
908
|
continue;
|
|
945
909
|
}
|
|
946
910
|
if (r.used) {
|
|
947
|
-
// 有足够信息判断该段为坏帧:直接丢弃并上报错误
|
|
948
911
|
emitErr(r.frame, r.err || "FRAME_INVALID");
|
|
949
912
|
assembleBuf = assembleBuf.slice(r.used);
|
|
950
913
|
continue;
|
|
951
914
|
}
|
|
952
|
-
// 需要更多数据
|
|
953
|
-
break;
|
|
915
|
+
break; // 需要更多数据
|
|
954
916
|
}
|
|
955
917
|
}
|
|
956
918
|
|
|
957
|
-
/***** -------------------------------- Frame parsers for DL/T645 & DL/T698.45 (FULL) End-------------------------------- *****/
|
|
919
|
+
/***** -------------------------------- Frame parsers for DL/T645 & DL/T698.45 (FULL) End -------------------------------- *****/
|
|
958
920
|
|
|
959
921
|
|
|
960
922
|
obj.serial.on('data', function (d) {
|
|
@@ -983,7 +945,7 @@ module.exports = function (RED) {
|
|
|
983
945
|
var last_sender = null;
|
|
984
946
|
if (obj.queue.length) { last_sender = obj.queue[0].sender; }
|
|
985
947
|
var msgout = obj.dequeue() || {};
|
|
986
|
-
if (binoutput !== "bin") { m = m.toString(); }
|
|
948
|
+
if (binoutput !== "bin") { m = m.toString(); }
|
|
987
949
|
msgout.payload = m;
|
|
988
950
|
msgout.port = port;
|
|
989
951
|
msgout.status = "OK";
|
|
@@ -996,7 +958,7 @@ module.exports = function (RED) {
|
|
|
996
958
|
if (obj.queue.length) { last_sender = obj.queue[0].sender; }
|
|
997
959
|
var msgout = obj.dequeue() || {};
|
|
998
960
|
if (binoutput !== "bin") { m = m.toString(); }
|
|
999
|
-
msgout.payload = m;
|
|
961
|
+
msgout.payload = m;
|
|
1000
962
|
msgout.port = port;
|
|
1001
963
|
msgout.status = "ERR_FRAME";
|
|
1002
964
|
msgout.reason = reason || "FRAME_INVALID";
|
|
@@ -1008,7 +970,7 @@ module.exports = function (RED) {
|
|
|
1008
970
|
|
|
1009
971
|
// —— 其余兼容模式(time/interbyte/count/char)保持原逻辑 ——
|
|
1010
972
|
// -------- existing legacy split modes (time/interbyte/count/char) --------
|
|
1011
|
-
|
|
973
|
+
|
|
1012
974
|
for (var z = 0; z < d.length; z++) {
|
|
1013
975
|
var c = d[z];
|
|
1014
976
|
if (c === waitfor) { active = true; }
|