UNPKG

@brobridge/atomic-flowcontrol

Version:
295 lines (264 loc) 10.1 kB
<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>