More work on the (better) AID

This commit is contained in:
Daniel Kluge
2023-12-04 17:10:03 +01:00
parent 389a28d7e7
commit 64279a6efa
6 changed files with 120 additions and 94 deletions
+38 -4
View File
@@ -3,6 +3,7 @@ import type { ConnectionConfiguration, AIMCMap } from "./types/aimcConf";
import AIMCParser from "./parser/AIMCParser"; import AIMCParser from "./parser/AIMCParser";
import Traverser from "./helper/traverser"; import Traverser from "./helper/traverser";
import AIDParser from "./parser/AIDParser"; import AIDParser from "./parser/AIDParser";
import type { InterfaceAction, InterfaceActionForm, InterfaceEvent, InterfaceEventForm, InterfaceProperty, InterfacePropertyForm } from "./types/aidConf";
/** /**
* The AIMC Mapper. * The AIMC Mapper.
@@ -49,14 +50,47 @@ export default class AIMCMapper {
const resolved = Traverser.resolveRelationship(this.env, ssm); const resolved = Traverser.resolveRelationship(this.env, ssm);
if (resolved === null) continue; if (resolved === null) continue;
// To know if we currently view a property, action or event, they reference keys can help us.
// We know the third to last must be the "InterfaceMetadata" and the second to last must be the "Properties", "Actions" or "Events".
// As we _maybe_ don't know if the first and second reference are switched we can check for the third to last.
const elemType = (ssm as types.RelationshipElement).first.keys.at(-3)?.value.toLocaleLowerCase() === "interfacemetadata" ? (ssm as types.RelationshipElement).first.keys.at(-2)?.value : (ssm as types.RelationshipElement).second.keys.at(-2)?.value;
let {first, second} = resolved; let {first, second} = resolved;
let parsedPropConf = AIDParser.parseInterfaceMetadataProperty(first as types.SubmodelElementCollection); let parsedPropConf: InterfaceProperty | InterfaceAction | InterfaceEvent | null = null;
switch (elemType?.toLocaleLowerCase()) {
case "properties":
parsedPropConf = AIDParser.parseInterfaceMetadataProperty(first as types.SubmodelElementCollection);
break;
case "actions":
parsedPropConf = AIDParser.parseInterfaceMetadataAction(first as types.SubmodelElementCollection);
break;
case "events":
parsedPropConf = AIDParser.parseInterfaceMetadataEvent(first as types.SubmodelElementCollection);
break;
default:
// Error. We have not found a valid type of element to parse.
continue;
}
if (parsedPropConf === null) { if (parsedPropConf === null) {
// maybe the property is in the first element // maybe the property is in the first element
[first, second] = [second, first]; [first, second] = [second, first];
parsedPropConf = AIDParser.parseInterfaceMetadataProperty(first as types.SubmodelElementCollection);
switch (elemType?.toLocaleLowerCase()) {
case "properties":
parsedPropConf = AIDParser.parseInterfaceMetadataProperty(first as types.SubmodelElementCollection);
break;
case "actions":
parsedPropConf = AIDParser.parseInterfaceMetadataAction(first as types.SubmodelElementCollection);
break;
case "events":
parsedPropConf = AIDParser.parseInterfaceMetadataEvent(first as types.SubmodelElementCollection);
break;
}
} }
if (parsedPropConf === null) continue; if (parsedPropConf === null) continue;
this.map.set(second, { this.map.set(second, {
@@ -113,8 +147,8 @@ export default class AIMCMapper {
const result = []; const result = [];
for (const [e, cc] of this.map.entries()) { for (const [e, cc] of this.map.entries()) {
const formsPath = cc.base + cc.forms.href; const formsPaths = cc.forms.map((f: InterfaceActionForm | InterfacePropertyForm | InterfaceEventForm) => cc.base + f.href);
if (formsPath === path) result.push(e); if (formsPaths.includes(path)) result.push(e);
} }
return result; return result;
+28 -15
View File
@@ -1,7 +1,8 @@
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 "./AIMCMapper"; import AIMCMapper from "./AIMCMapper";
import { v4 } from "uuid"; import { v4 } from "uuid";
import type { EndpointMetadata } from "./types/aidConf"; import type { EndpointMetadata, FormOp, InterfaceActionForm, InterfaceEventForm, InterfacePropertyForm } from "./types/aidConf";
import type { ConnectionConfiguration } from "./types/aimcConf";
type ConnectionType = "ON_DEMAND" | "PERMANENT"; type ConnectionType = "ON_DEMAND" | "PERMANENT";
@@ -94,51 +95,59 @@ export default abstract class AbstractConnectionObject<ConfigInterface> {
/** /**
* Read a property value from the asset. * Read a property value from the asset.
* @param prop Property * @param mapping ConnectionConfiguration for the property
* @returns Value of property casted to the type it says it should be. * @returns Value of property casted to the type it says it should be.
* @public * @public
* @virtual * @virtual
*/ */
public abstract readProperty(prop: types.Property): void; public abstract readProperty(mapping: ConnectionConfiguration): void;
/** /**
* Write a property value to the asset. * Write a property value to the asset.
* @param prop Property * @param mapping ConnectionConfiguration for the property
* @param value Value to write. * @param value Value to write.
* @public * @public
* @virtual * @virtual
*/ */
public abstract writeProperty(prop: types.Property, value: any): void; public abstract writeProperty(mapping: ConnectionConfiguration, value: any): void;
/** /**
* Observe a property value from the asset. * Observe a property value from the asset.
* @param prop Property * @param mapping ConnectionConfiguration for the property
* @param callback Callback to call when the property changes. * @param callback Callback to call when the property changes.
* @returns Whether the creation of an observer was successful. * @returns Whether the creation of an observer was successful.
* @public * @public
* @virtual * @virtual
*/ */
public abstract observeProperty(prop: types.Property, callback: (value: any) => void): boolean; public abstract observeProperty(mapping: ConnectionConfiguration, callback: (value: any) => void): boolean;
/**
* Unobserve a property value from the asset.
* @param mapping ConnectionConfiguration for the property
* @public
* @virtual
*/
public abstract unobserveProperty(mapping: ConnectionConfiguration): void;
/** /**
* Call an action on the asset synchronously. * Call an action on the asset synchronously.
* @param action Action * @param mapping ConnectionConfiguration for the action
* @param args Arguments * @param args Arguments
* @returns Return value of the action. * @returns Return value of the action.
* @public * @public
* @virtual * @virtual
*/ */
public abstract callActionSync(action: types.Operation, args: Record<string, any>): any; public abstract callActionSync(mapping: ConnectionConfiguration, args: Record<string, any>): any;
/** /**
* Call an action on the asset asynchronously. * Call an action on the asset asynchronously.
* @param action Action * @param mapping ConnectionConfiguration for the action
* @param args Arguments * @param args Arguments
* @returns Handle for the async action. * @returns Handle for the async action.
* @public * @public
* @virtual * @virtual
*/ */
public abstract callActionAsync(action: types.Operation, args: Record<string, any>): string | null; public abstract callActionAsync(mapping: ConnectionConfiguration, args: Record<string, any>): string | null;
/** /**
* Read the state of an async action. * Read the state of an async action.
@@ -164,21 +173,21 @@ export default abstract class AbstractConnectionObject<ConfigInterface> {
/** /**
* Subscribe to an event. * Subscribe to an event.
* @param event Event * @param mapping ConnectionConfiguration for the event
* @param callback Callback to call when the event occurs. * @param callback Callback to call when the event occurs.
* @returns Whether the subscription was successful. * @returns Whether the subscription was successful.
* @public * @public
* @virtual * @virtual
*/ */
public abstract subscribeEvent(event: types.BasicEventElement, callback: (event: types.BasicEventElement) => void): boolean; public abstract subscribeEvent(mapping: ConnectionConfiguration, callback: (event: types.BasicEventElement) => void): boolean;
/** /**
* Unsubscribe from an event. * Unsubscribe from an event.
* @param event Event * @param mapping ConnectionConfiguration for the event
* @public * @public
* @virtual * @virtual
*/ */
public abstract unsubscribeEvent(event: types.BasicEventElement): void; public abstract unsubscribeEvent(mapping: ConnectionConfiguration): void;
/** /**
* Generate a handle for an async action. * Generate a handle for an async action.
@@ -190,4 +199,8 @@ export default abstract class AbstractConnectionObject<ConfigInterface> {
this.asyncActionStateStore[handle] = {finished: false, result: null}; this.asyncActionStateStore[handle] = {finished: false, result: null};
return handle; return handle;
} }
protected static findValidForms(mapping: ConnectionConfiguration, op: FormOp): (InterfacePropertyForm | InterfaceActionForm | InterfaceEventForm)[] {
return mapping.forms.filter((form: InterfacePropertyForm | InterfaceActionForm | InterfaceEventForm) => form.op === op);
}
} }
+23 -41
View File
@@ -4,7 +4,8 @@ import type { Request } from "./types/requests";
import type AbstractConnectionObject from "./abstractConnectionObject"; import type AbstractConnectionObject from "./abstractConnectionObject";
import AIMCMapper from "./AIMCMapper"; import AIMCMapper from "./AIMCMapper";
import AIDParser from "./parser/AIDParser"; import AIDParser from "./parser/AIDParser";
import { EndpointMetadata } from "types/aidConf"; import type { EndpointMetadata, InterfaceActionForm, InterfaceEventForm, InterfacePropertyForm } from "./types/aidConf";
import type { ConnectionConfiguration } from "./types/aimcConf";
type AASRegistration = { type AASRegistration = {
aas: types.Environment, aas: types.Environment,
@@ -165,63 +166,44 @@ export default class MultiMessageBroker {
*/ */
private onInterfaceRequest(request: Request, registration: AASRegistrationPrepared): any { private onInterfaceRequest(request: Request, registration: AASRegistrationPrepared): any {
const getConnector = (target: types.Class) => { const getMapping = (target: types.Class) => {
const mapping = registration.mappingConfiguration?.get(target); const mapping = registration.mappingConfiguration?.get(target);
if (mapping === undefined) throw new ReferenceError(`No mapping found for ${(request.target as any).idShort}`); if (mapping === undefined) throw new ReferenceError(`No mapping found for ${(request.target as any).idShort}`);
return mapping;
}
const getConnector = (mapping: ConnectionConfiguration) => {
const connector = registration.connectorInterfaces?.find(connector => connector.endpointMetadata.base === mapping.base); const connector = registration.connectorInterfaces?.find(connector => connector.endpointMetadata.base === mapping.base);
if (connector === undefined) throw new ReferenceError(`No connector found for ${(request.target as any).idShort}`); if (connector === undefined) throw new ReferenceError(`No connector found for ${(request.target as any).idShort}`);
return connector; return connector;
} }
switch (request.type) { if (request.type === "xAsyncActionState" || request.type === "xAsyncActionResult") {
case "READ": { if (request.type === "xAsyncActionState") {
const connector = getConnector(request.target);
return connector.readProperty(request.target);
}
case "WRITE": {
const connector = getConnector(request.target);
return connector.writeProperty(request.target, request.extraData.value);
}
case "OBSERVE": {
const connector = getConnector(request.target);
return connector.observeProperty(request.target, request.extraData.callback);
}
case "SUBSCRIBE": {
const connector = getConnector(request.target);
return connector.subscribeEvent(request.target, request.extraData.callback);
}
case "UNSUBSCRIBE": {
const connector = getConnector(request.target);
return connector.unsubscribeEvent(request.target);
}
case "CALL": {
const connector = getConnector(request.target);
return connector.callActionSync(request.target, request.extraData.args);
}
case "CALL-ASYNC": {
const connector = getConnector(request.target);
return connector.callActionAsync(request.target, request.extraData.args);
}
case "GET-OP-STATE": {
const state = registration.connectorInterfaces.map(connector => connector.readAsyncActionState(request.target)).filter(state => state !== undefined)[0]; const state = registration.connectorInterfaces.map(connector => connector.readAsyncActionState(request.target)).filter(state => state !== undefined)[0];
if (state === undefined) throw new ReferenceError(`No connector found for operation handle ${request.target}`); if (state === undefined) throw new ReferenceError(`No connector found for operation handle ${request.target}`);
return state; return state;
} } else {
case "GET-OP-RESULT": {
const result = registration.connectorInterfaces.map(connector => connector.readAsyncActionResponse(request.target)).filter(result => result !== undefined)[0]; const result = registration.connectorInterfaces.map(connector => connector.readAsyncActionResponse(request.target)).filter(result => result !== undefined)[0];
if (result === undefined) throw new ReferenceError(`No connector found for operation handle ${request.target}`); if (result === undefined) throw new ReferenceError(`No connector found for operation handle ${request.target}`);
return result; return result;
} }
} }
else {
const mapping = getMapping(request.target);
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);
}
}
} }
/** /**
+1 -1
View File
@@ -48,7 +48,7 @@ It should be pretty easy to extend this.
So let's derive some of this from the WoT and see what we can achieve. So let's derive some of this from the WoT and see what we can achieve.
*/ */
type FormOp = "readproperty" | export type FormOp = "readproperty" |
"writeproperty" | "writeproperty" |
"observeproperty" | "observeproperty" |
"unobserveproperty" | "unobserveproperty" |
+3 -2
View File
@@ -1,6 +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 type { AvailableEndpoint } from "./common"; import type { AvailableEndpoint } from "./common";
import type { EndpointMetadata, InterfaceProperty } from "./aidConf"; import type { EndpointMetadata, InterfaceAction, InterfaceEvent, InterfaceProperty } from "./aidConf";
export type AssetInterfacesMappingConfiguration = Record<AvailableEndpoint, MappingConfEntry[]>; export type AssetInterfacesMappingConfiguration = Record<AvailableEndpoint, MappingConfEntry[]>;
@@ -16,4 +16,5 @@ export type MappingConfiguration = {
export type AIMCMap = Map<types.Class, ConnectionConfiguration>; export type AIMCMap = Map<types.Class, ConnectionConfiguration>;
export type ConnectionConfiguration = EndpointMetadata & InterfaceProperty; // Wenn Transformationen implementiert werden, dann hier anpassen type InterfaceElement = InterfaceProperty | InterfaceAction | InterfaceEvent;
export type ConnectionConfiguration = EndpointMetadata & InterfaceElement; // Wenn Transformationen implementiert werden, dann hier anpassen
+27 -31
View File
@@ -4,7 +4,7 @@ import type { types } from "@aas-core-works/aas-core3.0-typescript";
* Read Property Request * Read Property Request
*/ */
type GetRequest = { type GetRequest = {
type: "READ"; type: "readProperty";
target: types.Property; target: types.Property;
} }
@@ -17,7 +17,7 @@ type GetRequestCallback = (request: GetRequest) => any;
* Write Property Request * Write Property Request
*/ */
type WriteRequest = { type WriteRequest = {
type: "WRITE"; type: "writeProperty";
target: types.Property; target: types.Property;
extraData: { extraData: {
value: any; value: any;
@@ -33,7 +33,7 @@ type WriteRequestCallback = (request: WriteRequest) => void;
* Observe Property Request * Observe Property Request
*/ */
type ObserveRequest = { type ObserveRequest = {
type: "OBSERVE"; type: "observeProperty";
target: types.Property; target: types.Property;
extraData: { extraData: {
callback: (value: any) => void; callback: (value: any) => void;
@@ -45,11 +45,27 @@ type ObserveRequest = {
*/ */
type ObserveRequestCallback = (request: ObserveRequest) => boolean; type ObserveRequestCallback = (request: ObserveRequest) => boolean;
/**
* Unobserve Property Request
*/
type UnobserveRequest = {
type: "unobserveProperty";
target: types.Property;
extraData: {
callback: (value: any) => void;
}
}
/**
* Unobserve Property Request Callback
*/
type UnobserveRequestCallback = (request: ObserveRequest) => boolean;
/** /**
* Subscribe Event Request * Subscribe Event Request
*/ */
type SubscribeRequest = { type SubscribeRequest = {
type: "SUBSCRIBE"; type: "subscribeEvent";
target: types.BasicEventElement; target: types.BasicEventElement;
extraData: { extraData: {
callback: (value: any) => void; callback: (value: any) => void;
@@ -65,7 +81,7 @@ type SubscribeRequestCallback = (request: SubscribeRequest) => boolean;
* Unsubscribe Event Request * Unsubscribe Event Request
*/ */
type UnsubscribeRequest = { type UnsubscribeRequest = {
type: "UNSUBSCRIBE"; type: "unsubscribeEvent";
target: types.BasicEventElement; target: types.BasicEventElement;
} }
@@ -78,9 +94,10 @@ type UnsubscribeRequestCallback = (request: UnsubscribeRequest) => void;
* Call Operation Request * Call Operation Request
*/ */
type CallRequest = { type CallRequest = {
type: "CALL"; type: "invokeAction";
target: types.Operation; target: types.Operation;
extraData: { extraData: {
async: boolean;
args: Record<string, any>; args: Record<string, any>;
} }
} }
@@ -90,27 +107,11 @@ type CallRequest = {
*/ */
type CallRequestCallback = (request: CallRequest) => any; type CallRequestCallback = (request: CallRequest) => any;
/**
* Call Async Operation Request
*/
type CallAsyncRequest = {
type: "CALL-ASYNC";
target: types.Operation;
extraData: {
args: Record<string, any>;
}
}
/**
* Call Async Operation Request Callback
*/
type CallAsyncRequestCallback = (request: CallAsyncRequest) => string | null;
/** /**
* Get Async Operation State Request * Get Async Operation State Request
*/ */
type AsyncStateRequest = { type AsyncStateRequest = {
type: "GET-OP-STATE"; type: "xAsyncActionState";
target: string; target: string;
} }
@@ -123,7 +124,7 @@ type AsyncStateRequestCallback = (request: AsyncStateRequest) => boolean;
* Get Async Operation Result Request * Get Async Operation Result Request
*/ */
type AsyncResultRequest = { type AsyncResultRequest = {
type: "GET-OP-RESULT"; type: "xAsyncActionResult";
target: string; target: string;
} }
@@ -132,17 +133,12 @@ type AsyncResultRequest = {
*/ */
type AsyncResultRequestCallback = (request: AsyncResultRequest) => any; type AsyncResultRequestCallback = (request: AsyncResultRequest) => any;
/**
* Possible Request Types
*/
type RequestType = "READ" | "WRITE" | "OBSERVE" | "CALL" | "CALL-ASYNC" | "GET-OP-STATE" | "GET-OP-RESULT" | "SUBSCRIBE" | "UNSUBSCRIBE"
/** /**
* Combined Request Type * Combined Request Type
*/ */
export type Request = GetRequest | WriteRequest | ObserveRequest | CallRequest | CallAsyncRequest | AsyncStateRequest | AsyncResultRequest | SubscribeRequest | UnsubscribeRequest; export type Request = GetRequest | WriteRequest | ObserveRequest | UnobserveRequest | CallRequest | AsyncStateRequest | AsyncResultRequest | SubscribeRequest | UnsubscribeRequest;
/** /**
* Combined Request Callback Type * Combined Request Callback Type
*/ */
export type OnRequestCallback = (request: Request) => any & GetRequestCallback & WriteRequestCallback & ObserveRequestCallback & CallRequestCallback & CallAsyncRequestCallback & AsyncStateRequestCallback & AsyncResultRequestCallback & SubscribeRequestCallback & UnsubscribeRequestCallback; export type OnRequestCallback = (request: Request) => any & GetRequestCallback & WriteRequestCallback & ObserveRequestCallback & UnobserveRequestCallback & CallRequestCallback & AsyncStateRequestCallback & AsyncResultRequestCallback & SubscribeRequestCallback & UnsubscribeRequestCallback;