- Added:
  - Added `internalInstances: INopeObservable<string[]>` 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.
This commit is contained in:
Martin Karkowski 2022-06-27 11:46:29 +02:00
parent d67e1de191
commit 935b6a7abf
11 changed files with 467 additions and 73 deletions

View File

@ -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<string[]>` 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.

View File

@ -1 +1 @@
1.1.00
1.1.1

View File

@ -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";

View File

@ -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";

View File

@ -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<string[]>}
* @memberof NopeInstanceManager
*/
public readonly internalInstances: INopeObservable<string[]>;
/**
* Create the Instance Manager.
*/
constructor(
public options: INopeDispatcherOptions,
protected _generateObservable: <T>() => INopeObservable<T>,
@ -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();

View File

@ -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";

View File

@ -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<keyof typeof SERVICES_NAME>;
* }} [opts]
* @return {Promise<{
* manualSyncTime?: () => Promise<void>;
* 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<void>;
* }>} The provided Functions.
*/
export async function addAllBaseServices(dispatcher: INopeDispatcher) {
export async function addAllBaseServices(
dispatcher: INopeDispatcher,
opts: {
services?: Array<keyof typeof SERVICES_NAME>;
} = {}
): Promise<{
manualSyncTime?: () => Promise<void>;
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<void>;
}> {
await dispatcher.ready.waitFor();
const services = {
...(await generateDefineMaster(dispatcher)),
...(await generatePingServices(dispatcher)),
...(await enableTimeSyncing(dispatcher)),
...(await enablingSyncingData(dispatcher)),
};
let services: {
[index: string]: (...args) => Promise<any>;
} = {};
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;
}

View File

@ -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<string[]>}
* @memberof INopeInstanceManager
*/
readonly internalInstances: INopeObservable<string[]>;
/**
* 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"

50
package-lock.json generated
View File

@ -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"
}
}
}
}

View File

@ -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/**/*",

View File

@ -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
}