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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/zserial.js +152 -72
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-zserial",
3
- "version": "1.0.22",
3
+ "version": "1.0.24",
4
4
  "description": "Node-RED nodes to talk to serial ports",
5
5
  "dependencies": {
6
6
  "serialport": "^12.0.0"
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
- return;
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.on('data', function (msgout, sender) {
520
+ if (node[`_dataHandler_${curPort.serial.path}`]) {
521
+ return;
522
+ }
523
+ const dataHandler = function (msgout, sender) {
514
524
  if (sender !== node) { return; }
515
- msgout.status = "OK";
516
- zsend(msgout, null, null, curPort.serial.path, done);
517
- });
518
- curPort.on('timeout', function (msgout, sender) {
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.on('closed', function (port) {
546
- // node.warn(`串口已关闭:${port}`);
547
- });
548
- curPort.on('ready', function (port) {
549
- // node.warn(`串口已准备好:${port}`);
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 ... CS 16 ;兼容 FE* 前导
1007
+ // 698(68-LEN 变体):68 LL LH C ... DATA ... FCS(2) 16 ;兼容 FE* 前导
939
1008
  function tryParse698Len(input) {
940
- // FE 前导
1009
+ // 剥离前导 FE
941
1010
  const b = stripFE(input);
942
1011
  if (b.length < 6 || b[0] !== 0x68) return { ok: false };
943
- // 避免把 645 误判成 698-LEN
1012
+ // 避免把 645 错误识别为 698-LEN(645 在 b[7] 处固定为 0x68)
944
1013
  if (b.length >= 8 && b[7] === 0x68) return { ok: false };
945
1014
 
946
- // 在缓冲里寻找候选的 0x16 作为帧尾(支持一包多帧/脏数据)
947
- let end = b.indexOf(0x16, 5);
948
- while (end !== -1) {
949
- // —— 先试 2 字节 CRC-16/X.25(低字节在前),计算区间:从 LL 开始到 CRC 前一字节 ——
950
- if (end >= 3) {
951
- const lo = b[end - 2], hi = b[end - 1];
952
- const fcs = (hi << 8) | lo;
953
- const calc = crc16x25(b, /*start=*/1, /*end=*/end - 2); // [LL..CRC-1]
954
- if (calc === fcs) {
955
- return { ok: true, used: end + 1, frame: b.slice(0, end + 1) };
956
- }
957
- }
958
-
959
- // —— 再试 1 字节和校验(sum8),计算区间:从 control 起到 CS 前 ——
960
- if (end >= 2) {
961
- const cs = b[end - 1];
962
- const sum = sum8(b, /*start=*/3, /*end=*/end - 1);
963
- if (cs === sum) {
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
- // 找下一个 0x16
969
- end = b.indexOf(0x16, end + 1);
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
- return { ok: false };
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