@janart19/node-red-fusebox
Version:
A collection of Fusebox-specific custom nodes for Node-RED
230 lines (194 loc) • 9.28 kB
HTML
<script type="text/javascript">
RED.nodes.registerType("fusebox-query-data-streams", {
category: 'fusebox',
color: '#93b4f3',
// Define the default values for the node configuration
defaults: {
name: { value: "" },
controller: { value: "", type: "fusebox-controller", required: true },
protocol: { value: "UDP", validate: function (v) { return ['UDP', 'HTTP'].includes(v); } },
queryInterval: { value: 5, validate: function (v) { return Number.isInteger(parseInt(v)) && parseInt(v) > 2; } }
},
// Define the inputs and outputs of the node
inputs: 0,
outputs: 1,
icon: 'node-red/bridge-dash.svg',
label: function () {
const controller = this.controller;
const protocol = this.protocol;
let defaultName = `receive data streams`;
if (controller && protocol) defaultName = `receive data streams: ${protocol}`;
return this.name || defaultName;
},
paletteLabel: function () {
return "receive data streams"
},
// Update form fields
oneditprepare: function () {
const node = this;
let _controller = {};
// Define event listeners for form fields
$("#node-input-controller").on('change', function () {
queryControllerConfig();
});
$("#node-input-protocol").on('change', function () {
updateInterval();
validateProtocol();
});
$("#node-input-protocol, #node-input-queryInterval").on('change', function () {
updateTipText();
});
// Get the controller node configuration to show the tip text
function queryControllerConfig() {
const nodeId = $("#node-input-controller").val();
$.getJSON(`fusebox/controllerNodeConfig?id=${nodeId}`, function (data) {
_controller = data;
// Execute functions after successful query
updateTipText();
validateProtocol();
}).fail(function () {
console.error("Failed to get controller configuration!");
});
}
// Show the query interval field only when the protocol is HTTP
function updateInterval() {
const protocol = $("#node-input-protocol").val();
const el = $("#node-input-queryInterval-div");
if (protocol === "HTTP") {
el.show();
} else {
el.hide();
}
}
// Update the tip text on input change
function updateTipText() {
const protocol = $("#node-input-protocol").val();
const queryInterval = $("#node-input-queryInterval").val();
const uniqueId = _controller.uniqueId || "???";
const udpPort = _controller.udpPort || "???";
let tipText1 = "";
let tipText2 = `In addition, received data will be saved to global context under the key: '${uniqueId}_states'`;
if (protocol === "HTTP") {
tipText1 += `Controller (${uniqueId}) data will be queried using HTTP every ${queryInterval} seconds.`;
} else {
tipText1 += `Controller (${uniqueId}) data will be sent in real-time to port ${udpPort} using a UDP channel.`;
}
$("#node-input-tip-text-1").text(tipText1);
$("#node-input-tip-text-2").text(tipText2);
}
function validateProtocol() {
const el = $("#node-input-protocol");
const protocol = el.val();
let valid;
valid = _controller.host && ['UDP', 'HTTP'].includes(protocol);
if (_controller.host !== "127.0.0.1" && protocol === "UDP") {
valid = false;
}
el.css('border-color', valid ? '' : 'red');
}
},
// Save the values from the UI to the node configuration
oneditsave: function () {
const node = this;
node.controller = $("#node-input-controller").val();
node.protocol = $("#node-input-protocol").val();
node.queryInterval = $("#node-input-queryInterval").val();
}
});
</script>
<!-- Define style for the form fields -->
<style type="text/css">
.query-data-streams-div .form-row {
margin-bottom: 10px;
}
.query-data-streams-div .form-row label {
width: 33% ;
vertical-align: middle;
}
.query-data-streams-div .form-row div,
.query-data-streams-div .form-row input,
.query-data-streams-div .form-row select {
max-width: 66% ;
}
.query-data-streams-div .form-row select {
width: 66% ;
}
.query-data-streams-div .form-tips {
max-width: 100% ;
text-align: center;
}
</style>
<!-- Form fields are defined in the template below -->
<script type="text/html" data-template-name="fusebox-query-data-streams">
<div class="query-data-streams-div">
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-row">
<label for="node-input-controller"><i class="fa fa-search"></i> Controller</label>
<select id="node-input-controller">
<option value="" disabled selected>Select controller...</option>
</select>
</div>
<div class="form-row">
<label for="node-input-protocol"><i class="fa fa-exchange"></i> Protocol</label>
<select id="node-input-protocol">
<option value="UDP">UDP</option>
<option value="HTTP">HTTP</option>
</select>
</div>
<div class="form-row" id="node-input-queryInterval-div" style="display:none;">
<label for="node-input-queryInterval"><i class="fa fa-clock-o"></i> Query interval (s)</label>
<input type="number" id="node-input-queryInterval">
</div>
<div class="form-tips" id="node-input-tip-text-1"></div>
<div class="form-tips" id="node-input-tip-text-2"></div>
</div>
</script>
<!-- Define node description -->
<script type="text/html" data-help-name="fusebox-query-data-streams">
<p>Listen (or query) for real-time local data stream data from a controller, outputting the values both as a <code>msg</code> and also saving the latest values to the global context.</p>
<p>This should be the source node of all data stream related flows.</p>
<h3>Parameters</h3>
<dl class="message-properties">
<dt class="optional">name <span class="property-type">string</span></dt>
<dd>User-friendly name for the node</dd>
<dt>controller <span class="property-type">controller</span></dt>
<dd>The source of the data streams on the local network. Localhost (127.0.0.1) for most cases.</dd>
<dt>protocol <span class="property-type">string</span></dt>
<dd>Specifies how data will be received. UDP enables real-time data, while HTTP makes periodical queries.</dd>
<dt class="optional">interval <span class="property-type">int</span></dt>
<dd>Required for the HTTP protocol, where a query is made every X seconds.</dd>
</dl>
<h3>Output</h3>
<dl class="message-properties">
<dt>controller <span class="property-type">object</span></dt>
<dd>Each output <code>msg</code> includes info about the source of the data, e.g. <code>uniqueId</code>, <code>host</code>, <code>protocol</code>.</dd>
<dt>payload <span class="property-type">object</span></dt>
<dd>
The data stream values, e.g. <code style="white-space: normal;">{ "ABCW": { "values": [...], "status": int, "timestamp": int } }</code>.
<br>
Each UDP output message includes only one data stream, while HTTP output can include multiple data streams, formatted as an array.
</dd>
</dl>
<h3>Additional details</h3>
<p>This node receives data streams from a specified controller and formats the output payload to include values, statuses, and timestamps for each data stream name.</p>
<p>This data is also saved to the global context, making it accessible to other nodes in any flow.</p>
<p>
Example output message:
<code style="white-space: pre-wrap;">
{
"payload": {
"key1": { "values": [...], "status": int, "timestamp": int },
"key2": {...},
},
"controller": {
"name": "ABCW",
"uniqueId": "local",
"host": "127.0.0.1"
}
}
</code>
</p>
</script>