Skip to content

Commit

Permalink
DUI3-174 Cancel ops on doc swap (#3504)
Browse files Browse the repository at this point in the history
* Revit, Acad cancel all ops and set global notification to UI

- UI is implemented

* Document SetGlobalNotification

* Remove old ICancelable from bindings

* Remove autocad logic
  • Loading branch information
oguzhankoral authored Jun 13, 2024
1 parent f6a23eb commit ccf143e
Show file tree
Hide file tree
Showing 9 changed files with 74 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@
using Speckle.Connectors.Utils;
using Speckle.Connectors.Utils.Cancellation;
using Speckle.Connectors.Utils.Operations;
using ICancelable = System.Reactive.Disposables.ICancelable;

namespace Speckle.Connectors.ArcGIS.Bindings;

public sealed class ArcGISReceiveBinding : IReceiveBinding, ICancelable
public sealed class ArcGISReceiveBinding : IReceiveBinding
{
public string Name { get; } = "receiveBinding";
private readonly CancellationManager _cancellationManager;
Expand Down Expand Up @@ -84,11 +83,4 @@ private void OnSendOperationProgress(string modelCardId, string status, double?
}

public void CancelReceive(string modelCardId) => _cancellationManager.CancelOperation(modelCardId);

public void Dispose()
{
IsDisposed = true;
}

public bool IsDisposed { get; private set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.Utils.Cancellation;
using ICancelable = System.Reactive.Disposables.ICancelable;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Connectors.DUI.Models.Card.SendFilter;
Expand All @@ -21,7 +20,7 @@

namespace Speckle.Connectors.ArcGIS.Bindings;

public sealed class ArcGISSendBinding : ISendBinding, ICancelable
public sealed class ArcGISSendBinding : ISendBinding
{
public string Name => "sendBinding";
public SendBindingUICommands Commands { get; }
Expand Down Expand Up @@ -364,11 +363,4 @@ private void OnSendOperationProgress(string modelCardId, string status, double?
{
Commands.SetModelProgress(modelCardId, new ModelCardProgress(modelCardId, status, progress));
}

public void Dispose()
{
IsDisposed = true;
}

public bool IsDisposed { get; private set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Connectors.Utils;
using Speckle.Connectors.Utils.Operations;
using ICancelable = System.Reactive.Disposables.ICancelable;

namespace Speckle.Connectors.Autocad.Bindings;

public sealed class AutocadReceiveBinding : IReceiveBinding, ICancelable
public sealed class AutocadReceiveBinding : IReceiveBinding
{
public string Name => "receiveBinding";
public IBridge Parent { get; }
Expand Down Expand Up @@ -79,16 +78,4 @@ private void OnSendOperationProgress(string modelCardId, string status, double?
{
Commands.SetModelProgress(modelCardId, new ModelCardProgress(modelCardId, status, progress));
}

// POC: JEDD: We should not update the UI until a OperationCancelledException is caught, we don't want to show the UI as cancelled
// until the actual operation is cancelled (thrown exception).
// I think there's room for us to introduce a cancellation pattern for bindings to do this and avoid this _cancellationManager
public void CancelSend(string modelCardId) => _cancellationManager.CancelOperation(modelCardId);

public void Dispose()
{
IsDisposed = true;
}

public bool IsDisposed { get; private set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,13 @@
using Speckle.Connectors.Autocad.Operations.Send;
using Speckle.Connectors.DUI.Exceptions;
using Speckle.Connectors.Utils.Operations;
using ICancelable = System.Reactive.Disposables.ICancelable;
using Speckle.Connectors.DUI.Models.Card.SendFilter;
using Speckle.Connectors.Utils;
using Speckle.Connectors.Utils.Caching;

namespace Speckle.Connectors.Autocad.Bindings;

public sealed class AutocadSendBinding : ISendBinding, ICancelable
public sealed class AutocadSendBinding : ISendBinding
{
public string Name { get; } = "sendBinding";
public SendBindingUICommands Commands { get; }
Expand Down Expand Up @@ -178,11 +177,4 @@ private void OnSendOperationProgress(string modelCardId, string status, double?
}

public void CancelSend(string modelCardId) => _cancellationManager.CancelOperation(modelCardId);

public void Dispose()
{
IsDisposed = true;
}

public bool IsDisposed { get; private set; }
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using Autodesk.Revit.DB;
using Speckle.Connectors.DUI.Models.Card.SendFilter;
using Speckle.Connectors.Utils.Cancellation;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.Revit.Plugin;
using Speckle.Connectors.Utils;
Expand All @@ -11,27 +10,29 @@
using Speckle.Connectors.DUI.Exceptions;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.Utils.Caching;
using Speckle.Connectors.Utils.Cancellation;
using Speckle.Connectors.Utils.Operations;

namespace Speckle.Connectors.Revit.Bindings;

internal sealed class RevitSendBinding : RevitBaseBinding, ICancelable, ISendBinding
internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
{
// POC:does it need injecting?
public CancellationManager CancellationManager { get; } = new();

// POC: does it need injecting?
private HashSet<string> ChangedObjectIds { get; set; } = new();

private readonly RevitSettings _revitSettings;
private readonly IRevitIdleManager _idleManager;
private readonly CancellationManager _cancellationManager;
private readonly IUnitOfWorkFactory _unitOfWorkFactory;
private readonly ISendConversionCache _sendConversionCache;

public RevitSendBinding(
IRevitIdleManager idleManager,
RevitContext revitContext,
DocumentModelStore store,
CancellationManager cancellationManager,
IBridge bridge,
IUnitOfWorkFactory unitOfWorkFactory,
RevitSettings revitSettings,
Expand All @@ -40,6 +41,7 @@ ISendConversionCache sendConversionCache
: base("sendBinding", store, bridge, revitContext)
{
_idleManager = idleManager;
_cancellationManager = cancellationManager;
_unitOfWorkFactory = unitOfWorkFactory;
_revitSettings = revitSettings;
_sendConversionCache = sendConversionCache;
Expand All @@ -48,6 +50,7 @@ ISendConversionCache sendConversionCache
// TODO expiry events
// TODO filters need refresh events
revitContext.UIApplication.NotNull().Application.DocumentChanged += (_, e) => DocChangeHandler(e);
Store.DocumentChanged += (_, _) => OnDocumentChanged();
}

public List<ISendFilter> GetSendFilters()
Expand All @@ -57,7 +60,7 @@ public List<ISendFilter> GetSendFilters()

public void CancelSend(string modelCardId)
{
CancellationManager.CancelOperation(modelCardId);
_cancellationManager.CancelOperation(modelCardId);
}

public SendBindingUICommands Commands { get; }
Expand All @@ -75,7 +78,7 @@ public async Task Send(string modelCardId)

// POC: probably the CTS SHOULD be injected as InstancePerLifetimeScope and then
// it can be injected where needed instead of passing it around like a bomb :D
CancellationTokenSource cts = CancellationManager.InitCancellationTokenSource(modelCardId);
CancellationTokenSource cts = _cancellationManager.InitCancellationTokenSource(modelCardId);

using IUnitOfWork<SendOperation<ElementId>> sendOperation = _unitOfWorkFactory.Resolve<
SendOperation<ElementId>
Expand Down Expand Up @@ -178,4 +181,19 @@ private void RunExpirationChecks()
Commands.SetModelsExpired(expiredSenderIds);
ChangedObjectIds = new HashSet<string>();
}

// POC: Will be re-addressed later with better UX with host apps that are friendly on async doc operations.
// That's why don't bother for now how to get rid of from dup logic in other bindings.
private void OnDocumentChanged()
{
if (_cancellationManager.NumberOfOperations > 0)
{
_cancellationManager.CancelAllOperations();
Commands.SetGlobalNotification(
ToastNotificationType.INFO,
"Document Switch",
"Operations cancelled because of document swap!"
);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@

namespace Speckle.Connectors.Rhino7.Bindings;

public class RhinoReceiveBinding : IReceiveBinding, ICancelable
public class RhinoReceiveBinding : IReceiveBinding
{
public string Name { get; set; } = "receiveBinding";
public IBridge Parent { get; set; }
public CancellationManager CancellationManager { get; set; }

private readonly CancellationManager _cancellationManager;
private readonly DocumentModelStore _store;
private readonly IUnitOfWorkFactory _unitOfWorkFactory;
public ReceiveBindingUICommands Commands { get; }
Expand All @@ -30,11 +30,11 @@ IUnitOfWorkFactory unitOfWorkFactory
Parent = parent;
_store = store;
_unitOfWorkFactory = unitOfWorkFactory;
CancellationManager = cancellationManager;
_cancellationManager = cancellationManager;
Commands = new ReceiveBindingUICommands(parent);
}

public void CancelReceive(string modelCardId) => CancellationManager.CancelOperation(modelCardId);
public void CancelReceive(string modelCardId) => _cancellationManager.CancelOperation(modelCardId);

public async Task Receive(string modelCardId)
{
Expand All @@ -49,7 +49,7 @@ public async Task Receive(string modelCardId)
}

// Init cancellation token source -> Manager also cancel it if exist before
CancellationTokenSource cts = CancellationManager.InitCancellationTokenSource(modelCardId);
CancellationTokenSource cts = _cancellationManager.InitCancellationTokenSource(modelCardId);

// Receive host objects
HostObjectBuilderResult conversionResults = await unitOfWork.Service
Expand Down Expand Up @@ -84,12 +84,5 @@ private void OnSendOperationProgress(string modelCardId, string status, double?
Commands.SetModelProgress(modelCardId, new ModelCardProgress(modelCardId, status, progress));
}

public void CancelSend(string modelCardId) => CancellationManager.CancelOperation(modelCardId);

public void Dispose()
{
IsDisposed = true;
}

public bool IsDisposed { get; private set; }
public void CancelSend(string modelCardId) => _cancellationManager.CancelOperation(modelCardId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Connectors.Rhino7.HostApp;
using Speckle.Connectors.Utils.Cancellation;
using ICancelable = System.Reactive.Disposables.ICancelable;
using Speckle.Autofac.DependencyInjection;
using Rhino.DocObjects;
using Speckle.Connectors.DUI.Exceptions;
Expand All @@ -17,7 +16,7 @@

namespace Speckle.Connectors.Rhino7.Bindings;

public sealed class RhinoSendBinding : ISendBinding, ICancelable
public sealed class RhinoSendBinding : ISendBinding
{
public string Name { get; } = "sendBinding";
public SendBindingUICommands Commands { get; }
Expand Down Expand Up @@ -213,11 +212,4 @@ private void RunExpirationChecks()
Commands.SetModelsExpired(expiredSenderIds);
ChangedObjectIds = new HashSet<string>();
}

public void Dispose()
{
IsDisposed = true;
}

public bool IsDisposed { get; private set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,20 @@ public static class BasicConnectorBindingEvents
public const string DOCUMENT_CHANGED = "documentChanged";
}

public enum ToastNotificationType
{
SUCCESS,
WARNING,
DANGER,
INFO
}

public class BasicConnectorBindingCommands
{
private const string NOTIFY_DOCUMENT_CHANGED_EVENT_NAME = "documentChanged";
private const string SET_MODEL_PROGRESS_UI_COMMAND_NAME = "setModelProgress";
private const string SET_MODEL_ERROR_UI_COMMAND_NAME = "setModelError";
private const string SET_GLOBAL_NOTIFICATION = "setGlobalNotification";

protected IBridge Bridge { get; }

Expand All @@ -47,6 +56,25 @@ public BasicConnectorBindingCommands(IBridge bridge)

public void NotifyDocumentChanged() => Bridge.Send(NOTIFY_DOCUMENT_CHANGED_EVENT_NAME);

/// <summary>
/// Use it whenever you want to send global toast notification to UI.
/// </summary>
/// <param name="type"> Level of notification, see <see cref="ToastNotificationType"/> for types</param>
/// <param name="title"> Title of the notification</param>
/// <param name="message"> Message in the toast notification.</param>
/// <param name="autoClose"> Closes toast notification in set timeout in UI. Default is true.</param>
public void SetGlobalNotification(ToastNotificationType type, string title, string message, bool autoClose = true) =>
Bridge.Send(
SET_GLOBAL_NOTIFICATION,
new
{
type,
title,
description = message,
autoClose
}
);

public void SetModelProgress(string modelCardId, ModelCardProgress progress) =>
Bridge.Send(SET_MODEL_PROGRESS_UI_COMMAND_NAME, new { modelCardId, progress });

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ public class CancellationManager
/// </summary>
private readonly Dictionary<string, CancellationTokenSource> _operationsInProgress = new();

public int NumberOfOperations => _operationsInProgress.Count;

/// <summary>
/// Get token with registered id.
/// </summary>
Expand All @@ -30,6 +32,16 @@ public bool IsExist(string id)
return _operationsInProgress.ContainsKey(id);
}

public void CancelAllOperations()
{
foreach (var operation in _operationsInProgress)
{
operation.Value.Cancel();
operation.Value.Dispose();
}
_operationsInProgress.Clear();
}

/// <summary>
/// Initialize a token source for cancellable operation.
/// </summary>
Expand Down

0 comments on commit ccf143e

Please sign in to comment.