node-red-zelecproto 0.0.7 → 0.0.9

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 (4) hide show
  1. package/645.js +88 -30
  2. package/AGENTS.md +133 -0
  3. package/package.json +1 -1
  4. package/test.js +33 -6
package/645.js CHANGED
@@ -28,6 +28,10 @@ function build645Frame(i, reverseAddr, csMode) {
28
28
  return buildEncrypted645Frame(i, addrBytes, reverseAddr, csMode);
29
29
  }
30
30
 
31
+ if (oad === '04040300') {
32
+ return buildSwitchScreen645Frame(i, addrBytes, csMode);
33
+ }
34
+
31
35
 
32
36
  // OAD 加密:倒序 + 每字节 +0x33
33
37
  const cmdEx = encryptOAD(oad); // "xx xx xx xx"
@@ -50,6 +54,38 @@ function build645Frame(i, reverseAddr, csMode) {
50
54
  });
51
55
  }
52
56
 
57
+ function buildSwitchScreen645Frame(i, addrBytes, csMode) {
58
+ const payload = i.payload || {};
59
+ const screenRaw = payload.screenContent || payload.switchScreenContent || payload.switchScreen || payload.itemContent || payload.dataHex;
60
+ let screenHex = String(screenRaw || '').replace(/\s+/g, '').toUpperCase();
61
+ if (!screenHex) throw new Error('04040300 屏显切换需要提供 screenContent/itemContent');
62
+ if (screenHex.length === 8) screenHex += '00';
63
+ if (!/^[0-9A-F]{10}$/.test(screenHex)) throw new Error('04040300 切屏内容必须是 10 位 HEX,例如 0001000000');
64
+
65
+ const screenBody = screenHex.slice(0, 8);
66
+ const optionByte = screenHex.slice(8, 10);
67
+ const oadBytes = hexToBytes(encryptOAD('04040300').replace(/\s+/g, ''));
68
+ const dataBytes = [
69
+ ...oadBytes,
70
+ ...hexToBytes(reverseHexBytes(screenBody)).map(b => (b + 0x33) & 0xFF),
71
+ ...hexToBytes(optionByte).map(b => (b + 0x33) & 0xFF)
72
+ ];
73
+
74
+ const C = 0x11;
75
+ const L = dataBytes.length;
76
+ const frameNoCS = [0x68, ...addrBytes, 0x68, C, L, ...dataBytes];
77
+ const cs = calc645CSForBytes(frameNoCS, csMode);
78
+ const csHex = cs.toString(16).toUpperCase().padStart(2, '0');
79
+ const fullHex = (`FE FE FE FE ${bytesToHex(frameNoCS)} ${csHex} 16`).replace(/\s+/g, '');
80
+ let _payload = i.payload || {};
81
+ return Object.assign(i, _payload, {
82
+ com_exec_addr: bytesToHex(addrBytes).replace(/\s+/g, ''),
83
+ cmdEx: bytesToHex(oadBytes).replace(/\s+/g, ''),
84
+ switchScreenContent: screenHex,
85
+ payload: fullHex
86
+ });
87
+ }
88
+
53
89
  /**
54
90
  * 读取地址
55
91
  */
@@ -220,7 +256,16 @@ function reverseHexBytes(hex) {
220
256
 
221
257
  /******************** 解码:batchMsg(返回统一对象,含 exec_addr/di/value 等) ********************/
222
258
  function decode645(_msg) {
223
- let _reslt = {};
259
+ // 创建一个全新的对象,不依赖于传入的 _msg 对象
260
+ const result = {};
261
+
262
+ // 复制原始消息的非 payload 属性
263
+ for (const key in _msg) {
264
+ if (_msg.hasOwnProperty(key) && key !== 'payload') {
265
+ result[key] = _msg[key];
266
+ }
267
+ }
268
+
224
269
  // ===== 工具 =====
225
270
  function calc645Checksum(u8) {
226
271
  let s = 0;
@@ -238,23 +283,23 @@ function decode645(_msg) {
238
283
  // ===== 取入参并清洗 =====
239
284
  // 允许传 {put:'...'} 或 {payload:'...'},优先 put
240
285
  let put = ((_msg.payload && _msg.payload.put) || _msg.put || _msg.payload || '').toString();
241
- if (!put) return { ok: false, reason: 'empty' };
286
+ if (!put) return Object.assign(result, { ok: false, reason: 'empty' });
242
287
 
243
288
  // 去前导 FE…FE(最多4个),并去空格
244
289
  put = f_stripLeadingFE(put.replace(/\s+/g, '')).toUpperCase();
245
290
 
246
291
  const buf = hexStringToBuffer(put);
247
- if (!buf || buf.length < 12) return { ok: false, reason: 'too_short', raw: put };
292
+ if (!buf || buf.length < 12) return Object.assign(result, { ok: false, reason: 'too_short', raw: put });
248
293
 
249
294
  // ===== 基本结构:68 AA(6) 68 CTRL LEN DATA(n) CS 16 =====
250
295
  // 容错:定位到第一个 0x68
251
296
  let p0 = 0;
252
297
  while (p0 < buf.length && buf[p0] !== 0x68) p0++;
253
- if (p0 + 12 > buf.length) return { ok: false, reason: 'no_head68', raw: put };
298
+ if (p0 + 12 > buf.length) return Object.assign(result, { ok: false, reason: 'no_head68', raw: put });
254
299
 
255
300
  // 第二个 0x68 在地址后
256
301
  const p68_2 = p0 + 7;
257
- if (buf[p0] !== 0x68 || buf[p68_2] !== 0x68) return { ok: false, reason: 'bad_68_pair', raw: put };
302
+ if (buf[p0] !== 0x68 || buf[p68_2] !== 0x68) return Object.assign(result, { ok: false, reason: 'bad_68_pair', raw: put });
258
303
 
259
304
  // 读出地址区(A0..A5)
260
305
  const addrBytes = buf.subarray(p0 + 1, p0 + 7);
@@ -269,8 +314,8 @@ function decode645(_msg) {
269
314
  const pCS = pDataEnd;
270
315
  const pEnd = pDataEnd + 1;
271
316
 
272
- if (len < 0 || pEnd >= buf.length) return { ok: false, reason: 'len_oob', exec_addr, raw: put };
273
- if (buf[pEnd] !== 0x16) return { ok: false, reason: 'no_end16', exec_addr, raw: put };
317
+ if (len < 0 || pEnd >= buf.length) return Object.assign(result, { ok: false, reason: 'len_oob', exec_addr, raw: put });
318
+ if (buf[pEnd] !== 0x16) return Object.assign(result, { ok: false, reason: 'no_end16', exec_addr, raw: put });
274
319
 
275
320
  // —— CS 双模校验:std(第二个68后)与 full(第一个68起)任一通过即可 ——
276
321
  const cs_frame = buf[pCS];
@@ -278,19 +323,19 @@ function decode645(_msg) {
278
323
  const cs_full = calc645Checksum(buf.subarray(p0, pCS));
279
324
  const cs_ok = (cs_std === cs_frame) || (cs_full === cs_frame);
280
325
  if (!cs_ok) {
281
- return {
326
+ return Object.assign(result, {
282
327
  success: false, reason: 'cs_fail',
283
328
  cs_frame: cs_frame.toString(16).toUpperCase().padStart(2, '0'),
284
329
  cs_std: cs_std.toString(16).toUpperCase().padStart(2, '0'),
285
330
  cs_full: cs_full.toString(16).toUpperCase().padStart(2, '0'),
286
331
  exec_addr, raw: put
287
- };
332
+ });
288
333
  }
289
334
 
290
335
  const dataRaw = Array.from(buf.subarray(pDataStart, pDataEnd));
291
336
  // ===== 控制确认/错误帧 =====
292
337
  if (len === 0 && ctrl === 0x9C) {
293
- return {
338
+ return Object.assign(result, {
294
339
  ok: true,
295
340
  type: "control_ack",
296
341
  exec_addr,
@@ -300,12 +345,12 @@ function decode645(_msg) {
300
345
  description: "控制命令执行成功 (0x9C)",
301
346
  success: cs_ok,
302
347
  raw: put
303
- };
348
+ });
304
349
  }
305
350
 
306
351
  if (ctrl === 0xDC) {
307
352
  const detail = minus33(dataRaw);
308
- return {
353
+ return Object.assign(result, {
309
354
  ok: true,
310
355
  type: "control_ack_ext",
311
356
  exec_addr,
@@ -315,12 +360,12 @@ function decode645(_msg) {
315
360
  statusBytes: bytesToHex(detail).replace(/\s+/g, ""),
316
361
  success: true,
317
362
  raw: put
318
- };
363
+ });
319
364
  }
320
365
 
321
366
  if (ctrl === 0xDA) {
322
367
  const detail = minus33(dataRaw);
323
- return {
368
+ return Object.assign(result, {
324
369
  ok: false,
325
370
  type: "control_error",
326
371
  exec_addr,
@@ -331,12 +376,12 @@ function decode645(_msg) {
331
376
  detail: detail.length ? detail[0].toString(16).toUpperCase().padStart(2, "0") : null,
332
377
  data: bytesToHex(detail).replace(/\s+/g, ""),
333
378
  raw: put
334
- };
379
+ });
335
380
  }
336
381
 
337
382
  if (ctrl === 0xD1) {
338
383
  const errRaw = minus33(dataRaw);
339
- return {
384
+ return Object.assign(result, {
340
385
  ok: false,
341
386
  type: "control_error",
342
387
  exec_addr,
@@ -347,13 +392,13 @@ function decode645(_msg) {
347
392
  detail: errRaw.length ? errRaw[0].toString(16).toUpperCase().padStart(2, "0") : null,
348
393
  data: bytesToHex(errRaw).replace(/\s+/g, ""),
349
394
  raw: put
350
- };
395
+ });
351
396
  }
352
397
  // ===== 读地址响应:CTRL=0x93 且 LEN=0x06 =====
353
398
  if (ctrl === 0x93 && len === 0x06) {
354
399
  const addrMinus33 = minus33(dataRaw);
355
400
  const addr_from_data = bcdAddr12FromLE(Uint8Array.from(addrMinus33));
356
- return {
401
+ return Object.assign(result, {
357
402
  ok: true,
358
403
  type: 'address_response',
359
404
  exec_addr, // 帧头解析出的地址
@@ -363,7 +408,7 @@ function decode645(_msg) {
363
408
  addr_bytes_hex,
364
409
  success: cs_ok,
365
410
  raw: put
366
- };
411
+ });
367
412
  }
368
413
 
369
414
  // ===== 普通数据响应:DATA-0x33,首4字节(倒序)为 DI =====
@@ -371,7 +416,7 @@ function decode645(_msg) {
371
416
  let di = '';
372
417
  if (arrPush.length >= 4) di = Buffer.from(arrPush.slice(0, 4).reverse()).toString('hex').toUpperCase();
373
418
 
374
- // 动态判定是否为 “日冻结/结算日冻结” 的 DI(不再限定 30 天,可到 62 等任意 1B 日号)
419
+ // 动态判定是否为 "日冻结/结算日冻结" 的 DI(不再限定 30 天,可到 62 等任意 1B 日号)
375
420
  function isDailyFreezeDI(di) {
376
421
  // 645 常见日冻结:050601dd(正向)、050602dd(反向);dd 为 1B 日号(01~3E/3F/…,扩展都能兜住)
377
422
  return /^05060[12][0-9A-F]{2}$/i.test(di || '');
@@ -383,7 +428,7 @@ function decode645(_msg) {
383
428
 
384
429
 
385
430
  //判定为组合电量
386
- function isToatalDI(di) {
431
+ function isTootalDI(di) {
387
432
  let arr = ['0000FF00', '0001FF00', '0002FF00']
388
433
  return arr.includes(di)
389
434
  }
@@ -403,7 +448,7 @@ function decode645(_msg) {
403
448
 
404
449
  let value = '';
405
450
  try {
406
- // // —— 读数据“请求帧”:CTRL=0x11 且 LEN=0x04,仅含 DI,无数值 ——
451
+ // // —— 读数据"请求帧":CTRL=0x11 且 LEN=0x04,仅含 DI,无数值 ——
407
452
  // if (ctrl === 0x11 && len === 0x04 && arrPush.length === 4) {
408
453
  // return Object.assign(_msg, {
409
454
  // ok: true,
@@ -663,6 +708,20 @@ function decode645(_msg) {
663
708
  '广播认证密钥有效': !!(v & (1 << 6)), '主控密钥不可恢复': !!(v & (1 << 7))
664
709
  }
665
710
  };
711
+ } else if (di === '04040300' && arrPush.length >= 5) {
712
+ const body = arrPush.slice(4);
713
+ const option = body.length > 0 ? body[body.length - 1] : null;
714
+ const screenContent = body.length > 1
715
+ ? Buffer.from(body.slice(0, -1).reverse()).toString('hex').toUpperCase()
716
+ : '';
717
+ value = {
718
+ type: 'switch_screen',
719
+ description: '屏显切换',
720
+ screenContent,
721
+ option: option == null ? null : option.toString(16).toUpperCase().padStart(2, '0'),
722
+ itemContent: screenContent + (option == null ? '' : option.toString(16).toUpperCase().padStart(2, '0')),
723
+ rawMinus33: bytesToHex(arrPush).replace(/\s+/g, '')
724
+ };
666
725
  } else if (di === '04000102' && arrPush.length >= 3) {
667
726
  value = Buffer.from(arrPush.slice(-3).reverse()).toString('hex').toUpperCase(); // "HHMMSS"
668
727
  } else if (di === '04000101' && arrPush.length >= 8) {
@@ -673,19 +732,18 @@ function decode645(_msg) {
673
732
  } else if (di === '03300D00' && arrPush.length >= 4) {
674
733
  value = bytesToIntBE(arrPush.slice(4).reverse());
675
734
  }
676
- else if ((isDailyFreezeDI(di) || isSettlementFreezeDI(di) || isToatalDI(di)) && arrPush.length >= 8) {
735
+ else if ((isDailyFreezeDI(di) || isSettlementFreezeDI(di) || isTootalDI(di)) && arrPush.length >= 8) {
677
736
  // else if ((arrDays.includes(di) || arrDaysFX.includes(di) || arrDaysZX.includes(di) || arrDaysJSR.includes(di)) && arrPush.length >= 8) {
678
737
  // const energyNum = bytesToIntBE(arrPush.slice(4, 8).reverse());
679
738
  // value = Math.round(energyNum / 100 * 100) / 100;
680
739
  // DATA-0x33 后:DI(4) + 5×(4B 小端 BCD) = 24B LEN → 与 698 的日冻结 record 风格一致
681
- // DATA-0x33 后: DI(4) + N×(4B 小端BCD);常见为 5 组(总/尖/峰/平/谷),也有只回 1 组“总”的情况
682
-
740
+ // DATA-0x33 后: DI(4) + N×(4B 小端BCD);常见为 5 组(总/尖/峰/平/谷),也有只回 1 组"总"的情况
683
741
  const afterDI = arrPush.slice(4);
684
742
  const labels = ['总', '尖', '峰', '平', '谷'];
685
743
 
686
744
 
687
745
  // 0000FF00 / 0001FF00 / 0002FF00:乱码模式
688
- const isTotalBlock = isToatalDI(di);
746
+ const isTotalBlock = isTootalDI(di);
689
747
 
690
748
  // 4B 小端 BCD → 十进制(2 小数位,单位 kWh)
691
749
  function parseEnergyLE4(b4) {
@@ -724,11 +782,11 @@ function decode645(_msg) {
724
782
  // 如设备回了超过 5 组(极少见)或多余字节,挂个 extra 方便排查,不影响主结果
725
783
  const extraStart = parseCount * 4;
726
784
  if (afterDI.length > extraStart) {
727
- _reslt.extraRaw = bytesToHex(afterDI.slice(extraStart)).replace(/\s+/g, '');
785
+ result.extraRaw = bytesToHex(afterDI.slice(extraStart)).replace(/\s+/g, '');
728
786
  }
729
787
 
730
788
  value = items.map(it => it.value); // 与你 698 解析保持一致:value 返回纯数值数组
731
- _reslt.payload = { data: items, value }; // 附带明细(含 rawBCD)
789
+ result.payload = { data: items, value }; // 附带明细(含 rawBCD)
732
790
  } else if (di === '04000402' && arrPush.length >= 8) {
733
791
  value = Buffer.from(arrPush.slice(4).reverse()).toString('hex').toUpperCase();
734
792
  } else if (arrPub.includes(di) && arrPush.length >= 8) {
@@ -794,10 +852,10 @@ function decode645(_msg) {
794
852
  };
795
853
  }
796
854
  } catch (e) {
797
- return { ok: false, reason: 'decode_exception', exec_addr, di, ctrl, len, raw: put, err: String(e) };
855
+ return Object.assign(result, { ok: false, reason: 'decode_exception', exec_addr, di, ctrl, len, raw: put, err: String(e) });
798
856
  }
799
857
 
800
- return Object.assign(_reslt, {
858
+ return Object.assign(result, {
801
859
  ok: true,
802
860
  type: 'data_response',
803
861
  exec_addr,
package/AGENTS.md ADDED
@@ -0,0 +1,133 @@
1
+ # AGENTS.md
2
+
3
+ This file contains guidelines and commands for agentic coding agents working in the node-red-zelecproto repository.
4
+
5
+ ## Global Settings
6
+ - **Proxy**: Execute `proxy` command before running any network-related commands to enable proxy connection
7
+
8
+ ## Build/Test/Lint Commands
9
+
10
+ ### Current Commands
11
+ - `npm test` - Currently shows "Error: no test specified" (placeholder)
12
+ - No build, lint, or typecheck commands are currently configured
13
+
14
+ ### Testing
15
+ - Use `node test.js` to run the manual test file
16
+ - Test file located at: `test.js`
17
+ - For single test execution, run `node -e "require('./test.js')"`
18
+
19
+ ### Development
20
+ - No build process required (Node-RED nodes load directly)
21
+ - Manual testing via Node-RED flow editor recommended
22
+
23
+ ## Code Style Guidelines
24
+
25
+ ### File Structure and Naming
26
+ - Main node files: `zelecproto.js`, `zbatchproto.js`, `zeleble.js`
27
+ - Protocol implementations: `645.js`, `698.js`, `ble.js`
28
+ - HTML definitions: `zelecproto.html`, `zbatchproto.html`, `zeleble.html`
29
+ - Icons in: `icons/` directory (SVG format)
30
+ - Use kebab-case for file names
31
+
32
+ ### Module Pattern
33
+ All Node-RED nodes must follow this pattern:
34
+ ```javascript
35
+ module.exports = function (RED) {
36
+ "use strict";
37
+
38
+ // Require dependencies
39
+ var proto645 = require("./645");
40
+
41
+ function NodeName(n) {
42
+ RED.nodes.createNode(this, n);
43
+ var node = this;
44
+
45
+ this.on("input", function (msg, send, done) {
46
+ // Process message
47
+ send(msg);
48
+ done();
49
+ });
50
+
51
+ this.on('close', () => {
52
+ // Cleanup if needed
53
+ });
54
+ }
55
+
56
+ RED.nodes.registerType("nodename", NodeName);
57
+ }
58
+ ```
59
+
60
+ ### Import/Require Style
61
+ - Use `var` for requires (consistent with existing codebase)
62
+ - Local dependencies first: `var proto645 = require("./645");`
63
+ - Group requires at top of module function
64
+ - Use relative paths for local files
65
+
66
+ ### Code Formatting
67
+ - Use strict mode: `"use strict";` at top of module function
68
+ - Indentation: 4 spaces (consistent with existing files)
69
+ - Semicolons required
70
+ - String quotes: single quotes preferred
71
+ - Object property access: dot notation for known properties, bracket notation for dynamic
72
+
73
+ ### Naming Conventions
74
+ - Node functions: PascalCase (e.g., `zelecproto`, `zbatchproto`)
75
+ - Variables: camelCase (e.g., `msg`, `node`, `addrBytes`)
76
+ - Constants: UPPER_SNAKE_CASE (e.g., `START`, `END`, `OAD`)
77
+ - File names: kebab-case (e.g., `zelecproto.js`, `645.js`)
78
+
79
+ ### Error Handling
80
+ - Use `throw new Error()` for validation errors
81
+ - Include descriptive error messages
82
+ - Validate input parameters before processing
83
+ - Example: `if (addrRaw.length !== 12) throw new Error('com_exec_addr 必须是 6 字节(12个HEX字符)');`
84
+
85
+ ### Message Processing
86
+ - Always use the three-parameter input handler: `function (msg, send, done)`
87
+ - Call `send(msg)` before `done()`
88
+ - Clean up temporary properties: `delete msg._proto`
89
+ - Preserve original message structure when possible
90
+
91
+ ### Protocol Implementation
92
+ - 645 Protocol: Focus on DL/T 645 frame building/parsing
93
+ - 698 Protocol: DL/T 698.45 encode/decode with CRC-16/X-25
94
+ - BLE Protocol: 国网多芯物联表蓝牙通信协议
95
+ - Each protocol file should export a function that takes `msg` and returns modified `msg`
96
+
97
+ ### HTML Node Definitions
98
+ - Use data-template-name attribute matching node type
99
+ - Include name field in all nodes
100
+ - Set appropriate category, color, and icon
101
+ - Category: 'zutils' for all nodes
102
+ - Icons: reference SVG files in icons/ directory
103
+
104
+ ### Comments and Documentation
105
+ - Use Chinese comments for protocol-specific explanations (consistent with existing code)
106
+ - Use JSDoc-style comments for functions
107
+ - Include protocol references and frame format descriptions
108
+ - Example: `// 国网多芯物联表《蓝牙通信及脉冲检定说明手册(0224)》协议`
109
+
110
+ ### Buffer/Hex Handling
111
+ - Use `Buffer.from()` for creating buffers
112
+ - Hex strings should be uppercase without spaces for final output
113
+ - Use helper functions for hex/bytes conversion
114
+ - Maintain consistent hex formatting across protocols
115
+
116
+ ### Node-RED Integration
117
+ - Register nodes in package.json under `node-red.nodes`
118
+ - Minimum Node.js version: >=15
119
+ - Minimum Node-RED version: >=1.3
120
+ - No external dependencies currently required
121
+
122
+ ## Development Workflow
123
+ 1. Modify protocol implementation files as needed
124
+ 2. Test with `node test.js` or via Node-RED flow editor
125
+ 3. Ensure all nodes follow the standard module pattern
126
+ 4. Verify HTML definitions match JavaScript node registrations
127
+ 5. Test message processing with sample data
128
+
129
+ ## Protocol-Specific Notes
130
+ - 645: Handle address reversal and OAD encryption
131
+ - 698: Implement CRC-16/X-25 checksum validation
132
+ - BLE: Follow frame format with Start/End markers
133
+ - All protocols should support both encode and decode operations
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-red-zelecproto",
3
- "version": "0.0.7",
3
+ "version": "0.0.9",
4
4
  "description": "node-red zelecproto node",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/test.js CHANGED
@@ -7,9 +7,11 @@ let msg = {};
7
7
  // msg.payload = 'FE FE FE FE 68 30 00 C3 05 77 59 63 00 15 19 11 59 0F 90 00 17 85 01 01 30 1B 07 00 01 01 01 02 02 00 02 02 06 00 00 00 00 00 00 00 01 00 04 B2 F3 8B 23 F2 7C 16'
8
8
  // msg.payload = 'FE FE FE FE 68 33 00 C3 05 60 80 63 00 15 19 11 8B E3 90 00 1A 85 01 01 30 13 0A 00 01 01 01 02 02 00 02 02 1C 07 E3 07 17 0B 06 1B 00 00 00 01 00 04 2C 96 C6 41 EF 15 16'
9
9
  // msg.payload = 'FE FE FE FE 68 2C 00 C3 05 16 01 00 00 00 00 11 54 84 90 00 13 85 01 01 30 13 0A 00 01 01 01 02 02 00 02 02 00 00 00 00 01 00 04 EE E6 E8 86 13 64 16'
10
- msg.payload = 'FEFEFEFE68983528070000689306CB685B3A33339316'
11
- msg.proto = 645
12
- msg.mode = 'decode'
10
+ // msg.payload = '685134234200006893068467567533336F16'
11
+ // msg.proto = 645
12
+ // msg.mode = 'decode'
13
+
14
+
13
15
 
14
16
 
15
17
 
@@ -17,6 +19,31 @@ msg.mode = 'decode'
17
19
  // console.log(proto698(msg));
18
20
 
19
21
 
20
- let nmsg = proto645(msg);
21
- console.log(nmsg);
22
- console.log(JSON.stringify(nmsg));
22
+
23
+
24
+
25
+
26
+ // msg.payload=[
27
+ // {
28
+ // barcode:'1',
29
+ // payload : '685134234200006893068467567533336F16',
30
+ // proto : 645
31
+ // }
32
+ // ]
33
+
34
+ // let nmsg = proto645(msg);
35
+ // console.log(nmsg);
36
+ // console.log(JSON.stringify(nmsg));
37
+
38
+
39
+
40
+ // msg.payload=[
41
+ // {
42
+ // barcode:'1',
43
+ // payload : 'FE FE FE FE 68 52 00 C3 05 60 50 30 00 49 25 11 BA 8D 90 00 39 85 01 01 00 00 04 00 01 01 05 14 00 00 00 00 00 C2 91 74 14 00 00 00 00 00 10 CE 12 14 00 00 00 00 00 0A 24 C2 14 00 00 00 00 00 66 C8 DA 14 00 00 00 00 00 3B FC 54 00 00 01 00 04 8D 62 D9 49 53 D1 16',
44
+ // proto : 698
45
+ // }
46
+ // ]
47
+
48
+ // console.log(JSON.stringify(proto698(msg)));
49
+ // // console.log(proto698(msg));