@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.
- package/CHANGELOG.md +6 -0
- package/dist/cjs/src/index.d.ts +1 -0
- package/dist/cjs/src/index.d.ts.map +1 -1
- package/dist/cjs/src/index.js +3 -1
- package/dist/cjs/src/index.js.map +1 -1
- package/dist/cjs/src/manager.d.ts +45 -20
- package/dist/cjs/src/manager.d.ts.map +1 -1
- package/dist/cjs/src/manager.js +83 -66
- package/dist/cjs/src/manager.js.map +1 -1
- package/dist/cjs/src/slave-transport.d.ts +61 -0
- package/dist/cjs/src/slave-transport.d.ts.map +1 -0
- package/dist/cjs/src/slave-transport.js +136 -0
- package/dist/cjs/src/slave-transport.js.map +1 -0
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/src/index.d.ts +1 -0
- package/dist/esm/src/index.d.ts.map +1 -1
- package/dist/esm/src/index.js +1 -0
- package/dist/esm/src/index.js.map +1 -1
- package/dist/esm/src/manager.d.ts +45 -20
- package/dist/esm/src/manager.d.ts.map +1 -1
- package/dist/esm/src/manager.js +82 -66
- package/dist/esm/src/manager.js.map +1 -1
- package/dist/esm/src/slave-transport.d.ts +61 -0
- package/dist/esm/src/slave-transport.d.ts.map +1 -0
- package/dist/esm/src/slave-transport.js +132 -0
- package/dist/esm/src/slave-transport.js.map +1 -0
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/package.json +2 -2
|
@@ -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 {
|
|
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
|
|
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
|
-
* -
|
|
17
|
-
* - RTU
|
|
18
|
-
* - TCP
|
|
19
|
-
* -
|
|
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
|
-
*
|
|
23
|
-
* -
|
|
24
|
-
* -
|
|
25
|
-
* -
|
|
26
|
-
* -
|
|
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
|
|
36
|
+
private readonly connections;
|
|
30
37
|
/**
|
|
31
38
|
* Get or create a transport for the given configuration.
|
|
32
|
-
* Returns
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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;
|
|
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"}
|
package/dist/esm/src/manager.js
CHANGED
|
@@ -1,86 +1,68 @@
|
|
|
1
1
|
import { Mutex } from 'async-mutex';
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
2
|
+
import ModbusRTU from 'modbus-serial';
|
|
3
|
+
import { SlaveTransport } from './slave-transport.js';
|
|
4
4
|
/**
|
|
5
|
-
* TransportManager pools
|
|
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
|
-
* -
|
|
10
|
-
* - RTU
|
|
11
|
-
* - TCP
|
|
12
|
-
* -
|
|
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
|
-
*
|
|
16
|
-
* -
|
|
17
|
-
* -
|
|
18
|
-
* -
|
|
19
|
-
* -
|
|
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.
|
|
30
|
+
this.connections = new Map();
|
|
24
31
|
}
|
|
25
32
|
/**
|
|
26
33
|
* Get or create a transport for the given configuration.
|
|
27
|
-
* Returns
|
|
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
|
|
38
|
+
* @returns SlaveTransport instance for the specific slave device
|
|
31
39
|
*/
|
|
32
40
|
async getTransport(config) {
|
|
33
|
-
const key = this.
|
|
41
|
+
const key = this.getPhysicalConnectionKey(config);
|
|
34
42
|
const isRTU = this.isRTUConfig(config);
|
|
35
|
-
//
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
|
52
|
+
isRTU,
|
|
51
53
|
};
|
|
52
|
-
this.
|
|
53
|
-
return wrappedTransport;
|
|
54
|
+
this.connections.set(key, entry);
|
|
54
55
|
}
|
|
55
|
-
//
|
|
56
|
-
|
|
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
|
|
60
|
+
* Get statistics about managed connections
|
|
79
61
|
*
|
|
80
62
|
* @returns Transport statistics
|
|
81
63
|
*/
|
|
82
64
|
getStats() {
|
|
83
|
-
const entries = Array.from(this.
|
|
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
|
|
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.
|
|
77
|
+
const closePromises = Array.from(this.connections.values()).map(async (entry) => {
|
|
96
78
|
try {
|
|
97
|
-
await
|
|
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
|
|
101
|
-
console.error('Error closing
|
|
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.
|
|
89
|
+
this.connections.clear();
|
|
106
90
|
}
|
|
107
91
|
/**
|
|
108
|
-
* Generate a unique key for a
|
|
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
|
|
98
|
+
* @returns Physical connection key string
|
|
114
99
|
*/
|
|
115
|
-
|
|
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":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;
|
|
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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"}
|