diff --git a/src/AasxPackageExplorer/MainWindow.CommandBindings.cs b/src/AasxPackageExplorer/MainWindow.CommandBindings.cs index 4b3b11fd..bd498475 100644 --- a/src/AasxPackageExplorer/MainWindow.CommandBindings.cs +++ b/src/AasxPackageExplorer/MainWindow.CommandBindings.cs @@ -223,6 +223,9 @@ private async Task CommandBinding_GeneralDispatch( MainMenu?.SetChecked("HintsMenu", hintsMode); } + // trigger re-indexing + TriggerPendingReIndexElements(); + // try to remember current selected data object object currMdo = null; if (DisplayElements.SelectedItem != null) diff --git a/src/AasxPackageExplorer/MainWindow.xaml.cs b/src/AasxPackageExplorer/MainWindow.xaml.cs index f1155383..0a35d7dc 100644 --- a/src/AasxPackageExplorer/MainWindow.xaml.cs +++ b/src/AasxPackageExplorer/MainWindow.xaml.cs @@ -433,6 +433,8 @@ public void UiLoadPackageWithNew( if (!doNotNavigateAfterLoaded) Logic?.UiCheckIfActivateLoadedNavTo(); + TriggerPendingReIndexElements(); + if (indexItems && packItem?.Container?.Env?.AasEnv != null) packItem.Container.SignificantElements = new IndexOfSignificantAasElements(packItem.Container.Env.AasEnv); @@ -1167,6 +1169,9 @@ private async void Window_Loaded(object sender, RoutedEventArgs e) if (Options.Curr.ShowEvents) PanelConcurrentSetVisibleIfRequired(true, targetEvents: true); + // trigger re-index + TriggerPendingReIndexElements(); + // script file to launch? if (Options.Curr.ScriptFn.HasContent()) { @@ -1494,6 +1499,11 @@ private async Task MainTimer_HandleLambdaAction(AnyUiLambdaActionBase lab) UiHandleReRenderAnyUiInEntityPanel("", larrep.Mode, larrep.UseInnerGrid, updateElemsOnly: larrep.UpdateElemsOnly); } + + if (lab is AnyUiLambdaActionReIndexIdentifiables lareii) + { + TriggerPendingReIndexElements(); + } } private async Task MainTimer_HandleEntityPanel() @@ -2492,6 +2502,9 @@ public void MainTaimer_HandleIncomingAasEvents() private DateTime _mainTimer_LastCheckForDiaryEvents; private DateTime _mainTimer_LastCheckForAnimationElements = DateTime.Now; + private bool _mainTimer_PendingReIndexElements = false; + private DateTime _mainTimer_LastCheckForReIndexElements = DateTime.Now; + private async Task MainTimer_Tick(object sender, EventArgs e) { MainTimer_HandleLogMessages(); @@ -2518,14 +2531,34 @@ private async Task MainTimer_Tick(object sender, EventArgs e) PackageCentral.MainItem.Container.SignificantElements); _mainTimer_LastCheckForAnimationElements = DateTime.Now; } - } + + // do re-index? + deltaSecs = (DateTime.Now - _mainTimer_LastCheckForReIndexElements).TotalSeconds; + if (deltaSecs >= 1.0 && _mainTimer_PendingReIndexElements) + { + // dis-engage + _mainTimer_PendingReIndexElements = false; + + // be modest for the time being + PackageCentral.MainItem?.Container?.ReIndexIdentifiables(); + + // Info + Log.Singleton.Info("Re-indexing Identifiables for faster access."); + } + } MainTimer_PeriodicalTaskForSelectedEntity(); MainTaimer_HandleIncomingAasEvents(); DisplayElements.UpdateFromQueuedEvents(); } - private void SetProgressBar() + public void TriggerPendingReIndexElements() + { + _mainTimer_LastCheckForReIndexElements = DateTime.Now; + _mainTimer_PendingReIndexElements = true; + } + + private void SetProgressBar() { SetProgressBar(0.0, ""); } diff --git a/src/AasxPackageLogic/DispEditHelperEntities.cs b/src/AasxPackageLogic/DispEditHelperEntities.cs index 51e42ffe..21db04f8 100644 --- a/src/AasxPackageLogic/DispEditHelperEntities.cs +++ b/src/AasxPackageLogic/DispEditHelperEntities.cs @@ -2385,7 +2385,7 @@ public void DisplayOrEditAasEntityConceptDescription( } else { - lambdaRf(false); + lambdaRf(true); lambdaIdf(); lambdaIsCaseOf(); lambdaEDS(false); diff --git a/src/AasxPackageLogic/DispEditHelperModules.cs b/src/AasxPackageLogic/DispEditHelperModules.cs index 368c23b3..5dfabf6b 100644 --- a/src/AasxPackageLogic/DispEditHelperModules.cs +++ b/src/AasxPackageLogic/DispEditHelperModules.cs @@ -2475,7 +2475,7 @@ public List DisplayOrEditEntityCheckValueEvalItems( if (rf == null) return checkItems; - // what to check + // try gain information from the given referable itself var rec = CheckReferableForExtensionRecords(rf).FirstOrDefault(); if (rec == null) { @@ -2483,6 +2483,24 @@ public List DisplayOrEditEntityCheckValueEvalItems( rec = AasSmtQualifiers.FindSmtQualifiers(rf, removeQualifers: false); } + // if not, can access semanticId -> ConcepTdescription? + if (rec == null && rf is Aas.IHasSemantics rfsem + && rfsem.SemanticId?.IsValid() == true + && rfsem.SemanticId.Count() == 1 + && packages != null) + { + // try find + foreach (var x in packages.LookupAllIdent(rfsem.SemanticId.Keys[0].Value)) + if (x.Item2 is Aas.IConceptDescription rfsemCd) + { + var rec2 = CheckReferableForExtensionRecords(rfsemCd).FirstOrDefault(); + if (rec2 == null) + rec2 = AasSmtQualifiers.FindSmtQualifiers(rf, removeQualifers: false); + if (rec2 != null) + rec = rec2; + } + } + // some checks can be done on the static record function, as record entities might // be only on subordinate elements diff --git a/src/AasxPackageLogic/DispEditHelperSammModules.cs b/src/AasxPackageLogic/DispEditHelperSammModules.cs index 50e572ee..f622c747 100644 --- a/src/AasxPackageLogic/DispEditHelperSammModules.cs +++ b/src/AasxPackageLogic/DispEditHelperSammModules.cs @@ -2319,117 +2319,6 @@ public void ExportSammModelFromConceptDescription( } } - /// - /// This class provides a layered access to a list (a IEnumerable) of - /// Aas.IIdentifiable. This access could be provided by each time - /// blindly iterating through the list or by caching the Ids in a - /// dictionary. Result can be access immedeatily or selected by an - /// lambda, so that only portions of the Identifiable are directly available - /// and strongly typed. - /// - /// Subtype of Aas.IIdentifiable - /// Result type, selected by an lambda - public class IdentifiableLookupStore where I : Aas.IIdentifiable where ME : class - { - protected IEnumerable _originalData = null; - - protected Func _lambdaSelectResult = null; - - protected MultiValueDictionary _lookup = null; - - protected bool IsValidForDict() => - _originalData != null && _lambdaSelectResult != null && _lookup != null; - - public void StartDictionaryAccess( - IEnumerable originalData, - Func lambdaSelectResult) - { - // remember - _originalData = originalData; - _lambdaSelectResult = lambdaSelectResult; - - // create the dictionary - _lookup = new MultiValueDictionary(); - if (!IsValidForDict()) - { - _lookup = null; - return; - } - - // populate - foreach (var idf in _originalData) - if (idf?.Id != null) - _lookup.Add(idf.Id, idf); - } - - /// - /// Lookup all elements for id idKey and - /// return the declared Identifiable subtype. - /// - public IEnumerable LookupAllIdent(string idKey) - { - if (idKey == null || !IsValidForDict() || !_lookup.ContainsKey(idKey)) - yield break; - - foreach (var idf in _lookup[idKey]) - yield return idf; - } - - /// - /// Lookup first element for id idKey and - /// return the declared Identifiable subtype. Else: return null - /// - public I LookupFirstIdent(string idKey) - { - return LookupAllIdent(idKey).FirstOrDefault(); - } - - /// - /// Lookup all elements for id idKey and - /// return the result of the given lambda selection. - /// - public IEnumerable LookupAllResult(string idKey) - { - foreach (var idf in LookupAllIdent(idKey)) - { - var res = _lambdaSelectResult?.Invoke(idf); - if (res != null) - yield return res; - } - } - - /// - /// Lookup first element for id idKey and - /// return the result of the given lambda selection. - /// - public ME LookupFirstResult(string idKey) - { - return LookupAllResult(idKey).FirstOrDefault(); - } - - /// - /// Lookup first element for id idKey and - /// return the result of the given lambda selection. - /// Note: just a shortcut to LookupFirstResult() - /// - public ME Lookup(string idKey) - { - return LookupFirstResult(idKey); - } - - public IEnumerable LookupFor(IEnumerable references) - { - // access - if (references == null) - yield break; - - // translare - foreach (var inref in references) - { - yield return LookupFirstResult(inref?.Value); - } - } - } public class SammIdfTuple { @@ -2450,7 +2339,7 @@ public SammIdfTuple( /// /// Dedicated IdentifiableLookupStore for Samm.ModelElements in ConceptDescriptions. /// - public class SammModelElementLookupStore : IdentifiableLookupStore + public class SammModelElementLookupStore : PackageCentral.IdentifiableLookupStore { /// /// Lookup first element for id idKey and @@ -2471,6 +2360,19 @@ public T Lookup(SammReference sr) where T : Samm.ModelElement { return LookupFirstResult(sr?.Value) as T; } + + public IEnumerable LookupFor(IEnumerable references) + { + // access + if (references == null) + yield break; + + // translare + foreach (var inref in references) + { + yield return LookupFirstResult(inref?.Value); + } + } } /// @@ -2625,7 +2527,7 @@ public void CreateSubmodelInstanceFromAspectCD( // create store var store = new SammModelElementLookupStore(); store.StartDictionaryAccess( - env.ConceptDescriptions, + new[] { env.ConceptDescriptions }, lambdaSelectResult: (cd) => { var me = DispEditHelperSammModules.CheckReferableForSammElements(cd).FirstOrDefault(); return new SammIdfTuple(cd, me); diff --git a/src/AasxPackageLogic/MainWindowAnyUiDialogs.cs b/src/AasxPackageLogic/MainWindowAnyUiDialogs.cs index 8d4c7457..c1959ed6 100644 --- a/src/AasxPackageLogic/MainWindowAnyUiDialogs.cs +++ b/src/AasxPackageLogic/MainWindowAnyUiDialogs.cs @@ -180,6 +180,8 @@ public async Task CommandBinding_GeneralDispatchAnyUiDialogs( PackageCentral.MainItem.Container.SignificantElements = new IndexOfSignificantAasElements(PackageCentral.MainItem.Container.Env.AasEnv); + DisplayContext.EmitOutsideAction(new AnyUiLambdaActionReIndexIdentifiables()); + // may be was saved to flush events MainWindow.CheckIfToFlushEvents(); diff --git a/src/AasxPackageLogic/PackageCentral/IdentifiableLookupStore.cs b/src/AasxPackageLogic/PackageCentral/IdentifiableLookupStore.cs new file mode 100644 index 00000000..65b69dda --- /dev/null +++ b/src/AasxPackageLogic/PackageCentral/IdentifiableLookupStore.cs @@ -0,0 +1,118 @@ +/* +Copyright (c) 2018-2023 Festo SE & Co. KG +Author: Michael Hoffmeister + +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 AdminShellNS; +using Extensions; +using System; +using System.Collections.Generic; +using System.Linq; +using Aas = AasCore.Aas3_0; + +namespace AasxPackageLogic.PackageCentral +{ + /// + /// This class provides a layered access to a list (a IEnumerable) of + /// Aas.IIdentifiable. This access could be provided by each time + /// blindly iterating through the list or by caching the Ids in a + /// dictionary. Result can be access immedeatily or selected by an + /// lambda, so that only portions of the Identifiable are directly available + /// and strongly typed. + /// + /// Subtype of Aas.IIdentifiable + /// Result type, selected by an lambda + public class IdentifiableLookupStore where I : Aas.IIdentifiable where ME : class + { + protected IEnumerable[] _originalData = null; + + protected Func _lambdaSelectResult = null; + + protected MultiValueDictionary _lookup = null; + + protected bool IsValidForDict() => + _originalData != null && _lambdaSelectResult != null && _lookup != null; + + public void StartDictionaryAccess( + IEnumerable[] originalData, + Func lambdaSelectResult) + { + // remember + _originalData = originalData; + _lambdaSelectResult = lambdaSelectResult; + + // create the dictionary + _lookup = new MultiValueDictionary(); + if (!IsValidForDict()) + { + _lookup = null; + return; + } + + // populate + foreach (var odItem in _originalData) + foreach (var idf in odItem) + if (idf?.Id != null) + _lookup.Add(idf.Id, idf); + } + + /// + /// Lookup all elements for id idKey and + /// return the declared Identifiable subtype. + /// + public IEnumerable LookupAllIdent(string idKey) + { + if (idKey == null || !IsValidForDict() || !_lookup.ContainsKey(idKey)) + yield break; + + foreach (var idf in _lookup[idKey]) + yield return idf; + } + + /// + /// Lookup first element for id idKey and + /// return the declared Identifiable subtype. Else: return null + /// + public I LookupFirstIdent(string idKey) + { + return LookupAllIdent(idKey).FirstOrDefault(); + } + + /// + /// Lookup all elements for id idKey and + /// return the result of the given lambda selection. + /// + public IEnumerable LookupAllResult(string idKey) + { + foreach (var idf in LookupAllIdent(idKey)) + { + var res = _lambdaSelectResult?.Invoke(idf); + if (res != null) + yield return res; + } + } + + /// + /// Lookup first element for id idKey and + /// return the result of the given lambda selection. + /// + public ME LookupFirstResult(string idKey) + { + return LookupAllResult(idKey).FirstOrDefault(); + } + + /// + /// Lookup first element for id idKey and + /// return the result of the given lambda selection. + /// Note: just a shortcut to LookupFirstResult() + /// + public ME Lookup(string idKey) + { + return LookupFirstResult(idKey); + } + } +} diff --git a/src/AasxPackageLogic/PackageCentral/PackageCentral.cs b/src/AasxPackageLogic/PackageCentral/PackageCentral.cs index 3dadc148..ae588500 100644 --- a/src/AasxPackageLogic/PackageCentral/PackageCentral.cs +++ b/src/AasxPackageLogic/PackageCentral/PackageCentral.cs @@ -12,6 +12,7 @@ This source code may use other Open Source software components (see LICENSE.txt) using System; using System.Collections.Generic; using System.Threading.Tasks; +using Aas = AasCore.Aas3_0; namespace AasxPackageLogic.PackageCentral { @@ -322,10 +323,22 @@ public IEnumerable GetAllPackageEnv(Func> LookupAllIdent(string idKey) + { + foreach (var cnt in GetAllContainer()) + if (cnt.IdentifiableLookup != null) + foreach (var idf in cnt.IdentifiableLookup.LookupAllIdent(idKey)) + yield return new Tuple(cnt, idf); + } + + // + // Event management + // + + private PackageConnectorEventStore _eventStore = null; // replaced by store within AasEventCollectionViewer // reason: update to ObservableCollection needs to be done in DispatcherThread public PackageConnectorEventStore EventStore { get { return _eventStore; } } diff --git a/src/AasxPackageLogic/PackageCentral/PackageContainerBase.cs b/src/AasxPackageLogic/PackageCentral/PackageContainerBase.cs index a8d6f599..34d7977a 100644 --- a/src/AasxPackageLogic/PackageCentral/PackageContainerBase.cs +++ b/src/AasxPackageLogic/PackageCentral/PackageContainerBase.cs @@ -235,15 +235,24 @@ public enum CopyMode { None = 0, Serialized = 1, BusinessData = 2 } [JsonIgnore] public AdminShellPackageEnv Env = new AdminShellPackageEnv(); + [JsonIgnore] public Format IsFormat = Format.Unknown; /// - /// To be used by the main application to hold important data about the package. + /// To be maintained by the main application to hold important data about the package. /// [JsonIgnore] public IndexOfSignificantAasElements SignificantElements = null; + /// + /// To be maintained by the main application to speed up the process of looking up + /// Identifiables, esp. ConceptDescriptions. + /// + [JsonIgnore] + public IdentifiableLookupStore IdentifiableLookup + = new IdentifiableLookupStore(); + /// /// Limks to the PackageCentral. Only on init. /// @@ -326,7 +335,6 @@ public static Format EvalFormat(string fn) return res; } - [JsonIgnore] public bool IsOpen { get { return Env != null && Env.IsOpen; } } @@ -383,6 +391,23 @@ public IEnumerable GetAllConnectors() yield return conn; } + // + // Identifiable management + // + + public void ReIndexIdentifiables() + { + // emergency + if (IdentifiableLookup == null) + IdentifiableLookup = new IdentifiableLookupStore(); + + // just start again + IdentifiableLookup.StartDictionaryAccess( + new IEnumerable[] { Env?.AasEnv?.AssetAdministrationShells, Env?.AasEnv.Submodels, + Env?.AasEnv?.ConceptDescriptions}, + lambdaSelectResult: (idf) => idf ); + } + // // Event management // diff --git a/src/AnyUi/AnyUiBase.cs b/src/AnyUi/AnyUiBase.cs index a97d857e..2aa22b0c 100644 --- a/src/AnyUi/AnyUiBase.cs +++ b/src/AnyUi/AnyUiBase.cs @@ -563,11 +563,16 @@ public class AnyUiLambdaActionRedrawAllElementsBase : AnyUiLambdaActionBase public bool RedrawCurrentEntity = false; } - /// - /// This class is the base class for event handlers, which can attached to special - /// events of Any UI controls - /// - public class AnyUiSpecialActionBase + /// + /// Request to re-index all Identifiables. + /// + public class AnyUiLambdaActionReIndexIdentifiables : AnyUiLambdaActionBase { } + + /// + /// This class is the base class for event handlers, which can attached to special + /// events of Any UI controls + /// + public class AnyUiSpecialActionBase { }