node-red-zelecproto 0.0.8 → 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.
- package/645.js +50 -0
- package/AGENTS.md +133 -0
- package/package.json +1 -1
- package/test.js +26 -13
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
|
*/
|
|
@@ -672,6 +708,20 @@ function decode645(_msg) {
|
|
|
672
708
|
'广播认证密钥有效': !!(v & (1 << 6)), '主控密钥不可恢复': !!(v & (1 << 7))
|
|
673
709
|
}
|
|
674
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
|
+
};
|
|
675
725
|
} else if (di === '04000102' && arrPush.length >= 3) {
|
|
676
726
|
value = Buffer.from(arrPush.slice(-3).reverse()).toString('hex').toUpperCase(); // "HHMMSS"
|
|
677
727
|
} else if (di === '04000101' && arrPush.length >= 8) {
|
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
package/test.js
CHANGED
|
@@ -7,9 +7,9 @@ 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 = '685134234200006893068467567533336F16'
|
|
11
|
-
msg.proto = 645
|
|
12
|
-
msg.mode = 'decode'
|
|
10
|
+
// msg.payload = '685134234200006893068467567533336F16'
|
|
11
|
+
// msg.proto = 645
|
|
12
|
+
// msg.mode = 'decode'
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
|
|
@@ -23,14 +23,27 @@ msg.mode = 'decode'
|
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
|
|
26
|
-
msg.payload=[
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
]
|
|
26
|
+
// msg.payload=[
|
|
27
|
+
// {
|
|
28
|
+
// barcode:'1',
|
|
29
|
+
// payload : '685134234200006893068467567533336F16',
|
|
30
|
+
// proto : 645
|
|
31
|
+
// }
|
|
32
|
+
// ]
|
|
33
33
|
|
|
34
|
-
let nmsg = proto645(msg);
|
|
35
|
-
console.log(nmsg);
|
|
36
|
-
// console.log(JSON.stringify(nmsg));
|
|
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));
|