implement flows runtime stop/start API and UI

This commit is contained in:
Steve-Mcl
2022-06-08 21:56:17 +01:00
parent 62cd3b2061
commit 68331fc40c
18 changed files with 657 additions and 63 deletions

View File

@@ -0,0 +1,4 @@
<svg width="32" height="32" xmlns="http://www.w3.org/2000/svg">
<path color="#000" fill="#8c101c" d="M0 0h32v32H0z"></path>
<path style="fill:#ffffff;stroke:#000000;stroke-width:0" d="M 24,16 8,24 8,8 Z" fill="none" stroke="#000" stroke-width="1.5"></path>
</svg>

After

Width:  |  Height:  |  Size: 271 B

View File

@@ -0,0 +1,4 @@
<svg width="32" height="32" xmlns="http://www.w3.org/2000/svg">
<path color="#000" fill="#8c101c" d="M0 0h32v32H0z"></path>
<rect style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0;" width="15" height="15" x="8" y="8.5"></rect>
</svg>

After

Width:  |  Height:  |  Size: 256 B

View File

@@ -297,6 +297,10 @@ var RED = (function() {
// handled below
return;
}
if (notificationId === "flows-run-state") {
// handled in editor-client/src/js/runtime.js
return;
}
if (notificationId === "project-update") {
loader.start(RED._("event.loadingProject"), 0);
RED.nodes.clear();
@@ -747,6 +751,7 @@ var RED = (function() {
RED.keyboard.init(buildMainMenu);
RED.nodes.init();
RED.runtime.init()
RED.comms.connect();
$("#red-ui-main-container").show();

View File

@@ -0,0 +1,53 @@
RED.runtime = (function() {
let state = ""
let settings = {ui: true, enabled: true};
const STOPPED = "stopped"
const STARTED = "started"
return {
init: function() {
// refresh the current runtime status from server
settings = RED.settings.runtimeState;
RED.runtime.requestState()
// {id:"flows-run-state", started: false, state: "stopped", retain:true}
RED.comms.subscribe("notification/flows-run-state",function(topic,msg) {
RED.events.emit("flows-run-state",msg);
RED.runtime.updateState(msg.state);
});
},
get state() {
return state
},
get started() {
return state === STARTED
},
get states() {
return { STOPPED, STARTED }
},
updateState: function(newState) {
state = newState;
// disable pointer events on node buttons (e.g. inject/debug nodes)
$(".red-ui-flow-node-button").toggleClass("red-ui-flow-node-button-stopped", state === STOPPED)
// show/hide Start/Stop based on current state
if(!RED.settings.runtimeState || RED.settings.runtimeState.ui !== false) {
RED.menu.setVisible("deploymenu-item-runtime-stop", state === STARTED)
RED.menu.setVisible("deploymenu-item-runtime-start", state === STOPPED)
}
},
requestState: function(callback) {
$.ajax({
headers: {
"Accept":"application/json"
},
cache: false,
url: 'flows/state',
success: function(data) {
RED.runtime.updateState(data.state)
if(callback) {
callback(data.state)
}
}
});
}
}
})()

View File

@@ -139,6 +139,9 @@ RED.menu = (function() {
if (opt.disabled) {
item.addClass("disabled");
}
if (opt.visible === false) {
item.addClass("hide");
}
}
@@ -249,6 +252,14 @@ RED.menu = (function() {
}
}
function setVisible(id,state) {
if (!state) {
$("#"+id).parent().addClass("hide");
} else {
$("#"+id).parent().removeClass("hide");
}
}
function addItem(id,opt) {
var item = createMenuItem(opt);
if (opt !== null && opt.group) {
@@ -305,6 +316,7 @@ RED.menu = (function() {
isSelected: isSelected,
toggleSelected: toggleSelected,
setDisabled: setDisabled,
setVisible: setVisible,
addItem: addItem,
removeItem: removeItem,
setAction: setAction,

View File

@@ -63,16 +63,18 @@ RED.deploy = (function() {
'</a>'+
'<a id="red-ui-header-button-deploy-options" class="red-ui-deploy-button" href="#"><i class="fa fa-caret-down"></i></a>'+
'</span></li>').prependTo(".red-ui-header-toolbar");
RED.menu.init({id:"red-ui-header-button-deploy-options",
options: [
{id:"deploymenu-item-full",toggle:"deploy-type",icon:"red/images/deploy-full.svg",label:RED._("deploy.full"),sublabel:RED._("deploy.fullDesc"),selected: true, onselect:function(s) { if(s){changeDeploymentType("full")}}},
{id:"deploymenu-item-flow",toggle:"deploy-type",icon:"red/images/deploy-flows.svg",label:RED._("deploy.modifiedFlows"),sublabel:RED._("deploy.modifiedFlowsDesc"), onselect:function(s) {if(s){changeDeploymentType("flows")}}},
{id:"deploymenu-item-node",toggle:"deploy-type",icon:"red/images/deploy-nodes.svg",label:RED._("deploy.modifiedNodes"),sublabel:RED._("deploy.modifiedNodesDesc"),onselect:function(s) { if(s){changeDeploymentType("nodes")}}},
null,
{id:"deploymenu-item-reload", icon:"red/images/deploy-reload.svg",label:RED._("deploy.restartFlows"),sublabel:RED._("deploy.restartFlowsDesc"),onselect:"core:restart-flows"},
]
});
const mainMenuItems = [
{id:"deploymenu-item-full",toggle:"deploy-type",icon:"red/images/deploy-full.svg",label:RED._("deploy.full"),sublabel:RED._("deploy.fullDesc"),selected: true, onselect:function(s) { if(s){changeDeploymentType("full")}}},
{id:"deploymenu-item-flow",toggle:"deploy-type",icon:"red/images/deploy-flows.svg",label:RED._("deploy.modifiedFlows"),sublabel:RED._("deploy.modifiedFlowsDesc"), onselect:function(s) {if(s){changeDeploymentType("flows")}}},
{id:"deploymenu-item-node",toggle:"deploy-type",icon:"red/images/deploy-nodes.svg",label:RED._("deploy.modifiedNodes"),sublabel:RED._("deploy.modifiedNodesDesc"),onselect:function(s) { if(s){changeDeploymentType("nodes")}}},
null
]
if(!RED.settings.runtimeState || RED.settings.runtimeState.ui !== false) {
mainMenuItems.push({id:"deploymenu-item-runtime-start", icon:"red/images/start.svg",label:"Start"/*RED._("deploy.startFlows")*/,sublabel:"Start Flows" /*RED._("deploy.startFlowsDesc")*/,onselect:"core:start-flows", visible:false})
mainMenuItems.push({id:"deploymenu-item-runtime-stop", icon:"red/images/stop.svg",label:"Stop"/*RED._("deploy.startFlows")*/,sublabel:"Stop Flows" /*RED._("deploy.startFlowsDesc")*/,onselect:"core:stop-flows", visible:false})
}
mainMenuItems.push({id:"deploymenu-item-reload", icon:"red/images/deploy-reload.svg",label:RED._("deploy.restartFlows"),sublabel:RED._("deploy.restartFlowsDesc"),onselect:"core:restart-flows"})
RED.menu.init({id:"red-ui-header-button-deploy-options", options: mainMenuItems });
} else if (type == "simple") {
var label = options.label || RED._("deploy.deploy");
var icon = 'red/images/deploy-full-o.svg';
@@ -100,6 +102,8 @@ RED.deploy = (function() {
RED.actions.add("core:deploy-flows",save);
if (type === "default") {
RED.actions.add("core:stop-flows",function() { stopStartFlows("stop") });
RED.actions.add("core:start-flows",function() { stopStartFlows("start") });
RED.actions.add("core:restart-flows",restart);
RED.actions.add("core:set-deploy-type-to-full",function() { RED.menu.setSelected("deploymenu-item-full",true);});
RED.actions.add("core:set-deploy-type-to-modified-flows",function() { RED.menu.setSelected("deploymenu-item-flow",true); });
@@ -270,18 +274,74 @@ RED.deploy = (function() {
function sanitize(html) {
return html.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")
}
function restart() {
var startTime = Date.now();
$(".red-ui-deploy-button-content").css('opacity',0);
$(".red-ui-deploy-button-spinner").show();
var deployWasEnabled = !$("#red-ui-header-button-deploy").hasClass("disabled");
$("#red-ui-header-button-deploy").addClass("disabled");
deployInflight = true;
function shadeShow() {
$("#red-ui-header-shade").show();
$("#red-ui-editor-shade").show();
$("#red-ui-palette-shade").show();
$("#red-ui-sidebar-shade").show();
}
function shadeHide() {
$("#red-ui-header-shade").hide();
$("#red-ui-editor-shade").hide();
$("#red-ui-palette-shade").hide();
$("#red-ui-sidebar-shade").hide();
}
function deployButtonSetBusy(){
$(".red-ui-deploy-button-content").css('opacity',0);
$(".red-ui-deploy-button-spinner").show();
$("#red-ui-header-button-deploy").addClass("disabled");
}
function deployButtonClearBusy(){
$(".red-ui-deploy-button-content").css('opacity',1);
$(".red-ui-deploy-button-spinner").hide();
}
function stopStartFlows(state) {
const startTime = Date.now();
const deployWasEnabled = !$("#red-ui-header-button-deploy").hasClass("disabled");
deployInflight = true;
deployButtonSetBusy();
shadeShow();
RED.runtime.updateState(state);
$.ajax({
url:"flows/state",
type: "POST",
data: {state: state},
headers: {
"Node-RED-Flow-Run-State-Change": state
}
}).done(function(data,textStatus,xhr) {
if (deployWasEnabled) {
$("#red-ui-header-button-deploy").removeClass("disabled");
}
RED.runtime.updateState((data && data.state) || "unknown" )
RED.notify('<p>Done</p>',"success");
}).fail(function(xhr,textStatus,err) {
if (deployWasEnabled) {
$("#red-ui-header-button-deploy").removeClass("disabled");
}
if (xhr.status === 401) {
RED.notify("Not authorized" ,"error");
} else if (xhr.responseText) {
RED.notify("Operation failed: " + xhr.responseText,"error");
} else {
RED.notify("Operation failed: no response","error");
}
RED.runtime.requestState()
}).always(function() {
const delta = Math.max(0,300-(Date.now()-startTime));
setTimeout(function() {
deployButtonClearBusy();
shadeHide()
deployInflight = false;
},delta);
});
}
function restart() {
var startTime = Date.now();
var deployWasEnabled = !$("#red-ui-header-button-deploy").hasClass("disabled");
deployInflight = true;
deployButtonSetBusy();
$.ajax({
url:"flows",
type: "POST",
@@ -307,15 +367,10 @@ RED.deploy = (function() {
RED.notify(RED._("deploy.deployFailed",{message:RED._("deploy.errors.noResponse")}),"error");
}
}).always(function() {
deployInflight = false;
var delta = Math.max(0,300-(Date.now()-startTime));
setTimeout(function() {
$(".red-ui-deploy-button-content").css('opacity',1);
$(".red-ui-deploy-button-spinner").hide();
$("#red-ui-header-shade").hide();
$("#red-ui-editor-shade").hide();
$("#red-ui-palette-shade").hide();
$("#red-ui-sidebar-shade").hide();
deployButtonClearBusy();
deployInflight = false;
},delta);
});
}
@@ -450,21 +505,17 @@ RED.deploy = (function() {
const nns = RED.nodes.createCompleteNodeSet();
const startTime = Date.now();
$(".red-ui-deploy-button-content").css('opacity', 0);
$(".red-ui-deploy-button-spinner").show();
$("#red-ui-header-button-deploy").addClass("disabled");
deployButtonSetBusy();
const data = { flows: nns };
if (!force) {
data.runtimeState = RED.runtime.state;
if (data.runtimeState === RED.runtime.states.STOPPED || force) {
data._rev = RED.nodes.version();
} else {
data.rev = RED.nodes.version();
}
deployInflight = true;
$("#red-ui-header-shade").show();
$("#red-ui-editor-shade").show();
$("#red-ui-palette-shade").show();
$("#red-ui-sidebar-shade").show();
shadeShow();
$.ajax({
url: "flows",
type: "POST",
@@ -550,15 +601,11 @@ RED.deploy = (function() {
RED.notify(RED._("deploy.deployFailed", { message: RED._("deploy.errors.noResponse") }), "error");
}
}).always(function () {
deployInflight = false;
const delta = Math.max(0, 300 - (Date.now() - startTime));
setTimeout(function () {
$(".red-ui-deploy-button-content").css('opacity', 1);
$(".red-ui-deploy-button-spinner").hide();
$("#red-ui-header-shade").hide();
$("#red-ui-editor-shade").hide();
$("#red-ui-palette-shade").hide();
$("#red-ui-sidebar-shade").hide();
deployInflight = false;
deployButtonClearBusy()
shadeHide()
}, delta);
});
}

View File

@@ -4792,6 +4792,9 @@ RED.view = (function() {
if (d._def.button) {
var buttonEnabled = isButtonEnabled(d);
this.__buttonGroup__.classList.toggle("red-ui-flow-node-button-disabled", !buttonEnabled);
if(RED.runtime && Object.hasOwn(RED.runtime,'started')) {
this.__buttonGroup__.classList.toggle("red-ui-flow-node-button-stopped", !RED.runtime.started);
}
var x = d._def.align == "right"?d.w-6:-25;
if (d._def.button.toggle && !d[d._def.button.toggle]) {

View File

@@ -176,6 +176,13 @@
cursor: default;
}
}
&.red-ui-flow-node-button-stopped {
opacity: 0.4;
.red-ui-flow-node-button-button {
cursor: default;
pointer-events: none;
}
}
}
.red-ui-flow-node-button-button {
cursor: pointer;