@atelieruldetehnologii/node-red-custome-solutions 0.62.22

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.
Files changed (107) hide show
  1. package/NOTICE +56 -0
  2. package/PROPRIETARY-LICENSE.txt +157 -0
  3. package/README.md +105 -0
  4. package/TERMS-AND-CONDITIONS.md +311 -0
  5. package/advantech-read/advantech-read.html +775 -0
  6. package/advantech-read/advantech-read.js +449 -0
  7. package/advantech-write/advantech-write.html +413 -0
  8. package/advantech-write/advantech-write.js +338 -0
  9. package/bess-control/bess-control.html +569 -0
  10. package/bess-control/bess-control.js +541 -0
  11. package/bess-control/lib/allocation.js +149 -0
  12. package/bess-control/lib/batteries.js +86 -0
  13. package/bess-control/lib/helpers.js +157 -0
  14. package/bess-control/lib/messages.js +131 -0
  15. package/bess-control/lib/runtime.js +156 -0
  16. package/carlo-gavazzi/carlo-gavazzi.html +4327 -0
  17. package/carlo-gavazzi/carlo-gavazzi.js +3081 -0
  18. package/control-export/control-export.html +3644 -0
  19. package/control-export/control-export.js +2192 -0
  20. package/control-reactive/control-reactive.html +3136 -0
  21. package/control-reactive/control-reactive.js +2124 -0
  22. package/fronius-emulator/README.md +258 -0
  23. package/fronius-emulator/fronius-emulator.html +170 -0
  24. package/fronius-emulator/fronius-emulator.js +520 -0
  25. package/fronius-emulator/lib/binary-codec.js +65 -0
  26. package/fronius-emulator/lib/fronius-register-map.js +185 -0
  27. package/fronius-emulator/lib/modbus-parser.js +61 -0
  28. package/fronius-emulator/lib/modbus-tcp-server.js +224 -0
  29. package/fronius-emulator/lib/validation.js +206 -0
  30. package/fronius-emulator/lib/virtual-meter.js +132 -0
  31. package/fronius-read/fronius-read.html +3944 -0
  32. package/fronius-read/fronius-read.js +1466 -0
  33. package/fronius-read/lib/inverter-profiles.js +711 -0
  34. package/fronius-read/lib/sunspec-core.js +303 -0
  35. package/fronius-read/lib/sunspec-model-parsers.js +845 -0
  36. package/fronius-write/fronius-write.html +1789 -0
  37. package/fronius-write/fronius-write.js +2531 -0
  38. package/influxdb/influxdb.html +1077 -0
  39. package/influxdb/influxdb.js +1487 -0
  40. package/janitza/janitza.html +2562 -0
  41. package/janitza/janitza.js +1500 -0
  42. package/janitza/lib/janitza-profiles.js +371 -0
  43. package/lib/a3-test-server.js +199 -0
  44. package/lib/adam-6000-profiles.js +173 -0
  45. package/lib/licensing/constants.js +103 -0
  46. package/lib/licensing/device-id.js +251 -0
  47. package/lib/licensing/errors.js +21 -0
  48. package/lib/licensing/license-client.js +377 -0
  49. package/lib/licensing/license-state.js +50 -0
  50. package/lib/licensing/node-guard.js +150 -0
  51. package/lib/licensing/package-integrity.js +167 -0
  52. package/lib/licensing/package-integrity.json +513 -0
  53. package/lib/licensing/public-key.pem +3 -0
  54. package/lib/licensing/public-key.pem.example +2 -0
  55. package/lib/licensing/public-keys.js +18 -0
  56. package/lib/licensing/storage.js +63 -0
  57. package/lib/licensing/token-verifier.js +84 -0
  58. package/lib/numbers.js +26 -0
  59. package/license/atelier-license.html +88 -0
  60. package/license/atelier-license.js +170 -0
  61. package/modbus-tcp-discover/modbus-tcp-discover.html +190 -0
  62. package/modbus-tcp-discover/modbus-tcp-discover.js +706 -0
  63. package/multi-battery/multi-battery.html +649 -0
  64. package/multi-battery/multi-battery.js +668 -0
  65. package/multi-grid/multi-grid.html +745 -0
  66. package/multi-grid/multi-grid.js +1392 -0
  67. package/multi-inverter/lib/engine-core.js +237 -0
  68. package/multi-inverter/multi-inverter.html +2674 -0
  69. package/multi-inverter/multi-inverter.js +1393 -0
  70. package/package.json +109 -0
  71. package/phoenix-contact/lib/eem-ma370-advanced.js +175 -0
  72. package/phoenix-contact/lib/eem-ma370.js +138 -0
  73. package/phoenix-contact/lib/integrations.js +149 -0
  74. package/phoenix-contact/lib/phoenix-contact-profile.js +35030 -0
  75. package/phoenix-contact/phoenix-contact.html +840 -0
  76. package/phoenix-contact/phoenix-contact.js +679 -0
  77. package/pv-generator/pv-generator.html +284 -0
  78. package/pv-generator/pv-generator.js +387 -0
  79. package/pylontech-read/pylontech-read.html +763 -0
  80. package/pylontech-read/pylontech-read.js +518 -0
  81. package/pylontech-write/pylontech-write.html +405 -0
  82. package/pylontech-write/pylontech-write.js +546 -0
  83. package/scada-aggregator/lib/aggregate.js +72 -0
  84. package/scada-aggregator/lib/groups.js +39 -0
  85. package/scada-aggregator/scada-aggregator.html +108 -0
  86. package/scada-aggregator/scada-aggregator.js +117 -0
  87. package/scada-server/lib/aggregator-map.js +58 -0
  88. package/scada-server/lib/encoding.js +235 -0
  89. package/scada-server/lib/modbus-server.js +492 -0
  90. package/scada-server/lib/object-path.js +18 -0
  91. package/scada-server/lib/register-map.js +288 -0
  92. package/scada-server/scada-server.html +1419 -0
  93. package/scada-server/scada-server.js +704 -0
  94. package/scripts/check-syntax.js +66 -0
  95. package/scripts/package-integrity.js +46 -0
  96. package/scripts/package-release.js +116 -0
  97. package/scripts/run-tests.js +2178 -0
  98. package/scripts/tgz-integrity.js +74 -0
  99. package/sma-read/lib/sunspec-core.js +83 -0
  100. package/sma-read/lib/sunspec-model-parsers.js +853 -0
  101. package/sma-read/sma-read.html +659 -0
  102. package/sma-read/sma-read.js +458 -0
  103. package/sma-write/sma-write.html +105 -0
  104. package/sma-write/sma-write.js +270 -0
  105. package/victron-read/lib/mqtt-state.js +176 -0
  106. package/victron-read/victron-read.html +1074 -0
  107. package/victron-read/victron-read.js +1222 -0
@@ -0,0 +1,449 @@
1
+ module.exports = function (RED) {
2
+ "use strict";
3
+
4
+ const { runLicensedNode } = require("../lib/licensing/node-guard");
5
+
6
+ const ModbusRTU = require("modbus-serial");
7
+ const {
8
+ profileForModel,
9
+ normalizeModel
10
+ } = require("../lib/adam-6000-profiles");
11
+
12
+ function toInt(value, fallback) {
13
+ const n = Math.round(Number(value));
14
+ return Number.isFinite(n) ? n : fallback;
15
+ }
16
+
17
+ function toNonNegativeInt(value, fallback) {
18
+ const n = toInt(value, fallback);
19
+ return n >= 0 ? n : fallback;
20
+ }
21
+
22
+ function toPositiveInt(value, fallback) {
23
+ const n = toInt(value, fallback);
24
+ return n > 0 ? n : fallback;
25
+ }
26
+
27
+ function toFinite(value, fallback) {
28
+ const n = Number(value);
29
+ return Number.isFinite(n) ? n : fallback;
30
+ }
31
+
32
+ function toPollInterval(value, fallback) {
33
+ if (String(value || "").trim().toLowerCase() === "off") return 0;
34
+ const n = toInt(value, fallback);
35
+ return n > 0 ? n : 0;
36
+ }
37
+
38
+ function manualAddressToOffset(address, area) {
39
+ const n = toInt(address, 0);
40
+ if (area === "holding" && n >= 40001) return n - 40001;
41
+ if (area === "input" && n >= 30001) return n - 30001;
42
+ if (area === "coil" && n >= 1) return n - 1;
43
+ return Math.max(0, n);
44
+ }
45
+
46
+ function resolveAddress(address, area, addressMode) {
47
+ if (addressMode === "zero") {
48
+ return Math.max(0, toInt(address, 0));
49
+ }
50
+ return manualAddressToOffset(address, area);
51
+ }
52
+
53
+ function boolArray(values, count) {
54
+ const result = [];
55
+ for (let i = 0; i < count; i += 1) {
56
+ result.push(!!values[i]);
57
+ }
58
+ return result;
59
+ }
60
+
61
+ function buildChannelMessages(node, payload, triggerMsg, kind) {
62
+ const key = ["di", "do", "ai", "ao"].includes(kind) ? kind : "di";
63
+ const startKey = key + "_start";
64
+ const values = Array.isArray(payload[key]) ? payload[key] : [];
65
+ return values.map(function (value, index) {
66
+ const channel = index + 1;
67
+ return {
68
+ topic: (node.name || node.host || "advantech-read") + "/" + key + "/" + channel,
69
+ payload: key === "di" || key === "do" ? !!value : value,
70
+ channel: channel,
71
+ channel_index: index,
72
+ channel_type: key,
73
+ address: (payload[startKey] || node[key + "Start"]) + index,
74
+ module_model: payload.module_model,
75
+ module_model_code: payload.module_model_code,
76
+ detected_by: payload.detected_by,
77
+ format: key === "ai" ? payload.ai_format : undefined,
78
+ ts: payload.ts,
79
+ input: triggerMsg
80
+ };
81
+ });
82
+ }
83
+
84
+ function buildDigitalMessages(node, payload, triggerMsg, kind) {
85
+ return buildChannelMessages(node, payload, triggerMsg, kind);
86
+ }
87
+
88
+ function buildFamilyPayload(payload, kind) {
89
+ const values = Array.isArray(payload[kind]) ? payload[kind] : [];
90
+ const out = {};
91
+ values.forEach(function (value, index) {
92
+ out[kind + (index + 1)] = kind === "di" || kind === "do" ? !!value : value;
93
+ });
94
+ return out;
95
+ }
96
+
97
+ function buildFamilyAggregateMessage(node, payload, triggerMsg, kind) {
98
+ return {
99
+ topic: (node.name || node.host || "advantech-read") + "/" + kind,
100
+ payload: buildFamilyPayload(payload, kind),
101
+ channel_type: kind,
102
+ module_model: payload.module_model,
103
+ module_model_code: payload.module_model_code,
104
+ detected_by: payload.detected_by,
105
+ channel_count: Array.isArray(payload[kind]) ? payload[kind].length : 0,
106
+ start_address: payload[kind + "_start"],
107
+ format: kind === "ai" ? payload.ai_format : undefined,
108
+ ts: payload.ts,
109
+ input: triggerMsg
110
+ };
111
+ }
112
+
113
+ function cloneAggregatePayload(payload, include) {
114
+ const out = {
115
+ status: payload.status,
116
+ host: payload.host,
117
+ port: payload.port,
118
+ unit_id: payload.unit_id,
119
+ module_model: payload.module_model,
120
+ module_model_code: payload.module_model_code,
121
+ detected_by: payload.detected_by,
122
+ channel_counts: payload.channel_counts,
123
+ ts: payload.ts
124
+ };
125
+
126
+ ["di", "do", "ai", "ao"].forEach(function (key) {
127
+ if (!include[key]) return;
128
+ if (payload[key] === undefined) return;
129
+ out[key] = payload[key];
130
+ if (payload[key + "_start"] !== undefined) {
131
+ out[key + "_start"] = payload[key + "_start"];
132
+ }
133
+ if (key === "di" && payload.di_global_names !== undefined) {
134
+ out.di_global_names = payload.di_global_names;
135
+ }
136
+ if (key === "do" && payload.do_global_names !== undefined) {
137
+ out.do_global_names = payload.do_global_names;
138
+ }
139
+ if (key === "ai" && payload.ai_global_names !== undefined) {
140
+ out.ai_global_names = payload.ai_global_names;
141
+ }
142
+ if (key === "ao" && payload.ao_global_names !== undefined) {
143
+ out.ao_global_names = payload.ao_global_names;
144
+ }
145
+ if (key === "ai" && payload.ai_format !== undefined) {
146
+ out.ai_format = payload.ai_format;
147
+ }
148
+ });
149
+
150
+ return out;
151
+ }
152
+
153
+ function buildOutputMessages(node, payload, triggerMsg) {
154
+ const messages = [];
155
+ const families = [
156
+ { kind: "di", enabled: node.readDi, mode: node.diOutputMode },
157
+ { kind: "do", enabled: node.readDo, mode: node.doOutputMode },
158
+ { kind: "ai", enabled: node.readAi, mode: node.aiOutputMode },
159
+ { kind: "ao", enabled: node.readAo, mode: node.aoOutputMode }
160
+ ];
161
+
162
+ families.forEach(function (family) {
163
+ if (!family.enabled || payload[family.kind] === undefined) return;
164
+ if (family.mode === "split") {
165
+ buildChannelMessages(node, payload, triggerMsg, family.kind).forEach(function (message) {
166
+ messages.push(message);
167
+ });
168
+ } else {
169
+ messages.push(buildFamilyAggregateMessage(node, payload, triggerMsg, family.kind));
170
+ }
171
+ });
172
+
173
+ if (node.debugOutput) {
174
+ messages.push({
175
+ topic: (node.name || node.host || "advantech-read") + "/debug",
176
+ payload: cloneAggregatePayload(payload, { di: true, do: true, ai: true, ao: true }),
177
+ channel_type: "debug",
178
+ input: triggerMsg
179
+ });
180
+ }
181
+
182
+ if (!messages.length) return null;
183
+ return messages;
184
+ }
185
+
186
+ function safeGlobalPart(value, fallback) {
187
+ const text = String(value || fallback || "adam")
188
+ .trim()
189
+ .toLowerCase()
190
+ .replace(/^adam-/, "adam_")
191
+ .replace(/[^a-z0-9_]+/g, "_")
192
+ .replace(/^_+|_+$/g, "");
193
+ return text || String(fallback || "adam");
194
+ }
195
+
196
+ function parseGlobalNames(value) {
197
+ return String(value || "")
198
+ .split(/[\n,;]+/)
199
+ .map(function (item) {
200
+ return item.trim();
201
+ });
202
+ }
203
+
204
+ function defaultGlobalName(node, profile, kind, index) {
205
+ const base = node[kind + "GlobalPrefix"] || "advantech";
206
+ return safeGlobalPart(base, "advantech") + "_" + kind + (index + 1);
207
+ }
208
+
209
+ function saveChannelGlobals(node, profile, values, kind) {
210
+ const enabled = !!node["save" + kind.toUpperCase()[0] + kind.slice(1) + "Globals"];
211
+ if (!enabled || !Array.isArray(values)) return [];
212
+ const globalCtx = node.context().global;
213
+ const customNames = parseGlobalNames(node[kind + "GlobalNames"]);
214
+ const names = [];
215
+
216
+ values.forEach(function (value, index) {
217
+ const name = customNames[index] || defaultGlobalName(node, profile, kind, index);
218
+ globalCtx.set(name, kind === "di" || kind === "do" ? !!value : value);
219
+ names.push(name);
220
+ });
221
+
222
+ return names;
223
+ }
224
+
225
+ function saveDigitalGlobals(node, profile, values, kind) {
226
+ return saveChannelGlobals(node, profile, values, kind);
227
+ }
228
+
229
+ function scaleRegister(value, scale, offset) {
230
+ const signed = value > 32767 ? value - 65536 : value;
231
+ const scaled = signed * scale + offset;
232
+ return Number.isFinite(scaled) ? Number(scaled.toFixed(6)) : signed;
233
+ }
234
+
235
+ function decodeFloat32(words, index, wordOrder) {
236
+ const buffer = Buffer.alloc(4);
237
+ const hi = wordOrder === "lsw_first" ? words[index + 1] : words[index];
238
+ const lo = wordOrder === "lsw_first" ? words[index] : words[index + 1];
239
+ buffer.writeUInt16BE(hi || 0, 0);
240
+ buffer.writeUInt16BE(lo || 0, 2);
241
+ return Number(buffer.readFloatBE(0).toFixed(6));
242
+ }
243
+
244
+ function AdvantechReadNode(config) {
245
+ RED.nodes.createNode(this, config);
246
+ const node = this;
247
+
248
+ runLicensedNode(node, RED, config, function startLicensedRuntime() {
249
+
250
+ node.name = String(config.name || "").trim();
251
+ node.host = String(config.host || "").trim();
252
+ node.port = toPositiveInt(config.port, 502);
253
+ node.unitId = toPositiveInt(config.unitId, 1);
254
+ node.pollInterval = toPollInterval(config.pollInterval, 5000);
255
+ node.addressMode = config.addressMode === "zero" ? "zero" : "manual";
256
+ node.moduleType = String(config.moduleType || "ADAM-6066").trim();
257
+ node.outputCount = toPositiveInt(config.outputs, 1);
258
+ node.diOutputMode = config.diOutputMode === "split"
259
+ || (config.diOutputMode === undefined && node.outputCount > 1) ? "split" : "aggregate";
260
+ node.doOutputMode = config.doOutputMode === "split" ? "split" : "aggregate";
261
+ node.aiOutputMode = config.aiOutputMode === "split" ? "split" : "aggregate";
262
+ node.aoOutputMode = config.aoOutputMode === "split" ? "split" : "aggregate";
263
+ node.saveDiGlobals = !!config.saveDiGlobals;
264
+ node.saveDoGlobals = !!config.saveDoGlobals;
265
+ node.saveAiGlobals = !!config.saveAiGlobals;
266
+ node.saveAoGlobals = !!config.saveAoGlobals;
267
+ node.debugOutput = !!config.debugOutput;
268
+ node.diGlobalPrefix = String(config.diGlobalPrefix || "").trim();
269
+ node.doGlobalPrefix = String(config.doGlobalPrefix || "").trim();
270
+ node.aiGlobalPrefix = String(config.aiGlobalPrefix || "").trim();
271
+ node.aoGlobalPrefix = String(config.aoGlobalPrefix || "").trim();
272
+ node.diGlobalNames = String(config.diGlobalNames || "").trim();
273
+ node.doGlobalNames = String(config.doGlobalNames || "").trim();
274
+ node.aiGlobalNames = String(config.aiGlobalNames || "").trim();
275
+ node.aoGlobalNames = String(config.aoGlobalNames || "").trim();
276
+ node.readDi = config.readDi !== false;
277
+ node.readDo = config.readDo !== false;
278
+ node.readAi = config.readAi !== false;
279
+ node.readAo = config.readAo !== false;
280
+ node.diStart = toNonNegativeInt(config.diStart, 1);
281
+ node.doStart = toNonNegativeInt(config.doStart, 17);
282
+ node.aiStart = toNonNegativeInt(config.aiStart, 40001);
283
+ node.aoStart = toNonNegativeInt(config.aoStart, 40011);
284
+ node.diCount = toNonNegativeInt(config.diCount, 12);
285
+ node.doCount = toNonNegativeInt(config.doCount, 6);
286
+ node.aiCount = toNonNegativeInt(config.aiCount, 0);
287
+ node.aoCount = toNonNegativeInt(config.aoCount, 0);
288
+ node.aiFormat = config.aiFormat === "float32" ? "float32" : "int16";
289
+ node.aiScale = toFinite(config.aiScale, 1);
290
+ node.aiOffset = toFinite(config.aiOffset, 0);
291
+ node.aoScale = toFinite(config.aoScale, 1);
292
+ node.aoOffset = toFinite(config.aoOffset, 0);
293
+ node.wordOrder = config.wordOrder === "lsw_first" ? "lsw_first" : "msw_first";
294
+
295
+ let timer = null;
296
+ let busy = false;
297
+ let closing = false;
298
+ let client = new ModbusRTU();
299
+
300
+ async function connect() {
301
+ if (!client.isOpen) {
302
+ await client.connectTCP(node.host, { port: node.port });
303
+ }
304
+ client.setID(node.unitId);
305
+ client.setTimeout(2500);
306
+ }
307
+
308
+ async function closeConnection(done) {
309
+ try {
310
+ if (client && client.isOpen) {
311
+ client.close(done || function () {});
312
+ return;
313
+ }
314
+ } catch (e) {
315
+ // ignore close errors
316
+ }
317
+ client = new ModbusRTU();
318
+ if (done) done();
319
+ }
320
+
321
+ async function getProfile() {
322
+ const configured = profileForModel(node.moduleType);
323
+ if (configured) {
324
+ configured.detectedBy = "manual";
325
+ configured.modelCode = normalizeModel(node.moduleType);
326
+ return configured;
327
+ }
328
+
329
+ return {
330
+ model: node.moduleType && node.moduleType !== "auto" ? node.moduleType : "Manual",
331
+ modelCode: "",
332
+ diCount: node.diCount,
333
+ doCount: node.doCount,
334
+ aiCount: node.aiCount,
335
+ aoCount: node.aoCount,
336
+ detectedBy: "manual"
337
+ };
338
+ }
339
+
340
+ async function pollOnce(triggerMsg) {
341
+ if (busy || closing) return;
342
+ busy = true;
343
+
344
+ try {
345
+ await connect();
346
+ const profile = await getProfile();
347
+ const diCount = toNonNegativeInt(profile.diCount, 0);
348
+ const doCount = toNonNegativeInt(profile.doCount, 0);
349
+ const aiCount = toNonNegativeInt(profile.aiCount, 0);
350
+ const aoCount = toNonNegativeInt(profile.aoCount, 0);
351
+
352
+ const payload = {
353
+ status: "ok",
354
+ host: node.host,
355
+ port: node.port,
356
+ unit_id: node.unitId,
357
+ module_model: profile.model || null,
358
+ module_model_code: profile.modelCode || null,
359
+ detected_by: profile.detectedBy || null,
360
+ channel_counts: {
361
+ di: diCount,
362
+ do: doCount,
363
+ ai: aiCount,
364
+ ao: aoCount
365
+ },
366
+ ts: new Date().toISOString()
367
+ };
368
+
369
+ if ((node.readDi || node.diOutputMode === "split" || node.saveDiGlobals) && diCount > 0) {
370
+ const start = resolveAddress(node.diStart, "coil", node.addressMode);
371
+ const response = await client.readCoils(start, diCount);
372
+ payload.di = boolArray(response.data || [], diCount);
373
+ payload.di_start = node.diStart;
374
+ payload.di_global_names = saveChannelGlobals(node, profile, payload.di, "di");
375
+ }
376
+
377
+ if ((node.readDo || node.doOutputMode === "split" || node.saveDoGlobals) && doCount > 0) {
378
+ const start = resolveAddress(node.doStart, "coil", node.addressMode);
379
+ const response = await client.readCoils(start, doCount);
380
+ payload.do = boolArray(response.data || [], doCount);
381
+ payload.do_start = node.doStart;
382
+ payload.do_global_names = saveChannelGlobals(node, profile, payload.do, "do");
383
+ }
384
+
385
+ if ((node.readAi || node.aiOutputMode === "split" || node.saveAiGlobals) && aiCount > 0) {
386
+ const start = resolveAddress(node.aiStart, "holding", node.addressMode);
387
+ const count = node.aiFormat === "float32" ? aiCount * 2 : aiCount;
388
+ const response = await client.readHoldingRegisters(start, count);
389
+ const words = response.data || [];
390
+ payload.ai = [];
391
+ for (let i = 0; i < aiCount; i += 1) {
392
+ payload.ai.push(
393
+ node.aiFormat === "float32"
394
+ ? decodeFloat32(words, i * 2, node.wordOrder)
395
+ : scaleRegister(words[i] || 0, node.aiScale, node.aiOffset)
396
+ );
397
+ }
398
+ payload.ai_start = node.aiStart;
399
+ payload.ai_format = node.aiFormat;
400
+ payload.ai_global_names = saveChannelGlobals(node, profile, payload.ai, "ai");
401
+ }
402
+
403
+ if ((node.readAo || node.aoOutputMode === "split" || node.saveAoGlobals) && aoCount > 0) {
404
+ const start = resolveAddress(node.aoStart, "holding", node.addressMode);
405
+ const response = await client.readHoldingRegisters(start, aoCount);
406
+ payload.ao = (response.data || []).slice(0, aoCount).map(function (value) {
407
+ return scaleRegister(value, node.aoScale, node.aoOffset);
408
+ });
409
+ payload.ao_start = node.aoStart;
410
+ payload.ao_global_names = saveChannelGlobals(node, profile, payload.ao, "ao");
411
+ }
412
+
413
+ node.status({ fill: "green", shape: "dot", text: (profile.model || "ADAM") + " read ok" });
414
+ const messages = buildOutputMessages(node, payload, triggerMsg);
415
+ if (messages) node.send(messages);
416
+ } catch (error) {
417
+ node.status({ fill: "red", shape: "ring", text: "error" });
418
+ node.error(error.message || error, triggerMsg || {});
419
+ await closeConnection();
420
+ } finally {
421
+ busy = false;
422
+ }
423
+ }
424
+
425
+ if (node.pollInterval > 0) {
426
+ timer = setInterval(function () {
427
+ pollOnce();
428
+ }, node.pollInterval);
429
+ pollOnce();
430
+ }
431
+
432
+ node.on("input", function (msg) {
433
+ pollOnce(msg);
434
+ });
435
+
436
+ node.on("close", function (removed, done) {
437
+ closing = true;
438
+ if (timer) {
439
+ clearInterval(timer);
440
+ timer = null;
441
+ }
442
+ closeConnection(done);
443
+ });
444
+
445
+ });
446
+ }
447
+
448
+ RED.nodes.registerType("advantech-read", AdvantechReadNode);
449
+ };