added connection field to subscribe node
This commit is contained in:
@@ -5,6 +5,7 @@ RED.nodes.registerType('xilica-subscribe', {
|
|||||||
defaults: {
|
defaults: {
|
||||||
name: { value: "" },
|
name: { value: "" },
|
||||||
action: { value: "subscribe" },
|
action: { value: "subscribe" },
|
||||||
|
connection: { value: "", type: "xilica-connection" },
|
||||||
rules: { value: [] },
|
rules: { value: [] },
|
||||||
transport: { value: "TCP" }
|
transport: { value: "TCP" }
|
||||||
},
|
},
|
||||||
@@ -120,6 +121,10 @@ RED.nodes.registerType('xilica-subscribe', {
|
|||||||
<label for="node-input-name">Name</label>
|
<label for="node-input-name">Name</label>
|
||||||
<input type="text" id="node-input-name">
|
<input type="text" id="node-input-name">
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="node-input-connection">Connection</label>
|
||||||
|
<input type="text" id="node-input-connection">
|
||||||
|
</div>
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<label for="node-input-action">Action</label>
|
<label for="node-input-action">Action</label>
|
||||||
<select id="node-input-action">
|
<select id="node-input-action">
|
||||||
@@ -142,13 +147,13 @@ RED.nodes.registerType('xilica-subscribe', {
|
|||||||
|
|
||||||
<script type="text/x-red" data-help-name="xilica-subscribe">
|
<script type="text/x-red" data-help-name="xilica-subscribe">
|
||||||
<p>Builds <code>SUBSCRIBE</code> or <code>UNSUBSCRIBE</code> commands for a Xilica Solaro processor.</p>
|
<p>Builds <code>SUBSCRIBE</code> or <code>UNSUBSCRIBE</code> commands for a Xilica Solaro processor.</p>
|
||||||
<p>On each input message, the node sets <code>msg.payload</code> to one or more commands separated by carriage returns. Wire the output into a <code>xilica-command</code> node to send them over TCP.</p>
|
<p>On each input message, the node builds the commands and sends them directly to the selected <code>xilica-connection</code>. It then outputs a message with the commands that were sent.</p>
|
||||||
<p><strong>Action</strong> selects whether to generate <code>SUBSCRIBE</code> or <code>UNSUBSCRIBE</code> commands.</p>
|
<p><strong>Action</strong> selects whether to generate <code>SUBSCRIBE</code> or <code>UNSUBSCRIBE</code> commands.</p>
|
||||||
<p><strong>Rules</strong> define which control names to generate, similar to the Switch node's rule list:</p>
|
<p><strong>Rules</strong> define which control names to generate, similar to the Switch node's rule list:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><strong>Type ==</strong>: the Value field is used as a full control name (e.g. <code>MASTER_GAIN</code>).</li>
|
<li><strong>Type ==</strong>: the Value field is used as a full control name (e.g. <code>MASTER_GAIN</code>).</li>
|
||||||
<li><strong>Type Indexed</strong>: the Value field is a base string (e.g. <code>CH</code>), and the From/To fields define a numeric range (e.g. 1–48 → <code>CH1</code>…<code>CH48</code>).</li>
|
<li><strong>Type Indexed</strong>: the Value field is a base string (e.g. <code>CH</code>), and the From/To fields define a numeric range (e.g. 1–48 → <code>CH1</code>…<code>CH48</code>).</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p><strong>Transport</strong> selects the subscription transport (<code>TCP</code> or <code>UDP</code>). The node generates commands like <code>SUBSCRIBE CH1 "TCP"</code> or <code>SUBSCRIBE CH1 "UDP"</code>.</p>
|
<p><strong>Transport</strong> selects the subscription transport (<code>TCP</code> or <code>UDP</code>). The node generates commands like <code>SUBSCRIBE CH1 "TCP"</code> or <code>SUBSCRIBE CH1 "UDP"</code> and sends them over the selected connection.</p>
|
||||||
<p>The actual Xilica device connection (IP/port/password) is chosen by the <code>xilica-command</code> node that this node is wired into, via its <code>xilica-connection</code> config.</p>
|
<p>The Xilica device connection (IP/port/password) is chosen directly on this node via the <strong>Connection</strong> field.</p>
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
const net = require("net");
|
||||||
|
|
||||||
module.exports = function (RED) {
|
module.exports = function (RED) {
|
||||||
function buildControlNamesFromRules(rules) {
|
function buildControlNamesFromRules(rules) {
|
||||||
const names = [];
|
const names = [];
|
||||||
@@ -65,6 +67,7 @@ module.exports = function (RED) {
|
|||||||
node.action = config.action || "subscribe";
|
node.action = config.action || "subscribe";
|
||||||
node.rules = Array.isArray(config.rules) ? config.rules : [];
|
node.rules = Array.isArray(config.rules) ? config.rules : [];
|
||||||
node.transport = config.transport || "TCP";
|
node.transport = config.transport || "TCP";
|
||||||
|
node.connectionConfig = RED.nodes.getNode(config.connection);
|
||||||
|
|
||||||
node.on("input", (msg, send, done) => {
|
node.on("input", (msg, send, done) => {
|
||||||
send =
|
send =
|
||||||
@@ -95,10 +98,120 @@ module.exports = function (RED) {
|
|||||||
(name) => action + " " + name + ' "' + transport + '"',
|
(name) => action + " " + name + ' "' + transport + '"',
|
||||||
);
|
);
|
||||||
|
|
||||||
msg.payload = lines.join("\r");
|
const rawCommand = lines.join("\r");
|
||||||
|
|
||||||
send(msg);
|
const conn = node.connectionConfig;
|
||||||
done();
|
if (!conn || !conn.host) {
|
||||||
|
node.status({
|
||||||
|
fill: "red",
|
||||||
|
shape: "ring",
|
||||||
|
text: "no connection",
|
||||||
|
});
|
||||||
|
node.warn(
|
||||||
|
"xilica-subscribe: no connection configured, outputting commands only",
|
||||||
|
);
|
||||||
|
msg.payload = rawCommand;
|
||||||
|
send(msg);
|
||||||
|
done();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const host = conn.host;
|
||||||
|
const port = conn.port || 10007;
|
||||||
|
const timeout = conn.timeout || 5000;
|
||||||
|
const password =
|
||||||
|
conn.credentials && conn.credentials.password
|
||||||
|
? conn.credentials.password
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const client = net.createConnection({ host, port });
|
||||||
|
let buffer = "";
|
||||||
|
let phase = password ? "login" : "command";
|
||||||
|
let finished = false;
|
||||||
|
|
||||||
|
function finishSuccess() {
|
||||||
|
if (finished) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
finished = true;
|
||||||
|
client.end();
|
||||||
|
node.status({});
|
||||||
|
|
||||||
|
msg.raw = buffer;
|
||||||
|
msg.payload = rawCommand;
|
||||||
|
|
||||||
|
send(msg);
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
|
||||||
|
function finishError(err) {
|
||||||
|
if (finished) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
finished = true;
|
||||||
|
client.destroy();
|
||||||
|
node.status({ fill: "red", shape: "ring", text: "error" });
|
||||||
|
node.error(err, msg);
|
||||||
|
done(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
client.setTimeout(timeout);
|
||||||
|
|
||||||
|
client.on("connect", () => {
|
||||||
|
node.status({ fill: "green", shape: "dot", text: "connected" });
|
||||||
|
try {
|
||||||
|
if (phase === "login") {
|
||||||
|
client.write("LOGIN " + password + "\r");
|
||||||
|
} else {
|
||||||
|
client.write(rawCommand + "\r");
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
finishError(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on("data", (dataBuf) => {
|
||||||
|
buffer += dataBuf.toString("utf8");
|
||||||
|
let idx = buffer.indexOf("\r");
|
||||||
|
while (idx !== -1) {
|
||||||
|
const line = buffer.slice(0, idx);
|
||||||
|
buffer = buffer.slice(idx + 1);
|
||||||
|
|
||||||
|
if (phase === "login") {
|
||||||
|
if (line && line.startsWith("ERROR=")) {
|
||||||
|
finishError(new Error("Login failed: " + line));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
phase = "command";
|
||||||
|
try {
|
||||||
|
client.write(rawCommand + "\r");
|
||||||
|
} catch (err) {
|
||||||
|
finishError(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
idx = buffer.indexOf("\r");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on("timeout", () => {
|
||||||
|
if (phase === "command") {
|
||||||
|
finishSuccess();
|
||||||
|
} else {
|
||||||
|
finishError(new Error("Timeout sending SUBSCRIBE to Xilica"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on("error", (err) => {
|
||||||
|
finishError(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on("end", () => {
|
||||||
|
if (!finished) {
|
||||||
|
finishSuccess();
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user