Draw the rest of the f-ing owl 🦉

This commit is contained in:
Daniel Kluge
2023-12-06 16:15:54 +01:00
parent 64279a6efa
commit 76ec6e4c9d
13 changed files with 1981 additions and 132 deletions
+5 -4
View File
@@ -1,9 +1,10 @@
import type { types } from "@aas-core-works/aas-core3.0-typescript";
import type { ConnectionConfiguration, AIMCMap } from "./types/aimcConf";
import type { AIMCMap } from "./types/aimcConf";
import type { ConnectionConfiguration } from "./types/requests";
import type { InterfaceAction, InterfaceActionForm, InterfaceEvent, InterfaceEventForm, InterfaceProperty, InterfacePropertyForm } from "./types/aidConf";
import AIMCParser from "./parser/AIMCParser";
import Traverser from "./helper/traverser";
import AIDParser from "./parser/AIDParser";
import type { InterfaceAction, InterfaceActionForm, InterfaceEvent, InterfaceEventForm, InterfaceProperty, InterfacePropertyForm } from "./types/aidConf";
/**
* The AIMC Mapper.
@@ -93,10 +94,10 @@ export default class AIMCMapper {
if (parsedPropConf === null) continue;
this.map.set(second, {
this.map.set(second, ({
...ep,
...parsedPropConf
});
} as any as ConnectionConfiguration));
}
}
}
+12 -16
View File
@@ -1,8 +1,8 @@
import type { types } from "@aas-core-works/aas-core3.0-typescript";
import AIMCMapper from "./AIMCMapper";
import { v4 } from "uuid";
import type { EndpointMetadata, FormOp, InterfaceActionForm, InterfaceEventForm, InterfacePropertyForm } from "./types/aidConf";
import type { ConnectionConfiguration } from "./types/aimcConf";
import type { EndpointMetadata } from "./types/aidConf";
import type { ConnectionConfiguration, MMBEvent } from "./types/requests";
type ConnectionType = "ON_DEMAND" | "PERMANENT";
@@ -51,12 +51,12 @@ export default abstract class AbstractConnectionObject<ConfigInterface> {
* A store for all observers and their callbacks.
* @sealed
*/
protected readonly observerStore: Record<any, ((value: any) => void)[]> = {};
protected readonly observerStore: Record<any, { target: types.Property, cb:((event: MMBEvent) => void) }[]> = {};
/**
* A store for all event subscriptions and their callbacks.
* @sealed
*/
protected readonly eventSubStore: Record<any, ((value: any) => void)[]> = {};
protected readonly eventSubStore: Record<any, { target: types.Class, cb:((event: MMBEvent) => void) }[]> = {};
/**
* A store for all async action handles and their results.
* @sealed
@@ -100,7 +100,7 @@ export default abstract class AbstractConnectionObject<ConfigInterface> {
* @public
* @virtual
*/
public abstract readProperty(mapping: ConnectionConfiguration): void;
public abstract readProperty(target: types.Property, mapping: ConnectionConfiguration): void;
/**
* Write a property value to the asset.
@@ -109,7 +109,7 @@ export default abstract class AbstractConnectionObject<ConfigInterface> {
* @public
* @virtual
*/
public abstract writeProperty(mapping: ConnectionConfiguration, value: any): void;
public abstract writeProperty(target: types.Property, mapping: ConnectionConfiguration, value: any): void;
/**
* Observe a property value from the asset.
@@ -119,7 +119,7 @@ export default abstract class AbstractConnectionObject<ConfigInterface> {
* @public
* @virtual
*/
public abstract observeProperty(mapping: ConnectionConfiguration, callback: (value: any) => void): boolean;
public abstract observeProperty(target: types.Property, mapping: ConnectionConfiguration, callback: (value: any) => void): boolean;
/**
* Unobserve a property value from the asset.
@@ -127,7 +127,7 @@ export default abstract class AbstractConnectionObject<ConfigInterface> {
* @public
* @virtual
*/
public abstract unobserveProperty(mapping: ConnectionConfiguration): void;
public abstract unobserveProperty(target: types.Property, mapping: ConnectionConfiguration): void;
/**
* Call an action on the asset synchronously.
@@ -137,7 +137,7 @@ export default abstract class AbstractConnectionObject<ConfigInterface> {
* @public
* @virtual
*/
public abstract callActionSync(mapping: ConnectionConfiguration, args: Record<string, any>): any;
public abstract callActionSync(target: types.Operation, mapping: ConnectionConfiguration, args: Record<string, any>): any;
/**
* Call an action on the asset asynchronously.
@@ -147,7 +147,7 @@ export default abstract class AbstractConnectionObject<ConfigInterface> {
* @public
* @virtual
*/
public abstract callActionAsync(mapping: ConnectionConfiguration, args: Record<string, any>): string | null;
public abstract callActionAsync(target: types.Operation, mapping: ConnectionConfiguration, args: Record<string, any>): string | null;
/**
* Read the state of an async action.
@@ -179,7 +179,7 @@ export default abstract class AbstractConnectionObject<ConfigInterface> {
* @public
* @virtual
*/
public abstract subscribeEvent(mapping: ConnectionConfiguration, callback: (event: types.BasicEventElement) => void): boolean;
public abstract subscribeEvent(target: types.Class, mapping: ConnectionConfiguration, callback: (event: MMBEvent) => void): boolean;
/**
* Unsubscribe from an event.
@@ -187,7 +187,7 @@ export default abstract class AbstractConnectionObject<ConfigInterface> {
* @public
* @virtual
*/
public abstract unsubscribeEvent(mapping: ConnectionConfiguration): void;
public abstract unsubscribeEvent(target: types.Class, mapping: ConnectionConfiguration): void;
/**
* Generate a handle for an async action.
@@ -199,8 +199,4 @@ export default abstract class AbstractConnectionObject<ConfigInterface> {
this.asyncActionStateStore[handle] = {finished: false, result: null};
return handle;
}
protected static findValidForms(mapping: ConnectionConfiguration, op: FormOp): (InterfacePropertyForm | InterfaceActionForm | InterfaceEventForm)[] {
return mapping.forms.filter((form: InterfacePropertyForm | InterfaceActionForm | InterfaceEventForm) => form.op === op);
}
}
+17 -9
View File
@@ -4,8 +4,8 @@ import type { Request } from "./types/requests";
import type AbstractConnectionObject from "./abstractConnectionObject";
import AIMCMapper from "./AIMCMapper";
import AIDParser from "./parser/AIDParser";
import type { EndpointMetadata, InterfaceActionForm, InterfaceEventForm, InterfacePropertyForm } from "./types/aidConf";
import type { ConnectionConfiguration } from "./types/aimcConf";
import type { EndpointMetadata, InterfaceActionForm, InterfaceEventForm, InterfaceForm, InterfacePropertyForm } from "./types/aidConf";
import type { ConnectionConfiguration } from "./types/requests";
type AASRegistration = {
aas: types.Environment,
@@ -192,16 +192,24 @@ export default class MultiMessageBroker {
}
else {
const mapping = getMapping(request.target);
mapping.forms = mapping.forms.filter((form: InterfaceForm) => form.op.toLocaleLowerCase() === request.type.toLocaleLowerCase());
if (mapping.forms.length === 0) throw new ReferenceError(`No form found for ${(request.target as any).idShort} with operation ${request.type}`);
const connector = getConnector(mapping);
switch (request.type) {
case "readProperty": return connector.readProperty(mapping);
case "writeProperty": return connector.writeProperty(mapping, request.extraData.value);
case "observeProperty": return connector.observeProperty(mapping, request.extraData.callback);
case "unobserveProperty": return connector.unobserveProperty(mapping);
case "subscribeEvent": return connector.subscribeEvent(mapping, request.extraData.callback);
case "unsubscribeEvent": return connector.unsubscribeEvent(mapping);
case "invokeAction": return request.extraData.async ? connector.callActionAsync(mapping, request.extraData.args) : connector.callActionSync(mapping, request.extraData.args);
case "readProperty": return connector.readProperty(request.target, mapping);
case "writeProperty": return connector.writeProperty(request.target, mapping, request.extraData.value);
case "observeProperty": return connector.observeProperty(request.target, mapping, request.extraData.callback);
case "unobserveProperty": return connector.unobserveProperty(request.target, mapping);
case "subscribeEvent": {
return connector.subscribeEvent(request.target, mapping, request.extraData.callback);
}
case "unsubscribeEvent": {
return connector.unsubscribeEvent(request.target, mapping);
}
case "invokeAction": return request.extraData.async ? connector.callActionAsync(request.target, mapping, request.extraData.args) : connector.callActionSync(request.target, mapping, request.extraData.args);
}
}
}
+9 -10
View File
@@ -2,7 +2,6 @@ import * as aasCore from "@aas-core-works/aas-core3.0-typescript";
import type { AssetInterfacesDescription, EndpointMetadata, InterfaceDescription, InterfaceMetadata, InterfaceAction, InterfaceEvent, InterfaceProperty, InterfacePropertyForm, InterfaceActionForm, InterfaceFormBase, InterfaceFormBaseHTTP, InterfaceEventForm } from "../types/aidConf";
import { AvailableEndpoint, endpointAvailable } from "../types/common";
import Traverser from "../helper/traverser";
import { Property } from "@aas-core-works/aas-core3.0-typescript/dist/types/types";
/**
* Class for static methods to parse the AssetInterfacesDescription Submodel
@@ -285,7 +284,7 @@ export default class AIDParser {
if (!endpoint) return true;
return validOperations[endpoint][element].includes(operation);
return validOperations[endpoint][element].includes(operation.toLocaleLowerCase());
}
private static parseAdditionalFormProperties(form: aasCore.types.SubmodelElementCollection, currentlyParsed: any, endpointProtocol?: AvailableEndpoint): InterfacePropertyForm | InterfaceActionForm | InterfaceEventForm {
@@ -297,21 +296,21 @@ export default class AIDParser {
// If method is set and it's a valid HTTP method we take it, else we use the default
// setting for read/write
const methodName = ["GET", "PUT", "POST", "DELETE", "PATCH"].includes(method.value?.toLocaleUpperCase() ?? "") ? method.value?.toLocaleUpperCase() : AIDParser.getDefaultMethod(currentlyParsed.op) ?? "GET";
currentlyParsed["htv:methodName"] = methodName;
currentlyParsed.htv_methodName = methodName;
}
const headers = Traverser.findElement(form, element => (element as any).idShort?.toLocaleLowerCase() === "htv:headers");
if (headers !== null && aasCore.types.isSubmodelElementList(headers)) {
const parsedHeaders: { "htv:fieldName": string, "htv:fieldValue": string }[] = [];
const parsedHeaders: { htv_fieldName: string, htv_fieldValue: string }[] = [];
for (const header of headers.overValueOrEmpty()) {
if (!aasCore.types.isSubmodelElementCollection(header) || !header.value) continue;
const fieldName = Traverser.findElement(header, element => (element as any).idShort?.toLocaleLowerCase() === "htv:fieldname");
const fieldValue = Traverser.findElement(header, element => (element as any).idShort?.toLocaleLowerCase() === "htv:fieldvalue");
if (fieldName === null || !aasCore.types.isProperty(fieldName) || !fieldName.value || fieldValue === null || !aasCore.types.isProperty(fieldValue) || !fieldValue.value) continue;
parsedHeaders.push({ "htv:fieldName": fieldName.value, "htv:fieldValue": fieldValue.value });
parsedHeaders.push({ htv_fieldName: fieldName.value, htv_fieldValue: fieldValue.value });
}
currentlyParsed["htv:headers"] = parsedHeaders;
currentlyParsed.htv_headers = parsedHeaders;
}
break;
}
@@ -322,12 +321,12 @@ export default class AIDParser {
// If controlPacket is set and it's a valid MQTT control packet we take it, else we use the default
// setting for read/write
const controlPacketValue = ["PUBLISH", "SUBSCRIBE", "UNSUBSCRIBE"].includes(controlPacket.value?.toLocaleUpperCase() ?? "") ? controlPacket.value?.toLocaleUpperCase() : AIDParser.getDefaultMethod(currentlyParsed.op);
currentlyParsed["mqv:controlPacketValue"] = controlPacketValue;
currentlyParsed.mqv_controlPacketValue = controlPacketValue;
}
const options = Traverser.findElement(form, element => (element as any).idShort?.toLocaleLowerCase() === "mqv:options");
if (options !== null && aasCore.types.isSubmodelElementList(options)) {
const parsedOptions: ({ "mqv:optionName": string, "mqv:optionValue": number | boolean })[] = [];
const parsedOptions: ({ mqv_optionName: string, mqv_optionValue: number | boolean })[] = [];
for (const option of options.overValueOrEmpty()) {
if (!aasCore.types.isSubmodelElementCollection(option) || !option.value) continue;
@@ -335,9 +334,9 @@ export default class AIDParser {
const optionValue = Traverser.findElement(option, element => (element as any).idShort?.toLocaleLowerCase() === "mqv:optionvalue");
if (optionName === null || !aasCore.types.isProperty(optionName) || !optionName.value || optionValue === null || !aasCore.types.isProperty(optionValue) || !optionValue.value) continue;
const value = Number.isNaN(Number.parseInt(optionValue.value)) ? optionValue.value === "true" : Number.parseInt(optionValue.value);
parsedOptions.push({ "mqv:optionName": optionName.value, "mqv:optionValue": value });
parsedOptions.push({ mqv_optionName: optionName.value, mqv_optionValue: value });
}
currentlyParsed["mqv:options"] = parsedOptions;
currentlyParsed.mqv_options = parsedOptions;
}
break;
+34 -42
View File
@@ -48,25 +48,25 @@ It should be pretty easy to extend this.
So let's derive some of this from the WoT and see what we can achieve.
*/
export type FormOp = "readproperty" |
"writeproperty" |
"observeproperty" |
"unobserveproperty" |
"invokeaction" |
"subscribeevent" |
"unsubscribeevent";
export type FormOp = "readProperty" |
"writeProperty" |
"observeProperty" |
"unobserveProperty" |
"invokeAction" |
"subscribeEvent" |
"unsubscribeEvent";
// The following are defined in WoT but not useful in our case
//"readallproperties" |
//"writeallproperties" |
//"readmultipleproperties" |
//"writemultipleproperties";
type FormOpHTTPProp = "readproperty" | "writeproperty";
type FormOpHTTPAction = "invokeaction";
type FormOpHTTPProp = "readProperty" | "writeProperty";
type FormOpHTTPAction = "invokeAction";
type FormOpMQTTProp = "readproperty" | "writeproperty" | "observeproperty" | "unobserveproperty";
type FormOpMQTTAction = "invokeaction";
type FormOpMQTTEvent = "subscribeevent" | "unsubscribeevent";
type FormOpMQTTProp = "readProperty" | "writeProperty" | "observeProperty" | "unobserveProperty";
type FormOpMQTTAction = "invokeAction";
type FormOpMQTTEvent = "subscribeEvent" | "unsubscribeEvent";
export type InterfaceFormBase = {
href: string;
@@ -75,50 +75,51 @@ export type InterfaceFormBase = {
}
export type InterfaceFormBaseHTTP = InterfaceFormBase & {
"htv:methodName": "GET" | "PUT" | "POST" | "DELETE" | "PATCH";
"htv:headers": {
"htv:fieldName": string;
"htv:fieldValue": string;
htv_methodName?: "GET" | "PUT" | "POST" | "DELETE" | "PATCH";
htv_headers?: {
htv_fieldName: string;
htv_fieldValue: string;
}[];
op: FormOpHTTPProp | FormOpHTTPAction;
}
type IFormHTTPProp = InterfaceFormBaseHTTP & {
export type InterfaceFormHTTPProp = InterfaceFormBaseHTTP & {
op: FormOpHTTPProp;
}
type IFormHTTPAction = InterfaceFormBaseHTTP & {
export type InterfaceFormHTTPAction = InterfaceFormBaseHTTP & {
op: FormOpHTTPAction;
}
export type InterfaceFormBaseMQTT = InterfaceFormBase & {
"mqv:controlPacketValue": "PUBLISH" | "SUBSCRIBE" | "UNSUBSCRIBE";
"mqv:options": ({
"mqv:optionName": "qos";
"mqv:optionValue": 0 | 1 | 2;
mqv_controlPacketValue?: "PUBLISH" | "SUBSCRIBE" | "UNSUBSCRIBE";
mqv_options?: ({
mqv_optionName: "qos";
mqv_optionValue: 0 | 1 | 2;
} | {
"mqv:optionName": "retain" | "dup";
"mqv:optionValue": boolean;
mqv_optionName: "retain" | "dup";
mqv_optionValue: boolean;
})[];
op: FormOpMQTTProp | FormOpMQTTAction | FormOpMQTTEvent;
}
type IFormMQTTProp = InterfaceFormBaseMQTT & {
export type InterfaceFormMQTTProp = InterfaceFormBaseMQTT & {
op: FormOpMQTTProp;
}
type IFormMQTTAction = InterfaceFormBaseMQTT & {
export type InterfaceFormMQTTAction = InterfaceFormBaseMQTT & {
op: FormOpMQTTAction;
}
type IFormMQTTEvent = InterfaceFormBaseMQTT & {
export type InterfaceFormMQTTEvent = InterfaceFormBaseMQTT & {
op: FormOpMQTTEvent;
}
export type InterfacePropertyForm = IFormHTTPProp | IFormMQTTProp;
export type InterfaceActionForm = IFormHTTPAction | IFormMQTTAction;
export type InterfaceEventForm = IFormMQTTEvent;
export type InterfacePropertyForm = InterfaceFormHTTPProp | InterfaceFormMQTTProp;
export type InterfaceActionForm = InterfaceFormHTTPAction | InterfaceFormMQTTAction;
export type InterfaceEventForm = InterfaceFormMQTTEvent;
export type InterfaceForm = InterfacePropertyForm | InterfaceActionForm | InterfaceEventForm;
export type InterfaceProperty = {
title?: string;
@@ -139,20 +140,11 @@ export type InterfaceProperty = {
}
/** @beta */
export type InterfaceAction = InterfaceActionSync | InterfaceActionAsync;
type InterfaceActionBase = {
export type InterfaceAction = {
title?: string;
type: string;
type?: string;
forms: InterfaceActionForm[];
}
type InterfaceActionSync = InterfaceActionBase & {
async: false;
}
type InterfaceActionAsync = {
async: true;
async: boolean;
}
/** @beta */
+2 -5
View File
@@ -1,6 +1,6 @@
import type { types } from "@aas-core-works/aas-core3.0-typescript";
import type { AvailableEndpoint } from "./common";
import type { EndpointMetadata, InterfaceAction, InterfaceEvent, InterfaceProperty } from "./aidConf";
import type { ConnectionConfiguration } from "./requests";
export type AssetInterfacesMappingConfiguration = Record<AvailableEndpoint, MappingConfEntry[]>;
@@ -14,7 +14,4 @@ export type MappingConfiguration = {
SourceSinkMappings: types.RelationshipElement[];
}
export type AIMCMap = Map<types.Class, ConnectionConfiguration>;
type InterfaceElement = InterfaceProperty | InterfaceAction | InterfaceEvent;
export type ConnectionConfiguration = EndpointMetadata & InterfaceElement; // Wenn Transformationen implementiert werden, dann hier anpassen
export type AIMCMap = Map<types.Class, ConnectionConfiguration>;
+14 -5
View File
@@ -1,4 +1,5 @@
import type { types } from "@aas-core-works/aas-core3.0-typescript";
import type { EndpointMetadata, InterfaceForm } from "./aidConf";
/**
* Read Property Request
@@ -36,7 +37,7 @@ type ObserveRequest = {
type: "observeProperty";
target: types.Property;
extraData: {
callback: (value: any) => void;
callback: (event: MMBEvent) => void;
}
}
@@ -52,7 +53,7 @@ type UnobserveRequest = {
type: "unobserveProperty";
target: types.Property;
extraData: {
callback: (value: any) => void;
callback: (event: MMBEvent) => void;
}
}
@@ -66,9 +67,9 @@ type UnobserveRequestCallback = (request: ObserveRequest) => boolean;
*/
type SubscribeRequest = {
type: "subscribeEvent";
target: types.BasicEventElement;
target: types.Class;
extraData: {
callback: (value: any) => void;
callback: (event: MMBEvent) => void;
}
}
@@ -82,7 +83,7 @@ type SubscribeRequestCallback = (request: SubscribeRequest) => boolean;
*/
type UnsubscribeRequest = {
type: "unsubscribeEvent";
target: types.BasicEventElement;
target: types.Class;
}
/**
@@ -142,3 +143,11 @@ export type Request = GetRequest | WriteRequest | ObserveRequest | UnobserveRequ
* Combined Request Callback Type
*/
export type OnRequestCallback = (request: Request) => any & GetRequestCallback & WriteRequestCallback & ObserveRequestCallback & UnobserveRequestCallback & CallRequestCallback & AsyncStateRequestCallback & AsyncResultRequestCallback & SubscribeRequestCallback & UnsubscribeRequestCallback;
export type ConnectionConfiguration = EndpointMetadata & InterfaceForm; // Wenn Transformationen implementiert werden, dann hier anpassen
export type MMBEvent = {
type: "changed" | "event";
target: types.Class;
value: any;
}