@ya-modbus/mqtt-bridge 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 +41 -0
- package/LICENSE +674 -0
- package/README.md +190 -0
- package/dist/bin/ya-modbus-bridge.d.ts +9 -0
- package/dist/bin/ya-modbus-bridge.d.ts.map +1 -0
- package/dist/bin/ya-modbus-bridge.js +10 -0
- package/dist/bin/ya-modbus-bridge.js.map +1 -0
- package/dist/src/cli.d.ts +4 -0
- package/dist/src/cli.d.ts.map +1 -0
- package/dist/src/cli.js +109 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/device-manager.d.ts +17 -0
- package/dist/src/device-manager.d.ts.map +1 -0
- package/dist/src/device-manager.js +79 -0
- package/dist/src/device-manager.js.map +1 -0
- package/dist/src/driver-loader.d.ts +53 -0
- package/dist/src/driver-loader.d.ts.map +1 -0
- package/dist/src/driver-loader.js +120 -0
- package/dist/src/driver-loader.js.map +1 -0
- package/dist/src/index.d.ts +13 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +285 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/polling-scheduler.d.ts +48 -0
- package/dist/src/polling-scheduler.d.ts.map +1 -0
- package/dist/src/polling-scheduler.js +128 -0
- package/dist/src/polling-scheduler.js.map +1 -0
- package/dist/src/types.d.ts +86 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/utils/__mocks__/package-info.d.ts +9 -0
- package/dist/src/utils/__mocks__/package-info.d.ts.map +1 -0
- package/dist/src/utils/__mocks__/package-info.js +11 -0
- package/dist/src/utils/__mocks__/package-info.js.map +1 -0
- package/dist/src/utils/__mocks__/process.d.ts +20 -0
- package/dist/src/utils/__mocks__/process.d.ts.map +1 -0
- package/dist/src/utils/__mocks__/process.js +37 -0
- package/dist/src/utils/__mocks__/process.js.map +1 -0
- package/dist/src/utils/config-validator.d.ts +3 -0
- package/dist/src/utils/config-validator.d.ts.map +1 -0
- package/dist/src/utils/config-validator.js +32 -0
- package/dist/src/utils/config-validator.js.map +1 -0
- package/dist/src/utils/config.d.ts +3 -0
- package/dist/src/utils/config.d.ts.map +1 -0
- package/dist/src/utils/config.js +52 -0
- package/dist/src/utils/config.js.map +1 -0
- package/dist/src/utils/device-validation.d.ts +31 -0
- package/dist/src/utils/device-validation.d.ts.map +1 -0
- package/dist/src/utils/device-validation.js +70 -0
- package/dist/src/utils/device-validation.js.map +1 -0
- package/dist/src/utils/package-info.d.ts +5 -0
- package/dist/src/utils/package-info.d.ts.map +1 -0
- package/dist/src/utils/package-info.js +11 -0
- package/dist/src/utils/package-info.js.map +1 -0
- package/dist/src/utils/process.d.ts +10 -0
- package/dist/src/utils/process.d.ts.map +1 -0
- package/dist/src/utils/process.js +13 -0
- package/dist/src/utils/process.js.map +1 -0
- package/dist/src/utils/test-utils.d.ts +313 -0
- package/dist/src/utils/test-utils.d.ts.map +1 -0
- package/dist/src/utils/test-utils.js +535 -0
- package/dist/src/utils/test-utils.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +63 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import mqtt from 'mqtt';
|
|
2
|
+
import { DeviceManager } from './device-manager.js';
|
|
3
|
+
import { DriverLoader } from './driver-loader.js';
|
|
4
|
+
import { PollingScheduler } from './polling-scheduler.js';
|
|
5
|
+
export { loadConfig } from './utils/config.js';
|
|
6
|
+
export function createBridge(config, dependencies) {
|
|
7
|
+
let status = {
|
|
8
|
+
state: 'stopped',
|
|
9
|
+
timestamp: Date.now(),
|
|
10
|
+
};
|
|
11
|
+
let client = null;
|
|
12
|
+
const topicPrefix = config.topicPrefix ?? 'modbus';
|
|
13
|
+
// Subscriptions map needed because mqtt client doesn't expose handler lookup
|
|
14
|
+
const subscriptions = new Map();
|
|
15
|
+
// Use injected dependencies or create new instances
|
|
16
|
+
const driverLoader = dependencies?.driverLoader ?? new DriverLoader();
|
|
17
|
+
const deviceManager = dependencies?.deviceManager ?? new DeviceManager(driverLoader);
|
|
18
|
+
// Bridge reference to be set after bridge object is created
|
|
19
|
+
// This ensures publishData is always defined before PollingScheduler starts
|
|
20
|
+
let bridgeRef = null;
|
|
21
|
+
// Publish function that will be called from polling
|
|
22
|
+
const publishData = (deviceId, data) => {
|
|
23
|
+
if (!bridgeRef) {
|
|
24
|
+
// Bridge not fully initialized yet - skip publishing
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const payload = JSON.stringify({
|
|
28
|
+
deviceId,
|
|
29
|
+
timestamp: Date.now(),
|
|
30
|
+
data,
|
|
31
|
+
});
|
|
32
|
+
// Publish to device-specific topic
|
|
33
|
+
void bridgeRef.publish(`${deviceId}/data`, payload, { qos: 0 }).catch((error) => {
|
|
34
|
+
console.error(`Failed to publish data for device ${deviceId}:`, error);
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
// Handle data from polling
|
|
38
|
+
const handlePollingData = (deviceId, data) => {
|
|
39
|
+
publishData(deviceId, data);
|
|
40
|
+
// Update device status and reset failure count on success
|
|
41
|
+
deviceManager.updateDeviceState(deviceId, {
|
|
42
|
+
lastPoll: Date.now(),
|
|
43
|
+
lastUpdate: Date.now(),
|
|
44
|
+
consecutiveFailures: 0,
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
// Handle polling errors
|
|
48
|
+
const handlePollingError = (deviceId, error, failureCount) => {
|
|
49
|
+
console.error(`Polling error for device ${deviceId}:`, error);
|
|
50
|
+
deviceManager.updateDeviceState(deviceId, {
|
|
51
|
+
consecutiveFailures: failureCount,
|
|
52
|
+
errors: [error.message],
|
|
53
|
+
});
|
|
54
|
+
};
|
|
55
|
+
const pollingScheduler = dependencies?.pollingScheduler ?? new PollingScheduler(handlePollingData, handlePollingError);
|
|
56
|
+
const bridge = {
|
|
57
|
+
start() {
|
|
58
|
+
return new Promise((resolve, reject) => {
|
|
59
|
+
status = {
|
|
60
|
+
state: 'starting',
|
|
61
|
+
timestamp: Date.now(),
|
|
62
|
+
};
|
|
63
|
+
const mqttOptions = {
|
|
64
|
+
clean: true,
|
|
65
|
+
reconnectPeriod: config.mqtt.reconnectPeriod ?? 5000,
|
|
66
|
+
resubscribe: true, // Automatic resubscription on reconnect (default: true)
|
|
67
|
+
};
|
|
68
|
+
if (config.mqtt.clientId) {
|
|
69
|
+
mqttOptions.clientId = config.mqtt.clientId;
|
|
70
|
+
}
|
|
71
|
+
if (config.mqtt.username) {
|
|
72
|
+
mqttOptions.username = config.mqtt.username;
|
|
73
|
+
}
|
|
74
|
+
if (config.mqtt.password) {
|
|
75
|
+
mqttOptions.password = config.mqtt.password;
|
|
76
|
+
}
|
|
77
|
+
client = mqtt.connect(config.mqtt.url, mqttOptions);
|
|
78
|
+
let isInitialConnection = true;
|
|
79
|
+
client.on('connect', () => {
|
|
80
|
+
status = {
|
|
81
|
+
state: 'running',
|
|
82
|
+
timestamp: Date.now(),
|
|
83
|
+
};
|
|
84
|
+
// Automatic resubscription is handled by mqtt.js (resubscribe: true)
|
|
85
|
+
// Start polling scheduler
|
|
86
|
+
pollingScheduler.start();
|
|
87
|
+
if (isInitialConnection) {
|
|
88
|
+
isInitialConnection = false;
|
|
89
|
+
resolve();
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
client.on('error', (error) => {
|
|
93
|
+
status = {
|
|
94
|
+
state: 'error',
|
|
95
|
+
timestamp: Date.now(),
|
|
96
|
+
errors: [error.message],
|
|
97
|
+
};
|
|
98
|
+
reject(error);
|
|
99
|
+
});
|
|
100
|
+
// No need to track disconnect/offline - client.connected property handles this
|
|
101
|
+
client.on('reconnect', () => {
|
|
102
|
+
// Note: reconnect event fires when attempting to reconnect
|
|
103
|
+
// mqttConnected status is updated by 'connect' event
|
|
104
|
+
});
|
|
105
|
+
client.on('message', (topic, payload, packet) => {
|
|
106
|
+
const handler = subscriptions.get(topic);
|
|
107
|
+
if (handler) {
|
|
108
|
+
const message = {
|
|
109
|
+
topic,
|
|
110
|
+
payload,
|
|
111
|
+
qos: packet.qos,
|
|
112
|
+
retain: packet.retain,
|
|
113
|
+
};
|
|
114
|
+
try {
|
|
115
|
+
handler(message);
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
// Prevent handler errors from crashing the bridge
|
|
119
|
+
console.error(`Error in message handler for topic ${topic}:`, error);
|
|
120
|
+
status = {
|
|
121
|
+
...status,
|
|
122
|
+
errors: [
|
|
123
|
+
...(status.errors ?? []),
|
|
124
|
+
/* istanbul ignore next - defensive: handlers always throw Error */
|
|
125
|
+
`Handler error for ${topic}: ${error instanceof Error ? error.message : String(error)}`,
|
|
126
|
+
],
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
},
|
|
133
|
+
async stop() {
|
|
134
|
+
status = {
|
|
135
|
+
state: 'stopping',
|
|
136
|
+
timestamp: Date.now(),
|
|
137
|
+
};
|
|
138
|
+
// Stop polling first
|
|
139
|
+
pollingScheduler.stop();
|
|
140
|
+
// Cleanup device manager (unloads drivers)
|
|
141
|
+
await deviceManager.clear();
|
|
142
|
+
// Close all transports managed by the transport manager
|
|
143
|
+
await driverLoader.closeAllTransports();
|
|
144
|
+
if (client) {
|
|
145
|
+
// Cleanup event listeners and subscriptions to prevent memory leaks
|
|
146
|
+
client.removeAllListeners();
|
|
147
|
+
subscriptions.clear();
|
|
148
|
+
await new Promise((resolve) => {
|
|
149
|
+
if (!client) {
|
|
150
|
+
resolve();
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
client.end(false, {}, () => {
|
|
154
|
+
status = {
|
|
155
|
+
state: 'stopped',
|
|
156
|
+
timestamp: Date.now(),
|
|
157
|
+
};
|
|
158
|
+
client = null;
|
|
159
|
+
resolve();
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
status = {
|
|
165
|
+
state: 'stopped',
|
|
166
|
+
timestamp: Date.now(),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
getStatus() {
|
|
171
|
+
return {
|
|
172
|
+
...status,
|
|
173
|
+
deviceCount: deviceManager.getDeviceCount(),
|
|
174
|
+
// Use client.connected property directly
|
|
175
|
+
mqttConnected: client?.connected ?? false,
|
|
176
|
+
};
|
|
177
|
+
},
|
|
178
|
+
publish(topic, payload, options) {
|
|
179
|
+
return new Promise((resolve, reject) => {
|
|
180
|
+
if (!client) {
|
|
181
|
+
reject(new Error('MQTT client not initialized'));
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
if (!client.connected) {
|
|
185
|
+
reject(new Error('MQTT client not connected'));
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
const fullTopic = `${topicPrefix}/${topic}`;
|
|
189
|
+
const publishOptions = {};
|
|
190
|
+
if (options?.qos !== undefined) {
|
|
191
|
+
publishOptions.qos = options.qos;
|
|
192
|
+
}
|
|
193
|
+
if (options?.retain !== undefined) {
|
|
194
|
+
publishOptions.retain = options.retain;
|
|
195
|
+
}
|
|
196
|
+
client.publish(fullTopic, payload, publishOptions, (error) => {
|
|
197
|
+
if (error) {
|
|
198
|
+
reject(error);
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
resolve();
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
},
|
|
206
|
+
subscribe(topic, handler, options) {
|
|
207
|
+
return new Promise((resolve, reject) => {
|
|
208
|
+
if (!client) {
|
|
209
|
+
reject(new Error('MQTT client not initialized'));
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
if (!client.connected) {
|
|
213
|
+
reject(new Error('MQTT client not connected'));
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
const fullTopic = `${topicPrefix}/${topic}`;
|
|
217
|
+
const subscribeOptions = {
|
|
218
|
+
qos: options?.qos ?? 0,
|
|
219
|
+
};
|
|
220
|
+
client.subscribe(fullTopic, subscribeOptions, (error) => {
|
|
221
|
+
if (error) {
|
|
222
|
+
reject(error);
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
subscriptions.set(fullTopic, handler);
|
|
226
|
+
resolve();
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
},
|
|
231
|
+
unsubscribe(topic) {
|
|
232
|
+
return new Promise((resolve, reject) => {
|
|
233
|
+
if (!client) {
|
|
234
|
+
reject(new Error('MQTT client not initialized'));
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
if (!client.connected) {
|
|
238
|
+
reject(new Error('MQTT client not connected'));
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
const fullTopic = `${topicPrefix}/${topic}`;
|
|
242
|
+
client.unsubscribe(fullTopic, (error) => {
|
|
243
|
+
if (error) {
|
|
244
|
+
reject(error);
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
subscriptions.delete(fullTopic);
|
|
248
|
+
resolve();
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
},
|
|
253
|
+
async addDevice(deviceConfig) {
|
|
254
|
+
await deviceManager.addDevice(deviceConfig);
|
|
255
|
+
// Schedule device for polling if enabled
|
|
256
|
+
if (deviceConfig.enabled !== false) {
|
|
257
|
+
const driver = driverLoader.getDriver(deviceConfig.deviceId);
|
|
258
|
+
if (!driver) {
|
|
259
|
+
throw new Error(`Driver for ${deviceConfig.deviceId} not found after loading. ` +
|
|
260
|
+
`This may indicate the device was removed during initialization.`);
|
|
261
|
+
}
|
|
262
|
+
pollingScheduler.scheduleDevice(deviceConfig.deviceId, deviceConfig, driver);
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
async removeDevice(deviceId) {
|
|
266
|
+
// Unschedule polling first
|
|
267
|
+
pollingScheduler.unscheduleDevice(deviceId);
|
|
268
|
+
// Remove device (unloads driver)
|
|
269
|
+
await deviceManager.removeDevice(deviceId);
|
|
270
|
+
},
|
|
271
|
+
getDevice(deviceId) {
|
|
272
|
+
return deviceManager.getDevice(deviceId);
|
|
273
|
+
},
|
|
274
|
+
listDevices() {
|
|
275
|
+
return deviceManager.listDevices();
|
|
276
|
+
},
|
|
277
|
+
getDeviceConfig(deviceId) {
|
|
278
|
+
return deviceManager.getDeviceConfig(deviceId);
|
|
279
|
+
},
|
|
280
|
+
};
|
|
281
|
+
// Set bridge reference now that bridge is defined
|
|
282
|
+
bridgeRef = bridge;
|
|
283
|
+
return bridge;
|
|
284
|
+
}
|
|
285
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { DeviceDriver } from '@ya-modbus/driver-types';
|
|
2
|
+
import type { DeviceConfig } from './types.js';
|
|
3
|
+
type DataCallback = (deviceId: string, data: Record<string, unknown>) => void;
|
|
4
|
+
type ErrorCallback = (deviceId: string, error: Error, failureCount: number) => void;
|
|
5
|
+
/**
|
|
6
|
+
* Coordinates polling across multiple devices
|
|
7
|
+
*/
|
|
8
|
+
export declare class PollingScheduler {
|
|
9
|
+
private onData;
|
|
10
|
+
private onError;
|
|
11
|
+
private devices;
|
|
12
|
+
private running;
|
|
13
|
+
constructor(onData: DataCallback, onError: ErrorCallback);
|
|
14
|
+
/**
|
|
15
|
+
* Schedule a device for polling
|
|
16
|
+
*/
|
|
17
|
+
scheduleDevice(deviceId: string, config: DeviceConfig, driver: DeviceDriver): void;
|
|
18
|
+
/**
|
|
19
|
+
* Stop polling a device and remove it from the schedule
|
|
20
|
+
*/
|
|
21
|
+
unscheduleDevice(deviceId: string): void;
|
|
22
|
+
/**
|
|
23
|
+
* Start polling all scheduled devices
|
|
24
|
+
*/
|
|
25
|
+
start(): void;
|
|
26
|
+
/**
|
|
27
|
+
* Stop polling all devices
|
|
28
|
+
*/
|
|
29
|
+
stop(): void;
|
|
30
|
+
/**
|
|
31
|
+
* Check if a device is scheduled for polling
|
|
32
|
+
*/
|
|
33
|
+
isScheduled(deviceId: string): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Start polling for a specific device
|
|
36
|
+
*/
|
|
37
|
+
private startDevicePolling;
|
|
38
|
+
/**
|
|
39
|
+
* Schedule the next poll for a device, with backoff if needed
|
|
40
|
+
*/
|
|
41
|
+
private scheduleNextPoll;
|
|
42
|
+
/**
|
|
43
|
+
* Poll a device once
|
|
44
|
+
*/
|
|
45
|
+
private pollDevice;
|
|
46
|
+
}
|
|
47
|
+
export {};
|
|
48
|
+
//# sourceMappingURL=polling-scheduler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"polling-scheduler.d.ts","sourceRoot":"","sources":["../../src/polling-scheduler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AAE3D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAU9C,KAAK,YAAY,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAA;AAC7E,KAAK,aAAa,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAA;AAInF;;GAEG;AACH,qBAAa,gBAAgB;IAKzB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,OAAO;IALjB,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,OAAO,CAAQ;gBAGb,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,aAAa;IAGhC;;OAEG;IACH,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI;IAelF;;OAEG;IACH,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAQxC;;OAEG;IACH,KAAK,IAAI,IAAI;IAQb;;OAEG;IACH,IAAI,IAAI,IAAI;IAWZ;;OAEG;IACH,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAItC;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAQ1B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAkBxB;;OAEG;YACW,UAAU;CAyCzB"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
const DEFAULT_POLLING_INTERVAL = 5000; // 5 seconds
|
|
2
|
+
/**
|
|
3
|
+
* Coordinates polling across multiple devices
|
|
4
|
+
*/
|
|
5
|
+
export class PollingScheduler {
|
|
6
|
+
onData;
|
|
7
|
+
onError;
|
|
8
|
+
devices = new Map();
|
|
9
|
+
running = false;
|
|
10
|
+
constructor(onData, onError) {
|
|
11
|
+
this.onData = onData;
|
|
12
|
+
this.onError = onError;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Schedule a device for polling
|
|
16
|
+
*/
|
|
17
|
+
scheduleDevice(deviceId, config, driver) {
|
|
18
|
+
const interval = config.polling?.interval ?? DEFAULT_POLLING_INTERVAL;
|
|
19
|
+
this.devices.set(deviceId, {
|
|
20
|
+
config,
|
|
21
|
+
driver,
|
|
22
|
+
interval,
|
|
23
|
+
lastFailureCount: 0,
|
|
24
|
+
});
|
|
25
|
+
if (this.running) {
|
|
26
|
+
this.startDevicePolling(deviceId);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Stop polling a device and remove it from the schedule
|
|
31
|
+
*/
|
|
32
|
+
unscheduleDevice(deviceId) {
|
|
33
|
+
const device = this.devices.get(deviceId);
|
|
34
|
+
if (device?.timer) {
|
|
35
|
+
clearTimeout(device.timer);
|
|
36
|
+
}
|
|
37
|
+
this.devices.delete(deviceId);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Start polling all scheduled devices
|
|
41
|
+
*/
|
|
42
|
+
start() {
|
|
43
|
+
this.running = true;
|
|
44
|
+
for (const deviceId of this.devices.keys()) {
|
|
45
|
+
this.startDevicePolling(deviceId);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Stop polling all devices
|
|
50
|
+
*/
|
|
51
|
+
stop() {
|
|
52
|
+
this.running = false;
|
|
53
|
+
for (const device of this.devices.values()) {
|
|
54
|
+
if (device.timer) {
|
|
55
|
+
clearTimeout(device.timer);
|
|
56
|
+
delete device.timer;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Check if a device is scheduled for polling
|
|
62
|
+
*/
|
|
63
|
+
isScheduled(deviceId) {
|
|
64
|
+
return this.devices.has(deviceId);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Start polling for a specific device
|
|
68
|
+
*/
|
|
69
|
+
startDevicePolling(deviceId) {
|
|
70
|
+
const device = this.devices.get(deviceId);
|
|
71
|
+
if (!device)
|
|
72
|
+
return;
|
|
73
|
+
// Schedule first poll after the interval
|
|
74
|
+
this.scheduleNextPoll(deviceId);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Schedule the next poll for a device, with backoff if needed
|
|
78
|
+
*/
|
|
79
|
+
scheduleNextPoll(deviceId) {
|
|
80
|
+
const device = this.devices.get(deviceId);
|
|
81
|
+
if (!device || !this.running)
|
|
82
|
+
return;
|
|
83
|
+
const maxRetries = device.config.polling?.maxRetries ?? 3;
|
|
84
|
+
const retryBackoff = device.config.polling?.retryBackoff ?? device.interval * 2;
|
|
85
|
+
// Use backoff if we've exceeded max retries
|
|
86
|
+
const delay = device.lastFailureCount >= maxRetries ? retryBackoff : device.interval;
|
|
87
|
+
device.timer = setTimeout(() => {
|
|
88
|
+
const currentDevice = this.devices.get(deviceId);
|
|
89
|
+
if (!currentDevice || !this.running)
|
|
90
|
+
return;
|
|
91
|
+
void this.pollDevice(deviceId);
|
|
92
|
+
}, delay);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Poll a device once
|
|
96
|
+
*/
|
|
97
|
+
async pollDevice(deviceId) {
|
|
98
|
+
const device = this.devices.get(deviceId);
|
|
99
|
+
if (!device || !this.running)
|
|
100
|
+
return;
|
|
101
|
+
try {
|
|
102
|
+
const dataPointIds = device.driver.dataPoints.map((dp) => dp.id);
|
|
103
|
+
const data = await device.driver.readDataPoints(dataPointIds);
|
|
104
|
+
// Reset failure count BEFORE callback - polling succeeded regardless of callback result
|
|
105
|
+
device.lastFailureCount = 0;
|
|
106
|
+
try {
|
|
107
|
+
this.onData(deviceId, data);
|
|
108
|
+
}
|
|
109
|
+
catch (callbackError) {
|
|
110
|
+
// Data callback threw - log but don't count as polling failure
|
|
111
|
+
console.error(`Error in data callback for device ${deviceId}:`, callbackError instanceof Error ? callbackError : new Error(String(callbackError)));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
// Increment failure count - scheduler owns this state
|
|
116
|
+
device.lastFailureCount++;
|
|
117
|
+
try {
|
|
118
|
+
this.onError(deviceId, error instanceof Error ? error : new Error(String(error)), device.lastFailureCount);
|
|
119
|
+
}
|
|
120
|
+
catch (callbackError) {
|
|
121
|
+
// Error callback threw - log but state already updated
|
|
122
|
+
console.error(`Error in error callback for device ${deviceId}:`, callbackError instanceof Error ? callbackError : new Error(String(callbackError)));
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
this.scheduleNextPoll(deviceId);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=polling-scheduler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"polling-scheduler.js","sourceRoot":"","sources":["../../src/polling-scheduler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
export interface MqttBridgeConfig {
|
|
2
|
+
mqtt: {
|
|
3
|
+
url: string;
|
|
4
|
+
clientId?: string;
|
|
5
|
+
username?: string;
|
|
6
|
+
password?: string;
|
|
7
|
+
reconnectPeriod?: number;
|
|
8
|
+
};
|
|
9
|
+
stateDir?: string;
|
|
10
|
+
topicPrefix?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface BridgeStatus {
|
|
13
|
+
state: 'starting' | 'running' | 'stopping' | 'stopped' | 'error';
|
|
14
|
+
timestamp: number;
|
|
15
|
+
deviceCount: number;
|
|
16
|
+
mqttConnected?: boolean;
|
|
17
|
+
errors?: string[];
|
|
18
|
+
}
|
|
19
|
+
export interface PublishOptions {
|
|
20
|
+
qos?: 0 | 1 | 2;
|
|
21
|
+
retain?: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface SubscribeOptions {
|
|
24
|
+
qos?: 0 | 1 | 2;
|
|
25
|
+
}
|
|
26
|
+
export interface MqttMessage {
|
|
27
|
+
topic: string;
|
|
28
|
+
payload: Buffer;
|
|
29
|
+
qos: 0 | 1 | 2;
|
|
30
|
+
retain: boolean;
|
|
31
|
+
}
|
|
32
|
+
export type MessageHandler = (message: MqttMessage) => void;
|
|
33
|
+
export interface PollingConfig {
|
|
34
|
+
interval: number;
|
|
35
|
+
maxRetries?: number;
|
|
36
|
+
retryBackoff?: number;
|
|
37
|
+
}
|
|
38
|
+
export interface DeviceConfig {
|
|
39
|
+
deviceId: string;
|
|
40
|
+
driver: string;
|
|
41
|
+
connection: DeviceConnection;
|
|
42
|
+
enabled?: boolean;
|
|
43
|
+
polling?: PollingConfig;
|
|
44
|
+
}
|
|
45
|
+
export type DeviceConnection = RTUConnection | TCPConnection;
|
|
46
|
+
export interface RTUConnection {
|
|
47
|
+
type: 'rtu';
|
|
48
|
+
port: string;
|
|
49
|
+
baudRate: number;
|
|
50
|
+
slaveId: number;
|
|
51
|
+
parity: 'none' | 'even' | 'odd';
|
|
52
|
+
dataBits: 7 | 8;
|
|
53
|
+
stopBits: 1 | 2;
|
|
54
|
+
timeout?: number;
|
|
55
|
+
}
|
|
56
|
+
export interface TCPConnection {
|
|
57
|
+
type: 'tcp';
|
|
58
|
+
host: string;
|
|
59
|
+
port?: number;
|
|
60
|
+
slaveId: number;
|
|
61
|
+
timeout?: number;
|
|
62
|
+
}
|
|
63
|
+
export interface DeviceStatus {
|
|
64
|
+
deviceId: string;
|
|
65
|
+
state: 'connecting' | 'connected' | 'disconnected' | 'error';
|
|
66
|
+
enabled: boolean;
|
|
67
|
+
connected: boolean;
|
|
68
|
+
lastUpdate?: number;
|
|
69
|
+
lastPoll?: number;
|
|
70
|
+
consecutiveFailures?: number;
|
|
71
|
+
errors?: string[];
|
|
72
|
+
}
|
|
73
|
+
export interface MqttBridge {
|
|
74
|
+
start(): Promise<void>;
|
|
75
|
+
stop(): Promise<void>;
|
|
76
|
+
getStatus(): BridgeStatus;
|
|
77
|
+
publish(topic: string, payload: string | Buffer, options?: PublishOptions): Promise<void>;
|
|
78
|
+
subscribe(topic: string, handler: MessageHandler, options?: SubscribeOptions): Promise<void>;
|
|
79
|
+
unsubscribe(topic: string): Promise<void>;
|
|
80
|
+
addDevice(config: DeviceConfig): Promise<void>;
|
|
81
|
+
removeDevice(deviceId: string): Promise<void>;
|
|
82
|
+
getDevice(deviceId: string): DeviceStatus | undefined;
|
|
83
|
+
listDevices(): DeviceStatus[];
|
|
84
|
+
getDeviceConfig(deviceId: string): DeviceConfig | undefined;
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE;QACJ,GAAG,EAAE,MAAM,CAAA;QACX,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,eAAe,CAAC,EAAE,MAAM,CAAA;KACzB,CAAA;IACD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,UAAU,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,OAAO,CAAA;IAChE,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IACf,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IACd,MAAM,EAAE,OAAO,CAAA;CAChB;AAED,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAA;AAE3D,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,gBAAgB,CAAA;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,OAAO,CAAC,EAAE,aAAa,CAAA;CACxB;AAED,MAAM,MAAM,gBAAgB,GAAG,aAAa,GAAG,aAAa,CAAA;AAE5D,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,KAAK,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,KAAK,CAAA;IAC/B,QAAQ,EAAE,CAAC,GAAG,CAAC,CAAA;IACf,QAAQ,EAAE,CAAC,GAAG,CAAC,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,KAAK,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,YAAY,GAAG,WAAW,GAAG,cAAc,GAAG,OAAO,CAAA;IAC5D,OAAO,EAAE,OAAO,CAAA;IAChB,SAAS,EAAE,OAAO,CAAA;IAClB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;CAClB;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACrB,SAAS,IAAI,YAAY,CAAA;IACzB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACzF,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC5F,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACzC,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC9C,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC7C,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAAA;IACrD,WAAW,IAAI,YAAY,EAAE,CAAA;IAC7B,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAAA;CAC5D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package-info.d.ts","sourceRoot":"","sources":["../../../../src/utils/__mocks__/package-info.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,wBAAgB,cAAc,IAAI;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAMzE"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-mock for package info utilities
|
|
3
|
+
* Used in tests via jest.mock('./utils/package-info.js')
|
|
4
|
+
*/
|
|
5
|
+
export function getPackageInfo() {
|
|
6
|
+
return {
|
|
7
|
+
version: '0.0.0',
|
|
8
|
+
description: 'MQTT bridge for ya-modbus - orchestrates device management, polling, and MQTT publishing',
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=package-info.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package-info.js","sourceRoot":"","sources":["../../../../src/utils/__mocks__/package-info.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,UAAU,cAAc;IAC5B,OAAO;QACL,OAAO,EAAE,OAAO;QAChB,WAAW,EACT,0FAA0F;KAC7F,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-mock for process utilities
|
|
3
|
+
* Used in tests via jest.mock('./utils/process.js')
|
|
4
|
+
*/
|
|
5
|
+
import type { ProcessUtils } from '../process.js';
|
|
6
|
+
/**
|
|
7
|
+
* Map of registered signal handlers
|
|
8
|
+
* Populated by onSignal mock implementation
|
|
9
|
+
* Automatically cleared before each test
|
|
10
|
+
*/
|
|
11
|
+
export declare const signalHandlers: Map<string, () => void>;
|
|
12
|
+
/**
|
|
13
|
+
* Trigger a signal handler that was registered via onSignal
|
|
14
|
+
*
|
|
15
|
+
* @param signal - The signal to trigger (e.g., 'SIGINT', 'SIGTERM')
|
|
16
|
+
* @returns true if handler was found and called, false otherwise
|
|
17
|
+
*/
|
|
18
|
+
export declare function triggerSignal(signal: string): boolean;
|
|
19
|
+
export declare const processUtils: ProcessUtils;
|
|
20
|
+
//# sourceMappingURL=process.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process.d.ts","sourceRoot":"","sources":["../../../../src/utils/__mocks__/process.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAEjD;;;;GAIG;AACH,eAAO,MAAM,cAAc,oBAAyB,IAAI,CAAG,CAAA;AAE3D;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAOrD;AAQD,eAAO,MAAM,YAAY,EAAE,YAK1B,CAAA"}
|