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 { 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] 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; 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 { public static traverseByShortIds(start: types.Class, shortIds: string[]): types.Class | null {
let current: types.Class | null = start; let current: types.Class | null = start;
for (const shortId of shortIds) { 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 type InterfaceConnectionObject from "interfaceConnectionObject";
import Traverser from "./helper/traverser"; import Traverser from "./helper/traverser";
import type { AIMCMapper } from "./parser/AIMCMapper"; import type { AIMCMapper } from "./parser/AIMCMapper";
import { AssetInterfaceDescription } from "types/aidConf"; import { AssetInterfacesDescription } from "types/aidConf";
import AIDParser from "./parser/AIDParser"; import AIDParser from "./parser/AIDParser";
type AASRegistration = { type AASRegistration = {
+12 -10
View File
@@ -1,15 +1,15 @@
import * as aasCore from "@aas-core-works/aas-core3.0-typescript"; 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 { AvailableEndpoint, endpointAvailable } from "../types/common";
import Traverser from "../helper/traverser"; import Traverser from "../helper/traverser";
export default class AIDParser { 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"); const sm = Traverser.findSMByIdShort(env, "AssetInterfaceDescription");
if (sm === null || sm.submodelElements === null) return null; if (sm === null || sm.submodelElements === null) return null;
const parsed = {} as AssetInterfaceDescription; const parsed = {} as AssetInterfacesDescription;
sm.submodelElements.forEach(e => { sm.submodelElements.forEach(e => {
if (!aasCore.types.isSubmodelElementList(e) || !e.idShort || !e.value) return; 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"); const prop = Traverser.findElement(interfaceMeta, element => (element as any).idShort.toLocaleLowerCase() === "properties");
if (prop === null || !aasCore.types.isSubmodelElementList(prop) || !prop.value) return null; // Mandatory 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; if (!aasCore.types.isSubmodelElementCollection(element)) continue;
const parsedProp = AIDParser.parseInterfaceMetadataProperty(element, endpointProtocol); const parsedProp = AIDParser.parseInterfaceMetadataProperty(element, endpointProtocol);
if (parsedProp === null) continue; if (parsedProp === null) continue;
@@ -84,7 +84,7 @@ export default class AIDParser {
const actions = Traverser.findElement(interfaceMeta, element => (element as any).idShort.toLocaleLowerCase() === "actions"); const actions = Traverser.findElement(interfaceMeta, element => (element as any).idShort.toLocaleLowerCase() === "actions");
if (actions === null || !aasCore.types.isSubmodelElementList(actions) || !actions.value) return null; // Mandatory 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; if (!aasCore.types.isSubmodelElementCollection(element)) continue;
const parsedAction = AIDParser.parseInterfaceMetadataProperty(element, endpointProtocol); const parsedAction = AIDParser.parseInterfaceMetadataProperty(element, endpointProtocol);
if (parsedAction === null) continue; if (parsedAction === null) continue;
@@ -95,7 +95,7 @@ export default class AIDParser {
} }
public static parseInterfaceMetadataProperty(prop: aasCore.types.SubmodelElementCollection, endpointProtocol?: AvailableEndpoint): InterfaceProperty | null { 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; const parsed = {} as InterfaceProperty;
@@ -106,19 +106,21 @@ export default class AIDParser {
else parsed[key] = prop.value; else parsed[key] = prop.value;
} */ } */
const parsedForms = [] as InterfacePropertyForm[];
const forms = Traverser.findElement(prop, element => (element as any).idShort.toLocaleLowerCase() === "forms"); const forms = Traverser.findElement(prop, element => (element as any).idShort.toLocaleLowerCase() === "forms");
if (forms === null || !aasCore.types.isSubmodelElementList(forms) || !forms.value) return null; // Mandatory 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; if (!aasCore.types.isSubmodelElementCollection(element)) continue;
const parsedForm = AIDParser.parseInterfaceMetadataPropertyForm(element, endpointProtocol); const parsedForm = AIDParser.parseInterfaceMetadataPropertyForm(element, endpointProtocol);
if (parsedForm === null) continue; if (parsedForm === null) continue;
parsed.forms.push(parsedForm); parsedForms.push(parsedForm);
} }
// TODO // TODO
// Other values based on endpoint protocol // 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 { private static parseInterfaceMetadataPropertyForm(form: aasCore.types.SubmodelElementCollection, endpointProtocol?: AvailableEndpoint): InterfacePropertyForm | null {
@@ -143,7 +145,7 @@ export default class AIDParser {
return null; 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 // TODO
return null; 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 { 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"; import { AvailableEndpoint } from "./common";
export type AssetInterfaceDescription = { export type AssetInterfacesDescription = {
[key in AvailableEndpoint]: InterfaceDescription[]; [key in AvailableEndpoint]: InterfaceDescription[];
}; };
+11 -6
View File
@@ -1,14 +1,19 @@
import type { types } from "@aas-core-works/aas-core3.0-typescript"; 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 = { export type MappingConfEntry = {
EndpointMetaDataReference: types.ReferenceElement; EndpointMetaDataReference:types.SubmodelElementCollection;
MappingConfiguration: MappingConfiguration; MappingConfiguration: MappingConfiguration[];
} }
export type MappingConfiguration = { export type MappingConfiguration = {
InterfaceMetaDataReference: types.ReferenceElement; InterfaceMetaDataReference: types.SubmodelElementCollection;
SourceSinkMappings: types.RelationshipElement[]; 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 const availableEndpoints = ["HTTP", "MQTT", "Modbus"] as const;
export type AvailableEndpoint = typeof availableEndpoints[number]; 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()); const idx = availableEndpoints.map(e => e.toLocaleLowerCase()).findIndex(e => e === endpoint.toLocaleLowerCase());
if (idx === -1) return null; if (idx === -1) return null;
return availableEndpoints[idx]; return availableEndpoints[idx];
}
export type ResolvedRelationshipElement = {
first: types.Class;
second: types.Class;
} }