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