node-zserial 1.0.28 → 1.0.30
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 +57 -6
package/package.json
CHANGED
package/zserial.js
CHANGED
|
@@ -1091,18 +1091,19 @@ module.exports = function (RED) {
|
|
|
1091
1091
|
const calcB = crc16x25(b, 1, expectedEnd - 2);
|
|
1092
1092
|
const fcsOK = (calcA === fcs) || (calcB === fcs);
|
|
1093
1093
|
|
|
1094
|
-
//
|
|
1095
|
-
//
|
|
1094
|
+
// 现场经常遇到:帧内容完整(68..16 边界正确),但 FCS 因链路噪声/串口转换器问题偶发不一致。
|
|
1095
|
+
// 若此处直接判为错误帧并 dequeue,会导致上层“偶尔无法解码”(尤其是请求-响应严格匹配的场景)。
|
|
1096
|
+
// 因此:当边界与长度域一致时,允许“容错通过”,并在 _meta 中标记 fcs_ok=false 供上层排障。
|
|
1096
1097
|
if (!fcsOK) {
|
|
1097
1098
|
return {
|
|
1098
|
-
ok:
|
|
1099
|
+
ok: true,
|
|
1099
1100
|
used: expectedEnd + 1,
|
|
1100
1101
|
frame: b.slice(0, expectedEnd + 1),
|
|
1101
1102
|
fcs_ok: false,
|
|
1102
1103
|
fcs_frame: fcs,
|
|
1103
1104
|
fcs_calc_a: calcA,
|
|
1104
1105
|
fcs_calc_b: calcB,
|
|
1105
|
-
err: "
|
|
1106
|
+
err: "698_LEN_FCS_FAIL_TOLERATED"
|
|
1106
1107
|
};
|
|
1107
1108
|
}
|
|
1108
1109
|
|
|
@@ -1161,6 +1162,53 @@ module.exports = function (RED) {
|
|
|
1161
1162
|
assembleBuf = assembleBuf.slice(k);
|
|
1162
1163
|
}
|
|
1163
1164
|
|
|
1165
|
+
// 若缓存以 0x68 开始但迟迟无法凑齐完整帧,且中途出现新一轮 0xFE 0xFE 0xFE 0xFE 前导,
|
|
1166
|
+
// 说明上一帧很可能被截断/丢字节(现场常见:串口/转换器插入前导或上层超时提前取走)。
|
|
1167
|
+
// 这种情况下必须“强制丢弃”残片并从新的 FE 前导重新同步,否则会一直卡住导致后续帧都解析不出来。
|
|
1168
|
+
function findFeRun4(buf) {
|
|
1169
|
+
// 查找连续 4 个 0xFE 的起始位置
|
|
1170
|
+
for (let i = 0; i <= buf.length - 4; i++) {
|
|
1171
|
+
if (buf[i] === 0xFE && buf[i + 1] === 0xFE && buf[i + 2] === 0xFE && buf[i + 3] === 0xFE) return i;
|
|
1172
|
+
}
|
|
1173
|
+
return -1;
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
// 仅对 698-LEN(0x68...0x16)这种“依赖长度域”的帧做强制重同步处理
|
|
1177
|
+
if (assembleBuf.length >= 4 && assembleBuf[0] === 0x68) {
|
|
1178
|
+
const LL0 = assembleBuf[1] >>> 0;
|
|
1179
|
+
const LH0 = assembleBuf[2] >>> 0;
|
|
1180
|
+
let Lraw0 = (LH0 << 8) | LL0;
|
|
1181
|
+
const isKB0 = (Lraw0 & 0x4000) !== 0;
|
|
1182
|
+
Lraw0 &= 0x3FFF;
|
|
1183
|
+
const L0 = isKB0 ? (Lraw0 << 10) : Lraw0;
|
|
1184
|
+
const expectedEnd0 = 1 + L0;
|
|
1185
|
+
|
|
1186
|
+
// 只有在“明显是长帧但当前数据不足”时才触发该策略
|
|
1187
|
+
if (L0 >= 6 && assembleBuf.length < expectedEnd0 + 1) {
|
|
1188
|
+
const fePos = findFeRun4(assembleBuf);
|
|
1189
|
+
if (fePos > 0) {
|
|
1190
|
+
// fePos 之前的内容属于上一帧残片,作为错误帧上报并丢弃;
|
|
1191
|
+
// 然后保留从 FE 开始的内容,留给下一轮解析。
|
|
1192
|
+
const bad = assembleBuf.slice(0, fePos);
|
|
1193
|
+
emitErr(Buffer.from(bad), "FRAME_TRUNCATED_RESYNC_FE");
|
|
1194
|
+
assembleBuf = assembleBuf.slice(fePos);
|
|
1195
|
+
|
|
1196
|
+
// 重新执行一次前导剔噪/FE 剥离(与上面逻辑一致)
|
|
1197
|
+
let ss = 0;
|
|
1198
|
+
while (ss < assembleBuf.length) {
|
|
1199
|
+
const cc = assembleBuf[ss];
|
|
1200
|
+
if (cc === 0xFE || cc === 0x68 || cc === 0x7E) break;
|
|
1201
|
+
ss++;
|
|
1202
|
+
}
|
|
1203
|
+
if (ss > 0) assembleBuf = assembleBuf.slice(ss);
|
|
1204
|
+
if (assembleBuf.length && assembleBuf[0] === 0xFE) {
|
|
1205
|
+
let kk = 0; while (kk < assembleBuf.length && assembleBuf[kk] === 0xFE) kk++;
|
|
1206
|
+
assembleBuf = assembleBuf.slice(kk);
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1164
1212
|
// 抽帧
|
|
1165
1213
|
while (assembleBuf.length >= 5) {
|
|
1166
1214
|
// // 优先 HDLC,再 645,再 698-Len(避免误判)
|
|
@@ -1255,12 +1303,14 @@ module.exports = function (RED) {
|
|
|
1255
1303
|
d,
|
|
1256
1304
|
// 完整 OK 帧
|
|
1257
1305
|
function (frameBuf) {
|
|
1306
|
+
// frame 模式下,务必保持 Buffer 原样输出(避免 0x00 等字节在字符串链路中被截断/损坏)
|
|
1307
|
+
// 同时附带 payload_hex 便于日志/排障。
|
|
1258
1308
|
var m = Buffer.from(frameBuf);
|
|
1259
1309
|
var last_sender = null;
|
|
1260
1310
|
if (obj.queue.length) { last_sender = obj.queue[0].sender; }
|
|
1261
1311
|
var msgout = obj.dequeue() || {};
|
|
1262
|
-
if (binoutput !== "bin") { m = m.toString(); }
|
|
1263
1312
|
msgout.payload = m;
|
|
1313
|
+
msgout.payload_hex = m.toString('hex').toUpperCase();
|
|
1264
1314
|
msgout.port = port;
|
|
1265
1315
|
msgout.status = "OK";
|
|
1266
1316
|
|
|
@@ -1275,12 +1325,13 @@ module.exports = function (RED) {
|
|
|
1275
1325
|
},
|
|
1276
1326
|
// 错误帧(已有边界但校验/结束符不通过)
|
|
1277
1327
|
function (badBuf, reason) {
|
|
1328
|
+
// 错误帧同样保持 Buffer 输出,并提供 HEX 字符串,便于定位截断点/前导位置
|
|
1278
1329
|
var m = Buffer.from(badBuf);
|
|
1279
1330
|
var last_sender = null;
|
|
1280
1331
|
if (obj.queue.length) { last_sender = obj.queue[0].sender; }
|
|
1281
1332
|
var msgout = obj.dequeue() || {};
|
|
1282
|
-
if (binoutput !== "bin") { m = m.toString(); }
|
|
1283
1333
|
msgout.payload = m;
|
|
1334
|
+
msgout.payload_hex = m.toString('hex').toUpperCase();
|
|
1284
1335
|
msgout.port = port;
|
|
1285
1336
|
msgout.status = "ERR_FRAME";
|
|
1286
1337
|
msgout.reason = reason || "FRAME_INVALID";
|