diff --git a/packages/models-library/src/models_library/api_schemas_dynamic_sidecar/ports.py b/packages/models-library/src/models_library/api_schemas_dynamic_sidecar/ports.py index 5863b53b2bc6..01214a39537f 100644 --- a/packages/models-library/src/models_library/api_schemas_dynamic_sidecar/ports.py +++ b/packages/models-library/src/models_library/api_schemas_dynamic_sidecar/ports.py @@ -11,14 +11,14 @@ class OutputStatus(StrAutoEnum): UPLOAD_STARTED = auto() UPLOAD_WAS_ABORTED = auto() UPLOAD_FINISHED_SUCCESSFULLY = auto() - UPLOAD_FINISHED_WITH_ERRROR = auto() + UPLOAD_FINISHED_WITH_ERROR = auto() class InputStatus(StrAutoEnum): DOWNLOAD_STARTED = auto() DOWNLOAD_WAS_ABORTED = auto() DOWNLOAD_FINISHED_SUCCESSFULLY = auto() - DOWNLOAD_FINISHED_WITH_ERRROR = auto() + DOWNLOAD_FINISHED_WITH_ERROR = auto() class _PortStatusCommon(BaseModel): diff --git a/services/dynamic-sidecar/src/simcore_service_dynamic_sidecar/modules/notifications/_notifications_ports.py b/services/dynamic-sidecar/src/simcore_service_dynamic_sidecar/modules/notifications/_notifications_ports.py index ae48f19a973f..6a8c45e35daf 100644 --- a/services/dynamic-sidecar/src/simcore_service_dynamic_sidecar/modules/notifications/_notifications_ports.py +++ b/services/dynamic-sidecar/src/simcore_service_dynamic_sidecar/modules/notifications/_notifications_ports.py @@ -52,7 +52,7 @@ async def send_output_port_upload_finished_with_error( self, port_key: ServicePortKey ) -> None: await self._send_output_port_status( - port_key, OutputStatus.UPLOAD_FINISHED_WITH_ERRROR + port_key, OutputStatus.UPLOAD_FINISHED_WITH_ERROR ) async def send_input_port_download_started(self, port_key: ServicePortKey) -> None: @@ -74,5 +74,5 @@ async def send_input_port_download_finished_with_error( self, port_key: ServicePortKey ) -> None: await self._send_input_port_status( - port_key, InputStatus.DOWNLOAD_FINISHED_WITH_ERRROR + port_key, InputStatus.DOWNLOAD_FINISHED_WITH_ERROR ) diff --git a/services/dynamic-sidecar/tests/unit/test_modules_notifier.py b/services/dynamic-sidecar/tests/unit/test_modules_notifier.py index 654d2bb16191..c7f7e7701668 100644 --- a/services/dynamic-sidecar/tests/unit/test_modules_notifier.py +++ b/services/dynamic-sidecar/tests/unit/test_modules_notifier.py @@ -289,7 +289,7 @@ async def test_notifier_send_input_port_status( await port_notifier.send_input_port_download_finished_succesfully( port_key ) - case InputStatus.DOWNLOAD_FINISHED_WITH_ERRROR: + case InputStatus.DOWNLOAD_FINISHED_WITH_ERROR: await port_notifier.send_input_port_download_finished_with_error( port_key ) @@ -378,7 +378,7 @@ async def test_notifier_send_output_port_status( await port_notifier.send_output_port_upload_finished_successfully( port_key ) - case OutputStatus.UPLOAD_FINISHED_WITH_ERRROR: + case OutputStatus.UPLOAD_FINISHED_WITH_ERROR: await port_notifier.send_output_port_upload_finished_with_error( port_key ) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/Dashboard.js b/services/static-webserver/client/source/class/osparc/dashboard/Dashboard.js index effe47c25c4a..b26b4d52e17b 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/Dashboard.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/Dashboard.js @@ -138,7 +138,8 @@ qx.Class.define("osparc.dashboard.Dashboard", { }); const tabButton = tabPage.getChildControl("button"); tabButton.set({ - minWidth: 50 + minWidth: 50, + maxHeight: 36, }); tabButton.ttt = label; tabButton.getChildControl("label").set({ diff --git a/services/static-webserver/client/source/class/osparc/data/model/IframeHandler.js b/services/static-webserver/client/source/class/osparc/data/model/IframeHandler.js index 39dbde53c8fe..1dd46c13b134 100644 --- a/services/static-webserver/client/source/class/osparc/data/model/IframeHandler.js +++ b/services/static-webserver/client/source/class/osparc/data/model/IframeHandler.js @@ -23,6 +23,12 @@ qx.Class.define("osparc.data.model.IframeHandler", { this.setStudy(study); this.setNode(node); + node.getStatus().addListener("changeInteractive", e => { + const newStatus = e.getData(); + const oldStatus = e.getOldData(); + this.__statusInteractiveChanged(newStatus, oldStatus); + }); + this.__initLoadingPage(); this.__initIFrame(); }, @@ -51,12 +57,6 @@ qx.Class.define("osparc.data.model.IframeHandler", { check: "osparc.widget.PersistentIframe", init: null, nullable: true - }, - - polling: { - check: "Boolean", - init: null, - nullable: true } }, @@ -69,12 +69,7 @@ qx.Class.define("osparc.data.model.IframeHandler", { __stopRequestingStatus: null, __retriesLeft: null, - startPolling: function() { - if (this.isPolling()) { - return; - } - this.setPolling(true); - + checkState: function() { this.getNode().getStatus().getProgressSequence() .resetSequence(); @@ -87,7 +82,7 @@ qx.Class.define("osparc.data.model.IframeHandler", { .resetSequence(); this.__unresponsiveRetries = 5; - this.__nodeState(false); + this.__nodeState(); this.getIFrame().resetSource(); }, @@ -124,47 +119,27 @@ qx.Class.define("osparc.data.model.IframeHandler", { }); loadingPage.addExtraWidget(sequenceWidget); - nodeStatus.addListener("changeInteractive", () => { - loadingPage.setHeader(this.__getLoadingPageHeader()); - const status = nodeStatus.getInteractive(); - if (["idle", "failed"].includes(status)) { - const startButton = new qx.ui.form.Button().set({ - label: this.tr("Start"), - icon: "@FontAwesome5Solid/play/18", - font: "text-18", - allowGrowX: false, - height: 32 - }); - startButton.addListener("execute", () => node.requestStartNode()); - loadingPage.addWidgetToMessages(startButton); - } else { - loadingPage.setMessages([]); - } - }, this); this.setLoadingPage(loadingPage); }, - __getLoadingPageHeader: function() { + __getLoadingPageHeader: function(status) { const node = this.getNode(); - let statusText = this.tr("Starting"); - const status = node.getStatus().getInteractive(); - if (status) { - statusText = status.charAt(0).toUpperCase() + status.slice(1); + if (status === undefined) { + status = node.getStatus().getInteractive(); } + const statusText = status ? (status.charAt(0).toUpperCase() + status.slice(1)) : this.tr("Starting"); const metadata = node.getMetaData(); const versionDisplay = osparc.service.Utils.extractVersionDisplay(metadata); return statusText + " " + node.getLabel() + " v" + versionDisplay + ""; }, - __nodeState: function(starting=true) { + __nodeState: function() { // Check if study is still there if (this.getStudy() === null || this.__stopRequestingStatus === true) { - this.setPolling(false); return; } // Check if node is still there if (this.getStudy().getWorkbench().getNode(this.getNode().getNodeId()) === null) { - this.setPolling(false); return; } @@ -176,7 +151,7 @@ qx.Class.define("osparc.data.model.IframeHandler", { } }; osparc.data.Resources.fetch("studies", "getNode", params) - .then(data => this.__onNodeState(data, starting)) + .then(data => this.onNodeState(data)) .catch(err => { let errorMsg = `Error retrieving ${node.getLabel()} status: ${err}`; if ("status" in err && err.status === 406) { @@ -191,7 +166,6 @@ qx.Class.define("osparc.data.model.IframeHandler", { }; node.fireDataEvent("showInLogger", errorMsgData); if ("status" in err && err.status === 406) { - this.setPolling(false); return; } if (this.__unresponsiveRetries > 0) { @@ -203,32 +177,24 @@ qx.Class.define("osparc.data.model.IframeHandler", { }; node.fireDataEvent("showInLogger", retryMsgData); this.__unresponsiveRetries--; - const interval = Math.floor(Math.random() * 5000) + 3000; - setTimeout(() => this.__nodeState(), interval); } else { - this.setPolling(false); node.getStatus().setInteractive("failed"); osparc.FlashMessenger.getInstance().logAs(this.tr("There was an error starting") + " " + node.getLabel(), "ERROR"); } }); }, - __onNodeState: function(data, starting=true) { + onNodeState: function(data) { const serviceState = data["service_state"]; const nodeId = data["service_uuid"]; const node = this.getNode(); const status = node.getStatus(); - let nextPollIn = null; - let pollingInNextStage = null; switch (serviceState) { case "idle": { status.setInteractive(serviceState); - if (starting && this.__unresponsiveRetries>0) { + if (this.__unresponsiveRetries>0) { // a bit of a hack. We will get rid of it when the backend pushes the states this.__unresponsiveRetries--; - nextPollIn = 2000; - } else { - this.setPolling(false); } break; } @@ -248,7 +214,6 @@ qx.Class.define("osparc.data.model.IframeHandler", { node.fireDataEvent("showInLogger", msgData); } status.setInteractive(serviceState); - nextPollIn = 10000; break; } case "stopping": @@ -256,28 +221,25 @@ qx.Class.define("osparc.data.model.IframeHandler", { case "starting": case "pulling": { status.setInteractive(serviceState); - nextPollIn = 5000; break; } case "running": { if (nodeId !== node.getNodeId()) { break; } - if (!starting) { - status.setInteractive("stopping"); - nextPollIn = 5000; - break; - } const { srvUrl, isDynamicV2 } = osparc.utils.Utils.computeServiceUrl(data); node.setDynamicV2(isDynamicV2); - if (srvUrl) { + if ( + srvUrl && + srvUrl !== node.getServiceUrl() // if it's already connected, do not restart the connection process + ) { + this.__statusInteractiveChanged("connecting", node.getStatus().getInteractive()); this.__retriesLeft = 40; this.__waitForServiceReady(srvUrl); } - pollingInNextStage = true; break; } case "complete": @@ -297,18 +259,10 @@ qx.Class.define("osparc.data.model.IframeHandler", { console.error(serviceState, "service state not supported"); break; } - if (nextPollIn) { - qx.event.Timer.once(() => this.__nodeState(starting), this, nextPollIn); - } else if (pollingInNextStage !== true) { - this.setPolling(false); - } }, __waitForServiceReady: function(srvUrl) { - this.getNode().getStatus().setInteractive("connecting"); - if (this.__retriesLeft === 0) { - this.setPolling(false); return; } @@ -317,7 +271,6 @@ qx.Class.define("osparc.data.model.IframeHandler", { // Check if node is still there if (this.getStudy().getWorkbench().getNode(this.getNode().getNodeId()) === null) { - this.setPolling(false); return; } const interval = 5000; @@ -335,7 +288,6 @@ qx.Class.define("osparc.data.model.IframeHandler", { console.log("Connecting: fetch's response status", response.status); } if (response.status < 400) { - this.setPolling(false); this.__serviceReadyIn(srvUrl); } else { console.log(`Connecting: ${srvUrl} is not reachable. Status: ${response.status}`); @@ -356,16 +308,57 @@ qx.Class.define("osparc.data.model.IframeHandler", { const node = this.getNode(); node.setServiceUrl(srvUrl); node.getStatus().setInteractive("ready"); - const msg = "Service ready on " + srvUrl; - const msgData = { - nodeId: node.getNodeId(), - msg, - level: "INFO" - }; - node.fireDataEvent("showInLogger", msgData); - this.__restartIFrame(); - if (!node.isDynamicV2()) { - node.callRetrieveInputs(); + }, + + __statusInteractiveChanged: function(status, oldStatus) { + if (status === oldStatus) { + return; + } + + const node = this.getNode(); + + const loadingPage = node.getLoadingPage(); + loadingPage.setHeader(this.__getLoadingPageHeader(status)); + loadingPage.clearMessages(); + if (["idle", "failed"].includes(status)) { + const startButton = new qx.ui.form.Button().set({ + label: this.tr("Start"), + icon: "@FontAwesome5Solid/play/18", + font: "text-18", + allowGrowX: false, + height: 32 + }); + startButton.addListener("execute", () => node.requestStartNode()); + loadingPage.addWidgetToMessages(startButton); + } + + if (status === "ready") { + const msg = `Service ${node.getLabel()} ${status}`; + const msgData = { + nodeId: node.getNodeId(), + msg, + level: "INFO" + }; + node.fireDataEvent("showInLogger", msgData); + + // will switch to iframe's content + this.__restartIFrame(); + if (!node.isDynamicV2()) { + node.callRetrieveInputs(); + } + } else if (["idle", "failed", "stopping"].includes(status) && oldStatus) { + const msg = `Service ${node.getLabel()} ${status}`; + const msgData = { + nodeId: node.getNodeId(), + msg, + level: "INFO" + }; + node.fireDataEvent("showInLogger", msgData); + + // will switch to the loading page + node.resetServiceUrl(); + this.getIFrame().resetSource(); + this.fireEvent("iframeChanged"); } }, @@ -394,7 +387,7 @@ qx.Class.define("osparc.data.model.IframeHandler", { const node = this.getNode(); const status = node.getStatus().getInteractive(); // it might have been stopped - if (status === "ready") { + if (["running", "ready"].includes(status)) { this.getIFrame().resetSource(); this.getIFrame().setSource(node.getServiceUrl()); diff --git a/services/static-webserver/client/source/class/osparc/data/model/Node.js b/services/static-webserver/client/source/class/osparc/data/model/Node.js index 6373d8835cad..c54e7e1cf0e1 100644 --- a/services/static-webserver/client/source/class/osparc/data/model/Node.js +++ b/services/static-webserver/client/source/class/osparc/data/model/Node.js @@ -905,7 +905,7 @@ qx.Class.define("osparc.data.model.Node", { } }; osparc.data.Resources.fetch("studies", "startNode", params) - .then(() => this.startPollingState()) + .then(() => this.checkState()) .catch(err => { if ("status" in err && (err.status === 409 || err.status === 402)) { osparc.FlashMessenger.getInstance().logAs(err.message, "WARNING"); @@ -1055,7 +1055,7 @@ qx.Class.define("osparc.data.model.Node", { } }, - startPollingState: function() { + checkState: function() { if (this.isDynamic()) { const metadata = this.getMetaData(); const msg = "Starting " + metadata.key + ":" + metadata.version + "..."; @@ -1067,7 +1067,7 @@ qx.Class.define("osparc.data.model.Node", { this.fireDataEvent("showInLogger", msgData); if (this.getIframeHandler()) { - this.getIframeHandler().startPolling(); + this.getIframeHandler().checkState(); } else { console.error(this.getLabel() + " iframe handler not ready"); } diff --git a/services/static-webserver/client/source/class/osparc/data/model/Workbench.js b/services/static-webserver/client/source/class/osparc/data/model/Workbench.js index 3120a22f7f15..63cd8212fe2b 100644 --- a/services/static-webserver/client/source/class/osparc/data/model/Workbench.js +++ b/services/static-webserver/client/source/class/osparc/data/model/Workbench.js @@ -104,7 +104,7 @@ qx.Class.define("osparc.data.model.Workbench", { initWorkbench: function() { const allModels = this.getNodes(); const nodes = Object.values(allModels); - nodes.forEach(node => node.startPollingState()); + nodes.forEach(node => node.checkState()); }, getUpstreamCompNodes: function(node, recursive = true, upstreamNodes = new Set()) { @@ -306,7 +306,7 @@ qx.Class.define("osparc.data.model.Workbench", { node.populateNodeData(); this.giveUniqueNameToNode(node, node.getLabel()); - node.startPollingState(); + node.checkState(); return node; } catch (err) { diff --git a/services/static-webserver/client/source/class/osparc/desktop/StudyEditor.js b/services/static-webserver/client/source/class/osparc/desktop/StudyEditor.js index 1218c22fc558..5b1e0764b18b 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/StudyEditor.js +++ b/services/static-webserver/client/source/class/osparc/desktop/StudyEditor.js @@ -287,6 +287,7 @@ qx.Class.define("osparc.desktop.StudyEditor", { this.__listenToNodeProgress(); this.__listenToNoMoreCreditsEvents(); this.__listenToEvent(); + this.__listenToServiceStatus(); }, __listenToLogger: function() { @@ -414,6 +415,26 @@ qx.Class.define("osparc.desktop.StudyEditor", { } }, + __listenToServiceStatus: function() { + const socket = osparc.wrapper.WebSocket.getInstance(); + + // callback for events + if (!socket.slotExists("serviceStatus")) { + socket.on("serviceStatus", data => { + const nodeId = data["service_uuid"]; + const workbench = this.getStudy().getWorkbench(); + const node = workbench.getNode(nodeId); + if (node) { + if (node.getIframeHandler()) { + node.getIframeHandler().onNodeState(data); + } + } else if (osparc.data.Permissions.getInstance().isTester()) { + console.log("Ignored ws 'progress' msg", data); + } + }, this); + } + }, + __reloadSnapshotsAndIterations: function() { const isVCDisabled = osparc.utils.DisabledPlugins.isVersionControlDisabled(); if (!isVCDisabled) { diff --git a/services/static-webserver/client/source/class/osparc/desktop/organizations/ServicesList.js b/services/static-webserver/client/source/class/osparc/desktop/organizations/ServicesList.js index 6a2123753123..9d2d7a6fa1ec 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/organizations/ServicesList.js +++ b/services/static-webserver/client/source/class/osparc/desktop/organizations/ServicesList.js @@ -88,7 +88,7 @@ qx.Class.define("osparc.desktop.organizations.ServicesList", { item.addListener("openMoreInfo", e => { const serviceKey = e.getData()["key"]; const serviceVersion = e.getData()["version"]; - osparc.store.Store.getService(serviceKey, serviceVersion) + osparc.store.Services.getService(serviceKey, serviceVersion) .then(serviceData => { if (serviceData) { serviceData["resourceType"] = "service"; diff --git a/services/static-webserver/client/source/class/osparc/viewer/NodeViewer.js b/services/static-webserver/client/source/class/osparc/viewer/NodeViewer.js index 94aeb50ef5f2..57d97839dc79 100644 --- a/services/static-webserver/client/source/class/osparc/viewer/NodeViewer.js +++ b/services/static-webserver/client/source/class/osparc/viewer/NodeViewer.js @@ -79,7 +79,7 @@ qx.Class.define("osparc.viewer.NodeViewer", { const iframeHandler = node.getIframeHandler(); if (iframeHandler) { - iframeHandler.startPolling(); + iframeHandler.checkState(); iframeHandler.addListener("iframeChanged", () => this.__iFrameChanged(), this); iframeHandler.getIFrame().addListener("load", () => this.__iFrameChanged(), this); this.__iFrameChanged();