node-zserial 1.0.31 → 1.0.33

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 +2 -2
  2. package/zserial.js +41 -21
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "node-zserial",
3
- "version": "1.0.31",
3
+ "version": "1.0.33",
4
4
  "description": "Node-RED nodes to talk to serial ports",
5
5
  "dependencies": {
6
6
  "serialport": "^12.0.0"
7
7
  },
8
8
  "repository": {
9
9
  "type": "git",
10
- "url": "https://github.com/bensonzhow/node-red-zserialport.git"
10
+ "url": "git+https://github.com/bensonzhow/node-red-zserialport.git"
11
11
  },
12
12
  "keywords": [
13
13
  "node-red",
package/zserial.js CHANGED
@@ -1035,8 +1035,10 @@ module.exports = function (RED) {
1035
1035
 
1036
1036
  // 698(68-LEN 变体):68 LL LH C ... DATA ... FCS(2) 16 ;兼容 FE* 前导
1037
1037
  function tryParse698Len(input) {
1038
- // 剥离前导 FE(注意:这里只做 698-LEN 侧的兼容;645 另有自己的 FE 处理)
1039
- const b = stripFE(input);
1038
+ // 统计 FE 前导(用于 used/frame 回到原始 input)
1039
+ let feCount = 0;
1040
+ while (feCount < input.length && input[feCount] === 0xFE) feCount++;
1041
+ const b = input.slice(feCount);
1040
1042
  if (b.length < 6 || b[0] !== 0x68) return { ok: false };
1041
1043
 
1042
1044
  // ---- 读取长度域 ----
@@ -1072,7 +1074,7 @@ module.exports = function (RED) {
1072
1074
  // 掉电次数等业务回包通常远小于 2KB;超出则高度可疑,丢 1 字节推进重同步
1073
1075
  const MAX_698_LEN_L = 2048;
1074
1076
  if (L > MAX_698_LEN_L) {
1075
- return { ok: false, used: 1, frame: b.slice(0, 1), err: "698_LEN_TOO_LARGE" };
1077
+ return { ok: false, used: feCount + 1, frame: input.slice(0, feCount + 1), err: "698_LEN_TOO_LARGE" };
1076
1078
  }
1077
1079
 
1078
1080
  // 约束 2:控制域 C 在 b[3],不可能是 0x16(结束符)
@@ -1080,6 +1082,15 @@ module.exports = function (RED) {
1080
1082
  if (b.length >= 4 && b[3] === 0x16) {
1081
1083
  return { ok: false };
1082
1084
  }
1085
+ // 约束 3:控制域 C 必须看起来像 698 的控制域(功能码通常为 1=链路管理 或 3=用户数据)
1086
+ // 目的:避免 645 的地址字节被当成 698 控制域,从而把 645 整帧误判为 698-LEN 候选帧并被消费掉。
1087
+ // 698 控制域:bit0..bit3 为功能码(1 或 3 最常见);其他位为 DIR/PRM/分帧/扰码标志。
1088
+ const C = b[3] >>> 0;
1089
+ const func = C & 0x0F;
1090
+ if (!(func === 0x01 || func === 0x03)) {
1091
+ // 不消费任何字节,让 645 解析器继续尝试
1092
+ return { ok: false };
1093
+ }
1083
1094
  // ------------------------------------------------------------------------
1084
1095
 
1085
1096
  // 期望 0x16 的位置(相对 b 起点):1 + L
@@ -1093,7 +1104,7 @@ module.exports = function (RED) {
1093
1104
  // 若 expectedEnd 位置不是 0x16,说明起点错位或存在脏字节
1094
1105
  // 这里消费 1 字节,避免死循环卡住
1095
1106
  if (b[expectedEnd] !== 0x16) {
1096
- return { ok: false, used: 1, frame: b.slice(0, 1), err: "698_LEN_BAD_END" };
1107
+ return { ok: false, used: feCount + 1, frame: input.slice(0, feCount + 1), err: "698_LEN_BAD_END" };
1097
1108
  }
1098
1109
 
1099
1110
  // ------------------------ FCS 校验(CRC-16/X.25) ------------------------
@@ -1117,8 +1128,8 @@ module.exports = function (RED) {
1117
1128
  if (!fcsOK) {
1118
1129
  return {
1119
1130
  ok: false,
1120
- used: expectedEnd + 1,
1121
- frame: b.slice(0, expectedEnd + 1),
1131
+ used: feCount + expectedEnd + 1,
1132
+ frame: input.slice(0, feCount + expectedEnd + 1),
1122
1133
  fcs_ok: false,
1123
1134
  fcs_frame: fcs,
1124
1135
  fcs_calc_a: calcA,
@@ -1130,8 +1141,8 @@ module.exports = function (RED) {
1130
1141
  // FCS 通过:返回完整帧(含 0x68..0x16)
1131
1142
  return {
1132
1143
  ok: true,
1133
- used: expectedEnd + 1,
1134
- frame: b.slice(0, expectedEnd + 1),
1144
+ used: feCount + expectedEnd + 1,
1145
+ frame: input.slice(0, feCount + expectedEnd + 1),
1135
1146
  fcs_ok: true
1136
1147
  };
1137
1148
  }
@@ -1222,34 +1233,43 @@ module.exports = function (RED) {
1222
1233
  continue;
1223
1234
  }
1224
1235
  if (r.used) {
1225
- // 如果 parser 返回了“可消费”的错误帧(例如 698-LEN FCS_FAIL),
1226
- // 这里把诊断信息写入 frame._meta,供上层日志/排障使用。
1227
- if (r && typeof r === 'object') {
1236
+ // resync/drop1 类(通常 used 很小)不应上报 ERR_FRAME,否则会产生大量重复输出
1237
+ const used = r.used >>> 0;
1238
+ const frameBuf = r.frame ? Buffer.from(r.frame) : null;
1239
+ const frameLen = frameBuf ? frameBuf.length : 0;
1240
+
1241
+ // 判定是否为“仅推进同步点”的消费:
1242
+ // - used == 1 或 frameLen <= 1:典型 drop1
1243
+ // - 或 err 属于已知的 resync 类原因
1244
+ const errCode = (r && r.err) ? String(r.err) : "";
1245
+ const isResyncDrop = (used <= 1) || (frameLen <= 1) ||
1246
+ errCode === "DROP_7E_BADFRAME" ||
1247
+ errCode === "645_BAD_SHAPE_DROP1" ||
1248
+ errCode === "698_LEN_BAD_END" ||
1249
+ errCode === "698_LEN_TOO_LARGE" ||
1250
+ errCode === "645_SECOND_68_NOT_FOUND";
1251
+
1252
+ if (!isResyncDrop) {
1253
+ // 只有“闭合候选帧但校验/结构失败”的情况才上报 ERR_FRAME
1228
1254
  try {
1229
1255
  if (r.frame) {
1230
- // 统一把元数据挂到 frame 上,避免引用未定义变量导致运行期异常
1231
1256
  r.frame._meta = Object.assign({}, r.frame._meta || {}, {
1232
- // proto:用于上层区分到底是 645 / 698-hdlc / 698-len / ble 等
1233
1257
  proto: (r.fcs_ok === false) ? '698-len' : (r.proto || undefined),
1234
- // FCS 校验信息(仅当 698-LEN 校验失败时有意义)
1235
1258
  fcs_ok: (typeof r.fcs_ok === 'boolean') ? r.fcs_ok : undefined,
1236
1259
  fcs_frame: r.fcs_frame,
1237
1260
  fcs_calc_a: r.fcs_calc_a,
1238
1261
  fcs_calc_b: r.fcs_calc_b,
1239
- // err:更精确的错误原因
1240
1262
  err: r.err || "FRAME_INVALID"
1241
1263
  });
1242
1264
  }
1243
1265
  } catch (e) {
1244
- // ignore:不要因为诊断信息影响主流程
1266
+ // ignore
1245
1267
  }
1268
+ emitErr(r.frame, errCode || "FRAME_INVALID");
1246
1269
  }
1247
1270
 
1248
- // 错误帧统一走 emitErr,reason 以 r.err 为准
1249
- emitErr(r.frame, (r && r.err) ? r.err : "FRAME_INVALID");
1250
-
1251
- // 消费掉该段,防止 assembleBuf 卡死
1252
- assembleBuf = assembleBuf.slice(r.used);
1271
+ // 无论是否上报,都必须消费,防止 assembleBuf 卡死
1272
+ assembleBuf = assembleBuf.slice(used);
1253
1273
  continue;
1254
1274
  }
1255
1275
  break; // 需要更多数据