From 935b6a7abf7e28c7df36f973792b900ace026999 Mon Sep 17 00:00:00 2001 From: Martin Karkowski Date: Mon, 27 Jun 2022 11:46:29 +0200 Subject: [PATCH] # 1.1.1 - Added: - Added `internalInstances: INopeObservable` to `InstanceManager`.: - Added dev-depencies for libraries. - Modified: - Modified `addAllBaseServices` now includes some options, which can be used to determine the specific service to load. --- CHANGELOG.md | 7 + contribute/VERSION | 2 +- lib/communication/bridge.ts | 3 - .../layers/IoSocketClientLayer.ts | 3 - .../InstanceManager/InstanceManager.ts | 25 +- lib/dispatcher/baseServices/connectivy.ts | 3 - lib/dispatcher/baseServices/index.ts | 101 +++++- .../nope/nopeInstanceManager.interface.ts | 9 + package-lock.json | 50 +-- package.json | 2 +- wiki/14-eventDistributor.ipynb | 335 ++++++++++++++++++ 11 files changed, 467 insertions(+), 73 deletions(-) create mode 100644 wiki/14-eventDistributor.ipynb diff --git a/CHANGELOG.md b/CHANGELOG.md index 576f5fd..e5e0736 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -99,3 +99,10 @@ Inital commit, which is working with the browser - provided libraries - Added dev-depencies for libraries. - added `ui.loader` a backend component to readin the ui. + +# 1.1.1 +- Added: + - Added `internalInstances: INopeObservable` to `InstanceManager`.: + - Added dev-depencies for libraries. +- Modified: + - Modified `addAllBaseServices` now includes some options, which can be used to determine the specific service to load. \ No newline at end of file diff --git a/contribute/VERSION b/contribute/VERSION index 04a535c..8cfbc90 100644 --- a/contribute/VERSION +++ b/contribute/VERSION @@ -1 +1 @@ -1.1.00 \ No newline at end of file +1.1.1 \ No newline at end of file diff --git a/lib/communication/bridge.ts b/lib/communication/bridge.ts index b446d75..89f617d 100644 --- a/lib/communication/bridge.ts +++ b/lib/communication/bridge.ts @@ -1,9 +1,6 @@ /** * @author Martin Karkowski * @email m.karkowski@zema.de - * @create date 2020-11-06 08:52:36 - * @modify date 2022-01-03 17:34:07 - * @desc [description] */ import { EventEmitter } from "events"; diff --git a/lib/communication/layers/IoSocketClientLayer.ts b/lib/communication/layers/IoSocketClientLayer.ts index 8f08f9b..924b264 100644 --- a/lib/communication/layers/IoSocketClientLayer.ts +++ b/lib/communication/layers/IoSocketClientLayer.ts @@ -1,9 +1,6 @@ /** * @author Martin Karkowski * @email m.karkowski@zema.de - * @create date 2021-03-22 19:03:15 - * @modify date 2022-01-03 17:34:43 - * @desc [description] */ import { connect } from "socket.io-client"; diff --git a/lib/dispatcher/InstanceManager/InstanceManager.ts b/lib/dispatcher/InstanceManager/InstanceManager.ts index 76bf7d3..5d27dd3 100644 --- a/lib/dispatcher/InstanceManager/InstanceManager.ts +++ b/lib/dispatcher/InstanceManager/InstanceManager.ts @@ -1,9 +1,6 @@ /** * @author Martin Karkowski * @email m.karkowski@zema.de - * @create date 2022-01-03 19:31:44 - * @modify date 2022-01-03 19:31:44 - * @desc [description] */ import { ILogger } from "js-logger"; @@ -14,6 +11,7 @@ import { SPLITCHAR } from "../../helpers/objectMethods"; import { defineNopeLogger } from "../../logger/getLogger"; import { DEBUG } from "../../logger/index.browser"; import { NopeGenericWrapper } from "../../module/index"; +import { NopeObservable } from "../../observables"; import { IAvailableInstancesMsg, ICommunicationBridge, @@ -140,6 +138,18 @@ export class NopeInstanceManager implements INopeInstanceManager { IAvailableInstancesMsg >; + /** + * Contains the identifiers of the instances, which are hosted in the provided dispatcher. + * + * @author M.Karkowski + * @type {INopeObservable} + * @memberof NopeInstanceManager + */ + public readonly internalInstances: INopeObservable; + + /** + * Create the Instance Manager. + */ constructor( public options: INopeDispatcherOptions, protected _generateObservable: () => INopeObservable, @@ -192,6 +202,9 @@ export class NopeInstanceManager implements INopeInstanceManager { this._mappingOfRemoteDispatchersAndInstances ); + this.internalInstances = new NopeObservable(); + this.internalInstances.setContent([]); + // We will subscribe to some generators. this._rpcManager.services.data.subscribe((_) => { // Clear the Mapping of the Generators @@ -249,6 +262,9 @@ export class NopeInstanceManager implements INopeInstanceManager { _this._instances.get(identifier).instance.toDescription() ), }); + + // Update the Instances + this.internalInstances.setContent(Array.from(this._internalInstances)); } /** @@ -943,6 +959,9 @@ export class NopeInstanceManager implements INopeInstanceManager { this._initializingInstance = new Map(); this._externalInstancesNames = new Set(); + // Reset the instances + this.internalInstances.setContent([]); + if (this._communicator.connected.getContent()) { // Update the Instances this._sendAvailableInstances(); diff --git a/lib/dispatcher/baseServices/connectivy.ts b/lib/dispatcher/baseServices/connectivy.ts index b5b77da..45774d7 100644 --- a/lib/dispatcher/baseServices/connectivy.ts +++ b/lib/dispatcher/baseServices/connectivy.ts @@ -1,9 +1,6 @@ /** * @author Martin Karkowski * @email m.karkowski@zema.de - * @create date 2022-01-11 17:38:19 - * @modify date 2022-01-14 08:29:02 - * @desc [description] */ import { avgOfArray, maxOfArray, minOfArray } from "../../helpers/arrayMethods"; diff --git a/lib/dispatcher/baseServices/index.ts b/lib/dispatcher/baseServices/index.ts index bf4352a..3302145 100644 --- a/lib/dispatcher/baseServices/index.ts +++ b/lib/dispatcher/baseServices/index.ts @@ -1,8 +1,6 @@ /** * @author Martin Karkowski * @email m.karkowski@zema.de - * @create date 2022-01-14 20:33:33 - * @modify date 2022-01-14 20:39:11 * @desc [description] */ @@ -23,22 +21,103 @@ export { waitForDispatcher, }; +/** + * Helper to define simpler names for the Services + */ +export const SERVICES_NAME = { + defineMaster: generateDefineMaster, + pingService: generatePingServices, + timeSyncingService: enableTimeSyncing, + syncingDataService: enablingSyncingData, +}; + /** * Helper, which will enable all BaseServices * * @author M.Karkowski * @export - * @param {INopeDispatcher} dispatcher - * @return {*} + * @param {INopeDispatcher} dispatcher The Dispatcher to use. + * @param {{ + * services?: Array; + * }} [opts] + * @return {Promise<{ + * manualSyncTime?: () => Promise; + * determinePing?: (target: string) => Promise<{ + * requestId: string; + * dispatcherId: string; + * timestamp: number; + * isMaster: boolean; + * ping: number; + * }>; + * pingAll?: () => Promise<{ + * pings: Promise<{ + * requestId: string; + * dispatcherId: string; + * timestamp: number; + * isMaster: boolean; + * ping: number; + * }>[]; + * avg: number; + * max: { + * max: number; + * index: number; + * }; + * min: { + * min: number; + * index: number; + * }; + * }>; + * setMaster?: () => Promise; + * }>} The provided Functions. */ -export async function addAllBaseServices(dispatcher: INopeDispatcher) { +export async function addAllBaseServices( + dispatcher: INopeDispatcher, + opts: { + services?: Array; + } = {} +): Promise<{ + manualSyncTime?: () => Promise; + determinePing?: (target: string) => Promise<{ + requestId: string; + dispatcherId: string; + timestamp: number; + isMaster: boolean; + ping: number; + }>; + pingAll?: () => Promise<{ + pings: Promise<{ + requestId: string; + dispatcherId: string; + timestamp: number; + isMaster: boolean; + ping: number; + }>[]; + avg: number; + max: { + max: number; + index: number; + }; + min: { + min: number; + index: number; + }; + }>; + setMaster?: () => Promise; +}> { await dispatcher.ready.waitFor(); - const services = { - ...(await generateDefineMaster(dispatcher)), - ...(await generatePingServices(dispatcher)), - ...(await enableTimeSyncing(dispatcher)), - ...(await enablingSyncingData(dispatcher)), - }; + let services: { + [index: string]: (...args) => Promise; + } = {}; + + if (opts.services) { + for (const name of opts.services) { + services = Object.assign(services, await SERVICES_NAME[name]); + } + } else { + for (const name in SERVICES_NAME) { + services = Object.assign(services, await SERVICES_NAME[name]); + } + } return services; } diff --git a/lib/types/nope/nopeInstanceManager.interface.ts b/lib/types/nope/nopeInstanceManager.interface.ts index 72f4c0f..3d9b16a 100644 --- a/lib/types/nope/nopeInstanceManager.interface.ts +++ b/lib/types/nope/nopeInstanceManager.interface.ts @@ -92,6 +92,15 @@ export interface INopeInstanceManager { IAvailableInstancesMsg >; + /** + * Contains the identifiers of the instances, which are hosted in the provided dispatcher. + * + * @author M.Karkowski + * @type {INopeObservable} + * @memberof INopeInstanceManager + */ + readonly internalInstances: INopeObservable; + /** * Registers a Constructor, that enables other NopeInstanceManagers to create an instance of the * given type. Therefore a callback "cb" is registered with the given "typeIdentifier" diff --git a/package-lock.json b/package-lock.json index 95209d9..799e7c9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "nope", - "version": "1.0.35", + "version": "1.1.00", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "nope", - "version": "1.0.35", + "version": "1.1.00", "license": "MIT", "dependencies": { "argparse": "^2.0.1", @@ -40,7 +40,6 @@ "nope-js": "bin/nope" }, "devDependencies": { - "@angular/core": "^14.0.3", "@types/async": "^3.2.12", "@types/chai": "^4.3.0", "@types/lodash": "^4.14.178", @@ -68,22 +67,6 @@ "webpack-cli": "^4.8.0" } }, - "node_modules/@angular/core": { - "version": "14.0.3", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-14.0.3.tgz", - "integrity": "sha512-Z71BrEIJuMGm/BdK9Lh8IJwADQqA8qPeUVppCs67CABZdwA3sK0kC+iobauWXcweXU30BdQYc7HyZe2x7rcdmQ==", - "dev": true, - "dependencies": { - "tslib": "^2.3.0" - }, - "engines": { - "node": "^14.15.0 || >=16.10.0" - }, - "peerDependencies": { - "rxjs": "^6.5.3 || ^7.4.0", - "zone.js": "~0.11.4" - } - }, "node_modules/@babel/runtime": { "version": "7.16.7", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.7.tgz", @@ -18289,28 +18272,9 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } - }, - "node_modules/zone.js": { - "version": "0.11.6", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.6.tgz", - "integrity": "sha512-umJqFtKyZlPli669gB1gOrRE9hxUUGkZr7mo878z+NEBJZZixJkKeVYfnoLa7g25SseUDc92OZrMKKHySyJrFg==", - "dev": true, - "peer": true, - "dependencies": { - "tslib": "^2.3.0" - } } }, "dependencies": { - "@angular/core": { - "version": "14.0.3", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-14.0.3.tgz", - "integrity": "sha512-Z71BrEIJuMGm/BdK9Lh8IJwADQqA8qPeUVppCs67CABZdwA3sK0kC+iobauWXcweXU30BdQYc7HyZe2x7rcdmQ==", - "dev": true, - "requires": { - "tslib": "^2.3.0" - } - }, "@babel/runtime": { "version": "7.16.7", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.7.tgz", @@ -32438,16 +32402,6 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true - }, - "zone.js": { - "version": "0.11.6", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.6.tgz", - "integrity": "sha512-umJqFtKyZlPli669gB1gOrRE9hxUUGkZr7mo878z+NEBJZZixJkKeVYfnoLa7g25SseUDc92OZrMKKHySyJrFg==", - "dev": true, - "peer": true, - "requires": { - "tslib": "^2.3.0" - } } } } diff --git a/package.json b/package.json index 8abe346..f8bf927 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nope", - "version": "1.1.00", + "version": "1.1.1", "description": "NoPE Runtime for Nodejs. For Browser-Support please use nope-browser", "files": [ "dist-nodejs/**/*", diff --git a/wiki/14-eventDistributor.ipynb b/wiki/14-eventDistributor.ipynb new file mode 100644 index 0000000..2ff7e5a --- /dev/null +++ b/wiki/14-eventDistributor.ipynb @@ -0,0 +1,335 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# NoPE - Event-Distributor\n", + "\n", + "The NoPE-Dispatcher uses one `eventDistributor`. The distributor is used to share the events accross to other distributors.\n", + "\n", + "## An Event\n", + "\n", + "A event is not persisent. Once it is fired the `event-listeners` will be informed. There is ***no*** historic access to events. If you subscribed after the Event has been fired, you wont get \n", + "\n", + "## " + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "// First lets install nope using npm\n", + "const nope = require(\"../dist-nodejs/index.nodejs\")\n", + "\n", + "// Create a communicator:\n", + "// We will use the event layer (which just runs internally)\n", + "const communicator = nope.getLayer(\"event\");\n", + "\n", + "// Lets create our dispatcher\n", + "\n", + "// 1. Dispatcher simulates our local system\n", + "const localDispatcher = nope.dispatcher.getDispatcher({\n", + " communicator,\n", + " id: \"local\"\n", + "}, {\n", + " singleton: false,\n", + " useBaseServices: false\n", + "});" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> For Jupyter we need an extra async wrapper to wait for initalizing the dispatcher:\n", + "\n", + "see here for the details in Jupyter: https://n-riesco.github.io/ijavascript/doc/async.ipynb.html" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "true" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "$$.async();\n", + "// Lets wait for our element to be ready.\n", + "localDispatcher.ready.waitFor().then($$.done);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we want to listen to newly connected dispatchers. For this purpose, we create an observer, which will listen to changes." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "// Subscribe to changes\n", + "const observer = localDispatcher.eventDistributor.emit(\"eventName\",\"eventData\");" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Additionally we want to show the currently connected dispatchers. In this data the own dispatcher will **allways** be included:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "connectedDispatchers = [ 'local' ]\n", + "localDispatcherIncluded = true\n" + ] + } + ], + "source": [ + "// Show our connected Dispatchers\n", + "let connectedDispatchers = localDispatcher.connectivityManager.dispatchers.data.getContent();\n", + "let localDispatcherIncluded = connectedDispatchers.includes(localDispatcher.id);\n", + "\n", + "// Now lets log our results.\n", + "console.log(\"connectedDispatchers =\", connectedDispatchers);\n", + "console.log(\"localDispatcherIncluded =\", localDispatcherIncluded);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now that we have implemented our listeners and have seen the connected dispatchers (which is only the `\"local\"`-dispatchre), We will add an additional dispatcher. This should result in calling our `onChange`-listener. Additionally, we wait until our `remoteDispatcher` is initalized" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2022-01-20T11:39:55.766Z onChange - listener\n", + "\tadded = [ 'remote' ]\n", + "\tremoved = []\n" + ] + } + ], + "source": [ + "// 2. Dispatcher simulates our remote system\n", + "const remoteDispatcher = nope.dispatcher.getDispatcher({\n", + " communicator,\n", + " id: \"remote\"\n", + "}, {\n", + " singleton: false,\n", + " useBaseServices: false\n", + "});\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we want to see, which system is the current master. This should be our `local`." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "master = local\n" + ] + } + ], + "source": [ + "// We expect to be the master, because the localDispatcher has been created first.\n", + "console.log(\"master =\", localDispatcher.connectivityManager.master.id);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can now force the remote dispatcher to be our master, by setting the master. (For this purpose we can later use a base service ==> then we just have to call the service) " + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "$$.async();\n", + "\n", + "remoteDispatcher.connectivityManager.isMaster = true;\n", + "localDispatcher.connectivityManager.isMaster = false;\n", + "\n", + "// Our messaging is async ==> we wait an amount of time\n", + "setTimeout(() => $$.done(),1000);" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "master = remote\n", + "master-info = {\n", + " id: 'remote',\n", + " env: 'javascript',\n", + " version: '1.0.0',\n", + " isMaster: true,\n", + " host: {\n", + " cores: 8,\n", + " cpu: {\n", + " model: 'Intel(R) Core(TM) i7-8565U CPU',\n", + " speed: 1992,\n", + " usage: 0.0038778477944740875\n", + " },\n", + " os: 'win32',\n", + " ram: { usedPerc: 0.362681220626356, free: 20676, total: 32442 },\n", + " name: 'nz-078'\n", + " },\n", + " pid: 18068,\n", + " timestamp: 1642678798813,\n", + " upTime: 3049,\n", + " status: 0\n", + "}\n" + ] + } + ], + "source": [ + "// We expect the master to be the remote.\n", + "console.log(\"master =\", localDispatcher.connectivityManager.master.id);\n", + "console.log(\"master-info =\", localDispatcher.connectivityManager.master);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now lets see what happens if we adapt the heartbeat intervall of our *local* instance. We want to receive every 50 ms a heartbeat:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2022-01-20T11:40:01.089Z master-info = 0\n", + "2022-01-20T11:40:01.789Z master-info = 1\n", + "2022-01-20T11:40:02.536Z master-info = 2\n", + "2022-01-20T11:40:03.543Z master-info = 3\n", + "2022-01-20T11:40:03.977Z onChange - listener\n", + "\tadded = []\n", + "\tremoved = [ 'remote' ]\n", + "2022-01-20T11:40:04.547Z onChange - listener\n", + "\tadded = [ 'remote' ]\n", + "\tremoved = []\n" + ] + } + ], + "source": [ + "$$.async()\n", + "\n", + "const renderStatus = () => {\n", + " console.log((new Date()).toISOString(),\"master-info =\", localDispatcher.connectivityManager.master.status)\n", + "}\n", + "\n", + "setTimeout(renderStatus, 50);\n", + "setTimeout(renderStatus, 750);\n", + "setTimeout(renderStatus, 1500);\n", + "setTimeout(renderStatus, 2500);\n", + "\n", + "\n", + "localDispatcher.connectivityManager.setTimings({\n", + " // our system will send every 50 ms an heartbeat.\n", + " sendAliveInterval: 250,\n", + " // we will check that after\n", + " checkInterval: 125,\n", + " // will mark dispatchers as slow after not receiving heartbeats for 50ms\n", + " slow: 500,\n", + " // we will mark dispatchers with a warning flag after 50 ms\n", + " warn: 1000,\n", + " // we mark it as dead after 0.5 s\n", + " dead: 2000,\n", + " // We will remove the dispatcher after 1 s\n", + " remove: 3000,\n", + "});\n", + "\n", + "remoteDispatcher.connectivityManager.setTimings({\n", + " // our system will send every 50 ms an heartbeat.\n", + " sendAliveInterval: 5000,\n", + "});\n", + "\n", + "\n", + "\n", + "// We reset the timeouts.\n", + "setTimeout(() => localDispatcher.connectivityManager.setTimings({}), 3000);\n", + "setTimeout(() => remoteDispatcher.connectivityManager.setTimings({}), 3000);\n", + "setTimeout(() => $$.done(), 5000);\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "JavaScript (Node.js)", + "language": "javascript", + "name": "javascript" + }, + "language_info": { + "file_extension": ".js", + "mimetype": "application/javascript", + "name": "javascript", + "version": "17.3.1" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +}