node-zserial 1.0.16 → 1.0.18
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 +67 -87
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,97 @@ 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;
|
|
849
|
-
if (b.length <
|
|
850
|
-
|
|
851
|
-
// 若字节7为 68,多为 645,交给 645 解析器
|
|
834
|
+
// 剥 FE 前导
|
|
835
|
+
const b = stripFE(input);
|
|
836
|
+
if (b.length < 6 || b[0] !== 0x68) return { ok: false };
|
|
837
|
+
// 避免把 645 误判成 698-LEN
|
|
852
838
|
if (b.length >= 8 && b[7] === 0x68) return { ok: false };
|
|
853
839
|
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
840
|
+
// 在缓冲里寻找候选的 0x16 作为帧尾(支持一包多帧/脏数据)
|
|
841
|
+
let end = b.indexOf(0x16, 5);
|
|
842
|
+
while (end !== -1) {
|
|
843
|
+
// —— ① 先试 2 字节 CRC-16/X.25(低字节在前),计算区间:从 LL 开始到 CRC 前一字节 ——
|
|
844
|
+
if (end >= 3) {
|
|
845
|
+
const lo = b[end - 2], hi = b[end - 1];
|
|
846
|
+
const fcs = (hi << 8) | lo;
|
|
847
|
+
const calc = crc16x25(b, /*start=*/1, /*end=*/end - 2); // [LL..CRC-1]
|
|
848
|
+
if (calc === fcs) {
|
|
849
|
+
return { ok: true, used: end + 1, frame: b.slice(0, end + 1) };
|
|
850
|
+
}
|
|
851
|
+
}
|
|
860
852
|
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
853
|
+
// —— ② 再试 1 字节和校验(sum8),计算区间:从 control 起到 CS 前 ——
|
|
854
|
+
if (end >= 2) {
|
|
855
|
+
const cs = b[end - 1];
|
|
856
|
+
const sum = sum8(b, /*start=*/3, /*end=*/end - 1);
|
|
857
|
+
if (cs === sum) {
|
|
858
|
+
return { ok: true, used: end + 1, frame: b.slice(0, end + 1) };
|
|
859
|
+
}
|
|
860
|
+
}
|
|
867
861
|
|
|
868
|
-
|
|
869
|
-
|
|
862
|
+
// 找下一个 0x16
|
|
863
|
+
end = b.indexOf(0x16, end + 1);
|
|
870
864
|
}
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
}
|
|
874
|
-
return { ok: true, used: total, frame };
|
|
865
|
+
|
|
866
|
+
// 还不能定论,继续累计字节
|
|
867
|
+
return { ok: false };
|
|
875
868
|
}
|
|
876
869
|
|
|
877
|
-
//
|
|
870
|
+
// 698(HDLC):7E ... [FCS(lo,hi)] 7E,支持 0x7D 转义与 X.25 FCS
|
|
878
871
|
function tryParse698HDLC(input) {
|
|
879
872
|
const b = input;
|
|
880
|
-
if (b.length < 6) return { ok: false };
|
|
873
|
+
if (b.length < 6) return { ok: false };
|
|
881
874
|
if (b[0] !== 0x7E) return { ok: false };
|
|
882
|
-
|
|
883
|
-
// 找到下一枚结束 0x7E(允许中间出现其他 0x7E? 正常应视为下一帧结束)
|
|
884
875
|
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 透明传输反转义
|
|
876
|
+
for (let i = 1; i < b.length; i++) { if (b[i] === 0x7E) { endPos = i; break; } }
|
|
877
|
+
if (endPos === -1) return { ok: false }; // 半包
|
|
878
|
+
const rawFrame = b.slice(0, endPos + 1);
|
|
879
|
+
const payloadEscaped = b.slice(1, endPos); // 去 7E
|
|
893
880
|
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:最后两字节,低字节在前
|
|
881
|
+
if (payload.length < 3) return { ok: false, used: endPos + 1, frame: rawFrame, err: "698_HDLC_TOO_SHORT" };
|
|
900
882
|
const fcsLo = payload[payload.length - 2];
|
|
901
883
|
const fcsHi = payload[payload.length - 1];
|
|
902
884
|
const fcs = (fcsHi << 8) | fcsLo;
|
|
903
885
|
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 };
|
|
886
|
+
if (calc !== fcs) return { ok: false, used: endPos + 1, frame: rawFrame, err: "698_HDLC_CRC_FAIL" };
|
|
887
|
+
return { ok: true, used: endPos + 1, frame: rawFrame }; // 原始含 7E
|
|
910
888
|
}
|
|
911
889
|
|
|
912
|
-
//
|
|
890
|
+
// 统一喂入器:抽帧、报错、剔噪
|
|
913
891
|
let assembleBuf = Buffer.alloc(0);
|
|
914
892
|
function feedAndExtract(d, emitOk, emitErr) {
|
|
915
893
|
if (!Buffer.isBuffer(d)) d = Buffer.from(d);
|
|
916
894
|
assembleBuf = Buffer.concat([assembleBuf, d]);
|
|
917
895
|
|
|
918
|
-
//
|
|
896
|
+
// 防溢出
|
|
919
897
|
if (assembleBuf.length > bufMaxSize) {
|
|
920
898
|
emitErr(Buffer.from(assembleBuf), "BUFFER_OVERFLOW_DROP_OLD");
|
|
921
899
|
assembleBuf = Buffer.alloc(0);
|
|
922
900
|
return;
|
|
923
901
|
}
|
|
924
902
|
|
|
925
|
-
//
|
|
903
|
+
// 前导剔噪:仅保留以 FE/68/7E 开头;若以 FE 开头,剥离所有 FE 前导
|
|
926
904
|
let s = 0;
|
|
927
905
|
while (s < assembleBuf.length) {
|
|
928
906
|
const c = assembleBuf[s];
|
|
@@ -930,10 +908,14 @@ module.exports = function (RED) {
|
|
|
930
908
|
s++;
|
|
931
909
|
}
|
|
932
910
|
if (s > 0) assembleBuf = assembleBuf.slice(s);
|
|
911
|
+
if (assembleBuf.length && assembleBuf[0] === 0xFE) {
|
|
912
|
+
let k = 0; while (k < assembleBuf.length && assembleBuf[k] === 0xFE) k++;
|
|
913
|
+
assembleBuf = assembleBuf.slice(k);
|
|
914
|
+
}
|
|
933
915
|
|
|
934
|
-
//
|
|
916
|
+
// 抽帧
|
|
935
917
|
while (assembleBuf.length >= 5) {
|
|
936
|
-
// 优先 HDLC
|
|
918
|
+
// 优先 HDLC,再 645,再 698-Len(避免误判)
|
|
937
919
|
let r = tryParse698HDLC(assembleBuf);
|
|
938
920
|
if (!r.ok) r = tryParse645(assembleBuf);
|
|
939
921
|
if (!r.ok) r = tryParse698Len(assembleBuf);
|
|
@@ -944,17 +926,15 @@ module.exports = function (RED) {
|
|
|
944
926
|
continue;
|
|
945
927
|
}
|
|
946
928
|
if (r.used) {
|
|
947
|
-
// 有足够信息判断该段为坏帧:直接丢弃并上报错误
|
|
948
929
|
emitErr(r.frame, r.err || "FRAME_INVALID");
|
|
949
930
|
assembleBuf = assembleBuf.slice(r.used);
|
|
950
931
|
continue;
|
|
951
932
|
}
|
|
952
|
-
// 需要更多数据
|
|
953
|
-
break;
|
|
933
|
+
break; // 需要更多数据
|
|
954
934
|
}
|
|
955
935
|
}
|
|
956
936
|
|
|
957
|
-
/***** -------------------------------- Frame parsers for DL/T645 & DL/T698.45 (FULL) End-------------------------------- *****/
|
|
937
|
+
/***** -------------------------------- Frame parsers for DL/T645 & DL/T698.45 (FULL) End -------------------------------- *****/
|
|
958
938
|
|
|
959
939
|
|
|
960
940
|
obj.serial.on('data', function (d) {
|
|
@@ -983,7 +963,7 @@ module.exports = function (RED) {
|
|
|
983
963
|
var last_sender = null;
|
|
984
964
|
if (obj.queue.length) { last_sender = obj.queue[0].sender; }
|
|
985
965
|
var msgout = obj.dequeue() || {};
|
|
986
|
-
if (binoutput !== "bin") { m = m.toString(); }
|
|
966
|
+
if (binoutput !== "bin") { m = m.toString(); }
|
|
987
967
|
msgout.payload = m;
|
|
988
968
|
msgout.port = port;
|
|
989
969
|
msgout.status = "OK";
|
|
@@ -996,7 +976,7 @@ module.exports = function (RED) {
|
|
|
996
976
|
if (obj.queue.length) { last_sender = obj.queue[0].sender; }
|
|
997
977
|
var msgout = obj.dequeue() || {};
|
|
998
978
|
if (binoutput !== "bin") { m = m.toString(); }
|
|
999
|
-
msgout.payload = m;
|
|
979
|
+
msgout.payload = m;
|
|
1000
980
|
msgout.port = port;
|
|
1001
981
|
msgout.status = "ERR_FRAME";
|
|
1002
982
|
msgout.reason = reason || "FRAME_INVALID";
|
|
@@ -1008,7 +988,7 @@ module.exports = function (RED) {
|
|
|
1008
988
|
|
|
1009
989
|
// —— 其余兼容模式(time/interbyte/count/char)保持原逻辑 ——
|
|
1010
990
|
// -------- existing legacy split modes (time/interbyte/count/char) --------
|
|
1011
|
-
|
|
991
|
+
|
|
1012
992
|
for (var z = 0; z < d.length; z++) {
|
|
1013
993
|
var c = d[z];
|
|
1014
994
|
if (c === waitfor) { active = true; }
|