Created mapper

This commit is contained in:
Daniel Kluge
2023-10-28 18:09:29 +02:00
parent 7fe12e8c3a
commit 46d1a6e9a8
8 changed files with 179 additions and 18 deletions
+10
View File
@@ -1,5 +1,7 @@
import { types } from "@aas-core-works/aas-core3.0-typescript";
import { RelationshipElement } from "@aas-core-works/aas-core3.0-typescript/dist/types/types";
import { ResolvedRelationshipElement } from "types/common";
const GLOBALLY_IDENTIFIABLES = [types.KeyTypes.GlobalReference, types.KeyTypes.AssetAdministrationShell, types.KeyTypes.ConceptDescription, types.KeyTypes.Identifiable, types.KeyTypes.Submodel]
@@ -40,6 +42,14 @@ export default class Traverser {
return current;
}
public static resolveRelationship(env: types.Environment, relationship: RelationshipElement): ResolvedRelationshipElement | null {
const first = Traverser.resolveReference(env, relationship.first);
const second = Traverser.resolveReference(env, relationship.second);
if (first === null || second === null) return null;
return { first, second };
}
public static traverseByShortIds(start: types.Class, shortIds: string[]): types.Class | null {
let current: types.Class | null = start;
for (const shortId of shortIds) {
+1 -1
View File
@@ -4,7 +4,7 @@ import type { Request } from "types/requests";
import type InterfaceConnectionObject from "interfaceConnectionObject";
import Traverser from "./helper/traverser";
import type { AIMCMapper } from "./parser/AIMCMapper";
import { AssetInterfaceDescription } from "types/aidConf";
import { AssetInterfacesDescription } from "types/aidConf";
import AIDParser from "./parser/AIDParser";
type AASRegistration = {
+12 -10
View File
@@ -1,15 +1,15 @@
import * as aasCore from "@aas-core-works/aas-core3.0-typescript";
import type { AssetInterfaceDescription, EndpointMetadata, InterfaceDescription, InterfaceMetadata, InterfaceAction, InterfaceEvent, InterfaceProperty, InterfacePropertyForm } from "types/aidConf";
import type { AssetInterfacesDescription, EndpointMetadata, InterfaceDescription, InterfaceMetadata, InterfaceAction, InterfaceEvent, InterfaceProperty, InterfacePropertyForm } from "types/aidConf";
import { AvailableEndpoint, endpointAvailable } from "../types/common";
import Traverser from "../helper/traverser";
export default class AIDParser {
public static parse(env: aasCore.types.Environment): AssetInterfaceDescription | null {
public static parse(env: aasCore.types.Environment): AssetInterfacesDescription | null {
const sm = Traverser.findSMByIdShort(env, "AssetInterfaceDescription");
if (sm === null || sm.submodelElements === null) return null;
const parsed = {} as AssetInterfaceDescription;
const parsed = {} as AssetInterfacesDescription;
sm.submodelElements.forEach(e => {
if (!aasCore.types.isSubmodelElementList(e) || !e.idShort || !e.value) return;
@@ -75,7 +75,7 @@ export default class AIDParser {
const prop = Traverser.findElement(interfaceMeta, element => (element as any).idShort.toLocaleLowerCase() === "properties");
if (prop === null || !aasCore.types.isSubmodelElementList(prop) || !prop.value) return null; // Mandatory
for (const element of prop.value) {
for (const element of prop.overValueOrEmpty()) {
if (!aasCore.types.isSubmodelElementCollection(element)) continue;
const parsedProp = AIDParser.parseInterfaceMetadataProperty(element, endpointProtocol);
if (parsedProp === null) continue;
@@ -84,7 +84,7 @@ export default class AIDParser {
const actions = Traverser.findElement(interfaceMeta, element => (element as any).idShort.toLocaleLowerCase() === "actions");
if (actions === null || !aasCore.types.isSubmodelElementList(actions) || !actions.value) return null; // Mandatory
for (const element of actions.value) {
for (const element of actions.overValueOrEmpty()) {
if (!aasCore.types.isSubmodelElementCollection(element)) continue;
const parsedAction = AIDParser.parseInterfaceMetadataProperty(element, endpointProtocol);
if (parsedAction === null) continue;
@@ -95,7 +95,7 @@ export default class AIDParser {
}
public static parseInterfaceMetadataProperty(prop: aasCore.types.SubmodelElementCollection, endpointProtocol?: AvailableEndpoint): InterfaceProperty | null {
if (!prop.value || prop.idShort?.toLocaleLowerCase() !== "properties") return null;
if (!aasCore.types.isSubmodelElementCollection(prop) || !prop.value || prop.idShort?.toLocaleLowerCase() !== "properties") return null;
const parsed = {} as InterfaceProperty;
@@ -106,19 +106,21 @@ export default class AIDParser {
else parsed[key] = prop.value;
} */
const parsedForms = [] as InterfacePropertyForm[];
const forms = Traverser.findElement(prop, element => (element as any).idShort.toLocaleLowerCase() === "forms");
if (forms === null || !aasCore.types.isSubmodelElementList(forms) || !forms.value) return null; // Mandatory
for (const element of forms.value) {
for (const element of forms.overValueOrEmpty()) {
if (!aasCore.types.isSubmodelElementCollection(element)) continue;
const parsedForm = AIDParser.parseInterfaceMetadataPropertyForm(element, endpointProtocol);
if (parsedForm === null) continue;
parsed.forms.push(parsedForm);
parsedForms.push(parsedForm);
}
// TODO
// Other values based on endpoint protocol
return parsed;
return { ...parsed, forms: parsedForms } as InterfaceProperty;
}
private static parseInterfaceMetadataPropertyForm(form: aasCore.types.SubmodelElementCollection, endpointProtocol?: AvailableEndpoint): InterfacePropertyForm | null {
@@ -143,7 +145,7 @@ export default class AIDParser {
return null;
}
public static parseInterfaceMetadataEvent(event: aasCore.types.SubmodelElementCollection, endpointProtocol?: AvailableEndpoint): InterfaceAction | null {
public static parseInterfaceMetadataEvent(event: aasCore.types.SubmodelElementCollection, endpointProtocol?: AvailableEndpoint): InterfaceEvent | null {
// TODO
return null;
}
+57
View File
@@ -1,3 +1,60 @@
import { types } from "@aas-core-works/aas-core3.0-typescript";
import { ConnectionConfiguration, ElementMap } from "types/aimcConf";
import AIMCParser from "./AIMCParser";
import Traverser from "helper/traverser";
import AIDParser from "./AIDParser";
import { SubmodelElementCollection } from "@aas-core-works/aas-core3.0-typescript/dist/types/types";
export class AIMCMapper {
private map: ElementMap = {};
public constructor(private readonly env: types.Environment) {
this.generate();
}
private generate(): void {
const aimc = AIMCParser.parse(this.env);
if (aimc === null) return;
for (const endpointConf of Object.values(aimc)) {
for (const entry of endpointConf) {
const ep = AIDParser.parseEndpointMetaData(entry.EndpointMetaDataReference);
if (ep === null) continue;
for (const mapping of entry.MappingConfiguration) {
//const ifm = AIDParser.parseInterfaceMetaData(mapping.InterfaceMetaDataReference);
//if (ifm === null) continue;
// Why do we need this? We already have the interface metadata reference in every ssm
for (const ssm of mapping.SourceSinkMappings) {
const resolved = Traverser.resolveRelationship(this.env, ssm);
if (resolved === null) continue;
const path = ssm.first.keys.join("/");
const parsedPropConf = AIDParser.parseInterfaceMetadataProperty(resolved.second as SubmodelElementCollection);
if (parsedPropConf === null) continue;
this.map[path] = {
...ep,
...parsedPropConf
}
}
}
}
}
}
public getMap(): ElementMap {
return this.map;
}
public get(path: string[]): ConnectionConfiguration | undefined {
return this.map[path.join("/")];
}
public getByIdShort(idShort: string): ConnectionConfiguration[] {
return Object.keys(this.map).filter(e => e.endsWith(idShort)).map(e => this.map[e]);
}
}
+80
View File
@@ -0,0 +1,80 @@
import * as aasCore from "@aas-core-works/aas-core3.0-typescript";
import Traverser from "helper/traverser";
import { AssetInterfacesMappingConfiguration, MappingConfEntry, MappingConfiguration } from "types/aimcConf";
import { endpointAvailable, ResolvedRelationshipElement } from "types/common";
export default class AIMCParser {
public static parse(env: aasCore.types.Environment): AssetInterfacesMappingConfiguration | null {
const sm = Traverser.findSMByIdShort(env, "AssetInterfaceMappingConfiguration");
if (sm === null || sm.submodelElements === null) return null;
const parsed = {} as AssetInterfacesMappingConfiguration;
sm.submodelElements.forEach(e => {
if (!aasCore.types.isSubmodelElementList(e) || !e.idShort || !e.value) return;
const endpointProto = endpointAvailable(e.idShort);
if (!endpointProto) return;
parsed[endpointProto] = e.value.map(element => {
if (!aasCore.types.isSubmodelElementCollection(element)) return null;
return AIMCParser.parseAIMCEntry(element, env);
}).filter(e => e !== null) as MappingConfEntry[];
});
return parsed;
}
public static parseAIMCEntry(entry: aasCore.types.SubmodelElementCollection, environment: aasCore.types.Environment): MappingConfEntry | null {
const ep = Traverser.findElement(entry, element => (element as any).idShort.toLocaleLowerCase() === "endpointmetadatareference");
const mc = Traverser.findElement(entry, element => (element as any).idShort.toLocaleLowerCase() === "mappingconfiguration");
if (ep === null || !aasCore.types.isReference(ep) || !ep.keys || !ep.keys[0] || !mc || !aasCore.types.isSubmodelElementList(mc) || !mc.value) return null;
const resolvedEp = Traverser.resolveReference(environment, ep);
if (resolvedEp === null || !aasCore.types.isSubmodelElementCollection(resolvedEp) || resolvedEp.idShort?.toLocaleLowerCase() !== "endpointmetadatareference") return null;
const parsedMc: (MappingConfiguration|null)[] = []
for (const conf of mc.overValueOrEmpty()) {
if (!aasCore.types.isSubmodelElementCollection(conf)) continue;
const parsed = AIMCParser.parseMappingConfiguration(conf, environment);
if (parsed === null) continue;
parsedMc.push(parsed);
}
return {
EndpointMetaDataReference: resolvedEp,
MappingConfiguration: parsedMc.filter(e => e !== null) as MappingConfiguration[]
};
}
public static parseMappingConfiguration(conf: aasCore.types.SubmodelElementCollection, env: aasCore.types.Environment): MappingConfiguration | null {
if (!conf.value) return null;
const parsed = {
InterfaceMetaDataReference: null,
SourceSinkMappings: []
} as any;
const ifMetaRef = Traverser.findElement(conf, element => (element as any).idShort.toLocaleLowerCase() === "interfacemetadatareference");
if (!ifMetaRef || !aasCore.types.isReference(ifMetaRef) || !ifMetaRef.keys) return null;
const ifMeta = Traverser.resolveReference(env, ifMetaRef);
if (!ifMeta || !aasCore.types.isSubmodelElementCollection(ifMeta) || ifMeta.idShort?.toLocaleLowerCase() !== "interfacemetadata") return null;
parsed.InterfaceMetaDataReference = ifMeta;
const sourceSinkMappings = Traverser.findElement(conf, element => (element as any).idShort.toLocaleLowerCase() === "sourcesinkmappings");
if (!sourceSinkMappings || !aasCore.types.isSubmodelElementList(sourceSinkMappings) || !sourceSinkMappings.value) return null;
for (const ssm of sourceSinkMappings.overValueOrEmpty()) {
if (!aasCore.types.isRelationshipElement(ssm)) continue;
// No resolve, to allow for mapping
//const resolved = Traverser.resolveRelationship(env, ssm);
//if (resolved === null) continue;
parsed.SourceSinkMappings.push(ssm as ResolvedRelationshipElement);
}
return parsed as MappingConfiguration;
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
import { AvailableEndpoint } from "./common";
export type AssetInterfaceDescription = {
export type AssetInterfacesDescription = {
[key in AvailableEndpoint]: InterfaceDescription[];
};
+11 -6
View File
@@ -1,14 +1,19 @@
import type { types } from "@aas-core-works/aas-core3.0-typescript";
import { AvailableEndpoint } from "./common";
import type { AvailableEndpoint } from "./common";
import type { EndpointMetadata, InterfaceProperty } from "./aidConf";
export type AssetInterfaceMappingConfiguration = Record<AvailableEndpoint, MappingConfEntry[]>;
export type AssetInterfacesMappingConfiguration = Record<AvailableEndpoint, MappingConfEntry[]>;
export type MappingConfEntry = {
EndpointMetaDataReference: types.ReferenceElement;
MappingConfiguration: MappingConfiguration;
EndpointMetaDataReference:types.SubmodelElementCollection;
MappingConfiguration: MappingConfiguration[];
}
export type MappingConfiguration = {
InterfaceMetaDataReference: types.ReferenceElement;
InterfaceMetaDataReference: types.SubmodelElementCollection;
SourceSinkMappings: types.RelationshipElement[];
}
}
export type ElementMap = Record<string, ConnectionConfiguration>;
export type ConnectionConfiguration = EndpointMetadata & InterfaceProperty; // Wenn Transformationen implementiert werden, dann hier anpassen
+7
View File
@@ -1,3 +1,5 @@
import type { types } from "@aas-core-works/aas-core3.0-typescript";
export const availableEndpoints = ["HTTP", "MQTT", "Modbus"] as const;
export type AvailableEndpoint = typeof availableEndpoints[number];
@@ -5,4 +7,9 @@ export function endpointAvailable(endpoint: string): AvailableEndpoint | null {
const idx = availableEndpoints.map(e => e.toLocaleLowerCase()).findIndex(e => e === endpoint.toLocaleLowerCase());
if (idx === -1) return null;
return availableEndpoints[idx];
}
export type ResolvedRelationshipElement = {
first: types.Class;
second: types.Class;
}