/** * @author Martin Karkowski * @email m.karkowski@zema.de * @create date 2020-10-12 18:53:00 * @modify date 2020-11-23 08:12:29 * @desc [description] */ import { inject, injectable } from "inversify"; import { DISPATCHER_INSTANCE, OBSERVABLE_FACTORY } from "../symbols/identifiers"; import { ICallOptions } from "../types/nope/nopeCommunication.interface"; import { INopeDispatcher } from "../types/nope/nopeDispatcher.interface"; import { IFunctionOptions, INopeModuleDescription, IPropertyOptions } from "../types/nope/nopeModule.interface"; import { INopeObservable } from "../types/nope/nopeObservable.interface"; import { INopePromise } from "../types/nope/nopePromise.interface"; import { NopeBaseModule } from "./BaseModule"; @injectable() export class NopeGenericModule extends NopeBaseModule { private _type = ""; /** * * Return the Class Identifier. */ public get type(): string { return this._type; } /** * Return the Class Identifier. */ public set type(value: string) { this._type = value; } public dynamicInstanceMethods: { [index: string]: (...args) => INopePromise } = {}; public dynamicInstanceProperties: {[index: string]: INopeObservable} = {}; public dynamicInstanceMethodsWithOptions: { [index: string]: (options: Partial, ...args) => INopePromise } = {}; protected _description: INopeModuleDescription; /** * Function, used to add the Attributes based on the Description. * * @param {INopeModuleDescription} description * @param {('overwrite' | 'add')} [mode='overwrite'] * @memberof NopeGenericModule */ public fromDescription(description: INopeModuleDescription, mode:"overwrite" | "add" = "overwrite"): void{ const _this = this; if (mode === "overwrite"){ this.dispose(); this.author = description.author; this.description = description.description; this.type = description.type; this.identifier = description.identifier; } if (this.author == null){ this.author = description.author; } if (this.description == null){ this.description = description.description; } if (this.version == null){ this.version = description.version; } if (this.identifier == null){ this.identifier = description.identifier; } for (const name in description.functions){ const options = description.functions[name]; const func = (...args) => { return _this._dispatcher.performCall(options.id, args, options); }; const funcWithCustomOptions = (_options:Partial, ...args) => { return _this._dispatcher.performCall(options.id, args, Object.assign({}, options, _options )); }; if (this.dynamicInstanceMethods[name]){ throw Error("Name alread used. Not able to use the method name twice"); } (this.dynamicInstanceMethods as any)[name] = func; if (this.dynamicInstanceMethodsWithOptions[name]){ throw Error("Name alread used. Not able to use the method name twice"); } (this.dynamicInstanceMethodsWithOptions as any)[name] = funcWithCustomOptions; // If the Function isnt dynamic, register it on the Object itself. if (!options.isDynamic) { if (this[name]){ throw Error("Name alread used. Not able to use the method name twice"); } this[name] = func; } this._registeredFunctions.set(name, { func, options }); } for (const name in description.properties){ const options = description.properties[name]; // Add only elements, that are subscribed. // Properties, which are only publishing // should throw an error, if data is published // in a remote. This is done to maintain // consistency. // let mode = prop.mode; if (this.dynamicInstanceProperties[name]){ throw Error("Name alread used. Not able to use the method name twice"); } // Make shure it isnt published. // options.preventSendingToRegistery = true; // Register the Observable: this.dynamicInstanceProperties[name] = this._dispatcher.registerObservable( // Assign a new Observable. this._observableFactory(), // Use the provided Properties: options ); if (!options.isDynamic) { if (this[name]){ throw Error("Name alread used. Not able to use the method name twice"); } // Use the Same Element. this[name] = this.dynamicInstanceProperties[name]; } this._registeredProperties.set(name, { observable: this.dynamicInstanceProperties[name], options }); } } /** * Creates an instance of NopeGenericModule. * @param {INopeDispatcher} _dispatcher * @param {() => INopeObservable} _observableFactory * @memberof NopeGenericModule */ constructor( @inject(DISPATCHER_INSTANCE) _dispatcher: INopeDispatcher, @inject(OBSERVABLE_FACTORY) protected _observableFactory: () => INopeObservable ) { super(_dispatcher); } public async listFunctions(): Promise<{ func: (...args: any[]) => Promise; options: IFunctionOptions; }[]> { const _this =this; return Object.getOwnPropertyNames(this.dynamicInstanceMethods).map(name => { return { func: _this.dynamicInstanceMethods[name], options: null }; }); } public async registerProperty(name: string, observable: INopeObservable, options: IPropertyOptions): Promise { throw Error("Function Should not be called on remote!"); } public async registerMethod(name: string, func: (...args: any[]) => Promise, options: IFunctionOptions): Promise { throw Error("Function Should not be called on remote!"); } public async unregisterFunction(name: string): Promise { throw Error("Function Should not be called on remote!"); } public async unregisterProperty(name: string): Promise { throw Error("Function Should not be called on remote!"); } public async init(): Promise { try { await super.init(); } catch (e) { throw Error("Call fromDescription before using"); } } public async dispose(): Promise { for (const name in this.dynamicInstanceProperties){ this.dynamicInstanceProperties[name].dispose(); // Remove Reference delete this[name]; } this._registeredFunctions.clear(); this.dynamicInstanceProperties = {}; for (const name in this.dynamicInstanceMethods) { delete this[name]; delete this.dynamicInstanceMethods[name]; } this._registeredProperties.clear(); } }