@ya-modbus/transport 0.4.1-refactor-scope-driver-packages.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/CHANGELOG.md +25 -0
- package/LICENSE +674 -0
- package/README.md +47 -0
- package/dist/create-modbus-transport.d.ts +16 -0
- package/dist/create-modbus-transport.d.ts.map +1 -0
- package/dist/create-modbus-transport.js +74 -0
- package/dist/create-modbus-transport.js.map +1 -0
- package/dist/factory.d.ts +22 -0
- package/dist/factory.d.ts.map +1 -0
- package/dist/factory.js +33 -0
- package/dist/factory.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/manager.d.ts +66 -0
- package/dist/manager.d.ts.map +1 -0
- package/dist/manager.js +130 -0
- package/dist/manager.js.map +1 -0
- package/dist/mutex-transport.d.ts +26 -0
- package/dist/mutex-transport.d.ts.map +1 -0
- package/dist/mutex-transport.js +62 -0
- package/dist/mutex-transport.js.map +1 -0
- package/dist/retry.d.ts +25 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +46 -0
- package/dist/retry.js.map +1 -0
- package/dist/rtu-transport.d.ts +33 -0
- package/dist/rtu-transport.d.ts.map +1 -0
- package/dist/rtu-transport.js +24 -0
- package/dist/rtu-transport.js.map +1 -0
- package/dist/tcp-transport.d.ts +27 -0
- package/dist/tcp-transport.d.ts.map +1 -0
- package/dist/tcp-transport.js +19 -0
- package/dist/tcp-transport.js.map +1 -0
- package/package.json +47 -0
package/README.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# @ya-modbus/transport
|
|
2
|
+
|
|
3
|
+
Modbus transport implementations for RTU (serial) and TCP connections.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- RTU (RS-485/serial) transport
|
|
8
|
+
- TCP/IP transport
|
|
9
|
+
- Automatic retry on transient failures
|
|
10
|
+
- Factory for auto-detecting transport type
|
|
11
|
+
- TypeScript support
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @ya-modbus/transport
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import { createTransport } from '@ya-modbus/transport'
|
|
23
|
+
|
|
24
|
+
// RTU (serial) transport
|
|
25
|
+
const rtuTransport = await createTransport({
|
|
26
|
+
port: '/dev/ttyUSB0',
|
|
27
|
+
baudRate: 9600,
|
|
28
|
+
dataBits: 8,
|
|
29
|
+
parity: 'none',
|
|
30
|
+
stopBits: 1,
|
|
31
|
+
slaveId: 1,
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
// TCP transport
|
|
35
|
+
const tcpTransport = await createTransport({
|
|
36
|
+
host: '192.168.1.100',
|
|
37
|
+
port: 502, // optional, defaults to 502
|
|
38
|
+
slaveId: 1,
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
// Use transport with driver
|
|
42
|
+
await rtuTransport.readHoldingRegisters(0, 10)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## License
|
|
46
|
+
|
|
47
|
+
GPL-3.0-or-later
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Transport } from '@ya-modbus/driver-types';
|
|
2
|
+
import type ModbusRTU from 'modbus-serial';
|
|
3
|
+
import { type RetryLogger } from './retry.js';
|
|
4
|
+
/**
|
|
5
|
+
* Create a Transport implementation from a ModbusRTU client
|
|
6
|
+
*
|
|
7
|
+
* This factory wraps all modbus-serial client methods with retry logic
|
|
8
|
+
* and converts them to the Transport interface expected by device drivers.
|
|
9
|
+
*
|
|
10
|
+
* @param client - Configured ModbusRTU client instance
|
|
11
|
+
* @param maxRetries - Maximum retry attempts (default: 3, use 1 for discovery to disable retries)
|
|
12
|
+
* @param logger - Optional callback to log retry attempts for debugging
|
|
13
|
+
* @returns Transport implementation
|
|
14
|
+
*/
|
|
15
|
+
export declare function createModbusTransport(client: ModbusRTU, maxRetries?: number, logger?: RetryLogger): Transport;
|
|
16
|
+
//# sourceMappingURL=create-modbus-transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-modbus-transport.d.ts","sourceRoot":"","sources":["../src/create-modbus-transport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AACxD,OAAO,KAAK,SAAS,MAAM,eAAe,CAAA;AAE1C,OAAO,EAAa,KAAK,WAAW,EAAE,MAAM,YAAY,CAAA;AAExD;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,SAAS,EACjB,UAAU,GAAE,MAAU,EACtB,MAAM,CAAC,EAAE,WAAW,GACnB,SAAS,CAoGX"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { withRetry } from './retry.js';
|
|
2
|
+
/**
|
|
3
|
+
* Create a Transport implementation from a ModbusRTU client
|
|
4
|
+
*
|
|
5
|
+
* This factory wraps all modbus-serial client methods with retry logic
|
|
6
|
+
* and converts them to the Transport interface expected by device drivers.
|
|
7
|
+
*
|
|
8
|
+
* @param client - Configured ModbusRTU client instance
|
|
9
|
+
* @param maxRetries - Maximum retry attempts (default: 3, use 1 for discovery to disable retries)
|
|
10
|
+
* @param logger - Optional callback to log retry attempts for debugging
|
|
11
|
+
* @returns Transport implementation
|
|
12
|
+
*/
|
|
13
|
+
export function createModbusTransport(client, maxRetries = 3, logger) {
|
|
14
|
+
return {
|
|
15
|
+
async readHoldingRegisters(address, count) {
|
|
16
|
+
return withRetry(async () => {
|
|
17
|
+
const result = await client.readHoldingRegisters(address, count);
|
|
18
|
+
return result.buffer;
|
|
19
|
+
}, maxRetries, logger);
|
|
20
|
+
},
|
|
21
|
+
async readInputRegisters(address, count) {
|
|
22
|
+
return withRetry(async () => {
|
|
23
|
+
const result = await client.readInputRegisters(address, count);
|
|
24
|
+
return result.buffer;
|
|
25
|
+
}, maxRetries, logger);
|
|
26
|
+
},
|
|
27
|
+
async readCoils(address, count) {
|
|
28
|
+
return withRetry(async () => {
|
|
29
|
+
const result = await client.readCoils(address, count);
|
|
30
|
+
return result.buffer;
|
|
31
|
+
}, maxRetries, logger);
|
|
32
|
+
},
|
|
33
|
+
async readDiscreteInputs(address, count) {
|
|
34
|
+
return withRetry(async () => {
|
|
35
|
+
const result = await client.readDiscreteInputs(address, count);
|
|
36
|
+
return result.buffer;
|
|
37
|
+
}, maxRetries, logger);
|
|
38
|
+
},
|
|
39
|
+
async writeSingleRegister(address, value) {
|
|
40
|
+
return withRetry(async () => {
|
|
41
|
+
await client.writeRegister(address, value);
|
|
42
|
+
}, maxRetries, logger);
|
|
43
|
+
},
|
|
44
|
+
async writeMultipleRegisters(address, values) {
|
|
45
|
+
return withRetry(async () => {
|
|
46
|
+
await client.writeRegisters(address, values);
|
|
47
|
+
}, maxRetries, logger);
|
|
48
|
+
},
|
|
49
|
+
async writeSingleCoil(address, value) {
|
|
50
|
+
return withRetry(async () => {
|
|
51
|
+
await client.writeCoil(address, value);
|
|
52
|
+
}, maxRetries, logger);
|
|
53
|
+
},
|
|
54
|
+
async writeMultipleCoils(address, values) {
|
|
55
|
+
return withRetry(async () => {
|
|
56
|
+
// Convert Buffer to boolean array
|
|
57
|
+
const bools = [];
|
|
58
|
+
for (let i = 0; i < values.length * 8; i++) {
|
|
59
|
+
const byteIndex = Math.floor(i / 8);
|
|
60
|
+
const bitIndex = i % 8;
|
|
61
|
+
const byte = values[byteIndex]; // byteIndex < values.length due to loop condition
|
|
62
|
+
bools.push((byte & (1 << bitIndex)) !== 0);
|
|
63
|
+
}
|
|
64
|
+
await client.writeCoils(address, bools);
|
|
65
|
+
}, maxRetries, logger);
|
|
66
|
+
},
|
|
67
|
+
async close() {
|
|
68
|
+
return new Promise((resolve) => {
|
|
69
|
+
client.close(resolve);
|
|
70
|
+
});
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=create-modbus-transport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-modbus-transport.js","sourceRoot":"","sources":["../src/create-modbus-transport.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Transport } from '@ya-modbus/driver-types';
|
|
2
|
+
import { type RTUConfig } from './rtu-transport.js';
|
|
3
|
+
import { type TCPConfig } from './tcp-transport.js';
|
|
4
|
+
export type { RTUConfig, TCPConfig };
|
|
5
|
+
/**
|
|
6
|
+
* Combined transport configuration
|
|
7
|
+
* Can be either RTU or TCP, but not both
|
|
8
|
+
*/
|
|
9
|
+
export type TransportConfig = RTUConfig | TCPConfig;
|
|
10
|
+
/**
|
|
11
|
+
* Create a transport instance based on configuration
|
|
12
|
+
*
|
|
13
|
+
* Detects whether to create RTU or TCP transport based on the config:
|
|
14
|
+
* - If `port` is provided → RTU transport
|
|
15
|
+
* - If `host` is provided → TCP transport
|
|
16
|
+
*
|
|
17
|
+
* @param config - Transport configuration (RTU or TCP)
|
|
18
|
+
* @returns Transport implementation
|
|
19
|
+
* @throws Error if config is invalid
|
|
20
|
+
*/
|
|
21
|
+
export declare function createTransport(config: TransportConfig): Promise<Transport>;
|
|
22
|
+
//# sourceMappingURL=factory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AAExD,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,oBAAoB,CAAA;AACvE,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAGvE,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,CAAA;AAEpC;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,SAAS,CAAA;AASnD;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,CAAC,CAejF"}
|
package/dist/factory.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { createRTUTransport } from './rtu-transport.js';
|
|
2
|
+
import { createTCPTransport } from './tcp-transport.js';
|
|
3
|
+
/**
|
|
4
|
+
* Type guard to check if config is RTU
|
|
5
|
+
*/
|
|
6
|
+
function isRTUConfig(config) {
|
|
7
|
+
return 'port' in config && !('host' in config);
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Create a transport instance based on configuration
|
|
11
|
+
*
|
|
12
|
+
* Detects whether to create RTU or TCP transport based on the config:
|
|
13
|
+
* - If `port` is provided → RTU transport
|
|
14
|
+
* - If `host` is provided → TCP transport
|
|
15
|
+
*
|
|
16
|
+
* @param config - Transport configuration (RTU or TCP)
|
|
17
|
+
* @returns Transport implementation
|
|
18
|
+
* @throws Error if config is invalid
|
|
19
|
+
*/
|
|
20
|
+
export async function createTransport(config) {
|
|
21
|
+
// Validate that exactly one of port/host is provided
|
|
22
|
+
if ('port' in config && 'host' in config) {
|
|
23
|
+
throw new Error('Cannot specify both port (RTU) and host (TCP)');
|
|
24
|
+
}
|
|
25
|
+
if (isRTUConfig(config)) {
|
|
26
|
+
return createRTUTransport(config);
|
|
27
|
+
}
|
|
28
|
+
if ('host' in config) {
|
|
29
|
+
return createTCPTransport(config);
|
|
30
|
+
}
|
|
31
|
+
throw new Error('Either port (for RTU) or host (for TCP) must be specified');
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=factory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factory.js","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAkB,MAAM,oBAAoB,CAAA;AACvE,OAAO,EAAE,kBAAkB,EAAkB,MAAM,oBAAoB,CAAA;AAWvE;;GAEG;AACH,SAAS,WAAW,CAAC,MAAuB;IAC1C,OAAO,MAAM,IAAI,MAAM,IAAI,CAAC,CAAC,MAAM,IAAI,MAAM,CAAC,CAAA;AAChD,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,MAAuB;IAC3D,qDAAqD;IACrD,IAAI,MAAM,IAAI,MAAM,IAAI,MAAM,IAAI,MAAM,EAAE,CAAC;QACzC,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAA;IAClE,CAAC;IAED,IAAI,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC;QACxB,OAAO,kBAAkB,CAAC,MAAM,CAAC,CAAA;IACnC,CAAC;IAED,IAAI,MAAM,IAAI,MAAM,EAAE,CAAC;QACrB,OAAO,kBAAkB,CAAC,MAAM,CAAC,CAAA;IACnC,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAA;AAC9E,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @ya-modbus/transport - Modbus transport implementations
|
|
3
|
+
*
|
|
4
|
+
* Provides RTU and TCP transport implementations with automatic retry logic
|
|
5
|
+
* and transport pooling with mutex-based RTU bus serialization
|
|
6
|
+
*/
|
|
7
|
+
export { createTransport, type TransportConfig } from './factory.js';
|
|
8
|
+
export { createRTUTransport, type RTUConfig } from './rtu-transport.js';
|
|
9
|
+
export { createTCPTransport, type TCPConfig } from './tcp-transport.js';
|
|
10
|
+
export { createModbusTransport } from './create-modbus-transport.js';
|
|
11
|
+
export { withRetry, MAX_RETRIES, RETRY_DELAY_MS, type RetryLogger } from './retry.js';
|
|
12
|
+
export { TransportManager, type TransportStats } from './manager.js';
|
|
13
|
+
export { MutexTransport } from './mutex-transport.js';
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,eAAe,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAA;AACpE,OAAO,EAAE,kBAAkB,EAAE,KAAK,SAAS,EAAE,MAAM,oBAAoB,CAAA;AACvE,OAAO,EAAE,kBAAkB,EAAE,KAAK,SAAS,EAAE,MAAM,oBAAoB,CAAA;AACvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAA;AACpE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,cAAc,EAAE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAA;AACrF,OAAO,EAAE,gBAAgB,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAA;AACpE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @ya-modbus/transport - Modbus transport implementations
|
|
3
|
+
*
|
|
4
|
+
* Provides RTU and TCP transport implementations with automatic retry logic
|
|
5
|
+
* and transport pooling with mutex-based RTU bus serialization
|
|
6
|
+
*/
|
|
7
|
+
export { createTransport } from './factory.js';
|
|
8
|
+
export { createRTUTransport } from './rtu-transport.js';
|
|
9
|
+
export { createTCPTransport } from './tcp-transport.js';
|
|
10
|
+
export { createModbusTransport } from './create-modbus-transport.js';
|
|
11
|
+
export { withRetry, MAX_RETRIES, RETRY_DELAY_MS } from './retry.js';
|
|
12
|
+
export { TransportManager } from './manager.js';
|
|
13
|
+
export { MutexTransport } from './mutex-transport.js';
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,eAAe,EAAwB,MAAM,cAAc,CAAA;AACpE,OAAO,EAAE,kBAAkB,EAAkB,MAAM,oBAAoB,CAAA;AACvE,OAAO,EAAE,kBAAkB,EAAkB,MAAM,oBAAoB,CAAA;AACvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAA;AACpE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,cAAc,EAAoB,MAAM,YAAY,CAAA;AACrF,OAAO,EAAE,gBAAgB,EAAuB,MAAM,cAAc,CAAA;AACpE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { Transport } from '@ya-modbus/driver-types';
|
|
2
|
+
import { type TransportConfig } from './factory.js';
|
|
3
|
+
/**
|
|
4
|
+
* Statistics about managed transports
|
|
5
|
+
*/
|
|
6
|
+
export interface TransportStats {
|
|
7
|
+
totalTransports: number;
|
|
8
|
+
rtuTransports: number;
|
|
9
|
+
tcpTransports: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* TransportManager pools transport instances and provides mutex-based
|
|
13
|
+
* serialization for all transport operations to prevent concurrent access issues.
|
|
14
|
+
*
|
|
15
|
+
* Key behaviors:
|
|
16
|
+
* - All transports (RTU and TCP) are pooled by connection configuration
|
|
17
|
+
* - RTU transports are pooled by bus configuration (port, baud rate, parity, etc.)
|
|
18
|
+
* - TCP transports are pooled by host and port
|
|
19
|
+
* - Multiple devices on the same connection share a single transport instance
|
|
20
|
+
* - All operations are serialized using async-mutex to prevent race conditions
|
|
21
|
+
*
|
|
22
|
+
* Why TCP needs mutex protection:
|
|
23
|
+
* - Many Modbus devices allow only one or a limited number of TCP connections
|
|
24
|
+
* - Even with multiple connections, devices often process requests sequentially
|
|
25
|
+
* - Concurrent requests can cause timeouts, dropped packets, or incorrect responses
|
|
26
|
+
* - Serialization ensures reliable communication regardless of device limitations
|
|
27
|
+
*/
|
|
28
|
+
export declare class TransportManager {
|
|
29
|
+
private readonly transports;
|
|
30
|
+
/**
|
|
31
|
+
* Get or create a transport for the given configuration.
|
|
32
|
+
* Returns mutex-wrapped shared transport if one exists for the same connection.
|
|
33
|
+
*
|
|
34
|
+
* @param config - RTU or TCP transport configuration
|
|
35
|
+
* @returns Transport instance wrapped with mutex for thread-safe operations
|
|
36
|
+
*/
|
|
37
|
+
getTransport(config: TransportConfig): Promise<Transport>;
|
|
38
|
+
/**
|
|
39
|
+
* Get statistics about managed transports
|
|
40
|
+
*
|
|
41
|
+
* @returns Transport statistics
|
|
42
|
+
*/
|
|
43
|
+
getStats(): TransportStats;
|
|
44
|
+
/**
|
|
45
|
+
* Close all managed transports and clear the pool.
|
|
46
|
+
* Errors during close are logged but do not stop the process.
|
|
47
|
+
*/
|
|
48
|
+
closeAll(): Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* Generate a unique key for a transport configuration.
|
|
51
|
+
* For RTU: Key includes all bus parameters (port, baud, parity, etc.)
|
|
52
|
+
* For TCP: Key includes host and port
|
|
53
|
+
*
|
|
54
|
+
* @param config - Transport configuration
|
|
55
|
+
* @returns Connection key string
|
|
56
|
+
*/
|
|
57
|
+
private getConnectionKey;
|
|
58
|
+
/**
|
|
59
|
+
* Type guard to check if config is RTU
|
|
60
|
+
*
|
|
61
|
+
* @param config - Transport configuration
|
|
62
|
+
* @returns True if RTU config
|
|
63
|
+
*/
|
|
64
|
+
private isRTUConfig;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../src/manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AAGxD,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,cAAc,CAAA;AAepE;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,eAAe,EAAE,MAAM,CAAA;IACvB,aAAa,EAAE,MAAM,CAAA;IACrB,aAAa,EAAE,MAAM,CAAA;CACtB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAoC;IAE/D;;;;;;OAMG;IACG,YAAY,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,CAAC;IAqD/D;;;;OAIG;IACH,QAAQ,IAAI,cAAc;IAS1B;;;OAGG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAc/B;;;;;;;OAOG;IACH,OAAO,CAAC,gBAAgB;IASxB;;;;;OAKG;IACH,OAAO,CAAC,WAAW;CAGpB"}
|
package/dist/manager.js
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { Mutex } from 'async-mutex';
|
|
2
|
+
import { createTransport } from './factory.js';
|
|
3
|
+
import { MutexTransport } from './mutex-transport.js';
|
|
4
|
+
/**
|
|
5
|
+
* TransportManager pools transport instances and provides mutex-based
|
|
6
|
+
* serialization for all transport operations to prevent concurrent access issues.
|
|
7
|
+
*
|
|
8
|
+
* Key behaviors:
|
|
9
|
+
* - All transports (RTU and TCP) are pooled by connection configuration
|
|
10
|
+
* - RTU transports are pooled by bus configuration (port, baud rate, parity, etc.)
|
|
11
|
+
* - TCP transports are pooled by host and port
|
|
12
|
+
* - Multiple devices on the same connection share a single transport instance
|
|
13
|
+
* - All operations are serialized using async-mutex to prevent race conditions
|
|
14
|
+
*
|
|
15
|
+
* Why TCP needs mutex protection:
|
|
16
|
+
* - Many Modbus devices allow only one or a limited number of TCP connections
|
|
17
|
+
* - Even with multiple connections, devices often process requests sequentially
|
|
18
|
+
* - Concurrent requests can cause timeouts, dropped packets, or incorrect responses
|
|
19
|
+
* - Serialization ensures reliable communication regardless of device limitations
|
|
20
|
+
*/
|
|
21
|
+
export class TransportManager {
|
|
22
|
+
transports = new Map();
|
|
23
|
+
/**
|
|
24
|
+
* Get or create a transport for the given configuration.
|
|
25
|
+
* Returns mutex-wrapped shared transport if one exists for the same connection.
|
|
26
|
+
*
|
|
27
|
+
* @param config - RTU or TCP transport configuration
|
|
28
|
+
* @returns Transport instance wrapped with mutex for thread-safe operations
|
|
29
|
+
*/
|
|
30
|
+
async getTransport(config) {
|
|
31
|
+
const key = this.getConnectionKey(config);
|
|
32
|
+
const isRTU = this.isRTUConfig(config);
|
|
33
|
+
// For RTU, reuse existing transport if available
|
|
34
|
+
if (isRTU) {
|
|
35
|
+
const entry = this.transports.get(key);
|
|
36
|
+
if (entry) {
|
|
37
|
+
return entry.wrappedTransport;
|
|
38
|
+
}
|
|
39
|
+
// Create new RTU transport with mutex wrapper
|
|
40
|
+
const rawTransport = await createTransport(config);
|
|
41
|
+
const mutex = new Mutex();
|
|
42
|
+
const wrappedTransport = new MutexTransport(rawTransport, mutex);
|
|
43
|
+
const newEntry = {
|
|
44
|
+
rawTransport,
|
|
45
|
+
wrappedTransport,
|
|
46
|
+
mutex,
|
|
47
|
+
config,
|
|
48
|
+
isRTU: true,
|
|
49
|
+
};
|
|
50
|
+
this.transports.set(key, newEntry);
|
|
51
|
+
return wrappedTransport;
|
|
52
|
+
}
|
|
53
|
+
// For TCP, reuse existing transport if available
|
|
54
|
+
// TCP is pooled like RTU because many devices support only one connection
|
|
55
|
+
// or process requests sequentially even with multiple connections
|
|
56
|
+
const entry = this.transports.get(key);
|
|
57
|
+
if (entry) {
|
|
58
|
+
return entry.wrappedTransport;
|
|
59
|
+
}
|
|
60
|
+
// Create new TCP transport with mutex wrapper
|
|
61
|
+
// Mutex prevents concurrent requests that could cause timeouts or errors
|
|
62
|
+
const rawTransport = await createTransport(config);
|
|
63
|
+
const mutex = new Mutex();
|
|
64
|
+
const wrappedTransport = new MutexTransport(rawTransport, mutex);
|
|
65
|
+
const newEntry = {
|
|
66
|
+
rawTransport,
|
|
67
|
+
wrappedTransport,
|
|
68
|
+
mutex,
|
|
69
|
+
config,
|
|
70
|
+
isRTU: false,
|
|
71
|
+
};
|
|
72
|
+
this.transports.set(key, newEntry);
|
|
73
|
+
return wrappedTransport;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Get statistics about managed transports
|
|
77
|
+
*
|
|
78
|
+
* @returns Transport statistics
|
|
79
|
+
*/
|
|
80
|
+
getStats() {
|
|
81
|
+
const entries = Array.from(this.transports.values());
|
|
82
|
+
return {
|
|
83
|
+
totalTransports: entries.length,
|
|
84
|
+
rtuTransports: entries.filter((e) => e.isRTU).length,
|
|
85
|
+
tcpTransports: entries.filter((e) => !e.isRTU).length,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Close all managed transports and clear the pool.
|
|
90
|
+
* Errors during close are logged but do not stop the process.
|
|
91
|
+
*/
|
|
92
|
+
async closeAll() {
|
|
93
|
+
const closePromises = Array.from(this.transports.values()).map(async (entry) => {
|
|
94
|
+
try {
|
|
95
|
+
await entry.rawTransport.close();
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
// Log but don't throw - we want to close all transports
|
|
99
|
+
console.error('Error closing transport:', error);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
await Promise.all(closePromises);
|
|
103
|
+
this.transports.clear();
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Generate a unique key for a transport configuration.
|
|
107
|
+
* For RTU: Key includes all bus parameters (port, baud, parity, etc.)
|
|
108
|
+
* For TCP: Key includes host and port
|
|
109
|
+
*
|
|
110
|
+
* @param config - Transport configuration
|
|
111
|
+
* @returns Connection key string
|
|
112
|
+
*/
|
|
113
|
+
getConnectionKey(config) {
|
|
114
|
+
if (this.isRTUConfig(config)) {
|
|
115
|
+
return `rtu:${config.port}:${config.baudRate}:${config.dataBits}:${config.parity}:${config.stopBits}`;
|
|
116
|
+
}
|
|
117
|
+
// TCP config - safe to access host/port because isRTUConfig returned false
|
|
118
|
+
return `tcp:${config.host}:${config.port ?? 502}`;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Type guard to check if config is RTU
|
|
122
|
+
*
|
|
123
|
+
* @param config - Transport configuration
|
|
124
|
+
* @returns True if RTU config
|
|
125
|
+
*/
|
|
126
|
+
isRTUConfig(config) {
|
|
127
|
+
return 'port' in config && typeof config.port === 'string' && !('host' in config);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../src/manager.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAEnC,OAAO,EAAE,eAAe,EAAwB,MAAM,cAAc,CAAA;AAEpE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAsBrD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,OAAO,gBAAgB;IACV,UAAU,GAAG,IAAI,GAAG,EAA0B,CAAA;IAE/D;;;;;;OAMG;IACH,KAAK,CAAC,YAAY,CAAC,MAAuB;QACxC,MAAM,GAAG,GAAG,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAA;QACzC,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAA;QAEtC,iDAAiD;QACjD,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;YACtC,IAAI,KAAK,EAAE,CAAC;gBACV,OAAO,KAAK,CAAC,gBAAgB,CAAA;YAC/B,CAAC;YAED,8CAA8C;YAC9C,MAAM,YAAY,GAAG,MAAM,eAAe,CAAC,MAAM,CAAC,CAAA;YAClD,MAAM,KAAK,GAAG,IAAI,KAAK,EAAE,CAAA;YACzB,MAAM,gBAAgB,GAAG,IAAI,cAAc,CAAC,YAAY,EAAE,KAAK,CAAC,CAAA;YAEhE,MAAM,QAAQ,GAAmB;gBAC/B,YAAY;gBACZ,gBAAgB;gBAChB,KAAK;gBACL,MAAM;gBACN,KAAK,EAAE,IAAI;aACZ,CAAA;YACD,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAA;YAClC,OAAO,gBAAgB,CAAA;QACzB,CAAC;QAED,iDAAiD;QACjD,0EAA0E;QAC1E,kEAAkE;QAClE,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QACtC,IAAI,KAAK,EAAE,CAAC;YACV,OAAO,KAAK,CAAC,gBAAgB,CAAA;QAC/B,CAAC;QAED,8CAA8C;QAC9C,yEAAyE;QACzE,MAAM,YAAY,GAAG,MAAM,eAAe,CAAC,MAAM,CAAC,CAAA;QAClD,MAAM,KAAK,GAAG,IAAI,KAAK,EAAE,CAAA;QACzB,MAAM,gBAAgB,GAAG,IAAI,cAAc,CAAC,YAAY,EAAE,KAAK,CAAC,CAAA;QAEhE,MAAM,QAAQ,GAAmB;YAC/B,YAAY;YACZ,gBAAgB;YAChB,KAAK;YACL,MAAM;YACN,KAAK,EAAE,KAAK;SACb,CAAA;QACD,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAA;QAElC,OAAO,gBAAgB,CAAA;IACzB,CAAC;IAED;;;;OAIG;IACH,QAAQ;QACN,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,CAAA;QACpD,OAAO;YACL,eAAe,EAAE,OAAO,CAAC,MAAM;YAC/B,aAAa,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,MAAM;YACpD,aAAa,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,MAAM;SACtD,CAAA;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,QAAQ;QACZ,MAAM,aAAa,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;YAC7E,IAAI,CAAC;gBACH,MAAM,KAAK,CAAC,YAAY,CAAC,KAAK,EAAE,CAAA;YAClC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,wDAAwD;gBACxD,OAAO,CAAC,KAAK,CAAC,0BAA0B,EAAE,KAAK,CAAC,CAAA;YAClD,CAAC;QACH,CAAC,CAAC,CAAA;QAEF,MAAM,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAA;QAChC,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAA;IACzB,CAAC;IAED;;;;;;;OAOG;IACK,gBAAgB,CAAC,MAAuB;QAC9C,IAAI,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC;YAC7B,OAAO,OAAO,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAA;QACvG,CAAC;QAED,2EAA2E;QAC3E,OAAO,OAAO,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,IAAI,GAAG,EAAE,CAAA;IACnD,CAAC;IAED;;;;;OAKG;IACK,WAAW,CAAC,MAAuB;QACzC,OAAO,MAAM,IAAI,MAAM,IAAI,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,IAAI,MAAM,CAAC,CAAA;IACnF,CAAC;CACF"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Transport } from '@ya-modbus/driver-types';
|
|
2
|
+
import { Mutex } from 'async-mutex';
|
|
3
|
+
/**
|
|
4
|
+
* Wraps a transport with mutex-based serialization.
|
|
5
|
+
* All operations are executed sequentially to prevent concurrent access issues.
|
|
6
|
+
*
|
|
7
|
+
* Used for:
|
|
8
|
+
* - RTU transports: Prevents bus collisions when multiple devices share a serial bus
|
|
9
|
+
* - TCP transports: Many Modbus devices support only one connection or process
|
|
10
|
+
* requests sequentially. Serialization prevents timeouts and communication errors.
|
|
11
|
+
*/
|
|
12
|
+
export declare class MutexTransport implements Transport {
|
|
13
|
+
private readonly transport;
|
|
14
|
+
private readonly mutex;
|
|
15
|
+
constructor(transport: Transport, mutex: Mutex);
|
|
16
|
+
readHoldingRegisters(address: number, count: number): Promise<Buffer>;
|
|
17
|
+
readInputRegisters(address: number, count: number): Promise<Buffer>;
|
|
18
|
+
readCoils(address: number, count: number): Promise<Buffer>;
|
|
19
|
+
readDiscreteInputs(address: number, count: number): Promise<Buffer>;
|
|
20
|
+
writeSingleRegister(address: number, value: number): Promise<void>;
|
|
21
|
+
writeSingleCoil(address: number, value: boolean): Promise<void>;
|
|
22
|
+
writeMultipleRegisters(address: number, values: Buffer): Promise<void>;
|
|
23
|
+
writeMultipleCoils(address: number, values: Buffer): Promise<void>;
|
|
24
|
+
close(): Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=mutex-transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mutex-transport.d.ts","sourceRoot":"","sources":["../src/mutex-transport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AACxD,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAEnC;;;;;;;;GAQG;AACH,qBAAa,cAAe,YAAW,SAAS;IAE5C,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,KAAK;gBADL,SAAS,EAAE,SAAS,EACpB,KAAK,EAAE,KAAK;IAGzB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAMrE,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAMnE,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAM1D,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAMnE,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMlE,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAM/D,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMtE,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMlE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAI7B"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wraps a transport with mutex-based serialization.
|
|
3
|
+
* All operations are executed sequentially to prevent concurrent access issues.
|
|
4
|
+
*
|
|
5
|
+
* Used for:
|
|
6
|
+
* - RTU transports: Prevents bus collisions when multiple devices share a serial bus
|
|
7
|
+
* - TCP transports: Many Modbus devices support only one connection or process
|
|
8
|
+
* requests sequentially. Serialization prevents timeouts and communication errors.
|
|
9
|
+
*/
|
|
10
|
+
export class MutexTransport {
|
|
11
|
+
transport;
|
|
12
|
+
mutex;
|
|
13
|
+
constructor(transport, mutex) {
|
|
14
|
+
this.transport = transport;
|
|
15
|
+
this.mutex = mutex;
|
|
16
|
+
}
|
|
17
|
+
async readHoldingRegisters(address, count) {
|
|
18
|
+
return this.mutex.runExclusive(() => {
|
|
19
|
+
return this.transport.readHoldingRegisters(address, count);
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
async readInputRegisters(address, count) {
|
|
23
|
+
return this.mutex.runExclusive(() => {
|
|
24
|
+
return this.transport.readInputRegisters(address, count);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
async readCoils(address, count) {
|
|
28
|
+
return this.mutex.runExclusive(() => {
|
|
29
|
+
return this.transport.readCoils(address, count);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
async readDiscreteInputs(address, count) {
|
|
33
|
+
return this.mutex.runExclusive(() => {
|
|
34
|
+
return this.transport.readDiscreteInputs(address, count);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
async writeSingleRegister(address, value) {
|
|
38
|
+
await this.mutex.runExclusive(async () => {
|
|
39
|
+
await this.transport.writeSingleRegister(address, value);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
async writeSingleCoil(address, value) {
|
|
43
|
+
await this.mutex.runExclusive(async () => {
|
|
44
|
+
await this.transport.writeSingleCoil(address, value);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
async writeMultipleRegisters(address, values) {
|
|
48
|
+
return this.mutex.runExclusive(() => {
|
|
49
|
+
return this.transport.writeMultipleRegisters(address, values);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
async writeMultipleCoils(address, values) {
|
|
53
|
+
return this.mutex.runExclusive(() => {
|
|
54
|
+
return this.transport.writeMultipleCoils(address, values);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
async close() {
|
|
58
|
+
// No mutex needed for close - just delegate
|
|
59
|
+
return this.transport.close();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=mutex-transport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mutex-transport.js","sourceRoot":"","sources":["../src/mutex-transport.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AACH,MAAM,OAAO,cAAc;IAEN;IACA;IAFnB,YACmB,SAAoB,EACpB,KAAY;QADZ,cAAS,GAAT,SAAS,CAAW;QACpB,UAAK,GAAL,KAAK,CAAO;IAC5B,CAAC;IAEJ,KAAK,CAAC,oBAAoB,CAAC,OAAe,EAAE,KAAa;QACvD,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,GAAG,EAAE;YAClC,OAAO,IAAI,CAAC,SAAS,CAAC,oBAAoB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;QAC5D,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,KAAK,CAAC,kBAAkB,CAAC,OAAe,EAAE,KAAa;QACrD,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,GAAG,EAAE;YAClC,OAAO,IAAI,CAAC,SAAS,CAAC,kBAAkB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;QAC1D,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,OAAe,EAAE,KAAa;QAC5C,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,GAAG,EAAE;YAClC,OAAO,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;QACjD,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,KAAK,CAAC,kBAAkB,CAAC,OAAe,EAAE,KAAa;QACrD,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,GAAG,EAAE;YAClC,OAAO,IAAI,CAAC,SAAS,CAAC,kBAAkB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;QAC1D,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,KAAK,CAAC,mBAAmB,CAAC,OAAe,EAAE,KAAa;QACtD,MAAM,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,KAAK,IAAI,EAAE;YACvC,MAAM,IAAI,CAAC,SAAS,CAAC,mBAAmB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;QAC1D,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,OAAe,EAAE,KAAc;QACnD,MAAM,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,KAAK,IAAI,EAAE;YACvC,MAAM,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;QACtD,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,KAAK,CAAC,sBAAsB,CAAC,OAAe,EAAE,MAAc;QAC1D,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,GAAG,EAAE;YAClC,OAAO,IAAI,CAAC,SAAS,CAAC,sBAAsB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;QAC/D,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,KAAK,CAAC,kBAAkB,CAAC,OAAe,EAAE,MAAc;QACtD,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,GAAG,EAAE;YAClC,OAAO,IAAI,CAAC,SAAS,CAAC,kBAAkB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;QAC3D,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,KAAK,CAAC,KAAK;QACT,4CAA4C;QAC5C,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAA;IAC/B,CAAC;CACF"}
|
package/dist/retry.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retry configuration and utilities for Modbus transport operations
|
|
3
|
+
*/
|
|
4
|
+
/** Maximum number of retry attempts for failed operations */
|
|
5
|
+
export declare const MAX_RETRIES = 3;
|
|
6
|
+
/** Delay between retry attempts in milliseconds */
|
|
7
|
+
export declare const RETRY_DELAY_MS = 100;
|
|
8
|
+
/**
|
|
9
|
+
* Logger function to track retry attempts
|
|
10
|
+
*
|
|
11
|
+
* @param attempt - The attempt number (1-indexed)
|
|
12
|
+
* @param error - The error that caused the retry
|
|
13
|
+
*/
|
|
14
|
+
export type RetryLogger = (attempt: number, error: Error) => void;
|
|
15
|
+
/**
|
|
16
|
+
* Retry a function with fixed delay between attempts
|
|
17
|
+
*
|
|
18
|
+
* @param fn - The async function to retry
|
|
19
|
+
* @param maxRetries - Maximum number of attempts (default: MAX_RETRIES)
|
|
20
|
+
* @param logger - Optional callback to log retry attempts
|
|
21
|
+
* @returns Promise resolving to the function's return value
|
|
22
|
+
* @throws The last error encountered if all retries are exhausted
|
|
23
|
+
*/
|
|
24
|
+
export declare function withRetry<T>(fn: () => Promise<T>, maxRetries?: number, logger?: RetryLogger): Promise<T>;
|
|
25
|
+
//# sourceMappingURL=retry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../src/retry.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,6DAA6D;AAC7D,eAAO,MAAM,WAAW,IAAI,CAAA;AAE5B,mDAAmD;AACnD,eAAO,MAAM,cAAc,MAAM,CAAA;AAYjC;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;AAEjE;;;;;;;;GAQG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAC/B,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,UAAU,GAAE,MAAoB,EAChC,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,CAAC,CAAC,CAqBZ"}
|
package/dist/retry.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retry configuration and utilities for Modbus transport operations
|
|
3
|
+
*/
|
|
4
|
+
/** Maximum number of retry attempts for failed operations */
|
|
5
|
+
export const MAX_RETRIES = 3;
|
|
6
|
+
/** Delay between retry attempts in milliseconds */
|
|
7
|
+
export const RETRY_DELAY_MS = 100;
|
|
8
|
+
/**
|
|
9
|
+
* Sleep for a specified duration
|
|
10
|
+
*
|
|
11
|
+
* @param ms - Duration in milliseconds
|
|
12
|
+
* @returns Promise that resolves after the specified duration
|
|
13
|
+
*/
|
|
14
|
+
function sleep(ms) {
|
|
15
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Retry a function with fixed delay between attempts
|
|
19
|
+
*
|
|
20
|
+
* @param fn - The async function to retry
|
|
21
|
+
* @param maxRetries - Maximum number of attempts (default: MAX_RETRIES)
|
|
22
|
+
* @param logger - Optional callback to log retry attempts
|
|
23
|
+
* @returns Promise resolving to the function's return value
|
|
24
|
+
* @throws The last error encountered if all retries are exhausted
|
|
25
|
+
*/
|
|
26
|
+
export async function withRetry(fn, maxRetries = MAX_RETRIES, logger) {
|
|
27
|
+
let lastError = new Error('No retry attempts were made');
|
|
28
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
29
|
+
try {
|
|
30
|
+
return await fn();
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
34
|
+
// Don't retry on the last attempt
|
|
35
|
+
if (attempt < maxRetries) {
|
|
36
|
+
// Log the retry attempt if logger is provided
|
|
37
|
+
if (logger) {
|
|
38
|
+
logger(attempt, lastError);
|
|
39
|
+
}
|
|
40
|
+
await sleep(RETRY_DELAY_MS);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
throw lastError;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=retry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.js","sourceRoot":"","sources":["../src/retry.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,6DAA6D;AAC7D,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,CAAA;AAE5B,mDAAmD;AACnD,MAAM,CAAC,MAAM,cAAc,GAAG,GAAG,CAAA;AAEjC;;;;;GAKG;AACH,SAAS,KAAK,CAAC,EAAU;IACvB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAA;AAC1D,CAAC;AAUD;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,EAAoB,EACpB,aAAqB,WAAW,EAChC,MAAoB;IAEpB,IAAI,SAAS,GAAU,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAA;IAE/D,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,UAAU,EAAE,OAAO,EAAE,EAAE,CAAC;QACvD,IAAI,CAAC;YACH,OAAO,MAAM,EAAE,EAAE,CAAA;QACnB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,SAAS,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;YAErE,kCAAkC;YAClC,IAAI,OAAO,GAAG,UAAU,EAAE,CAAC;gBACzB,8CAA8C;gBAC9C,IAAI,MAAM,EAAE,CAAC;oBACX,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,CAAA;gBAC5B,CAAC;gBACD,MAAM,KAAK,CAAC,cAAc,CAAC,CAAA;YAC7B,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,SAAS,CAAA;AACjB,CAAC"}
|