njs-modbus 1.3.2 → 1.3.4

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/dist/index.mjs ADDED
@@ -0,0 +1,2106 @@
1
+ import EventEmitter from 'node:events';
2
+ import { SerialPort } from 'serialport';
3
+ import { Socket, createServer } from 'node:net';
4
+ import { createSocket } from 'node:dgram';
5
+
6
+ var ErrorCode;
7
+ (function (ErrorCode) {
8
+ ErrorCode[ErrorCode["ILLEGAL_FUNCTION"] = 1] = "ILLEGAL_FUNCTION";
9
+ ErrorCode[ErrorCode["ILLEGAL_DATA_ADDRESS"] = 2] = "ILLEGAL_DATA_ADDRESS";
10
+ ErrorCode[ErrorCode["ILLEGAL_DATA_VALUE"] = 3] = "ILLEGAL_DATA_VALUE";
11
+ ErrorCode[ErrorCode["SERVER_DEVICE_FAILURE"] = 4] = "SERVER_DEVICE_FAILURE";
12
+ ErrorCode[ErrorCode["ACKNOWLEDGE"] = 5] = "ACKNOWLEDGE";
13
+ ErrorCode[ErrorCode["SERVER_DEVICE_BUSY"] = 6] = "SERVER_DEVICE_BUSY";
14
+ ErrorCode[ErrorCode["MEMORY_PARITY_ERROR"] = 8] = "MEMORY_PARITY_ERROR";
15
+ ErrorCode[ErrorCode["GATEWAY_PATH_UNAVAILABLE"] = 10] = "GATEWAY_PATH_UNAVAILABLE";
16
+ ErrorCode[ErrorCode["GATEWAY_TARGET_DEVICE_FAILED_TO_RESPOND"] = 11] = "GATEWAY_TARGET_DEVICE_FAILED_TO_RESPOND";
17
+ })(ErrorCode || (ErrorCode = {}));
18
+ const PREFIX = 'MODBUS_ERROR_CODE_';
19
+ function getErrorByCode(code) {
20
+ return new Error(PREFIX + code);
21
+ }
22
+ function getCodeByError(err) {
23
+ if (err.message.startsWith(PREFIX)) {
24
+ return Number(err.message.slice(PREFIX.length));
25
+ }
26
+ return ErrorCode.SERVER_DEVICE_FAILURE;
27
+ }
28
+
29
+ class AbstractPhysicalLayer extends EventEmitter {
30
+ }
31
+
32
+ class SerialPhysicalLayer extends AbstractPhysicalLayer {
33
+ get isOpen() {
34
+ return this._serialport.isOpen;
35
+ }
36
+ get destroyed() {
37
+ return this._destroyed;
38
+ }
39
+ get baudRate() {
40
+ return this._baudRate;
41
+ }
42
+ constructor(options) {
43
+ super();
44
+ Object.defineProperty(this, "TYPE", {
45
+ enumerable: true,
46
+ configurable: true,
47
+ writable: true,
48
+ value: 'SERIAL'
49
+ });
50
+ Object.defineProperty(this, "_serialport", {
51
+ enumerable: true,
52
+ configurable: true,
53
+ writable: true,
54
+ value: void 0
55
+ });
56
+ Object.defineProperty(this, "_destroyed", {
57
+ enumerable: true,
58
+ configurable: true,
59
+ writable: true,
60
+ value: false
61
+ });
62
+ Object.defineProperty(this, "_baudRate", {
63
+ enumerable: true,
64
+ configurable: true,
65
+ writable: true,
66
+ value: void 0
67
+ });
68
+ this._serialport = new SerialPort(Object.assign(Object.assign({}, options), { autoOpen: false }));
69
+ this._baudRate = options.baudRate;
70
+ }
71
+ open() {
72
+ if (this.destroyed) {
73
+ return Promise.reject(new Error('Port is destroyed'));
74
+ }
75
+ return new Promise((resolve, reject) => {
76
+ this._serialport.open((error) => {
77
+ if (error) {
78
+ reject(error);
79
+ }
80
+ else {
81
+ this._serialport.on('data', (data) => {
82
+ this.emit('data', data, (data) => this.write(data));
83
+ });
84
+ this._serialport.on('error', (error) => {
85
+ this.emit('error', error);
86
+ });
87
+ this._serialport.on('close', () => {
88
+ this._serialport.removeAllListeners();
89
+ this.emit('close');
90
+ });
91
+ resolve();
92
+ }
93
+ });
94
+ });
95
+ }
96
+ write(data) {
97
+ return new Promise((resolve, reject) => {
98
+ if (this.isOpen) {
99
+ this._serialport.write(data, (error) => {
100
+ if (error) {
101
+ reject(error);
102
+ }
103
+ else {
104
+ this.emit('write', data);
105
+ resolve();
106
+ }
107
+ });
108
+ }
109
+ else {
110
+ reject(new Error('Port is not open'));
111
+ }
112
+ });
113
+ }
114
+ close() {
115
+ return new Promise((resolve) => {
116
+ this._serialport.removeAllListeners();
117
+ this._serialport.close(() => {
118
+ resolve();
119
+ });
120
+ });
121
+ }
122
+ destroy() {
123
+ this._destroyed = true;
124
+ this.removeAllListeners();
125
+ return this.close();
126
+ }
127
+ }
128
+
129
+ class TcpClientPhysicalLayer extends AbstractPhysicalLayer {
130
+ get isOpen() {
131
+ return this._isOpen;
132
+ }
133
+ get destroyed() {
134
+ return this._destroyed;
135
+ }
136
+ constructor(options) {
137
+ super();
138
+ Object.defineProperty(this, "TYPE", {
139
+ enumerable: true,
140
+ configurable: true,
141
+ writable: true,
142
+ value: 'NET'
143
+ });
144
+ Object.defineProperty(this, "_socket", {
145
+ enumerable: true,
146
+ configurable: true,
147
+ writable: true,
148
+ value: void 0
149
+ });
150
+ Object.defineProperty(this, "_isOpen", {
151
+ enumerable: true,
152
+ configurable: true,
153
+ writable: true,
154
+ value: false
155
+ });
156
+ Object.defineProperty(this, "_destroyed", {
157
+ enumerable: true,
158
+ configurable: true,
159
+ writable: true,
160
+ value: false
161
+ });
162
+ this._socket = new Socket(options);
163
+ }
164
+ open(options) {
165
+ if (this.destroyed) {
166
+ return Promise.reject(new Error('Port is destroyed'));
167
+ }
168
+ return new Promise((resolve, reject) => {
169
+ let called = false;
170
+ this._socket.connect(options !== null && options !== void 0 ? options : { port: 502 }, () => {
171
+ called = true;
172
+ this._isOpen = true;
173
+ this._socket.on('data', (data) => {
174
+ this.emit('data', data, (data) => this.write(data));
175
+ });
176
+ this._socket.on('close', () => {
177
+ this._isOpen = false;
178
+ this._socket.removeAllListeners();
179
+ this.emit('close');
180
+ });
181
+ resolve();
182
+ });
183
+ this._socket.on('error', (error) => {
184
+ if (called) {
185
+ this.emit('error', error);
186
+ }
187
+ else {
188
+ reject(error);
189
+ }
190
+ });
191
+ });
192
+ }
193
+ write(data) {
194
+ return new Promise((resolve, reject) => {
195
+ if (this.isOpen) {
196
+ this._socket.write(data, (error) => {
197
+ if (error) {
198
+ reject(error);
199
+ }
200
+ else {
201
+ this.emit('write', data);
202
+ resolve();
203
+ }
204
+ });
205
+ }
206
+ else {
207
+ reject(new Error('Port is not open'));
208
+ }
209
+ });
210
+ }
211
+ close() {
212
+ return new Promise((resolve) => {
213
+ this._isOpen = false;
214
+ this._socket.removeAllListeners();
215
+ this._socket.destroy();
216
+ resolve();
217
+ });
218
+ }
219
+ destroy() {
220
+ this._destroyed = true;
221
+ this.removeAllListeners();
222
+ return this.close();
223
+ }
224
+ }
225
+
226
+ class TcpServerPhysicalLayer extends AbstractPhysicalLayer {
227
+ get isOpen() {
228
+ return this._isOpen;
229
+ }
230
+ get destroyed() {
231
+ return this._destroyed;
232
+ }
233
+ constructor(options) {
234
+ super();
235
+ Object.defineProperty(this, "TYPE", {
236
+ enumerable: true,
237
+ configurable: true,
238
+ writable: true,
239
+ value: 'NET'
240
+ });
241
+ Object.defineProperty(this, "_server", {
242
+ enumerable: true,
243
+ configurable: true,
244
+ writable: true,
245
+ value: void 0
246
+ });
247
+ Object.defineProperty(this, "_isOpen", {
248
+ enumerable: true,
249
+ configurable: true,
250
+ writable: true,
251
+ value: false
252
+ });
253
+ Object.defineProperty(this, "_destroyed", {
254
+ enumerable: true,
255
+ configurable: true,
256
+ writable: true,
257
+ value: false
258
+ });
259
+ Object.defineProperty(this, "_sockets", {
260
+ enumerable: true,
261
+ configurable: true,
262
+ writable: true,
263
+ value: new Set()
264
+ });
265
+ this._server = createServer(options, (socket) => {
266
+ this._sockets.add(socket);
267
+ socket.on('data', (data) => {
268
+ this.emit('data', data, (data) => new Promise((resolve, reject) => {
269
+ socket.write(data, (error) => {
270
+ if (error) {
271
+ reject(error);
272
+ }
273
+ else {
274
+ resolve();
275
+ }
276
+ });
277
+ }));
278
+ });
279
+ socket.once('close', () => {
280
+ socket.removeAllListeners();
281
+ this._sockets.delete(socket);
282
+ });
283
+ });
284
+ }
285
+ open(options) {
286
+ if (this.destroyed) {
287
+ return Promise.reject(new Error('Port is destroyed'));
288
+ }
289
+ return new Promise((resolve, reject) => {
290
+ var _a;
291
+ let called = false;
292
+ this._server.listen(Object.assign(Object.assign({}, options), { port: (_a = options === null || options === void 0 ? void 0 : options.port) !== null && _a !== void 0 ? _a : 502 }), () => {
293
+ called = true;
294
+ this._isOpen = true;
295
+ this._sockets.clear();
296
+ this._server.on('close', () => {
297
+ this._isOpen = false;
298
+ this._server.removeAllListeners();
299
+ for (const socket of this._sockets) {
300
+ socket.removeAllListeners();
301
+ }
302
+ this.emit('close');
303
+ });
304
+ resolve();
305
+ });
306
+ this._server.on('error', (error) => {
307
+ if (called) {
308
+ this.emit('error', error);
309
+ }
310
+ else {
311
+ reject(error);
312
+ }
313
+ });
314
+ });
315
+ }
316
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
317
+ write(data) {
318
+ return new Promise((resolve, reject) => {
319
+ reject(new Error('Not supported'));
320
+ });
321
+ }
322
+ close() {
323
+ return new Promise((resolve) => {
324
+ this._isOpen = false;
325
+ this._server.removeAllListeners();
326
+ for (const socket of this._sockets) {
327
+ socket.removeAllListeners();
328
+ }
329
+ this._server.close(() => {
330
+ resolve();
331
+ });
332
+ });
333
+ }
334
+ destroy() {
335
+ this._destroyed = true;
336
+ this.removeAllListeners();
337
+ return this.close();
338
+ }
339
+ }
340
+
341
+ class UdpPhysicalLayer extends AbstractPhysicalLayer {
342
+ get isOpen() {
343
+ return this._isOpen;
344
+ }
345
+ get destroyed() {
346
+ return this._destroyed;
347
+ }
348
+ /**
349
+ *
350
+ * @param options
351
+ * @param remote If omitted, as server.
352
+ * Otherwise as client.
353
+ */
354
+ constructor(options, remote) {
355
+ var _a, _b;
356
+ super();
357
+ Object.defineProperty(this, "TYPE", {
358
+ enumerable: true,
359
+ configurable: true,
360
+ writable: true,
361
+ value: 'NET'
362
+ });
363
+ Object.defineProperty(this, "_socket", {
364
+ enumerable: true,
365
+ configurable: true,
366
+ writable: true,
367
+ value: void 0
368
+ });
369
+ Object.defineProperty(this, "_isOpen", {
370
+ enumerable: true,
371
+ configurable: true,
372
+ writable: true,
373
+ value: false
374
+ });
375
+ Object.defineProperty(this, "_destroyed", {
376
+ enumerable: true,
377
+ configurable: true,
378
+ writable: true,
379
+ value: false
380
+ });
381
+ Object.defineProperty(this, "_port", {
382
+ enumerable: true,
383
+ configurable: true,
384
+ writable: true,
385
+ value: void 0
386
+ });
387
+ Object.defineProperty(this, "_address", {
388
+ enumerable: true,
389
+ configurable: true,
390
+ writable: true,
391
+ value: void 0
392
+ });
393
+ Object.defineProperty(this, "isServer", {
394
+ enumerable: true,
395
+ configurable: true,
396
+ writable: true,
397
+ value: void 0
398
+ });
399
+ this._socket = createSocket(Object.assign(Object.assign({}, options), { type: (_a = options === null || options === void 0 ? void 0 : options.type) !== null && _a !== void 0 ? _a : 'udp4' }), (msg, rinfo) => {
400
+ this.emit('data', msg, (data) => new Promise((resolve, reject) => {
401
+ this._socket.send(data, rinfo.port, rinfo.address, (error) => {
402
+ if (error) {
403
+ reject(error);
404
+ }
405
+ else {
406
+ resolve();
407
+ }
408
+ });
409
+ }));
410
+ });
411
+ this.isServer = !remote;
412
+ this._port = (_b = remote === null || remote === void 0 ? void 0 : remote.port) !== null && _b !== void 0 ? _b : 502;
413
+ this._address = remote === null || remote === void 0 ? void 0 : remote.address;
414
+ }
415
+ open(options) {
416
+ if (this.destroyed) {
417
+ return Promise.reject(new Error('Port is destroyed'));
418
+ }
419
+ return new Promise((resolve, reject) => {
420
+ var _a;
421
+ if (this.isServer) {
422
+ let called = false;
423
+ this._socket.bind(Object.assign(Object.assign({}, options), { port: (_a = options === null || options === void 0 ? void 0 : options.port) !== null && _a !== void 0 ? _a : 502 }), () => {
424
+ called = true;
425
+ this._isOpen = true;
426
+ this._socket.on('close', () => {
427
+ this._isOpen = false;
428
+ this._socket.removeAllListeners();
429
+ this.emit('close');
430
+ });
431
+ resolve();
432
+ });
433
+ this._socket.on('error', (error) => {
434
+ if (called) {
435
+ this.emit('error', error);
436
+ }
437
+ else {
438
+ reject(error);
439
+ }
440
+ });
441
+ }
442
+ else {
443
+ this._isOpen = true;
444
+ resolve();
445
+ }
446
+ });
447
+ }
448
+ write(data) {
449
+ return new Promise((resolve, reject) => {
450
+ if (this.isOpen) {
451
+ this._socket.send(data, this._port, this._address, (error) => {
452
+ if (error) {
453
+ reject(error);
454
+ }
455
+ else {
456
+ this.emit('write', data);
457
+ resolve();
458
+ }
459
+ });
460
+ }
461
+ else {
462
+ reject(new Error('Port is not open'));
463
+ }
464
+ });
465
+ }
466
+ close() {
467
+ return new Promise((resolve) => {
468
+ this._isOpen = false;
469
+ this._socket.removeAllListeners();
470
+ this._socket.close(() => {
471
+ resolve();
472
+ });
473
+ });
474
+ }
475
+ destroy() {
476
+ this._destroyed = true;
477
+ this.removeAllListeners();
478
+ return this.close();
479
+ }
480
+ }
481
+
482
+ class AbstractApplicationLayer extends EventEmitter {
483
+ }
484
+
485
+ function checkRange(value, range) {
486
+ if (range) {
487
+ if (typeof range[0] === 'number' && typeof range[1] === 'number') {
488
+ if (range[0] < range[1]) {
489
+ return (Array.isArray(value) ? value : [value]).every((n) => n >= range[0] && n <= range[1]);
490
+ }
491
+ }
492
+ else if (range.length > 0) {
493
+ for (const r of range) {
494
+ if (r[0] < r[1]) {
495
+ if ((Array.isArray(value) ? value : [value]).every((n) => n >= r[0] && n <= r[1])) {
496
+ return true;
497
+ }
498
+ }
499
+ }
500
+ return false;
501
+ }
502
+ }
503
+ return true;
504
+ }
505
+
506
+ const TABLE = [
507
+ 0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241, 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440, 0xcc01,
508
+ 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40, 0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841, 0xd801, 0x18c0,
509
+ 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40, 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41, 0x1400, 0xd4c1, 0xd581,
510
+ 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641, 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040, 0xf001, 0x30c0, 0x3180, 0xf141,
511
+ 0x3300, 0xf3c1, 0xf281, 0x3240, 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441, 0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01,
512
+ 0x3fc0, 0x3e80, 0xfe41, 0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840, 0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0,
513
+ 0x2a80, 0xea41, 0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40, 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681,
514
+ 0x2640, 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041, 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240,
515
+ 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441, 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41, 0xaa01,
516
+ 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840, 0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41, 0xbe01, 0x7ec0,
517
+ 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40, 0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640, 0x7200, 0xb2c1, 0xb381,
518
+ 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041, 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241, 0x9601, 0x56c0, 0x5780, 0x9741,
519
+ 0x5500, 0x95c1, 0x9481, 0x5440, 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40, 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901,
520
+ 0x59c0, 0x5880, 0x9841, 0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40, 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0,
521
+ 0x4c80, 0x8c41, 0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641, 0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081,
522
+ 0x4040,
523
+ ];
524
+ function crc(data) {
525
+ let crc = 0xffff;
526
+ for (let index = 0; index < data.length; index++) {
527
+ crc = (TABLE[(crc ^ data[index]) & 0xff] ^ (crc >> 8)) & 0xffff;
528
+ }
529
+ return crc;
530
+ }
531
+
532
+ /**
533
+ * Get time interval between message frames witch well-known as 3.5T.
534
+ * @param baudRate Serial port baud rate.
535
+ * @param {number} [approximation=48] Approximate number of bits corresponding to 3.5T.
536
+ * @returns `ms`.
537
+ */
538
+ function getThreePointFiveT(baudRate, approximation = 48) {
539
+ return (approximation * 1000) / baudRate;
540
+ }
541
+
542
+ function lrc(data) {
543
+ return (~data.reduce((sum, n) => sum + n, 0) + 1) & 0xff;
544
+ }
545
+
546
+ class RtuApplicationLayer extends AbstractApplicationLayer {
547
+ constructor(physicalLayer,
548
+ /**
549
+ * The time interval between two frames, support two formats:
550
+ * - bit: `48bit` as default
551
+ * - millisecond: `20ms`
552
+ */
553
+ intervalBetweenFrames) {
554
+ super();
555
+ Object.defineProperty(this, "_waitingResponse", {
556
+ enumerable: true,
557
+ configurable: true,
558
+ writable: true,
559
+ value: void 0
560
+ });
561
+ Object.defineProperty(this, "_timerThreePointFive", {
562
+ enumerable: true,
563
+ configurable: true,
564
+ writable: true,
565
+ value: void 0
566
+ });
567
+ Object.defineProperty(this, "_bufferRx", {
568
+ enumerable: true,
569
+ configurable: true,
570
+ writable: true,
571
+ value: Buffer.alloc(0)
572
+ });
573
+ Object.defineProperty(this, "_removeAllListeners", {
574
+ enumerable: true,
575
+ configurable: true,
576
+ writable: true,
577
+ value: []
578
+ });
579
+ let threePointFiveT = 0;
580
+ if (physicalLayer.TYPE === 'SERIAL') {
581
+ if (intervalBetweenFrames && intervalBetweenFrames.endsWith('ms')) {
582
+ threePointFiveT = Number(intervalBetweenFrames.slice(0, -2));
583
+ }
584
+ else {
585
+ threePointFiveT = Math.ceil(physicalLayer.baudRate > 19200
586
+ ? 1.8
587
+ : getThreePointFiveT(physicalLayer.baudRate, intervalBetweenFrames ? Number(intervalBetweenFrames.slice(0, -3)) : 48));
588
+ }
589
+ }
590
+ const handleData = (data, response) => {
591
+ this._bufferRx = Buffer.concat([this._bufferRx, data]);
592
+ clearTimeout(this._timerThreePointFive);
593
+ const handleData = () => {
594
+ this.framing(this._bufferRx, (error, frame) => {
595
+ if (this._waitingResponse) {
596
+ if (error && error.message === 'Insufficient data length') {
597
+ return;
598
+ }
599
+ this._waitingResponse.callback(error, frame);
600
+ this._bufferRx = Buffer.alloc(0);
601
+ }
602
+ else {
603
+ if (!error) {
604
+ this.emit('framing', frame, response);
605
+ }
606
+ this._bufferRx = Buffer.alloc(0);
607
+ }
608
+ });
609
+ };
610
+ if (threePointFiveT) {
611
+ this._timerThreePointFive = setTimeout(handleData, threePointFiveT);
612
+ }
613
+ else {
614
+ handleData();
615
+ }
616
+ };
617
+ physicalLayer.on('data', handleData);
618
+ this._removeAllListeners.push(() => {
619
+ physicalLayer.removeListener('data', handleData);
620
+ });
621
+ const handleClose = () => {
622
+ clearTimeout(this._timerThreePointFive);
623
+ this._bufferRx = Buffer.alloc(0);
624
+ };
625
+ physicalLayer.on('close', handleClose);
626
+ this._removeAllListeners.push(() => {
627
+ physicalLayer.removeListener('close', handleClose);
628
+ });
629
+ }
630
+ framing(buffer, callback) {
631
+ if (buffer.length >= 4) {
632
+ const frame = {
633
+ unit: buffer[0],
634
+ fc: buffer[1],
635
+ data: Array.from(buffer.subarray(2, buffer.length - 2)),
636
+ buffer,
637
+ };
638
+ if (this._waitingResponse) {
639
+ for (const check of this._waitingResponse.preCheck) {
640
+ const res = check(frame);
641
+ if (typeof res === 'undefined') {
642
+ callback(new Error('Insufficient data length'));
643
+ return;
644
+ }
645
+ if (typeof res === 'number') {
646
+ if (frame.data.length < res) {
647
+ callback(new Error('Insufficient data length'));
648
+ return;
649
+ }
650
+ if (frame.data.length !== res) {
651
+ callback(new Error('Invalid response'));
652
+ return;
653
+ }
654
+ }
655
+ if (!res) {
656
+ callback(new Error('Invalid response'));
657
+ return;
658
+ }
659
+ }
660
+ }
661
+ const crcPassed = buffer.readUInt16LE(buffer.length - 2) === crc(buffer.subarray(0, buffer.length - 2));
662
+ if (crcPassed) {
663
+ callback(null, frame);
664
+ }
665
+ else {
666
+ callback(new Error('CRC check failed'));
667
+ }
668
+ }
669
+ else {
670
+ callback(new Error('Insufficient data length'));
671
+ }
672
+ }
673
+ startWaitingResponse(preCheck, callback) {
674
+ this._waitingResponse = { preCheck, callback };
675
+ clearTimeout(this._timerThreePointFive);
676
+ this._bufferRx = Buffer.alloc(0);
677
+ }
678
+ stopWaitingResponse() {
679
+ this._waitingResponse = undefined;
680
+ }
681
+ encode(data) {
682
+ const buffer = Buffer.alloc(data.data.length + 4);
683
+ buffer.writeUInt8(data.unit, 0);
684
+ buffer.writeUInt8(data.fc, 1);
685
+ data.data.forEach((num, index) => {
686
+ buffer.writeUInt8(num, 2 + index);
687
+ });
688
+ buffer.writeUInt16LE(crc(buffer.subarray(0, -2)), buffer.length - 2);
689
+ return buffer;
690
+ }
691
+ destroy() {
692
+ this.removeAllListeners();
693
+ for (const removeAllListener of this._removeAllListeners) {
694
+ removeAllListener();
695
+ }
696
+ clearTimeout(this._timerThreePointFive);
697
+ }
698
+ }
699
+
700
+ const CHAR_CODE = {
701
+ COLON: ':'.charCodeAt(0),
702
+ CR: '\r'.charCodeAt(0),
703
+ LF: '\n'.charCodeAt(0),
704
+ };
705
+ class AsciiApplicationLayer extends AbstractApplicationLayer {
706
+ constructor(physicalLayer) {
707
+ super();
708
+ Object.defineProperty(this, "_waitingResponse", {
709
+ enumerable: true,
710
+ configurable: true,
711
+ writable: true,
712
+ value: void 0
713
+ });
714
+ Object.defineProperty(this, "_status", {
715
+ enumerable: true,
716
+ configurable: true,
717
+ writable: true,
718
+ value: 'idle'
719
+ });
720
+ Object.defineProperty(this, "_frame", {
721
+ enumerable: true,
722
+ configurable: true,
723
+ writable: true,
724
+ value: []
725
+ });
726
+ Object.defineProperty(this, "_removeAllListeners", {
727
+ enumerable: true,
728
+ configurable: true,
729
+ writable: true,
730
+ value: []
731
+ });
732
+ const handleData = (data, response) => {
733
+ data.forEach((value) => {
734
+ switch (this._status) {
735
+ case 'idle': {
736
+ if (value === CHAR_CODE.COLON) {
737
+ this._status = 'reception';
738
+ this._frame = [];
739
+ }
740
+ break;
741
+ }
742
+ case 'reception': {
743
+ if (value === CHAR_CODE.COLON) {
744
+ this._frame = [];
745
+ }
746
+ else if (value === CHAR_CODE.CR) {
747
+ this._status = 'waiting end';
748
+ }
749
+ else {
750
+ this._frame.push(value);
751
+ }
752
+ break;
753
+ }
754
+ case 'waiting end': {
755
+ if (value === CHAR_CODE.COLON) {
756
+ this._status = 'reception';
757
+ this._frame = [];
758
+ }
759
+ else {
760
+ this._status = 'idle';
761
+ if (value === CHAR_CODE.LF) {
762
+ this.framing(Buffer.from(this._frame), (error, frame) => {
763
+ if (this._waitingResponse) {
764
+ this._waitingResponse.callback(error, frame);
765
+ }
766
+ else if (!error) {
767
+ this.emit('framing', frame, response);
768
+ }
769
+ });
770
+ }
771
+ }
772
+ break;
773
+ }
774
+ }
775
+ });
776
+ };
777
+ physicalLayer.on('data', handleData);
778
+ this._removeAllListeners.push(() => {
779
+ physicalLayer.removeListener('data', handleData);
780
+ });
781
+ const handleClose = () => {
782
+ this._status = 'idle';
783
+ this._frame = [];
784
+ };
785
+ physicalLayer.on('close', handleClose);
786
+ this._removeAllListeners.push(() => {
787
+ physicalLayer.removeListener('close', handleClose);
788
+ });
789
+ }
790
+ framing(_buffer, callback) {
791
+ if (_buffer.length >= 6) {
792
+ if (_buffer.length % 2 === 0) {
793
+ const ascii = [];
794
+ let num = '';
795
+ for (const value of _buffer) {
796
+ num += String.fromCharCode(value);
797
+ if (num.length === 2) {
798
+ ascii.push(Number('0x' + num));
799
+ num = '';
800
+ }
801
+ }
802
+ const buffer = Buffer.from(ascii);
803
+ const frame = {
804
+ unit: buffer[0],
805
+ fc: buffer[1],
806
+ data: Array.from(buffer.subarray(2, buffer.length - 1)),
807
+ buffer: _buffer,
808
+ };
809
+ if (this._waitingResponse) {
810
+ for (const check of this._waitingResponse.preCheck) {
811
+ const res = check(frame);
812
+ if (typeof res === 'undefined') {
813
+ callback(new Error('Insufficient data length'));
814
+ return;
815
+ }
816
+ if (typeof res === 'number') {
817
+ if (frame.data.length < res) {
818
+ callback(new Error('Insufficient data length'));
819
+ return;
820
+ }
821
+ if (frame.data.length !== res) {
822
+ callback(new Error('Invalid response'));
823
+ return;
824
+ }
825
+ }
826
+ if (!res) {
827
+ callback(new Error('Invalid response'));
828
+ return;
829
+ }
830
+ }
831
+ }
832
+ const lrcPassed = buffer[buffer.length - 1] === lrc(buffer.subarray(0, buffer.length - 1));
833
+ if (lrcPassed) {
834
+ callback(null, frame);
835
+ }
836
+ else {
837
+ callback(new Error('LRC check failed'));
838
+ }
839
+ }
840
+ else {
841
+ callback(new Error('Invalid data'));
842
+ }
843
+ }
844
+ else {
845
+ callback(new Error('Insufficient data length'));
846
+ }
847
+ }
848
+ startWaitingResponse(preCheck, callback) {
849
+ this._waitingResponse = { preCheck, callback };
850
+ this._status = 'idle';
851
+ this._frame = [];
852
+ }
853
+ stopWaitingResponse() {
854
+ this._waitingResponse = undefined;
855
+ }
856
+ encode(data) {
857
+ const buffer = Buffer.alloc(data.data.length + 3);
858
+ buffer.writeUInt8(data.unit, 0);
859
+ buffer.writeUInt8(data.fc, 1);
860
+ data.data.forEach((num, index) => {
861
+ buffer.writeUInt8(num, 2 + index);
862
+ });
863
+ buffer.writeUInt8(lrc(buffer.subarray(0, -1)), buffer.length - 1);
864
+ let frame = ':';
865
+ for (const value of buffer) {
866
+ frame += value.toString(16).toUpperCase().padStart(2, '0');
867
+ }
868
+ frame += '\r\n';
869
+ return Buffer.from(frame);
870
+ }
871
+ destroy() {
872
+ this.removeAllListeners();
873
+ for (const removeAllListener of this._removeAllListeners) {
874
+ removeAllListener();
875
+ }
876
+ }
877
+ }
878
+
879
+ class TcpApplicationLayer extends AbstractApplicationLayer {
880
+ constructor(physicalLayer) {
881
+ super();
882
+ Object.defineProperty(this, "_waitingResponse", {
883
+ enumerable: true,
884
+ configurable: true,
885
+ writable: true,
886
+ value: void 0
887
+ });
888
+ Object.defineProperty(this, "_transactionId", {
889
+ enumerable: true,
890
+ configurable: true,
891
+ writable: true,
892
+ value: 1
893
+ });
894
+ Object.defineProperty(this, "_removeAllListeners", {
895
+ enumerable: true,
896
+ configurable: true,
897
+ writable: true,
898
+ value: []
899
+ });
900
+ const handleData = (data, response) => {
901
+ this.framing(data, (error, frame) => {
902
+ if (this._waitingResponse) {
903
+ this._waitingResponse.callback(error, frame);
904
+ }
905
+ else if (!error) {
906
+ this.emit('framing', frame, response);
907
+ }
908
+ });
909
+ };
910
+ physicalLayer.on('data', handleData);
911
+ this._removeAllListeners.push(() => {
912
+ physicalLayer.removeListener('data', handleData);
913
+ });
914
+ }
915
+ framing(buffer, callback) {
916
+ if (buffer.length >= 8) {
917
+ if (buffer[2] === 0 && buffer[3] === 0 && buffer.readUInt16BE(4) === buffer.length - 6) {
918
+ const frame = {
919
+ transaction: buffer.readUInt16BE(0),
920
+ unit: buffer[6],
921
+ fc: buffer[7],
922
+ data: Array.from(buffer.subarray(8)),
923
+ buffer,
924
+ };
925
+ if (this._waitingResponse) {
926
+ for (const check of this._waitingResponse.preCheck) {
927
+ const res = check(frame);
928
+ if (typeof res === 'undefined') {
929
+ callback(new Error('Insufficient data length'));
930
+ return;
931
+ }
932
+ if (typeof res === 'number') {
933
+ if (frame.data.length < res) {
934
+ callback(new Error('Insufficient data length'));
935
+ return;
936
+ }
937
+ if (frame.data.length !== res) {
938
+ callback(new Error('Invalid response'));
939
+ return;
940
+ }
941
+ }
942
+ if (!res) {
943
+ callback(new Error('Invalid response'));
944
+ return;
945
+ }
946
+ }
947
+ }
948
+ callback(null, frame);
949
+ }
950
+ else {
951
+ callback(new Error('Invalid data'));
952
+ }
953
+ }
954
+ else {
955
+ callback(new Error('Insufficient data length'));
956
+ }
957
+ }
958
+ startWaitingResponse(preCheck, callback) {
959
+ this._waitingResponse = { preCheck, callback };
960
+ }
961
+ stopWaitingResponse() {
962
+ this._waitingResponse = undefined;
963
+ }
964
+ encode(data) {
965
+ var _a;
966
+ const buffer = Buffer.alloc(data.data.length + 8);
967
+ buffer.writeUInt16BE((_a = data.transaction) !== null && _a !== void 0 ? _a : this._transactionId, 0);
968
+ buffer.writeUInt16BE(0, 2);
969
+ buffer.writeUInt16BE(data.data.length + 2, 4);
970
+ buffer.writeUInt8(data.unit, 6);
971
+ buffer.writeUInt8(data.fc, 7);
972
+ data.data.forEach((num, index) => {
973
+ buffer.writeUInt8(num, 8 + index);
974
+ });
975
+ this._transactionId = (this._transactionId + 1) % 256 || 1;
976
+ return buffer;
977
+ }
978
+ destroy() {
979
+ this.removeAllListeners();
980
+ for (const removeAllListener of this._removeAllListeners) {
981
+ removeAllListener();
982
+ }
983
+ }
984
+ }
985
+
986
+ class ModbusMaster extends EventEmitter {
987
+ get isOpen() {
988
+ return this.physicalLayer.isOpen;
989
+ }
990
+ get destroyed() {
991
+ return this.physicalLayer.destroyed;
992
+ }
993
+ constructor(applicationLayer, physicalLayer, timeout = 1000) {
994
+ super();
995
+ Object.defineProperty(this, "applicationLayer", {
996
+ enumerable: true,
997
+ configurable: true,
998
+ writable: true,
999
+ value: applicationLayer
1000
+ });
1001
+ Object.defineProperty(this, "physicalLayer", {
1002
+ enumerable: true,
1003
+ configurable: true,
1004
+ writable: true,
1005
+ value: physicalLayer
1006
+ });
1007
+ Object.defineProperty(this, "timeout", {
1008
+ enumerable: true,
1009
+ configurable: true,
1010
+ writable: true,
1011
+ value: timeout
1012
+ });
1013
+ Object.defineProperty(this, "writeFC1", {
1014
+ enumerable: true,
1015
+ configurable: true,
1016
+ writable: true,
1017
+ value: void 0
1018
+ });
1019
+ Object.defineProperty(this, "writeFC2", {
1020
+ enumerable: true,
1021
+ configurable: true,
1022
+ writable: true,
1023
+ value: void 0
1024
+ });
1025
+ Object.defineProperty(this, "writeFC3", {
1026
+ enumerable: true,
1027
+ configurable: true,
1028
+ writable: true,
1029
+ value: void 0
1030
+ });
1031
+ Object.defineProperty(this, "writeFC4", {
1032
+ enumerable: true,
1033
+ configurable: true,
1034
+ writable: true,
1035
+ value: void 0
1036
+ });
1037
+ Object.defineProperty(this, "writeFC5", {
1038
+ enumerable: true,
1039
+ configurable: true,
1040
+ writable: true,
1041
+ value: void 0
1042
+ });
1043
+ Object.defineProperty(this, "writeFC6", {
1044
+ enumerable: true,
1045
+ configurable: true,
1046
+ writable: true,
1047
+ value: void 0
1048
+ });
1049
+ Object.defineProperty(this, "writeFC15", {
1050
+ enumerable: true,
1051
+ configurable: true,
1052
+ writable: true,
1053
+ value: void 0
1054
+ });
1055
+ Object.defineProperty(this, "writeFC16", {
1056
+ enumerable: true,
1057
+ configurable: true,
1058
+ writable: true,
1059
+ value: void 0
1060
+ });
1061
+ Object.defineProperty(this, "handleFC17", {
1062
+ enumerable: true,
1063
+ configurable: true,
1064
+ writable: true,
1065
+ value: void 0
1066
+ });
1067
+ Object.defineProperty(this, "handleFC22", {
1068
+ enumerable: true,
1069
+ configurable: true,
1070
+ writable: true,
1071
+ value: void 0
1072
+ });
1073
+ Object.defineProperty(this, "handleFC23", {
1074
+ enumerable: true,
1075
+ configurable: true,
1076
+ writable: true,
1077
+ value: void 0
1078
+ });
1079
+ Object.defineProperty(this, "handleFC43_14", {
1080
+ enumerable: true,
1081
+ configurable: true,
1082
+ writable: true,
1083
+ value: void 0
1084
+ });
1085
+ this.writeFC1 = this.readCoils;
1086
+ this.writeFC2 = this.readDiscreteInputs;
1087
+ this.writeFC3 = this.readHoldingRegisters;
1088
+ this.writeFC4 = this.readInputRegisters;
1089
+ this.writeFC5 = this.writeSingleCoil;
1090
+ this.writeFC6 = this.writeSingleRegister;
1091
+ this.writeFC15 = this.writeMultipleCoils;
1092
+ this.writeFC16 = this.writeMultipleRegisters;
1093
+ this.handleFC17 = this.reportServerId;
1094
+ this.handleFC22 = this.maskWriteRegister;
1095
+ this.handleFC23 = this.readAndWriteMultipleRegisters;
1096
+ this.handleFC43_14 = this.readDeviceIdentification;
1097
+ physicalLayer.on('error', (error) => {
1098
+ this.emit('error', error);
1099
+ });
1100
+ physicalLayer.on('close', () => {
1101
+ this.emit('close');
1102
+ });
1103
+ }
1104
+ waitResponse(request, response, timeout) {
1105
+ return new Promise((resolve, reject) => {
1106
+ this.physicalLayer
1107
+ .write(request.data)
1108
+ .then(() => {
1109
+ if (request.broadcast) {
1110
+ resolve();
1111
+ }
1112
+ else {
1113
+ const tid = setTimeout(() => {
1114
+ this.applicationLayer.stopWaitingResponse();
1115
+ reject(new Error('Timeout'));
1116
+ }, timeout);
1117
+ this.applicationLayer.startWaitingResponse(response.preCheck, (error, frame) => {
1118
+ clearTimeout(tid);
1119
+ this.applicationLayer.stopWaitingResponse();
1120
+ if (error) {
1121
+ reject(error);
1122
+ }
1123
+ else {
1124
+ resolve(frame);
1125
+ }
1126
+ });
1127
+ }
1128
+ })
1129
+ .catch((error) => {
1130
+ reject(error);
1131
+ });
1132
+ });
1133
+ }
1134
+ writeFC1Or2(unit, fc, address, length, timeout) {
1135
+ const byteCount = Math.ceil(length / 8);
1136
+ const bufferTx = Buffer.alloc(4);
1137
+ bufferTx.writeUInt16BE(address, 0);
1138
+ bufferTx.writeUInt16BE(length, 2);
1139
+ return this.waitResponse({
1140
+ data: this.applicationLayer.encode({
1141
+ unit,
1142
+ fc,
1143
+ data: Array.from(bufferTx),
1144
+ }),
1145
+ broadcast: unit === 0,
1146
+ }, {
1147
+ preCheck: [(frame) => frame.unit === unit && frame.fc === fc, () => 1 + byteCount, (frame) => frame.data[0] === byteCount],
1148
+ }, timeout).then((frame) => {
1149
+ if (frame) {
1150
+ return Object.assign(Object.assign({}, frame), { data: Array.from({ length }).map((_, index) => (frame.data[1 + ~~(index / 8)] & (1 << index % 8)) > 0) });
1151
+ }
1152
+ });
1153
+ }
1154
+ readCoils(unit, address, length, timeout = this.timeout) {
1155
+ return this.writeFC1Or2(unit, 0x01, address, length, timeout);
1156
+ }
1157
+ readDiscreteInputs(unit, address, length, timeout = this.timeout) {
1158
+ return this.writeFC1Or2(unit, 0x02, address, length, timeout);
1159
+ }
1160
+ writeFC3Or4(unit, fc, address, length, timeout) {
1161
+ const byteCount = length * 2;
1162
+ const bufferTx = Buffer.alloc(4);
1163
+ bufferTx.writeUInt16BE(address, 0);
1164
+ bufferTx.writeUInt16BE(length, 2);
1165
+ return this.waitResponse({
1166
+ data: this.applicationLayer.encode({
1167
+ unit,
1168
+ fc,
1169
+ data: Array.from(bufferTx),
1170
+ }),
1171
+ broadcast: unit === 0,
1172
+ }, {
1173
+ preCheck: [(frame) => frame.unit === unit && frame.fc === fc, () => 1 + byteCount, (frame) => frame.data[0] === byteCount],
1174
+ }, timeout).then((frame) => {
1175
+ if (frame) {
1176
+ const bufferRx = Buffer.from(frame.data.slice(1));
1177
+ return Object.assign(Object.assign({}, frame), { data: Array.from({ length }).map((_, index) => bufferRx.readUInt16BE(index * 2)) });
1178
+ }
1179
+ });
1180
+ }
1181
+ readHoldingRegisters(unit, address, length, timeout = this.timeout) {
1182
+ return this.writeFC3Or4(unit, 0x03, address, length, timeout);
1183
+ }
1184
+ readInputRegisters(unit, address, length, timeout = this.timeout) {
1185
+ return this.writeFC3Or4(unit, 0x04, address, length, timeout);
1186
+ }
1187
+ writeSingleCoil(unit, address, value, timeout = this.timeout) {
1188
+ const fc = 0x05;
1189
+ const bufferTx = Buffer.alloc(4);
1190
+ bufferTx.writeUInt16BE(address, 0);
1191
+ bufferTx.writeUInt16BE(value ? 0xff00 : 0x0000, 2);
1192
+ return this.waitResponse({
1193
+ data: this.applicationLayer.encode({
1194
+ unit,
1195
+ fc,
1196
+ data: Array.from(bufferTx),
1197
+ }),
1198
+ broadcast: unit === 0,
1199
+ }, {
1200
+ preCheck: [
1201
+ (frame) => frame.unit === unit && frame.fc === fc,
1202
+ () => bufferTx.length,
1203
+ (frame) => frame.data.every((v, i) => v === bufferTx[i]),
1204
+ ],
1205
+ }, timeout).then((frame) => {
1206
+ if (frame) {
1207
+ return Object.assign(Object.assign({}, frame), { data: value });
1208
+ }
1209
+ });
1210
+ }
1211
+ writeSingleRegister(unit, address, value, timeout = this.timeout) {
1212
+ const fc = 0x06;
1213
+ const bufferTx = Buffer.alloc(4);
1214
+ bufferTx.writeUInt16BE(address, 0);
1215
+ bufferTx.writeUInt16BE(value, 2);
1216
+ return this.waitResponse({
1217
+ data: this.applicationLayer.encode({
1218
+ unit,
1219
+ fc,
1220
+ data: Array.from(bufferTx),
1221
+ }),
1222
+ broadcast: unit === 0,
1223
+ }, {
1224
+ preCheck: [
1225
+ (frame) => frame.unit === unit && frame.fc === fc,
1226
+ () => bufferTx.length,
1227
+ (frame) => frame.data.every((v, i) => v === bufferTx[i]),
1228
+ ],
1229
+ }, timeout).then((frame) => {
1230
+ if (frame) {
1231
+ return Object.assign(Object.assign({}, frame), { data: value });
1232
+ }
1233
+ });
1234
+ }
1235
+ writeMultipleCoils(unit, address, value, timeout = this.timeout) {
1236
+ const fc = 0x0f;
1237
+ const byteCount = Math.ceil(value.length / 8);
1238
+ const bufferTx = Buffer.alloc(5 + byteCount);
1239
+ bufferTx.writeUInt16BE(address, 0);
1240
+ bufferTx.writeUInt16BE(value.length, 2);
1241
+ bufferTx.writeUInt8(byteCount, 4);
1242
+ value.forEach((v, i) => {
1243
+ if (v) {
1244
+ bufferTx[5 + ~~(i / 8)] |= 1 << i % 8;
1245
+ }
1246
+ });
1247
+ return this.waitResponse({
1248
+ data: this.applicationLayer.encode({
1249
+ unit,
1250
+ fc,
1251
+ data: Array.from(bufferTx),
1252
+ }),
1253
+ broadcast: unit === 0,
1254
+ }, {
1255
+ preCheck: [(frame) => frame.unit === unit && frame.fc === fc, () => 4, (frame) => frame.data.every((v, i) => v === bufferTx[i])],
1256
+ }, timeout).then((frame) => {
1257
+ if (frame) {
1258
+ return Object.assign(Object.assign({}, frame), { data: value });
1259
+ }
1260
+ });
1261
+ }
1262
+ writeMultipleRegisters(unit, address, value, timeout = this.timeout) {
1263
+ const fc = 0x10;
1264
+ const byteCount = value.length * 2;
1265
+ const bufferTx = Buffer.alloc(5 + byteCount);
1266
+ bufferTx.writeUInt16BE(address, 0);
1267
+ bufferTx.writeUInt16BE(value.length, 2);
1268
+ bufferTx.writeUInt8(byteCount, 4);
1269
+ value.forEach((v, i) => {
1270
+ bufferTx.writeUInt16BE(v, 5 + i * 2);
1271
+ });
1272
+ return this.waitResponse({
1273
+ data: this.applicationLayer.encode({
1274
+ unit,
1275
+ fc,
1276
+ data: Array.from(bufferTx),
1277
+ }),
1278
+ broadcast: unit === 0,
1279
+ }, {
1280
+ preCheck: [(frame) => frame.unit === unit && frame.fc === fc, () => 4, (frame) => frame.data.every((v, i) => v === bufferTx[i])],
1281
+ }, timeout).then((frame) => {
1282
+ if (frame) {
1283
+ return Object.assign(Object.assign({}, frame), { data: value });
1284
+ }
1285
+ });
1286
+ }
1287
+ reportServerId(unit, timeout = this.timeout) {
1288
+ const fc = 0x11;
1289
+ return this.waitResponse({
1290
+ data: this.applicationLayer.encode({
1291
+ unit,
1292
+ fc,
1293
+ data: [],
1294
+ }),
1295
+ broadcast: unit === 0,
1296
+ }, {
1297
+ preCheck: [
1298
+ (frame) => frame.unit === unit && frame.fc === fc,
1299
+ (frame) => {
1300
+ if (frame.data.length >= 3) {
1301
+ return 1 + frame.data[0];
1302
+ }
1303
+ },
1304
+ ],
1305
+ }, timeout).then((frame) => {
1306
+ if (frame) {
1307
+ return Object.assign(Object.assign({}, frame), { data: {
1308
+ serverId: frame.data[1],
1309
+ runIndicatorStatus: frame.data[2] === 0xff,
1310
+ additionalData: frame.data.slice(3),
1311
+ } });
1312
+ }
1313
+ });
1314
+ }
1315
+ maskWriteRegister(unit, address, andMask, orMask, timeout = this.timeout) {
1316
+ const fc = 0x16;
1317
+ const bufferTx = Buffer.alloc(6);
1318
+ bufferTx.writeUInt16BE(address, 0);
1319
+ bufferTx.writeUInt16BE(andMask, 2);
1320
+ bufferTx.writeUInt16BE(orMask, 4);
1321
+ return this.waitResponse({
1322
+ data: this.applicationLayer.encode({
1323
+ unit,
1324
+ fc,
1325
+ data: Array.from(bufferTx),
1326
+ }),
1327
+ broadcast: unit === 0,
1328
+ }, {
1329
+ preCheck: [(frame) => frame.unit === unit && frame.fc === fc, () => 6, (frame) => frame.data.every((v, i) => v === bufferTx[i])],
1330
+ }, timeout).then((frame) => {
1331
+ if (frame) {
1332
+ return Object.assign(Object.assign({}, frame), { data: { andMask, orMask } });
1333
+ }
1334
+ });
1335
+ }
1336
+ readAndWriteMultipleRegisters(unit, read, write, timeout = this.timeout) {
1337
+ const fc = 0x17;
1338
+ const byteCount = write.value.length * 2;
1339
+ const bufferTx = Buffer.alloc(9 + byteCount);
1340
+ bufferTx.writeUInt16BE(read.address, 0);
1341
+ bufferTx.writeUInt16BE(read.length, 2);
1342
+ bufferTx.writeUInt16BE(write.address, 4);
1343
+ bufferTx.writeUInt16BE(write.value.length, 6);
1344
+ bufferTx.writeUInt8(byteCount, 8);
1345
+ write.value.forEach((v, i) => {
1346
+ bufferTx.writeUInt16BE(v, 9 + i * 2);
1347
+ });
1348
+ return this.waitResponse({
1349
+ data: this.applicationLayer.encode({
1350
+ unit,
1351
+ fc,
1352
+ data: Array.from(bufferTx),
1353
+ }),
1354
+ broadcast: unit === 0,
1355
+ }, {
1356
+ preCheck: [(frame) => frame.unit === unit && frame.fc === fc, () => 1 + byteCount, (frame) => frame.data[0] === byteCount],
1357
+ }, timeout).then((frame) => {
1358
+ if (frame) {
1359
+ const bufferRx = Buffer.from(frame.data.slice(1));
1360
+ return Object.assign(Object.assign({}, frame), { data: Array.from({ length: read.length }).map((_, index) => bufferRx.readUInt16BE(index * 2)) });
1361
+ }
1362
+ });
1363
+ }
1364
+ readDeviceIdentification(unit, readDeviceIDCode, objectId, timeout = this.timeout) {
1365
+ const fc = 0x2b;
1366
+ return this.waitResponse({
1367
+ data: this.applicationLayer.encode({
1368
+ unit,
1369
+ fc,
1370
+ data: [0x0e, readDeviceIDCode, objectId],
1371
+ }),
1372
+ broadcast: unit === 0,
1373
+ }, {
1374
+ preCheck: [
1375
+ (frame) => frame.unit === unit && frame.fc === fc,
1376
+ (frame) => {
1377
+ if (frame.data.length >= 6) {
1378
+ if (frame.data[0] === 0x0e && frame.data[1] === readDeviceIDCode) {
1379
+ const objects = [];
1380
+ let object = [];
1381
+ for (const v of frame.data.slice(6)) {
1382
+ switch (object.length) {
1383
+ case 0:
1384
+ case 1: {
1385
+ object.push(v);
1386
+ break;
1387
+ }
1388
+ case 2: {
1389
+ object.push([v]);
1390
+ break;
1391
+ }
1392
+ case 3: {
1393
+ object[2].push(v);
1394
+ if (object[1] === object[2].length) {
1395
+ objects.push(2 + object[1]);
1396
+ object = [];
1397
+ }
1398
+ break;
1399
+ }
1400
+ }
1401
+ }
1402
+ if (objects.length === frame.data[5]) {
1403
+ return 6 + objects.reduce((previous, current) => previous + current, 0);
1404
+ }
1405
+ }
1406
+ else {
1407
+ return false;
1408
+ }
1409
+ }
1410
+ },
1411
+ ],
1412
+ }, timeout).then((frame) => {
1413
+ if (frame) {
1414
+ const objects = [];
1415
+ let object = [];
1416
+ for (const v of frame.data.slice(6)) {
1417
+ switch (object.length) {
1418
+ case 0:
1419
+ case 1: {
1420
+ object.push(v);
1421
+ break;
1422
+ }
1423
+ case 2: {
1424
+ object.push([v]);
1425
+ break;
1426
+ }
1427
+ case 3: {
1428
+ object[2].push(v);
1429
+ if (object[1] === object[2].length) {
1430
+ objects.push({ id: object[0], value: Buffer.from(object[2]).toString() });
1431
+ object = [];
1432
+ }
1433
+ break;
1434
+ }
1435
+ }
1436
+ }
1437
+ return Object.assign(Object.assign({}, frame), { data: {
1438
+ readDeviceIDCode,
1439
+ conformityLevel: frame.data[2],
1440
+ moreFollows: frame.data[3] === 0xff,
1441
+ nextObjectId: frame.data[4],
1442
+ objects,
1443
+ } });
1444
+ }
1445
+ });
1446
+ }
1447
+ open(...args) {
1448
+ return this.physicalLayer.open(...args);
1449
+ }
1450
+ close() {
1451
+ return this.physicalLayer.close();
1452
+ }
1453
+ destroy() {
1454
+ this.removeAllListeners();
1455
+ this.applicationLayer.destroy();
1456
+ return this.physicalLayer.destroy();
1457
+ }
1458
+ }
1459
+
1460
+ /******************************************************************************
1461
+ Copyright (c) Microsoft Corporation.
1462
+
1463
+ Permission to use, copy, modify, and/or distribute this software for any
1464
+ purpose with or without fee is hereby granted.
1465
+
1466
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
1467
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
1468
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
1469
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
1470
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
1471
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
1472
+ PERFORMANCE OF THIS SOFTWARE.
1473
+ ***************************************************************************** */
1474
+ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */
1475
+
1476
+
1477
+ function __awaiter(thisArg, _arguments, P, generator) {
1478
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
1479
+ return new (P || (P = Promise))(function (resolve, reject) {
1480
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
1481
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
1482
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
1483
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
1484
+ });
1485
+ }
1486
+
1487
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
1488
+ var e = new Error(message);
1489
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
1490
+ };
1491
+
1492
+ class ModbusSlave extends EventEmitter {
1493
+ get isOpen() {
1494
+ return this.physicalLayer.isOpen;
1495
+ }
1496
+ get destroyed() {
1497
+ return this.physicalLayer.destroyed;
1498
+ }
1499
+ constructor(model, applicationLayer, physicalLayer) {
1500
+ super();
1501
+ Object.defineProperty(this, "model", {
1502
+ enumerable: true,
1503
+ configurable: true,
1504
+ writable: true,
1505
+ value: model
1506
+ });
1507
+ Object.defineProperty(this, "applicationLayer", {
1508
+ enumerable: true,
1509
+ configurable: true,
1510
+ writable: true,
1511
+ value: applicationLayer
1512
+ });
1513
+ Object.defineProperty(this, "physicalLayer", {
1514
+ enumerable: true,
1515
+ configurable: true,
1516
+ writable: true,
1517
+ value: physicalLayer
1518
+ });
1519
+ Object.defineProperty(this, "unit", {
1520
+ enumerable: true,
1521
+ configurable: true,
1522
+ writable: true,
1523
+ value: 1
1524
+ });
1525
+ if (typeof model.unit !== 'undefined') {
1526
+ this.unit = model.unit;
1527
+ }
1528
+ applicationLayer.on('framing', (frame, _response) => __awaiter(this, void 0, void 0, function* () {
1529
+ if (!(frame.unit === 0x00 || frame.unit === this.unit)) {
1530
+ return;
1531
+ }
1532
+ const response = (data) => __awaiter(this, void 0, void 0, function* () {
1533
+ if (frame.unit === 0x00) {
1534
+ return;
1535
+ }
1536
+ try {
1537
+ yield _response(data);
1538
+ }
1539
+ catch (error) { }
1540
+ });
1541
+ if (model.interceptor) {
1542
+ try {
1543
+ const data = yield model.interceptor(frame.fc, frame.data);
1544
+ if (data) {
1545
+ response(this.applicationLayer.encode(Object.assign(Object.assign({}, frame), { data })));
1546
+ return;
1547
+ }
1548
+ }
1549
+ catch (error) {
1550
+ this.responseError(frame, response, error);
1551
+ return;
1552
+ }
1553
+ }
1554
+ switch (frame.fc) {
1555
+ case 0x01: {
1556
+ this.handleFC1(frame, response);
1557
+ break;
1558
+ }
1559
+ case 0x02: {
1560
+ this.handleFC2(frame, response);
1561
+ break;
1562
+ }
1563
+ case 0x03: {
1564
+ this.handleFC3(frame, response);
1565
+ break;
1566
+ }
1567
+ case 0x04: {
1568
+ this.handleFC4(frame, response);
1569
+ break;
1570
+ }
1571
+ case 0x05: {
1572
+ this.handleFC5(frame, response);
1573
+ break;
1574
+ }
1575
+ case 0x06: {
1576
+ this.handleFC6(frame, response);
1577
+ break;
1578
+ }
1579
+ case 0x0f: {
1580
+ this.handleFC15(frame, response);
1581
+ break;
1582
+ }
1583
+ case 0x10: {
1584
+ this.handleFC16(frame, response);
1585
+ break;
1586
+ }
1587
+ case 0x11: {
1588
+ this.handleFC17(frame, response);
1589
+ break;
1590
+ }
1591
+ case 0x16: {
1592
+ this.handleFC22(frame, response);
1593
+ break;
1594
+ }
1595
+ case 0x17: {
1596
+ this.handleFC23(frame, response);
1597
+ break;
1598
+ }
1599
+ case 0x2b: {
1600
+ this.handleFC43_14(frame, response);
1601
+ break;
1602
+ }
1603
+ default: {
1604
+ this.responseError(frame, response, getErrorByCode(ErrorCode.ILLEGAL_FUNCTION));
1605
+ break;
1606
+ }
1607
+ }
1608
+ }));
1609
+ physicalLayer.on('error', (error) => {
1610
+ this.emit('error', error);
1611
+ });
1612
+ physicalLayer.on('close', () => {
1613
+ this.emit('close');
1614
+ });
1615
+ }
1616
+ handleFC1(frame, response) {
1617
+ var _a, _b;
1618
+ if (frame.data.length === 4) {
1619
+ if (this.model.readCoils) {
1620
+ const bufferRx = Buffer.from(frame.data);
1621
+ const address = bufferRx.readUInt16BE(0);
1622
+ const length = bufferRx.readUInt16BE(2);
1623
+ if (length >= 0x0001 && length <= 0x07d0) {
1624
+ if (checkRange([address, address + length], (_b = (_a = this.model).getAddressRange) === null || _b === void 0 ? void 0 : _b.call(_a).coils)) {
1625
+ Promise.resolve(this.model.readCoils(address, length))
1626
+ .then((coils) => {
1627
+ const bufferTx = Buffer.alloc(Math.ceil(length / 8));
1628
+ coils.forEach((coil, index) => {
1629
+ if (coil) {
1630
+ bufferTx[~~(index / 8)] |= 1 << index % 8;
1631
+ }
1632
+ });
1633
+ response(this.applicationLayer.encode(Object.assign(Object.assign({}, frame), { data: [bufferTx.length].concat(Array.from(bufferTx)) })));
1634
+ })
1635
+ .catch((error) => {
1636
+ this.responseError(frame, response, error);
1637
+ });
1638
+ }
1639
+ else {
1640
+ this.responseError(frame, response, getErrorByCode(ErrorCode.ILLEGAL_DATA_ADDRESS));
1641
+ }
1642
+ }
1643
+ else {
1644
+ this.responseError(frame, response, getErrorByCode(ErrorCode.ILLEGAL_DATA_VALUE));
1645
+ }
1646
+ }
1647
+ else {
1648
+ this.responseError(frame, response, getErrorByCode(ErrorCode.ILLEGAL_FUNCTION));
1649
+ }
1650
+ }
1651
+ }
1652
+ handleFC2(frame, response) {
1653
+ var _a, _b;
1654
+ if (frame.data.length === 4) {
1655
+ if (this.model.readDiscreteInputs) {
1656
+ const bufferRx = Buffer.from(frame.data);
1657
+ const address = bufferRx.readUInt16BE(0);
1658
+ const length = bufferRx.readUInt16BE(2);
1659
+ if (length >= 0x0001 && length <= 0x07d0) {
1660
+ if (checkRange([address, address + length], (_b = (_a = this.model).getAddressRange) === null || _b === void 0 ? void 0 : _b.call(_a).discreteInputs)) {
1661
+ Promise.resolve(this.model.readDiscreteInputs(address, length))
1662
+ .then((discreteInputs) => {
1663
+ const bufferTx = Buffer.alloc(Math.ceil(length / 8));
1664
+ discreteInputs.forEach((discreteInput, index) => {
1665
+ if (discreteInput) {
1666
+ bufferTx[~~(index / 8)] |= 1 << index % 8;
1667
+ }
1668
+ });
1669
+ response(this.applicationLayer.encode(Object.assign(Object.assign({}, frame), { data: [bufferTx.length].concat(Array.from(bufferTx)) })));
1670
+ })
1671
+ .catch((error) => {
1672
+ this.responseError(frame, response, error);
1673
+ });
1674
+ }
1675
+ else {
1676
+ this.responseError(frame, response, getErrorByCode(ErrorCode.ILLEGAL_DATA_ADDRESS));
1677
+ }
1678
+ }
1679
+ else {
1680
+ this.responseError(frame, response, getErrorByCode(ErrorCode.ILLEGAL_DATA_VALUE));
1681
+ }
1682
+ }
1683
+ else {
1684
+ this.responseError(frame, response, getErrorByCode(ErrorCode.ILLEGAL_FUNCTION));
1685
+ }
1686
+ }
1687
+ }
1688
+ handleFC3(frame, response) {
1689
+ var _a, _b;
1690
+ if (frame.data.length === 4) {
1691
+ if (this.model.readHoldingRegisters) {
1692
+ const bufferRx = Buffer.from(frame.data);
1693
+ const address = bufferRx.readUInt16BE(0);
1694
+ const length = bufferRx.readUInt16BE(2);
1695
+ if (length >= 0x0001 && length <= 0x007d) {
1696
+ if (checkRange([address, address + length], (_b = (_a = this.model).getAddressRange) === null || _b === void 0 ? void 0 : _b.call(_a).holdingRegisters)) {
1697
+ Promise.resolve(this.model.readHoldingRegisters(address, length))
1698
+ .then((registers) => {
1699
+ const bufferTx = Buffer.alloc(length * 2);
1700
+ registers.forEach((register, index) => {
1701
+ bufferTx.writeUInt16BE(register, index * 2);
1702
+ });
1703
+ response(this.applicationLayer.encode(Object.assign(Object.assign({}, frame), { data: [bufferTx.length].concat(Array.from(bufferTx)) })));
1704
+ })
1705
+ .catch((error) => {
1706
+ this.responseError(frame, response, error);
1707
+ });
1708
+ }
1709
+ else {
1710
+ this.responseError(frame, response, getErrorByCode(ErrorCode.ILLEGAL_DATA_ADDRESS));
1711
+ }
1712
+ }
1713
+ else {
1714
+ this.responseError(frame, response, getErrorByCode(ErrorCode.ILLEGAL_DATA_VALUE));
1715
+ }
1716
+ }
1717
+ else {
1718
+ this.responseError(frame, response, getErrorByCode(ErrorCode.ILLEGAL_FUNCTION));
1719
+ }
1720
+ }
1721
+ }
1722
+ handleFC4(frame, response) {
1723
+ var _a, _b;
1724
+ if (frame.data.length === 4) {
1725
+ if (this.model.readInputRegisters) {
1726
+ const bufferRx = Buffer.from(frame.data);
1727
+ const address = bufferRx.readUInt16BE(0);
1728
+ const length = bufferRx.readUInt16BE(2);
1729
+ if (length >= 0x0001 && length <= 0x007d) {
1730
+ if (checkRange([address, address + length], (_b = (_a = this.model).getAddressRange) === null || _b === void 0 ? void 0 : _b.call(_a).inputRegisters)) {
1731
+ Promise.resolve(this.model.readInputRegisters(address, length))
1732
+ .then((registers) => {
1733
+ const bufferTx = Buffer.alloc(length * 2);
1734
+ registers.forEach((register, index) => {
1735
+ bufferTx.writeUInt16BE(register, index * 2);
1736
+ });
1737
+ response(this.applicationLayer.encode(Object.assign(Object.assign({}, frame), { data: [bufferTx.length].concat(Array.from(bufferTx)) })));
1738
+ })
1739
+ .catch((error) => {
1740
+ this.responseError(frame, response, error);
1741
+ });
1742
+ }
1743
+ else {
1744
+ this.responseError(frame, response, getErrorByCode(ErrorCode.ILLEGAL_DATA_ADDRESS));
1745
+ }
1746
+ }
1747
+ else {
1748
+ this.responseError(frame, response, getErrorByCode(ErrorCode.ILLEGAL_DATA_VALUE));
1749
+ }
1750
+ }
1751
+ else {
1752
+ this.responseError(frame, response, getErrorByCode(ErrorCode.ILLEGAL_FUNCTION));
1753
+ }
1754
+ }
1755
+ }
1756
+ handleFC5(frame, response) {
1757
+ var _a, _b;
1758
+ if (frame.data.length === 4) {
1759
+ if (this.model.writeSingleCoil) {
1760
+ const bufferRx = Buffer.from(frame.data);
1761
+ const address = bufferRx.readUInt16BE(0);
1762
+ const value = bufferRx.readUInt16BE(2);
1763
+ if (value === 0x0000 || value === 0xff00) {
1764
+ if (checkRange(address, (_b = (_a = this.model).getAddressRange) === null || _b === void 0 ? void 0 : _b.call(_a).coils)) {
1765
+ Promise.resolve(this.model.writeSingleCoil(address, value === 0xff00))
1766
+ .then(() => {
1767
+ response(this.applicationLayer.encode(frame));
1768
+ })
1769
+ .catch((error) => {
1770
+ this.responseError(frame, response, error);
1771
+ });
1772
+ }
1773
+ else {
1774
+ this.responseError(frame, response, getErrorByCode(ErrorCode.ILLEGAL_DATA_ADDRESS));
1775
+ }
1776
+ }
1777
+ else {
1778
+ this.responseError(frame, response, getErrorByCode(ErrorCode.ILLEGAL_DATA_VALUE));
1779
+ }
1780
+ }
1781
+ else {
1782
+ this.responseError(frame, response, getErrorByCode(ErrorCode.ILLEGAL_FUNCTION));
1783
+ }
1784
+ }
1785
+ }
1786
+ handleFC6(frame, response) {
1787
+ var _a, _b;
1788
+ if (frame.data.length === 4) {
1789
+ if (this.model.writeSingleRegister) {
1790
+ const bufferRx = Buffer.from(frame.data);
1791
+ const address = bufferRx.readUInt16BE(0);
1792
+ const value = bufferRx.readUInt16BE(2);
1793
+ if (value >= 0x0000 && value <= 0xffff) {
1794
+ if (checkRange(address, (_b = (_a = this.model).getAddressRange) === null || _b === void 0 ? void 0 : _b.call(_a).holdingRegisters)) {
1795
+ Promise.resolve(this.model.writeSingleRegister(address, value))
1796
+ .then(() => {
1797
+ response(this.applicationLayer.encode(frame));
1798
+ })
1799
+ .catch((error) => {
1800
+ this.responseError(frame, response, error);
1801
+ });
1802
+ }
1803
+ else {
1804
+ this.responseError(frame, response, getErrorByCode(ErrorCode.ILLEGAL_DATA_ADDRESS));
1805
+ }
1806
+ }
1807
+ else {
1808
+ this.responseError(frame, response, getErrorByCode(ErrorCode.ILLEGAL_DATA_VALUE));
1809
+ }
1810
+ }
1811
+ else {
1812
+ this.responseError(frame, response, getErrorByCode(ErrorCode.ILLEGAL_FUNCTION));
1813
+ }
1814
+ }
1815
+ }
1816
+ handleFC15(frame, response) {
1817
+ var _a, _b;
1818
+ if (frame.data.length > 5 && frame.data.length === 5 + frame.data[4]) {
1819
+ if (this.model.writeMultipleCoils || this.model.writeSingleCoil) {
1820
+ const bufferRx = Buffer.from(frame.data);
1821
+ const address = bufferRx.readUInt16BE(0);
1822
+ const length = bufferRx.readUInt16BE(2);
1823
+ const byteCount = bufferRx[4];
1824
+ if (length >= 0x0001 && length <= 0x07b0 && byteCount === Math.ceil(length / 8)) {
1825
+ if (checkRange([address, address + length], (_b = (_a = this.model).getAddressRange) === null || _b === void 0 ? void 0 : _b.call(_a).coils)) {
1826
+ const value = Array.from({ length }).map((_, index) => (bufferRx[5 + ~~(index / 8)] & (1 << index % 8)) > 0);
1827
+ Promise.resolve(this.model.writeMultipleCoils
1828
+ ? this.model.writeMultipleCoils(address, value)
1829
+ : Promise.all(value.map((v, i) => this.model.writeSingleCoil(address + i, v))))
1830
+ .then(() => {
1831
+ response(this.applicationLayer.encode(Object.assign(Object.assign({}, frame), { data: Array.from(bufferRx).slice(0, 4) })));
1832
+ })
1833
+ .catch((error) => {
1834
+ this.responseError(frame, response, error);
1835
+ });
1836
+ }
1837
+ else {
1838
+ this.responseError(frame, response, getErrorByCode(ErrorCode.ILLEGAL_DATA_ADDRESS));
1839
+ }
1840
+ }
1841
+ else {
1842
+ this.responseError(frame, response, getErrorByCode(ErrorCode.ILLEGAL_DATA_VALUE));
1843
+ }
1844
+ }
1845
+ else {
1846
+ this.responseError(frame, response, getErrorByCode(ErrorCode.ILLEGAL_FUNCTION));
1847
+ }
1848
+ }
1849
+ }
1850
+ handleFC16(frame, response) {
1851
+ var _a, _b;
1852
+ if (frame.data.length > 5 && frame.data.length === 5 + frame.data[4]) {
1853
+ if (this.model.writeMultipleRegisters || this.model.writeSingleRegister) {
1854
+ const bufferRx = Buffer.from(frame.data);
1855
+ const address = bufferRx.readUInt16BE(0);
1856
+ const length = bufferRx.readUInt16BE(2);
1857
+ const byteCount = bufferRx[4];
1858
+ if (length >= 0x0001 && length <= 0x007b && byteCount === length * 2) {
1859
+ if (checkRange([address, address + length], (_b = (_a = this.model).getAddressRange) === null || _b === void 0 ? void 0 : _b.call(_a).holdingRegisters)) {
1860
+ const value = Array.from({ length }).map((_, index) => bufferRx.readUInt16BE(5 + index * 2));
1861
+ Promise.resolve(this.model.writeMultipleRegisters
1862
+ ? this.model.writeMultipleRegisters(address, value)
1863
+ : Promise.all(value.map((v, i) => this.model.writeSingleRegister(address + i, v))))
1864
+ .then(() => {
1865
+ response(this.applicationLayer.encode(Object.assign(Object.assign({}, frame), { data: Array.from(bufferRx).slice(0, 4) })));
1866
+ })
1867
+ .catch((error) => {
1868
+ this.responseError(frame, response, error);
1869
+ });
1870
+ }
1871
+ else {
1872
+ this.responseError(frame, response, getErrorByCode(ErrorCode.ILLEGAL_DATA_ADDRESS));
1873
+ }
1874
+ }
1875
+ else {
1876
+ this.responseError(frame, response, getErrorByCode(ErrorCode.ILLEGAL_DATA_VALUE));
1877
+ }
1878
+ }
1879
+ else {
1880
+ this.responseError(frame, response, getErrorByCode(ErrorCode.ILLEGAL_FUNCTION));
1881
+ }
1882
+ }
1883
+ }
1884
+ handleFC17(frame, response) {
1885
+ if (frame.data.length === 0) {
1886
+ if (this.model.reportServerId) {
1887
+ Promise.resolve(this.model.reportServerId())
1888
+ .then(({ serverId = this.unit, runIndicatorStatus = true, additionalData = [] }) => {
1889
+ response(this.applicationLayer.encode(Object.assign(Object.assign({}, frame), { data: [2 + additionalData.length, serverId, runIndicatorStatus ? 0xff : 0x00].concat(additionalData) })));
1890
+ })
1891
+ .catch((error) => {
1892
+ this.responseError(frame, response, error);
1893
+ });
1894
+ }
1895
+ else {
1896
+ this.responseError(frame, response, getErrorByCode(ErrorCode.ILLEGAL_FUNCTION));
1897
+ }
1898
+ }
1899
+ }
1900
+ handleFC22(frame, response) {
1901
+ var _a, _b;
1902
+ if (frame.data.length === 6) {
1903
+ if (this.model.maskWriteRegister || (this.model.readHoldingRegisters && this.model.writeSingleRegister)) {
1904
+ const bufferRx = Buffer.from(frame.data);
1905
+ const address = bufferRx.readUInt16BE(0);
1906
+ const andMask = bufferRx.readUInt16BE(2);
1907
+ const orMask = bufferRx.readUInt16BE(4);
1908
+ if (checkRange(address, (_b = (_a = this.model).getAddressRange) === null || _b === void 0 ? void 0 : _b.call(_a).holdingRegisters)) {
1909
+ Promise.resolve(this.model.maskWriteRegister
1910
+ ? this.model.maskWriteRegister(address, andMask, orMask)
1911
+ : Promise.resolve(this.model.readHoldingRegisters(address, 1)).then(([value]) => {
1912
+ return Promise.resolve(this.model.writeSingleRegister(address, (value & andMask) | (orMask & (~andMask & 0xff))));
1913
+ }))
1914
+ .then(() => {
1915
+ response(this.applicationLayer.encode(frame));
1916
+ })
1917
+ .catch((error) => {
1918
+ this.responseError(frame, response, error);
1919
+ });
1920
+ }
1921
+ else {
1922
+ this.responseError(frame, response, getErrorByCode(ErrorCode.ILLEGAL_DATA_ADDRESS));
1923
+ }
1924
+ }
1925
+ else {
1926
+ this.responseError(frame, response, getErrorByCode(ErrorCode.ILLEGAL_FUNCTION));
1927
+ }
1928
+ }
1929
+ }
1930
+ handleFC23(frame, response) {
1931
+ var _a, _b;
1932
+ if (frame.data.length > 9 && frame.data.length === 9 + frame.data[8]) {
1933
+ if (this.model.readHoldingRegisters && (this.model.writeMultipleRegisters || this.model.writeSingleRegister)) {
1934
+ const bufferRx = Buffer.from(frame.data);
1935
+ const address = {
1936
+ read: bufferRx.readUInt16BE(0),
1937
+ write: bufferRx.readUInt16BE(4),
1938
+ };
1939
+ const length = {
1940
+ read: bufferRx.readUInt16BE(2),
1941
+ write: bufferRx.readUInt16BE(6),
1942
+ };
1943
+ const byteCount = bufferRx[8];
1944
+ if (length.read >= 0x0001 &&
1945
+ length.read <= 0x007d &&
1946
+ length.write >= 0x0001 &&
1947
+ length.write <= 0x0079 &&
1948
+ byteCount === length.write * 2) {
1949
+ if (checkRange([address.read, address.read + length.read, address.write, address.write + length.write], (_b = (_a = this.model).getAddressRange) === null || _b === void 0 ? void 0 : _b.call(_a).holdingRegisters)) {
1950
+ const value = Array.from({ length: length.write }).map((_, index) => bufferRx.readUInt16BE(9 + index * 2));
1951
+ Promise.resolve(this.model.writeMultipleRegisters
1952
+ ? this.model.writeMultipleRegisters(address.write, value)
1953
+ : Promise.all(value.map((v, i) => this.model.writeSingleRegister(address.write + i, v))))
1954
+ .then(() => Promise.resolve(this.model.readHoldingRegisters(address.read, length.read)))
1955
+ .then((registers) => {
1956
+ const bufferTx = Buffer.alloc(length.read * 2);
1957
+ registers.forEach((register, index) => {
1958
+ bufferTx.writeUInt16BE(register, index * 2);
1959
+ });
1960
+ response(this.applicationLayer.encode(Object.assign(Object.assign({}, frame), { data: [bufferTx.length].concat(Array.from(bufferTx)) })));
1961
+ })
1962
+ .catch((error) => {
1963
+ this.responseError(frame, response, error);
1964
+ });
1965
+ }
1966
+ else {
1967
+ this.responseError(frame, response, getErrorByCode(ErrorCode.ILLEGAL_DATA_ADDRESS));
1968
+ }
1969
+ }
1970
+ else {
1971
+ this.responseError(frame, response, getErrorByCode(ErrorCode.ILLEGAL_DATA_VALUE));
1972
+ }
1973
+ }
1974
+ else {
1975
+ this.responseError(frame, response, getErrorByCode(ErrorCode.ILLEGAL_FUNCTION));
1976
+ }
1977
+ }
1978
+ }
1979
+ handleFC43_14(frame, response) {
1980
+ if (frame.data.length === 3) {
1981
+ if (frame.data[0] === 0x0e && this.model.readDeviceIdentification) {
1982
+ const readDeviceIDCode = frame.data[1];
1983
+ let objectID = frame.data[2];
1984
+ switch (readDeviceIDCode) {
1985
+ case 0x01: {
1986
+ if (objectID > 0x02 || (objectID > 0x06 && objectID < 0x80)) {
1987
+ objectID = 0x00;
1988
+ }
1989
+ break;
1990
+ }
1991
+ case 0x02: {
1992
+ if (objectID >= 0x80 || (objectID > 0x06 && objectID < 0x80)) {
1993
+ objectID = 0x00;
1994
+ }
1995
+ break;
1996
+ }
1997
+ case 0x03: {
1998
+ if (objectID > 0x06 && objectID < 0x80) {
1999
+ objectID = 0x00;
2000
+ }
2001
+ break;
2002
+ }
2003
+ case 0x04: {
2004
+ if (objectID > 0x06 && objectID < 0x80) {
2005
+ this.responseError(frame, response, getErrorByCode(ErrorCode.ILLEGAL_DATA_ADDRESS));
2006
+ return;
2007
+ }
2008
+ break;
2009
+ }
2010
+ default: {
2011
+ this.responseError(frame, response, getErrorByCode(ErrorCode.ILLEGAL_DATA_VALUE));
2012
+ return;
2013
+ }
2014
+ }
2015
+ Promise.resolve(this.model.readDeviceIdentification())
2016
+ .then((identification) => {
2017
+ const objects = new Map([
2018
+ [0x00, 'null'],
2019
+ [0x01, 'null'],
2020
+ [0x02, 'null'],
2021
+ ]);
2022
+ for (const [key, value] of Object.entries(identification)) {
2023
+ const id = parseInt(key);
2024
+ if (!isNaN(id) && id >= 0 && id <= 255) {
2025
+ objects.set(id, value);
2026
+ }
2027
+ }
2028
+ if (!objects.has(objectID)) {
2029
+ if (readDeviceIDCode === 0x04) {
2030
+ this.responseError(frame, response, getErrorByCode(ErrorCode.ILLEGAL_DATA_ADDRESS));
2031
+ return;
2032
+ }
2033
+ objectID = 0x00;
2034
+ }
2035
+ const ids = [];
2036
+ let totalLength = 10;
2037
+ let lastID = 0;
2038
+ let conformityLevel = 0x81;
2039
+ for (const [id, value] of objects.entries()) {
2040
+ if (id < 0x00 || (id >= 0x07 && id <= 0x7f) || id > 0xff) {
2041
+ this.responseError(frame, response, getErrorByCode(ErrorCode.SERVER_DEVICE_FAILURE));
2042
+ return;
2043
+ }
2044
+ if (id > 0x02) {
2045
+ conformityLevel = 0x82;
2046
+ }
2047
+ if (id > 0x80) {
2048
+ conformityLevel = 0x83;
2049
+ }
2050
+ if (objectID > id) {
2051
+ continue;
2052
+ }
2053
+ if (value.length > 245) {
2054
+ this.responseError(frame, response, getErrorByCode(ErrorCode.SERVER_DEVICE_FAILURE));
2055
+ return;
2056
+ }
2057
+ if (lastID !== 0) {
2058
+ continue;
2059
+ }
2060
+ if (value.length + 2 > 253 - totalLength) {
2061
+ if (lastID === 0) {
2062
+ lastID = id;
2063
+ }
2064
+ }
2065
+ else {
2066
+ totalLength += value.length + 2;
2067
+ ids.push(id);
2068
+ if (readDeviceIDCode === 0x04) {
2069
+ break;
2070
+ }
2071
+ }
2072
+ }
2073
+ ids.sort((a, b) => a - b);
2074
+ response(this.applicationLayer.encode(Object.assign(Object.assign({}, frame), { data: [0x0e, readDeviceIDCode, conformityLevel, lastID === 0 ? 0x00 : 0xff, lastID, ids.length].concat(ids
2075
+ .map((id) => {
2076
+ const value = objects.get(id);
2077
+ return [id, value.length].concat(Array.from(Buffer.from(value)));
2078
+ })
2079
+ .flat()) })));
2080
+ })
2081
+ .catch((error) => {
2082
+ this.responseError(frame, response, error);
2083
+ });
2084
+ }
2085
+ else {
2086
+ this.responseError(frame, response, getErrorByCode(ErrorCode.ILLEGAL_FUNCTION));
2087
+ }
2088
+ }
2089
+ }
2090
+ responseError(frame, response, error) {
2091
+ response(this.applicationLayer.encode(Object.assign(Object.assign({}, frame), { fc: frame.fc | 0x80, data: [getCodeByError(error)] })));
2092
+ }
2093
+ open(...args) {
2094
+ return this.physicalLayer.open(...args);
2095
+ }
2096
+ close() {
2097
+ return this.physicalLayer.close();
2098
+ }
2099
+ destroy() {
2100
+ this.removeAllListeners();
2101
+ this.applicationLayer.destroy();
2102
+ return this.physicalLayer.destroy();
2103
+ }
2104
+ }
2105
+
2106
+ export { AbstractApplicationLayer, AbstractPhysicalLayer, AsciiApplicationLayer, ErrorCode, ModbusMaster, ModbusSlave, RtuApplicationLayer, SerialPhysicalLayer, TcpApplicationLayer, TcpClientPhysicalLayer, TcpServerPhysicalLayer, UdpPhysicalLayer, getCodeByError, getErrorByCode };