@brobridge/atomic-flowcontrol
Version:
Flow Control Module for Brobridge Atomic
295 lines (264 loc) • 10.1 kB
HTML
<script type="text/javascript">
RED.nodes.registerType('Flow Control', {
category: 'function',
color: '#225588',
defaults: {
name: { value: "" },
targetNodeId: { value: "" },
selectMode: { value: "auto" },
selectedNodes: { value: [] },
action: { value: "continue" },
},
inputs: 1,
outputs: 0,
icon: 'font-awesome/fa-fast-forward',
label: function () {
let label = this.name;
if (!label) {
switch(this.action) {
case 'continue':
label = 'Continue';
break;
case 'break':
label = 'Break';
break;
default:
label = 'Flow Control';
}
}
return label;
},
oneditprepare: function () {
let node = this;
let headers = {}
// Getting token information from local storage
let authInfo = localStorage.getItem('auth-tokens');
if (authInfo != null) {
let tokenInfo = JSON.parse(localStorage.getItem('auth-tokens'));
headers.Authorization = 'Bearer ' + tokenInfo.access_token;
}
// Getting available nodes
let resp = $.ajax({
dataType: 'json',
cache: false,
url: '/nodes/@brobridge/atomic-flowcontrol/apis/controllableNodes',
headers: headers,
async: false
});
let candidateNodeIDs = resp.responseJSON;
var scope = node.selectedNodes || [];
this._resize = function () {
var rows = $("#dialog-form>div:not(.node-input-target-list-row)");
var height = $("#dialog-form").height();
for (var i = 0; i < rows.length; i++) {
height -= $(rows[i]).outerHeight(true);
}
var editorRow = $("#dialog-form>div.node-input-target-list-row");
editorRow.css("height", height + "px");
};
var search = $("#node-input-status-target-filter").searchBox({
style: "compact",
delay: 300,
change: function () {
var val = $(this).val().trim().toLowerCase();
if (val === "") {
dirList.treeList("filter", null);
search.searchBox("count", "");
} else {
var count = dirList.treeList("filter", function (item) {
return item.label.toLowerCase().indexOf(val) > -1 || item.node.type.toLowerCase().indexOf(val) > -1
});
search.searchBox("count", count + " / " + candidateNodes.length);
}
}
});
var dirList = $("#node-input-status-target-container-div").css({ width: "100%", height: "100%" })
.treeList({ multi: true }).on("treelistitemmouseover", function (e, item) {
item.node.highlighted = true;
item.node.dirty = true;
RED.view.redraw();
}).on("treelistitemmouseout", function (e, item) {
item.node.highlighted = false;
item.node.dirty = true;
RED.view.redraw();
})
var candidateNodes = RED.nodes.filterNodes({}).filter(function (n) {
if (candidateNodeIDs.includes(n.id)) {
return true;
}
return false;
});
var allChecked = true;
var items = [];
var nodeItemMap = {};
candidateNodes.forEach(function (n) {
if (n.id === node.id) {
return;
}
var isChecked = scope.indexOf(n.id) !== -1;
allChecked = allChecked && isChecked;
var nodeDef = RED.nodes.getType(n.type);
var label;
var sublabel;
if (nodeDef) {
var l = nodeDef.label;
label = (typeof l === "function" ? l.call(n) : l) || "";
sublabel = n.type;
if (sublabel.indexOf("subflow:") === 0) {
var subflowId = sublabel.substring(8);
var subflow = RED.nodes.subflow(subflowId);
sublabel = "subflow : " + subflow.name;
}
}
if (!nodeDef || !label) {
label = n.type;
}
nodeItemMap[n.id] = {
node: n,
label: label,
sublabel: sublabel,
selected: isChecked,
checkbox: true
};
items.push(nodeItemMap[n.id]);
});
dirList.treeList('data', items);
$("#node-input-targetNodes-select").on("click", function (e) {
e.preventDefault();
var preselected = dirList.treeList('selected').map(function (n) { return n.node.id });
RED.tray.hide();
RED.view.selectNodes({
selected: preselected,
onselect: function (selection) {
RED.tray.show();
var newlySelected = {};
selection.forEach(function (n) {
newlySelected[n.id] = true;
if (nodeItemMap[n.id]) {
nodeItemMap[n.id].treeList.select(true);
}
})
preselected.forEach(function (id) {
if (!newlySelected[id]) {
nodeItemMap[id].treeList.select(false);
}
})
},
oncancel: function () {
RED.tray.show();
},
filter: function (n) {
console.log(n)
if (n.hasOwnProperty("flowControllable") && n.flowControllable == undefined) {
n.flowControllable = true;
}
if (n.flowControllable && n.id !== node.id) {
return true;
}
return false;
}
});
});
$("#node-input-selectMode").on("change", function (e) {
var selectMode = $(this).val();
if (selectMode === "specific") {
$(".node-input-target-row").show();
} else {
$(".node-input-target-row").hide();
}
node._resize();
});
switch (this.selectMode) {
case 'specific':
$("#node-input-selectMode").val("specific");
break;
default:
$("#node-input-selectMode").val("auto");
}
$("#node-input-selectMode").trigger("change");
},
oneditsave: function () {
var selectMode = $("#node-input-selectMode").val();
switch (selectMode) {
case 'specific':
this.selectedNodes = $("#node-input-status-target-container-div").treeList('selected').map(function (i) {
return i.node.id;
})
break;
default:
this.selectedNodes = [];
}
},
oneditresize: function (size) {
this._resize();
}
});
</script>
<script type="text/x-red" data-template-name="Flow Control">
<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-action"><i class="fa fa-play"></i> Action</label>
<select type="text" id="node-input-action">
<option value="continue">Continue</option>
<option value="break">Break</option>
</select>
</div>
<div class="form-row">
<label for="node-input-selectMode"><i class="fa fa-cube"></i> Target Node</label>
<select id="node-input-selectMode">
<option value="auto">Auto Select</option>
<option value="specific">Specific Nodes</option>
</select>
</div>
<div class="form-row node-input-target-row">
<button type="button" id="node-input-targetNodes-select" class="red-ui-button">Select node on the screen</button>
</div>
<div class="form-row node-input-target-row node-input-target-list-row" style="position: relative; min-height: 100px">
<div style="position: absolute; top: -30px; right: 0;"><input type="text" id="node-input-status-target-filter"></div>
<div id="node-input-status-target-container-div"></div>
</div>
</script>
<script type="text/html" data-help-name="Flow Control">
<p>The Flow Control node is a powerful tool for managing data flows in Brobridge Atomic applications. It allows you to pause and resume the processing of large datasets in a controlled manner, optimizing performance and reducing memory consumption.</p>
<h3>Overview</h3>
<p>This node works with any node that implements the flow control interface (has the <code>flowControllable</code> property set to <code>true</code>). It provides precise control over data streams through two primary actions.</p>
<h3>Actions</h3>
<dl>
<dt>Continue</dt>
<dd>
<p>Resumes processing of paused data streams. When the <code>"continue"</code> action is selected, the node signals to target nodes to resume processing for the specified sessions.</p>
</dd>
<dt>Break</dt>
<dd>
<p>Stops processing a stream and releases associated resources. This is useful when you want to terminate sessions that are no longer needed.</p>
</dd>
</dl>
<h3>Target Node Selection</h3>
<dl>
<dt>Auto Select</dt>
<dd>
<p>Automatically identifies target nodes based on the session ID in the incoming message. This mode is convenient for simple flows.</p>
</dd>
<dt>Specific Nodes</dt>
<dd>
<p>Manually select which nodes to control using the node selection interface. This provides precise control in complex flows.</p>
</dd>
</dl>
<h3>Input</h3>
<p>The node expects a message with a <code>sessions</code> array containing session identifiers:</p>
<pre><code>{
sessions: ["nodeId-sessionId", "nodeId-sessionId", ...],
// Optional
selectMode: "auto" // Override the node's configured selection mode
}</code></pre>
<h3>Usage Tips</h3>
<ul>
<li>Each session ID consists of a node ID and session identifier separated by a hyphen</li>
<li>The node can handle multiple sessions independently</li>
<li>For optimal performance, place Flow Control nodes strategically to manage memory usage</li>
<li>Include error handling to manage cases where target nodes are unavailable</li>
</ul>
</script>