Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 929db22785 | |||
| 0b26e021cd | |||
| a0d5e3b2d4 | |||
| 1ebc32f07e | |||
| a3b1cd52ad | |||
| c7cfb8c6c7 | |||
| 93246157c7 | |||
| 931d7ff8d2 | |||
| 18499bdc69 | |||
| e78914955c | |||
| 87569c9c66 | |||
| 189ea3a19b | |||
| 370588fa89 | |||
| c9802396f5 | |||
| 9644444ea7 | |||
| bb0fffd4d7 | |||
| c719cfd66f | |||
| 92435ce1a1 | |||
| 71409e8af8 | |||
| 8060f46882 | |||
| 6671edd36e | |||
| bc15ff3a34 | |||
| de275dcf02 | |||
| 6158739df0 |
+29
-26
@@ -4,33 +4,36 @@
|
||||
|
||||
# * @specklesystems/connectors
|
||||
|
||||
# Core
|
||||
# Not needed, falls back on team approval
|
||||
|
||||
# Objects
|
||||
# Converters require product owner approval, anything else falls back to team approval
|
||||
|
||||
Objects/ConverterAutocadCivil/* @clairekuang @connorivy
|
||||
Objects/ConverterCSI/* @connorivy
|
||||
Objects/ConverterDynamo/* @teocomi @alanrynne
|
||||
Objects/ConverterMicrostation/* @connorivy
|
||||
Objects/ConverterRevit/* @connorivy @teocomi
|
||||
Objects/ConverterRhinoGh/* @alanrynne @clairekuang
|
||||
Objects/ConverterTeklaStructures/* @connorivy
|
||||
Objects/StructuralUtilities/PolygonMesher/* @connorivy
|
||||
|
||||
# Connectors
|
||||
|
||||
ConnectorAutocadCivil/* @clairekuang
|
||||
ConnectorArchicad/* @jozseflkiss
|
||||
ConnectorCSI/* @connorivy
|
||||
ConnectorDynamo/* @teocomi @alanrynne
|
||||
ConnectorGrasshopper/* @alanrynne @clairekuang
|
||||
ConnectorMicrostation/* @clairekuang
|
||||
ConnectorRevit/* @teocomi @connorivy
|
||||
ConnectorRhino/* @clairekuang @alanrynne
|
||||
ConnectorTeklaStructures/* @connorivy
|
||||
/Connectors/ArcGIS/* @KatKatKateryna
|
||||
/Connectors/Autocad/* @clairekuang @oguzhankoral @didimitrie
|
||||
/Connectors/Civil3d/* @clairekuang @oguzhankoral @didimitrie
|
||||
/Connectors/CSi/* @bjoernsteinhagen @dogukankaratas
|
||||
/Connectors/Navisworks/* @jsdbroughton
|
||||
/Connectors/Revit/* @clairekuang @oguzhankoral @didimitrie
|
||||
/Connectors/Rhino/* @clairekuang @oguzhankoral @didimitrie
|
||||
/Connectors/Tekla/* @bjoernsteinhagen @dogukankaratas
|
||||
|
||||
# DesktopUI
|
||||
# Converters
|
||||
/Convertors/ArcGIS/* @KatKatKateryna
|
||||
/Convertors/Autocad/* @clairekuang @oguzhankoral @didimitrie
|
||||
/Convertors/Civil3d/* @clairekuang @oguzhankoral @didimitrie
|
||||
/Convertors/CSi/* @bjoernsteinhagen @dogukankaratas
|
||||
/Convertors/Navisworks/* @jsdbroughton
|
||||
/Convertors/Revit/* @clairekuang @oguzhankoral @didimitrie
|
||||
/Convertors/Rhino/* @clairekuang @oguzhankoral @didimitrie
|
||||
/Convertors/Tekla/* @bjoernsteinhagen @dogukankaratas
|
||||
|
||||
DesktopUI2/* @teocomi @clairekuang
|
||||
# DUI
|
||||
|
||||
/DUI3/* @clairekuang @oguzhankoral @didimitrie
|
||||
|
||||
# Importers
|
||||
/Importers/* @JR-Morgan @didimitrie @oguzhankoral @adamhathcock
|
||||
|
||||
# SDK
|
||||
/SDK/* @JR-Morgan @clairekuang @didimitrie @oguzhankoral @adamhathcock
|
||||
|
||||
# Build
|
||||
/Build/* @JR-Morgan @oguzhankoral @adamhathcock
|
||||
|
||||
@@ -15,7 +15,6 @@ using Speckle.Connectors.Common.Operations;
|
||||
using Speckle.Connectors.Common.Threading;
|
||||
using Speckle.Connectors.DUI.Bindings;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
using Speckle.Connectors.DUI.Exceptions;
|
||||
using Speckle.Connectors.DUI.Logging;
|
||||
using Speckle.Connectors.DUI.Models;
|
||||
@@ -71,7 +70,6 @@ public sealed class ArcGISSendBinding : ISendBinding
|
||||
IArcGISConversionSettingsFactory arcGisConversionSettingsFactory,
|
||||
MapMembersUtils mapMemberUtils,
|
||||
IThreadContext threadContext,
|
||||
IEventAggregator eventAggregator,
|
||||
ITopLevelExceptionHandler topLevelExceptionHandler
|
||||
)
|
||||
{
|
||||
@@ -90,7 +88,10 @@ public sealed class ArcGISSendBinding : ISendBinding
|
||||
Parent = parent;
|
||||
Commands = new SendBindingUICommands(parent);
|
||||
SubscribeToArcGISEvents();
|
||||
eventAggregator.GetEvent<DocumentStoreChangedEvent>().Subscribe(OnDocumentStoreChangedEvent);
|
||||
_store.DocumentChanged += (_, _) =>
|
||||
{
|
||||
_sendConversionCache.ClearCache();
|
||||
};
|
||||
}
|
||||
|
||||
private void OnDocumentStoreChangedEvent(object _) => _sendConversionCache.ClearCache();
|
||||
|
||||
@@ -3,7 +3,6 @@ using ArcGIS.Desktop.Mapping;
|
||||
using Speckle.Connectors.ArcGIS.Utils;
|
||||
using Speckle.Connectors.DUI.Bindings;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
using Speckle.Connectors.DUI.Models;
|
||||
using Speckle.Connectors.DUI.Models.Card;
|
||||
using Speckle.Sdk;
|
||||
@@ -21,24 +20,28 @@ public class BasicConnectorBinding : IBasicConnectorBinding
|
||||
public BasicConnectorBindingCommands Commands { get; }
|
||||
private readonly DocumentModelStore _store;
|
||||
private readonly ISpeckleApplication _speckleApplication;
|
||||
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
|
||||
|
||||
public BasicConnectorBinding(
|
||||
DocumentModelStore store,
|
||||
IBrowserBridge parent,
|
||||
ISpeckleApplication speckleApplication,
|
||||
IEventAggregator eventAggregator
|
||||
ITopLevelExceptionHandler topLevelExceptionHandler
|
||||
)
|
||||
{
|
||||
_store = store;
|
||||
_speckleApplication = speckleApplication;
|
||||
_topLevelExceptionHandler = topLevelExceptionHandler;
|
||||
Parent = parent;
|
||||
Commands = new BasicConnectorBindingCommands(parent);
|
||||
|
||||
eventAggregator.GetEvent<DocumentStoreChangedEvent>().Subscribe(OnDocumentStoreChangedEvent);
|
||||
_store.DocumentChanged += (_, _) =>
|
||||
_topLevelExceptionHandler.FireAndForget(async () =>
|
||||
{
|
||||
await Commands.NotifyDocumentChanged();
|
||||
});
|
||||
}
|
||||
|
||||
private async Task OnDocumentStoreChangedEvent(object _) => await Commands.NotifyDocumentChanged();
|
||||
|
||||
public string GetSourceApplicationName() => _speckleApplication.Slug;
|
||||
|
||||
public string GetSourceApplicationVersion() => _speckleApplication.HostApplicationVersion;
|
||||
|
||||
@@ -4,7 +4,6 @@ using ArcGIS.Desktop.Mapping;
|
||||
using ArcGIS.Desktop.Mapping.Events;
|
||||
using Speckle.Connectors.Common.Threading;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
using Speckle.Connectors.DUI.Models;
|
||||
using Speckle.Connectors.DUI.Utils;
|
||||
|
||||
@@ -13,18 +12,15 @@ namespace Speckle.Connectors.ArcGIS.Utils;
|
||||
public class ArcGISDocumentStore : DocumentModelStore
|
||||
{
|
||||
private readonly IThreadContext _threadContext;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
|
||||
public ArcGISDocumentStore(
|
||||
IJsonSerializer jsonSerializer,
|
||||
ITopLevelExceptionHandler topLevelExceptionHandler,
|
||||
IThreadContext threadContext,
|
||||
IEventAggregator eventAggregator
|
||||
IThreadContext threadContext
|
||||
)
|
||||
: base(jsonSerializer)
|
||||
{
|
||||
_threadContext = threadContext;
|
||||
_eventAggregator = eventAggregator;
|
||||
ActiveMapViewChangedEvent.Subscribe(a => topLevelExceptionHandler.CatchUnhandled(() => OnMapViewChanged(a)), true);
|
||||
ProjectSavingEvent.Subscribe(
|
||||
_ =>
|
||||
@@ -42,16 +38,13 @@ public class ArcGISDocumentStore : DocumentModelStore
|
||||
},
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
public override async Task OnDocumentStoreInitialized()
|
||||
{
|
||||
// in case plugin was loaded into already opened Map, read metadata from the current Map
|
||||
if (!IsDocumentInit && MapView.Active != null)
|
||||
{
|
||||
IsDocumentInit = true;
|
||||
LoadState();
|
||||
await _eventAggregator.GetEvent<DocumentStoreChangedEvent>().PublishAsync(new object());
|
||||
OnDocumentChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,7 +69,7 @@ public class ArcGISDocumentStore : DocumentModelStore
|
||||
/// <summary>
|
||||
/// On map view switch, this event trigger twice, first for outgoing view, second for incoming view.
|
||||
/// </summary>
|
||||
private async void OnMapViewChanged(ActiveMapViewChangedEventArgs args)
|
||||
private void OnMapViewChanged(ActiveMapViewChangedEventArgs args)
|
||||
{
|
||||
if (args.IncomingView is null)
|
||||
{
|
||||
@@ -85,7 +78,7 @@ public class ArcGISDocumentStore : DocumentModelStore
|
||||
|
||||
IsDocumentInit = true;
|
||||
LoadState();
|
||||
await _eventAggregator.GetEvent<DocumentStoreChangedEvent>().PublishAsync(new object());
|
||||
OnDocumentChanged();
|
||||
}
|
||||
|
||||
protected override void HostAppSaveState(string modelCardState) =>
|
||||
|
||||
+9
-6
@@ -4,7 +4,6 @@ using Speckle.Connectors.Autocad.HostApp.Extensions;
|
||||
using Speckle.Connectors.Common.Threading;
|
||||
using Speckle.Connectors.DUI.Bindings;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
using Speckle.Connectors.DUI.Models;
|
||||
using Speckle.Connectors.DUI.Models.Card;
|
||||
using Speckle.Sdk;
|
||||
@@ -22,6 +21,7 @@ public class AutocadBasicConnectorBinding : IBasicConnectorBinding
|
||||
private readonly DocumentModelStore _store;
|
||||
private readonly ISpeckleApplication _speckleApplication;
|
||||
private readonly IThreadContext _threadContext;
|
||||
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
|
||||
private readonly ILogger<AutocadBasicConnectorBinding> _logger;
|
||||
|
||||
public BasicConnectorBindingCommands Commands { get; }
|
||||
@@ -32,8 +32,8 @@ public class AutocadBasicConnectorBinding : IBasicConnectorBinding
|
||||
IAccountManager accountManager,
|
||||
ISpeckleApplication speckleApplication,
|
||||
ILogger<AutocadBasicConnectorBinding> logger,
|
||||
IEventAggregator eventAggregator,
|
||||
IThreadContext threadContext
|
||||
IThreadContext threadContext,
|
||||
ITopLevelExceptionHandler topLevelExceptionHandler
|
||||
)
|
||||
{
|
||||
_store = store;
|
||||
@@ -41,13 +41,16 @@ public class AutocadBasicConnectorBinding : IBasicConnectorBinding
|
||||
_accountManager = accountManager;
|
||||
_speckleApplication = speckleApplication;
|
||||
Commands = new BasicConnectorBindingCommands(parent);
|
||||
eventAggregator.GetEvent<DocumentStoreChangedEvent>().Subscribe(OnDocumentStoreChangedEvent);
|
||||
_logger = logger;
|
||||
_threadContext = threadContext;
|
||||
_topLevelExceptionHandler = topLevelExceptionHandler;
|
||||
_store.DocumentChanged += (_, _) =>
|
||||
_topLevelExceptionHandler.FireAndForget(async () =>
|
||||
{
|
||||
await Commands.NotifyDocumentChanged();
|
||||
});
|
||||
}
|
||||
|
||||
private async Task OnDocumentStoreChangedEvent(object _) => await Commands.NotifyDocumentChanged();
|
||||
|
||||
public string GetConnectorVersion() => _speckleApplication.SpeckleVersion;
|
||||
|
||||
public string GetSourceApplicationName() => _speckleApplication.Slug;
|
||||
|
||||
+9
-2
@@ -2,6 +2,7 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Speckle.Connectors.Common.Cancellation;
|
||||
using Speckle.Connectors.Common.Operations;
|
||||
using Speckle.Connectors.Common.Threading;
|
||||
using Speckle.Connectors.DUI.Bindings;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Logging;
|
||||
@@ -25,6 +26,7 @@ public sealed class AutocadReceiveBinding : IReceiveBinding
|
||||
private readonly ILogger<AutocadReceiveBinding> _logger;
|
||||
private readonly IAutocadConversionSettingsFactory _autocadConversionSettingsFactory;
|
||||
private readonly ISpeckleApplication _speckleApplication;
|
||||
private readonly IThreadContext _threadContext;
|
||||
|
||||
private ReceiveBindingUICommands Commands { get; }
|
||||
|
||||
@@ -36,7 +38,8 @@ public sealed class AutocadReceiveBinding : IReceiveBinding
|
||||
IOperationProgressManager operationProgressManager,
|
||||
ILogger<AutocadReceiveBinding> logger,
|
||||
IAutocadConversionSettingsFactory autocadConversionSettingsFactory,
|
||||
ISpeckleApplication speckleApplication
|
||||
ISpeckleApplication speckleApplication,
|
||||
IThreadContext threadContext
|
||||
)
|
||||
{
|
||||
_store = store;
|
||||
@@ -46,13 +49,17 @@ public sealed class AutocadReceiveBinding : IReceiveBinding
|
||||
_logger = logger;
|
||||
_autocadConversionSettingsFactory = autocadConversionSettingsFactory;
|
||||
_speckleApplication = speckleApplication;
|
||||
_threadContext = threadContext;
|
||||
Parent = parent;
|
||||
Commands = new ReceiveBindingUICommands(parent);
|
||||
}
|
||||
|
||||
public void CancelReceive(string modelCardId) => _cancellationManager.CancelOperation(modelCardId);
|
||||
|
||||
public async Task Receive(string modelCardId)
|
||||
public async Task Receive(string modelCardId) =>
|
||||
await _threadContext.RunOnMainAsync(async () => await ReceiveInternal(modelCardId));
|
||||
|
||||
public async Task ReceiveInternal(string modelCardId)
|
||||
{
|
||||
using var scope = _serviceProvider.CreateScope();
|
||||
scope
|
||||
|
||||
+19
-28
@@ -1,48 +1,42 @@
|
||||
using Autodesk.AutoCAD.ApplicationServices;
|
||||
using Autodesk.AutoCAD.DatabaseServices;
|
||||
using Autodesk.AutoCAD.EditorInput;
|
||||
using Speckle.Connectors.Autocad.HostApp.Extensions;
|
||||
using Speckle.Connectors.Autocad.Plugin;
|
||||
using Speckle.Connectors.Common.Threading;
|
||||
using Speckle.Connectors.DUI.Bindings;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
|
||||
namespace Speckle.Connectors.Autocad.Bindings;
|
||||
|
||||
public class AutocadSelectionBinding : ISelectionBinding
|
||||
{
|
||||
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
|
||||
private readonly IThreadContext _threadContext;
|
||||
private const string SELECTION_EVENT = "setSelection";
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly HashSet<string> _visitedDocuments = new();
|
||||
private readonly HashSet<Document> _visitedDocuments = new();
|
||||
|
||||
public string Name => "selectionBinding";
|
||||
|
||||
public IBrowserBridge Parent { get; }
|
||||
|
||||
public AutocadSelectionBinding(IBrowserBridge parent, IEventAggregator eventAggregator)
|
||||
public AutocadSelectionBinding(
|
||||
IBrowserBridge parent,
|
||||
ITopLevelExceptionHandler topLevelExceptionHandler,
|
||||
IThreadContext threadContext
|
||||
)
|
||||
{
|
||||
_eventAggregator = eventAggregator;
|
||||
_topLevelExceptionHandler = topLevelExceptionHandler;
|
||||
_threadContext = threadContext;
|
||||
Parent = parent;
|
||||
|
||||
// POC: Use here Context for doc. In converters it's OK but we are still lacking to use context into bindings.
|
||||
// It is with the case of if binding created with already a document
|
||||
// This is valid when user opens acad file directly double clicking
|
||||
TryRegisterDocumentForSelection(Application.DocumentManager.MdiActiveDocument);
|
||||
eventAggregator.GetEvent<DocumentActivatedEvent>().Subscribe(OnDocumentChanged);
|
||||
eventAggregator.GetEvent<ImpliedSelectionChangedEvent>().Subscribe(OnSelectionChanged);
|
||||
eventAggregator.GetEvent<DocumentToBeDestroyedEvent>().Subscribe(OnDocumentDestroyed);
|
||||
Application.DocumentManager.DocumentActivated += (_, e) =>
|
||||
_topLevelExceptionHandler.CatchUnhandled(() => OnDocumentChanged(e.Document));
|
||||
}
|
||||
|
||||
private void OnDocumentDestroyed(DocumentCollectionEventArgs e)
|
||||
{
|
||||
if (!_visitedDocuments.Contains(e.Document.Name))
|
||||
{
|
||||
e.Document.ImpliedSelectionChanged -= DocumentOnImpliedSelectionChanged;
|
||||
_visitedDocuments.Remove(e.Document.Name);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDocumentChanged(DocumentCollectionEventArgs e) => TryRegisterDocumentForSelection(e.Document);
|
||||
private void OnDocumentChanged(Document? document) => TryRegisterDocumentForSelection(document);
|
||||
|
||||
private void TryRegisterDocumentForSelection(Document? document)
|
||||
{
|
||||
@@ -51,24 +45,21 @@ public class AutocadSelectionBinding : ISelectionBinding
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_visitedDocuments.Contains(document.Name))
|
||||
if (!_visitedDocuments.Contains(document))
|
||||
{
|
||||
document.ImpliedSelectionChanged += DocumentOnImpliedSelectionChanged;
|
||||
document.ImpliedSelectionChanged += (_, _) =>
|
||||
_topLevelExceptionHandler.FireAndForget(async () => await _threadContext.RunOnMainAsync(OnSelectionChanged));
|
||||
|
||||
_visitedDocuments.Add(document.Name);
|
||||
_visitedDocuments.Add(document);
|
||||
}
|
||||
}
|
||||
|
||||
// ReSharper disable once AsyncVoidMethod
|
||||
private async void DocumentOnImpliedSelectionChanged(object? sender, EventArgs e) =>
|
||||
await _eventAggregator.GetEvent<ImpliedSelectionChangedEvent>().PublishAsync(e);
|
||||
|
||||
// NOTE: Autocad 2022 caused problems, so we need to refactor things a bit in here to always store
|
||||
// selection info locally (and get it updated by the event, which we can control to run on the main thread).
|
||||
// Ui requests to GetSelection() should just return this local copy that is kept up to date by the event handler.
|
||||
private SelectionInfo _selectionInfo;
|
||||
|
||||
private async Task OnSelectionChanged(EventArgs _)
|
||||
private async Task OnSelectionChanged()
|
||||
{
|
||||
_selectionInfo = GetSelectionInternal();
|
||||
await Parent.Send(SELECTION_EVENT, _selectionInfo);
|
||||
|
||||
+22
-52
@@ -1,19 +1,16 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Autodesk.AutoCAD.ApplicationServices;
|
||||
using Autodesk.AutoCAD.DatabaseServices;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Speckle.Connectors.Autocad.HostApp.Extensions;
|
||||
using Speckle.Connectors.Autocad.Operations.Send;
|
||||
using Speckle.Connectors.Autocad.Plugin;
|
||||
using Speckle.Connectors.Common.Caching;
|
||||
using Speckle.Connectors.Common.Cancellation;
|
||||
using Speckle.Connectors.Common.Operations;
|
||||
using Speckle.Connectors.Common.Threading;
|
||||
using Speckle.Connectors.DUI.Bindings;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
using Speckle.Connectors.DUI.Exceptions;
|
||||
using Speckle.Connectors.DUI.Logging;
|
||||
using Speckle.Connectors.DUI.Models;
|
||||
@@ -42,7 +39,8 @@ public abstract class AutocadSendBaseBinding : ISendBinding
|
||||
private readonly ILogger<AutocadSendBinding> _logger;
|
||||
private readonly ISpeckleApplication _speckleApplication;
|
||||
private readonly IThreadContext _threadContext;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
|
||||
private readonly IAppIdleManager _idleManager;
|
||||
|
||||
/// <summary>
|
||||
/// Used internally to aggregate the changed objects' id. Note we're using a concurrent dictionary here as the expiry check method is not thread safe, and this was causing problems. See:
|
||||
@@ -63,7 +61,8 @@ public abstract class AutocadSendBaseBinding : ISendBinding
|
||||
ILogger<AutocadSendBinding> logger,
|
||||
ISpeckleApplication speckleApplication,
|
||||
IThreadContext threadContext,
|
||||
IEventAggregator eventAggregator
|
||||
ITopLevelExceptionHandler topLevelExceptionHandler,
|
||||
IAppIdleManager idleManager
|
||||
)
|
||||
{
|
||||
_store = store;
|
||||
@@ -75,46 +74,29 @@ public abstract class AutocadSendBaseBinding : ISendBinding
|
||||
_logger = logger;
|
||||
_speckleApplication = speckleApplication;
|
||||
_threadContext = threadContext;
|
||||
_eventAggregator = eventAggregator;
|
||||
_topLevelExceptionHandler = topLevelExceptionHandler;
|
||||
_idleManager = idleManager;
|
||||
Parent = parent;
|
||||
Commands = new SendBindingUICommands(parent);
|
||||
|
||||
Application.DocumentManager.DocumentActivated += (_, args) =>
|
||||
_topLevelExceptionHandler.CatchUnhandled(() => SubscribeToObjectChanges(args.Document));
|
||||
|
||||
if (Application.DocumentManager.CurrentDocument != null)
|
||||
{
|
||||
// catches the case when autocad just opens up with a blank new doc
|
||||
TryRegisterSubscribeToObjectChanges(Application.DocumentManager.CurrentDocument);
|
||||
SubscribeToObjectChanges(Application.DocumentManager.CurrentDocument);
|
||||
}
|
||||
// Since ids of the objects generates from same seed, we should clear the cache always whenever doc swapped.
|
||||
|
||||
eventAggregator.GetEvent<DocumentActivatedEvent>().Subscribe(SubscribeToObjectChanges);
|
||||
eventAggregator.GetEvent<DocumentStoreChangedEvent>().Subscribe(OnDocumentStoreChangedEvent);
|
||||
eventAggregator.GetEvent<DocumentToBeDestroyedEvent>().Subscribe(OnDocumentDestroyed);
|
||||
eventAggregator.GetEvent<ObjectAppendedEvent>().Subscribe(OnObjectAppended);
|
||||
eventAggregator.GetEvent<ObjectErasedEvent>().Subscribe(ObjectErased);
|
||||
eventAggregator.GetEvent<ObjectModifiedEvent>().Subscribe(ObjectModified);
|
||||
}
|
||||
|
||||
private void OnDocumentDestroyed(DocumentCollectionEventArgs args)
|
||||
{
|
||||
Document doc = args.Document;
|
||||
if (!_docSubsTracker.Contains(doc.Name))
|
||||
_store.DocumentChanged += (_, _) =>
|
||||
{
|
||||
doc.Database.ObjectAppended -= DatabaseOnObjectAppended;
|
||||
doc.Database.ObjectErased -= DatabaseOnObjectErased;
|
||||
doc.Database.ObjectModified -= DatabaseObjectModified;
|
||||
|
||||
_docSubsTracker.Remove(doc.Name);
|
||||
}
|
||||
_sendConversionCache.ClearCache();
|
||||
};
|
||||
}
|
||||
|
||||
private void OnDocumentStoreChangedEvent(object _) => _sendConversionCache.ClearCache();
|
||||
|
||||
private readonly List<string> _docSubsTracker = new();
|
||||
|
||||
private void SubscribeToObjectChanges(DocumentCollectionEventArgs e) =>
|
||||
TryRegisterSubscribeToObjectChanges(e.Document);
|
||||
|
||||
private void TryRegisterSubscribeToObjectChanges(Document? doc)
|
||||
private void SubscribeToObjectChanges(Document doc)
|
||||
{
|
||||
if (doc == null || doc.Database == null || _docSubsTracker.Contains(doc.Name))
|
||||
{
|
||||
@@ -122,33 +104,21 @@ public abstract class AutocadSendBaseBinding : ISendBinding
|
||||
}
|
||||
|
||||
_docSubsTracker.Add(doc.Name);
|
||||
doc.Database.ObjectAppended += DatabaseOnObjectAppended;
|
||||
doc.Database.ObjectErased += DatabaseOnObjectErased;
|
||||
doc.Database.ObjectModified += DatabaseObjectModified;
|
||||
doc.Database.ObjectAppended += (_, e) => OnObjectChanged(e.DBObject);
|
||||
doc.Database.ObjectErased += (_, e) => OnObjectChanged(e.DBObject);
|
||||
doc.Database.ObjectModified += (_, e) => OnObjectChanged(e.DBObject);
|
||||
}
|
||||
|
||||
private async void DatabaseOnObjectAppended(object sender, ObjectEventArgs e) =>
|
||||
await _eventAggregator.GetEvent<ObjectAppendedEvent>().PublishAsync(e);
|
||||
|
||||
private async void DatabaseOnObjectErased(object sender, ObjectErasedEventArgs e) =>
|
||||
await _eventAggregator.GetEvent<ObjectErasedEvent>().PublishAsync(e);
|
||||
|
||||
private async void DatabaseObjectModified(object sender, ObjectEventArgs e) =>
|
||||
await _eventAggregator.GetEvent<ObjectModifiedEvent>().PublishAsync(e);
|
||||
|
||||
private void OnObjectAppended(ObjectEventArgs e) => OnChangeChangedObjectIds(e.DBObject);
|
||||
|
||||
private void ObjectErased(ObjectErasedEventArgs e) => OnChangeChangedObjectIds(e.DBObject);
|
||||
|
||||
private void ObjectModified(ObjectEventArgs e) => OnChangeChangedObjectIds(e.DBObject);
|
||||
private void OnObjectChanged(DBObject dbObject) =>
|
||||
_topLevelExceptionHandler.CatchUnhandled(() => OnChangeChangedObjectIds(dbObject));
|
||||
|
||||
private void OnChangeChangedObjectIds(DBObject dBObject)
|
||||
{
|
||||
ChangedObjectIds[dBObject.GetSpeckleApplicationId()] = 1;
|
||||
_eventAggregator.GetEvent<IdleEvent>().OneTimeSubscribe(nameof(AutocadSendBinding), RunExpirationChecks);
|
||||
_idleManager.SubscribeToIdle(nameof(AutocadSendBinding), async () => await RunExpirationChecks());
|
||||
}
|
||||
|
||||
private async Task RunExpirationChecks(object _)
|
||||
private async Task RunExpirationChecks()
|
||||
{
|
||||
var senders = _store.GetSenders();
|
||||
string[] objectIdsList = ChangedObjectIds.Keys.ToArray();
|
||||
@@ -175,7 +145,7 @@ public abstract class AutocadSendBaseBinding : ISendBinding
|
||||
public List<ICardSetting> GetSendSettings() => [];
|
||||
|
||||
public async Task Send(string modelCardId) =>
|
||||
await _threadContext.RunOnWorkerAsync(async () => await SendInternal(modelCardId));
|
||||
await _threadContext.RunOnMainAsync(async () => await SendInternal(modelCardId));
|
||||
|
||||
protected abstract void InitializeSettings(IServiceProvider serviceProvider);
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ using Speckle.Connectors.Common.Cancellation;
|
||||
using Speckle.Connectors.Common.Threading;
|
||||
using Speckle.Connectors.DUI.Bindings;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
using Speckle.Connectors.DUI.Models;
|
||||
using Speckle.Connectors.DUI.Models.Card.SendFilter;
|
||||
using Speckle.Converters.Autocad;
|
||||
@@ -30,7 +29,8 @@ public sealed class AutocadSendBinding : AutocadSendBaseBinding
|
||||
IAutocadConversionSettingsFactory autocadConversionSettingsFactory,
|
||||
ISpeckleApplication speckleApplication,
|
||||
IThreadContext threadContext,
|
||||
IEventAggregator eventAggregator
|
||||
ITopLevelExceptionHandler topLevelExceptionHandler,
|
||||
IAppIdleManager appIdleManager
|
||||
)
|
||||
: base(
|
||||
store,
|
||||
@@ -43,7 +43,8 @@ public sealed class AutocadSendBinding : AutocadSendBaseBinding
|
||||
logger,
|
||||
speckleApplication,
|
||||
threadContext,
|
||||
eventAggregator
|
||||
topLevelExceptionHandler,
|
||||
appIdleManager
|
||||
)
|
||||
{
|
||||
_autocadConversionSettingsFactory = autocadConversionSettingsFactory;
|
||||
|
||||
+3
@@ -13,6 +13,7 @@ using Speckle.Connectors.Common.Operations;
|
||||
using Speckle.Connectors.Common.Threading;
|
||||
using Speckle.Connectors.DUI;
|
||||
using Speckle.Connectors.DUI.Bindings;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Models.Card.SendFilter;
|
||||
using Speckle.Connectors.DUI.WebView;
|
||||
using Speckle.Sdk.Models.GraphTraversal;
|
||||
@@ -48,6 +49,8 @@ public static class SharedRegistration
|
||||
serviceCollection.AddScoped<AutocadMaterialUnpacker>();
|
||||
serviceCollection.AddScoped<IAutocadMaterialBaker, AutocadMaterialBaker>();
|
||||
|
||||
serviceCollection.AddSingleton<IAppIdleManager, AutocadIdleManager>();
|
||||
|
||||
// operation progress manager
|
||||
serviceCollection.AddSingleton<IOperationProgressManager, OperationProgressManager>();
|
||||
|
||||
|
||||
+14
-20
@@ -1,6 +1,4 @@
|
||||
using Autodesk.AutoCAD.ApplicationServices;
|
||||
using Speckle.Connectors.Autocad.Plugin;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Models;
|
||||
using Speckle.Connectors.DUI.Utils;
|
||||
|
||||
@@ -11,28 +9,19 @@ public class AutocadDocumentStore : DocumentModelStore
|
||||
private const string NULL_DOCUMENT_NAME = "Null Doc";
|
||||
private string _previousDocName;
|
||||
private readonly AutocadDocumentManager _autocadDocumentManager;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
|
||||
|
||||
public AutocadDocumentStore(
|
||||
IJsonSerializer jsonSerializer,
|
||||
AutocadDocumentManager autocadDocumentManager,
|
||||
IEventAggregator eventAggregator
|
||||
ITopLevelExceptionHandler topLevelExceptionHandler
|
||||
)
|
||||
: base(jsonSerializer)
|
||||
{
|
||||
_autocadDocumentManager = autocadDocumentManager;
|
||||
_eventAggregator = eventAggregator;
|
||||
_topLevelExceptionHandler = topLevelExceptionHandler;
|
||||
_previousDocName = NULL_DOCUMENT_NAME;
|
||||
|
||||
eventAggregator.GetEvent<DocumentActivatedEvent>().Subscribe(DocChanged);
|
||||
|
||||
// since below event triggered as secondary, it breaks the logic in OnDocChangeInternal function, leaving it here for now.
|
||||
// Autodesk.AutoCAD.ApplicationServices.Application.DocumentWindowCollection.DocumentWindowActivated += (_, args) =>
|
||||
// OnDocChangeInternal((Document)args.DocumentWindow.Document);
|
||||
}
|
||||
|
||||
public override async Task OnDocumentStoreInitialized()
|
||||
{
|
||||
// POC: Will be addressed to move it into AutocadContext!
|
||||
if (Application.DocumentManager.MdiActiveDocument != null)
|
||||
{
|
||||
@@ -40,13 +29,18 @@ public class AutocadDocumentStore : DocumentModelStore
|
||||
// POC: this logic might go when we have document management in context
|
||||
// It is with the case of if binding created with already a document
|
||||
// This is valid when user opens acad file directly double clicking
|
||||
await TryDocChanged(Application.DocumentManager.MdiActiveDocument);
|
||||
OnDocChangeInternal(Application.DocumentManager.MdiActiveDocument);
|
||||
}
|
||||
|
||||
Application.DocumentManager.DocumentActivated += (_, e) =>
|
||||
topLevelExceptionHandler.CatchUnhandled(() => OnDocChangeInternal(e.Document));
|
||||
|
||||
// since below event triggered as secondary, it breaks the logic in OnDocChangeInternal function, leaving it here for now.
|
||||
// Autodesk.AutoCAD.ApplicationServices.Application.DocumentWindowCollection.DocumentWindowActivated += (_, args) =>
|
||||
// OnDocChangeInternal((Document)args.DocumentWindow.Document);
|
||||
}
|
||||
|
||||
private async Task DocChanged(DocumentCollectionEventArgs e) => await TryDocChanged(e.Document);
|
||||
|
||||
private async Task TryDocChanged(Document? doc)
|
||||
private void OnDocChangeInternal(Document? doc)
|
||||
{
|
||||
var currentDocName = doc != null ? doc.Name : NULL_DOCUMENT_NAME;
|
||||
if (_previousDocName == currentDocName)
|
||||
@@ -56,7 +50,7 @@ public class AutocadDocumentStore : DocumentModelStore
|
||||
|
||||
_previousDocName = currentDocName;
|
||||
LoadState();
|
||||
await _eventAggregator.GetEvent<DocumentStoreChangedEvent>().PublishAsync(new object());
|
||||
OnDocumentChanged();
|
||||
}
|
||||
|
||||
protected override void LoadState()
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.InterfaceGenerator;
|
||||
|
||||
namespace Speckle.Connectors.Autocad.HostApp;
|
||||
|
||||
public partial interface IAutocadIdleManager : IAppIdleManager;
|
||||
|
||||
[GenerateAutoInterface]
|
||||
public sealed class AutocadIdleManager(IIdleCallManager idleCallManager)
|
||||
: AppIdleManager(idleCallManager),
|
||||
IAutocadIdleManager
|
||||
{
|
||||
private readonly IIdleCallManager _idleCallManager = idleCallManager;
|
||||
|
||||
protected override void AddEvent() => Application.Idle += AutocadAppOnIdle;
|
||||
|
||||
private void AutocadAppOnIdle(object? sender, EventArgs e) =>
|
||||
_idleCallManager.AppOnIdle(() => Application.Idle -= AutocadAppOnIdle);
|
||||
}
|
||||
+19
-8
@@ -160,15 +160,21 @@ public class AutocadHostObjectBuilder(
|
||||
var converted = converter.Convert(obj);
|
||||
|
||||
// 2: handle result
|
||||
if (converted is Entity entity)
|
||||
switch (converted)
|
||||
{
|
||||
var bakedEntity = BakeObject(entity, obj, layerName);
|
||||
convertedEntities.Add(bakedEntity);
|
||||
}
|
||||
else if (converted is List<(Entity, Base)> fallbackConversionResult)
|
||||
{
|
||||
var bakedFallbackEntities = BakeObjectsAsGroup(fallbackConversionResult, obj, layerName, baseLayerNamePrefix);
|
||||
convertedEntities.UnionWith(bakedFallbackEntities);
|
||||
case Entity entity:
|
||||
var bakedEntity = BakeObject(entity, obj, layerName);
|
||||
convertedEntities.Add(bakedEntity);
|
||||
break;
|
||||
|
||||
case List<(Entity, Base)> listConversionResult: // this is from fallback conversion for brep/brepx/subdx/extrusionx
|
||||
var bakedFallbackEntities = BakeObjectsAsGroup(listConversionResult, obj, layerName, baseLayerNamePrefix);
|
||||
convertedEntities.UnionWith(bakedFallbackEntities);
|
||||
break;
|
||||
|
||||
default:
|
||||
// TODO: capture defualt case with report object here? Same as in Rhino
|
||||
break;
|
||||
}
|
||||
|
||||
tr.Commit();
|
||||
@@ -208,6 +214,11 @@ public class AutocadHostObjectBuilder(
|
||||
entities.Add(conversionResult);
|
||||
}
|
||||
|
||||
if (entities.Count <= 1) // return if empty list or only one, because we don't want to create empty or single item groups.
|
||||
{
|
||||
return entities;
|
||||
}
|
||||
|
||||
var tr = Application.DocumentManager.CurrentDocument.Database.TransactionManager.TopTransaction;
|
||||
var groupDictionary = (DBDictionary)
|
||||
tr.GetObject(Application.DocumentManager.CurrentDocument.Database.GroupDictionaryId, OpenMode.ForWrite);
|
||||
|
||||
@@ -4,7 +4,6 @@ using Autodesk.AutoCAD.Windows;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Speckle.Connectors.Common;
|
||||
using Speckle.Connectors.DUI;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
using Speckle.Connectors.DUI.WebView;
|
||||
#if AUTOCAD
|
||||
using Speckle.Connectors.Autocad.DependencyInjection;
|
||||
@@ -49,7 +48,6 @@ public class AutocadCommand
|
||||
services.AddCivil3dConverters();
|
||||
#endif
|
||||
Container = services.BuildServiceProvider();
|
||||
AutocadEvents.Register(Container.GetRequiredService<IEventAggregator>());
|
||||
Container.UseDUI();
|
||||
|
||||
var panelWebView = Container.GetRequiredService<DUI3ControlWebView>();
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
using Autodesk.AutoCAD.ApplicationServices;
|
||||
using Autodesk.AutoCAD.DatabaseServices;
|
||||
using Speckle.Connectors.Common.Threading;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
|
||||
namespace Speckle.Connectors.Autocad.Plugin;
|
||||
|
||||
public class DocumentActivatedEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
: ThreadedEvent<DocumentCollectionEventArgs>(threadContext, exceptionHandler);
|
||||
|
||||
public class DocumentToBeDestroyedEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
: ThreadedEvent<DocumentCollectionEventArgs>(threadContext, exceptionHandler);
|
||||
|
||||
public class ImpliedSelectionChangedEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
: ThreadedEvent<EventArgs>(threadContext, exceptionHandler);
|
||||
|
||||
public class ObjectAppendedEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
: ThreadedEvent<ObjectEventArgs>(threadContext, exceptionHandler);
|
||||
|
||||
public class ObjectErasedEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
: ThreadedEvent<ObjectErasedEventArgs>(threadContext, exceptionHandler);
|
||||
|
||||
public class ObjectModifiedEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
: ThreadedEvent<ObjectEventArgs>(threadContext, exceptionHandler);
|
||||
|
||||
public static class AutocadEvents
|
||||
{
|
||||
public static void Register(IEventAggregator eventAggregator)
|
||||
{
|
||||
Application.Idle += async (_, e) => await eventAggregator.GetEvent<IdleEvent>().PublishAsync(e);
|
||||
|
||||
Application.DocumentManager.DocumentActivated += async (_, e) =>
|
||||
await eventAggregator.GetEvent<DocumentActivatedEvent>().PublishAsync(e);
|
||||
Application.DocumentManager.DocumentToBeDestroyed += async (_, e) =>
|
||||
await eventAggregator.GetEvent<DocumentToBeDestroyedEvent>().PublishAsync(e);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -21,6 +21,7 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\AutocadColorUnpacker.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\AutocadGroupBaker.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\AutocadGroupUnpacker.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\AutocadIdleManager.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\AutocadInstanceBaker.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\AutocadInstanceUnpacker.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\AutocadLayerBaker.cs" />
|
||||
@@ -40,7 +41,6 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\AutocadRootObject.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\AutocadRootObjectBaseBuilder.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\AutocadRootObjectBuilder.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Plugin\AutocadEvents.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Plugin\AutocadRibbon.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Plugin\AutocadExtensionApplication.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Plugin\AutocadCommand.cs" />
|
||||
|
||||
@@ -6,7 +6,6 @@ using Speckle.Connectors.Common.Cancellation;
|
||||
using Speckle.Connectors.Common.Threading;
|
||||
using Speckle.Connectors.DUI.Bindings;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
using Speckle.Connectors.DUI.Models;
|
||||
using Speckle.Connectors.DUI.Models.Card.SendFilter;
|
||||
using Speckle.Converters.Autocad;
|
||||
@@ -34,7 +33,8 @@ public sealed class Civil3dSendBinding : AutocadSendBaseBinding
|
||||
IAutocadConversionSettingsFactory autocadConversionSettingsFactory,
|
||||
ISpeckleApplication speckleApplication,
|
||||
IThreadContext threadContext,
|
||||
IEventAggregator eventAggregator
|
||||
ITopLevelExceptionHandler topLevelExceptionHandler,
|
||||
IAppIdleManager appIdleManager
|
||||
)
|
||||
: base(
|
||||
store,
|
||||
@@ -47,7 +47,8 @@ public sealed class Civil3dSendBinding : AutocadSendBaseBinding
|
||||
logger,
|
||||
speckleApplication,
|
||||
threadContext,
|
||||
eventAggregator
|
||||
topLevelExceptionHandler,
|
||||
appIdleManager
|
||||
)
|
||||
{
|
||||
_civil3dConversionSettingsFactory = civil3dConversionSettingsFactory;
|
||||
|
||||
+8
-7
@@ -1,6 +1,5 @@
|
||||
using Speckle.Connectors.DUI.Bindings;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
using Speckle.Connectors.DUI.Models;
|
||||
using Speckle.Connectors.DUI.Models.Card;
|
||||
using Speckle.Sdk;
|
||||
@@ -11,7 +10,7 @@ public class CsiSharedBasicConnectorBinding : IBasicConnectorBinding
|
||||
{
|
||||
private readonly ISpeckleApplication _speckleApplication;
|
||||
private readonly DocumentModelStore _store;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
|
||||
public string Name => "baseBinding";
|
||||
public IBrowserBridge Parent { get; }
|
||||
public BasicConnectorBindingCommands Commands { get; }
|
||||
@@ -20,20 +19,22 @@ public class CsiSharedBasicConnectorBinding : IBasicConnectorBinding
|
||||
IBrowserBridge parent,
|
||||
ISpeckleApplication speckleApplication,
|
||||
DocumentModelStore store,
|
||||
IEventAggregator eventAggregator
|
||||
ITopLevelExceptionHandler topLevelExceptionHandler
|
||||
)
|
||||
{
|
||||
Parent = parent;
|
||||
_speckleApplication = speckleApplication;
|
||||
_store = store;
|
||||
_topLevelExceptionHandler = topLevelExceptionHandler;
|
||||
Commands = new BasicConnectorBindingCommands(Parent);
|
||||
_eventAggregator = eventAggregator;
|
||||
|
||||
_eventAggregator.GetEvent<DocumentStoreChangedEvent>().Subscribe(OnDocumentStoreChangedEvent);
|
||||
_store.DocumentChanged += (_, _) =>
|
||||
_topLevelExceptionHandler.FireAndForget(async () =>
|
||||
{
|
||||
await Commands.NotifyDocumentChanged();
|
||||
});
|
||||
}
|
||||
|
||||
private async Task OnDocumentStoreChangedEvent(object _) => await Commands.NotifyDocumentChanged();
|
||||
|
||||
public string GetConnectorVersion() => _speckleApplication.SpeckleVersion;
|
||||
|
||||
public string GetSourceApplicationName() => _speckleApplication.Slug;
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
using Speckle.Connectors.CSiShared.Events;
|
||||
using Speckle.Connectors.CSiShared.HostApp;
|
||||
using Speckle.Connectors.CSiShared.HostApp;
|
||||
using Speckle.Connectors.CSiShared.Utils;
|
||||
using Speckle.Connectors.DUI.Bindings;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
using Speckle.Converters.CSiShared.Utils;
|
||||
using Timer = System.Timers.Timer;
|
||||
|
||||
namespace Speckle.Connectors.CSiShared.Bindings;
|
||||
|
||||
public sealed class CsiSharedSelectionBinding : ISelectionBinding
|
||||
public class CsiSharedSelectionBinding : ISelectionBinding, IDisposable
|
||||
{
|
||||
private bool _disposed;
|
||||
private readonly Timer _selectionTimer;
|
||||
private readonly ICsiApplicationService _csiApplicationService;
|
||||
private HashSet<string> _lastSelection = new();
|
||||
|
||||
@@ -19,16 +20,18 @@ public sealed class CsiSharedSelectionBinding : ISelectionBinding
|
||||
public CsiSharedSelectionBinding(
|
||||
IBrowserBridge parent,
|
||||
ICsiApplicationService csiApplicationService,
|
||||
IEventAggregator eventAggregator
|
||||
ITopLevelExceptionHandler topLevelExceptionHandler
|
||||
)
|
||||
{
|
||||
Parent = parent;
|
||||
_csiApplicationService = csiApplicationService;
|
||||
|
||||
eventAggregator.GetEvent<SelectionBindingEvent>().SubscribePeriodic(TimeSpan.FromSeconds(1), CheckSelectionChanged);
|
||||
_selectionTimer = new Timer(1000);
|
||||
_selectionTimer.Elapsed += (_, _) => topLevelExceptionHandler.CatchUnhandled(CheckSelectionChanged);
|
||||
_selectionTimer.Start();
|
||||
}
|
||||
|
||||
private void CheckSelectionChanged(object _)
|
||||
private void CheckSelectionChanged()
|
||||
{
|
||||
var currentSelection = GetSelection();
|
||||
var currentIds = new HashSet<string>(currentSelection.SelectedObjectIds);
|
||||
@@ -40,6 +43,24 @@ public sealed class CsiSharedSelectionBinding : ISelectionBinding
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_selectionTimer?.Dispose();
|
||||
}
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the selection and creates an encoded ID (objectType and objectName).
|
||||
/// </summary>
|
||||
@@ -56,22 +77,18 @@ public sealed class CsiSharedSelectionBinding : ISelectionBinding
|
||||
|
||||
var encodedIds = new List<string>(numberItems);
|
||||
var typeCounts = new Dictionary<string, int>();
|
||||
|
||||
for (int i = 0; i < numberItems; i++)
|
||||
{
|
||||
var typeKey = (ModelObjectType)objectType[i];
|
||||
var typeName = typeKey.ToString();
|
||||
|
||||
encodedIds.Add(ObjectIdentifier.Encode(objectType[i], objectName[i]));
|
||||
typeCounts[typeName] = (typeCounts.TryGetValue(typeName, out var count) ? count : 0) + 1; // NOTE: Cross-framework compatibility (net 48 and net8)
|
||||
}
|
||||
|
||||
var summary =
|
||||
encodedIds.Count == 0
|
||||
? "No objects selected."
|
||||
: $"{encodedIds.Count} objects ({string.Join(", ",
|
||||
typeCounts.Select(kv => $"{kv.Value} {kv.Key}"))})";
|
||||
|
||||
return new SelectionInfo(encodedIds, summary);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
using Speckle.Connectors.Common.Threading;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
|
||||
namespace Speckle.Connectors.CSiShared.Events;
|
||||
|
||||
public class ModelChangedEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
: PeriodicThreadedEvent(threadContext, exceptionHandler);
|
||||
@@ -1,8 +0,0 @@
|
||||
using Speckle.Connectors.Common.Threading;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
|
||||
namespace Speckle.Connectors.CSiShared.Events;
|
||||
|
||||
public class SelectionBindingEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
: PeriodicThreadedEvent(threadContext, exceptionHandler);
|
||||
@@ -1,22 +1,24 @@
|
||||
using System.IO;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Speckle.Connectors.CSiShared.Events;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Models;
|
||||
using Speckle.Connectors.DUI.Utils;
|
||||
using Speckle.Sdk;
|
||||
using Speckle.Sdk.Helpers;
|
||||
using Speckle.Sdk.Logging;
|
||||
using Timer = System.Timers.Timer;
|
||||
|
||||
namespace Speckle.Connectors.CSiShared.HostApp;
|
||||
|
||||
public class CsiDocumentModelStore : DocumentModelStore
|
||||
public class CsiDocumentModelStore : DocumentModelStore, IDisposable
|
||||
{
|
||||
private readonly ISpeckleApplication _speckleApplication;
|
||||
private readonly ILogger<CsiDocumentModelStore> _logger;
|
||||
private readonly ICsiApplicationService _csiApplicationService;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
|
||||
private readonly Timer _modelCheckTimer;
|
||||
private string _lastModelFilename = string.Empty;
|
||||
private bool _disposed;
|
||||
private string HostAppUserDataPath { get; set; }
|
||||
private string DocumentStateFile { get; set; }
|
||||
private string ModelPathHash { get; set; }
|
||||
@@ -26,18 +28,22 @@ public class CsiDocumentModelStore : DocumentModelStore
|
||||
ISpeckleApplication speckleApplication,
|
||||
ILogger<CsiDocumentModelStore> logger,
|
||||
ICsiApplicationService csiApplicationService,
|
||||
IEventAggregator eventAggregator
|
||||
ITopLevelExceptionHandler topLevelExceptionHandler
|
||||
)
|
||||
: base(jsonSerializer)
|
||||
{
|
||||
_speckleApplication = speckleApplication;
|
||||
_logger = logger;
|
||||
_csiApplicationService = csiApplicationService;
|
||||
_eventAggregator = eventAggregator;
|
||||
eventAggregator.GetEvent<ModelChangedEvent>().SubscribePeriodic(TimeSpan.FromSeconds(1), CheckModelChanges);
|
||||
_topLevelExceptionHandler = topLevelExceptionHandler;
|
||||
|
||||
// initialize timer to check for model changes
|
||||
_modelCheckTimer = new Timer(1000);
|
||||
_modelCheckTimer.Elapsed += (_, _) => _topLevelExceptionHandler.CatchUnhandled(CheckModelChanges);
|
||||
_modelCheckTimer.Start();
|
||||
}
|
||||
|
||||
private async Task CheckModelChanges(object _)
|
||||
private void CheckModelChanges()
|
||||
{
|
||||
string currentFilename = _csiApplicationService.SapModel.GetModelFilename();
|
||||
|
||||
@@ -49,8 +55,7 @@ public class CsiDocumentModelStore : DocumentModelStore
|
||||
_lastModelFilename = currentFilename;
|
||||
SetPaths();
|
||||
LoadState();
|
||||
|
||||
await _eventAggregator.GetEvent<DocumentStoreChangedEvent>().PublishAsync(new object());
|
||||
OnDocumentChanged();
|
||||
}
|
||||
|
||||
public override Task OnDocumentStoreInitialized()
|
||||
@@ -120,4 +125,25 @@ public class CsiDocumentModelStore : DocumentModelStore
|
||||
ClearAndSave();
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
_modelCheckTimer.Dispose();
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Bindings\CsiSharedBasicConnectorBinding.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Bindings\CsiSharedSelectionBinding.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Bindings\CsiSharedSendBinding.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Events\ModelChangedEvent.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Events\SelectionBindingEvent.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Filters\CsiSharedSelectionFilter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\MaterialUnpacker.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\CsiSendCollectionManager.cs" />
|
||||
|
||||
+8
-9
@@ -1,35 +1,34 @@
|
||||
using Speckle.Connector.Navisworks.Services;
|
||||
using Speckle.Connectors.DUI.Bindings;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
|
||||
namespace Speckle.Connector.Navisworks.Bindings;
|
||||
|
||||
public class NavisworksSelectionBinding : ISelectionBinding
|
||||
{
|
||||
private readonly IAppIdleManager _idleManager;
|
||||
private readonly IElementSelectionService _selectionService;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private const string SELECTION_EVENT = "setSelection";
|
||||
public string Name { get; } = "selectionBinding";
|
||||
public IBrowserBridge Parent { get; }
|
||||
|
||||
public NavisworksSelectionBinding(
|
||||
IAppIdleManager idleManager,
|
||||
IBrowserBridge parent,
|
||||
IElementSelectionService selectionService,
|
||||
IEventAggregator eventAggregator
|
||||
IElementSelectionService selectionService
|
||||
)
|
||||
{
|
||||
_idleManager = idleManager;
|
||||
_selectionService = selectionService;
|
||||
_eventAggregator = eventAggregator;
|
||||
Parent = parent;
|
||||
|
||||
eventAggregator.GetEvent<SelectionChangedEvent>().Subscribe(OnSelectionChange);
|
||||
NavisworksApp.ActiveDocument.CurrentSelection.Changed += OnSelectionChange;
|
||||
}
|
||||
|
||||
private void OnSelectionChange(object _) =>
|
||||
_eventAggregator.GetEvent<IdleEvent>().OneTimeSubscribe(nameof(NavisworksSelectionBinding), UpdateSelectionAsync);
|
||||
private void OnSelectionChange(object? o, EventArgs eventArgs) =>
|
||||
_idleManager.SubscribeToIdle(nameof(NavisworksSelectionBinding), async () => await UpdateSelectionAsync());
|
||||
|
||||
private async Task UpdateSelectionAsync(object _)
|
||||
private async Task UpdateSelectionAsync()
|
||||
{
|
||||
var selInfo = GetSelection();
|
||||
await Parent.Send(SELECTION_EVENT, selInfo);
|
||||
|
||||
+9
-2
@@ -4,6 +4,7 @@ using Speckle.Connector.Navisworks.Operations.Send.Settings;
|
||||
using Speckle.Connector.Navisworks.Services;
|
||||
using Speckle.Connectors.Common.Cancellation;
|
||||
using Speckle.Connectors.Common.Operations;
|
||||
using Speckle.Connectors.Common.Threading;
|
||||
using Speckle.Connectors.DUI.Bindings;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Exceptions;
|
||||
@@ -38,6 +39,7 @@ public class NavisworksSendBinding : ISendBinding
|
||||
private readonly INavisworksConversionSettingsFactory _conversionSettingsFactory;
|
||||
private readonly ToSpeckleSettingsManagerNavisworks _toSpeckleSettingsManagerNavisworks;
|
||||
private readonly IElementSelectionService _selectionService;
|
||||
private readonly IThreadContext _threadContext;
|
||||
|
||||
public NavisworksSendBinding(
|
||||
DocumentModelStore store,
|
||||
@@ -51,7 +53,8 @@ public class NavisworksSendBinding : ISendBinding
|
||||
ISdkActivityFactory activityFactory,
|
||||
INavisworksConversionSettingsFactory conversionSettingsFactory,
|
||||
ToSpeckleSettingsManagerNavisworks toSpeckleSettingsManagerNavisworks,
|
||||
IElementSelectionService selectionService
|
||||
IElementSelectionService selectionService,
|
||||
IThreadContext threadContext
|
||||
)
|
||||
{
|
||||
Parent = parent;
|
||||
@@ -67,6 +70,7 @@ public class NavisworksSendBinding : ISendBinding
|
||||
_conversionSettingsFactory = conversionSettingsFactory;
|
||||
_toSpeckleSettingsManagerNavisworks = toSpeckleSettingsManagerNavisworks;
|
||||
_selectionService = selectionService;
|
||||
_threadContext = threadContext;
|
||||
SubscribeToNavisworksEvents();
|
||||
}
|
||||
|
||||
@@ -82,7 +86,10 @@ public class NavisworksSendBinding : ISendBinding
|
||||
new ConvertHiddenElementsSetting(false),
|
||||
];
|
||||
|
||||
public async Task Send(string modelCardId)
|
||||
public async Task Send(string modelCardId) =>
|
||||
await _threadContext.RunOnMainAsync(async () => await SendInternal(modelCardId));
|
||||
|
||||
private async Task SendInternal(string modelCardId)
|
||||
{
|
||||
using var activity = _activityFactory.Start();
|
||||
try
|
||||
|
||||
+3
@@ -11,6 +11,7 @@ using Speckle.Connectors.Common.Operations;
|
||||
using Speckle.Connectors.Common.Threading;
|
||||
using Speckle.Connectors.DUI;
|
||||
using Speckle.Connectors.DUI.Bindings;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Models;
|
||||
using Speckle.Connectors.DUI.Models.Card.SendFilter;
|
||||
using Speckle.Connectors.DUI.WebView;
|
||||
@@ -51,6 +52,8 @@ public static class NavisworksConnectorServiceRegistration
|
||||
serviceCollection.AddScoped<NavisworksMaterialUnpacker>();
|
||||
serviceCollection.AddScoped<NavisworksColorUnpacker>();
|
||||
|
||||
serviceCollection.AddSingleton<IAppIdleManager, NavisworksIdleManager>();
|
||||
|
||||
// Sending operations
|
||||
serviceCollection.AddScoped<IRootObjectBuilder<NAV.ModelItem>, NavisworksRootObjectBuilder>();
|
||||
serviceCollection.AddScoped<SendOperation<NAV.ModelItem>>();
|
||||
|
||||
+23
-26
@@ -1,7 +1,7 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Speckle.Connector.Navisworks.Bindings;
|
||||
using Speckle.Connectors.DUI.Bindings;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
|
||||
namespace Speckle.Connector.Navisworks.HostApp;
|
||||
|
||||
@@ -12,7 +12,8 @@ namespace Speckle.Connector.Navisworks.HostApp;
|
||||
public sealed class NavisworksDocumentEvents
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
|
||||
private readonly IAppIdleManager _idleManager;
|
||||
private readonly object _subscriptionLock = new();
|
||||
|
||||
private bool _isSubscribed;
|
||||
@@ -25,22 +26,23 @@ public sealed class NavisworksDocumentEvents
|
||||
/// Initializes a new instance of the <see cref="NavisworksDocumentEvents"/> class and subscribes to document events.
|
||||
/// </summary>
|
||||
/// <param name="serviceProvider">The service provider for dependency injection.</param>
|
||||
public NavisworksDocumentEvents(IServiceProvider serviceProvider, IEventAggregator eventAggregator)
|
||||
public NavisworksDocumentEvents(
|
||||
IServiceProvider serviceProvider,
|
||||
ITopLevelExceptionHandler topLevelExceptionHandler,
|
||||
IAppIdleManager idleManager
|
||||
)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
_eventAggregator = eventAggregator;
|
||||
_topLevelExceptionHandler = topLevelExceptionHandler;
|
||||
_idleManager = idleManager;
|
||||
|
||||
_eventAggregator.GetEvent<ActiveDocumentChangingEvent>().Subscribe(UnsubscribeFromDocumentModelEvents);
|
||||
_eventAggregator.GetEvent<ActiveDocumentChangedEvent>().Subscribe(SubscribeToDocumentModelEvents);
|
||||
_eventAggregator.GetEvent<CollectionChangingEvent>().Subscribe(HandleDocumentModelCountChanging);
|
||||
_eventAggregator.GetEvent<CollectionChangedEvent>().Subscribe(HandleDocumentModelCountChanged);
|
||||
SubscribeToDocumentModelEvents(new object());
|
||||
SubscribeToDocumentModelEvents();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribes to document-level events to monitor model state changes.
|
||||
/// </summary>
|
||||
private void SubscribeToDocumentModelEvents(object _)
|
||||
private void SubscribeToDocumentModelEvents()
|
||||
{
|
||||
lock (_subscriptionLock)
|
||||
{
|
||||
@@ -52,39 +54,34 @@ public sealed class NavisworksDocumentEvents
|
||||
var activeDocument = NavisworksApp.ActiveDocument;
|
||||
if (activeDocument != null)
|
||||
{
|
||||
activeDocument.Models.CollectionChanged += OnCollectionChanged;
|
||||
activeDocument.Models.CollectionChanging += OnCollectionChanging;
|
||||
activeDocument.Models.CollectionChanged += HandleDocumentModelCountChanged;
|
||||
activeDocument.Models.CollectionChanging += HandleDocumentModelCountChanging;
|
||||
}
|
||||
|
||||
_isSubscribed = true;
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnCollectionChanged(object sender, EventArgs _) =>
|
||||
await _eventAggregator.GetEvent<CollectionChangedEvent>().PublishAsync(sender);
|
||||
|
||||
private async void OnCollectionChanging(object sender, EventArgs _) =>
|
||||
await _eventAggregator.GetEvent<CollectionChangingEvent>().PublishAsync(sender);
|
||||
|
||||
/// <summary>
|
||||
/// Tracks the current model count before changes occur.
|
||||
/// </summary>
|
||||
private void HandleDocumentModelCountChanging(object sender) =>
|
||||
private void HandleDocumentModelCountChanging(object sender, EventArgs e) =>
|
||||
_priorModelCount = ((NAV.Document)sender).Models.Count;
|
||||
|
||||
/// <summary>
|
||||
/// Schedules processing of model count changes during idle time.
|
||||
/// </summary>
|
||||
private void HandleDocumentModelCountChanged(object sender)
|
||||
private void HandleDocumentModelCountChanged(object sender, EventArgs e)
|
||||
{
|
||||
_finalModelCount = ((NAV.Document)sender).Models.Count;
|
||||
|
||||
_eventAggregator
|
||||
.GetEvent<IdleEvent>()
|
||||
.OneTimeSubscribe(nameof(NavisworksDocumentEvents), ProcessModelStateChangeAsync);
|
||||
_topLevelExceptionHandler.CatchUnhandled(
|
||||
() =>
|
||||
_idleManager.SubscribeToIdle(nameof(NavisworksDocumentEvents), async () => await ProcessModelStateChangeAsync())
|
||||
);
|
||||
}
|
||||
|
||||
private async Task ProcessModelStateChangeAsync(object _)
|
||||
private async Task ProcessModelStateChangeAsync()
|
||||
{
|
||||
if (_isProcessing)
|
||||
{
|
||||
@@ -133,8 +130,8 @@ public sealed class NavisworksDocumentEvents
|
||||
|
||||
private void UnsubscribeFromModelEvents(NAV.Document document)
|
||||
{
|
||||
document.Models.CollectionChanged -= OnCollectionChanged;
|
||||
document.Models.CollectionChanging -= OnCollectionChanging;
|
||||
document.Models.CollectionChanged -= HandleDocumentModelCountChanged;
|
||||
document.Models.CollectionChanging -= HandleDocumentModelCountChanging;
|
||||
|
||||
var sendBinding = _serviceProvider
|
||||
.GetRequiredService<IEnumerable<IBinding>>()
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
|
||||
namespace Speckle.Connector.Navisworks.HostApp;
|
||||
|
||||
/// <summary>
|
||||
/// Manages the scheduling of deferred operations during Navisworks idle periods.
|
||||
/// Ensures UI updates and operations are batched efficiently to prevent UI freezing.
|
||||
/// </summary>
|
||||
public sealed class NavisworksIdleManager : AppIdleManager
|
||||
{
|
||||
private readonly IIdleCallManager _idleCallManager;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the NavisworksIdleManager.
|
||||
/// </summary>
|
||||
/// <param name="idleCallManager">The manager responsible for queuing and executing deferred operations.</param>
|
||||
public NavisworksIdleManager(IIdleCallManager idleCallManager)
|
||||
: base(idleCallManager)
|
||||
{
|
||||
_idleCallManager = idleCallManager;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribes to Navisworks idle events when operations are queued.
|
||||
/// </summary>
|
||||
protected override void AddEvent() => NavisworksApp.Idle += NavisworksAppOnIdle;
|
||||
|
||||
private void NavisworksAppOnIdle(object? sender, EventArgs e) =>
|
||||
_idleCallManager.AppOnIdle(() => NavisworksApp.Idle -= NavisworksAppOnIdle);
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
using Speckle.Connectors.Common.Threading;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
|
||||
namespace Speckle.Connector.Navisworks;
|
||||
|
||||
public class SelectionChangedEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
: ThreadedEvent<object>(threadContext, exceptionHandler);
|
||||
|
||||
public class ActiveDocumentChangingEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
: ThreadedEvent<object>(threadContext, exceptionHandler);
|
||||
|
||||
public class ActiveDocumentChangedEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
: ThreadedEvent<object>(threadContext, exceptionHandler);
|
||||
|
||||
public class CollectionChangingEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
: ThreadedEvent<object>(threadContext, exceptionHandler);
|
||||
|
||||
public class CollectionChangedEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
: ThreadedEvent<object>(threadContext, exceptionHandler);
|
||||
|
||||
public static class NavisworksEvents
|
||||
{
|
||||
public static void Register(IEventAggregator eventAggregator)
|
||||
{
|
||||
NavisworksApp.Idle += async (_, _) => await eventAggregator.GetEvent<IdleEvent>().PublishAsync(new object());
|
||||
NavisworksApp.ActiveDocument.CurrentSelection.Changed += async (_, _) =>
|
||||
await eventAggregator.GetEvent<SelectionChangedEvent>().PublishAsync(new object());
|
||||
NavisworksApp.ActiveDocumentChanging += async (_, _) =>
|
||||
await eventAggregator.GetEvent<ActiveDocumentChangingEvent>().PublishAsync(new object());
|
||||
NavisworksApp.ActiveDocumentChanged += async (_, _) =>
|
||||
await eventAggregator.GetEvent<ActiveDocumentChangedEvent>().PublishAsync(new object());
|
||||
}
|
||||
}
|
||||
-2
@@ -7,7 +7,6 @@ using Speckle.Connector.Navisworks.HostApp;
|
||||
using Speckle.Connector.Navisworks.Plugin.Tools;
|
||||
using Speckle.Connectors.Common;
|
||||
using Speckle.Connectors.DUI;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
using Speckle.Connectors.DUI.WebView;
|
||||
using Speckle.Converter.Navisworks.DependencyInjection;
|
||||
using Speckle.Sdk.Host;
|
||||
@@ -46,7 +45,6 @@ internal sealed class Connector : NAV.Plugins.DockPanePlugin
|
||||
services.AddNavisworksConverter();
|
||||
|
||||
Container = services.BuildServiceProvider();
|
||||
NavisworksEvents.Register(Container.GetRequiredService<IEventAggregator>());
|
||||
Container.UseDUI();
|
||||
Container.GetRequiredService<NavisworksDocumentEvents>();
|
||||
|
||||
|
||||
+1
-1
@@ -18,8 +18,8 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\NavisworksColorUnpacker.cs"/>
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\NavisworksDocumentEvents.cs"/>
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\NavisworksDocumentModelStore.cs"/>
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\NavisworksIdleManager.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\NavisworksMaterialUnpacker.cs"/>
|
||||
<Compile Include="$(MSBuildThisFileDirectory)NavisworksEvents.cs"/>
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\GeometryNodeMerger.cs"/>
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\NavisworksRootObjectBuilder.cs"/>
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\ConvertHiddenEleementsSetting.cs"/>
|
||||
|
||||
+10
-7
@@ -1,7 +1,6 @@
|
||||
using Autodesk.Revit.DB;
|
||||
using Revit.Async;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
using Speckle.Connectors.DUI.Models;
|
||||
using Speckle.Connectors.DUI.Models.Card;
|
||||
using Speckle.Connectors.RevitShared;
|
||||
@@ -21,15 +20,16 @@ internal sealed class BasicConnectorBindingRevit : IBasicConnectorBinding
|
||||
public BasicConnectorBindingCommands Commands { get; }
|
||||
|
||||
private readonly DocumentModelStore _store;
|
||||
private readonly IRevitContext _revitContext;
|
||||
private readonly RevitContext _revitContext;
|
||||
private readonly ISpeckleApplication _speckleApplication;
|
||||
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
|
||||
|
||||
public BasicConnectorBindingRevit(
|
||||
DocumentModelStore store,
|
||||
IBrowserBridge parent,
|
||||
IRevitContext revitContext,
|
||||
RevitContext revitContext,
|
||||
ISpeckleApplication speckleApplication,
|
||||
IEventAggregator eventAggregator
|
||||
ITopLevelExceptionHandler topLevelExceptionHandler
|
||||
)
|
||||
{
|
||||
Name = "baseBinding";
|
||||
@@ -37,13 +37,16 @@ internal sealed class BasicConnectorBindingRevit : IBasicConnectorBinding
|
||||
_store = store;
|
||||
_revitContext = revitContext;
|
||||
_speckleApplication = speckleApplication;
|
||||
_topLevelExceptionHandler = topLevelExceptionHandler;
|
||||
Commands = new BasicConnectorBindingCommands(parent);
|
||||
|
||||
eventAggregator.GetEvent<DocumentStoreChangedEvent>().Subscribe(OnDocumentStoreChangedEvent);
|
||||
_store.DocumentChanged += (_, _) =>
|
||||
_topLevelExceptionHandler.FireAndForget(async () =>
|
||||
{
|
||||
await Commands.NotifyDocumentChanged();
|
||||
});
|
||||
}
|
||||
|
||||
private async Task OnDocumentStoreChangedEvent(object _) => await Commands.NotifyDocumentChanged();
|
||||
|
||||
public string GetConnectorVersion() => _speckleApplication.SpeckleVersion;
|
||||
|
||||
public string GetSourceApplicationName() => _speckleApplication.Slug;
|
||||
|
||||
@@ -8,7 +8,6 @@ using Speckle.Connectors.Common.Cancellation;
|
||||
using Speckle.Connectors.Common.Operations;
|
||||
using Speckle.Connectors.DUI.Bindings;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
using Speckle.Connectors.DUI.Exceptions;
|
||||
using Speckle.Connectors.DUI.Logging;
|
||||
using Speckle.Connectors.DUI.Models;
|
||||
@@ -17,7 +16,6 @@ using Speckle.Connectors.DUI.Models.Card.SendFilter;
|
||||
using Speckle.Connectors.DUI.Settings;
|
||||
using Speckle.Connectors.Revit.HostApp;
|
||||
using Speckle.Connectors.Revit.Operations.Send.Settings;
|
||||
using Speckle.Connectors.Revit.Plugin;
|
||||
using Speckle.Connectors.RevitShared.Operations.Send.Filters;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.RevitShared.Helpers;
|
||||
@@ -29,7 +27,8 @@ namespace Speckle.Connectors.Revit.Bindings;
|
||||
|
||||
internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
{
|
||||
private readonly IRevitContext _revitContext;
|
||||
private readonly IAppIdleManager _idleManager;
|
||||
private readonly RevitContext _revitContext;
|
||||
private readonly DocumentModelStore _store;
|
||||
private readonly ICancellationManager _cancellationManager;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
@@ -40,7 +39,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
private readonly ElementUnpacker _elementUnpacker;
|
||||
private readonly IRevitConversionSettingsFactory _revitConversionSettingsFactory;
|
||||
private readonly ISpeckleApplication _speckleApplication;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
|
||||
|
||||
/// <summary>
|
||||
/// Used internally to aggregate the changed objects' id. Note we're using a concurrent dictionary here as the expiry check method is not thread safe, and this was causing problems. See:
|
||||
@@ -51,7 +50,8 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
private ConcurrentDictionary<ElementId, byte> ChangedObjectIds { get; set; } = new();
|
||||
|
||||
public RevitSendBinding(
|
||||
IRevitContext revitContext,
|
||||
IAppIdleManager idleManager,
|
||||
RevitContext revitContext,
|
||||
DocumentModelStore store,
|
||||
ICancellationManager cancellationManager,
|
||||
IBrowserBridge bridge,
|
||||
@@ -63,10 +63,11 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
ElementUnpacker elementUnpacker,
|
||||
IRevitConversionSettingsFactory revitConversionSettingsFactory,
|
||||
ISpeckleApplication speckleApplication,
|
||||
IEventAggregator eventAggregator
|
||||
ITopLevelExceptionHandler topLevelExceptionHandler
|
||||
)
|
||||
: base("sendBinding", bridge)
|
||||
{
|
||||
_idleManager = idleManager;
|
||||
_revitContext = revitContext;
|
||||
_store = store;
|
||||
_cancellationManager = cancellationManager;
|
||||
@@ -78,14 +79,15 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
_elementUnpacker = elementUnpacker;
|
||||
_revitConversionSettingsFactory = revitConversionSettingsFactory;
|
||||
_speckleApplication = speckleApplication;
|
||||
_eventAggregator = eventAggregator;
|
||||
_topLevelExceptionHandler = topLevelExceptionHandler;
|
||||
|
||||
Commands = new SendBindingUICommands(bridge);
|
||||
// TODO expiry events
|
||||
// TODO filters need refresh events
|
||||
|
||||
eventAggregator.GetEvent<DocumentChangedEvent>().Subscribe(DocChangeHandler);
|
||||
eventAggregator.GetEvent<DocumentStoreChangedEvent>().Subscribe(OnDocumentStoreChangedEvent);
|
||||
revitContext.UIApplication.NotNull().Application.DocumentChanged += (_, e) =>
|
||||
_topLevelExceptionHandler.CatchUnhandled(() => DocChangeHandler(e));
|
||||
_store.DocumentChanged += (_, _) => topLevelExceptionHandler.FireAndForget(async () => await OnDocumentChanged());
|
||||
}
|
||||
|
||||
private async Task OnDocumentStoreChangedEvent(object _) => await Commands.NotifyDocumentChanged();
|
||||
@@ -173,7 +175,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
private async Task<List<Element>> RefreshElementsOnSender(SenderModelCard modelCard)
|
||||
{
|
||||
var activeUIDoc =
|
||||
_revitContext.UIApplication.ActiveUIDocument
|
||||
_revitContext.UIApplication.NotNull().ActiveUIDocument
|
||||
?? throw new SpeckleException("Unable to retrieve active UI document");
|
||||
|
||||
if (modelCard.SendFilter is IRevitSendFilter viewFilter)
|
||||
@@ -249,7 +251,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
|
||||
if (addedElementIds.Count > 0)
|
||||
{
|
||||
_eventAggregator.GetEvent<IdleEvent>().OneTimeSubscribe(nameof(PostSetObjectIds), PostSetObjectIds);
|
||||
_idleManager.SubscribeToIdle(nameof(PostSetObjectIds), PostSetObjectIds);
|
||||
}
|
||||
|
||||
if (HaveUnitsChanged(e.GetDocument()))
|
||||
@@ -269,8 +271,8 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
_sendConversionCache.EvictObjects(unpackedObjectIds);
|
||||
}
|
||||
|
||||
_eventAggregator.GetEvent<IdleEvent>().OneTimeSubscribe(nameof(CheckFilterExpiration), CheckFilterExpiration);
|
||||
_eventAggregator.GetEvent<IdleEvent>().OneTimeSubscribe(nameof(RunExpirationChecks), RunExpirationChecks);
|
||||
_idleManager.SubscribeToIdle(nameof(CheckFilterExpiration), CheckFilterExpiration);
|
||||
_idleManager.SubscribeToIdle(nameof(RunExpirationChecks), RunExpirationChecks);
|
||||
}
|
||||
|
||||
// Keeps track of doc and current units
|
||||
@@ -307,7 +309,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
return false;
|
||||
}
|
||||
|
||||
private async Task PostSetObjectIds(object _)
|
||||
private async Task PostSetObjectIds()
|
||||
{
|
||||
foreach (var sender in _store.GetSenders().ToList())
|
||||
{
|
||||
@@ -318,7 +320,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
/// <summary>
|
||||
/// Notifies ui if any filters need refreshing. Currently, this only applies for view filters.
|
||||
/// </summary>
|
||||
private async Task CheckFilterExpiration(object _)
|
||||
private async Task CheckFilterExpiration()
|
||||
{
|
||||
// NOTE: below code seems like more make sense in terms of performance but it causes unmanaged exception on Revit
|
||||
// using var viewCollector = new FilteredElementCollector(RevitContext.UIApplication?.ActiveUIDocument.Document);
|
||||
@@ -329,17 +331,21 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
// await Commands.RefreshSendFilters();
|
||||
// }
|
||||
|
||||
if (ChangedObjectIds.Keys.Any(e => _revitContext.UIApplication.ActiveUIDocument.Document.GetElement(e) is View))
|
||||
if (
|
||||
ChangedObjectIds.Keys.Any(e =>
|
||||
_revitContext.UIApplication.NotNull().ActiveUIDocument.Document.GetElement(e) is View
|
||||
)
|
||||
)
|
||||
{
|
||||
await Commands.RefreshSendFilters();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RunExpirationChecks(object _)
|
||||
private async Task RunExpirationChecks()
|
||||
{
|
||||
var senders = _store.GetSenders().ToList();
|
||||
// string[] objectIdsList = ChangedObjectIds.Keys.ToArray();
|
||||
var doc = _revitContext.UIApplication.ActiveUIDocument.Document;
|
||||
var doc = _revitContext.UIApplication.NotNull().ActiveUIDocument.Document;
|
||||
|
||||
if (doc == null)
|
||||
{
|
||||
@@ -393,6 +399,11 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
viewFilter.SetContext(_revitContext);
|
||||
}
|
||||
|
||||
if (modelCard.SendFilter is null || modelCard.SendFilter.IdMap is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var selectedObjects = modelCard.SendFilter.NotNull().IdMap.NotNull().Values;
|
||||
var intersection = selectedObjects.Intersect(objUniqueIds).ToList();
|
||||
bool isExpired = intersection.Count != 0;
|
||||
|
||||
@@ -1,27 +1,47 @@
|
||||
using Speckle.Connectors.DUI.Bindings;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
using Speckle.Connectors.Revit.Plugin;
|
||||
using Speckle.Converters.RevitShared.Helpers;
|
||||
using Speckle.Sdk.Common;
|
||||
|
||||
namespace Speckle.Connectors.Revit.Bindings;
|
||||
|
||||
// POC: we need a base a RevitBaseBinding
|
||||
internal sealed class SelectionBinding : RevitBaseBinding, ISelectionBinding
|
||||
internal sealed class SelectionBinding : RevitBaseBinding, ISelectionBinding, IDisposable
|
||||
{
|
||||
private readonly IRevitContext _revitContext;
|
||||
#if REVIT2022
|
||||
private readonly System.Timers.Timer _selectionTimer;
|
||||
#endif
|
||||
private readonly RevitContext _revitContext;
|
||||
private readonly IAppIdleManager _idleManager;
|
||||
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
|
||||
|
||||
public SelectionBinding(IRevitContext revitContext, IBrowserBridge parent, IEventAggregator eventAggregator)
|
||||
public SelectionBinding(
|
||||
RevitContext revitContext,
|
||||
IBrowserBridge parent,
|
||||
IAppIdleManager idleManager,
|
||||
ITopLevelExceptionHandler topLevelExceptionHandler
|
||||
)
|
||||
: base("selectionBinding", parent)
|
||||
{
|
||||
_revitContext = revitContext;
|
||||
eventAggregator.GetEvent<SelectionChangedEvent>().Subscribe(OnSelectionChanged);
|
||||
_idleManager = idleManager;
|
||||
_topLevelExceptionHandler = topLevelExceptionHandler;
|
||||
|
||||
#if REVIT2022
|
||||
// NOTE: getting the selection data should be a fast function all, even for '000s of elements - and having a timer hitting it every 1s is ok.
|
||||
_selectionTimer = new System.Timers.Timer(1000);
|
||||
_selectionTimer.Elapsed += (_, _) => _topLevelExceptionHandler.CatchUnhandled(OnSelectionChanged);
|
||||
_selectionTimer.Start();
|
||||
#else
|
||||
|
||||
_revitContext.UIApplication.NotNull().SelectionChanged += (_, _) =>
|
||||
_idleManager.SubscribeToIdle(nameof(SelectionBinding), OnSelectionChanged);
|
||||
#endif
|
||||
}
|
||||
|
||||
private void OnSelectionChanged(object _)
|
||||
private void OnSelectionChanged()
|
||||
{
|
||||
if (_revitContext.UIApplication.ActiveUIDocument == null)
|
||||
if (_revitContext.UIApplication.NotNull().ActiveUIDocument == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -30,7 +50,7 @@ internal sealed class SelectionBinding : RevitBaseBinding, ISelectionBinding
|
||||
|
||||
public SelectionInfo GetSelection()
|
||||
{
|
||||
if (_revitContext.UIApplication.ActiveUIDocument == null)
|
||||
if (_revitContext.UIApplication.NotNull().ActiveUIDocument == null)
|
||||
{
|
||||
return new SelectionInfo(Array.Empty<string>(), "No objects selected.");
|
||||
}
|
||||
@@ -46,4 +66,11 @@ internal sealed class SelectionBinding : RevitBaseBinding, ISelectionBinding
|
||||
.ToList();
|
||||
return new SelectionInfo(selectionIds, $"{selectionIds.Count} objects selected.");
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
#if REVIT2022
|
||||
_selectionTimer.Dispose();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -15,7 +15,6 @@ using Speckle.Connectors.Revit.Operations.Send;
|
||||
using Speckle.Connectors.Revit.Operations.Send.Settings;
|
||||
using Speckle.Connectors.Revit.Plugin;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.RevitShared.Helpers;
|
||||
using Speckle.Sdk.Models.GraphTraversal;
|
||||
|
||||
namespace Speckle.Connectors.Revit.DependencyInjection;
|
||||
@@ -45,7 +44,8 @@ public static class ServiceRegistration
|
||||
|
||||
serviceCollection.AddSingleton<IBinding>(sp => sp.GetRequiredService<IBasicConnectorBinding>());
|
||||
serviceCollection.AddSingleton<IBasicConnectorBinding, BasicConnectorBindingRevit>();
|
||||
serviceCollection.AddSingleton<IRevitContext>(sp => sp.GetRequiredService<IRevitPlugin>());
|
||||
|
||||
serviceCollection.AddSingleton<IAppIdleManager, RevitIdleManager>();
|
||||
|
||||
// send operation and dependencies
|
||||
serviceCollection.AddScoped<SendOperation<ElementId>>();
|
||||
|
||||
@@ -9,9 +9,9 @@ namespace Speckle.Connectors.Revit.HostApp;
|
||||
/// </summary>
|
||||
public class ElementUnpacker
|
||||
{
|
||||
private readonly IRevitContext _revitContext;
|
||||
private readonly RevitContext _revitContext;
|
||||
|
||||
public ElementUnpacker(IRevitContext revitContext)
|
||||
public ElementUnpacker(RevitContext revitContext)
|
||||
{
|
||||
_revitContext = revitContext;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
using Autodesk.Revit.DB;
|
||||
using Autodesk.Revit.DB.ExtensibleStorage;
|
||||
using Autodesk.Revit.UI;
|
||||
using Autodesk.Revit.UI.Events;
|
||||
using Revit.Async;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Models;
|
||||
using Speckle.Connectors.DUI.Utils;
|
||||
using Speckle.Connectors.Revit.Plugin;
|
||||
using Speckle.Converters.RevitShared.Helpers;
|
||||
using Speckle.Sdk.Common;
|
||||
|
||||
namespace Speckle.Connectors.Revit.HostApp;
|
||||
|
||||
@@ -16,43 +17,48 @@ internal sealed class RevitDocumentStore : DocumentModelStore
|
||||
// POC: move to somewhere central?
|
||||
private static readonly Guid s_revitDocumentStoreId = new("D35B3695-EDC9-4E15-B62A-D3FC2CB83FA3");
|
||||
|
||||
private readonly IRevitContext _revitContext;
|
||||
private readonly IAppIdleManager _idleManager;
|
||||
private readonly RevitContext _revitContext;
|
||||
private readonly DocumentModelStorageSchema _documentModelStorageSchema;
|
||||
private readonly IdStorageSchema _idStorageSchema;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
|
||||
|
||||
public RevitDocumentStore(
|
||||
IRevitContext revitContext,
|
||||
IAppIdleManager idleManager,
|
||||
RevitContext revitContext,
|
||||
IJsonSerializer jsonSerializer,
|
||||
DocumentModelStorageSchema documentModelStorageSchema,
|
||||
IdStorageSchema idStorageSchema,
|
||||
IEventAggregator eventAggregator
|
||||
ITopLevelExceptionHandler topLevelExceptionHandler
|
||||
)
|
||||
: base(jsonSerializer)
|
||||
{
|
||||
_idleManager = idleManager;
|
||||
_revitContext = revitContext;
|
||||
_documentModelStorageSchema = documentModelStorageSchema;
|
||||
_idStorageSchema = idStorageSchema;
|
||||
_eventAggregator = eventAggregator;
|
||||
_topLevelExceptionHandler = topLevelExceptionHandler;
|
||||
|
||||
eventAggregator.GetEvent<DocumentOpenedEvent>().Subscribe(OnDocumentOpen);
|
||||
eventAggregator.GetEvent<DocumentOpeningEvent>().Subscribe(OnDocumentOpen);
|
||||
eventAggregator.GetEvent<ViewActivatedEvent>().Subscribe(OnViewActivated);
|
||||
UIApplication uiApplication = _revitContext.UIApplication.NotNull();
|
||||
|
||||
uiApplication.ViewActivated += (s, e) => _topLevelExceptionHandler.CatchUnhandled(() => OnViewActivated(s, e));
|
||||
|
||||
uiApplication.Application.DocumentOpening += (_, _) =>
|
||||
_topLevelExceptionHandler.CatchUnhandled(() => IsDocumentInit = false);
|
||||
|
||||
uiApplication.Application.DocumentOpened += (_, _) =>
|
||||
_topLevelExceptionHandler.CatchUnhandled(() => IsDocumentInit = false);
|
||||
|
||||
// There is no event that we can hook here for double-click file open...
|
||||
// It is kind of harmless since we create this object as "SingleInstance".
|
||||
LoadState();
|
||||
OnDocumentChanged();
|
||||
}
|
||||
|
||||
private void OnDocumentOpen(object _) => IsDocumentInit = false;
|
||||
|
||||
public override Task OnDocumentStoreInitialized() =>
|
||||
_eventAggregator.GetEvent<DocumentStoreChangedEvent>().PublishAsync(new object());
|
||||
|
||||
/// <summary>
|
||||
/// This is the place where we track document switch for new document -> Responsible to Read from new doc
|
||||
/// </summary>
|
||||
private void OnViewActivated(ViewActivatedEventArgs e)
|
||||
private void OnViewActivated(object? _, ViewActivatedEventArgs e)
|
||||
{
|
||||
if (e.Document == null)
|
||||
{
|
||||
@@ -66,13 +72,14 @@ internal sealed class RevitDocumentStore : DocumentModelStore
|
||||
}
|
||||
|
||||
IsDocumentInit = true;
|
||||
_eventAggregator.GetEvent<IdleEvent>().OneTimeSubscribe(nameof(RevitDocumentStore), OnIdleEvent);
|
||||
}
|
||||
|
||||
private async Task OnIdleEvent(object _)
|
||||
{
|
||||
LoadState();
|
||||
await _eventAggregator.GetEvent<DocumentStoreChangedEvent>().PublishAsync(new object());
|
||||
_idleManager.SubscribeToIdle(
|
||||
nameof(RevitDocumentStore),
|
||||
() =>
|
||||
{
|
||||
LoadState();
|
||||
OnDocumentChanged();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
protected override void HostAppSaveState(string modelCardState)
|
||||
|
||||
+1
-1
@@ -4,5 +4,5 @@ namespace Speckle.Connectors.RevitShared.Operations.Send.Filters;
|
||||
|
||||
public interface IRevitSendFilter
|
||||
{
|
||||
public void SetContext(IRevitContext revitContext);
|
||||
public void SetContext(RevitContext revitContext);
|
||||
}
|
||||
|
||||
+5
-4
@@ -4,6 +4,7 @@ using Speckle.Connectors.DUI.Models.Card.SendFilter;
|
||||
using Speckle.Connectors.DUI.Utils;
|
||||
using Speckle.Connectors.Revit.HostApp;
|
||||
using Speckle.Converters.RevitShared.Helpers;
|
||||
using Speckle.Sdk.Common;
|
||||
|
||||
namespace Speckle.Connectors.RevitShared.Operations.Send.Filters;
|
||||
|
||||
@@ -11,7 +12,7 @@ public record CategoryData(string Name, string Id);
|
||||
|
||||
public class RevitCategoriesFilter : DiscriminatedObject, ISendFilter, IRevitSendFilter
|
||||
{
|
||||
private IRevitContext _revitContext;
|
||||
private RevitContext _revitContext;
|
||||
private Document? _doc;
|
||||
public string Id { get; set; } = "revitCategories";
|
||||
public string Name { get; set; } = "Categories";
|
||||
@@ -24,10 +25,10 @@ public class RevitCategoriesFilter : DiscriminatedObject, ISendFilter, IRevitSen
|
||||
|
||||
public RevitCategoriesFilter() { }
|
||||
|
||||
public RevitCategoriesFilter(IRevitContext revitContext)
|
||||
public RevitCategoriesFilter(RevitContext revitContext)
|
||||
{
|
||||
_revitContext = revitContext;
|
||||
_doc = _revitContext.UIApplication.ActiveUIDocument.Document;
|
||||
_doc = _revitContext.UIApplication.NotNull().ActiveUIDocument.Document;
|
||||
|
||||
GetCategories();
|
||||
}
|
||||
@@ -83,7 +84,7 @@ public class RevitCategoriesFilter : DiscriminatedObject, ISendFilter, IRevitSen
|
||||
/// NOTE: this is needed since we need doc on `GetObjectIds()` function after it deserialized.
|
||||
/// DI doesn't help here to pass RevitContext from constructor.
|
||||
/// </summary>
|
||||
public void SetContext(IRevitContext revitContext)
|
||||
public void SetContext(RevitContext revitContext)
|
||||
{
|
||||
_revitContext = revitContext;
|
||||
_doc = _revitContext.UIApplication?.ActiveUIDocument.Document;
|
||||
|
||||
+3
-3
@@ -8,7 +8,7 @@ namespace Speckle.Connectors.RevitShared.Operations.Send.Filters;
|
||||
|
||||
public class RevitViewsFilter : DiscriminatedObject, ISendFilter, IRevitSendFilter
|
||||
{
|
||||
private IRevitContext _revitContext;
|
||||
private RevitContext _revitContext;
|
||||
private Document? _doc;
|
||||
public string Id { get; set; } = "revitViews";
|
||||
public string Name { get; set; } = "Views";
|
||||
@@ -21,7 +21,7 @@ public class RevitViewsFilter : DiscriminatedObject, ISendFilter, IRevitSendFilt
|
||||
|
||||
public RevitViewsFilter() { }
|
||||
|
||||
public RevitViewsFilter(IRevitContext revitContext)
|
||||
public RevitViewsFilter(RevitContext revitContext)
|
||||
{
|
||||
_revitContext = revitContext;
|
||||
_doc = _revitContext.UIApplication?.ActiveUIDocument.Document;
|
||||
@@ -109,7 +109,7 @@ public class RevitViewsFilter : DiscriminatedObject, ISendFilter, IRevitSendFilt
|
||||
/// NOTE: this is needed since we need doc on `GetObjectIds()` function after it deserialized.
|
||||
/// DI doesn't help here to pass RevitContext from constructor.
|
||||
/// </summary>
|
||||
public void SetContext(IRevitContext revitContext)
|
||||
public void SetContext(RevitContext revitContext)
|
||||
{
|
||||
_revitContext = revitContext;
|
||||
_doc = _revitContext.UIApplication?.ActiveUIDocument.Document;
|
||||
|
||||
+2
-2
@@ -13,7 +13,7 @@ namespace Speckle.Connectors.Revit.Operations.Send.Settings;
|
||||
[GenerateAutoInterface]
|
||||
public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
|
||||
{
|
||||
private readonly IRevitContext _revitContext;
|
||||
private readonly RevitContext _revitContext;
|
||||
private readonly ISendConversionCache _sendConversionCache;
|
||||
private readonly ElementUnpacker _elementUnpacker;
|
||||
|
||||
@@ -23,7 +23,7 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
|
||||
private readonly Dictionary<string, bool?> _sendNullParamsCache = new();
|
||||
|
||||
public ToSpeckleSettingsManager(
|
||||
IRevitContext revitContext,
|
||||
RevitContext revitContext,
|
||||
ISendConversionCache sendConversionCache,
|
||||
ElementUnpacker elementUnpacker
|
||||
)
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
using Speckle.Converters.RevitShared.Helpers;
|
||||
|
||||
namespace Speckle.Connectors.Revit.Plugin;
|
||||
|
||||
internal interface IRevitPlugin : IRevitContext
|
||||
internal interface IRevitPlugin
|
||||
{
|
||||
void Initialise();
|
||||
void Shutdown();
|
||||
|
||||
@@ -3,13 +3,15 @@ using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using Autodesk.Revit.ApplicationServices;
|
||||
using Autodesk.Revit.UI;
|
||||
using CefSharp;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Revit.Async;
|
||||
using Speckle.Connectors.Common;
|
||||
using Speckle.Connectors.DUI.Bindings;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
using Speckle.Converters.RevitShared.Helpers;
|
||||
using Speckle.Sdk;
|
||||
|
||||
namespace Speckle.Connectors.Revit.Plugin;
|
||||
@@ -19,6 +21,7 @@ internal sealed class RevitCefPlugin : IRevitPlugin
|
||||
private readonly UIControlledApplication _uIControlledApplication;
|
||||
private readonly IServiceProvider _serviceProvider; // should be lazy to ensure the bindings are not created too early
|
||||
private readonly BindingOptions _bindingOptions;
|
||||
private readonly RevitContext _revitContext;
|
||||
private readonly CefSharpPanel _cefSharpPanel;
|
||||
private readonly ISpeckleApplication _speckleApplication;
|
||||
|
||||
@@ -26,17 +29,17 @@ internal sealed class RevitCefPlugin : IRevitPlugin
|
||||
UIControlledApplication uIControlledApplication,
|
||||
IServiceProvider serviceProvider,
|
||||
BindingOptions bindingOptions,
|
||||
RevitContext revitContext,
|
||||
CefSharpPanel cefSharpPanel,
|
||||
ISpeckleApplication speckleApplication,
|
||||
IEventAggregator eventAggregator
|
||||
ISpeckleApplication speckleApplication
|
||||
)
|
||||
{
|
||||
_uIControlledApplication = uIControlledApplication;
|
||||
_serviceProvider = serviceProvider;
|
||||
_bindingOptions = bindingOptions;
|
||||
_revitContext = revitContext;
|
||||
_cefSharpPanel = cefSharpPanel;
|
||||
_speckleApplication = speckleApplication;
|
||||
eventAggregator.GetEvent<ApplicationInitializedEvent>().Subscribe(OnApplicationInitialized);
|
||||
}
|
||||
|
||||
public void Initialise()
|
||||
@@ -44,6 +47,7 @@ internal sealed class RevitCefPlugin : IRevitPlugin
|
||||
// Create and register panels before app initialized. this is needed for double-click file open
|
||||
CreateTabAndRibbonPanel(_uIControlledApplication);
|
||||
RegisterDockablePane();
|
||||
_uIControlledApplication.ControlledApplication.ApplicationInitialized += OnApplicationInitialized;
|
||||
}
|
||||
|
||||
public void Shutdown()
|
||||
@@ -90,14 +94,27 @@ internal sealed class RevitCefPlugin : IRevitPlugin
|
||||
$"Speckle.Connectors.Revit{_speckleApplication.HostApplicationVersion}.Assets.logo32.png",
|
||||
path
|
||||
);
|
||||
dui3Button.ToolTip = "Next Gen Speckle Connector (Beta) for Revit";
|
||||
dui3Button.ToolTip = "Speckle (Beta) for Revit";
|
||||
//dui3Button.AvailabilityClassName = typeof(CmdAvailabilityViews).FullName;
|
||||
dui3Button.SetContextualHelp(new ContextualHelp(ContextualHelpType.Url, "https://speckle.systems"));
|
||||
}
|
||||
|
||||
private void OnApplicationInitialized(UIApplication uiApplication)
|
||||
private void OnApplicationInitialized(object? sender, Autodesk.Revit.DB.Events.ApplicationInitializedEventArgs e)
|
||||
{
|
||||
UIApplication = uiApplication;
|
||||
var uiApplication = new UIApplication(sender as Application);
|
||||
_revitContext.UIApplication = uiApplication;
|
||||
|
||||
// POC: might be worth to interface this out, we shall see...
|
||||
RevitTask.Initialize(uiApplication);
|
||||
|
||||
PostApplicationInit(); // for double-click file open
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Actions to run after UiApplication initialized. This is needed for double-click file open issue.
|
||||
/// </summary>
|
||||
private void PostApplicationInit()
|
||||
{
|
||||
var bindings = _serviceProvider.GetRequiredService<IEnumerable<IBinding>>();
|
||||
// binding the bindings to each bridge
|
||||
foreach (IBinding binding in bindings)
|
||||
@@ -106,7 +123,7 @@ internal sealed class RevitCefPlugin : IRevitPlugin
|
||||
binding.Parent.AssociateWithBinding(binding);
|
||||
}
|
||||
|
||||
_cefSharpPanel.Browser.IsBrowserInitializedChanged += (_, e) =>
|
||||
_cefSharpPanel.Browser.IsBrowserInitializedChanged += (sender, e) =>
|
||||
{
|
||||
if (e.NewValue is false)
|
||||
{
|
||||
@@ -142,7 +159,7 @@ internal sealed class RevitCefPlugin : IRevitPlugin
|
||||
// Otherwise pane cannot be registered for double-click file open.
|
||||
_uIControlledApplication.RegisterDockablePane(
|
||||
RevitExternalApplication.DockablePanelId,
|
||||
"Speckle (Beta)",
|
||||
"Speckle (Beta) for Revit",
|
||||
_cefSharpPanel
|
||||
);
|
||||
}
|
||||
@@ -164,6 +181,4 @@ internal sealed class RevitCefPlugin : IRevitPlugin
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public UIApplication UIApplication { get; private set; }
|
||||
}
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
using Autodesk.Revit.ApplicationServices;
|
||||
using Autodesk.Revit.UI;
|
||||
using Autodesk.Revit.UI.Events;
|
||||
using Speckle.Connectors.Common.Threading;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
|
||||
namespace Speckle.Connectors.Revit.Plugin;
|
||||
|
||||
public class ApplicationInitializedEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
: ThreadedEvent<UIApplication>(threadContext, exceptionHandler);
|
||||
|
||||
public class ViewActivatedEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
: ThreadedEvent<ViewActivatedEventArgs>(threadContext, exceptionHandler);
|
||||
|
||||
public class DocumentOpeningEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
: ThreadedEvent<object>(threadContext, exceptionHandler);
|
||||
|
||||
public class DocumentOpenedEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
: ThreadedEvent<object>(threadContext, exceptionHandler);
|
||||
|
||||
public class SelectionChangedEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
: ThreadedEvent<object>(threadContext, exceptionHandler);
|
||||
|
||||
public class DocumentChangedEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
: ThreadedEvent<Autodesk.Revit.DB.Events.DocumentChangedEventArgs>(threadContext, exceptionHandler);
|
||||
|
||||
#if REVIT2022
|
||||
public class PeriodicSelectionEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
: PeriodicThreadedEvent(threadContext, exceptionHandler);
|
||||
#endif
|
||||
|
||||
public static class RevitEvents
|
||||
{
|
||||
private static IEventAggregator? s_eventAggregator;
|
||||
|
||||
public static void Register(IEventAggregator eventAggregator, UIControlledApplication application)
|
||||
{
|
||||
s_eventAggregator = eventAggregator;
|
||||
application.Idling += async (_, _) => await eventAggregator.GetEvent<IdleEvent>().PublishAsync(new object());
|
||||
application.ControlledApplication.ApplicationInitialized += async (sender, _) =>
|
||||
await eventAggregator
|
||||
.GetEvent<ApplicationInitializedEvent>()
|
||||
.PublishAsync(new UIApplication(sender as Application));
|
||||
application.ViewActivated += async (_, args) =>
|
||||
await eventAggregator.GetEvent<ViewActivatedEvent>().PublishAsync(args);
|
||||
application.ControlledApplication.DocumentOpened += async (_, _) =>
|
||||
await eventAggregator.GetEvent<DocumentOpenedEvent>().PublishAsync(new object());
|
||||
application.ControlledApplication.DocumentOpening += async (_, _) =>
|
||||
await eventAggregator.GetEvent<DocumentOpeningEvent>().PublishAsync(new object());
|
||||
application.ControlledApplication.DocumentChanged += async (_, args) =>
|
||||
await eventAggregator.GetEvent<DocumentChangedEvent>().PublishAsync(args);
|
||||
|
||||
#if REVIT2022
|
||||
// NOTE: getting the selection data should be a fast function all, even for '000s of elements - and having a timer hitting it every 1s is ok.
|
||||
eventAggregator.GetEvent<PeriodicSelectionEvent>().SubscribePeriodic(TimeSpan.FromSeconds(1), OnSelectionChanged);
|
||||
#else
|
||||
|
||||
application.SelectionChanged += (_, _) =>
|
||||
eventAggregator.GetEvent<IdleEvent>().OneTimeSubscribe("Selection", OnSelectionChanged);
|
||||
#endif
|
||||
}
|
||||
|
||||
private static async Task OnSelectionChanged(object _)
|
||||
{
|
||||
if (s_eventAggregator is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
await s_eventAggregator.GetEvent<SelectionChangedEvent>().PublishAsync(new object());
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ using Microsoft.Extensions.Logging;
|
||||
using Revit.Async;
|
||||
using Speckle.Connectors.Common;
|
||||
using Speckle.Connectors.DUI;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
using Speckle.Connectors.Revit.DependencyInjection;
|
||||
using Speckle.Converters.RevitShared;
|
||||
using Speckle.Sdk;
|
||||
@@ -53,7 +52,6 @@ internal sealed class RevitExternalApplication : IExternalApplication
|
||||
_container.UseDUI();
|
||||
|
||||
RevitTask.Initialize(application);
|
||||
RevitEvents.Register(_container.GetRequiredService<IEventAggregator>(), application);
|
||||
// resolve root object
|
||||
_revitPlugin = _container.GetRequiredService<IRevitPlugin>();
|
||||
_revitPlugin.Initialise();
|
||||
|
||||
+1
-1
@@ -44,10 +44,10 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\ToSpeckleSettingsManager.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\ReferencePointSetting.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\DetailLevelSetting.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Plugin\RevitEvents.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Plugin\IRevitPlugin.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Plugin\RevitCommand.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Plugin\RevitExternalApplication.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Plugin\RevitIdleManager.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Plugin\RevitThreadContext.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Plugin\RevitCefPlugin.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
+11
-9
@@ -4,7 +4,6 @@ using Rhino.Geometry;
|
||||
using Speckle.Connectors.Common.Caching;
|
||||
using Speckle.Connectors.DUI.Bindings;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
using Speckle.Connectors.DUI.Models;
|
||||
using Speckle.Connectors.DUI.Models.Card;
|
||||
using Speckle.Connectors.Rhino.Extensions;
|
||||
@@ -22,29 +21,32 @@ public sealed class RhinoBasicConnectorBinding : IBasicConnectorBinding
|
||||
private readonly DocumentModelStore _store;
|
||||
private readonly ISendConversionCache _sendConversionCache;
|
||||
private readonly ISpeckleApplication _speckleApplication;
|
||||
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
|
||||
|
||||
public RhinoBasicConnectorBinding(
|
||||
DocumentModelStore store,
|
||||
IBrowserBridge parent,
|
||||
ISendConversionCache sendConversionCache,
|
||||
ISpeckleApplication speckleApplication,
|
||||
IEventAggregator eventAggregator
|
||||
ITopLevelExceptionHandler topLevelExceptionHandler
|
||||
)
|
||||
{
|
||||
_store = store;
|
||||
Parent = parent;
|
||||
_sendConversionCache = sendConversionCache;
|
||||
_speckleApplication = speckleApplication;
|
||||
_topLevelExceptionHandler = topLevelExceptionHandler;
|
||||
Commands = new BasicConnectorBindingCommands(parent);
|
||||
|
||||
eventAggregator.GetEvent<DocumentStoreChangedEvent>().Subscribe(OnDocumentStoreChangedEvent);
|
||||
}
|
||||
_store.DocumentChanged += (_, _) =>
|
||||
_topLevelExceptionHandler.FireAndForget(async () =>
|
||||
{
|
||||
await Commands.NotifyDocumentChanged();
|
||||
// Note: this prevents scaling issues when copy-pasting from one rhino doc to another in the same session.
|
||||
_sendConversionCache.ClearCache();
|
||||
});
|
||||
|
||||
private async Task OnDocumentStoreChangedEvent(object _)
|
||||
{
|
||||
await Commands.NotifyDocumentChanged();
|
||||
// Note: this prevents scaling issues when copy-pasting from one rhino doc to another in the same session.
|
||||
_sendConversionCache.ClearCache();
|
||||
// eventAggregator.GetEvent<DocumentStoreChangedEvent>().Subscribe(OnDocumentStoreChangedEvent);
|
||||
}
|
||||
|
||||
public string GetConnectorVersion() => _speckleApplication.SpeckleVersion;
|
||||
|
||||
@@ -2,32 +2,31 @@ using Rhino;
|
||||
using Rhino.DocObjects;
|
||||
using Speckle.Connectors.DUI.Bindings;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
using Speckle.Connectors.RhinoShared;
|
||||
|
||||
namespace Speckle.Connectors.Rhino.Bindings;
|
||||
|
||||
public class RhinoSelectionBinding : ISelectionBinding
|
||||
{
|
||||
private readonly IAppIdleManager _idleManager;
|
||||
private const string SELECTION_EVENT = "setSelection";
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
|
||||
public string Name => "selectionBinding";
|
||||
public IBrowserBridge Parent { get; }
|
||||
|
||||
public RhinoSelectionBinding(IBrowserBridge parent, IEventAggregator eventAggregator)
|
||||
public RhinoSelectionBinding(IAppIdleManager idleManager, IBrowserBridge parent)
|
||||
{
|
||||
_idleManager = idleManager;
|
||||
Parent = parent;
|
||||
_eventAggregator = eventAggregator;
|
||||
eventAggregator.GetEvent<SelectObjects>().Subscribe(OnSelectionChange);
|
||||
eventAggregator.GetEvent<DeselectObjects>().Subscribe(OnSelectionChange);
|
||||
eventAggregator.GetEvent<DeselectAllObjects>().Subscribe(OnSelectionChange);
|
||||
|
||||
RhinoDoc.SelectObjects += OnSelectionChange;
|
||||
RhinoDoc.DeselectObjects += OnSelectionChange;
|
||||
RhinoDoc.DeselectAllObjects += OnSelectionChange;
|
||||
}
|
||||
|
||||
private void OnSelectionChange(EventArgs eventArgs) =>
|
||||
_eventAggregator.GetEvent<IdleEvent>().OneTimeSubscribe(nameof(RhinoSelectionBinding), UpdateSelection);
|
||||
private void OnSelectionChange(object? o, EventArgs eventArgs) =>
|
||||
_idleManager.SubscribeToIdle(nameof(RhinoSelectionBinding), UpdateSelection);
|
||||
|
||||
private void UpdateSelection(object _)
|
||||
private void UpdateSelection()
|
||||
{
|
||||
SelectionInfo selInfo = GetSelection();
|
||||
Parent.Send(SELECTION_EVENT, selInfo);
|
||||
|
||||
@@ -10,14 +10,12 @@ using Speckle.Connectors.Common.Cancellation;
|
||||
using Speckle.Connectors.Common.Operations;
|
||||
using Speckle.Connectors.DUI.Bindings;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
using Speckle.Connectors.DUI.Exceptions;
|
||||
using Speckle.Connectors.DUI.Logging;
|
||||
using Speckle.Connectors.DUI.Models;
|
||||
using Speckle.Connectors.DUI.Models.Card;
|
||||
using Speckle.Connectors.DUI.Models.Card.SendFilter;
|
||||
using Speckle.Connectors.DUI.Settings;
|
||||
using Speckle.Connectors.RhinoShared;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.Rhino;
|
||||
using Speckle.Sdk;
|
||||
@@ -42,7 +40,8 @@ public sealed class RhinoSendBinding : ISendBinding
|
||||
private readonly IRhinoConversionSettingsFactory _rhinoConversionSettingsFactory;
|
||||
private readonly ISpeckleApplication _speckleApplication;
|
||||
private readonly ISdkActivityFactory _activityFactory;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
|
||||
private readonly IAppIdleManager _idleManager;
|
||||
|
||||
/// <summary>
|
||||
/// Used internally to aggregate the changed objects' id. Objects in this list will be reconverted.
|
||||
@@ -64,6 +63,7 @@ public sealed class RhinoSendBinding : ISendBinding
|
||||
|
||||
public RhinoSendBinding(
|
||||
DocumentModelStore store,
|
||||
IAppIdleManager idleManager,
|
||||
IBrowserBridge parent,
|
||||
IEnumerable<ISendFilter> sendFilters,
|
||||
IServiceProvider serviceProvider,
|
||||
@@ -74,10 +74,11 @@ public sealed class RhinoSendBinding : ISendBinding
|
||||
IRhinoConversionSettingsFactory rhinoConversionSettingsFactory,
|
||||
ISpeckleApplication speckleApplication,
|
||||
ISdkActivityFactory activityFactory,
|
||||
IEventAggregator eventAggregator
|
||||
ITopLevelExceptionHandler topLevelExceptionHandler
|
||||
)
|
||||
{
|
||||
_store = store;
|
||||
_idleManager = idleManager;
|
||||
_serviceProvider = serviceProvider;
|
||||
_sendFilters = sendFilters.ToList();
|
||||
_cancellationManager = cancellationManager;
|
||||
@@ -87,203 +88,194 @@ public sealed class RhinoSendBinding : ISendBinding
|
||||
_rhinoConversionSettingsFactory = rhinoConversionSettingsFactory;
|
||||
_speckleApplication = speckleApplication;
|
||||
Parent = parent;
|
||||
_topLevelExceptionHandler = topLevelExceptionHandler;
|
||||
Commands = new SendBindingUICommands(parent); // POC: Commands are tightly coupled with their bindings, at least for now, saves us injecting a factory.
|
||||
_activityFactory = activityFactory;
|
||||
_eventAggregator = eventAggregator;
|
||||
PreviousUnitSystem = RhinoDoc.ActiveDoc.ModelUnitSystem;
|
||||
SubscribeToRhinoEvents(eventAggregator);
|
||||
SubscribeToRhinoEvents();
|
||||
}
|
||||
|
||||
private void SubscribeToRhinoEvents(IEventAggregator eventAggregator)
|
||||
#pragma warning disable CA1502
|
||||
private void SubscribeToRhinoEvents()
|
||||
#pragma warning restore CA1502
|
||||
{
|
||||
eventAggregator.GetEvent<BeginCommandEvent>().Subscribe(OnBeginCommandEvent);
|
||||
Command.BeginCommand += (_, e) =>
|
||||
{
|
||||
if (e.CommandEnglishName == "BlockEdit")
|
||||
{
|
||||
var selectedObject = RhinoDoc.ActiveDoc.Objects.GetSelectedObjects(false, false).First();
|
||||
ChangedObjectIds[selectedObject.Id.ToString()] = 1;
|
||||
}
|
||||
|
||||
eventAggregator.GetEvent<ActiveDocumentChanged>().Subscribe(OnActiveDocumentChanged);
|
||||
if (e.CommandEnglishName == "Ungroup")
|
||||
{
|
||||
foreach (RhinoObject selectedObject in RhinoDoc.ActiveDoc.Objects.GetSelectedObjects(false, false))
|
||||
{
|
||||
ChangedObjectIdsInGroupsOrLayers[selectedObject.Id.ToString()] = 1;
|
||||
}
|
||||
_idleManager.SubscribeToIdle("a", RunExpirationChecks);
|
||||
_idleManager.SubscribeToIdle(nameof(RhinoSendBinding), RunExpirationChecks);
|
||||
}
|
||||
};
|
||||
|
||||
RhinoDoc.ActiveDocumentChanged += (_, e) =>
|
||||
{
|
||||
PreviousUnitSystem = e.Document.ModelUnitSystem;
|
||||
};
|
||||
|
||||
// NOTE: BE CAREFUL handling things in this event handler since it is triggered whenever we save something into file!
|
||||
eventAggregator.GetEvent<DocumentPropertiesChanged>().Subscribe(OnDocumentPropertiesChanged);
|
||||
RhinoDoc.DocumentPropertiesChanged += async (_, e) =>
|
||||
{
|
||||
var newUnit = e.Document.ModelUnitSystem;
|
||||
if (newUnit != PreviousUnitSystem)
|
||||
{
|
||||
PreviousUnitSystem = newUnit;
|
||||
|
||||
eventAggregator.GetEvent<AddRhinoObject>().Subscribe(OnAddRhinoObject);
|
||||
await InvalidateAllSender();
|
||||
}
|
||||
};
|
||||
|
||||
eventAggregator.GetEvent<DeleteRhinoObject>().Subscribe(OnDeleteRhinoObject);
|
||||
RhinoDoc.AddRhinoObject += (_, e) =>
|
||||
_topLevelExceptionHandler.CatchUnhandled(() =>
|
||||
{
|
||||
if (!_store.IsDocumentInit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ChangedObjectIds[e.ObjectId.ToString()] = 1;
|
||||
_idleManager.SubscribeToIdle(nameof(RhinoSendBinding), RunExpirationChecks);
|
||||
});
|
||||
|
||||
RhinoDoc.DeleteRhinoObject += (_, e) =>
|
||||
_topLevelExceptionHandler.CatchUnhandled(() =>
|
||||
{
|
||||
if (!_store.IsDocumentInit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ChangedObjectIds[e.ObjectId.ToString()] = 1;
|
||||
_idleManager.SubscribeToIdle(nameof(RhinoSendBinding), RunExpirationChecks);
|
||||
});
|
||||
|
||||
// NOTE: Catches an object's material change from one user defined doc material to another. Does not catch (as the top event is not triggered) swapping material sources for an object or moving to/from the default material (this is handled below)!
|
||||
eventAggregator.GetEvent<RenderMaterialsTableEvent>().Subscribe(OnRenderMaterialsTableEvent);
|
||||
RhinoDoc.RenderMaterialsTableEvent += (_, args) =>
|
||||
_topLevelExceptionHandler.CatchUnhandled(() =>
|
||||
{
|
||||
if (!_store.IsDocumentInit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
eventAggregator.GetEvent<GroupTableEvent>().Subscribe(OnGroupTableEvent);
|
||||
if (args is RhinoDoc.RenderMaterialAssignmentChangedEventArgs changedEventArgs)
|
||||
{
|
||||
ChangedObjectIds[changedEventArgs.ObjectId.ToString()] = 1;
|
||||
_idleManager.SubscribeToIdle(nameof(RhinoSendBinding), RunExpirationChecks);
|
||||
}
|
||||
});
|
||||
|
||||
eventAggregator.GetEvent<LayerTableEvent>().Subscribe(OnLayerTableEvent);
|
||||
RhinoDoc.GroupTableEvent += (_, args) =>
|
||||
_topLevelExceptionHandler.CatchUnhandled(() =>
|
||||
{
|
||||
if (!_store.IsDocumentInit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var obj in RhinoDoc.ActiveDoc.Groups.GroupMembers(args.GroupIndex))
|
||||
{
|
||||
ChangedObjectIdsInGroupsOrLayers[obj.Id.ToString()] = 1;
|
||||
}
|
||||
_idleManager.SubscribeToIdle(nameof(RhinoSendBinding), RunExpirationChecks);
|
||||
});
|
||||
|
||||
RhinoDoc.LayerTableEvent += (_, args) =>
|
||||
_topLevelExceptionHandler.CatchUnhandled(() =>
|
||||
{
|
||||
if (!_store.IsDocumentInit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
args.EventType == LayerTableEventType.Deleted
|
||||
|| args.EventType == LayerTableEventType.Current
|
||||
|| args.EventType == LayerTableEventType.Added
|
||||
)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var layer = RhinoDoc.ActiveDoc.Layers[args.LayerIndex];
|
||||
|
||||
var allLayers = args.Document.Layers.Where(l => /* NOTE: layer path may actually be null in some cases (rhino's fault, not ours) */
|
||||
l.FullPath != null && l.FullPath.Contains(layer.Name)
|
||||
); // not e imperfect, but layer.GetChildren(true) is valid only in v8 and above; this filter will include the original layer.
|
||||
foreach (var childLayer in allLayers)
|
||||
{
|
||||
var sublayerObjs = RhinoDoc.ActiveDoc.Objects.FindByLayer(childLayer) ?? [];
|
||||
foreach (var obj in sublayerObjs)
|
||||
{
|
||||
ChangedObjectIdsInGroupsOrLayers[obj.Id.ToString()] = 1;
|
||||
}
|
||||
}
|
||||
_idleManager.SubscribeToIdle(nameof(RhinoSendBinding), RunExpirationChecks);
|
||||
});
|
||||
|
||||
// Catches and stores changed material ids. These are then used in the expiry checks to invalidate all objects that have assigned any of those material ids.
|
||||
eventAggregator.GetEvent<MaterialTableEvent>().Subscribe(OnMaterialTableEvent);
|
||||
|
||||
eventAggregator.GetEvent<ModifyObjectAttributes>().Subscribe(OnModifyObjectAttributes);
|
||||
|
||||
eventAggregator.GetEvent<ReplaceRhinoObject>().Subscribe(OnReplaceRhinoObject);
|
||||
}
|
||||
|
||||
private void OnActiveDocumentChanged(DocumentEventArgs e) => PreviousUnitSystem = e.Document.ModelUnitSystem;
|
||||
|
||||
private async Task OnDocumentPropertiesChanged(DocumentEventArgs e)
|
||||
{
|
||||
var newUnit = e.Document.ModelUnitSystem;
|
||||
if (newUnit != PreviousUnitSystem)
|
||||
{
|
||||
PreviousUnitSystem = newUnit;
|
||||
|
||||
await InvalidateAllSender();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnBeginCommandEvent(CommandEventArgs e)
|
||||
{
|
||||
if (e.CommandEnglishName == "BlockEdit")
|
||||
{
|
||||
var selectedObject = RhinoDoc.ActiveDoc.Objects.GetSelectedObjects(false, false).First();
|
||||
ChangedObjectIds[selectedObject.Id.ToString()] = 1;
|
||||
}
|
||||
|
||||
if (e.CommandEnglishName == "Ungroup")
|
||||
{
|
||||
foreach (RhinoObject selectedObject in RhinoDoc.ActiveDoc.Objects.GetSelectedObjects(false, false))
|
||||
RhinoDoc.MaterialTableEvent += (_, args) =>
|
||||
_topLevelExceptionHandler.CatchUnhandled(() =>
|
||||
{
|
||||
ChangedObjectIdsInGroupsOrLayers[selectedObject.Id.ToString()] = 1;
|
||||
}
|
||||
_eventAggregator.GetEvent<IdleEvent>().OneTimeSubscribe(nameof(RhinoSendBinding), RunExpirationChecks);
|
||||
}
|
||||
}
|
||||
if (!_store.IsDocumentInit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
private void OnAddRhinoObject(RhinoObjectEventArgs e)
|
||||
{
|
||||
if (!_store.IsDocumentInit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (args.EventType == MaterialTableEventType.Modified)
|
||||
{
|
||||
ChangedMaterialIndexes[args.Index] = 1;
|
||||
_idleManager.SubscribeToIdle(nameof(RhinoSendBinding), RunExpirationChecks);
|
||||
}
|
||||
});
|
||||
|
||||
ChangedObjectIds[e.ObjectId.ToString()] = 1;
|
||||
_eventAggregator.GetEvent<IdleEvent>().OneTimeSubscribe(nameof(RhinoSendBinding), RunExpirationChecks);
|
||||
}
|
||||
|
||||
private void OnDeleteRhinoObject(RhinoObjectEventArgs e)
|
||||
{
|
||||
if (!_store.IsDocumentInit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ChangedObjectIds[e.ObjectId.ToString()] = 1;
|
||||
_eventAggregator.GetEvent<IdleEvent>().OneTimeSubscribe(nameof(RhinoSendBinding), RunExpirationChecks);
|
||||
}
|
||||
|
||||
private void OnRenderMaterialsTableEvent(RhinoDoc.RenderContentTableEventArgs e)
|
||||
{
|
||||
if (!_store.IsDocumentInit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (e is RhinoDoc.RenderMaterialAssignmentChangedEventArgs changedEventArgs)
|
||||
{
|
||||
ChangedObjectIds[changedEventArgs.ObjectId.ToString()] = 1;
|
||||
_eventAggregator.GetEvent<IdleEvent>().OneTimeSubscribe(nameof(RhinoSendBinding), RunExpirationChecks);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGroupTableEvent(GroupTableEventArgs args)
|
||||
{
|
||||
if (!_store.IsDocumentInit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var obj in RhinoDoc.ActiveDoc.Groups.GroupMembers(args.GroupIndex))
|
||||
{
|
||||
ChangedObjectIdsInGroupsOrLayers[obj.Id.ToString()] = 1;
|
||||
}
|
||||
_eventAggregator.GetEvent<IdleEvent>().OneTimeSubscribe(nameof(RhinoSendBinding), RunExpirationChecks);
|
||||
}
|
||||
|
||||
private void OnLayerTableEvent(LayerTableEventArgs args)
|
||||
{
|
||||
if (!_store.IsDocumentInit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
args.EventType == LayerTableEventType.Deleted
|
||||
|| args.EventType == LayerTableEventType.Current
|
||||
|| args.EventType == LayerTableEventType.Added
|
||||
)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var layer = RhinoDoc.ActiveDoc.Layers[args.LayerIndex];
|
||||
if (layer.Name is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// add all objects from the changed layers and sublayers to the non-destructively changed object list.
|
||||
var allLayers = args.Document.Layers.Where(l => /* NOTE: layer path may actually be null in some cases (rhino's fault, not ours) */
|
||||
l.FullPath != null && l.FullPath.Contains(layer.Name)
|
||||
); // not e imperfect, but layer.GetChildren(true) is valid only in v8 and above; this filter will include the original layer.
|
||||
foreach (var childLayer in allLayers)
|
||||
{
|
||||
var sublayerObjs = RhinoDoc.ActiveDoc.Objects.FindByLayer(childLayer) ?? [];
|
||||
foreach (var obj in sublayerObjs)
|
||||
RhinoDoc.ModifyObjectAttributes += (_, e) =>
|
||||
_topLevelExceptionHandler.CatchUnhandled(() =>
|
||||
{
|
||||
ChangedObjectIdsInGroupsOrLayers[obj.Id.ToString()] = 1;
|
||||
}
|
||||
}
|
||||
_eventAggregator.GetEvent<IdleEvent>().OneTimeSubscribe(nameof(RhinoSendBinding), RunExpirationChecks);
|
||||
}
|
||||
if (!_store.IsDocumentInit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
private void OnMaterialTableEvent(MaterialTableEventArgs args)
|
||||
{
|
||||
if (!_store.IsDocumentInit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// NOTE: not sure yet we want to track every attribute changes yet. Explicitly tracking atts that change commit data. TBD
|
||||
if (
|
||||
e.OldAttributes.LayerIndex != e.NewAttributes.LayerIndex
|
||||
|| e.OldAttributes.MaterialSource != e.NewAttributes.MaterialSource
|
||||
|| e.OldAttributes.MaterialIndex != e.NewAttributes.MaterialIndex // NOTE: this does not work when swapping around from custom doc materials, it works when you swap TO/FROM default material
|
||||
|| e.OldAttributes.ColorSource != e.NewAttributes.ColorSource
|
||||
|| e.OldAttributes.ObjectColor != e.NewAttributes.ObjectColor
|
||||
|| e.OldAttributes.Name != e.NewAttributes.Name
|
||||
|| e.OldAttributes.UserStringCount != e.NewAttributes.UserStringCount
|
||||
|| e.OldAttributes.GetUserStrings() != e.NewAttributes.GetUserStrings()
|
||||
)
|
||||
{
|
||||
ChangedObjectIds[e.RhinoObject.Id.ToString()] = 1;
|
||||
_idleManager.SubscribeToIdle(nameof(RhinoSendBinding), RunExpirationChecks);
|
||||
}
|
||||
});
|
||||
|
||||
if (args.EventType == MaterialTableEventType.Modified)
|
||||
{
|
||||
ChangedMaterialIndexes[args.Index] = 1;
|
||||
_eventAggregator.GetEvent<IdleEvent>().OneTimeSubscribe(nameof(RhinoSendBinding), RunExpirationChecks);
|
||||
}
|
||||
}
|
||||
RhinoDoc.ReplaceRhinoObject += (_, e) =>
|
||||
_topLevelExceptionHandler.CatchUnhandled(() =>
|
||||
{
|
||||
if (!_store.IsDocumentInit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
private void OnModifyObjectAttributes(RhinoModifyObjectAttributesEventArgs e)
|
||||
{
|
||||
if (!_store.IsDocumentInit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE: not sure yet we want to track every attribute changes yet. TBD
|
||||
// NOTE: we might want to track here user strings too (once we send them out), and more!
|
||||
if (
|
||||
e.OldAttributes.LayerIndex != e.NewAttributes.LayerIndex
|
||||
|| e.OldAttributes.MaterialSource != e.NewAttributes.MaterialSource
|
||||
|| e.OldAttributes.MaterialIndex != e.NewAttributes.MaterialIndex // NOTE: this does not work when swapping around from custom doc materials, it works when you swap TO/FROM default material
|
||||
)
|
||||
{
|
||||
ChangedObjectIds[e.RhinoObject.Id.ToString()] = 1;
|
||||
_eventAggregator.GetEvent<IdleEvent>().OneTimeSubscribe(nameof(RhinoSendBinding), RunExpirationChecks);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnReplaceRhinoObject(RhinoReplaceObjectEventArgs e)
|
||||
{
|
||||
if (!_store.IsDocumentInit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ChangedObjectIds[e.NewRhinoObject.Id.ToString()] = 1;
|
||||
ChangedObjectIds[e.OldRhinoObject.Id.ToString()] = 1;
|
||||
_eventAggregator.GetEvent<IdleEvent>().OneTimeSubscribe(nameof(RhinoSendBinding), RunExpirationChecks);
|
||||
ChangedObjectIds[e.NewRhinoObject.Id.ToString()] = 1;
|
||||
ChangedObjectIds[e.OldRhinoObject.Id.ToString()] = 1;
|
||||
_idleManager.SubscribeToIdle(nameof(RhinoSendBinding), RunExpirationChecks);
|
||||
});
|
||||
}
|
||||
|
||||
public List<ISendFilter> GetSendFilters() => _sendFilters;
|
||||
@@ -350,7 +342,7 @@ public sealed class RhinoSendBinding : ISendBinding
|
||||
/// <summary>
|
||||
/// Checks if any sender model cards contain any of the changed objects. If so, also updates the changed objects hashset for each model card - this last part is important for on send change detection.
|
||||
/// </summary>
|
||||
private async Task RunExpirationChecks(object _)
|
||||
private async Task RunExpirationChecks()
|
||||
{
|
||||
// Note: added here a guard against executing this if there's no active doc present.
|
||||
if (RhinoDoc.ActiveDoc == null)
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
using Rhino.DocObjects;
|
||||
|
||||
namespace Speckle.Connectors.Rhino.HostApp.Properties;
|
||||
|
||||
/// <summary>
|
||||
/// Extracts properties for rhino objects.
|
||||
/// </summary>
|
||||
public class PropertiesExtractor
|
||||
{
|
||||
public Dictionary<string, object?> GetProperties(RhinoObject rhObject)
|
||||
{
|
||||
Dictionary<string, object?> properties = new();
|
||||
var userStrings = rhObject.Attributes.GetUserStrings();
|
||||
foreach (var key in userStrings.AllKeys)
|
||||
{
|
||||
// POC: could not determine how to extract the value of a formula user string.
|
||||
// So for now we are skipping them
|
||||
if (userStrings[key].StartsWith("%<"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
properties[key] = userStrings[key];
|
||||
}
|
||||
|
||||
return properties;
|
||||
}
|
||||
}
|
||||
@@ -1,37 +1,38 @@
|
||||
using Rhino;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Models;
|
||||
using Speckle.Connectors.DUI.Utils;
|
||||
using Speckle.Connectors.RhinoShared;
|
||||
|
||||
namespace Speckle.Connectors.Rhino.HostApp;
|
||||
|
||||
public class RhinoDocumentStore : DocumentModelStore
|
||||
{
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
|
||||
private const string SPECKLE_KEY = "Speckle_DUI3";
|
||||
public override bool IsDocumentInit { get; set; } = true; // Note: because of rhino implementation details regarding expiry checking of sender cards.
|
||||
|
||||
public RhinoDocumentStore(IJsonSerializer jsonSerializer, IEventAggregator eventAggregator)
|
||||
public RhinoDocumentStore(IJsonSerializer jsonSerializer, ITopLevelExceptionHandler topLevelExceptionHandler)
|
||||
: base(jsonSerializer)
|
||||
{
|
||||
_eventAggregator = eventAggregator;
|
||||
eventAggregator.GetEvent<BeginOpenDocument>().Subscribe(OnBeginOpenDocument);
|
||||
eventAggregator.GetEvent<EndOpenDocument>().Subscribe(OnEndOpenDocument);
|
||||
}
|
||||
_topLevelExceptionHandler = topLevelExceptionHandler;
|
||||
RhinoDoc.BeginOpenDocument += (_, _) => topLevelExceptionHandler.CatchUnhandled(() => IsDocumentInit = false);
|
||||
RhinoDoc.EndOpenDocument += (_, e) =>
|
||||
topLevelExceptionHandler.CatchUnhandled(() =>
|
||||
{
|
||||
if (e.Merge)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
private void OnBeginOpenDocument(object _) => IsDocumentInit = false;
|
||||
if (e.Document == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
private async Task OnEndOpenDocument(DocumentOpenEventArgs e)
|
||||
{
|
||||
if (e.Document == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IsDocumentInit = true;
|
||||
LoadState();
|
||||
await _eventAggregator.GetEvent<DocumentStoreChangedEvent>().PublishAsync(new object());
|
||||
IsDocumentInit = true;
|
||||
LoadState();
|
||||
OnDocumentChanged();
|
||||
});
|
||||
}
|
||||
|
||||
protected override void HostAppSaveState(string modelCardState)
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
using Rhino;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
|
||||
namespace Speckle.Connectors.Rhino.HostApp;
|
||||
|
||||
/// <summary>
|
||||
/// Rhino Idle Manager is a helper util to manage deferred actions.
|
||||
/// </summary>
|
||||
public sealed class RhinoIdleManager(IIdleCallManager idleCallManager) : AppIdleManager(idleCallManager)
|
||||
{
|
||||
private readonly IIdleCallManager _idleCallManager = idleCallManager;
|
||||
|
||||
protected override void AddEvent()
|
||||
{
|
||||
RhinoApp.Idle += RhinoAppOnIdle;
|
||||
}
|
||||
|
||||
private void RhinoAppOnIdle(object? sender, EventArgs e) =>
|
||||
_idleCallManager.AppOnIdle(() => RhinoApp.Idle -= RhinoAppOnIdle);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Rhino;
|
||||
using Rhino.DocObjects;
|
||||
using Rhino.Geometry;
|
||||
@@ -13,6 +13,7 @@ using Speckle.Sdk.Common.Exceptions;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.Collections;
|
||||
using Speckle.Sdk.Models.Instances;
|
||||
using RenderMaterial = Rhino.Render.RenderMaterial;
|
||||
|
||||
namespace Speckle.Connectors.Rhino.HostApp;
|
||||
|
||||
@@ -128,9 +129,11 @@ public class RhinoInstanceBaker : IInstanceBaker<IReadOnlyCollection<string>>
|
||||
string instanceProxyId = instanceProxy.applicationId ?? instanceProxy.id.NotNull();
|
||||
|
||||
ObjectAttributes atts = new() { LayerIndex = layerIndex };
|
||||
if (_materialBaker.ObjectIdAndMaterialIndexMap.TryGetValue(instanceProxyId, out int mIndex))
|
||||
if (
|
||||
_materialBaker.ObjectIdAndMaterialIndexMap.TryGetValue(instanceProxyId, out RenderMaterial renderMaterial)
|
||||
)
|
||||
{
|
||||
atts.MaterialIndex = mIndex;
|
||||
atts.RenderMaterial = renderMaterial;
|
||||
atts.MaterialSource = ObjectMaterialSource.MaterialFromObject;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Rhino;
|
||||
using Rhino.DocObjects;
|
||||
using Rhino.Render;
|
||||
using Speckle.Connectors.Common.Operations.Receive;
|
||||
using Speckle.Sdk;
|
||||
using Speckle.Sdk.Common;
|
||||
@@ -115,11 +116,11 @@ public class RhinoLayerBaker : TraversalContextUnpacker
|
||||
if (
|
||||
_materialBaker.ObjectIdAndMaterialIndexMap.TryGetValue(
|
||||
collection.applicationId ?? collection.id.NotNull(),
|
||||
out int mIndex
|
||||
out RenderMaterial renderMaterial
|
||||
)
|
||||
)
|
||||
{
|
||||
newLayer.RenderMaterialIndex = mIndex;
|
||||
newLayer.RenderMaterial = renderMaterial;
|
||||
}
|
||||
|
||||
// set color
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Rhino;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.Rhino;
|
||||
@@ -7,6 +7,7 @@ using Speckle.Sdk;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Common.Exceptions;
|
||||
using Material = Rhino.DocObjects.Material;
|
||||
using RenderMaterial = Rhino.Render.RenderMaterial;
|
||||
|
||||
namespace Speckle.Connectors.Rhino.HostApp;
|
||||
|
||||
@@ -25,9 +26,9 @@ public class RhinoMaterialBaker
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A map keeping track of ids, <b>either layer id or object id</b>, and their material index. It's generated from the material proxy list as we bake materials; <see cref="BakeMaterials"/> must be called in advance for this to be populated with the correct data.
|
||||
/// A map keeping track of ids, <b>either layer id or object id</b>, and their render material guid. It's generated from the material proxy list as we bake materials; <see cref="BakeMaterials"/> must be called in advance for this to be populated with the correct data.
|
||||
/// </summary>
|
||||
public Dictionary<string, int> ObjectIdAndMaterialIndexMap { get; } = new();
|
||||
public Dictionary<string, RenderMaterial> ObjectIdAndMaterialIndexMap { get; } = new();
|
||||
|
||||
public void BakeMaterials(IReadOnlyCollection<RenderMaterialProxy> speckleRenderMaterialProxies, string baseLayerName)
|
||||
{
|
||||
@@ -67,19 +68,23 @@ public class RhinoMaterialBaker
|
||||
rhinoMaterial.Shine = shine;
|
||||
}
|
||||
|
||||
int matIndex = doc.Materials.Add(rhinoMaterial);
|
||||
// We are creating a render material and adding it to the render material table because render materials have a guid independent of objects they are applied to.
|
||||
// Regular materials and the material table is populated by materials applied to objects: the same material can therefore have multiple entries in the material table if it is applied to multiple objects
|
||||
// see: https://discourse.mcneel.com/t/render-material-events/99886/5
|
||||
RenderMaterial rhinoRenderMaterial = RenderMaterial.FromMaterial(rhinoMaterial, doc);
|
||||
|
||||
// POC: check on matIndex -1, means we haven't created anything - this is most likely an recoverable error at this stage
|
||||
if (matIndex == -1)
|
||||
if (doc.RenderMaterials.Add(rhinoRenderMaterial))
|
||||
{
|
||||
// Create the object <> render material guid map
|
||||
foreach (var objectId in proxy.objects)
|
||||
{
|
||||
ObjectIdAndMaterialIndexMap[objectId] = rhinoRenderMaterial;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ConversionException("Failed to add a material to the document.");
|
||||
}
|
||||
|
||||
// Create the object <> material index map
|
||||
foreach (var objectId in proxy.objects)
|
||||
{
|
||||
ObjectIdAndMaterialIndexMap[objectId] = matIndex;
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when (!ex.IsFatal())
|
||||
{
|
||||
@@ -95,18 +100,13 @@ public class RhinoMaterialBaker
|
||||
public void PurgeMaterials(string namePrefix)
|
||||
{
|
||||
var currentDoc = RhinoDoc.ActiveDoc; // POC: too much right now to interface around
|
||||
foreach (Material material in currentDoc.Materials)
|
||||
// POC: looping through the render material table somehow doesn't capture all render materials!! That's why we're doing it this way.
|
||||
var materialsToDelete = currentDoc.RenderMaterials.Where(o => o.DisplayName.Contains(namePrefix)).ToList();
|
||||
foreach (RenderMaterial materialToDelete in materialsToDelete)
|
||||
{
|
||||
try
|
||||
if (!currentDoc.RenderMaterials.Remove(materialToDelete))
|
||||
{
|
||||
if (!material.IsDeleted && material.Name != null && material.Name.Contains(namePrefix))
|
||||
{
|
||||
currentDoc.Materials.Delete(material);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when (!ex.IsFatal())
|
||||
{
|
||||
_logger.LogError(ex, "Failed to purge a material from the document");
|
||||
_logger.LogError("Failed to purge a material from the document");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,17 +29,12 @@ public class RhinoMaterialUnpacker
|
||||
/// Processes an object's material and adds the object id to a material proxy in <see cref="RenderMaterialProxies"/> if object color is set ByObject or ByParent.
|
||||
/// </summary>
|
||||
/// <param name="objId"></param>
|
||||
private void ProcessObjectMaterial(
|
||||
string objId,
|
||||
RenderMaterial? renderMaterial,
|
||||
Material? material,
|
||||
ObjectMaterialSource source
|
||||
)
|
||||
private void ProcessObjectMaterial(string objId, RenderMaterial renderMaterial, ObjectMaterialSource source)
|
||||
{
|
||||
switch (source)
|
||||
{
|
||||
case ObjectMaterialSource.MaterialFromObject:
|
||||
AddObjectIdToRenderMaterialProxy(objId, renderMaterial, material);
|
||||
AddObjectIdToRenderMaterialProxy(objId, renderMaterial);
|
||||
break;
|
||||
|
||||
// POC: skip if object material source is *not* by object. we don't support render material inheritance atm bc alex disagrees with the concept
|
||||
@@ -48,52 +43,29 @@ public class RhinoMaterialUnpacker
|
||||
}
|
||||
}
|
||||
|
||||
private void AddObjectIdToRenderMaterialProxy(string objectId, RenderMaterial? renderMaterial, Material? material)
|
||||
private void AddObjectIdToRenderMaterialProxy(string objectId, RenderMaterial renderMaterial)
|
||||
{
|
||||
string? renderMaterialId = renderMaterial?.Id.ToString() ?? material?.Id.ToString();
|
||||
string? renderMaterialId = renderMaterial.Id.ToString();
|
||||
|
||||
if (renderMaterialId is not null)
|
||||
if (RenderMaterialProxies.TryGetValue(renderMaterialId, out RenderMaterialProxy? proxy))
|
||||
{
|
||||
if (RenderMaterialProxies.TryGetValue(renderMaterialId, out RenderMaterialProxy? proxy))
|
||||
proxy.objects.Add(objectId);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (
|
||||
ConvertMaterialToRenderMaterialProxy(renderMaterialId, renderMaterial) is RenderMaterialProxy newRenderMaterial
|
||||
)
|
||||
{
|
||||
proxy.objects.Add(objectId);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (
|
||||
ConvertMaterialToRenderMaterialProxy(renderMaterialId, renderMaterial, material)
|
||||
is RenderMaterialProxy newRenderMaterial
|
||||
)
|
||||
{
|
||||
newRenderMaterial.objects.Add(objectId);
|
||||
RenderMaterialProxies[renderMaterialId] = newRenderMaterial;
|
||||
}
|
||||
newRenderMaterial.objects.Add(objectId);
|
||||
RenderMaterialProxies[renderMaterialId] = newRenderMaterial;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private RenderMaterialProxy? ConvertMaterialToRenderMaterialProxy(
|
||||
string materialId,
|
||||
RenderMaterial? renderMaterial,
|
||||
Material? material
|
||||
)
|
||||
private RenderMaterialProxy? ConvertMaterialToRenderMaterialProxy(string materialId, RenderMaterial renderMaterial)
|
||||
{
|
||||
// TY Rhino api for being a bit confused about materials 💖
|
||||
SpeckleRenderMaterial? myMaterial = null;
|
||||
if (renderMaterial is not null)
|
||||
{
|
||||
myMaterial = ConvertRenderMaterialToSpeckle(renderMaterial);
|
||||
}
|
||||
else if (material is not null)
|
||||
{
|
||||
RenderMaterial convertedRender = ConvertMaterialToRenderMaterial(material);
|
||||
myMaterial = ConvertRenderMaterialToSpeckle(convertedRender);
|
||||
}
|
||||
|
||||
if (myMaterial is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
SpeckleRenderMaterial myMaterial = ConvertRenderMaterialToSpeckle(renderMaterial);
|
||||
|
||||
RenderMaterialProxy renderMaterialProxy =
|
||||
new()
|
||||
@@ -120,18 +92,26 @@ public class RhinoMaterialUnpacker
|
||||
// Stage 1: unpack materials from objects
|
||||
foreach (RhinoObject rootObj in atomicObjects)
|
||||
{
|
||||
// materials are confusing in rhino. we need both render material and material because objects can have either assigned
|
||||
// materials are confusing in rhino - some objects can have render materials, other may only have a material.
|
||||
// see: https://discourse.mcneel.com/t/getting-material-from-rhinoobject/114870/6
|
||||
// basically, materials (old) are created PER OBJECT if they are assigned per object. This means the same material will have diff ids when called from the material table
|
||||
// unfortunately, in the case where no rendermaterial exists, we'll have to create duplicate proxies.
|
||||
RenderMaterial? rhinoRenderMaterial = rootObj.GetRenderMaterial(true);
|
||||
Material? rhinoMaterial = rootObj.GetMaterial(true);
|
||||
if (rhinoRenderMaterial is null)
|
||||
{
|
||||
if (rootObj.GetMaterial(true) is Material rhinoMaterial)
|
||||
{
|
||||
rhinoRenderMaterial = RenderMaterial.FromMaterial(rhinoMaterial, currentDoc);
|
||||
}
|
||||
else // could not get rendermaterial or material
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ProcessObjectMaterial(
|
||||
rootObj.Id.ToString(),
|
||||
rhinoRenderMaterial,
|
||||
rhinoMaterial,
|
||||
rootObj.Attributes.MaterialSource
|
||||
);
|
||||
ProcessObjectMaterial(rootObj.Id.ToString(), rhinoRenderMaterial, rootObj.Attributes.MaterialSource);
|
||||
}
|
||||
catch (Exception ex) when (!ex.IsFatal())
|
||||
{
|
||||
@@ -142,19 +122,29 @@ public class RhinoMaterialUnpacker
|
||||
// Stage 2: make sure we collect layer materials as well
|
||||
foreach (Layer layer in layers)
|
||||
{
|
||||
// materials are confusing in rhino. we will first try to get layer render material and then material by index if null
|
||||
// materials are confusing in rhino - some objects can have render materials, other may only have a material.
|
||||
// see: https://discourse.mcneel.com/t/getting-material-from-rhinoobject/114870/6
|
||||
// basically, materials (old) are created PER OBJECT if they are assigned per object. This means the same material will have diff ids when called from the material table
|
||||
// unfortunately, in the case where no rendermaterial exists, we'll have to create duplicate proxies.
|
||||
RenderMaterial? rhinoRenderMaterial = layer.RenderMaterial;
|
||||
Material? rhinoMaterial =
|
||||
layer.RenderMaterialIndex == -1 ? null : currentDoc.Materials[layer.RenderMaterialIndex];
|
||||
if (rhinoRenderMaterial is null)
|
||||
{
|
||||
if (layer.RenderMaterialIndex == -1) // no material assigned
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
rhinoRenderMaterial = RenderMaterial.FromMaterial(
|
||||
currentDoc.Materials[layer.RenderMaterialIndex],
|
||||
currentDoc
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ProcessObjectMaterial(
|
||||
layer.Id.ToString(),
|
||||
rhinoRenderMaterial,
|
||||
rhinoMaterial,
|
||||
ObjectMaterialSource.MaterialFromObject
|
||||
);
|
||||
ProcessObjectMaterial(layer.Id.ToString(), rhinoRenderMaterial, ObjectMaterialSource.MaterialFromObject);
|
||||
}
|
||||
catch (Exception ex) when (!ex.IsFatal())
|
||||
{
|
||||
@@ -165,21 +155,6 @@ public class RhinoMaterialUnpacker
|
||||
return RenderMaterialProxies.Values.ToList();
|
||||
}
|
||||
|
||||
// converts a rhino material to a rhino render material
|
||||
private RenderMaterial ConvertMaterialToRenderMaterial(Material material)
|
||||
{
|
||||
// get physically based render material
|
||||
Material pbMaterial = material;
|
||||
if (!material.IsPhysicallyBased)
|
||||
{
|
||||
pbMaterial = new();
|
||||
pbMaterial.CopyFrom(material);
|
||||
pbMaterial.ToPhysicallyBased();
|
||||
}
|
||||
|
||||
return RenderMaterial.FromMaterial(pbMaterial, null);
|
||||
}
|
||||
|
||||
private SpeckleRenderMaterial ConvertRenderMaterialToSpeckle(RenderMaterial renderMaterial)
|
||||
{
|
||||
PhysicallyBasedMaterial pbRenderMaterial = renderMaterial.ConvertToPhysicallyBased(
|
||||
|
||||
+6
-6
@@ -16,6 +16,7 @@ using Speckle.Sdk.Logging;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.Collections;
|
||||
using Speckle.Sdk.Models.Instances;
|
||||
using RenderMaterial = Rhino.Render.RenderMaterial;
|
||||
|
||||
namespace Speckle.Connectors.Rhino.Operations.Receive;
|
||||
|
||||
@@ -183,6 +184,7 @@ public class RhinoHostObjectBuilder : IHostObjectBuilder
|
||||
|
||||
if (conversionIds.Count == 0)
|
||||
{
|
||||
// TODO: add this condition to report object - same as in autocad
|
||||
throw new SpeckleException($"Failed to convert object.");
|
||||
}
|
||||
|
||||
@@ -299,18 +301,16 @@ public class RhinoHostObjectBuilder : IHostObjectBuilder
|
||||
{
|
||||
var objectId = originalObject.applicationId ?? originalObject.id.NotNull();
|
||||
|
||||
if (_materialBaker.ObjectIdAndMaterialIndexMap.TryGetValue(objectId, out int mIndex))
|
||||
if (_materialBaker.ObjectIdAndMaterialIndexMap.TryGetValue(objectId, out RenderMaterial oRenderMaterial))
|
||||
{
|
||||
atts.MaterialIndex = mIndex;
|
||||
atts.MaterialSource = ObjectMaterialSource.MaterialFromObject;
|
||||
atts.RenderMaterial = oRenderMaterial; // no need to set source since setting render material handles this
|
||||
}
|
||||
else if (
|
||||
parentObjectId is not null
|
||||
&& (_materialBaker.ObjectIdAndMaterialIndexMap.TryGetValue(parentObjectId, out int mIndexSpeckleObj))
|
||||
&& (_materialBaker.ObjectIdAndMaterialIndexMap.TryGetValue(parentObjectId, out RenderMaterial pRenderMaterial))
|
||||
)
|
||||
{
|
||||
atts.MaterialIndex = mIndexSpeckleObj;
|
||||
atts.MaterialSource = ObjectMaterialSource.MaterialFromObject;
|
||||
atts.RenderMaterial = pRenderMaterial; // no need to set source since setting render material handles this
|
||||
}
|
||||
|
||||
if (_colorBaker.ObjectColorsIdMap.TryGetValue(objectId, out (Color, ObjectColorSource) color))
|
||||
|
||||
+17
@@ -8,6 +8,7 @@ using Speckle.Connectors.Common.Instances;
|
||||
using Speckle.Connectors.Common.Operations;
|
||||
using Speckle.Connectors.DUI.Models.Card.SendFilter;
|
||||
using Speckle.Connectors.Rhino.HostApp;
|
||||
using Speckle.Connectors.Rhino.HostApp.Properties;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.Rhino;
|
||||
using Speckle.Sdk;
|
||||
@@ -32,6 +33,7 @@ public class RhinoRootObjectBuilder : IRootObjectBuilder<RhinoObject>
|
||||
private readonly RhinoGroupUnpacker _groupUnpacker;
|
||||
private readonly RhinoMaterialUnpacker _materialUnpacker;
|
||||
private readonly RhinoColorUnpacker _colorUnpacker;
|
||||
private readonly PropertiesExtractor _propertiesExtractor;
|
||||
private readonly ILogger<RhinoRootObjectBuilder> _logger;
|
||||
private readonly ISdkActivityFactory _activityFactory;
|
||||
|
||||
@@ -44,6 +46,7 @@ public class RhinoRootObjectBuilder : IRootObjectBuilder<RhinoObject>
|
||||
RhinoGroupUnpacker groupUnpacker,
|
||||
RhinoMaterialUnpacker materialUnpacker,
|
||||
RhinoColorUnpacker colorUnpacker,
|
||||
PropertiesExtractor propertiesExtractor,
|
||||
ILogger<RhinoRootObjectBuilder> logger,
|
||||
ISdkActivityFactory activityFactory
|
||||
)
|
||||
@@ -56,6 +59,7 @@ public class RhinoRootObjectBuilder : IRootObjectBuilder<RhinoObject>
|
||||
_rootToSpeckleConverter = rootToSpeckleConverter;
|
||||
_materialUnpacker = materialUnpacker;
|
||||
_colorUnpacker = colorUnpacker;
|
||||
_propertiesExtractor = propertiesExtractor;
|
||||
_logger = logger;
|
||||
_activityFactory = activityFactory;
|
||||
}
|
||||
@@ -165,6 +169,19 @@ public class RhinoRootObjectBuilder : IRootObjectBuilder<RhinoObject>
|
||||
converted.applicationId = applicationId;
|
||||
}
|
||||
|
||||
// add name and properties
|
||||
// POC: this is NOT done in the converter because we don't have a RootToSpeckle converter that captures all top level converters
|
||||
if (!string.IsNullOrEmpty(rhinoObject.Attributes.Name))
|
||||
{
|
||||
converted["name"] = rhinoObject.Attributes.Name;
|
||||
}
|
||||
|
||||
var properties = _propertiesExtractor.GetProperties(rhinoObject);
|
||||
if (properties.Count > 0)
|
||||
{
|
||||
converted["properties"] = properties;
|
||||
}
|
||||
|
||||
// add to host
|
||||
collectionHost.elements.Add(converted);
|
||||
|
||||
|
||||
-3
@@ -2,9 +2,7 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
using Rhino.PlugIns;
|
||||
using Speckle.Connectors.Common;
|
||||
using Speckle.Connectors.DUI;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
using Speckle.Connectors.Rhino.DependencyInjection;
|
||||
using Speckle.Connectors.RhinoShared;
|
||||
using Speckle.Converters.Rhino;
|
||||
using Speckle.Sdk;
|
||||
using Speckle.Sdk.Host;
|
||||
@@ -53,7 +51,6 @@ public class SpeckleConnectorsRhinoPlugin : PlugIn
|
||||
|
||||
// but the Rhino connector has `.rhp` as it is extension.
|
||||
Container = services.BuildServiceProvider();
|
||||
RhinoEvents.Register(Container.GetRequiredService<IEventAggregator>());
|
||||
Container.UseDUI();
|
||||
|
||||
return LoadReturnCode.Success;
|
||||
|
||||
@@ -10,11 +10,13 @@ using Speckle.Connectors.Common.Operations;
|
||||
using Speckle.Connectors.Common.Threading;
|
||||
using Speckle.Connectors.DUI;
|
||||
using Speckle.Connectors.DUI.Bindings;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Models.Card.SendFilter;
|
||||
using Speckle.Connectors.DUI.WebView;
|
||||
using Speckle.Connectors.Rhino.Bindings;
|
||||
using Speckle.Connectors.Rhino.Filters;
|
||||
using Speckle.Connectors.Rhino.HostApp;
|
||||
using Speckle.Connectors.Rhino.HostApp.Properties;
|
||||
using Speckle.Connectors.Rhino.Operations.Receive;
|
||||
using Speckle.Connectors.Rhino.Operations.Send;
|
||||
using Speckle.Connectors.Rhino.Plugin;
|
||||
@@ -52,6 +54,7 @@ public static class ServiceRegistration
|
||||
|
||||
// register send conversion cache
|
||||
serviceCollection.AddSingleton<ISendConversionCache, SendConversionCache>();
|
||||
serviceCollection.AddSingleton<IAppIdleManager, RhinoIdleManager>();
|
||||
|
||||
// register send operation and dependencies
|
||||
serviceCollection.AddScoped<SendOperation<RhinoObject>>();
|
||||
@@ -79,6 +82,8 @@ public static class ServiceRegistration
|
||||
serviceCollection.AddScoped<RhinoColorBaker>();
|
||||
serviceCollection.AddScoped<RhinoColorUnpacker>();
|
||||
|
||||
serviceCollection.AddScoped<PropertiesExtractor>();
|
||||
|
||||
// operation progress manager
|
||||
serviceCollection.AddSingleton<IOperationProgressManager, OperationProgressManager>();
|
||||
}
|
||||
|
||||
@@ -1,87 +1,87 @@
|
||||
using Rhino;
|
||||
using Rhino.Commands;
|
||||
using Rhino.DocObjects;
|
||||
using Rhino.DocObjects.Tables;
|
||||
using Speckle.Connectors.Common.Threading;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
|
||||
namespace Speckle.Connectors.RhinoShared;
|
||||
|
||||
public class BeginOpenDocument(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
: ThreadedEvent<DocumentOpenEventArgs>(threadContext, exceptionHandler);
|
||||
|
||||
public class EndOpenDocument(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
: ThreadedEvent<DocumentOpenEventArgs>(threadContext, exceptionHandler);
|
||||
|
||||
public class SelectObjects(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
: ThreadedEvent<RhinoObjectSelectionEventArgs>(threadContext, exceptionHandler);
|
||||
|
||||
public class DeselectObjects(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
: ThreadedEvent<RhinoObjectSelectionEventArgs>(threadContext, exceptionHandler);
|
||||
|
||||
public class DeselectAllObjects(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
: ThreadedEvent<RhinoDeselectAllObjectsEventArgs>(threadContext, exceptionHandler);
|
||||
|
||||
public class ActiveDocumentChanged(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
: ThreadedEvent<DocumentEventArgs>(threadContext, exceptionHandler);
|
||||
|
||||
public class DocumentPropertiesChanged(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
: ThreadedEvent<DocumentEventArgs>(threadContext, exceptionHandler);
|
||||
|
||||
public class AddRhinoObject(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
: ThreadedEvent<RhinoObjectEventArgs>(threadContext, exceptionHandler);
|
||||
|
||||
public class DeleteRhinoObject(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
: ThreadedEvent<RhinoObjectEventArgs>(threadContext, exceptionHandler);
|
||||
|
||||
public class RenderMaterialsTableEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
: ThreadedEvent<RhinoDoc.RenderContentTableEventArgs>(threadContext, exceptionHandler);
|
||||
|
||||
public class MaterialTableEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
: ThreadedEvent<MaterialTableEventArgs>(threadContext, exceptionHandler);
|
||||
|
||||
public class ModifyObjectAttributes(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
: ThreadedEvent<RhinoModifyObjectAttributesEventArgs>(threadContext, exceptionHandler);
|
||||
|
||||
public class ReplaceRhinoObject(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
: ThreadedEvent<RhinoReplaceObjectEventArgs>(threadContext, exceptionHandler);
|
||||
|
||||
public class GroupTableEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
: ThreadedEvent<GroupTableEventArgs>(threadContext, exceptionHandler);
|
||||
|
||||
public class LayerTableEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
: ThreadedEvent<LayerTableEventArgs>(threadContext, exceptionHandler);
|
||||
|
||||
public class BeginCommandEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
: ThreadedEvent<CommandEventArgs>(threadContext, exceptionHandler);
|
||||
|
||||
public static class RhinoEvents
|
||||
{
|
||||
public static void Register(IEventAggregator eventAggregator)
|
||||
{
|
||||
RhinoApp.Idle += async (_, e) => await eventAggregator.GetEvent<IdleEvent>().PublishAsync(e);
|
||||
|
||||
RhinoDoc.BeginOpenDocument += async (_, e) => await eventAggregator.GetEvent<BeginOpenDocument>().PublishAsync(e);
|
||||
RhinoDoc.EndOpenDocument += async (_, e) => await eventAggregator.GetEvent<EndOpenDocument>().PublishAsync(e);
|
||||
RhinoDoc.SelectObjects += async (_, e) => await eventAggregator.GetEvent<SelectObjects>().PublishAsync(e);
|
||||
RhinoDoc.DeselectObjects += async (_, e) => await eventAggregator.GetEvent<DeselectObjects>().PublishAsync(e);
|
||||
RhinoDoc.DeselectAllObjects += async (_, e) => await eventAggregator.GetEvent<DeselectAllObjects>().PublishAsync(e);
|
||||
RhinoDoc.ActiveDocumentChanged += async (_, e) =>
|
||||
await eventAggregator.GetEvent<ActiveDocumentChanged>().PublishAsync(e);
|
||||
RhinoDoc.DocumentPropertiesChanged += async (_, e) =>
|
||||
await eventAggregator.GetEvent<DocumentPropertiesChanged>().PublishAsync(e);
|
||||
RhinoDoc.AddRhinoObject += async (_, e) => await eventAggregator.GetEvent<AddRhinoObject>().PublishAsync(e);
|
||||
RhinoDoc.DeleteRhinoObject += async (_, e) => await eventAggregator.GetEvent<DeleteRhinoObject>().PublishAsync(e);
|
||||
RhinoDoc.RenderMaterialsTableEvent += async (_, e) =>
|
||||
await eventAggregator.GetEvent<RenderMaterialsTableEvent>().PublishAsync(e);
|
||||
RhinoDoc.MaterialTableEvent += async (_, e) => await eventAggregator.GetEvent<MaterialTableEvent>().PublishAsync(e);
|
||||
RhinoDoc.ModifyObjectAttributes += async (_, e) =>
|
||||
await eventAggregator.GetEvent<ModifyObjectAttributes>().PublishAsync(e);
|
||||
RhinoDoc.ReplaceRhinoObject += async (_, e) => await eventAggregator.GetEvent<ReplaceRhinoObject>().PublishAsync(e);
|
||||
RhinoDoc.GroupTableEvent += async (_, e) => await eventAggregator.GetEvent<GroupTableEvent>().PublishAsync(e);
|
||||
RhinoDoc.LayerTableEvent += async (_, e) => await eventAggregator.GetEvent<LayerTableEvent>().PublishAsync(e);
|
||||
|
||||
Command.BeginCommand += async (_, e) => await eventAggregator.GetEvent<BeginCommandEvent>().PublishAsync(e);
|
||||
}
|
||||
}
|
||||
// using Rhino;
|
||||
// using Rhino.Commands;
|
||||
// using Rhino.DocObjects;
|
||||
// using Rhino.DocObjects.Tables;
|
||||
// using Speckle.Connectors.Common.Threading;
|
||||
// using Speckle.Connectors.DUI.Bridge;
|
||||
// using Speckle.Connectors.DUI.Eventing;
|
||||
//
|
||||
// namespace Speckle.Connectors.RhinoShared;
|
||||
//
|
||||
// public class BeginOpenDocument(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
// : ThreadedEvent<DocumentOpenEventArgs>(threadContext, exceptionHandler);
|
||||
//
|
||||
// public class EndOpenDocument(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
// : ThreadedEvent<DocumentOpenEventArgs>(threadContext, exceptionHandler);
|
||||
//
|
||||
// public class SelectObjects(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
// : ThreadedEvent<RhinoObjectSelectionEventArgs>(threadContext, exceptionHandler);
|
||||
//
|
||||
// public class DeselectObjects(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
// : ThreadedEvent<RhinoObjectSelectionEventArgs>(threadContext, exceptionHandler);
|
||||
//
|
||||
// public class DeselectAllObjects(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
// : ThreadedEvent<RhinoDeselectAllObjectsEventArgs>(threadContext, exceptionHandler);
|
||||
//
|
||||
// public class ActiveDocumentChanged(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
// : ThreadedEvent<DocumentEventArgs>(threadContext, exceptionHandler);
|
||||
//
|
||||
// public class DocumentPropertiesChanged(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
// : ThreadedEvent<DocumentEventArgs>(threadContext, exceptionHandler);
|
||||
//
|
||||
// public class AddRhinoObject(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
// : ThreadedEvent<RhinoObjectEventArgs>(threadContext, exceptionHandler);
|
||||
//
|
||||
// public class DeleteRhinoObject(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
// : ThreadedEvent<RhinoObjectEventArgs>(threadContext, exceptionHandler);
|
||||
//
|
||||
// public class RenderMaterialsTableEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
// : ThreadedEvent<RhinoDoc.RenderContentTableEventArgs>(threadContext, exceptionHandler);
|
||||
//
|
||||
// public class MaterialTableEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
// : ThreadedEvent<MaterialTableEventArgs>(threadContext, exceptionHandler);
|
||||
//
|
||||
// public class ModifyObjectAttributes(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
// : ThreadedEvent<RhinoModifyObjectAttributesEventArgs>(threadContext, exceptionHandler);
|
||||
//
|
||||
// public class ReplaceRhinoObject(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
// : ThreadedEvent<RhinoReplaceObjectEventArgs>(threadContext, exceptionHandler);
|
||||
//
|
||||
// public class GroupTableEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
// : ThreadedEvent<GroupTableEventArgs>(threadContext, exceptionHandler);
|
||||
//
|
||||
// public class LayerTableEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
// : ThreadedEvent<LayerTableEventArgs>(threadContext, exceptionHandler);
|
||||
//
|
||||
// public class BeginCommandEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
// : ThreadedEvent<CommandEventArgs>(threadContext, exceptionHandler);
|
||||
//
|
||||
// public static class RhinoEvents
|
||||
// {
|
||||
// public static void Register(IEventAggregator eventAggregator)
|
||||
// {
|
||||
// RhinoApp.Idle += async (_, e) => await eventAggregator.GetEvent<IdleEvent>().PublishAsync(e);
|
||||
//
|
||||
// RhinoDoc.BeginOpenDocument += async (_, e) => await eventAggregator.GetEvent<BeginOpenDocument>().PublishAsync(e);
|
||||
// RhinoDoc.EndOpenDocument += async (_, e) => await eventAggregator.GetEvent<EndOpenDocument>().PublishAsync(e);
|
||||
// RhinoDoc.SelectObjects += async (_, e) => await eventAggregator.GetEvent<SelectObjects>().PublishAsync(e);
|
||||
// RhinoDoc.DeselectObjects += async (_, e) => await eventAggregator.GetEvent<DeselectObjects>().PublishAsync(e);
|
||||
// RhinoDoc.DeselectAllObjects += async (_, e) => await eventAggregator.GetEvent<DeselectAllObjects>().PublishAsync(e);
|
||||
// RhinoDoc.ActiveDocumentChanged += async (_, e) =>
|
||||
// await eventAggregator.GetEvent<ActiveDocumentChanged>().PublishAsync(e);
|
||||
// RhinoDoc.DocumentPropertiesChanged += async (_, e) =>
|
||||
// await eventAggregator.GetEvent<DocumentPropertiesChanged>().PublishAsync(e);
|
||||
// RhinoDoc.AddRhinoObject += async (_, e) => await eventAggregator.GetEvent<AddRhinoObject>().PublishAsync(e);
|
||||
// RhinoDoc.DeleteRhinoObject += async (_, e) => await eventAggregator.GetEvent<DeleteRhinoObject>().PublishAsync(e);
|
||||
// RhinoDoc.RenderMaterialsTableEvent += async (_, e) =>
|
||||
// await eventAggregator.GetEvent<RenderMaterialsTableEvent>().PublishAsync(e);
|
||||
// RhinoDoc.MaterialTableEvent += async (_, e) => await eventAggregator.GetEvent<MaterialTableEvent>().PublishAsync(e);
|
||||
// RhinoDoc.ModifyObjectAttributes += async (_, e) =>
|
||||
// await eventAggregator.GetEvent<ModifyObjectAttributes>().PublishAsync(e);
|
||||
// RhinoDoc.ReplaceRhinoObject += async (_, e) => await eventAggregator.GetEvent<ReplaceRhinoObject>().PublishAsync(e);
|
||||
// RhinoDoc.GroupTableEvent += async (_, e) => await eventAggregator.GetEvent<GroupTableEvent>().PublishAsync(e);
|
||||
// RhinoDoc.LayerTableEvent += async (_, e) => await eventAggregator.GetEvent<LayerTableEvent>().PublishAsync(e);
|
||||
//
|
||||
// Command.BeginCommand += async (_, e) => await eventAggregator.GetEvent<BeginCommandEvent>().PublishAsync(e);
|
||||
// }
|
||||
// }
|
||||
|
||||
+2
@@ -21,6 +21,8 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Bindings\RhinoReceiveBinding.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Bindings\RhinoSendBinding.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Bindings\RhinoSelectionBinding.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Properties\PropertiesExtractor.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\RhinoIdleManager.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)RhinoEvents.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Registration\ServiceRegistration.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Extensions\BoundingBox.cs" />
|
||||
|
||||
+9
-6
@@ -2,7 +2,6 @@ using System.Collections;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Speckle.Connectors.DUI.Bindings;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
using Speckle.Connectors.DUI.Models;
|
||||
using Speckle.Connectors.DUI.Models.Card;
|
||||
using Speckle.Sdk;
|
||||
@@ -20,14 +19,15 @@ public class TeklaBasicConnectorBinding : IBasicConnectorBinding
|
||||
public IBrowserBridge Parent { get; }
|
||||
private readonly ILogger<TeklaBasicConnectorBinding> _logger;
|
||||
private readonly TSM.Model _model;
|
||||
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
|
||||
|
||||
public TeklaBasicConnectorBinding(
|
||||
IBrowserBridge parent,
|
||||
ISpeckleApplication speckleApplication,
|
||||
DocumentModelStore store,
|
||||
ILogger<TeklaBasicConnectorBinding> logger,
|
||||
IEventAggregator eventAggregator,
|
||||
TSM.Model model
|
||||
TSM.Model model,
|
||||
ITopLevelExceptionHandler topLevelExceptionHandler
|
||||
)
|
||||
{
|
||||
_speckleApplication = speckleApplication;
|
||||
@@ -35,12 +35,15 @@ public class TeklaBasicConnectorBinding : IBasicConnectorBinding
|
||||
Parent = parent;
|
||||
_logger = logger;
|
||||
_model = model;
|
||||
_topLevelExceptionHandler = topLevelExceptionHandler;
|
||||
Commands = new BasicConnectorBindingCommands(parent);
|
||||
eventAggregator.GetEvent<DocumentStoreChangedEvent>().Subscribe(OnDocumentStoreChangedEvent);
|
||||
_store.DocumentChanged += (_, _) =>
|
||||
_topLevelExceptionHandler.FireAndForget(async () =>
|
||||
{
|
||||
await Commands.NotifyDocumentChanged();
|
||||
});
|
||||
}
|
||||
|
||||
private async Task OnDocumentStoreChangedEvent(object _) => await Commands.NotifyDocumentChanged();
|
||||
|
||||
public string GetSourceApplicationName() => _speckleApplication.Slug;
|
||||
|
||||
public string GetSourceApplicationVersion() => _speckleApplication.HostApplicationVersion;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using Speckle.Connectors.DUI.Bindings;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
using Tekla.Structures.Model;
|
||||
|
||||
namespace Speckle.Connectors.TeklaShared.Bindings;
|
||||
@@ -9,29 +8,34 @@ public class TeklaSelectionBinding : ISelectionBinding
|
||||
{
|
||||
private const string SELECTION_EVENT = "setSelection";
|
||||
private readonly object _selectionEventHandlerLock = new object();
|
||||
private readonly IAppIdleManager _idleManager;
|
||||
private readonly Events _events;
|
||||
private readonly Tekla.Structures.Model.UI.ModelObjectSelector _selector;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
|
||||
public string Name => "selectionBinding";
|
||||
public IBrowserBridge Parent { get; }
|
||||
|
||||
public TeklaSelectionBinding(
|
||||
IAppIdleManager idleManager,
|
||||
IBrowserBridge parent,
|
||||
Tekla.Structures.Model.UI.ModelObjectSelector selector,
|
||||
IEventAggregator eventAggregator
|
||||
Events events,
|
||||
Tekla.Structures.Model.UI.ModelObjectSelector selector
|
||||
)
|
||||
{
|
||||
_idleManager = idleManager;
|
||||
Parent = parent;
|
||||
_selector = selector;
|
||||
_eventAggregator = eventAggregator;
|
||||
_events = events;
|
||||
|
||||
eventAggregator.GetEvent<SelectionChangeEvent>().Subscribe(OnSelectionChangeEvent);
|
||||
_events.SelectionChange += OnSelectionChangeEvent;
|
||||
_events.Register();
|
||||
}
|
||||
|
||||
private void OnSelectionChangeEvent(object _)
|
||||
private void OnSelectionChangeEvent()
|
||||
{
|
||||
lock (_selectionEventHandlerLock)
|
||||
{
|
||||
_idleManager.SubscribeToIdle(nameof(TeklaSelectionBinding), UpdateSelection);
|
||||
UpdateSelection();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ using Speckle.Connectors.Common.Cancellation;
|
||||
using Speckle.Connectors.Common.Operations;
|
||||
using Speckle.Connectors.DUI.Bindings;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
using Speckle.Connectors.DUI.Exceptions;
|
||||
using Speckle.Connectors.DUI.Logging;
|
||||
using Speckle.Connectors.DUI.Models;
|
||||
@@ -43,6 +42,7 @@ public sealed class TeklaSendBinding : ISendBinding
|
||||
private readonly ISdkActivityFactory _activityFactory;
|
||||
private readonly Model _model;
|
||||
private readonly ToSpeckleSettingsManager _toSpeckleSettingsManager;
|
||||
private readonly Events _events;
|
||||
|
||||
private ConcurrentDictionary<string, byte> ChangedObjectIds { get; set; } = new();
|
||||
|
||||
@@ -58,8 +58,7 @@ public sealed class TeklaSendBinding : ISendBinding
|
||||
ITeklaConversionSettingsFactory teklaConversionSettingsFactory,
|
||||
ISpeckleApplication speckleApplication,
|
||||
ISdkActivityFactory activityFactory,
|
||||
ToSpeckleSettingsManager toSpeckleSettingsManager,
|
||||
IEventAggregator eventAggregator
|
||||
ToSpeckleSettingsManager toSpeckleSettingsManager
|
||||
)
|
||||
{
|
||||
_store = store;
|
||||
@@ -77,11 +76,18 @@ public sealed class TeklaSendBinding : ISendBinding
|
||||
_toSpeckleSettingsManager = toSpeckleSettingsManager;
|
||||
|
||||
_model = new Model();
|
||||
eventAggregator.GetEvent<ModelObjectChangedEvent>().Subscribe(ModelHandler_OnChange);
|
||||
_events = new Events();
|
||||
SubscribeToTeklaEvents();
|
||||
}
|
||||
|
||||
private void SubscribeToTeklaEvents()
|
||||
{
|
||||
_events.ModelObjectChanged += OnModelObjectChanged;
|
||||
_events.Register();
|
||||
}
|
||||
|
||||
// subscribes the all changes in a modelobject
|
||||
private void ModelHandler_OnChange(List<ChangeData> changes)
|
||||
private void OnModelObjectChanged(List<ChangeData> changes)
|
||||
{
|
||||
foreach (var change in changes)
|
||||
{
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
using Speckle.Connectors.Common.Threading;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
|
||||
namespace Speckle.Connectors.TeklaShared;
|
||||
|
||||
public class SelectionChangeEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
: ThreadedEvent<object>(threadContext, exceptionHandler);
|
||||
|
||||
public class ModelObjectChangedEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
: ThreadedEvent<List<TSM.ChangeData>>(threadContext, exceptionHandler);
|
||||
|
||||
public class ModelLoadEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
: ThreadedEvent<object>(threadContext, exceptionHandler);
|
||||
|
||||
public static class TeklaEvents
|
||||
{
|
||||
public static void Register(Tekla.Structures.Model.Events events, IEventAggregator eventAggregator)
|
||||
{
|
||||
events.UnRegister();
|
||||
events.SelectionChange += async () =>
|
||||
await eventAggregator.GetEvent<SelectionChangeEvent>().PublishAsync(new object());
|
||||
events.ModelObjectChanged += async x => await eventAggregator.GetEvent<ModelObjectChangedEvent>().PublishAsync(x);
|
||||
events.ModelLoad += async () => await eventAggregator.GetEvent<ModelLoadEvent>().PublishAsync(new object());
|
||||
events.Register();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
using Speckle.Connectors.DUI.Models;
|
||||
using Speckle.Connectors.DUI.Utils;
|
||||
using Speckle.Sdk;
|
||||
@@ -11,40 +10,34 @@ namespace Speckle.Connectors.TeklaShared.HostApp;
|
||||
public class TeklaDocumentModelStore : DocumentModelStore
|
||||
{
|
||||
private readonly ILogger<TeklaDocumentModelStore> _logger;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly ISqLiteJsonCacheManager _jsonCacheManager;
|
||||
private readonly TSM.Model _model;
|
||||
private string? _modelKey;
|
||||
private readonly TSM.Events _events;
|
||||
|
||||
public TeklaDocumentModelStore(
|
||||
IJsonSerializer jsonSerializer,
|
||||
ILogger<TeklaDocumentModelStore> logger,
|
||||
ISqLiteJsonCacheManagerFactory jsonCacheManagerFactory,
|
||||
IEventAggregator eventAggregator
|
||||
ISqLiteJsonCacheManagerFactory jsonCacheManagerFactory
|
||||
)
|
||||
: base(jsonSerializer)
|
||||
{
|
||||
_logger = logger;
|
||||
_eventAggregator = eventAggregator;
|
||||
_jsonCacheManager = jsonCacheManagerFactory.CreateForUser("ConnectorsFileData");
|
||||
_events = new TSM.Events();
|
||||
_model = new TSM.Model();
|
||||
GenerateKey();
|
||||
eventAggregator.GetEvent<ModelLoadEvent>().Subscribe(OnModelLoadEvent);
|
||||
}
|
||||
|
||||
private async Task OnModelLoadEvent(object _)
|
||||
{
|
||||
GenerateKey();
|
||||
LoadState();
|
||||
await _eventAggregator.GetEvent<DocumentStoreChangedEvent>().PublishAsync(new object());
|
||||
}
|
||||
|
||||
public override async Task OnDocumentStoreInitialized()
|
||||
{
|
||||
_events.ModelLoad += () =>
|
||||
{
|
||||
GenerateKey();
|
||||
LoadState();
|
||||
OnDocumentChanged();
|
||||
};
|
||||
_events.Register();
|
||||
if (SpeckleTeklaPanelHost.IsInitialized)
|
||||
{
|
||||
LoadState();
|
||||
await _eventAggregator.GetEvent<DocumentStoreChangedEvent>().PublishAsync(new object());
|
||||
OnDocumentChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Tekla.Structures.Model;
|
||||
|
||||
namespace Speckle.Connectors.TeklaShared.HostApp;
|
||||
|
||||
public sealed class TeklaIdleManager : AppIdleManager
|
||||
{
|
||||
private readonly IIdleCallManager _idleCallManager;
|
||||
private readonly Events _events;
|
||||
|
||||
public TeklaIdleManager(IIdleCallManager idleCallManager, Events events)
|
||||
: base(idleCallManager)
|
||||
{
|
||||
_idleCallManager = idleCallManager;
|
||||
_events = events;
|
||||
}
|
||||
|
||||
protected override void AddEvent()
|
||||
{
|
||||
_events.ModelSave += TeklaEventsOnIdle;
|
||||
_events.Register();
|
||||
}
|
||||
|
||||
private void TeklaEventsOnIdle()
|
||||
{
|
||||
_idleCallManager.AppOnIdle(() =>
|
||||
{
|
||||
_events.ModelSave -= TeklaEventsOnIdle;
|
||||
_events.UnRegister();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,8 @@ public static class ServiceRegistration
|
||||
services.AddDUI<DefaultThreadContext, TeklaDocumentModelStore>();
|
||||
services.AddDUIView();
|
||||
|
||||
services.AddSingleton<IAppIdleManager, TeklaIdleManager>();
|
||||
|
||||
services.AddSingleton<IBinding, TestBinding>();
|
||||
services.AddSingleton<IBinding, ConfigBinding>();
|
||||
services.AddSingleton<IBinding, AccountBinding>();
|
||||
|
||||
+1
-1
@@ -17,13 +17,13 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Bindings\TeklaBasicConnectorBinding.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Bindings\TeklaSelectionBinding.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Bindings\TeklaSendBinding.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Events.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Extensions\ModelObjectExtension.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Extensions\SpeckleApplicationIdExtensions.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Filters\TeklaSelectionFilter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)GlobalUsing.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\SendCollectionManager.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\TeklaDocumentModelStore.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\TeklaIdleManager.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\TeklaMaterialUnpacker.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\SendRebarsAsSolidSetting.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\ToSpeckleSettingsManager.cs" />
|
||||
|
||||
@@ -6,7 +6,6 @@ using System.Windows.Forms.Integration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Speckle.Connectors.Common;
|
||||
using Speckle.Connectors.DUI;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
using Speckle.Connectors.DUI.WebView;
|
||||
using Speckle.Converters.TeklaShared;
|
||||
using Speckle.Sdk.Host;
|
||||
@@ -91,7 +90,6 @@ public class SpeckleTeklaPanelHost : PluginFormBase
|
||||
services.AddTeklaConverters();
|
||||
|
||||
Container = services.BuildServiceProvider();
|
||||
TeklaEvents.Register(Container.GetRequiredService<Events>(), Container.GetRequiredService<IEventAggregator>());
|
||||
Container.UseDUI();
|
||||
|
||||
Model = new Model();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Autodesk.AutoCAD.DatabaseServices;
|
||||
// using Speckle.Converters.AutocadShared.ToSpeckle;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.Common.Objects;
|
||||
using Speckle.Converters.Common.Registration;
|
||||
@@ -12,13 +13,17 @@ public class AutocadRootToSpeckleConverter : IRootToSpeckleConverter
|
||||
private readonly IConverterManager<IToSpeckleTopLevelConverter> _toSpeckle;
|
||||
private readonly IConverterSettingsStore<AutocadConversionSettings> _settingsStore;
|
||||
|
||||
// private readonly PropertiesExtractor _propertiesExtractor; // NOTE: commented out as we can't test (no sample file)
|
||||
|
||||
public AutocadRootToSpeckleConverter(
|
||||
IConverterManager<IToSpeckleTopLevelConverter> toSpeckle,
|
||||
IConverterSettingsStore<AutocadConversionSettings> settingsStore
|
||||
// PropertiesExtractor propertiesExtractor // NOTE: commented out as we can't test (no sample file)
|
||||
)
|
||||
{
|
||||
_toSpeckle = toSpeckle;
|
||||
_settingsStore = settingsStore;
|
||||
// _propertiesExtractor = propertiesExtractor; // NOTE: commented out as we can't test (no sample file)
|
||||
}
|
||||
|
||||
public Base Convert(object target)
|
||||
@@ -39,6 +44,15 @@ public class AutocadRootToSpeckleConverter : IRootToSpeckleConverter
|
||||
var objectConverter = _toSpeckle.ResolveConverter(type);
|
||||
|
||||
var convertedObject = objectConverter.Convert(dbObject);
|
||||
|
||||
// NOTE: commented out as we can't test (no sample file)
|
||||
// add properties
|
||||
// Dictionary<string, object?> properties = _propertiesExtractor.GetProperties((Entity)dbObject);
|
||||
// if (properties.Count > 0)
|
||||
// {
|
||||
// convertedObject["properties"] = properties;
|
||||
// }
|
||||
|
||||
tr.Commit();
|
||||
return convertedObject;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Reflection;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Speckle.Converters.AutocadShared.ToSpeckle;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.Common.Registration;
|
||||
using Speckle.Sdk;
|
||||
@@ -23,5 +24,9 @@ public static class ServiceRegistration
|
||||
ConverterSettingsStore<AutocadConversionSettings>
|
||||
>();
|
||||
serviceCollection.AddMatchingInterfacesAsTransient(Assembly.GetExecutingAssembly());
|
||||
|
||||
// add other classes
|
||||
// serviceCollection.AddScoped<PropertiesExtractor>(); // NOTE: commented out until we can test in acad
|
||||
serviceCollection.AddScoped<ExtensionDictionaryExtractor>();
|
||||
}
|
||||
}
|
||||
|
||||
+6
@@ -18,6 +18,10 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Extensions\ListExtensions.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ServiceRegistration.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\Geometry\AutocadPolycurveToHostConverter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\Geometry\BrepToHostConverter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\Geometry\ExtrusionXToHostConverter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\Geometry\SubDXToHostConverter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\Geometry\BrepXToHostConverter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\Geometry\MeshToHostConverter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\Geometry\DataObjectConverter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\Raw\AutocadPolycurveToHostPolyline2dRawConverter.cs" />
|
||||
@@ -44,6 +48,8 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToSpeckle\Geometry\RegionToSpeckleConverter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToSpeckle\Geometry\SurfaceToSpeckleConverter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToSpeckle\Geometry\Solid3dToSpeckleConverter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToSpeckle\Properties\ExtensionDictionaryExtractor.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToSpeckle\Properties\PropertiesExtractor.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToSpeckle\Raw\BrepToSpeckleRawConverter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToSpeckle\Raw\CircularArc2dToSpeckleRawConverter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToSpeckle\Raw\DBArcToSpeckleRawConverter.cs" />
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.Common.Objects;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Converters.Autocad.Geometry;
|
||||
|
||||
/// <summary>
|
||||
/// Converts a Brep to a List(PolyFaceMesh,Mesh)> as fallback conversion
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The return type is (Entity,Base) instead of the specific type (PolyfaceMesh, Mesh) so this result can be picked up by a generic list case in the SpeckleToHost connector object baking. This is essentially one-to-many fallback conversion.
|
||||
/// </remarks>
|
||||
[NameAndRankValue(typeof(SOG.Brep), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)]
|
||||
public class BrepToHostConverter : IToHostTopLevelConverter, ITypedConverter<SOG.Brep, List<(ADB.Entity a, Base b)>>
|
||||
{
|
||||
private readonly ITypedConverter<SOG.Mesh, ADB.PolyFaceMesh> _meshConverter;
|
||||
|
||||
public BrepToHostConverter(ITypedConverter<SOG.Mesh, ADB.PolyFaceMesh> meshConverter)
|
||||
{
|
||||
_meshConverter = meshConverter;
|
||||
}
|
||||
|
||||
public object Convert(Base target) => Convert((SOG.Brep)target);
|
||||
|
||||
/// <remarks>
|
||||
/// Unlikey case, but we need to handle multiple meshes inside of brepx displayvalue
|
||||
/// </remarks>
|
||||
public List<(ADB.Entity a, Base b)> Convert(SOG.Brep target)
|
||||
{
|
||||
var result = new List<ADB.PolyFaceMesh>();
|
||||
foreach (SOG.Mesh mesh in target.displayValue)
|
||||
{
|
||||
ADB.PolyFaceMesh convertedMesh = _meshConverter.Convert(mesh);
|
||||
result.Add(convertedMesh);
|
||||
}
|
||||
|
||||
return result.Zip(target.displayValue, (a, b) => ((ADB.Entity)a, (Base)b)).ToList();
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.Common.Objects;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Converters.Autocad.Geometry;
|
||||
|
||||
/// <summary>
|
||||
/// Converts a BrepX to a List(PolyFaceMesh,Mesh)> as fallback conversion
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The return type is (Entity,Base) instead of the specific type (PolyfaceMesh, Mesh) so this result can be picked up by a generic list case in the SpeckleToHost connector object baking. This is essentially one-to-many fallback conversion.
|
||||
/// </remarks>
|
||||
[NameAndRankValue(typeof(SOG.BrepX), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)]
|
||||
public class BrepXToHostConverter : IToHostTopLevelConverter, ITypedConverter<SOG.BrepX, List<(ADB.Entity a, Base b)>>
|
||||
{
|
||||
private readonly ITypedConverter<SOG.Mesh, ADB.PolyFaceMesh> _meshConverter;
|
||||
|
||||
public BrepXToHostConverter(ITypedConverter<SOG.Mesh, ADB.PolyFaceMesh> meshConverter)
|
||||
{
|
||||
_meshConverter = meshConverter;
|
||||
}
|
||||
|
||||
public object Convert(Base target) => Convert((SOG.BrepX)target);
|
||||
|
||||
/// <remarks>
|
||||
/// Unlikey case, but we need to handle multiple meshes inside of brepx displayvalue.
|
||||
/// </remarks>
|
||||
public List<(ADB.Entity a, Base b)> Convert(SOG.BrepX target)
|
||||
{
|
||||
var result = new List<ADB.PolyFaceMesh>();
|
||||
foreach (SOG.Mesh mesh in target.displayValue)
|
||||
{
|
||||
ADB.PolyFaceMesh convertedMesh = _meshConverter.Convert(mesh);
|
||||
result.Add(convertedMesh);
|
||||
}
|
||||
|
||||
return result.Zip(target.displayValue, (a, b) => ((ADB.Entity)a, (Base)b)).ToList();
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.Common.Objects;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Converters.Autocad.Geometry;
|
||||
|
||||
/// <summary>
|
||||
/// Converts a ExtrusionX to a List(PolyFaceMesh,Mesh)> as fallback conversion
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The return type is (Entity,Base) instead of the specific type (PolyfaceMesh, Mesh) so this result can be picked up by a generic list case in the SpeckleToHost connector object baking. This is essentially one-to-many fallback conversion.
|
||||
/// </remarks>
|
||||
[NameAndRankValue(typeof(SOG.ExtrusionX), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)]
|
||||
public class ExtrusionXToHostConverter
|
||||
: IToHostTopLevelConverter,
|
||||
ITypedConverter<SOG.ExtrusionX, List<(ADB.Entity a, Base b)>>
|
||||
{
|
||||
private readonly ITypedConverter<SOG.Mesh, ADB.PolyFaceMesh> _meshConverter;
|
||||
|
||||
public ExtrusionXToHostConverter(ITypedConverter<SOG.Mesh, ADB.PolyFaceMesh> meshConverter)
|
||||
{
|
||||
_meshConverter = meshConverter;
|
||||
}
|
||||
|
||||
public object Convert(Base target) => Convert((SOG.ExtrusionX)target);
|
||||
|
||||
/// <remarks>
|
||||
/// Unlikey case, but we need to handle multiple meshes inside of extrusionx displayvalue
|
||||
/// </remarks>
|
||||
public List<(ADB.Entity a, Base b)> Convert(SOG.ExtrusionX target)
|
||||
{
|
||||
var result = new List<ADB.PolyFaceMesh>();
|
||||
foreach (SOG.Mesh mesh in target.displayValue)
|
||||
{
|
||||
ADB.PolyFaceMesh convertedMesh = _meshConverter.Convert(mesh);
|
||||
result.Add(convertedMesh);
|
||||
}
|
||||
|
||||
return result.Zip(target.displayValue, (a, b) => ((ADB.Entity)a, (Base)b)).ToList();
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.Common.Objects;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Converters.Autocad.Geometry;
|
||||
|
||||
/// <summary>
|
||||
/// Converts a SubDX to a List(PolyFaceMesh,Mesh)> as fallback conversion
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The return type is (Entity,Base) instead of the specific type (PolyfaceMesh, Mesh) so this result can be picked up by a generic list case in the SpeckleToHost connector object baking. This is essentially one-to-many fallback conversion.
|
||||
/// </remarks>
|
||||
[NameAndRankValue(typeof(SOG.SubDX), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)]
|
||||
public class SubDXToHostConverter : IToHostTopLevelConverter, ITypedConverter<SOG.SubDX, List<(ADB.Entity a, Base b)>>
|
||||
{
|
||||
private readonly ITypedConverter<SOG.Mesh, ADB.PolyFaceMesh> _meshConverter;
|
||||
|
||||
public SubDXToHostConverter(ITypedConverter<SOG.Mesh, ADB.PolyFaceMesh> meshConverter)
|
||||
{
|
||||
_meshConverter = meshConverter;
|
||||
}
|
||||
|
||||
public object Convert(Base target) => Convert((SOG.SubDX)target);
|
||||
|
||||
/// <remarks>
|
||||
/// Unlikey case, but we need to handle multiple meshes inside of subdx displayvalue
|
||||
/// </remarks>
|
||||
public List<(ADB.Entity a, Base b)> Convert(SOG.SubDX target)
|
||||
{
|
||||
var result = new List<ADB.PolyFaceMesh>();
|
||||
foreach (SOG.Mesh mesh in target.displayValue)
|
||||
{
|
||||
ADB.PolyFaceMesh convertedMesh = _meshConverter.Convert(mesh);
|
||||
result.Add(convertedMesh);
|
||||
}
|
||||
|
||||
return result.Zip(target.displayValue, (a, b) => ((ADB.Entity)a, (Base)b)).ToList();
|
||||
}
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
using Speckle.Converters.Autocad;
|
||||
using Speckle.Converters.Common;
|
||||
|
||||
namespace Speckle.Converters.AutocadShared.ToSpeckle;
|
||||
|
||||
/// <summary>
|
||||
/// Extracts extension dictionaries out from an element. Expects to be scoped per operation.
|
||||
/// </summary>
|
||||
public class ExtensionDictionaryExtractor
|
||||
{
|
||||
private readonly IConverterSettingsStore<AutocadConversionSettings> _settingsStore;
|
||||
|
||||
public ExtensionDictionaryExtractor(IConverterSettingsStore<AutocadConversionSettings> settingsStore)
|
||||
{
|
||||
_settingsStore = settingsStore;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts extension dictionary out from an entity. Expects to be scoped per operation.
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <returns></returns>
|
||||
public Dictionary<string, object?>? GetExtensionDictionary(ADB.Entity entity)
|
||||
{
|
||||
if (entity is null || entity.ExtensionDictionary == ADB.ObjectId.Null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Dictionary<string, object?> extensionDictionaryDict = new();
|
||||
|
||||
using (ADB.Transaction tr = _settingsStore.Current.Document.TransactionManager.StartTransaction())
|
||||
{
|
||||
var extensionDictionary = (ADB.DBDictionary)tr.GetObject(entity.ExtensionDictionary, ADB.OpenMode.ForRead, false);
|
||||
|
||||
foreach (ADB.DBDictionaryEntry entry in extensionDictionary)
|
||||
{
|
||||
if (tr.GetObject(entry.Value, ADB.OpenMode.ForRead) is ADB.Xrecord xRecord) // sometimes these can be RXClass objects, in property sets
|
||||
{
|
||||
Dictionary<string, object?> entryDict = new();
|
||||
foreach (ADB.TypedValue xEntry in xRecord.Data)
|
||||
{
|
||||
entryDict[xEntry.TypeCode.ToString()] = xEntry.Value;
|
||||
}
|
||||
|
||||
if (entryDict.Count > 0)
|
||||
{
|
||||
extensionDictionaryDict[$"{entry.Key}"] = entryDict;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tr.Commit();
|
||||
}
|
||||
|
||||
return extensionDictionaryDict.Count > 0 ? extensionDictionaryDict : null;
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
namespace Speckle.Converters.AutocadShared.ToSpeckle;
|
||||
|
||||
/// <summary>
|
||||
/// Extracts properties for autocad entities. NOTE: currently not in use in acad
|
||||
/// </summary>
|
||||
public class PropertiesExtractor
|
||||
{
|
||||
private readonly ExtensionDictionaryExtractor _extensionDictionaryExtractor;
|
||||
|
||||
public PropertiesExtractor(ExtensionDictionaryExtractor extensionDictionaryExtractor)
|
||||
{
|
||||
_extensionDictionaryExtractor = extensionDictionaryExtractor;
|
||||
}
|
||||
|
||||
public Dictionary<string, object?> GetProperties(ADB.Entity entity)
|
||||
{
|
||||
Dictionary<string, object?> properties = new();
|
||||
AddDictionaryToPropertyDictionary(
|
||||
_extensionDictionaryExtractor.GetExtensionDictionary(entity),
|
||||
"Extension Dictionary",
|
||||
properties
|
||||
);
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
private void AddDictionaryToPropertyDictionary(
|
||||
Dictionary<string, object?>? entryDictionary,
|
||||
string entryName,
|
||||
Dictionary<string, object?> propertyDictionary
|
||||
)
|
||||
{
|
||||
if (entryDictionary is not null && entryDictionary.Count > 0)
|
||||
{
|
||||
propertyDictionary.Add(entryName, entryDictionary);
|
||||
}
|
||||
}
|
||||
}
|
||||
+16
-4
@@ -11,14 +11,17 @@ public class Civil3dRootToSpeckleConverter : IRootToSpeckleConverter
|
||||
{
|
||||
private readonly IConverterManager<IToSpeckleTopLevelConverter> _toSpeckle;
|
||||
private readonly IConverterSettingsStore<Civil3dConversionSettings> _settingsStore;
|
||||
private readonly ToSpeckle.PropertiesExtractor _propertiesExtractor;
|
||||
|
||||
public Civil3dRootToSpeckleConverter(
|
||||
IConverterManager<IToSpeckleTopLevelConverter> toSpeckle,
|
||||
IConverterSettingsStore<Civil3dConversionSettings> settingsStore
|
||||
IConverterSettingsStore<Civil3dConversionSettings> settingsStore,
|
||||
ToSpeckle.PropertiesExtractor propertiesExtractor
|
||||
)
|
||||
{
|
||||
_toSpeckle = toSpeckle;
|
||||
_settingsStore = settingsStore;
|
||||
_propertiesExtractor = propertiesExtractor;
|
||||
}
|
||||
|
||||
public Base Convert(object target)
|
||||
@@ -31,7 +34,6 @@ public class Civil3dRootToSpeckleConverter : IRootToSpeckleConverter
|
||||
}
|
||||
|
||||
Type type = dbObject.GetType();
|
||||
object objectToConvert = dbObject;
|
||||
|
||||
// check first for civil type objects
|
||||
// POC: some classes (eg Civil.DatabaseServices.CogoPoint) actually inherit from Autocad.DatabaseServices.Entity instead of Civil!!
|
||||
@@ -39,7 +41,6 @@ public class Civil3dRootToSpeckleConverter : IRootToSpeckleConverter
|
||||
if (target is CDB.Entity civilEntity)
|
||||
{
|
||||
type = civilEntity.GetType();
|
||||
objectToConvert = civilEntity;
|
||||
}
|
||||
|
||||
var objectConverter = _toSpeckle.ResolveConverter(type);
|
||||
@@ -50,7 +51,18 @@ public class Civil3dRootToSpeckleConverter : IRootToSpeckleConverter
|
||||
{
|
||||
using (var tr = _settingsStore.Current.Document.Database.TransactionManager.StartTransaction())
|
||||
{
|
||||
var result = objectConverter.Convert(objectToConvert);
|
||||
var result = objectConverter.Convert(target);
|
||||
|
||||
// NOTE: we can not test acad objects props, so commented out.
|
||||
// // we need to capture properties on autocad entities
|
||||
// if (target is ADB.Entity autocadEntity)
|
||||
// {
|
||||
// var properties = _propertiesExtractor.GetProperties(autocadEntity);
|
||||
// if (properties.Count > 0)
|
||||
// {
|
||||
// result["properties"] = properties;
|
||||
// }
|
||||
// }
|
||||
|
||||
tr.Commit();
|
||||
return result;
|
||||
|
||||
@@ -35,7 +35,8 @@ public static class ServiceRegistration
|
||||
>();
|
||||
|
||||
// add other classes
|
||||
serviceCollection.AddScoped<PropertiesExtractor>();
|
||||
serviceCollection.AddScoped<Speckle.Converters.Civil3dShared.ToSpeckle.PropertiesExtractor>(); // for civil
|
||||
// serviceCollection.AddScoped<Speckle.Converters.AutocadShared.ToSpeckle.PropertiesExtractor>(); // for autocad // NOTE: we can't test for acad, so we're kicking this out from acad
|
||||
serviceCollection.AddScoped<PartDataExtractor>();
|
||||
serviceCollection.AddScoped<DisplayValueExtractor>();
|
||||
serviceCollection.AddScoped<BaseCurveExtractor>();
|
||||
|
||||
-2
@@ -57,8 +57,6 @@ public class CogoPointToSpeckleTopLevelConverter : IToSpeckleTopLevelConverter
|
||||
// add additional class properties
|
||||
civilObject["pointNumber"] = target.PointNumber;
|
||||
civilObject["northing"] = target.Northing;
|
||||
//civilObject["latitude"] = target.Latitude; // might not be necessary, and also sometimes throws if transforms are not enabled
|
||||
//civilObject["longitude"] = target.Longitude; // might not be necessary, and also sometimes throws if transforms are not enabled
|
||||
|
||||
return civilObject;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,21 @@ using Autodesk.Revit.UI;
|
||||
|
||||
namespace Speckle.Converters.RevitShared.Helpers;
|
||||
|
||||
public interface IRevitContext
|
||||
public class RevitContext
|
||||
{
|
||||
public UIApplication UIApplication { get; }
|
||||
private UIApplication? _uiApplication;
|
||||
|
||||
public UIApplication? UIApplication
|
||||
{
|
||||
get => _uiApplication;
|
||||
set
|
||||
{
|
||||
if (_uiApplication != null)
|
||||
{
|
||||
throw new ArgumentException("UIApplication already set");
|
||||
}
|
||||
|
||||
_uiApplication = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ public static class ServiceRegistration
|
||||
serviceCollection.AddApplicationConverters<RevitToSpeckleUnitConverter, DB.ForgeTypeId>(converterAssembly);
|
||||
|
||||
serviceCollection.AddScoped<IRootToHostConverter, RevitRootToHostConverter>();
|
||||
serviceCollection.AddSingleton(new RevitContext());
|
||||
|
||||
serviceCollection.AddSingleton(new RevitToHostCacheSingleton());
|
||||
serviceCollection.AddSingleton(new RevitToSpeckleCacheSingleton());
|
||||
|
||||
|
||||
+3
-2
@@ -1,12 +1,13 @@
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.RevitShared.Helpers;
|
||||
using Speckle.InterfaceGenerator;
|
||||
using Speckle.Sdk.Common;
|
||||
|
||||
namespace Speckle.Converters.RevitShared.Settings;
|
||||
|
||||
[GenerateAutoInterface]
|
||||
public class RevitConversionSettingsFactory(
|
||||
IRevitContext revitContext,
|
||||
RevitContext revitContext,
|
||||
IHostToSpeckleUnitConverter<DB.ForgeTypeId> unitConverter
|
||||
) : IRevitConversionSettingsFactory
|
||||
{
|
||||
@@ -17,7 +18,7 @@ public class RevitConversionSettingsFactory(
|
||||
double tolerance = 0.0164042 // 5mm in ft
|
||||
)
|
||||
{
|
||||
var document = revitContext.UIApplication.ActiveUIDocument.Document;
|
||||
var document = revitContext.UIApplication.NotNull().ActiveUIDocument.Document;
|
||||
return new(
|
||||
document,
|
||||
detailLevelType,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Reflection;
|
||||
using System.Reflection;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Rhino;
|
||||
using Speckle.Converters.Common;
|
||||
@@ -23,6 +23,7 @@ public static class ServiceRegistration
|
||||
IConverterSettingsStore<RhinoConversionSettings>,
|
||||
ConverterSettingsStore<RhinoConversionSettings>
|
||||
>();
|
||||
|
||||
return serviceCollection;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-6
@@ -26,12 +26,7 @@ public abstract class RhinoObjectToSpeckleTopLevelConverter<TTopLevelIn, TInRaw,
|
||||
|
||||
var result = Conversion.Convert(typedGeometry);
|
||||
|
||||
// POC: Any common operations for all RhinoObjects should be done here, not on the specific implementer
|
||||
// Things like user-dictionaries and other user-defined metadata.
|
||||
if (!string.IsNullOrEmpty(typedTarget.Attributes.Name))
|
||||
{
|
||||
result["name"] = typedTarget.Attributes.Name;
|
||||
}
|
||||
// POC: Any common operations for all RhinoObjects should NOT be done here, because not all top level converters use this class sigh
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ public static class ServiceRegistration
|
||||
serviceCollection.AddScoped<DisplayValueExtractor>();
|
||||
serviceCollection.AddScoped<ClassPropertyExtractor>();
|
||||
serviceCollection.AddScoped<ReportPropertyExtractor>();
|
||||
serviceCollection.AddScoped<UserDefinedAttributesExtractor>();
|
||||
serviceCollection.AddScoped<PropertiesExtractor>();
|
||||
|
||||
serviceCollection.AddRootCommon<TeklaRootToSpeckleConverter>(converterAssembly);
|
||||
|
||||
+14
-1
@@ -3,10 +3,15 @@ namespace Speckle.Converters.TeklaShared.ToSpeckle.Helpers;
|
||||
public class PropertiesExtractor
|
||||
{
|
||||
private readonly ReportPropertyExtractor _reportPropertyExtractor;
|
||||
private readonly UserDefinedAttributesExtractor _userDefinedAttributesExtractor;
|
||||
|
||||
public PropertiesExtractor(ReportPropertyExtractor reportPropertyExtractor)
|
||||
public PropertiesExtractor(
|
||||
ReportPropertyExtractor reportPropertyExtractor,
|
||||
UserDefinedAttributesExtractor userDefinedAttributesExtractor
|
||||
)
|
||||
{
|
||||
_reportPropertyExtractor = reportPropertyExtractor;
|
||||
_userDefinedAttributesExtractor = userDefinedAttributesExtractor;
|
||||
}
|
||||
|
||||
public Dictionary<string, object?> GetProperties(TSM.ModelObject modelObject)
|
||||
@@ -19,6 +24,14 @@ public class PropertiesExtractor
|
||||
properties.Add("Report", report);
|
||||
}
|
||||
|
||||
Dictionary<string, object?> userDefinedAttributes = _userDefinedAttributesExtractor.GetUserDefinedAttributes(
|
||||
modelObject
|
||||
);
|
||||
if (userDefinedAttributes.Count > 0)
|
||||
{
|
||||
properties.Add("User Defined Attributes", userDefinedAttributes);
|
||||
}
|
||||
|
||||
return properties;
|
||||
}
|
||||
}
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
using System.Collections;
|
||||
|
||||
namespace Speckle.Converters.TeklaShared.ToSpeckle.Helpers;
|
||||
|
||||
public class UserDefinedAttributesExtractor
|
||||
{
|
||||
public Dictionary<string, object?> GetUserDefinedAttributes(TSM.ModelObject modelObject)
|
||||
{
|
||||
var userProperties = new Dictionary<string, object?>();
|
||||
var userValues = new Hashtable();
|
||||
|
||||
if (!modelObject.GetAllUserProperties(ref userValues))
|
||||
{
|
||||
// NOTE: Return empty dictionary if no user properties found or failed to get properties
|
||||
return userProperties;
|
||||
}
|
||||
|
||||
foreach (DictionaryEntry entry in userValues)
|
||||
{
|
||||
if (entry.Value != null && !string.IsNullOrEmpty(entry.Value.ToString()))
|
||||
{
|
||||
var propertyName = entry.Key.ToString();
|
||||
var propertyValue = entry.Value;
|
||||
|
||||
if (propertyName != null)
|
||||
{
|
||||
userProperties[propertyName] = propertyValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return userProperties;
|
||||
}
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Speckle.Connectors.Common.Threading;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
using Speckle.Testing;
|
||||
|
||||
namespace Speckle.Connectors.DUI.Tests.Bridge;
|
||||
|
||||
public class TopLevelExceptionHandlerTests : MoqTest
|
||||
{
|
||||
[Test]
|
||||
public void CatchUnhandledAction_Happy()
|
||||
{
|
||||
var logger = Create<ILogger<TopLevelExceptionHandler>>(MockBehavior.Loose);
|
||||
var eventAggregator = Create<IEventAggregator>();
|
||||
var sut = new TopLevelExceptionHandler(logger.Object, eventAggregator.Object);
|
||||
|
||||
sut.CatchUnhandled(() => { });
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CatchUnhandledAction_Exception()
|
||||
{
|
||||
var logger = Create<ILogger<TopLevelExceptionHandler>>(MockBehavior.Loose);
|
||||
var eventAggregator = Create<IEventAggregator>();
|
||||
|
||||
eventAggregator
|
||||
.Setup(x => x.GetEvent<ExceptionEvent>())
|
||||
.Returns(new ExceptionEvent(Create<IThreadContext>().Object, Create<ITopLevelExceptionHandler>().Object));
|
||||
|
||||
var sut = new TopLevelExceptionHandler(logger.Object, eventAggregator.Object);
|
||||
|
||||
sut.CatchUnhandled(() => throw new InvalidOperationException());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CatchUnhandledFunc_Happy()
|
||||
{
|
||||
var val = 2;
|
||||
var logger = Create<ILogger<TopLevelExceptionHandler>>(MockBehavior.Loose);
|
||||
var eventAggregator = Create<IEventAggregator>();
|
||||
var sut = new TopLevelExceptionHandler(logger.Object, eventAggregator.Object);
|
||||
|
||||
var returnVal = sut.CatchUnhandled(() => val);
|
||||
returnVal.Value.Should().Be(val);
|
||||
returnVal.Exception.Should().BeNull();
|
||||
returnVal.IsSuccess.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CatchUnhandledFunc_Exception()
|
||||
{
|
||||
var logger = Create<ILogger<TopLevelExceptionHandler>>(MockBehavior.Loose);
|
||||
var eventAggregator = Create<IEventAggregator>();
|
||||
|
||||
eventAggregator
|
||||
.Setup(x => x.GetEvent<ExceptionEvent>())
|
||||
.Returns(new ExceptionEvent(Create<IThreadContext>().Object, Create<ITopLevelExceptionHandler>().Object));
|
||||
|
||||
var sut = new TopLevelExceptionHandler(logger.Object, eventAggregator.Object);
|
||||
|
||||
var returnVal = sut.CatchUnhandled((Func<string>)(() => throw new InvalidOperationException()));
|
||||
returnVal.Value.Should().BeNull();
|
||||
returnVal.Exception.Should().BeOfType<InvalidOperationException>();
|
||||
returnVal.IsSuccess.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CatchUnhandledFunc_Exception_Fatal()
|
||||
{
|
||||
var logger = Create<ILogger<TopLevelExceptionHandler>>(MockBehavior.Loose);
|
||||
var eventAggregator = Create<IEventAggregator>();
|
||||
var sut = new TopLevelExceptionHandler(logger.Object, eventAggregator.Object);
|
||||
|
||||
Assert.Throws<AppDomainUnloadedException>(
|
||||
() => sut.CatchUnhandled(new Func<string>(() => throw new AppDomainUnloadedException()))
|
||||
);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task CatchUnhandledFuncAsync_Happy()
|
||||
{
|
||||
var val = 2;
|
||||
var logger = Create<ILogger<TopLevelExceptionHandler>>(MockBehavior.Loose);
|
||||
var eventAggregator = Create<IEventAggregator>();
|
||||
var sut = new TopLevelExceptionHandler(logger.Object, eventAggregator.Object);
|
||||
|
||||
var returnVal = await sut.CatchUnhandledAsync(() => Task.FromResult(val));
|
||||
returnVal.Value.Should().Be(val);
|
||||
returnVal.Exception.Should().BeNull();
|
||||
returnVal.IsSuccess.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task CatchUnhandledFuncAsync_Exception()
|
||||
{
|
||||
var logger = Create<ILogger<TopLevelExceptionHandler>>(MockBehavior.Loose);
|
||||
var eventAggregator = Create<IEventAggregator>();
|
||||
|
||||
eventAggregator
|
||||
.Setup(x => x.GetEvent<ExceptionEvent>())
|
||||
.Returns(new ExceptionEvent(Create<IThreadContext>().Object, Create<ITopLevelExceptionHandler>().Object));
|
||||
|
||||
var sut = new TopLevelExceptionHandler(logger.Object, eventAggregator.Object);
|
||||
|
||||
var returnVal = await sut.CatchUnhandledAsync(new Func<Task<string>>(() => throw new InvalidOperationException()));
|
||||
returnVal.Value.Should().BeNull();
|
||||
returnVal.Exception.Should().BeOfType<InvalidOperationException>();
|
||||
returnVal.IsSuccess.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CatchUnhandledFuncAsync_Exception_Fatal()
|
||||
{
|
||||
var logger = Create<ILogger<TopLevelExceptionHandler>>(MockBehavior.Loose);
|
||||
var eventAggregator = Create<IEventAggregator>();
|
||||
var sut = new TopLevelExceptionHandler(logger.Object, eventAggregator.Object);
|
||||
|
||||
var exception = Assert.ThrowsAsync<AppDomainUnloadedException>(
|
||||
async () => await sut.CatchUnhandledAsync(new Func<Task<string>>(() => throw new AppDomainUnloadedException()))
|
||||
);
|
||||
exception.Should().BeOfType<AppDomainUnloadedException>();
|
||||
}
|
||||
}
|
||||
@@ -1,440 +0,0 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NUnit.Framework;
|
||||
using Speckle.Connectors.Common.Threading;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
using Speckle.Testing;
|
||||
|
||||
namespace Speckle.Connectors.DUI.Tests.Eventing;
|
||||
|
||||
public class TestEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
: ThreadedEvent<object>(threadContext, exceptionHandler);
|
||||
|
||||
public class TestOneTimeEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
: OneTimeThreadedEvent<object>(threadContext, exceptionHandler);
|
||||
|
||||
public class TestPeriodicThreadedEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
|
||||
: PeriodicThreadedEvent(threadContext, exceptionHandler);
|
||||
|
||||
public class EventAggregatorTests : MoqTest
|
||||
{
|
||||
[Test]
|
||||
public async Task Sub_Async_DisposeToken()
|
||||
{
|
||||
s_val = false;
|
||||
var services = new ServiceCollection();
|
||||
var exceptionHandler = new TopLevelExceptionHandler(
|
||||
Create<ILogger<TopLevelExceptionHandler>>().Object,
|
||||
Create<IEventAggregator>().Object
|
||||
);
|
||||
services.AddSingleton(Create<IThreadContext>().Object);
|
||||
services.AddSingleton<ITopLevelExceptionHandler>(exceptionHandler);
|
||||
services.AddTransient<TestEvent>();
|
||||
services.AddSingleton<IEventAggregator, EventAggregator>();
|
||||
var serviceProvider = services.BuildServiceProvider();
|
||||
|
||||
var subscriptionToken = Test_Sub_Async_DisposeToken(serviceProvider);
|
||||
var eventAggregator = serviceProvider.GetRequiredService<IEventAggregator>();
|
||||
await eventAggregator.GetEvent<TestEvent>().PublishAsync(new object());
|
||||
|
||||
s_val.Should().BeTrue();
|
||||
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
subscriptionToken.IsActive.Should().BeTrue();
|
||||
subscriptionToken.Dispose();
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
subscriptionToken.IsActive.Should().BeFalse();
|
||||
}
|
||||
|
||||
private SubscriptionToken Test_Sub_Async_DisposeToken(IServiceProvider serviceProvider)
|
||||
{
|
||||
var eventAggregator = serviceProvider.GetRequiredService<IEventAggregator>();
|
||||
var subscriptionToken = eventAggregator.GetEvent<TestEvent>().Subscribe(OnTestAsyncSubscribe);
|
||||
return subscriptionToken;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Sub_Async_SubscribeToken()
|
||||
{
|
||||
s_val = false;
|
||||
var services = new ServiceCollection();
|
||||
var exceptionHandler = new TopLevelExceptionHandler(
|
||||
Create<ILogger<TopLevelExceptionHandler>>().Object,
|
||||
Create<IEventAggregator>().Object
|
||||
);
|
||||
services.AddSingleton(Create<IThreadContext>().Object);
|
||||
services.AddSingleton<ITopLevelExceptionHandler>(exceptionHandler);
|
||||
services.AddTransient<TestEvent>();
|
||||
services.AddSingleton<IEventAggregator, EventAggregator>();
|
||||
var serviceProvider = services.BuildServiceProvider();
|
||||
|
||||
var subscriptionToken = Test_Sub_Async_DisposeToken(serviceProvider);
|
||||
var eventAggregator = serviceProvider.GetRequiredService<IEventAggregator>();
|
||||
await eventAggregator.GetEvent<TestEvent>().PublishAsync(new object());
|
||||
|
||||
s_val.Should().BeTrue();
|
||||
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
subscriptionToken.IsActive.Should().BeTrue();
|
||||
eventAggregator.GetEvent<TestEvent>().Unsubscribe(subscriptionToken);
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
subscriptionToken.IsActive.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Sub_Sync()
|
||||
{
|
||||
s_val = false;
|
||||
var services = new ServiceCollection();
|
||||
var exceptionHandler = new TopLevelExceptionHandler(
|
||||
Create<ILogger<TopLevelExceptionHandler>>().Object,
|
||||
Create<IEventAggregator>().Object
|
||||
);
|
||||
services.AddSingleton(Create<IThreadContext>().Object);
|
||||
services.AddSingleton<ITopLevelExceptionHandler>(exceptionHandler);
|
||||
services.AddTransient<TestEvent>();
|
||||
|
||||
services.AddSingleton<IEventAggregator, EventAggregator>();
|
||||
var serviceProvider = services.BuildServiceProvider();
|
||||
|
||||
var subscriptionToken = Test_Sub_Sync(serviceProvider);
|
||||
var eventAggregator = serviceProvider.GetRequiredService<IEventAggregator>();
|
||||
await eventAggregator.GetEvent<TestEvent>().PublishAsync(new object());
|
||||
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
subscriptionToken.IsActive.Should().BeTrue();
|
||||
s_val.Should().BeTrue();
|
||||
eventAggregator.GetEvent<TestEvent>().Unsubscribe(subscriptionToken);
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
subscriptionToken.IsActive.Should().BeFalse();
|
||||
}
|
||||
|
||||
private SubscriptionToken Test_Sub_Sync(IServiceProvider serviceProvider)
|
||||
{
|
||||
var eventAggregator = serviceProvider.GetRequiredService<IEventAggregator>();
|
||||
var subscriptionToken = eventAggregator.GetEvent<TestEvent>().Subscribe(OnTestSyncSubscribe);
|
||||
return subscriptionToken;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Sub_Sync_Static()
|
||||
{
|
||||
s_val = false;
|
||||
var services = new ServiceCollection();
|
||||
var exceptionHandler = new TopLevelExceptionHandler(
|
||||
Create<ILogger<TopLevelExceptionHandler>>().Object,
|
||||
Create<IEventAggregator>().Object
|
||||
);
|
||||
services.AddSingleton(Create<IThreadContext>().Object);
|
||||
services.AddSingleton<ITopLevelExceptionHandler>(exceptionHandler);
|
||||
services.AddTransient<TestEvent>();
|
||||
|
||||
services.AddSingleton<IEventAggregator, EventAggregator>();
|
||||
var serviceProvider = services.BuildServiceProvider();
|
||||
|
||||
var subscriptionToken = Test_Sub_Sync_Static(serviceProvider);
|
||||
var eventAggregator = serviceProvider.GetRequiredService<IEventAggregator>();
|
||||
await eventAggregator.GetEvent<TestEvent>().PublishAsync(new object());
|
||||
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
subscriptionToken.IsActive.Should().BeTrue();
|
||||
s_val.Should().BeTrue();
|
||||
eventAggregator.GetEvent<TestEvent>().Unsubscribe(subscriptionToken);
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
subscriptionToken.IsActive.Should().BeFalse();
|
||||
}
|
||||
|
||||
private static SubscriptionToken Test_Sub_Sync_Static(IServiceProvider serviceProvider)
|
||||
{
|
||||
var eventAggregator = serviceProvider.GetRequiredService<IEventAggregator>();
|
||||
var subscriptionToken = eventAggregator.GetEvent<TestEvent>().Subscribe(OnTestSyncStaticSubscribe);
|
||||
return subscriptionToken;
|
||||
}
|
||||
|
||||
private static void OnTestSyncStaticSubscribe(object _)
|
||||
{
|
||||
s_val = true;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Sub_Async_Static()
|
||||
{
|
||||
s_val = false;
|
||||
var services = new ServiceCollection();
|
||||
var exceptionHandler = new TopLevelExceptionHandler(
|
||||
Create<ILogger<TopLevelExceptionHandler>>().Object,
|
||||
Create<IEventAggregator>().Object
|
||||
);
|
||||
services.AddSingleton(Create<IThreadContext>().Object);
|
||||
services.AddSingleton<ITopLevelExceptionHandler>(exceptionHandler);
|
||||
services.AddTransient<TestEvent>();
|
||||
|
||||
services.AddSingleton<IEventAggregator, EventAggregator>();
|
||||
var serviceProvider = services.BuildServiceProvider();
|
||||
|
||||
var subscriptionToken = Test_Sub_Async_Static(serviceProvider);
|
||||
var eventAggregator = serviceProvider.GetRequiredService<IEventAggregator>();
|
||||
await eventAggregator.GetEvent<TestEvent>().PublishAsync(new object());
|
||||
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
subscriptionToken.IsActive.Should().BeTrue();
|
||||
s_val.Should().BeTrue();
|
||||
eventAggregator.GetEvent<TestEvent>().Unsubscribe(subscriptionToken);
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
subscriptionToken.IsActive.Should().BeFalse();
|
||||
}
|
||||
|
||||
private static SubscriptionToken Test_Sub_Async_Static(IServiceProvider serviceProvider)
|
||||
{
|
||||
var eventAggregator = serviceProvider.GetRequiredService<IEventAggregator>();
|
||||
var subscriptionToken = eventAggregator.GetEvent<TestEvent>().Subscribe(OnTestAsyncStaticSubscribe);
|
||||
return subscriptionToken;
|
||||
}
|
||||
|
||||
private static Task OnTestAsyncStaticSubscribe(object _)
|
||||
{
|
||||
s_val = true;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task OnTestAsyncSubscribe(object _)
|
||||
{
|
||||
s_val = true;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Onetime_Async()
|
||||
{
|
||||
s_val = false;
|
||||
var services = new ServiceCollection();
|
||||
var exceptionHandler = new TopLevelExceptionHandler(
|
||||
Create<ILogger<TopLevelExceptionHandler>>().Object,
|
||||
Create<IEventAggregator>().Object
|
||||
);
|
||||
services.AddSingleton(Create<IThreadContext>().Object);
|
||||
services.AddSingleton<ITopLevelExceptionHandler>(exceptionHandler);
|
||||
services.AddTransient<TestOneTimeEvent>();
|
||||
|
||||
services.AddSingleton<IEventAggregator, EventAggregator>();
|
||||
var serviceProvider = services.BuildServiceProvider();
|
||||
|
||||
var subscriptionToken = Test_Onetime_Sub_Async(serviceProvider);
|
||||
var eventAggregator = serviceProvider.GetRequiredService<IEventAggregator>();
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
subscriptionToken.IsActive.Should().BeTrue();
|
||||
|
||||
await eventAggregator.GetEvent<TestOneTimeEvent>().PublishAsync(new object());
|
||||
|
||||
s_val.Should().BeTrue();
|
||||
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
subscriptionToken.IsActive.Should().BeFalse();
|
||||
subscriptionToken.Dispose();
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
subscriptionToken.IsActive.Should().BeFalse();
|
||||
}
|
||||
|
||||
private SubscriptionToken Test_Onetime_Sub_Async(IServiceProvider serviceProvider)
|
||||
{
|
||||
var eventAggregator = serviceProvider.GetRequiredService<IEventAggregator>();
|
||||
var subscriptionToken = eventAggregator.GetEvent<TestOneTimeEvent>().OneTimeSubscribe("test", OnTestAsyncSubscribe);
|
||||
return subscriptionToken;
|
||||
}
|
||||
|
||||
private SubscriptionToken Test_Onetime_Sub_Sync(IServiceProvider serviceProvider)
|
||||
{
|
||||
var eventAggregator = serviceProvider.GetRequiredService<IEventAggregator>();
|
||||
var subscriptionToken = eventAggregator.GetEvent<TestOneTimeEvent>().OneTimeSubscribe("test", OnTestSyncSubscribe);
|
||||
return subscriptionToken;
|
||||
}
|
||||
|
||||
private void OnTestSyncSubscribe(object _)
|
||||
{
|
||||
s_val = true;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Onetime_Sync()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
var exceptionHandler = new TopLevelExceptionHandler(
|
||||
Create<ILogger<TopLevelExceptionHandler>>().Object,
|
||||
Create<IEventAggregator>().Object
|
||||
);
|
||||
services.AddSingleton(Create<IThreadContext>().Object);
|
||||
services.AddSingleton<ITopLevelExceptionHandler>(exceptionHandler);
|
||||
services.AddTransient<TestOneTimeEvent>();
|
||||
services.AddSingleton<IEventAggregator, EventAggregator>();
|
||||
var serviceProvider = services.BuildServiceProvider();
|
||||
|
||||
var subscriptionToken = Test_Onetime_Sub_Sync(serviceProvider);
|
||||
var eventAggregator = serviceProvider.GetRequiredService<IEventAggregator>();
|
||||
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
subscriptionToken.IsActive.Should().BeTrue();
|
||||
|
||||
await eventAggregator.GetEvent<TestOneTimeEvent>().PublishAsync(new object());
|
||||
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
subscriptionToken.IsActive.Should().BeFalse();
|
||||
s_val.Should().BeTrue();
|
||||
eventAggregator.GetEvent<TestOneTimeEvent>().Unsubscribe(subscriptionToken);
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
subscriptionToken.IsActive.Should().BeFalse();
|
||||
}
|
||||
|
||||
private static bool s_val;
|
||||
|
||||
[Test]
|
||||
public async Task Sub_WeakReference()
|
||||
{
|
||||
s_val = false;
|
||||
var services = new ServiceCollection();
|
||||
var exceptionHandler = new TopLevelExceptionHandler(
|
||||
Create<ILogger<TopLevelExceptionHandler>>().Object,
|
||||
Create<IEventAggregator>().Object
|
||||
);
|
||||
services.AddSingleton(Create<IThreadContext>().Object);
|
||||
services.AddSingleton<ITopLevelExceptionHandler>(exceptionHandler);
|
||||
services.AddTransient<TestEvent>();
|
||||
services.AddSingleton<IEventAggregator, EventAggregator>();
|
||||
var serviceProvider = services.BuildServiceProvider();
|
||||
TestWeakReference(serviceProvider);
|
||||
|
||||
s_val.Should().BeFalse();
|
||||
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
GC.Collect();
|
||||
|
||||
await serviceProvider.GetRequiredService<IEventAggregator>().GetEvent<TestEvent>().PublishAsync(new object());
|
||||
s_val.Should().BeFalse();
|
||||
}
|
||||
|
||||
//keep in a separate method to avoid referencing ObjectWithAction
|
||||
private static void TestWeakReference(IServiceProvider serviceProvider)
|
||||
{
|
||||
var x = new ObjectWithAction(
|
||||
new Action(() =>
|
||||
{
|
||||
s_val = true;
|
||||
})
|
||||
);
|
||||
var eventAggregator = serviceProvider.GetRequiredService<IEventAggregator>();
|
||||
eventAggregator.GetEvent<TestEvent>().Subscribe(x.Test1);
|
||||
x = null;
|
||||
}
|
||||
|
||||
private sealed class ObjectWithAction(Action action)
|
||||
{
|
||||
public void Test1(object _)
|
||||
{
|
||||
action();
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Periodic_Async()
|
||||
{
|
||||
s_val = false;
|
||||
var services = new ServiceCollection();
|
||||
var exceptionHandler = new TopLevelExceptionHandler(
|
||||
Create<ILogger<TopLevelExceptionHandler>>().Object,
|
||||
Create<IEventAggregator>().Object
|
||||
);
|
||||
services.AddSingleton(Create<IThreadContext>().Object);
|
||||
services.AddSingleton<ITopLevelExceptionHandler>(exceptionHandler);
|
||||
services.AddTransient<TestPeriodicThreadedEvent>();
|
||||
|
||||
services.AddSingleton<IEventAggregator, EventAggregator>();
|
||||
var serviceProvider = services.BuildServiceProvider();
|
||||
|
||||
var subscriptionToken = Test_Periodic_Sub_Async(serviceProvider);
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
subscriptionToken.IsActive.Should().BeTrue();
|
||||
|
||||
await Task.Delay(2000);
|
||||
s_val.Should().BeTrue();
|
||||
subscriptionToken.Unsubscribe();
|
||||
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
subscriptionToken.IsActive.Should().BeFalse();
|
||||
subscriptionToken.Dispose();
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
subscriptionToken.IsActive.Should().BeFalse();
|
||||
}
|
||||
|
||||
private SubscriptionToken Test_Periodic_Sub_Async(IServiceProvider serviceProvider)
|
||||
{
|
||||
var eventAggregator = serviceProvider.GetRequiredService<IEventAggregator>();
|
||||
var subscriptionToken = eventAggregator
|
||||
.GetEvent<TestPeriodicThreadedEvent>()
|
||||
.SubscribePeriodic(TimeSpan.FromSeconds(1), OnTestAsyncSubscribe);
|
||||
return subscriptionToken;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Periodic_Sync()
|
||||
{
|
||||
s_val = false;
|
||||
var services = new ServiceCollection();
|
||||
var exceptionHandler = new TopLevelExceptionHandler(
|
||||
Create<ILogger<TopLevelExceptionHandler>>().Object,
|
||||
Create<IEventAggregator>().Object
|
||||
);
|
||||
services.AddSingleton(Create<IThreadContext>().Object);
|
||||
services.AddSingleton<ITopLevelExceptionHandler>(exceptionHandler);
|
||||
services.AddTransient<TestPeriodicThreadedEvent>();
|
||||
|
||||
services.AddSingleton<IEventAggregator, EventAggregator>();
|
||||
var serviceProvider = services.BuildServiceProvider();
|
||||
|
||||
var subscriptionToken = Test_Periodic_Sub_Sync(serviceProvider);
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
subscriptionToken.IsActive.Should().BeTrue();
|
||||
|
||||
await Task.Delay(2000);
|
||||
s_val.Should().BeTrue();
|
||||
subscriptionToken.Unsubscribe();
|
||||
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
subscriptionToken.IsActive.Should().BeFalse();
|
||||
subscriptionToken.Dispose();
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
subscriptionToken.IsActive.Should().BeFalse();
|
||||
}
|
||||
|
||||
private SubscriptionToken Test_Periodic_Sub_Sync(IServiceProvider serviceProvider)
|
||||
{
|
||||
var eventAggregator = serviceProvider.GetRequiredService<IEventAggregator>();
|
||||
var subscriptionToken = eventAggregator
|
||||
.GetEvent<TestPeriodicThreadedEvent>()
|
||||
.SubscribePeriodic(TimeSpan.FromSeconds(1), OnTestSyncSubscribe);
|
||||
return subscriptionToken;
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@ using System.Runtime.InteropServices;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Speckle.Connectors.Common.Threading;
|
||||
using Speckle.Connectors.DUI.Bindings;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
using Speckle.Connectors.DUI.Utils;
|
||||
using Speckle.Newtonsoft.Json;
|
||||
using Speckle.Sdk.Common;
|
||||
@@ -64,7 +63,6 @@ public sealed class BrowserBridge : IBrowserBridge
|
||||
ILogger<BrowserBridge> logger,
|
||||
IBrowserScriptExecutor browserScriptExecutor,
|
||||
IThreadOptions threadOptions,
|
||||
IEventAggregator eventAggregator,
|
||||
ITopLevelExceptionHandler topLevelExceptionHandler
|
||||
)
|
||||
{
|
||||
@@ -75,7 +73,6 @@ public sealed class BrowserBridge : IBrowserBridge
|
||||
_browserScriptExecutor = browserScriptExecutor;
|
||||
_threadOptions = threadOptions;
|
||||
_topLevelExceptionHandler = topLevelExceptionHandler;
|
||||
eventAggregator.GetEvent<ExceptionEvent>().Subscribe(OnExceptionEvent, ThreadOption.MainThread);
|
||||
}
|
||||
|
||||
private async Task OnExceptionEvent(Exception ex) =>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
using Speckle.InterfaceGenerator;
|
||||
using Speckle.Sdk;
|
||||
|
||||
@@ -22,15 +21,13 @@ namespace Speckle.Connectors.DUI.Bridge;
|
||||
public sealed class TopLevelExceptionHandler : ITopLevelExceptionHandler
|
||||
{
|
||||
private readonly ILogger<TopLevelExceptionHandler> _logger;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
public string Name => nameof(TopLevelExceptionHandler);
|
||||
|
||||
private const string UNHANDLED_LOGGER_TEMPLATE = "An unhandled Exception occured";
|
||||
|
||||
public TopLevelExceptionHandler(ILogger<TopLevelExceptionHandler> logger, IEventAggregator eventAggregator)
|
||||
public TopLevelExceptionHandler(ILogger<TopLevelExceptionHandler> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
_eventAggregator = eventAggregator;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -66,7 +63,7 @@ public sealed class TopLevelExceptionHandler : ITopLevelExceptionHandler
|
||||
catch (Exception ex) when (!ex.IsFatal())
|
||||
{
|
||||
_logger.LogError(ex, UNHANDLED_LOGGER_TEMPLATE);
|
||||
_eventAggregator.GetEvent<ExceptionEvent>().PublishAsync(ex).Wait();
|
||||
// _eventAggregator.GetEvent<ExceptionEvent>().PublishAsync(ex).Wait();
|
||||
return new(ex);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -104,7 +101,7 @@ public sealed class TopLevelExceptionHandler : ITopLevelExceptionHandler
|
||||
catch (Exception ex) when (!ex.IsFatal())
|
||||
{
|
||||
_logger.LogError(ex, UNHANDLED_LOGGER_TEMPLATE);
|
||||
await _eventAggregator.GetEvent<ExceptionEvent>().PublishAsync(ex);
|
||||
// await _eventAggregator.GetEvent<ExceptionEvent>().PublishAsync(ex);
|
||||
return new(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
using System.Reflection;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Speckle.Connectors.Common.Threading;
|
||||
using Speckle.Connectors.DUI.Bindings;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Eventing;
|
||||
using Speckle.Connectors.DUI.Models;
|
||||
using Speckle.Sdk;
|
||||
using Speckle.Sdk.Transports;
|
||||
@@ -26,32 +24,15 @@ public static class ContainerRegistration
|
||||
|
||||
serviceCollection.AddMatchingInterfacesAsTransient(Assembly.GetAssembly(typeof(IdleCallManager)));
|
||||
serviceCollection.AddMatchingInterfacesAsTransient(Assembly.GetAssembly(typeof(IServerTransportFactory)));
|
||||
serviceCollection.AddEventsAsTransient(Assembly.GetAssembly(typeof(TDocumentStore)));
|
||||
serviceCollection.AddEventsAsTransient(Assembly.GetAssembly(typeof(IdleCallManager)));
|
||||
serviceCollection.AddSingleton<IEventAggregator, EventAggregator>();
|
||||
|
||||
serviceCollection.AddSingleton<IBinding, TopLevelExceptionHandlerBinding>(sp =>
|
||||
sp.GetRequiredService<TopLevelExceptionHandlerBinding>()
|
||||
);
|
||||
serviceCollection.AddSingleton<TopLevelExceptionHandlerBinding>();
|
||||
serviceCollection.AddSingleton<ITopLevelExceptionHandler, TopLevelExceptionHandler>();
|
||||
serviceCollection.AddTransient<ExceptionEvent>();
|
||||
}
|
||||
|
||||
public static IServiceCollection AddEventsAsTransient(this IServiceCollection serviceCollection, Assembly assembly)
|
||||
{
|
||||
foreach (var type in assembly.ExportedTypes.Where(t => t.IsNonAbstractClass()))
|
||||
{
|
||||
if (type.FindInterfaces((i, _) => i == typeof(ISpeckleEvent), null).Length != 0)
|
||||
{
|
||||
serviceCollection.TryAddTransient(type);
|
||||
}
|
||||
}
|
||||
|
||||
return serviceCollection;
|
||||
}
|
||||
|
||||
public static IServiceProvider UseDUI(this IServiceProvider serviceProvider, bool initializeDocumentStore = true)
|
||||
public static void UseDUI(this IServiceProvider serviceProvider)
|
||||
{
|
||||
//observe the unobserved!
|
||||
TaskScheduler.UnobservedTaskException += (_, args) =>
|
||||
@@ -76,12 +57,5 @@ public static class ContainerRegistration
|
||||
args.SetObserved();
|
||||
}
|
||||
};
|
||||
|
||||
if (initializeDocumentStore)
|
||||
{
|
||||
serviceProvider.GetRequiredService<DocumentModelStore>().OnDocumentStoreInitialized().Wait();
|
||||
}
|
||||
|
||||
return serviceProvider;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Speckle.Sdk.Common;
|
||||
|
||||
namespace Speckle.Connectors.DUI.Eventing;
|
||||
|
||||
public class DelegateReference
|
||||
{
|
||||
private readonly WeakReference<object>? _weakReference;
|
||||
private readonly MethodInfo _method;
|
||||
private readonly Type _delegateType;
|
||||
|
||||
public DelegateReference(Delegate @delegate, EventFeatures features)
|
||||
{
|
||||
var target = @delegate.Target;
|
||||
_method = @delegate.Method;
|
||||
var messageType = @delegate.Method.GetParameters()[0].ParameterType;
|
||||
if (features.HasFlag(EventFeatures.IsAsync))
|
||||
{
|
||||
_delegateType = typeof(Func<,>).MakeGenericType(messageType, typeof(Task));
|
||||
}
|
||||
else
|
||||
{
|
||||
_delegateType = typeof(Action<>).MakeGenericType(messageType);
|
||||
}
|
||||
if (target != null)
|
||||
{
|
||||
//anonymous methods are always strong....should we do this? - doing a brief search says yes
|
||||
if (
|
||||
features.HasFlag(EventFeatures.ForceStrongReference)
|
||||
|| Attribute.IsDefined(_method.DeclaringType.NotNull(), typeof(CompilerGeneratedAttribute))
|
||||
)
|
||||
{
|
||||
throw new EventSubscriptionException("Cannot subscribe to a delegate that was generated by the compiler.");
|
||||
}
|
||||
|
||||
_weakReference = new WeakReference<object>(target);
|
||||
}
|
||||
else
|
||||
{
|
||||
_weakReference = null;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> Invoke(object message)
|
||||
{
|
||||
if (_weakReference == null)
|
||||
{
|
||||
var method = Delegate.CreateDelegate(_delegateType, null, _method);
|
||||
|
||||
var task = method.DynamicInvoke(message) as Task;
|
||||
|
||||
if (task is not null)
|
||||
{
|
||||
await task;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
if (!_weakReference.TryGetTarget(out object target))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var method2 = Delegate.CreateDelegate(_delegateType, target, _method);
|
||||
|
||||
var task2 = method2.DynamicInvoke(message) as Task;
|
||||
|
||||
if (task2 is not null)
|
||||
{
|
||||
await task2;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Speckle.Connectors.DUI.Eventing;
|
||||
|
||||
public interface IEventAggregator
|
||||
{
|
||||
TEventType GetEvent<TEventType>()
|
||||
where TEventType : EventBase;
|
||||
}
|
||||
|
||||
//based on Prism.Events at verison 8
|
||||
// which was MIT https://github.com/PrismLibrary/Prism/tree/952e343f585b068ccb7d3478d3982485253a0508/src/Prism.Events
|
||||
// License https://github.com/PrismLibrary/Prism/blob/952e343f585b068ccb7d3478d3982485253a0508/LICENSE
|
||||
public class EventAggregator(IServiceProvider serviceProvider) : IEventAggregator
|
||||
{
|
||||
private readonly Dictionary<Type, EventBase> _events = new();
|
||||
|
||||
public TEventType GetEvent<TEventType>()
|
||||
where TEventType : EventBase
|
||||
{
|
||||
lock (_events)
|
||||
{
|
||||
if (!_events.TryGetValue(typeof(TEventType), out var existingEvent))
|
||||
{
|
||||
existingEvent = (TEventType)serviceProvider.GetRequiredService(typeof(TEventType));
|
||||
_events[typeof(TEventType)] = existingEvent;
|
||||
}
|
||||
return (TEventType)existingEvent;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
namespace Speckle.Connectors.DUI.Eventing;
|
||||
|
||||
///<summary>
|
||||
/// Defines a base class to publish and subscribe to events.
|
||||
///</summary>
|
||||
public abstract class EventBase
|
||||
{
|
||||
private readonly List<IEventSubscription> _subscriptions = new();
|
||||
|
||||
protected SubscriptionToken InternalSubscribe(IEventSubscription eventSubscription)
|
||||
{
|
||||
lock (_subscriptions)
|
||||
{
|
||||
_subscriptions.Add(eventSubscription);
|
||||
}
|
||||
return eventSubscription.SubscriptionToken;
|
||||
}
|
||||
|
||||
protected async Task InternalPublish(params object[] arguments)
|
||||
{
|
||||
var executionStrategies = PruneAndReturnStrategies();
|
||||
foreach (var executionStrategy in executionStrategies)
|
||||
{
|
||||
//It's important that we call executeStrategy outside of the lock(_subscription)
|
||||
await executionStrategy(arguments);
|
||||
}
|
||||
}
|
||||
|
||||
private List<Func<object[], Task>> PruneAndReturnStrategies()
|
||||
{
|
||||
lock (_subscriptions)
|
||||
{
|
||||
List<Func<object[], Task>> ret = new();
|
||||
for (var i = _subscriptions.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var listItem = _subscriptions[i].GetExecutionStrategy();
|
||||
|
||||
if (listItem == null)
|
||||
{
|
||||
// Prune from main list. Log?
|
||||
_subscriptions.RemoveAt(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret.Add(listItem);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
public void Unsubscribe(SubscriptionToken token)
|
||||
{
|
||||
lock (_subscriptions)
|
||||
{
|
||||
IEventSubscription? subscription = _subscriptions.FirstOrDefault(evt => evt.SubscriptionToken.Equals(token));
|
||||
if (subscription != null)
|
||||
{
|
||||
_subscriptions.Remove(subscription);
|
||||
token.Unsubscribe(); //calling dispose is circular
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool Contains(SubscriptionToken token)
|
||||
{
|
||||
lock (_subscriptions)
|
||||
{
|
||||
IEventSubscription subscription = _subscriptions.FirstOrDefault(evt => evt.SubscriptionToken == token);
|
||||
return subscription != null;
|
||||
}
|
||||
}
|
||||
|
||||
public void Prune()
|
||||
{
|
||||
lock (_subscriptions)
|
||||
{
|
||||
for (var i = _subscriptions.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (_subscriptions[i].GetExecutionStrategy() == null)
|
||||
{
|
||||
_subscriptions.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
// ReSharper disable InconsistentNaming
|
||||
namespace Speckle.Connectors.DUI.Eventing;
|
||||
|
||||
[Flags]
|
||||
public enum EventFeatures
|
||||
{
|
||||
None = 0,
|
||||
OneTime = 1,
|
||||
IsAsync = 2,
|
||||
ForceStrongReference = 4
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user