Files
speckle-sharp-connectors/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadBasicConnectorBinding.cs
T
Oğuzhan Koral 93246157c7 Fix: threading on browser bridge + remove event aggr (#574)
* Fix threading bug

* untangle revit

* revert rhino

* revert autocad

* revert rest

* remove event agg and rhino implementation

* remove event aggr from revit

* remove event aggr from autocad and civil

* remove event aggr tests

* remove event aggr from tekla

* Remove event aggr for navis

* remove event aggr from csi etabs

* remove event aggr for arcgis

* remove remaining pieces

* push autocad to run operations on main thread

* adds teklaidlemanager

* bring navis idle manager back

* run send on main

* remove unused function

---------

Co-authored-by: Dogukan Karatas <karatasdogukan@gmail.com>
2025-02-10 17:50:26 +03:00

196 lines
6.4 KiB
C#

using Autodesk.AutoCAD.DatabaseServices;
using Microsoft.Extensions.Logging;
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.Models;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Sdk;
using Speckle.Sdk.Common;
using Speckle.Sdk.Credentials;
namespace Speckle.Connectors.Autocad.Bindings;
public class AutocadBasicConnectorBinding : IBasicConnectorBinding
{
private readonly IAccountManager _accountManager;
public string Name { get; set; } = "baseBinding";
public IBrowserBridge Parent { get; }
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; }
public AutocadBasicConnectorBinding(
DocumentModelStore store,
IBrowserBridge parent,
IAccountManager accountManager,
ISpeckleApplication speckleApplication,
ILogger<AutocadBasicConnectorBinding> logger,
IThreadContext threadContext,
ITopLevelExceptionHandler topLevelExceptionHandler
)
{
_store = store;
Parent = parent;
_accountManager = accountManager;
_speckleApplication = speckleApplication;
Commands = new BasicConnectorBindingCommands(parent);
_logger = logger;
_threadContext = threadContext;
_topLevelExceptionHandler = topLevelExceptionHandler;
_store.DocumentChanged += (_, _) =>
_topLevelExceptionHandler.FireAndForget(async () =>
{
await Commands.NotifyDocumentChanged();
});
}
public string GetConnectorVersion() => _speckleApplication.SpeckleVersion;
public string GetSourceApplicationName() => _speckleApplication.Slug;
public string GetSourceApplicationVersion() => _speckleApplication.HostApplicationVersion;
public Account[] GetAccounts() => _accountManager.GetAccounts().ToArray();
public DocumentInfo? GetDocumentInfo()
{
// POC: Will be addressed to move it into AutocadContext!
var doc = Application.DocumentManager.MdiActiveDocument;
if (doc is null)
{
return null;
}
string name = doc.Name.Split(System.IO.Path.PathSeparator).Last();
return new DocumentInfo(doc.Name, name, doc.GetHashCode().ToString());
}
public DocumentModelStore GetDocumentState() => _store;
public void AddModel(ModelCard model) => _store.AddModel(model);
public void UpdateModel(ModelCard model) => _store.UpdateModel(model);
public void RemoveModel(ModelCard model) => _store.RemoveModel(model);
public async Task HighlightObjects(IReadOnlyList<string> objectIds)
{
// POC: Will be addressed to move it into AutocadContext!
var doc = Application.DocumentManager.MdiActiveDocument;
var dbObjects = doc.GetObjects(objectIds);
var acadObjectIds = dbObjects.Select(tuple => tuple.Root.Id).ToArray();
await HighlightObjectsOnView(acadObjectIds);
}
public async Task HighlightModel(string modelCardId)
{
// POC: Will be addressed to move it into AutocadContext!
var doc = Application.DocumentManager.MdiActiveDocument;
if (doc == null)
{
return;
}
var objectIds = Array.Empty<ObjectId>();
var model = _store.GetModelById(modelCardId);
if (model == null)
{
_logger.LogError("Model was null when highlighting received model");
return;
}
if (model is SenderModelCard senderModelCard)
{
var dbObjects = doc.GetObjects(senderModelCard.SendFilter.NotNull().RefreshObjectIds());
objectIds = dbObjects.Select(tuple => tuple.Root.Id).ToArray();
}
if (model is ReceiverModelCard receiverModelCard)
{
var dbObjects = doc.GetObjects(receiverModelCard.BakedObjectIds.NotNull());
objectIds = dbObjects.Select(tuple => tuple.Root.Id).ToArray();
}
if (objectIds.Length == 0)
{
await Commands.SetModelError(modelCardId, new OperationCanceledException("No objects found to highlight."));
return;
}
await HighlightObjectsOnView(objectIds, modelCardId);
}
private async Task HighlightObjectsOnView(ObjectId[] objectIds, string? modelCardId = null)
{
var doc = Application.DocumentManager.MdiActiveDocument;
await _threadContext.RunOnMainAsync(async () =>
{
try
{
doc.Editor.SetImpliedSelection([]); // Deselects
try
{
doc.Editor.SetImpliedSelection(objectIds);
}
catch (Exception e) when (!e.IsFatal())
{
// SWALLOW REASON:
// If the objects under the blocks, it won't be able to select them.
// If we try, API will throw the invalid input error, because we request something from API that Autocad doesn't
// handle it on its current canvas. Block elements only selectable when in its scope.
}
doc.Editor.UpdateScreen();
Extents3d selectedExtents = new();
var tr = doc.TransactionManager.StartTransaction();
foreach (ObjectId objectId in objectIds)
{
try
{
var entity = (Entity?)tr.GetObject(objectId, OpenMode.ForRead);
if (entity?.GeometricExtents != null)
{
selectedExtents.AddExtents(entity.GeometricExtents);
}
}
catch (Exception e) when (!e.IsFatal())
{
// Note: we're swallowing exeptions here because of a weird case when receiving blocks, we would have
// acad api throw an error on accessing entity.GeometricExtents.
// may also throw Autodesk.AutoCAD.Runtime.Exception for invalid extents on objects like rays and xlines
}
}
doc.Editor.Zoom(selectedExtents);
tr.Commit();
Autodesk.AutoCAD.Internal.Utils.FlushGraphics();
}
catch (Exception ex) when (!ex.IsFatal())
{
if (modelCardId != null)
{
await Commands.SetModelError(modelCardId, new OperationCanceledException("Failed to highlight objects."));
}
else
{
// This will happen, in some cases, where we highlight individual objects. Should be caught by the top level handler and not
// crash the host app.
throw;
}
}
});
}
}