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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/zserial.js +67 -87
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-zserial",
3
- "version": "1.0.16",
3
+ "version": "1.0.18",
4
4
  "description": "Node-RED nodes to talk to serial ports",
5
5
  "dependencies": {
6
6
  "serialport": "^12.0.0"
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
- // clear partials so they don't leak into next request
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
- // 计算 8bit 累加和([start,end))
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(HDLC/LAPB 常用):init 0xFFFF, poly 0x1021, refin/refout=true(实现中按位),xorout 0xFFFF
785
+ // CRC-16/X25init 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; // 0x8408 = reflect(0x1021)
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 解转义:0x7D 0xXX -> 0xXX ^ 0x20
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
- // 解析 645:FE* 68 + 6 addr + 68 + ctrl + len + data + cs + 16
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 csIdx = total - 2;
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 (endFlag !== 0x16) {
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
- // 解析 698 “Len 变体”:68 LL LH C ... DATA ... CS 16
832
+ // 698(68-LEN 变体):68 LL LH C ... DATA ... CS 16 ;兼容 FE* 前导
846
833
  function tryParse698Len(input) {
847
- // 注意:不要 stripFE(698 不用 FE 前导);但现场可能混入 FE 噪声,外层会先剔除非 {FE,68,7E}
848
- const b = input;
849
- if (b.length < 5) return { ok: false };
850
- if (b[0] !== 0x68) return { ok: false };
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
- const len = (b[1] | (b[2] << 8)) >>> 0;
855
- if (len <= 0 || len > 4096) {
856
- return { ok: false, used: 1, frame: b.slice(0, 1), err: "698_BAD_LENGTH" };
857
- }
858
- const total = 1 + 2 + len + 2; // 68 + LL,LH + payload(len) + CS,16
859
- if (b.length < total) return { ok: false }; // 半包
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
- const csIdx = total - 2;
862
- const endIdx = total - 1;
863
- const endFlag = b[endIdx];
864
- const csExpect = sum8(b, 3, csIdx); // 从 control(索引3) 起,到 CS 前
865
- const csGot = b[csIdx];
866
- const frame = b.slice(0, total);
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
- if (endFlag !== 0x16) {
869
- return { ok: false, used: total, frame, err: "698_END_FLAG_16_MISSING" };
862
+ // 找下一个 0x16
863
+ end = b.indexOf(0x16, end + 1);
870
864
  }
871
- if (csGot !== csExpect) {
872
- return { ok: false, used: total, frame, err: "698_CHECKSUM_MISMATCH" };
873
- }
874
- return { ok: true, used: total, frame };
865
+
866
+ // 还不能定论,继续累计字节
867
+ return { ok: false };
875
868
  }
876
869
 
877
- // 解析 698 标准 HDLC7E ... FCS(lo,hi) 7E,支持 0x7D 透明传输与 FCS 校验
870
+ // 698HDLC):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 }; // 7E + 最小帧 + 7E
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
- if (b[i] === 0x7E) { endPos = i; break; }
887
- }
888
- if (endPos === -1) return { ok: false }; // 半包
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
- if (calc !== fcs) {
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
- // 剔除前导噪声:仅保留以 0xFE(645 前导)、0x68(645/698-Len)或 0x7E(698-HDLC)开头
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(避免 7E 被误当噪声),其次 645,再次 698-Len
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; }