@ya-modbus/transport 0.7.1 → 0.7.2

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.
@@ -1 +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"}
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;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA"}
@@ -1,5 +1,5 @@
1
1
  import type { Transport } from '@ya-modbus/driver-types';
2
- import { type TransportConfig } from './factory.js';
2
+ import type { TransportConfig } from './factory.js';
3
3
  /**
4
4
  * Statistics about managed transports
5
5
  */
@@ -9,52 +9,77 @@ export interface TransportStats {
9
9
  tcpTransports: number;
10
10
  }
11
11
  /**
12
- * TransportManager pools transport instances and provides mutex-based
13
- * serialization for all transport operations to prevent concurrent access issues.
12
+ * TransportManager pools modbus-serial clients and provides slave-specific transports.
14
13
  *
15
14
  * 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
15
+ * - Pools modbus-serial clients by physical connection (excluding slave ID)
16
+ * - RTU clients are pooled by bus configuration (port, baud rate, parity, etc.)
17
+ * - TCP clients are pooled by host and port
18
+ * - Returns SlaveTransport instances that set the slave ID before each operation
19
+ * - Multiple devices on the same connection share a single client and mutex
20
20
  * - All operations are serialized using async-mutex to prevent race conditions
21
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
22
+ * This architecture solves the multi-device problem:
23
+ * - Each device gets its own SlaveTransport with its own slave ID
24
+ * - All devices on the same bus share the same client and mutex
25
+ * - Slave ID is set dynamically before each operation
26
+ * - No more hardcoded slave IDs that affect all devices on a bus
27
+ *
28
+ * IMPORTANT: Shared Resource Ownership
29
+ * Multiple SlaveTransport instances share the same client and mutex. This is
30
+ * intentional and correct:
31
+ * - Shared client: Avoids port contention (can't open serial port twice)
32
+ * - Shared mutex: Ensures serialized access to the bus
33
+ * - SlaveTransport.close(): Does not close shared client (use closeAll() for cleanup)
27
34
  */
28
35
  export declare class TransportManager {
29
- private readonly transports;
36
+ private readonly connections;
30
37
  /**
31
38
  * Get or create a transport for the given configuration.
32
- * Returns mutex-wrapped shared transport if one exists for the same connection.
39
+ * Returns a new SlaveTransport instance bound to the slave ID in the config.
40
+ * Multiple devices on the same physical connection share the same client and mutex.
33
41
  *
34
42
  * @param config - RTU or TCP transport configuration
35
- * @returns Transport instance wrapped with mutex for thread-safe operations
43
+ * @returns SlaveTransport instance for the specific slave device
36
44
  */
37
45
  getTransport(config: TransportConfig): Promise<Transport>;
38
46
  /**
39
- * Get statistics about managed transports
47
+ * Get statistics about managed connections
40
48
  *
41
49
  * @returns Transport statistics
42
50
  */
43
51
  getStats(): TransportStats;
44
52
  /**
45
- * Close all managed transports and clear the pool.
53
+ * Close all managed connections and clear the pool.
46
54
  * Errors during close are logged but do not stop the process.
47
55
  */
48
56
  closeAll(): Promise<void>;
49
57
  /**
50
- * Generate a unique key for a transport configuration.
58
+ * Generate a unique key for a physical connection.
59
+ * Excludes slave ID so multiple devices on the same bus share a connection.
51
60
  * For RTU: Key includes all bus parameters (port, baud, parity, etc.)
52
61
  * For TCP: Key includes host and port
53
62
  *
54
63
  * @param config - Transport configuration
55
- * @returns Connection key string
64
+ * @returns Physical connection key string
65
+ */
66
+ private getPhysicalConnectionKey;
67
+ /**
68
+ * Create and configure an RTU modbus-serial client.
69
+ * Does NOT set slave ID - that's handled by SlaveTransport.
70
+ *
71
+ * @param config - RTU configuration
72
+ * @returns Connected ModbusRTU client
73
+ */
74
+ private createRTUClient;
75
+ /**
76
+ * Create and configure a TCP modbus-serial client.
77
+ * Does NOT set slave ID - that's handled by SlaveTransport.
78
+ *
79
+ * @param config - TCP configuration
80
+ * @returns Connected ModbusRTU client
56
81
  */
57
- private getConnectionKey;
82
+ private createTCPClient;
58
83
  /**
59
84
  * Type guard to check if config is RTU
60
85
  *
@@ -1 +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"}
1
+ {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../../src/manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AAIxD,OAAO,KAAK,EAAwB,eAAe,EAAE,MAAM,cAAc,CAAA;AAazE;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,eAAe,EAAE,MAAM,CAAA;IACvB,aAAa,EAAE,MAAM,CAAA;IACrB,aAAa,EAAE,MAAM,CAAA;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqC;IAEjE;;;;;;;OAOG;IACG,YAAY,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,CAAC;IA6B/D;;;;OAIG;IACH,QAAQ,IAAI,cAAc;IAS1B;;;OAGG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB/B;;;;;;;;OAQG;IACH,OAAO,CAAC,wBAAwB;IAShC;;;;;;OAMG;YACW,eAAe;IAe7B;;;;;;OAMG;YACW,eAAe;IAU7B;;;;;OAKG;IACH,OAAO,CAAC,WAAW;CAGpB"}
@@ -1,86 +1,68 @@
1
1
  import { Mutex } from 'async-mutex';
2
- import { createTransport } from './factory.js';
3
- import { MutexTransport } from './mutex-transport.js';
2
+ import ModbusRTU from 'modbus-serial';
3
+ import { SlaveTransport } from './slave-transport.js';
4
4
  /**
5
- * TransportManager pools transport instances and provides mutex-based
6
- * serialization for all transport operations to prevent concurrent access issues.
5
+ * TransportManager pools modbus-serial clients and provides slave-specific transports.
7
6
  *
8
7
  * 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
8
+ * - Pools modbus-serial clients by physical connection (excluding slave ID)
9
+ * - RTU clients are pooled by bus configuration (port, baud rate, parity, etc.)
10
+ * - TCP clients are pooled by host and port
11
+ * - Returns SlaveTransport instances that set the slave ID before each operation
12
+ * - Multiple devices on the same connection share a single client and mutex
13
13
  * - All operations are serialized using async-mutex to prevent race conditions
14
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
15
+ * This architecture solves the multi-device problem:
16
+ * - Each device gets its own SlaveTransport with its own slave ID
17
+ * - All devices on the same bus share the same client and mutex
18
+ * - Slave ID is set dynamically before each operation
19
+ * - No more hardcoded slave IDs that affect all devices on a bus
20
+ *
21
+ * IMPORTANT: Shared Resource Ownership
22
+ * Multiple SlaveTransport instances share the same client and mutex. This is
23
+ * intentional and correct:
24
+ * - Shared client: Avoids port contention (can't open serial port twice)
25
+ * - Shared mutex: Ensures serialized access to the bus
26
+ * - SlaveTransport.close(): Does not close shared client (use closeAll() for cleanup)
20
27
  */
21
28
  export class TransportManager {
22
29
  constructor() {
23
- this.transports = new Map();
30
+ this.connections = new Map();
24
31
  }
25
32
  /**
26
33
  * Get or create a transport for the given configuration.
27
- * Returns mutex-wrapped shared transport if one exists for the same connection.
34
+ * Returns a new SlaveTransport instance bound to the slave ID in the config.
35
+ * Multiple devices on the same physical connection share the same client and mutex.
28
36
  *
29
37
  * @param config - RTU or TCP transport configuration
30
- * @returns Transport instance wrapped with mutex for thread-safe operations
38
+ * @returns SlaveTransport instance for the specific slave device
31
39
  */
32
40
  async getTransport(config) {
33
- const key = this.getConnectionKey(config);
41
+ const key = this.getPhysicalConnectionKey(config);
34
42
  const isRTU = this.isRTUConfig(config);
35
- // For RTU, reuse existing transport if available
36
- if (isRTU) {
37
- const entry = this.transports.get(key);
38
- if (entry) {
39
- return entry.wrappedTransport;
40
- }
41
- // Create new RTU transport with mutex wrapper
42
- const rawTransport = await createTransport(config);
43
- const mutex = new Mutex();
44
- const wrappedTransport = new MutexTransport(rawTransport, mutex);
45
- const newEntry = {
46
- rawTransport,
47
- wrappedTransport,
48
- mutex,
43
+ // Get or create the shared connection
44
+ let entry = this.connections.get(key);
45
+ if (!entry) {
46
+ // Create new client for this physical connection
47
+ const client = isRTU ? await this.createRTUClient(config) : await this.createTCPClient(config);
48
+ entry = {
49
+ client,
50
+ mutex: new Mutex(),
49
51
  config,
50
- isRTU: true,
52
+ isRTU,
51
53
  };
52
- this.transports.set(key, newEntry);
53
- return wrappedTransport;
54
+ this.connections.set(key, entry);
54
55
  }
55
- // For TCP, reuse existing transport if available
56
- // TCP is pooled like RTU because many devices support only one connection
57
- // or process requests sequentially even with multiple connections
58
- const entry = this.transports.get(key);
59
- if (entry) {
60
- return entry.wrappedTransport;
61
- }
62
- // Create new TCP transport with mutex wrapper
63
- // Mutex prevents concurrent requests that could cause timeouts or errors
64
- const rawTransport = await createTransport(config);
65
- const mutex = new Mutex();
66
- const wrappedTransport = new MutexTransport(rawTransport, mutex);
67
- const newEntry = {
68
- rawTransport,
69
- wrappedTransport,
70
- mutex,
71
- config,
72
- isRTU: false,
73
- };
74
- this.transports.set(key, newEntry);
75
- return wrappedTransport;
56
+ // Return a new SlaveTransport for this specific slave
57
+ return new SlaveTransport(config.slaveId, entry.client, entry.mutex, config.maxRetries ?? 3, config.logger);
76
58
  }
77
59
  /**
78
- * Get statistics about managed transports
60
+ * Get statistics about managed connections
79
61
  *
80
62
  * @returns Transport statistics
81
63
  */
82
64
  getStats() {
83
- const entries = Array.from(this.transports.values());
65
+ const entries = Array.from(this.connections.values());
84
66
  return {
85
67
  totalTransports: entries.length,
86
68
  rtuTransports: entries.filter((e) => e.isRTU).length,
@@ -88,37 +70,71 @@ export class TransportManager {
88
70
  };
89
71
  }
90
72
  /**
91
- * Close all managed transports and clear the pool.
73
+ * Close all managed connections and clear the pool.
92
74
  * Errors during close are logged but do not stop the process.
93
75
  */
94
76
  async closeAll() {
95
- const closePromises = Array.from(this.transports.values()).map(async (entry) => {
77
+ const closePromises = Array.from(this.connections.values()).map(async (entry) => {
96
78
  try {
97
- await entry.rawTransport.close();
79
+ await new Promise((resolve) => {
80
+ entry.client.close(resolve);
81
+ });
98
82
  }
99
83
  catch (error) {
100
- // Log but don't throw - we want to close all transports
101
- console.error('Error closing transport:', error);
84
+ // Log but don't throw - we want to close all connections
85
+ console.error('Error closing connection:', error);
102
86
  }
103
87
  });
104
88
  await Promise.all(closePromises);
105
- this.transports.clear();
89
+ this.connections.clear();
106
90
  }
107
91
  /**
108
- * Generate a unique key for a transport configuration.
92
+ * Generate a unique key for a physical connection.
93
+ * Excludes slave ID so multiple devices on the same bus share a connection.
109
94
  * For RTU: Key includes all bus parameters (port, baud, parity, etc.)
110
95
  * For TCP: Key includes host and port
111
96
  *
112
97
  * @param config - Transport configuration
113
- * @returns Connection key string
98
+ * @returns Physical connection key string
114
99
  */
115
- getConnectionKey(config) {
100
+ getPhysicalConnectionKey(config) {
116
101
  if (this.isRTUConfig(config)) {
117
102
  return `rtu:${config.port}:${config.baudRate}:${config.dataBits}:${config.parity}:${config.stopBits}`;
118
103
  }
119
104
  // TCP config - safe to access host/port because isRTUConfig returned false
120
105
  return `tcp:${config.host}:${config.port ?? 502}`;
121
106
  }
107
+ /**
108
+ * Create and configure an RTU modbus-serial client.
109
+ * Does NOT set slave ID - that's handled by SlaveTransport.
110
+ *
111
+ * @param config - RTU configuration
112
+ * @returns Connected ModbusRTU client
113
+ */
114
+ async createRTUClient(config) {
115
+ const client = new ModbusRTU();
116
+ await client.connectRTUBuffered(config.port, {
117
+ baudRate: config.baudRate,
118
+ dataBits: config.dataBits,
119
+ parity: config.parity,
120
+ stopBits: config.stopBits,
121
+ });
122
+ client.setTimeout(config.timeout ?? 1000);
123
+ return client;
124
+ }
125
+ /**
126
+ * Create and configure a TCP modbus-serial client.
127
+ * Does NOT set slave ID - that's handled by SlaveTransport.
128
+ *
129
+ * @param config - TCP configuration
130
+ * @returns Connected ModbusRTU client
131
+ */
132
+ async createTCPClient(config) {
133
+ const client = new ModbusRTU();
134
+ await client.connectTCP(config.host, { port: config.port ?? 502 });
135
+ client.setTimeout(config.timeout ?? 1000);
136
+ return client;
137
+ }
122
138
  /**
123
139
  * Type guard to check if config is RTU
124
140
  *
@@ -1 +1 @@
1
- {"version":3,"file":"manager.js","sourceRoot":"","sources":["../../../src/manager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"manager.js","sourceRoot":"","sources":["../../../src/manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ import type { Transport } from '@ya-modbus/driver-types';
2
+ import type { Mutex } from 'async-mutex';
3
+ import type ModbusRTU from 'modbus-serial';
4
+ import { type RetryLogger } from './retry.js';
5
+ /**
6
+ * SlaveTransport wraps a shared ModbusRTU client with slave-specific addressing.
7
+ *
8
+ * Multiple SlaveTransport instances can share the same physical connection (serial port or TCP),
9
+ * each communicating with a different slave device. The slave ID is set before each operation
10
+ * to ensure requests are directed to the correct device.
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * const client = new ModbusRTU()
15
+ * await client.connectRTUBuffered('/dev/ttyUSB0', { baudRate: 9600 })
16
+ * const mutex = new Mutex()
17
+ *
18
+ * // Two devices on the same serial port
19
+ * const device1 = new SlaveTransport(1, client, mutex, 3)
20
+ * const device2 = new SlaveTransport(2, client, mutex, 3)
21
+ *
22
+ * // Each device talks to its own slave ID
23
+ * await device1.readHoldingRegisters(0, 10) // Reads from slave 1
24
+ * await device2.readHoldingRegisters(0, 10) // Reads from slave 2
25
+ * ```
26
+ */
27
+ export declare class SlaveTransport implements Transport {
28
+ private readonly slaveId;
29
+ private readonly client;
30
+ private readonly mutex;
31
+ private readonly maxRetries;
32
+ private readonly logger?;
33
+ /**
34
+ * @param slaveId - Modbus slave/unit ID for this device
35
+ * @param client - Shared ModbusRTU client instance
36
+ * @param mutex - Shared mutex for serializing operations on the bus
37
+ * @param maxRetries - Maximum retry attempts for failed operations (default: 3)
38
+ * @param logger - Optional callback to log retry attempts
39
+ */
40
+ constructor(slaveId: number, client: ModbusRTU, mutex: Mutex, maxRetries?: number, logger?: RetryLogger | undefined);
41
+ readHoldingRegisters(address: number, count: number): Promise<Buffer>;
42
+ readInputRegisters(address: number, count: number): Promise<Buffer>;
43
+ readCoils(address: number, count: number): Promise<Buffer>;
44
+ readDiscreteInputs(address: number, count: number): Promise<Buffer>;
45
+ writeSingleRegister(address: number, value: number): Promise<void>;
46
+ writeMultipleRegisters(address: number, values: Buffer): Promise<void>;
47
+ writeSingleCoil(address: number, value: boolean): Promise<void>;
48
+ writeMultipleCoils(address: number, values: Buffer): Promise<void>;
49
+ /**
50
+ * Close the underlying client connection.
51
+ *
52
+ * IMPORTANT: This method does not use mutex protection and should only
53
+ * be called during shutdown via TransportManager.closeAll(). Calling
54
+ * this while operations are in progress may cause connection errors.
55
+ *
56
+ * Individual SlaveTransport instances should NOT call close() directly
57
+ * as the client is shared across multiple devices on the same bus.
58
+ */
59
+ close(): Promise<void>;
60
+ }
61
+ //# sourceMappingURL=slave-transport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slave-transport.d.ts","sourceRoot":"","sources":["../../../src/slave-transport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AACxD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,KAAK,SAAS,MAAM,eAAe,CAAA;AAE1C,OAAO,EAAa,KAAK,WAAW,EAAE,MAAM,YAAY,CAAA;AAExD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,cAAe,YAAW,SAAS;IAS5C,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IAZ1B;;;;;;OAMG;gBAEgB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,SAAS,EACjB,KAAK,EAAE,KAAK,EACZ,UAAU,GAAE,MAAU,EACtB,MAAM,CAAC,EAAE,WAAW,YAAA;IAGjC,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAcrE,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAcnE,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAc1D,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAcnE,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAalE,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAatE,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAa/D,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBxE;;;;;;;;;OASG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAM7B"}
@@ -0,0 +1,132 @@
1
+ import { withRetry } from './retry.js';
2
+ /**
3
+ * SlaveTransport wraps a shared ModbusRTU client with slave-specific addressing.
4
+ *
5
+ * Multiple SlaveTransport instances can share the same physical connection (serial port or TCP),
6
+ * each communicating with a different slave device. The slave ID is set before each operation
7
+ * to ensure requests are directed to the correct device.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * const client = new ModbusRTU()
12
+ * await client.connectRTUBuffered('/dev/ttyUSB0', { baudRate: 9600 })
13
+ * const mutex = new Mutex()
14
+ *
15
+ * // Two devices on the same serial port
16
+ * const device1 = new SlaveTransport(1, client, mutex, 3)
17
+ * const device2 = new SlaveTransport(2, client, mutex, 3)
18
+ *
19
+ * // Each device talks to its own slave ID
20
+ * await device1.readHoldingRegisters(0, 10) // Reads from slave 1
21
+ * await device2.readHoldingRegisters(0, 10) // Reads from slave 2
22
+ * ```
23
+ */
24
+ export class SlaveTransport {
25
+ /**
26
+ * @param slaveId - Modbus slave/unit ID for this device
27
+ * @param client - Shared ModbusRTU client instance
28
+ * @param mutex - Shared mutex for serializing operations on the bus
29
+ * @param maxRetries - Maximum retry attempts for failed operations (default: 3)
30
+ * @param logger - Optional callback to log retry attempts
31
+ */
32
+ constructor(slaveId, client, mutex, maxRetries = 3, logger) {
33
+ this.slaveId = slaveId;
34
+ this.client = client;
35
+ this.mutex = mutex;
36
+ this.maxRetries = maxRetries;
37
+ this.logger = logger;
38
+ }
39
+ async readHoldingRegisters(address, count) {
40
+ return this.mutex.runExclusive(async () => {
41
+ return withRetry(async () => {
42
+ this.client.setID(this.slaveId);
43
+ const result = await this.client.readHoldingRegisters(address, count);
44
+ return result.buffer;
45
+ }, this.maxRetries, this.logger);
46
+ });
47
+ }
48
+ async readInputRegisters(address, count) {
49
+ return this.mutex.runExclusive(async () => {
50
+ return withRetry(async () => {
51
+ this.client.setID(this.slaveId);
52
+ const result = await this.client.readInputRegisters(address, count);
53
+ return result.buffer;
54
+ }, this.maxRetries, this.logger);
55
+ });
56
+ }
57
+ async readCoils(address, count) {
58
+ return this.mutex.runExclusive(async () => {
59
+ return withRetry(async () => {
60
+ this.client.setID(this.slaveId);
61
+ const result = await this.client.readCoils(address, count);
62
+ return result.buffer;
63
+ }, this.maxRetries, this.logger);
64
+ });
65
+ }
66
+ async readDiscreteInputs(address, count) {
67
+ return this.mutex.runExclusive(async () => {
68
+ return withRetry(async () => {
69
+ this.client.setID(this.slaveId);
70
+ const result = await this.client.readDiscreteInputs(address, count);
71
+ return result.buffer;
72
+ }, this.maxRetries, this.logger);
73
+ });
74
+ }
75
+ async writeSingleRegister(address, value) {
76
+ return this.mutex.runExclusive(async () => {
77
+ return withRetry(async () => {
78
+ this.client.setID(this.slaveId);
79
+ await this.client.writeRegister(address, value);
80
+ }, this.maxRetries, this.logger);
81
+ });
82
+ }
83
+ async writeMultipleRegisters(address, values) {
84
+ return this.mutex.runExclusive(async () => {
85
+ return withRetry(async () => {
86
+ this.client.setID(this.slaveId);
87
+ await this.client.writeRegisters(address, values);
88
+ }, this.maxRetries, this.logger);
89
+ });
90
+ }
91
+ async writeSingleCoil(address, value) {
92
+ return this.mutex.runExclusive(async () => {
93
+ return withRetry(async () => {
94
+ this.client.setID(this.slaveId);
95
+ await this.client.writeCoil(address, value);
96
+ }, this.maxRetries, this.logger);
97
+ });
98
+ }
99
+ async writeMultipleCoils(address, values) {
100
+ return this.mutex.runExclusive(async () => {
101
+ return withRetry(async () => {
102
+ this.client.setID(this.slaveId);
103
+ // Convert Buffer to boolean array
104
+ const bools = [];
105
+ for (let i = 0; i < values.length * 8; i++) {
106
+ const byteIndex = Math.floor(i / 8);
107
+ const bitIndex = i % 8;
108
+ const byte = values[byteIndex];
109
+ bools.push((byte & (1 << bitIndex)) !== 0);
110
+ }
111
+ await this.client.writeCoils(address, bools);
112
+ }, this.maxRetries, this.logger);
113
+ });
114
+ }
115
+ /**
116
+ * Close the underlying client connection.
117
+ *
118
+ * IMPORTANT: This method does not use mutex protection and should only
119
+ * be called during shutdown via TransportManager.closeAll(). Calling
120
+ * this while operations are in progress may cause connection errors.
121
+ *
122
+ * Individual SlaveTransport instances should NOT call close() directly
123
+ * as the client is shared across multiple devices on the same bus.
124
+ */
125
+ async close() {
126
+ // Don't use mutex for close operation
127
+ return new Promise((resolve) => {
128
+ this.client.close(resolve);
129
+ });
130
+ }
131
+ }
132
+ //# sourceMappingURL=slave-transport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slave-transport.js","sourceRoot":"","sources":["../../../src/slave-transport.ts"],"names":[],"mappings":"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"}