diff --git a/nodes/xilica-subscribe.html b/nodes/xilica-subscribe.html
index ef4bc7c..3b1f059 100644
--- a/nodes/xilica-subscribe.html
+++ b/nodes/xilica-subscribe.html
@@ -5,10 +5,7 @@ RED.nodes.registerType('xilica-subscribe', {
defaults: {
name: { value: "" },
action: { value: "subscribe" },
- mode: { value: "list" },
- targets: { value: "" },
- startIndex: { value: 1 },
- endIndex: { value: 1 },
+ rules: { value: [] },
transport: { value: "TCP" }
},
inputs: 1,
@@ -20,6 +17,100 @@ RED.nodes.registerType('xilica-subscribe', {
return this.name;
}
return "xilica " + act;
+ },
+ oneditprepare: function () {
+ var node = this;
+ var rules = node.rules || [];
+
+ $("#node-input-rules").editableList({
+ addItem: function (container, index, rule) {
+ rule = rule || {};
+
+ var row = $('
').appendTo(container);
+
+ var typeField = $(' ', {
+ class: "node-input-rule-type",
+ style: "width: 120px; margin-right: 5px;"
+ }).appendTo(row);
+ $(' ', { value: "eq", text: "==" }).appendTo(typeField);
+ $(' ', { value: "idx", text: "Indexed" }).appendTo(typeField);
+
+ var valueField = $(' ', {
+ class: "node-input-rule-value",
+ type: "text",
+ style: "width: 180px; margin-right: 5px;"
+ }).appendTo(row);
+
+ var fromField = $(' ', {
+ class: "node-input-rule-from",
+ type: "number",
+ style: "width: 80px; margin-right: 5px;",
+ placeholder: "From"
+ }).appendTo(row);
+
+ var toField = $(' ', {
+ class: "node-input-rule-to",
+ type: "number",
+ style: "width: 80px;",
+ placeholder: "To"
+ }).appendTo(row);
+
+ function updateVisibility() {
+ var t = typeField.val();
+ if (t === "idx") {
+ fromField.show();
+ toField.show();
+ } else {
+ fromField.hide();
+ toField.hide();
+ }
+ }
+
+ typeField.on("change", updateVisibility);
+
+ typeField.val(rule.t || "eq");
+ valueField.val(rule.v || "");
+ if (rule.from !== undefined) {
+ fromField.val(rule.from);
+ }
+ if (rule.to !== undefined) {
+ toField.val(rule.to);
+ }
+
+ updateVisibility();
+ },
+ removable: true,
+ sortable: true
+ });
+
+ (rules || []).forEach(function (r) {
+ $("#node-input-rules").editableList('addItem', r);
+ });
+ },
+ oneditsave: function () {
+ var rules = [];
+ $("#node-input-rules").editableList('items').each(function () {
+ var row = $(this);
+ var type = $(".node-input-rule-type", row).val() || "eq";
+ var value = $(".node-input-rule-value", row).val() || "";
+ var from = $(".node-input-rule-from", row).val();
+ var to = $(".node-input-rule-to", row).val();
+
+ var rule = { t: type, v: value };
+
+ if (type === "idx") {
+ if (from !== "") {
+ rule.from = parseInt(from, 10);
+ }
+ if (to !== "") {
+ rule.to = parseInt(to, 10);
+ }
+ }
+
+ rules.push(rule);
+ });
+
+ this.rules = rules;
}
});
@@ -37,25 +128,8 @@ RED.nodes.registerType('xilica-subscribe', {
- Mode
-
- Explicit list
- Starts with
- Ends with
- Starts with and ends with
-
-
-
- Targets
-
-
-
- Start index
-
-
-
Transport
@@ -67,13 +141,10 @@ RED.nodes.registerType('xilica-subscribe', {
Builds SUBSCRIBE or UNSUBSCRIBE commands for a Xilica Solaro processor.
On each input message, the node sets msg.payload to one or more commands separated by carriage returns. Wire the output into a xilica-command node to send them over TCP.
Action selects whether to generate SUBSCRIBE or UNSUBSCRIBE commands.
-
Mode controls how the Targets field is interpreted:
+
Rules define which control names to generate, similar to the Switch node's rule list:
- Explicit list : each non-empty line in Targets is used as a full control name (e.g. MASTER_GAIN).
- Starts with : each line in Targets is treated as a prefix (e.g. CH), and Start/End index define the numeric range (e.g. 1–48 → CH1…CH48).
- Ends with : each line in Targets is treated as a suffix (e.g. _OUT), and Start/End index define the numeric range (e.g. 1–4 → 1_OUT…4_OUT).
- Starts with and ends with : each line in Targets is interpreted as prefix|suffix (for example CH|OUT), and Start/End index define the numeric range (e.g. 4–28 → CH4OUT…CH28OUT).
+ Type == : the Value field is used as a full control name (e.g. MASTER_GAIN).
+ Type Indexed : the Value field is a base string (e.g. CH), and the From/To fields define a numeric range (e.g. 1–48 → CH1…CH48).
The Transport field controls the subscription transport string, typically TCP. The node generates commands like SUBSCRIBE CH1 "TCP".
-
diff --git a/nodes/xilica-subscribe.js b/nodes/xilica-subscribe.js
index 7b77901..03cea7f 100644
--- a/nodes/xilica-subscribe.js
+++ b/nodes/xilica-subscribe.js
@@ -1,76 +1,53 @@
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);
+ function buildControlNamesFromRules(rules) {
const names = [];
- if (startIndex > endIndex) {
- const tmp = startIndex;
- startIndex = endIndex;
- endIndex = tmp;
+ if (!Array.isArray(rules)) {
+ return names;
}
- 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) {
+ rules.forEach((rule) => {
+ if (!rule) {
return;
}
- for (let i = startIndex; i <= endIndex; i += 1) {
- names.push(prefix + i + suffix);
+
+ const type = (rule.t || rule.type || "eq").toString();
+
+ if (type === "idx") {
+ const base = (rule.v || rule.base || "").trim();
+ if (!base) {
+ return;
+ }
+
+ let from = parseInt(rule.from, 10);
+ let to = parseInt(rule.to, 10);
+
+ if (Number.isNaN(from) && Number.isNaN(to)) {
+ return;
+ }
+ if (Number.isNaN(from)) {
+ from = to;
+ }
+ if (Number.isNaN(to)) {
+ to = from;
+ }
+ if (from > to) {
+ const tmp = from;
+ from = to;
+ to = tmp;
+ }
+
+ for (let i = from; i <= to; i += 1) {
+ names.push(base + i);
+ }
+ } else {
+ const name = (rule.v || rule.value || "").trim();
+ if (name) {
+ names.push(name);
+ }
}
});
- 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);
+ return names;
}
function normaliseAction(action) {
@@ -86,10 +63,7 @@ module.exports = function (RED) {
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.rules = Array.isArray(config.rules) ? config.rules : [];
node.transport = config.transport || "TCP";
node.on("input", (msg, send, done) => {
@@ -101,37 +75,17 @@ module.exports = function (RED) {
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 rules = Array.isArray(msg.rules) ? msg.rules : node.rules;
const transport =
typeof msg.transport === "string" && msg.transport.trim().length
? msg.transport.trim()
: node.transport;
- const names = buildControlNames(mode, targets || "", startIndex, endIndex);
+ const names = buildControlNamesFromRules(rules);
if (!names.length) {
- node.warn("xilica-subscribe: no targets defined");
+ node.warn("xilica-subscribe: no rules defined");
send(msg);
done();
return;
@@ -150,4 +104,3 @@ module.exports = function (RED) {
RED.nodes.registerType("xilica-subscribe", XilicaSubscribe);
};
-