node-zserial 1.0.22 → 1.0.24
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 +152 -72
package/package.json
CHANGED
package/zserial.js
CHANGED
|
@@ -24,7 +24,7 @@ module.exports = function (RED) {
|
|
|
24
24
|
node.send(msg);
|
|
25
25
|
done();
|
|
26
26
|
}, node);
|
|
27
|
-
}
|
|
27
|
+
}
|
|
28
28
|
else if (msg.serialConfigs) {
|
|
29
29
|
let allLen = msg.serialConfigs.length;
|
|
30
30
|
let total = msg.serialConfigs.length;
|
|
@@ -329,7 +329,7 @@ module.exports = function (RED) {
|
|
|
329
329
|
node.successMsg = {};
|
|
330
330
|
node.errorMsg = {};
|
|
331
331
|
node._msg = null
|
|
332
|
-
|
|
332
|
+
|
|
333
333
|
function initMsg(msg) {
|
|
334
334
|
node._msg = msg;
|
|
335
335
|
node.totallenth = msg.serialConfigs.length;
|
|
@@ -338,7 +338,7 @@ module.exports = function (RED) {
|
|
|
338
338
|
node.errorMsg = {};
|
|
339
339
|
}
|
|
340
340
|
function zsend(msg, err, alldone, port, done) {
|
|
341
|
-
|
|
341
|
+
|
|
342
342
|
let payload = msg || err;
|
|
343
343
|
node._msg.payload = payload;
|
|
344
344
|
node.totalMsg[port] = payload
|
|
@@ -358,7 +358,6 @@ module.exports = function (RED) {
|
|
|
358
358
|
sendAll(done);
|
|
359
359
|
}
|
|
360
360
|
function onMsg(msg, send, done) {
|
|
361
|
-
|
|
362
361
|
initMsg(msg);
|
|
363
362
|
if (!msg.serialConfigs) {
|
|
364
363
|
node.error("需要配置批量配置:msg.serialConfigs");
|
|
@@ -370,14 +369,14 @@ module.exports = function (RED) {
|
|
|
370
369
|
for (var i = 0; i < msg.serialConfigs.length; i++) {
|
|
371
370
|
var serialConfig = msg.serialConfigs[i];
|
|
372
371
|
serialConfig._msgid = msg._msgid + "_" + i;
|
|
373
|
-
getSerialServer(msg, serialConfig, done);
|
|
372
|
+
getSerialServer( msg, serialConfig, done);
|
|
374
373
|
}
|
|
375
374
|
}
|
|
376
375
|
|
|
377
376
|
function sendAll(done) {
|
|
377
|
+
|
|
378
378
|
try {
|
|
379
379
|
let len = Object.keys(node.totalMsg).length;
|
|
380
|
-
|
|
381
380
|
if (len == node.totallenth) {
|
|
382
381
|
let payload = {
|
|
383
382
|
totalMsg: node.totalMsg,
|
|
@@ -501,53 +500,82 @@ module.exports = function (RED) {
|
|
|
501
500
|
|
|
502
501
|
}
|
|
503
502
|
|
|
503
|
+
function isCurNode(msgout, curMsg, _ndoe, sender) {
|
|
504
|
+
// if (sender == node) { return true; }
|
|
505
|
+
if (msgout.request_msgid && msgout.request_msgid.startsWith(curMsg._msgid)) {
|
|
506
|
+
return true
|
|
507
|
+
}
|
|
508
|
+
return false
|
|
509
|
+
}
|
|
510
|
+
|
|
504
511
|
function setCallback(msg, serialConfig, done) {
|
|
505
512
|
let curPort = serialPool.get(serialConfig);
|
|
506
|
-
//
|
|
507
|
-
if (curPort._isBindEventInit) {
|
|
508
|
-
|
|
509
|
-
}
|
|
513
|
+
// 确保当前节点只绑定一次事件
|
|
514
|
+
// if (curPort._isBindEventInit) {
|
|
515
|
+
// return;
|
|
516
|
+
// }
|
|
510
517
|
// node.warn("setCallback called for " + curPort.serial.path);
|
|
511
|
-
curPort._isBindEventInit = true;
|
|
518
|
+
// curPort._isBindEventInit = true;
|
|
512
519
|
|
|
513
|
-
curPort.
|
|
520
|
+
if (node[`_dataHandler_${curPort.serial.path}`]) {
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
const dataHandler = function (msgout, sender) {
|
|
514
524
|
if (sender !== node) { return; }
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
525
|
+
try {
|
|
526
|
+
msgout.status = "OK";
|
|
527
|
+
zsend(msgout, null, null, curPort.serial.path, done);
|
|
528
|
+
} catch (error) {
|
|
529
|
+
node.error(error)
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
const timeoutHandler = function (msgout, sender) {
|
|
519
535
|
if (sender !== node) { return; }
|
|
520
536
|
msgout.status = "ERR_TIMEOUT";
|
|
521
537
|
msgout.port = curPort.serial.path;
|
|
522
538
|
node.status({ fill: "red", shape: "ring", text: "timeout:::" + curPort.serial.path });
|
|
523
539
|
zsend(null, msgout, null, curPort.serial.path, done);
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
curPort.on('initerror', function (port, retryNum, olderr) {
|
|
527
|
-
// zsend(null, {
|
|
528
|
-
// status: "ERR_INIT",
|
|
529
|
-
// text: `请检查端口是否打开,重试次数${retryNum}`,
|
|
530
|
-
// error: olderr,
|
|
531
|
-
// port: port
|
|
532
|
-
// }, null, curPort.serial.path, done);
|
|
533
|
-
});
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
curPort.on('retryerror', function (port, retryNum) {
|
|
537
|
-
// curPort._retryNum = 0;
|
|
538
|
-
// zsend(null, {
|
|
539
|
-
// status: "ERR_retry",
|
|
540
|
-
// text: `重试${retryNum}失败`,
|
|
541
|
-
// port: port
|
|
542
|
-
// }, null, curPort.serial.path, done);
|
|
543
|
-
});
|
|
540
|
+
}
|
|
544
541
|
|
|
545
|
-
curPort.
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
curPort.on('
|
|
549
|
-
|
|
550
|
-
|
|
542
|
+
node[`_dataHandler_${curPort.serial.path}`] = dataHandler
|
|
543
|
+
node[`_timeoutHandler_${curPort.serial.path}`] = timeoutHandler
|
|
544
|
+
curPort.on('data', dataHandler);
|
|
545
|
+
curPort.on('timeout', timeoutHandler);
|
|
546
|
+
|
|
547
|
+
node.on('close', function () {
|
|
548
|
+
node[`_dataHandler_${curPort.serial.path}`] = null
|
|
549
|
+
node[`_timeoutHandler_${curPort.serial.path}`] = null
|
|
550
|
+
curPort.off('data', dataHandler);
|
|
551
|
+
curPort.off('timeout', timeoutHandler);
|
|
552
|
+
})
|
|
553
|
+
|
|
554
|
+
// curPort.on('initerror', function (port, retryNum, olderr) {
|
|
555
|
+
// // zsend(null, {
|
|
556
|
+
// // status: "ERR_INIT",
|
|
557
|
+
// // text: `请检查端口是否打开,重试次数${retryNum}`,
|
|
558
|
+
// // error: olderr,
|
|
559
|
+
// // port: port
|
|
560
|
+
// // }, null, curPort.serial.path, done);
|
|
561
|
+
// });
|
|
562
|
+
|
|
563
|
+
|
|
564
|
+
// curPort.on('retryerror', function (port, retryNum) {
|
|
565
|
+
// // curPort._retryNum = 0;
|
|
566
|
+
// // zsend(null, {
|
|
567
|
+
// // status: "ERR_retry",
|
|
568
|
+
// // text: `重试${retryNum}失败`,
|
|
569
|
+
// // port: port
|
|
570
|
+
// // }, null, curPort.serial.path, done);
|
|
571
|
+
// });
|
|
572
|
+
|
|
573
|
+
// curPort.on('closed', function (port) {
|
|
574
|
+
// // node.warn(`串口已关闭:${port}`);
|
|
575
|
+
// });
|
|
576
|
+
// curPort.on('ready', function (port) {
|
|
577
|
+
// // node.warn(`串口已准备好:${port}`);
|
|
578
|
+
// });
|
|
551
579
|
}
|
|
552
580
|
|
|
553
581
|
this.on("input", function (msg, send, done) {
|
|
@@ -876,6 +904,47 @@ module.exports = function (RED) {
|
|
|
876
904
|
return Buffer.from(out);
|
|
877
905
|
}
|
|
878
906
|
|
|
907
|
+
|
|
908
|
+
// GNW 蓝牙帧:7E 7E 7E 5A ... CS 7E A5
|
|
909
|
+
function tryParseBleGNW(input) {
|
|
910
|
+
const b = input;
|
|
911
|
+
if (b.length < 9) return { ok: false }; // 最短:4起始 + 1LEN + 1CMD + 0DATA + 1CS + 2结束
|
|
912
|
+
// 形态判定:必须以 7E 7E 7E 5A 开头
|
|
913
|
+
if (!(b[0] === 0x7E && b[1] === 0x7E && b[2] === 0x7E && b[3] === 0x5A)) {
|
|
914
|
+
// 若不是,看看后面是否有候选起点(容错脏字节)
|
|
915
|
+
const n = b.indexOf(0x7E);
|
|
916
|
+
if (n > 0) return { ok: false, used: n, frame: b.slice(0, n), err: "BLE_NO_7E7E7E5A_AT_START" };
|
|
917
|
+
return { ok: false };
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
// 向后找结束 A5,要求倒数第二个字节是 0x7E(结束符 7E A5)
|
|
921
|
+
let endPos = -1;
|
|
922
|
+
for (let i = 5; i < b.length; i++) {
|
|
923
|
+
if (b[i] === 0xA5 && b[i - 1] === 0x7E) { endPos = i; break; }
|
|
924
|
+
}
|
|
925
|
+
if (endPos === -1) return { ok: false }; // 半包,继续累计
|
|
926
|
+
|
|
927
|
+
const frame = b.slice(0, endPos + 1); // [0 .. A5]
|
|
928
|
+
// 基本字段位置:len 在 [4],cmd 在 [5],CS 在倒数第3个字节
|
|
929
|
+
const L = frame[4] >>> 0;
|
|
930
|
+
const cs = frame[frame.length - 3] >>> 0;
|
|
931
|
+
|
|
932
|
+
// (宽松)长度一致性:实测总长 T 与 L 的关系为 T = L + 3
|
|
933
|
+
const T = frame.length;
|
|
934
|
+
if ((T - 3) !== L) {
|
|
935
|
+
// 不强制失败,给出“长度不一致”的错误帧提示并丢掉该段,避免卡死
|
|
936
|
+
return { ok: false, used: frame.length, frame, err: "BLE_LEN_MISMATCH" };
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
// 和校验:从起始 0x7E 到 CS 之前所有字节累加低8位
|
|
940
|
+
let sum = 0;
|
|
941
|
+
for (let i = 0; i < frame.length - 3; i++) sum = (sum + (frame[i] >>> 0)) & 0xFF;
|
|
942
|
+
if (sum !== cs) {
|
|
943
|
+
return { ok: false, used: frame.length, frame, err: "BLE_CS_FAIL" };
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
return { ok: true, used: frame.length, frame };
|
|
947
|
+
}
|
|
879
948
|
// 645:FE* 68 + 6 addr + 68 + ctrl + len + data + cs + 16
|
|
880
949
|
function tryParse645(input) {
|
|
881
950
|
// 统计 FE 前导(仅用于 used,不参与 CS 计算)
|
|
@@ -935,42 +1004,47 @@ module.exports = function (RED) {
|
|
|
935
1004
|
return { ok: false };
|
|
936
1005
|
}
|
|
937
1006
|
|
|
938
|
-
// 698(68-LEN 变体):68 LL LH C ... DATA ...
|
|
1007
|
+
// 698(68-LEN 变体):68 LL LH C ... DATA ... FCS(2) 16 ;兼容 FE* 前导
|
|
939
1008
|
function tryParse698Len(input) {
|
|
940
|
-
//
|
|
1009
|
+
// 剥离前导 FE
|
|
941
1010
|
const b = stripFE(input);
|
|
942
1011
|
if (b.length < 6 || b[0] !== 0x68) return { ok: false };
|
|
943
|
-
// 避免把 645
|
|
1012
|
+
// 避免把 645 错误识别为 698-LEN(645 在 b[7] 处固定为 0x68)
|
|
944
1013
|
if (b.length >= 8 && b[7] === 0x68) return { ok: false };
|
|
945
1014
|
|
|
946
|
-
//
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
return { ok: true, used: end + 1, frame: b.slice(0, end + 1) };
|
|
965
|
-
}
|
|
966
|
-
}
|
|
1015
|
+
// ---- 严格按长度域 L 判帧 ----
|
|
1016
|
+
const LL = b[1] >>> 0;
|
|
1017
|
+
const LH = b[2] >>> 0;
|
|
1018
|
+
let Lraw = (LH << 8) | LL; // 含单位位、保留位
|
|
1019
|
+
const isKB = (Lraw & 0x4000) !== 0; // bit14:单位位(0=字节,1=KB)
|
|
1020
|
+
Lraw &= 0x3FFF; // 清除单位位与保留位,仅保留长度值
|
|
1021
|
+
|
|
1022
|
+
// 仅支持常见“字节”为单位的场景;若为 KB 单位,展开至字节
|
|
1023
|
+
let L = isKB ? (Lraw << 10) : Lraw; // KB -> *1024
|
|
1024
|
+
|
|
1025
|
+
// 期望 0x16 的位置(相对 b 的起点):1 + L
|
|
1026
|
+
const expectedEnd = 1 + L;
|
|
1027
|
+
// 缓冲区数据还不够,继续累计
|
|
1028
|
+
if (b.length < expectedEnd + 1) return { ok: false };
|
|
1029
|
+
// 不是以 0x16 结尾,说明前面有脏字节或起点错位,丢弃 1 字节继续
|
|
1030
|
+
if (b[expectedEnd] !== 0x16) {
|
|
1031
|
+
return { ok: false, used: 1, frame: b.slice(0, 1), err: "698_LEN_BAD_END" };
|
|
1032
|
+
}
|
|
967
1033
|
|
|
968
|
-
|
|
969
|
-
|
|
1034
|
+
// FCS 校验(CRC‑16/X.25),计算区间:[LL..FCS 前一字节]
|
|
1035
|
+
const fcsLo = b[expectedEnd - 2];
|
|
1036
|
+
const fcsHi = b[expectedEnd - 1];
|
|
1037
|
+
const fcs = (fcsHi << 8) | fcsLo;
|
|
1038
|
+
const calc = crc16x25(b, 1, expectedEnd - 2);
|
|
1039
|
+
if (calc !== fcs) {
|
|
1040
|
+
return { ok: false, used: 1, frame: b.slice(0, 1), err: "698_LEN_FCS_FAIL" };
|
|
970
1041
|
}
|
|
971
1042
|
|
|
972
|
-
//
|
|
973
|
-
|
|
1043
|
+
// (可选)HCS 校验:从 LL 开始至 HCS 前一字节
|
|
1044
|
+
// 按 698.45,HCS 位于地址域之后;为避免复杂度,这里不强制校验 HCS。
|
|
1045
|
+
// 仅在需要时可补:解析地址域长度后再计算 HCS。
|
|
1046
|
+
|
|
1047
|
+
return { ok: true, used: expectedEnd + 1, frame: b.slice(0, expectedEnd + 1) };
|
|
974
1048
|
}
|
|
975
1049
|
|
|
976
1050
|
// 698(HDLC):7E ... [FCS(lo,hi)] 7E,支持 0x7D 转义与 X.25 FCS
|
|
@@ -1021,8 +1095,14 @@ module.exports = function (RED) {
|
|
|
1021
1095
|
|
|
1022
1096
|
// 抽帧
|
|
1023
1097
|
while (assembleBuf.length >= 5) {
|
|
1024
|
-
// 优先 HDLC,再 645,再 698-Len(避免误判)
|
|
1025
|
-
let r = tryParse698HDLC(assembleBuf);
|
|
1098
|
+
// // 优先 HDLC,再 645,再 698-Len(避免误判)
|
|
1099
|
+
// let r = tryParse698HDLC(assembleBuf);
|
|
1100
|
+
// if (!r.ok) r = tryParse645(assembleBuf);
|
|
1101
|
+
// if (!r.ok) r = tryParse698Len(assembleBuf);
|
|
1102
|
+
|
|
1103
|
+
// --fix-20251108-先 BLE(7E7E7E5A…7EA5),再 698-HDLC,再 645,再 698-LEN
|
|
1104
|
+
let r = tryParseBleGNW(assembleBuf);
|
|
1105
|
+
if (!r.ok) r = tryParse698HDLC(assembleBuf);
|
|
1026
1106
|
if (!r.ok) r = tryParse645(assembleBuf);
|
|
1027
1107
|
if (!r.ok) r = tryParse698Len(assembleBuf);
|
|
1028
1108
|
|