2020-10-12 16:13:10 +00:00
|
|
|
/**
|
|
|
|
* @author Martin Karkowski
|
|
|
|
* @email m.karkowski@zema.de
|
|
|
|
* @create date 2020-10-12 17:54:08
|
2020-10-13 16:22:04 +00:00
|
|
|
* @modify date 2020-10-13 17:42:18
|
2020-10-12 16:13:10 +00:00
|
|
|
* @desc [description]
|
|
|
|
*/
|
|
|
|
|
2020-10-13 13:18:25 +00:00
|
|
|
import { inject, injectable } from 'inversify';
|
|
|
|
import { replaceAll } from "../helpers/stringMethods";
|
|
|
|
import { DISPATCHER_INSTANCE } from '../symbols/identifiers';
|
|
|
|
import { INopeDispatcher } from "../types/nopeDispatcher.interface";
|
2020-10-13 16:22:04 +00:00
|
|
|
import { IAuthor, IFunctionOptions, INopeModule, INopeModuleDescription, IPropertyOptions, IVersion } from "../types/nopeModule.interface";
|
2020-10-13 13:18:25 +00:00
|
|
|
import { INopeObservable } from "../types/nopeObservable.interface";
|
2020-10-12 16:13:10 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Base Implementation of a Module.
|
|
|
|
*
|
|
|
|
* @export
|
|
|
|
* @class BaseModule
|
2020-10-13 13:18:25 +00:00
|
|
|
* @implements {INopeModule}
|
2020-10-12 16:13:10 +00:00
|
|
|
*/
|
2020-10-13 13:18:25 +00:00
|
|
|
@injectable()
|
|
|
|
export class NopeBaseModule implements INopeModule {
|
2020-10-12 16:13:10 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Module Name must be written in lowercase.
|
|
|
|
*/
|
2020-10-13 13:18:25 +00:00
|
|
|
private _type = '';
|
|
|
|
public get type(): string {
|
|
|
|
return this._type;
|
2020-10-12 16:13:10 +00:00
|
|
|
}
|
2020-10-13 13:18:25 +00:00
|
|
|
public set type(value: string) {
|
2020-10-12 16:13:10 +00:00
|
|
|
const _replacements = [
|
|
|
|
[' ', ''],
|
|
|
|
// ['.', ''],
|
|
|
|
// ['_', ''],
|
|
|
|
];
|
|
|
|
|
|
|
|
let adapted = value;
|
|
|
|
|
|
|
|
for (const [search, replace] of _replacements) {
|
|
|
|
adapted = replaceAll(adapted, search, replace);
|
|
|
|
}
|
|
|
|
|
2020-10-13 13:18:25 +00:00
|
|
|
this._type = adapted.toLowerCase();
|
2020-10-12 16:13:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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<string, {
|
|
|
|
func: (...args: any[]) => Promise<any>,
|
|
|
|
options: IFunctionOptions
|
|
|
|
}>
|
|
|
|
|
|
|
|
protected _registeredProperties: Map<string, {
|
|
|
|
observable: INopeObservable<any>
|
|
|
|
options: IPropertyOptions
|
|
|
|
}>
|
2020-10-13 13:18:25 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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<any>; } = {};
|
|
|
|
|
|
|
|
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;
|
2020-10-12 16:13:10 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates an instance of BaseModule.
|
|
|
|
* @memberof BaseModule
|
|
|
|
*/
|
2020-10-13 13:18:25 +00:00
|
|
|
constructor(@inject(DISPATCHER_INSTANCE) protected _dispatcher: INopeDispatcher) {
|
|
|
|
this._type = null;
|
2020-10-12 16:13:10 +00:00
|
|
|
this.description = null;
|
|
|
|
this.author = null;
|
|
|
|
this.version = null;
|
2020-10-13 13:18:25 +00:00
|
|
|
this.identifier = null;
|
|
|
|
this._registeredFunctions = new Map();
|
|
|
|
this._registeredProperties = new Map();
|
2020-10-12 16:13:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public async registerProperty<T, K = any, S = T, G = T>(name: string, observable: INopeObservable<T, S, G>, options: IPropertyOptions<K>): Promise<void> {
|
|
|
|
// Unregister the Function
|
|
|
|
await this.unregisterProperty(name);
|
|
|
|
|
|
|
|
// Adapt the Topics
|
|
|
|
if (typeof options.topic === 'string'){
|
2020-10-13 13:18:25 +00:00
|
|
|
options.topic = this.identifier + '.prop.' + name;
|
2020-10-12 16:13:10 +00:00
|
|
|
} else if (typeof options.topic === 'object'){
|
2020-10-13 13:18:25 +00:00
|
|
|
if (options.topic.subscribe && !options.topic.subscribe.startsWith(this.identifier + '.prop.') ){
|
|
|
|
options.topic.subscribe = this.identifier + '.prop.' + options.topic.subscribe;
|
2020-10-12 16:13:10 +00:00
|
|
|
}
|
2020-10-13 13:18:25 +00:00
|
|
|
if (options.topic.publish && !options.topic.publish.startsWith(this.identifier + '.prop.')){
|
|
|
|
options.topic.publish = this.identifier + '.prop.' + options.topic.publish;
|
2020-10-12 16:13:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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<any>, options: IFunctionOptions): Promise<void> {
|
|
|
|
// Unregister the Function
|
|
|
|
await this.unregisterFunction(name);
|
|
|
|
|
|
|
|
// Adapt the Method ID
|
2020-10-13 13:18:25 +00:00
|
|
|
options.id = this.identifier + '.method.' + name;
|
2020-10-12 16:13:10 +00:00
|
|
|
|
|
|
|
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<void> {
|
|
|
|
// Test if the Method is already registerd,
|
|
|
|
// If so => unregister it first.
|
|
|
|
if (this._registeredFunctions.has(name)) {
|
2020-10-13 13:18:25 +00:00
|
|
|
this._dispatcher.unregisterFunction(this._registeredFunctions.get(name).func, {
|
2020-10-12 16:13:10 +00:00
|
|
|
preventSendingToRegistery: true
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public async unregisterProperty<T, K = any, S = T, G = T>(name: string): Promise<void> {
|
|
|
|
// Test if the Property is already registerd,
|
|
|
|
// If so => unregister it first.
|
|
|
|
if (this._registeredProperties.has(name)) {
|
2020-10-13 13:18:25 +00:00
|
|
|
this._dispatcher.unregisterObservable(this._registeredProperties.get(name).observable, {
|
2020-10-12 16:13:10 +00:00
|
|
|
preventSendingToRegistery: true
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-13 13:18:25 +00:00
|
|
|
public async listFunctions(): Promise<{ func: (...args: any[]) => Promise<any>; options: IFunctionOptions; }[]> {
|
|
|
|
return Array.from(this._registeredFunctions.values());
|
2020-10-12 16:13:10 +00:00
|
|
|
}
|
|
|
|
|
2020-10-13 13:18:25 +00:00
|
|
|
public async listProperties(): Promise<Array<{ observable: INopeObservable<any>, options: IPropertyOptions }>> {
|
|
|
|
return Array.from(this._registeredProperties.values());
|
2020-10-12 16:13:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public async init(): Promise<void> {
|
|
|
|
// In this base Implementation, check if every requried property is set
|
|
|
|
// correctly. If not => raise an error.
|
|
|
|
|
2020-10-13 13:18:25 +00:00
|
|
|
if (this.type === null){
|
2020-10-12 16:13:10 +00:00
|
|
|
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')
|
|
|
|
}
|
2020-10-13 13:18:25 +00:00
|
|
|
|
|
|
|
if (this.identifier === null){
|
|
|
|
throw Error('Please Provide an Identifier for the Module before initializing')
|
|
|
|
}
|
2020-10-12 16:13:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
|
|
|
}
|
2020-10-13 13:18:25 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|
2020-10-12 16:13:10 +00:00
|
|
|
|
2020-10-13 13:18:25 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2020-10-12 16:13:10 +00:00
|
|
|
}
|