njs-modbus 2.0.0 → 2.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/README.md +2 -1
- package/dist/index.cjs +122 -23
- package/dist/index.d.ts +8 -2
- package/dist/index.mjs +122 -23
- package/dist/src/layers/application/ascii-application-layer.d.ts +1 -0
- package/dist/src/layers/application/rtu-application-layer.d.ts +1 -0
- package/dist/src/layers/application/tcp-application-layer.d.ts +1 -0
- package/dist/src/master/master.d.ts +2 -1
- package/dist/src/slave/slave.d.ts +2 -0
- package/dist/src/types.d.ts +1 -1
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -8,6 +8,7 @@ A pure JavaScript implementation of MODBUS for NodeJS.
|
|
|
8
8
|
[](http://www.npm-stats.com/~packages/njs-modbus)
|
|
9
9
|
[](https://www.npmjs.com/package/njs-modbus)
|
|
10
10
|
[](https://bundlephobia.com/package/njs-modbus)
|
|
11
|
+
[](https://github.com/xiejay97/njs-modbus/actions)
|
|
11
12
|
<!-- prettier-ignore-end -->
|
|
12
13
|
|
|
13
14
|
</div>
|
|
@@ -188,7 +189,7 @@ If you broadcast over serial (RTU or ASCII), per Modbus over Serial Line V1.02
|
|
|
188
189
|
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
189
190
|
|
|
190
191
|
await master.writeSingleRegister(0, 0x0000, 0x1234); // broadcast
|
|
191
|
-
await sleep(100);
|
|
192
|
+
await sleep(100); // turnaround — tune per devices
|
|
192
193
|
await master.writeSingleRegister(1, 0x0000, 0x5678); // unicast to next slave
|
|
193
194
|
```
|
|
194
195
|
|
package/dist/index.cjs
CHANGED
|
@@ -423,6 +423,9 @@ class SerialPhysicalLayer extends AbstractPhysicalLayer {
|
|
|
423
423
|
});
|
|
424
424
|
}
|
|
425
425
|
destroy() {
|
|
426
|
+
if (this._destroyed) {
|
|
427
|
+
return Promise.resolve();
|
|
428
|
+
}
|
|
426
429
|
this._destroyed = true;
|
|
427
430
|
return this.close().then(() => {
|
|
428
431
|
this.removeAllListeners();
|
|
@@ -562,6 +565,9 @@ class TcpClientPhysicalLayer extends AbstractPhysicalLayer {
|
|
|
562
565
|
});
|
|
563
566
|
}
|
|
564
567
|
destroy() {
|
|
568
|
+
if (this._destroyed) {
|
|
569
|
+
return Promise.resolve();
|
|
570
|
+
}
|
|
565
571
|
this._destroyed = true;
|
|
566
572
|
return this.close().then(() => {
|
|
567
573
|
this.removeAllListeners();
|
|
@@ -711,10 +717,18 @@ class TcpServerPhysicalLayer extends AbstractPhysicalLayer {
|
|
|
711
717
|
for (const [socket] of this._connections) {
|
|
712
718
|
socket.destroy();
|
|
713
719
|
}
|
|
714
|
-
|
|
720
|
+
try {
|
|
721
|
+
server.close();
|
|
722
|
+
}
|
|
723
|
+
catch (_a) {
|
|
724
|
+
resolve();
|
|
725
|
+
}
|
|
715
726
|
});
|
|
716
727
|
}
|
|
717
728
|
destroy() {
|
|
729
|
+
if (this._destroyed) {
|
|
730
|
+
return Promise.resolve();
|
|
731
|
+
}
|
|
718
732
|
this._destroyed = true;
|
|
719
733
|
return this.close().then(() => {
|
|
720
734
|
this.removeAllListeners();
|
|
@@ -945,6 +959,9 @@ class UdpPhysicalLayer extends AbstractPhysicalLayer {
|
|
|
945
959
|
});
|
|
946
960
|
}
|
|
947
961
|
destroy() {
|
|
962
|
+
if (this._destroyed) {
|
|
963
|
+
return Promise.resolve();
|
|
964
|
+
}
|
|
948
965
|
this._destroyed = true;
|
|
949
966
|
return this.close().then(() => {
|
|
950
967
|
this.removeAllListeners();
|
|
@@ -1024,6 +1041,12 @@ class RtuApplicationLayer extends AbstractApplicationLayer {
|
|
|
1024
1041
|
writable: true,
|
|
1025
1042
|
value: void 0
|
|
1026
1043
|
});
|
|
1044
|
+
Object.defineProperty(this, "_destroyed", {
|
|
1045
|
+
enumerable: true,
|
|
1046
|
+
configurable: true,
|
|
1047
|
+
writable: true,
|
|
1048
|
+
value: false
|
|
1049
|
+
});
|
|
1027
1050
|
const { intervalBetweenFrames, interCharTimeout } = options;
|
|
1028
1051
|
let threePointFiveT = 0;
|
|
1029
1052
|
let onePointFiveT = 0;
|
|
@@ -1274,14 +1297,20 @@ class RtuApplicationLayer extends AbstractApplicationLayer {
|
|
|
1274
1297
|
return buffer;
|
|
1275
1298
|
}
|
|
1276
1299
|
destroy() {
|
|
1300
|
+
if (this._destroyed) {
|
|
1301
|
+
return;
|
|
1302
|
+
}
|
|
1303
|
+
this._destroyed = true;
|
|
1277
1304
|
this.removeAllListeners();
|
|
1278
1305
|
for (const removeListener of this._removeAllListeners) {
|
|
1279
1306
|
removeListener();
|
|
1280
1307
|
}
|
|
1308
|
+
this._removeAllListeners = [];
|
|
1281
1309
|
for (const state of this._states.values()) {
|
|
1282
1310
|
this.clearStateTimers(state);
|
|
1283
1311
|
}
|
|
1284
1312
|
this._states.clear();
|
|
1313
|
+
this._customFunctionCodes.clear();
|
|
1285
1314
|
}
|
|
1286
1315
|
}
|
|
1287
1316
|
|
|
@@ -1334,6 +1363,12 @@ class AsciiApplicationLayer extends AbstractApplicationLayer {
|
|
|
1334
1363
|
writable: true,
|
|
1335
1364
|
value: []
|
|
1336
1365
|
});
|
|
1366
|
+
Object.defineProperty(this, "_destroyed", {
|
|
1367
|
+
enumerable: true,
|
|
1368
|
+
configurable: true,
|
|
1369
|
+
writable: true,
|
|
1370
|
+
value: false
|
|
1371
|
+
});
|
|
1337
1372
|
this.lenientHex = (_a = options.lenientHex) !== null && _a !== void 0 ? _a : false;
|
|
1338
1373
|
const lenientHex = this.lenientHex;
|
|
1339
1374
|
const isHexChar = (value) => {
|
|
@@ -1471,10 +1506,15 @@ class AsciiApplicationLayer extends AbstractApplicationLayer {
|
|
|
1471
1506
|
return out;
|
|
1472
1507
|
}
|
|
1473
1508
|
destroy() {
|
|
1509
|
+
if (this._destroyed) {
|
|
1510
|
+
return;
|
|
1511
|
+
}
|
|
1512
|
+
this._destroyed = true;
|
|
1474
1513
|
this.removeAllListeners();
|
|
1475
1514
|
for (const removeListener of this._removeAllListeners) {
|
|
1476
1515
|
removeListener();
|
|
1477
1516
|
}
|
|
1517
|
+
this._removeAllListeners = [];
|
|
1478
1518
|
this._states.clear();
|
|
1479
1519
|
}
|
|
1480
1520
|
}
|
|
@@ -1508,6 +1548,12 @@ class TcpApplicationLayer extends AbstractApplicationLayer {
|
|
|
1508
1548
|
writable: true,
|
|
1509
1549
|
value: []
|
|
1510
1550
|
});
|
|
1551
|
+
Object.defineProperty(this, "_destroyed", {
|
|
1552
|
+
enumerable: true,
|
|
1553
|
+
configurable: true,
|
|
1554
|
+
writable: true,
|
|
1555
|
+
value: false
|
|
1556
|
+
});
|
|
1511
1557
|
const handleData = (data, response, connection) => {
|
|
1512
1558
|
var _a;
|
|
1513
1559
|
let buffer = (_a = this._buffers.get(connection.id)) !== null && _a !== void 0 ? _a : EMPTY_BUFFER;
|
|
@@ -1590,10 +1636,15 @@ class TcpApplicationLayer extends AbstractApplicationLayer {
|
|
|
1590
1636
|
return buffer;
|
|
1591
1637
|
}
|
|
1592
1638
|
destroy() {
|
|
1639
|
+
if (this._destroyed) {
|
|
1640
|
+
return;
|
|
1641
|
+
}
|
|
1642
|
+
this._destroyed = true;
|
|
1593
1643
|
this.removeAllListeners();
|
|
1594
1644
|
for (const removeListener of this._removeAllListeners) {
|
|
1595
1645
|
removeListener();
|
|
1596
1646
|
}
|
|
1647
|
+
this._removeAllListeners = [];
|
|
1597
1648
|
this._buffers.clear();
|
|
1598
1649
|
}
|
|
1599
1650
|
}
|
|
@@ -1704,7 +1755,7 @@ class ModbusMaster extends EventEmitter {
|
|
|
1704
1755
|
return this.physicalLayer.isOpen;
|
|
1705
1756
|
}
|
|
1706
1757
|
get destroyed() {
|
|
1707
|
-
return this.physicalLayer.destroyed;
|
|
1758
|
+
return this._cleanLevel === 'destroy' || this.physicalLayer.destroyed;
|
|
1708
1759
|
}
|
|
1709
1760
|
constructor(applicationLayer, physicalLayer, options = {}) {
|
|
1710
1761
|
var _a, _b;
|
|
@@ -1745,11 +1796,11 @@ class ModbusMaster extends EventEmitter {
|
|
|
1745
1796
|
writable: true,
|
|
1746
1797
|
value: 1
|
|
1747
1798
|
});
|
|
1748
|
-
Object.defineProperty(this, "
|
|
1799
|
+
Object.defineProperty(this, "_cleanLevel", {
|
|
1749
1800
|
enumerable: true,
|
|
1750
1801
|
configurable: true,
|
|
1751
1802
|
writable: true,
|
|
1752
|
-
value:
|
|
1803
|
+
value: 'none'
|
|
1753
1804
|
});
|
|
1754
1805
|
Object.defineProperty(this, "timeout", {
|
|
1755
1806
|
enumerable: true,
|
|
@@ -1903,7 +1954,7 @@ class ModbusMaster extends EventEmitter {
|
|
|
1903
1954
|
}
|
|
1904
1955
|
_exchange(adu, preCheck, timeout, broadcast) {
|
|
1905
1956
|
return new Promise((resolve, reject) => {
|
|
1906
|
-
if (this.
|
|
1957
|
+
if (this._cleanLevel !== 'none') {
|
|
1907
1958
|
reject(new ModbusError(ModbusErrorCode.MASTER_CLOSED, 'Master closed'));
|
|
1908
1959
|
return;
|
|
1909
1960
|
}
|
|
@@ -2056,7 +2107,7 @@ class ModbusMaster extends EventEmitter {
|
|
|
2056
2107
|
if (frame) {
|
|
2057
2108
|
const runStatusIndex = 1 + serverIdLength;
|
|
2058
2109
|
return Object.assign(Object.assign({}, frame), { data: {
|
|
2059
|
-
serverId:
|
|
2110
|
+
serverId: Array.from(frame.data.subarray(1, runStatusIndex)),
|
|
2060
2111
|
runIndicatorStatus: frame.data[runStatusIndex] === 0xff,
|
|
2061
2112
|
additionalData: Array.from(frame.data.subarray(runStatusIndex + 1)),
|
|
2062
2113
|
} });
|
|
@@ -2164,26 +2215,42 @@ class ModbusMaster extends EventEmitter {
|
|
|
2164
2215
|
}
|
|
2165
2216
|
});
|
|
2166
2217
|
}
|
|
2218
|
+
_clean(level) {
|
|
2219
|
+
if (this._cleanLevel === 'destroy') {
|
|
2220
|
+
return;
|
|
2221
|
+
}
|
|
2222
|
+
if (this._cleanLevel === 'close' && level === 'close') {
|
|
2223
|
+
return;
|
|
2224
|
+
}
|
|
2225
|
+
const errorCode = level === 'destroy' ? ModbusErrorCode.MASTER_DESTROYED : ModbusErrorCode.MASTER_CLOSED;
|
|
2226
|
+
const message = level === 'destroy' ? 'Master destroyed' : 'Master closed';
|
|
2227
|
+
const queued = this._queue.splice(0);
|
|
2228
|
+
for (const item of queued) {
|
|
2229
|
+
item.cancel(new ModbusError(errorCode, message));
|
|
2230
|
+
}
|
|
2231
|
+
this._masterSession.stopAll(new ModbusError(errorCode, message));
|
|
2232
|
+
this._cleanLevel = level;
|
|
2233
|
+
}
|
|
2167
2234
|
open(...args) {
|
|
2168
|
-
this.
|
|
2235
|
+
if (this._cleanLevel === 'destroy') {
|
|
2236
|
+
return Promise.reject(new ModbusError(ModbusErrorCode.PORT_DESTROYED, 'Master is destroyed'));
|
|
2237
|
+
}
|
|
2238
|
+
this._cleanLevel = 'none';
|
|
2239
|
+
this._nextTid = 1;
|
|
2169
2240
|
return this.physicalLayer.open(...args);
|
|
2170
2241
|
}
|
|
2171
2242
|
close() {
|
|
2172
|
-
this.
|
|
2173
|
-
|
|
2174
|
-
for (const item of queued) {
|
|
2175
|
-
item.cancel(new ModbusError(ModbusErrorCode.MASTER_CLOSED, 'Master closed'));
|
|
2243
|
+
if (this._cleanLevel === 'destroy') {
|
|
2244
|
+
return Promise.resolve();
|
|
2176
2245
|
}
|
|
2177
|
-
this.
|
|
2246
|
+
this._clean('close');
|
|
2178
2247
|
return this.physicalLayer.close();
|
|
2179
2248
|
}
|
|
2180
2249
|
destroy() {
|
|
2181
|
-
this.
|
|
2182
|
-
|
|
2183
|
-
for (const item of queued) {
|
|
2184
|
-
item.cancel(new ModbusError(ModbusErrorCode.MASTER_DESTROYED, 'Master destroyed'));
|
|
2250
|
+
if (this._cleanLevel === 'destroy') {
|
|
2251
|
+
return Promise.resolve();
|
|
2185
2252
|
}
|
|
2186
|
-
this.
|
|
2253
|
+
this._clean('destroy');
|
|
2187
2254
|
this.removeAllListeners();
|
|
2188
2255
|
this.applicationLayer.destroy();
|
|
2189
2256
|
return this.physicalLayer.destroy();
|
|
@@ -2195,7 +2262,7 @@ class ModbusSlave extends EventEmitter {
|
|
|
2195
2262
|
return this.physicalLayer.isOpen;
|
|
2196
2263
|
}
|
|
2197
2264
|
get destroyed() {
|
|
2198
|
-
return this.physicalLayer.destroyed;
|
|
2265
|
+
return this._cleanLevel === 'destroy' || this.physicalLayer.destroyed;
|
|
2199
2266
|
}
|
|
2200
2267
|
constructor(applicationLayer, physicalLayer, options = {}) {
|
|
2201
2268
|
var _a;
|
|
@@ -2242,6 +2309,12 @@ class ModbusSlave extends EventEmitter {
|
|
|
2242
2309
|
writable: true,
|
|
2243
2310
|
value: new Map()
|
|
2244
2311
|
});
|
|
2312
|
+
Object.defineProperty(this, "_cleanLevel", {
|
|
2313
|
+
enumerable: true,
|
|
2314
|
+
configurable: true,
|
|
2315
|
+
writable: true,
|
|
2316
|
+
value: 'none'
|
|
2317
|
+
});
|
|
2245
2318
|
this.concurrent = (_a = options.concurrent) !== null && _a !== void 0 ? _a : false;
|
|
2246
2319
|
if (this.concurrent && this.applicationLayer.PROTOCOL !== 'TCP') {
|
|
2247
2320
|
throw new ModbusError(ModbusErrorCode.CONCURRENT_NOT_TCP, 'concurrent mode requires a Modbus TCP application layer');
|
|
@@ -2271,14 +2344,14 @@ class ModbusSlave extends EventEmitter {
|
|
|
2271
2344
|
if (!q) {
|
|
2272
2345
|
return;
|
|
2273
2346
|
}
|
|
2274
|
-
q.items
|
|
2347
|
+
q.items = [];
|
|
2275
2348
|
if (!q.processing) {
|
|
2276
2349
|
this._queues.delete(connection.id);
|
|
2277
2350
|
}
|
|
2278
2351
|
});
|
|
2279
2352
|
physicalLayer.on('close', () => {
|
|
2280
2353
|
for (const q of this._queues.values()) {
|
|
2281
|
-
q.items
|
|
2354
|
+
q.items = [];
|
|
2282
2355
|
}
|
|
2283
2356
|
this._queues.clear();
|
|
2284
2357
|
this.emit('close');
|
|
@@ -2567,8 +2640,8 @@ class ModbusSlave extends EventEmitter {
|
|
|
2567
2640
|
return;
|
|
2568
2641
|
}
|
|
2569
2642
|
try {
|
|
2570
|
-
const { serverId = (_a = model.unit) !== null && _a !== void 0 ? _a : 1, runIndicatorStatus = true, additionalData = [] } = yield model.reportServerId();
|
|
2571
|
-
const serverIdBytes =
|
|
2643
|
+
const { serverId = [(_a = model.unit) !== null && _a !== void 0 ? _a : 1], runIndicatorStatus = true, additionalData = [] } = yield model.reportServerId();
|
|
2644
|
+
const serverIdBytes = serverId;
|
|
2572
2645
|
const byteCount = serverIdBytes.length + 1 + additionalData.length;
|
|
2573
2646
|
if (byteCount > 255) {
|
|
2574
2647
|
yield this.responseError(frame, response, getErrorByCode(exports.ErrorCode.SERVER_DEVICE_FAILURE));
|
|
@@ -2974,14 +3047,40 @@ class ModbusSlave extends EventEmitter {
|
|
|
2974
3047
|
this._customFunctionCodes.delete(fc);
|
|
2975
3048
|
this.applicationLayer.removeCustomFunctionCode(fc);
|
|
2976
3049
|
}
|
|
3050
|
+
_clean(level) {
|
|
3051
|
+
if (this._cleanLevel === 'destroy') {
|
|
3052
|
+
return;
|
|
3053
|
+
}
|
|
3054
|
+
if (this._cleanLevel === 'close' && level === 'close') {
|
|
3055
|
+
return;
|
|
3056
|
+
}
|
|
3057
|
+
for (const q of this._queues.values()) {
|
|
3058
|
+
q.items = [];
|
|
3059
|
+
}
|
|
3060
|
+
this._queues.clear();
|
|
3061
|
+
this._locks.clear();
|
|
3062
|
+
if (level === 'destroy') {
|
|
3063
|
+
this._customFunctionCodes.clear();
|
|
3064
|
+
this.models.clear();
|
|
3065
|
+
}
|
|
3066
|
+
this._cleanLevel = level;
|
|
3067
|
+
}
|
|
2977
3068
|
open(...args) {
|
|
3069
|
+
this._cleanLevel = 'none';
|
|
2978
3070
|
return this.physicalLayer.open(...args);
|
|
2979
3071
|
}
|
|
2980
3072
|
close() {
|
|
3073
|
+
if (this._cleanLevel === 'destroy') {
|
|
3074
|
+
return Promise.resolve();
|
|
3075
|
+
}
|
|
3076
|
+
this._clean('close');
|
|
2981
3077
|
return this.physicalLayer.close();
|
|
2982
3078
|
}
|
|
2983
3079
|
destroy() {
|
|
2984
|
-
this.
|
|
3080
|
+
if (this._cleanLevel === 'destroy') {
|
|
3081
|
+
return Promise.resolve();
|
|
3082
|
+
}
|
|
3083
|
+
this._clean('destroy');
|
|
2985
3084
|
this.removeAllListeners();
|
|
2986
3085
|
this.applicationLayer.destroy();
|
|
2987
3086
|
return this.physicalLayer.destroy();
|
package/dist/index.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ interface ApplicationDataUnit {
|
|
|
12
12
|
}
|
|
13
13
|
interface ServerId {
|
|
14
14
|
/** Server ID; may be 1 byte (number) or multi-byte (number[]) per device spec. */
|
|
15
|
-
serverId?: number
|
|
15
|
+
serverId?: number[];
|
|
16
16
|
runIndicatorStatus?: boolean;
|
|
17
17
|
additionalData?: number[];
|
|
18
18
|
}
|
|
@@ -313,6 +313,7 @@ declare class AsciiApplicationLayer extends AbstractApplicationLayer {
|
|
|
313
313
|
readonly lenientHex: boolean;
|
|
314
314
|
private _states;
|
|
315
315
|
private _removeAllListeners;
|
|
316
|
+
private _destroyed;
|
|
316
317
|
constructor(physicalLayer: SerialPhysicalLayer | TcpServerPhysicalLayer | TcpClientPhysicalLayer | UdpPhysicalLayer, options?: AsciiApplicationLayerOptions);
|
|
317
318
|
private getState;
|
|
318
319
|
flush(): void;
|
|
@@ -362,6 +363,7 @@ declare class RtuApplicationLayer extends AbstractApplicationLayer {
|
|
|
362
363
|
private _removeAllListeners;
|
|
363
364
|
private _threePointFiveT;
|
|
364
365
|
private _onePointFiveT;
|
|
366
|
+
private _destroyed;
|
|
365
367
|
constructor(physicalLayer: SerialPhysicalLayer | TcpServerPhysicalLayer | TcpClientPhysicalLayer | UdpPhysicalLayer, options?: RtuApplicationLayerOptions);
|
|
366
368
|
private getState;
|
|
367
369
|
private clearStateTimers;
|
|
@@ -383,6 +385,7 @@ declare class TcpApplicationLayer extends AbstractApplicationLayer {
|
|
|
383
385
|
private _transactionId;
|
|
384
386
|
private _buffers;
|
|
385
387
|
private _removeAllListeners;
|
|
388
|
+
private _destroyed;
|
|
386
389
|
constructor(physicalLayer: TcpServerPhysicalLayer | TcpClientPhysicalLayer | UdpPhysicalLayer);
|
|
387
390
|
private tryExtract;
|
|
388
391
|
private processFrame;
|
|
@@ -418,7 +421,7 @@ declare class ModbusMaster<A extends AbstractApplicationLayer, P extends Abstrac
|
|
|
418
421
|
private _queue;
|
|
419
422
|
private _draining;
|
|
420
423
|
private _nextTid;
|
|
421
|
-
private
|
|
424
|
+
private _cleanLevel;
|
|
422
425
|
timeout: number;
|
|
423
426
|
readonly concurrent: boolean;
|
|
424
427
|
get isOpen(): boolean;
|
|
@@ -484,6 +487,7 @@ declare class ModbusMaster<A extends AbstractApplicationLayer, P extends Abstrac
|
|
|
484
487
|
removeCustomFunctionCode(fc: number): void;
|
|
485
488
|
sendCustomFC(unit: 0, fc: number, data: Buffer | number[], timeout?: number): Promise<void>;
|
|
486
489
|
sendCustomFC(unit: number, fc: number, data: Buffer | number[], timeout?: number): Promise<Buffer>;
|
|
490
|
+
private _clean;
|
|
487
491
|
open(...args: Parameters<P['open']>): Promise<void>;
|
|
488
492
|
close(): Promise<void>;
|
|
489
493
|
destroy(): Promise<void>;
|
|
@@ -563,6 +567,7 @@ declare class ModbusSlave<A extends AbstractApplicationLayer, P extends Abstract
|
|
|
563
567
|
private _queues;
|
|
564
568
|
private _customFunctionCodes;
|
|
565
569
|
private _locks;
|
|
570
|
+
private _cleanLevel;
|
|
566
571
|
get isOpen(): boolean;
|
|
567
572
|
get destroyed(): boolean;
|
|
568
573
|
constructor(applicationLayer: A, physicalLayer: P, options?: ModbusSlaveOptions);
|
|
@@ -588,6 +593,7 @@ declare class ModbusSlave<A extends AbstractApplicationLayer, P extends Abstract
|
|
|
588
593
|
remove(unit: number): void;
|
|
589
594
|
addCustomFunctionCode(cfc: CustomFunctionCode): void;
|
|
590
595
|
removeCustomFunctionCode(fc: number): void;
|
|
596
|
+
private _clean;
|
|
591
597
|
open(...args: Parameters<P['open']>): Promise<void>;
|
|
592
598
|
close(): Promise<void>;
|
|
593
599
|
destroy(): Promise<void>;
|
package/dist/index.mjs
CHANGED
|
@@ -421,6 +421,9 @@ class SerialPhysicalLayer extends AbstractPhysicalLayer {
|
|
|
421
421
|
});
|
|
422
422
|
}
|
|
423
423
|
destroy() {
|
|
424
|
+
if (this._destroyed) {
|
|
425
|
+
return Promise.resolve();
|
|
426
|
+
}
|
|
424
427
|
this._destroyed = true;
|
|
425
428
|
return this.close().then(() => {
|
|
426
429
|
this.removeAllListeners();
|
|
@@ -560,6 +563,9 @@ class TcpClientPhysicalLayer extends AbstractPhysicalLayer {
|
|
|
560
563
|
});
|
|
561
564
|
}
|
|
562
565
|
destroy() {
|
|
566
|
+
if (this._destroyed) {
|
|
567
|
+
return Promise.resolve();
|
|
568
|
+
}
|
|
563
569
|
this._destroyed = true;
|
|
564
570
|
return this.close().then(() => {
|
|
565
571
|
this.removeAllListeners();
|
|
@@ -709,10 +715,18 @@ class TcpServerPhysicalLayer extends AbstractPhysicalLayer {
|
|
|
709
715
|
for (const [socket] of this._connections) {
|
|
710
716
|
socket.destroy();
|
|
711
717
|
}
|
|
712
|
-
|
|
718
|
+
try {
|
|
719
|
+
server.close();
|
|
720
|
+
}
|
|
721
|
+
catch (_a) {
|
|
722
|
+
resolve();
|
|
723
|
+
}
|
|
713
724
|
});
|
|
714
725
|
}
|
|
715
726
|
destroy() {
|
|
727
|
+
if (this._destroyed) {
|
|
728
|
+
return Promise.resolve();
|
|
729
|
+
}
|
|
716
730
|
this._destroyed = true;
|
|
717
731
|
return this.close().then(() => {
|
|
718
732
|
this.removeAllListeners();
|
|
@@ -943,6 +957,9 @@ class UdpPhysicalLayer extends AbstractPhysicalLayer {
|
|
|
943
957
|
});
|
|
944
958
|
}
|
|
945
959
|
destroy() {
|
|
960
|
+
if (this._destroyed) {
|
|
961
|
+
return Promise.resolve();
|
|
962
|
+
}
|
|
946
963
|
this._destroyed = true;
|
|
947
964
|
return this.close().then(() => {
|
|
948
965
|
this.removeAllListeners();
|
|
@@ -1022,6 +1039,12 @@ class RtuApplicationLayer extends AbstractApplicationLayer {
|
|
|
1022
1039
|
writable: true,
|
|
1023
1040
|
value: void 0
|
|
1024
1041
|
});
|
|
1042
|
+
Object.defineProperty(this, "_destroyed", {
|
|
1043
|
+
enumerable: true,
|
|
1044
|
+
configurable: true,
|
|
1045
|
+
writable: true,
|
|
1046
|
+
value: false
|
|
1047
|
+
});
|
|
1025
1048
|
const { intervalBetweenFrames, interCharTimeout } = options;
|
|
1026
1049
|
let threePointFiveT = 0;
|
|
1027
1050
|
let onePointFiveT = 0;
|
|
@@ -1272,14 +1295,20 @@ class RtuApplicationLayer extends AbstractApplicationLayer {
|
|
|
1272
1295
|
return buffer;
|
|
1273
1296
|
}
|
|
1274
1297
|
destroy() {
|
|
1298
|
+
if (this._destroyed) {
|
|
1299
|
+
return;
|
|
1300
|
+
}
|
|
1301
|
+
this._destroyed = true;
|
|
1275
1302
|
this.removeAllListeners();
|
|
1276
1303
|
for (const removeListener of this._removeAllListeners) {
|
|
1277
1304
|
removeListener();
|
|
1278
1305
|
}
|
|
1306
|
+
this._removeAllListeners = [];
|
|
1279
1307
|
for (const state of this._states.values()) {
|
|
1280
1308
|
this.clearStateTimers(state);
|
|
1281
1309
|
}
|
|
1282
1310
|
this._states.clear();
|
|
1311
|
+
this._customFunctionCodes.clear();
|
|
1283
1312
|
}
|
|
1284
1313
|
}
|
|
1285
1314
|
|
|
@@ -1332,6 +1361,12 @@ class AsciiApplicationLayer extends AbstractApplicationLayer {
|
|
|
1332
1361
|
writable: true,
|
|
1333
1362
|
value: []
|
|
1334
1363
|
});
|
|
1364
|
+
Object.defineProperty(this, "_destroyed", {
|
|
1365
|
+
enumerable: true,
|
|
1366
|
+
configurable: true,
|
|
1367
|
+
writable: true,
|
|
1368
|
+
value: false
|
|
1369
|
+
});
|
|
1335
1370
|
this.lenientHex = (_a = options.lenientHex) !== null && _a !== void 0 ? _a : false;
|
|
1336
1371
|
const lenientHex = this.lenientHex;
|
|
1337
1372
|
const isHexChar = (value) => {
|
|
@@ -1469,10 +1504,15 @@ class AsciiApplicationLayer extends AbstractApplicationLayer {
|
|
|
1469
1504
|
return out;
|
|
1470
1505
|
}
|
|
1471
1506
|
destroy() {
|
|
1507
|
+
if (this._destroyed) {
|
|
1508
|
+
return;
|
|
1509
|
+
}
|
|
1510
|
+
this._destroyed = true;
|
|
1472
1511
|
this.removeAllListeners();
|
|
1473
1512
|
for (const removeListener of this._removeAllListeners) {
|
|
1474
1513
|
removeListener();
|
|
1475
1514
|
}
|
|
1515
|
+
this._removeAllListeners = [];
|
|
1476
1516
|
this._states.clear();
|
|
1477
1517
|
}
|
|
1478
1518
|
}
|
|
@@ -1506,6 +1546,12 @@ class TcpApplicationLayer extends AbstractApplicationLayer {
|
|
|
1506
1546
|
writable: true,
|
|
1507
1547
|
value: []
|
|
1508
1548
|
});
|
|
1549
|
+
Object.defineProperty(this, "_destroyed", {
|
|
1550
|
+
enumerable: true,
|
|
1551
|
+
configurable: true,
|
|
1552
|
+
writable: true,
|
|
1553
|
+
value: false
|
|
1554
|
+
});
|
|
1509
1555
|
const handleData = (data, response, connection) => {
|
|
1510
1556
|
var _a;
|
|
1511
1557
|
let buffer = (_a = this._buffers.get(connection.id)) !== null && _a !== void 0 ? _a : EMPTY_BUFFER;
|
|
@@ -1588,10 +1634,15 @@ class TcpApplicationLayer extends AbstractApplicationLayer {
|
|
|
1588
1634
|
return buffer;
|
|
1589
1635
|
}
|
|
1590
1636
|
destroy() {
|
|
1637
|
+
if (this._destroyed) {
|
|
1638
|
+
return;
|
|
1639
|
+
}
|
|
1640
|
+
this._destroyed = true;
|
|
1591
1641
|
this.removeAllListeners();
|
|
1592
1642
|
for (const removeListener of this._removeAllListeners) {
|
|
1593
1643
|
removeListener();
|
|
1594
1644
|
}
|
|
1645
|
+
this._removeAllListeners = [];
|
|
1595
1646
|
this._buffers.clear();
|
|
1596
1647
|
}
|
|
1597
1648
|
}
|
|
@@ -1702,7 +1753,7 @@ class ModbusMaster extends EventEmitter {
|
|
|
1702
1753
|
return this.physicalLayer.isOpen;
|
|
1703
1754
|
}
|
|
1704
1755
|
get destroyed() {
|
|
1705
|
-
return this.physicalLayer.destroyed;
|
|
1756
|
+
return this._cleanLevel === 'destroy' || this.physicalLayer.destroyed;
|
|
1706
1757
|
}
|
|
1707
1758
|
constructor(applicationLayer, physicalLayer, options = {}) {
|
|
1708
1759
|
var _a, _b;
|
|
@@ -1743,11 +1794,11 @@ class ModbusMaster extends EventEmitter {
|
|
|
1743
1794
|
writable: true,
|
|
1744
1795
|
value: 1
|
|
1745
1796
|
});
|
|
1746
|
-
Object.defineProperty(this, "
|
|
1797
|
+
Object.defineProperty(this, "_cleanLevel", {
|
|
1747
1798
|
enumerable: true,
|
|
1748
1799
|
configurable: true,
|
|
1749
1800
|
writable: true,
|
|
1750
|
-
value:
|
|
1801
|
+
value: 'none'
|
|
1751
1802
|
});
|
|
1752
1803
|
Object.defineProperty(this, "timeout", {
|
|
1753
1804
|
enumerable: true,
|
|
@@ -1901,7 +1952,7 @@ class ModbusMaster extends EventEmitter {
|
|
|
1901
1952
|
}
|
|
1902
1953
|
_exchange(adu, preCheck, timeout, broadcast) {
|
|
1903
1954
|
return new Promise((resolve, reject) => {
|
|
1904
|
-
if (this.
|
|
1955
|
+
if (this._cleanLevel !== 'none') {
|
|
1905
1956
|
reject(new ModbusError(ModbusErrorCode.MASTER_CLOSED, 'Master closed'));
|
|
1906
1957
|
return;
|
|
1907
1958
|
}
|
|
@@ -2054,7 +2105,7 @@ class ModbusMaster extends EventEmitter {
|
|
|
2054
2105
|
if (frame) {
|
|
2055
2106
|
const runStatusIndex = 1 + serverIdLength;
|
|
2056
2107
|
return Object.assign(Object.assign({}, frame), { data: {
|
|
2057
|
-
serverId:
|
|
2108
|
+
serverId: Array.from(frame.data.subarray(1, runStatusIndex)),
|
|
2058
2109
|
runIndicatorStatus: frame.data[runStatusIndex] === 0xff,
|
|
2059
2110
|
additionalData: Array.from(frame.data.subarray(runStatusIndex + 1)),
|
|
2060
2111
|
} });
|
|
@@ -2162,26 +2213,42 @@ class ModbusMaster extends EventEmitter {
|
|
|
2162
2213
|
}
|
|
2163
2214
|
});
|
|
2164
2215
|
}
|
|
2216
|
+
_clean(level) {
|
|
2217
|
+
if (this._cleanLevel === 'destroy') {
|
|
2218
|
+
return;
|
|
2219
|
+
}
|
|
2220
|
+
if (this._cleanLevel === 'close' && level === 'close') {
|
|
2221
|
+
return;
|
|
2222
|
+
}
|
|
2223
|
+
const errorCode = level === 'destroy' ? ModbusErrorCode.MASTER_DESTROYED : ModbusErrorCode.MASTER_CLOSED;
|
|
2224
|
+
const message = level === 'destroy' ? 'Master destroyed' : 'Master closed';
|
|
2225
|
+
const queued = this._queue.splice(0);
|
|
2226
|
+
for (const item of queued) {
|
|
2227
|
+
item.cancel(new ModbusError(errorCode, message));
|
|
2228
|
+
}
|
|
2229
|
+
this._masterSession.stopAll(new ModbusError(errorCode, message));
|
|
2230
|
+
this._cleanLevel = level;
|
|
2231
|
+
}
|
|
2165
2232
|
open(...args) {
|
|
2166
|
-
this.
|
|
2233
|
+
if (this._cleanLevel === 'destroy') {
|
|
2234
|
+
return Promise.reject(new ModbusError(ModbusErrorCode.PORT_DESTROYED, 'Master is destroyed'));
|
|
2235
|
+
}
|
|
2236
|
+
this._cleanLevel = 'none';
|
|
2237
|
+
this._nextTid = 1;
|
|
2167
2238
|
return this.physicalLayer.open(...args);
|
|
2168
2239
|
}
|
|
2169
2240
|
close() {
|
|
2170
|
-
this.
|
|
2171
|
-
|
|
2172
|
-
for (const item of queued) {
|
|
2173
|
-
item.cancel(new ModbusError(ModbusErrorCode.MASTER_CLOSED, 'Master closed'));
|
|
2241
|
+
if (this._cleanLevel === 'destroy') {
|
|
2242
|
+
return Promise.resolve();
|
|
2174
2243
|
}
|
|
2175
|
-
this.
|
|
2244
|
+
this._clean('close');
|
|
2176
2245
|
return this.physicalLayer.close();
|
|
2177
2246
|
}
|
|
2178
2247
|
destroy() {
|
|
2179
|
-
this.
|
|
2180
|
-
|
|
2181
|
-
for (const item of queued) {
|
|
2182
|
-
item.cancel(new ModbusError(ModbusErrorCode.MASTER_DESTROYED, 'Master destroyed'));
|
|
2248
|
+
if (this._cleanLevel === 'destroy') {
|
|
2249
|
+
return Promise.resolve();
|
|
2183
2250
|
}
|
|
2184
|
-
this.
|
|
2251
|
+
this._clean('destroy');
|
|
2185
2252
|
this.removeAllListeners();
|
|
2186
2253
|
this.applicationLayer.destroy();
|
|
2187
2254
|
return this.physicalLayer.destroy();
|
|
@@ -2193,7 +2260,7 @@ class ModbusSlave extends EventEmitter {
|
|
|
2193
2260
|
return this.physicalLayer.isOpen;
|
|
2194
2261
|
}
|
|
2195
2262
|
get destroyed() {
|
|
2196
|
-
return this.physicalLayer.destroyed;
|
|
2263
|
+
return this._cleanLevel === 'destroy' || this.physicalLayer.destroyed;
|
|
2197
2264
|
}
|
|
2198
2265
|
constructor(applicationLayer, physicalLayer, options = {}) {
|
|
2199
2266
|
var _a;
|
|
@@ -2240,6 +2307,12 @@ class ModbusSlave extends EventEmitter {
|
|
|
2240
2307
|
writable: true,
|
|
2241
2308
|
value: new Map()
|
|
2242
2309
|
});
|
|
2310
|
+
Object.defineProperty(this, "_cleanLevel", {
|
|
2311
|
+
enumerable: true,
|
|
2312
|
+
configurable: true,
|
|
2313
|
+
writable: true,
|
|
2314
|
+
value: 'none'
|
|
2315
|
+
});
|
|
2243
2316
|
this.concurrent = (_a = options.concurrent) !== null && _a !== void 0 ? _a : false;
|
|
2244
2317
|
if (this.concurrent && this.applicationLayer.PROTOCOL !== 'TCP') {
|
|
2245
2318
|
throw new ModbusError(ModbusErrorCode.CONCURRENT_NOT_TCP, 'concurrent mode requires a Modbus TCP application layer');
|
|
@@ -2269,14 +2342,14 @@ class ModbusSlave extends EventEmitter {
|
|
|
2269
2342
|
if (!q) {
|
|
2270
2343
|
return;
|
|
2271
2344
|
}
|
|
2272
|
-
q.items
|
|
2345
|
+
q.items = [];
|
|
2273
2346
|
if (!q.processing) {
|
|
2274
2347
|
this._queues.delete(connection.id);
|
|
2275
2348
|
}
|
|
2276
2349
|
});
|
|
2277
2350
|
physicalLayer.on('close', () => {
|
|
2278
2351
|
for (const q of this._queues.values()) {
|
|
2279
|
-
q.items
|
|
2352
|
+
q.items = [];
|
|
2280
2353
|
}
|
|
2281
2354
|
this._queues.clear();
|
|
2282
2355
|
this.emit('close');
|
|
@@ -2565,8 +2638,8 @@ class ModbusSlave extends EventEmitter {
|
|
|
2565
2638
|
return;
|
|
2566
2639
|
}
|
|
2567
2640
|
try {
|
|
2568
|
-
const { serverId = (_a = model.unit) !== null && _a !== void 0 ? _a : 1, runIndicatorStatus = true, additionalData = [] } = yield model.reportServerId();
|
|
2569
|
-
const serverIdBytes =
|
|
2641
|
+
const { serverId = [(_a = model.unit) !== null && _a !== void 0 ? _a : 1], runIndicatorStatus = true, additionalData = [] } = yield model.reportServerId();
|
|
2642
|
+
const serverIdBytes = serverId;
|
|
2570
2643
|
const byteCount = serverIdBytes.length + 1 + additionalData.length;
|
|
2571
2644
|
if (byteCount > 255) {
|
|
2572
2645
|
yield this.responseError(frame, response, getErrorByCode(ErrorCode.SERVER_DEVICE_FAILURE));
|
|
@@ -2972,14 +3045,40 @@ class ModbusSlave extends EventEmitter {
|
|
|
2972
3045
|
this._customFunctionCodes.delete(fc);
|
|
2973
3046
|
this.applicationLayer.removeCustomFunctionCode(fc);
|
|
2974
3047
|
}
|
|
3048
|
+
_clean(level) {
|
|
3049
|
+
if (this._cleanLevel === 'destroy') {
|
|
3050
|
+
return;
|
|
3051
|
+
}
|
|
3052
|
+
if (this._cleanLevel === 'close' && level === 'close') {
|
|
3053
|
+
return;
|
|
3054
|
+
}
|
|
3055
|
+
for (const q of this._queues.values()) {
|
|
3056
|
+
q.items = [];
|
|
3057
|
+
}
|
|
3058
|
+
this._queues.clear();
|
|
3059
|
+
this._locks.clear();
|
|
3060
|
+
if (level === 'destroy') {
|
|
3061
|
+
this._customFunctionCodes.clear();
|
|
3062
|
+
this.models.clear();
|
|
3063
|
+
}
|
|
3064
|
+
this._cleanLevel = level;
|
|
3065
|
+
}
|
|
2975
3066
|
open(...args) {
|
|
3067
|
+
this._cleanLevel = 'none';
|
|
2976
3068
|
return this.physicalLayer.open(...args);
|
|
2977
3069
|
}
|
|
2978
3070
|
close() {
|
|
3071
|
+
if (this._cleanLevel === 'destroy') {
|
|
3072
|
+
return Promise.resolve();
|
|
3073
|
+
}
|
|
3074
|
+
this._clean('close');
|
|
2979
3075
|
return this.physicalLayer.close();
|
|
2980
3076
|
}
|
|
2981
3077
|
destroy() {
|
|
2982
|
-
this.
|
|
3078
|
+
if (this._cleanLevel === 'destroy') {
|
|
3079
|
+
return Promise.resolve();
|
|
3080
|
+
}
|
|
3081
|
+
this._clean('destroy');
|
|
2983
3082
|
this.removeAllListeners();
|
|
2984
3083
|
this.applicationLayer.destroy();
|
|
2985
3084
|
return this.physicalLayer.destroy();
|
|
@@ -15,6 +15,7 @@ export declare class AsciiApplicationLayer extends AbstractApplicationLayer {
|
|
|
15
15
|
readonly lenientHex: boolean;
|
|
16
16
|
private _states;
|
|
17
17
|
private _removeAllListeners;
|
|
18
|
+
private _destroyed;
|
|
18
19
|
constructor(physicalLayer: SerialPhysicalLayer | TcpServerPhysicalLayer | TcpClientPhysicalLayer | UdpPhysicalLayer, options?: AsciiApplicationLayerOptions);
|
|
19
20
|
private getState;
|
|
20
21
|
flush(): void;
|
|
@@ -43,6 +43,7 @@ export declare class RtuApplicationLayer extends AbstractApplicationLayer {
|
|
|
43
43
|
private _removeAllListeners;
|
|
44
44
|
private _threePointFiveT;
|
|
45
45
|
private _onePointFiveT;
|
|
46
|
+
private _destroyed;
|
|
46
47
|
constructor(physicalLayer: SerialPhysicalLayer | TcpServerPhysicalLayer | TcpClientPhysicalLayer | UdpPhysicalLayer, options?: RtuApplicationLayerOptions);
|
|
47
48
|
private getState;
|
|
48
49
|
private clearStateTimers;
|
|
@@ -6,6 +6,7 @@ export declare class TcpApplicationLayer extends AbstractApplicationLayer {
|
|
|
6
6
|
private _transactionId;
|
|
7
7
|
private _buffers;
|
|
8
8
|
private _removeAllListeners;
|
|
9
|
+
private _destroyed;
|
|
9
10
|
constructor(physicalLayer: TcpServerPhysicalLayer | TcpClientPhysicalLayer | UdpPhysicalLayer);
|
|
10
11
|
private tryExtract;
|
|
11
12
|
private processFrame;
|
|
@@ -30,7 +30,7 @@ export declare class ModbusMaster<A extends AbstractApplicationLayer, P extends
|
|
|
30
30
|
private _queue;
|
|
31
31
|
private _draining;
|
|
32
32
|
private _nextTid;
|
|
33
|
-
private
|
|
33
|
+
private _cleanLevel;
|
|
34
34
|
timeout: number;
|
|
35
35
|
readonly concurrent: boolean;
|
|
36
36
|
get isOpen(): boolean;
|
|
@@ -96,6 +96,7 @@ export declare class ModbusMaster<A extends AbstractApplicationLayer, P extends
|
|
|
96
96
|
removeCustomFunctionCode(fc: number): void;
|
|
97
97
|
sendCustomFC(unit: 0, fc: number, data: Buffer | number[], timeout?: number): Promise<void>;
|
|
98
98
|
sendCustomFC(unit: number, fc: number, data: Buffer | number[], timeout?: number): Promise<Buffer>;
|
|
99
|
+
private _clean;
|
|
99
100
|
open(...args: Parameters<P['open']>): Promise<void>;
|
|
100
101
|
close(): Promise<void>;
|
|
101
102
|
destroy(): Promise<void>;
|
|
@@ -60,6 +60,7 @@ export declare class ModbusSlave<A extends AbstractApplicationLayer, P extends A
|
|
|
60
60
|
private _queues;
|
|
61
61
|
private _customFunctionCodes;
|
|
62
62
|
private _locks;
|
|
63
|
+
private _cleanLevel;
|
|
63
64
|
get isOpen(): boolean;
|
|
64
65
|
get destroyed(): boolean;
|
|
65
66
|
constructor(applicationLayer: A, physicalLayer: P, options?: ModbusSlaveOptions);
|
|
@@ -85,6 +86,7 @@ export declare class ModbusSlave<A extends AbstractApplicationLayer, P extends A
|
|
|
85
86
|
remove(unit: number): void;
|
|
86
87
|
addCustomFunctionCode(cfc: CustomFunctionCode): void;
|
|
87
88
|
removeCustomFunctionCode(fc: number): void;
|
|
89
|
+
private _clean;
|
|
88
90
|
open(...args: Parameters<P['open']>): Promise<void>;
|
|
89
91
|
close(): Promise<void>;
|
|
90
92
|
destroy(): Promise<void>;
|
package/dist/src/types.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export interface ApplicationDataUnit {
|
|
|
8
8
|
}
|
|
9
9
|
export interface ServerId {
|
|
10
10
|
/** Server ID; may be 1 byte (number) or multi-byte (number[]) per device spec. */
|
|
11
|
-
serverId?: number
|
|
11
|
+
serverId?: number[];
|
|
12
12
|
runIndicatorStatus?: boolean;
|
|
13
13
|
additionalData?: number[];
|
|
14
14
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "njs-modbus",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "A pure JavaScript implemetation of Modbus for NodeJS.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"modbus",
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
},
|
|
21
21
|
"license": "MIT",
|
|
22
22
|
"author": "Xie Jay <xiejay97@gmail.com>",
|
|
23
|
+
"packageManager": "pnpm@10.10.0",
|
|
23
24
|
"sideEffects": false,
|
|
24
25
|
"type": "module",
|
|
25
26
|
"main": "dist/index.cjs",
|
|
@@ -32,7 +33,7 @@
|
|
|
32
33
|
],
|
|
33
34
|
"scripts": {
|
|
34
35
|
"build": "rollup -c",
|
|
35
|
-
"test": "tsx --test
|
|
36
|
+
"test": "tsx --test test/*.test.ts",
|
|
36
37
|
"util:sort-package-json": "sort-package-json"
|
|
37
38
|
},
|
|
38
39
|
"devDependencies": {
|
|
@@ -59,6 +60,7 @@
|
|
|
59
60
|
"serialport": ">=12.0.0",
|
|
60
61
|
"sort-package-json": "^3.2.1",
|
|
61
62
|
"tslib": "^2.8.1",
|
|
63
|
+
"tsx": "^4.21.0",
|
|
62
64
|
"typescript": "~5.8.3",
|
|
63
65
|
"typescript-eslint": "^8.34.0"
|
|
64
66
|
},
|