njs-modbus 2.1.0 → 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 +264 -154
- package/README.zh-CN.md +314 -0
- package/dist/index.cjs +1887 -1066
- package/dist/index.d.ts +368 -215
- package/dist/index.mjs +1884 -1065
- 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 -17
- package/dist/src/slave/slave.d.ts +57 -33
- package/dist/src/types.d.ts +2 -2
- 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 +8 -8
- 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,8 +1,8 @@
|
|
|
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)
|
|
@@ -11,194 +11,304 @@ A pure JavaScript implementation of MODBUS for NodeJS.
|
|
|
11
11
|
[](https://github.com/xiejay97/njs-modbus/actions)
|
|
12
12
|
<!-- prettier-ignore-end -->
|
|
13
13
|
|
|
14
|
-
</div>
|
|
15
|
-
|
|
16
|
-
## Introduction
|
|
17
|
-
|
|
18
|
-
`njs-modbus` is designed as a layered architecture, including the physical layer and the application layer:
|
|
19
|
-
|
|
20
|
-
- Physical layer implements Serial Port, TCP/IP and UDP/IP.
|
|
21
|
-
- Application layer implements RTU, ASCII and TCP.
|
|
22
|
-
|
|
23
|
-
`njs-modbus` provide both client and server.
|
|
24
|
-
|
|
25
14
|
## Features
|
|
26
15
|
|
|
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
|
-
|
|
52
|
-
- Modbus RTU
|
|
53
|
-
- Modbus ASCII
|
|
54
|
-
- Modbus TCP/IP
|
|
55
|
-
- Modbus UDP/IP
|
|
56
|
-
- Modbus RTU/ASCII Over TCP/IP
|
|
57
|
-
- Modbus RTU/ASCII Over UDP/IP
|
|
58
|
-
|
|
59
|
-
#### 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
|
|
60
41
|
|
|
61
42
|
```bash
|
|
62
43
|
npm install njs-modbus
|
|
63
44
|
```
|
|
64
45
|
|
|
65
|
-
|
|
46
|
+
For serial transport, also install the peer dependency:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npm install serialport
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Quick Start
|
|
66
53
|
|
|
67
|
-
###
|
|
54
|
+
### TCP Master
|
|
68
55
|
|
|
69
56
|
```typescript
|
|
70
|
-
import {
|
|
71
|
-
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
});
|
|
84
|
-
})
|
|
85
|
-
.catch((error) => {
|
|
86
|
-
console.log(error);
|
|
87
|
-
});
|
|
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();
|
|
88
70
|
```
|
|
89
71
|
|
|
90
|
-
###
|
|
72
|
+
### TCP Slave
|
|
91
73
|
|
|
92
74
|
```typescript
|
|
93
|
-
import
|
|
94
|
-
import { SerialPhysicalLayer, RtuApplicationLayer, ModbusSlave } from 'njs-modbus';
|
|
75
|
+
import { ModbusSlave } from 'njs-modbus';
|
|
95
76
|
|
|
96
|
-
const
|
|
97
|
-
|
|
77
|
+
const slave = new ModbusSlave({
|
|
78
|
+
physical: { type: 'TCP_SERVER' },
|
|
79
|
+
protocol: { type: 'TCP' },
|
|
80
|
+
});
|
|
98
81
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
holdingRegisters: new Map<number, number>(),
|
|
104
|
-
};
|
|
105
|
-
const slave1: ModbusSlaveModel = {
|
|
106
|
-
readDiscreteInputs: (address, length) => {
|
|
107
|
-
return Array.from({ length }).map((_, i) => {
|
|
108
|
-
const discreteInput = slave1Data.discreteInputs.get(address + i);
|
|
109
|
-
if (typeof discreteInput === 'undefined') {
|
|
110
|
-
return false;
|
|
111
|
-
}
|
|
112
|
-
return discreteInput;
|
|
113
|
-
});
|
|
82
|
+
slave.add({
|
|
83
|
+
unit: 1,
|
|
84
|
+
readHoldingRegisters: (address, length) => {
|
|
85
|
+
return Array.from({ length }, (_, i) => address + i);
|
|
114
86
|
},
|
|
87
|
+
});
|
|
115
88
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
const coil = slave1Data.coils.get(address + i);
|
|
119
|
-
if (typeof coil === 'undefined') {
|
|
120
|
-
return false;
|
|
121
|
-
}
|
|
122
|
-
return coil;
|
|
123
|
-
});
|
|
124
|
-
},
|
|
125
|
-
writeSingleCoil: (address, value) => {
|
|
126
|
-
slave1Data.coils.set(address, value);
|
|
127
|
-
},
|
|
89
|
+
await slave.open({ port: 502 });
|
|
90
|
+
```
|
|
128
91
|
|
|
129
|
-
|
|
130
|
-
return Array.from({ length }).map((_, i) => {
|
|
131
|
-
const inputRegister = slave1Data.inputRegisters.get(address + i);
|
|
132
|
-
if (typeof inputRegister === 'undefined') {
|
|
133
|
-
return 0;
|
|
134
|
-
}
|
|
135
|
-
return inputRegister;
|
|
136
|
-
});
|
|
137
|
-
},
|
|
92
|
+
### Serial RTU Master
|
|
138
93
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
145
|
-
return holdingRegister;
|
|
146
|
-
});
|
|
94
|
+
```typescript
|
|
95
|
+
const master = new ModbusMaster({
|
|
96
|
+
physical: {
|
|
97
|
+
type: 'SERIAL',
|
|
98
|
+
opts: { path: '/dev/ttyUSB0', baudRate: 9600 },
|
|
147
99
|
},
|
|
148
|
-
|
|
149
|
-
|
|
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
|
+
},
|
|
150
140
|
},
|
|
141
|
+
protocol: { type: 'TCP' },
|
|
142
|
+
});
|
|
143
|
+
```
|
|
151
144
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
0x05: 'Regular:ModelName',
|
|
161
|
-
0x06: 'Regular:UserApplicationName',
|
|
162
|
-
0x80: 'Extended:Extended',
|
|
163
|
-
0xff: 'Extended:Extended',
|
|
164
|
-
}),
|
|
165
|
-
};
|
|
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()`. |
|
|
166
153
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
console.log(error);
|
|
177
|
-
});
|
|
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
|
+
})
|
|
178
163
|
```
|
|
179
164
|
|
|
180
|
-
|
|
165
|
+
RTU / ASCII framing options:
|
|
181
166
|
|
|
182
|
-
|
|
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
|
+
```
|
|
183
177
|
|
|
184
|
-
|
|
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
|
|
185
203
|
|
|
186
|
-
|
|
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
|
|
187
213
|
|
|
188
214
|
```typescript
|
|
189
|
-
|
|
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 },
|
|
190
236
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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
|
+
});
|
|
194
245
|
```
|
|
195
246
|
|
|
196
|
-
|
|
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
|
+
```
|
|
197
285
|
|
|
198
|
-
##
|
|
286
|
+
## Error Handling
|
|
199
287
|
|
|
200
|
-
|
|
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
|
+
```
|
|
201
311
|
|
|
202
312
|
## License
|
|
203
313
|
|
|
204
|
-
[](/LICENSE)
|