From 5f0664fa816dd8c682e31160e7248563ade273ae Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Wed, 22 Apr 2020 13:57:41 +0200 Subject: [PATCH] Remote Backends Implemented --- Canvas.js | 2 - ChartingSpace/PlottersManager.js | 3 + .../FunctionLibraries/SessionFunctions.js | 75 +++++++++++++++---- .../FunctionLibraries/TaskFunctions.js | 54 +++++++++++-- DesignSpace/Workspace/Workspace.js | 48 ++++++++++++ EventsServerClient.js | 20 ++--- FloatingSpace/CircularProgressBar.js | 22 ++++-- FloatingSpace/UiObject.js | 31 +++++--- Panels/Layer.js | 4 +- PostLoader.js | 5 +- ProductStorage.js | 10 +-- 11 files changed, 211 insertions(+), 63 deletions(-) diff --git a/Canvas.js b/Canvas.js index 1d0e440d0..b13ac359c 100644 --- a/Canvas.js +++ b/Canvas.js @@ -130,8 +130,6 @@ function newCanvas () { animation.initialize() thisObject.animation = animation - /* Low Level Infraestructure First */ - animation.addCallBackFunction('System Event Handler Physics', eventsServerClient.physics) /* Spcaces Physics */ animation.addCallBackFunction('CockpitSpace Physics', thisObject.cockpitSpace.physics) diff --git a/ChartingSpace/PlottersManager.js b/ChartingSpace/PlottersManager.js index 5610ec1b1..a0002b6fa 100644 --- a/ChartingSpace/PlottersManager.js +++ b/ChartingSpace/PlottersManager.js @@ -151,6 +151,8 @@ function newPlottersManager () { if (host === undefined) { host = 'localhost' } if (webPort === undefined) { webPort = window.location.port } + let eventsServerClient = canvas.designSpace.workspace.eventsServerClients.get(layer.networkNode.id) + storage.initialize( dataMine, bot, @@ -162,6 +164,7 @@ function newPlottersManager () { timeFrame, host, webPort, + eventsServerClient, onProductStorageInitialized ) diff --git a/DesignSpace/Workspace/FunctionLibraries/SessionFunctions.js b/DesignSpace/Workspace/FunctionLibraries/SessionFunctions.js index 6d0b3d230..e1f7883e2 100644 --- a/DesignSpace/Workspace/FunctionLibraries/SessionFunctions.js +++ b/DesignSpace/Workspace/FunctionLibraries/SessionFunctions.js @@ -7,26 +7,15 @@ function newSessionFunctions () { return thisObject function runSession (node, functionLibraryProtocolNode, functionLibraryDependenciesFilter, callBackFunction) { - /* We can not run a sessionif its parent process is not running. Less if it does not have a parent. */ - if (node.payload.parentNode === undefined) { - node.payload.uiObject.setErrorMessage('Session needs a Process Instance parent to be able to run.') + if (validations(node) !== true) { callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) return } - if (node.payload.parentNode.payload.uiObject.isRunning !== true) { - node.payload.uiObject.setErrorMessage('Session needs a Process Instance parent to be running to be able to run.') - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) - return - } + let networkNode = node.payload.parentNode.payload.parentNode.payload.parentNode.payload.parentNode.payload.parentNode.payload.parentNode.payload.parentNode + let eventsServerClient = canvas.designSpace.workspace.eventsServerClients.get(networkNode.id) - if (node.payload.referenceParent === undefined) { - node.payload.uiObject.setErrorMessage('Session needs to reference a Trading System.') - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) - return - } - - node.payload.uiObject.run(callBackFunction) + node.payload.uiObject.run(eventsServerClient, callBackFunction) let key = node.name + '-' + node.type + '-' + node.id @@ -77,9 +66,65 @@ function newSessionFunctions () { } function stopSession (node, functionLibraryProtocolNode, callBackFunction) { + if (validations(node) !== true) { + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + return + } + + let networkNode = node.payload.parentNode.payload.parentNode.payload.parentNode.payload.parentNode.payload.parentNode.payload.parentNode.payload.parentNode + let eventsServerClient = canvas.designSpace.workspace.eventsServerClients.get(networkNode.id) + let key = node.name + '-' + node.type + '-' + node.id eventsServerClient.raiseEvent(key, 'Stop Session') node.payload.uiObject.stop(callBackFunction) } + + function validations (node) { + if (node.payload.parentNode === undefined) { + node.payload.uiObject.setErrorMessage('Session needs a Process Instance parent to be able to run.') + return + } + + if (node.payload.parentNode.payload.parentNode === undefined) { + node.payload.uiObject.setErrorMessage('Session needs to be inside a Trading Process Instance.') + return + } + + if (node.payload.parentNode.payload.parentNode.payload.parentNode === undefined) { + node.payload.uiObject.setErrorMessage('Session needs to be inside a Task.') + return + } + + if (node.payload.parentNode.payload.parentNode.payload.parentNode.payload.parentNode === undefined) { + node.payload.uiObject.setErrorMessage('Session needs to be inside a Task Manager.') + return + } + + if (node.payload.parentNode.payload.parentNode.payload.parentNode.payload.parentNode.payload.parentNode === undefined) { + node.payload.uiObject.setErrorMessage('Session needs to be inside Exchange Tasks.') + return + } + + if (node.payload.parentNode.payload.parentNode.payload.parentNode.payload.parentNode.payload.parentNode.payload.parentNode === undefined) { + node.payload.uiObject.setErrorMessage('Session needs to be inside a Testing or Production Environment.') + return + } + + if (node.payload.parentNode.payload.parentNode.payload.parentNode.payload.parentNode.payload.parentNode.payload.parentNode.payload.parentNode === undefined) { + node.payload.uiObject.setErrorMessage('Session needs to be inside a Network Node.') + return + } + + if (node.payload.parentNode.payload.uiObject.isRunning !== true) { + node.payload.uiObject.setErrorMessage('Session needs a Process Instance parent to be running.') + return + } + + if (node.payload.referenceParent === undefined) { + node.payload.uiObject.setErrorMessage('Session needs to reference a Trading System.') + return + } + return true + } } diff --git a/DesignSpace/Workspace/FunctionLibraries/TaskFunctions.js b/DesignSpace/Workspace/FunctionLibraries/TaskFunctions.js index 6c89285ed..f9a617a8a 100644 --- a/DesignSpace/Workspace/FunctionLibraries/TaskFunctions.js +++ b/DesignSpace/Workspace/FunctionLibraries/TaskFunctions.js @@ -13,19 +13,17 @@ function newTaskFunctions () { return thisObject function runTask (node, functionLibraryProtocolNode, callBackFunction) { - /* Check if it is possible to Run or not */ - if (node.bot === undefined) { - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) - return - } - if (node.bot.processes.length === 0) { + if (validations(node) !== true) { callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) return } + let networkNode = node.payload.parentNode.payload.parentNode.payload.parentNode.payload.parentNode + let eventsServerClient = canvas.designSpace.workspace.eventsServerClients.get(networkNode.id) + for (let i = 0; i < node.bot.processes.length; i++) { let process = node.bot.processes[i] - process.payload.uiObject.run() + process.payload.uiObject.run(eventsServerClient) } let lightingPath = '->Task->' + @@ -72,11 +70,19 @@ function newTaskFunctions () { return } - node.payload.uiObject.run(callBackFunction) + node.payload.uiObject.run(eventsServerClient, callBackFunction) eventsServerClient.raiseEvent('Task Manager', 'Run Task', event) } function stopTask (node, functionLibraryProtocolNode, callBackFunction) { + if (validations(node) !== true) { + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + return + } + + let networkNode = node.payload.parentNode.payload.parentNode.payload.parentNode.payload.parentNode + let eventsServerClient = canvas.designSpace.workspace.eventsServerClients.get(networkNode.id) + let event = { taskId: node.id, taskName: node.name @@ -94,6 +100,38 @@ function newTaskFunctions () { } } + function validations (node) { + if (node.bot === undefined) { + node.payload.uiObject.setErrorMessage('Task needs to have a Bot Instance.') + return + } + if (node.bot.processes.length === 0) { + node.payload.uiObject.setErrorMessage('Task Bot Instance needs to have al least once Process Instance.') + return + } + + if (node.payload.parentNode === undefined) { + node.payload.uiObject.setErrorMessage('Task needs to be inside a Task Manager.') + return + } + + if (node.payload.parentNode.payload.parentNode === undefined) { + node.payload.uiObject.setErrorMessage('Task needs to be inside Exchange Tasks.') + return + } + + if (node.payload.parentNode.payload.parentNode.payload.parentNode === undefined) { + node.payload.uiObject.setErrorMessage('Task needs to be inside a Testing or Production Environment or a Data Mining node.') + return + } + + if (node.payload.parentNode.payload.parentNode.payload.parentNode.payload.parentNode === undefined) { + node.payload.uiObject.setErrorMessage('Task needs to be inside a Network Node.') + return + } + return true + } + function runAllTasks (taskManager, functionLibraryProtocolNode) { for (let i = 0; i < taskManager.tasks.length; i++) { let node = taskManager.tasks[i] diff --git a/DesignSpace/Workspace/Workspace.js b/DesignSpace/Workspace/Workspace.js index dedea1c5b..d076dcb8f 100644 --- a/DesignSpace/Workspace/Workspace.js +++ b/DesignSpace/Workspace/Workspace.js @@ -10,6 +10,7 @@ function newWorkspace () { container: undefined, enabled: false, nodeChildren: undefined, + eventsServerClients: new Map(), save: saveWorkspace, getHierarchyHeads: getHierarchyHeads, getNodeThatIsOnFocus: getNodeThatIsOnFocus, @@ -93,6 +94,7 @@ function newWorkspace () { thisObject.workspaceNode = JSON.parse(savedWorkspace) } functionLibraryUiObjectsFromNodes.recreateWorkspace(thisObject.workspaceNode) + setupEventsServerClients() thisObject.enabled = true setInterval(saveWorkspace, 60000) @@ -101,6 +103,38 @@ function newWorkspace () { } } + function setupEventsServerClients () { + for (let i = 0; i < thisObject.workspaceNode.rootNodes.length; i++) { + let rootNode = thisObject.workspaceNode.rootNodes[i] + + if (rootNode.type === 'Network') { + for (let j = 0; j < rootNode.networkNodes.length; j++) { + let networkNode = rootNode.networkNodes[j] + + let host + let webSocketsPort + /* At this point the node does not have the payload property yet, that is why we have to do this manually */ + try { + let code = JSON.parse(networkNode.code) + host = code.host + webSocketsPort = code.webSocketsPort + } catch (err) { + console.log('[ERROR] networkNode ' + networkNode.name + ' has an invalid configuration. Cannot know the host name and webSocketsPort.') + return + } + + if (host === undefined) { host = 'localhost' } + if (webSocketsPort === undefined) { webSocketsPort = '8080' } + + let eventsServerClient = newEventsServerClient(host, webSocketsPort) + eventsServerClient.initialize() + + thisObject.eventsServerClients.set(networkNode.id, eventsServerClient) + } + } + } + } + function chainDetachNode (node) { functionLibraryChainAttachDetach.chainDetachNode(node, thisObject.workspaceNode.rootNodes) } @@ -130,6 +164,19 @@ function newWorkspace () { } function physics () { + eventsServerClientsPhysics() + replacingWorkspacePhysics() + } + + function eventsServerClientsPhysics () { + thisObject.eventsServerClients.forEach(applyPhysics) + + function applyPhysics (eventServerClient) { + eventServerClient.physics() + } + } + + function replacingWorkspacePhysics () { if (thisObject.enabled !== true) { return } if (workingAtTask > 0) { @@ -151,6 +198,7 @@ function newWorkspace () { break case 4: functionLibraryUiObjectsFromNodes.recreateWorkspace(thisObject.workspaceNode, true) + setupEventsServerClients() workingAtTask++ break case 5: diff --git a/EventsServerClient.js b/EventsServerClient.js index f5d05ea8f..a94c1495a 100644 --- a/EventsServerClient.js +++ b/EventsServerClient.js @@ -1,5 +1,5 @@ -function newEventsServerClient () { +function newEventsServerClient (host, port) { /* Web Sockets Connection */ const MODULE_NAME = 'System Event Handler' @@ -8,7 +8,7 @@ function newEventsServerClient () { const logger = newWebDebugLog() logger.fileName = MODULE_NAME - const WEB_SOCKETS_URL = 'ws://localhost:8080' + const WEB_SOCKETS_URL = 'ws://' + host + ':' + port + '' let WEB_SOCKETS_CONNECTION let thisObject = { @@ -56,12 +56,7 @@ function newEventsServerClient () { responseWaiters.set(command.callerId, responseCallBack) } - if (WEB_SOCKETS_CONNECTION.readyState === 1) { - // 1 means connected and ready. - sendToWebSocketServer(command) - } else { - console.log('WebSocket message could not be sent because the connection was not ready. Message = ' + JSON.stringify(command)) - } + sendToWebSocketServer(command) } function sendToWebSocketServer (command) { @@ -72,7 +67,12 @@ function newEventsServerClient () { commandsSentByTimestamp.set(stringNonce, timestamp) let messageToWebSocketServer = 'Web Browser' + '|*|' + stringNonce + '|*|' + JSON.stringify(command) - WEB_SOCKETS_CONNECTION.send(messageToWebSocketServer) + + if (WEB_SOCKETS_CONNECTION.readyState === 1) { + WEB_SOCKETS_CONNECTION.send(messageToWebSocketServer) + } else { + console.log('WebSocket message could not be sent because the connection was not ready. Will retry soon. Message = ' + JSON.stringify(command)) + } } function createEventHandler (eventHandlerName, callerId, responseCallBack) { @@ -153,7 +153,7 @@ function newEventsServerClient () { } if (thisObject.isConnected() !== true) { - canvas.cockpitSpace.setStatus('Connection with the local backend lost. Please check that all localhost servers are running.', 100, canvas.cockpitSpace.statusTypes.WARNING) + canvas.cockpitSpace.setStatus('Connecting with ' + host + '. Please hold on until the connection can be stablished.', 100, canvas.cockpitSpace.statusTypes.WARNING) } else { retryCommandsPhysics() } diff --git a/FloatingSpace/CircularProgressBar.js b/FloatingSpace/CircularProgressBar.js index 8e6a55336..d662589c8 100644 --- a/FloatingSpace/CircularProgressBar.js +++ b/FloatingSpace/CircularProgressBar.js @@ -22,27 +22,35 @@ function newCircularProgressBar () { thisObject.container.frame.position.y = 0 let opacityCounters = [] - let eventSubscriptionIdHeartbeat - let needToDrawRing = true + let eventsServerClient return thisObject function finalize () { - let key = thisObject.payload.node.name + '-' + thisObject.payload.node.type + '-' + thisObject.payload.node.id - if (eventSubscriptionIdHeartbeat !== undefined) { - eventsServerClient.stopListening(key, eventSubscriptionIdHeartbeat) - } + finalizeEventsServerClient() thisObject.container = undefined thisObject.payload = undefined thisObject.fitFunction = undefined } - function initialize (payload) { + function finalizeEventsServerClient () { + if (eventsServerClient !== undefined) { + let key = thisObject.payload.node.name + '-' + thisObject.payload.node.type + '-' + thisObject.payload.node.id + if (eventSubscriptionIdHeartbeat !== undefined) { + eventsServerClient.stopListening(key, eventSubscriptionIdHeartbeat) + } + } + } + + function initialize (payload, pEventsServerClient) { thisObject.payload = payload + finalizeEventsServerClient() + eventsServerClient = pEventsServerClient + for (let i = 0; i < 60; i++) { opacityCounters.push(0) } diff --git a/FloatingSpace/UiObject.js b/FloatingSpace/UiObject.js index 43f389648..136c3c9a0 100644 --- a/FloatingSpace/UiObject.js +++ b/FloatingSpace/UiObject.js @@ -117,17 +117,12 @@ function newUiObject () { let referenceLineCounter = 0 let chainLineCounter = 0 + let eventsServerClient + return thisObject function finalize () { - let key = thisObject.payload.node.name + '-' + thisObject.payload.node.type + '-' + thisObject.payload.node.id - - if (eventSubscriptionIdOnRunning !== undefined) { - eventsServerClient.stopListening(key, eventSubscriptionIdOnRunning, 'UiObject') - } - if (eventSubscriptionIdOnStopped !== undefined) { - eventsServerClient.stopListening(key, eventSubscriptionIdOnStopped, 'UiObject') - } + finalizeEventsServerClient() thisObject.container.eventHandler.stopListening(selfFocusEventSubscriptionId) thisObject.container.eventHandler.stopListening(selfNotFocusEventSubscriptionId) @@ -173,6 +168,19 @@ function newUiObject () { onRunningCallBackFunction = undefined } + function finalizeEventsServerClient () { + if (eventsServerClient !== undefined) { + let key = thisObject.payload.node.name + '-' + thisObject.payload.node.type + '-' + thisObject.payload.node.id + + if (eventSubscriptionIdOnRunning !== undefined) { + eventsServerClient.stopListening(key, eventSubscriptionIdOnRunning, 'UiObject') + } + if (eventSubscriptionIdOnStopped !== undefined) { + eventsServerClient.stopListening(key, eventSubscriptionIdOnStopped, 'UiObject') + } + } + } + function initialize (payload, menuItemsInitialValues) { thisObject.payload = payload @@ -750,14 +758,17 @@ function newUiObject () { } } - function run (callBackFunction) { + function run (pEventsServerClient, callBackFunction) { + finalizeEventsServerClient() + eventsServerClient = pEventsServerClient + /* We setup the circular progress bar. */ if (thisObject.circularProgressBar !== undefined) { thisObject.circularProgressBar.finalize() } thisObject.circularProgressBar = newCircularProgressBar() - thisObject.circularProgressBar.initialize(thisObject.payload) + thisObject.circularProgressBar.initialize(thisObject.payload, eventsServerClient) thisObject.circularProgressBar.fitFunction = thisObject.fitFunction thisObject.circularProgressBar.container = thisObject.container diff --git a/Panels/Layer.js b/Panels/Layer.js index 217d3d805..44e067063 100644 --- a/Panels/Layer.js +++ b/Panels/Layer.js @@ -154,9 +154,9 @@ function newLayer () { let lightingPath = '->Layer->' + 'Data Product->Single Market Data->Exchange Data Products->' + - 'Session Independent Data->Session Based Data->' + + 'Session Reference->Session Independent Data->Session Based Data->Exchange Sessions->Session Based Data->Data Storage->Network Node->' + 'Data Storage->Network Node->' + - 'Session Reference->Backtesting Session->Paper Trading Session->Fordward Testing Session->Live Trading Session->' + + 'Backtesting Session->Paper Trading Session->Fordward Testing Session->Live Trading Session->' + 'Market->Market Base Asset->Asset->' + 'Market Quoted Asset->Asset->' + 'Exchange Markets->Crypto Exchange->' + diff --git a/PostLoader.js b/PostLoader.js index 0af816538..d47eb8add 100644 --- a/PostLoader.js +++ b/PostLoader.js @@ -1,6 +1,5 @@ let canvas let markets -let eventsServerClient let APP_SCHEMA_MAP = new Map() let APP_SCHEMA_ARRAY = [] @@ -23,9 +22,7 @@ function newDashboard () { function start () { try { setBrowserEvents() - - eventsServerClient = newEventsServerClient() - eventsServerClient.initialize(setUpAppSchema) + setUpAppSchema() function setUpAppSchema () { APP_SCHEMA_ARRAY = getAppSchema() diff --git a/ProductStorage.js b/ProductStorage.js index 181486dcf..68c76ef1b 100644 --- a/ProductStorage.js +++ b/ProductStorage.js @@ -74,7 +74,7 @@ function newProductStorage (name) { } } - function initialize (dataMine, bot, session, product, exchange, market, pDatetime, pTimeFrame, host, port, callBackFunction) { + function initialize (dataMine, bot, session, product, exchange, market, pDatetime, pTimeFrame, host, port, eventsServerClient, callBackFunction) { try { datetime = pDatetime timeFrame = pTimeFrame @@ -90,7 +90,7 @@ function newProductStorage (name) { dataSetsToLoad++ let marketFiles = newMarketFiles() - marketFiles.initialize(dataMine, bot, session, product, dataset, exchange, market, host, port, onMarketFileReady) + marketFiles.initialize(dataMine, bot, session, product, dataset, exchange, market, host, port, eventsServerClient, onMarketFileReady) thisObject.marketFiles.push(marketFiles) } break @@ -99,7 +99,7 @@ function newProductStorage (name) { dataSetsToLoad++ let dailyFiles = newDailyFiles() - dailyFiles.initialize(dataMine, bot, session, product, dataset, exchange, market, pDatetime, pTimeFrame, host, port, onDailyFileReady) + dailyFiles.initialize(dataMine, bot, session, product, dataset, exchange, market, pDatetime, pTimeFrame, host, port, eventsServerClient, onDailyFileReady) thisObject.dailyFiles.push(dailyFiles) } break @@ -108,7 +108,7 @@ function newProductStorage (name) { dataSetsToLoad++ let singleFile = newSingleFile() - singleFile.initialize(dataMine, bot, session, product, dataset, exchange, market, host, port, onSingleFileReady) + singleFile.initialize(dataMine, bot, session, product, dataset, exchange, market, host, port, eventsServerClient, onSingleFileReady) thisObject.singleFile.push(singleFile) } break @@ -117,7 +117,7 @@ function newProductStorage (name) { dataSetsToLoad++ let fileSequences = newFileSequence() - fileSequences.initialize(dataMine, bot, session, product, dataset, exchange, market, host, port, onFileSequenceReady) + fileSequences.initialize(dataMine, bot, session, product, dataset, exchange, market, host, port, eventsServerClient, onFileSequenceReady) thisObject.fileSequences.push(fileSequences) } break