Skip to content

Commit

Permalink
Send receive reports (#3448)
Browse files Browse the repository at this point in the history
* Conversion Results

* Rhino

* ReceiveConversionResult

* Fixed highlighting

* Moved result out of core

* SendConversionResult

* Send error message instead all serialized exception

* Align conversion results with UI report

* Highlight object on Rhino

* Throw inner exception for highlight object

* Highlight sub elements of group in Rhino

* WIP

* Arcgis Receive Reporting

* split

* Acad highlight objects method

* Revit highlight objects method

* Arcgis highlight objects method

* Have public setters for deserialization

* wip

* wip so jedd can push

* wip refactoring revit

* wip acad (send)

* wip - acad receive

* wip arcgis refactor

* Remove using

* pass correct IDs to bakedObjId

* pass layers (not features) IDs to model card BakedObjects.

---------

Co-authored-by: Jedd Morgan <[email protected]>
Co-authored-by: Dimitrie Stefanescu <[email protected]>
Co-authored-by: KatKatKateryna <[email protected]>
  • Loading branch information
4 people authored Jun 6, 2024
1 parent 3191365 commit 406e1e2
Show file tree
Hide file tree
Showing 39 changed files with 529 additions and 299 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public async Task Receive(string modelCardId)
using IUnitOfWork<ReceiveOperation> unitOfWork = _unitOfWorkFactory.Resolve<ReceiveOperation>();

// Receive host objects
IEnumerable<string> receivedObjectIds = await unitOfWork.Service
var receiveOperationResults = await unitOfWork.Service
.Execute(
modelCard.AccountId.NotNull(), // POC: I hear -you are saying why we're passing them separately. Not sure pass the DUI3-> Connectors.DUI project dependency to the SDK-> Connector.Utils
modelCard.ProjectId.NotNull(),
Expand All @@ -63,7 +63,12 @@ public async Task Receive(string modelCardId)
)
.ConfigureAwait(false);

Commands.SetModelReceiveResult(modelCardId, receivedObjectIds.ToList());
modelCard.BakedObjectIds = receiveOperationResults.BakedObjectIds.ToList();
Commands.SetModelReceiveResult(
modelCardId,
receiveOperationResults.BakedObjectIds,
receiveOperationResults.ConversionResults
);
}
// Catch here specific exceptions if they related to model card.
catch (OperationCanceledException)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ public async Task Send(string modelCardId)
.ConfigureAwait(false);

// Store the converted references in memory for future send operations, overwriting the existing values for the given application id.
foreach (var kvp in result.convertedReferences)
foreach (var kvp in result.ConvertedReferences)
{
_convertedObjectReferences[kvp.Key + modelCard.ProjectId] = kvp.Value;
}
Expand All @@ -322,7 +322,7 @@ public async Task Send(string modelCardId)
})
.ConfigureAwait(false);

Commands.SetModelCreatedVersionId(modelCardId, sendResult.rootObjId);
Commands.SetModelSendResult(modelCardId, sendResult.RootObjId, sendResult.ConversionResults);
}
// Catch here specific exceptions if they related to model card.
catch (SpeckleSendFilterException e)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,10 @@ public BasicConnectorBinding(DocumentModelStore store, ArcGISSettings settings,

public void RemoveModel(ModelCard model) => _store.RemoveModel(model);

public async void HighlightModel(string modelCardId)
{
MapView mapView = MapView.Active;
public void HighlightObjects(List<string> objectIds) => HighlightObjectsOnView(objectIds);

public void HighlightModel(string modelCardId)
{
var model = _store.GetModelById(modelCardId);

if (model is null)
Expand All @@ -71,13 +71,19 @@ public async void HighlightModel(string modelCardId)

if (model is ReceiverModelCard receiverModelCard)
{
objectIds = receiverModelCard.ReceiveResult?.BakedObjectIds.NotNull();
objectIds = receiverModelCard.BakedObjectIds.NotNull();
}

if (objectIds is null)
{
return;
}
HighlightObjectsOnView(objectIds);
}

private async void HighlightObjectsOnView(List<string> objectIds)
{
MapView mapView = MapView.Active;

await QueuedTask
.Run(() =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
using Speckle.Connectors.Utils.Builders;
using Speckle.Autofac;
using Speckle.Connectors.ArcGIS.Filters;
using Speckle.Connectors.ArcGIS.HostApp;
using Speckle.Connectors.DUI;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.Utils;
using Speckle.Connectors.Utils.Operations;
Expand All @@ -33,7 +33,7 @@ public void Load(SpeckleContainerBuilder builder)

// POC: Overwriting the SyncToMainThread to SyncToCurrentThread for ArcGIS only!
// On SendOperation, once we called QueuedTask, it expect to run everything on same thread.
builder.AddSingletonInstance<ISyncToThread, SyncToCurrentThread>();
builder.AddSingletonInstance<ISyncToThread, SyncToQueuedTask>();

builder.AddSingleton<DocumentModelStore, ArcGISDocumentStore>();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using ArcGIS.Desktop.Framework.Threading.Tasks;
using Speckle.Connectors.Utils.Operations;

namespace Speckle.Connectors.ArcGIS.HostApp;

public class SyncToQueuedTask : ISyncToThread
{
public Task<T> RunOnThread<T>(Func<T> func) => QueuedTask.Run(func);
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
using System.Diagnostics;
using ArcGIS.Desktop.Mapping;
using Speckle.Connectors.Utils.Builders;
using Speckle.Converters.Common;
using Speckle.Core.Logging;
using Speckle.Core.Models;
using ArcGIS.Desktop.Framework.Threading.Tasks;
using Speckle.Converters.ArcGIS3.Utils;
using ArcGIS.Core.Geometry;
using Objects.GIS;
using Speckle.Connectors.Utils.Conversion;
using Speckle.Core.Models.GraphTraversal;
using Speckle.Converters.ArcGIS3;

Expand Down Expand Up @@ -49,11 +48,11 @@ List<string> objectIds
return ($"{string.Join("\\", objPath)}", converted, parentId);
}

public (string, string) ConvertNativeLayers(Base obj, string[] path, List<string> objectIds)
public (string path, string converted) ConvertNativeLayers(Collection obj, string[] path, List<string> objectIds)
{
string converted = (string)_converter.Convert(obj);
objectIds.Add(obj.id);
string objPath = $"{string.Join("\\", path)}\\{((Collection)obj).name}";
string objPath = $"{string.Join("\\", path)}\\{obj.name}";
return (objPath, converted);
}

Expand Down Expand Up @@ -106,7 +105,7 @@ private bool HasGISParent(TraversalContext context)
return vectorLayers.Count + rasterLayers.Count > 0;
}

public IEnumerable<string> Build(
public HostObjectBuilderResult Build(
Base rootObject,
string projectName,
string modelName,
Expand All @@ -128,9 +127,11 @@ CancellationToken cancellationToken
int count = 0;
Dictionary<string, (string path, Geometry converted, string? parentId)> convertedGeometries = new();
List<string> objectIds = new();
List<(string, string)> convertedGISObjects = new();
List<(string path, string converted)> convertedGISObjects = new();

// 1. convert everything
List<ReceiveConversionResult> results = new(objectsToConvert.Count);
List<string> bakedObjectIds = new();
foreach (var item in objectsToConvert)
{
(string[] path, Base obj, string? parentId) = item;
Expand All @@ -139,68 +140,48 @@ CancellationToken cancellationToken
{
if (obj is VectorLayer or Objects.GIS.RasterLayer)
{
// POC: QueuedTask
var task = QueuedTask.Run(() =>
{
convertedGISObjects.Add(ConvertNativeLayers(obj, path, objectIds));
});
task.Wait(cancellationToken);

onOperationProgressed?.Invoke("Converting", (double)++count / allCount);
var result = ConvertNativeLayers((Collection)obj, path, objectIds);
convertedGISObjects.Add(result);
// NOTE: Dim doesn't really know what is what - is the result.path the id of the obj?
// TODO: is the type in here basically a GIS Layer?
results.Add(new(Status.SUCCESS, obj, result.path, "GIS Layer"));
}
else
{
// POC: QueuedTask
QueuedTask.Run(() =>
{
convertedGeometries[obj.id] = ConvertNonNativeGeometries(obj, path, parentId, objectIds);
});
onOperationProgressed?.Invoke("Converting", (double)++count / allCount);
var result = ConvertNonNativeGeometries(obj, path, parentId, objectIds);
convertedGeometries[obj.id] = result;

// NOTE: Dim doesn't really know what is what - is the result.path the id of the obj?
results.Add(new(Status.SUCCESS, obj, result.path, result.converted.GetType().ToString())); //POC: what native id?, path may not be unique
// TODO: Do we need this here? I remember oguzhan saying something that selection/object highlighting is weird in arcgis (weird is subjective)
// bakedObjectIds.Add(result.path);
}
}
catch (Exception e) when (!e.IsFatal()) // DO NOT CATCH SPECIFIC STUFF, conversion errors should be recoverable
catch (Exception ex) when (!ex.IsFatal()) // DO NOT CATCH SPECIFIC STUFF, conversion errors should be recoverable
{
// POC: report, etc.
Debug.WriteLine("conversion error happened.");
results.Add(new(Status.ERROR, obj, null, null, ex));
}
onOperationProgressed?.Invoke("Converting", (double)++count / allCount);
}

// 2. convert Database entries with non-GIS geometry datasets
try
{
onOperationProgressed?.Invoke("Writing to Database", null);
convertedGISObjects.AddRange(_nonGisFeaturesUtils.WriteGeometriesToDatasets(convertedGeometries));
}
catch (Exception e) when (!e.IsFatal()) // DO NOT CATCH SPECIFIC STUFF, conversion errors should be recoverable
{
// POC: report, etc.
Debug.WriteLine("conversion error happened.");
}

onOperationProgressed?.Invoke("Writing to Database", null);
convertedGISObjects.AddRange(_nonGisFeaturesUtils.WriteGeometriesToDatasets(convertedGeometries));

int bakeCount = 0;
List<string> bakedLayersURIs = new();
onOperationProgressed?.Invoke("Adding to Map", bakeCount);
// 3. add layer and tables to the Table Of Content
foreach ((string, string) databaseObj in convertedGISObjects)
foreach (var databaseObj in convertedGISObjects)
{
cancellationToken.ThrowIfCancellationRequested();

// BAKE OBJECTS HERE
// POC: QueuedTask
var task = QueuedTask.Run(() =>
{
try
{
bakedLayersURIs.Add(AddDatasetsToMap(databaseObj));
}
catch (Exception e) when (!e.IsFatal())
{
// log error ("Layer X couldn't be added to Map"), but not cancel all operations
}
onOperationProgressed?.Invoke("Adding to Map", (double)++bakeCount / convertedGISObjects.Count);
});
task.Wait(cancellationToken);
bakedObjectIds.Add(AddDatasetsToMap(databaseObj));
onOperationProgressed?.Invoke("Adding to Map", (double)++bakeCount / convertedGISObjects.Count);
}

return bakedLayersURIs;
// TODO: validated a correct set regarding bakedobject ids
return new(bakedObjectIds, results);
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using ArcGIS.Desktop.Mapping;
using Speckle.Autofac.DependencyInjection;
using Speckle.Connectors.Utils.Builders;
using Speckle.Connectors.Utils.Conversion;
using Speckle.Connectors.Utils.Operations;
using Speckle.Converters.Common;
using Speckle.Core.Logging;
using Speckle.Core.Models;

namespace Speckle.Connectors.ArcGis.Operations.Send;
Expand All @@ -19,7 +21,7 @@ public RootObjectBuilder(IUnitOfWorkFactory unitOfWorkFactory)
_unitOfWorkFactory = unitOfWorkFactory;
}

public Base Build(
public RootObjectBuilderResult Build(
IReadOnlyList<MapMember> objects,
SendInfo sendInfo,
Action<string, double?>? onOperationProgressed = null,
Expand All @@ -35,6 +37,7 @@ public Base Build(

Collection rootObjectCollection = new(); //TODO: Collections

List<SendConversionResult> results = new(objects.Count);
foreach (MapMember mapMember in objects)
{
ct.ThrowIfCancellationRequested();
Expand All @@ -59,24 +62,17 @@ public Base Build(

// add to host
collectionHost.elements.Add(converted);
results.Add(new(Status.SUCCESS, applicationId, mapMember.GetType().Name, converted));
}
// POC: Exception handling on conversion logic must be revisited after several connectors have working conversions
catch (SpeckleConversionException e)
catch (Exception ex) when (!ex.IsFatal())
{
// POC: DO something with the exception
Console.WriteLine(e);
continue;
}
catch (NotSupportedException e)
{
// POC: DO something with the exception
Console.WriteLine(e);
continue;
results.Add(new(Status.ERROR, applicationId, mapMember.GetType().Name, null, ex));
// POC: add logging
}

onOperationProgressed?.Invoke("Converting", (double)++count / objects.Count);
}

return rootObjectCollection;
return new(rootObjectCollection, results);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,16 @@ public string GetConnectorVersion() =>

public void RemoveModel(ModelCard model) => _store.RemoveModel(model);

public void HighlightObjects(List<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();
HighlightObjectsOnView(acadObjectIds);
}

public void HighlightModel(string modelCardId)
{
// POC: Will be addressed to move it into AutocadContext!
Expand All @@ -88,7 +98,7 @@ public void HighlightModel(string modelCardId)

if (model is ReceiverModelCard receiverModelCard)
{
var dbObjects = doc.GetObjects((receiverModelCard.ReceiveResult?.BakedObjectIds).NotNull());
var dbObjects = doc.GetObjects(receiverModelCard.BakedObjectIds.NotNull());
objectIds = dbObjects.Select(tuple => tuple.Root.Id).ToArray();
}

Expand All @@ -98,6 +108,13 @@ public void HighlightModel(string modelCardId)
return;
}

HighlightObjectsOnView(objectIds);
}

private void HighlightObjectsOnView(ObjectId[] objectIds)
{
var doc = Application.DocumentManager.MdiActiveDocument;

Parent.RunOnMainThread(() =>
{
doc.Editor.SetImpliedSelection(Array.Empty<ObjectId>()); // Deselects
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace Speckle.Connectors.Autocad.Bindings;

public sealed class AutocadReceiveBinding : IReceiveBinding, ICancelable
{
public string Name { get; } = "receiveBinding";
public string Name => "receiveBinding";
public IBridge Parent { get; }

private readonly DocumentModelStore _store;
Expand Down Expand Up @@ -53,7 +53,7 @@ public async Task Receive(string modelCardId)
CancellationTokenSource cts = _cancellationManager.InitCancellationTokenSource(modelCardId);

// Receive host objects
IEnumerable<string> receivedObjectIds = await unitOfWork.Service
var operationResults = await unitOfWork.Service
.Execute(
modelCard.AccountId.NotNull(), // POC: I hear -you are saying why we're passing them separately. Not sure pass the DUI3-> Connectors.DUI project dependency to the SDK-> Connector.Utils
modelCard.ProjectId.NotNull(),
Expand All @@ -65,7 +65,7 @@ public async Task Receive(string modelCardId)
)
.ConfigureAwait(false);

Commands.SetModelReceiveResult(modelCardId, receivedObjectIds.ToList());
Commands.SetModelReceiveResult(modelCardId, operationResults.BakedObjectIds, operationResults.ConversionResults);
}
// Catch here specific exceptions if they related to model card.
catch (OperationCanceledException)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,15 +163,15 @@ private async Task SendInternal(string modelCardId)
.ConfigureAwait(false);

// Store the converted references in memory for future send operations, overwriting the existing values for the given application id.
foreach (var kvp in sendResult.convertedReferences)
foreach (var kvp in sendResult.ConvertedReferences)
{
_convertedObjectReferences[kvp.Key + modelCard.ProjectId] = kvp.Value;
}

// It's important to reset the model card's list of changed obj ids so as to ensure we accurately keep track of changes between send operations.
modelCard.ChangedObjectIds = new();

Commands.SetModelCreatedVersionId(modelCardId, sendResult.rootObjId);
Commands.SetModelSendResult(modelCardId, sendResult.RootObjId, sendResult.ConversionResults);
}
// Catch here specific exceptions if they related to model card.
catch (OperationCanceledException)
Expand Down
Loading

0 comments on commit 406e1e2

Please sign in to comment.