Files
speckle-sharp-connectors/Connectors/Autocad/Speckle.Connectors.AutocadShared/Operations/Send/AutocadRootObjectBuilder.cs
T
2024-07-04 11:56:34 +01:00

114 lines
4.0 KiB
C#

using System.Diagnostics;
using Autodesk.AutoCAD.DatabaseServices;
using Speckle.Connectors.Utils.Builders;
using Speckle.Connectors.Utils.Caching;
using Speckle.Connectors.Utils.Conversion;
using Speckle.Connectors.Utils.Instances;
using Speckle.Connectors.Utils.Operations;
using Speckle.Converters.Common;
using Speckle.Core.Logging;
using Speckle.Core.Models;
using Speckle.Core.Models.Instances;
namespace Speckle.Connectors.Autocad.Operations.Send;
public class AutocadRootObjectBuilder : IRootObjectBuilder<AutocadRootObject>
{
private readonly IRootToSpeckleConverter _converter;
private readonly string[] _documentPathSeparator = { "\\" };
private readonly ISendConversionCache _sendConversionCache;
private readonly IInstanceObjectsManager<AutocadRootObject, List<Entity>> _instanceObjectsManager;
public AutocadRootObjectBuilder(
IRootToSpeckleConverter converter,
ISendConversionCache sendConversionCache,
IInstanceObjectsManager<AutocadRootObject, List<Entity>> instanceObjectManager
)
{
_converter = converter;
_sendConversionCache = sendConversionCache;
_instanceObjectsManager = instanceObjectManager;
}
public RootObjectBuilderResult Build(
IReadOnlyList<AutocadRootObject> objects,
SendInfo sendInfo,
Action<string, double?>? onOperationProgressed = null,
CancellationToken ct = default
)
{
Collection modelWithLayers =
new()
{
name = Application.DocumentManager.CurrentDocument.Name // POC: https://spockle.atlassian.net/browse/CNX-9319
.Split(_documentPathSeparator, StringSplitOptions.None)
.Reverse()
.First(),
collectionType = "root"
};
// Cached dictionary to create Collection for autocad entity layers. We first look if collection exists. If so use it otherwise create new one for that layer.
Dictionary<string, Collection> collectionCache = new();
int count = 0;
var (atomicObjects, instanceProxies, instanceDefinitionProxies) = _instanceObjectsManager.UnpackSelection(objects);
// POC: until we formalise a bit more the root object
modelWithLayers["instanceDefinitionProxies"] = instanceDefinitionProxies;
List<SendConversionResult> results = new();
var cacheHitCount = 0;
foreach (var (dbObject, applicationId) in atomicObjects)
{
ct.ThrowIfCancellationRequested();
try
{
Base converted;
if (dbObject is BlockReference && instanceProxies.TryGetValue(applicationId, out InstanceProxy instanceProxy))
{
converted = instanceProxy;
}
else if (_sendConversionCache.TryGetValue(sendInfo.ProjectId, applicationId, out ObjectReference value))
{
converted = value;
cacheHitCount++;
}
else
{
converted = _converter.Convert(dbObject);
converted.applicationId = applicationId;
}
// Create and add a collection for each layer if not done so already.
if ((dbObject as Entity)?.Layer is string layer)
{
if (!collectionCache.TryGetValue(layer, out Collection? collection))
{
collection = new Collection() { name = layer, collectionType = "layer" };
collectionCache[layer] = collection;
modelWithLayers.elements.Add(collectionCache[layer]);
}
collection.elements.Add(converted);
}
results.Add(new(Status.SUCCESS, applicationId, dbObject.GetType().ToString(), converted));
}
catch (Exception ex) when (!ex.IsFatal())
{
results.Add(new(Status.ERROR, applicationId, dbObject.GetType().ToString(), null, ex));
// POC: add logging
}
onOperationProgressed?.Invoke("Converting", (double)++count / atomicObjects.Count);
}
// POC: Log would be nice, or can be removed.
Debug.WriteLine(
$"Cache hit count {cacheHitCount} out of {objects.Count} ({(double)cacheHitCount / objects.Count})"
);
return new(modelWithLayers, results);
}
}