njs-modbus 2.0.1 → 3.0.2
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/README.md +265 -154
- package/README.zh-CN.md +314 -0
- package/dist/index.cjs +1887 -1074
- package/dist/index.d.ts +369 -217
- package/dist/index.mjs +1884 -1073
- package/dist/src/error-code.d.ts +2 -24
- package/dist/src/layers/application/abstract-application-layer.d.ts +12 -7
- package/dist/src/layers/application/ascii-application-layer.d.ts +8 -9
- package/dist/src/layers/application/rtu-application-layer.d.ts +21 -44
- package/dist/src/layers/application/tcp-application-layer.d.ts +8 -6
- package/dist/src/layers/physical/abstract-physical-layer.d.ts +40 -12
- package/dist/src/layers/physical/index.d.ts +9 -3
- package/dist/src/layers/physical/serial-physical-layer.d.ts +43 -13
- package/dist/src/layers/physical/tcp-client-physical-layer.d.ts +12 -10
- package/dist/src/layers/physical/tcp-physical-connection.d.ts +17 -0
- package/dist/src/layers/physical/tcp-server-physical-layer.d.ts +23 -12
- package/dist/src/layers/physical/udp-client-physical-layer.d.ts +35 -0
- package/dist/src/layers/physical/udp-server-physical-layer.d.ts +54 -0
- package/dist/src/layers/physical/utils.d.ts +39 -0
- package/dist/src/layers/physical/vars.d.ts +11 -0
- package/dist/src/master/master.d.ts +45 -18
- package/dist/src/slave/slave.d.ts +57 -33
- package/dist/src/types.d.ts +3 -3
- package/dist/src/utils/index.d.ts +4 -2
- package/dist/src/utils/rtu-timing.d.ts +49 -0
- package/dist/src/utils/whitelist.d.ts +11 -0
- package/package.json +9 -7
- package/dist/src/layers/physical/udp-physical-layer.d.ts +0 -42
- package/dist/src/utils/genConnectionId.d.ts +0 -2
- package/dist/test/adu-buffer.test.d.ts +0 -1
- package/dist/test/ascii-hex-sentry.test.d.ts +0 -1
- package/dist/test/ascii-hex-validation.test.d.ts +0 -1
- package/dist/test/ascii-tcp-fragmentation.test.d.ts +0 -1
- package/dist/test/check-range.test.d.ts +0 -1
- package/dist/test/fallback-atomic.test.d.ts +0 -1
- package/dist/test/fallback-serial.test.d.ts +0 -1
- package/dist/test/fc17-serverid-validation.test.d.ts +0 -1
- package/dist/test/fc43-conformity.test.d.ts +0 -1
- package/dist/test/fc43-utf8-objects.test.d.ts +0 -1
- package/dist/test/gen-connection-id.test.d.ts +0 -1
- package/dist/test/helpers/raw-tcp.d.ts +0 -38
- package/dist/test/master-concurrent.test.d.ts +0 -1
- package/dist/test/modbus-error.test.d.ts +0 -1
- package/dist/test/physical-lifecycle.test.d.ts +0 -1
- package/dist/test/predict-rtu.test.d.ts +0 -1
- package/dist/test/rtu-custom-fc.test.d.ts +0 -1
- package/dist/test/rtu-pool-overflow.test.d.ts +0 -1
- package/dist/test/rtu-t15-timing.test.d.ts +0 -1
- package/dist/test/rtu-t35-default.test.d.ts +0 -1
- package/dist/test/rtu-t35-strict.test.d.ts +0 -1
- package/dist/test/rtu-tcp-fragmentation.test.d.ts +0 -1
- package/dist/test/serial-e2e.test.d.ts +0 -1
- package/dist/test/slave-multi-connection.test.d.ts +0 -1
- package/dist/test/slave.test.d.ts +0 -1
- package/dist/test/tcp-fragmentation.test.d.ts +0 -1
- package/dist/test/udp-multi-client.test.d.ts +0 -1
package/README.md
CHANGED
|
@@ -1,203 +1,314 @@
|
|
|
1
1
|
# njs-modbus
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
English | [简体中文](./README.zh-CN.md)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
A pure JavaScript implementation of Modbus for Node.js.
|
|
6
6
|
|
|
7
7
|
<!-- prettier-ignore-start -->
|
|
8
8
|
[](http://www.npm-stats.com/~packages/njs-modbus)
|
|
9
9
|
[](https://www.npmjs.com/package/njs-modbus)
|
|
10
10
|
[](https://bundlephobia.com/package/njs-modbus)
|
|
11
|
+
[](https://github.com/xiejay97/njs-modbus/actions)
|
|
11
12
|
<!-- prettier-ignore-end -->
|
|
12
13
|
|
|
13
|
-
</div>
|
|
14
|
-
|
|
15
|
-
## Introduction
|
|
16
|
-
|
|
17
|
-
`njs-modbus` is designed as a layered architecture, including the physical layer and the application layer:
|
|
18
|
-
|
|
19
|
-
- Physical layer implements Serial Port, TCP/IP and UDP/IP.
|
|
20
|
-
- Application layer implements RTU, ASCII and TCP.
|
|
21
|
-
|
|
22
|
-
`njs-modbus` provide both client and server.
|
|
23
|
-
|
|
24
14
|
## Features
|
|
25
15
|
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
30
|
-
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
|
36
|
-
|
|
37
|
-
|
|
|
38
|
-
|
|
|
39
|
-
|
|
|
40
|
-
|
|
|
41
|
-
|
|
|
42
|
-
|
|
|
43
|
-
|
|
|
44
|
-
|
|
|
45
|
-
|
|
|
46
|
-
|
|
|
47
|
-
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
- Modbus RTU
|
|
52
|
-
- Modbus ASCII
|
|
53
|
-
- Modbus TCP/IP
|
|
54
|
-
- Modbus UDP/IP
|
|
55
|
-
- Modbus RTU/ASCII Over TCP/IP
|
|
56
|
-
- Modbus RTU/ASCII Over UDP/IP
|
|
57
|
-
|
|
58
|
-
#### Installation
|
|
16
|
+
- **Protocols:** Modbus TCP, RTU, ASCII
|
|
17
|
+
- **Transports:** Serial, TCP client/server, UDP client/server
|
|
18
|
+
- **Master:** FIFO or pipelined concurrent requests (TCP only)
|
|
19
|
+
- **Slave:** Per-connection FIFO or concurrent processing (TCP only)
|
|
20
|
+
- **Custom function codes** with pluggable framing
|
|
21
|
+
- **Broadcasting** (unit = 0)
|
|
22
|
+
- **Full TypeScript**
|
|
23
|
+
- **Zero runtime dependencies** (peer-depends on `serialport` for serial)
|
|
24
|
+
|
|
25
|
+
| Code | Name |
|
|
26
|
+
|------|------|
|
|
27
|
+
| 01 | Read Coils |
|
|
28
|
+
| 02 | Read Discrete Inputs |
|
|
29
|
+
| 03 | Read Holding Registers |
|
|
30
|
+
| 04 | Read Input Registers |
|
|
31
|
+
| 05 | Write Single Coil |
|
|
32
|
+
| 06 | Write Single Register |
|
|
33
|
+
| 15 | Write Multiple Coils |
|
|
34
|
+
| 16 | Write Multiple Registers |
|
|
35
|
+
| 17 | Report Server ID |
|
|
36
|
+
| 22 | Mask Write Register |
|
|
37
|
+
| 23 | Read/Write Multiple Registers |
|
|
38
|
+
| 43/14 | Read Device Identification |
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
59
41
|
|
|
60
42
|
```bash
|
|
61
43
|
npm install njs-modbus
|
|
62
44
|
```
|
|
63
45
|
|
|
64
|
-
|
|
46
|
+
For serial transport, also install the peer dependency:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npm install serialport
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Quick Start
|
|
65
53
|
|
|
66
|
-
###
|
|
54
|
+
### TCP Master
|
|
67
55
|
|
|
68
56
|
```typescript
|
|
69
|
-
import {
|
|
70
|
-
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
});
|
|
83
|
-
})
|
|
84
|
-
.catch((error) => {
|
|
85
|
-
console.log(error);
|
|
86
|
-
});
|
|
57
|
+
import { ModbusMaster } from 'njs-modbus';
|
|
58
|
+
|
|
59
|
+
const master = new ModbusMaster({
|
|
60
|
+
physical: { type: 'TCP_CLIENT' },
|
|
61
|
+
protocol: { type: 'TCP' },
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
await master.open({ port: 502, host: '192.168.1.10' });
|
|
65
|
+
|
|
66
|
+
const res = await master.readHoldingRegisters(1, 0, 10);
|
|
67
|
+
console.log(res.data);
|
|
68
|
+
|
|
69
|
+
await master.close();
|
|
87
70
|
```
|
|
88
71
|
|
|
89
|
-
###
|
|
72
|
+
### TCP Slave
|
|
90
73
|
|
|
91
74
|
```typescript
|
|
92
|
-
import
|
|
93
|
-
import { SerialPhysicalLayer, RtuApplicationLayer, ModbusSlave } from 'njs-modbus';
|
|
75
|
+
import { ModbusSlave } from 'njs-modbus';
|
|
94
76
|
|
|
95
|
-
const
|
|
96
|
-
|
|
77
|
+
const slave = new ModbusSlave({
|
|
78
|
+
physical: { type: 'TCP_SERVER' },
|
|
79
|
+
protocol: { type: 'TCP' },
|
|
80
|
+
});
|
|
97
81
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
holdingRegisters: new Map<number, number>(),
|
|
103
|
-
};
|
|
104
|
-
const slave1: ModbusSlaveModel = {
|
|
105
|
-
readDiscreteInputs: (address, length) => {
|
|
106
|
-
return Array.from({ length }).map((_, i) => {
|
|
107
|
-
const discreteInput = slave1Data.discreteInputs.get(address + i);
|
|
108
|
-
if (typeof discreteInput === 'undefined') {
|
|
109
|
-
return false;
|
|
110
|
-
}
|
|
111
|
-
return discreteInput;
|
|
112
|
-
});
|
|
82
|
+
slave.add({
|
|
83
|
+
unit: 1,
|
|
84
|
+
readHoldingRegisters: (address, length) => {
|
|
85
|
+
return Array.from({ length }, (_, i) => address + i);
|
|
113
86
|
},
|
|
87
|
+
});
|
|
114
88
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
const coil = slave1Data.coils.get(address + i);
|
|
118
|
-
if (typeof coil === 'undefined') {
|
|
119
|
-
return false;
|
|
120
|
-
}
|
|
121
|
-
return coil;
|
|
122
|
-
});
|
|
123
|
-
},
|
|
124
|
-
writeSingleCoil: (address, value) => {
|
|
125
|
-
slave1Data.coils.set(address, value);
|
|
126
|
-
},
|
|
89
|
+
await slave.open({ port: 502 });
|
|
90
|
+
```
|
|
127
91
|
|
|
128
|
-
|
|
129
|
-
return Array.from({ length }).map((_, i) => {
|
|
130
|
-
const inputRegister = slave1Data.inputRegisters.get(address + i);
|
|
131
|
-
if (typeof inputRegister === 'undefined') {
|
|
132
|
-
return 0;
|
|
133
|
-
}
|
|
134
|
-
return inputRegister;
|
|
135
|
-
});
|
|
136
|
-
},
|
|
92
|
+
### Serial RTU Master
|
|
137
93
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}
|
|
144
|
-
return holdingRegister;
|
|
145
|
-
});
|
|
94
|
+
```typescript
|
|
95
|
+
const master = new ModbusMaster({
|
|
96
|
+
physical: {
|
|
97
|
+
type: 'SERIAL',
|
|
98
|
+
opts: { path: '/dev/ttyUSB0', baudRate: 9600 },
|
|
146
99
|
},
|
|
147
|
-
|
|
148
|
-
|
|
100
|
+
protocol: { type: 'RTU' },
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
await master.open();
|
|
104
|
+
const res = await master.readHoldingRegisters(1, 0, 10);
|
|
105
|
+
await master.close();
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### RTU over TCP
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
const master = new ModbusMaster({
|
|
112
|
+
physical: { type: 'TCP_CLIENT' },
|
|
113
|
+
protocol: { type: 'RTU' },
|
|
114
|
+
});
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Physical Layers
|
|
118
|
+
|
|
119
|
+
All physical layers expose `open()` / `close()`, a `state` property, and events: `'open'`, `'connect'`, `'close'`, `'error'`.
|
|
120
|
+
|
|
121
|
+
| Type | Class | `open(...)` args |
|
|
122
|
+
|------|-------|-----------------|
|
|
123
|
+
| `SERIAL` | `SerialPhysicalLayer` | none |
|
|
124
|
+
| `TCP_CLIENT` | `TcpClientPhysicalLayer` | `SocketConnectOpts` |
|
|
125
|
+
| `TCP_SERVER` | `TcpServerPhysicalLayer` | `ListenOptions` |
|
|
126
|
+
| `UDP_CLIENT` | `UdpClientPhysicalLayer` | `{ port, address }` |
|
|
127
|
+
| `UDP_SERVER` | `UdpServerPhysicalLayer` | `BindOptions` |
|
|
128
|
+
|
|
129
|
+
### Server options
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
const slave = new ModbusSlave({
|
|
133
|
+
physical: {
|
|
134
|
+
type: 'TCP_SERVER',
|
|
135
|
+
opts: {
|
|
136
|
+
whitelist: ['192.168.1.10', '10.0.0.5'], // IPv4-mapped IPv6 normalized automatically
|
|
137
|
+
maxConnections: 10,
|
|
138
|
+
idleTimeout: 30000, // evict inactive connections, 0 = disable
|
|
139
|
+
},
|
|
149
140
|
},
|
|
141
|
+
protocol: { type: 'TCP' },
|
|
142
|
+
});
|
|
143
|
+
```
|
|
150
144
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
0x05: 'Regular:ModelName',
|
|
160
|
-
0x06: 'Regular:UserApplicationName',
|
|
161
|
-
0x80: 'Extended:Extended',
|
|
162
|
-
0xff: 'Extended:Extended',
|
|
163
|
-
}),
|
|
164
|
-
};
|
|
145
|
+
### Physical layer options
|
|
146
|
+
|
|
147
|
+
| Option | Type | Description |
|
|
148
|
+
|--------|------|-------------|
|
|
149
|
+
| `whitelist` | `string[]` | Allowed client IPs. `::ffff:` prefix is stripped automatically. |
|
|
150
|
+
| `maxConnections` | `number` | Max concurrent connections. New connections are silently dropped when exceeded. |
|
|
151
|
+
| `idleTimeout` | `number` | Idle timeout in ms before evicting a connection. Default `30000`. Pass `0` to disable. |
|
|
152
|
+
| `socketOpts` / `serverOpts` | `object` | Forwarded to Node.js `createSocket()` / `createServer()`. |
|
|
165
153
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
console.log(error);
|
|
176
|
-
});
|
|
154
|
+
## Master API
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
new ModbusMaster({
|
|
158
|
+
physical: { type: 'TCP_CLIENT' },
|
|
159
|
+
protocol: { type: 'TCP' }, // 'TCP' | 'RTU' | 'ASCII'
|
|
160
|
+
timeout?: 1000, // per-request timeout (ms)
|
|
161
|
+
concurrent?: false, // pipelined mode (TCP only)
|
|
162
|
+
})
|
|
177
163
|
```
|
|
178
164
|
|
|
179
|
-
|
|
165
|
+
RTU / ASCII framing options:
|
|
180
166
|
|
|
181
|
-
|
|
167
|
+
```typescript
|
|
168
|
+
protocol: {
|
|
169
|
+
type: 'RTU',
|
|
170
|
+
opts: {
|
|
171
|
+
intervalBetweenFrames: { unit: 'ms', value: 20 },
|
|
172
|
+
interCharTimeout: { unit: 'bit', value: 10 },
|
|
173
|
+
poolSize: 1024,
|
|
174
|
+
},
|
|
175
|
+
}
|
|
176
|
+
```
|
|
182
177
|
|
|
183
|
-
|
|
178
|
+
### Methods
|
|
179
|
+
|
|
180
|
+
| Method | Description |
|
|
181
|
+
|--------|-------------|
|
|
182
|
+
| `open(...args)` | Open the physical layer. Can only be called once; after `close()` the instance is dead. |
|
|
183
|
+
| `close()` | Close immediately. In-flight and queued requests are rejected. |
|
|
184
|
+
| `readCoils(unit, address, length, timeout?)` | FC 01 |
|
|
185
|
+
| `readDiscreteInputs(unit, address, length, timeout?)` | FC 02 |
|
|
186
|
+
| `readHoldingRegisters(unit, address, length, timeout?)` | FC 03 |
|
|
187
|
+
| `readInputRegisters(unit, address, length, timeout?)` | FC 04 |
|
|
188
|
+
| `writeSingleCoil(unit, address, value, timeout?)` | FC 05 |
|
|
189
|
+
| `writeSingleRegister(unit, address, value, timeout?)` | FC 06 |
|
|
190
|
+
| `writeMultipleCoils(unit, address, values, timeout?)` | FC 15 |
|
|
191
|
+
| `writeMultipleRegisters(unit, address, values, timeout?)` | FC 16 |
|
|
192
|
+
| `reportServerId(unit, serverIdLength?, timeout?)` | FC 17 |
|
|
193
|
+
| `maskWriteRegister(unit, address, andMask, orMask, timeout?)` | FC 22 |
|
|
194
|
+
| `readAndWriteMultipleRegisters(unit, read, write, timeout?)` | FC 23 |
|
|
195
|
+
| `readDeviceIdentification(unit, readDeviceIDCode, objectId, timeout?)` | FC 43/14 |
|
|
196
|
+
| `sendCustomFC(unit, fc, data, timeout?)` | Send a user-defined function code |
|
|
197
|
+
| `addCustomFunctionCode(cfc)` | Register custom FC for framing |
|
|
198
|
+
| `removeCustomFunctionCode(fc)` | Unregister custom FC |
|
|
199
|
+
|
|
200
|
+
Broadcast (`unit = 0`) resolves `void` — slaves never respond.
|
|
201
|
+
|
|
202
|
+
## Slave API
|
|
184
203
|
|
|
185
|
-
|
|
204
|
+
```typescript
|
|
205
|
+
new ModbusSlave({
|
|
206
|
+
physical: { type: 'TCP_SERVER' },
|
|
207
|
+
protocol: { type: 'TCP' },
|
|
208
|
+
concurrent?: false, // per-connection concurrent (TCP only)
|
|
209
|
+
})
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Register a model
|
|
186
213
|
|
|
187
214
|
```typescript
|
|
188
|
-
|
|
215
|
+
slave.add({
|
|
216
|
+
unit: 1,
|
|
217
|
+
|
|
218
|
+
// Optional: intercept any FC before default dispatch
|
|
219
|
+
interceptor?: (fc, data) => Buffer | undefined,
|
|
220
|
+
|
|
221
|
+
readCoils?: (address, length) => boolean[],
|
|
222
|
+
readDiscreteInputs?: (address, length) => boolean[],
|
|
223
|
+
readHoldingRegisters?: (address, length) => number[],
|
|
224
|
+
readInputRegisters?: (address, length) => number[],
|
|
225
|
+
|
|
226
|
+
writeSingleCoil?: (address, value) => void,
|
|
227
|
+
writeMultipleCoils?: (address, values) => void,
|
|
228
|
+
|
|
229
|
+
writeSingleRegister?: (address, value) => void,
|
|
230
|
+
writeMultipleRegisters?: (address, values) => void,
|
|
231
|
+
|
|
232
|
+
maskWriteRegister?: (address, andMask, orMask) => void,
|
|
233
|
+
|
|
234
|
+
reportServerId?: () => ServerId,
|
|
235
|
+
readDeviceIdentification?: () => { [index: number]: string },
|
|
189
236
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
237
|
+
// Optional address range validation
|
|
238
|
+
getAddressRange?: () => ({
|
|
239
|
+
discreteInputs?: [number, number] | [number, number][];
|
|
240
|
+
coils?: [number, number] | [number, number][];
|
|
241
|
+
inputRegisters?: [number, number] | [number, number][];
|
|
242
|
+
holdingRegisters?: [number, number] | [number, number][];
|
|
243
|
+
}),
|
|
244
|
+
});
|
|
193
245
|
```
|
|
194
246
|
|
|
195
|
-
|
|
247
|
+
Missing handlers return `ILLEGAL_FUNCTION`. Out-of-range addresses return `ILLEGAL_DATA_ADDRESS`. Handler throws become `SERVER_DEVICE_FAILURE` unless the error is a `ModbusError`.
|
|
248
|
+
|
|
249
|
+
| Method | Description |
|
|
250
|
+
|--------|-------------|
|
|
251
|
+
| `add(model)` | Register a slave model |
|
|
252
|
+
| `remove(unit)` | Remove a slave model |
|
|
253
|
+
| `open(...args)` | Open the physical layer. One-shot: cannot reopen after `close()`. |
|
|
254
|
+
| `close()` | Close immediately |
|
|
255
|
+
| `addCustomFunctionCode(cfc)` | Register a custom FC |
|
|
256
|
+
| `removeCustomFunctionCode(fc)` | Unregister a custom FC |
|
|
257
|
+
|
|
258
|
+
## Custom Function Codes
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
import type { CustomFunctionCode } from 'njs-modbus';
|
|
262
|
+
|
|
263
|
+
const cfc: CustomFunctionCode = {
|
|
264
|
+
fc: 0x50,
|
|
265
|
+
predictRequestLength: (buffer) => {
|
|
266
|
+
if (buffer.length < 2) return null;
|
|
267
|
+
return 4 + buffer[1];
|
|
268
|
+
},
|
|
269
|
+
predictResponseLength: (buffer) => {
|
|
270
|
+
if (buffer.length < 2) return null;
|
|
271
|
+
return 4 + buffer[1];
|
|
272
|
+
},
|
|
273
|
+
handle: async (data, unit) => {
|
|
274
|
+
return Buffer.from([data[1], data[0]]);
|
|
275
|
+
},
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
// Slave: framing + dispatch
|
|
279
|
+
slave.addCustomFunctionCode(cfc);
|
|
280
|
+
|
|
281
|
+
// Master: framing only
|
|
282
|
+
master.addCustomFunctionCode(cfc);
|
|
283
|
+
const response = await master.sendCustomFC(1, 0x50, [0xab, 0xcd]);
|
|
284
|
+
```
|
|
196
285
|
|
|
197
|
-
##
|
|
286
|
+
## Error Handling
|
|
198
287
|
|
|
199
|
-
|
|
288
|
+
```typescript
|
|
289
|
+
import { ErrorCode, ModbusError, getErrorByCode } from 'njs-modbus';
|
|
290
|
+
|
|
291
|
+
// ErrorCode.ILLEGAL_FUNCTION = 0x01
|
|
292
|
+
// ErrorCode.ILLEGAL_DATA_ADDRESS = 0x02
|
|
293
|
+
// ErrorCode.ILLEGAL_DATA_VALUE = 0x03
|
|
294
|
+
// ErrorCode.SERVER_DEVICE_FAILURE = 0x04
|
|
295
|
+
// ErrorCode.ACKNOWLEDGE = 0x05
|
|
296
|
+
// ErrorCode.SERVER_DEVICE_BUSY = 0x06
|
|
297
|
+
// ErrorCode.MEMORY_PARITY_ERROR = 0x08
|
|
298
|
+
// ErrorCode.GATEWAY_PATH_UNAVAILABLE = 0x0a
|
|
299
|
+
// ErrorCode.GATEWAY_TARGET_DEVICE_FAILED_TO_RESPOND = 0x0b
|
|
300
|
+
|
|
301
|
+
slave.add({
|
|
302
|
+
unit: 1,
|
|
303
|
+
readHoldingRegisters: (address) => {
|
|
304
|
+
if (address > 100) {
|
|
305
|
+
throw getErrorByCode(ErrorCode.ILLEGAL_DATA_ADDRESS);
|
|
306
|
+
}
|
|
307
|
+
return [42];
|
|
308
|
+
},
|
|
309
|
+
});
|
|
310
|
+
```
|
|
200
311
|
|
|
201
312
|
## License
|
|
202
313
|
|
|
203
|
-
[](/LICENSE)
|