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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/zserial.js +47 -85
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-zserial",
3
- "version": "1.0.16",
3
+ "version": "1.0.17",
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,79 @@ 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;
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
- // 若字节7 68,多为 645,交给 645 解析器
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
- 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 }; // 半包
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); // 从 control(索引3) 起,到 CS 前
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 (endFlag !== 0x16) {
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
- // 解析 698 标准 HDLC7E ... FCS(lo,hi) 7E,支持 0x7D 透明传输与 FCS 校验
852
+ // 698HDLC):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 }; // 7E + 最小帧 + 7E
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
- 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 透明传输反转义
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
- 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 };
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
- // 剔除前导噪声:仅保留以 0xFE(645 前导)、0x68(645/698-Len)或 0x7E(698-HDLC)开头
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(避免 7E 被误当噪声),其次 645,再次 698-Len
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; }