/** * @author Martin Karkowski * @email m.karkowski@zema.de * @create date 2020-10-12 17:54:08 * @modify date 2020-10-13 17:42:18 * @desc [description] */ import { inject, injectable } from 'inversify'; import { replaceAll } from "../helpers/stringMethods"; import { DISPATCHER_INSTANCE } from '../symbols/identifiers'; import { INopeDispatcher } from "../types/nopeDispatcher.interface"; import { IAuthor, IFunctionOptions, INopeModule, INopeModuleDescription, IPropertyOptions, IVersion } from "../types/nopeModule.interface"; import { INopeObservable } from "../types/nopeObservable.interface"; /** * Base Implementation of a Module. * * @export * @class BaseModule * @implements {INopeModule} */ @injectable() export class NopeBaseModule implements INopeModule { /** * Module Name must be written in lowercase. */ private _type = ''; public get type(): string { return this._type; } public set type(value: string) { const _replacements = [ [' ', ''], // ['.', ''], // ['_', ''], ]; let adapted = value; for (const [search, replace] of _replacements) { adapted = replaceAll(adapted, search, replace); } this._type = adapted.toLowerCase(); } /** * A Description of the Module. This is used to Describe roughly * what the module is capable of * doing. * * @type {string} * @memberof BaseModule */ public description: string; /** * A Description of the Author. Use to Mail etc. * * @type {IAuthor} * @memberof BaseModule */ public author: IAuthor; /** * Description of the provided Version of the Module. * * @type {IVersion} * @memberof BaseModule */ public version: IVersion; protected _registeredFunctions: Map Promise, options: IFunctionOptions }> protected _registeredProperties: Map options: IPropertyOptions }> /** * Public getter for the functions * * @readonly * @memberof BaseModule */ public get functions() { const ret: { [index: string]: IFunctionOptions; } = {}; for (const [name, funcs] of this._registeredFunctions.entries()){ ret[name] = funcs.options; } return ret; } /** * Public get to receive a Description of the Properties * * @readonly * @memberof BaseModule */ public get properties() { const ret: { [index: string]: IPropertyOptions; } = {}; for (const [name, funcs] of this._registeredProperties.entries()){ ret[name] = funcs.options; } return ret; } /** * The Identifier of the Module. * * @type {string} * @memberof BaseModule */ public identifier: string; /** * Creates an instance of BaseModule. * @memberof BaseModule */ constructor(@inject(DISPATCHER_INSTANCE) protected _dispatcher: INopeDispatcher) { this._type = null; this.description = null; this.author = null; this.version = null; this.identifier = null; this._registeredFunctions = new Map(); this._registeredProperties = new Map(); } public async registerProperty(name: string, observable: INopeObservable, options: IPropertyOptions): Promise { // Unregister the Function await this.unregisterProperty(name); // Adapt the Topics if (typeof options.topic === 'string'){ options.topic = this.identifier + '.prop.' + name; } else if (typeof options.topic === 'object'){ if (options.topic.subscribe && !options.topic.subscribe.startsWith(this.identifier + '.prop.') ){ options.topic.subscribe = this.identifier + '.prop.' + options.topic.subscribe; } if (options.topic.publish && !options.topic.publish.startsWith(this.identifier + '.prop.')){ options.topic.publish = this.identifier + '.prop.' + options.topic.publish; } } const _observable = await this._dispatcher.registerObservable(observable, options); // Register the new Property. this._registeredProperties.set(name, { observable: _observable, options }); } public async registerFunction(name: string, func: (...args: any[]) => Promise, options: IFunctionOptions): Promise { // Unregister the Function await this.unregisterFunction(name); // Adapt the Method ID options.id = this.identifier + '.method.' + name; const _func = await this._dispatcher.registerFunction(func, options); // Register the new Function. this._registeredFunctions.set(name, { func: _func, options }); } public async unregisterFunction(name: string): Promise { // Test if the Method is already registerd, // If so => unregister it first. if (this._registeredFunctions.has(name)) { this._dispatcher.unregisterFunction(this._registeredFunctions.get(name).func, { preventSendingToRegistery: true }); } } public async unregisterProperty(name: string): Promise { // Test if the Property is already registerd, // If so => unregister it first. if (this._registeredProperties.has(name)) { this._dispatcher.unregisterObservable(this._registeredProperties.get(name).observable, { preventSendingToRegistery: true }); } } public async listFunctions(): Promise<{ func: (...args: any[]) => Promise; options: IFunctionOptions; }[]> { return Array.from(this._registeredFunctions.values()); } public async listProperties(): Promise, options: IPropertyOptions }>> { return Array.from(this._registeredProperties.values()); } public async init(): Promise { // In this base Implementation, check if every requried property is set // correctly. If not => raise an error. if (this.type === null){ throw Error('Please Provide a Name for the Module before initializing') } if (this.description === null){ throw Error('Please Provide a Description for the Module before initializing') } if (this.author === null){ throw Error('Please Provide an Author for the Module before initializing') } if (this.version === null){ throw Error('Please Provide a Version for the Module before initializing') } if (this.identifier === null){ throw Error('Please Provide an Identifier for the Module before initializing') } } public async dispose(){ // Unregister all Methods and Functions for (const name of this._registeredFunctions.keys()){ await this.unregisterFunction(name); } // Remove all known Functions this._registeredFunctions.clear(); // Unregister all Properties. for (const name of this._registeredProperties.keys()){ await this.unregisterProperty(name); } // Remove all known Properties. this._registeredProperties.clear(); } public toDescription(): INopeModuleDescription { const ret: INopeModuleDescription = { author: this.author, description: this.description, functions: this.functions, identifier: this.identifier, properties: this.properties, type: this.type, version: this.version } return ret; } }