njs-modbus 1.3.2 → 1.3.3

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