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
+37 -3
View File
@@ -3,6 +3,7 @@ import type { ConnectionConfiguration, AIMCMap } from "./types/aimcConf";
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.
@@ -49,14 +50,47 @@ export default class AIMCMapper {
const resolved = Traverser.resolveRelationship(this.env, ssm);
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 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) {
// maybe the property is in the first element
[first, second] = [second, first];
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;
this.map.set(second, {
@@ -113,8 +147,8 @@ export default class AIMCMapper {
const result = [];
for (const [e, cc] of this.map.entries()) {
const formsPath = cc.base + cc.forms.href;
if (formsPath === path) result.push(e);
const formsPaths = cc.forms.map((f: InterfaceActionForm | InterfacePropertyForm | InterfaceEventForm) => cc.base + f.href);
if (formsPaths.includes(path)) result.push(e);
}
return result;
+28 -15
View File
@@ -1,7 +1,8 @@
import type { types } from "@aas-core-works/aas-core3.0-typescript";
import AIMCMapper from "./AIMCMapper";
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";
@@ -94,51 +95,59 @@ export default abstract class AbstractConnectionObject<ConfigInterface> {
/**
* 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.
* @public
* @virtual
*/
public abstract readProperty(prop: types.Property): void;
public abstract readProperty(mapping: ConnectionConfiguration): void;
/**
* Write a property value to the asset.
* @param prop Property
* @param mapping ConnectionConfiguration for the property
* @param value Value to write.
* @public
* @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.
* @param prop Property
* @param mapping ConnectionConfiguration for the property
* @param callback Callback to call when the property changes.
* @returns Whether the creation of an observer was successful.
* @public
* @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.
* @param action Action
* @param mapping ConnectionConfiguration for the action
* @param args Arguments
* @returns Return value of the action.
* @public
* @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.
* @param action Action
* @param mapping ConnectionConfiguration for the action
* @param args Arguments
* @returns Handle for the async action.
* @public
* @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.
@@ -164,21 +173,21 @@ export default abstract class AbstractConnectionObject<ConfigInterface> {
/**
* Subscribe to an event.
* @param event Event
* @param mapping ConnectionConfiguration for the event
* @param callback Callback to call when the event occurs.
* @returns Whether the subscription was successful.
* @public
* @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.
* @param event Event
* @param mapping ConnectionConfiguration for the event
* @public
* @virtual
*/
public abstract unsubscribeEvent(event: types.BasicEventElement): void;
public abstract unsubscribeEvent(mapping: ConnectionConfiguration): void;
/**
* Generate a handle for an async action.
@@ -190,4 +199,8 @@ 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);
}
}
+23 -41
View File
@@ -4,7 +4,8 @@ import type { Request } from "./types/requests";
import type AbstractConnectionObject from "./abstractConnectionObject";
import AIMCMapper from "./AIMCMapper";
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 = {
aas: types.Environment,
@@ -165,63 +166,44 @@ export default class MultiMessageBroker {
*/
private onInterfaceRequest(request: Request, registration: AASRegistrationPrepared): any {
const getConnector = (target: types.Class) => {
const getMapping = (target: types.Class) => {
const mapping = registration.mappingConfiguration?.get(target);
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);
if (connector === undefined) throw new ReferenceError(`No connector found for ${(request.target as any).idShort}`);
return connector;
}
switch (request.type) {
case "READ": {
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": {
if (request.type === "xAsyncActionState" || request.type === "xAsyncActionResult") {
if (request.type === "xAsyncActionState") {
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}`);
return state;
}
case "GET-OP-RESULT": {
} else {
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}`);
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.
*/
type FormOp = "readproperty" |
export type FormOp = "readproperty" |
"writeproperty" |
"observeproperty" |
"unobserveproperty" |
+3 -2
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, InterfaceProperty } from "./aidConf";
import type { EndpointMetadata, InterfaceAction, InterfaceEvent, InterfaceProperty } from "./aidConf";
export type AssetInterfacesMappingConfiguration = Record<AvailableEndpoint, MappingConfEntry[]>;
@@ -16,4 +16,5 @@ export type MappingConfiguration = {
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
*/
type GetRequest = {
type: "READ";
type: "readProperty";
target: types.Property;
}
@@ -17,7 +17,7 @@ type GetRequestCallback = (request: GetRequest) => any;
* Write Property Request
*/
type WriteRequest = {
type: "WRITE";
type: "writeProperty";
target: types.Property;
extraData: {
value: any;
@@ -33,7 +33,7 @@ type WriteRequestCallback = (request: WriteRequest) => void;
* Observe Property Request
*/
type ObserveRequest = {
type: "OBSERVE";
type: "observeProperty";
target: types.Property;
extraData: {
callback: (value: any) => void;
@@ -45,11 +45,27 @@ type ObserveRequest = {
*/
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
*/
type SubscribeRequest = {
type: "SUBSCRIBE";
type: "subscribeEvent";
target: types.BasicEventElement;
extraData: {
callback: (value: any) => void;
@@ -65,7 +81,7 @@ type SubscribeRequestCallback = (request: SubscribeRequest) => boolean;
* Unsubscribe Event Request
*/
type UnsubscribeRequest = {
type: "UNSUBSCRIBE";
type: "unsubscribeEvent";
target: types.BasicEventElement;
}
@@ -78,9 +94,10 @@ type UnsubscribeRequestCallback = (request: UnsubscribeRequest) => void;
* Call Operation Request
*/
type CallRequest = {
type: "CALL";
type: "invokeAction";
target: types.Operation;
extraData: {
async: boolean;
args: Record<string, any>;
}
}
@@ -90,27 +107,11 @@ type CallRequest = {
*/
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
*/
type AsyncStateRequest = {
type: "GET-OP-STATE";
type: "xAsyncActionState";
target: string;
}
@@ -123,7 +124,7 @@ type AsyncStateRequestCallback = (request: AsyncStateRequest) => boolean;
* Get Async Operation Result Request
*/
type AsyncResultRequest = {
type: "GET-OP-RESULT";
type: "xAsyncActionResult";
target: string;
}
@@ -132,17 +133,12 @@ type AsyncResultRequest = {
*/
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
*/
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
*/
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;