node-zserial 1.0.35 → 1.0.37
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 +101 -97
package/package.json
CHANGED
package/zserial.js
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
1
|
|
|
2
|
-
/**
|
|
3
|
-
*
|
|
4
|
-
* version: 1.0.34
|
|
5
|
-
*/
|
|
6
2
|
module.exports = function (RED) {
|
|
7
3
|
/*jshint -W082 */
|
|
8
4
|
"use strict";
|
|
@@ -356,8 +352,7 @@ module.exports = function (RED) {
|
|
|
356
352
|
err.sendPayload = err.payload;
|
|
357
353
|
err.payload = ''
|
|
358
354
|
}
|
|
359
|
-
|
|
360
|
-
node.errorMsg[port] = Object.assign(serialConfig,err)
|
|
355
|
+
node.errorMsg[port] = err
|
|
361
356
|
node.send([null, node._msg, null]);
|
|
362
357
|
}
|
|
363
358
|
sendAll(done);
|
|
@@ -374,7 +369,7 @@ module.exports = function (RED) {
|
|
|
374
369
|
for (var i = 0; i < msg.serialConfigs.length; i++) {
|
|
375
370
|
var serialConfig = msg.serialConfigs[i];
|
|
376
371
|
serialConfig._msgid = msg._msgid + "_" + i;
|
|
377
|
-
getSerialServer(msg, serialConfig, done);
|
|
372
|
+
getSerialServer( msg, serialConfig, done);
|
|
378
373
|
}
|
|
379
374
|
}
|
|
380
375
|
|
|
@@ -557,12 +552,12 @@ module.exports = function (RED) {
|
|
|
557
552
|
})
|
|
558
553
|
}
|
|
559
554
|
|
|
560
|
-
function afterClosed(port)
|
|
555
|
+
function afterClosed(port){
|
|
561
556
|
node[`_dataHandler_${port}`] = null
|
|
562
557
|
node[`_timeoutHandler_${port}`] = null
|
|
563
558
|
}
|
|
564
559
|
|
|
565
|
-
if
|
|
560
|
+
if(!node._afterClosed){
|
|
566
561
|
node._afterClosed = afterClosed;
|
|
567
562
|
serialPool.on('afterClosed', node._afterClosed);
|
|
568
563
|
}
|
|
@@ -650,6 +645,7 @@ module.exports = function (RED) {
|
|
|
650
645
|
addchar = addchar.replace("\\n", "\n").replace("\\r", "\r").replace("\\t", "\t").replace("\\e", "\e").replace("\\f", "\f").replace("\\0", "\0"); // jshint ignore:line
|
|
651
646
|
|
|
652
647
|
if (addchar.substr(0, 2) == "0x") { addchar = new Buffer.from([addchar]); }
|
|
648
|
+
let assembleBuf = Buffer.alloc(0);
|
|
653
649
|
connections[id] = (function () {
|
|
654
650
|
var obj = {
|
|
655
651
|
_emitter: new events.EventEmitter(),
|
|
@@ -660,11 +656,12 @@ module.exports = function (RED) {
|
|
|
660
656
|
_retryNum: 0,
|
|
661
657
|
tout: null,
|
|
662
658
|
queue: [],
|
|
663
|
-
//
|
|
659
|
+
// 防重复推进:同一次 serial 'data' 回调只允许 dequeue 一次。
|
|
664
660
|
_rxToken: 0,
|
|
665
661
|
_rxDequeuedToken: -1,
|
|
666
|
-
//
|
|
662
|
+
// 发送序号:用于标记本次 writehead 的队首请求。
|
|
667
663
|
_txSeq: 0,
|
|
664
|
+
_framePending: null,
|
|
668
665
|
on: function (a, b) { this._emitter.on(a, b); },
|
|
669
666
|
once: function (a, b) { this._emitter.once(a, b); },
|
|
670
667
|
close: function (cb) {
|
|
@@ -710,24 +707,36 @@ module.exports = function (RED) {
|
|
|
710
707
|
writehead: function () {
|
|
711
708
|
if (!this.queue.length) { return; }
|
|
712
709
|
var qobj = this.queue[0];
|
|
713
|
-
// 标记本次发送的队首请求(用于防止 timeout 与接收回包边界竞态导致双推进)
|
|
714
710
|
qobj._done = false;
|
|
715
711
|
qobj._txId = (++obj._txSeq);
|
|
716
712
|
this.write(qobj.payload, qobj.cb);
|
|
717
713
|
var msg = qobj.msg;
|
|
718
714
|
var timeout = msg.timeout || responsetimeout;
|
|
719
|
-
|
|
720
|
-
|
|
715
|
+
var maxPartialWait = msg.maxPartialWait || serialConfig.maxPartialWait || (timeout * 2);
|
|
716
|
+
var scheduleTimeout = function (delay) {
|
|
717
|
+
obj.tout = setTimeout(onResponseTimeout, delay);
|
|
718
|
+
};
|
|
719
|
+
var onResponseTimeout = function () {
|
|
721
720
|
obj.tout = null;
|
|
722
721
|
|
|
723
|
-
//
|
|
724
|
-
// 若队列已因正常回包 dequeue 推进,则该 timeout 已失效,直接忽略,避免连续发送/错位。
|
|
722
|
+
// 只处理仍然挂在队首的同一个请求;正常回包已推进队列时,该 timeout 失效。
|
|
725
723
|
if (!obj.queue || !obj.queue.length) { return; }
|
|
726
724
|
if (obj.queue[0] !== qobj) { return; }
|
|
727
725
|
if (qobj._done) { return; }
|
|
726
|
+
|
|
727
|
+
if (spliton === "frame" && obj._framePending && obj._framePending.proto === '698-len') {
|
|
728
|
+
var now = Date.now();
|
|
729
|
+
var startedAt = obj._framePending.since || now;
|
|
730
|
+
var waited = now - startedAt;
|
|
731
|
+
if (waited < maxPartialWait) {
|
|
732
|
+
scheduleTimeout(Math.max(50, Math.min(timeout, maxPartialWait - waited)));
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
|
|
728
737
|
qobj._done = true;
|
|
729
738
|
|
|
730
|
-
var msgout = obj.dequeue() || {};
|
|
739
|
+
var msgout = obj.dequeue(true) || {};
|
|
731
740
|
msgout.port = id;
|
|
732
741
|
// // if we have some leftover stuff, just send it
|
|
733
742
|
// if (i !== 0) {
|
|
@@ -754,8 +763,11 @@ module.exports = function (RED) {
|
|
|
754
763
|
if (m) {
|
|
755
764
|
if (spliton === "frame") {
|
|
756
765
|
const mbuf = Buffer.from(m);
|
|
757
|
-
msgout.
|
|
758
|
-
msgout.
|
|
766
|
+
msgout.partial_payload = mbuf;
|
|
767
|
+
msgout.partial_payload_hex = mbuf.toString('hex').toUpperCase();
|
|
768
|
+
msgout.payload = Buffer.alloc(0);
|
|
769
|
+
msgout.payload_hex = "";
|
|
770
|
+
msgout.reason = "FRAME_TIMEOUT_PARTIAL";
|
|
759
771
|
} else {
|
|
760
772
|
if (binoutput !== "bin") { m = m.toString(); }
|
|
761
773
|
msgout.payload = m;
|
|
@@ -769,25 +781,27 @@ module.exports = function (RED) {
|
|
|
769
781
|
}
|
|
770
782
|
}
|
|
771
783
|
msgout.status = "ERR_TIMEOUT";
|
|
784
|
+
obj._framePending = null;
|
|
772
785
|
/* Notify the sender that a timeout occurred */
|
|
773
786
|
obj._emitter.emit('timeout', msgout, qobj.sender);
|
|
774
|
-
}
|
|
787
|
+
};
|
|
788
|
+
scheduleTimeout(timeout);
|
|
775
789
|
},
|
|
776
|
-
dequeue: function () {
|
|
790
|
+
dequeue: function (ignoreRxTokenGuard) {
|
|
777
791
|
// if we are trying to dequeue stuff from an
|
|
778
792
|
// empty queue, that's an unsolicited message
|
|
779
793
|
if (!this.queue.length) { return null; }
|
|
780
794
|
|
|
781
|
-
//
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
795
|
+
// 同一次 serial 'data' 回调内只允许一次出队,避免粘包/误判让队列连续推进。
|
|
796
|
+
if (!ignoreRxTokenGuard) {
|
|
797
|
+
if (typeof this._rxToken === 'number' && this._rxDequeuedToken === this._rxToken) {
|
|
798
|
+
return null;
|
|
799
|
+
}
|
|
800
|
+
if (typeof this._rxToken === 'number') {
|
|
801
|
+
this._rxDequeuedToken = this._rxToken;
|
|
802
|
+
}
|
|
788
803
|
}
|
|
789
804
|
|
|
790
|
-
// 出队前标记 done:用于 timeout 回调校验
|
|
791
805
|
var qobj = this.queue[0];
|
|
792
806
|
if (qobj && qobj._done !== true) { qobj._done = true; }
|
|
793
807
|
|
|
@@ -797,13 +811,10 @@ module.exports = function (RED) {
|
|
|
797
811
|
request_msgid: msg._msgid,
|
|
798
812
|
});
|
|
799
813
|
delete msg.payload;
|
|
800
|
-
|
|
801
|
-
// 取消当前请求的响应超时计时器
|
|
802
814
|
if (obj.tout) {
|
|
803
815
|
clearTimeout(obj.tout);
|
|
804
816
|
obj.tout = null;
|
|
805
817
|
}
|
|
806
|
-
|
|
807
818
|
this.queue.shift();
|
|
808
819
|
this.writehead();
|
|
809
820
|
return msg;
|
|
@@ -997,8 +1008,7 @@ module.exports = function (RED) {
|
|
|
997
1008
|
if (b.length >= 8 && b[7] !== 0x68) {
|
|
998
1009
|
const n = b.indexOf(0x68, 1);
|
|
999
1010
|
if (n > 0) return { ok: false, used: feCount + n, frame: input.slice(0, feCount + n), err: "645_SECOND_68_NOT_FOUND" };
|
|
1000
|
-
|
|
1001
|
-
return { ok: false, used: feCount + 1, frame: input.slice(0, feCount + 1), err: "645_BAD_SHAPE_DROP1" };
|
|
1011
|
+
return { ok: false };
|
|
1002
1012
|
}
|
|
1003
1013
|
if (b.length < 10) return { ok: false }; // 还缺 CTRL/LEN
|
|
1004
1014
|
|
|
@@ -1074,10 +1084,8 @@ module.exports = function (RED) {
|
|
|
1074
1084
|
|
|
1075
1085
|
// 698(68-LEN 变体):68 LL LH C ... DATA ... FCS(2) 16 ;兼容 FE* 前导
|
|
1076
1086
|
function tryParse698Len(input) {
|
|
1077
|
-
//
|
|
1078
|
-
|
|
1079
|
-
while (feCount < input.length && input[feCount] === 0xFE) feCount++;
|
|
1080
|
-
const b = input.slice(feCount);
|
|
1087
|
+
// 剥离前导 FE(注意:这里只做 698-LEN 侧的兼容;645 另有自己的 FE 处理)
|
|
1088
|
+
const b = stripFE(input);
|
|
1081
1089
|
if (b.length < 6 || b[0] !== 0x68) return { ok: false };
|
|
1082
1090
|
|
|
1083
1091
|
// ---- 读取长度域 ----
|
|
@@ -1108,12 +1116,10 @@ module.exports = function (RED) {
|
|
|
1108
1116
|
return { ok: false };
|
|
1109
1117
|
}
|
|
1110
1118
|
|
|
1111
|
-
|
|
1112
|
-
// 长度上限:防止错位/噪声把 L 解读成极大值导致 assembleBuf 误判为半包长期等待
|
|
1113
|
-
// 掉电次数等业务回包通常远小于 2KB;超出则高度可疑,丢 1 字节推进重同步
|
|
1119
|
+
// 长度上限:防止错位/噪声把 L 解读成极大值导致 assembleBuf 长期等待。
|
|
1114
1120
|
const MAX_698_LEN_L = 2048;
|
|
1115
1121
|
if (L > MAX_698_LEN_L) {
|
|
1116
|
-
return { ok: false, used:
|
|
1122
|
+
return { ok: false, used: 1, frame: input.slice(0, 1), err: "698_LEN_TOO_LARGE" };
|
|
1117
1123
|
}
|
|
1118
1124
|
|
|
1119
1125
|
// 约束 2:控制域 C 在 b[3],不可能是 0x16(结束符)
|
|
@@ -1121,13 +1127,10 @@ module.exports = function (RED) {
|
|
|
1121
1127
|
if (b.length >= 4 && b[3] === 0x16) {
|
|
1122
1128
|
return { ok: false };
|
|
1123
1129
|
}
|
|
1124
|
-
// 约束 3
|
|
1125
|
-
// 目的:避免 645 的地址字节被当成 698 控制域,从而把 645 整帧误判为 698-LEN 候选帧并被消费掉。
|
|
1126
|
-
// 698 控制域:bit0..bit3 为功能码(1 或 3 最常见);其他位为 DIR/PRM/分帧/扰码标志。
|
|
1130
|
+
// 约束 3:控制域功能码应为常见 698 值,避免把其它 0x68 开头数据误当成长 698 半包。
|
|
1127
1131
|
const C = b[3] >>> 0;
|
|
1128
1132
|
const func = C & 0x0F;
|
|
1129
1133
|
if (!(func === 0x01 || func === 0x03)) {
|
|
1130
|
-
// 不消费任何字节,让 645 解析器继续尝试
|
|
1131
1134
|
return { ok: false };
|
|
1132
1135
|
}
|
|
1133
1136
|
// ------------------------------------------------------------------------
|
|
@@ -1137,13 +1140,22 @@ module.exports = function (RED) {
|
|
|
1137
1140
|
// 你现有逻辑采用 expectedEnd = 1 + L,并要求 b[expectedEnd] == 0x16
|
|
1138
1141
|
const expectedEnd = 1 + L;
|
|
1139
1142
|
|
|
1140
|
-
//
|
|
1141
|
-
|
|
1143
|
+
// 半包:继续累积。这里必须显式告诉主循环“这是 698-LEN 半包”,
|
|
1144
|
+
// 否则后续 645 parser 会把第一个 0x68 当作错位字节丢掉。
|
|
1145
|
+
if (b.length < expectedEnd + 1) {
|
|
1146
|
+
return {
|
|
1147
|
+
ok: false,
|
|
1148
|
+
pending: true,
|
|
1149
|
+
proto: '698-len',
|
|
1150
|
+
expected: expectedEnd + 1,
|
|
1151
|
+
have: b.length
|
|
1152
|
+
};
|
|
1153
|
+
}
|
|
1142
1154
|
|
|
1143
1155
|
// 若 expectedEnd 位置不是 0x16,说明起点错位或存在脏字节
|
|
1144
1156
|
// 这里消费 1 字节,避免死循环卡住
|
|
1145
1157
|
if (b[expectedEnd] !== 0x16) {
|
|
1146
|
-
return { ok: false, used:
|
|
1158
|
+
return { ok: false, used: 1, frame: input.slice(0, 1), err: "698_LEN_BAD_END" };
|
|
1147
1159
|
}
|
|
1148
1160
|
|
|
1149
1161
|
// ------------------------ FCS 校验(CRC-16/X.25) ------------------------
|
|
@@ -1161,14 +1173,12 @@ module.exports = function (RED) {
|
|
|
1161
1173
|
const calcB = crc16x25(b, 1, expectedEnd - 2);
|
|
1162
1174
|
const fcsOK = (calcA === fcs) || (calcB === fcs);
|
|
1163
1175
|
|
|
1164
|
-
//
|
|
1165
|
-
// 若此处直接判为错误帧并 dequeue,会导致上层“偶尔无法解码”(尤其是请求-响应严格匹配的场景)。
|
|
1166
|
-
// 因此:当边界与长度域一致时,允许“容错通过”,并在 _meta 中标记 fcs_ok=false 供上层排障。
|
|
1176
|
+
// 边界完整但 FCS 不通过时上报错误帧;队首请求继续等待后续正确帧或最终超时。
|
|
1167
1177
|
if (!fcsOK) {
|
|
1168
1178
|
return {
|
|
1169
1179
|
ok: false,
|
|
1170
|
-
used:
|
|
1171
|
-
frame:
|
|
1180
|
+
used: expectedEnd + 1,
|
|
1181
|
+
frame: b.slice(0, expectedEnd + 1),
|
|
1172
1182
|
fcs_ok: false,
|
|
1173
1183
|
fcs_frame: fcs,
|
|
1174
1184
|
fcs_calc_a: calcA,
|
|
@@ -1180,8 +1190,8 @@ module.exports = function (RED) {
|
|
|
1180
1190
|
// FCS 通过:返回完整帧(含 0x68..0x16)
|
|
1181
1191
|
return {
|
|
1182
1192
|
ok: true,
|
|
1183
|
-
used:
|
|
1184
|
-
frame:
|
|
1193
|
+
used: expectedEnd + 1,
|
|
1194
|
+
frame: b.slice(0, expectedEnd + 1),
|
|
1185
1195
|
fcs_ok: true
|
|
1186
1196
|
};
|
|
1187
1197
|
}
|
|
@@ -1197,34 +1207,40 @@ module.exports = function (RED) {
|
|
|
1197
1207
|
const rawFrame = b.slice(0, endPos + 1);
|
|
1198
1208
|
const payloadEscaped = b.slice(1, endPos); // 去 7E
|
|
1199
1209
|
const payload = unescapeHDLC(payloadEscaped);
|
|
1200
|
-
|
|
1201
|
-
if (payload.length < 3) return { ok: false };
|
|
1202
|
-
// 严格形态约束:698-HDLC 的“帧格式域”通常以 0xA0/0xA8/0xB0 等开头(高四位为 0xA 或 0xB)。
|
|
1203
|
-
// 若不满足,极可能只是链路噪声/误同步的 0x7E,不应在 frame 模式下被消费掉(否则会 dequeue 错配)。
|
|
1204
|
-
const fmt = payload[0] >>> 0;
|
|
1205
|
-
const hi = fmt & 0xF0;
|
|
1206
|
-
if (!(hi === 0xA0 || hi === 0xB0)) {
|
|
1207
|
-
return { ok: false }; // 不消费,交给 698-LEN/645 再尝试
|
|
1208
|
-
}
|
|
1210
|
+
if (payload.length < 3) return { ok: false, used: endPos + 1, frame: rawFrame, err: "698_HDLC_TOO_SHORT" };
|
|
1209
1211
|
const fcsLo = payload[payload.length - 2];
|
|
1210
1212
|
const fcsHi = payload[payload.length - 1];
|
|
1211
1213
|
const fcs = (fcsHi << 8) | fcsLo;
|
|
1212
1214
|
const calc = crc16x25(payload, 0, payload.length - 2);
|
|
1213
|
-
|
|
1214
|
-
if (calc !== fcs) return { ok: false };
|
|
1215
|
+
if (calc !== fcs) return { ok: false, used: endPos + 1, frame: rawFrame, err: "698_HDLC_CRC_FAIL" };
|
|
1215
1216
|
return { ok: true, used: endPos + 1, frame: rawFrame }; // 原始含 7E
|
|
1216
1217
|
}
|
|
1217
1218
|
|
|
1218
1219
|
// 统一喂入器:抽帧、报错、剔噪
|
|
1219
|
-
let assembleBuf = Buffer.alloc(0);
|
|
1220
1220
|
function feedAndExtract(d, emitOk, emitErr) {
|
|
1221
1221
|
if (!Buffer.isBuffer(d)) d = Buffer.from(d);
|
|
1222
1222
|
assembleBuf = Buffer.concat([assembleBuf, d]);
|
|
1223
1223
|
|
|
1224
|
-
//
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1224
|
+
// 防溢出
|
|
1225
|
+
if (assembleBuf.length > bufMaxSize) {
|
|
1226
|
+
emitErr(Buffer.from(assembleBuf), "BUFFER_OVERFLOW_DROP_OLD");
|
|
1227
|
+
assembleBuf = Buffer.alloc(0);
|
|
1228
|
+
obj._framePending = null;
|
|
1229
|
+
return;
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
// 前导剔噪:仅保留以 FE/68/7E 开头;若以 FE 开头,剥离所有 FE 前导
|
|
1233
|
+
let s = 0;
|
|
1234
|
+
while (s < assembleBuf.length) {
|
|
1235
|
+
const c = assembleBuf[s];
|
|
1236
|
+
if (c === 0xFE || c === 0x68 || c === 0x7E) break;
|
|
1237
|
+
s++;
|
|
1238
|
+
}
|
|
1239
|
+
if (s > 0) assembleBuf = assembleBuf.slice(s);
|
|
1240
|
+
if (assembleBuf.length && assembleBuf[0] === 0xFE) {
|
|
1241
|
+
let k = 0; while (k < assembleBuf.length && assembleBuf[k] === 0xFE) k++;
|
|
1242
|
+
assembleBuf = assembleBuf.slice(k);
|
|
1243
|
+
}
|
|
1228
1244
|
|
|
1229
1245
|
// 抽帧
|
|
1230
1246
|
while (assembleBuf.length >= 5) {
|
|
@@ -1237,21 +1253,17 @@ module.exports = function (RED) {
|
|
|
1237
1253
|
// 关键:698-LEN 必须优先于 645,否则遇到 698 帧内出现 0x68(如示例报文第7字节)会被 645 误判并卡住
|
|
1238
1254
|
let r = tryParseBleGNW(assembleBuf);
|
|
1239
1255
|
if (!r.ok) r = tryParse698HDLC(assembleBuf);
|
|
1240
|
-
|
|
1241
|
-
// ---- 兜底:0x7E 起始已形成候选段(存在第二个 0x7E),但 BLE/HDLC 均无法校验通过 -> 丢 1 字节推进重同步 ----
|
|
1242
|
-
// 仅在候选段“闭合”时执行(有第二个 0x7E),避免误伤半包;并要求 endPos>=5,避免误伤 7E7E/7E7E7E 前导
|
|
1243
|
-
if (!r.ok && assembleBuf.length && assembleBuf[0] === 0x7E) {
|
|
1244
|
-
const endPos = assembleBuf.indexOf(0x7E, 1);
|
|
1245
|
-
if (endPos >= 5) {
|
|
1246
|
-
r = { ok: false, used: 1, frame: assembleBuf.slice(0, 1), err: "DROP_7E_BADFRAME" };
|
|
1247
|
-
}
|
|
1248
|
-
}
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
1256
|
if (!r.ok) r = tryParse698Len(assembleBuf);
|
|
1257
|
+
if (!r.ok && r.pending) {
|
|
1258
|
+
obj._framePending = Object.assign({}, r, {
|
|
1259
|
+
since: (obj._framePending && obj._framePending.proto === r.proto) ? obj._framePending.since : Date.now()
|
|
1260
|
+
});
|
|
1261
|
+
break;
|
|
1262
|
+
}
|
|
1252
1263
|
if (!r.ok) r = tryParse645(assembleBuf);
|
|
1253
1264
|
|
|
1254
1265
|
if (r.ok) {
|
|
1266
|
+
obj._framePending = null;
|
|
1255
1267
|
// 附加解析元数据(例如 698 FCS 校验结果),供上层排障
|
|
1256
1268
|
if (r && typeof r === 'object') {
|
|
1257
1269
|
try {
|
|
@@ -1272,14 +1284,10 @@ module.exports = function (RED) {
|
|
|
1272
1284
|
continue;
|
|
1273
1285
|
}
|
|
1274
1286
|
if (r.used) {
|
|
1275
|
-
|
|
1287
|
+
obj._framePending = null;
|
|
1276
1288
|
const used = r.used >>> 0;
|
|
1277
1289
|
const frameBuf = r.frame ? Buffer.from(r.frame) : null;
|
|
1278
1290
|
const frameLen = frameBuf ? frameBuf.length : 0;
|
|
1279
|
-
|
|
1280
|
-
// 判定是否为“仅推进同步点”的消费:
|
|
1281
|
-
// - used == 1 或 frameLen <= 1:典型 drop1
|
|
1282
|
-
// - 或 err 属于已知的 resync 类原因
|
|
1283
1291
|
const errCode = (r && r.err) ? String(r.err) : "";
|
|
1284
1292
|
const isResyncDrop = (used <= 1) || (frameLen <= 1) ||
|
|
1285
1293
|
errCode === "DROP_7E_BADFRAME" ||
|
|
@@ -1289,7 +1297,7 @@ module.exports = function (RED) {
|
|
|
1289
1297
|
errCode === "645_SECOND_68_NOT_FOUND";
|
|
1290
1298
|
|
|
1291
1299
|
if (!isResyncDrop) {
|
|
1292
|
-
|
|
1300
|
+
obj._framePending = null;
|
|
1293
1301
|
try {
|
|
1294
1302
|
if (r.frame) {
|
|
1295
1303
|
r.frame._meta = Object.assign({}, r.frame._meta || {}, {
|
|
@@ -1302,12 +1310,11 @@ module.exports = function (RED) {
|
|
|
1302
1310
|
});
|
|
1303
1311
|
}
|
|
1304
1312
|
} catch (e) {
|
|
1305
|
-
// ignore
|
|
1313
|
+
// ignore:诊断信息不能影响主流程
|
|
1306
1314
|
}
|
|
1307
1315
|
emitErr(r.frame, errCode || "FRAME_INVALID");
|
|
1308
1316
|
}
|
|
1309
1317
|
|
|
1310
|
-
// 无论是否上报,都必须消费,防止 assembleBuf 卡死
|
|
1311
1318
|
assembleBuf = assembleBuf.slice(used);
|
|
1312
1319
|
continue;
|
|
1313
1320
|
}
|
|
@@ -1319,7 +1326,6 @@ module.exports = function (RED) {
|
|
|
1319
1326
|
|
|
1320
1327
|
|
|
1321
1328
|
obj.serial.on('data', function (d) {
|
|
1322
|
-
// 标记本次接收事件 token(用于防止同一次 data 回调内重复 dequeue 推进队列)
|
|
1323
1329
|
obj._rxToken = (obj._rxToken || 0) + 1;
|
|
1324
1330
|
// RED.log.info("data::::" + d);
|
|
1325
1331
|
function emitData(data) {
|
|
@@ -1366,17 +1372,15 @@ module.exports = function (RED) {
|
|
|
1366
1372
|
function (badBuf, reason) {
|
|
1367
1373
|
// 错误帧同样保持 Buffer 输出,并提供 HEX 字符串,便于定位截断点/前导位置
|
|
1368
1374
|
var m = Buffer.from(badBuf);
|
|
1369
|
-
var
|
|
1370
|
-
if (obj.queue.length) { last_sender = obj.queue[0].sender; }
|
|
1371
|
-
// 关键修复:错误帧(CRC/CS/结尾不符)不应 dequeue,否则会把“请求上下文”弹出队列,导致后续正确回包无法匹配
|
|
1372
|
-
var msgout = (obj.queue && obj.queue.length) ? Object.assign({}, obj.queue[0].msg) : {};
|
|
1375
|
+
var msgout = {};
|
|
1373
1376
|
msgout.payload = m;
|
|
1374
1377
|
msgout.payload_hex = m.toString('hex').toUpperCase();
|
|
1375
1378
|
msgout.port = port;
|
|
1376
1379
|
msgout.status = "ERR_FRAME";
|
|
1377
1380
|
msgout.reason = reason || "FRAME_INVALID";
|
|
1378
1381
|
msgout.is_unsolicited = !(obj.queue && obj.queue.length);
|
|
1379
|
-
|
|
1382
|
+
// 错误帧不绑定请求 sender,也不 dequeue;队首请求继续等完整帧或超时。
|
|
1383
|
+
obj._emitter.emit('data', msgout, null);
|
|
1380
1384
|
}
|
|
1381
1385
|
);
|
|
1382
1386
|
return; // 已处理
|