node-zserial 1.0.32 → 1.0.34
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 +2 -2
- package/zserial.js +52 -3
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-zserial",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.34",
|
|
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
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
|
|
2
|
+
/**
|
|
3
|
+
*
|
|
4
|
+
* version: 1.0.34
|
|
5
|
+
*/
|
|
2
6
|
module.exports = function (RED) {
|
|
3
7
|
/*jshint -W082 */
|
|
4
8
|
"use strict";
|
|
@@ -655,6 +659,11 @@ module.exports = function (RED) {
|
|
|
655
659
|
_retryNum: 0,
|
|
656
660
|
tout: null,
|
|
657
661
|
queue: [],
|
|
662
|
+
// ---- 防重复推进:同一次 serial 'data' 回调只允许 dequeue 一次 ----
|
|
663
|
+
_rxToken: 0,
|
|
664
|
+
_rxDequeuedToken: -1,
|
|
665
|
+
// ---- 发送序号:用于标记本次 writehead 的队首请求 ----
|
|
666
|
+
_txSeq: 0,
|
|
658
667
|
on: function (a, b) { this._emitter.on(a, b); },
|
|
659
668
|
once: function (a, b) { this._emitter.once(a, b); },
|
|
660
669
|
close: function (cb) {
|
|
@@ -700,11 +709,23 @@ module.exports = function (RED) {
|
|
|
700
709
|
writehead: function () {
|
|
701
710
|
if (!this.queue.length) { return; }
|
|
702
711
|
var qobj = this.queue[0];
|
|
712
|
+
// 标记本次发送的队首请求(用于防止 timeout 与接收回包边界竞态导致双推进)
|
|
713
|
+
qobj._done = false;
|
|
714
|
+
qobj._txId = (++obj._txSeq);
|
|
703
715
|
this.write(qobj.payload, qobj.cb);
|
|
704
716
|
var msg = qobj.msg;
|
|
705
717
|
var timeout = msg.timeout || responsetimeout;
|
|
706
718
|
this.tout = setTimeout(function () {
|
|
707
|
-
this.tout
|
|
719
|
+
// 注意:回调内 this 不是 obj,统一使用 obj.tout
|
|
720
|
+
obj.tout = null;
|
|
721
|
+
|
|
722
|
+
// 关键保护:timeout 触发时,只允许处理“仍然挂在队首的同一个 qobj”。
|
|
723
|
+
// 若队列已因正常回包 dequeue 推进,则该 timeout 已失效,直接忽略,避免连续发送/错位。
|
|
724
|
+
if (!obj.queue || !obj.queue.length) { return; }
|
|
725
|
+
if (obj.queue[0] !== qobj) { return; }
|
|
726
|
+
if (qobj._done) { return; }
|
|
727
|
+
qobj._done = true;
|
|
728
|
+
|
|
708
729
|
var msgout = obj.dequeue() || {};
|
|
709
730
|
msgout.port = id;
|
|
710
731
|
// // if we have some leftover stuff, just send it
|
|
@@ -755,16 +776,33 @@ module.exports = function (RED) {
|
|
|
755
776
|
// if we are trying to dequeue stuff from an
|
|
756
777
|
// empty queue, that's an unsolicited message
|
|
757
778
|
if (!this.queue.length) { return null; }
|
|
758
|
-
|
|
779
|
+
|
|
780
|
+
// 关键保护:同一次 serial 'data' 回调(同 _rxToken)内只允许 dequeue 一次。
|
|
781
|
+
// 防止 frame 自动解析在同一批输入字节上被多协议/多分支误判两次,导致队列被推进两次(连续发送)。
|
|
782
|
+
if (typeof this._rxToken === 'number' && this._rxDequeuedToken === this._rxToken) {
|
|
783
|
+
return null;
|
|
784
|
+
}
|
|
785
|
+
if (typeof this._rxToken === 'number') {
|
|
786
|
+
this._rxDequeuedToken = this._rxToken;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
// 出队前标记 done:用于 timeout 回调校验
|
|
790
|
+
var qobj = this.queue[0];
|
|
791
|
+
if (qobj && qobj._done !== true) { qobj._done = true; }
|
|
792
|
+
|
|
793
|
+
var msg = Object.assign({}, qobj.msg);
|
|
759
794
|
msg = Object.assign(msg, {
|
|
760
795
|
request_payload: msg.payload,
|
|
761
796
|
request_msgid: msg._msgid,
|
|
762
797
|
});
|
|
763
798
|
delete msg.payload;
|
|
764
|
-
|
|
799
|
+
|
|
800
|
+
// 取消当前请求的响应超时计时器
|
|
801
|
+
if (obj.tout) {
|
|
765
802
|
clearTimeout(obj.tout);
|
|
766
803
|
obj.tout = null;
|
|
767
804
|
}
|
|
805
|
+
|
|
768
806
|
this.queue.shift();
|
|
769
807
|
this.writehead();
|
|
770
808
|
return msg;
|
|
@@ -1082,6 +1120,15 @@ module.exports = function (RED) {
|
|
|
1082
1120
|
if (b.length >= 4 && b[3] === 0x16) {
|
|
1083
1121
|
return { ok: false };
|
|
1084
1122
|
}
|
|
1123
|
+
// 约束 3:控制域 C 必须看起来像 698 的控制域(功能码通常为 1=链路管理 或 3=用户数据)
|
|
1124
|
+
// 目的:避免 645 的地址字节被当成 698 控制域,从而把 645 整帧误判为 698-LEN 候选帧并被消费掉。
|
|
1125
|
+
// 698 控制域:bit0..bit3 为功能码(1 或 3 最常见);其他位为 DIR/PRM/分帧/扰码标志。
|
|
1126
|
+
const C = b[3] >>> 0;
|
|
1127
|
+
const func = C & 0x0F;
|
|
1128
|
+
if (!(func === 0x01 || func === 0x03)) {
|
|
1129
|
+
// 不消费任何字节,让 645 解析器继续尝试
|
|
1130
|
+
return { ok: false };
|
|
1131
|
+
}
|
|
1085
1132
|
// ------------------------------------------------------------------------
|
|
1086
1133
|
|
|
1087
1134
|
// 期望 0x16 的位置(相对 b 起点):1 + L
|
|
@@ -1271,6 +1318,8 @@ module.exports = function (RED) {
|
|
|
1271
1318
|
|
|
1272
1319
|
|
|
1273
1320
|
obj.serial.on('data', function (d) {
|
|
1321
|
+
// 标记本次接收事件 token(用于防止同一次 data 回调内重复 dequeue 推进队列)
|
|
1322
|
+
obj._rxToken = (obj._rxToken || 0) + 1;
|
|
1274
1323
|
// RED.log.info("data::::" + d);
|
|
1275
1324
|
function emitData(data) {
|
|
1276
1325
|
if (active === true) {
|