From 977c780f3471e2aeb78e05cd69005bbb7790d897 Mon Sep 17 00:00:00 2001 From: Michael Hoffmeister Date: Sun, 29 Oct 2023 15:00:28 +0100 Subject: [PATCH] * more SAMM models * CD sort order structured * box icons --- src/AasxCore.Samm2_2_0/SammClasses.cs | 370 ++++++++++-- .../AdminShellCollections.cs | 2 + .../Extensions/ExtendConceptDescription.cs | 42 +- .../options-debug.MIHO.json | 3 +- src/AasxPackageLogic/DispEditHelperBasics.cs | 12 +- .../DispEditHelperEntities.cs | 2 +- .../DispEditHelperMiniModules.cs | 3 +- src/AasxPackageLogic/DispEditHelperModules.cs | 562 +++++++++++++++--- src/AasxPackageLogic/VisualAasxElements.cs | 96 ++- .../DiplayVisualAasxElements.xaml.cs | 8 +- src/AnyUi/AnyUiSmallWidgetToolkit.cs | 25 +- 11 files changed, 970 insertions(+), 155 deletions(-) diff --git a/src/AasxCore.Samm2_2_0/SammClasses.cs b/src/AasxCore.Samm2_2_0/SammClasses.cs index 23cce5c1e..4d163d5cf 100644 --- a/src/AasxCore.Samm2_2_0/SammClasses.cs +++ b/src/AasxCore.Samm2_2_0/SammClasses.cs @@ -12,6 +12,7 @@ This source code may use other Open Source software components (see LICENSE.txt) using System.Linq.Expressions; using System.Reflection.PortableExecutable; using System.Runtime.Serialization; +using AasCore.Aas3_0; using AdminShellNS; using Extensions; using Newtonsoft.Json; @@ -45,12 +46,30 @@ public interface ISammSelfDescription /// get short name, which can als be used to distinguish elements. /// Exmaple: samm-x /// - public string GetSelfName(); + string GetSelfName(); /// /// Get URN of this element class. /// - public string GetSelfUrn(); + string GetSelfUrn(); + } + + /// + /// Shall be implemented in order to give hints about the + /// (hierarchical) structuring of elements + /// + public interface ISammStructureModel + { + /// + /// True, if a top element of a hierarchy + /// + bool IsTopElement(); + + /// + /// Iterate over all the SAMM elements referenced from this instance + /// without further recursion (see AasCore). + /// + IEnumerable DescendOnce(); } /// @@ -59,6 +78,10 @@ public interface ISammSelfDescription /// public class ModelElement { + // Note: + // The SAMM meta model details, that every element has a name. + // For AAS, the name is given by the Id of the ConceptDescription + /// /// Human readable name in a specific language. This attribute may be defined multiple /// times for different languages but only once for a specific language. There should @@ -237,23 +260,11 @@ public enum SammEncoding /// public class SammReference { - private string Rf { get; set; } - - public SammReference(string rf) - { - Rf = rf; - } - - public static implicit operator string(SammReference sr) - { - if (sr == null) - return ""; - return sr.Rf; - } + public string Value { get; set; } - public static implicit operator SammReference(string rf) + public SammReference(string val = "") { - return new SammReference(rf); + Value = val; } } @@ -404,12 +415,30 @@ public class FixedPointConstraint : Constraint /// /// /// - public class Characteristic : ModelElement + public class Characteristic : ModelElement, ISammSelfDescription, ISammStructureModel { + // self description + public string GetSelfName() => "samm-characteristic"; + public string GetSelfUrn() => "urn:samm:org.eclipse.esmf.samm:characteristic:2.1.0#Characteristic"; + + // structure model + public bool IsTopElement() => false; + public IEnumerable DescendOnce() + { + if (DataType != null) + yield return DataType; + } + /// /// Reference to a scalar or complex (Entity) data type. See Section "Type System" in the Aspect Meta Model. + /// Also the scalar data types (e.g. xsd:decimal) are treated as references in the first degree. /// - public SammDataType? DataType { get; set; } + public SammReference DataType { get; set; } + + public Characteristic() + { + DataType = new SammReference(""); + } } /// @@ -422,8 +451,8 @@ public class Characteristic : ModelElement public class Trait : Characteristic, ISammSelfDescription { // self description - public string GetSelfName() => "samm-trait"; - public string GetSelfUrn() => "urn:samm:org.eclipse.esmf.samm:characteristic:2.1.0#Trait"; + public new string GetSelfName() => "samm-trait"; + public new string GetSelfUrn() => "urn:samm:org.eclipse.esmf.samm:characteristic:2.1.0#Trait"; /// /// The Characterstic that is being constrained. @@ -447,8 +476,8 @@ public class Trait : Characteristic, ISammSelfDescription public class Quantifiable : Characteristic, ISammSelfDescription { // self description - public string GetSelfName() => "samm-quantifiable"; - public string GetSelfUrn() => "urn:samm:org.eclipse.esmf.samm:characteristic:2.1.0#Quantifiable"; + public new string GetSelfName() => "samm-quantifiable"; + public new string GetSelfUrn() => "urn:samm:org.eclipse.esmf.samm:characteristic:2.1.0#Quantifiable"; /// /// Reference to a Unit as defined in the Unit catalog @@ -464,16 +493,13 @@ public class Quantifiable : Characteristic, ISammSelfDescription public class Measurement : Characteristic, ISammSelfDescription { // self description - public string GetSelfName() => "samm-measurement"; - public string GetSelfUrn() => "urn:samm:org.eclipse.esmf.samm:characteristic:2.1.0#Measurement"; + public new string GetSelfName() => "samm-measurement"; + public new string GetSelfUrn() => "urn:samm:org.eclipse.esmf.samm:characteristic:2.1.0#Measurement"; /// /// Reference to a Unit as defined in the Unit catalog /// - [JsonIgnore] public SammReference Unit { get; set; } - [JsonProperty(PropertyName = "Unit")] - public string JsonUnit { get => "" + Unit; } public Measurement() { @@ -489,8 +515,8 @@ public Measurement() public class Enumeration : Characteristic, ISammSelfDescription { // self description - public string GetSelfName() => "samm-enumeration"; - public string GetSelfUrn() => "urn:samm:org.eclipse.esmf.samm:characteristic:2.1.0#Enumeration"; + public new string GetSelfName() => "samm-enumeration"; + public new string GetSelfUrn() => "urn:samm:org.eclipse.esmf.samm:characteristic:2.1.0#Enumeration"; /// /// List of possible values. The dataType of each of the values must match the @@ -534,8 +560,8 @@ public State() public class Duration : Characteristic, ISammSelfDescription { // self description - public string GetSelfName() => "samm-duration"; - public string GetSelfUrn() => "urn:samm:org.eclipse.esmf.samm:characteristic:2.1.0#Duration"; + public new string GetSelfName() => "samm-duration"; + public new string GetSelfUrn() => "urn:samm:org.eclipse.esmf.samm:characteristic:2.1.0#Duration"; /// /// Reference to a Unit as defined in the Unit catalog. The referenced unit or its referenceUnit @@ -551,7 +577,7 @@ public class Duration : Characteristic, ISammSelfDescription public Duration() { - Unit = ""; + Unit = new SammReference(); } } @@ -564,8 +590,8 @@ public Duration() public class Collection : Characteristic, ISammSelfDescription { // self description - public string GetSelfName() => "samm-collection"; - public string GetSelfUrn() => "urn:samm:org.eclipse.esmf.samm:characteristic:2.1.0#Collection"; + public new string GetSelfName() => "samm-collection"; + public new string GetSelfUrn() => "urn:samm:org.eclipse.esmf.samm:characteristic:2.1.0#Collection"; /// /// Reference to a Characteristic which describes the individual elements contained in the Collection. @@ -574,7 +600,7 @@ public class Collection : Characteristic, ISammSelfDescription public Collection() { - ElementCharacteristic = ""; + ElementCharacteristic = new SammReference(); } } @@ -643,8 +669,8 @@ public class TimeSeries : SortedSet, ISammSelfDescription public class Code : Characteristic, ISammSelfDescription { // self description - public string GetSelfName() => "samm-code"; - public string GetSelfUrn() => "urn:samm:org.eclipse.esmf.samm:characteristic:2.1.0#Code"; + public new string GetSelfName() => "samm-code"; + public new string GetSelfUrn() => "urn:samm:org.eclipse.esmf.samm:characteristic:2.1.0#Code"; } /// @@ -657,8 +683,8 @@ public class Code : Characteristic, ISammSelfDescription public class Either : Characteristic, ISammSelfDescription { // self description - public string GetSelfName() => "samm-either"; - public string GetSelfUrn() => "urn:samm:org.eclipse.esmf.samm:characteristic:2.1.0#Either"; + public new string GetSelfName() => "samm-either"; + public new string GetSelfUrn() => "urn:samm:org.eclipse.esmf.samm:characteristic:2.1.0#Either"; /// /// The left side of the Either. The attribute references another Characteristic which describes the value. @@ -686,8 +712,8 @@ public Either() public class SingleEntity : Characteristic, ISammSelfDescription { // self description - public string GetSelfName() => "samm-single-entity"; - public string GetSelfUrn() => "urn:samm:org.eclipse.esmf.samm:characteristic:2.1.0#SingleEntity"; + public new string GetSelfName() => "samm-single-entity"; + public new string GetSelfUrn() => "urn:samm:org.eclipse.esmf.samm:characteristic:2.1.0#SingleEntity"; } /// @@ -701,8 +727,8 @@ public class SingleEntity : Characteristic, ISammSelfDescription public class StructuredValue: Characteristic, ISammSelfDescription { // self description - public string GetSelfName() => "samm-structured-value"; - public string GetSelfUrn() => "urn:samm:org.eclipse.esmf.samm:characteristic:2.1.0#StructuredValue"; + public new string GetSelfName() => "samm-structured-value"; + public new string GetSelfUrn() => "urn:samm:org.eclipse.esmf.samm:characteristic:2.1.0#StructuredValue"; /// /// The regular expression used to deconstruct the value into parts that are mapped to separate @@ -725,6 +751,76 @@ public StructuredValue() } } + /// + /// A Property represents a named value. This element is optional and can appear multiple times in a model ([0..n]). + /// One Property has exactly one Characteristic. + /// + /// + public class Property : ModelElement, ISammSelfDescription, ISammStructureModel + { + // self description + public string GetSelfName() => "samm-property"; + public string GetSelfUrn() => "urn:bamm:io.openmanufacturing:meta-model:1.0.0#Property"; + + // structure model + public bool IsTopElement() => false; + public IEnumerable DescendOnce() + { + if (Characteristic != null) + yield return Characteristic; + } + + /// + /// This provides an example value for the Property, which requires that the entered data type has been defined + /// in a corresponding Characteristic. It is important to ensure that the data type has the correct format. + /// Find the Data Types (SAMM 2.1.0) with an example value. + /// + public string? ExampleValue { get; set; } + + /// + /// One Property has exactly one Characteristic. + /// + public SammReference Characteristic { get; set; } + + public Property() + { + Characteristic = new SammReference(""); + } + } + + /// + /// An Aspect is the root element of each Aspect Model and has a number of Properties, Events, and Operations. + /// This element is mandatory and must appear exactly once per model. + /// It has any number of Properties, Operations and Events ([0..n]). + /// + public class Aspect : ModelElement, ISammSelfDescription, ISammStructureModel + { + // self description + public string GetSelfName() => "samm-aspect"; + public string GetSelfUrn() => "urn:bamm:io.openmanufacturing:meta-model:1.0.0#Aspect"; + + // structure model + public bool IsTopElement() => true; + public IEnumerable DescendOnce() + { + if (Properties != null) + foreach (var x in Properties) + yield return x; + } + + // own + public List Properties { get; set; } + public List Events { get; set; } + public List Operations { get; set; } + + public Aspect() + { + Properties = new List(); + Events = new List(); + Operations = new List(); + } + } + /// /// Provides some constant values to the model. /// @@ -750,6 +846,127 @@ public static class Constants typeof(SingleEntity), typeof(StructuredValue) }; + + public static Type[] AddableElements = + { + // Top level + typeof(Aspect), + typeof(Property), + // Characteristic + typeof(Characteristic), + typeof(Trait), + typeof(Quantifiable), + typeof(Measurement), + typeof(Enumeration), + typeof(State), + typeof(Duration), + typeof(Collection), + typeof(List), + typeof(Set), + typeof(SortedSet), + typeof(TimeSeries), + typeof(Code), + typeof(Either), + typeof(SingleEntity), + typeof(StructuredValue) + }; + + /// + /// Holds information, how model element types should be rendered on the screen. + /// + public class SammElementRenderInfo + { + public string DisplayName = ""; + public string Abbreviation = ""; + public uint Foreground = 0x00000000; + public uint Background = 0x00000000; + } + + private static Dictionary _renderInfo = + new Dictionary(); + + public static SammElementRenderInfo? GetRenderInfo(Type t) + { + if (t != null && _renderInfo.ContainsKey(t)) + return _renderInfo[t]; + return null; + } + + static Constants() + { + _renderInfo.Add(typeof(Aspect), new SammElementRenderInfo() { + DisplayName = "Aspect", + Abbreviation = "A", + Foreground = 0xFF000000, + Background = 0xFF8298E0 + }); + + _renderInfo.Add(typeof(Property), new SammElementRenderInfo() + { + DisplayName = "Property", + Abbreviation = "P", + Foreground = 0xFF000000, + Background = 0xFFC5C8D4 + }); + + _renderInfo.Add(typeof(Characteristic), new SammElementRenderInfo() + { + DisplayName = "Characteristic", + Abbreviation = "C", + Foreground = 0xFF000000, + Background = 0xFFD6E2A6 + }); + + _renderInfo.Add(typeof(IEntity), new SammElementRenderInfo() + { + DisplayName = "Entity", + Abbreviation = "E", + Foreground = 0xFF000000, + Background = 0xFFAEADE0 + }); + + _renderInfo.Add(typeof(UnaryExpression), new SammElementRenderInfo() + { + DisplayName = "Unit", + Abbreviation = "U", + Foreground = 0xFF000000, + Background = 0xFFB9AB50 + }); + + _renderInfo.Add(typeof(Constraint), new SammElementRenderInfo() + { + DisplayName = "Constraint", + Abbreviation = "C", + Foreground = 0xFF000000, + Background = 0xFF74AEAF + }); + + _renderInfo.Add(typeof(Trait), new SammElementRenderInfo() + { + DisplayName = "Trait", + Abbreviation = "T", + Foreground = 0xFF000000, + Background = 0xFF74AEAF + }); + + _renderInfo.Add(typeof(Operation), new SammElementRenderInfo() + { + DisplayName = "Operation", + Abbreviation = "O", + Foreground = 0xFF000000, + Background = 0xFFD5BFDA + }); + + _renderInfo.Add(typeof(EventArgs), new SammElementRenderInfo() + { + DisplayName = "Event", + Abbreviation = "E", + Foreground = 0xFF000000, + Background = 0xFFB9D8FA + }); + } + + public static uint RenderBackground = 0xFFEFEFF0; } public static class Util @@ -776,13 +993,18 @@ public static bool HasSammSemanticId(Aas.IHasSemantics hasSem) public static Dictionary SammUrnToType = new Dictionary(); + public static Dictionary SammTypeToName = new Dictionary(); + static Util() { // dictionary from URN to type - foreach (var st in Constants.AddableCharacteristic) + foreach (var st in Constants.AddableElements) { if (Activator.CreateInstance(st, new object[] { }) is ISammSelfDescription ssd) + { SammUrnToType.Add(ssd.GetSelfUrn().ToLower(), st); + SammTypeToName.Add(st, "" + ssd.GetSelfName()); + } } } @@ -794,5 +1016,63 @@ static Util() return SammUrnToType[urn.ToLower()]; return null; } + + public static string? GetNameFromSammType(Type? sammType) + { + if (sammType == null) + return null; + if (SammTypeToName.ContainsKey(sammType)) + return SammTypeToName[sammType]; + return null; + } + + /// + /// Any chars which are sitting between "meaningful words" within a URI + /// + public static char[] UriDelimiters = new[] { + ':', '/', '+', '?', '[', ']', '@', '!', '$', '&', + '\'', '(', ')', '*', ',', ';', '.', '=' }; + + public static string? ShortenUri(string? uri) + { + // corner case + if (uri == null) + return null; + uri = uri.Trim(); + if (uri.Length < 1) + return ""; + + // simple case: find a anchor / '#' + var trimPos = uri.LastIndexOf('#'); + + // be more flexible? + if (trimPos < 0) + trimPos = uri.LastIndexOfAny(UriDelimiters); + + // ok, trim + if (trimPos >= 0) + uri = uri.Substring(0, trimPos); + + // return + return uri; + } + + public static string? LastWordOfUri(string? uri, string elseStr = "") + { + // corner case + if (uri == null) + return null; + uri = uri.Trim(); + if (uri.Length < 1) + return ""; + + // find delimiter? + var li = uri.LastIndexOf('#'); + if (li < 0) + li = uri.LastIndexOfAny(UriDelimiters); + if (li > 0) + return uri.Substring(li + 1); + return elseStr; + } } } \ No newline at end of file diff --git a/src/AasxCsharpLibrary/AdminShellCollections.cs b/src/AasxCsharpLibrary/AdminShellCollections.cs index 32e6774ab..414f16168 100644 --- a/src/AasxCsharpLibrary/AdminShellCollections.cs +++ b/src/AasxCsharpLibrary/AdminShellCollections.cs @@ -48,6 +48,8 @@ public IEnumerable> Keys return dict.Values; } } + + public void Clear() => dict.Clear(); } public class DoubleSidedDict diff --git a/src/AasxCsharpLibrary/Extensions/ExtendConceptDescription.cs b/src/AasxCsharpLibrary/Extensions/ExtendConceptDescription.cs index 27a9490ad..7d764e485 100644 --- a/src/AasxCsharpLibrary/Extensions/ExtendConceptDescription.cs +++ b/src/AasxCsharpLibrary/Extensions/ExtendConceptDescription.cs @@ -310,5 +310,45 @@ public static void AddIsCaseOf(this IConceptDescription cd, cd.IsCaseOf = new List(); cd.IsCaseOf.Add(ico); } - } + + /// + /// Returns false, if there is another element with same idShort in the list + /// + public static bool CheckIdShortIsUnique(this List cds, string idShort) + { + idShort = idShort?.Trim(); + if (idShort == null || idShort.Length < 1) + return false; + + var res = true; + foreach (var smw in cds) + if (smw != null && smw.IdShort != null && smw.IdShort == idShort) + { + res = false; + break; + } + + return res; + } + + /// + /// Creates ids with numerical index according to template string, until a unique idShort is found + /// + public static string IterateIdShortTemplateToBeUnique(this List cds, string idShortTemplate, int maxNum) + { + if (idShortTemplate == null || maxNum < 1 || !idShortTemplate.Contains("{0")) + return null; + + int i = 1; + while (i < maxNum) + { + var ids = string.Format(idShortTemplate, i); + if (cds.CheckIdShortIsUnique(ids)) + return ids; + i++; + } + + return null; + } + } } diff --git a/src/AasxPackageExplorer/options-debug.MIHO.json b/src/AasxPackageExplorer/options-debug.MIHO.json index d0b980ff7..fbca1b4d3 100644 --- a/src/AasxPackageExplorer/options-debug.MIHO.json +++ b/src/AasxPackageExplorer/options-debug.MIHO.json @@ -18,7 +18,7 @@ /* "AasxToLoad": "C:\\MIHO\\Develop\\Aasx\\repo\\Festo_SPAU_VR3.aasx", */ // "AasxToLoad": "C:\\HOMI\\Develop\\Aasx\\repo\\PxC_aasT_2900542_ELR_H3_I_SC__24DC_500AC_0_6.aasx", // "AasxToLoad": "C:\\HOMI\\Develop\\Aasx\\repo\\Festo_SPAU-B2R-H-G18FD-L-PNLK-PNVBA-M8U_V3c.aasx", - "AasxToLoad": "C:\\Users\\homi0002\\Desktop\\TempDesktop\\230619 Digi Twin Festo AASX Assessment\\231002_Feedback_1\\8001203_64be10f8-d4c8-4226-a56b-901b9fcbe29c_fb1.aasx", + // "AasxToLoad": "C:\\Users\\homi0002\\Desktop\\TempDesktop\\230619 Digi Twin Festo AASX Assessment\\231002_Feedback_1\\8001203_64be10f8-d4c8-4226-a56b-901b9fcbe29c_fb1.aasx", // "AasxToLoad": "C:\\HOMI\\Develop\\Aasx\\repo\\01_Festo_SPAU_VR3_DPPV2.aasx", // "AasxToLoad": "C:\\HOMI\\Develop\\Aasx\\repo\\bomtest1.aasx", // "AasxToLoad": "C:\\HOMI\\Develop\\Aasx\\repo\\leitungssatz_tier1.aasx", @@ -26,6 +26,7 @@ // "AasxToLoad": "C:\\HOMI\\Develop\\Aasx\\repo\\SMT_Sample_B.aasx", // "AuxToLoad": "C:\\HOMI\\Develop\\Aasx\\repo\\SMT_Sample_A.aasx", // "AasxToLoad": "C:\\HOMI\\Develop\\Aasx\\repo\\00_FestoDemoBox-Module-2-Kopie2.aasx", + "AasxToLoad": "C:\\HOMI\\Develop\\Aasx\\repo\\samm_spiel.aasx", "WindowLeft": 200, "WindowTop": -1, "WindowWidth": 900, diff --git a/src/AasxPackageLogic/DispEditHelperBasics.cs b/src/AasxPackageLogic/DispEditHelperBasics.cs index c667a669d..3ce0f4540 100644 --- a/src/AasxPackageLogic/DispEditHelperBasics.cs +++ b/src/AasxPackageLogic/DispEditHelperBasics.cs @@ -300,7 +300,8 @@ public void AddKeyValueExRef( string[] auxButtonToolTips = null, AnyUiLambdaActionBase takeOverLambdaAction = null, bool limitToOneRowForNoEdit = false, - int comboBoxMinWidth = -1) + int comboBoxMinWidth = -1, + bool noFirstColumnWidth = false) { AddKeyValue( view, key, value, nullValue, repo, setValue, comboBoxItems, comboBoxIsEditable, @@ -308,7 +309,8 @@ public void AddKeyValueExRef( auxButtonTitles, auxButtonToolTips, takeOverLambdaAction, (value == null) ? 0 : value.GetHashCode(), containingObject: containingObject, limitToOneRowForNoEdit: limitToOneRowForNoEdit, - comboBoxMinWidth: comboBoxMinWidth); + comboBoxMinWidth: comboBoxMinWidth, + noFirstColumnWidth: noFirstColumnWidth); } /// @@ -344,7 +346,8 @@ public void AddKeyValue( Nullable valueHash = null, object containingObject = null, bool limitToOneRowForNoEdit = false, - int comboBoxMinWidth = -1) + int comboBoxMinWidth = -1, + bool noFirstColumnWidth = false) { // draw anyway? if (repo != null && value == null) @@ -380,7 +383,8 @@ public void AddKeyValue( g.Margin = new AnyUiThickness(0, 1, 0, 1); var gc1 = new AnyUiColumnDefinition(); gc1.Width = AnyUiGridLength.Auto; - gc1.MinWidth = this.GetWidth(FirstColumnWidth.Standard); + if (!noFirstColumnWidth) + gc1.MinWidth = this.GetWidth(FirstColumnWidth.Standard); g.ColumnDefinitions.Add(gc1); var gc2 = new AnyUiColumnDefinition(); gc2.Width = new AnyUiGridLength(1.0, AnyUiGridUnitType.Star); diff --git a/src/AasxPackageLogic/DispEditHelperEntities.cs b/src/AasxPackageLogic/DispEditHelperEntities.cs index 430c356b6..a35efc61f 100644 --- a/src/AasxPackageLogic/DispEditHelperEntities.cs +++ b/src/AasxPackageLogic/DispEditHelperEntities.cs @@ -859,7 +859,7 @@ public void DisplayOrEditAasEntityAasEnv( minWidth: 250, items: new[] { "List index", "idShort", "Identification", "By AasSubmodel", - "By SubmodelElements" + "By SubmodelElements", "Structured" }), (o) => { diff --git a/src/AasxPackageLogic/DispEditHelperMiniModules.cs b/src/AasxPackageLogic/DispEditHelperMiniModules.cs index dba2cb78f..e90369b01 100644 --- a/src/AasxPackageLogic/DispEditHelperMiniModules.cs +++ b/src/AasxPackageLogic/DispEditHelperMiniModules.cs @@ -981,7 +981,8 @@ public void ExtensionHelper( padding: new AnyUiThickness(5, 0, 5, 0)); // special case: SAMM extension - if (Samm.Util.HasSammSemanticId(extension)) + // TODO: enable + if (false && Samm.Util.HasSammSemanticId(extension)) { substack.Add(new AnyUiLabel() { diff --git a/src/AasxPackageLogic/DispEditHelperModules.cs b/src/AasxPackageLogic/DispEditHelperModules.cs index 52bc3a0b6..48ead8eab 100644 --- a/src/AasxPackageLogic/DispEditHelperModules.cs +++ b/src/AasxPackageLogic/DispEditHelperModules.cs @@ -7,6 +7,7 @@ This source code is licensed under the Apache License 2.0 (see LICENSE.txt). This source code may use other Open Source software components (see LICENSE.txt). */ +using AasCore.Samm2_2_0; using AasxAmlImExport; using AasxCompatibilityModels; using AasxIntegrationBase; @@ -19,6 +20,8 @@ This source code may use other Open Source software components (see LICENSE.txt) using System.Data; using System.IO; using System.Linq; +using System.Windows.Media; +using System.Xaml; using static System.Windows.Forms.VisualStyles.VisualStyleElement.Window; using Aas = AasCore.Aas3_0; using Samm = AasCore.Samm2_2_0; @@ -2403,6 +2406,252 @@ public void DisplayOrEditEntityFileResource(AnyUiStackPanel stack, // // + public Type SammExtensionHelperSelectSammType(Type[] addableElements) + { + // create choices + var fol = new List(); + foreach (var stp in addableElements) + fol.Add(new AnyUiDialogueListItem("" + stp.Name, stp)); + + // prompt for this list + var uc = new AnyUiDialogueDataSelectFromList( + caption: "Select SAMM element type to add .."); + uc.ListOfItems = fol; + this.context.StartFlyoverModal(uc); + if (uc.Result && uc.ResultItem != null && uc.ResultItem.Tag != null && + ((Type)uc.ResultItem.Tag).IsAssignableTo(typeof(Samm.ModelElement))) + return (Type)uc.ResultItem.Tag; + return null; + } + + public void SammExtensionHelperUpdateJson(Aas.IExtension se, Type sammType, Samm.ModelElement sammInst) + { + // trivial + if (se == null || sammType == null || sammInst == null) + return; + + // do a full fledged, carefull serialization + string json = ""; + try + { + var settings = new JsonSerializerSettings + { + // SerializationBinder = new DisplayNameSerializationBinder(new[] { typeof(AasEventMsgEnvelope) }), + NullValueHandling = NullValueHandling.Ignore, + ReferenceLoopHandling = ReferenceLoopHandling.Serialize, + TypeNameHandling = TypeNameHandling.None, + Formatting = Formatting.Indented + }; + settings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter()); + //settings.Converters.Add(new AdminShellConverters.AdaptiveAasIClassConverter( + // AdminShellConverters.AdaptiveAasIClassConverter.ConversionMode.AasCore)); + json = JsonConvert.SerializeObject(sammInst, sammType, settings); + } + catch (Exception ex) + { + LogInternally.That.SilentlyIgnoredError(ex); + } + + // save this to the extension + se.Value = json; + se.ValueType = DataTypeDefXsd.String; + } + + public void SammExtensionHelperAddSammReference( + Aas.Environment env, AnyUiStackPanel stack, string caption, + Samm.ModelElement sammInst, + Aas.IReferable relatedReferable, + Samm.SammReference sr, + Action setValue, + bool noFirstColumnWidth = false) + { + AddKeyValueExRef( + stack, "" + caption, sammInst, + value: "" + sr?.Value, null, repo, + setValue: v => + { + setValue?.Invoke(new Samm.SammReference((string)v)); + return new AnyUiLambdaActionNone(); + }, + noFirstColumnWidth: noFirstColumnWidth, + auxButtonTitles: new[] { "Existing ..", "New ..", "Jump" }, + auxButtonToolTips: new[] { + "Select existing ConceptDescription.", + "Create a new ConceptDescription for SAMM use.", + "Jump to ConceptDescription with given Id." + }, + auxButtonLambda: (i) => + { + if (i == 0) + { + var k2 = SmartSelectAasEntityKeys( + packages, + PackageCentral.PackageCentral.Selector.MainAuxFileRepo, + "ConceptDescription"); + if (k2 != null && k2.Count >= 1) + { + setValue?.Invoke(new Samm.SammReference("" + k2[0].Value)); + return new AnyUiLambdaActionRedrawEntity(); + } + } + + if (i == 1) + { + // select type + var sammTypeToCreate = SammExtensionHelperSelectSammType(Samm.Constants.AddableElements); + if (sammTypeToCreate == null) + return new AnyUiLambdaActionNone(); + + // select name + var newUri = Samm.Util.ShortenUri( + "" + (relatedReferable as Aas.IIdentifiable)?.Id); + var uc = new AnyUiDialogueDataTextBox( + "New Id for SAMM element:", + symbol: AnyUiMessageBoxImage.Question, + maxWidth: 1400, + text: "" + newUri); + if (!this.context.StartFlyoverModal(uc)) + return new AnyUiLambdaActionNone(); + newUri = uc.Text; + + // select idShort + var newIdShort = Samm.Util.LastWordOfUri(newUri); + var uc2 = new AnyUiDialogueDataTextBox( + "New idShort for SAMM element:", + symbol: AnyUiMessageBoxImage.Question, + maxWidth: 1400, + text: "" + newIdShort); + if (!this.context.StartFlyoverModal(uc2)) + return new AnyUiLambdaActionNone(); + newIdShort = uc2.Text; + if (newIdShort.HasContent() != true) + { + newIdShort = env?.ConceptDescriptions? + .IterateIdShortTemplateToBeUnique("samm{0:0000}", 9999); + } + + // make sure, the name is a new, valid Id for CDs + if (newUri?.HasContent() != true || + null != env?.FindConceptDescriptionById(newUri)) + { + Log.Singleton.Error("Invalid (used?) Id for a new ConceptDescriptin. Aborting!"); + return new AnyUiLambdaActionNone(); + } + + // add the new name to the current element + setValue?.Invoke(new Samm.SammReference(newUri)); + + // now create a new CD for the new SAMM element + var newCD = new Aas.ConceptDescription( + id: newUri, + idShort: newIdShort); + + // create new SAMM element + var newSamm = Activator.CreateInstance( + sammTypeToCreate, new object[] { }) as Samm.ModelElement; + + var newSammSsd = newSamm as Samm.ISammSelfDescription; + + var newSammExt = new Aas.Extension( + name: "" + newSammSsd?.GetSelfName(), + semanticId: new Aas.Reference(ReferenceTypes.ExternalReference, + (new[] { new Aas.Key(KeyTypes.GlobalReference, + newSammSsd.GetSelfUrn()) }) + .Cast().ToList()), + value: ""); + newCD.Extensions = new List { newSammExt }; + + // fill with empty data content for SAMM + SammExtensionHelperUpdateJson(newSammExt, sammTypeToCreate, newSamm); + + // save CD + env?.ConceptDescriptions?.Add(newCD); + + // now, jump to this new CD + return new AnyUiLambdaActionRedrawAllElements(nextFocus: newCD, isExpanded: true); + } + + if (i == 2 && sr?.Value?.HasContent() == true) + { + return new AnyUiLambdaActionNavigateTo( + new Aas.Reference( + Aas.ReferenceTypes.ModelReference, + new Aas.IKey[] { + new Aas.Key(KeyTypes.ConceptDescription, sr.Value) + }.ToList())); + } + + return new AnyUiLambdaActionNone(); + }); + } + + /// + /// Shall provide rather quick access to information .. + /// + public static Type CheckReferableForSammExtensionType(Aas.IReferable rf) + { + // access + if (rf?.Extensions == null) + return null; + + // find any? + foreach (var se in rf.Extensions) + { + var sammType = Samm.Util.GetTypeFromUrn(Samm.Util.GetSammUrn(se)); + if (sammType != null) + return sammType; + } + + // no? + return null; + } + + public static IEnumerable CheckReferableForSammElements(Aas.IReferable rf) + { + // access + if (rf?.Extensions == null) + yield break; + + // find any? + foreach (var se in rf.Extensions) + { + // get type + var sammType = Samm.Util.GetTypeFromUrn(Samm.Util.GetSammUrn(se)); + if (sammType == null) + continue; + + // get instance data + ModelElement sammInst = null; + + // try to de-serializa extension value + try + { + if (se.Value != null) + sammInst = JsonConvert.DeserializeObject(se.Value, sammType) as ModelElement; + } + catch (Exception ex) + { + LogInternally.That.SilentlyIgnoredError(ex); + sammInst = null; + } + + if (sammInst == null) + continue; + + // give back + yield return sammInst; + } + } + + /// + /// Shall provide rather quick access to information .. + /// + /// Null, if not a SAMM model element + public static string CheckReferableForSammExtensionTypeName(Type sammType) + { + return Samm.Util.GetNameFromSammType(sammType); + } + public void DisplayOrEditEntitySammExtensions( Aas.Environment env, AnyUiStackPanel stack, List sammExtension, @@ -2451,10 +2700,10 @@ public void DisplayOrEditEntitySammExtensions( stack, "Spec. records:", repo: repo, superMenu: superMenu, ticketMenu: new AasxMenu() - .AddAction("add-quantifiable", "Add Quantifiable", - "A value which can be quantified and may have a unit.") - .AddAction("add-measurement", "Add Measurement", - "A measurement is a numeric value with an associated unit and quantity kind.") + .AddAction("add-aspect", "Add Aspect", + "Add single top level of any SAMM aspect model.") + .AddAction("add-property", "Add Property", + "Add a named value element to the aspect or its sub-entities.") .AddAction("auto-enumeration", "Add Enumeration", "An enumeration represents a list of possible values.") .AddAction("auto-collection", "Add Collection", @@ -2468,14 +2717,14 @@ public void DisplayOrEditEntitySammExtensions( "Deletes last extension."), ticketAction: (buttonNdx, ticket) => { - Samm.Characteristic newChar = null; + Samm.ModelElement newChar = null; switch (buttonNdx) { case 0: - newChar = new Samm.Quantifiable(); + newChar = new Samm.Aspect(); break; case 1: - newChar = new Samm.Measurement(); + newChar = new Samm.Property(); break; case 2: newChar = new Samm.Enumeration(); @@ -2490,22 +2739,14 @@ public void DisplayOrEditEntitySammExtensions( if (buttonNdx == 5) { - // add other - var fol = new List(); - foreach (var stp in Samm.Constants.AddableCharacteristic) - fol.Add(new AnyUiDialogueListItem("" + stp.Name, stp)); - - // prompt for this list - var uc = new AnyUiDialogueDataSelectFromList( - caption: "Select SAMM element to add .."); - uc.ListOfItems = fol; - this.context.StartFlyoverModal(uc); - if (uc.Result && uc.ResultItem != null && uc.ResultItem.Tag != null && - ((Type)uc.ResultItem.Tag).IsAssignableTo(typeof(Samm.Characteristic))) + // select + var sammTypeToCreate = SammExtensionHelperSelectSammType(Samm.Constants.AddableElements); + + if (sammTypeToCreate != null) { // to which? newChar = Activator.CreateInstance( - (Type)uc.ResultItem.Tag, new object[] { }) as Samm.Characteristic; + sammTypeToCreate, new object[] { }) as Samm.ModelElement; } } @@ -2535,6 +2776,8 @@ public void DisplayOrEditEntitySammExtensions( // now use the normal mechanism to deal with editMode or not .. if (sammExtension != null && sammExtension.Count > 0) { + var numSammExt = 0; + for (int i = 0; i < sammExtension.Count; i++) { // get type @@ -2542,15 +2785,53 @@ public void DisplayOrEditEntitySammExtensions( var sammType = Samm.Util.GetTypeFromUrn(Samm.Util.GetSammUrn(se)); if (sammType == null) { - stack.Add(new AnyUiLabel() { Content = "(unknown SAMM type)" }); continue; } + // more then one? + this.AddHintBubble( + stack, hintMode, + new[] { + new HintCheck( + () => numSammExt > 0, + "Only one SAMM extension per ConceptDescription allowed!", + breakIfTrue: true)}); + // indicate - this.AddGroup(stack, $"SAMM extension [{i+1}]: {sammType.Name}", levelColors.SubSection); + numSammExt++; + + AnyUiFrameworkElement iconElem = null; + var ri = Samm.Constants.GetRenderInfo(sammType); + if (ri != null) + { + iconElem = new AnyUiBorder() + { + Background = new AnyUiBrush(ri.Background), + BorderBrush = new AnyUiBrush(ri.Foreground), + BorderThickness = new AnyUiThickness(2.0f), + MinHeight = 25, + MinWidth = 25, + Child = new AnyUiTextBlock() + { + Text = "" + ri.Abbreviation, + HorizontalAlignment = AnyUiHorizontalAlignment.Center, + VerticalAlignment = AnyUiVerticalAlignment.Center, + Foreground = new AnyUiBrush(ri.Foreground), + Background = AnyUi.AnyUiBrushes.Transparent, + }, + HorizontalAlignment = AnyUiHorizontalAlignment.Center, + VerticalAlignment = AnyUiVerticalAlignment.Center, + Margin = new AnyUiThickness(0, 0, 10, 0), + SkipForTarget = AnyUiTargetPlatform.Browser + }; + } + + this.AddGroup(stack, $"SAMM extension [{i + 1}]: {sammType.Name}", + levelColors.SubSection.Bg, levelColors.SubSection.Fg, + iconElement: iconElem); - // get instance data - object sammInst = null; + // get instance data + object sammInst = null; if (false) { // Note: right now, create fresh instance @@ -2584,30 +2865,7 @@ public void DisplayOrEditEntitySammExtensions( // editing actions need to asynchronously write back values Action WriteSammInstBack = () => { - // do a full fledged, carefull serialization - string json = ""; - try - { - var settings = new JsonSerializerSettings - { - // SerializationBinder = new DisplayNameSerializationBinder(new[] { typeof(AasEventMsgEnvelope) }), - NullValueHandling = NullValueHandling.Ignore, - ReferenceLoopHandling = ReferenceLoopHandling.Serialize, - TypeNameHandling = TypeNameHandling.None, - Formatting = Formatting.Indented - }; - settings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter()); - //settings.Converters.Add(new AdminShellConverters.AdaptiveAasIClassConverter( - // AdminShellConverters.AdaptiveAasIClassConverter.ConversionMode.AasCore)); - json = JsonConvert.SerializeObject(sammInst, sammType, settings); - } catch (Exception ex) - { - LogInternally.That.SilentlyIgnoredError(ex); - } - - // save this to the extension - se.Value = json; - se.ValueType = DataTypeDefXsd.String; + SammExtensionHelperUpdateJson(se, sammType, sammInst as Samm.ModelElement); }; // okay, try to build up a edit field by reflection @@ -2624,8 +2882,117 @@ public void DisplayOrEditEntitySammExtensions( // List of SammReference? if (pii.PropertyType.IsAssignableTo(typeof(List))) { - ; - } + var lsr = (List)pii.GetValue(sammInst); + + Action> lambdaSetValue = (v) => + { + pii.SetValue(sammInst, v); + WriteSammInstBack(); + }; + + if (this.SafeguardAccess(stack, repo, lsr, "" + pii.Name + ":", + "Create data element!", + v => { + lambdaSetValue(new List()); + return new AnyUiLambdaActionRedrawEntity(); + })) + { + // Head + var sg = this.AddSubGrid(stack, "" + pii.Name + ":", + rows: 1 + lsr.Count, cols: 2, + minWidthFirstCol: GetWidth(FirstColumnWidth.Standard), + colWidths: new[] { "*", "#" }); + + AnyUiUIElement.RegisterControl( + AddSmallButtonTo(sg, 0, 1, + margin: new AnyUiThickness(2, 2, 2, 2), + padding: new AnyUiThickness(5, 0, 5, 0), + content: "\u2795"), + (v) => + { + lsr.Add(new Samm.SammReference()); + lambdaSetValue(lsr); + return new AnyUiLambdaActionRedrawEntity(); + }); + + // single references + for (int lsri = 0; lsri < lsr.Count; lsri++) + { + // remember lambda safe + var theLsri = lsri; + + // Stack in the 1st column + var sp1 = AddSmallStackPanelTo(sg, 1 + lsri, 0); + SammExtensionHelperAddSammReference( + env, sp1, $"[{lsri}]", + (Samm.ModelElement)sammInst, relatedReferable, + lsr[lsri], + noFirstColumnWidth: true, + setValue: (v) => { + lsr[theLsri] = v; + lambdaSetValue(lsr); + }); + + if (false) + { + // remove button + AnyUiUIElement.RegisterControl( + AddSmallButtonTo(sg, 1 + lsri, 1, + margin: new AnyUiThickness(2, 2, 2, 2), + padding: new AnyUiThickness(5, 0, 5, 0), + content: "-"), + (v) => + { + lsr.RemoveAt(theLsri); + lambdaSetValue(lsr); + return new AnyUiLambdaActionRedrawEntity(); + }); + } + else + { + // button [hamburger] + AddSmallContextMenuItemTo( + sg, 1 + lsri, 1, + "\u22ee", + repo, new[] { + "\u2702", "Delete", + "\u25b2", "Move Up", + "\u25bc", "Move Down", + }, + margin: new AnyUiThickness(2, 2, 2, 2), + padding: new AnyUiThickness(5, 0, 5, 0), + menuItemLambda: (o) => + { + var action = false; + + if (o is int ti) + switch (ti) + { + case 0: + lsr.RemoveAt(theLsri); + action = true; + break; + case 1: + MoveElementInListUpwards(lsr, lsr[theLsri]); + action = true; + break; + case 2: + MoveElementInListDownwards(lsr, lsr[theLsri]); + action = true; + break; + } + + if (action) + { + lambdaSetValue(lsr); + return new AnyUiLambdaActionRedrawEntity(); + } + return new AnyUiLambdaActionNone(); + }); + } + } + } + } // List of Constraint? if (pii.PropertyType.IsAssignableTo(typeof(List))) @@ -2636,52 +3003,73 @@ public void DisplayOrEditEntitySammExtensions( // single SammReference? if (pii.PropertyType.IsAssignableTo(typeof(Samm.SammReference))) { - // var tempKeys = new Aas.IKey[] { - // new Aas.Key(KeyTypes.ConceptDescription, (string)pii.GetValue(sammInst)) - // }.ToList(); - - //AddKeyListKeys( - // stack, "" + pii.Name, - // keys: tempKeys, - // repo: repo, - // packages: packages, - // selector: PackageCentral.PackageCentral.Selector.MainAuxFileRepo, - // addExistingEntities: "ConceptDescription", - // emitCustomEvent: (o) => - // { - // var valBack = ""; - // if (tempKeys.Count >= 1) - // valBack = tempKeys[0].Value; - // pii.SetValue(sammInst, valBack); - // WriteSammInstBack(); - // }); - var sr = (Samm.SammReference)pii.GetValue(sammInst); - AddKeyValueExRef( - stack, "" + pii.Name, sammInst, - value: "" + sr, null, repo, - setValue: v => - { - pii.SetValue(sammInst, new Samm.SammReference((string) v)); + SammExtensionHelperAddSammReference( + env, stack, "" + pii.Name, (Samm.ModelElement) sammInst, relatedReferable, + sr, + setValue: (v) => { + pii.SetValue(sammInst, v); WriteSammInstBack(); - return new AnyUiLambdaActionNone(); - }, - auxButtonTitles: new[] { "Existing ..", "Jump" }, - auxButtonToolTips: new[] { - "Select existing ConceptDescription", - "Jump to ConceptDescription with id" - }, - auxButtonLambda: (i) => - { - return new AnyUiLambdaActionNone(); }); } // List of string? if (pii.PropertyType.IsAssignableTo(typeof(List))) { - ; + var ls = (List)pii.GetValue(sammInst); + if (ls == null) + { + Log.Singleton.Error("Internal error in SAMM element. Aborting."); + continue; + } + + var sg = this.AddSubGrid(stack, "" + pii.Name + ":", + rows: 1 + ls.Count, cols: 2, + minWidthFirstCol: GetWidth(FirstColumnWidth.Standard), + colWidths: new[] { "*", "#" }); + + AnyUiUIElement.RegisterControl( + AddSmallButtonTo(sg, 0, 1, + margin: new AnyUiThickness(2, 2, 2, 2), + padding: new AnyUiThickness(5, 0, 5, 0), + content: "Add blank"), + (v) => + { + ls.Add(""); + pii.SetValue(sammInst, ls); + WriteSammInstBack(); + return new AnyUiLambdaActionRedrawEntity(); + }); + + for (int lsi=0; lsi + { + ls[theLsi] = (string)v; + pii.SetValue(sammInst, ls); + WriteSammInstBack(); + return new AnyUiLambdaActionRedrawEntity(); + }); + + AnyUiUIElement.RegisterControl( + AddSmallButtonTo(sg, 1 + lsi, 1, + margin: new AnyUiThickness(2, 2, 2, 2), + padding: new AnyUiThickness(5, 0, 5, 0), + content: "-"), + (v) => + { + ls.RemoveAt(theLsi); + pii.SetValue(sammInst, ls); + WriteSammInstBack(); + return new AnyUiLambdaActionRedrawEntity(); + }); + } } // single string? diff --git a/src/AasxPackageLogic/VisualAasxElements.cs b/src/AasxPackageLogic/VisualAasxElements.cs index 6339c216e..1070d7ea0 100644 --- a/src/AasxPackageLogic/VisualAasxElements.cs +++ b/src/AasxPackageLogic/VisualAasxElements.cs @@ -12,6 +12,7 @@ This source code may use other Open Source software components (see LICENSE.txt) using AdminShellNS; using AnyUi; using Extensions; +using Namotion.Reflection; using System; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -19,6 +20,7 @@ This source code may use other Open Source software components (see LICENSE.txt) using System.Globalization; using System.Linq; using Aas = AasCore.Aas3_0; +using Samm = AasCore.Samm2_2_0; // ReSharper disable VirtualMemberCallInConstructor @@ -561,7 +563,7 @@ public enum ItemType "Environment", "AdministrationShells", "ConceptDescriptions", "Package", "OrphanSubmodels", "AllSubmodels", "SupplementalFiles", "Value.Aas.Reference", "Empty", "Dummy" }; - public enum ConceptDescSortOrder { None = 0, IdShort, Id, BySubmodel, BySme } + public enum ConceptDescSortOrder { None = 0, IdShort, Id, BySubmodel, BySme, Structured } public string thePackageSourceFn; public AdminShellPackageEnv thePackage = null; @@ -1229,7 +1231,27 @@ public override void RefreshFromMainData() this.Info += " = " + vl; } - //TODO (jtikekar, 0000-00-00): support DataSpecificationPhysicalUnit + // SAMM? + var sammType = DispEditHelperModules.CheckReferableForSammExtensionType(theCD); + var sammName = DispEditHelperModules.CheckReferableForSammExtensionTypeName(sammType); + if (sammName?.HasContent() == true) + { + // completely reformat the Caption + this.Caption = $"\"{"" + theCD.IdShort}\" \uff5f{sammName}\uff60 {"" + theCD.Id}"; + + // do model element colors? + var ri = Samm.Constants.GetRenderInfo(sammType); + if (ri != null) + { + this.TagString = "" + ri.Abbreviation; + this.Border = new AnyUiColor(ri.Background); + this.Background = new AnyUiColor(Samm.Constants.RenderBackground); + this.TagBg = new AnyUiColor(ri.Background); + this.TagFg = new AnyUiColor(ri.Foreground); + } + } + + //TODO (jtikekar, 0000-00-00): support DataSpecificationPhysicalUnit #if SupportDataSpecificationPhysicalUnit var dspu = theCD.GetPhysicalUnit(); if (dspu != null) @@ -1242,7 +1264,7 @@ public override void RefreshFromMainData() } #endif - } + } } // sorting @@ -1502,6 +1524,9 @@ public class ListOfVisualElement : ObservableCollection private VisualElementEnvironmentItem tiPackage = null, tiEnv = null, tiShells = null, tiCDs = null; + private MultiValueDictionary _idToReferable = + new MultiValueDictionary(); + private MultiValueDictionary _cdReferred = new MultiValueDictionary(); @@ -1870,6 +1895,45 @@ private void GenerateInnerElementsForConceptDescriptions( if (env == null || tiCDs == null || root == null) return; + // + // try to approach structures first + // + + if (tiCDs.CdSortOrder == VisualElementEnvironmentItem.ConceptDescSortOrder.Structured) + { + // recursive lambda!! + Action lambdaAddRecurse = null; + lambdaAddRecurse = (tiParent, cd) => + { + // add + var tiCD = GenerateVisualElementsForSingleCD(cache, env, cd, tiParent); + _cdReferred.Add(cd, tiCD); + + // look for descendants + foreach (var me in DispEditHelperModules.CheckReferableForSammElements(cd)) + if (me is Samm.ISammStructureModel ssm) + foreach (var sr in ssm.DescendOnce()) + { + // try to find SAMM elements + if (sr?.Value?.HasContent() != true || !_idToReferable.ContainsKey(sr.Value)) + continue; + + foreach (var y in _idToReferable[sr.Value]) + if (y is Aas.IConceptDescription foundCD) + lambdaAddRecurse(tiCD, foundCD); + } + }; + + // visit top nodes to start the lambda + foreach (var cd in env.ConceptDescriptions) + foreach (var me in DispEditHelperModules.CheckReferableForSammElements(cd)) + if (me is Samm.ISammStructureModel ssm && ssm.IsTopElement()) + { + // add && recurse + lambdaAddRecurse(tiCDs, cd); + } + } + // // create // @@ -1885,7 +1949,11 @@ private void GenerateInnerElementsForConceptDescriptions( && _cdToSm.ContainsKey(cd)) continue; - GenerateVisualElementsForSingleCD(cache, env, cd, tiCDs); + if (tiCDs.CdSortOrder == VisualElementEnvironmentItem.ConceptDescSortOrder.Structured + && _cdReferred.ContainsKey(cd)) + continue; + + GenerateVisualElementsForSingleCD(cache, env, cd, tiCDs); } // @@ -1938,19 +2006,33 @@ public void AddVisualElementsFromShellEnv( OptionLazyLoadingFirst = lazyLoadingFirst; // quickly connect the Identifiables to the environment + // and index them in order to quickly look them up { - foreach (var aas in env.AssetAdministrationShells) + _idToReferable.Clear(); + _cdReferred.Clear(); + _cdToSm.Clear(); + + foreach (var aas in env.AssetAdministrationShells) if (aas != null) + { aas.Parent = env; + _idToReferable.Add(aas.Id, aas); + } foreach (var sm in env.Submodels) if (sm != null) + { sm.Parent = env; + _idToReferable.Add(sm.Id, sm); + } - foreach (var cd in env.ConceptDescriptions) + foreach (var cd in env.ConceptDescriptions) if (cd != null) + { cd.Parent = env; - } + _idToReferable.Add(cd.Id, cd); + } + } // many operations try diff --git a/src/AasxWpfControlLibrary/DiplayVisualAasxElements.xaml.cs b/src/AasxWpfControlLibrary/DiplayVisualAasxElements.xaml.cs index a370c194d..29462827a 100644 --- a/src/AasxWpfControlLibrary/DiplayVisualAasxElements.xaml.cs +++ b/src/AasxWpfControlLibrary/DiplayVisualAasxElements.xaml.cs @@ -475,8 +475,6 @@ public bool TrySelectVisualElement(VisualElementGeneric ve, bool? wishExpanded) if (ve == null) return false; - // select (but no callback!) - SelectSingleVisualElement(ve, preventFireItem: true); if (wishExpanded == true) { @@ -490,7 +488,11 @@ public bool TrySelectVisualElement(VisualElementGeneric ve, bool? wishExpanded) } if (wishExpanded == false) ve.IsExpanded = false; - Woodoo(ve); + + // select (but no callback!) + SelectSingleVisualElement(ve, preventFireItem: true); + + Woodoo(ve); this.Refresh(); diff --git a/src/AnyUi/AnyUiSmallWidgetToolkit.cs b/src/AnyUi/AnyUiSmallWidgetToolkit.cs index 79c7c76c7..32968992d 100644 --- a/src/AnyUi/AnyUiSmallWidgetToolkit.cs +++ b/src/AnyUi/AnyUiSmallWidgetToolkit.cs @@ -620,9 +620,10 @@ public void AddGroup(AnyUiStackPanel view, string name, AnyUiBrushTuple colors, public void AddGroup(AnyUiStackPanel view, string name, AnyUiBrush background, AnyUiBrush foreground, bool requestAuxButton = false, string auxButtonTitle = null, Func auxButtonLambda = null, - string[] auxContextHeader = null, Func auxContextLambda = null) + string[] auxContextHeader = null, Func auxContextLambda = null, + AnyUiFrameworkElement iconElement = null) { - var g = AddSmallGrid(1, 4, new[] { "*", "#", "#", "#" }, margin: new AnyUiThickness(0, 13, 0, 0)); + var g = AddSmallGrid(1, 4, new[] { "#", "*", "#", "#" }, margin: new AnyUiThickness(0, 13, 0, 0)); // manually add label (legacy?) var l = new AnyUiLabel(); @@ -633,16 +634,30 @@ public void AddGroup(AnyUiStackPanel view, string name, AnyUiBrush background, A l.Content = "" + name; l.FontWeight = AnyUiFontWeight.Bold; AnyUiGrid.SetRow(l, 0); - AnyUiGrid.SetColumn(l, 0); + AnyUiGrid.SetColumn(l, 1); g.Children.Add(l); view.Children.Add(g); + // add icon + if (iconElement != null) + { + AnyUiGrid.SetRow(iconElement, 0); + AnyUiGrid.SetColumn(iconElement, 0); + g.Children.Add(iconElement); + l.VerticalContentAlignment = AnyUiVerticalAlignment.Bottom; + } + else + { + // make sure no effect + g.ColumnDefinitions[0].Width = new AnyUiGridLength(0, AnyUiGridUnitType.Pixel); + } + // auxButton if (requestAuxButton && auxButtonTitle != null && auxButtonLambda != null) { AnyUiUIElement.RegisterControl( AddSmallButtonTo( - g, 0, 1, + g, 0, 2, margin: new AnyUiThickness(2, 2, 2, 2), padding: new AnyUiThickness(5, 0, 5, 0), content: auxButtonTitle), @@ -653,7 +668,7 @@ public void AddGroup(AnyUiStackPanel view, string name, AnyUiBrush background, A if (requestAuxButton && auxContextHeader != null && auxContextLambda != null) { AddSmallContextMenuItemTo( - g, 0, 2, + g, 0, 3, "\u22ee", auxContextHeader.ToArray(), margin: new AnyUiThickness(2, 2, 2, 2),