From 5f883e6beec15f9d1e669eedd60eff4592a4b3ae Mon Sep 17 00:00:00 2001 From: Dimitrie Stefanescu Date: Sat, 8 Jun 2024 17:35:36 +0100 Subject: [PATCH] Dim/dui3/send caching di (DUI3-79) (#3491) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(dui3): wip send caching di implements rhino * fix(dui3): fixes fatal crash on object deleted and re-send/highlight i distinctly remember fixing this before in dui3 😔 * feat(dui3): di send cache in acad * wip(dui3): di cache revit wip * fix(revit): we seem to be cursed to be unable to do basic boolean logic * feat(dui3): wraps up di caching revit * feat(caching): cleanup * chore(dui3): rename arcgis object builder as others * feat(dui3): implements di cache in arcgis - NOT TESTED * chore(caching): adds comment on nullability * feat(arcgis): makes cache non-nullable * fix(caching): makes cache non-nullable where we know it's going to be non-nullable: specifically, in host app implementations of the root object builder and send bindings * chore(dui3): removes a bunch of dead code and comments * feat(caching): adds a "null" do nothing cache and makes it non-optional in the root object sender * chore(caching): removes unnecessary comment * fix(acgis): registers cache! --- .../Bindings/ArcGISSendBinding.cs | 26 +++---- .../ArcGISConnectorModule.cs | 8 ++- ...tBuilder.cs => ArcGISRootObjectBuilder.cs} | 21 ++++-- .../Bindings/AutocadSendBinding.cs | 29 +++----- .../AutocadConnectorModule.cs | 4 ++ .../HostApp/Extensions/DocumentExtensions.cs | 20 +++++- .../Send/AutocadRootObjectBuilder.cs | 20 ++++-- .../Bindings/BasicConnectorBindingRevit.cs | 2 +- .../{SendBinding.cs => RevitSendBinding.cs} | 71 ++++--------------- .../RevitConnectorModule.cs | 6 +- .../Operations/Send/RevitRootObjectBuilder.cs | 24 +++++-- .../Speckle.Connectors.RevitShared.projitems | 2 +- .../Bindings/RhinoSendBinding.cs | 35 +++------ .../RhinoConnectorModule.cs | 4 ++ .../Operations/Send/RhinoRootObjectBuilder.cs | 19 +++-- .../Models/Card/SenderModelCard.cs | 5 +- .../Caching/ISendConversionCache.cs | 23 ++++++ .../Caching/NullSendConversionCache.cs | 19 +++++ .../Caching/SendConversionCache.cs | 28 ++++++++ .../Operations/RootObjectSender.cs | 7 +- .../Operations/SendInfo.cs | 18 +---- .../SpeckleTopLevelExceptionHandler.cs | 44 ------------ 22 files changed, 217 insertions(+), 218 deletions(-) rename DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Operations/Send/{RootObjectBuilder.cs => ArcGISRootObjectBuilder.cs} (74%) rename DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/{SendBinding.cs => RevitSendBinding.cs} (72%) create mode 100644 DUI3-DX/Sdk/Speckle.Connectors.Utils/Caching/ISendConversionCache.cs create mode 100644 DUI3-DX/Sdk/Speckle.Connectors.Utils/Caching/NullSendConversionCache.cs create mode 100644 DUI3-DX/Sdk/Speckle.Connectors.Utils/Caching/SendConversionCache.cs delete mode 100644 DUI3-DX/Sdk/Speckle.Connectors.Utils/SpeckleTopLevelExceptionHandler.cs diff --git a/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/ArcGISSendBinding.cs b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/ArcGISSendBinding.cs index 7391a8cea3..95a4a0abc5 100644 --- a/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/ArcGISSendBinding.cs +++ b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/ArcGISSendBinding.cs @@ -16,8 +16,8 @@ using ArcGIS.Core.Data; using Speckle.Connectors.DUI.Exceptions; using Speckle.Connectors.Utils; +using Speckle.Connectors.Utils.Caching; using Speckle.Connectors.Utils.Operations; -using Speckle.Core.Models; namespace Speckle.Connectors.ArcGIS.Bindings; @@ -31,8 +31,7 @@ public sealed class ArcGISSendBinding : ISendBinding, ICancelable private readonly IUnitOfWorkFactory _unitOfWorkFactory; // POC: unused? :D private readonly List _sendFilters; private readonly CancellationManager _cancellationManager; - - private readonly Dictionary _convertedObjectReferences = new(); + private readonly ISendConversionCache _sendConversionCache; /// /// Used internally to aggregate the changed objects' id. @@ -46,14 +45,15 @@ public ArcGISSendBinding( IBridge parent, IEnumerable sendFilters, IUnitOfWorkFactory unitOfWorkFactory, - CancellationManager cancellationManager + CancellationManager cancellationManager, + ISendConversionCache sendConversionCache ) { _store = store; _unitOfWorkFactory = unitOfWorkFactory; _sendFilters = sendFilters.ToList(); _cancellationManager = cancellationManager; - + _sendConversionCache = sendConversionCache; Parent = parent; Commands = new SendBindingUICommands(parent); SubscribeToArcGISEvents(); @@ -277,9 +277,7 @@ public async Task Send(string modelCardId) modelCard.AccountId.NotNull(), modelCard.ProjectId.NotNull(), modelCard.ModelId.NotNull(), - "ArcGIS", - _convertedObjectReferences, - modelCard.ChangedObjectIds + "ArcGIS" ); var sendResult = await QueuedTask @@ -309,15 +307,6 @@ public async Task Send(string modelCardId) ) .ConfigureAwait(false); - // Store the converted references in memory for future send operations, overwriting the existing values for the given application id. - foreach (var kvp in result.ConvertedReferences) - { - _convertedObjectReferences[kvp.Key + modelCard.ProjectId] = kvp.Value; - } - - // It's important to reset the model card's list of changed obj ids so as to ensure we accurately keep track of changes between send operations. - modelCard.ChangedObjectIds = new(); - return result; }) .ConfigureAwait(false); @@ -347,6 +336,8 @@ private void RunExpirationChecks(bool idsDeleted) List expiredSenderIds = new(); string[] objectIdsList = ChangedObjectIds.ToArray(); + _sendConversionCache.EvictObjects(objectIdsList); + foreach (SenderModelCard sender in senders) { var objIds = sender.SendFilter.NotNull().GetObjectIds(); @@ -355,7 +346,6 @@ private void RunExpirationChecks(bool idsDeleted) if (isExpired) { expiredSenderIds.Add(sender.ModelCardId.NotNull()); - sender.ChangedObjectIds.UnionWith(intersection.NotNull()); // Update the model card object Ids if (idsDeleted && sender.SendFilter is ArcGISSelectionFilter filter) diff --git a/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/DependencyInjection/ArcGISConnectorModule.cs b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/DependencyInjection/ArcGISConnectorModule.cs index 91ddd3ee24..1c5c0a8559 100644 --- a/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/DependencyInjection/ArcGISConnectorModule.cs +++ b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/DependencyInjection/ArcGISConnectorModule.cs @@ -14,6 +14,7 @@ using Speckle.Connectors.DUI; using Speckle.Connectors.DUI.Models; using Speckle.Connectors.Utils; +using Speckle.Connectors.Utils.Caching; using Speckle.Connectors.Utils.Operations; using Speckle.Core.Models.GraphTraversal; @@ -53,7 +54,10 @@ public void Load(SpeckleContainerBuilder builder) // register send operation and dependencies builder.AddScoped>(); - builder.AddScoped(); - builder.AddSingleton, RootObjectBuilder>(); + builder.AddScoped(); + builder.AddSingleton, ArcGISRootObjectBuilder>(); + + // register send conversion cache + builder.AddSingleton(); } } diff --git a/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Operations/Send/RootObjectBuilder.cs b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Operations/Send/ArcGISRootObjectBuilder.cs similarity index 74% rename from DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Operations/Send/RootObjectBuilder.cs rename to DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Operations/Send/ArcGISRootObjectBuilder.cs index c07f396bbb..9b371194e0 100644 --- a/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Operations/Send/RootObjectBuilder.cs +++ b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Operations/Send/ArcGISRootObjectBuilder.cs @@ -1,6 +1,8 @@ +using System.Diagnostics; using ArcGIS.Desktop.Mapping; using Speckle.Autofac.DependencyInjection; using Speckle.Connectors.Utils.Builders; +using Speckle.Connectors.Utils.Caching; using Speckle.Connectors.Utils.Conversion; using Speckle.Connectors.Utils.Operations; using Speckle.Converters.Common; @@ -12,13 +14,15 @@ namespace Speckle.Connectors.ArcGis.Operations.Send; /// /// Stateless builder object to turn an ISendFilter into a object /// -public class RootObjectBuilder : IRootObjectBuilder +public class ArcGISRootObjectBuilder : IRootObjectBuilder { private readonly IUnitOfWorkFactory _unitOfWorkFactory; + private readonly ISendConversionCache _sendConversionCache; - public RootObjectBuilder(IUnitOfWorkFactory unitOfWorkFactory) + public ArcGISRootObjectBuilder(IUnitOfWorkFactory unitOfWorkFactory, ISendConversionCache sendConversionCache) { _unitOfWorkFactory = unitOfWorkFactory; + _sendConversionCache = sendConversionCache; } public RootObjectBuilderResult Build( @@ -38,6 +42,8 @@ public RootObjectBuilderResult Build( Collection rootObjectCollection = new(); //TODO: Collections List results = new(objects.Count); + var cacheHitCount = 0; + foreach (MapMember mapMember in objects) { ct.ThrowIfCancellationRequested(); @@ -47,12 +53,10 @@ public RootObjectBuilderResult Build( try { Base converted; - if ( - !sendInfo.ChangedObjectIds.Contains(applicationId) - && sendInfo.ConvertedObjects.TryGetValue(applicationId + sendInfo.ProjectId, out ObjectReference? value) - ) + if (_sendConversionCache.TryGetValue(sendInfo.ProjectId, applicationId, out ObjectReference value)) { converted = value; + cacheHitCount++; } else { @@ -73,6 +77,11 @@ public RootObjectBuilderResult Build( onOperationProgressed?.Invoke("Converting", (double)++count / objects.Count); } + // POC: Log would be nice, or can be removed. + Debug.WriteLine( + $"Cache hit count {cacheHitCount} out of {objects.Count} ({(double)cacheHitCount / objects.Count})" + ); + return new(rootObjectCollection, results); } } diff --git a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadSendBinding.cs b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadSendBinding.cs index 4321772b6e..a994b9eb9f 100644 --- a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadSendBinding.cs +++ b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadSendBinding.cs @@ -10,10 +10,10 @@ using Speckle.Connectors.Autocad.Operations.Send; using Speckle.Connectors.DUI.Exceptions; using Speckle.Connectors.Utils.Operations; -using Speckle.Core.Models; using ICancelable = System.Reactive.Disposables.ICancelable; using Speckle.Connectors.DUI.Models.Card.SendFilter; using Speckle.Connectors.Utils; +using Speckle.Connectors.Utils.Caching; namespace Speckle.Connectors.Autocad.Bindings; @@ -29,17 +29,13 @@ public sealed class AutocadSendBinding : ISendBinding, ICancelable private readonly CancellationManager _cancellationManager; private readonly IUnitOfWorkFactory _unitOfWorkFactory; private readonly AutocadSettings _autocadSettings; + private readonly ISendConversionCache _sendConversionCache; /// /// Used internally to aggregate the changed objects' id. /// private HashSet ChangedObjectIds { get; set; } = new(); - /// - /// Keeps track of previously converted objects as a dictionary of (applicationId, object reference). - /// - private readonly Dictionary _convertedObjectReferences = new(); - public AutocadSendBinding( DocumentModelStore store, AutocadIdleManager idleManager, @@ -47,7 +43,8 @@ public AutocadSendBinding( IEnumerable sendFilters, CancellationManager cancellationManager, AutocadSettings autocadSettings, - IUnitOfWorkFactory unitOfWorkFactory + IUnitOfWorkFactory unitOfWorkFactory, + ISendConversionCache sendConversionCache ) { _store = store; @@ -56,7 +53,7 @@ IUnitOfWorkFactory unitOfWorkFactory _autocadSettings = autocadSettings; _cancellationManager = cancellationManager; _sendFilters = sendFilters.ToList(); - + _sendConversionCache = sendConversionCache; Parent = parent; Commands = new SendBindingUICommands(parent); @@ -95,6 +92,8 @@ private void RunExpirationChecks() string[] objectIdsList = ChangedObjectIds.ToArray(); List expiredSenderIds = new(); + _sendConversionCache.EvictObjects(objectIdsList); + foreach (SenderModelCard modelCard in senders) { var intersection = modelCard.SendFilter.NotNull().GetObjectIds().Intersect(objectIdsList).ToList(); @@ -102,7 +101,6 @@ private void RunExpirationChecks() if (isExpired) { expiredSenderIds.Add(modelCard.ModelCardId.NotNull()); - modelCard.ChangedObjectIds.UnionWith(intersection); } } @@ -148,9 +146,7 @@ private async Task SendInternal(string modelCardId) modelCard.AccountId.NotNull(), modelCard.ProjectId.NotNull(), modelCard.ModelId.NotNull(), - _autocadSettings.HostAppInfo.Name, - _convertedObjectReferences, - modelCard.ChangedObjectIds + _autocadSettings.HostAppInfo.Name ); var sendResult = await uow.Service @@ -162,15 +158,6 @@ private async Task SendInternal(string modelCardId) ) .ConfigureAwait(false); - // Store the converted references in memory for future send operations, overwriting the existing values for the given application id. - foreach (var kvp in sendResult.ConvertedReferences) - { - _convertedObjectReferences[kvp.Key + modelCard.ProjectId] = kvp.Value; - } - - // It's important to reset the model card's list of changed obj ids so as to ensure we accurately keep track of changes between send operations. - modelCard.ChangedObjectIds = new(); - Commands.SetModelSendResult(modelCardId, sendResult.RootObjId, sendResult.ConversionResults); } // Catch here specific exceptions if they related to model card. diff --git a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/DependencyInjection/AutocadConnectorModule.cs b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/DependencyInjection/AutocadConnectorModule.cs index e2ec747ba9..98733f04d2 100644 --- a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/DependencyInjection/AutocadConnectorModule.cs +++ b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/DependencyInjection/AutocadConnectorModule.cs @@ -14,6 +14,7 @@ using Speckle.Connectors.DUI.WebView; using Speckle.Connectors.Utils; using Speckle.Connectors.Utils.Builders; +using Speckle.Connectors.Utils.Caching; using Speckle.Connectors.Utils.Operations; using Speckle.Core.Models.GraphTraversal; @@ -58,5 +59,8 @@ public void Load(SpeckleContainerBuilder builder) // register send filters builder.AddTransient(); + + // register send conversion cache + builder.AddSingleton(); } } diff --git a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/Extensions/DocumentExtensions.cs b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/Extensions/DocumentExtensions.cs index a0ab9e54a2..300dd8e94f 100644 --- a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/Extensions/DocumentExtensions.cs +++ b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/Extensions/DocumentExtensions.cs @@ -1,4 +1,5 @@ using Autodesk.AutoCAD.DatabaseServices; +using Autodesk.AutoCAD.Runtime; using Speckle.Connectors.Autocad.Operations.Send; namespace Speckle.Connectors.Autocad.HostApp.Extensions; @@ -17,13 +18,26 @@ public static List GetObjects(this Document doc, IEnumerable< if (long.TryParse(objectIdHandle, out long parsedId)) { Handle handle = new(parsedId); - if (doc.Database.TryGetObjectId(handle, out ObjectId myObjectId)) + // Note: Fatal crash happens here when objects are deleted, so we need to catch it. + try { - if (tr.GetObject(myObjectId, OpenMode.ForRead) is DBObject dbObject) + if (doc.Database.TryGetObjectId(handle, out ObjectId myObjectId)) { - objects.Add(new(dbObject, objectIdHandle)); + if (tr.GetObject(myObjectId, OpenMode.ForRead) is DBObject dbObject) + { + objects.Add(new(dbObject, objectIdHandle)); + } } } + catch (Autodesk.AutoCAD.Runtime.Exception e) + { + if (e.ErrorStatus == ErrorStatus.WasErased) // Note: TBD if we want to catch more things in here. For now maybe not, but it does seem like this function gets into "crashes the host app territory" + { + continue; + } + + throw; + } } } } diff --git a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Operations/Send/AutocadRootObjectBuilder.cs b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Operations/Send/AutocadRootObjectBuilder.cs index cb49f54f13..3073a07b25 100644 --- a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Operations/Send/AutocadRootObjectBuilder.cs +++ b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Operations/Send/AutocadRootObjectBuilder.cs @@ -1,5 +1,7 @@ -using Autodesk.AutoCAD.DatabaseServices; +using System.Diagnostics; +using Autodesk.AutoCAD.DatabaseServices; using Speckle.Connectors.Utils.Builders; +using Speckle.Connectors.Utils.Caching; using Speckle.Connectors.Utils.Conversion; using Speckle.Connectors.Utils.Operations; using Speckle.Converters.Common; @@ -12,10 +14,12 @@ public class AutocadRootObjectBuilder : IRootObjectBuilder { private readonly IRootToSpeckleConverter _converter; private readonly string[] _documentPathSeparator = { "\\" }; + private readonly ISendConversionCache _sendConversionCache; - public AutocadRootObjectBuilder(IRootToSpeckleConverter converter) + public AutocadRootObjectBuilder(IRootToSpeckleConverter converter, ISendConversionCache sendConversionCache) { _converter = converter; + _sendConversionCache = sendConversionCache; } public RootObjectBuilderResult Build( @@ -40,6 +44,7 @@ public RootObjectBuilderResult Build( int count = 0; List results = new(objects.Count); + var cacheHitCount = 0; foreach (var (dbObject, applicationId) in objects) { ct.ThrowIfCancellationRequested(); @@ -47,12 +52,10 @@ public RootObjectBuilderResult Build( try { Base converted; - if ( - !sendInfo.ChangedObjectIds.Contains(applicationId) - && sendInfo.ConvertedObjects.TryGetValue(applicationId + sendInfo.ProjectId, out ObjectReference value) // POC: Interface out constructing keys here to use it otherplaces with a same code. -> https://spockle.atlassian.net/browse/CNX-9313 - ) + if (_sendConversionCache.TryGetValue(sendInfo.ProjectId, applicationId, out ObjectReference value)) { converted = value; + cacheHitCount++; } else { @@ -84,6 +87,11 @@ public RootObjectBuilderResult Build( onOperationProgressed?.Invoke("Converting", (double)++count / objects.Count); } + // POC: Log would be nice, or can be removed. + Debug.WriteLine( + $"Cache hit count {cacheHitCount} out of {objects.Count} ({(double)cacheHitCount / objects.Count})" + ); + return new(modelWithLayers, results); } } diff --git a/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/BasicConnectorBindingRevit.cs b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/BasicConnectorBindingRevit.cs index 7b1382ce7b..3aa6be233f 100644 --- a/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/BasicConnectorBindingRevit.cs +++ b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/BasicConnectorBindingRevit.cs @@ -88,7 +88,7 @@ public void HighlightModel(string modelCardId) SenderModelCard model = (SenderModelCard)_store.GetModelById(modelCardId); var elementIds = model.SendFilter.NotNull().GetObjectIds().Select(ElementId.Parse).ToList(); - if (elementIds.Count != 0) + if (elementIds.Count == 0) { Commands.SetModelError(modelCardId, new InvalidOperationException("No objects found to highlight.")); return; diff --git a/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/SendBinding.cs b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/RevitSendBinding.cs similarity index 72% rename from DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/SendBinding.cs rename to DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/RevitSendBinding.cs index a721ac2283..bbfa2a815b 100644 --- a/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/SendBinding.cs +++ b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/RevitSendBinding.cs @@ -3,7 +3,6 @@ using Speckle.Connectors.Utils.Cancellation; using Speckle.Connectors.DUI.Bridge; using Speckle.Connectors.Revit.Plugin; -using Speckle.Core.Logging; using Speckle.Connectors.Utils; using Speckle.Converters.RevitShared.Helpers; using Speckle.Connectors.DUI.Models.Card; @@ -11,12 +10,12 @@ using Speckle.Autofac.DependencyInjection; using Speckle.Connectors.DUI.Exceptions; using Speckle.Connectors.DUI.Models; +using Speckle.Connectors.Utils.Caching; using Speckle.Connectors.Utils.Operations; -using Speckle.Core.Models; namespace Speckle.Connectors.Revit.Bindings; -internal sealed class SendBinding : RevitBaseBinding, ICancelable, ISendBinding +internal sealed class RevitSendBinding : RevitBaseBinding, ICancelable, ISendBinding { // POC:does it need injecting? public CancellationManager CancellationManager { get; } = new(); @@ -24,28 +23,27 @@ internal sealed class SendBinding : RevitBaseBinding, ICancelable, ISendBinding // POC: does it need injecting? private HashSet ChangedObjectIds { get; set; } = new(); - /// - /// Keeps track of previously converted objects as a dictionary of (applicationId, object reference). - /// - private readonly Dictionary _convertedObjectReferences = new(); - private readonly RevitSettings _revitSettings; private readonly IRevitIdleManager _idleManager; private readonly IUnitOfWorkFactory _unitOfWorkFactory; + private readonly ISendConversionCache _sendConversionCache; - public SendBinding( + public RevitSendBinding( IRevitIdleManager idleManager, RevitContext revitContext, DocumentModelStore store, IBridge bridge, IUnitOfWorkFactory unitOfWorkFactory, - RevitSettings revitSettings + RevitSettings revitSettings, + ISendConversionCache sendConversionCache ) : base("sendBinding", store, bridge, revitContext) { _idleManager = idleManager; _unitOfWorkFactory = unitOfWorkFactory; _revitSettings = revitSettings; + _sendConversionCache = sendConversionCache; + Commands = new SendBindingUICommands(bridge); // TODO expiry events // TODO filters need refresh events @@ -57,13 +55,6 @@ public List GetSendFilters() return new List { new RevitSelectionFilter() { IsDefault = true } }; } - public async Task Send(string modelCardId) - { - await SpeckleTopLevelExceptionHandler - .Run(() => HandleSend(modelCardId), HandleSpeckleException, HandleUnexpectedException, HandleFatalException) - .ConfigureAwait(false); - } - public void CancelSend(string modelCardId) { CancellationManager.CancelOperation(modelCardId); @@ -71,11 +62,9 @@ public void CancelSend(string modelCardId) public SendBindingUICommands Commands { get; } - private async Task HandleSend(string modelCardId) + public async Task Send(string modelCardId) { - // POC: this try catch pattern is coming from Rhino. I see there is a semi implemented "SpeckleTopLevelExceptionHandler" - // above that wraps the call of this HandleSend, but it currently is not doing anything - resulting in all errors from here - // bubbling up to the bridge. + // Note: removed top level handling thing as it was confusing me try { if (Store.GetModelById(modelCardId) is not SenderModelCard modelCard) @@ -108,9 +97,7 @@ private async Task HandleSend(string modelCardId) modelCard.AccountId.NotNull(), modelCard.ProjectId.NotNull(), modelCard.ModelId.NotNull(), - _revitSettings.HostSlug.NotNull(), - _convertedObjectReferences, - modelCard.ChangedObjectIds + _revitSettings.HostSlug.NotNull() ); var sendResult = await sendOperation.Service @@ -122,16 +109,6 @@ private async Task HandleSend(string modelCardId) ) .ConfigureAwait(false); - // Store the converted references in memory for future send operations, overwriting the existing values for the given application id. - foreach (var kvp in sendResult.ConvertedReferences) - { - _convertedObjectReferences[kvp.Key + modelCard.ProjectId] = kvp.Value; - } - - // It's important to reset the model card's list of changed obj ids so as to ensure we accurately keep track of changes between send operations. - modelCard.ChangedObjectIds = new(); - - //TODO: send full send resul to UI? Commands.SetModelSendResult(modelCardId, sendResult.RootObjId, sendResult.ConversionResults); } // Catch here specific exceptions if they related to model card. @@ -150,27 +127,6 @@ private void OnSendOperationProgress(string modelCardId, string status, double? Commands.SetModelProgress(modelCardId, new ModelCardProgress(modelCardId, status, progress)); } - private bool HandleSpeckleException(SpeckleException spex) - { - // POC: do something here - - return false; - } - - private bool HandleUnexpectedException(Exception ex) - { - // POC: do something here - - return false; - } - - private bool HandleFatalException(Exception ex) - { - // POC: do something here - - return false; - } - /// /// Keeps track of the changed element ids as well as checks if any of them need to trigger /// a filter refresh (e.g., views being added). @@ -207,14 +163,15 @@ private void RunExpirationChecks() string[] objectIdsList = ChangedObjectIds.ToArray(); List expiredSenderIds = new(); + _sendConversionCache.EvictObjects(objectIdsList); + foreach (SenderModelCard modelCard in senders) { var intersection = modelCard.SendFilter.NotNull().GetObjectIds().Intersect(objectIdsList).ToList(); - bool isExpired = modelCard.SendFilter.CheckExpiry(ChangedObjectIds.ToArray()); + bool isExpired = intersection.Count != 0; if (isExpired) { expiredSenderIds.Add(modelCard.ModelCardId.NotNull()); - modelCard.ChangedObjectIds.UnionWith(intersection); } } diff --git a/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/DependencyInjection/RevitConnectorModule.cs b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/DependencyInjection/RevitConnectorModule.cs index 4d6a302e27..c0193834cd 100644 --- a/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/DependencyInjection/RevitConnectorModule.cs +++ b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/DependencyInjection/RevitConnectorModule.cs @@ -11,6 +11,7 @@ using Speckle.Connectors.Revit.Plugin; using Speckle.Connectors.Utils; using Speckle.Connectors.Utils.Builders; +using Speckle.Connectors.Utils.Caching; using Speckle.Connectors.Utils.Operations; namespace Speckle.Connectors.Revit.DependencyInjection; @@ -50,12 +51,15 @@ public void Load(SpeckleContainerBuilder builder) builder.AddSingleton(); builder.AddSingleton(); builder.AddSingleton(); - builder.AddSingleton(); + builder.AddSingleton(); //no receive? builder.AddSingleton(); // send operation and dependencies builder.AddScoped>(); builder.AddScoped, RevitRootObjectBuilder>(); + + // register send conversion cache + builder.AddSingleton(); } } diff --git a/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Send/RevitRootObjectBuilder.cs b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Send/RevitRootObjectBuilder.cs index 5d42abaa10..b22ef0a3ca 100644 --- a/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Send/RevitRootObjectBuilder.cs +++ b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Send/RevitRootObjectBuilder.cs @@ -1,9 +1,11 @@ +using System.Diagnostics; using Speckle.Converters.Common; using Speckle.Core.Models; using Autodesk.Revit.DB; using Speckle.Connectors.DUI.Exceptions; using Speckle.Converters.RevitShared.Helpers; using Speckle.Connectors.Utils.Builders; +using Speckle.Connectors.Utils.Caching; using Speckle.Connectors.Utils.Conversion; using Speckle.Connectors.Utils.Operations; using Speckle.Core.Logging; @@ -17,12 +19,17 @@ public class RevitRootObjectBuilder : IRootObjectBuilder private readonly IRevitConversionContextStack _contextStack; private readonly Dictionary _collectionCache; private readonly Collection _rootObject; + private readonly ISendConversionCache _sendConversionCache; - public RevitRootObjectBuilder(IRootToSpeckleConverter converter, IRevitConversionContextStack contextStack) + public RevitRootObjectBuilder( + IRootToSpeckleConverter converter, + IRevitConversionContextStack contextStack, + ISendConversionCache sendConversionCache + ) { _converter = converter; _contextStack = contextStack; - + _sendConversionCache = sendConversionCache; // Note, this class is instantiated per unit of work (aka per send operation), so we can safely initialize what we need in here. _collectionCache = new Dictionary(); _rootObject = new Collection() @@ -62,7 +69,7 @@ public RootObjectBuilderResult Build( } var countProgress = 0; // because for(int i = 0; ...) loops are so last year - + var cacheHitCount = 0; List results = new(revitElements.Count); foreach (Element revitElement in revitElements) { @@ -76,12 +83,10 @@ public RootObjectBuilderResult Build( try { Base converted; - if ( - !sendInfo.ChangedObjectIds.Contains(applicationId) - && sendInfo.ConvertedObjects.TryGetValue(applicationId + sendInfo.ProjectId, out ObjectReference value) - ) + if (_sendConversionCache.TryGetValue(sendInfo.ProjectId, applicationId, out ObjectReference value)) { converted = value; + cacheHitCount++; } else { @@ -101,6 +106,11 @@ public RootObjectBuilderResult Build( onOperationProgressed?.Invoke("Converting", (double)++countProgress / revitElements.Count); } + // POC: Log would be nice, or can be removed. + Debug.WriteLine( + $"Cache hit count {cacheHitCount} out of {objects.Count} ({(double)cacheHitCount / objects.Count})" + ); + return new(_rootObject, results); } diff --git a/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Speckle.Connectors.RevitShared.projitems b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Speckle.Connectors.RevitShared.projitems index d7bcb0a2d3..8a671aefc9 100644 --- a/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Speckle.Connectors.RevitShared.projitems +++ b/DUI3-DX/Connectors/Revit/Speckle.Connectors.RevitShared/Speckle.Connectors.RevitShared.projitems @@ -20,7 +20,7 @@ - + diff --git a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Bindings/RhinoSendBinding.cs b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Bindings/RhinoSendBinding.cs index e767b9c3cf..6f3b66e6ff 100644 --- a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Bindings/RhinoSendBinding.cs +++ b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Bindings/RhinoSendBinding.cs @@ -1,4 +1,3 @@ -using System.Diagnostics.CodeAnalysis; using Rhino; using Speckle.Connectors.DUI.Bindings; using Speckle.Connectors.DUI.Bridge; @@ -12,9 +11,9 @@ using Speckle.Connectors.DUI.Exceptions; using Speckle.Connectors.DUI.Models.Card.SendFilter; using Speckle.Connectors.Utils.Operations; -using Speckle.Core.Models; using Speckle.Connectors.DUI.Settings; using Speckle.Connectors.Utils; +using Speckle.Connectors.Utils.Caching; namespace Speckle.Connectors.Rhino7.Bindings; @@ -37,10 +36,7 @@ public sealed class RhinoSendBinding : ISendBinding, ICancelable /// private HashSet ChangedObjectIds { get; set; } = new(); - /// - /// Keeps track of previously converted objects as a dictionary of (applicationId, object reference). - /// - private readonly Dictionary _convertedObjectReferences = new(); + private readonly ISendConversionCache _sendConversionCache; public RhinoSendBinding( DocumentModelStore store, @@ -50,7 +46,8 @@ public RhinoSendBinding( SendOperation sendOperation, IUnitOfWorkFactory unitOfWorkFactory, RhinoSettings rhinoSettings, - CancellationManager cancellationManager + CancellationManager cancellationManager, + ISendConversionCache sendConversionCache ) { _store = store; @@ -60,6 +57,7 @@ CancellationManager cancellationManager _sendFilters = sendFilters.ToList(); _rhinoSettings = rhinoSettings; _cancellationManager = cancellationManager; + _sendConversionCache = sendConversionCache; Parent = parent; Commands = new SendBindingUICommands(parent); // POC: Commands are tightly coupled with their bindings, at least for now, saves us injecting a factory. SubscribeToRhinoEvents(); @@ -127,11 +125,6 @@ public List GetSendSettings() }; } - [SuppressMessage( - "Maintainability", - "CA1506:Avoid excessive class coupling", - Justification = "Being refactored on in parallel, muting this issue so CI can pass initially." - )] public async Task Send(string modelCardId) { using var unitOfWork = _unitOfWorkFactory.Resolve>(); @@ -163,9 +156,7 @@ public async Task Send(string modelCardId) modelCard.AccountId.NotNull(), modelCard.ProjectId.NotNull(), modelCard.ModelId.NotNull(), - _rhinoSettings.HostAppInfo.Name, - _convertedObjectReferences, - modelCard.ChangedObjectIds + _rhinoSettings.HostAppInfo.Name ); var sendResult = await unitOfWork.Service @@ -177,15 +168,6 @@ public async Task Send(string modelCardId) ) .ConfigureAwait(false); - // Store the converted references in memory for future send operations, overwriting the existing values for the given application id. - foreach (var kvp in sendResult.ConvertedReferences) - { - _convertedObjectReferences[kvp.Key + modelCard.ProjectId] = kvp.Value; - } - - // It's important to reset the model card's list of changed obj ids so as to ensure we accurately keep track of changes between send operations. - modelCard.ChangedObjectIds = new(); - Commands.SetModelSendResult(modelCardId, sendResult.RootObjId, sendResult.ConversionResults); } // Catch here specific exceptions if they related to model card. @@ -216,14 +198,15 @@ private void RunExpirationChecks() string[] objectIdsList = ChangedObjectIds.ToArray(); List expiredSenderIds = new(); + _sendConversionCache.EvictObjects(objectIdsList); + foreach (SenderModelCard modelCard in senders) { var intersection = modelCard.SendFilter.NotNull().GetObjectIds().Intersect(objectIdsList).ToList(); - var isExpired = modelCard.SendFilter.NotNull().CheckExpiry(ChangedObjectIds.ToArray()); + var isExpired = intersection.Count != 0; if (isExpired) { expiredSenderIds.Add(modelCard.ModelCardId.NotNull()); - modelCard.ChangedObjectIds.UnionWith(intersection.NotNull()); } } diff --git a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/DependencyInjection/RhinoConnectorModule.cs b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/DependencyInjection/RhinoConnectorModule.cs index 15475b4f62..96d6aaa56b 100644 --- a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/DependencyInjection/RhinoConnectorModule.cs +++ b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/DependencyInjection/RhinoConnectorModule.cs @@ -19,6 +19,7 @@ using Speckle.Connectors.Rhino7.Operations.Receive; using Speckle.Connectors.Utils; using Speckle.Connectors.Utils.Builders; +using Speckle.Connectors.Utils.Caching; using Speckle.Connectors.Utils.Operations; using Speckle.Core.Models.GraphTraversal; @@ -62,6 +63,9 @@ public void Load(SpeckleContainerBuilder builder) builder.AddScoped(); builder.AddScoped(); + // register send conversion cache + builder.AddSingleton(); + // register send operation and dependencies builder.AddScoped>(); builder.AddSingleton(DefaultTraversal.CreateTraversalFunc()); diff --git a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Operations/Send/RhinoRootObjectBuilder.cs b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Operations/Send/RhinoRootObjectBuilder.cs index cfb94ca620..2d5996c97a 100644 --- a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Operations/Send/RhinoRootObjectBuilder.cs +++ b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Operations/Send/RhinoRootObjectBuilder.cs @@ -1,3 +1,4 @@ +using System.Diagnostics; using Rhino.DocObjects; using Rhino; using Speckle.Core.Models; @@ -5,6 +6,7 @@ using Speckle.Converters.Common; using Speckle.Connectors.DUI.Models.Card.SendFilter; using Speckle.Connectors.Utils.Builders; +using Speckle.Connectors.Utils.Caching; using Speckle.Connectors.Utils.Conversion; using Speckle.Connectors.Utils.Operations; using Speckle.Core.Logging; @@ -17,10 +19,12 @@ namespace Speckle.Connectors.Rhino7.Operations.Send; public class RhinoRootObjectBuilder : IRootObjectBuilder { private readonly IUnitOfWorkFactory _unitOfWorkFactory; + private readonly ISendConversionCache _sendConversionCache; - public RhinoRootObjectBuilder(IUnitOfWorkFactory unitOfWorkFactory) + public RhinoRootObjectBuilder(IUnitOfWorkFactory unitOfWorkFactory, ISendConversionCache sendConversionCache) { _unitOfWorkFactory = unitOfWorkFactory; + _sendConversionCache = sendConversionCache; } public RootObjectBuilderResult Build( @@ -49,6 +53,7 @@ private RootObjectBuilderResult ConvertObjects( // POC: Handle blocks. List results = new(rhinoObjects.Count); + var cacheHitCount = 0; foreach (RhinoObject rhinoObject in rhinoObjects) { cancellationToken.ThrowIfCancellationRequested(); @@ -65,12 +70,11 @@ private RootObjectBuilderResult ConvertObjects( // What we actually do here is check if the object has been previously converted AND has not changed. // If that's the case, we insert in the host collection just its object reference which has been saved from the prior conversion. Base converted; - if ( - !sendInfo.ChangedObjectIds.Contains(applicationId) - && sendInfo.ConvertedObjects.TryGetValue(applicationId + sendInfo.ProjectId, out ObjectReference value) - ) + + if (_sendConversionCache.TryGetValue(sendInfo.ProjectId, applicationId, out ObjectReference value)) { converted = value; + cacheHitCount++; } else { @@ -94,6 +98,11 @@ private RootObjectBuilderResult ConvertObjects( // Thread.Sleep(550); } + // POC: Log would be nice, or can be removed. + Debug.WriteLine( + $"Cache hit count {cacheHitCount} out of {rhinoObjects.Count} ({(double)cacheHitCount / rhinoObjects.Count})" + ); + // 5. profit return new(rootObjectCollection, results); } diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/SenderModelCard.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/SenderModelCard.cs index ecd33fba44..1bee9faabc 100644 --- a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/SenderModelCard.cs +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/SenderModelCard.cs @@ -1,5 +1,4 @@ using Speckle.Connectors.DUI.Models.Card.SendFilter; -using Speckle.Newtonsoft.Json; namespace Speckle.Connectors.DUI.Models.Card; @@ -7,6 +6,6 @@ public class SenderModelCard : ModelCard { public ISendFilter? SendFilter { get; set; } - [JsonIgnore] - public HashSet ChangedObjectIds { get; set; } = new(); + // [JsonIgnore] + // public HashSet ChangedObjectIds { get; set; } = new(); } diff --git a/DUI3-DX/Sdk/Speckle.Connectors.Utils/Caching/ISendConversionCache.cs b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Caching/ISendConversionCache.cs new file mode 100644 index 0000000000..ce5bfc41a2 --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Caching/ISendConversionCache.cs @@ -0,0 +1,23 @@ +using Speckle.Core.Models; + +namespace Speckle.Connectors.Utils.Caching; + +/// +/// Stores object references resulting from a send operation. These can be retrieved back during a subsequent send operation to bypass conversion if +/// they have not been changed. On large sends with small changes, this makes the process much speedier! +/// Note: do not and should not persist between file opens, should just persist in memory between send operations. Always eagerly invalidate. +/// If you ever implement a different conversion cache, do remember that objects in speckle are namespaced to each project (stream). E.g., if you send A to project C and project D, A needs to exist twice in the db. As such, always namespace stored references by project id. +/// Further note: Caching is optional in the send ops; an instance of this should be injected only in applications where we know we can rely on change tracking! +/// +public interface ISendConversionCache +{ + void StoreSendResult(string projectId, Dictionary convertedReferences); + + /// + /// Call this method whenever you need to invalidate a set of objects that have changed in the host app. + /// Failure to do so correctly will result in cache poisoning and incorrect version creation (stale objects). + /// + /// + public void EvictObjects(IEnumerable objectIds); + bool TryGetValue(string projectId, string applicationId, out ObjectReference objectReference); +} diff --git a/DUI3-DX/Sdk/Speckle.Connectors.Utils/Caching/NullSendConversionCache.cs b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Caching/NullSendConversionCache.cs new file mode 100644 index 0000000000..ec8e85f46c --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Caching/NullSendConversionCache.cs @@ -0,0 +1,19 @@ +using Speckle.Core.Models; + +namespace Speckle.Connectors.Utils.Caching; + +/// +/// A null send conversion cache for future use in connectors that cannot support . It does nothing! +/// +public class NullSendConversionCache : ISendConversionCache +{ + public void StoreSendResult(string projectId, Dictionary convertedReferences) { } + + public void EvictObjects(IEnumerable objectIds) { } + + public bool TryGetValue(string projectId, string applicationId, out ObjectReference objectReference) + { + objectReference = new ObjectReference(); + return false; + } +} diff --git a/DUI3-DX/Sdk/Speckle.Connectors.Utils/Caching/SendConversionCache.cs b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Caching/SendConversionCache.cs new file mode 100644 index 0000000000..94a9883927 --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Caching/SendConversionCache.cs @@ -0,0 +1,28 @@ +using Speckle.Core.Models; + +namespace Speckle.Connectors.Utils.Caching; + +/// +public class SendConversionCache : ISendConversionCache +{ + public SendConversionCache() { } + + private Dictionary<(string applicationId, string projectId), ObjectReference> Cache { get; set; } = new(); // NOTE: as this dude's accessed from potentially more operations at the same time, it might be safer to bless him as a concurrent dictionary. + + public void StoreSendResult(string projectId, Dictionary convertedReferences) + { + foreach (var kvp in convertedReferences) + { + Cache[(kvp.Key, projectId)] = kvp.Value; + } + } + + /// + public void EvictObjects(IEnumerable objectIds) => + Cache = Cache + .Where(kvp => !objectIds.Contains(kvp.Key.applicationId)) + .ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + + public bool TryGetValue(string projectId, string applicationId, out ObjectReference objectReference) => + Cache.TryGetValue((applicationId, projectId), out objectReference); +} diff --git a/DUI3-DX/Sdk/Speckle.Connectors.Utils/Operations/RootObjectSender.cs b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Operations/RootObjectSender.cs index c46faeb7fc..0234361f66 100644 --- a/DUI3-DX/Sdk/Speckle.Connectors.Utils/Operations/RootObjectSender.cs +++ b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Operations/RootObjectSender.cs @@ -1,3 +1,4 @@ +using Speckle.Connectors.Utils.Caching; using Speckle.Core.Api; using Speckle.Core.Credentials; using Speckle.Core.Models; @@ -15,10 +16,12 @@ public sealed class RootObjectSender : IRootObjectSender { // POC: Revisit this factory pattern, I think we could solve this higher up by injecting a scoped factory for `SendOperation` in the SendBinding private readonly ServerTransport.Factory _transportFactory; + private readonly ISendConversionCache _sendConversionCache; - public RootObjectSender(ServerTransport.Factory transportFactory) + public RootObjectSender(ServerTransport.Factory transportFactory, ISendConversionCache sendConversionCache) { _transportFactory = transportFactory; + _sendConversionCache = sendConversionCache; } public async Task<(string rootObjId, Dictionary convertedReferences)> Send( @@ -37,6 +40,8 @@ public RootObjectSender(ServerTransport.Factory transportFactory) ITransport transport = _transportFactory(account, sendInfo.ProjectId, 60, null); var sendResult = await SendHelper.Send(commitObject, transport, true, null, ct).ConfigureAwait(false); + _sendConversionCache.StoreSendResult(sendInfo.ProjectId, sendResult.convertedReferences); + ct.ThrowIfCancellationRequested(); onOperationProgressed?.Invoke("Linking version to model...", null); diff --git a/DUI3-DX/Sdk/Speckle.Connectors.Utils/Operations/SendInfo.cs b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Operations/SendInfo.cs index 65cd622466..0b45d1161f 100644 --- a/DUI3-DX/Sdk/Speckle.Connectors.Utils/Operations/SendInfo.cs +++ b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Operations/SendInfo.cs @@ -1,31 +1,17 @@ -using Speckle.Core.Models; - -namespace Speckle.Connectors.Utils.Operations; +namespace Speckle.Connectors.Utils.Operations; public readonly struct SendInfo { - public SendInfo( - string accountId, - string projectId, - string modelId, - string sourceApplication, - IReadOnlyDictionary convertedObjects, - ISet changedObjectIds - ) + public SendInfo(string accountId, string projectId, string modelId, string sourceApplication) { AccountId = accountId; ProjectId = projectId; ModelId = modelId; SourceApplication = sourceApplication; - ConvertedObjects = convertedObjects; - ChangedObjectIds = changedObjectIds; } public string AccountId { get; } public string ProjectId { get; } public string ModelId { get; } public string SourceApplication { get; } - public IReadOnlyDictionary ConvertedObjects { get; } - - public ISet ChangedObjectIds { get; } } diff --git a/DUI3-DX/Sdk/Speckle.Connectors.Utils/SpeckleTopLevelExceptionHandler.cs b/DUI3-DX/Sdk/Speckle.Connectors.Utils/SpeckleTopLevelExceptionHandler.cs deleted file mode 100644 index 0eb92feff5..0000000000 --- a/DUI3-DX/Sdk/Speckle.Connectors.Utils/SpeckleTopLevelExceptionHandler.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Speckle.Core.Logging; - -namespace Speckle.Connectors.Utils; - -// POC: consider wisdom of static -public static class SpeckleTopLevelExceptionHandler -{ - // POC: async/await? - // handlers for - public static async Task Run( - Func run, - Func? speckleError = null, - Func? unexpectedError = null, - Func? fatalError = null - ) - { - // POC: TL-handler - try - { - await run().ConfigureAwait(false); - } - catch (SpeckleException spex) - { - if (speckleError == null || !speckleError(spex)) - { - throw; - } - } - catch (Exception ex) when (!ex.IsFatal()) - { - if (unexpectedError == null || !unexpectedError(ex)) - { - throw; - } - } - catch (Exception ex) when (ex.IsFatal()) - { - if (fatalError == null || !fatalError(ex)) - { - throw; - } - } - } -}