njs-modbus 1.0.0 → 1.1.0
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/LICENSE +21 -0
- package/README.md +1 -1
- package/dist/index.cjs +39 -25
- package/dist/index.d.ts +8 -8
- package/dist/index.mjs +39 -25
- package/dist/src/layers/physical/tcp-client-physical-layer.d.ts +1 -1
- package/dist/src/layers/physical/tcp-server-physical-layer.d.ts +1 -1
- package/dist/src/layers/physical/udp-physical-layer.d.ts +1 -1
- package/dist/src/slave/slave.d.ts +5 -5
- package/dist/src/utils/checkRange.d.ts +1 -2
- package/package.json +1 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Xie Jay
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -181,7 +181,7 @@ For more advanced examples, check out [examples](/examples) included in the repo
|
|
|
181
181
|
|
|
182
182
|
## Contributing
|
|
183
183
|
|
|
184
|
-
Please read our [contributing guide](/
|
|
184
|
+
Please read our [contributing guide](/CODE_OF_CONDUCT.md) first.
|
|
185
185
|
|
|
186
186
|
## License
|
|
187
187
|
|
package/dist/index.cjs
CHANGED
|
@@ -158,7 +158,7 @@ class TcpClientPhysicalLayer extends AbstractPhysicalLayer {
|
|
|
158
158
|
}
|
|
159
159
|
return new Promise((resolve, reject) => {
|
|
160
160
|
let called = false;
|
|
161
|
-
this._socket.connect(
|
|
161
|
+
this._socket.connect(options !== null && options !== void 0 ? options : { port: 502 }, () => {
|
|
162
162
|
called = true;
|
|
163
163
|
this._isOpen = true;
|
|
164
164
|
this._socket.on('data', (data) => {
|
|
@@ -279,7 +279,7 @@ class TcpServerPhysicalLayer extends AbstractPhysicalLayer {
|
|
|
279
279
|
return new Promise((resolve, reject) => {
|
|
280
280
|
var _a;
|
|
281
281
|
let called = false;
|
|
282
|
-
this._server.listen(Object.assign(Object.assign({}, options), { port: (_a = options.port) !== null && _a !== void 0 ? _a : 502 }), () => {
|
|
282
|
+
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 }), () => {
|
|
283
283
|
called = true;
|
|
284
284
|
this._isOpen = true;
|
|
285
285
|
this._sockets.clear();
|
|
@@ -410,7 +410,7 @@ class UdpPhysicalLayer extends AbstractPhysicalLayer {
|
|
|
410
410
|
var _a;
|
|
411
411
|
if (this.isServer) {
|
|
412
412
|
let called = false;
|
|
413
|
-
this._socket.bind(Object.assign(Object.assign({}, options), { port: (_a = options.port) !== null && _a !== void 0 ? _a : 502 }), () => {
|
|
413
|
+
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 }), () => {
|
|
414
414
|
called = true;
|
|
415
415
|
this._isOpen = true;
|
|
416
416
|
this._socket.on('close', () => {
|
|
@@ -472,8 +472,22 @@ class AbstractApplicationLayer extends EventEmitter {
|
|
|
472
472
|
}
|
|
473
473
|
|
|
474
474
|
function checkRange(value, range) {
|
|
475
|
-
if (range
|
|
476
|
-
|
|
475
|
+
if (range) {
|
|
476
|
+
if (typeof range[0] === 'number' && typeof range[1] === 'number') {
|
|
477
|
+
if (range[0] < range[1]) {
|
|
478
|
+
return (Array.isArray(value) ? value : [value]).every((n) => n >= range[0] && n <= range[1]);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
else if (range.length > 0) {
|
|
482
|
+
for (const r of range) {
|
|
483
|
+
if (r[0] < r[1]) {
|
|
484
|
+
if ((Array.isArray(value) ? value : [value]).every((n) => n >= r[0] && n <= r[1])) {
|
|
485
|
+
return true;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
return false;
|
|
490
|
+
}
|
|
477
491
|
}
|
|
478
492
|
return true;
|
|
479
493
|
}
|
|
@@ -1368,14 +1382,14 @@ class ModbusSlave extends EventEmitter {
|
|
|
1368
1382
|
});
|
|
1369
1383
|
}
|
|
1370
1384
|
handleFC1(frame, response) {
|
|
1371
|
-
var _a;
|
|
1385
|
+
var _a, _b;
|
|
1372
1386
|
if (frame.data.length === 4) {
|
|
1373
1387
|
if (this.model.readCoils) {
|
|
1374
1388
|
const bufferRx = Buffer.from(frame.data);
|
|
1375
1389
|
const address = bufferRx.readUInt16BE(0);
|
|
1376
1390
|
const length = bufferRx.readUInt16BE(2);
|
|
1377
1391
|
if (length >= 0x0001 && length <= 0x07d0) {
|
|
1378
|
-
if (checkRange([address, address + length], (_a = this.model.
|
|
1392
|
+
if (checkRange([address, address + length], (_b = (_a = this.model).getAddressRange) === null || _b === void 0 ? void 0 : _b.call(_a).coils)) {
|
|
1379
1393
|
Promise.resolve(this.model.readCoils(address, length))
|
|
1380
1394
|
.then((coils) => {
|
|
1381
1395
|
const bufferTx = Buffer.alloc(Math.ceil(length / 8));
|
|
@@ -1404,14 +1418,14 @@ class ModbusSlave extends EventEmitter {
|
|
|
1404
1418
|
}
|
|
1405
1419
|
}
|
|
1406
1420
|
handleFC2(frame, response) {
|
|
1407
|
-
var _a;
|
|
1421
|
+
var _a, _b;
|
|
1408
1422
|
if (frame.data.length === 4) {
|
|
1409
1423
|
if (this.model.readDiscreteInputs) {
|
|
1410
1424
|
const bufferRx = Buffer.from(frame.data);
|
|
1411
1425
|
const address = bufferRx.readUInt16BE(0);
|
|
1412
1426
|
const length = bufferRx.readUInt16BE(2);
|
|
1413
1427
|
if (length >= 0x0001 && length <= 0x07d0) {
|
|
1414
|
-
if (checkRange([address, address + length], (_a = this.model.
|
|
1428
|
+
if (checkRange([address, address + length], (_b = (_a = this.model).getAddressRange) === null || _b === void 0 ? void 0 : _b.call(_a).discreteInputs)) {
|
|
1415
1429
|
Promise.resolve(this.model.readDiscreteInputs(address, length))
|
|
1416
1430
|
.then((discreteInputs) => {
|
|
1417
1431
|
const bufferTx = Buffer.alloc(Math.ceil(length / 8));
|
|
@@ -1440,14 +1454,14 @@ class ModbusSlave extends EventEmitter {
|
|
|
1440
1454
|
}
|
|
1441
1455
|
}
|
|
1442
1456
|
handleFC3(frame, response) {
|
|
1443
|
-
var _a;
|
|
1457
|
+
var _a, _b;
|
|
1444
1458
|
if (frame.data.length === 4) {
|
|
1445
1459
|
if (this.model.readHoldingRegisters) {
|
|
1446
1460
|
const bufferRx = Buffer.from(frame.data);
|
|
1447
1461
|
const address = bufferRx.readUInt16BE(0);
|
|
1448
1462
|
const length = bufferRx.readUInt16BE(2);
|
|
1449
1463
|
if (length >= 0x0001 && length <= 0x007d) {
|
|
1450
|
-
if (checkRange([address, address + length], (_a = this.model.
|
|
1464
|
+
if (checkRange([address, address + length], (_b = (_a = this.model).getAddressRange) === null || _b === void 0 ? void 0 : _b.call(_a).holdingRegisters)) {
|
|
1451
1465
|
Promise.resolve(this.model.readHoldingRegisters(address, length))
|
|
1452
1466
|
.then((registers) => {
|
|
1453
1467
|
const bufferTx = Buffer.alloc(length * 2);
|
|
@@ -1474,14 +1488,14 @@ class ModbusSlave extends EventEmitter {
|
|
|
1474
1488
|
}
|
|
1475
1489
|
}
|
|
1476
1490
|
handleFC4(frame, response) {
|
|
1477
|
-
var _a;
|
|
1491
|
+
var _a, _b;
|
|
1478
1492
|
if (frame.data.length === 4) {
|
|
1479
1493
|
if (this.model.readInputRegisters) {
|
|
1480
1494
|
const bufferRx = Buffer.from(frame.data);
|
|
1481
1495
|
const address = bufferRx.readUInt16BE(0);
|
|
1482
1496
|
const length = bufferRx.readUInt16BE(2);
|
|
1483
1497
|
if (length >= 0x0001 && length <= 0x007d) {
|
|
1484
|
-
if (checkRange([address, address + length], (_a = this.model.
|
|
1498
|
+
if (checkRange([address, address + length], (_b = (_a = this.model).getAddressRange) === null || _b === void 0 ? void 0 : _b.call(_a).inputRegisters)) {
|
|
1485
1499
|
Promise.resolve(this.model.readInputRegisters(address, length))
|
|
1486
1500
|
.then((registers) => {
|
|
1487
1501
|
const bufferTx = Buffer.alloc(length * 2);
|
|
@@ -1508,14 +1522,14 @@ class ModbusSlave extends EventEmitter {
|
|
|
1508
1522
|
}
|
|
1509
1523
|
}
|
|
1510
1524
|
handleFC5(frame, response) {
|
|
1511
|
-
var _a;
|
|
1525
|
+
var _a, _b;
|
|
1512
1526
|
if (frame.data.length === 4) {
|
|
1513
1527
|
if (this.model.writeSingleCoil) {
|
|
1514
1528
|
const bufferRx = Buffer.from(frame.data);
|
|
1515
1529
|
const address = bufferRx.readUInt16BE(0);
|
|
1516
1530
|
const value = bufferRx.readUInt16BE(2);
|
|
1517
1531
|
if (value === 0x0000 || value === 0xff00) {
|
|
1518
|
-
if (checkRange(address, (_a = this.model.
|
|
1532
|
+
if (checkRange(address, (_b = (_a = this.model).getAddressRange) === null || _b === void 0 ? void 0 : _b.call(_a).coils)) {
|
|
1519
1533
|
Promise.resolve(this.model.writeSingleCoil(address, value === 0xff00))
|
|
1520
1534
|
.then(() => {
|
|
1521
1535
|
response(this.applicationLayer.encode(frame));
|
|
@@ -1538,14 +1552,14 @@ class ModbusSlave extends EventEmitter {
|
|
|
1538
1552
|
}
|
|
1539
1553
|
}
|
|
1540
1554
|
handleFC6(frame, response) {
|
|
1541
|
-
var _a;
|
|
1555
|
+
var _a, _b;
|
|
1542
1556
|
if (frame.data.length === 4) {
|
|
1543
1557
|
if (this.model.writeSingleRegister) {
|
|
1544
1558
|
const bufferRx = Buffer.from(frame.data);
|
|
1545
1559
|
const address = bufferRx.readUInt16BE(0);
|
|
1546
1560
|
const value = bufferRx.readUInt16BE(2);
|
|
1547
1561
|
if (value >= 0x0000 && value <= 0xffff) {
|
|
1548
|
-
if (checkRange(address, (_a = this.model.
|
|
1562
|
+
if (checkRange(address, (_b = (_a = this.model).getAddressRange) === null || _b === void 0 ? void 0 : _b.call(_a).holdingRegisters)) {
|
|
1549
1563
|
Promise.resolve(this.model.writeSingleRegister(address, value))
|
|
1550
1564
|
.then(() => {
|
|
1551
1565
|
response(this.applicationLayer.encode(frame));
|
|
@@ -1568,7 +1582,7 @@ class ModbusSlave extends EventEmitter {
|
|
|
1568
1582
|
}
|
|
1569
1583
|
}
|
|
1570
1584
|
handleFC15(frame, response) {
|
|
1571
|
-
var _a;
|
|
1585
|
+
var _a, _b;
|
|
1572
1586
|
if (frame.data.length > 5 && frame.data.length === 5 + frame.data[4]) {
|
|
1573
1587
|
if (this.model.writeMultipleCoils || this.model.writeSingleCoil) {
|
|
1574
1588
|
const bufferRx = Buffer.from(frame.data);
|
|
@@ -1576,7 +1590,7 @@ class ModbusSlave extends EventEmitter {
|
|
|
1576
1590
|
const length = bufferRx.readUInt16BE(2);
|
|
1577
1591
|
const byteCount = bufferRx[4];
|
|
1578
1592
|
if (length >= 0x0001 && length <= 0x07b0 && byteCount === Math.ceil(length / 8)) {
|
|
1579
|
-
if (checkRange([address, address + length], (_a = this.model.
|
|
1593
|
+
if (checkRange([address, address + length], (_b = (_a = this.model).getAddressRange) === null || _b === void 0 ? void 0 : _b.call(_a).coils)) {
|
|
1580
1594
|
const value = Array.from({ length }).map((_, index) => (bufferRx[5 + ~~(index / 8)] & (1 << index % 8)) > 0);
|
|
1581
1595
|
Promise.resolve(this.model.writeMultipleCoils
|
|
1582
1596
|
? this.model.writeMultipleCoils(address, value)
|
|
@@ -1602,7 +1616,7 @@ class ModbusSlave extends EventEmitter {
|
|
|
1602
1616
|
}
|
|
1603
1617
|
}
|
|
1604
1618
|
handleFC16(frame, response) {
|
|
1605
|
-
var _a;
|
|
1619
|
+
var _a, _b;
|
|
1606
1620
|
if (frame.data.length > 5 && frame.data.length === 5 + frame.data[4]) {
|
|
1607
1621
|
if (this.model.writeMultipleRegisters || this.model.writeSingleRegister) {
|
|
1608
1622
|
const bufferRx = Buffer.from(frame.data);
|
|
@@ -1610,7 +1624,7 @@ class ModbusSlave extends EventEmitter {
|
|
|
1610
1624
|
const length = bufferRx.readUInt16BE(2);
|
|
1611
1625
|
const byteCount = bufferRx[4];
|
|
1612
1626
|
if (length >= 0x0001 && length <= 0x007b && byteCount === length * 2) {
|
|
1613
|
-
if (checkRange([address, address + length], (_a = this.model.
|
|
1627
|
+
if (checkRange([address, address + length], (_b = (_a = this.model).getAddressRange) === null || _b === void 0 ? void 0 : _b.call(_a).holdingRegisters)) {
|
|
1614
1628
|
const value = Array.from({ length }).map((_, index) => bufferRx.readUInt16BE(5 + index * 2));
|
|
1615
1629
|
Promise.resolve(this.model.writeMultipleRegisters
|
|
1616
1630
|
? this.model.writeMultipleRegisters(address, value)
|
|
@@ -1652,14 +1666,14 @@ class ModbusSlave extends EventEmitter {
|
|
|
1652
1666
|
}
|
|
1653
1667
|
}
|
|
1654
1668
|
handleFC22(frame, response) {
|
|
1655
|
-
var _a;
|
|
1669
|
+
var _a, _b;
|
|
1656
1670
|
if (frame.data.length === 6) {
|
|
1657
1671
|
if (this.model.maskWriteRegister || (this.model.readHoldingRegisters && this.model.writeSingleRegister)) {
|
|
1658
1672
|
const bufferRx = Buffer.from(frame.data);
|
|
1659
1673
|
const address = bufferRx.readUInt16BE(0);
|
|
1660
1674
|
const andMask = bufferRx.readUInt16BE(2);
|
|
1661
1675
|
const orMask = bufferRx.readUInt16BE(4);
|
|
1662
|
-
if (checkRange(address, (_a = this.model.
|
|
1676
|
+
if (checkRange(address, (_b = (_a = this.model).getAddressRange) === null || _b === void 0 ? void 0 : _b.call(_a).holdingRegisters)) {
|
|
1663
1677
|
Promise.resolve(this.model.maskWriteRegister
|
|
1664
1678
|
? this.model.maskWriteRegister(address, andMask, orMask)
|
|
1665
1679
|
: Promise.resolve(this.model.readHoldingRegisters(address, 1)).then(([value]) => {
|
|
@@ -1682,7 +1696,7 @@ class ModbusSlave extends EventEmitter {
|
|
|
1682
1696
|
}
|
|
1683
1697
|
}
|
|
1684
1698
|
handleFC23(frame, response) {
|
|
1685
|
-
var _a;
|
|
1699
|
+
var _a, _b;
|
|
1686
1700
|
if (frame.data.length > 9 && frame.data.length === 9 + frame.data[8]) {
|
|
1687
1701
|
if (this.model.readHoldingRegisters && (this.model.writeMultipleRegisters || this.model.writeSingleRegister)) {
|
|
1688
1702
|
const bufferRx = Buffer.from(frame.data);
|
|
@@ -1700,7 +1714,7 @@ class ModbusSlave extends EventEmitter {
|
|
|
1700
1714
|
length.write >= 0x0001 &&
|
|
1701
1715
|
length.write <= 0x0079 &&
|
|
1702
1716
|
byteCount === length.write * 2) {
|
|
1703
|
-
if (checkRange([address.read, address.read + length.read, address.write, address.write + length.write], (_a = this.model.
|
|
1717
|
+
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)) {
|
|
1704
1718
|
const value = Array.from({ length: length.write }).map((_, index) => bufferRx.readUInt16BE(9 + index * 2));
|
|
1705
1719
|
Promise.resolve(this.model.writeMultipleRegisters
|
|
1706
1720
|
? this.model.writeMultipleRegisters(address.write, value)
|
package/dist/index.d.ts
CHANGED
|
@@ -77,7 +77,7 @@ declare class TcpClientPhysicalLayer extends AbstractPhysicalLayer {
|
|
|
77
77
|
get isOpen(): boolean;
|
|
78
78
|
get destroyed(): boolean;
|
|
79
79
|
constructor(options?: SocketConstructorOpts);
|
|
80
|
-
open(options
|
|
80
|
+
open(options?: SocketConnectOpts): Promise<void>;
|
|
81
81
|
write(data: Buffer): Promise<void>;
|
|
82
82
|
close(): Promise<void>;
|
|
83
83
|
destroy(): Promise<void>;
|
|
@@ -92,7 +92,7 @@ declare class TcpServerPhysicalLayer extends AbstractPhysicalLayer {
|
|
|
92
92
|
get isOpen(): boolean;
|
|
93
93
|
get destroyed(): boolean;
|
|
94
94
|
constructor(options?: NetConnectOpts);
|
|
95
|
-
open(options
|
|
95
|
+
open(options?: ListenOptions): Promise<void>;
|
|
96
96
|
write(data: Buffer): Promise<void>;
|
|
97
97
|
close(): Promise<void>;
|
|
98
98
|
destroy(): Promise<void>;
|
|
@@ -118,7 +118,7 @@ declare class UdpPhysicalLayer extends AbstractPhysicalLayer {
|
|
|
118
118
|
port?: number;
|
|
119
119
|
address?: string;
|
|
120
120
|
});
|
|
121
|
-
open(options
|
|
121
|
+
open(options?: BindOptions): Promise<void>;
|
|
122
122
|
write(data: Buffer): Promise<void>;
|
|
123
123
|
close(): Promise<void>;
|
|
124
124
|
destroy(): Promise<void>;
|
|
@@ -298,11 +298,11 @@ interface ModbusSlaveModel {
|
|
|
298
298
|
readDeviceIdentification?: FConvertPromise<() => {
|
|
299
299
|
[index: number]: string;
|
|
300
300
|
}>;
|
|
301
|
-
|
|
302
|
-
discreteInputs?: [number, number];
|
|
303
|
-
coils?: [number, number];
|
|
304
|
-
inputRegisters?: [number, number];
|
|
305
|
-
holdingRegisters?: [number, number];
|
|
301
|
+
getAddressRange?: () => {
|
|
302
|
+
discreteInputs?: [number, number] | [number, number][];
|
|
303
|
+
coils?: [number, number] | [number, number][];
|
|
304
|
+
inputRegisters?: [number, number] | [number, number][];
|
|
305
|
+
holdingRegisters?: [number, number] | [number, number][];
|
|
306
306
|
};
|
|
307
307
|
}
|
|
308
308
|
interface ModbusSlaveEvents {
|
package/dist/index.mjs
CHANGED
|
@@ -156,7 +156,7 @@ class TcpClientPhysicalLayer extends AbstractPhysicalLayer {
|
|
|
156
156
|
}
|
|
157
157
|
return new Promise((resolve, reject) => {
|
|
158
158
|
let called = false;
|
|
159
|
-
this._socket.connect(
|
|
159
|
+
this._socket.connect(options !== null && options !== void 0 ? options : { port: 502 }, () => {
|
|
160
160
|
called = true;
|
|
161
161
|
this._isOpen = true;
|
|
162
162
|
this._socket.on('data', (data) => {
|
|
@@ -277,7 +277,7 @@ class TcpServerPhysicalLayer extends AbstractPhysicalLayer {
|
|
|
277
277
|
return new Promise((resolve, reject) => {
|
|
278
278
|
var _a;
|
|
279
279
|
let called = false;
|
|
280
|
-
this._server.listen(Object.assign(Object.assign({}, options), { port: (_a = options.port) !== null && _a !== void 0 ? _a : 502 }), () => {
|
|
280
|
+
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 }), () => {
|
|
281
281
|
called = true;
|
|
282
282
|
this._isOpen = true;
|
|
283
283
|
this._sockets.clear();
|
|
@@ -408,7 +408,7 @@ class UdpPhysicalLayer extends AbstractPhysicalLayer {
|
|
|
408
408
|
var _a;
|
|
409
409
|
if (this.isServer) {
|
|
410
410
|
let called = false;
|
|
411
|
-
this._socket.bind(Object.assign(Object.assign({}, options), { port: (_a = options.port) !== null && _a !== void 0 ? _a : 502 }), () => {
|
|
411
|
+
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 }), () => {
|
|
412
412
|
called = true;
|
|
413
413
|
this._isOpen = true;
|
|
414
414
|
this._socket.on('close', () => {
|
|
@@ -470,8 +470,22 @@ class AbstractApplicationLayer extends EventEmitter {
|
|
|
470
470
|
}
|
|
471
471
|
|
|
472
472
|
function checkRange(value, range) {
|
|
473
|
-
if (range
|
|
474
|
-
|
|
473
|
+
if (range) {
|
|
474
|
+
if (typeof range[0] === 'number' && typeof range[1] === 'number') {
|
|
475
|
+
if (range[0] < range[1]) {
|
|
476
|
+
return (Array.isArray(value) ? value : [value]).every((n) => n >= range[0] && n <= range[1]);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
else if (range.length > 0) {
|
|
480
|
+
for (const r of range) {
|
|
481
|
+
if (r[0] < r[1]) {
|
|
482
|
+
if ((Array.isArray(value) ? value : [value]).every((n) => n >= r[0] && n <= r[1])) {
|
|
483
|
+
return true;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
return false;
|
|
488
|
+
}
|
|
475
489
|
}
|
|
476
490
|
return true;
|
|
477
491
|
}
|
|
@@ -1366,14 +1380,14 @@ class ModbusSlave extends EventEmitter {
|
|
|
1366
1380
|
});
|
|
1367
1381
|
}
|
|
1368
1382
|
handleFC1(frame, response) {
|
|
1369
|
-
var _a;
|
|
1383
|
+
var _a, _b;
|
|
1370
1384
|
if (frame.data.length === 4) {
|
|
1371
1385
|
if (this.model.readCoils) {
|
|
1372
1386
|
const bufferRx = Buffer.from(frame.data);
|
|
1373
1387
|
const address = bufferRx.readUInt16BE(0);
|
|
1374
1388
|
const length = bufferRx.readUInt16BE(2);
|
|
1375
1389
|
if (length >= 0x0001 && length <= 0x07d0) {
|
|
1376
|
-
if (checkRange([address, address + length], (_a = this.model.
|
|
1390
|
+
if (checkRange([address, address + length], (_b = (_a = this.model).getAddressRange) === null || _b === void 0 ? void 0 : _b.call(_a).coils)) {
|
|
1377
1391
|
Promise.resolve(this.model.readCoils(address, length))
|
|
1378
1392
|
.then((coils) => {
|
|
1379
1393
|
const bufferTx = Buffer.alloc(Math.ceil(length / 8));
|
|
@@ -1402,14 +1416,14 @@ class ModbusSlave extends EventEmitter {
|
|
|
1402
1416
|
}
|
|
1403
1417
|
}
|
|
1404
1418
|
handleFC2(frame, response) {
|
|
1405
|
-
var _a;
|
|
1419
|
+
var _a, _b;
|
|
1406
1420
|
if (frame.data.length === 4) {
|
|
1407
1421
|
if (this.model.readDiscreteInputs) {
|
|
1408
1422
|
const bufferRx = Buffer.from(frame.data);
|
|
1409
1423
|
const address = bufferRx.readUInt16BE(0);
|
|
1410
1424
|
const length = bufferRx.readUInt16BE(2);
|
|
1411
1425
|
if (length >= 0x0001 && length <= 0x07d0) {
|
|
1412
|
-
if (checkRange([address, address + length], (_a = this.model.
|
|
1426
|
+
if (checkRange([address, address + length], (_b = (_a = this.model).getAddressRange) === null || _b === void 0 ? void 0 : _b.call(_a).discreteInputs)) {
|
|
1413
1427
|
Promise.resolve(this.model.readDiscreteInputs(address, length))
|
|
1414
1428
|
.then((discreteInputs) => {
|
|
1415
1429
|
const bufferTx = Buffer.alloc(Math.ceil(length / 8));
|
|
@@ -1438,14 +1452,14 @@ class ModbusSlave extends EventEmitter {
|
|
|
1438
1452
|
}
|
|
1439
1453
|
}
|
|
1440
1454
|
handleFC3(frame, response) {
|
|
1441
|
-
var _a;
|
|
1455
|
+
var _a, _b;
|
|
1442
1456
|
if (frame.data.length === 4) {
|
|
1443
1457
|
if (this.model.readHoldingRegisters) {
|
|
1444
1458
|
const bufferRx = Buffer.from(frame.data);
|
|
1445
1459
|
const address = bufferRx.readUInt16BE(0);
|
|
1446
1460
|
const length = bufferRx.readUInt16BE(2);
|
|
1447
1461
|
if (length >= 0x0001 && length <= 0x007d) {
|
|
1448
|
-
if (checkRange([address, address + length], (_a = this.model.
|
|
1462
|
+
if (checkRange([address, address + length], (_b = (_a = this.model).getAddressRange) === null || _b === void 0 ? void 0 : _b.call(_a).holdingRegisters)) {
|
|
1449
1463
|
Promise.resolve(this.model.readHoldingRegisters(address, length))
|
|
1450
1464
|
.then((registers) => {
|
|
1451
1465
|
const bufferTx = Buffer.alloc(length * 2);
|
|
@@ -1472,14 +1486,14 @@ class ModbusSlave extends EventEmitter {
|
|
|
1472
1486
|
}
|
|
1473
1487
|
}
|
|
1474
1488
|
handleFC4(frame, response) {
|
|
1475
|
-
var _a;
|
|
1489
|
+
var _a, _b;
|
|
1476
1490
|
if (frame.data.length === 4) {
|
|
1477
1491
|
if (this.model.readInputRegisters) {
|
|
1478
1492
|
const bufferRx = Buffer.from(frame.data);
|
|
1479
1493
|
const address = bufferRx.readUInt16BE(0);
|
|
1480
1494
|
const length = bufferRx.readUInt16BE(2);
|
|
1481
1495
|
if (length >= 0x0001 && length <= 0x007d) {
|
|
1482
|
-
if (checkRange([address, address + length], (_a = this.model.
|
|
1496
|
+
if (checkRange([address, address + length], (_b = (_a = this.model).getAddressRange) === null || _b === void 0 ? void 0 : _b.call(_a).inputRegisters)) {
|
|
1483
1497
|
Promise.resolve(this.model.readInputRegisters(address, length))
|
|
1484
1498
|
.then((registers) => {
|
|
1485
1499
|
const bufferTx = Buffer.alloc(length * 2);
|
|
@@ -1506,14 +1520,14 @@ class ModbusSlave extends EventEmitter {
|
|
|
1506
1520
|
}
|
|
1507
1521
|
}
|
|
1508
1522
|
handleFC5(frame, response) {
|
|
1509
|
-
var _a;
|
|
1523
|
+
var _a, _b;
|
|
1510
1524
|
if (frame.data.length === 4) {
|
|
1511
1525
|
if (this.model.writeSingleCoil) {
|
|
1512
1526
|
const bufferRx = Buffer.from(frame.data);
|
|
1513
1527
|
const address = bufferRx.readUInt16BE(0);
|
|
1514
1528
|
const value = bufferRx.readUInt16BE(2);
|
|
1515
1529
|
if (value === 0x0000 || value === 0xff00) {
|
|
1516
|
-
if (checkRange(address, (_a = this.model.
|
|
1530
|
+
if (checkRange(address, (_b = (_a = this.model).getAddressRange) === null || _b === void 0 ? void 0 : _b.call(_a).coils)) {
|
|
1517
1531
|
Promise.resolve(this.model.writeSingleCoil(address, value === 0xff00))
|
|
1518
1532
|
.then(() => {
|
|
1519
1533
|
response(this.applicationLayer.encode(frame));
|
|
@@ -1536,14 +1550,14 @@ class ModbusSlave extends EventEmitter {
|
|
|
1536
1550
|
}
|
|
1537
1551
|
}
|
|
1538
1552
|
handleFC6(frame, response) {
|
|
1539
|
-
var _a;
|
|
1553
|
+
var _a, _b;
|
|
1540
1554
|
if (frame.data.length === 4) {
|
|
1541
1555
|
if (this.model.writeSingleRegister) {
|
|
1542
1556
|
const bufferRx = Buffer.from(frame.data);
|
|
1543
1557
|
const address = bufferRx.readUInt16BE(0);
|
|
1544
1558
|
const value = bufferRx.readUInt16BE(2);
|
|
1545
1559
|
if (value >= 0x0000 && value <= 0xffff) {
|
|
1546
|
-
if (checkRange(address, (_a = this.model.
|
|
1560
|
+
if (checkRange(address, (_b = (_a = this.model).getAddressRange) === null || _b === void 0 ? void 0 : _b.call(_a).holdingRegisters)) {
|
|
1547
1561
|
Promise.resolve(this.model.writeSingleRegister(address, value))
|
|
1548
1562
|
.then(() => {
|
|
1549
1563
|
response(this.applicationLayer.encode(frame));
|
|
@@ -1566,7 +1580,7 @@ class ModbusSlave extends EventEmitter {
|
|
|
1566
1580
|
}
|
|
1567
1581
|
}
|
|
1568
1582
|
handleFC15(frame, response) {
|
|
1569
|
-
var _a;
|
|
1583
|
+
var _a, _b;
|
|
1570
1584
|
if (frame.data.length > 5 && frame.data.length === 5 + frame.data[4]) {
|
|
1571
1585
|
if (this.model.writeMultipleCoils || this.model.writeSingleCoil) {
|
|
1572
1586
|
const bufferRx = Buffer.from(frame.data);
|
|
@@ -1574,7 +1588,7 @@ class ModbusSlave extends EventEmitter {
|
|
|
1574
1588
|
const length = bufferRx.readUInt16BE(2);
|
|
1575
1589
|
const byteCount = bufferRx[4];
|
|
1576
1590
|
if (length >= 0x0001 && length <= 0x07b0 && byteCount === Math.ceil(length / 8)) {
|
|
1577
|
-
if (checkRange([address, address + length], (_a = this.model.
|
|
1591
|
+
if (checkRange([address, address + length], (_b = (_a = this.model).getAddressRange) === null || _b === void 0 ? void 0 : _b.call(_a).coils)) {
|
|
1578
1592
|
const value = Array.from({ length }).map((_, index) => (bufferRx[5 + ~~(index / 8)] & (1 << index % 8)) > 0);
|
|
1579
1593
|
Promise.resolve(this.model.writeMultipleCoils
|
|
1580
1594
|
? this.model.writeMultipleCoils(address, value)
|
|
@@ -1600,7 +1614,7 @@ class ModbusSlave extends EventEmitter {
|
|
|
1600
1614
|
}
|
|
1601
1615
|
}
|
|
1602
1616
|
handleFC16(frame, response) {
|
|
1603
|
-
var _a;
|
|
1617
|
+
var _a, _b;
|
|
1604
1618
|
if (frame.data.length > 5 && frame.data.length === 5 + frame.data[4]) {
|
|
1605
1619
|
if (this.model.writeMultipleRegisters || this.model.writeSingleRegister) {
|
|
1606
1620
|
const bufferRx = Buffer.from(frame.data);
|
|
@@ -1608,7 +1622,7 @@ class ModbusSlave extends EventEmitter {
|
|
|
1608
1622
|
const length = bufferRx.readUInt16BE(2);
|
|
1609
1623
|
const byteCount = bufferRx[4];
|
|
1610
1624
|
if (length >= 0x0001 && length <= 0x007b && byteCount === length * 2) {
|
|
1611
|
-
if (checkRange([address, address + length], (_a = this.model.
|
|
1625
|
+
if (checkRange([address, address + length], (_b = (_a = this.model).getAddressRange) === null || _b === void 0 ? void 0 : _b.call(_a).holdingRegisters)) {
|
|
1612
1626
|
const value = Array.from({ length }).map((_, index) => bufferRx.readUInt16BE(5 + index * 2));
|
|
1613
1627
|
Promise.resolve(this.model.writeMultipleRegisters
|
|
1614
1628
|
? this.model.writeMultipleRegisters(address, value)
|
|
@@ -1650,14 +1664,14 @@ class ModbusSlave extends EventEmitter {
|
|
|
1650
1664
|
}
|
|
1651
1665
|
}
|
|
1652
1666
|
handleFC22(frame, response) {
|
|
1653
|
-
var _a;
|
|
1667
|
+
var _a, _b;
|
|
1654
1668
|
if (frame.data.length === 6) {
|
|
1655
1669
|
if (this.model.maskWriteRegister || (this.model.readHoldingRegisters && this.model.writeSingleRegister)) {
|
|
1656
1670
|
const bufferRx = Buffer.from(frame.data);
|
|
1657
1671
|
const address = bufferRx.readUInt16BE(0);
|
|
1658
1672
|
const andMask = bufferRx.readUInt16BE(2);
|
|
1659
1673
|
const orMask = bufferRx.readUInt16BE(4);
|
|
1660
|
-
if (checkRange(address, (_a = this.model.
|
|
1674
|
+
if (checkRange(address, (_b = (_a = this.model).getAddressRange) === null || _b === void 0 ? void 0 : _b.call(_a).holdingRegisters)) {
|
|
1661
1675
|
Promise.resolve(this.model.maskWriteRegister
|
|
1662
1676
|
? this.model.maskWriteRegister(address, andMask, orMask)
|
|
1663
1677
|
: Promise.resolve(this.model.readHoldingRegisters(address, 1)).then(([value]) => {
|
|
@@ -1680,7 +1694,7 @@ class ModbusSlave extends EventEmitter {
|
|
|
1680
1694
|
}
|
|
1681
1695
|
}
|
|
1682
1696
|
handleFC23(frame, response) {
|
|
1683
|
-
var _a;
|
|
1697
|
+
var _a, _b;
|
|
1684
1698
|
if (frame.data.length > 9 && frame.data.length === 9 + frame.data[8]) {
|
|
1685
1699
|
if (this.model.readHoldingRegisters && (this.model.writeMultipleRegisters || this.model.writeSingleRegister)) {
|
|
1686
1700
|
const bufferRx = Buffer.from(frame.data);
|
|
@@ -1698,7 +1712,7 @@ class ModbusSlave extends EventEmitter {
|
|
|
1698
1712
|
length.write >= 0x0001 &&
|
|
1699
1713
|
length.write <= 0x0079 &&
|
|
1700
1714
|
byteCount === length.write * 2) {
|
|
1701
|
-
if (checkRange([address.read, address.read + length.read, address.write, address.write + length.write], (_a = this.model.
|
|
1715
|
+
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)) {
|
|
1702
1716
|
const value = Array.from({ length: length.write }).map((_, index) => bufferRx.readUInt16BE(9 + index * 2));
|
|
1703
1717
|
Promise.resolve(this.model.writeMultipleRegisters
|
|
1704
1718
|
? this.model.writeMultipleRegisters(address.write, value)
|
|
@@ -8,7 +8,7 @@ export declare class TcpClientPhysicalLayer extends AbstractPhysicalLayer {
|
|
|
8
8
|
get isOpen(): boolean;
|
|
9
9
|
get destroyed(): boolean;
|
|
10
10
|
constructor(options?: SocketConstructorOpts);
|
|
11
|
-
open(options
|
|
11
|
+
open(options?: SocketConnectOpts): Promise<void>;
|
|
12
12
|
write(data: Buffer): Promise<void>;
|
|
13
13
|
close(): Promise<void>;
|
|
14
14
|
destroy(): Promise<void>;
|
|
@@ -9,7 +9,7 @@ export declare class TcpServerPhysicalLayer extends AbstractPhysicalLayer {
|
|
|
9
9
|
get isOpen(): boolean;
|
|
10
10
|
get destroyed(): boolean;
|
|
11
11
|
constructor(options?: NetConnectOpts);
|
|
12
|
-
open(options
|
|
12
|
+
open(options?: ListenOptions): Promise<void>;
|
|
13
13
|
write(data: Buffer): Promise<void>;
|
|
14
14
|
close(): Promise<void>;
|
|
15
15
|
destroy(): Promise<void>;
|
|
@@ -20,7 +20,7 @@ export declare class UdpPhysicalLayer extends AbstractPhysicalLayer {
|
|
|
20
20
|
port?: number;
|
|
21
21
|
address?: string;
|
|
22
22
|
});
|
|
23
|
-
open(options
|
|
23
|
+
open(options?: BindOptions): Promise<void>;
|
|
24
24
|
write(data: Buffer): Promise<void>;
|
|
25
25
|
close(): Promise<void>;
|
|
26
26
|
destroy(): Promise<void>;
|
|
@@ -33,11 +33,11 @@ export interface ModbusSlaveModel {
|
|
|
33
33
|
readDeviceIdentification?: FConvertPromise<() => {
|
|
34
34
|
[index: number]: string;
|
|
35
35
|
}>;
|
|
36
|
-
|
|
37
|
-
discreteInputs?: [number, number];
|
|
38
|
-
coils?: [number, number];
|
|
39
|
-
inputRegisters?: [number, number];
|
|
40
|
-
holdingRegisters?: [number, number];
|
|
36
|
+
getAddressRange?: () => {
|
|
37
|
+
discreteInputs?: [number, number] | [number, number][];
|
|
38
|
+
coils?: [number, number] | [number, number][];
|
|
39
|
+
inputRegisters?: [number, number] | [number, number][];
|
|
40
|
+
holdingRegisters?: [number, number] | [number, number][];
|
|
41
41
|
};
|
|
42
42
|
}
|
|
43
43
|
interface ModbusSlaveEvents {
|
|
@@ -1,2 +1 @@
|
|
|
1
|
-
export declare function checkRange(value: number, range?: [number, number]): boolean;
|
|
2
|
-
export declare function checkRange(values: number[], range?: [number, number]): boolean;
|
|
1
|
+
export declare function checkRange(value: number | number[], range?: [number, number] | [number, number][]): boolean;
|