node-zserial 1.0.27 → 1.0.28

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 +81 -24
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-zserial",
3
- "version": "1.0.27",
3
+ "version": "1.0.28",
4
4
  "description": "Node-RED nodes to talk to serial ports",
5
5
  "dependencies": {
6
6
  "serialport": "^12.0.0"
package/zserial.js CHANGED
@@ -1023,45 +1023,79 @@ module.exports = function (RED) {
1023
1023
 
1024
1024
  // 698(68-LEN 变体):68 LL LH C ... DATA ... FCS(2) 16 ;兼容 FE* 前导
1025
1025
  function tryParse698Len(input) {
1026
- // 剥离前导 FE
1026
+ // 剥离前导 FE(注意:这里只做 698-LEN 侧的兼容;645 另有自己的 FE 处理)
1027
1027
  const b = stripFE(input);
1028
1028
  if (b.length < 6 || b[0] !== 0x68) return { ok: false };
1029
- // ---- 严格按长度域 L 判帧 ----
1029
+
1030
+ // ---- 读取长度域 ----
1031
+ // 68 [LL] [LH] [C] ... [FCS(lo)] [FCS(hi)] 16
1030
1032
  const LL = b[1] >>> 0;
1031
1033
  const LH = b[2] >>> 0;
1032
- let Lraw = (LH << 8) | LL; // 含单位位、保留位
1033
- const isKB = (Lraw & 0x4000) !== 0; // bit14:单位位(0=字节,1=KB)
1034
- Lraw &= 0x3FFF; // 清除单位位与保留位,仅保留长度值
1035
1034
 
1036
- // 仅支持常见“字节”为单位的场景;若为 KB 单位,展开至字节
1037
- let L = isKB ? (Lraw << 10) : Lraw; // KB -> *1024
1035
+ // Lraw 含单位位/保留位:bit14 为单位位(0=字节,1=KB
1036
+ let Lraw = (LH << 8) | LL;
1037
+ const isKB = (Lraw & 0x4000) !== 0;
1038
+ Lraw &= 0x3FFF; // 清掉单位位/保留位,只留下数值
1039
+
1040
+ // 绝大多数场景长度单位为“字节”;若遇到 KB 单位则折算
1041
+ const L = isKB ? (Lraw << 10) : Lraw; // KB -> *1024
1042
+
1043
+ // ------------------------ 关键防误判(核心修复点) ------------------------
1044
+ // 误判根因:645 帧形态为 68 + 6字节地址 + 68 ...,地址字节中“偶然出现 0x16”
1045
+ // 例如:68 02 80 16 00 00 00 68 ...
1046
+ // 若把 LL=0x02、LH=0x80 解读为 698-LEN 长度,则 L=2,expectedEnd=3,b[3]=0x16
1047
+ // 从而被 698-LEN 误判为 “68 02 80 16” 一帧,导致吞掉 645 的帧头并产生残片。
1048
+
1049
+ // 约束 1:698-LEN 的 L 不可能极短(至少应包含:C + 头字段 + FCS(2))
1050
+ // 这里给经验下限 6(偏保守,既能挡住 L=2,又尽量不误伤极端设备)
1051
+ // 若现场仍有碰撞,可把下限提高到 8 或 10(建议先保留 6)。
1052
+ const MIN_698_LEN_L = 6;
1053
+ if (L < MIN_698_LEN_L) {
1054
+ // 不消费任何字节,让后续 parser(如 645)继续尝试
1055
+ return { ok: false };
1056
+ }
1038
1057
 
1039
- // 期望 0x16 的位置(相对 b 的起点):1 + L
1058
+ // 约束 2:控制域 C b[3],不可能是 0x16(结束符)
1059
+ // 若 b[3] == 0x16,几乎必然是 645 地址域碰撞导致的误判
1060
+ if (b.length >= 4 && b[3] === 0x16) {
1061
+ return { ok: false };
1062
+ }
1063
+ // ------------------------------------------------------------------------
1064
+
1065
+ // 期望 0x16 的位置(相对 b 起点):1 + L
1066
+ // 解释:L 表示从 LL 开始到 FCS(含) 之前的长度?各厂实现略有差异
1067
+ // 你现有逻辑采用 expectedEnd = 1 + L,并要求 b[expectedEnd] == 0x16
1040
1068
  const expectedEnd = 1 + L;
1041
- // 缓冲区数据还不够,继续累计
1069
+
1070
+ // 半包:继续累积
1042
1071
  if (b.length < expectedEnd + 1) return { ok: false };
1043
- // 不是以 0x16 结尾,说明前面有脏字节或起点错位,丢弃 1 字节继续
1072
+
1073
+ // 若 expectedEnd 位置不是 0x16,说明起点错位或存在脏字节
1074
+ // 这里消费 1 字节,避免死循环卡住
1044
1075
  if (b[expectedEnd] !== 0x16) {
1045
1076
  return { ok: false, used: 1, frame: b.slice(0, 1), err: "698_LEN_BAD_END" };
1046
1077
  }
1047
1078
 
1048
- // FCS 校验(CRC16/X.25),FCS 在 0x16 前两字节(低字节在前)
1049
- // 不同设备实现存在“覆盖起点差异”:
1050
- // - 常见实现A:从 0x68 开始到 FCS 前(不含FCS与16)
1051
- // - 常见实现B:从长度域(LL)开始到 FCS 前
1079
+ // ------------------------ FCS 校验(CRC-16/X.25 ------------------------
1080
+ // FCS 位于 0x16 前两字节,低字节在前(lo, hi)
1081
+ if (expectedEnd - 2 < 0) return { ok: false };
1082
+
1052
1083
  const fcsLo = b[expectedEnd - 2];
1053
1084
  const fcsHi = b[expectedEnd - 1];
1054
1085
  const fcs = (fcsHi << 8) | fcsLo;
1055
1086
 
1056
- const calcA = crc16x25(b, 0, expectedEnd - 2); // 覆盖 [0 .. FCS前)
1057
- const calcB = crc16x25(b, 1, expectedEnd - 2); // 覆盖 [LL .. FCS前)
1087
+ // 不同实现对“CRC 覆盖范围”存在差异,常见两种:
1088
+ // A) 0x68 开始算到 FCS 前(不含 FCS 0x16)
1089
+ // B) 从 LL 开始算到 FCS 前(不含 FCS 与 0x16)
1090
+ const calcA = crc16x25(b, 0, expectedEnd - 2);
1091
+ const calcB = crc16x25(b, 1, expectedEnd - 2);
1058
1092
  const fcsOK = (calcA === fcs) || (calcB === fcs);
1059
1093
 
1060
- // 重要:即便 FCS 不通过,也返回“可截帧”的结果,避免被 645 误判卡死
1061
- // 上层可通过 msgout.fcs_ok / reason 做告警与进一步排查
1094
+ // 注意:FCS 不通过时不能返回 ok:true(否则会“强制截帧”吞掉其它协议帧)
1095
+ // 但为了防止卡死,这里仍消费掉该段,并作为错误帧上报给 emitErr。
1062
1096
  if (!fcsOK) {
1063
1097
  return {
1064
- ok: true,
1098
+ ok: false,
1065
1099
  used: expectedEnd + 1,
1066
1100
  frame: b.slice(0, expectedEnd + 1),
1067
1101
  fcs_ok: false,
@@ -1072,10 +1106,7 @@ module.exports = function (RED) {
1072
1106
  };
1073
1107
  }
1074
1108
 
1075
- // (可选)HCS 校验:从 LL 开始至 HCS 前一字节
1076
- // 按 698.45,HCS 位于地址域之后;为避免复杂度,这里不强制校验 HCS。
1077
- // 仅在需要时可补:解析地址域长度后再计算 HCS。
1078
-
1109
+ // FCS 通过:返回完整帧(含 0x68..0x16)
1079
1110
  return {
1080
1111
  ok: true,
1081
1112
  used: expectedEnd + 1,
@@ -1165,7 +1196,33 @@ module.exports = function (RED) {
1165
1196
  continue;
1166
1197
  }
1167
1198
  if (r.used) {
1168
- emitErr(r.frame, r.err || "FRAME_INVALID");
1199
+ // 如果 parser 返回了“可消费”的错误帧(例如 698-LEN FCS_FAIL),
1200
+ // 这里把诊断信息写入 frame._meta,供上层日志/排障使用。
1201
+ if (r && typeof r === 'object') {
1202
+ try {
1203
+ if (r.frame) {
1204
+ // 统一把元数据挂到 frame 上,避免引用未定义变量导致运行期异常
1205
+ r.frame._meta = Object.assign({}, r.frame._meta || {}, {
1206
+ // proto:用于上层区分到底是 645 / 698-hdlc / 698-len / ble 等
1207
+ proto: (r.fcs_ok === false) ? '698-len' : (r.proto || undefined),
1208
+ // FCS 校验信息(仅当 698-LEN 校验失败时有意义)
1209
+ fcs_ok: (typeof r.fcs_ok === 'boolean') ? r.fcs_ok : undefined,
1210
+ fcs_frame: r.fcs_frame,
1211
+ fcs_calc_a: r.fcs_calc_a,
1212
+ fcs_calc_b: r.fcs_calc_b,
1213
+ // err:更精确的错误原因
1214
+ err: r.err || "FRAME_INVALID"
1215
+ });
1216
+ }
1217
+ } catch (e) {
1218
+ // ignore:不要因为诊断信息影响主流程
1219
+ }
1220
+ }
1221
+
1222
+ // 错误帧统一走 emitErr,reason 以 r.err 为准
1223
+ emitErr(r.frame, (r && r.err) ? r.err : "FRAME_INVALID");
1224
+
1225
+ // 消费掉该段,防止 assembleBuf 卡死
1169
1226
  assembleBuf = assembleBuf.slice(r.used);
1170
1227
  continue;
1171
1228
  }