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.
- package/package.json +1 -1
- package/zserial.js +81 -24
package/package.json
CHANGED
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
|
-
|
|
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
|
-
//
|
|
1037
|
-
let
|
|
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
|
-
//
|
|
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
|
-
|
|
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 校验(CRC
|
|
1049
|
-
//
|
|
1050
|
-
|
|
1051
|
-
|
|
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
|
-
|
|
1057
|
-
|
|
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
|
-
//
|
|
1061
|
-
//
|
|
1094
|
+
// 注意:FCS 不通过时不能返回 ok:true(否则会“强制截帧”吞掉其它协议帧)
|
|
1095
|
+
// 但为了防止卡死,这里仍消费掉该段,并作为错误帧上报给 emitErr。
|
|
1062
1096
|
if (!fcsOK) {
|
|
1063
1097
|
return {
|
|
1064
|
-
ok:
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
}
|