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