node-zserial 1.0.25 → 1.0.27

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 +98 -22
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-zserial",
3
- "version": "1.0.25",
3
+ "version": "1.0.27",
4
4
  "description": "Node-RED nodes to talk to serial ports",
5
5
  "dependencies": {
6
6
  "serialport": "^12.0.0"
package/zserial.js CHANGED
@@ -586,7 +586,7 @@ module.exports = function (RED) {
586
586
  var serialPool = (function () {
587
587
  var connections = {};
588
588
  var _zemitter = new events.EventEmitter();
589
-
589
+ _zemitter.setMaxListeners(500); // 设置最大监听器数量为500,防止警告
590
590
  return {
591
591
  on: function (event, callback) { _zemitter.on(event, callback); },
592
592
  off: function (event, callback) { _zemitter.off(event, callback); },
@@ -956,17 +956,42 @@ module.exports = function (RED) {
956
956
  const endIdx = total - 1; // 末尾 0x16 索引
957
957
  const csIdx = total - 2; // ★ CS 在倒数第2个字节
958
958
 
959
- // CS = 从第一个 0x68 起累加到 CS 之前(不含 CS)
959
+ // CS 双模校验:
960
+ // - full:从第一个 0x68 起累加到 CS 前(不含 CS)
961
+ // - std :从第二个 0x68 后(C+L+DATA)累加到 CS 前
960
962
  const csOK = (end) => {
961
- const e = end; // e == total
962
- const cs = b[e - 2]; // csIdx
963
- const s = sum8(b, 0, e - 2);
964
- return cs === s;
963
+ const e = end; // e == total
964
+ const cs = b[e - 2]; // CS 在倒数第2字节
965
+
966
+ // full 模式
967
+ const sFull = sum8(b, 0, e - 2);
968
+ if (cs === sFull) return { ok: true, mode: 'full', calc: sFull };
969
+
970
+ // std 模式(需确保第二个 0x68 存在于固定位置7)
971
+ if (b.length >= 10 && b[7] === 0x68) {
972
+ // 第二个 0x68 后开始:CTRL(8) + LEN(9) + DATA...
973
+ const sStd = sum8(b, 8, e - 2);
974
+ if (cs === sStd) return { ok: true, mode: 'std', calc: sStd };
975
+ return { ok: false, mode: 'std', calc: sStd, calc_full: sFull };
976
+ }
977
+ return { ok: false, mode: 'full', calc: sFull };
965
978
  };
966
979
 
967
980
  // 1) 优先按 LEN 快速命中
968
- if (b.length >= total && b[endIdx] === 0x16 && csOK(total)) {
969
- return { ok: true, used: feCount + total, frame: input.slice(0, feCount + total) };
981
+ if (b.length >= total && b[endIdx] === 0x16) {
982
+ const ck = csOK(total);
983
+ if (ck && ck.ok) {
984
+ const frame = input.slice(0, feCount + total);
985
+ // 透出校验模式,便于排障:部分设备使用 std 模式
986
+ try {
987
+ frame._meta = Object.assign({}, frame._meta || {}, {
988
+ proto: '645',
989
+ cs_mode: ck.mode,
990
+ cs_calc: ck.calc
991
+ });
992
+ } catch (e) { }
993
+ return { ok: true, used: feCount + total, frame };
994
+ }
970
995
  }
971
996
 
972
997
  // 2) 回溯:向后找 0x16 做候选结尾,再校验 CS(兼容异常 LEN/粘包)
@@ -974,10 +999,17 @@ module.exports = function (RED) {
974
999
  if (b[end - 1] !== 0x16) continue;
975
1000
  // 候选帧至少要有 …… + CS + 16,因此 end >= 13 且 csIdx=end-2 >= 0
976
1001
  if (end >= 13) {
977
- const cs = b[end - 2];
978
- const s = sum8(b, 0, end - 2);
979
- if (cs === s) {
980
- return { ok: true, used: feCount + end, frame: input.slice(0, feCount + end) };
1002
+ const ck = csOK(end);
1003
+ if (ck && ck.ok) {
1004
+ const frame = input.slice(0, feCount + end);
1005
+ try {
1006
+ frame._meta = Object.assign({}, frame._meta || {}, {
1007
+ proto: '645',
1008
+ cs_mode: ck.mode,
1009
+ cs_calc: ck.calc
1010
+ });
1011
+ } catch (e) { }
1012
+ return { ok: true, used: feCount + end, frame };
981
1013
  }
982
1014
  }
983
1015
  }
@@ -994,9 +1026,6 @@ module.exports = function (RED) {
994
1026
  // 剥离前导 FE
995
1027
  const b = stripFE(input);
996
1028
  if (b.length < 6 || b[0] !== 0x68) return { ok: false };
997
- // 避免把 645 错误识别为 698-LEN(645 在 b[7] 处固定为 0x68)
998
- if (b.length >= 8 && b[7] === 0x68) return { ok: false };
999
-
1000
1029
  // ---- 严格按长度域 L 判帧 ----
1001
1030
  const LL = b[1] >>> 0;
1002
1031
  const LH = b[2] >>> 0;
@@ -1016,20 +1045,43 @@ module.exports = function (RED) {
1016
1045
  return { ok: false, used: 1, frame: b.slice(0, 1), err: "698_LEN_BAD_END" };
1017
1046
  }
1018
1047
 
1019
- // FCS 校验(CRC‑16/X.25),计算区间:[LL..FCS 前一字节]
1048
+ // FCS 校验(CRC‑16/X.25),FCS 在 0x16 前两字节(低字节在前)
1049
+ // 不同设备实现存在“覆盖起点差异”:
1050
+ // - 常见实现A:从 0x68 开始到 FCS 前(不含FCS与16)
1051
+ // - 常见实现B:从长度域(LL)开始到 FCS 前
1020
1052
  const fcsLo = b[expectedEnd - 2];
1021
1053
  const fcsHi = b[expectedEnd - 1];
1022
1054
  const fcs = (fcsHi << 8) | fcsLo;
1023
- const calc = crc16x25(b, 1, expectedEnd - 2);
1024
- if (calc !== fcs) {
1025
- return { ok: false, used: 1, frame: b.slice(0, 1), err: "698_LEN_FCS_FAIL" };
1055
+
1056
+ const calcA = crc16x25(b, 0, expectedEnd - 2); // 覆盖 [0 .. FCS前)
1057
+ const calcB = crc16x25(b, 1, expectedEnd - 2); // 覆盖 [LL .. FCS前)
1058
+ const fcsOK = (calcA === fcs) || (calcB === fcs);
1059
+
1060
+ // 重要:即便 FCS 不通过,也返回“可截帧”的结果,避免被 645 误判卡死
1061
+ // 上层可通过 msgout.fcs_ok / reason 做告警与进一步排查
1062
+ if (!fcsOK) {
1063
+ return {
1064
+ ok: true,
1065
+ used: expectedEnd + 1,
1066
+ frame: b.slice(0, expectedEnd + 1),
1067
+ fcs_ok: false,
1068
+ fcs_frame: fcs,
1069
+ fcs_calc_a: calcA,
1070
+ fcs_calc_b: calcB,
1071
+ err: "698_LEN_FCS_FAIL"
1072
+ };
1026
1073
  }
1027
1074
 
1028
1075
  // (可选)HCS 校验:从 LL 开始至 HCS 前一字节
1029
1076
  // 按 698.45,HCS 位于地址域之后;为避免复杂度,这里不强制校验 HCS。
1030
1077
  // 仅在需要时可补:解析地址域长度后再计算 HCS。
1031
1078
 
1032
- return { ok: true, used: expectedEnd + 1, frame: b.slice(0, expectedEnd + 1) };
1079
+ return {
1080
+ ok: true,
1081
+ used: expectedEnd + 1,
1082
+ frame: b.slice(0, expectedEnd + 1),
1083
+ fcs_ok: true
1084
+ };
1033
1085
  }
1034
1086
 
1035
1087
  // 698(HDLC):7E ... [FCS(lo,hi)] 7E,支持 0x7D 转义与 X.25 FCS
@@ -1085,13 +1137,29 @@ module.exports = function (RED) {
1085
1137
  // if (!r.ok) r = tryParse645(assembleBuf);
1086
1138
  // if (!r.ok) r = tryParse698Len(assembleBuf);
1087
1139
 
1088
- // --fix-20251108-先 BLE(7E7E7E5A…7EA5),再 698-HDLC,再 645,再 698-LEN
1140
+ // --fix-20251213-先 BLE(7E7E7E5A…7EA5),再 698-HDLC,再 698-LEN,再 645
1141
+ // 关键:698-LEN 必须优先于 645,否则遇到 698 帧内出现 0x68(如示例报文第7字节)会被 645 误判并卡住
1089
1142
  let r = tryParseBleGNW(assembleBuf);
1090
1143
  if (!r.ok) r = tryParse698HDLC(assembleBuf);
1091
- if (!r.ok) r = tryParse645(assembleBuf);
1092
1144
  if (!r.ok) r = tryParse698Len(assembleBuf);
1145
+ if (!r.ok) r = tryParse645(assembleBuf);
1093
1146
 
1094
1147
  if (r.ok) {
1148
+ // 附加解析元数据(例如 698 FCS 校验结果),供上层排障
1149
+ if (r && typeof r === 'object') {
1150
+ try {
1151
+ r.frame._meta = {
1152
+ proto: r.proto || undefined,
1153
+ fcs_ok: (typeof r.fcs_ok === 'boolean') ? r.fcs_ok : undefined,
1154
+ err: r.err || undefined,
1155
+ fcs_frame: r.fcs_frame,
1156
+ fcs_calc_a: r.fcs_calc_a,
1157
+ fcs_calc_b: r.fcs_calc_b
1158
+ };
1159
+ } catch (e) {
1160
+ // ignore
1161
+ }
1162
+ }
1095
1163
  emitOk(r.frame);
1096
1164
  assembleBuf = assembleBuf.slice(r.used);
1097
1165
  continue;
@@ -1138,6 +1206,14 @@ module.exports = function (RED) {
1138
1206
  msgout.payload = m;
1139
1207
  msgout.port = port;
1140
1208
  msgout.status = "OK";
1209
+
1210
+ // 若解析器附带了元数据(例如 698 FCS 校验结果),一并输出
1211
+ if (frameBuf && frameBuf._meta) {
1212
+ msgout.frame_meta = frameBuf._meta;
1213
+ // 便于后续 GC:避免 assembleBuf 长期引用
1214
+ try { delete frameBuf._meta; } catch (e) { }
1215
+ }
1216
+
1141
1217
  obj._emitter.emit('data', msgout, last_sender);
1142
1218
  },
1143
1219
  // 错误帧(已有边界但校验/结束符不通过)