@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/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"}
@@ -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"}
@@ -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"}
@@ -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":"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"}
@@ -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"}
@@ -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"}