From 7d2fa4ed3e7170572d3f699a05d1c12558f6a930 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Thu, 12 Sep 2019 20:44:25 +0200 Subject: [PATCH] Initial Commit --- .gitattributes | 2 + .gitignore | 18 +++++ .gitmodules | 36 +++++++++ SystemEventHandler.js | 178 ++++++++++++++++++++++++++++++++++++++++++ TaskManager.njsproj | 39 +++++++++ TaskManager.sln | 25 ++++++ package.json | 14 ++++ server.js | 111 ++++++++++++++++++++++++++ 8 files changed, 423 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 SystemEventHandler.js create mode 100644 TaskManager.njsproj create mode 100644 TaskManager.sln create mode 100644 package.json create mode 100644 server.js diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..eba1110b5 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..d3a984f49 --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ + +.vs/* +obj/* +bin/* +/.vscode + +.env +.env.dev + +/node_modules +/dependency + +/.nyc_output +/coverage +debug.log +Windows-Superalgos-Desktop.exe +package-lock.json +MacOs-Superalgos-Desktop.app diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..7e1eb1e2d --- /dev/null +++ b/.gitmodules @@ -0,0 +1,36 @@ +[submodule "Plotters/AAMasters/plotters/Plotters-Trading-Simulation"] + path = Plotters/AAMasters/plotters/Plotters-Trading-Simulation + url = https://github.com/AAMasters/Plotters-Trading-Simulation.git + branch = develop +[submodule "Plotters/AAMasters/plotters/Plotters-Candles-Volumes"] + path = Plotters/AAMasters/plotters/Plotters-Candles-Volumes + url = https://github.com/AAMasters/Plotters-Candles-Volumes.git + branch = develop +[submodule "Plotters/AAMasters/plotters/Plotters-Bollinger-Channels"] + path = Plotters/AAMasters/plotters/Plotters-Bollinger-Channels + url = https://github.com/AAMasters/Plotters-Bollinger-Channels.git + branch = develop +[submodule "Plotters/AAMasters/plotters/Plotters-Bollinger-Bands"] + path = Plotters/AAMasters/plotters/Plotters-Bollinger-Bands + url = https://github.com/AAMasters/Plotters-Bollinger-Bands.git + branch = develop +[submodule "Plotters/AAMasters/plotters/Plotters-Stairs-Patterns"] + path = Plotters/AAMasters/plotters/Plotters-Stairs-Patterns + url = https://github.com/AAMasters/Plotters-Stairs-Patterns.git + branch = develop +[submodule "Plotters/AAMasters/plotters/Plotters-Trading"] + path = Plotters/AAMasters/plotters/Plotters-Trading + url = https://github.com/AAMasters/Plotters-Trading.git + branch = develop +[submodule "Plotters/AAVikings/plotters/Plotters-Linear-Regression-Curve"] + path = Plotters/AAVikings/plotters/Plotters-Linear-Regression-Curve + url = https://github.com/AAVikings/Plotters-Linear-Regression-Curve.git + branch = master +[submodule "Files-Component"] + path = Files-Component + url = https://github.com/Superalgos/Files-Component.git + branch = develop +[submodule "CanvasApp"] + path = CanvasApp + url = https://github.com/Superalgos/CanvasApp.git + branch = develop \ No newline at end of file diff --git a/SystemEventHandler.js b/SystemEventHandler.js new file mode 100644 index 000000000..7620ca72e --- /dev/null +++ b/SystemEventHandler.js @@ -0,0 +1,178 @@ +exports.newSystemEventHandler = function newSystemEventHandler(ipc) { + let thisObject = { + initialize: initialize, + finalize: finalize, + createEventHandler: createEventHandler, + deleteEventHandler: deleteEventHandler, + listenToEvent: listenToEvent, + stopListening: stopListening, + raiseEvent: raiseEvent + } + + let eventListeners = new Map() + let responseWaiters = new Map() + + ipc.config.id = 'Task Server'; + ipc.config.retry = 1500; + ipc.config.silent = true; + + return thisObject + + function initialize(canllerName, callBackFunction) { + + ipc.connectTo( + 'world', + function () { + ipc.of.world.on( + 'connect', + function () { + ipc.log('## Connected to Superalgos Event Server ##'.rainbow, ipc.config.delay); + + let eventCommand = { + action: 'greetings', + from: canllerName + } + + sendCommand(eventCommand) + + callBackFunction() + } + ); + ipc.of.world.on( + 'disconnect', + function () { + ipc.log('Disconnected from Superalgos Event Server '.notice); + } + ); + ipc.of.world.on( + 'message', // here is where all incomming messages are processed. + function (data) { + ipc.log('Got a message from Superalgos Event Server: '.debug, data); + + let message = JSON.parse(data) + + if (message.action === 'Event Raised') { + let key + if (message.callerId) { + key = message.eventHandlerName + '-' + message.eventType + '-' + message.callerId + } else { + key = message.eventHandlerName + '-' + message.eventType + } + let handler = eventListeners.get(key) + if (handler) { + handler.callBack(message) + } else { + console.log(key + ' not found so could not deliver event raised.') + console.log(' Message = ' + data) + } + return + } + + if (message.action === 'Event Server Response') { + let handler = responseWaiters.get(message.callerId) + if (handler) { + handler(message) + } + return + } + } + ); + } + ); + } + + function finalize() { + /* Before disconnecting we will forze all eventListeners to stop listening. */ + const eventListenersArray = [...eventListeners.values()] + for (let i = 0; i < eventListenersArray.length; i++) { + let handler = eventListenersArray[i] + let eventCommand = { + action: 'stopListening', + eventHandlerName: handler.eventHandlerName, + eventType: handler.eventType, + callerId: handler.callerId + } + sendCommand(eventCommand) + } + ipc.disconnect('world'); + } + + function sendCommand(command, responseCallBack, eventsCallBack) { + + if (command.action === 'listenToEvent') { + let key + if (command.callerId) { + key = command.eventHandlerName + '-' + command.eventType + '-' + command.callerId + } else { + key = command.eventHandlerName + '-' + command.eventType + } + let handler = { + eventHandlerName: command.eventHandlerName, + eventType: command.eventType, + callerId: command.callerId, + callBack: eventsCallBack + } + eventListeners.set(key, handler) + } + if (command.callerId && responseCallBack) { + responseWaiters.set(command.callerId, responseCallBack) + } + + ipc.of.world.emit( + 'message', + JSON.stringify(command) + ) + } + + function createEventHandler(eventHandlerName, callerId, responseCallBack) { + let eventCommand = { + action: 'createEventHandler', + eventHandlerName: eventHandlerName, + callerId: callerId + } + sendCommand(eventCommand, responseCallBack) + } + + function deleteEventHandler(eventHandlerName, callerId, responseCallBack) { + let eventCommand = { + action: 'deleteEventHandler', + eventHandlerName: eventHandlerName, + callerId: callerId + } + sendCommand(eventCommand, responseCallBack) + } + + function listenToEvent(eventHandlerName, eventType, extraData, callerId, responseCallBack, eventsCallBack) { + let eventCommand = { + action: 'listenToEvent', + eventHandlerName: eventHandlerName, + eventType: eventType, + extraData: extraData, + callerId: callerId + } + sendCommand(eventCommand, responseCallBack, eventsCallBack) + } + + function stopListening(eventHandlerName, eventType, eventSubscriptionId, callerId, responseCallBack) { + /* User either needs to specify a valid eventSubscriptionId OR the 3 params: eventHandlerName, eventType, callerId which were used when start listening to events. */ + let eventCommand = { + action: 'stopListening', + eventHandlerName: eventHandlerName, + eventType: eventType, + eventSubscriptionId: eventSubscriptionId, + callerId: callerId + } + sendCommand(eventCommand, responseCallBack) + } + + function raiseEvent(eventHandlerName, eventType, event, callerId, responseCallBack) { + let eventCommand = { + action: 'raiseEvent', + eventHandlerName: eventHandlerName, + eventType: eventType, + event: event, + callerId: callerId + } + sendCommand(eventCommand, responseCallBack) + } +} \ No newline at end of file diff --git a/TaskManager.njsproj b/TaskManager.njsproj new file mode 100644 index 000000000..301f1ed74 --- /dev/null +++ b/TaskManager.njsproj @@ -0,0 +1,39 @@ + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + TaskManager + BackendServer + + + + Debug + 2.0 + 6a53ad2e-c389-4d2c-bded-23dbab231315 + + + server.js + False + + + . + . + v4.0 + {3AF33F2E-1136-4D97-BBB7-1795711AC8B8};{9092AA53-FB77-4645-B42D-1CCCA6BD08BD} + false + + + true + + + true + + + + + + + + + + \ No newline at end of file diff --git a/TaskManager.sln b/TaskManager.sln new file mode 100644 index 000000000..33ecf3244 --- /dev/null +++ b/TaskManager.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27130.2027 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9092AA53-FB77-4645-B42D-1CCCA6BD08BD}") = "TaskManager", "TaskManager.njsproj", "{6A53AD2E-C389-4D2C-BDED-23DBAB231315}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6A53AD2E-C389-4D2C-BDED-23DBAB231315}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6A53AD2E-C389-4D2C-BDED-23DBAB231315}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6A53AD2E-C389-4D2C-BDED-23DBAB231315}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6A53AD2E-C389-4D2C-BDED-23DBAB231315}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {9E8A5837-6A3F-44AC-AB5C-7CCAFDE75049} + EndGlobalSection +EndGlobal diff --git a/package.json b/package.json new file mode 100644 index 000000000..567884cf5 --- /dev/null +++ b/package.json @@ -0,0 +1,14 @@ +{ + "name": "backend-server", + "version": "0.0.0", + "description": "BackendServer", + "main": "app.js", + "author": { + "name": "Luis" + }, + "dependencies": { + "child_process": "^1.0.2", + "dotenv": "^8.1.0", + "node-ipc": "^9.1.1" + } +} diff --git a/server.js b/server.js new file mode 100644 index 000000000..322cbaa1d --- /dev/null +++ b/server.js @@ -0,0 +1,111 @@ +/* + +This server creates and destroys Task Servers according to what is going on at the UI. Each of these created servers will do their own +work. The responsibility of this server is to manage those children processes in order to be in sync with what the end user want and is defining +at the user interface. + +*/ + +const { fork } = require('child_process') +require('dotenv').config() +tasks = new Map() + +const EVENT_HANDLER_MODULE = require('./SystemEventHandler.js'); +const IPC = require('node-ipc'); +global.SYSTEM_EVENT_HANDLER = EVENT_HANDLER_MODULE.newSystemEventHandler(IPC) +global.SYSTEM_EVENT_HANDLER.initialize('Task Manager', bootLoader) + +console.log('Task Manager Server Started.') + +/* + +Here we will create an Event Handler for this server. After that we will listen to events happening here. Elements of the UI will raise events here, +as a way to communicate what is going on with the user interacting with UI components. That is the way this server will know for example when a new +task needs to be created or destroyed. + +*/ + +function bootLoader() { + global.SYSTEM_EVENT_HANDLER.createEventHandler('Task Manager') + global.SYSTEM_EVENT_HANDLER.listenToEvent('Task Manager', 'Run Task', undefined, undefined, undefined, runTask) + global.SYSTEM_EVENT_HANDLER.listenToEvent('Task Manager', 'Stop Task', undefined, undefined, undefined, stopTask) +} + +function runTask(message) { + console.log('[INFO] Task Manager -> server -> runTask -> Entering function.') + + if (message.event === undefined) { + console.log('[WARN] Task Manager -> server -> runTask -> Message Received Without Event -> message = ' + JSON.stringify(message)) + return + } + + if (message.event.taskId === undefined) { + console.log('[WARN] Task Manager -> server -> runTask -> Message Received Without taskId -> message = ' + JSON.stringify(message)) + return + } + + if (message.event.definition === undefined) { + console.log('[WARN] Task Manager -> server -> runTask -> Message Received Without Definition -> message = ' + JSON.stringify(message)) + return + } + + console.log('[INFO] Task Manager -> server -> runTask -> Task Name = ' + message.event.taskName) + console.log('[INFO] Task Manager -> server -> runTask -> Task Id = ' + message.event.taskId) + + let path = process.env.TASK_SERVER_PATH + '/server.js' + + /* Workarround to avoid having the same debub port at the forked process which makes it crash. */ + for (let i = 0; i < process.execArgv.length; i++) { + let argument = process.execArgv[i] + if (argument.indexOf('--inspect') > -1) { + process.execArgv[i] = '' + } + } + + /* Forking this process. */ + let task = { + childProcess: fork(path , [message.event.definition], { stdio: 'inherit' }), + id: message.event.taskId, + name: message.event.taskName + } + + tasks.set(message.event.taskId, task) + + task.childProcess.on('error', (err) => { + console.log('[ERROR] Task Manager -> server -> runTask -> Problem with Task Name = ' + task.name) + console.log('[ERROR] Task Manager -> server -> runTask -> Problem with Task Id = ' + task.id) + console.log(`[ERROR] Task Manager -> server -> runTask -> Task Manager exited with error ${err}`) + }) + task.childProcess.on('close', (code, signal) => { + console.log('[INFO] Task Manager -> server -> runTask -> Task Terminated. -> Task Name = ' + task.name) + console.log('[INFO] Task Manager -> server -> runTask -> Task Terminated. -> Task Id = ' + task.id) + tasks.delete(task.id) + }) +} + +function stopTask(message) { + + console.log('[INFO] Task Manager -> server -> stopTask -> Entering function.') + + if (message.event === undefined) { + console.log('[WARN] Task Manager -> server -> stopTask -> Message Received Without Event -> message = ' + JSON.stringify(message)) + return + } + + if (message.event.taskId === undefined) { + console.log('[WARN] Task Manager -> server -> stopTask -> Message Received Without taskId -> message = ' + JSON.stringify(message)) + return + } + + console.log('[INFO] Task Manager -> server -> stopTask -> Task Name = ' + message.event.taskName) + console.log('[INFO] Task Manager -> server -> stopTask -> Task Id = ' + message.event.taskId) + + let task = tasks.get(message.event.taskId) + + if (task) { + task.childProcess.send('Stop this Task'); + console.log('[INFO] Task Manager -> server -> stopTask -> Child Process instructed to finish.') + } else { + console.log('[WARN] Task Manager -> server -> stopTask -> Cannot delete Task that does not exist.') + } +} \ No newline at end of file