More work on connector

This commit is contained in:
Daniel Kluge
2023-10-30 12:51:57 +01:00
parent 870fce9247
commit 523935a0d5
6 changed files with 131 additions and 30 deletions
+32 -1
View File
@@ -11,11 +11,13 @@
"dependencies": { "dependencies": {
"@aas-core-works/aas-core3.0-typescript": "^1.0.0-rc.3", "@aas-core-works/aas-core3.0-typescript": "^1.0.0-rc.3",
"express": "^4.18.2", "express": "^4.18.2",
"mqtt": "^5.1.2" "mqtt": "^5.1.2",
"uuid": "^9.0.1"
}, },
"devDependencies": { "devDependencies": {
"@types/express": "^4.17.19", "@types/express": "^4.17.19",
"@types/node": "^20.8.2", "@types/node": "^20.8.2",
"@types/uuid": "^9.0.6",
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
"typescript": "^5.2.2" "typescript": "^5.2.2"
} }
@@ -193,6 +195,12 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"node_modules/@types/uuid": {
"version": "9.0.6",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.6.tgz",
"integrity": "sha512-BT2Krtx4xaO6iwzwMFUYvWBWkV2pr37zD68Vmp1CDV196MzczBRxuEpD6Pr395HAgebC/co7hOphs53r8V7jew==",
"dev": true
},
"node_modules/@types/ws": { "node_modules/@types/ws": {
"version": "8.5.7", "version": "8.5.7",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.7.tgz", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.7.tgz",
@@ -1358,6 +1366,18 @@
"node": ">= 0.4.0" "node": ">= 0.4.0"
} }
}, },
"node_modules/uuid": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
],
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/v8-compile-cache-lib": { "node_modules/v8-compile-cache-lib": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
@@ -1577,6 +1597,12 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"@types/uuid": {
"version": "9.0.6",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.6.tgz",
"integrity": "sha512-BT2Krtx4xaO6iwzwMFUYvWBWkV2pr37zD68Vmp1CDV196MzczBRxuEpD6Pr395HAgebC/co7hOphs53r8V7jew==",
"dev": true
},
"@types/ws": { "@types/ws": {
"version": "8.5.7", "version": "8.5.7",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.7.tgz", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.7.tgz",
@@ -2431,6 +2457,11 @@
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="
}, },
"uuid": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="
},
"v8-compile-cache-lib": { "v8-compile-cache-lib": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
+3 -1
View File
@@ -12,12 +12,14 @@
"devDependencies": { "devDependencies": {
"@types/express": "^4.17.19", "@types/express": "^4.17.19",
"@types/node": "^20.8.2", "@types/node": "^20.8.2",
"@types/uuid": "^9.0.6",
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
"typescript": "^5.2.2" "typescript": "^5.2.2"
}, },
"dependencies": { "dependencies": {
"@aas-core-works/aas-core3.0-typescript": "^1.0.0-rc.3", "@aas-core-works/aas-core3.0-typescript": "^1.0.0-rc.3",
"express": "^4.18.2", "express": "^4.18.2",
"mqtt": "^5.1.2" "mqtt": "^5.1.2",
"uuid": "^9.0.1"
} }
} }
+74 -19
View File
@@ -1,6 +1,7 @@
import * as mqtt from "mqtt"; import * as mqtt from "mqtt";
import type { Property, Operation, OperationVariable, BasicEventElement } from "@aas-core-works/aas-core3.0-typescript/dist/types/types"; import type { Property, Operation, OperationVariable, BasicEventElement } from "@aas-core-works/aas-core3.0-typescript/dist/types/types";
import InterfaceConnectionObject from "interfaceConnectionObject"; import InterfaceConnectionObject from "interfaceConnectionObject";
import type { types } from "@aas-core-works/aas-core3.0-typescript";
class MQTTConnector extends InterfaceConnectionObject<mqtt.IClientOptions> { class MQTTConnector extends InterfaceConnectionObject<mqtt.IClientOptions> {
protected name: string = "MQTT Connector"; protected name: string = "MQTT Connector";
@@ -10,13 +11,17 @@ class MQTTConnector extends InterfaceConnectionObject<mqtt.IClientOptions> {
private client: mqtt.MqttClient|null = null; private client: mqtt.MqttClient|null = null;
private store: Record<string, any> = {}; private messageStore: Record<string, any> = {};
public connect(): boolean { public connect(): boolean {
if (!this.client) this.client = mqtt.connect(this.connectionParameter); if (!this.client) this.client = mqtt.connect(this.endpointMetadata.base, this.connectionParameter);
this.client.on("message", (topic, message) => { this.client.on("message", (topic, message) => {
// If json is expected you could parse it here // If json is expected you could parse it here
this.store[topic] = message.toString("utf-8"); this.messageStore[topic] = message.toString("utf-8");
// Notify observers
if (this.observerStore[topic] !== undefined) {
this.observerStore[topic].forEach(cb => cb(message.toString("utf-8")));
}
}); });
this.client.subscribe("#"); this.client.subscribe("#");
return this.client.connected; return this.client.connected;
@@ -27,35 +32,85 @@ class MQTTConnector extends InterfaceConnectionObject<mqtt.IClientOptions> {
this.client = null; this.client = null;
} }
public readProperty(prop: Property): void { public readProperty(prop: types.Property): any {
const cc = this.mapper.get(prop);
if (cc === undefined) return null;
const errorReturn = cc.default ?? null;
if (!this.client || !this.client.connected) return errorReturn;
const value = cc.forms.map(f => this.messageStore[f.href.substring(1)]).filter(v => v !== undefined)[0];
if (value === undefined) return errorReturn;
switch (cc.type) {
case "integer":
return Number.parseInt(value);
case "float":
return Number.parseFloat(value);
case "boolean":
return !!value;
case "string":
default:
return value;
}
}
public writeProperty(prop: types.Property, value: any): boolean {
const cc = this.mapper.get(prop);
if (cc === undefined || !this.client || !this.client.connected) return false;
const topic = cc.forms.map(f => f.href.substring(1))[0];
if (topic === undefined) return false;
this.client.publish(topic, value.toString());
return true;
}
public observeProperty(prop: types.Property, callback: (value: any) => void): boolean {
const cc = this.mapper.get(prop);
if (cc === undefined || !cc.observable || !this.client || !this.client.connected) return false;
cc.forms.map(f => f.href.substring(1)).forEach(topic => {
if (this.observerStore[topic] === undefined) this.observerStore[topic] = [callback];
else this.observerStore[topic].push(callback);
});
return true;
}
public callActionSync(action: types.Operation, args: OperationVariable[]): boolean {
// TODO
// Vorher mal ne ordentliche Mapping-Definition
throw new Error("Method not implemented."); throw new Error("Method not implemented.");
} }
public writeProperty(prop: Property, value: any): void { public callActionAsync(action: types.Operation, args: OperationVariable[]): string | null {
// TODO
// Vorher mal ne ordentliche Mapping-Definition
throw new Error("Method not implemented."); throw new Error("Method not implemented.");
} }
public observeProperty(prop: Property, callback: (value: any) => void): boolean { public readAsyncActionResponse(action: types.Operation): any {
// TODO
// Vorher mal ne ordentliche Mapping-Definition
throw new Error("Method not implemented."); throw new Error("Method not implemented.");
} }
public callActionSync(action: Operation, args: OperationVariable[]) { public subscribeEvent(event: types.BasicEventElement, callback: (event: BasicEventElement) => void): boolean {
// TODO
// Vorher mal ne ordentliche Mapping-Definition
throw new Error("Method not implemented."); throw new Error("Method not implemented.");
} }
public callActionAsync(action: Operation, args: OperationVariable[]): Promise<boolean> { public unsubscribeEvent(event: types.BasicEventElement): void {
throw new Error("Method not implemented."); // TODO
} // Vorher mal ne ordentliche Mapping-Definition
public readAsyncActionResponse(action: Operation): Promise<any> {
throw new Error("Method not implemented.");
}
public subscribeEvent(callback: (event: BasicEventElement) => void): boolean {
throw new Error("Method not implemented.");
}
public unsubscribeEvent(): void {
throw new Error("Method not implemented."); throw new Error("Method not implemented.");
} }
+1 -1
View File
@@ -1,6 +1,6 @@
import MultiMessageBroker from "./multimessageBroker"; import MultiMessageBroker from "./multimessageBroker";
import HTTPInterfaceServer from "./example_modules/httpInterfaceServer"; import HTTPInterfaceServer from "./example_modules/httpInterfaceServer";
import FileImporter from "helper/fileImporter"; import FileImporter from "./helper/fileImporter";
const aas = FileImporter.readAASByPath("../AID-AIMC-Full-Example-20230911T1608.json"); const aas = FileImporter.readAASByPath("../AID-AIMC-Full-Example-20230911T1608.json");
+18 -5
View File
@@ -1,4 +1,6 @@
import type { types } from "@aas-core-works/aas-core3.0-typescript"; import type { types } from "@aas-core-works/aas-core3.0-typescript";
import { AIMCMapper } from "parser/AIMCMapper";
import { v4 } from "uuid";
import type { EndpointMetadata } from "types/aidConf"; import type { EndpointMetadata } from "types/aidConf";
type ConnectionType = "ON_DEMAND" | "PERMANENT"; type ConnectionType = "ON_DEMAND" | "PERMANENT";
@@ -11,9 +13,14 @@ export default abstract class InterfaceConnectionObject<ConfigInterface> {
protected abstract readonly connectionType: ConnectionType; protected abstract readonly connectionType: ConnectionType;
protected abstract readonly supportsSubscriptions: boolean; protected abstract readonly supportsSubscriptions: boolean;
protected observerStore: Record<any, ((value: any) => void)[]> = {};
protected eventSubStore: Record<any, ((value: any) => void)[]> = {};
protected asyncActionStateStore: Record<any, {finished: boolean, result: any}> = {};
public constructor( public constructor(
protected readonly connectionParameter: ConfigInterface, protected readonly connectionParameter: ConfigInterface,
protected readonly endpointMetadata: EndpointMetadata, protected readonly endpointMetadata: EndpointMetadata,
protected readonly mapper: AIMCMapper,
private readonly onResponseCallback: OnResponseCallback) {} private readonly onResponseCallback: OnResponseCallback) {}
public abstract connect(): boolean; public abstract connect(): boolean;
@@ -26,13 +33,19 @@ export default abstract class InterfaceConnectionObject<ConfigInterface> {
public abstract observeProperty(prop: types.Property, callback: (value: any) => void): boolean; public abstract observeProperty(prop: types.Property, callback: (value: any) => void): boolean;
public abstract callActionSync(action: types.Operation, args: types.OperationVariable[]): any; public abstract callActionSync(action: types.Operation, args: types.OperationVariable[]): boolean;
public abstract callActionAsync(action: types.Operation, args: types.OperationVariable[]): Promise<boolean>; public abstract callActionAsync(action: types.Operation, args: types.OperationVariable[]): string | null;
public abstract readAsyncActionResponse(action: types.Operation): Promise<any>; public abstract readAsyncActionResponse(action: types.Operation): any;
public abstract subscribeEvent(callback: (event: types.BasicEventElement) => void): boolean; public abstract subscribeEvent(event: types.BasicEventElement, callback: (event: types.BasicEventElement) => void): boolean;
public abstract unsubscribeEvent(): void; public abstract unsubscribeEvent(event: types.BasicEventElement): void;
protected generateAsyncHandle() {
const handle = v4();
this.asyncActionStateStore[handle] = {finished: false, result: null};
return handle;
}
} }
+1 -1
View File
@@ -14,6 +14,6 @@ export type MappingConfiguration = {
SourceSinkMappings: types.RelationshipElement[]; SourceSinkMappings: types.RelationshipElement[];
} }
export type ElementMap = Record<string, ConnectionConfiguration>; export type AIMCMap = Map<types.Class, ConnectionConfiguration>;
export type ConnectionConfiguration = EndpointMetadata & InterfaceProperty; // Wenn Transformationen implementiert werden, dann hier anpassen export type ConnectionConfiguration = EndpointMetadata & InterfaceProperty; // Wenn Transformationen implementiert werden, dann hier anpassen