diff --git a/src/AasxCsharpLibrary/Extensions/ExtendISubmodelElement.cs b/src/AasxCsharpLibrary/Extensions/ExtendISubmodelElement.cs index 6de3cc85..58c0dab4 100644 --- a/src/AasxCsharpLibrary/Extensions/ExtendISubmodelElement.cs +++ b/src/AasxCsharpLibrary/Extensions/ExtendISubmodelElement.cs @@ -182,7 +182,20 @@ public static IQualifier FindQualifierOfType(this ISubmodelElement submodelEleme } - public static IReference GetModelReference(this ISubmodelElement sme, bool includeParents = true) + public static IEnumerable FindQualifierOfAnyType( + this ISubmodelElement submodelElement, string[] qualifierTypes) + { + if (qualifierTypes == null || qualifierTypes.Length < 1) + yield break; + foreach (var qualifierType in qualifierTypes) + { + var res = FindQualifierOfType(submodelElement, qualifierType); + if (res != null) + yield return res; + } + } + + public static IReference GetModelReference(this ISubmodelElement sme, bool includeParents = true) { // this will be the tail of our chain var keyList = new List(); diff --git a/src/AasxPackageExplorer/options-debug.MIHO.json b/src/AasxPackageExplorer/options-debug.MIHO.json index b7d23843..556718cd 100644 --- a/src/AasxPackageExplorer/options-debug.MIHO.json +++ b/src/AasxPackageExplorer/options-debug.MIHO.json @@ -28,8 +28,9 @@ // "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\\SMT_and_SAMM_Showcase_v01.aasx", - "AasxToLoad": "C:\\HOMI\\Develop\\Aasx\\repo\\Run2_DNP_plus_CD_SM_step_2.aasx", + // "AasxToLoad": "C:\\HOMI\\Develop\\Aasx\\repo\\samm-test\\Run2_DNP_plus_CD_SM_step_2.aasx", // "AasxToLoad": "C:\\HOMI\\Develop\\Aasx\\repo\\Test1.aasx", + "AasxToLoad": "C:\\HOMI\\Develop\\Aasx\\repo\\samm-test\\SMT_and_SAMM_Showcase_v02.aasx", "WindowLeft": 200, "WindowTop": -1, "WindowWidth": 900, diff --git a/src/AasxPackageLogic/DispEditHelperExtensions.cs b/src/AasxPackageLogic/DispEditHelperExtensions.cs index 4966ef6b..c413ec21 100644 --- a/src/AasxPackageLogic/DispEditHelperExtensions.cs +++ b/src/AasxPackageLogic/DispEditHelperExtensions.cs @@ -309,13 +309,13 @@ protected void ExtensionHelperAddEnumField( }); } - public AnyUiLambdaActionBase ExtensionHelperIdfReferenceAction( + public AnyUiLambdaActionBase ExtensionHelperIdfReferenceAction( Aas.Environment env, Aas.IReferable relatedReferable, int actionIndex, - ExtIdfReference sr, - Action setValue, - Func createInstance) + T sr, + Action setValue, + Func createInstance) where T : ExtIdfReference { if (actionIndex == 0) { @@ -343,18 +343,18 @@ public AnyUiLambdaActionBase ExtensionHelperIdfReferenceAction( return new AnyUiLambdaActionNone(); } - public void ExtensionHelperAddIdfReference( + public void ExtensionHelperAddIdfReference( Aas.Environment env, AnyUiStackPanel stack, string caption, ExtensionRecordBase recInst, Aas.IReferable relatedReferable, - ExtIdfReference sr, - Action setValue, - Func createInstance, + T sr, + Action setValue, + Func createInstance, int firstColumnWidth = -1, // -1 = Standard string[] presetList = null, - bool showButtons = true) + bool showButtons = true) where T : ExtIdfReference { var grid = AddSmallGrid(1, 2, colWidths: new[] { "*", "#" }); stack.Add(grid); @@ -386,25 +386,41 @@ public void ExtensionHelperAddIdfReference( setValue: setValue, createInstance: createInstance); }); + + if (sr is ExtIdfCardinalityReference cardrf) + { + // edit + ExtensionHelperAddEnumField( + grid, 0, 1, + "", + typeForEnum: typeof(SmtCardinality), + enumValue: cardrf.Cardinality, + setEnumValue: (o) => + { + cardrf.Cardinality = (SmtCardinality)o; + setValue?.Invoke(cardrf as T); + return new AnyUiLambdaActionNone(); + }); + } } - public void ExtensionHelperAddListOfIdfReference( + public void ExtensionHelperAddListOfIdfReference( Aas.Environment env, AnyUiStackPanel stack, QuickLookupIdentifiable lookupIdf, string caption, ExtensionRecordBase recInst, Aas.IReferable relatedReferable, - List value, - Action> setValue, - Func createInstance) + List value, + Action> setValue, + Func createInstance) where T : ExtIdfReference { this.AddVerticalSpace(stack); if (this.SafeguardAccess(stack, repo, value, "" + caption + ":", "Create data element!", v => { - setValue?.Invoke(new List(new ExtIdfReference[] { createInstance?.Invoke("") })); + setValue?.Invoke(new List(new T[] { createInstance?.Invoke("") })); return new AnyUiLambdaActionRedrawEntity(); })) { @@ -544,6 +560,14 @@ public void ExtensionHelperAddEditFieldsByReflection( var propType = pii.PropertyType; var underlyingType = Nullable.GetUnderlyingType(propType); + // some flags logic + var propFlags = pii.GetCustomAttribute()?.Flags; + if (propFlags != null) + { + if (relatedReferable is Aas.IConceptDescription && propFlags.Contains("SKIPFORCD")) + continue; + } + // make hint lambda Action hintLambda = (hint) => { @@ -682,7 +706,7 @@ public void ExtensionHelperAddEditFieldsByReflection( hintLambda(lidr == null || lidr.Count < 1); // edit - ExtensionHelperAddListOfIdfReference( + ExtensionHelperAddListOfIdfReference( env, stack, lookupIdf, caption: "" + pii.Name, @@ -697,6 +721,31 @@ public void ExtensionHelperAddEditFieldsByReflection( createInstance: (eir) => new ExtIdfReference(eir)); } + // List of SmtIdfCardinalityReference? + if (pii.PropertyType.IsAssignableTo(typeof(List))) + { + // value + var lidr = (List)pii.GetValue(recInst); + + // hint? + hintLambda(lidr == null || lidr.Count < 1); + + // edit + ExtensionHelperAddListOfIdfReference( + env, stack, + lookupIdf, + caption: "" + pii.Name, + recInst, + relatedReferable, + value: lidr, + setValue: (v) => + { + pii.SetValue(recInst, v); + setValue?.Invoke(recInst); + }, + createInstance: (eir) => new ExtIdfCardinalityReference(eir)); + } + // single IdfReference? if (pii.PropertyType.IsAssignableTo(typeof(ExtIdfReference))) { @@ -1367,8 +1416,10 @@ public static SmtAttributeRecord FindSmtQualifiers(Aas.IReferable rf, bool remov // to convert rec = rec ?? new SmtAttributeRecord(); - if (qti.Type == "SMT/Cardinality") - rec.Cardinality = (SmtCardinality) + if (qti.Type == "SMT/Cardinality" + || qti.Type == "Cardinality" + || qti.Type == "Multiplicity") + rec.SmeCardinality = (SmtCardinality) EnumHelper.GetEnumMemberFromValueString(qf.Value); if (qti.Type == "SMT/EitherOr") @@ -1426,6 +1477,20 @@ public static bool ConvertSmtQualifiersToExtension(Aas.IReferable rf) } } + /// + /// Some string based flags to attach to the property + /// + [System.AttributeUsage(System.AttributeTargets.Field | System.AttributeTargets.Property, AllowMultiple = true)] + public class ExtensionFlagsAttribute : System.Attribute + { + public string Flags = ""; + + public ExtensionFlagsAttribute(string flags) + { + Flags = flags; + } + } + /// /// This attribute gives a list of given presets to an field or property. /// in order to avoid cycles @@ -1498,6 +1563,21 @@ public ExtIdfReference(string val = "") } } + /// + /// This class creates a reference to an Identifiable, which "feels" like a string. + /// A cardinality is included, as well + /// + public class ExtIdfCardinalityReference : ExtIdfReference + { + public SmtCardinality Cardinality { get; set; } + + public ExtIdfCardinalityReference(string val = "", SmtCardinality cardinality = SmtCardinality.One) + { + Value = val; + Cardinality = cardinality; + } + } + /// /// Shall be implemented in order to give hints about the /// (hierarchical) structuring of elements @@ -1573,8 +1653,10 @@ IEnumerable IAbstractStructureModel.DescendOnc // attributes [ExtensionHintAttribute("Specifies, how many SubmodelElement instances of this " + - "SMT element are allowed in the actual collection (hierarchy level of the Submodel).")] - public AasSmtQualifiers.SmtCardinality Cardinality { get; set; } = AasSmtQualifiers.SmtCardinality.One; + "SMT element are allowed in the actual collection (hierarchy level of the Submodel). " + + "Note: This attribute is only valid for annotating SMEs, not CDs!")] + [ExtensionFlags("SKIPFORCD")] + public AasSmtQualifiers.SmtCardinality SmeCardinality { get; set; } = AasSmtQualifiers.SmtCardinality.One; [ExtensionHintAttribute("Specifies an id of an equivalence class. " + "Only ids in the range [A-Za-z0-9] are allowed. If multiple SMT elements feature the same equivalency " + @@ -1631,7 +1713,7 @@ IEnumerable IAbstractStructureModel.DescendOnc [ExtensionHintAttribute("\u00ab Experimental \u00bb Specifies a list of concepts, which are " + "organized, hierarchically structured, within this concept. This does not impose a type-like " + "semantic. A concept might be organized by multiple concepts.")] - public List Organizes { get; set; } = null; + public List Organizes { get; set; } = null; // // Init @@ -1653,11 +1735,11 @@ static SmtAttributeRecord() }).ToArray(); } - public string CardinalityShort() + public static string CardinalityShort(SmtCardinality card) { if (CardinalitiesShort == null) return ""; - var i = ((int)this.Cardinality) % CardinalitiesShort.Length; + var i = ((int)card) % CardinalitiesShort.Length; return CardinalitiesShort[i]; } @@ -1665,6 +1747,14 @@ public string CardinalityShort() // Access // + public IEnumerable FindOrganizesForId(string id) + { + if (Organizes != null) + foreach (var org in Organizes) + if (org?.Value != null && org.Value == id) + yield return org; + } + public static bool ConceptSuitableForSubmodelCreate(SmtAttributeRecord smtRec) { // access @@ -1700,6 +1790,7 @@ public static IEnumerable yield return new ConceptOrganizedChildItem() { Cd = cd2, + Card = orgId.Cardinality, SmtRec = smtRec2 }; } @@ -1742,7 +1833,7 @@ public void JoinAttributes(SmtAttributeRecord other) }; // apply - Cardinality = other.Cardinality; + SmeCardinality = other.SmeCardinality; EitherOr = JoinStrings(EitherOr, other.EitherOr, "|"); InitialValue = OverStrings(InitialValue, other.InitialValue); DefaultValue = OverStrings(DefaultValue, other.DefaultValue); @@ -1766,7 +1857,7 @@ public void JoinAttributes(SmtAttributeRecord other) if (other.Organizes != null && other.Organizes.Count > 0) foreach (var oorg in other.Organizes) { - Organizes = Organizes ?? new List(); + Organizes = Organizes ?? new List(); var found = false; foreach (var org in Organizes) if (org?.Value == oorg?.Value) @@ -1949,7 +2040,9 @@ public List PerformAttributeCheck(Aas.IMultiLanguagePrope /// Note: this function needs a lambda for looking up SMT attribute records /// of subordinate elements either by Referable or SemanticId reference. /// - public static List PerformAttributeCheck(List elems, + public static List PerformAttributeCheck( + Aas.IReferable parentRf, + List elems, Func lambdaLookupSmtRec, List inList = null) { @@ -1958,6 +2051,9 @@ public static List PerformAttributeCheck(List(); + // try to access attributes of the parent + var smtParent = lambdaLookupSmtRec?.Invoke(parentRf); + // make two dictionaries on these elements // (to count elemens per semantic id and have SmtAttributes available) var elemPerSemId = new MultiValueDictionary(); @@ -1985,13 +2081,19 @@ public static List PerformAttributeCheck(List 1) + if (card == SmtCardinality.ZeroToOne && els.Count > 1) complain = "[0..1]"; // give out @@ -2105,9 +2207,27 @@ public static bool TakeoverSmOrganizeToCds( if (already) continue; + // try identify cardinality + var card = SmtCardinality.One; + var smeFromDictKey = dict[dictKey].FirstOrDefault() as Aas.ISubmodelElement; + + // access cardinality of the SME by qualifier + var qf = smeFromDictKey?.FindQualifierOfAnyType(new[] { + "SMT/Cardinality", "Cardinality", "Multiplicity" })?.FirstOrDefault(); + if (qf?.Value != null) + card = (SmtCardinality) EnumHelper.GetEnumMemberFromValueString( + qf.Value, valElse: SmtCardinality.One); + + // access cardinality of the SME by SmtAttributeRec? + var smtRecFromSme = DispEditHelperExtensions + .CheckReferableForExtensionRecords(smeFromDictKey)? + .FirstOrDefault(); + if (smtRecFromSme != null) + card = smtRecFromSme.SmeCardinality; + // without further ado, just put it in! - smtRec.Organizes = smtRec.Organizes ?? new List(); - smtRec.Organizes.Add(new ExtIdfReference(dictKey)); + smtRec.Organizes = smtRec.Organizes ?? new List(); + smtRec.Organizes.Add(new ExtIdfCardinalityReference(dictKey, card)); changes = true; } diff --git a/src/AasxPackageLogic/DispEditHelperMiniModules.cs b/src/AasxPackageLogic/DispEditHelperMiniModules.cs index 792bae44..16becb65 100644 --- a/src/AasxPackageLogic/DispEditHelperMiniModules.cs +++ b/src/AasxPackageLogic/DispEditHelperMiniModules.cs @@ -1558,7 +1558,8 @@ public void ValueListHelper( public class ConceptOrganizedChildItem { public Aas.IConceptDescription Cd; - public SmtAttributeRecord SmtRec; + public AasSmtQualifiers.SmtCardinality Card = AasSmtQualifiers.SmtCardinality.One; + public SmtAttributeRecord SmtRec; } /// @@ -1619,7 +1620,7 @@ protected static List DispSmeListAddNewCheckForSmtItem res.Add(new AnyUiDialogueDataGridRow() { // Text = $"{cd.IdShort} (Submodel) {cd.Id}", - Cells = (new[] { "-", cand.SmtRec.CardinalityShort(), "SM", + Cells = (new[] { "-", SmtAttributeRecord.CardinalityShort(cand.Card), "SM", cand.Cd.IdShort, cand.Cd.Id }).ToList(), Tag = new DispSmeListAddNewSmtItemRecord() { @@ -1636,7 +1637,7 @@ protected static List DispSmeListAddNewCheckForSmtItem res.Add(new AnyUiDialogueDataGridRow() { // Text = $"{cd.IdShort} ({smet.ToString()}) {cd.Id}", - Cells = (new[] { "-", cand.SmtRec.CardinalityShort(), + Cells = (new[] { "-", SmtAttributeRecord.CardinalityShort(cand.Card), ExtendISubmodelElement.ToString(smet), cand.Cd.IdShort, cand.Cd.Id }).ToList(), Tag = new DispSmeListAddNewSmtItemRecord() diff --git a/src/AasxPackageLogic/DispEditHelperModules.cs b/src/AasxPackageLogic/DispEditHelperModules.cs index 21b51e79..23ebdd6f 100644 --- a/src/AasxPackageLogic/DispEditHelperModules.cs +++ b/src/AasxPackageLogic/DispEditHelperModules.cs @@ -2510,11 +2510,18 @@ public List DisplayOrEditEntityCheckValueEvalItems( }; if (rf is Aas.ISubmodel sm) - checkItems = SmtAttributeRecord.PerformAttributeCheck(sm.SubmodelElements, inList: checkItems, + checkItems = SmtAttributeRecord.PerformAttributeCheck( + sm, sm.SubmodelElements, inList: checkItems, lambdaLookupSmtRec: lambdaLookupSmtRec); if (rf is Aas.ISubmodelElementCollection smc) - checkItems = SmtAttributeRecord.PerformAttributeCheck(smc.Value, inList: checkItems, + checkItems = SmtAttributeRecord.PerformAttributeCheck( + smc, smc.Value, inList: checkItems, + lambdaLookupSmtRec: lambdaLookupSmtRec); + + if (rf is Aas.ISubmodelElementList sml) + checkItems = SmtAttributeRecord.PerformAttributeCheck( + sml, sml.Value, inList: checkItems, lambdaLookupSmtRec: lambdaLookupSmtRec); // perform the check on factual record of this element diff --git a/src/AasxPackageLogic/DispEditHelperSammModules.cs b/src/AasxPackageLogic/DispEditHelperSammModules.cs index f911a089..b82edd3f 100644 --- a/src/AasxPackageLogic/DispEditHelperSammModules.cs +++ b/src/AasxPackageLogic/DispEditHelperSammModules.cs @@ -2652,9 +2652,9 @@ public static IEnumerable var smtRec = new SmtAttributeRecord(); // poor mens cardinality - smtRec.Cardinality = AasSmtQualifiers.SmtCardinality.One; + smtRec.SmeCardinality = AasSmtQualifiers.SmtCardinality.One; if (child is OptionalSammReference osr && osr.Optional) - smtRec.Cardinality = AasSmtQualifiers.SmtCardinality.ZeroToOne; + smtRec.SmeCardinality = AasSmtQualifiers.SmtCardinality.ZeroToOne; // poor mens initial / default / example value smtRec.ExampleValue = childProp.ExampleValue; @@ -2670,6 +2670,7 @@ public static IEnumerable yield return new DispEditHelperMiniModules.ConceptOrganizedChildItem() { Cd = childCd, + Card = smtRec.SmeCardinality, SmtRec = smtRec }; }