From 667f954358920a287df710d84a915da540394e5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niko=20Vuji=C4=87?= Date: Wed, 31 Dec 2025 08:54:22 +0100 Subject: [PATCH] subscribe and interval nodes added --- nodes/xilica-interval.html | 40 ++++++++++ nodes/xilica-interval.js | 33 ++++++++ nodes/xilica-subscribe.html | 79 +++++++++++++++++++ nodes/xilica-subscribe.js | 153 ++++++++++++++++++++++++++++++++++++ package.json | 4 +- 5 files changed, 308 insertions(+), 1 deletion(-) create mode 100644 nodes/xilica-interval.html create mode 100644 nodes/xilica-interval.js create mode 100644 nodes/xilica-subscribe.html create mode 100644 nodes/xilica-subscribe.js diff --git a/nodes/xilica-interval.html b/nodes/xilica-interval.html new file mode 100644 index 0000000..196c049 --- /dev/null +++ b/nodes/xilica-interval.html @@ -0,0 +1,40 @@ + + + + + + diff --git a/nodes/xilica-interval.js b/nodes/xilica-interval.js new file mode 100644 index 0000000..33519b9 --- /dev/null +++ b/nodes/xilica-interval.js @@ -0,0 +1,33 @@ +module.exports = function (RED) { + function XilicaInterval(config) { + RED.nodes.createNode(this, config); + const node = this; + + node.interval = parseInt(config.interval, 10) || 100; + + node.on("input", (msg, send, done) => { + send = + send || + function () { + node.send.apply(node, arguments); + }; + done = done || function () {}; + + let interval = node.interval; + if (Object.prototype.hasOwnProperty.call(msg, "interval")) { + const v = parseInt(msg.interval, 10); + if (!Number.isNaN(v) && v > 0) { + interval = v; + } + } + + msg.payload = "INTERVAL " + interval; + + send(msg); + done(); + }); + } + + RED.nodes.registerType("xilica-interval", XilicaInterval); +}; + diff --git a/nodes/xilica-subscribe.html b/nodes/xilica-subscribe.html new file mode 100644 index 0000000..ef4bc7c --- /dev/null +++ b/nodes/xilica-subscribe.html @@ -0,0 +1,79 @@ + + + + + + diff --git a/nodes/xilica-subscribe.js b/nodes/xilica-subscribe.js new file mode 100644 index 0000000..7b77901 --- /dev/null +++ b/nodes/xilica-subscribe.js @@ -0,0 +1,153 @@ +module.exports = function (RED) { + function buildNamesFromList(targets) { + return targets + .split(/\r?\n/) + .map((l) => l.trim()) + .filter((l) => l.length > 0); + } + + function buildNamesStartsWith(targets, startIndex, endIndex) { + const prefixes = buildNamesFromList(targets); + const names = []; + if (startIndex > endIndex) { + const tmp = startIndex; + startIndex = endIndex; + endIndex = tmp; + } + prefixes.forEach((prefix) => { + for (let i = startIndex; i <= endIndex; i += 1) { + names.push(prefix + i); + } + }); + return names; + } + + function buildNamesEndsWith(targets, startIndex, endIndex) { + const suffixes = buildNamesFromList(targets); + const names = []; + if (startIndex > endIndex) { + const tmp = startIndex; + startIndex = endIndex; + endIndex = tmp; + } + suffixes.forEach((suffix) => { + for (let i = startIndex; i <= endIndex; i += 1) { + names.push(String(i) + suffix); + } + }); + return names; + } + + function buildNamesStartsAndEnds(targets, startIndex, endIndex) { + const patterns = buildNamesFromList(targets); + const names = []; + if (startIndex > endIndex) { + const tmp = startIndex; + startIndex = endIndex; + endIndex = tmp; + } + patterns.forEach((pattern) => { + const parts = pattern.split("|"); + const prefix = (parts[0] || "").trim(); + const suffix = (parts[1] || "").trim(); + if (!prefix && !suffix) { + return; + } + for (let i = startIndex; i <= endIndex; i += 1) { + names.push(prefix + i + suffix); + } + }); + return names; + } + + function buildControlNames(mode, targets, startIndex, endIndex) { + if (mode === "startsWith") { + return buildNamesStartsWith(targets, startIndex, endIndex); + } + if (mode === "endsWith") { + return buildNamesEndsWith(targets, startIndex, endIndex); + } + if (mode === "startsAndEnds") { + return buildNamesStartsAndEnds(targets, startIndex, endIndex); + } + return buildNamesFromList(targets); + } + + function normaliseAction(action) { + const a = (action || "").toString().toLowerCase(); + if (a === "unsubscribe" || a === "unsub") { + return "UNSUBSCRIBE"; + } + return "SUBSCRIBE"; + } + + function XilicaSubscribe(config) { + RED.nodes.createNode(this, config); + const node = this; + + node.action = config.action || "subscribe"; + node.mode = config.mode || "list"; + node.targets = config.targets || ""; + node.startIndex = parseInt(config.startIndex, 10) || 1; + node.endIndex = parseInt(config.endIndex, 10) || node.startIndex; + node.transport = config.transport || "TCP"; + + node.on("input", (msg, send, done) => { + send = + send || + function () { + node.send.apply(node, arguments); + }; + done = done || function () {}; + + const action = normaliseAction(msg.action || node.action); + const mode = (msg.mode || node.mode || "list").toString(); + + const targets = + typeof msg.targets === "string" && msg.targets.trim().length + ? msg.targets + : node.targets; + + let startIndex = node.startIndex; + let endIndex = node.endIndex; + if (Object.prototype.hasOwnProperty.call(msg, "startIndex")) { + const v = parseInt(msg.startIndex, 10); + if (!Number.isNaN(v)) { + startIndex = v; + } + } + if (Object.prototype.hasOwnProperty.call(msg, "endIndex")) { + const v = parseInt(msg.endIndex, 10); + if (!Number.isNaN(v)) { + endIndex = v; + } + } + + const transport = + typeof msg.transport === "string" && msg.transport.trim().length + ? msg.transport.trim() + : node.transport; + + const names = buildControlNames(mode, targets || "", startIndex, endIndex); + + if (!names.length) { + node.warn("xilica-subscribe: no targets defined"); + send(msg); + done(); + return; + } + + const lines = names.map( + (name) => action + " " + name + ' "' + transport + '"', + ); + + msg.payload = lines.join("\r"); + + send(msg); + done(); + }); + } + + RED.nodes.registerType("xilica-subscribe", XilicaSubscribe); +}; + diff --git a/package.json b/package.json index 42af8b5..ef6bb44 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,9 @@ "nodes": { "xilica-ping": "nodes/xilica-ping.js", "xilica-connection": "nodes/xilica-connection.js", - "xilica-command": "nodes/xilica-command.js" + "xilica-command": "nodes/xilica-command.js", + "xilica-interval": "nodes/xilica-interval.js", + "xilica-subscribe": "nodes/xilica-subscribe.js" } } }