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.
- package/package.json +1 -1
- package/zserial.js +98 -22
package/package.json
CHANGED
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
|
|
959
|
+
// CS 双模校验:
|
|
960
|
+
// - full:从第一个 0x68 起累加到 CS 前(不含 CS)
|
|
961
|
+
// - std :从第二个 0x68 后(C+L+DATA)累加到 CS 前
|
|
960
962
|
const csOK = (end) => {
|
|
961
|
-
const e = end;
|
|
962
|
-
const cs = b[e - 2]; //
|
|
963
|
-
|
|
964
|
-
|
|
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
|
|
969
|
-
|
|
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
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
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
|
|
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
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
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 {
|
|
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-
|
|
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
|
// 错误帧(已有边界但校验/结束符不通过)
|