@ya-modbus/mqtt-bridge 0.6.1 → 0.7.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 +4 -2
- package/dist/cjs/bin/ya-modbus-bridge.d.ts.map +1 -0
- package/dist/cjs/bin/ya-modbus-bridge.js +12 -0
- package/dist/cjs/bin/ya-modbus-bridge.js.map +1 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/src/cli.d.ts.map +1 -0
- package/dist/cjs/src/cli.js +113 -0
- package/dist/cjs/src/cli.js.map +1 -0
- package/dist/cjs/src/device-manager.d.ts.map +1 -0
- package/dist/cjs/src/device-manager.js +82 -0
- package/dist/cjs/src/device-manager.js.map +1 -0
- package/dist/cjs/src/driver-loader.d.ts.map +1 -0
- package/dist/cjs/src/driver-loader.js +122 -0
- package/dist/cjs/src/driver-loader.js.map +1 -0
- package/dist/cjs/src/index.d.ts.map +1 -0
- package/dist/cjs/src/index.js +291 -0
- package/dist/cjs/src/index.js.map +1 -0
- package/dist/cjs/src/polling-scheduler.d.ts.map +1 -0
- package/dist/cjs/src/polling-scheduler.js +130 -0
- package/dist/cjs/src/polling-scheduler.js.map +1 -0
- package/dist/cjs/src/types.d.ts.map +1 -0
- package/dist/cjs/src/types.js +3 -0
- package/dist/{src → cjs/src}/types.js.map +1 -1
- package/dist/cjs/src/utils/config-validator.d.ts.map +1 -0
- package/dist/cjs/src/utils/config-validator.js +35 -0
- package/dist/cjs/src/utils/config-validator.js.map +1 -0
- package/dist/cjs/src/utils/config.d.ts.map +1 -0
- package/dist/cjs/src/utils/config.js +55 -0
- package/dist/cjs/src/utils/config.js.map +1 -0
- package/dist/cjs/src/utils/device-validation.d.ts.map +1 -0
- package/dist/cjs/src/utils/device-validation.js +74 -0
- package/dist/cjs/src/utils/device-validation.js.map +1 -0
- package/dist/cjs/src/utils/package-info.d.ts.map +1 -0
- package/dist/cjs/src/utils/package-info.js +30 -0
- package/dist/cjs/src/utils/package-info.js.map +1 -0
- package/dist/cjs/src/utils/process.d.ts.map +1 -0
- package/dist/cjs/src/utils/process.js +16 -0
- package/dist/cjs/src/utils/process.js.map +1 -0
- package/dist/cjs/src/utils/test-utils.d.ts.map +1 -0
- package/dist/cjs/src/utils/test-utils.js +556 -0
- package/dist/cjs/src/utils/test-utils.js.map +1 -0
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -0
- package/dist/esm/bin/ya-modbus-bridge.d.ts +9 -0
- package/dist/esm/bin/ya-modbus-bridge.d.ts.map +1 -0
- package/dist/esm/bin/ya-modbus-bridge.js.map +1 -0
- package/dist/esm/package.json +1 -0
- package/dist/esm/src/cli.d.ts +4 -0
- package/dist/esm/src/cli.d.ts.map +1 -0
- package/dist/esm/src/cli.js.map +1 -0
- package/dist/esm/src/device-manager.d.ts +17 -0
- package/dist/esm/src/device-manager.d.ts.map +1 -0
- package/dist/{src → esm/src}/device-manager.js +2 -3
- package/dist/esm/src/device-manager.js.map +1 -0
- package/dist/esm/src/driver-loader.d.ts +53 -0
- package/dist/esm/src/driver-loader.d.ts.map +1 -0
- package/dist/{src → esm/src}/driver-loader.js +2 -4
- package/dist/esm/src/driver-loader.js.map +1 -0
- package/dist/esm/src/index.d.ts +13 -0
- package/dist/esm/src/index.d.ts.map +1 -0
- package/dist/esm/src/index.js.map +1 -0
- package/dist/esm/src/polling-scheduler.d.ts +48 -0
- package/dist/esm/src/polling-scheduler.d.ts.map +1 -0
- package/dist/{src → esm/src}/polling-scheduler.js +2 -4
- package/dist/esm/src/polling-scheduler.js.map +1 -0
- package/dist/esm/src/types.d.ts +86 -0
- package/dist/esm/src/types.d.ts.map +1 -0
- package/dist/esm/src/types.js.map +1 -0
- package/dist/esm/src/utils/config-validator.d.ts +3 -0
- package/dist/esm/src/utils/config-validator.d.ts.map +1 -0
- package/dist/esm/src/utils/config-validator.js.map +1 -0
- package/dist/esm/src/utils/config.d.ts +3 -0
- package/dist/esm/src/utils/config.d.ts.map +1 -0
- package/dist/esm/src/utils/config.js.map +1 -0
- package/dist/esm/src/utils/device-validation.d.ts +31 -0
- package/dist/esm/src/utils/device-validation.d.ts.map +1 -0
- package/dist/esm/src/utils/device-validation.js.map +1 -0
- package/dist/esm/src/utils/package-info.d.ts +5 -0
- package/dist/esm/src/utils/package-info.d.ts.map +1 -0
- package/dist/esm/src/utils/package-info.js +27 -0
- package/dist/esm/src/utils/package-info.js.map +1 -0
- package/dist/esm/src/utils/process.d.ts +10 -0
- package/dist/esm/src/utils/process.d.ts.map +1 -0
- package/dist/esm/src/utils/process.js.map +1 -0
- package/dist/esm/src/utils/test-utils.d.ts +313 -0
- package/dist/esm/src/utils/test-utils.d.ts.map +1 -0
- package/dist/esm/src/utils/test-utils.js.map +1 -0
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -0
- package/package.json +27 -10
- package/dist/bin/ya-modbus-bridge.d.ts.map +0 -1
- package/dist/bin/ya-modbus-bridge.js.map +0 -1
- package/dist/src/cli.d.ts.map +0 -1
- package/dist/src/cli.js.map +0 -1
- package/dist/src/device-manager.d.ts.map +0 -1
- package/dist/src/device-manager.js.map +0 -1
- package/dist/src/driver-loader.d.ts.map +0 -1
- package/dist/src/driver-loader.js.map +0 -1
- package/dist/src/index.d.ts.map +0 -1
- package/dist/src/index.js.map +0 -1
- package/dist/src/polling-scheduler.d.ts.map +0 -1
- package/dist/src/polling-scheduler.js.map +0 -1
- package/dist/src/types.d.ts.map +0 -1
- package/dist/src/utils/__mocks__/package-info.d.ts +0 -9
- package/dist/src/utils/__mocks__/package-info.d.ts.map +0 -1
- package/dist/src/utils/__mocks__/package-info.js +0 -11
- package/dist/src/utils/__mocks__/package-info.js.map +0 -1
- package/dist/src/utils/__mocks__/process.d.ts +0 -20
- package/dist/src/utils/__mocks__/process.d.ts.map +0 -1
- package/dist/src/utils/__mocks__/process.js +0 -37
- package/dist/src/utils/__mocks__/process.js.map +0 -1
- package/dist/src/utils/config-validator.d.ts.map +0 -1
- package/dist/src/utils/config-validator.js.map +0 -1
- package/dist/src/utils/config.d.ts.map +0 -1
- package/dist/src/utils/config.js.map +0 -1
- package/dist/src/utils/device-validation.d.ts.map +0 -1
- package/dist/src/utils/device-validation.js.map +0 -1
- package/dist/src/utils/package-info.d.ts.map +0 -1
- package/dist/src/utils/package-info.js +0 -11
- package/dist/src/utils/package-info.js.map +0 -1
- package/dist/src/utils/process.d.ts.map +0 -1
- package/dist/src/utils/process.js.map +0 -1
- package/dist/src/utils/test-utils.d.ts.map +0 -1
- package/dist/src/utils/test-utils.js.map +0 -1
- package/dist/tsconfig.tsbuildinfo +0 -1
- /package/dist/{bin → cjs/bin}/ya-modbus-bridge.d.ts +0 -0
- /package/dist/{src → cjs/src}/cli.d.ts +0 -0
- /package/dist/{src → cjs/src}/device-manager.d.ts +0 -0
- /package/dist/{src → cjs/src}/driver-loader.d.ts +0 -0
- /package/dist/{src → cjs/src}/index.d.ts +0 -0
- /package/dist/{src → cjs/src}/polling-scheduler.d.ts +0 -0
- /package/dist/{src → cjs/src}/types.d.ts +0 -0
- /package/dist/{src → cjs/src}/utils/config-validator.d.ts +0 -0
- /package/dist/{src → cjs/src}/utils/config.d.ts +0 -0
- /package/dist/{src → cjs/src}/utils/device-validation.d.ts +0 -0
- /package/dist/{src → cjs/src}/utils/package-info.d.ts +0 -0
- /package/dist/{src → cjs/src}/utils/process.d.ts +0 -0
- /package/dist/{src → cjs/src}/utils/test-utils.d.ts +0 -0
- /package/dist/{bin → esm/bin}/ya-modbus-bridge.js +0 -0
- /package/dist/{src → esm/src}/cli.js +0 -0
- /package/dist/{src → esm/src}/index.js +0 -0
- /package/dist/{src → esm/src}/types.js +0 -0
- /package/dist/{src → esm/src}/utils/config-validator.js +0 -0
- /package/dist/{src → esm/src}/utils/config.js +0 -0
- /package/dist/{src → esm/src}/utils/device-validation.js +0 -0
- /package/dist/{src → esm/src}/utils/process.js +0 -0
- /package/dist/{src → esm/src}/utils/test-utils.js +0 -0
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loadConfig = void 0;
|
|
4
|
+
exports.createBridge = createBridge;
|
|
5
|
+
const tslib_1 = require("tslib");
|
|
6
|
+
const mqtt_1 = tslib_1.__importDefault(require("mqtt"));
|
|
7
|
+
const device_manager_js_1 = require("./device-manager.js");
|
|
8
|
+
const driver_loader_js_1 = require("./driver-loader.js");
|
|
9
|
+
const polling_scheduler_js_1 = require("./polling-scheduler.js");
|
|
10
|
+
var config_js_1 = require("./utils/config.js");
|
|
11
|
+
Object.defineProperty(exports, "loadConfig", { enumerable: true, get: function () { return config_js_1.loadConfig; } });
|
|
12
|
+
function createBridge(config, dependencies) {
|
|
13
|
+
let status = {
|
|
14
|
+
state: 'stopped',
|
|
15
|
+
timestamp: Date.now(),
|
|
16
|
+
};
|
|
17
|
+
let client = null;
|
|
18
|
+
const topicPrefix = config.topicPrefix ?? 'modbus';
|
|
19
|
+
// Subscriptions map needed because mqtt client doesn't expose handler lookup
|
|
20
|
+
const subscriptions = new Map();
|
|
21
|
+
// Use injected dependencies or create new instances
|
|
22
|
+
const driverLoader = dependencies?.driverLoader ?? new driver_loader_js_1.DriverLoader();
|
|
23
|
+
const deviceManager = dependencies?.deviceManager ?? new device_manager_js_1.DeviceManager(driverLoader);
|
|
24
|
+
// Bridge reference to be set after bridge object is created
|
|
25
|
+
// This ensures publishData is always defined before PollingScheduler starts
|
|
26
|
+
let bridgeRef = null;
|
|
27
|
+
// Publish function that will be called from polling
|
|
28
|
+
const publishData = (deviceId, data) => {
|
|
29
|
+
if (!bridgeRef) {
|
|
30
|
+
// Bridge not fully initialized yet - skip publishing
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const payload = JSON.stringify({
|
|
34
|
+
deviceId,
|
|
35
|
+
timestamp: Date.now(),
|
|
36
|
+
data,
|
|
37
|
+
});
|
|
38
|
+
// Publish to device-specific topic
|
|
39
|
+
void bridgeRef.publish(`${deviceId}/data`, payload, { qos: 0 }).catch((error) => {
|
|
40
|
+
console.error(`Failed to publish data for device ${deviceId}:`, error);
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
// Handle data from polling
|
|
44
|
+
const handlePollingData = (deviceId, data) => {
|
|
45
|
+
publishData(deviceId, data);
|
|
46
|
+
// Update device status and reset failure count on success
|
|
47
|
+
deviceManager.updateDeviceState(deviceId, {
|
|
48
|
+
lastPoll: Date.now(),
|
|
49
|
+
lastUpdate: Date.now(),
|
|
50
|
+
consecutiveFailures: 0,
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
// Handle polling errors
|
|
54
|
+
const handlePollingError = (deviceId, error, failureCount) => {
|
|
55
|
+
console.error(`Polling error for device ${deviceId}:`, error);
|
|
56
|
+
deviceManager.updateDeviceState(deviceId, {
|
|
57
|
+
consecutiveFailures: failureCount,
|
|
58
|
+
errors: [error.message],
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
const pollingScheduler = dependencies?.pollingScheduler ?? new polling_scheduler_js_1.PollingScheduler(handlePollingData, handlePollingError);
|
|
62
|
+
const bridge = {
|
|
63
|
+
start() {
|
|
64
|
+
return new Promise((resolve, reject) => {
|
|
65
|
+
status = {
|
|
66
|
+
state: 'starting',
|
|
67
|
+
timestamp: Date.now(),
|
|
68
|
+
};
|
|
69
|
+
const mqttOptions = {
|
|
70
|
+
clean: true,
|
|
71
|
+
reconnectPeriod: config.mqtt.reconnectPeriod ?? 5000,
|
|
72
|
+
resubscribe: true, // Automatic resubscription on reconnect (default: true)
|
|
73
|
+
};
|
|
74
|
+
if (config.mqtt.clientId) {
|
|
75
|
+
mqttOptions.clientId = config.mqtt.clientId;
|
|
76
|
+
}
|
|
77
|
+
if (config.mqtt.username) {
|
|
78
|
+
mqttOptions.username = config.mqtt.username;
|
|
79
|
+
}
|
|
80
|
+
if (config.mqtt.password) {
|
|
81
|
+
mqttOptions.password = config.mqtt.password;
|
|
82
|
+
}
|
|
83
|
+
client = mqtt_1.default.connect(config.mqtt.url, mqttOptions);
|
|
84
|
+
let isInitialConnection = true;
|
|
85
|
+
client.on('connect', () => {
|
|
86
|
+
status = {
|
|
87
|
+
state: 'running',
|
|
88
|
+
timestamp: Date.now(),
|
|
89
|
+
};
|
|
90
|
+
// Automatic resubscription is handled by mqtt.js (resubscribe: true)
|
|
91
|
+
// Start polling scheduler
|
|
92
|
+
pollingScheduler.start();
|
|
93
|
+
if (isInitialConnection) {
|
|
94
|
+
isInitialConnection = false;
|
|
95
|
+
resolve();
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
client.on('error', (error) => {
|
|
99
|
+
status = {
|
|
100
|
+
state: 'error',
|
|
101
|
+
timestamp: Date.now(),
|
|
102
|
+
errors: [error.message],
|
|
103
|
+
};
|
|
104
|
+
reject(error);
|
|
105
|
+
});
|
|
106
|
+
// No need to track disconnect/offline - client.connected property handles this
|
|
107
|
+
client.on('reconnect', () => {
|
|
108
|
+
// Note: reconnect event fires when attempting to reconnect
|
|
109
|
+
// mqttConnected status is updated by 'connect' event
|
|
110
|
+
});
|
|
111
|
+
client.on('message', (topic, payload, packet) => {
|
|
112
|
+
const handler = subscriptions.get(topic);
|
|
113
|
+
if (handler) {
|
|
114
|
+
const message = {
|
|
115
|
+
topic,
|
|
116
|
+
payload,
|
|
117
|
+
qos: packet.qos,
|
|
118
|
+
retain: packet.retain,
|
|
119
|
+
};
|
|
120
|
+
try {
|
|
121
|
+
handler(message);
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
// Prevent handler errors from crashing the bridge
|
|
125
|
+
console.error(`Error in message handler for topic ${topic}:`, error);
|
|
126
|
+
status = {
|
|
127
|
+
...status,
|
|
128
|
+
errors: [
|
|
129
|
+
...(status.errors ?? []),
|
|
130
|
+
/* istanbul ignore next - defensive: handlers always throw Error */
|
|
131
|
+
`Handler error for ${topic}: ${error instanceof Error ? error.message : String(error)}`,
|
|
132
|
+
],
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
},
|
|
139
|
+
async stop() {
|
|
140
|
+
status = {
|
|
141
|
+
state: 'stopping',
|
|
142
|
+
timestamp: Date.now(),
|
|
143
|
+
};
|
|
144
|
+
// Stop polling first
|
|
145
|
+
pollingScheduler.stop();
|
|
146
|
+
// Cleanup device manager (unloads drivers)
|
|
147
|
+
await deviceManager.clear();
|
|
148
|
+
// Close all transports managed by the transport manager
|
|
149
|
+
await driverLoader.closeAllTransports();
|
|
150
|
+
if (client) {
|
|
151
|
+
// Cleanup event listeners and subscriptions to prevent memory leaks
|
|
152
|
+
client.removeAllListeners();
|
|
153
|
+
subscriptions.clear();
|
|
154
|
+
await new Promise((resolve) => {
|
|
155
|
+
if (!client) {
|
|
156
|
+
resolve();
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
client.end(false, {}, () => {
|
|
160
|
+
status = {
|
|
161
|
+
state: 'stopped',
|
|
162
|
+
timestamp: Date.now(),
|
|
163
|
+
};
|
|
164
|
+
client = null;
|
|
165
|
+
resolve();
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
status = {
|
|
171
|
+
state: 'stopped',
|
|
172
|
+
timestamp: Date.now(),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
getStatus() {
|
|
177
|
+
return {
|
|
178
|
+
...status,
|
|
179
|
+
deviceCount: deviceManager.getDeviceCount(),
|
|
180
|
+
// Use client.connected property directly
|
|
181
|
+
mqttConnected: client?.connected ?? false,
|
|
182
|
+
};
|
|
183
|
+
},
|
|
184
|
+
publish(topic, payload, options) {
|
|
185
|
+
return new Promise((resolve, reject) => {
|
|
186
|
+
if (!client) {
|
|
187
|
+
reject(new Error('MQTT client not initialized'));
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
if (!client.connected) {
|
|
191
|
+
reject(new Error('MQTT client not connected'));
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
const fullTopic = `${topicPrefix}/${topic}`;
|
|
195
|
+
const publishOptions = {};
|
|
196
|
+
if (options?.qos !== undefined) {
|
|
197
|
+
publishOptions.qos = options.qos;
|
|
198
|
+
}
|
|
199
|
+
if (options?.retain !== undefined) {
|
|
200
|
+
publishOptions.retain = options.retain;
|
|
201
|
+
}
|
|
202
|
+
client.publish(fullTopic, payload, publishOptions, (error) => {
|
|
203
|
+
if (error) {
|
|
204
|
+
reject(error);
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
resolve();
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
},
|
|
212
|
+
subscribe(topic, handler, options) {
|
|
213
|
+
return new Promise((resolve, reject) => {
|
|
214
|
+
if (!client) {
|
|
215
|
+
reject(new Error('MQTT client not initialized'));
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
if (!client.connected) {
|
|
219
|
+
reject(new Error('MQTT client not connected'));
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
const fullTopic = `${topicPrefix}/${topic}`;
|
|
223
|
+
const subscribeOptions = {
|
|
224
|
+
qos: options?.qos ?? 0,
|
|
225
|
+
};
|
|
226
|
+
client.subscribe(fullTopic, subscribeOptions, (error) => {
|
|
227
|
+
if (error) {
|
|
228
|
+
reject(error);
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
subscriptions.set(fullTopic, handler);
|
|
232
|
+
resolve();
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
},
|
|
237
|
+
unsubscribe(topic) {
|
|
238
|
+
return new Promise((resolve, reject) => {
|
|
239
|
+
if (!client) {
|
|
240
|
+
reject(new Error('MQTT client not initialized'));
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
if (!client.connected) {
|
|
244
|
+
reject(new Error('MQTT client not connected'));
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
const fullTopic = `${topicPrefix}/${topic}`;
|
|
248
|
+
client.unsubscribe(fullTopic, (error) => {
|
|
249
|
+
if (error) {
|
|
250
|
+
reject(error);
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
subscriptions.delete(fullTopic);
|
|
254
|
+
resolve();
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
},
|
|
259
|
+
async addDevice(deviceConfig) {
|
|
260
|
+
await deviceManager.addDevice(deviceConfig);
|
|
261
|
+
// Schedule device for polling if enabled
|
|
262
|
+
if (deviceConfig.enabled !== false) {
|
|
263
|
+
const driver = driverLoader.getDriver(deviceConfig.deviceId);
|
|
264
|
+
if (!driver) {
|
|
265
|
+
throw new Error(`Driver for ${deviceConfig.deviceId} not found after loading. ` +
|
|
266
|
+
`This may indicate the device was removed during initialization.`);
|
|
267
|
+
}
|
|
268
|
+
pollingScheduler.scheduleDevice(deviceConfig.deviceId, deviceConfig, driver);
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
async removeDevice(deviceId) {
|
|
272
|
+
// Unschedule polling first
|
|
273
|
+
pollingScheduler.unscheduleDevice(deviceId);
|
|
274
|
+
// Remove device (unloads driver)
|
|
275
|
+
await deviceManager.removeDevice(deviceId);
|
|
276
|
+
},
|
|
277
|
+
getDevice(deviceId) {
|
|
278
|
+
return deviceManager.getDevice(deviceId);
|
|
279
|
+
},
|
|
280
|
+
listDevices() {
|
|
281
|
+
return deviceManager.listDevices();
|
|
282
|
+
},
|
|
283
|
+
getDeviceConfig(deviceId) {
|
|
284
|
+
return deviceManager.getDeviceConfig(deviceId);
|
|
285
|
+
},
|
|
286
|
+
};
|
|
287
|
+
// Set bridge reference now that bridge is defined
|
|
288
|
+
bridgeRef = bridge;
|
|
289
|
+
return bridge;
|
|
290
|
+
}
|
|
291
|
+
//# 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 @@
|
|
|
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,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PollingScheduler = void 0;
|
|
4
|
+
const DEFAULT_POLLING_INTERVAL = 5000; // 5 seconds
|
|
5
|
+
/**
|
|
6
|
+
* Coordinates polling across multiple devices
|
|
7
|
+
*/
|
|
8
|
+
class PollingScheduler {
|
|
9
|
+
constructor(onData, onError) {
|
|
10
|
+
this.onData = onData;
|
|
11
|
+
this.onError = onError;
|
|
12
|
+
this.devices = new Map();
|
|
13
|
+
this.running = false;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Schedule a device for polling
|
|
17
|
+
*/
|
|
18
|
+
scheduleDevice(deviceId, config, driver) {
|
|
19
|
+
const interval = config.polling?.interval ?? DEFAULT_POLLING_INTERVAL;
|
|
20
|
+
this.devices.set(deviceId, {
|
|
21
|
+
config,
|
|
22
|
+
driver,
|
|
23
|
+
interval,
|
|
24
|
+
lastFailureCount: 0,
|
|
25
|
+
});
|
|
26
|
+
if (this.running) {
|
|
27
|
+
this.startDevicePolling(deviceId);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Stop polling a device and remove it from the schedule
|
|
32
|
+
*/
|
|
33
|
+
unscheduleDevice(deviceId) {
|
|
34
|
+
const device = this.devices.get(deviceId);
|
|
35
|
+
if (device?.timer) {
|
|
36
|
+
clearTimeout(device.timer);
|
|
37
|
+
}
|
|
38
|
+
this.devices.delete(deviceId);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Start polling all scheduled devices
|
|
42
|
+
*/
|
|
43
|
+
start() {
|
|
44
|
+
this.running = true;
|
|
45
|
+
for (const deviceId of this.devices.keys()) {
|
|
46
|
+
this.startDevicePolling(deviceId);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Stop polling all devices
|
|
51
|
+
*/
|
|
52
|
+
stop() {
|
|
53
|
+
this.running = false;
|
|
54
|
+
for (const device of this.devices.values()) {
|
|
55
|
+
if (device.timer) {
|
|
56
|
+
clearTimeout(device.timer);
|
|
57
|
+
delete device.timer;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Check if a device is scheduled for polling
|
|
63
|
+
*/
|
|
64
|
+
isScheduled(deviceId) {
|
|
65
|
+
return this.devices.has(deviceId);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Start polling for a specific device
|
|
69
|
+
*/
|
|
70
|
+
startDevicePolling(deviceId) {
|
|
71
|
+
const device = this.devices.get(deviceId);
|
|
72
|
+
if (!device)
|
|
73
|
+
return;
|
|
74
|
+
// Schedule first poll after the interval
|
|
75
|
+
this.scheduleNextPoll(deviceId);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Schedule the next poll for a device, with backoff if needed
|
|
79
|
+
*/
|
|
80
|
+
scheduleNextPoll(deviceId) {
|
|
81
|
+
const device = this.devices.get(deviceId);
|
|
82
|
+
if (!device || !this.running)
|
|
83
|
+
return;
|
|
84
|
+
const maxRetries = device.config.polling?.maxRetries ?? 3;
|
|
85
|
+
const retryBackoff = device.config.polling?.retryBackoff ?? device.interval * 2;
|
|
86
|
+
// Use backoff if we've exceeded max retries
|
|
87
|
+
const delay = device.lastFailureCount >= maxRetries ? retryBackoff : device.interval;
|
|
88
|
+
device.timer = setTimeout(() => {
|
|
89
|
+
const currentDevice = this.devices.get(deviceId);
|
|
90
|
+
if (!currentDevice || !this.running)
|
|
91
|
+
return;
|
|
92
|
+
void this.pollDevice(deviceId);
|
|
93
|
+
}, delay);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Poll a device once
|
|
97
|
+
*/
|
|
98
|
+
async pollDevice(deviceId) {
|
|
99
|
+
const device = this.devices.get(deviceId);
|
|
100
|
+
if (!device || !this.running)
|
|
101
|
+
return;
|
|
102
|
+
try {
|
|
103
|
+
const dataPointIds = device.driver.dataPoints.map((dp) => dp.id);
|
|
104
|
+
const data = await device.driver.readDataPoints(dataPointIds);
|
|
105
|
+
// Reset failure count BEFORE callback - polling succeeded regardless of callback result
|
|
106
|
+
device.lastFailureCount = 0;
|
|
107
|
+
try {
|
|
108
|
+
this.onData(deviceId, data);
|
|
109
|
+
}
|
|
110
|
+
catch (callbackError) {
|
|
111
|
+
// Data callback threw - log but don't count as polling failure
|
|
112
|
+
console.error(`Error in data callback for device ${deviceId}:`, callbackError instanceof Error ? callbackError : new Error(String(callbackError)));
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
// Increment failure count - scheduler owns this state
|
|
117
|
+
device.lastFailureCount++;
|
|
118
|
+
try {
|
|
119
|
+
this.onError(deviceId, error instanceof Error ? error : new Error(String(error)), device.lastFailureCount);
|
|
120
|
+
}
|
|
121
|
+
catch (callbackError) {
|
|
122
|
+
// Error callback threw - log but state already updated
|
|
123
|
+
console.error(`Error in error callback for device ${deviceId}:`, callbackError instanceof Error ? callbackError : new Error(String(callbackError)));
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
this.scheduleNextPoll(deviceId);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
exports.PollingScheduler = PollingScheduler;
|
|
130
|
+
//# 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 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-validator.d.ts","sourceRoot":"","sources":["../../../../src/utils/config-validator.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AA4BnD,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,IAAI,gBAAgB,CAOlF"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateConfig = validateConfig;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const mqttConfigSchema = zod_1.z.object({
|
|
6
|
+
url: zod_1.z
|
|
7
|
+
.string()
|
|
8
|
+
.url()
|
|
9
|
+
.regex(/^(mqtt|mqtts|ws|wss):\/\//, {
|
|
10
|
+
message: 'URL must start with mqtt://, mqtts://, ws://, or wss://',
|
|
11
|
+
}),
|
|
12
|
+
clientId: zod_1.z.string().optional(),
|
|
13
|
+
username: zod_1.z.string().optional(),
|
|
14
|
+
password: zod_1.z.string().optional(),
|
|
15
|
+
reconnectPeriod: zod_1.z.number().int().positive().optional(),
|
|
16
|
+
});
|
|
17
|
+
const mqttBridgeConfigSchema = zod_1.z.object({
|
|
18
|
+
mqtt: mqttConfigSchema,
|
|
19
|
+
stateDir: zod_1.z.string().optional(),
|
|
20
|
+
topicPrefix: zod_1.z
|
|
21
|
+
.string()
|
|
22
|
+
// eslint-disable-next-line no-control-regex
|
|
23
|
+
.regex(/^[^+#/$\x00]+$/, {
|
|
24
|
+
message: 'Topic prefix must not contain MQTT special characters (+, #, /, $) or null character',
|
|
25
|
+
})
|
|
26
|
+
.optional(),
|
|
27
|
+
});
|
|
28
|
+
function validateConfig(config) {
|
|
29
|
+
const result = mqttBridgeConfigSchema.safeParse(config);
|
|
30
|
+
if (!result.success) {
|
|
31
|
+
const errors = result.error.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join(', ');
|
|
32
|
+
throw new Error(`Invalid configuration: ${errors}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=config-validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-validator.js","sourceRoot":"","sources":["../../../../src/utils/config-validator.ts"],"names":[],"mappings":";;AA8BA,wCAOC;AArCD,6BAAuB;AAIvB,MAAM,gBAAgB,GAAG,OAAC,CAAC,MAAM,CAAC;IAChC,GAAG,EAAE,OAAC;SACH,MAAM,EAAE;SACR,GAAG,EAAE;SACL,KAAK,CAAC,2BAA2B,EAAE;QAClC,OAAO,EAAE,yDAAyD;KACnE,CAAC;IACJ,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,eAAe,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;CACxD,CAAC,CAAA;AAEF,MAAM,sBAAsB,GAAG,OAAC,CAAC,MAAM,CAAC;IACtC,IAAI,EAAE,gBAAgB;IACtB,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,WAAW,EAAE,OAAC;SACX,MAAM,EAAE;QACT,4CAA4C;SAC3C,KAAK,CAAC,gBAAgB,EAAE;QACvB,OAAO,EACL,sFAAsF;KACzF,CAAC;SACD,QAAQ,EAAE;CACd,CAAC,CAAA;AAEF,SAAgB,cAAc,CAAC,MAAe;IAC5C,MAAM,MAAM,GAAG,sBAAsB,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;IAEvD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC7F,MAAM,IAAI,KAAK,CAAC,0BAA0B,MAAM,EAAE,CAAC,CAAA;IACrD,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../../src/utils/config.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AA6BnD,wBAAsB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA4B9E"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loadConfig = loadConfig;
|
|
4
|
+
const promises_1 = require("node:fs/promises");
|
|
5
|
+
const zod_1 = require("zod");
|
|
6
|
+
const mqttConfigSchema = zod_1.z.object({
|
|
7
|
+
url: zod_1.z
|
|
8
|
+
.string()
|
|
9
|
+
.url()
|
|
10
|
+
.regex(/^(mqtt|mqtts|ws|wss):\/\//, {
|
|
11
|
+
message: 'URL must start with mqtt://, mqtts://, ws://, or wss://',
|
|
12
|
+
})
|
|
13
|
+
.default('mqtt://localhost:1883'),
|
|
14
|
+
clientId: zod_1.z.string().optional(),
|
|
15
|
+
username: zod_1.z.string().optional(),
|
|
16
|
+
password: zod_1.z.string().optional(),
|
|
17
|
+
reconnectPeriod: zod_1.z.number().int().positive().optional(),
|
|
18
|
+
});
|
|
19
|
+
const mqttBridgeConfigSchema = zod_1.z.object({
|
|
20
|
+
mqtt: mqttConfigSchema.default({ url: 'mqtt://localhost:1883' }),
|
|
21
|
+
stateDir: zod_1.z.string().optional(),
|
|
22
|
+
topicPrefix: zod_1.z
|
|
23
|
+
.string()
|
|
24
|
+
// eslint-disable-next-line no-control-regex
|
|
25
|
+
.regex(/^[^+#/$\x00]+$/, {
|
|
26
|
+
message: 'Topic prefix must not contain MQTT special characters (+, #, /, $) or null character',
|
|
27
|
+
})
|
|
28
|
+
.optional(),
|
|
29
|
+
});
|
|
30
|
+
async function loadConfig(configPath) {
|
|
31
|
+
const content = await (0, promises_1.readFile)(configPath, 'utf-8');
|
|
32
|
+
const json = JSON.parse(content);
|
|
33
|
+
/* istanbul ignore next - defensive: JSON.parse never returns null */
|
|
34
|
+
const result = mqttBridgeConfigSchema.safeParse(json === null ? {} : json);
|
|
35
|
+
if (!result.success) {
|
|
36
|
+
const errors = result.error.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join(', ');
|
|
37
|
+
throw new Error(`Invalid configuration: ${errors}`);
|
|
38
|
+
}
|
|
39
|
+
// Convert Zod output (with T | undefined) to MqttBridgeConfig (with T?)
|
|
40
|
+
const config = {
|
|
41
|
+
mqtt: {
|
|
42
|
+
url: result.data.mqtt.url,
|
|
43
|
+
...(result.data.mqtt.clientId !== undefined && { clientId: result.data.mqtt.clientId }),
|
|
44
|
+
...(result.data.mqtt.username !== undefined && { username: result.data.mqtt.username }),
|
|
45
|
+
...(result.data.mqtt.password !== undefined && { password: result.data.mqtt.password }),
|
|
46
|
+
...(result.data.mqtt.reconnectPeriod !== undefined && {
|
|
47
|
+
reconnectPeriod: result.data.mqtt.reconnectPeriod,
|
|
48
|
+
}),
|
|
49
|
+
},
|
|
50
|
+
...(result.data.stateDir !== undefined && { stateDir: result.data.stateDir }),
|
|
51
|
+
...(result.data.topicPrefix !== undefined && { topicPrefix: result.data.topicPrefix }),
|
|
52
|
+
};
|
|
53
|
+
return config;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../../src/utils/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-validation.d.ts","sourceRoot":"","sources":["../../../../src/utils/device-validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AA+DvB,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAM7B,CAAA;AAEF,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAO1D"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deviceConfigSchema = void 0;
|
|
4
|
+
exports.validateDeviceConfig = validateDeviceConfig;
|
|
5
|
+
const zod_1 = require("zod");
|
|
6
|
+
// MQTT topic segment requirements:
|
|
7
|
+
// - Cannot contain null character
|
|
8
|
+
// - Cannot contain + or # (MQTT wildcards)
|
|
9
|
+
// - Cannot start with $ (reserved for system topics)
|
|
10
|
+
// - Cannot contain / (topic level separator)
|
|
11
|
+
// eslint-disable-next-line no-control-regex
|
|
12
|
+
const mqttTopicSegmentRegex = /^[^+#$/\u0000]+$/;
|
|
13
|
+
const deviceIdSchema = zod_1.z
|
|
14
|
+
.string()
|
|
15
|
+
.min(1, 'Device ID must not be empty')
|
|
16
|
+
.regex(mqttTopicSegmentRegex, {
|
|
17
|
+
message: 'Device ID must not contain MQTT special characters (+, #, /, $) or null character',
|
|
18
|
+
});
|
|
19
|
+
const driverNameSchema = zod_1.z
|
|
20
|
+
.string()
|
|
21
|
+
.min(1, 'Driver name must not be empty')
|
|
22
|
+
.regex(/^(@ya-modbus\/driver-[a-z0-9-]+|ya-modbus-driver-[a-z0-9-]+)$/, {
|
|
23
|
+
message: 'Driver must be @ya-modbus/driver-<name> or ya-modbus-driver-<name> with only lowercase letters, numbers, and hyphens',
|
|
24
|
+
})
|
|
25
|
+
.refine((name) => !name.includes('..') && !name.includes('\\'), {
|
|
26
|
+
message: 'Driver name cannot contain path traversal sequences',
|
|
27
|
+
});
|
|
28
|
+
const rtuConnectionSchema = zod_1.z.object({
|
|
29
|
+
type: zod_1.z.literal('rtu'),
|
|
30
|
+
port: zod_1.z.string().min(1),
|
|
31
|
+
baudRate: zod_1.z.number().positive(),
|
|
32
|
+
slaveId: zod_1.z.number().int().min(0).max(247),
|
|
33
|
+
parity: zod_1.z.enum(['none', 'even', 'odd']).optional(),
|
|
34
|
+
dataBits: zod_1.z.union([zod_1.z.literal(7), zod_1.z.literal(8)]).optional(),
|
|
35
|
+
stopBits: zod_1.z.union([zod_1.z.literal(1), zod_1.z.literal(2)]).optional(),
|
|
36
|
+
});
|
|
37
|
+
const tcpConnectionSchema = zod_1.z.object({
|
|
38
|
+
type: zod_1.z.literal('tcp'),
|
|
39
|
+
host: zod_1.z.string().min(1),
|
|
40
|
+
port: zod_1.z.number().int().positive().max(65535),
|
|
41
|
+
slaveId: zod_1.z.number().int().min(0).max(247),
|
|
42
|
+
});
|
|
43
|
+
const pollingConfigSchema = zod_1.z
|
|
44
|
+
.object({
|
|
45
|
+
interval: zod_1.z
|
|
46
|
+
.number()
|
|
47
|
+
.int()
|
|
48
|
+
.positive()
|
|
49
|
+
.min(100, 'Polling interval must be at least 100ms')
|
|
50
|
+
.max(86400000, 'Polling interval must not exceed 24 hours'),
|
|
51
|
+
maxRetries: zod_1.z
|
|
52
|
+
.number()
|
|
53
|
+
.int()
|
|
54
|
+
.nonnegative()
|
|
55
|
+
.max(100, 'Max retries should not exceed 100')
|
|
56
|
+
.optional(),
|
|
57
|
+
retryBackoff: zod_1.z.number().int().positive().optional(),
|
|
58
|
+
})
|
|
59
|
+
.optional();
|
|
60
|
+
exports.deviceConfigSchema = zod_1.z.object({
|
|
61
|
+
deviceId: deviceIdSchema,
|
|
62
|
+
driver: driverNameSchema,
|
|
63
|
+
connection: zod_1.z.union([rtuConnectionSchema, tcpConnectionSchema]),
|
|
64
|
+
enabled: zod_1.z.boolean().optional(),
|
|
65
|
+
polling: pollingConfigSchema,
|
|
66
|
+
});
|
|
67
|
+
function validateDeviceConfig(config) {
|
|
68
|
+
const result = exports.deviceConfigSchema.safeParse(config);
|
|
69
|
+
if (!result.success) {
|
|
70
|
+
const errors = result.error.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join(', ');
|
|
71
|
+
throw new Error(`Invalid device configuration: ${errors}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=device-validation.js.map
|