node-red-contrib-uos-nats 0.2.34 → 0.2.36
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/nodes/datahub-output.js +48 -16
- package/package.json +1 -1
package/nodes/datahub-output.js
CHANGED
|
@@ -96,9 +96,11 @@ module.exports = function (RED) {
|
|
|
96
96
|
};
|
|
97
97
|
|
|
98
98
|
const sendDefinitionUpdate = async (payloads, subjects) => {
|
|
99
|
+
console.log(`[DataHub Output] Publishing definition with ${definitions.length} vars...`);
|
|
99
100
|
const { payload, fingerprint: fp } = payloads.buildProviderDefinitionEvent(definitions);
|
|
100
101
|
fingerprint = fp;
|
|
101
102
|
await nc.publish(subjects.providerDefinitionChanged(this.providerId), payload);
|
|
103
|
+
console.log(`[DataHub Output] Definition published. FP: ${fp}`);
|
|
102
104
|
};
|
|
103
105
|
|
|
104
106
|
const handleRead = async (payloads, msg) => {
|
|
@@ -114,12 +116,48 @@ module.exports = function (RED) {
|
|
|
114
116
|
await nc.publish(msg.reply, response);
|
|
115
117
|
};
|
|
116
118
|
|
|
119
|
+
// Store loaded modules for heartbeat
|
|
120
|
+
let loadedPayloads = null;
|
|
121
|
+
let loadedSubjects = null;
|
|
122
|
+
|
|
123
|
+
const sendValuesUpdate = async () => {
|
|
124
|
+
if (!nc || nc.isClosed()) return;
|
|
125
|
+
if (!loadedPayloads || !loadedSubjects) return;
|
|
126
|
+
|
|
127
|
+
// If we have no definitions yet, nothing to send
|
|
128
|
+
if (definitions.length === 0) return;
|
|
129
|
+
|
|
130
|
+
const stateObj = {};
|
|
131
|
+
for (const s of stateMap.values()) {
|
|
132
|
+
stateObj[s.id] = s;
|
|
133
|
+
}
|
|
134
|
+
try {
|
|
135
|
+
const payload = loadedPayloads.buildVariablesChangedEvent(definitions, stateObj, fingerprint);
|
|
136
|
+
await nc.publish(loadedSubjects.varsChangedEvent(this.providerId), payload);
|
|
137
|
+
// console.log(`[DataHub Output] Heartbeat sent. State count: ${Object.keys(stateObj).length}`);
|
|
138
|
+
} catch (err) {
|
|
139
|
+
this.warn(`Heartbeat error: ${err.message}`);
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const valueHeartbeat = setInterval(() => {
|
|
144
|
+
sendValuesUpdate();
|
|
145
|
+
}, 1000); // 1.0s interval matches Python SDK
|
|
146
|
+
|
|
117
147
|
const start = async () => {
|
|
118
148
|
try {
|
|
149
|
+
console.log('[DataHub Output] Starting...');
|
|
119
150
|
this.status({ fill: 'yellow', shape: 'ring', text: 'connecting…' });
|
|
120
151
|
const [payloads, subjects] = await loadModules();
|
|
152
|
+
console.log('[DataHub Output] Modules loaded.');
|
|
153
|
+
loadedPayloads = payloads;
|
|
154
|
+
loadedSubjects = subjects;
|
|
155
|
+
|
|
121
156
|
nc = await connection.acquire();
|
|
157
|
+
console.log('[DataHub Output] NATS acquired.');
|
|
158
|
+
|
|
122
159
|
await sendDefinitionUpdate(payloads, subjects);
|
|
160
|
+
|
|
123
161
|
// Listen for Variable READ requests
|
|
124
162
|
sub = nc.subscribe(subjects.readVariablesQuery(this.providerId), {
|
|
125
163
|
callback: (err, msg) => {
|
|
@@ -130,6 +168,7 @@ module.exports = function (RED) {
|
|
|
130
168
|
handleRead(payloads, msg).catch((error) => this.warn(error.message));
|
|
131
169
|
},
|
|
132
170
|
});
|
|
171
|
+
console.log('[DataHub Output] Subscribed to Read Query.');
|
|
133
172
|
|
|
134
173
|
// Listen for Definition READ requests (Discovery)
|
|
135
174
|
// SKIPPED: Permission Violation on v1.loc.<id>.def.qry.read
|
|
@@ -191,7 +230,6 @@ module.exports = function (RED) {
|
|
|
191
230
|
return;
|
|
192
231
|
}
|
|
193
232
|
|
|
194
|
-
const [payloadsMod, subjectsMod] = await loadModules();
|
|
195
233
|
let definitionsChanged = false;
|
|
196
234
|
const states = [];
|
|
197
235
|
|
|
@@ -209,26 +247,19 @@ module.exports = function (RED) {
|
|
|
209
247
|
timestampNs: Date.now() * 1_000_000,
|
|
210
248
|
quality: 'GOOD',
|
|
211
249
|
};
|
|
212
|
-
states.push(state);
|
|
213
250
|
// states.push(state); // No longer pushing to a temporary 'states' array
|
|
214
251
|
stateMap.set(def.id, state); // Update the global stateMap
|
|
215
252
|
});
|
|
253
|
+
|
|
216
254
|
if (definitionsChanged) {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
await new Promise(r => setTimeout(r,
|
|
220
|
-
}
|
|
221
|
-
// Convert stateMap to Object for payload builder
|
|
222
|
-
const stateObj = {};
|
|
223
|
-
for (const s of stateMap.values()) {
|
|
224
|
-
stateObj[s.id] = s;
|
|
225
|
-
}
|
|
226
|
-
try {
|
|
227
|
-
const payload = payloadsMod.buildVariablesChangedEvent(definitions, stateObj, fingerprint);
|
|
228
|
-
await nc.publish(subjectsMod.varsChangedEvent(this.providerId), payload);
|
|
229
|
-
} catch (err) {
|
|
230
|
-
this.error(`[v0.2.15] Encoding Error: ${err.message}. State: ${JSON.stringify(stateObj)}`);
|
|
255
|
+
// If definition changed, we MUST publish definition first
|
|
256
|
+
await sendDefinitionUpdate(loadedPayloads, loadedSubjects);
|
|
257
|
+
await new Promise(r => setTimeout(r, 200));
|
|
231
258
|
}
|
|
259
|
+
|
|
260
|
+
// Publish values immediately on input (don't wait for heartbeat)
|
|
261
|
+
await sendValuesUpdate();
|
|
262
|
+
|
|
232
263
|
send(msg);
|
|
233
264
|
done();
|
|
234
265
|
}
|
|
@@ -247,6 +278,7 @@ module.exports = function (RED) {
|
|
|
247
278
|
|
|
248
279
|
this.on('close', async (done) => {
|
|
249
280
|
try {
|
|
281
|
+
if (valueHeartbeat) clearInterval(valueHeartbeat);
|
|
250
282
|
if (sub) {
|
|
251
283
|
await sub.drain();
|
|
252
284
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-red-contrib-uos-nats",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.36",
|
|
4
4
|
"description": "Node-RED nodes for Weidmüller u-OS Data Hub. Read, write, and provide variables via NATS protocol with OAuth2 authentication. Features: Variable Key resolution, custom icons, example flows, and provider definition caching.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "IoTUeli",
|