diff --git a/aas-web-ui/src/composables/ConceptDescriptionHandling.ts b/aas-web-ui/src/composables/ConceptDescriptionHandling.ts index 6d542da..bd2bca5 100644 --- a/aas-web-ui/src/composables/ConceptDescriptionHandling.ts +++ b/aas-web-ui/src/composables/ConceptDescriptionHandling.ts @@ -106,8 +106,44 @@ export function useConceptDescriptionHandling() { return conceptDescriptions; } + // Get the Definition from the EmbeddedDataSpecification of the ConceptDescription of the Property (if available) + function cdDefinition(prop: any) { + if (!prop.conceptDescriptions) { + getConceptDescriptions(prop).then((conceptDescriptions) => { + prop.conceptDescriptions = conceptDescriptions; + }); + } + if (!prop.conceptDescriptions || prop.conceptDescriptions.length == 0) { + return ''; + } + for (const conceptDescription of prop.conceptDescriptions) { + if (!conceptDescription.embeddedDataSpecifications) { + continue; + } + for (const embeddedDataSpecification of conceptDescription.embeddedDataSpecifications) { + if ( + embeddedDataSpecification.dataSpecificationContent && + embeddedDataSpecification.dataSpecificationContent.definition + ) { + const definitionEn = embeddedDataSpecification.dataSpecificationContent.definition.find( + (definition: any) => { + return definition.language === 'en' && definition.text !== ''; + } + ); + if (definitionEn && definitionEn.text) { + return definitionEn.text; + } + } else { + return ''; + } + } + } + return ''; + } + return { unitSuffix, getConceptDescriptions, + cdDefinition, }; } diff --git a/aas-web-ui/src/utils/DateUtils.ts b/aas-web-ui/src/utils/DateUtils.ts new file mode 100644 index 0000000..e2bb2ac --- /dev/null +++ b/aas-web-ui/src/utils/DateUtils.ts @@ -0,0 +1,13 @@ +// convert js date object to string (format: yyyy-MM-dd HH:mm:ss) +export function formatDate(date: Date) { + return ( + [date.getFullYear(), padTo2Digits(date.getMonth() + 1), padTo2Digits(date.getDate())].join('-') + + ' ' + + [padTo2Digits(date.getHours()), padTo2Digits(date.getMinutes()), padTo2Digits(date.getSeconds())].join(':') + ); +} + +// convert date element to digits +function padTo2Digits(num: number) { + return num.toString().padStart(2, '0'); +} diff --git a/aas-web-ui/src/utils/IDUtils.ts b/aas-web-ui/src/utils/IDUtils.ts index d2b977b..ca91a17 100644 --- a/aas-web-ui/src/utils/IDUtils.ts +++ b/aas-web-ui/src/utils/IDUtils.ts @@ -1,5 +1,50 @@ +import md5 from 'md5'; import { v4 as uuidv4 } from 'uuid'; export function UUID(): string { return uuidv4(); } + +export function generateUUIDFromString(str: any): string { + // create md5 hash from string + const hash = md5(str); + // create UUID from hash + const guid = + hash.substring(0, 8) + + '-' + + hash.substring(8, 12) + + '-' + + hash.substring(12, 16) + + '-' + + hash.substring(16, 20) + + '-' + + hash.substring(20, 32); + return guid; +} + +// Function to check if the idShort of a SubmodelElement matches the given idShort +export function checkIdShort( + referable: any, + idShort: string, + startsWith: boolean = false, + strict: boolean = false +): boolean { + if (idShort.trim() === '') return false; + + if (!referable || !referable.idShort || referable.idShort.length === 0) return false; + + if (startsWith) { + // For matching e.g. ProductImage{00} with idShort ProductImage + if (strict) { + return referable.idShort.startsWith(idShort); + } else { + return referable.idShort.toLowerCase().startsWith(idShort.toLowerCase()); + } + } else { + if (strict) { + return referable.idShort === idShort; + } else { + return referable.idShort.toLowerCase() === idShort.toLowerCase(); + } + } +} diff --git a/aas-web-ui/src/utils/SemanticIdUtils.ts b/aas-web-ui/src/utils/SemanticIdUtils.ts index 82137f9..be76eb9 100644 --- a/aas-web-ui/src/utils/SemanticIdUtils.ts +++ b/aas-web-ui/src/utils/SemanticIdUtils.ts @@ -1,3 +1,126 @@ +// Function to check if the SemanticID of a SubmodelElement matches the given SemanticID +export function checkSemanticId(submodelElement: any, semanticId: string): boolean { + // console.log('checkSemanticId', 'submodelElement', submodelElement, 'semanticId', semanticId); + if (semanticId.trim() == '') return false; + + if (!Array.isArray(submodelElement?.semanticId?.keys) || submodelElement.semanticId.keys.length == 0) return false; + + for (const key of submodelElement.semanticId.keys) { + // console.log('checkSemanticId: ', 'key of submodelElement', key.value, 'semanticId', semanticId); + if (key.value.startsWith('0112/')) { + return checkSemanticIdIecCdd(key.value, semanticId); + } else if (key.value.startsWith('0173-1#') || key.value.startsWith('0173/1///')) { + return checkSemanticIdEclassIrdi(key.value, semanticId); + } else if (key.value.startsWith('https://api.eclass-cdp.com/0173-1')) { + return checkSemanticIdEclassIrdiUrl(key.value, semanticId); + } else if (key.value.startsWith('http://') || key.value.startsWith('https://')) { + return checkSemanticIdIri(key.value, semanticId); + } else { + if (key.value === semanticId) return true; + } + } + + return false; +} + +export function checkSemanticIdEclassIrdi(keyValue: string, semanticId: string): boolean { + if (semanticId.trim() == '') return false; + + if (!keyValue.startsWith('0173-1#') && !keyValue.startsWith('0173/1///')) return false; + + if (keyValue.startsWith('0173-1#')) { + // Eclass IRDI like 0173-1#01-AHF578#001 + if (new RegExp(/\*\d{2}$/).test(keyValue)) { + keyValue = keyValue.slice(0, -3); + semanticId = semanticId.slice(0, -3); + } + if (new RegExp(/[#-]{1}\d{3}$/).test(semanticId) || new RegExp(/[#-]{1}\d{3}\*\d{1,}$/).test(semanticId)) { + return getEquivalentEclassSemanticIds(keyValue).includes(semanticId); + } + + // Eclass IRDI without version; like 0173-1#01-AHF578 + return ( + getEquivalentEclassSemanticIds(keyValue).findIndex((equivalentSemanticId) => { + return equivalentSemanticId.startsWith(semanticId); + }, semanticId) != -1 + ); + } else if (keyValue.startsWith('0173/1///')) { + if (new RegExp(/[#-]{1}\d{3}$/).test(semanticId) || new RegExp(/[#-]{1}\d{3}\*\d{1,}$/).test(semanticId)) { + // Eclass IRDI with version; like 0173/1///01#AHF578#001 + return getEquivalentEclassSemanticIds(keyValue).includes(semanticId); + } + + // Eclass IRDI without version; like 0173/1///01#AHF578 + return ( + getEquivalentEclassSemanticIds(keyValue).findIndex((equivalentSemanticId) => { + return equivalentSemanticId.startsWith(semanticId); + }, semanticId) != -1 + ); + } + + return false; +} + +export function checkSemanticIdEclassIrdiUrl(keyValue: string, semanticId: string): boolean { + if (semanticId.trim() == '') return false; + + if (!keyValue.startsWith('https://api.eclass-cdp.com/0173-1')) return false; + + // Eclass URL like https://api.eclass-cdp.com/0173-1-01-AHF578-001 + if (new RegExp(/[#-]{1}\d{3}$/).test(semanticId) || new RegExp(/[#-]{1}\d{3}~\d{1,}$/).test(semanticId)) { + // Eclass URL with version (like https://api.eclass-cdp.com/0173-1-01-AHF578-001) + return getEquivalentEclassSemanticIds(semanticId).includes(keyValue); + } + + // Eclass URL without version (like https://api.eclass-cdp.com/0173-1-01-AHF578) + return ( + getEquivalentEclassSemanticIds(keyValue).findIndex((equivalentSemanticId) => { + return equivalentSemanticId.startsWith(semanticId); + }, semanticId) != -1 + ); +} + +export function checkSemanticIdIecCdd(keyValue: string, semanticId: string): boolean { + if (semanticId.trim() == '') return false; + + if (!semanticId.startsWith('0112/')) return false; + if (!keyValue.startsWith('0112/')) return false; + + // IEC CDD like 0112/2///61987#ABN590#002 + if (new RegExp(/[#-]{1}\d{3}$/).test(semanticId)) { + // IEC CDD with version; like 0112/2///61987#ABN590#002 + if (keyValue === semanticId) { + return true; + } + } + + // IEC CDD without version; like 0112/2///61987#ABN590 + return keyValue.startsWith(semanticId); +} + +export function checkSemanticIdIri(keyValue: string, semanticId: string): boolean { + // console.log('checkSemanticIdIri: ', 'keyValue', keyValue, 'semanticId', semanticId); + if (semanticId.trim() == '') return false; + + if (!semanticId.startsWith('http://') && !semanticId.startsWith('https://')) return false; + if (!keyValue.startsWith('http://') && !keyValue.startsWith('https://')) return false; + + if (keyValue.endsWith('/')) keyValue = keyValue.substring(0, keyValue.length - 1); + if (semanticId.endsWith('/')) semanticId = semanticId.substring(0, semanticId.length - 1); + + if (new RegExp(/\/\d{1,}\/\d{1,}$/).test(semanticId)) { + // IRI with version like https://admin-shell.io/idta/CarbonFootprint/ProductCarbonFootprint/0/9/ + return getEquivalentIriSemanticIds(semanticId).includes(keyValue); + } + + // IRI without version like https://admin-shell.io/idta/CarbonFootprint/ProductCarbonFootprint/ + return ( + getEquivalentIriSemanticIds(keyValue).findIndex((equivalentSemanticId) => { + return equivalentSemanticId.startsWith(semanticId); + }, semanticId) != -1 + ); +} + export function getEquivalentEclassSemanticIds(semanticId: string): any[] { if ( semanticId.trim() === '' || diff --git a/aas-web-ui/src/utils/generalUtils.ts b/aas-web-ui/src/utils/generalUtils.ts new file mode 100644 index 0000000..b951a7d --- /dev/null +++ b/aas-web-ui/src/utils/generalUtils.ts @@ -0,0 +1,62 @@ +// Function to capitalize the first letter of a string +export function capitalizeFirstLetter(string: string) { + return string.charAt(0).toUpperCase() + string.slice(1); +} + +// Function to check if the valueType is a number +export function isNumber(valueType: string) { + if (!valueType) return false; + // List of all number types + const numberTypes = [ + 'double', + 'float', + 'integer', + 'int', + 'nonNegativeInteger', + 'positiveInteger', + 'unsignedLong', + 'unsignedInt', + 'unsignedShort', + 'unsignedByte', + 'nonPositiveInteger', + 'negativeInteger', + 'long', + 'short', + 'decimal', + 'byte', + ]; + // strip xs: from the property if it exists + if (valueType.includes('xs:')) { + valueType = valueType.replace('xs:', ''); + } + // check if the property is a number + if (numberTypes.includes(valueType)) { + return true; + } else { + return false; + } +} + +// Function to download a JSON File +export function downloadJson(obj: any, fileName: string) { + const jsonStr = JSON.stringify(obj, null, 4); + const blob = new Blob([jsonStr], { type: 'application/json' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = fileName; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); +} + +// Function to download a binary File +export function downloadFile(filename: string, fileContent: Blob) { + const link = document.createElement('a'); + link.href = window.URL.createObjectURL(fileContent); + link.download = filename; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); +}