diff --git a/AAS API on Azure.sln b/AAS API on Azure.sln index f81c686..dec421a 100644 --- a/AAS API on Azure.sln +++ b/AAS API on Azure.sln @@ -48,6 +48,18 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{D3185476 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AASX Command line", "AASX Command line", "{803CDCDE-B5BD-4EE7-A873-68B352F0A275}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AASX File Service", "AASX File Service", "{37B709F4-6530-461C-BB69-2E96286F7F67}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AAS Entire services", "AAS Entire services", "{498C9BA9-D946-4D8B-BB37-AD0FC939D457}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AAS Discovery", "AAS Discovery", "{6667EF7A-F23F-46AD-B3C7-BE52355F6BE7}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AAS Registry", "AAS Registry", "{0D24FB47-4A2B-4228-84C4-251642E6C1F7}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AAS Shell repository", "AAS Shell repository", "{60E05FDD-8466-4A85-AF46-82C451B53C37}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Common", "Common", "{FC582922-7227-4B4C-8FE2-28BEB8D06AF4}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -123,6 +135,20 @@ Global HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution + {37E281C2-FC84-4BD9-AFB5-AC55A9D92B0F} = {60E05FDD-8466-4A85-AF46-82C451B53C37} + {8246142F-9B71-424D-A219-9F4BA4C4B77B} = {FC582922-7227-4B4C-8FE2-28BEB8D06AF4} + {71AD2742-04E6-4C8C-8D94-C061DEE9D5A3} = {60E05FDD-8466-4A85-AF46-82C451B53C37} + {2295DDEC-6AD9-41A5-82A9-FA72BFBC98E8} = {60E05FDD-8466-4A85-AF46-82C451B53C37} + {5E55C6F8-30F0-46BC-8637-D5B397E04D6B} = {498C9BA9-D946-4D8B-BB37-AD0FC939D457} + {C82208F3-1191-4284-8D92-2E70C804E028} = {FC582922-7227-4B4C-8FE2-28BEB8D06AF4} + {E657B3A4-0D61-4EDB-8B42-E3B137D1AC01} = {6667EF7A-F23F-46AD-B3C7-BE52355F6BE7} + {A1582F1D-ED33-4197-A7C8-73BA6802435D} = {37B709F4-6530-461C-BB69-2E96286F7F67} + {E3D5DCB3-4CA1-4833-8A86-DA3D33D1748E} = {37B709F4-6530-461C-BB69-2E96286F7F67} + {82A9C310-4E09-4FD7-B3DB-F9E6C670A536} = {37B709F4-6530-461C-BB69-2E96286F7F67} + {F54DA57A-DC54-4F84-A0C7-0ED10B60C226} = {6667EF7A-F23F-46AD-B3C7-BE52355F6BE7} + {0D0AD4E2-C76D-43F7-B62E-6D386DF2A93E} = {0D24FB47-4A2B-4228-84C4-251642E6C1F7} + {A4F5E0BC-2400-422E-9D38-6B82BB3E1347} = {0D24FB47-4A2B-4228-84C4-251642E6C1F7} + {B3AA9231-9B5E-4A71-AE05-57A687CD9097} = {0D24FB47-4A2B-4228-84C4-251642E6C1F7} {ECD5A9BE-8210-4BA6-A1FC-F23A3F0C7B84} = {803CDCDE-B5BD-4EE7-A873-68B352F0A275} {5BBB7304-26FC-4690-BEF4-59782E720A68} = {803CDCDE-B5BD-4EE7-A873-68B352F0A275} {803CDCDE-B5BD-4EE7-A873-68B352F0A275} = {D3185476-EC73-4773-BA8D-E19147F729AE} diff --git a/tools/aasx-cmdline-tests/SimpleAASXImportTests.cs b/tools/aasx-cmdline-tests/SimpleAASXImportTests.cs index b45bf5a..ea7ef52 100644 --- a/tools/aasx-cmdline-tests/SimpleAASXImportTests.cs +++ b/tools/aasx-cmdline-tests/SimpleAASXImportTests.cs @@ -1,10 +1,13 @@ using AAS.AASX.CmdLine.Import; using AAS.AASX.CmdLine.Import.ADT; +using AdminShellNS; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; +using System.IO; +using System.Net.Http; using static AdminShellNS.AdminShellV20; namespace AAS.AASX.CmdLine.Test.Import @@ -14,6 +17,8 @@ public class SimpleAASXImportTests : AbstractTestSupport { private IAASXImporter importer; + private HttpClient httpClient; + [TestInitialize] public void Setup() { @@ -25,6 +30,7 @@ public void Setup() services.AddSingleton(); services.AddSingleton(); + services.AddHttpClient(); configuration = hostContext.Configuration; }) .Build(); @@ -33,6 +39,7 @@ public void Setup() IServiceProvider provider = serviceScope.ServiceProvider; importer = provider.GetRequiredService(); + this.httpClient = provider.GetRequiredService().CreateClient(); } [TestMethod] @@ -47,5 +54,24 @@ public void TestImportRelationShipElement01() string dtId = importer.ImportRelationshipElement(relElement).GetAwaiter().GetResult(); Assert.IsFalse(String.IsNullOrEmpty(dtId)); } + + [TestMethod] + public void TestImportConceptDescription() + { + string outputPath = Path.GetTempPath() + "01_Festo.aasx"; + byte[] fileBytes = this.httpClient.GetByteArrayAsync(new Uri("https://admin-shell-io.com/samples/aasx/01_Festo.aasx")).GetAwaiter().GetResult(); + System.IO.File.WriteAllBytes(outputPath, fileBytes); + + try + { + using var package = new AdminShellPackageEnv(outputPath); + + this.importer.ImportConceptDescription(package.AasEnv.ConceptDescriptions[0]).GetAwaiter().GetResult(); + } + finally + { + System.IO.File.Delete(outputPath); + } + } } } diff --git a/tools/aasx-cmdline/AASXImporter.cs b/tools/aasx-cmdline/AASXImporter.cs index 13cf88a..7482c10 100644 --- a/tools/aasx-cmdline/AASXImporter.cs +++ b/tools/aasx-cmdline/AASXImporter.cs @@ -11,6 +11,7 @@ public interface IAASXImporter public Task ImportRelationshipElement(RelationshipElement relElement, ImportContext processInfo = null); public Task ImportAnnotatedRelationshipElement(AnnotatedRelationshipElement relElement, ImportContext processInfo = null); + public Task ImportConceptDescription(ConceptDescription conceptDescription, ImportContext processInfo = null); } public class TwinRef diff --git a/tools/aasx-cmdline/ADTAASRepo.cs b/tools/aasx-cmdline/ADTAASRepo.cs index 79e7c34..b757dd4 100644 --- a/tools/aasx-cmdline/ADTAASRepo.cs +++ b/tools/aasx-cmdline/ADTAASRepo.cs @@ -124,22 +124,5 @@ public async Task FindTwinForReference(AdminShellV20.Reference reference else return null; } - - public async Task KeyExists(AdminShellV20.Key key) - { - string result = null; - - string queryString = $"SELECT * FROM digitaltwins dt WHERE IS_OF_MODEL('{ADTAASOntology.MODEL_KEY}') " + - $"AND key = '{key.type}' " + $"AND idType = '{AASUtils.URITOIRI(key.idType)}' " + $"AND value = '{key.value}'"; - - AsyncPageable queryResult = dtClient.QueryAsync(queryString); - await foreach (BasicDigitalTwin twin in queryResult) - { - result = twin.Id; - break; - } - - return result; - } } } diff --git a/tools/aasx-cmdline/ADTAASXPackageImporter.cs b/tools/aasx-cmdline/ADTAASXPackageImporter.cs index b190c08..124d4cd 100644 --- a/tools/aasx-cmdline/ADTAASXPackageImporter.cs +++ b/tools/aasx-cmdline/ADTAASXPackageImporter.cs @@ -5,6 +5,7 @@ using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; +using System.Data; using System.Linq; using System.Threading.Tasks; using static AdminShellNS.AdminShellV20; @@ -339,12 +340,26 @@ private async Task ImportReferenceElement(ReferenceElement refElement, I var twinData = CreateTwinForModel(ADTAASOntology.MODEL_REFERENCEELEMENT); AddDataElementAttributes(twinData, refElement); + + // Reference keys + for(int i = 1; i <= 8; i++) + { + var keyEntry = new BasicDigitalTwinComponent(); + twinData.Contents.Add($"key{i}", keyEntry); + if (refElement != null && refElement.value != null + && refElement.value.Keys != null && refElement.value.Keys.Count >= i) + { + var key = refElement.value.Keys[i - 1]; + keyEntry.Contents.Add("key", key.type); + keyEntry.Contents.Add("value", key.value); + keyEntry.Contents.Add("idType", AASUtils.URITOIRI(key.idType)); + } + } + await DoCreateOrReplaceDigitalTwinAsync(twinData, processInfo); await AddDataElementRelationships(twinData, refElement, processInfo); - await AddReference(twinData, refElement.value, "value", processInfo); - return twinData.Id; } @@ -501,7 +516,19 @@ private async Task ImportMultiLanguageProperty(MultiLanguageProperty pro AddDataElementAttributes(twinData, property); if (property.value != null) - twinData.Contents.Add("value", AASUtils.LangStringSetToString(property.value)); + { + Dictionary entries = new Dictionary(); + foreach (var item in property.value.langString) + { + entries.Add(item.lang, item.str); + } + + if (entries.Count > 0) { + var langStringSet = new BasicDigitalTwinComponent(); + langStringSet.Contents.Add("langString", entries); + twinData.Contents.Add("value", langStringSet); + } + } await DoCreateOrReplaceDigitalTwinAsync(twinData, processInfo); @@ -718,9 +745,22 @@ public async Task ImportConceptDescription(ConceptDescription conceptDescription // Start by creating a twin for the Concept description var twinData = CreateTwinForModel(ADTAASOntology.MODEL_CONCEPTDESCRIPTION); - AddIdentifiableAttributes(twinData, conceptDescription, - (conceptDescription.IEC61360Content != null && conceptDescription.IEC61360Content.shortName != null) ? - conceptDescription.IEC61360Content.shortName.GetDefaultStr() : null); + AddIdentifiableAttributes(twinData, conceptDescription); + + if ( conceptDescription.IEC61360Content != null && conceptDescription.IEC61360Content.shortName != null ) + { + var displayNameComp = twinData.Contents["displayName"] as BasicDigitalTwinComponent; + if (displayNameComp != null) + { + var entries = displayNameComp.Contents.ContainsKey("langString") ? displayNameComp.Contents["langString"] as Dictionary : null; + if (entries == null) + { + entries = new Dictionary(); + displayNameComp.Contents.Add("langString", entries); + } + entries.Add(LangStr.LANG_DEFAULT.ToUpper(), conceptDescription.IEC61360Content.shortName.GetDefaultStr()); + } + } await DoCreateOrReplaceDigitalTwinAsync(twinData, processInfo); @@ -748,13 +788,11 @@ private async Task AddHasDataSpecification(BasicDigitalTwin twinData, HasDataSpe if (content.preferredName != null) { - dsTwinData.Contents.Add("preferredName", AASUtils.LangStringSetIEC61360ToString( - content.preferredName)); + dsTwinData.Contents.Add("preferredName", CreateLangStringSetComponent(content.preferredName)); } if (content.shortName != null) { - dsTwinData.Contents.Add("shortName", AASUtils.LangStringSetIEC61360ToString( - content.shortName)); + dsTwinData.Contents.Add("shortName", CreateLangStringSetComponent(content.shortName)); } if (!string.IsNullOrEmpty(content.unit)) dsTwinData.Contents.Add("unit", content.unit); @@ -765,7 +803,7 @@ private async Task AddHasDataSpecification(BasicDigitalTwin twinData, HasDataSpe if (!string.IsNullOrEmpty(content.dataType)) dsTwinData.Contents.Add("dataType", content.dataType); if (content.definition != null) - dsTwinData.Contents.Add("definition", AASUtils.LangStringSetIEC61360ToString(content.definition)); + dsTwinData.Contents.Add("definition", CreateLangStringSetComponent(content.definition)); if (!string.IsNullOrEmpty(content.valueFormat)) dsTwinData.Contents.Add("valueFormat", content.valueFormat); // TODO: valueList @@ -783,6 +821,25 @@ private async Task AddHasDataSpecification(BasicDigitalTwin twinData, HasDataSpe } } + private static BasicDigitalTwinComponent CreateLangStringSetComponent(LangStringSetIEC61360 langStrs) + { + Dictionary entries = new Dictionary(); + foreach (var item in langStrs) + { + entries.Add(item.lang, item.str); + } + + if (entries.Count > 0) + { + var langStringSet = new BasicDigitalTwinComponent(); + langStringSet.Contents.Add("langString", entries); + + return langStringSet; + } + else + return null; + } + private async Task AddReferences(BasicDigitalTwin twinData, List references, string relationshipName, ImportContext processInfo) { foreach(var reference in references) @@ -793,72 +850,95 @@ private async Task AddReferences(BasicDigitalTwin twinData, List refe private async Task AddReference(BasicDigitalTwin twinData, Reference reference, string relationshipName, ImportContext processInfo) { - var refTwinData = await CreateReferenceTwin(twinData, relationshipName, processInfo); - await AddKeysOfReference(refTwinData, reference, processInfo); + if (reference != null && reference.Keys != null && reference.Keys.Count > 0) + await AddReference(twinData, reference.Keys, relationshipName, processInfo); } private async Task AddReference(BasicDigitalTwin twinData, List forKeys, string relationshipName, ImportContext processInfo) { - var refTwinData = await CreateReferenceTwin(twinData, relationshipName, processInfo); - await AddKeys(refTwinData, forKeys, processInfo); + if (forKeys != null && forKeys.Count > 0) + await CreateReferenceTwin(twinData, forKeys, relationshipName, processInfo); } - private async Task CreateReferenceTwin(BasicDigitalTwin twinData, string relationshipName, ImportContext processInfo) + private async Task CreateReferenceTwin(BasicDigitalTwin twinData, List forKeys, string relationshipName, ImportContext processInfo) { // Create Reference twin var refTwinData = CreateTwinForModel(ADTAASOntology.MODEL_REFERENCE); - await DoCreateOrReplaceDigitalTwinAsync(refTwinData, processInfo); - - // Create relationship between source twin and Reference twin - await DoCreateOrReplaceRelationshipAsync(twinData, relationshipName, refTwinData.Id); - - return refTwinData; - } - private async Task AddKeysOfReference(BasicDigitalTwin refTwinData, Reference reference, ImportContext processInfo) - { - if (reference.Keys != null && reference.Keys.Count > 0) - await AddKeys(refTwinData, reference.Keys, processInfo); - } - - private async Task AddKeys(BasicDigitalTwin refTwinData, List keys, ImportContext processInfo) - { - foreach (var key in keys) + if (forKeys != null && forKeys.Count > 0) + { + int count = 0; + foreach (var key in forKeys) + { + count++; + if (count <= 8) { + string keyPropName = $"key{count}"; + var keyTwinData = new BasicDigitalTwinComponent(); + keyTwinData.Contents.Add("key", key.type); + keyTwinData.Contents.Add("value", key.value); + keyTwinData.Contents.Add("idType", AASUtils.URITOIRI(key.idType)); + refTwinData.Contents.Add(keyPropName, keyTwinData); + } + else + { + _logger.LogError($"Reference contains more than the maximum 8 keys supported. {forKeys}"); + } + } + for(int i = count+1; i < 9; i++) + { + refTwinData.Contents.Add($"key{i}", new BasicDigitalTwinComponent()); + } + } + else { - string keyDtId = await KeyExists(key); - if (keyDtId == null) + for(int i = 1; i <= 8; i++) { - // Create key - var keyTwinData = CreateTwinForModel(ADTAASOntology.MODEL_KEY); - keyTwinData.Contents.Add("key", key.type); - keyTwinData.Contents.Add("value", key.value); - keyTwinData.Contents.Add("idType", AASUtils.URITOIRI(key.idType)); + refTwinData.Contents.Add($"key{i}", new BasicDigitalTwinComponent()); + } + } - await DoCreateOrReplaceDigitalTwinAsync(keyTwinData, processInfo); + await DoCreateOrReplaceDigitalTwinAsync(refTwinData, processInfo); - keyDtId = keyTwinData.Id; - } + // Create relationship between source twin and Reference twin + await DoCreateOrReplaceRelationshipAsync(twinData, relationshipName, refTwinData.Id); - await DoCreateOrReplaceRelationshipAsync(refTwinData, "key", keyDtId); - } + return refTwinData; } - private void AddReferableAttributes(BasicDigitalTwin twinData, Referable referable, string displayname = null) + private void AddReferableAttributes(BasicDigitalTwin twinData, Referable referable) { if (!string.IsNullOrEmpty(referable.idShort)) twinData.Contents.Add("idShort", referable.idShort); - if (!string.IsNullOrEmpty(displayname)) - twinData.Contents.Add("displayName", displayname); + + twinData.Contents.Add("displayName", new BasicDigitalTwinComponent()); + if (!string.IsNullOrEmpty(referable.category)) twinData.Contents.Add("category", referable.category); + if (referable.description != null) - twinData.Contents.Add("description", AASUtils.DescToString(referable.description)); + { + Dictionary descEntries = new Dictionary(); + foreach (var desc in referable.description.langString) + descEntries.Add(desc.lang, desc.str); + + if (descEntries.Count > 0) + { + var descTwinData = new BasicDigitalTwinComponent(); + descTwinData.Contents.Add("langString", descEntries); + twinData.Contents.Add("description", descTwinData); + } + else + twinData.Contents.Add("description", new BasicDigitalTwinComponent()); + } else + twinData.Contents.Add("description", new BasicDigitalTwinComponent()); + + twinData.Contents.Add("tags", CreateStandardTagsForImport()); } - private void AddIdentifiableAttributes(BasicDigitalTwin twinData, Identifiable identifiable, string displayname = null) + private void AddIdentifiableAttributes(BasicDigitalTwin twinData, Identifiable identifiable) { // Referable attributes - AddReferableAttributes(twinData, identifiable, displayname); + AddReferableAttributes(twinData, identifiable); // Identifiable attributes BasicDigitalTwinComponent identifier = new BasicDigitalTwinComponent(); @@ -970,5 +1050,14 @@ private async Task> DoCreateOrReplaceRelationshipAsy return result; } + + private BasicDigitalTwinComponent CreateStandardTagsForImport() + { + var tagsComponent = new BasicDigitalTwinComponent(); + + tagsComponent.Contents.Add("markers", new Dictionary() { { "import", true} }); + + return tagsComponent; + } } } diff --git a/tools/aasx-cmdline/AbstractADTCommand.cs b/tools/aasx-cmdline/AbstractADTCommand.cs index b3f79b5..2f3c2b6 100644 --- a/tools/aasx-cmdline/AbstractADTCommand.cs +++ b/tools/aasx-cmdline/AbstractADTCommand.cs @@ -116,11 +116,6 @@ public async Task AssetExists(Asset asset) return await queryResult.GetAsyncEnumerator().MoveNextAsync(); } - public async Task KeyExists(Key key) - { - return await this.aasRepo.KeyExists(key); - } - public async Task FindTwinForReference(Reference reference) { return await this.aasRepo.FindTwinForReference(reference); diff --git a/tools/aasx-cmdline/IAASRepo.cs b/tools/aasx-cmdline/IAASRepo.cs index 5205f92..a5b027a 100644 --- a/tools/aasx-cmdline/IAASRepo.cs +++ b/tools/aasx-cmdline/IAASRepo.cs @@ -8,7 +8,6 @@ namespace AAS.AASX.CmdLine { public interface IAASRepo { - public Task KeyExists(Key key); public Task FindTwinForReference(Reference reference); } }