diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 17615866f..71449e8e4 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -49,6 +49,7 @@ jobs: needs: build env: IS_TAG_BUILD: ${{ github.ref_type == 'tag' }} + IS_RELEASE_BRANCH: ${{ startsWith(github.ref_name, 'release/') || github.ref_name == 'main'}} steps: - name: 🔫 Trigger Build Installers uses: ALEEF02/workflow-dispatch@v3.0.0 @@ -57,7 +58,7 @@ jobs: workflow: Build Installers repo: specklesystems/connector-installers token: ${{ secrets.CONNECTORS_GH_TOKEN }} - inputs: '{ "run_id": "${{ github.run_id }}", "version": "${{ needs.build.outputs.version }}", "public_release": ${{ env.IS_TAG_BUILD }} }' + inputs: '{ "run_id": "${{ github.run_id }}", "version": "${{ needs.build.outputs.version }}", "public_release": ${{ env.IS_TAG_BUILD }}, "store_artifacts": ${{ env.IS_RELEASE_BRANCH }} }' ref: main wait-for-completion: true wait-for-completion-interval: 10s diff --git a/Build/packages.lock.json b/Build/packages.lock.json index 560d07d9a..df89bd19f 100644 --- a/Build/packages.lock.json +++ b/Build/packages.lock.json @@ -30,6 +30,15 @@ "System.Threading.Tasks.Dataflow": "8.0.0" } }, + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3" + } + }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -73,6 +82,11 @@ "resolved": "17.10.4", "contentHash": "wyABaqY+IHCMMSTQmcc3Ca6vbmg5BaEPgicnEgpll+4xyWZWlkQqUwafweUd9VAhBb4jqplMl6voUHQ6yfdUcg==" }, + "Microsoft.NETFramework.ReferenceAssemblies.net461": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA==" + }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", diff --git a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/ArcGISReceiveBinding.cs b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/ArcGISReceiveBinding.cs index f31984bc3..ef35bc039 100644 --- a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/ArcGISReceiveBinding.cs +++ b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/ArcGISReceiveBinding.cs @@ -76,23 +76,19 @@ public async Task Receive(string modelCardId) .ServiceProvider.GetRequiredService() .Execute( modelCard.GetReceiveInfo("ArcGIS"), // POC: get host app name from settings? same for GetSendInfo - cancellationToken, - (status, progress) => - _operationProgressManager.SetModelProgress( - Parent, - modelCardId, - new ModelCardProgress(modelCardId, status, progress), - cancellationToken - ) + _operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationToken), + cancellationToken ) .ConfigureAwait(false); modelCard.BakedObjectIds = receiveOperationResults.BakedObjectIds.ToList(); - Commands.SetModelReceiveResult( - modelCardId, - receiveOperationResults.BakedObjectIds, - receiveOperationResults.ConversionResults - ); + await Commands + .SetModelReceiveResult( + modelCardId, + receiveOperationResults.BakedObjectIds, + receiveOperationResults.ConversionResults + ) + .ConfigureAwait(false); } catch (OperationCanceledException) { @@ -104,7 +100,7 @@ public async Task Receive(string modelCardId) catch (Exception ex) when (!ex.IsFatal()) // UX reasons - we will report operation exceptions as model card error. We may change this later when we have more exception documentation { _logger.LogModelCardHandledError(ex); - Commands.SetModelError(modelCardId, ex); + await Commands.SetModelError(modelCardId, ex).ConfigureAwait(false); } } diff --git a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/ArcGISSelectionBinding.cs b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/ArcGISSelectionBinding.cs index 44cb30e72..6ef531c23 100644 --- a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/ArcGISSelectionBinding.cs +++ b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/ArcGISSelectionBinding.cs @@ -1,5 +1,6 @@ using ArcGIS.Desktop.Mapping; using ArcGIS.Desktop.Mapping.Events; +using Speckle.Connectors.ArcGIS.Utils; using Speckle.Connectors.DUI.Bindings; using Speckle.Connectors.DUI.Bridge; @@ -7,11 +8,13 @@ namespace Speckle.Connectors.ArcGIS.Bindings; public class ArcGISSelectionBinding : ISelectionBinding { + private readonly MapMembersUtils _mapMemberUtils; public string Name => "selectionBinding"; public IBrowserBridge Parent { get; } - public ArcGISSelectionBinding(IBrowserBridge parent) + public ArcGISSelectionBinding(IBrowserBridge parent, MapMembersUtils mapMemberUtils) { + _mapMemberUtils = mapMemberUtils; Parent = parent; var topLevelHandler = parent.TopLevelExceptionHandler; @@ -52,14 +55,8 @@ public SelectionInfo GetSelection() List allNestedMembers = new(); foreach (MapMember member in selectedMembers) { - if (member is GroupLayer group) - { - GetLayersFromGroup(group, allNestedMembers); - } - else - { - allNestedMembers.Add(member); - } + var layerMapMembers = _mapMemberUtils.UnpackMapLayers(selectedMembers); + allNestedMembers.AddRange(layerMapMembers); } List objectTypes = allNestedMembers diff --git a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/ArcGISSendBinding.cs b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/ArcGISSendBinding.cs index 1f70d7f65..b7eb88c8f 100644 --- a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/ArcGISSendBinding.cs +++ b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/ArcGISSendBinding.cs @@ -9,6 +9,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Speckle.Connectors.ArcGIS.Filters; +using Speckle.Connectors.ArcGIS.Utils; using Speckle.Connectors.Common.Caching; using Speckle.Connectors.Common.Cancellation; using Speckle.Connectors.Common.Operations; @@ -54,6 +55,7 @@ public sealed class ArcGISSendBinding : ISendBinding private List SubscribedLayers { get; set; } = new(); private List SubscribedTables { get; set; } = new(); + private readonly MapMembersUtils _mapMemberUtils; public ArcGISSendBinding( DocumentModelStore store, @@ -64,7 +66,8 @@ public ArcGISSendBinding( ISendConversionCache sendConversionCache, IOperationProgressManager operationProgressManager, ILogger logger, - IArcGISConversionSettingsFactory arcGisConversionSettingsFactory + IArcGISConversionSettingsFactory arcGisConversionSettingsFactory, + MapMembersUtils mapMemberUtils ) { _store = store; @@ -76,6 +79,7 @@ IArcGISConversionSettingsFactory arcGisConversionSettingsFactory _logger = logger; _topLevelExceptionHandler = parent.TopLevelExceptionHandler; _arcGISConversionSettingsFactory = arcGisConversionSettingsFactory; + _mapMemberUtils = mapMemberUtils; Parent = parent; Commands = new SendBindingUICommands(parent); @@ -89,22 +93,32 @@ IArcGISConversionSettingsFactory arcGisConversionSettingsFactory private void SubscribeToArcGISEvents() { LayersRemovedEvent.Subscribe( - a => _topLevelExceptionHandler.CatchUnhandled(() => GetIdsForLayersRemovedEvent(a)), + a => + _topLevelExceptionHandler.FireAndForget(async () => await GetIdsForLayersRemovedEvent(a).ConfigureAwait(false)), true ); StandaloneTablesRemovedEvent.Subscribe( - a => _topLevelExceptionHandler.CatchUnhandled(() => GetIdsForStandaloneTablesRemovedEvent(a)), + a => + _topLevelExceptionHandler.FireAndForget( + async () => await GetIdsForStandaloneTablesRemovedEvent(a).ConfigureAwait(false) + ), true ); MapPropertyChangedEvent.Subscribe( - a => _topLevelExceptionHandler.CatchUnhandled(() => GetIdsForMapPropertyChangedEvent(a)), + a => + _topLevelExceptionHandler.FireAndForget( + async () => await GetIdsForMapPropertyChangedEvent(a).ConfigureAwait(false) + ), true ); // Map units, CRS etc. MapMemberPropertiesChangedEvent.Subscribe( - a => _topLevelExceptionHandler.CatchUnhandled(() => GetIdsForMapMemberPropertiesChangedEvent(a)), + a => + _topLevelExceptionHandler.FireAndForget( + async () => await GetIdsForMapMemberPropertiesChangedEvent(a).ConfigureAwait(false) + ), true ); // e.g. Layer name @@ -181,28 +195,31 @@ private void SubscribeToAnyDataSourceChange(Table layerTable) { RowCreatedEvent.Subscribe( (args) => - { - OnRowChanged(args); - }, + Parent.TopLevelExceptionHandler.FireAndForget(async () => + { + await OnRowChanged(args).ConfigureAwait(false); + }), layerTable ); RowChangedEvent.Subscribe( (args) => - { - OnRowChanged(args); - }, + Parent.TopLevelExceptionHandler.FireAndForget(async () => + { + await OnRowChanged(args).ConfigureAwait(false); + }), layerTable ); RowDeletedEvent.Subscribe( (args) => - { - OnRowChanged(args); - }, + Parent.TopLevelExceptionHandler.FireAndForget(async () => + { + await OnRowChanged(args).ConfigureAwait(false); + }), layerTable ); } - private void OnRowChanged(RowChangedEventArgs args) + private async Task OnRowChanged(RowChangedEventArgs args) { if (args == null || MapView.Active == null) { @@ -241,60 +258,38 @@ private void OnRowChanged(RowChangedEventArgs args) } } - RunExpirationChecks(false); + await RunExpirationChecks(false).ConfigureAwait(false); } - private void GetIdsForLayersRemovedEvent(LayerEventsArgs args) + private async Task GetIdsForLayersRemovedEvent(LayerEventsArgs args) { foreach (Layer layer in args.Layers) { ChangedObjectIds[layer.URI] = 1; } - RunExpirationChecks(true); + await RunExpirationChecks(true).ConfigureAwait(false); } - private void GetIdsForStandaloneTablesRemovedEvent(StandaloneTableEventArgs args) + private async Task GetIdsForStandaloneTablesRemovedEvent(StandaloneTableEventArgs args) { foreach (StandaloneTable table in args.Tables) { ChangedObjectIds[table.URI] = 1; } - RunExpirationChecks(true); - } - - private void AddChangedNestedObjectIds(GroupLayer group) - { - ChangedObjectIds[group.URI] = 1; - foreach (var member in group.Layers) - { - if (member is GroupLayer subGroup) - { - AddChangedNestedObjectIds(subGroup); - } - else - { - ChangedObjectIds[member.URI] = 1; - } - } + await RunExpirationChecks(true).ConfigureAwait(false); } - private void GetIdsForMapPropertyChangedEvent(MapPropertyChangedEventArgs args) + private async Task GetIdsForMapPropertyChangedEvent(MapPropertyChangedEventArgs args) { foreach (Map map in args.Maps) { - foreach (MapMember member in map.Layers) + List allMapMembers = _mapMemberUtils.GetAllMapMembers(map); + foreach (MapMember member in allMapMembers) { - if (member is GroupLayer group) - { - AddChangedNestedObjectIds(group); - } - else - { - ChangedObjectIds[member.URI] = 1; - } + ChangedObjectIds[member.URI] = 1; } } - RunExpirationChecks(false); + await RunExpirationChecks(false).ConfigureAwait(false); } private void GetIdsForLayersAddedEvent(LayerEventsArgs args) @@ -316,7 +311,7 @@ private void GetIdsForStandaloneTablesAddedEvent(StandaloneTableEventArgs args) } } - private void GetIdsForMapMemberPropertiesChangedEvent(MapMemberPropertiesChangedEventArgs args) + private async Task GetIdsForMapMemberPropertiesChangedEvent(MapMemberPropertiesChangedEventArgs args) { // don't subscribe to all events (e.g. expanding group, changing visibility etc.) bool validEvent = false; @@ -344,7 +339,7 @@ private void GetIdsForMapMemberPropertiesChangedEvent(MapMemberPropertiesChanged { ChangedObjectIds[member.URI] = 1; } - RunExpirationChecks(false); + await RunExpirationChecks(false).ConfigureAwait(false); } } @@ -417,13 +412,7 @@ public async Task Send(string modelCardId) .Execute( mapMembers, modelCard.GetSendInfo("ArcGIS"), // POC: get host app name from settings? same for GetReceiveInfo - (status, progress) => - _operationProgressManager.SetModelProgress( - Parent, - modelCardId, - new ModelCardProgress(modelCardId, status, progress), - cancellationToken - ), + _operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationToken), cancellationToken ) .ConfigureAwait(false); @@ -432,7 +421,9 @@ public async Task Send(string modelCardId) }) .ConfigureAwait(false); - Commands.SetModelSendResult(modelCardId, sendResult.RootObjId, sendResult.ConversionResults); + await Commands + .SetModelSendResult(modelCardId, sendResult.RootObjId, sendResult.ConversionResults) + .ConfigureAwait(false); } catch (OperationCanceledException) { @@ -444,7 +435,7 @@ public async Task Send(string modelCardId) catch (Exception ex) when (!ex.IsFatal()) // UX reasons - we will report operation exceptions as model card error. We may change this later when we have more exception documentation { _logger.LogModelCardHandledError(ex); - Commands.SetModelError(modelCardId, ex); + await Commands.SetModelError(modelCardId, ex).ConfigureAwait(false); } } @@ -453,7 +444,7 @@ public async Task Send(string modelCardId) /// /// 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. /// - private void RunExpirationChecks(bool idsDeleted) + private async Task RunExpirationChecks(bool idsDeleted) { var senders = _store.GetSenders(); List expiredSenderIds = new(); @@ -479,7 +470,7 @@ private void RunExpirationChecks(bool idsDeleted) } } - Commands.SetModelsExpired(expiredSenderIds); + await Commands.SetModelsExpired(expiredSenderIds).ConfigureAwait(false); ChangedObjectIds = new(); } } diff --git a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/BasicConnectorBinding.cs b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/BasicConnectorBinding.cs index 86e4b1d7c..82804397a 100644 --- a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/BasicConnectorBinding.cs +++ b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Bindings/BasicConnectorBinding.cs @@ -30,9 +30,10 @@ public BasicConnectorBinding(DocumentModelStore store, IBrowserBridge parent, IS Commands = new BasicConnectorBindingCommands(parent); _store.DocumentChanged += (_, _) => - { - Commands.NotifyDocumentChanged(); - }; + parent.TopLevelExceptionHandler.FireAndForget(async () => + { + await Commands.NotifyDocumentChanged().ConfigureAwait(false); + }); } public string GetSourceApplicationName() => _speckleApplication.Slug; @@ -59,10 +60,10 @@ public BasicConnectorBinding(DocumentModelStore store, IBrowserBridge parent, IS public void RemoveModel(ModelCard model) => _store.RemoveModel(model); - public void HighlightObjects(List objectIds) => - HighlightObjectsOnView(objectIds.Select(x => new ObjectID(x)).ToList()); + public async Task HighlightObjects(IReadOnlyList objectIds) => + await HighlightObjectsOnView(objectIds.Select(x => new ObjectID(x)).ToList()).ConfigureAwait(false); - public void HighlightModel(string modelCardId) + public async Task HighlightModel(string modelCardId) { var model = _store.GetModelById(modelCardId); @@ -87,27 +88,27 @@ public void HighlightModel(string modelCardId) { return; } - HighlightObjectsOnView(objectIds); + await HighlightObjectsOnView(objectIds).ConfigureAwait(false); } - private async void HighlightObjectsOnView(List objectIds) + private async Task HighlightObjectsOnView(IReadOnlyList objectIds) { MapView mapView = MapView.Active; await QueuedTask - .Run(() => + .Run(async () => { List mapMembersFeatures = GetMapMembers(objectIds, mapView); ClearSelectionInTOC(); ClearSelection(); - SelectMapMembersInTOC(mapMembersFeatures); + await SelectMapMembersInTOC(mapMembersFeatures).ConfigureAwait(false); SelectMapMembersAndFeatures(mapMembersFeatures); mapView.ZoomToSelected(); }) .ConfigureAwait(false); } - private List GetMapMembers(List objectIds, MapView mapView) + private List GetMapMembers(IReadOnlyList objectIds, MapView mapView) { // find the layer on the map (from the objectID) and add the featureID is available List mapMembersFeatures = new(); @@ -145,7 +146,7 @@ private void ClearSelectionInTOC() MapView.Active.ClearTOCSelection(); } - private void SelectMapMembersAndFeatures(List mapMembersFeatures) + private void SelectMapMembersAndFeatures(IReadOnlyList mapMembersFeatures) { foreach (MapMemberFeature mapMemberFeat in mapMembersFeatures) { @@ -170,7 +171,7 @@ private void SelectMapMembersAndFeatures(List mapMembersFeatur } } - private void SelectMapMembersInTOC(List mapMembersFeatures) + private async Task SelectMapMembersInTOC(IReadOnlyList mapMembersFeatures) { List layers = new(); List tables = new(); @@ -186,7 +187,7 @@ private void SelectMapMembersInTOC(List mapMembersFeatures) } else { - QueuedTask.Run(() => layer.SetExpanded(true)); + await QueuedTask.Run(() => layer.SetExpanded(true)).ConfigureAwait(false); } } else if (member is StandaloneTable table) diff --git a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/HostApp/ArcGISColorManager.cs b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/HostApp/ArcGISColorManager.cs index 90e112ef4..970feb224 100644 --- a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/HostApp/ArcGISColorManager.cs +++ b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/HostApp/ArcGISColorManager.cs @@ -2,6 +2,7 @@ using ArcGIS.Core.CIM; using ArcGIS.Core.Data; using ArcGIS.Desktop.Mapping; +using Speckle.Connectors.Common.Operations; using Speckle.Converters.ArcGIS3.Utils; using Speckle.Objects; using Speckle.Objects.Other; @@ -55,14 +56,15 @@ public List UnpackColors(List<(MapMember, int)> mapMembersWithDispla /// /// /// - public void ParseColors(List colorProxies, Action? onOperationProgressed) + public async Task ParseColors(List colorProxies, IProgress onOperationProgressed) { // injected as Singleton, so we need to clean existing proxies first ObjectColorsIdMap = new(); var count = 0; foreach (ColorProxy colorProxy in colorProxies) { - onOperationProgressed?.Invoke("Converting colors", (double)++count / colorProxies.Count); + onOperationProgressed.Report(new("Converting colors", (double)++count / colorProxies.Count)); + await Task.Yield(); foreach (string objectId in colorProxy.objects) { Color convertedColor = Color.FromArgb(colorProxy.value); @@ -76,14 +78,18 @@ public void ParseColors(List colorProxies, Action? /// /// /// - public void ParseMaterials(List materialProxies, Action? onOperationProgressed) + public async Task ParseMaterials( + List materialProxies, + IProgress onOperationProgressed + ) { // injected as Singleton, so we need to clean existing proxies first ObjectMaterialsIdMap = new(); var count = 0; foreach (RenderMaterialProxy colorProxy in materialProxies) { - onOperationProgressed?.Invoke("Converting materials", (double)++count / materialProxies.Count); + onOperationProgressed.Report(new("Converting materials", (double)++count / materialProxies.Count)); + await Task.Yield(); foreach (string objectId in colorProxy.objects) { Color convertedColor = Color.FromArgb(colorProxy.value.diffuse); @@ -310,6 +316,11 @@ private void ProcessFeatureLayerColors(FeatureLayer layer, int displayPriority) int count = 1; using (RowCursor rowCursor = layer.Search()) { + // if layer doesn't have a valid data source (and the conversion likely failed), don't create a colorProxy + if (rowCursor is null) + { + return; + } while (rowCursor.MoveNext()) { string elementAppId = $"{layer.URI}_{count}"; @@ -445,6 +456,11 @@ private bool TryGetUniqueRendererColor( out int color ) { + if (uniqueRenderer.DefaultSymbol is null) + { + color = RbgToInt(255, 255, 255, 255); + return false; + } if (!TryGetSymbolColor(uniqueRenderer.DefaultSymbol.Symbol, out color)) // get default color { return false; @@ -487,6 +503,11 @@ out int color // set the group color to class symbol color if conditions are met if (groupConditionsMet) { + if (groupClass.Symbol is null) + { + color = RbgToInt(255, 255, 255, 255); + return false; + } if (!TryGetSymbolColor(groupClass.Symbol.Symbol, out color)) { return false; @@ -538,6 +559,11 @@ private bool TryGetGraduatedRendererColor( out int color ) { + if (graduatedRenderer.DefaultSymbol is null) + { + color = RbgToInt(255, 255, 255, 255); + return false; + } if (!TryGetSymbolColor(graduatedRenderer.DefaultSymbol.Symbol, out color)) // get default color { return false; diff --git a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/HostApp/SyncToQueuedTask.cs b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/HostApp/SyncToQueuedTask.cs deleted file mode 100644 index 946676ee1..000000000 --- a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/HostApp/SyncToQueuedTask.cs +++ /dev/null @@ -1,9 +0,0 @@ -using ArcGIS.Desktop.Framework.Threading.Tasks; -using Speckle.Connectors.Common.Operations; - -namespace Speckle.Connectors.ArcGIS.HostApp; - -public class SyncToQueuedTask : ISyncToThread -{ - public Task RunOnThread(Func func) => QueuedTask.Run(func); -} diff --git a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Operations/Receive/ArcGISHostObjectBuilder.cs b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Operations/Receive/ArcGISHostObjectBuilder.cs index dcd97e4d7..2b98a8300 100644 --- a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Operations/Receive/ArcGISHostObjectBuilder.cs +++ b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Operations/Receive/ArcGISHostObjectBuilder.cs @@ -62,7 +62,7 @@ public async Task Build( Base rootObject, string projectName, string modelName, - Action? onOperationProgressed, + IProgress onOperationProgressed, CancellationToken cancellationToken ) { @@ -70,7 +70,7 @@ CancellationToken cancellationToken // ATM, GIS commit CRS is stored per layer (in FeatureClass converter), but should be moved to the Root level too // Prompt the UI conversion started. Progress bar will swoosh. - onOperationProgressed?.Invoke("Converting", null); + onOperationProgressed.Report(new("Converting", null)); // get materials List? materials = (rootObject[ProxyKeys.RENDER_MATERIAL] as List) @@ -78,14 +78,14 @@ CancellationToken cancellationToken .ToList(); if (materials != null) { - _colorManager.ParseMaterials(materials, onOperationProgressed); + await _colorManager.ParseMaterials(materials, onOperationProgressed).ConfigureAwait(false); } // get colors List? colors = (rootObject[ProxyKeys.COLOR] as List)?.Cast().ToList(); if (colors != null) { - _colorManager.ParseColors(colors, onOperationProgressed); + await _colorManager.ParseColors(colors, onOperationProgressed).ConfigureAwait(false); } int count = 0; @@ -125,24 +125,32 @@ obj is GisNonGeometricFeature { results.Add(new(Status.ERROR, obj, null, null, ex)); } - onOperationProgressed?.Invoke("Converting", (double)++count / objectsToConvert.Count); + onOperationProgressed.Report(new("Converting", (double)++count / objectsToConvert.Count)); } // 2.1. Group conversionTrackers (to write into datasets) - onOperationProgressed?.Invoke("Grouping features into layers", null); + onOperationProgressed.Report(new("Grouping features into layers", null)); Dictionary> convertedGroups = await QueuedTask - .Run(() => + .Run(async () => { - return _featureClassUtils.GroupConversionTrackers(conversionTracker, onOperationProgressed); + return await _featureClassUtils + .GroupConversionTrackers(conversionTracker, (s, progres) => onOperationProgressed.Report(new(s, progres))) + .ConfigureAwait(false); }) .ConfigureAwait(false); // 2.2. Write groups of objects to Datasets - onOperationProgressed?.Invoke("Writing to Database", null); + onOperationProgressed.Report(new("Writing to Database", null)); await QueuedTask - .Run(() => + .Run(async () => { - _featureClassUtils.CreateDatasets(conversionTracker, convertedGroups, onOperationProgressed); + await _featureClassUtils + .CreateDatasets( + conversionTracker, + convertedGroups, + (s, progres) => onOperationProgressed.Report(new(s, progres)) + ) + .ConfigureAwait(false); }) .ConfigureAwait(false); @@ -153,7 +161,7 @@ await QueuedTask int bakeCount = 0; Dictionary bakedMapMembers = new(); - onOperationProgressed?.Invoke("Adding to Map", bakeCount); + onOperationProgressed.Report(new("Adding to Map", bakeCount)); foreach (var item in conversionTracker) { @@ -215,7 +223,8 @@ await QueuedTask // add report item AddResultsFromTracker(trackerItem, results); } - onOperationProgressed?.Invoke("Adding to Map", (double)++bakeCount / conversionTracker.Count); + + onOperationProgressed.Report(new("Adding to Map", (double)++bakeCount / conversionTracker.Count)); } // apply renderers to baked layers @@ -295,78 +304,80 @@ private void AddResultsFromTracker(ObjectConversionTracker trackerItem, List AddDatasetsToMap( + private async Task AddDatasetsToMap( ObjectConversionTracker trackerItem, Dictionary createdLayerGroups, string projectName, string modelName ) { - return QueuedTask.Run(() => - { - // get layer details - string? datasetId = trackerItem.DatasetId; // should not be null here - Uri uri = new($"{_settingsStore.Current.SpeckleDatabasePath.AbsolutePath.Replace('/', '\\')}\\{datasetId}"); - string nestedLayerName = trackerItem.NestedLayerName; - - // add group for the current layer - string shortName = nestedLayerName.Split("\\")[^1]; - string nestedLayerPath = string.Join("\\", nestedLayerName.Split("\\").SkipLast(1)); - - // if no general group layer found - if (createdLayerGroups.Count == 0) + return await QueuedTask + .Run(() => { - Map map = _settingsStore.Current.Map; - GroupLayer mainGroupLayer = LayerFactory.Instance.CreateGroupLayer(map, 0, $"{projectName}: {modelName}"); - mainGroupLayer.SetExpanded(true); - createdLayerGroups["Basic Speckle Group"] = mainGroupLayer; // key doesn't really matter here - } + // get layer details + string? datasetId = trackerItem.DatasetId; // should not be null here + Uri uri = new($"{_settingsStore.Current.SpeckleDatabasePath.AbsolutePath.Replace('/', '\\')}\\{datasetId}"); + string nestedLayerName = trackerItem.NestedLayerName; - var groupLayer = CreateNestedGroupLayer(nestedLayerPath, createdLayerGroups); + // add group for the current layer + string shortName = nestedLayerName.Split("\\")[^1]; + string nestedLayerPath = string.Join("\\", nestedLayerName.Split("\\").SkipLast(1)); - // Most of the Speckle-written datasets will be containing geometry and added as Layers - // although, some datasets might be just tables (e.g. native GIS Tables, in the future maybe Revit schedules etc. - // We can create a connection to the dataset in advance and determine its type, but this will be more - // expensive, than assuming by default that it's a layer with geometry (which in most cases it's expected to be) - try - { - var layer = LayerFactory.Instance.CreateLayer(uri, groupLayer, layerName: shortName); - if (layer == null) + // if no general group layer found + if (createdLayerGroups.Count == 0) { - throw new SpeckleException($"Layer '{shortName}' was not created"); + Map map = _settingsStore.Current.Map; + GroupLayer mainGroupLayer = LayerFactory.Instance.CreateGroupLayer(map, 0, $"{projectName}: {modelName}"); + mainGroupLayer.SetExpanded(true); + createdLayerGroups["Basic Speckle Group"] = mainGroupLayer; // key doesn't really matter here } - layer.SetExpanded(false); - // if Scene - // https://community.esri.com/t5/arcgis-pro-sdk-questions/sdk-equivalent-to-changing-layer-s-elevation/td-p/1346139 - if (_settingsStore.Current.Map.IsScene) + var groupLayer = CreateNestedGroupLayer(nestedLayerPath, createdLayerGroups); + + // Most of the Speckle-written datasets will be containing geometry and added as Layers + // although, some datasets might be just tables (e.g. native GIS Tables, in the future maybe Revit schedules etc. + // We can create a connection to the dataset in advance and determine its type, but this will be more + // expensive, than assuming by default that it's a layer with geometry (which in most cases it's expected to be) + try { - var groundSurfaceLayer = _settingsStore.Current.Map.GetGroundElevationSurfaceLayer(); - var layerElevationSurface = new CIMLayerElevationSurface + var layer = LayerFactory.Instance.CreateLayer(uri, groupLayer, layerName: shortName); + if (layer == null) { - ElevationSurfaceLayerURI = groundSurfaceLayer.URI, - }; + throw new SpeckleException($"Layer '{shortName}' was not created"); + } + layer.SetExpanded(false); - // for Feature Layers - if (layer.GetDefinition() is CIMFeatureLayer cimLyr) + // if Scene + // https://community.esri.com/t5/arcgis-pro-sdk-questions/sdk-equivalent-to-changing-layer-s-elevation/td-p/1346139 + if (_settingsStore.Current.Map.IsScene) { - cimLyr.LayerElevation = layerElevationSurface; - layer.SetDefinition(cimLyr); + var groundSurfaceLayer = _settingsStore.Current.Map.GetGroundElevationSurfaceLayer(); + var layerElevationSurface = new CIMLayerElevationSurface + { + ElevationSurfaceLayerURI = groundSurfaceLayer.URI, + }; + + // for Feature Layers + if (layer.GetDefinition() is CIMFeatureLayer cimLyr) + { + cimLyr.LayerElevation = layerElevationSurface; + layer.SetDefinition(cimLyr); + } } - } - return (MapMember)layer; - } - catch (ArgumentException) - { - StandaloneTable table = StandaloneTableFactory.Instance.CreateStandaloneTable( - uri, - groupLayer, - tableName: shortName - ); - return table; - } - }); + return (MapMember)layer; + } + catch (ArgumentException) + { + StandaloneTable table = StandaloneTableFactory.Instance.CreateStandaloneTable( + uri, + groupLayer, + tableName: shortName + ); + return table; + } + }) + .ConfigureAwait(false); } private GroupLayer CreateNestedGroupLayer(string nestedLayerPath, Dictionary createdLayerGroups) diff --git a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Operations/Send/ArcGISRootObjectBuilder.cs b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Operations/Send/ArcGISRootObjectBuilder.cs index 604e3e5f3..148d5ef99 100644 --- a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Operations/Send/ArcGISRootObjectBuilder.cs +++ b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Operations/Send/ArcGISRootObjectBuilder.cs @@ -57,7 +57,7 @@ public async Task Build( #pragma warning restore CA1506 IReadOnlyList objects, SendInfo sendInfo, - Action? onOperationProgressed = null, + IProgress onOperationProgressed, CancellationToken ct = default ) { @@ -71,7 +71,7 @@ public async Task Build( List results = new(objects.Count); var cacheHitCount = 0; - List<(GroupLayer, Collection)> nestedGroups = new(); + List<(ILayerContainer, Collection)> nestedGroups = new(); // reorder selected layers by Table of Content (TOC) order List<(MapMember, int)> layersWithDisplayPriority = _mapMemberUtils.GetLayerDisplayPriority( @@ -79,7 +79,7 @@ public async Task Build( objects ); - onOperationProgressed?.Invoke("Converting", null); + onOperationProgressed.Report(new("Converting", null)); using (var __ = _activityFactory.Start("Converting objects")) { foreach ((MapMember mapMember, _) in layersWithDisplayPriority) @@ -112,7 +112,7 @@ public async Task Build( // don't use cache for group layers if ( - mapMember is not GroupLayer + mapMember is not ILayerContainer && _sendConversionCache.TryGetValue(sendInfo.ProjectId, applicationId, out ObjectReference? value) ) { @@ -121,7 +121,7 @@ mapMember is not GroupLayer } else { - if (mapMember is GroupLayer group) + if (mapMember is ILayerContainer group) { // group layer will always come before it's contained layers // keep active group last in the list @@ -183,7 +183,7 @@ mapMember is not GroupLayer } } - onOperationProgressed?.Invoke("Converting", (double)++count / objects.Count); + onOperationProgressed.Report(new("Converting", (double)++count / objects.Count)); } } diff --git a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Utils/MapMembersUtils.cs b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Utils/MapMembersUtils.cs index 95951cae3..f03092c5d 100644 --- a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Utils/MapMembersUtils.cs +++ b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Utils/MapMembersUtils.cs @@ -1,4 +1,3 @@ -using ArcGIS.Desktop.Internal.Mapping; using ArcGIS.Desktop.Mapping; namespace Speckle.Connectors.ArcGIS.Utils; @@ -28,21 +27,13 @@ public List UnpackMapLayers(IEnumerable mapMembersToUnpack List mapMembers = new(); foreach (var layer in mapMembersToUnpack) { + mapMembers.Add(layer); switch (layer) { - case GroupLayer subGroup: - mapMembers.Add(layer); - var subGroupMapMembers = UnpackMapLayers(subGroup.Layers); - mapMembers.AddRange(subGroupMapMembers); - break; - case ILayerContainerInternal subLayerContainerInternal: - mapMembers.Add(layer); - var subLayerMapMembers = UnpackMapLayers(subLayerContainerInternal.InternalLayers); + case ILayerContainer subGroup: + var subLayerMapMembers = UnpackMapLayers(subGroup.Layers); mapMembers.AddRange(subLayerMapMembers); break; - default: - mapMembers.Add(layer); - break; } } diff --git a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/packages.lock.json b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/packages.lock.json index 53c11bdb0..3d43e5fd2 100644 --- a/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/packages.lock.json +++ b/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/packages.lock.json @@ -14,6 +14,15 @@ "resolved": "2.2.0", "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" }, + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3" + } + }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -142,6 +151,11 @@ "System.Runtime.CompilerServices.Unsafe": "4.5.1" } }, + "Microsoft.NETFramework.ReferenceAssemblies.net461": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA==" + }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", @@ -230,8 +244,8 @@ "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )", - "Speckle.Sdk": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )", + "Speckle.Sdk": "[3.1.0-dev.162, )" } }, "speckle.connectors.dui": { @@ -239,7 +253,7 @@ "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", "Speckle.Connectors.Common": "[1.0.0, )", - "Speckle.Sdk": "[3.1.0-dev.145, )", + "Speckle.Sdk": "[3.1.0-dev.162, )", "System.Threading.Tasks.Dataflow": "[6.0.0, )" } }, @@ -264,7 +278,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )" } }, "Microsoft.Extensions.DependencyInjection": { @@ -296,18 +310,18 @@ }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "gpENXDB2i8SiXJZwrLVfB3BwKF1FHUSpPiw0BQ3Hl0V3821UWZjPYw5XK694oxJlVAeEMRvrMZMGsWOcK+3nMw==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "1fyaPjC1kVtroKfoXI7edJX0Uho/TNRWG9mtmIQ7jKhX1zElrJPSoH8zEIMqckZhGmwUrYjlQFPssASNCP721Q==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.145" + "Speckle.Sdk": "3.1.0-dev.162" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "YZLtitiVIxl12pNTXaB9gU+paNwFe++WBw6oIN6Rs5hFPialUgeKbpjh16Zq6wcptjBpvg0CHq29+d456QUAJQ==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "ulT3XNgqIQSmpQFK5YEEwRq4RA6emXBKQHnZxtCz37SJin1PnVpH9m8vOAF9FmaWsgSAfjhhjIZDruMu1oKxSg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", diff --git a/Connectors/Autocad/Speckle.Connectors.Autocad2022/Speckle.Connectors.Autocad2022.csproj b/Connectors/Autocad/Speckle.Connectors.Autocad2022/Speckle.Connectors.Autocad2022.csproj index 53fc11282..cdc7b30b1 100644 --- a/Connectors/Autocad/Speckle.Connectors.Autocad2022/Speckle.Connectors.Autocad2022.csproj +++ b/Connectors/Autocad/Speckle.Connectors.Autocad2022/Speckle.Connectors.Autocad2022.csproj @@ -2,9 +2,12 @@ net48 x64 + true 2022 $(DefineConstants);AUTOCAD;AUTOCAD2022;AUTOCAD2022_OR_GREATER Debug;Release;Local + Program + $(ProgramW6432)\Autodesk\AutoCAD $(AutoCADVersion)\acad.exe diff --git a/Connectors/Autocad/Speckle.Connectors.Autocad2022/packages.lock.json b/Connectors/Autocad/Speckle.Connectors.Autocad2022/packages.lock.json index 5db40f1a2..1cd4a4adc 100644 --- a/Connectors/Autocad/Speckle.Connectors.Autocad2022/packages.lock.json +++ b/Connectors/Autocad/Speckle.Connectors.Autocad2022/packages.lock.json @@ -2,6 +2,15 @@ "version": 2, "dependencies": { ".NETFramework,Version=v4.8": { + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3" + } + }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -137,6 +146,11 @@ "System.Runtime.CompilerServices.Unsafe": "4.5.1" } }, + "Microsoft.NETFramework.ReferenceAssemblies.net48": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ==" + }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", @@ -260,8 +274,8 @@ "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )", - "Speckle.Sdk": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )", + "Speckle.Sdk": "[3.1.0-dev.162, )" } }, "speckle.connectors.dui": { @@ -269,7 +283,7 @@ "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", "Speckle.Connectors.Common": "[1.0.0, )", - "Speckle.Sdk": "[3.1.0-dev.145, )", + "Speckle.Sdk": "[3.1.0-dev.162, )", "System.Threading.Tasks.Dataflow": "[6.0.0, )" } }, @@ -294,7 +308,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )" } }, "Microsoft.Extensions.DependencyInjection": { @@ -332,18 +346,18 @@ }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "gpENXDB2i8SiXJZwrLVfB3BwKF1FHUSpPiw0BQ3Hl0V3821UWZjPYw5XK694oxJlVAeEMRvrMZMGsWOcK+3nMw==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "1fyaPjC1kVtroKfoXI7edJX0Uho/TNRWG9mtmIQ7jKhX1zElrJPSoH8zEIMqckZhGmwUrYjlQFPssASNCP721Q==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.145" + "Speckle.Sdk": "3.1.0-dev.162" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "YZLtitiVIxl12pNTXaB9gU+paNwFe++WBw6oIN6Rs5hFPialUgeKbpjh16Zq6wcptjBpvg0CHq29+d456QUAJQ==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "ulT3XNgqIQSmpQFK5YEEwRq4RA6emXBKQHnZxtCz37SJin1PnVpH9m8vOAF9FmaWsgSAfjhhjIZDruMu1oKxSg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", diff --git a/Connectors/Autocad/Speckle.Connectors.Autocad2023/Speckle.Connectors.Autocad2023.csproj b/Connectors/Autocad/Speckle.Connectors.Autocad2023/Speckle.Connectors.Autocad2023.csproj index 4b52daee3..ce9c5379c 100644 --- a/Connectors/Autocad/Speckle.Connectors.Autocad2023/Speckle.Connectors.Autocad2023.csproj +++ b/Connectors/Autocad/Speckle.Connectors.Autocad2023/Speckle.Connectors.Autocad2023.csproj @@ -2,9 +2,12 @@ net48 x64 + true 2023 $(DefineConstants);AUTOCAD;AUTOCAD2023;AUTOCAD2022_OR_GREATER;AUTOCAD2023_OR_GREATER Debug;Release;Local + Program + $(ProgramW6432)\Autodesk\AutoCAD $(AutoCADVersion)\acad.exe diff --git a/Connectors/Autocad/Speckle.Connectors.Autocad2023/packages.lock.json b/Connectors/Autocad/Speckle.Connectors.Autocad2023/packages.lock.json index 848ab1403..c760a6a8c 100644 --- a/Connectors/Autocad/Speckle.Connectors.Autocad2023/packages.lock.json +++ b/Connectors/Autocad/Speckle.Connectors.Autocad2023/packages.lock.json @@ -2,6 +2,15 @@ "version": 2, "dependencies": { ".NETFramework,Version=v4.8": { + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3" + } + }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -137,6 +146,11 @@ "System.Runtime.CompilerServices.Unsafe": "4.5.1" } }, + "Microsoft.NETFramework.ReferenceAssemblies.net48": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ==" + }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", @@ -260,8 +274,8 @@ "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )", - "Speckle.Sdk": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )", + "Speckle.Sdk": "[3.1.0-dev.162, )" } }, "speckle.connectors.dui": { @@ -269,7 +283,7 @@ "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", "Speckle.Connectors.Common": "[1.0.0, )", - "Speckle.Sdk": "[3.1.0-dev.145, )", + "Speckle.Sdk": "[3.1.0-dev.162, )", "System.Threading.Tasks.Dataflow": "[6.0.0, )" } }, @@ -294,7 +308,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )" } }, "Microsoft.Extensions.DependencyInjection": { @@ -332,18 +346,18 @@ }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "gpENXDB2i8SiXJZwrLVfB3BwKF1FHUSpPiw0BQ3Hl0V3821UWZjPYw5XK694oxJlVAeEMRvrMZMGsWOcK+3nMw==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "1fyaPjC1kVtroKfoXI7edJX0Uho/TNRWG9mtmIQ7jKhX1zElrJPSoH8zEIMqckZhGmwUrYjlQFPssASNCP721Q==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.145" + "Speckle.Sdk": "3.1.0-dev.162" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "YZLtitiVIxl12pNTXaB9gU+paNwFe++WBw6oIN6Rs5hFPialUgeKbpjh16Zq6wcptjBpvg0CHq29+d456QUAJQ==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "ulT3XNgqIQSmpQFK5YEEwRq4RA6emXBKQHnZxtCz37SJin1PnVpH9m8vOAF9FmaWsgSAfjhhjIZDruMu1oKxSg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", diff --git a/Connectors/Autocad/Speckle.Connectors.Autocad2024/Speckle.Connectors.Autocad2024.csproj b/Connectors/Autocad/Speckle.Connectors.Autocad2024/Speckle.Connectors.Autocad2024.csproj index 6b2e10aea..9094b59b9 100644 --- a/Connectors/Autocad/Speckle.Connectors.Autocad2024/Speckle.Connectors.Autocad2024.csproj +++ b/Connectors/Autocad/Speckle.Connectors.Autocad2024/Speckle.Connectors.Autocad2024.csproj @@ -2,9 +2,12 @@ net48 x64 + true 2024 $(DefineConstants);AUTOCAD;AUTOCAD2024;AUTOCAD2022_OR_GREATER;AUTOCAD2023_OR_GREATER;AUTOCAD2024_OR_GREATER Debug;Release;Local + Program + $(ProgramW6432)\Autodesk\AutoCAD $(AutoCADVersion)\acad.exe diff --git a/Connectors/Autocad/Speckle.Connectors.Autocad2024/packages.lock.json b/Connectors/Autocad/Speckle.Connectors.Autocad2024/packages.lock.json index 487e222a2..49a789a5b 100644 --- a/Connectors/Autocad/Speckle.Connectors.Autocad2024/packages.lock.json +++ b/Connectors/Autocad/Speckle.Connectors.Autocad2024/packages.lock.json @@ -2,6 +2,15 @@ "version": 2, "dependencies": { ".NETFramework,Version=v4.8": { + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3" + } + }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -137,6 +146,11 @@ "System.Runtime.CompilerServices.Unsafe": "4.5.1" } }, + "Microsoft.NETFramework.ReferenceAssemblies.net48": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ==" + }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", @@ -260,8 +274,8 @@ "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )", - "Speckle.Sdk": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )", + "Speckle.Sdk": "[3.1.0-dev.162, )" } }, "speckle.connectors.dui": { @@ -269,7 +283,7 @@ "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", "Speckle.Connectors.Common": "[1.0.0, )", - "Speckle.Sdk": "[3.1.0-dev.145, )", + "Speckle.Sdk": "[3.1.0-dev.162, )", "System.Threading.Tasks.Dataflow": "[6.0.0, )" } }, @@ -295,7 +309,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )" } }, "Microsoft.Extensions.DependencyInjection": { @@ -333,18 +347,18 @@ }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "gpENXDB2i8SiXJZwrLVfB3BwKF1FHUSpPiw0BQ3Hl0V3821UWZjPYw5XK694oxJlVAeEMRvrMZMGsWOcK+3nMw==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "1fyaPjC1kVtroKfoXI7edJX0Uho/TNRWG9mtmIQ7jKhX1zElrJPSoH8zEIMqckZhGmwUrYjlQFPssASNCP721Q==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.145" + "Speckle.Sdk": "3.1.0-dev.162" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "YZLtitiVIxl12pNTXaB9gU+paNwFe++WBw6oIN6Rs5hFPialUgeKbpjh16Zq6wcptjBpvg0CHq29+d456QUAJQ==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "ulT3XNgqIQSmpQFK5YEEwRq4RA6emXBKQHnZxtCz37SJin1PnVpH9m8vOAF9FmaWsgSAfjhhjIZDruMu1oKxSg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", diff --git a/Connectors/Autocad/Speckle.Connectors.Autocad2025/Speckle.Connectors.Autocad2025.csproj b/Connectors/Autocad/Speckle.Connectors.Autocad2025/Speckle.Connectors.Autocad2025.csproj index f07bc0537..bb759038b 100644 --- a/Connectors/Autocad/Speckle.Connectors.Autocad2025/Speckle.Connectors.Autocad2025.csproj +++ b/Connectors/Autocad/Speckle.Connectors.Autocad2025/Speckle.Connectors.Autocad2025.csproj @@ -2,11 +2,14 @@ net8.0-windows x64 + true 2025 $(DefineConstants);AUTOCAD2025;AUTOCAD Debug;Release;Local true win-x64 + Program + $(ProgramW6432)\Autodesk\AutoCAD $(AutoCADVersion)\acad.exe diff --git a/Connectors/Autocad/Speckle.Connectors.Autocad2025/packages.lock.json b/Connectors/Autocad/Speckle.Connectors.Autocad2025/packages.lock.json index 5943d991f..4a022d9e4 100644 --- a/Connectors/Autocad/Speckle.Connectors.Autocad2025/packages.lock.json +++ b/Connectors/Autocad/Speckle.Connectors.Autocad2025/packages.lock.json @@ -2,6 +2,15 @@ "version": 2, "dependencies": { "net8.0-windows7.0": { + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3" + } + }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -136,6 +145,11 @@ "System.Runtime.CompilerServices.Unsafe": "4.5.1" } }, + "Microsoft.NETFramework.ReferenceAssemblies.net461": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA==" + }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", @@ -224,8 +238,8 @@ "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )", - "Speckle.Sdk": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )", + "Speckle.Sdk": "[3.1.0-dev.162, )" } }, "speckle.connectors.dui": { @@ -233,7 +247,7 @@ "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", "Speckle.Connectors.Common": "[1.0.0, )", - "Speckle.Sdk": "[3.1.0-dev.145, )", + "Speckle.Sdk": "[3.1.0-dev.162, )", "System.Threading.Tasks.Dataflow": "[6.0.0, )" } }, @@ -259,7 +273,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )" } }, "Microsoft.Extensions.DependencyInjection": { @@ -297,18 +311,18 @@ }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "gpENXDB2i8SiXJZwrLVfB3BwKF1FHUSpPiw0BQ3Hl0V3821UWZjPYw5XK694oxJlVAeEMRvrMZMGsWOcK+3nMw==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "1fyaPjC1kVtroKfoXI7edJX0Uho/TNRWG9mtmIQ7jKhX1zElrJPSoH8zEIMqckZhGmwUrYjlQFPssASNCP721Q==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.145" + "Speckle.Sdk": "3.1.0-dev.162" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "YZLtitiVIxl12pNTXaB9gU+paNwFe++WBw6oIN6Rs5hFPialUgeKbpjh16Zq6wcptjBpvg0CHq29+d456QUAJQ==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "ulT3XNgqIQSmpQFK5YEEwRq4RA6emXBKQHnZxtCz37SJin1PnVpH9m8vOAF9FmaWsgSAfjhhjIZDruMu1oKxSg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", diff --git a/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadBasicConnectorBinding.cs b/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadBasicConnectorBinding.cs index 0381cbac8..bf6a80b4c 100644 --- a/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadBasicConnectorBinding.cs +++ b/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadBasicConnectorBinding.cs @@ -37,10 +37,10 @@ ILogger logger _speckleApplication = speckleApplication; Commands = new BasicConnectorBindingCommands(parent); _store.DocumentChanged += (_, _) => - { - Commands.NotifyDocumentChanged(); - }; - + parent.TopLevelExceptionHandler.FireAndForget(async () => + { + await Commands.NotifyDocumentChanged().ConfigureAwait(false); + }); _logger = logger; } @@ -72,17 +72,17 @@ ILogger logger public void RemoveModel(ModelCard model) => _store.RemoveModel(model); - public void HighlightObjects(List objectIds) + public async Task HighlightObjects(IReadOnlyList 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); + await HighlightObjectsOnView(acadObjectIds).ConfigureAwait(false); } - public void HighlightModel(string modelCardId) + public async Task HighlightModel(string modelCardId) { // POC: Will be addressed to move it into AutocadContext! var doc = Application.DocumentManager.MdiActiveDocument; @@ -116,72 +116,78 @@ public void HighlightModel(string modelCardId) if (objectIds.Length == 0) { - Commands.SetModelError(modelCardId, new OperationCanceledException("No objects found to highlight.")); + await Commands + .SetModelError(modelCardId, new OperationCanceledException("No objects found to highlight.")) + .ConfigureAwait(false); return; } - HighlightObjectsOnView(objectIds, modelCardId); + await HighlightObjectsOnView(objectIds, modelCardId).ConfigureAwait(false); } - private void HighlightObjectsOnView(ObjectId[] objectIds, string? modelCardId = null) + private async Task HighlightObjectsOnView(ObjectId[] objectIds, string? modelCardId = null) { var doc = Application.DocumentManager.MdiActiveDocument; - Parent.RunOnMainThread(() => - { - try + await Parent + .RunOnMainThreadAsync(async () => { - doc.Editor.SetImpliedSelection(Array.Empty()); // 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) - { + doc.Editor.SetImpliedSelection(Array.Empty()); // Deselects try { - var entity = (Entity?)tr.GetObject(objectId, OpenMode.ForRead); - if (entity?.GeometricExtents != null) - { - selectedExtents.AddExtents(entity.GeometricExtents); - } + doc.Editor.SetImpliedSelection(objectIds); } 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. + // 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(); - doc.Editor.Zoom(selectedExtents); - tr.Commit(); - Autodesk.AutoCAD.Internal.Utils.FlushGraphics(); - } - catch (Exception ex) when (!ex.IsFatal()) - { - if (modelCardId != null) - { - Commands.SetModelError(modelCardId, new OperationCanceledException("Failed to highlight objects.")); + 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. + } + } + + doc.Editor.Zoom(selectedExtents); + tr.Commit(); + Autodesk.AutoCAD.Internal.Utils.FlushGraphics(); } - else + catch (Exception ex) when (!ex.IsFatal()) { - // 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; + if (modelCardId != null) + { + await Commands + .SetModelError(modelCardId, new OperationCanceledException("Failed to highlight objects.")) + .ConfigureAwait(false); + } + 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; + } } - } - }); + }) + .ConfigureAwait(false); } } diff --git a/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadReceiveBinding.cs b/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadReceiveBinding.cs index 27d6dd57b..d2d68a7fd 100644 --- a/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadReceiveBinding.cs +++ b/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadReceiveBinding.cs @@ -79,18 +79,14 @@ public async Task Receive(string modelCardId) .ServiceProvider.GetRequiredService() .Execute( modelCard.GetReceiveInfo(_speckleApplication.Slug), - cancellationToken, - (status, progress) => - _operationProgressManager.SetModelProgress( - Parent, - modelCardId, - new ModelCardProgress(modelCardId, status, progress), - cancellationToken - ) + _operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationToken), + cancellationToken ) .ConfigureAwait(false); - Commands.SetModelReceiveResult(modelCardId, operationResults.BakedObjectIds, operationResults.ConversionResults); + await Commands + .SetModelReceiveResult(modelCardId, operationResults.BakedObjectIds, operationResults.ConversionResults) + .ConfigureAwait(false); } catch (OperationCanceledException) { @@ -102,7 +98,7 @@ public async Task Receive(string modelCardId) catch (Exception ex) when (!ex.IsFatal()) // UX reasons - we will report operation exceptions as model card error. We may change this later when we have more exception documentation { _logger.LogModelCardHandledError(ex); - Commands.SetModelError(modelCardId, ex); + await Commands.SetModelError(modelCardId, ex).ConfigureAwait(false); } finally { diff --git a/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadSelectionBinding.cs b/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadSelectionBinding.cs index 2e7d789c7..0be4d5611 100644 --- a/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadSelectionBinding.cs +++ b/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadSelectionBinding.cs @@ -41,7 +41,9 @@ private void TryRegisterDocumentForSelection(Document? document) if (!_visitedDocuments.Contains(document)) { document.ImpliedSelectionChanged += (_, _) => - _topLevelExceptionHandler.CatchUnhandled(() => Parent.RunOnMainThread(OnSelectionChanged)); + _topLevelExceptionHandler.FireAndForget( + async () => await Parent.RunOnMainThreadAsync(OnSelectionChanged).ConfigureAwait(false) + ); _visitedDocuments.Add(document); } @@ -52,10 +54,10 @@ private void TryRegisterDocumentForSelection(Document? document) // Ui requests to GetSelection() should just return this local copy that is kept up to date by the event handler. private SelectionInfo _selectionInfo; - private void OnSelectionChanged() + private async Task OnSelectionChanged() { _selectionInfo = GetSelectionInternal(); - Parent.Send(SELECTION_EVENT, _selectionInfo); + await Parent.Send(SELECTION_EVENT, _selectionInfo).ConfigureAwait(false); } public SelectionInfo GetSelection() => _selectionInfo; diff --git a/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadSendBaseBinding.cs b/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadSendBaseBinding.cs new file mode 100644 index 000000000..dda2cca8e --- /dev/null +++ b/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadSendBaseBinding.cs @@ -0,0 +1,215 @@ +using System.Collections.Concurrent; +using Autodesk.AutoCAD.DatabaseServices; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Speckle.Connectors.Autocad.HostApp; +using Speckle.Connectors.Autocad.HostApp.Extensions; +using Speckle.Connectors.Autocad.Operations.Send; +using Speckle.Connectors.Common.Caching; +using Speckle.Connectors.Common.Cancellation; +using Speckle.Connectors.Common.Operations; +using Speckle.Connectors.DUI.Bindings; +using Speckle.Connectors.DUI.Bridge; +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.Sdk; +using Speckle.Sdk.Common; + +namespace Speckle.Connectors.Autocad.Bindings; + +public abstract class AutocadSendBaseBinding : ISendBinding +{ + public string Name => "sendBinding"; + public SendBindingUICommands Commands { get; } + private OperationProgressManager OperationProgressManager { get; } + public IBrowserBridge Parent { get; } + + private readonly DocumentModelStore _store; + private readonly IAutocadIdleManager _idleManager; + private readonly List _sendFilters; + private readonly CancellationManager _cancellationManager; + private readonly IServiceProvider _serviceProvider; + private readonly ISendConversionCache _sendConversionCache; + private readonly IOperationProgressManager _operationProgressManager; + private readonly ILogger _logger; + private readonly ITopLevelExceptionHandler _topLevelExceptionHandler; + private readonly ISpeckleApplication _speckleApplication; + + /// + /// 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: + /// [CNX-202: Unhandled Exception Occurred when receiving in Rhino](https://linear.app/speckle/issue/CNX-202/unhandled-exception-occurred-when-receiving-in-rhino) + /// As to why a concurrent dictionary, it's because it's the cheapest/easiest way to do so. + /// https://stackoverflow.com/questions/18922985/concurrent-hashsett-in-net-framework + /// + private ConcurrentDictionary ChangedObjectIds { get; set; } = new(); + + protected AutocadSendBaseBinding( + DocumentModelStore store, + IAutocadIdleManager idleManager, + IBrowserBridge parent, + IEnumerable sendFilters, + CancellationManager cancellationManager, + IServiceProvider serviceProvider, + ISendConversionCache sendConversionCache, + IOperationProgressManager operationProgressManager, + ILogger logger, + ISpeckleApplication speckleApplication + ) + { + _store = store; + _idleManager = idleManager; + _serviceProvider = serviceProvider; + _cancellationManager = cancellationManager; + _sendFilters = sendFilters.ToList(); + _sendConversionCache = sendConversionCache; + _operationProgressManager = operationProgressManager; + _logger = logger; + _speckleApplication = speckleApplication; + _topLevelExceptionHandler = parent.TopLevelExceptionHandler; + 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 + SubscribeToObjectChanges(Application.DocumentManager.CurrentDocument); + } + // Since ids of the objects generates from same seed, we should clear the cache always whenever doc swapped. + _store.DocumentChanged += (_, _) => + { + _sendConversionCache.ClearCache(); + }; + } + + private readonly List _docSubsTracker = new(); + + private void SubscribeToObjectChanges(Document doc) + { + if (doc == null || doc.Database == null || _docSubsTracker.Contains(doc.Name)) + { + return; + } + + _docSubsTracker.Add(doc.Name); + doc.Database.ObjectAppended += (_, e) => OnObjectChanged(e.DBObject); + doc.Database.ObjectErased += (_, e) => OnObjectChanged(e.DBObject); + doc.Database.ObjectModified += (_, e) => OnObjectChanged(e.DBObject); + } + + private void OnObjectChanged(DBObject dbObject) + { + _topLevelExceptionHandler.CatchUnhandled(() => OnChangeChangedObjectIds(dbObject)); + } + + private void OnChangeChangedObjectIds(DBObject dBObject) + { + ChangedObjectIds[dBObject.GetSpeckleApplicationId()] = 1; + _idleManager.SubscribeToIdle(nameof(AutocadSendBinding), RunExpirationChecks); + } + + private void RunExpirationChecks() + { + var senders = _store.GetSenders(); + string[] objectIdsList = ChangedObjectIds.Keys.ToArray(); + List expiredSenderIds = new(); + + _sendConversionCache.EvictObjects(objectIdsList); + + foreach (SenderModelCard modelCard in senders) + { + var intersection = modelCard.SendFilter.NotNull().GetObjectIds().Intersect(objectIdsList).ToList(); + bool isExpired = intersection.Count != 0; + if (isExpired) + { + expiredSenderIds.Add(modelCard.ModelCardId.NotNull()); + } + } + + Commands.SetModelsExpired(expiredSenderIds); + ChangedObjectIds = new(); + } + + public List GetSendFilters() => _sendFilters; + + public List GetSendSettings() => []; + + public async Task Send(string modelCardId) => + await Parent + .RunOnMainThreadAsync(async () => await SendInternal(modelCardId).ConfigureAwait(false)) + .ConfigureAwait(false); + + protected abstract void InitializeSettings(IServiceProvider serviceProvider); + + private async Task SendInternal(string modelCardId) + { + try + { + if (_store.GetModelById(modelCardId) is not SenderModelCard modelCard) + { + // Handle as GLOBAL ERROR at BrowserBridge + throw new InvalidOperationException("No publish model card was found."); + } + + using var scope = _serviceProvider.CreateScope(); + InitializeSettings(scope.ServiceProvider); + + CancellationToken cancellationToken = _cancellationManager.InitCancellationTokenSource(modelCardId); + + // Disable document activation (document creation and document switch) + // Not disabling results in DUI model card being out of sync with the active document + // The DocumentActivated event isn't usable probably because it is pushed to back of main thread queue + Application.DocumentManager.DocumentActivationEnabled = false; + + // Get elements to convert + List autocadObjects = Application.DocumentManager.CurrentDocument.GetObjects( + modelCard.SendFilter.NotNull().GetObjectIds() + ); + + if (autocadObjects.Count == 0) + { + // Handle as CARD ERROR in this function + throw new SpeckleSendFilterException("No objects were found to convert. Please update your publish filter!"); + } + + var sendResult = await scope + .ServiceProvider.GetRequiredService>() + .Execute( + autocadObjects, + modelCard.GetSendInfo(_speckleApplication.Slug), + _operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationToken), + cancellationToken + ) + .ConfigureAwait(false); + + await Commands + .SetModelSendResult(modelCardId, sendResult.RootObjId, sendResult.ConversionResults) + .ConfigureAwait(false); + } + catch (OperationCanceledException) + { + // SWALLOW -> UI handles it immediately, so we do not need to handle anything for now! + // Idea for later -> when cancel called, create promise from UI to solve it later with this catch block. + // So have 3 state on UI -> Cancellation clicked -> Cancelling -> Cancelled + return; + } + catch (Exception ex) when (!ex.IsFatal()) // UX reasons - we will report operation exceptions as model card error. We may change this later when we have more exception documentation + { + _logger.LogModelCardHandledError(ex); + await Commands.SetModelError(modelCardId, ex).ConfigureAwait(false); + } + finally + { + // renable document activation + Application.DocumentManager.DocumentActivationEnabled = true; + } + } + + public void CancelSend(string modelCardId) => _cancellationManager.CancelOperation(modelCardId); +} diff --git a/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadSendBinding.cs b/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadSendBinding.cs index 6856f58b6..bc2a28a96 100644 --- a/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadSendBinding.cs +++ b/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadSendBinding.cs @@ -1,54 +1,21 @@ -using System.Collections.Concurrent; -using Autodesk.AutoCAD.DatabaseServices; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Speckle.Connectors.Autocad.HostApp; -using Speckle.Connectors.Autocad.HostApp.Extensions; -using Speckle.Connectors.Autocad.Operations.Send; using Speckle.Connectors.Common.Caching; using Speckle.Connectors.Common.Cancellation; -using Speckle.Connectors.Common.Operations; using Speckle.Connectors.DUI.Bindings; using Speckle.Connectors.DUI.Bridge; -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.Converters.Autocad; using Speckle.Converters.Common; using Speckle.Sdk; -using Speckle.Sdk.Common; namespace Speckle.Connectors.Autocad.Bindings; -public sealed class AutocadSendBinding : ISendBinding +public sealed class AutocadSendBinding : AutocadSendBaseBinding { - public string Name => "sendBinding"; - public SendBindingUICommands Commands { get; } - private OperationProgressManager OperationProgressManager { get; } - public IBrowserBridge Parent { get; } - - private readonly DocumentModelStore _store; - private readonly IAutocadIdleManager _idleManager; - private readonly List _sendFilters; - private readonly CancellationManager _cancellationManager; - private readonly IServiceProvider _serviceProvider; - private readonly ISendConversionCache _sendConversionCache; - private readonly IOperationProgressManager _operationProgressManager; - private readonly ILogger _logger; - private readonly ITopLevelExceptionHandler _topLevelExceptionHandler; private readonly IAutocadConversionSettingsFactory _autocadConversionSettingsFactory; - private readonly ISpeckleApplication _speckleApplication; - - /// - /// 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: - /// [CNX-202: Unhandled Exception Occurred when receiving in Rhino](https://linear.app/speckle/issue/CNX-202/unhandled-exception-occurred-when-receiving-in-rhino) - /// As to why a concurrent dictionary, it's because it's the cheapest/easiest way to do so. - /// https://stackoverflow.com/questions/18922985/concurrent-hashsett-in-net-framework - /// - private ConcurrentDictionary ChangedObjectIds { get; set; } = new(); public AutocadSendBinding( DocumentModelStore store, @@ -63,163 +30,26 @@ public AutocadSendBinding( IAutocadConversionSettingsFactory autocadConversionSettingsFactory, ISpeckleApplication speckleApplication ) + : base( + store, + idleManager, + parent, + sendFilters, + cancellationManager, + serviceProvider, + sendConversionCache, + operationProgressManager, + logger, + speckleApplication + ) { - _store = store; - _idleManager = idleManager; - _serviceProvider = serviceProvider; - _cancellationManager = cancellationManager; - _sendFilters = sendFilters.ToList(); - _sendConversionCache = sendConversionCache; - _operationProgressManager = operationProgressManager; - _logger = logger; _autocadConversionSettingsFactory = autocadConversionSettingsFactory; - _speckleApplication = speckleApplication; - _topLevelExceptionHandler = parent.TopLevelExceptionHandler; - 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 - SubscribeToObjectChanges(Application.DocumentManager.CurrentDocument); - } - // Since ids of the objects generates from same seed, we should clear the cache always whenever doc swapped. - _store.DocumentChanged += (_, _) => - { - _sendConversionCache.ClearCache(); - }; - } - - private readonly List _docSubsTracker = new(); - - private void SubscribeToObjectChanges(Document doc) - { - if (doc == null || doc.Database == null || _docSubsTracker.Contains(doc.Name)) - { - return; - } - - _docSubsTracker.Add(doc.Name); - doc.Database.ObjectAppended += (_, e) => OnObjectChanged(e.DBObject); - doc.Database.ObjectErased += (_, e) => OnObjectChanged(e.DBObject); - doc.Database.ObjectModified += (_, e) => OnObjectChanged(e.DBObject); - } - - private void OnObjectChanged(DBObject dbObject) - { - _topLevelExceptionHandler.CatchUnhandled(() => OnChangeChangedObjectIds(dbObject)); - } - - private void OnChangeChangedObjectIds(DBObject dBObject) - { - ChangedObjectIds[dBObject.GetSpeckleApplicationId()] = 1; - _idleManager.SubscribeToIdle(nameof(AutocadSendBinding), RunExpirationChecks); - } - - private void RunExpirationChecks() - { - var senders = _store.GetSenders(); - string[] objectIdsList = ChangedObjectIds.Keys.ToArray(); - List expiredSenderIds = new(); - - _sendConversionCache.EvictObjects(objectIdsList); - - foreach (SenderModelCard modelCard in senders) - { - var intersection = modelCard.SendFilter.NotNull().GetObjectIds().Intersect(objectIdsList).ToList(); - bool isExpired = intersection.Count != 0; - if (isExpired) - { - expiredSenderIds.Add(modelCard.ModelCardId.NotNull()); - } - } - - Commands.SetModelsExpired(expiredSenderIds); - ChangedObjectIds = new(); } - public List GetSendFilters() => _sendFilters; - - public List GetSendSettings() => []; - - public Task Send(string modelCardId) + protected override void InitializeSettings(IServiceProvider serviceProvider) { - Parent.RunOnMainThread(async () => await SendInternal(modelCardId).ConfigureAwait(false)); - return Task.CompletedTask; + serviceProvider + .GetRequiredService>() + .Initialize(_autocadConversionSettingsFactory.Create(Application.DocumentManager.CurrentDocument)); } - - private async Task SendInternal(string modelCardId) - { - try - { - if (_store.GetModelById(modelCardId) is not SenderModelCard modelCard) - { - // Handle as GLOBAL ERROR at BrowserBridge - throw new InvalidOperationException("No publish model card was found."); - } - - using var scope = _serviceProvider.CreateScope(); - scope - .ServiceProvider.GetRequiredService>() - .Initialize(_autocadConversionSettingsFactory.Create(Application.DocumentManager.CurrentDocument)); - - CancellationToken cancellationToken = _cancellationManager.InitCancellationTokenSource(modelCardId); - - // Disable document activation (document creation and document switch) - // Not disabling results in DUI model card being out of sync with the active document - // The DocumentActivated event isn't usable probably because it is pushed to back of main thread queue - Application.DocumentManager.DocumentActivationEnabled = false; - - // Get elements to convert - List autocadObjects = Application.DocumentManager.CurrentDocument.GetObjects( - modelCard.SendFilter.NotNull().GetObjectIds() - ); - - if (autocadObjects.Count == 0) - { - // Handle as CARD ERROR in this function - throw new SpeckleSendFilterException("No objects were found to convert. Please update your publish filter!"); - } - - var sendResult = await scope - .ServiceProvider.GetRequiredService>() - .Execute( - autocadObjects, - modelCard.GetSendInfo(_speckleApplication.Slug), - (status, progress) => - _operationProgressManager.SetModelProgress( - Parent, - modelCardId, - new ModelCardProgress(modelCardId, status, progress), - cancellationToken - ), - cancellationToken - ) - .ConfigureAwait(false); - - Commands.SetModelSendResult(modelCardId, sendResult.RootObjId, sendResult.ConversionResults); - } - catch (OperationCanceledException) - { - // SWALLOW -> UI handles it immediately, so we do not need to handle anything for now! - // Idea for later -> when cancel called, create promise from UI to solve it later with this catch block. - // So have 3 state on UI -> Cancellation clicked -> Cancelling -> Cancelled - return; - } - catch (Exception ex) when (!ex.IsFatal()) // UX reasons - we will report operation exceptions as model card error. We may change this later when we have more exception documentation - { - _logger.LogModelCardHandledError(ex); - Commands.SetModelError(modelCardId, ex); - } - finally - { - // renable document activation - Application.DocumentManager.DocumentActivationEnabled = true; - } - } - - public void CancelSend(string modelCardId) => _cancellationManager.CancelOperation(modelCardId); } diff --git a/Connectors/Autocad/Speckle.Connectors.AutocadShared/DependencyInjection/AutocadConnectorModule.cs b/Connectors/Autocad/Speckle.Connectors.AutocadShared/DependencyInjection/AutocadConnectorModule.cs index 1b5760727..6e199726f 100644 --- a/Connectors/Autocad/Speckle.Connectors.AutocadShared/DependencyInjection/AutocadConnectorModule.cs +++ b/Connectors/Autocad/Speckle.Connectors.AutocadShared/DependencyInjection/AutocadConnectorModule.cs @@ -1,5 +1,11 @@ #if AUTOCAD +using System.Reflection; using Microsoft.Extensions.DependencyInjection; +using Speckle.Connectors.Autocad.Bindings; +using Speckle.Connectors.Autocad.Operations.Send; +using Speckle.Connectors.Common.Builders; +using Speckle.Connectors.DUI.Bindings; +using Speckle.Sdk; namespace Speckle.Connectors.Autocad.DependencyInjection; @@ -9,9 +15,16 @@ public static void AddAutocad(this IServiceCollection serviceCollection) { serviceCollection.AddAutocadBase(); - // Operations + // Send serviceCollection.LoadSend(); + serviceCollection.AddScoped, AutocadRootObjectBuilder>(); + + // Receive serviceCollection.LoadReceive(); + + // Register vertical specific bindings + serviceCollection.AddSingleton(); + serviceCollection.AddMatchingInterfacesAsTransient(Assembly.GetExecutingAssembly()); } } #endif diff --git a/Connectors/Autocad/Speckle.Connectors.AutocadShared/DependencyInjection/Civil3dConnectorModule.cs b/Connectors/Autocad/Speckle.Connectors.AutocadShared/DependencyInjection/Civil3dConnectorModule.cs index 3cfd3a5cb..b7e3b6ec0 100644 --- a/Connectors/Autocad/Speckle.Connectors.AutocadShared/DependencyInjection/Civil3dConnectorModule.cs +++ b/Connectors/Autocad/Speckle.Connectors.AutocadShared/DependencyInjection/Civil3dConnectorModule.cs @@ -1,5 +1,7 @@ #if CIVIL3D using Microsoft.Extensions.DependencyInjection; +using Speckle.Connectors.Autocad.Operations.Send; +using Speckle.Connectors.Common.Builders; namespace Speckle.Connectors.Autocad.DependencyInjection; @@ -8,7 +10,10 @@ public static class Civil3dConnectorModule public static void AddCivil3d(this IServiceCollection serviceCollection) { serviceCollection.AddAutocadBase(); + + // send serviceCollection.LoadSend(); + serviceCollection.AddScoped, Civil3dRootObjectBuilder>(); } } #endif diff --git a/Connectors/Autocad/Speckle.Connectors.AutocadShared/DependencyInjection/SharedRegistration.cs b/Connectors/Autocad/Speckle.Connectors.AutocadShared/DependencyInjection/SharedRegistration.cs index 762810747..865963b20 100644 --- a/Connectors/Autocad/Speckle.Connectors.AutocadShared/DependencyInjection/SharedRegistration.cs +++ b/Connectors/Autocad/Speckle.Connectors.AutocadShared/DependencyInjection/SharedRegistration.cs @@ -12,6 +12,7 @@ using Speckle.Connectors.Common.Operations; 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; @@ -49,7 +50,7 @@ public static void AddAutocadBase(this IServiceCollection serviceCollection) serviceCollection.AddScoped(); serviceCollection.AddScoped(); - serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); // operation progress manager serviceCollection.AddSingleton(); @@ -70,12 +71,6 @@ public static void LoadSend(this IServiceCollection serviceCollection) // Operations serviceCollection.AddScoped>(); - // Object Builders - serviceCollection.AddScoped, AutocadRootObjectBuilder>(); - - // Register bindings - serviceCollection.AddSingleton(); - // register send filters serviceCollection.AddTransient(); diff --git a/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadColorBaker.cs b/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadColorBaker.cs index c85986493..531ded14e 100644 --- a/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadColorBaker.cs +++ b/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadColorBaker.cs @@ -1,5 +1,6 @@ using Autodesk.AutoCAD.Colors; using Microsoft.Extensions.Logging; +using Speckle.Connectors.Common.Operations; using Speckle.Sdk; using Speckle.Sdk.Models.Proxies; using AutocadColor = Autodesk.AutoCAD.Colors.Color; @@ -28,14 +29,17 @@ public AutocadColorBaker(ILogger logger) /// /// /// - public void ParseColors(List colorProxies, Action? onOperationProgressed) + public async Task ParseColors( + IReadOnlyCollection colorProxies, + IProgress onOperationProgressed + ) { var count = 0; foreach (ColorProxy colorProxy in colorProxies) { try { - onOperationProgressed?.Invoke("Converting colors", (double)++count / colorProxies.Count); + onOperationProgressed.Report(new("Converting colors", (double)++count / colorProxies.Count)); // skip any colors with source = layer, since object color default source is by layer if (colorProxy["source"] is string source && source == "layer") @@ -60,6 +64,8 @@ public void ParseColors(List colorProxies, Action? { _logger.LogError(ex, "Failed parsing color proxy"); } + + await Task.Yield(); } } diff --git a/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadIdleManager.cs b/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadIdleManager.cs index d745e36bc..10ebddb6f 100644 --- a/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadIdleManager.cs +++ b/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadIdleManager.cs @@ -3,23 +3,20 @@ namespace Speckle.Connectors.Autocad.HostApp; +public partial interface IAutocadIdleManager : IAppIdleManager; + [GenerateAutoInterface] -public class AutocadIdleManager(IIdleCallManager idleCallManager) : IAutocadIdleManager +public sealed class AutocadIdleManager(IIdleCallManager idleCallManager) + : AppIdleManager(idleCallManager), + IAutocadIdleManager { - /// - /// Subscribe deferred action to AutocadIdle event to run it whenever Autocad become idle. - /// - /// Action to call whenever Autocad become Idle. - public void SubscribeToIdle(string id, Action action) => - idleCallManager.SubscribeToIdle( - id, - action, - () => - { - Application.Idle += AutocadAppOnIdle; - } - ); + private readonly IIdleCallManager _idleCallManager = idleCallManager; + + protected override void AddEvent() + { + Application.Idle += AutocadAppOnIdle; + } private void AutocadAppOnIdle(object? sender, EventArgs e) => - idleCallManager.AppOnIdle(() => Application.Idle -= AutocadAppOnIdle); + _idleCallManager.AppOnIdle(() => Application.Idle -= AutocadAppOnIdle); } diff --git a/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadInstanceBaker.cs b/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadInstanceBaker.cs index 080535164..35d44abe0 100644 --- a/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadInstanceBaker.cs +++ b/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadInstanceBaker.cs @@ -1,14 +1,17 @@ +using System.Diagnostics.CodeAnalysis; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.Geometry; using Microsoft.Extensions.Logging; using Speckle.Connectors.Autocad.HostApp.Extensions; using Speckle.Connectors.Common.Conversion; using Speckle.Connectors.Common.Instances; +using Speckle.Connectors.Common.Operations; using Speckle.Converters.Autocad; using Speckle.Converters.Common; using Speckle.DoubleNumerics; using Speckle.Sdk; using Speckle.Sdk.Common; +using Speckle.Sdk.Common.Exceptions; using Speckle.Sdk.Models; using Speckle.Sdk.Models.Collections; using Speckle.Sdk.Models.Instances; @@ -45,11 +48,12 @@ IConverterSettingsStore converterSettings _converterSettings = converterSettings; } - public BakeResult BakeInstances( - List<(Collection[] collectionPath, IInstanceComponent obj)> instanceComponents, + [SuppressMessage("Maintainability", "CA1506:Avoid excessive class coupling")] + public async Task BakeInstances( + IReadOnlyCollection<(Collection[] collectionPath, IInstanceComponent obj)> instanceComponents, Dictionary> applicationIdMap, string baseLayerName, - Action? onOperationProgressed + IProgress onOperationProgressed ) { var sortedInstanceComponents = instanceComponents @@ -69,7 +73,8 @@ public BakeResult BakeInstances( { try { - onOperationProgressed?.Invoke("Converting blocks", (double)++count / sortedInstanceComponents.Count); + onOperationProgressed.Report(new("Converting blocks", (double)++count / sortedInstanceComponents.Count)); + if (instanceOrDefinition is InstanceDefinitionProxy { applicationId: not null } definitionProxy) { // TODO: create definition (block table record) @@ -162,6 +167,7 @@ instanceOrDefinition is InstanceProxy instanceProxy } transaction.Commit(); + await Task.Yield(); return new(createdObjectIds, consumedObjectIds, conversionResults); } diff --git a/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadLayerUnpacker.cs b/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadLayerUnpacker.cs index 9905f4257..cad1c727e 100644 --- a/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadLayerUnpacker.cs +++ b/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadLayerUnpacker.cs @@ -1,4 +1,4 @@ -using Autodesk.AutoCAD.DatabaseServices; +using Autodesk.AutoCAD.DatabaseServices; using Speckle.Connectors.Autocad.HostApp.Extensions; using Speckle.Converters.Common; using Speckle.Sdk.Models.Collections; @@ -19,9 +19,6 @@ public Layer GetOrCreateSpeckleLayer(Entity entity, Transaction tr, out LayerTab } if (tr.GetObject(entity.LayerId, OpenMode.ForRead) is LayerTableRecord autocadLayer) { - // Layers and geometries can have same application ids..... - // We should prevent it for sketchup converter. Because when it happens "objects_to_bake" definition - // is changing on the way if it happens. speckleLayer = new Layer(layerName) { applicationId = autocadLayer.GetSpeckleApplicationId() }; // Do not use handle directly, see note in the 'GetSpeckleApplicationId' method _layerCollectionCache[layerName] = speckleLayer; layer = autocadLayer; diff --git a/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadMaterialBaker.cs b/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadMaterialBaker.cs index 086ae3a1c..4aea349fb 100644 --- a/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadMaterialBaker.cs +++ b/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/AutocadMaterialBaker.cs @@ -3,6 +3,7 @@ using Autodesk.AutoCAD.GraphicsInterface; using Microsoft.Extensions.Logging; using Speckle.Connectors.Common.Conversion; +using Speckle.Connectors.Common.Operations; using Speckle.Objects.Other; using Speckle.Sdk; using Speckle.Sdk.Models; @@ -90,10 +91,10 @@ public void PurgeMaterials(string namePrefix) transaction.Commit(); } - public void ParseAndBakeRenderMaterials( + public async Task ParseAndBakeRenderMaterials( List materialProxies, string baseLayerPrefix, - Action? onOperationProgressed + IProgress onOperationProgressed ) { using var transaction = Application.DocumentManager.CurrentDocument.Database.TransactionManager.StartTransaction(); @@ -109,7 +110,7 @@ public void ParseAndBakeRenderMaterials( var count = 0; foreach (RenderMaterialProxy materialProxy in materialProxies) { - onOperationProgressed?.Invoke("Converting render materials", (double)++count / materialProxies.Count); + onOperationProgressed.Report(new("Converting render materials", (double)++count / materialProxies.Count)); // bake render material RenderMaterial renderMaterial = materialProxy.value; @@ -139,6 +140,7 @@ public void ParseAndBakeRenderMaterials( } transaction.Commit(); + await Task.Yield(); } private (ObjectId, ReceiveConversionResult) BakeMaterial( diff --git a/Connectors/Autocad/Speckle.Connectors.AutocadShared/Operations/Receive/AutocadHostObjectBuilder.cs b/Connectors/Autocad/Speckle.Connectors.AutocadShared/Operations/Receive/AutocadHostObjectBuilder.cs index 6b3b1129d..c9bdc4ee9 100644 --- a/Connectors/Autocad/Speckle.Connectors.AutocadShared/Operations/Receive/AutocadHostObjectBuilder.cs +++ b/Connectors/Autocad/Speckle.Connectors.AutocadShared/Operations/Receive/AutocadHostObjectBuilder.cs @@ -52,118 +52,127 @@ RootObjectUnpacker rootObjectUnpacker _rootObjectUnpacker = rootObjectUnpacker; } - public Task Build( + public async Task Build( Base rootObject, string projectName, string modelName, - Action? onOperationProgressed, + IProgress onOperationProgressed, CancellationToken _ - ) => + ) + { // NOTE: This is the only place we apply ISyncToThread across connectors. We need to sync up with main thread here // after GetObject and Deserialization. It is anti-pattern now. Happiness level 3/10 but works. - _syncToThread.RunOnThread(() => - { - // Prompt the UI conversion started. Progress bar will swoosh. - onOperationProgressed?.Invoke("Converting", null); + return await _syncToThread + .RunOnThread( + async () => await BuildImpl(rootObject, projectName, modelName, onOperationProgressed).ConfigureAwait(false) + ) + .ConfigureAwait(false); + } + + private async Task BuildImpl( + Base rootObject, + string projectName, + string modelName, + IProgress onOperationProgressed + ) + { + // Prompt the UI conversion started. Progress bar will swoosh. + onOperationProgressed.Report(new("Converting", null)); + + // Layer filter for received commit with project and model name + _layerBaker.CreateLayerFilter(projectName, modelName); - // Layer filter for received commit with project and model name - _layerBaker.CreateLayerFilter(projectName, modelName); + // 0 - Clean then Rock n Roll! + string baseLayerPrefix = _autocadContext.RemoveInvalidChars($"SPK-{projectName}-{modelName}-"); + PreReceiveDeepClean(baseLayerPrefix); - // 0 - Clean then Rock n Roll! - string baseLayerPrefix = _autocadContext.RemoveInvalidChars($"SPK-{projectName}-{modelName}-"); - PreReceiveDeepClean(baseLayerPrefix); + // 1 - Unpack objects and proxies from root commit object + var unpackedRoot = _rootObjectUnpacker.Unpack(rootObject); - // 1 - Unpack objects and proxies from root commit object - var unpackedRoot = _rootObjectUnpacker.Unpack(rootObject); + // 2 - Split atomic objects and instance components with their path + var (atomicObjects, instanceComponents) = _rootObjectUnpacker.SplitAtomicObjectsAndInstances( + unpackedRoot.ObjectsToConvert + ); + var atomicObjectsWithPath = _layerBaker.GetAtomicObjectsWithPath(atomicObjects); + var instanceComponentsWithPath = _layerBaker.GetInstanceComponentsWithPath(instanceComponents); - // 2 - Split atomic objects and instance components with their path - var (atomicObjects, instanceComponents) = _rootObjectUnpacker.SplitAtomicObjectsAndInstances( - unpackedRoot.ObjectsToConvert + // POC: these are not captured by traversal, so we need to re-add them here + if (unpackedRoot.DefinitionProxies != null && unpackedRoot.DefinitionProxies.Count > 0) + { + var transformed = unpackedRoot.DefinitionProxies.Select(proxy => + (Array.Empty(), proxy as IInstanceComponent) ); - var atomicObjectsWithPath = _layerBaker.GetAtomicObjectsWithPath(atomicObjects); - var instanceComponentsWithPath = _layerBaker.GetInstanceComponentsWithPath(instanceComponents); + instanceComponentsWithPath.AddRange(transformed); + } - // POC: these are not captured by traversal, so we need to re-add them here - if (unpackedRoot.DefinitionProxies != null && unpackedRoot.DefinitionProxies.Count > 0) - { - var transformed = unpackedRoot.DefinitionProxies.Select(proxy => - (Array.Empty(), proxy as IInstanceComponent) - ); - instanceComponentsWithPath.AddRange(transformed); - } + // 3 - Bake materials and colors, as they are used later down the line by layers and objects + if (unpackedRoot.RenderMaterialProxies != null) + { + await _materialBaker + .ParseAndBakeRenderMaterials(unpackedRoot.RenderMaterialProxies, baseLayerPrefix, onOperationProgressed) + .ConfigureAwait(true); + } - // 3 - Bake materials and colors, as they are used later down the line by layers and objects - if (unpackedRoot.RenderMaterialProxies != null) + if (unpackedRoot.ColorProxies != null) + { + await _colorBaker.ParseColors(unpackedRoot.ColorProxies, onOperationProgressed).ConfigureAwait(true); + } + + // 5 - Convert atomic objects + List results = new(); + List bakedObjectIds = new(); + Dictionary> applicationIdMap = new(); + var count = 0; + foreach (var (layerPath, atomicObject) in atomicObjectsWithPath) + { + string objectId = atomicObject.applicationId ?? atomicObject.id; + onOperationProgressed.Report(new("Converting objects", (double)++count / atomicObjects.Count)); + try { - _materialBaker.ParseAndBakeRenderMaterials( - unpackedRoot.RenderMaterialProxies, - baseLayerPrefix, - onOperationProgressed + List convertedObjects = await ConvertObject(atomicObject, layerPath, baseLayerPrefix) + .ConfigureAwait(true); + + applicationIdMap[objectId] = convertedObjects; + + results.AddRange( + convertedObjects.Select(e => new ReceiveConversionResult( + Status.SUCCESS, + atomicObject, + e.GetSpeckleApplicationId(), + e.GetType().ToString() + )) ); - } - if (unpackedRoot.ColorProxies != null) - { - _colorBaker.ParseColors(unpackedRoot.ColorProxies, onOperationProgressed); + bakedObjectIds.AddRange(convertedObjects.Select(e => e.GetSpeckleApplicationId())); } - - // 5 - Convert atomic objects - List results = new(); - List bakedObjectIds = new(); - Dictionary> applicationIdMap = new(); - var count = 0; - foreach (var (layerPath, atomicObject) in atomicObjectsWithPath) + catch (Exception ex) when (!ex.IsFatal()) { - string objectId = atomicObject.applicationId ?? atomicObject.id; - onOperationProgressed?.Invoke("Converting objects", (double)++count / atomicObjects.Count); - try - { - List convertedObjects = ConvertObject(atomicObject, layerPath, baseLayerPrefix).ToList(); - - applicationIdMap[objectId] = convertedObjects; - - results.AddRange( - convertedObjects.Select(e => new ReceiveConversionResult( - Status.SUCCESS, - atomicObject, - e.GetSpeckleApplicationId(), - e.GetType().ToString() - )) - ); - - bakedObjectIds.AddRange(convertedObjects.Select(e => e.GetSpeckleApplicationId())); - } - catch (Exception ex) when (!ex.IsFatal()) - { - results.Add(new(Status.ERROR, atomicObject, null, null, ex)); - } + results.Add(new(Status.ERROR, atomicObject, null, null, ex)); } + } - // 6 - Convert instances - var (createdInstanceIds, consumedObjectIds, instanceConversionResults) = _instanceBaker.BakeInstances( - instanceComponentsWithPath, - applicationIdMap, - baseLayerPrefix, - onOperationProgressed - ); + // 6 - Convert instances + var (createdInstanceIds, consumedObjectIds, instanceConversionResults) = await _instanceBaker + .BakeInstances(instanceComponentsWithPath, applicationIdMap, baseLayerPrefix, onOperationProgressed) + .ConfigureAwait(true); - bakedObjectIds.RemoveAll(id => consumedObjectIds.Contains(id)); - bakedObjectIds.AddRange(createdInstanceIds); - results.RemoveAll(result => result.ResultId != null && consumedObjectIds.Contains(result.ResultId)); - results.AddRange(instanceConversionResults); + bakedObjectIds.RemoveAll(id => consumedObjectIds.Contains(id)); + bakedObjectIds.AddRange(createdInstanceIds); + results.RemoveAll(result => result.ResultId != null && consumedObjectIds.Contains(result.ResultId)); + results.AddRange(instanceConversionResults); - // 7 - Create groups - if (unpackedRoot.GroupProxies != null) - { - List groupResults = _groupBaker.CreateGroups( - unpackedRoot.GroupProxies, - applicationIdMap - ); - results.AddRange(groupResults); - } + // 7 - Create groups + if (unpackedRoot.GroupProxies != null) + { + List groupResults = _groupBaker.CreateGroups( + unpackedRoot.GroupProxies, + applicationIdMap + ); + results.AddRange(groupResults); + } - return new HostObjectBuilderResult(bakedObjectIds, results); - }); + return new HostObjectBuilderResult(bakedObjectIds, results); + } private void PreReceiveDeepClean(string baseLayerPrefix) { @@ -172,7 +181,7 @@ private void PreReceiveDeepClean(string baseLayerPrefix) _materialBaker.PurgeMaterials(baseLayerPrefix); } - private IEnumerable ConvertObject(Base obj, Collection[] layerPath, string baseLayerNamePrefix) + private async Task> ConvertObject(Base obj, Collection[] layerPath, string baseLayerNamePrefix) { string layerName = _layerBaker.CreateLayerForReceive(layerPath, baseLayerNamePrefix); var convertedEntities = new List(); @@ -195,6 +204,7 @@ private IEnumerable ConvertObject(Base obj, Collection[] layerPath, stri } tr.Commit(); + await Task.Delay(10).ConfigureAwait(true); return convertedEntities; } diff --git a/Connectors/Autocad/Speckle.Connectors.AutocadShared/Operations/Send/AutocadRootObjectBaseBuilder.cs b/Connectors/Autocad/Speckle.Connectors.AutocadShared/Operations/Send/AutocadRootObjectBaseBuilder.cs new file mode 100644 index 000000000..3db8fe68f --- /dev/null +++ b/Connectors/Autocad/Speckle.Connectors.AutocadShared/Operations/Send/AutocadRootObjectBaseBuilder.cs @@ -0,0 +1,192 @@ +using System.Diagnostics.CodeAnalysis; +using Autodesk.AutoCAD.DatabaseServices; +using Microsoft.Extensions.Logging; +using Speckle.Connectors.Autocad.HostApp; +using Speckle.Connectors.Common.Builders; +using Speckle.Connectors.Common.Caching; +using Speckle.Connectors.Common.Conversion; +using Speckle.Connectors.Common.Extensions; +using Speckle.Connectors.Common.Operations; +using Speckle.Converters.Common; +using Speckle.Sdk; +using Speckle.Sdk.Logging; +using Speckle.Sdk.Models; +using Speckle.Sdk.Models.Collections; +using Speckle.Sdk.Models.Instances; + +namespace Speckle.Connectors.Autocad.Operations.Send; + +public abstract class AutocadRootObjectBaseBuilder : IRootObjectBuilder +{ + private readonly IRootToSpeckleConverter _converter; + private readonly string[] _documentPathSeparator = ["\\"]; + private readonly ISendConversionCache _sendConversionCache; + private readonly AutocadInstanceUnpacker _instanceUnpacker; + private readonly AutocadMaterialUnpacker _materialUnpacker; + private readonly AutocadColorUnpacker _colorUnpacker; + private readonly AutocadGroupUnpacker _groupUnpacker; + private readonly ILogger _logger; + private readonly ISdkActivityFactory _activityFactory; + + protected AutocadRootObjectBaseBuilder( + IRootToSpeckleConverter converter, + ISendConversionCache sendConversionCache, + AutocadInstanceUnpacker instanceObjectManager, + AutocadMaterialUnpacker materialUnpacker, + AutocadColorUnpacker colorUnpacker, + AutocadGroupUnpacker groupUnpacker, + ILogger logger, + ISdkActivityFactory activityFactory + ) + { + _converter = converter; + _sendConversionCache = sendConversionCache; + _instanceUnpacker = instanceObjectManager; + _materialUnpacker = materialUnpacker; + _colorUnpacker = colorUnpacker; + _groupUnpacker = groupUnpacker; + _logger = logger; + _activityFactory = activityFactory; + } + + public Task Build( + IReadOnlyList objects, + SendInfo sendInfo, + IProgress onOperationProgressed, + CancellationToken ct = default + ) => Task.FromResult(BuildSync(objects, sendInfo, onOperationProgressed, ct)); + + [SuppressMessage( + "Maintainability", + "CA1506:Avoid excessive class coupling", + Justification = """ + It is already simplified but has many different references since it is a builder. Do not know can we simplify it now. + Later we might consider to refactor proxies from one proxy manager? but we do not know the shape of it all potential + proxy classes yet. So I'm supressing this one now!!! + """ + )] + private RootObjectBuilderResult BuildSync( + IReadOnlyList objects, + SendInfo sendInfo, + IProgress onOperationProgressed, + CancellationToken ct = default + ) + { + // 0 - Init the root + Collection root = + new() + { + name = Application + .DocumentManager.CurrentDocument.Name // POC: https://spockle.atlassian.net/browse/CNX-9319 + .Split(_documentPathSeparator, StringSplitOptions.None) + .Reverse() + .First() + }; + + // TODO: better handling for document and transactions!! + Document doc = Application.DocumentManager.CurrentDocument; + using Transaction tr = doc.Database.TransactionManager.StartTransaction(); + + // 1 - Unpack the instances + var (atomicObjects, instanceProxies, instanceDefinitionProxies) = _instanceUnpacker.UnpackSelection(objects); + root[ProxyKeys.INSTANCE_DEFINITION] = instanceDefinitionProxies; + + // 2 - Unpack the groups + root[ProxyKeys.GROUP] = _groupUnpacker.UnpackGroups(atomicObjects); + using (var _ = _activityFactory.Start("Converting objects")) + { + // 3 - Convert atomic objects + List usedAcadLayers = new(); // Keeps track of autocad layers used, so we can pass them on later to the material and color unpacker. + List results = new(); + int count = 0; + foreach (var (entity, applicationId) in atomicObjects) + { + ct.ThrowIfCancellationRequested(); + using (var convertActivity = _activityFactory.Start("Converting object")) + { + // Create and add a collection for this entity if not done so already. + (Collection objectCollection, LayerTableRecord? autocadLayer) = CreateObjectCollection(entity, tr); + + if (autocadLayer is not null) + { + usedAcadLayers.Add(autocadLayer); + root.elements.Add(objectCollection); + } + + var result = ConvertAutocadEntity( + entity, + applicationId, + objectCollection, + instanceProxies, + sendInfo.ProjectId + ); + results.Add(result); + + onOperationProgressed.Report(new("Converting", (double)++count / atomicObjects.Count)); + } + } + + if (results.All(x => x.Status == Status.ERROR)) + { + throw new SpeckleConversionException("Failed to convert all objects."); // fail fast instead creating empty commit! It will appear as model card error with red color. + } + + // 4 - Unpack the render material proxies + root[ProxyKeys.RENDER_MATERIAL] = _materialUnpacker.UnpackMaterials(atomicObjects, usedAcadLayers); + + // 5 - Unpack the color proxies + root[ProxyKeys.COLOR] = _colorUnpacker.UnpackColors(atomicObjects, usedAcadLayers); + + // add any additional properties (most likely from verticals) + AddAdditionalProxiesToRoot(root); + + return new RootObjectBuilderResult(root, results); + } + } + + public virtual (Collection, LayerTableRecord?) CreateObjectCollection(Entity entity, Transaction tr) + { + return (new(), null); + } + + public virtual void AddAdditionalProxiesToRoot(Collection rootCollection) + { + return; + } + + private SendConversionResult ConvertAutocadEntity( + Entity entity, + string applicationId, + Collection collectionHost, + IReadOnlyDictionary instanceProxies, + string projectId + ) + { + string sourceType = entity.GetType().ToString(); + try + { + Base converted; + if (entity is BlockReference && instanceProxies.TryGetValue(applicationId, out InstanceProxy? instanceProxy)) + { + converted = instanceProxy; + } + else if (_sendConversionCache.TryGetValue(projectId, applicationId, out ObjectReference? value)) + { + converted = value; + } + else + { + converted = _converter.Convert(entity); + converted.applicationId = applicationId; + } + + collectionHost.elements.Add(converted); + return new(Status.SUCCESS, applicationId, sourceType, converted); + } + catch (Exception ex) when (!ex.IsFatal()) + { + _logger.LogSendConversionError(ex, sourceType); + return new(Status.ERROR, applicationId, sourceType, null, ex); + } + } +} diff --git a/Connectors/Autocad/Speckle.Connectors.AutocadShared/Operations/Send/AutocadRootObjectBuilder.cs b/Connectors/Autocad/Speckle.Connectors.AutocadShared/Operations/Send/AutocadRootObjectBuilder.cs index ba521c1ab..7278ecae6 100644 --- a/Connectors/Autocad/Speckle.Connectors.AutocadShared/Operations/Send/AutocadRootObjectBuilder.cs +++ b/Connectors/Autocad/Speckle.Connectors.AutocadShared/Operations/Send/AutocadRootObjectBuilder.cs @@ -1,180 +1,46 @@ -using System.Diagnostics.CodeAnalysis; using Autodesk.AutoCAD.DatabaseServices; using Microsoft.Extensions.Logging; using Speckle.Connectors.Autocad.HostApp; -using Speckle.Connectors.Common.Builders; using Speckle.Connectors.Common.Caching; -using Speckle.Connectors.Common.Conversion; -using Speckle.Connectors.Common.Extensions; -using Speckle.Connectors.Common.Operations; -using Speckle.Converters.Autocad; using Speckle.Converters.Common; -using Speckle.Sdk; using Speckle.Sdk.Logging; -using Speckle.Sdk.Models; using Speckle.Sdk.Models.Collections; -using Speckle.Sdk.Models.Instances; namespace Speckle.Connectors.Autocad.Operations.Send; -public class AutocadRootObjectBuilder : IRootObjectBuilder +public sealed class AutocadRootObjectBuilder : AutocadRootObjectBaseBuilder { - private readonly IRootToSpeckleConverter _converter; - private readonly string[] _documentPathSeparator = ["\\"]; - private readonly IConverterSettingsStore _converterSettings; - private readonly ISendConversionCache _sendConversionCache; - private readonly AutocadInstanceUnpacker _instanceUnpacker; - private readonly AutocadMaterialUnpacker _materialUnpacker; - private readonly AutocadColorUnpacker _colorUnpacker; private readonly AutocadLayerUnpacker _layerUnpacker; - private readonly AutocadGroupUnpacker _groupUnpacker; - private readonly ILogger _logger; - private readonly ISdkActivityFactory _activityFactory; public AutocadRootObjectBuilder( + AutocadLayerUnpacker layerUnpacker, IRootToSpeckleConverter converter, ISendConversionCache sendConversionCache, AutocadInstanceUnpacker instanceObjectManager, AutocadMaterialUnpacker materialUnpacker, AutocadColorUnpacker colorUnpacker, - AutocadLayerUnpacker layerUnpacker, AutocadGroupUnpacker groupUnpacker, ILogger logger, - ISdkActivityFactory activityFactory, - IConverterSettingsStore converterSettings + ISdkActivityFactory activityFactory ) + : base( + converter, + sendConversionCache, + instanceObjectManager, + materialUnpacker, + colorUnpacker, + groupUnpacker, + logger, + activityFactory + ) { - _converter = converter; - _sendConversionCache = sendConversionCache; - _instanceUnpacker = instanceObjectManager; - _materialUnpacker = materialUnpacker; - _colorUnpacker = colorUnpacker; _layerUnpacker = layerUnpacker; - _groupUnpacker = groupUnpacker; - _logger = logger; - _activityFactory = activityFactory; - _converterSettings = converterSettings; } - public Task Build( - IReadOnlyList objects, - SendInfo sendInfo, - Action? onOperationProgressed = null, - CancellationToken ct = default - ) => Task.FromResult(BuildSync(objects, sendInfo, onOperationProgressed, ct)); - - [SuppressMessage( - "Maintainability", - "CA1506:Avoid excessive class coupling", - Justification = """ - It is already simplified but has many different references since it is a builder. Do not know can we simplify it now. - Later we might consider to refactor proxies from one proxy manager? but we do not know the shape of it all potential - proxy classes yet. So I'm supressing this one now!!! - """ - )] - private RootObjectBuilderResult BuildSync( - IReadOnlyList objects, - SendInfo sendInfo, - Action? onOperationProgressed, - CancellationToken ct - ) - { - // 0 - Init the root - Collection root = - new() - { - name = Application - .DocumentManager.CurrentDocument.Name // POC: https://spockle.atlassian.net/browse/CNX-9319 - .Split(_documentPathSeparator, StringSplitOptions.None) - .Reverse() - .First() - }; - root["units"] = _converterSettings.Current.SpeckleUnits; - - // TODO: better handling for document and transactions!! - Document doc = Application.DocumentManager.CurrentDocument; - using Transaction tr = doc.Database.TransactionManager.StartTransaction(); - - // 1 - Unpack the instances - var (atomicObjects, instanceProxies, instanceDefinitionProxies) = _instanceUnpacker.UnpackSelection(objects); - root[ProxyKeys.INSTANCE_DEFINITION] = instanceDefinitionProxies; - - // 2 - Unpack the groups - root[ProxyKeys.GROUP] = _groupUnpacker.UnpackGroups(atomicObjects); - using (var _ = _activityFactory.Start("Converting objects")) - { - // 3 - Convert atomic objects - List usedAcadLayers = new(); // Keeps track of autocad layers used, so we can pass them on later to the material and color unpacker. - List results = new(); - int count = 0; - foreach (var (entity, applicationId) in atomicObjects) - { - ct.ThrowIfCancellationRequested(); - using (var convertActivity = _activityFactory.Start("Converting object")) - { - // Create and add a collection for each layer if not done so already. - Layer layer = _layerUnpacker.GetOrCreateSpeckleLayer(entity, tr, out LayerTableRecord? autocadLayer); - if (autocadLayer is not null) - { - usedAcadLayers.Add(autocadLayer); - root.elements.Add(layer); - } - - var result = ConvertAutocadEntity(entity, applicationId, layer, instanceProxies, sendInfo.ProjectId); - results.Add(result); - - onOperationProgressed?.Invoke("Converting", (double)++count / atomicObjects.Count); - } - } - - if (results.All(x => x.Status == Status.ERROR)) - { - throw new SpeckleConversionException("Failed to convert all objects."); // fail fast instead creating empty commit! It will appear as model card error with red color. - } - - // 4 - Unpack the render material proxies - root[ProxyKeys.RENDER_MATERIAL] = _materialUnpacker.UnpackMaterials(atomicObjects, usedAcadLayers); - - // 5 - Unpack the color proxies - root[ProxyKeys.COLOR] = _colorUnpacker.UnpackColors(atomicObjects, usedAcadLayers); - - return new RootObjectBuilderResult(root, results); - } - } - - private SendConversionResult ConvertAutocadEntity( - Entity entity, - string applicationId, - Collection collectionHost, - IReadOnlyDictionary instanceProxies, - string projectId - ) + public override (Collection, LayerTableRecord?) CreateObjectCollection(Entity entity, Transaction tr) { - string sourceType = entity.GetType().ToString(); - try - { - Base converted; - if (entity is BlockReference && instanceProxies.TryGetValue(applicationId, out InstanceProxy? instanceProxy)) - { - converted = instanceProxy; - } - else if (_sendConversionCache.TryGetValue(projectId, applicationId, out ObjectReference? value)) - { - converted = value; - } - else - { - converted = _converter.Convert(entity); - converted.applicationId = applicationId; - } + Layer layer = _layerUnpacker.GetOrCreateSpeckleLayer(entity, tr, out LayerTableRecord? autocadLayer); - collectionHost.elements.Add(converted); - return new(Status.SUCCESS, applicationId, sourceType, converted); - } - catch (Exception ex) when (!ex.IsFatal()) - { - _logger.LogSendConversionError(ex, sourceType); - return new(Status.ERROR, applicationId, sourceType, null, ex); - } + return (layer, autocadLayer); } } diff --git a/Connectors/Autocad/Speckle.Connectors.AutocadShared/Plugin/AutocadCommand.cs b/Connectors/Autocad/Speckle.Connectors.AutocadShared/Plugin/AutocadCommand.cs index 526e6ee7a..f3c177540 100644 --- a/Connectors/Autocad/Speckle.Connectors.AutocadShared/Plugin/AutocadCommand.cs +++ b/Connectors/Autocad/Speckle.Connectors.AutocadShared/Plugin/AutocadCommand.cs @@ -2,14 +2,15 @@ using Autodesk.AutoCAD.Runtime; using Autodesk.AutoCAD.Windows; using Microsoft.Extensions.DependencyInjection; -using Speckle.Connectors.Autocad.DependencyInjection; using Speckle.Connectors.Common; using Speckle.Connectors.DUI; using Speckle.Connectors.DUI.WebView; #if AUTOCAD +using Speckle.Connectors.Autocad.DependencyInjection; using Speckle.Converters.Autocad; #elif CIVIL3D -using Speckle.Converters.Civil3d; +using Speckle.Converters.Civil3dShared; +using Speckle.Connectors.Civil3dShared.DependencyInjection; #endif namespace Speckle.Connectors.Autocad.Plugin; @@ -30,7 +31,7 @@ public void Command() return; } - PaletteSet = new PaletteSet("Speckle (Beta) for Autocad", s_id) + PaletteSet = new PaletteSet($"Speckle (Beta) for {AppUtils.App.Name}", s_id) { Size = new Size(400, 500), DockEnabled = (DockSides)((int)DockSides.Left + (int)DockSides.Right) @@ -51,7 +52,7 @@ public void Command() var panelWebView = Container.GetRequiredService(); - PaletteSet.AddVisual("Speckle (Beta) for Autocad WebView", panelWebView); + PaletteSet.AddVisual($"Speckle (Beta) for {AppUtils.App.Name} WebView", panelWebView); FocusPalette(); } diff --git a/Connectors/Autocad/Speckle.Connectors.AutocadShared/Speckle.Connectors.AutocadShared.projitems b/Connectors/Autocad/Speckle.Connectors.AutocadShared/Speckle.Connectors.AutocadShared.projitems index 4798dce73..682bf35e2 100644 --- a/Connectors/Autocad/Speckle.Connectors.AutocadShared/Speckle.Connectors.AutocadShared.projitems +++ b/Connectors/Autocad/Speckle.Connectors.AutocadShared/Speckle.Connectors.AutocadShared.projitems @@ -13,9 +13,9 @@ + - @@ -39,6 +39,7 @@ + diff --git a/Connectors/Autocad/Speckle.Connectors.Civil3d2024/Speckle.Connectors.Civil3d2024.csproj b/Connectors/Autocad/Speckle.Connectors.Civil3d2024/Speckle.Connectors.Civil3d2024.csproj index 87f0841ce..66dcd0fa9 100644 --- a/Connectors/Autocad/Speckle.Connectors.Civil3d2024/Speckle.Connectors.Civil3d2024.csproj +++ b/Connectors/Autocad/Speckle.Connectors.Civil3d2024/Speckle.Connectors.Civil3d2024.csproj @@ -2,9 +2,12 @@ net48 x64 + true 2024 $(DefineConstants);CIVIL3D;CIVIL3D2024;CIVIL3D2024_OR_GREATER Debug;Release;Local + Program + $(ProgramW6432)\Autodesk\AutoCAD $(Civil3DVersion)\acad.exe @@ -19,4 +22,5 @@ + diff --git a/Connectors/Autocad/Speckle.Connectors.Civil3d2024/packages.lock.json b/Connectors/Autocad/Speckle.Connectors.Civil3d2024/packages.lock.json index d95249c49..8e875b34f 100644 --- a/Connectors/Autocad/Speckle.Connectors.Civil3d2024/packages.lock.json +++ b/Connectors/Autocad/Speckle.Connectors.Civil3d2024/packages.lock.json @@ -2,6 +2,15 @@ "version": 2, "dependencies": { ".NETFramework,Version=v4.8": { + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3" + } + }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -146,6 +155,11 @@ "System.Runtime.CompilerServices.Unsafe": "4.5.1" } }, + "Microsoft.NETFramework.ReferenceAssemblies.net48": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ==" + }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", @@ -269,8 +283,8 @@ "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )", - "Speckle.Sdk": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )", + "Speckle.Sdk": "[3.1.0-dev.162, )" } }, "speckle.connectors.dui": { @@ -278,7 +292,7 @@ "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", "Speckle.Connectors.Common": "[1.0.0, )", - "Speckle.Sdk": "[3.1.0-dev.145, )", + "Speckle.Sdk": "[3.1.0-dev.162, )", "System.Threading.Tasks.Dataflow": "[6.0.0, )" } }, @@ -304,7 +318,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )" } }, "Microsoft.Extensions.DependencyInjection": { @@ -342,18 +356,18 @@ }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "gpENXDB2i8SiXJZwrLVfB3BwKF1FHUSpPiw0BQ3Hl0V3821UWZjPYw5XK694oxJlVAeEMRvrMZMGsWOcK+3nMw==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "1fyaPjC1kVtroKfoXI7edJX0Uho/TNRWG9mtmIQ7jKhX1zElrJPSoH8zEIMqckZhGmwUrYjlQFPssASNCP721Q==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.145" + "Speckle.Sdk": "3.1.0-dev.162" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "YZLtitiVIxl12pNTXaB9gU+paNwFe++WBw6oIN6Rs5hFPialUgeKbpjh16Zq6wcptjBpvg0CHq29+d456QUAJQ==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "ulT3XNgqIQSmpQFK5YEEwRq4RA6emXBKQHnZxtCz37SJin1PnVpH9m8vOAF9FmaWsgSAfjhhjIZDruMu1oKxSg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", diff --git a/Connectors/Autocad/Speckle.Connectors.Civil3dShared/Bindings/Civil3dSendBinding.cs b/Connectors/Autocad/Speckle.Connectors.Civil3dShared/Bindings/Civil3dSendBinding.cs new file mode 100644 index 000000000..a1aa571b0 --- /dev/null +++ b/Connectors/Autocad/Speckle.Connectors.Civil3dShared/Bindings/Civil3dSendBinding.cs @@ -0,0 +1,66 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Speckle.Connectors.Autocad.Bindings; +using Speckle.Connectors.Autocad.HostApp; +using Speckle.Connectors.Common.Caching; +using Speckle.Connectors.Common.Cancellation; +using Speckle.Connectors.DUI.Bindings; +using Speckle.Connectors.DUI.Bridge; +using Speckle.Connectors.DUI.Models; +using Speckle.Connectors.DUI.Models.Card.SendFilter; +using Speckle.Converters.Autocad; +using Speckle.Converters.Civil3dShared; +using Speckle.Converters.Common; +using Speckle.Sdk; + +namespace Speckle.Connectors.Civil3dShared.Bindings; + +public sealed class Civil3dSendBinding : AutocadSendBaseBinding +{ + private readonly ICivil3dConversionSettingsFactory _civil3dConversionSettingsFactory; + private readonly IAutocadConversionSettingsFactory _autocadConversionSettingsFactory; + + public Civil3dSendBinding( + DocumentModelStore store, + IAutocadIdleManager idleManager, + IBrowserBridge parent, + IEnumerable sendFilters, + CancellationManager cancellationManager, + IServiceProvider serviceProvider, + ISendConversionCache sendConversionCache, + IOperationProgressManager operationProgressManager, + ILogger logger, + ICivil3dConversionSettingsFactory civil3dConversionSettingsFactory, + IAutocadConversionSettingsFactory autocadConversionSettingsFactory, + ISpeckleApplication speckleApplication + ) + : base( + store, + idleManager, + parent, + sendFilters, + cancellationManager, + serviceProvider, + sendConversionCache, + operationProgressManager, + logger, + speckleApplication + ) + { + _civil3dConversionSettingsFactory = civil3dConversionSettingsFactory; + _autocadConversionSettingsFactory = autocadConversionSettingsFactory; + } + + // POC: we're registering the conversion settings for autocad here because we need the autocad conversion settings to be able to use the autocad typed converters. + // POC: We need a separate send binding for civil3d due to using a different unit converter (needed for conversion settings construction) + protected override void InitializeSettings(IServiceProvider serviceProvider) + { + serviceProvider + .GetRequiredService>() + .Initialize(_civil3dConversionSettingsFactory.Create(Application.DocumentManager.CurrentDocument)); + + serviceProvider + .GetRequiredService>() + .Initialize(_autocadConversionSettingsFactory.Create(Application.DocumentManager.CurrentDocument)); + } +} diff --git a/Connectors/Autocad/Speckle.Connectors.Civil3dShared/DependencyInjection/Civil3dConnectorModule.cs b/Connectors/Autocad/Speckle.Connectors.Civil3dShared/DependencyInjection/Civil3dConnectorModule.cs new file mode 100644 index 000000000..7dcb841d2 --- /dev/null +++ b/Connectors/Autocad/Speckle.Connectors.Civil3dShared/DependencyInjection/Civil3dConnectorModule.cs @@ -0,0 +1,34 @@ +using System.Reflection; +using Microsoft.Extensions.DependencyInjection; +using Speckle.Connectors.Autocad.DependencyInjection; +using Speckle.Connectors.Autocad.Operations.Send; +using Speckle.Connectors.Civil3dShared.Bindings; +using Speckle.Connectors.Civil3dShared.Operations.Send; +using Speckle.Connectors.Common.Builders; +using Speckle.Connectors.DUI.Bindings; +using Speckle.Converters.Civil3dShared.Helpers; +using Speckle.Converters.Civil3dShared.ToSpeckle; +using Speckle.Sdk; + +namespace Speckle.Connectors.Civil3dShared.DependencyInjection; + +public static class Civil3dConnectorModule +{ + public static void AddCivil3d(this IServiceCollection serviceCollection) + { + serviceCollection.AddAutocadBase(); + serviceCollection.LoadSend(); + + // register civil specific send classes + serviceCollection.AddScoped, Civil3dRootObjectBuilder>(); + serviceCollection.AddSingleton(); + + // automatically detects the Class:IClass interface pattern to register all generated interfaces + serviceCollection.AddMatchingInterfacesAsTransient(Assembly.GetExecutingAssembly()); + + // additional classes + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + } +} diff --git a/Connectors/Autocad/Speckle.Connectors.Civil3dShared/Operations/Send/Civil3dRootObjectBuilder.cs b/Connectors/Autocad/Speckle.Connectors.Civil3dShared/Operations/Send/Civil3dRootObjectBuilder.cs new file mode 100644 index 000000000..db9850f07 --- /dev/null +++ b/Connectors/Autocad/Speckle.Connectors.Civil3dShared/Operations/Send/Civil3dRootObjectBuilder.cs @@ -0,0 +1,67 @@ +using Autodesk.AutoCAD.DatabaseServices; +using Microsoft.Extensions.Logging; +using Speckle.Connectors.Autocad.HostApp; +using Speckle.Connectors.Autocad.Operations.Send; +using Speckle.Connectors.Common.Caching; +using Speckle.Connectors.Common.Operations; +using Speckle.Converters.Civil3dShared.Helpers; +using Speckle.Converters.Civil3dShared.ToSpeckle; +using Speckle.Converters.Common; +using Speckle.Sdk.Logging; +using Speckle.Sdk.Models.Collections; + +namespace Speckle.Connectors.Civil3dShared.Operations.Send; + +public sealed class Civil3dRootObjectBuilder : AutocadRootObjectBaseBuilder +{ + private readonly AutocadLayerUnpacker _layerUnpacker; + private readonly PropertySetDefinitionHandler _propertySetDefinitionHandler; + private readonly CatchmentGroupHandler _catchmentGroupHandler; + private readonly PipeNetworkHandler _pipeNetworkHandler; + + public Civil3dRootObjectBuilder( + AutocadLayerUnpacker layerUnpacker, + PropertySetDefinitionHandler propertySetDefinitionHandler, + CatchmentGroupHandler catchmentGroupHandler, + PipeNetworkHandler pipeNetworkHandler, + IRootToSpeckleConverter converter, + ISendConversionCache sendConversionCache, + AutocadInstanceUnpacker instanceObjectManager, + AutocadMaterialUnpacker materialUnpacker, + AutocadColorUnpacker colorUnpacker, + AutocadGroupUnpacker groupUnpacker, + ILogger logger, + ISdkActivityFactory activityFactory + ) + : base( + converter, + sendConversionCache, + instanceObjectManager, + materialUnpacker, + colorUnpacker, + groupUnpacker, + logger, + activityFactory + ) + { + _layerUnpacker = layerUnpacker; + _propertySetDefinitionHandler = propertySetDefinitionHandler; + _catchmentGroupHandler = catchmentGroupHandler; + _pipeNetworkHandler = pipeNetworkHandler; + } + + public override (Collection, LayerTableRecord?) CreateObjectCollection(Entity entity, Transaction tr) + { + Layer layer = _layerUnpacker.GetOrCreateSpeckleLayer(entity, tr, out LayerTableRecord? autocadLayer); + + return (layer, autocadLayer); + } + + // POC: probably will need to add Network proxies as well + public override void AddAdditionalProxiesToRoot(Collection rootObject) + { + rootObject[ProxyKeys.PROPERTYSET_DEFINITIONS] = _propertySetDefinitionHandler.Definitions; + rootObject["catchmentGroupProxies"] = _catchmentGroupHandler.CatchmentGroupProxiesCache.Values.ToList(); + rootObject["pipeNetworkProxies"] = _pipeNetworkHandler.PipeNetworkProxiesCache.Values.ToList(); + } +} diff --git a/Connectors/Autocad/Speckle.Connectors.Civil3dShared/Speckle.Connectors.Civil3dShared.projitems b/Connectors/Autocad/Speckle.Connectors.Civil3dShared/Speckle.Connectors.Civil3dShared.projitems new file mode 100644 index 000000000..391ba05b1 --- /dev/null +++ b/Connectors/Autocad/Speckle.Connectors.Civil3dShared/Speckle.Connectors.Civil3dShared.projitems @@ -0,0 +1,21 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + 55E65D72-2FE8-4E61-891C-16A4D551CCF7 + + + Speckle.Connectors.Civil3dShared + + + + + + + + + + + + \ No newline at end of file diff --git a/Connectors/Autocad/Speckle.Connectors.Civil3dShared/Speckle.Connectors.Civil3dShared.shproj b/Connectors/Autocad/Speckle.Connectors.Civil3dShared/Speckle.Connectors.Civil3dShared.shproj new file mode 100644 index 000000000..3d715ea4a --- /dev/null +++ b/Connectors/Autocad/Speckle.Connectors.Civil3dShared/Speckle.Connectors.Civil3dShared.shproj @@ -0,0 +1,12 @@ + + + + {41BC679F-887F-44CF-971D-A5502EE87DB0} + + + + + + + diff --git a/Connectors/Revit/Speckle.Connectors.Revit2022/packages.lock.json b/Connectors/Revit/Speckle.Connectors.Revit2022/packages.lock.json index 10245828f..8e56f9e6b 100644 --- a/Connectors/Revit/Speckle.Connectors.Revit2022/packages.lock.json +++ b/Connectors/Revit/Speckle.Connectors.Revit2022/packages.lock.json @@ -11,6 +11,15 @@ "CefSharp.Common": "[65.0.1]" } }, + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3" + } + }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -165,6 +174,11 @@ "System.Runtime.CompilerServices.Unsafe": "4.5.1" } }, + "Microsoft.NETFramework.ReferenceAssemblies.net48": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ==" + }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", @@ -288,8 +302,8 @@ "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )", - "Speckle.Sdk": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )", + "Speckle.Sdk": "[3.1.0-dev.162, )" } }, "speckle.connectors.dui": { @@ -297,7 +311,7 @@ "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", "Speckle.Connectors.Common": "[1.0.0, )", - "Speckle.Sdk": "[3.1.0-dev.145, )", + "Speckle.Sdk": "[3.1.0-dev.162, )", "System.Threading.Tasks.Dataflow": "[6.0.0, )" } }, @@ -308,7 +322,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )" } }, "speckle.converters.revit2022": { @@ -347,11 +361,11 @@ }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "gpENXDB2i8SiXJZwrLVfB3BwKF1FHUSpPiw0BQ3Hl0V3821UWZjPYw5XK694oxJlVAeEMRvrMZMGsWOcK+3nMw==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "1fyaPjC1kVtroKfoXI7edJX0Uho/TNRWG9mtmIQ7jKhX1zElrJPSoH8zEIMqckZhGmwUrYjlQFPssASNCP721Q==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.145" + "Speckle.Sdk": "3.1.0-dev.162" } }, "Speckle.Revit.API": { @@ -362,9 +376,9 @@ }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "YZLtitiVIxl12pNTXaB9gU+paNwFe++WBw6oIN6Rs5hFPialUgeKbpjh16Zq6wcptjBpvg0CHq29+d456QUAJQ==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "ulT3XNgqIQSmpQFK5YEEwRq4RA6emXBKQHnZxtCz37SJin1PnVpH9m8vOAF9FmaWsgSAfjhhjIZDruMu1oKxSg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", diff --git a/Connectors/Revit/Speckle.Connectors.Revit2023/packages.lock.json b/Connectors/Revit/Speckle.Connectors.Revit2023/packages.lock.json index 12286f08c..ccab26bf5 100644 --- a/Connectors/Revit/Speckle.Connectors.Revit2023/packages.lock.json +++ b/Connectors/Revit/Speckle.Connectors.Revit2023/packages.lock.json @@ -11,6 +11,15 @@ "CefSharp.Common": "[92.0.260]" } }, + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3" + } + }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -165,6 +174,11 @@ "System.Runtime.CompilerServices.Unsafe": "4.5.1" } }, + "Microsoft.NETFramework.ReferenceAssemblies.net48": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ==" + }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", @@ -288,8 +302,8 @@ "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )", - "Speckle.Sdk": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )", + "Speckle.Sdk": "[3.1.0-dev.162, )" } }, "speckle.connectors.dui": { @@ -297,7 +311,7 @@ "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", "Speckle.Connectors.Common": "[1.0.0, )", - "Speckle.Sdk": "[3.1.0-dev.145, )", + "Speckle.Sdk": "[3.1.0-dev.162, )", "System.Threading.Tasks.Dataflow": "[6.0.0, )" } }, @@ -308,7 +322,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )" } }, "speckle.converters.revit2023": { @@ -347,11 +361,11 @@ }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "gpENXDB2i8SiXJZwrLVfB3BwKF1FHUSpPiw0BQ3Hl0V3821UWZjPYw5XK694oxJlVAeEMRvrMZMGsWOcK+3nMw==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "1fyaPjC1kVtroKfoXI7edJX0Uho/TNRWG9mtmIQ7jKhX1zElrJPSoH8zEIMqckZhGmwUrYjlQFPssASNCP721Q==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.145" + "Speckle.Sdk": "3.1.0-dev.162" } }, "Speckle.Revit.API": { @@ -362,9 +376,9 @@ }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "YZLtitiVIxl12pNTXaB9gU+paNwFe++WBw6oIN6Rs5hFPialUgeKbpjh16Zq6wcptjBpvg0CHq29+d456QUAJQ==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "ulT3XNgqIQSmpQFK5YEEwRq4RA6emXBKQHnZxtCz37SJin1PnVpH9m8vOAF9FmaWsgSAfjhhjIZDruMu1oKxSg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", diff --git a/Connectors/Revit/Speckle.Connectors.Revit2024/packages.lock.json b/Connectors/Revit/Speckle.Connectors.Revit2024/packages.lock.json index 36bb56210..12629c3d1 100644 --- a/Connectors/Revit/Speckle.Connectors.Revit2024/packages.lock.json +++ b/Connectors/Revit/Speckle.Connectors.Revit2024/packages.lock.json @@ -11,6 +11,15 @@ "CefSharp.Common": "[105.3.390]" } }, + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3" + } + }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -165,6 +174,11 @@ "System.Runtime.CompilerServices.Unsafe": "4.5.1" } }, + "Microsoft.NETFramework.ReferenceAssemblies.net48": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ==" + }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", @@ -288,8 +302,8 @@ "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )", - "Speckle.Sdk": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )", + "Speckle.Sdk": "[3.1.0-dev.162, )" } }, "speckle.connectors.dui": { @@ -297,7 +311,7 @@ "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", "Speckle.Connectors.Common": "[1.0.0, )", - "Speckle.Sdk": "[3.1.0-dev.145, )", + "Speckle.Sdk": "[3.1.0-dev.162, )", "System.Threading.Tasks.Dataflow": "[6.0.0, )" } }, @@ -308,7 +322,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )" } }, "speckle.converters.revit2024": { @@ -347,11 +361,11 @@ }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "gpENXDB2i8SiXJZwrLVfB3BwKF1FHUSpPiw0BQ3Hl0V3821UWZjPYw5XK694oxJlVAeEMRvrMZMGsWOcK+3nMw==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "1fyaPjC1kVtroKfoXI7edJX0Uho/TNRWG9mtmIQ7jKhX1zElrJPSoH8zEIMqckZhGmwUrYjlQFPssASNCP721Q==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.145" + "Speckle.Sdk": "3.1.0-dev.162" } }, "Speckle.Revit.API": { @@ -362,9 +376,9 @@ }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "YZLtitiVIxl12pNTXaB9gU+paNwFe++WBw6oIN6Rs5hFPialUgeKbpjh16Zq6wcptjBpvg0CHq29+d456QUAJQ==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "ulT3XNgqIQSmpQFK5YEEwRq4RA6emXBKQHnZxtCz37SJin1PnVpH9m8vOAF9FmaWsgSAfjhhjIZDruMu1oKxSg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", diff --git a/Connectors/Revit/Speckle.Connectors.Revit2025/packages.lock.json b/Connectors/Revit/Speckle.Connectors.Revit2025/packages.lock.json index 39e6473a6..bef844ca0 100644 --- a/Connectors/Revit/Speckle.Connectors.Revit2025/packages.lock.json +++ b/Connectors/Revit/Speckle.Connectors.Revit2025/packages.lock.json @@ -11,6 +11,15 @@ "CefSharp.Common.NETCore": "[119.4.30]" } }, + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3" + } + }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -158,6 +167,11 @@ "System.Runtime.CompilerServices.Unsafe": "4.5.1" } }, + "Microsoft.NETFramework.ReferenceAssemblies.net461": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA==" + }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", @@ -246,8 +260,8 @@ "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )", - "Speckle.Sdk": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )", + "Speckle.Sdk": "[3.1.0-dev.162, )" } }, "speckle.connectors.dui": { @@ -255,7 +269,7 @@ "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", "Speckle.Connectors.Common": "[1.0.0, )", - "Speckle.Sdk": "[3.1.0-dev.145, )", + "Speckle.Sdk": "[3.1.0-dev.162, )", "System.Threading.Tasks.Dataflow": "[6.0.0, )" } }, @@ -273,7 +287,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )" } }, "speckle.converters.revit2025": { @@ -318,11 +332,11 @@ }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "gpENXDB2i8SiXJZwrLVfB3BwKF1FHUSpPiw0BQ3Hl0V3821UWZjPYw5XK694oxJlVAeEMRvrMZMGsWOcK+3nMw==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "1fyaPjC1kVtroKfoXI7edJX0Uho/TNRWG9mtmIQ7jKhX1zElrJPSoH8zEIMqckZhGmwUrYjlQFPssASNCP721Q==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.145" + "Speckle.Sdk": "3.1.0-dev.162" } }, "Speckle.Revit.API": { @@ -333,9 +347,9 @@ }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "YZLtitiVIxl12pNTXaB9gU+paNwFe++WBw6oIN6Rs5hFPialUgeKbpjh16Zq6wcptjBpvg0CHq29+d456QUAJQ==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "ulT3XNgqIQSmpQFK5YEEwRq4RA6emXBKQHnZxtCz37SJin1PnVpH9m8vOAF9FmaWsgSAfjhhjIZDruMu1oKxSg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", diff --git a/Connectors/Revit/Speckle.Connectors.RevitShared.Cef/CefSharpPanel.xaml.cs b/Connectors/Revit/Speckle.Connectors.RevitShared.Cef/CefSharpPanel.xaml.cs index 9679ffd2a..ae78681ed 100644 --- a/Connectors/Revit/Speckle.Connectors.RevitShared.Cef/CefSharpPanel.xaml.cs +++ b/Connectors/Revit/Speckle.Connectors.RevitShared.Cef/CefSharpPanel.xaml.cs @@ -13,8 +13,16 @@ public CefSharpPanel() InitializeComponent(); } - public void ExecuteScriptAsyncMethod(string script) => - Browser.Dispatcher.Invoke(() => Browser.ExecuteScriptAsync(script), DispatcherPriority.Background); + public Task ExecuteScriptAsyncMethod(string script, CancellationToken cancellationToken) + { + Browser.Dispatcher.Invoke( + () => Browser.ExecuteScriptAsync(script), + DispatcherPriority.Background, + cancellationToken + ); + + return Task.CompletedTask; + } public bool IsBrowserInitialized => Browser.IsBrowserInitialized; public object BrowserElement => Browser; diff --git a/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/BasicConnectorBindingRevit.cs b/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/BasicConnectorBindingRevit.cs index 7fe9a1e82..98b5ab4d8 100644 --- a/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/BasicConnectorBindingRevit.cs +++ b/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/BasicConnectorBindingRevit.cs @@ -42,9 +42,10 @@ ILogger logger // POC: event binding? _store.DocumentChanged += (_, _) => - { - Commands.NotifyDocumentChanged(); - }; + parent.TopLevelExceptionHandler.FireAndForget(async () => + { + await Commands.NotifyDocumentChanged().ConfigureAwait(false); + }); } public string GetConnectorVersion() => _speckleApplication.SpeckleVersion; @@ -82,7 +83,7 @@ ILogger logger public void RemoveModel(ModelCard model) => _store.RemoveModel(model); - public void HighlightModel(string modelCardId) + public async Task HighlightModel(string modelCardId) { var model = _store.GetModelById(modelCardId); @@ -117,29 +118,33 @@ public void HighlightModel(string modelCardId) if (elementIds.Count == 0) { - Commands.SetModelError(modelCardId, new InvalidOperationException("No objects found to highlight.")); + await Commands + .SetModelError(modelCardId, new InvalidOperationException("No objects found to highlight.")) + .ConfigureAwait(false); return; } - HighlightObjectsOnView(elementIds); + await HighlightObjectsOnView(elementIds).ConfigureAwait(false); } /// /// Highlights the objects from the given ids. /// /// UniqueId's of the DB.Elements. - public void HighlightObjects(List objectIds) + public async Task HighlightObjects(IReadOnlyList objectIds) { var activeUIDoc = _revitContext.UIApplication?.ActiveUIDocument ?? throw new SpeckleException("Unable to retrieve active UI document"); - HighlightObjectsOnView( - objectIds.Select(uid => ElementIdHelper.GetElementIdFromUniqueId(activeUIDoc.Document, uid)).ToList() - ); + await HighlightObjectsOnView( + objectIds.Select(uid => ElementIdHelper.GetElementIdFromUniqueId(activeUIDoc.Document, uid)).ToList() + ) + .ConfigureAwait(false); + ; } - private void HighlightObjectsOnView(List objectIds) + private async Task HighlightObjectsOnView(List objectIds) { // POC: don't know if we can rely on storing the ActiveUIDocument, hence getting it each time var activeUIDoc = @@ -147,10 +152,13 @@ private void HighlightObjectsOnView(List objectIds) ?? throw new SpeckleException("Unable to retrieve active UI document"); // UiDocument operations should be wrapped into RevitTask, otherwise doesn't work on other tasks. - RevitTask.RunAsync(() => - { - activeUIDoc.Selection.SetElementIds(objectIds); - activeUIDoc.ShowElements(objectIds); - }); + await RevitTask + .RunAsync(() => + { + activeUIDoc.Selection.SetElementIds(objectIds); + activeUIDoc.ShowElements(objectIds); + }) + .ConfigureAwait(false); + ; } } diff --git a/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/RevitReceiveBinding.cs b/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/RevitReceiveBinding.cs index 51e9fdec2..27bb4868a 100644 --- a/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/RevitReceiveBinding.cs +++ b/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/RevitReceiveBinding.cs @@ -81,23 +81,15 @@ public async Task Receive(string modelCardId) .ServiceProvider.GetRequiredService() .Execute( modelCard.GetReceiveInfo(_speckleApplication.Slug), - cancellationToken, - (status, progress) => - _operationProgressManager.SetModelProgress( - Parent, - modelCardId, - new ModelCardProgress(modelCardId, status, progress), - cancellationToken - ) + _operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationToken), + cancellationToken ) .ConfigureAwait(false); modelCard.BakedObjectIds = conversionResults.BakedObjectIds.ToList(); - Commands.SetModelReceiveResult( - modelCardId, - conversionResults.BakedObjectIds, - conversionResults.ConversionResults - ); + await Commands + .SetModelReceiveResult(modelCardId, conversionResults.BakedObjectIds, conversionResults.ConversionResults) + .ConfigureAwait(false); } catch (OperationCanceledException) { @@ -108,7 +100,7 @@ public async Task Receive(string modelCardId) catch (Exception ex) when (!ex.IsFatal()) // UX reasons - we will report operation exceptions as model card error. We may change this later when we have more exception documentation { _logger.LogModelCardHandledError(ex); - Commands.SetModelError(modelCardId, ex); + await Commands.SetModelError(modelCardId, ex).ConfigureAwait(false); } } } diff --git a/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/RevitSendBinding.cs b/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/RevitSendBinding.cs index 2dab7cd4b..df3d88257 100644 --- a/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/RevitSendBinding.cs +++ b/Connectors/Revit/Speckle.Connectors.RevitShared/Bindings/RevitSendBinding.cs @@ -82,7 +82,8 @@ ISpeckleApplication speckleApplication revitContext.UIApplication.NotNull().Application.DocumentChanged += (_, e) => topLevelExceptionHandler.CatchUnhandled(() => DocChangeHandler(e)); }); - Store.DocumentChanged += (_, _) => topLevelExceptionHandler.CatchUnhandled(OnDocumentChanged); + Store.DocumentChanged += (_, _) => + topLevelExceptionHandler.FireAndForget(async () => await OnDocumentChanged().ConfigureAwait(false)); } public List GetSendFilters() @@ -101,10 +102,7 @@ public List GetSendSettings() => public SendBindingUICommands Commands { get; } - // yes we know Send function calls many different namespace, we know. But currently I don't see any simplification area we can work on! -#pragma warning disable CA1506 public async Task Send(string modelCardId) -#pragma warning restore CA1506 { // Note: removed top level handling thing as it was confusing me try @@ -148,18 +146,14 @@ public async Task Send(string modelCardId) .Execute( revitObjects, modelCard.GetSendInfo(_speckleApplication.Slug), - (status, progress) => - _operationProgressManager.SetModelProgress( - Parent, - modelCardId, - new ModelCardProgress(modelCardId, status, progress), - cancellationToken - ), + _operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationToken), cancellationToken ) .ConfigureAwait(false); - Commands.SetModelSendResult(modelCardId, sendResult.RootObjId, sendResult.ConversionResults); + await Commands + .SetModelSendResult(modelCardId, sendResult.RootObjId, sendResult.ConversionResults) + .ConfigureAwait(false); } catch (OperationCanceledException) { @@ -170,7 +164,7 @@ public async Task Send(string modelCardId) catch (Exception ex) when (!ex.IsFatal()) // UX reasons - we will report operation exceptions as model card error. We may change this later when we have more exception documentation { _logger.LogModelCardHandledError(ex); - Commands.SetModelError(modelCardId, ex); + await Commands.SetModelError(modelCardId, ex).ConfigureAwait(false); } } @@ -243,7 +237,7 @@ private bool HaveUnitsChanged(Document doc) return false; } - private void RunExpirationChecks() + private async Task RunExpirationChecks() { var senders = Store.GetSenders(); string[] objectIdsList = ChangedObjectIds.Keys.ToArray(); @@ -275,22 +269,24 @@ private void RunExpirationChecks() } } - Commands.SetModelsExpired(expiredSenderIds); + await Commands.SetModelsExpired(expiredSenderIds).ConfigureAwait(false); ChangedObjectIds = new(); } // 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() + private async Task OnDocumentChanged() { if (_cancellationManager.NumberOfOperations > 0) { _cancellationManager.CancelAllOperations(); - Commands.SetGlobalNotification( - ToastNotificationType.INFO, - "Document Switch", - "Operations cancelled because of document swap!" - ); + await Commands + .SetGlobalNotification( + ToastNotificationType.INFO, + "Document Switch", + "Operations cancelled because of document swap!" + ) + .ConfigureAwait(false); } } } diff --git a/Connectors/Revit/Speckle.Connectors.RevitShared/HostApp/RevitGroupBaker.cs b/Connectors/Revit/Speckle.Connectors.RevitShared/HostApp/RevitGroupBaker.cs index 81c2a8e4f..714ca742e 100644 --- a/Connectors/Revit/Speckle.Connectors.RevitShared/HostApp/RevitGroupBaker.cs +++ b/Connectors/Revit/Speckle.Connectors.RevitShared/HostApp/RevitGroupBaker.cs @@ -2,13 +2,11 @@ using Speckle.Connectors.Common.Operations.Receive; using Speckle.Converters.Common; using Speckle.Converters.RevitShared.Settings; -using Speckle.Sdk.Models.GraphTraversal; namespace Speckle.Connectors.Revit.HostApp; /// -/// On receive, this class will help structure atomic objects into nested revit groups based on the hierarchy that they're coming from. Expects to be a scoped dependency per receive operation. -/// How to use: during atomic object conversion, on each succesful conversion call . Afterward, at the end of the recieve operation, call to actually create the groups in the revit document. +/// Bakes all objects into a single top level group and pins it. /// public class RevitGroupBaker : TraversalContextUnpacker { @@ -21,62 +19,15 @@ public RevitGroupBaker(IConverterSettingsStore converte _revitUtils = revitUtils; } - /// - /// Adds the object to the correct group in preparation for at the end of the receive operation. - /// - /// - /// - public void AddToGroupMapping(TraversalContext traversalContext, Element revitElement) - { - var collectionPath = GetCollectionPath(traversalContext); - var currentLayerName = string.Empty; - FakeGroup? previousGroup = null; - var currentDepth = 0; - - foreach (var collection in collectionPath) - { - currentLayerName += collection.name + "-"; - if (_groupCache.TryGetValue(currentLayerName, out var g)) - { - previousGroup = g; - currentDepth++; - continue; - } - - var group = new FakeGroup() - { - // POC group names should be unique - Name = _revitUtils.RemoveInvalidChars(currentLayerName[..^1]), - Depth = currentDepth++, - Parent = previousGroup! - }; - _groupCache[currentLayerName] = group; - previousGroup = group; - } - - previousGroup!.Ids.Add(revitElement.Id); - } + private readonly List _elementIdsForTopLevelGroup = new(); - private readonly Dictionary _groupCache = new(); + public void AddToTopLevelGroup(Element revitElement) => _elementIdsForTopLevelGroup.Add(revitElement.Id); - /// - /// Bakes the accumulated groups in Revit, with their objects. - /// - /// - public void BakeGroups(string baseGroupName) + public void BakeGroupForTopLevel(string baseGroupName) { - var orderedGroups = _groupCache.Values.OrderByDescending(group => group.Depth); - Group? lastGroup = null; - - foreach (var group in orderedGroups) - { - var docGroup = _converterSettings.Current.Document.Create.NewGroup(group.Ids); - group.Parent?.Ids.Add(docGroup.Id); - docGroup.GroupType.Name = group.Name; - lastGroup = docGroup; - } - - lastGroup!.GroupType.Name = _revitUtils.RemoveInvalidChars(baseGroupName); + var docGroup = _converterSettings.Current.Document.Create.NewGroup(_elementIdsForTopLevelGroup); + docGroup.GroupType.Name = _revitUtils.RemoveInvalidChars(baseGroupName); + docGroup.Pinned = true; } public void PurgeGroups(string baseGroupName) @@ -86,25 +37,23 @@ public void PurgeGroups(string baseGroupName) foreach (var group in groups) { - List subgroupTypeIds = new List(); + var subgroupTypeIds = new List() { group.GroupType.Id }; CollectSubGroupTypeIds(document, group, subgroupTypeIds); document.Delete(subgroupTypeIds); } } - private List GetGroupsByName(Autodesk.Revit.DB.Document doc, string groupName) + private List GetGroupsByName(Document doc, string groupName) { var validGroupName = _revitUtils.RemoveInvalidChars(groupName); - using (var collector = new FilteredElementCollector(doc)) - { - ICollection groupElements = collector.OfClass(typeof(Group)).ToElements(); - List groups = groupElements.Cast().Where(g => g.GroupType.Name == validGroupName).ToList(); - return groups; - } + using var collector = new FilteredElementCollector(doc); + ICollection groupElements = collector.OfClass(typeof(Group)).ToElements(); + List groups = groupElements.Cast().Where(g => g.GroupType.Name == validGroupName).ToList(); + return groups; } - private void CollectSubGroupTypeIds(Autodesk.Revit.DB.Document document, Group group, List subGroupTypeIds) + private void CollectSubGroupTypeIds(Document document, Group group, List subGroupTypeIds) { ICollection groupMemberIds = group.GetMemberIds(); @@ -119,15 +68,4 @@ private void CollectSubGroupTypeIds(Autodesk.Revit.DB.Document document, Group g } } } - - /// - /// Little intermediate data structure that helps with the operations above. - /// - private sealed class FakeGroup - { - public List Ids { get; set; } = new(); - public int Depth { get; set; } - public string Name { get; set; } - public FakeGroup Parent { get; set; } - } } diff --git a/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Receive/ITransactionManager.cs b/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Receive/ITransactionManager.cs index 76bf66776..5fef78e9a 100644 --- a/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Receive/ITransactionManager.cs +++ b/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Receive/ITransactionManager.cs @@ -11,5 +11,5 @@ public interface ITransactionManager : IDisposable void StartSubtransaction(); // POC improve how the error handling behaviour is selected - void StartTransaction(bool enableFailurePreprocessor = false); + void StartTransaction(bool enableFailurePreprocessor = false, string name = "Speckle Transaction"); } diff --git a/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Receive/RevitContextAccessor.cs b/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Receive/RevitContextAccessor.cs index 297587790..e69de29bb 100644 --- a/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Receive/RevitContextAccessor.cs +++ b/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Receive/RevitContextAccessor.cs @@ -1,9 +0,0 @@ -using Revit.Async; -using Speckle.Connectors.Common.Operations; - -namespace Speckle.Connectors.Revit.Operations.Receive; - -internal sealed class RevitContextAccessor : ISyncToThread -{ - public Task RunOnThread(Func func) => RevitTask.RunAsync(func); -} diff --git a/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Receive/RevitHostObjectBuilder.cs b/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Receive/RevitHostObjectBuilder.cs index dafc18681..d49a5ff20 100644 --- a/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Receive/RevitHostObjectBuilder.cs +++ b/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Receive/RevitHostObjectBuilder.cs @@ -4,14 +4,21 @@ using Speckle.Connectors.Common.Builders; using Speckle.Connectors.Common.Conversion; using Speckle.Connectors.Common.Instances; +using Speckle.Connectors.Common.Operations; using Speckle.Connectors.Common.Operations.Receive; using Speckle.Connectors.Revit.HostApp; using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Converters.RevitShared; using Speckle.Converters.RevitShared.Helpers; using Speckle.Converters.RevitShared.Settings; +using Speckle.DoubleNumerics; +using Speckle.Objects; +using Speckle.Objects.Geometry; using Speckle.Sdk; using Speckle.Sdk.Logging; using Speckle.Sdk.Models; +using Transform = Speckle.Objects.Other.Transform; namespace Speckle.Connectors.Revit.Operations.Receive; @@ -19,13 +26,16 @@ internal sealed class RevitHostObjectBuilder : IHostObjectBuilder, IDisposable { private readonly IRootToHostConverter _converter; private readonly IConverterSettingsStore _converterSettings; - private readonly RevitMaterialCacheSingleton _revitMaterialCacheSingleton; + private readonly RevitToHostCacheSingleton _revitToHostCacheSingleton; private readonly ITransactionManager _transactionManager; private readonly ILocalToGlobalUnpacker _localToGlobalUnpacker; - private readonly LocalToGlobalConverterUtils _localToGlobalConverterUtils; private readonly RevitGroupBaker _groupBaker; private readonly RevitMaterialBaker _materialBaker; private readonly ILogger _logger; + private readonly ITypedConverter< + (Base atomicObject, List matrix), + DirectShape + > _localToGlobalDirectShapeConverter; private readonly RootObjectUnpacker _rootObjectUnpacker; private readonly ISdkActivityFactory _activityFactory; @@ -36,24 +46,24 @@ public RevitHostObjectBuilder( ITransactionManager transactionManager, ISdkActivityFactory activityFactory, ILocalToGlobalUnpacker localToGlobalUnpacker, - LocalToGlobalConverterUtils localToGlobalConverterUtils, RevitGroupBaker groupManager, RevitMaterialBaker materialBaker, RootObjectUnpacker rootObjectUnpacker, ILogger logger, - RevitMaterialCacheSingleton revitMaterialCacheSingleton + RevitToHostCacheSingleton revitToHostCacheSingleton, + ITypedConverter<(Base atomicObject, List matrix), DirectShape> localToGlobalDirectShapeConverter ) { _converter = converter; _converterSettings = converterSettings; _transactionManager = transactionManager; _localToGlobalUnpacker = localToGlobalUnpacker; - _localToGlobalConverterUtils = localToGlobalConverterUtils; _groupBaker = groupManager; _materialBaker = materialBaker; _rootObjectUnpacker = rootObjectUnpacker; _logger = logger; - _revitMaterialCacheSingleton = revitMaterialCacheSingleton; + _revitToHostCacheSingleton = revitToHostCacheSingleton; + _localToGlobalDirectShapeConverter = localToGlobalDirectShapeConverter; _activityFactory = activityFactory; } @@ -61,7 +71,7 @@ public Task Build( Base rootObject, string projectName, string modelName, - Action? onOperationProgressed, + IProgress onOperationProgressed, CancellationToken cancellationToken ) => RevitTask.RunAsync(() => BuildSync(rootObject, projectName, modelName, onOperationProgressed, cancellationToken)); @@ -70,33 +80,29 @@ private HostObjectBuilderResult BuildSync( Base rootObject, string projectName, string modelName, - Action? onOperationProgressed, + IProgress onOperationProgressed, CancellationToken cancellationToken ) { var baseGroupName = $"Project {projectName}: Model {modelName}"; // TODO: unify this across connectors! - onOperationProgressed?.Invoke("Converting", null); + onOperationProgressed.Report(new("Converting", null)); using var activity = _activityFactory.Start("Build"); // 0 - Clean then Rock n Roll! 🎸 - using TransactionGroup preReceiveCleanTransaction = new(_converterSettings.Current.Document, "Pre-receive clean"); - preReceiveCleanTransaction.Start(); - _transactionManager.StartTransaction(true); - - try - { - PreReceiveDeepClean(baseGroupName); - } - catch (Exception ex) when (!ex.IsFatal()) { - _logger.LogError(ex, "Failed to clean up before receive in Revit"); - } + _activityFactory.Start("Pre receive clean"); + _transactionManager.StartTransaction(true, "Pre receive clean"); + try + { + PreReceiveDeepClean(baseGroupName); + } + catch (Exception ex) when (!ex.IsFatal()) + { + _logger.LogError(ex, "Failed to clean up before receive in Revit"); + } - using (var _ = _activityFactory.Start("Commit")) - { _transactionManager.CommitTransaction(); - preReceiveCleanTransaction.Assimilate(); } // 1 - Unpack objects and proxies from root commit object @@ -106,57 +112,56 @@ CancellationToken cancellationToken unpackedRoot.ObjectsToConvert.ToList() ); - using TransactionGroup transactionGroup = - new(_converterSettings.Current.Document, $"Received data from {projectName}"); - transactionGroup.Start(); - _transactionManager.StartTransaction(); - + // 2 - Bake materials if (unpackedRoot.RenderMaterialProxies != null) { + _transactionManager.StartTransaction(true, "Baking materials"); _materialBaker.MapLayersRenderMaterials(unpackedRoot); - // NOTE: do not set _contextStack.RenderMaterialProxyCache directly, things stop working. Ogu/Dim do not know why :) not a problem as we hopefully will refactor some of these hacks out. var map = _materialBaker.BakeMaterials(unpackedRoot.RenderMaterialProxies, baseGroupName); foreach (var kvp in map) { - _revitMaterialCacheSingleton.ObjectIdAndMaterialIndexMap.Add(kvp.Key, kvp.Value); + _revitToHostCacheSingleton.MaterialsByObjectId.Add(kvp.Key, kvp.Value); } + _transactionManager.CommitTransaction(); } - var conversionResults = BakeObjects(localToGlobalMaps, onOperationProgressed, cancellationToken); - - using (var _ = _activityFactory.Start("Commit")) + // 3 - Bake objects + ( + HostObjectBuilderResult builderResult, + List<(DirectShape res, string applicationId)> postBakePaintTargets + ) conversionResults; { + using var _ = _activityFactory.Start("Baking objects"); + _transactionManager.StartTransaction(true, "Baking objects"); + conversionResults = BakeObjects(localToGlobalMaps, onOperationProgressed, cancellationToken); _transactionManager.CommitTransaction(); - transactionGroup.Assimilate(); } - using TransactionGroup createGroupTransaction = new(_converterSettings.Current.Document, "Creating group"); - createGroupTransaction.Start(); - _transactionManager.StartTransaction(true); - - try + // 4 - Paint solids { - _groupBaker.BakeGroups(baseGroupName); - } - catch (Exception ex) when (!ex.IsFatal()) - { - _logger.LogError(ex, "Failed to create group after receiving elements in Revit"); + using var _ = _activityFactory.Start("Painting solids"); + _transactionManager.StartTransaction(true, "Painting solids"); + PostBakePaint(conversionResults.postBakePaintTargets); + _transactionManager.CommitTransaction(); } - using (var _ = _activityFactory.Start("Commit")) + // 5 - Create group { + using var _ = _activityFactory.Start("Grouping"); + _transactionManager.StartTransaction(true, "Grouping"); + _groupBaker.BakeGroupForTopLevel(baseGroupName); _transactionManager.CommitTransaction(); - createGroupTransaction.Assimilate(); } - _revitMaterialCacheSingleton.ObjectIdAndMaterialIndexMap.Clear(); // Massive hack! - - return conversionResults; + return conversionResults.builderResult; } - private HostObjectBuilderResult BakeObjects( + private ( + HostObjectBuilderResult builderResult, + List<(DirectShape res, string applicationId)> postBakePaintTargets + ) BakeObjects( List localToGlobalMaps, - Action? onOperationProgressed, + IProgress onOperationProgressed, CancellationToken cancellationToken ) { @@ -165,47 +170,113 @@ CancellationToken cancellationToken var bakedObjectIds = new List(); int count = 0; + var postBakePaintTargets = new List<(DirectShape res, string applicationId)>(); + foreach (LocalToGlobalMap localToGlobalMap in localToGlobalMaps) { cancellationToken.ThrowIfCancellationRequested(); try { using var activity = _activityFactory.Start("BakeObject"); - var atomicObject = _localToGlobalConverterUtils.TransformObjects( - localToGlobalMap.AtomicObject, - localToGlobalMap.Matrix - ); - var result = _converter.Convert(atomicObject); - onOperationProgressed?.Invoke("Converting", (double)++count / localToGlobalMaps.Count); - - // Note: our current converter always returns a DS for now - if (result is DirectShape ds) + + // POC hack of the ages: try to pre transform curves before baking + // we need to bypass the local to global converter as there we don't have access to what we want. that service will/should stop existing. + if ( + localToGlobalMap.AtomicObject is ITransformable transformable and ICurve + && localToGlobalMap.Matrix.Count > 0 + && localToGlobalMap.AtomicObject["units"] is string units + ) + { + ITransformable? newTransformable = null; + foreach (var mat in localToGlobalMap.Matrix) + { + transformable.TransformTo(new Transform(mat, units), out newTransformable); + } + + localToGlobalMap.AtomicObject = (newTransformable as Base)!; + localToGlobalMap.Matrix = new(); // flush out the list, as we've applied the transforms already + } + + // actual conversion happens here! + var result = _converter.Convert(localToGlobalMap.AtomicObject); + onOperationProgressed.Report(new("Converting", (double)++count / localToGlobalMaps.Count)); + if (result is DirectShapeDefinitionWrapper) { - bakedObjectIds.Add(ds.UniqueId.ToString()); - _groupBaker.AddToGroupMapping(localToGlobalMap.TraversalContext, ds); + // direct shape creation happens here + DirectShape directShapes = _localToGlobalDirectShapeConverter.Convert( + (localToGlobalMap.AtomicObject, localToGlobalMap.Matrix) + ); + + bakedObjectIds.Add(directShapes.UniqueId); + _groupBaker.AddToTopLevelGroup(directShapes); + + if (localToGlobalMap.AtomicObject is IRawEncodedObject and Base myBase) + { + postBakePaintTargets.Add((directShapes, myBase.applicationId ?? myBase.id)); + } + + conversionResults.Add( + new(Status.SUCCESS, localToGlobalMap.AtomicObject, directShapes.UniqueId, "Direct Shape") + ); } else { - throw new SpeckleConversionException($"Failed to cast {result.GetType()} to Direct Shape."); + throw new SpeckleConversionException( + $"Failed to cast {result.GetType()} to direct shape definition wrapper." + ); } - conversionResults.Add(new(Status.SUCCESS, atomicObject, ds.UniqueId, "Direct Shape")); } catch (Exception ex) when (!ex.IsFatal()) { conversionResults.Add(new(Status.ERROR, localToGlobalMap.AtomicObject, null, null, ex)); + _logger.LogError(ex, $"Failed to convert object of type {localToGlobalMap.AtomicObject.speckle_type}"); + } + } + return (new(bakedObjectIds, conversionResults), postBakePaintTargets); + } + + /// + /// We're using this to assign materials to solids coming via the shape importer. + /// + /// + private void PostBakePaint(List<(DirectShape res, string applicationId)> paintTargets) + { + foreach (var (res, applicationId) in paintTargets) + { + var elGeometry = res.get_Geometry(new Options() { DetailLevel = ViewDetailLevel.Undefined }); + var materialId = ElementId.InvalidElementId; + if (_revitToHostCacheSingleton.MaterialsByObjectId.TryGetValue(applicationId, out var mappedElementId)) + { + materialId = mappedElementId; + } + + if (materialId == ElementId.InvalidElementId) + { + continue; + } + + // NOTE: some geometries fail to convert as solids, and the api defaults back to meshes (from the shape importer). These cannot be painted, so don't bother. + foreach (var geo in elGeometry) + { + if (geo is Solid s) + { + foreach (Face face in s.Faces) + { + _converterSettings.Current.Document.Paint(res.Id, face, materialId); + } + } } } - return new(bakedObjectIds, conversionResults); } private void PreReceiveDeepClean(string baseGroupName) { + DirectShapeLibrary.GetDirectShapeLibrary(_converterSettings.Current.Document).Reset(); // Note: this needs to be cleared, as it is being used in the converter + + _revitToHostCacheSingleton.MaterialsByObjectId.Clear(); // Massive hack! _groupBaker.PurgeGroups(baseGroupName); _materialBaker.PurgeMaterials(baseGroupName); } - public void Dispose() - { - _transactionManager?.Dispose(); - } + public void Dispose() => _transactionManager?.Dispose(); } diff --git a/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Receive/TransactionManager.cs b/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Receive/TransactionManager.cs index 2ed7f7ab5..71fcb7590 100644 --- a/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Receive/TransactionManager.cs +++ b/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Receive/TransactionManager.cs @@ -30,11 +30,11 @@ IFailuresPreprocessor errorPreprocessingService #pragma warning restore CA2213 // Disposable fields should be disposed // POC find a better way to use IFailuresPreprocessor - public void StartTransaction(bool enableFailurePreprocessor = false) + public void StartTransaction(bool enableFailurePreprocessor = false, string name = "Speckle Transaction") { if (_transaction == null || !_transaction.IsValidObject || _transaction.GetStatus() != TransactionStatus.Started) { - _transaction = new Transaction(Document, "Speckle Transaction"); + _transaction = new Transaction(Document, name); if (enableFailurePreprocessor) { diff --git a/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Send/RevitRootObjectBuilder.cs b/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Send/RevitRootObjectBuilder.cs index b832de98f..0cb8b15aa 100644 --- a/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Send/RevitRootObjectBuilder.cs +++ b/Connectors/Revit/Speckle.Connectors.RevitShared/Operations/Send/RevitRootObjectBuilder.cs @@ -23,11 +23,10 @@ public class RevitRootObjectBuilder : IRootObjectBuilder // POC: SendSelection and RevitConversionContextStack should be interfaces, former needs interfaces private readonly IRootToSpeckleConverter _converter; private readonly IConverterSettingsStore _converterSettings; - private readonly Collection _rootObject; private readonly ISendConversionCache _sendConversionCache; private readonly ElementUnpacker _elementUnpacker; private readonly SendCollectionManager _sendCollectionManager; - private readonly RevitMaterialCacheSingleton _revitMaterialCacheSingleton; + private readonly RevitToSpeckleCacheSingleton _revitToSpeckleCacheSingleton; private readonly ILogger _logger; private readonly ParameterDefinitionHandler _parameterDefinitionHandler; @@ -39,7 +38,7 @@ public RevitRootObjectBuilder( SendCollectionManager sendCollectionManager, ILogger logger, ParameterDefinitionHandler parameterDefinitionHandler, - RevitMaterialCacheSingleton revitMaterialCacheSingleton + RevitToSpeckleCacheSingleton revitToSpeckleCacheSingleton ) { _converter = converter; @@ -47,21 +46,15 @@ RevitMaterialCacheSingleton revitMaterialCacheSingleton _sendConversionCache = sendConversionCache; _elementUnpacker = elementUnpacker; _sendCollectionManager = sendCollectionManager; - _revitMaterialCacheSingleton = revitMaterialCacheSingleton; + _revitToSpeckleCacheSingleton = revitToSpeckleCacheSingleton; _logger = logger; _parameterDefinitionHandler = parameterDefinitionHandler; - - _rootObject = new Collection() - { - name = _converterSettings.Current.Document.PathName.Split('\\').Last().Split('.').First() - }; - _rootObject["units"] = _converterSettings.Current.SpeckleUnits; } public async Task Build( IReadOnlyList objects, SendInfo sendInfo, - Action? onOperationProgressed = null, + IProgress onOperationProgressed, CancellationToken ct = default ) { @@ -72,6 +65,11 @@ public async Task Build( throw new SpeckleException("Family Environment documents are not supported."); } + // 0 - Init the root + Collection rootObject = + new() { name = _converterSettings.Current.Document.PathName.Split('\\').Last().Split('.').First() }; + rootObject["units"] = _converterSettings.Current.SpeckleUnits; + var revitElements = new List(); // Convert ids to actual revit elements @@ -116,7 +114,8 @@ public async Task Build( converted.applicationId = applicationId; } - var collection = _sendCollectionManager.GetAndCreateObjectHostCollection(revitElement, _rootObject); + var collection = _sendCollectionManager.GetAndCreateObjectHostCollection(revitElement, rootObject); + collection.elements.Add(converted); results.Add(new(Status.SUCCESS, applicationId, sourceType, converted)); } @@ -126,7 +125,7 @@ public async Task Build( results.Add(new(Status.ERROR, applicationId, sourceType, null, ex)); } - onOperationProgressed?.Invoke("Converting", (double)++countProgress / atomicObjects.Count); + onOperationProgressed.Report(new("Converting", (double)++countProgress / atomicObjects.Count)); } if (results.All(x => x.Status == Status.ERROR)) @@ -135,11 +134,11 @@ public async Task Build( } var idsAndSubElementIds = _elementUnpacker.GetElementsAndSubelementIdsFromAtomicObjects(atomicObjects); - var materialProxies = _revitMaterialCacheSingleton.GetRenderMaterialProxyListForObjects(idsAndSubElementIds); - _rootObject[ProxyKeys.RENDER_MATERIAL] = materialProxies; + var materialProxies = _revitToSpeckleCacheSingleton.GetRenderMaterialProxyListForObjects(idsAndSubElementIds); + rootObject[ProxyKeys.RENDER_MATERIAL] = materialProxies; // NOTE: these are currently not used anywhere, so we could even skip them (?). - _rootObject[ProxyKeys.PARAMETER_DEFINITIONS] = _parameterDefinitionHandler.Definitions; + rootObject[ProxyKeys.PARAMETER_DEFINITIONS] = _parameterDefinitionHandler.Definitions; - return new RootObjectBuilderResult(_rootObject, results); + return new RootObjectBuilderResult(rootObject, results); } } diff --git a/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/RevitIdleManager.cs b/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/RevitIdleManager.cs index b4531ddf4..db20cfce0 100644 --- a/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/RevitIdleManager.cs +++ b/Connectors/Revit/Speckle.Connectors.RevitShared/Plugin/RevitIdleManager.cs @@ -2,43 +2,41 @@ using Autodesk.Revit.UI.Events; using Speckle.Connectors.DUI.Bridge; using Speckle.Converters.RevitShared.Helpers; -using Speckle.InterfaceGenerator; -using Speckle.Sdk; using Speckle.Sdk.Common; namespace Speckle.Connectors.Revit.Plugin; -[GenerateAutoInterface] -public sealed class RevitIdleManager(RevitContext revitContext, IIdleCallManager idleCallManager) : IRevitIdleManager +public interface IRevitIdleManager : IAppIdleManager +{ + public void RunAsync(Action action); +} + +public sealed class RevitIdleManager( + RevitContext revitContext, + IIdleCallManager idleCallManager, + ITopLevelExceptionHandler topLevelExceptionHandler +) : AppIdleManager(idleCallManager), IRevitIdleManager { private readonly UIApplication _uiApplication = revitContext.UIApplication.NotNull(); + private readonly IIdleCallManager _idleCallManager = idleCallManager; - /// - /// Subscribe deferred action to Idling event to run it whenever Revit becomes idle. - /// - /// Action to call whenever Revit becomes Idle. - /// some events in host app are trigerred many times, we might get 10x per object - /// Making this more like a deferred action, so we don't update the UI many times - public void SubscribeToIdle(string id, Action action) => - idleCallManager.SubscribeToIdle( - id, - action, - () => + protected override void AddEvent() + { + topLevelExceptionHandler.CatchUnhandled(() => + { + try { - try - { - _uiApplication.Idling += RevitAppOnIdle; - } - catch (Exception e) when (!e.IsFatal()) - { - // TODO: wrap this guy in the top level exception handler (?) - // This happens very rarely, see previous report [CNX-125: Autodesk.Revit.Exceptions.InvalidOperationException: Can not subscribe to an event during execution of that event!](https://linear.app/speckle/issue/CNX-125/autodeskrevitexceptionsinvalidoperationexception-can-not-subscribe-to) - } + _uiApplication.Idling += RevitAppOnIdle; } - ); + catch (Autodesk.Revit.Exceptions.InvalidOperationException) + { + // This happens very rarely, see previous report [CNX-125: Autodesk.Revit.Exceptions.InvalidOperationException: Can not subscribe to an event during execution of that event!](https://linear.app/speckle/issue/CNX-125/autodeskrevitexceptionsinvalidoperationexception-can-not-subscribe-to) + } + }); + } private void RevitAppOnIdle(object? sender, IdlingEventArgs e) => - idleCallManager.AppOnIdle(() => _uiApplication.Idling -= RevitAppOnIdle); + _idleCallManager.AppOnIdle(() => _uiApplication.Idling -= RevitAppOnIdle); public void RunAsync(Action action) { diff --git a/Connectors/Revit/Speckle.Connectors.RevitShared/Speckle.Connectors.RevitShared.projitems b/Connectors/Revit/Speckle.Connectors.RevitShared/Speckle.Connectors.RevitShared.projitems index e4e01eb32..4b450c7a1 100644 --- a/Connectors/Revit/Speckle.Connectors.RevitShared/Speckle.Connectors.RevitShared.projitems +++ b/Connectors/Revit/Speckle.Connectors.RevitShared/Speckle.Connectors.RevitShared.projitems @@ -33,7 +33,6 @@ - diff --git a/Connectors/Rhino/Speckle.Connectors.Rhino7/Speckle.Connectors.Rhino7.csproj b/Connectors/Rhino/Speckle.Connectors.Rhino7/Speckle.Connectors.Rhino7.csproj index 7f5b6168c..241571403 100644 --- a/Connectors/Rhino/Speckle.Connectors.Rhino7/Speckle.Connectors.Rhino7.csproj +++ b/Connectors/Rhino/Speckle.Connectors.Rhino7/Speckle.Connectors.Rhino7.csproj @@ -4,6 +4,12 @@ Debug;Release;Local 7 $(DefineConstants);RHINO7;RHINO7_OR_GREATER + .rhp + $(ProgramFiles)\Rhino $(RhinoVersion)\System\Rhino.exe + true + true + true + true @@ -17,5 +23,10 @@ + + + + + diff --git a/Connectors/Rhino/Speckle.Connectors.Rhino7/packages.lock.json b/Connectors/Rhino/Speckle.Connectors.Rhino7/packages.lock.json index d8bdfd8c6..8964caa3e 100644 --- a/Connectors/Rhino/Speckle.Connectors.Rhino7/packages.lock.json +++ b/Connectors/Rhino/Speckle.Connectors.Rhino7/packages.lock.json @@ -2,6 +2,15 @@ "version": 2, "dependencies": { ".NETFramework,Version=v4.8": { + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3" + } + }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -146,6 +155,11 @@ "System.Runtime.CompilerServices.Unsafe": "4.5.1" } }, + "Microsoft.NETFramework.ReferenceAssemblies.net48": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ==" + }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", @@ -269,8 +283,8 @@ "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )", - "Speckle.Sdk": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )", + "Speckle.Sdk": "[3.1.0-dev.162, )" } }, "speckle.connectors.dui": { @@ -278,7 +292,7 @@ "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", "Speckle.Connectors.Common": "[1.0.0, )", - "Speckle.Sdk": "[3.1.0-dev.145, )", + "Speckle.Sdk": "[3.1.0-dev.162, )", "System.Threading.Tasks.Dataflow": "[6.0.0, )" } }, @@ -296,7 +310,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )" } }, "speckle.converters.rhino7": { @@ -341,18 +355,18 @@ }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "gpENXDB2i8SiXJZwrLVfB3BwKF1FHUSpPiw0BQ3Hl0V3821UWZjPYw5XK694oxJlVAeEMRvrMZMGsWOcK+3nMw==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "1fyaPjC1kVtroKfoXI7edJX0Uho/TNRWG9mtmIQ7jKhX1zElrJPSoH8zEIMqckZhGmwUrYjlQFPssASNCP721Q==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.145" + "Speckle.Sdk": "3.1.0-dev.162" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "YZLtitiVIxl12pNTXaB9gU+paNwFe++WBw6oIN6Rs5hFPialUgeKbpjh16Zq6wcptjBpvg0CHq29+d456QUAJQ==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "ulT3XNgqIQSmpQFK5YEEwRq4RA6emXBKQHnZxtCz37SJin1PnVpH9m8vOAF9FmaWsgSAfjhhjIZDruMu1oKxSg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", diff --git a/Connectors/Rhino/Speckle.Connectors.Rhino8/Properties/launchSettings.json b/Connectors/Rhino/Speckle.Connectors.Rhino8/Properties/launchSettings.json index af44b2ac4..d842b29f8 100644 --- a/Connectors/Rhino/Speckle.Connectors.Rhino8/Properties/launchSettings.json +++ b/Connectors/Rhino/Speckle.Connectors.Rhino8/Properties/launchSettings.json @@ -3,7 +3,7 @@ "Speckle.Connectors.Rhino8": { "commandName": "Executable", "executablePath": "C:\\Program Files\\Rhino 8\\System\\Rhino.exe", - "commandLineArgs": "\"$(TargetDir)Speckle.Connectors.Rhino8.rhp\"" + "commandLineArgs": "\"$(TargetDir)Speckle.Connectors.Rhino8.rhp\" /netfx" } } } diff --git a/Connectors/Rhino/Speckle.Connectors.Rhino8/Speckle.Connectors.Rhino8.csproj b/Connectors/Rhino/Speckle.Connectors.Rhino8/Speckle.Connectors.Rhino8.csproj index 6d1450e11..55680b7f4 100644 --- a/Connectors/Rhino/Speckle.Connectors.Rhino8/Speckle.Connectors.Rhino8.csproj +++ b/Connectors/Rhino/Speckle.Connectors.Rhino8/Speckle.Connectors.Rhino8.csproj @@ -4,6 +4,12 @@ Debug;Release;Local 8 $(DefineConstants);RHINO8;RHINO7_OR_GREATER;RHIN08_OR_GREATER + .rhp + $(ProgramFiles)\Rhino $(RhinoVersion)\System\Rhino.exe + true + true + true + true @@ -16,6 +22,11 @@ + + + + + diff --git a/Connectors/Rhino/Speckle.Connectors.Rhino8/packages.lock.json b/Connectors/Rhino/Speckle.Connectors.Rhino8/packages.lock.json index b7832193a..e46e36d3e 100644 --- a/Connectors/Rhino/Speckle.Connectors.Rhino8/packages.lock.json +++ b/Connectors/Rhino/Speckle.Connectors.Rhino8/packages.lock.json @@ -2,6 +2,15 @@ "version": 2, "dependencies": { ".NETFramework,Version=v4.8": { + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3" + } + }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -146,6 +155,11 @@ "System.Runtime.CompilerServices.Unsafe": "4.5.1" } }, + "Microsoft.NETFramework.ReferenceAssemblies.net48": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ==" + }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", @@ -269,8 +283,8 @@ "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )", - "Speckle.Sdk": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )", + "Speckle.Sdk": "[3.1.0-dev.162, )" } }, "speckle.connectors.dui": { @@ -278,7 +292,7 @@ "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", "Speckle.Connectors.Common": "[1.0.0, )", - "Speckle.Sdk": "[3.1.0-dev.145, )", + "Speckle.Sdk": "[3.1.0-dev.162, )", "System.Threading.Tasks.Dataflow": "[6.0.0, )" } }, @@ -296,7 +310,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )" } }, "speckle.converters.rhino8": { @@ -341,18 +355,18 @@ }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "gpENXDB2i8SiXJZwrLVfB3BwKF1FHUSpPiw0BQ3Hl0V3821UWZjPYw5XK694oxJlVAeEMRvrMZMGsWOcK+3nMw==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "1fyaPjC1kVtroKfoXI7edJX0Uho/TNRWG9mtmIQ7jKhX1zElrJPSoH8zEIMqckZhGmwUrYjlQFPssASNCP721Q==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.145" + "Speckle.Sdk": "3.1.0-dev.162" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "YZLtitiVIxl12pNTXaB9gU+paNwFe++WBw6oIN6Rs5hFPialUgeKbpjh16Zq6wcptjBpvg0CHq29+d456QUAJQ==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "ulT3XNgqIQSmpQFK5YEEwRq4RA6emXBKQHnZxtCz37SJin1PnVpH9m8vOAF9FmaWsgSAfjhhjIZDruMu1oKxSg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Bindings/RhinoBasicConnectorBinding.cs b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Bindings/RhinoBasicConnectorBinding.cs index 32596d9cc..16d28833c 100644 --- a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Bindings/RhinoBasicConnectorBinding.cs +++ b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Bindings/RhinoBasicConnectorBinding.cs @@ -11,7 +11,7 @@ namespace Speckle.Connectors.Rhino.Bindings; -public class RhinoBasicConnectorBinding : IBasicConnectorBinding +public sealed class RhinoBasicConnectorBinding : IBasicConnectorBinding { public string Name => "baseBinding"; public IBrowserBridge Parent { get; } @@ -32,9 +32,10 @@ ISpeckleApplication speckleApplication Commands = new BasicConnectorBindingCommands(parent); _store.DocumentChanged += (_, _) => - { - Commands.NotifyDocumentChanged(); - }; + parent.TopLevelExceptionHandler.FireAndForget(async () => + { + await Commands.NotifyDocumentChanged().ConfigureAwait(false); + }); } public string GetConnectorVersion() => _speckleApplication.SpeckleVersion; @@ -60,7 +61,7 @@ ISpeckleApplication speckleApplication public void RemoveModel(ModelCard model) => _store.RemoveModel(model); - public void HighlightObjects(List objectIds) + public Task HighlightObjects(IReadOnlyList objectIds) { var objects = GetObjectsFromIds(objectIds); @@ -73,9 +74,10 @@ public void HighlightObjects(List objectIds) } HighlightObjectsOnView(objects.rhinoObjects, objects.groups); + return Task.CompletedTask; } - public void HighlightModel(string modelCardId) + public async Task HighlightModel(string modelCardId) { var objectIds = new List(); var myModel = _store.GetModelById(modelCardId); @@ -92,7 +94,9 @@ public void HighlightModel(string modelCardId) if (objectIds.Count == 0) { - Commands.SetModelError(modelCardId, new OperationCanceledException("No objects found to highlight.")); + await Commands + .SetModelError(modelCardId, new OperationCanceledException("No objects found to highlight.")) + .ConfigureAwait(false); return; } @@ -102,14 +106,16 @@ public void HighlightModel(string modelCardId) if (objects.rhinoObjects.Count == 0 && objects.groups.Count == 0) { - Commands.SetModelError(modelCardId, new OperationCanceledException("No objects found to highlight.")); + await Commands + .SetModelError(modelCardId, new OperationCanceledException("No objects found to highlight.")) + .ConfigureAwait(false); return; } HighlightObjectsOnView(objects.rhinoObjects, objects.groups); } - private (List rhinoObjects, List groups) GetObjectsFromIds(List objectIds) + private (List rhinoObjects, List groups) GetObjectsFromIds(IReadOnlyList objectIds) { List rhinoObjects = objectIds .Select((id) => RhinoDoc.ActiveDoc.Objects.FindId(new Guid(id))) diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Bindings/RhinoReceiveBinding.cs b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Bindings/RhinoReceiveBinding.cs index a5b1e0e2a..dcf60ab73 100644 --- a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Bindings/RhinoReceiveBinding.cs +++ b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Bindings/RhinoReceiveBinding.cs @@ -75,23 +75,15 @@ public async Task Receive(string modelCardId) .ServiceProvider.GetRequiredService() .Execute( modelCard.GetReceiveInfo(_speckleApplication.Slug), - cancellationToken, - (status, progress) => - _operationProgressManager.SetModelProgress( - Parent, - modelCardId, - new ModelCardProgress(modelCardId, status, progress), - cancellationToken - ) + _operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationToken), + cancellationToken ) .ConfigureAwait(false); modelCard.BakedObjectIds = conversionResults.BakedObjectIds.ToList(); - Commands.SetModelReceiveResult( - modelCardId, - conversionResults.BakedObjectIds, - conversionResults.ConversionResults - ); + await Commands + .SetModelReceiveResult(modelCardId, conversionResults.BakedObjectIds, conversionResults.ConversionResults) + .ConfigureAwait(false); } catch (OperationCanceledException) { @@ -103,7 +95,7 @@ public async Task Receive(string modelCardId) catch (Exception ex) when (!ex.IsFatal()) // UX reasons - we will report operation exceptions as model card error. We may change this later when we have more exception documentation { _logger.LogModelCardHandledError(ex); - Commands.SetModelError(modelCardId, ex); + await Commands.SetModelError(modelCardId, ex).ConfigureAwait(false); } } diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Bindings/RhinoSelectionBinding.cs b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Bindings/RhinoSelectionBinding.cs index f5fc16c08..d2679b8b3 100644 --- a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Bindings/RhinoSelectionBinding.cs +++ b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Bindings/RhinoSelectionBinding.cs @@ -2,19 +2,18 @@ using Rhino.DocObjects; using Speckle.Connectors.DUI.Bindings; using Speckle.Connectors.DUI.Bridge; -using Speckle.Connectors.Rhino.HostApp; namespace Speckle.Connectors.Rhino.Bindings; public class RhinoSelectionBinding : ISelectionBinding { - private readonly IRhinoIdleManager _idleManager; + private readonly IAppIdleManager _idleManager; private const string SELECTION_EVENT = "setSelection"; public string Name => "selectionBinding"; public IBrowserBridge Parent { get; } - public RhinoSelectionBinding(IRhinoIdleManager idleManager, IBrowserBridge parent) + public RhinoSelectionBinding(IAppIdleManager idleManager, IBrowserBridge parent) { _idleManager = idleManager; Parent = parent; diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Bindings/RhinoSendBinding.cs b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Bindings/RhinoSendBinding.cs index d7912654a..0670789a2 100644 --- a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Bindings/RhinoSendBinding.cs +++ b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Bindings/RhinoSendBinding.cs @@ -15,11 +15,11 @@ using Speckle.Connectors.DUI.Models.Card; using Speckle.Connectors.DUI.Models.Card.SendFilter; using Speckle.Connectors.DUI.Settings; -using Speckle.Connectors.Rhino.HostApp; using Speckle.Converters.Common; using Speckle.Converters.Rhino; using Speckle.Sdk; using Speckle.Sdk.Common; +using Speckle.Sdk.Logging; namespace Speckle.Connectors.Rhino.Bindings; @@ -30,7 +30,7 @@ public sealed class RhinoSendBinding : ISendBinding public IBrowserBridge Parent { get; } private readonly DocumentModelStore _store; - private readonly IRhinoIdleManager _idleManager; + private readonly IAppIdleManager _idleManager; private readonly IServiceProvider _serviceProvider; private readonly List _sendFilters; private readonly CancellationManager _cancellationManager; @@ -40,6 +40,7 @@ public sealed class RhinoSendBinding : ISendBinding private readonly ITopLevelExceptionHandler _topLevelExceptionHandler; private readonly IRhinoConversionSettingsFactory _rhinoConversionSettingsFactory; private readonly ISpeckleApplication _speckleApplication; + private readonly ISdkActivityFactory _activityFactory; /// /// 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 +52,7 @@ public sealed class RhinoSendBinding : ISendBinding public RhinoSendBinding( DocumentModelStore store, - IRhinoIdleManager idleManager, + IAppIdleManager idleManager, IBrowserBridge parent, IEnumerable sendFilters, IServiceProvider serviceProvider, @@ -60,7 +61,8 @@ public RhinoSendBinding( IOperationProgressManager operationProgressManager, ILogger logger, IRhinoConversionSettingsFactory rhinoConversionSettingsFactory, - ISpeckleApplication speckleApplication + ISpeckleApplication speckleApplication, + ISdkActivityFactory activityFactory ) { _store = store; @@ -76,6 +78,7 @@ ISpeckleApplication speckleApplication _topLevelExceptionHandler = parent.TopLevelExceptionHandler.Parent.TopLevelExceptionHandler; Parent = parent; Commands = new SendBindingUICommands(parent); // POC: Commands are tightly coupled with their bindings, at least for now, saves us injecting a factory. + _activityFactory = activityFactory; SubscribeToRhinoEvents(); } @@ -154,6 +157,7 @@ private void SubscribeToRhinoEvents() public async Task Send(string modelCardId) { + using var activity = _activityFactory.Start(); using var scope = _serviceProvider.CreateScope(); scope .ServiceProvider.GetRequiredService>() @@ -186,18 +190,14 @@ public async Task Send(string modelCardId) .Execute( rhinoObjects, modelCard.GetSendInfo(_speckleApplication.Slug), - (status, progress) => - _operationProgressManager.SetModelProgress( - Parent, - modelCardId, - new ModelCardProgress(modelCardId, status, progress), - cancellationToken - ), + _operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationToken), cancellationToken ) .ConfigureAwait(false); - Commands.SetModelSendResult(modelCardId, sendResult.RootObjId, sendResult.ConversionResults); + await Commands + .SetModelSendResult(modelCardId, sendResult.RootObjId, sendResult.ConversionResults) + .ConfigureAwait(false); } catch (OperationCanceledException) { @@ -209,7 +209,7 @@ public async Task Send(string modelCardId) catch (Exception ex) when (!ex.IsFatal()) // UX reasons - we will report operation exceptions as model card error. We may change this later when we have more exception documentation { _logger.LogModelCardHandledError(ex); - Commands.SetModelError(modelCardId, ex); + await Commands.SetModelError(modelCardId, ex).ConfigureAwait(false); } } @@ -218,7 +218,7 @@ public async Task Send(string modelCardId) /// /// 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. /// - private void RunExpirationChecks() + private async Task RunExpirationChecks() { var senders = _store.GetSenders(); string[] objectIdsList = ChangedObjectIds.Keys.ToArray(); // NOTE: could not copy to array happens here @@ -236,7 +236,7 @@ private void RunExpirationChecks() } } - Commands.SetModelsExpired(expiredSenderIds); + await Commands.SetModelsExpired(expiredSenderIds).ConfigureAwait(false); ChangedObjectIds = new(); } } diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/DependencyInjection/RhinoConnectorModule.cs b/Connectors/Rhino/Speckle.Connectors.RhinoShared/DependencyInjection/RhinoConnectorModule.cs new file mode 100644 index 000000000..e69de29bb diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoIdleManager.cs b/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoIdleManager.cs index 697d6cf2c..f536f5786 100644 --- a/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoIdleManager.cs +++ b/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoIdleManager.cs @@ -1,29 +1,20 @@ using Rhino; using Speckle.Connectors.DUI.Bridge; -using Speckle.InterfaceGenerator; namespace Speckle.Connectors.Rhino.HostApp; /// /// Rhino Idle Manager is a helper util to manage deferred actions. /// -[GenerateAutoInterface] -public class RhinoIdleManager(IIdleCallManager idleCallManager) : IRhinoIdleManager +public sealed class RhinoIdleManager(IIdleCallManager idleCallManager) : AppIdleManager(idleCallManager) { - /// - /// Subscribe deferred action to RhinoIdle event to run it whenever Rhino become idle. - /// - /// Action to call whenever Rhino become Idle. - public void SubscribeToIdle(string id, Action action) => - idleCallManager.SubscribeToIdle( - id, - action, - () => - { - RhinoApp.Idle += RhinoAppOnIdle; - } - ); + private readonly IIdleCallManager _idleCallManager = idleCallManager; + + protected override void AddEvent() + { + RhinoApp.Idle += RhinoAppOnIdle; + } private void RhinoAppOnIdle(object? sender, EventArgs e) => - idleCallManager.AppOnIdle(() => RhinoApp.Idle -= RhinoAppOnIdle); + _idleCallManager.AppOnIdle(() => RhinoApp.Idle -= RhinoAppOnIdle); } diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoInstanceBaker.cs b/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoInstanceBaker.cs index c3c971ba6..d8af6ecf8 100644 --- a/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoInstanceBaker.cs +++ b/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoInstanceBaker.cs @@ -4,10 +4,12 @@ using Rhino.Geometry; using Speckle.Connectors.Common.Conversion; using Speckle.Connectors.Common.Instances; +using Speckle.Connectors.Common.Operations; using Speckle.Connectors.Rhino.Extensions; using Speckle.DoubleNumerics; using Speckle.Sdk; using Speckle.Sdk.Common; +using Speckle.Sdk.Common.Exceptions; using Speckle.Sdk.Models; using Speckle.Sdk.Models.Collections; using Speckle.Sdk.Models.Instances; @@ -40,11 +42,11 @@ ILogger logger /// Instance definitions and instances that need creating. /// A dict mapping { original application id -> [resulting application ids post conversion] } /// - public BakeResult BakeInstances( - List<(Collection[] collectionPath, IInstanceComponent obj)> instanceComponents, + public async Task BakeInstances( + IReadOnlyCollection<(Collection[] collectionPath, IInstanceComponent obj)> instanceComponents, Dictionary> applicationIdMap, string baseLayerName, - Action? onOperationProgressed + IProgress onOperationProgressed ) { // var doc = _contextStack.Current.Document; @@ -62,7 +64,7 @@ public BakeResult BakeInstances( var consumedObjectIds = new List(); foreach (var (layerCollection, instanceOrDefinition) in sortedInstanceComponents) { - onOperationProgressed?.Invoke("Converting blocks", (double)++count / sortedInstanceComponents.Count); + onOperationProgressed.Report(new("Converting blocks", (double)++count / sortedInstanceComponents.Count)); try { if (instanceOrDefinition is InstanceDefinitionProxy definitionProxy) @@ -154,6 +156,7 @@ instanceOrDefinition is InstanceProxy instanceProxy } } + await Task.Yield(); return new(createdObjectIds, consumedObjectIds, conversionResults); } diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoLayerBaker.cs b/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoLayerBaker.cs index 14f4b62ea..0f956b9ed 100644 --- a/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoLayerBaker.cs +++ b/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoLayerBaker.cs @@ -46,8 +46,7 @@ public int GetAndCreateLayerFromPath(Collection[] collectionPath, string baseLay var currentLayerName = baseLayerName; var currentDocument = RhinoDoc.ActiveDoc; // POC: too much effort right now to wrap around the interfaced layers - Layer previousLayer = currentDocument.Layers.FindName(currentLayerName); - + Layer? previousLayer = currentDocument.Layers.FindName(currentLayerName); foreach (Collection collection in collectionPath) { currentLayerName += Layer.PathSeparator + collection.name; @@ -59,7 +58,7 @@ public int GetAndCreateLayerFromPath(Collection[] collectionPath, string baseLay } var cleanNewLayerName = collection.name.Replace("{", "").Replace("}", ""); - Layer newLayer = new() { Name = cleanNewLayerName, ParentLayerId = previousLayer.Id }; + Layer newLayer = new() { Name = cleanNewLayerName, ParentLayerId = previousLayer?.Id ?? Guid.Empty }; // set material if ( diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoMaterialBaker.cs b/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoMaterialBaker.cs index e89ca6ee8..b550c4592 100644 --- a/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoMaterialBaker.cs +++ b/Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoMaterialBaker.cs @@ -4,7 +4,7 @@ using Speckle.Converters.Rhino; using Speckle.Objects.Other; using Speckle.Sdk; -using Speckle.Sdk.Common; +using Speckle.Sdk.Common.Exceptions; using Material = Rhino.DocObjects.Material; namespace Speckle.Connectors.Rhino.HostApp; diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Operations/Receive/RhinoHostObjectBuilder.cs b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Operations/Receive/RhinoHostObjectBuilder.cs index 90a6a5783..d0d43edbb 100644 --- a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Operations/Receive/RhinoHostObjectBuilder.cs +++ b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Operations/Receive/RhinoHostObjectBuilder.cs @@ -3,6 +3,7 @@ using Rhino.Geometry; using Speckle.Connectors.Common.Builders; using Speckle.Connectors.Common.Conversion; +using Speckle.Connectors.Common.Operations; using Speckle.Connectors.Common.Operations.Receive; using Speckle.Connectors.Rhino.HostApp; using Speckle.Converters.Common; @@ -53,11 +54,11 @@ ISdkActivityFactory activityFactory _activityFactory = activityFactory; } - public Task Build( + public async Task Build( Base rootObject, string projectName, string modelName, - Action? onOperationProgressed, + IProgress onOperationProgressed, CancellationToken cancellationToken ) { @@ -89,7 +90,7 @@ CancellationToken cancellationToken } // 3 - Bake materials and colors, as they are used later down the line by layers and objects - onOperationProgressed?.Invoke("Converting materials and colors", null); + onOperationProgressed.Report(new("Converting materials and colors", null)); if (unpackedRoot.RenderMaterialProxies != null) { using var _ = _activityFactory.Start("Render Materials"); @@ -103,7 +104,7 @@ CancellationToken cancellationToken // 4 - Bake layers // See [CNX-325: Rhino: Change receive operation order to increase performance](https://linear.app/speckle/issue/CNX-325/rhino-change-receive-operation-order-to-increase-performance) - onOperationProgressed?.Invoke("Baking layers (redraw disabled)", null); + onOperationProgressed.Report(new("Baking layers (redraw disabled)", null)); using (var _ = _activityFactory.Start("Pre baking layers")) { using var layerNoDraw = new DisableRedrawScope(_converterSettings.Current.Document.Views); @@ -125,7 +126,7 @@ CancellationToken cancellationToken { using (var convertActivity = _activityFactory.Start("Converting object")) { - onOperationProgressed?.Invoke("Converting objects", (double)++count / atomicObjects.Count); + onOperationProgressed.Report(new("Converting objects", (double)++count / atomicObjects.Count)); try { // 1: get pre-created layer from cache in layer baker @@ -141,7 +142,19 @@ CancellationToken cancellationToken var guid = BakeObject(geometryBase, obj, layerIndex); conversionIds.Add(guid.ToString()); } - else if (result is IEnumerable<(object, Base)> fallbackConversionResult) + else if (result is List geometryBases) // one to many raw encoding case + { + // NOTE: I'm unhappy about this case (dim). It's needed as the raw encoder approach can hypothetically return + // multiple "geometry bases" - but this is not a fallback conversion. + // EXTRA NOTE: Oguzhan says i shouldn't be unhappy about this - it's a legitimate case + // EXTRA EXTRA NOTE: TY Ogu, i am no longer than unhappy about it. It's legit "mess". + foreach (var gb in geometryBases) + { + var guid = BakeObject(gb, obj, layerIndex); + conversionIds.Add(guid.ToString()); + } + } + else if (result is IEnumerable<(object, Base)> fallbackConversionResult) // one to many fallback conversion { var guids = BakeObjectsAsGroup(fallbackConversionResult, obj, layerIndex, baseLayerName); conversionIds.AddRange(guids.Select(id => id.ToString())); @@ -183,12 +196,9 @@ CancellationToken cancellationToken // 6 - Convert instances using (var _ = _activityFactory.Start("Converting instances")) { - var (createdInstanceIds, consumedObjectIds, instanceConversionResults) = _instanceBaker.BakeInstances( - instanceComponentsWithPath, - applicationIdMap, - baseLayerName, - onOperationProgressed - ); + var (createdInstanceIds, consumedObjectIds, instanceConversionResults) = await _instanceBaker + .BakeInstances(instanceComponentsWithPath, applicationIdMap, baseLayerName, onOperationProgressed) + .ConfigureAwait(false); bakedObjectIds.RemoveAll(id => consumedObjectIds.Contains(id)); // remove all objects that have been "consumed" bakedObjectIds.AddRange(createdInstanceIds); // add instance ids @@ -204,7 +214,7 @@ CancellationToken cancellationToken _converterSettings.Current.Document.Views.Redraw(); - return Task.FromResult(new HostObjectBuilderResult(bakedObjectIds, conversionResults)); + return new HostObjectBuilderResult(bakedObjectIds, conversionResults); } private void PreReceiveDeepClean(string baseLayerName) diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Operations/Send/RhinoRootObjectBuilder.cs b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Operations/Send/RhinoRootObjectBuilder.cs index a0119da16..ae9e34f36 100644 --- a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Operations/Send/RhinoRootObjectBuilder.cs +++ b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Operations/Send/RhinoRootObjectBuilder.cs @@ -60,18 +60,11 @@ ISdkActivityFactory activityFactory _activityFactory = activityFactory; } - public Task Build( + public async Task Build( IReadOnlyList rhinoObjects, SendInfo sendInfo, - Action? onOperationProgressed = null, + IProgress onOperationProgressed, CancellationToken cancellationToken = default - ) => Task.FromResult(BuildSync(rhinoObjects, sendInfo, onOperationProgressed, cancellationToken)); - - private RootObjectBuilderResult BuildSync( - IReadOnlyList rhinoObjects, - SendInfo sendInfo, - Action? onOperationProgressed, - CancellationToken cancellationToken ) { using var activity = _activityFactory.Start("Build"); @@ -114,7 +107,7 @@ CancellationToken cancellationToken results.Add(result); ++count; - onOperationProgressed?.Invoke("Converting", (double)count / atomicObjects.Count); + onOperationProgressed.Report(new("Converting", (double)count / atomicObjects.Count)); // NOTE: useful for testing ui states, pls keep for now so we can easily uncomment // Thread.Sleep(550); @@ -130,11 +123,14 @@ CancellationToken cancellationToken { // 4 - Unpack the render material proxies rootObjectCollection[ProxyKeys.RENDER_MATERIAL] = _materialUnpacker.UnpackRenderMaterial(atomicObjects); - + } + using (var _ = _activityFactory.Start("UnpackColors")) + { // 5 - Unpack the color proxies rootObjectCollection[ProxyKeys.COLOR] = _colorUnpacker.UnpackColors(atomicObjects, versionLayers.ToList()); } + await Task.Yield(); return new RootObjectBuilderResult(rootObjectCollection, results); } diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Plugin/RhinoPlugin.cs b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Plugin/RhinoPlugin.cs index 574f3142c..3a060c253 100644 --- a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Plugin/RhinoPlugin.cs +++ b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Plugin/RhinoPlugin.cs @@ -1,5 +1,5 @@ using Rhino; -using Speckle.Connectors.Rhino.HostApp; +using Speckle.Connectors.DUI.Bridge; using Speckle.Connectors.Rhino.Plugin; using Speckle.InterfaceGenerator; @@ -8,9 +8,9 @@ namespace Speckle.Connectors.Rhino.DependencyInjection; [GenerateAutoInterface] public class RhinoPlugin : IRhinoPlugin { - private readonly IRhinoIdleManager _idleManager; + private readonly IAppIdleManager _idleManager; - public RhinoPlugin(IRhinoIdleManager idleManager) + public RhinoPlugin(IAppIdleManager idleManager) { _idleManager = idleManager; } diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Registration/ServiceRegistration.cs b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Registration/ServiceRegistration.cs index 62b231a90..daf21e89f 100644 --- a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Registration/ServiceRegistration.cs +++ b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Registration/ServiceRegistration.cs @@ -10,6 +10,7 @@ using Speckle.Connectors.Common.Operations; 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; @@ -41,7 +42,7 @@ public static void AddRhino(this IServiceCollection serviceCollection) // Register other connector specific types serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); // Register bindings serviceCollection.AddSingleton(); diff --git a/Connectors/Tekla/Speckle.Connector.Tekla2024/Bindings/TeklaBasicConnectorBinding.cs b/Connectors/Tekla/Speckle.Connector.Tekla2024/Bindings/TeklaBasicConnectorBinding.cs new file mode 100644 index 000000000..0818a358f --- /dev/null +++ b/Connectors/Tekla/Speckle.Connector.Tekla2024/Bindings/TeklaBasicConnectorBinding.cs @@ -0,0 +1,48 @@ +using Speckle.Connectors.DUI.Bindings; +using Speckle.Connectors.DUI.Bridge; +using Speckle.Connectors.DUI.Models; +using Speckle.Connectors.DUI.Models.Card; +using Speckle.Sdk; + +namespace Speckle.Connector.Tekla2024.Bindings; + +public class TeklaBasicConnectorBinding : IBasicConnectorBinding +{ + private readonly ISpeckleApplication _speckleApplication; + private readonly DocumentModelStore _store; + public string Name => "baseBinding"; + public IBrowserBridge Parent { get; } + + public TeklaBasicConnectorBinding( + IBrowserBridge parent, + ISpeckleApplication speckleApplication, + DocumentModelStore store + ) + { + _speckleApplication = speckleApplication; + _store = store; + Parent = parent; + } + + public string GetSourceApplicationName() => _speckleApplication.Slug; + + public string GetSourceApplicationVersion() => _speckleApplication.HostApplicationVersion; + + public string GetConnectorVersion() => _speckleApplication.SpeckleVersion; + + public DocumentInfo? GetDocumentInfo() => new DocumentInfo("Test", "Test", "Test"); + + public DocumentModelStore GetDocumentState() => _store; + + public void AddModel(ModelCard model) => throw new NotImplementedException(); + + public void UpdateModel(ModelCard model) => throw new NotImplementedException(); + + public void RemoveModel(ModelCard model) => throw new NotImplementedException(); + + public Task HighlightModel(string modelCardId) => throw new NotImplementedException(); + + public Task HighlightObjects(IReadOnlyList objectIds) => throw new NotImplementedException(); + + public BasicConnectorBindingCommands Commands { get; } +} diff --git a/Connectors/Tekla/Speckle.Connector.Tekla2024/Bindings/TeklaSendBinding.cs b/Connectors/Tekla/Speckle.Connector.Tekla2024/Bindings/TeklaSendBinding.cs new file mode 100644 index 000000000..77c1f9977 --- /dev/null +++ b/Connectors/Tekla/Speckle.Connector.Tekla2024/Bindings/TeklaSendBinding.cs @@ -0,0 +1,27 @@ +using Speckle.Connectors.DUI.Bindings; +using Speckle.Connectors.DUI.Bridge; +using Speckle.Connectors.DUI.Models.Card.SendFilter; +using Speckle.Connectors.DUI.Settings; + +namespace Speckle.Connector.Tekla2024.Bindings; + +public class TeklaSendBinding : ISendBinding +{ + public string Name => "sendBinding"; + public IBrowserBridge Parent { get; } + + public TeklaSendBinding(IBrowserBridge parent) + { + Parent = parent; + } + + public List GetSendFilters() => []; + + public List GetSendSettings() => []; + + public Task Send(string modelCardId) => throw new NotImplementedException(); + + public void CancelSend(string modelCardId) => throw new NotImplementedException(); + + public SendBindingUICommands Commands { get; } +} diff --git a/Connectors/Tekla/Speckle.Connector.Tekla2024/DevDocs/InputObjectDependency.NOT_DEPENDENT.png b/Connectors/Tekla/Speckle.Connector.Tekla2024/DevDocs/InputObjectDependency.NOT_DEPENDENT.png new file mode 100644 index 000000000..a95b73cb5 Binary files /dev/null and b/Connectors/Tekla/Speckle.Connector.Tekla2024/DevDocs/InputObjectDependency.NOT_DEPENDENT.png differ diff --git a/Connectors/Tekla/Speckle.Connector.Tekla2024/HostApp/TeklaDocumentModelStore.cs b/Connectors/Tekla/Speckle.Connector.Tekla2024/HostApp/TeklaDocumentModelStore.cs new file mode 100644 index 000000000..891d0972b --- /dev/null +++ b/Connectors/Tekla/Speckle.Connector.Tekla2024/HostApp/TeklaDocumentModelStore.cs @@ -0,0 +1,17 @@ +using Speckle.Connectors.DUI.Models; +using Speckle.Newtonsoft.Json; + +namespace Speckle.Connector.Tekla2024.HostApp; + +public class TeklaDocumentModelStore : DocumentModelStore +{ + public TeklaDocumentModelStore( + JsonSerializerSettings jsonSerializerSettings + // ITopLevelExceptionHandler topLevelExceptionHandler + ) + : base(jsonSerializerSettings, true) { } + + public override void WriteToFile() { } + + public override void ReadFromFile() { } +} diff --git a/Connectors/Tekla/Speckle.Connector.Tekla2024/Plugin/TeklaPlugin.cs b/Connectors/Tekla/Speckle.Connector.Tekla2024/Plugin/TeklaPlugin.cs new file mode 100644 index 000000000..493f407d5 --- /dev/null +++ b/Connectors/Tekla/Speckle.Connector.Tekla2024/Plugin/TeklaPlugin.cs @@ -0,0 +1,18 @@ +using Tekla.Structures.Plugins; + +namespace Speckle.Connector.Tekla2024.Plugin; + +[Plugin("Speckle.Connectors.Tekla")] +[PluginUserInterface("Speckle.Connector.Tekla2024.SpeckleTeklaPanelHost")] +[InputObjectDependency(InputObjectDependency.NOT_DEPENDENT)] // See DevDocs/InputObjectDependency.NOT_DEPENDENT.png +public class TeklaPlugin : PluginBase +{ +#pragma warning disable IDE1006 + public override bool Run(List Input) + { + return true; + } +#pragma warning restore IDE1006 + + public override List DefineInput() => new(); +} diff --git a/Connectors/Tekla/Speckle.Connector.Tekla2024/Properties/launchSettings.json b/Connectors/Tekla/Speckle.Connector.Tekla2024/Properties/launchSettings.json new file mode 100644 index 000000000..cfcd63e10 --- /dev/null +++ b/Connectors/Tekla/Speckle.Connector.Tekla2024/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "Tekla2024": { + "commandName": "Executable", + "executablePath": "C:\\Program Files\\Tekla Structures\\2024.0\\bin\\TeklaStructures.exe" + } + } +} diff --git a/Connectors/Tekla/Speckle.Connector.Tekla2024/ServiceRegistration.cs b/Connectors/Tekla/Speckle.Connector.Tekla2024/ServiceRegistration.cs new file mode 100644 index 000000000..ebbe88e7e --- /dev/null +++ b/Connectors/Tekla/Speckle.Connector.Tekla2024/ServiceRegistration.cs @@ -0,0 +1,33 @@ +using Microsoft.Extensions.DependencyInjection; +using Speckle.Connector.Tekla2024.Bindings; +using Speckle.Connector.Tekla2024.HostApp; +using Speckle.Connectors.Common; +using Speckle.Connectors.DUI; +using Speckle.Connectors.DUI.Bindings; +using Speckle.Connectors.DUI.Models; +using Speckle.Connectors.DUI.WebView; + +namespace Speckle.Connector.Tekla2024; + +public static class ServiceRegistration +{ + public static void AddTekla(this IServiceCollection serviceCollection) + { + serviceCollection.AddConnectorUtils(); + serviceCollection.AddDUI(); + serviceCollection.AddDUIView(); + + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + + serviceCollection.AddSingleton(); + + serviceCollection.RegisterTopLevelExceptionHandler(); + + serviceCollection.AddSingleton(sp => sp.GetRequiredService()); + serviceCollection.AddSingleton(); + + serviceCollection.AddSingleton(); + } +} diff --git a/Connectors/Tekla/Speckle.Connector.Tekla2024/Speckle.Connector.Tekla2024.csproj b/Connectors/Tekla/Speckle.Connector.Tekla2024/Speckle.Connector.Tekla2024.csproj new file mode 100644 index 000000000..4dde51c83 --- /dev/null +++ b/Connectors/Tekla/Speckle.Connector.Tekla2024/Speckle.Connector.Tekla2024.csproj @@ -0,0 +1,32 @@ + + + + net48 + true + $(DefineConstants);TEKLA2024 + RD /S /Q "$(TargetDir)arm64\" + if EXIST "C:\Program Files\Tekla Structures\2024.0\" ( + xcopy "$(TargetDir)*.*" "C:\ProgramData\Trimble\Tekla Structures\2024.0\Environments\common\extensions\Speckle3TeklaStructures" /Y /I /E + ) + if EXIST "C:\TeklaStructures\2024.0\" ( + xcopy "$(TargetDir)*.*" "C:\TeklaStructures\2024.0\Environments\common\extensions\Speckle3TeklaStructures" /Y /I /E + ) + + + + + + + + + + + + + + + + + + + diff --git a/Connectors/Tekla/Speckle.Connector.Tekla2024/SpeckleTeklaPanelHost.cs b/Connectors/Tekla/Speckle.Connector.Tekla2024/SpeckleTeklaPanelHost.cs new file mode 100644 index 000000000..220cc8927 --- /dev/null +++ b/Connectors/Tekla/Speckle.Connector.Tekla2024/SpeckleTeklaPanelHost.cs @@ -0,0 +1,62 @@ +using System.Windows.Forms; +using System.Windows.Forms.Integration; +using Microsoft.Extensions.DependencyInjection; +using Speckle.Connectors.Common; +using Speckle.Connectors.DUI; +using Speckle.Connectors.DUI.WebView; +using Speckle.Sdk.Host; +using Tekla.Structures.Dialog; +using Tekla.Structures.Model; +using Tekla.Structures.Model.Operations; + +namespace Speckle.Connector.Tekla2024; + +public class SpeckleTeklaPanelHost : PluginFormBase +{ + private ElementHost Host { get; } + public Model Model { get; private set; } + + public static new ServiceProvider? Container { get; private set; } + + // TODO: private IDisposable? _disposableLogger; + + public SpeckleTeklaPanelHost() + { + var services = new ServiceCollection(); + services.Initialize(HostApplications.TeklaStructures, GetVersion()); + services.AddTekla(); + + // TODO: Add Tekla converters + + Container = services.BuildServiceProvider(); + Container.UseDUI(); // TODO: this might not needed? ISyncToThread? + + Model = new Model(); // don't know what is this.. + if (!Model.GetConnectionStatus()) + { + MessageBox.Show( + "Speckle connector connection failed. Please try again.", + "Error", + MessageBoxButtons.OK, + MessageBoxIcon.Error + ); + } + var webview = Container.GetRequiredService(); + Host = new() { Child = webview, Dock = DockStyle.Fill }; + Controls.Add(Host); + Operation.DisplayPrompt("Speckle connector initialized."); + + Show(); + Activate(); + Focus(); + } + + private HostAppVersion GetVersion() + { +#if TEKLA2024 + return HostAppVersion.v2024; +#else + throw new NotImplementedException(); +#endif + } +} diff --git a/Connectors/Tekla/Speckle.Connector.Tekla2024/app.config b/Connectors/Tekla/Speckle.Connector.Tekla2024/app.config new file mode 100644 index 000000000..1f0cab02a --- /dev/null +++ b/Connectors/Tekla/Speckle.Connector.Tekla2024/app.config @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/Connectors/Tekla/Speckle.Connector.Tekla2024/packages.lock.json b/Connectors/Tekla/Speckle.Connector.Tekla2024/packages.lock.json new file mode 100644 index 000000000..a827a4946 --- /dev/null +++ b/Connectors/Tekla/Speckle.Connector.Tekla2024/packages.lock.json @@ -0,0 +1,493 @@ +{ + "version": 2, + "dependencies": { + ".NETFramework,Version=v4.8": { + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3" + } + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.14.1, )", + "resolved": "1.14.1", + "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.6, )", + "resolved": "0.9.6", + "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" + }, + "Tekla.Structures.Dialog": { + "type": "Direct", + "requested": "[2024.0.2, )", + "resolved": "2024.0.2", + "contentHash": "VY1n7ROsUMHomHwJBYbmTt58M9qSuGqkcdWS8GTJUU5NOZsIsoS746fT7E+YBpUyP+9tyzibv4OW9vQj1sDEAA==", + "dependencies": { + "Tekla.Common.Geometry": "4.6.2", + "Tekla.Structures": "2024.0.2", + "Tekla.Structures.Catalogs": "2024.0.2", + "Tekla.Structures.Datatype": "2024.0.2", + "Tekla.Structures.Model": "2024.0.2", + "Tekla.Structures.Plugins": "2024.0.2", + "Tekla.Technology.Akit5": "5.15.0", + "Tekla.Technology.Scripting.Plugins": "5.5.0", + "Tekla.Technology.Serialization": "4.1.1", + "Trimble.Remoting": "1.0.2", + "Trimble.Technology.MsgLib": "2.2.22326" + } + }, + "Tekla.Structures.Model": { + "type": "Direct", + "requested": "[2024.0.2, )", + "resolved": "2024.0.2", + "contentHash": "GV7mqc3TX7h3QeCLmjCI8GlnbhAMYOvLMEED1TY+hpdeWtMuHxGS7GwfLgG10iUdjumqy5Qmn2VE3PIRhw5hWg==", + "dependencies": { + "Tekla.Common.Geometry": "4.6.2", + "Tekla.Structures": "2024.0.2", + "Tekla.Structures.Datatype": "2024.0.2", + "Tekla.Technology.Serialization": "4.1.1", + "Trimble.Remoting": "1.0.2" + } + }, + "Tekla.Structures.Plugins": { + "type": "Direct", + "requested": "[2024.0.2, )", + "resolved": "2024.0.2", + "contentHash": "40Dn4sAcfLJc1Gi6sK95tVz8BwmB4vvRjcZcFJ55F3HMr2mwtcJhvDQED0exlZuU3pbibjhUZaNz0/I16Mms/w==", + "dependencies": { + "Tekla.Structures": "2024.0.2", + "Tekla.Technology.Scripting.Plugins": "5.5.0", + "Tekla.Technology.Serialization": "4.1.1", + "Trimble.Remoting": "1.0.2" + } + }, + "GraphQL.Client": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "8yPNBbuVBpTptivyAlak4GZvbwbUcjeQTL4vN1HKHRuOykZ4r7l5fcLS6vpyPyLn0x8FsL31xbOIKyxbmR9rbA==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0", + "GraphQL.Client.Abstractions.Websocket": "6.0.0", + "System.Net.WebSockets.Client.Managed": "1.0.22", + "System.Reactive": "5.0.0" + } + }, + "GraphQL.Client.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "h7uzWFORHZ+CCjwr/ThAyXMr0DPpzEANDa4Uo54wqCQ+j7qUKwqYTgOrb1W40sqbvNaZm9v/X7It31SUw0maHA==", + "dependencies": { + "GraphQL.Primitives": "6.0.0" + } + }, + "GraphQL.Client.Abstractions.Websocket": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "Nr9bPf8gIOvLuXpqEpqr9z9jslYFJOvd0feHth3/kPqeR3uMbjF5pjiwh4jxyMcxHdr8Pb6QiXkV3hsSyt0v7A==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0" + } + }, + "GraphQL.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA==" + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.CSharp": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" + }, + "Microsoft.Data.Sqlite": { + "type": "Transitive", + "resolved": "7.0.7", + "contentHash": "tiNmV1oPy+Z2R7Wd0bPB/FxCr8B+/5q11OpDMG751GA/YuOL7MZrBFfzv5oFRlFe08K6sjrnbrauzzGIeNrzLQ==", + "dependencies": { + "Microsoft.Data.Sqlite.Core": "7.0.7", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" + } + }, + "Microsoft.Data.Sqlite.Core": { + "type": "Transitive", + "resolved": "7.0.7", + "contentHash": "21FRzcJhaTrlv7kTrqr/ltFcSQM2TyuTTPhUcjO8H73od7Bb3QraNW90c7lUucNI/245XPkKZG4fp7/7OsKCSg==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.2.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "UpZLNLBpIZ0GTebShui7xXYh6DmBHjWM8NxGxZbdQh/bPZ5e6YswqI+bru6BnEL5eWiOdodsXtEz3FROcgi/qg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Primitives": "2.2.0", + "System.ComponentModel.Annotations": "4.5.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==", + "dependencies": { + "System.Memory": "4.5.1", + "System.Runtime.CompilerServices.Unsafe": "4.5.1" + } + }, + "Microsoft.NETFramework.ReferenceAssemblies.net48": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Mono.Cecil": { + "type": "Transitive", + "resolved": "0.11.4", + "contentHash": "IC1h5g0NeJGHIUgzM1P82ld57knhP0IcQfrYITDPXlNpMYGUrsG5TxuaWTjaeqDNQMBDNZkB8L0rBnwsY6JHuQ==" + }, + "Polly": { + "type": "Transitive", + "resolved": "7.2.3", + "contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ==" + }, + "Polly.Contrib.WaitAndRetry": { + "type": "Transitive", + "resolved": "1.1.1", + "contentHash": "1MUQLiSo4KDkQe6nzQRhIU05lm9jlexX5BVsbuw0SL82ynZ+GzAHQxJVDPVBboxV37Po3SG077aX8DuSy8TkaA==" + }, + "Polly.Extensions.Http": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "drrG+hB3pYFY7w1c3BD+lSGYvH2oIclH8GRSehgfyP5kjnFnHKQuuBhuHLv+PWyFuaTDyk/vfRpnxOzd11+J8g==", + "dependencies": { + "Polly": "7.1.0" + } + }, + "Speckle.DoubleNumerics": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" + }, + "Speckle.Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.2", + "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" + }, + "SQLitePCLRaw.bundle_e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==", + "dependencies": { + "SQLitePCLRaw.lib.e_sqlite3": "2.1.4", + "SQLitePCLRaw.provider.dynamic_cdecl": "2.1.4" + } + }, + "SQLitePCLRaw.core": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==", + "dependencies": { + "System.Memory": "4.5.3" + } + }, + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + }, + "SQLitePCLRaw.provider.dynamic_cdecl": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "ZsaKKhgYF9B1fvcnOGKl3EycNAwd9CRWX7v0rEfuPWhQQ5Jjpvf2VEHahiLIGHio3hxi3EIKFJw9KvyowWOUAw==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "System.Net.WebSockets.Client.Managed": { + "type": "Transitive", + "resolved": "1.0.22", + "contentHash": "WqEOxPlXjuZrIjUtXNE9NxEfU/n5E35iV2PtoZdJSUC4tlrqwHnTee+wvMIM4OUaJWmwrymeqcgYrE0IkGAgLA==", + "dependencies": { + "System.Buffers": "4.4.0", + "System.Numerics.Vectors": "4.4.0" + } + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "System.Reflection.Emit.Lightweight": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "a4OLB4IITxAXJeV74MDx49Oq2+PsF6Sml54XAFv+2RyWwtDBcabzoxiiJRhdhx+gaohLh4hEGCLQyBozXoQPqA==" + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + }, + "System.Threading.Tasks.Extensions": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "Tekla.Common.Geometry": { + "type": "Transitive", + "resolved": "4.6.2", + "contentHash": "5+jJDmzC363ys51JbrQap0LdtflTpSJVpW9oUQbtlem+SBcwrFQkA16xg3ddKv6tQ0iMXwPe7DP0PbV+Qj9GMw==" + }, + "Tekla.Structures": { + "type": "Transitive", + "resolved": "2024.0.2", + "contentHash": "m7URRYK7sEzumr/+TJRNd4q2nutF9qMUbIIPmbr36cbaKiBGW0xxAgveTL8+7kkDqodhAGyUTVai5gOtvYi2SQ==", + "dependencies": { + "Tekla.Common.Geometry": "4.6.2", + "Tekla.Technology.Serialization": "4.1.1", + "Trimble.Remoting": "1.0.2" + } + }, + "Tekla.Structures.Catalogs": { + "type": "Transitive", + "resolved": "2024.0.2", + "contentHash": "1VUbPKtzTUMwUP21jqN+c1gLEtxi31+KYPis/n2Plty2k5Zj4gM3Q428KhjMgyHOoc7bWJX/kjfUQBCYuhqAsQ==", + "dependencies": { + "Tekla.Common.Geometry": "4.6.2", + "Tekla.Structures": "2024.0.2", + "Tekla.Structures.Datatype": "2024.0.2", + "Tekla.Structures.Model": "2024.0.2", + "Tekla.Technology.Serialization": "4.1.1", + "Trimble.Remoting": "1.0.2" + } + }, + "Tekla.Structures.Datatype": { + "type": "Transitive", + "resolved": "2024.0.2", + "contentHash": "LtLUtkYuni+R1F0UAPXWmgjAPBZORabG+/2YVOhJBj6+0x6Fg0PxgDtABNMNniN7A7BFXaYQRI/fuP35VL8BFQ==" + }, + "Tekla.Technology.Akit5": { + "type": "Transitive", + "resolved": "5.15.0", + "contentHash": "Rzw7m2aZE1AzzN4/wYqUU3UcEcGL4pUi4T6KUVjP3dfsckkeYu5nw8ZGB2L2yATZRPTjTItKQrG/jcsApT2TNw==" + }, + "Tekla.Technology.Scripting.Plugins": { + "type": "Transitive", + "resolved": "5.5.0", + "contentHash": "tuPQlV/hJHHRrY6LH0FMxVtmjs6TUw0u4WaXbd4GVbj9YAyJyiZCA2Q1YeOmy6rs2IoCyGivURaZsSkdg/5JPA==", + "dependencies": { + "Mono.Cecil": "0.11.4" + } + }, + "Tekla.Technology.Serialization": { + "type": "Transitive", + "resolved": "4.1.1", + "contentHash": "C++1hdVfSmoB+0M5cFvXxmLzVl3azi0CiI3owLZt4vHWI+EgYWEGMAlCk6ED4zfhR2FzT9VLu4RCFDHvopTERw==", + "dependencies": { + "System.Reflection.Emit.Lightweight": "4.7.0" + } + }, + "Trimble.Remoting": { + "type": "Transitive", + "resolved": "1.0.2", + "contentHash": "vwaLu07qxgUaSt5FRaR0xrH+6OuVTibfrOYoCVta/NmdA2XZlAqAF8/pIkBITRTqpY8Z8uBhV+mYfVfr+oevcg==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0", + "Tekla.Technology.Serialization": "4.1.1" + } + }, + "Trimble.Technology.Msglib": { + "type": "Transitive", + "resolved": "2.2.22326", + "contentHash": "I5sYxv7i4J5ZpK/1ELvJMGn3DzVEBq1S0dTyE0JQjo/bIHBmuuy3HSY2YrYCWcRPC2WsNv8t+TiAGzWCGrPs3Q==", + "dependencies": { + "Trimble.Technology.Rkit": "2.2.22312" + } + }, + "Trimble.Technology.Rkit": { + "type": "Transitive", + "resolved": "2.2.22312", + "contentHash": "OMwJAhps/tnmV99suab65b7Ex+H9mebbdNRaHwogPEwCRefWUr7/WaA4p43nfb+FxmBFO5FtudN3EEarggTSow==" + }, + "speckle.connectors.common": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", + "Speckle.Connectors.Logging": "[1.0.0, )", + "Speckle.Objects": "[3.1.0-dev.162, )", + "Speckle.Sdk": "[3.1.0-dev.162, )" + } + }, + "speckle.connectors.dui": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", + "Speckle.Connectors.Common": "[1.0.0, )", + "Speckle.Sdk": "[3.1.0-dev.162, )", + "System.Threading.Tasks.Dataflow": "[6.0.0, )" + } + }, + "speckle.connectors.dui.webview": { + "type": "Project", + "dependencies": { + "Microsoft.Web.WebView2": "[1.0.1938.49, )", + "Speckle.Connectors.DUI": "[1.0.0, )" + } + }, + "speckle.connectors.logging": { + "type": "Project" + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "MZtBIwfDFork5vfjpJdG5g8wuJFt7d/y3LOSVVtDK/76wlbtz6cjltfKHqLx2TKVqTj5/c41t77m1+h20zqtPA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "2.2.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging.Abstractions": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" + }, + "Microsoft.Web.WebView2": { + "type": "CentralTransitive", + "requested": "[1.0.1938.49, )", + "resolved": "1.0.1938.49", + "contentHash": "z8KnFnaTYzhA/ZnyRX0qGfS1NU5ZBJeClAH64F0fVDvdDJTvME7xl6zTJ0Jlfe1BtL3C0NH9xTy64shg2baKdw==" + }, + "Speckle.Objects": { + "type": "CentralTransitive", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "1fyaPjC1kVtroKfoXI7edJX0Uho/TNRWG9mtmIQ7jKhX1zElrJPSoH8zEIMqckZhGmwUrYjlQFPssASNCP721Q==", + "dependencies": { + "Speckle.Sdk": "3.1.0-dev.162" + } + }, + "Speckle.Sdk": { + "type": "CentralTransitive", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "ulT3XNgqIQSmpQFK5YEEwRq4RA6emXBKQHnZxtCz37SJin1PnVpH9m8vOAF9FmaWsgSAfjhhjIZDruMu1oKxSg==", + "dependencies": { + "GraphQL.Client": "6.0.0", + "Microsoft.CSharp": "4.7.0", + "Microsoft.Data.Sqlite": "7.0.7", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging": "2.2.0", + "Polly": "7.2.3", + "Polly.Contrib.WaitAndRetry": "1.1.1", + "Polly.Extensions.Http": "3.0.0", + "Speckle.DoubleNumerics": "4.0.1", + "Speckle.Newtonsoft.Json": "13.0.2" + } + }, + "System.Threading.Tasks.Dataflow": { + "type": "CentralTransitive", + "requested": "[6.0.0, )", + "resolved": "6.0.0", + "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" + } + } + } +} \ No newline at end of file diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/FeatureClassUtils.cs b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/FeatureClassUtils.cs index 7f851c03c..e3aecf85c 100644 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/FeatureClassUtils.cs +++ b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/FeatureClassUtils.cs @@ -72,9 +72,9 @@ private Geodatabase GetDatabase() return geodatabase; } - public Dictionary> GroupConversionTrackers( + public async Task>> GroupConversionTrackers( Dictionary conversionTracker, - Action? onOperationProgressed + Action onOperationProgressed ) { // 1. Sort features into groups by path and geom type @@ -130,16 +130,17 @@ private Geodatabase GetDatabase() geometryGroups[uniqueKey].Add((context, trackerItem)); ClearExistingDataset(uniqueKey); - onOperationProgressed?.Invoke("Grouping features into layers", count++ / conversionTracker.Count); + onOperationProgressed.Invoke("Grouping features into layers", count++ / conversionTracker.Count); + await Task.Yield(); } return geometryGroups; } - public void CreateDatasets( + public async Task CreateDatasets( Dictionary conversionTracker, Dictionary> featureClassElements, - Action? onOperationProgressed + Action onOperationProgressed ) { double count = 0; @@ -170,7 +171,7 @@ public void CreateDatasets( listOfContextAndTrackers.Select(x => x.Item2).ToList() ); - onOperationProgressed?.Invoke("Writing to Database", count++ / featureClassElements.Count); + onOperationProgressed.Invoke("Writing to Database", count++ / featureClassElements.Count); continue; } @@ -198,7 +199,8 @@ public void CreateDatasets( } } - onOperationProgressed?.Invoke("Writing to Database", count++ / featureClassElements.Count); + onOperationProgressed.Invoke("Writing to Database", count++ / featureClassElements.Count); + await Task.Yield(); } } diff --git a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/packages.lock.json b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/packages.lock.json index 7800a6e94..ce5d5d0a4 100644 --- a/Converters/ArcGIS/Speckle.Converters.ArcGIS3/packages.lock.json +++ b/Converters/ArcGIS/Speckle.Converters.ArcGIS3/packages.lock.json @@ -8,6 +8,15 @@ "resolved": "3.2.0.49743", "contentHash": "fmnYm+mD14Cz0Uqh1ij37SfLJerkyFHK5581y5tXT/l3H2ZvUmVuuxjYquXzyzj9p7IexQzMW4xCpxe+mD922g==" }, + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3" + } + }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -136,6 +145,11 @@ "System.Runtime.CompilerServices.Unsafe": "4.5.1" } }, + "Microsoft.NETFramework.ReferenceAssemblies.net461": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA==" + }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", @@ -223,7 +237,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )" } }, "Microsoft.Extensions.Logging": { @@ -246,18 +260,18 @@ }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "gpENXDB2i8SiXJZwrLVfB3BwKF1FHUSpPiw0BQ3Hl0V3821UWZjPYw5XK694oxJlVAeEMRvrMZMGsWOcK+3nMw==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "1fyaPjC1kVtroKfoXI7edJX0Uho/TNRWG9mtmIQ7jKhX1zElrJPSoH8zEIMqckZhGmwUrYjlQFPssASNCP721Q==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.145" + "Speckle.Sdk": "3.1.0-dev.162" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "YZLtitiVIxl12pNTXaB9gU+paNwFe++WBw6oIN6Rs5hFPialUgeKbpjh16Zq6wcptjBpvg0CHq29+d456QUAJQ==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "ulT3XNgqIQSmpQFK5YEEwRq4RA6emXBKQHnZxtCz37SJin1PnVpH9m8vOAF9FmaWsgSAfjhhjIZDruMu1oKxSg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", diff --git a/Converters/Autocad/Speckle.Converters.Autocad2022/packages.lock.json b/Converters/Autocad/Speckle.Converters.Autocad2022/packages.lock.json index 1263be458..d793366bf 100644 --- a/Converters/Autocad/Speckle.Converters.Autocad2022/packages.lock.json +++ b/Converters/Autocad/Speckle.Converters.Autocad2022/packages.lock.json @@ -2,6 +2,15 @@ "version": 2, "dependencies": { ".NETFramework,Version=v4.8": { + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3" + } + }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -137,6 +146,11 @@ "System.Runtime.CompilerServices.Unsafe": "4.5.1" } }, + "Microsoft.NETFramework.ReferenceAssemblies.net48": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ==" + }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", @@ -259,7 +273,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )" } }, "Microsoft.Extensions.Logging": { @@ -282,18 +296,18 @@ }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "gpENXDB2i8SiXJZwrLVfB3BwKF1FHUSpPiw0BQ3Hl0V3821UWZjPYw5XK694oxJlVAeEMRvrMZMGsWOcK+3nMw==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "1fyaPjC1kVtroKfoXI7edJX0Uho/TNRWG9mtmIQ7jKhX1zElrJPSoH8zEIMqckZhGmwUrYjlQFPssASNCP721Q==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.145" + "Speckle.Sdk": "3.1.0-dev.162" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "YZLtitiVIxl12pNTXaB9gU+paNwFe++WBw6oIN6Rs5hFPialUgeKbpjh16Zq6wcptjBpvg0CHq29+d456QUAJQ==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "ulT3XNgqIQSmpQFK5YEEwRq4RA6emXBKQHnZxtCz37SJin1PnVpH9m8vOAF9FmaWsgSAfjhhjIZDruMu1oKxSg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", diff --git a/Converters/Autocad/Speckle.Converters.Autocad2023/packages.lock.json b/Converters/Autocad/Speckle.Converters.Autocad2023/packages.lock.json index 0de215d7c..06502516a 100644 --- a/Converters/Autocad/Speckle.Converters.Autocad2023/packages.lock.json +++ b/Converters/Autocad/Speckle.Converters.Autocad2023/packages.lock.json @@ -2,6 +2,15 @@ "version": 2, "dependencies": { ".NETFramework,Version=v4.8": { + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3" + } + }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -137,6 +146,11 @@ "System.Runtime.CompilerServices.Unsafe": "4.5.1" } }, + "Microsoft.NETFramework.ReferenceAssemblies.net48": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ==" + }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", @@ -259,7 +273,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )" } }, "Microsoft.Extensions.Logging": { @@ -282,18 +296,18 @@ }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "gpENXDB2i8SiXJZwrLVfB3BwKF1FHUSpPiw0BQ3Hl0V3821UWZjPYw5XK694oxJlVAeEMRvrMZMGsWOcK+3nMw==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "1fyaPjC1kVtroKfoXI7edJX0Uho/TNRWG9mtmIQ7jKhX1zElrJPSoH8zEIMqckZhGmwUrYjlQFPssASNCP721Q==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.145" + "Speckle.Sdk": "3.1.0-dev.162" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "YZLtitiVIxl12pNTXaB9gU+paNwFe++WBw6oIN6Rs5hFPialUgeKbpjh16Zq6wcptjBpvg0CHq29+d456QUAJQ==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "ulT3XNgqIQSmpQFK5YEEwRq4RA6emXBKQHnZxtCz37SJin1PnVpH9m8vOAF9FmaWsgSAfjhhjIZDruMu1oKxSg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", diff --git a/Converters/Autocad/Speckle.Converters.Autocad2024/Speckle.Converters.Autocad2024.csproj b/Converters/Autocad/Speckle.Converters.Autocad2024/Speckle.Converters.Autocad2024.csproj index 533d3c93e..1f8eacd4c 100644 --- a/Converters/Autocad/Speckle.Converters.Autocad2024/Speckle.Converters.Autocad2024.csproj +++ b/Converters/Autocad/Speckle.Converters.Autocad2024/Speckle.Converters.Autocad2024.csproj @@ -2,7 +2,6 @@ net48 x64 - 2024 $(DefineConstants)AUTOCAD;AUTOCAD2024;AUTOCAD2022_OR_GREATER;AUTOCAD2023_OR_GREATER;AUTOCAD2024_OR_GREATER Debug;Release;Local diff --git a/Converters/Autocad/Speckle.Converters.Autocad2024/packages.lock.json b/Converters/Autocad/Speckle.Converters.Autocad2024/packages.lock.json index 281b57525..ae3c4c29f 100644 --- a/Converters/Autocad/Speckle.Converters.Autocad2024/packages.lock.json +++ b/Converters/Autocad/Speckle.Converters.Autocad2024/packages.lock.json @@ -2,6 +2,15 @@ "version": 2, "dependencies": { ".NETFramework,Version=v4.8": { + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3" + } + }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -137,6 +146,11 @@ "System.Runtime.CompilerServices.Unsafe": "4.5.1" } }, + "Microsoft.NETFramework.ReferenceAssemblies.net48": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ==" + }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", @@ -260,8 +274,8 @@ "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )", - "Speckle.Sdk": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )", + "Speckle.Sdk": "[3.1.0-dev.162, )" } }, "speckle.connectors.dui": { @@ -269,7 +283,7 @@ "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", "Speckle.Connectors.Common": "[1.0.0, )", - "Speckle.Sdk": "[3.1.0-dev.145, )", + "Speckle.Sdk": "[3.1.0-dev.162, )", "System.Threading.Tasks.Dataflow": "[6.0.0, )" } }, @@ -287,7 +301,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )" } }, "Microsoft.Extensions.DependencyInjection": { @@ -325,18 +339,18 @@ }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "gpENXDB2i8SiXJZwrLVfB3BwKF1FHUSpPiw0BQ3Hl0V3821UWZjPYw5XK694oxJlVAeEMRvrMZMGsWOcK+3nMw==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "1fyaPjC1kVtroKfoXI7edJX0Uho/TNRWG9mtmIQ7jKhX1zElrJPSoH8zEIMqckZhGmwUrYjlQFPssASNCP721Q==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.145" + "Speckle.Sdk": "3.1.0-dev.162" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "YZLtitiVIxl12pNTXaB9gU+paNwFe++WBw6oIN6Rs5hFPialUgeKbpjh16Zq6wcptjBpvg0CHq29+d456QUAJQ==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "ulT3XNgqIQSmpQFK5YEEwRq4RA6emXBKQHnZxtCz37SJin1PnVpH9m8vOAF9FmaWsgSAfjhhjIZDruMu1oKxSg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", diff --git a/Converters/Autocad/Speckle.Converters.Autocad2025/Speckle.Converters.Autocad2025.csproj b/Converters/Autocad/Speckle.Converters.Autocad2025/Speckle.Converters.Autocad2025.csproj index da319d194..90c8bdc9a 100644 --- a/Converters/Autocad/Speckle.Converters.Autocad2025/Speckle.Converters.Autocad2025.csproj +++ b/Converters/Autocad/Speckle.Converters.Autocad2025/Speckle.Converters.Autocad2025.csproj @@ -2,7 +2,6 @@ net8.0-windows x64 - 2025 $(DefineConstants);AUTOCAD2025;AUTOCAD Debug;Release;Local diff --git a/Converters/Autocad/Speckle.Converters.Autocad2025/packages.lock.json b/Converters/Autocad/Speckle.Converters.Autocad2025/packages.lock.json index 1ccdd07a0..aad3d3cc6 100644 --- a/Converters/Autocad/Speckle.Converters.Autocad2025/packages.lock.json +++ b/Converters/Autocad/Speckle.Converters.Autocad2025/packages.lock.json @@ -2,6 +2,15 @@ "version": 2, "dependencies": { "net8.0-windows7.0": { + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3" + } + }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -136,6 +145,11 @@ "System.Runtime.CompilerServices.Unsafe": "4.5.1" } }, + "Microsoft.NETFramework.ReferenceAssemblies.net461": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA==" + }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", @@ -224,8 +238,8 @@ "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )", - "Speckle.Sdk": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )", + "Speckle.Sdk": "[3.1.0-dev.162, )" } }, "speckle.connectors.dui": { @@ -233,7 +247,7 @@ "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", "Speckle.Connectors.Common": "[1.0.0, )", - "Speckle.Sdk": "[3.1.0-dev.145, )", + "Speckle.Sdk": "[3.1.0-dev.162, )", "System.Threading.Tasks.Dataflow": "[6.0.0, )" } }, @@ -251,7 +265,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )" } }, "Microsoft.Extensions.DependencyInjection": { @@ -289,18 +303,18 @@ }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "gpENXDB2i8SiXJZwrLVfB3BwKF1FHUSpPiw0BQ3Hl0V3821UWZjPYw5XK694oxJlVAeEMRvrMZMGsWOcK+3nMw==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "1fyaPjC1kVtroKfoXI7edJX0Uho/TNRWG9mtmIQ7jKhX1zElrJPSoH8zEIMqckZhGmwUrYjlQFPssASNCP721Q==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.145" + "Speckle.Sdk": "3.1.0-dev.162" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "YZLtitiVIxl12pNTXaB9gU+paNwFe++WBw6oIN6Rs5hFPialUgeKbpjh16Zq6wcptjBpvg0CHq29+d456QUAJQ==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "ulT3XNgqIQSmpQFK5YEEwRq4RA6emXBKQHnZxtCz37SJin1PnVpH9m8vOAF9FmaWsgSAfjhhjIZDruMu1oKxSg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/AutocadRootToHostConverter.cs b/Converters/Autocad/Speckle.Converters.AutocadShared/AutocadRootToHostConverter.cs index 8edea41f3..5f13ffa6d 100644 --- a/Converters/Autocad/Speckle.Converters.AutocadShared/AutocadRootToHostConverter.cs +++ b/Converters/Autocad/Speckle.Converters.AutocadShared/AutocadRootToHostConverter.cs @@ -2,7 +2,6 @@ using Speckle.Converters.Common; using Speckle.Converters.Common.Objects; using Speckle.Converters.Common.Registration; -using Speckle.Sdk.Common; using Speckle.Sdk.Models; namespace Speckle.Converters.Autocad; @@ -25,7 +24,7 @@ public Base Convert(object target) { if (target is not DBObject dbObject) { - throw new ConversionNotSupportedException( + throw new SpeckleConversionException( $"Conversion of {target.GetType().Name} to Speckle is not supported. Only objects that inherit from DBObject are." ); } @@ -42,7 +41,7 @@ public Base Convert(object target) if (objectConverter == null) { - throw new ConversionNotSupportedException($"No conversion found for {target.GetType().Name}"); + throw new SpeckleConversionException($"No conversion found for {target.GetType().Name}"); } var convertedObject = objectConverter.Convert(dbObject); diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/ServiceRegistration.cs b/Converters/Autocad/Speckle.Converters.AutocadShared/ServiceRegistration.cs index ba08d05b0..e5ca83c19 100644 --- a/Converters/Autocad/Speckle.Converters.AutocadShared/ServiceRegistration.cs +++ b/Converters/Autocad/Speckle.Converters.AutocadShared/ServiceRegistration.cs @@ -22,5 +22,6 @@ public static void AddAutocadConverters(this IServiceCollection serviceCollectio IConverterSettingsStore, ConverterSettingsStore >(); + serviceCollection.AddMatchingInterfacesAsTransient(Assembly.GetExecutingAssembly()); } } diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/SubDMeshToSpeckleConverter.cs b/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/SubDMeshToSpeckleConverter.cs index 212a9d93b..6e168f9a7 100644 --- a/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/SubDMeshToSpeckleConverter.cs +++ b/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Geometry/SubDMeshToSpeckleConverter.cs @@ -30,7 +30,9 @@ public SOG.Mesh RawConvert(ADB.SubDMesh target) var vertices = new List(target.Vertices.Count * 3); foreach (AG.Point3d vert in target.Vertices) { - vertices.AddRange(_pointConverter.Convert(vert).ToList()); + vertices.Add(vert.X); + vertices.Add(vert.Y); + vertices.Add(vert.Z); } // faces diff --git a/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/DBCurveToSpeckleRawConverter.cs b/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/DBCurveToSpeckleRawConverter.cs index 789d6db00..d12177389 100644 --- a/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/DBCurveToSpeckleRawConverter.cs +++ b/Converters/Autocad/Speckle.Converters.AutocadShared/ToSpeckle/Raw/DBCurveToSpeckleRawConverter.cs @@ -53,9 +53,9 @@ public Objects.ICurve Convert(ADB.Curve target) => target switch { ADB.Line line => _lineConverter.Convert(line), - //ADB.Polyline polyline => _polylineConverter.Convert(polyline), - //ADB.Polyline2d polyline2d => _polyline2dConverter.Convert(polyline2d), - //ADB.Polyline3d polyline3d => _polyline3dConverter.Convert(polyline3d), + ADB.Polyline polyline => _polylineConverter.Convert(polyline), + ADB.Polyline2d polyline2d => _polyline2dConverter.Convert(polyline2d), + ADB.Polyline3d polyline3d => _polyline3dConverter.Convert(polyline3d), ADB.Arc arc => _arcConverter.Convert(arc), ADB.Circle circle => _circleConverter.Convert(circle), ADB.Ellipse ellipse => _ellipseConverter.Convert(ellipse), diff --git a/Converters/Civil3d/Speckle.Converters.Civil3d2024/packages.lock.json b/Converters/Civil3d/Speckle.Converters.Civil3d2024/packages.lock.json index ef914dc48..1d5682905 100644 --- a/Converters/Civil3d/Speckle.Converters.Civil3d2024/packages.lock.json +++ b/Converters/Civil3d/Speckle.Converters.Civil3d2024/packages.lock.json @@ -2,6 +2,15 @@ "version": 2, "dependencies": { ".NETFramework,Version=v4.8": { + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3" + } + }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -146,6 +155,11 @@ "System.Runtime.CompilerServices.Unsafe": "4.5.1" } }, + "Microsoft.NETFramework.ReferenceAssemblies.net48": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ==" + }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", @@ -268,7 +282,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )" } }, "Microsoft.Extensions.Logging": { @@ -291,18 +305,18 @@ }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "gpENXDB2i8SiXJZwrLVfB3BwKF1FHUSpPiw0BQ3Hl0V3821UWZjPYw5XK694oxJlVAeEMRvrMZMGsWOcK+3nMw==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "1fyaPjC1kVtroKfoXI7edJX0Uho/TNRWG9mtmIQ7jKhX1zElrJPSoH8zEIMqckZhGmwUrYjlQFPssASNCP721Q==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.145" + "Speckle.Sdk": "3.1.0-dev.162" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "YZLtitiVIxl12pNTXaB9gU+paNwFe++WBw6oIN6Rs5hFPialUgeKbpjh16Zq6wcptjBpvg0CHq29+d456QUAJQ==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "ulT3XNgqIQSmpQFK5YEEwRq4RA6emXBKQHnZxtCz37SJin1PnVpH9m8vOAF9FmaWsgSAfjhhjIZDruMu1oKxSg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", diff --git a/Converters/Civil3d/Speckle.Converters.Civil3dShared/Civil3dConversionSettings.cs b/Converters/Civil3d/Speckle.Converters.Civil3dShared/Civil3dConversionSettings.cs index 96d14528c..e15a58213 100644 --- a/Converters/Civil3d/Speckle.Converters.Civil3dShared/Civil3dConversionSettings.cs +++ b/Converters/Civil3d/Speckle.Converters.Civil3dShared/Civil3dConversionSettings.cs @@ -1,7 +1,3 @@ -namespace Speckle.Converters.Civil3d; +namespace Speckle.Converters.Civil3dShared; -public record Civil3dConversionSettings -{ - public Document Document { get; init; } - public string SpeckleUnits { get; init; } -} +public record Civil3dConversionSettings(Document Document, string SpeckleUnits); diff --git a/Converters/Civil3d/Speckle.Converters.Civil3dShared/Civil3dConversionSettingsFactory.cs b/Converters/Civil3d/Speckle.Converters.Civil3dShared/Civil3dConversionSettingsFactory.cs new file mode 100644 index 000000000..47ebb0934 --- /dev/null +++ b/Converters/Civil3d/Speckle.Converters.Civil3dShared/Civil3dConversionSettingsFactory.cs @@ -0,0 +1,30 @@ +using Speckle.Converters.Common; +using Speckle.InterfaceGenerator; + +namespace Speckle.Converters.Civil3dShared; + +[GenerateAutoInterface] +public class Civil3dConversionSettingsFactory(IHostToSpeckleUnitConverter unitsConverter) + : ICivil3dConversionSettingsFactory +{ + public Civil3dConversionSettings Create(Document document) => + new(document, unitsConverter.ConvertOrThrow(GetDocBuiltInUnit(document))); + + private static AAEC.BuiltInUnit GetDocBuiltInUnit(Document doc) + { + AAEC.BuiltInUnit unit = AAEC.BuiltInUnit.Dimensionless; + using (ADB.Transaction tr = doc.Database.TransactionManager.StartTransaction()) + { + Autodesk.AutoCAD.DatabaseServices.ObjectId id = AAEC.ApplicationServices.DrawingSetupVariables.GetInstance( + doc.Database, + false + ); + if (tr.GetObject(id, ADB.OpenMode.ForRead) is AAEC.ApplicationServices.DrawingSetupVariables setupVariables) + { + unit = setupVariables.LinearUnit; + } + tr.Commit(); + } + return unit; + } +} diff --git a/Converters/Civil3d/Speckle.Converters.Civil3dShared/Civil3dRootToHostConverter.cs b/Converters/Civil3d/Speckle.Converters.Civil3dShared/Civil3dRootToHostConverter.cs deleted file mode 100644 index e9dbd3379..000000000 --- a/Converters/Civil3d/Speckle.Converters.Civil3dShared/Civil3dRootToHostConverter.cs +++ /dev/null @@ -1,60 +0,0 @@ -using Autodesk.AutoCAD.DatabaseServices; -using Speckle.Converters.Common; -using Speckle.Converters.Common.Objects; -using Speckle.Converters.Common.Registration; -using Speckle.Sdk.Common; -using Speckle.Sdk.Models; - -namespace Speckle.Converters.Civil3d; - -public class Civil3dRootToHostConverter : IRootToSpeckleConverter -{ - private readonly IConverterManager _toSpeckle; - private readonly IConverterSettingsStore _settingsStore; - - public Civil3dRootToHostConverter( - IConverterManager toSpeckle, - IConverterSettingsStore settingsStore - ) - { - _toSpeckle = toSpeckle; - _settingsStore = settingsStore; - } - - public Base Convert(object target) - { - if (target is not DBObject dbObject) - { - throw new ConversionNotSupportedException( - $"Conversion of {target.GetType().Name} to Speckle is not supported. Only objects that inherit from DBObject are." - ); - } - - Type type = dbObject.GetType(); - - try - { - using (var l = _settingsStore.Current.Document.LockDocument()) - { - using (var tr = _settingsStore.Current.Document.Database.TransactionManager.StartTransaction()) - { - var objectConverter = _toSpeckle.ResolveConverter(type); - - if (objectConverter == null) - { - throw new ConversionNotSupportedException($"No conversion found for {target.GetType().Name}"); - } - - var convertedObject = objectConverter.Convert(dbObject); - tr.Commit(); - return convertedObject; - } - } - } - catch (SpeckleConversionException e) - { - Console.WriteLine(e); - throw; // Just rethrowing for now, Logs may be needed here. - } - } -} diff --git a/Converters/Civil3d/Speckle.Converters.Civil3dShared/Civil3dRootToSpeckleConverter.cs b/Converters/Civil3d/Speckle.Converters.Civil3dShared/Civil3dRootToSpeckleConverter.cs new file mode 100644 index 000000000..812c438bb --- /dev/null +++ b/Converters/Civil3d/Speckle.Converters.Civil3dShared/Civil3dRootToSpeckleConverter.cs @@ -0,0 +1,117 @@ +using Autodesk.AutoCAD.DatabaseServices; +using Speckle.Converters.Civil3dShared.ToSpeckle; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Converters.Common.Registration; +using Speckle.Sdk.Models; + +namespace Speckle.Converters.Civil3dShared; + +public class Civil3dRootToSpeckleConverter : IRootToSpeckleConverter +{ + private readonly IConverterManager _toSpeckle; + private readonly IConverterSettingsStore _settingsStore; + private readonly PartDataExtractor _partDataExtractor; + private readonly PropertySetExtractor _propertySetExtractor; + private readonly GeneralPropertiesExtractor _generalPropertiesExtractor; + + public Civil3dRootToSpeckleConverter( + IConverterManager toSpeckle, + IConverterSettingsStore settingsStore, + PartDataExtractor partDataExtractor, + PropertySetExtractor propertySetExtractor, + GeneralPropertiesExtractor generalPropertiesExtractor + ) + { + _toSpeckle = toSpeckle; + _settingsStore = settingsStore; + _partDataExtractor = partDataExtractor; + _propertySetExtractor = propertySetExtractor; + _generalPropertiesExtractor = generalPropertiesExtractor; + } + + public Base Convert(object target) + { + if (target is not DBObject dbObject) + { + throw new SpeckleConversionException( + $"Conversion of {target.GetType().Name} to Speckle is not supported. Only objects that inherit from DBObject are." + ); + } + + Type type = dbObject.GetType(); + object objectToConvert = dbObject; + Dictionary properties = new(); + + // check first for civil type objects + if (target is CDB.Entity civilEntity) + { + type = civilEntity.GetType(); + objectToConvert = civilEntity; + + // get properties like partdata, property sets, general properties + properties = GetCivilEntityProperties(civilEntity); + } + + var objectConverter = _toSpeckle.ResolveConverter(type, true); + + if (objectConverter == null) + { + throw new SpeckleConversionException($"No conversion found for {target.GetType().Name}"); + } + + try + { + using (var l = _settingsStore.Current.Document.LockDocument()) + { + using (var tr = _settingsStore.Current.Document.Database.TransactionManager.StartTransaction()) + { + var result = objectConverter.Convert(objectToConvert); + + if (properties.Count > 0) + { + result["properties"] = properties; + } + + tr.Commit(); + return result; + } + } + } + catch (SpeckleConversionException e) + { + Console.WriteLine(e); + throw; // Just rethrowing for now, Logs may be needed here. + } + } + + private Dictionary GetCivilEntityProperties(CDB.Entity entity) + { + Dictionary properties = new(); + + // get general properties + Dictionary? generalProperties = _generalPropertiesExtractor.GetGeneralProperties(entity); + if (generalProperties is not null) + { + properties.Add("Properties", generalProperties); + } + + // get part data + Dictionary? partData = _partDataExtractor.GetPartData(entity); + if (partData is not null) + { + properties.Add("Part Data", partData); + } + + // get property set data + Dictionary? propertySets = _propertySetExtractor.GetPropertySets(entity); + if (propertySets is not null) + { + properties.Add("Property Sets", propertySets); + } + + // TODO: add XDATA here + + return properties; + } +} diff --git a/Converters/Civil3d/Speckle.Converters.Civil3dShared/Civil3dToSpeckleUnitConverter.cs b/Converters/Civil3d/Speckle.Converters.Civil3dShared/Civil3dToSpeckleUnitConverter.cs index 1dc191f3a..bbcc6698d 100644 --- a/Converters/Civil3d/Speckle.Converters.Civil3dShared/Civil3dToSpeckleUnitConverter.cs +++ b/Converters/Civil3d/Speckle.Converters.Civil3dShared/Civil3dToSpeckleUnitConverter.cs @@ -2,7 +2,7 @@ using Speckle.Sdk; using Speckle.Sdk.Common; -namespace Speckle.Converters.Civil3d; +namespace Speckle.Converters.Civil3dShared; public class Civil3dToSpeckleUnitConverter : IHostToSpeckleUnitConverter { diff --git a/Converters/Civil3d/Speckle.Converters.Civil3dShared/Extensions/SpeckleApplicationIdExtensions.cs b/Converters/Civil3d/Speckle.Converters.Civil3dShared/Extensions/SpeckleApplicationIdExtensions.cs new file mode 100644 index 000000000..82a1cf49e --- /dev/null +++ b/Converters/Civil3d/Speckle.Converters.Civil3dShared/Extensions/SpeckleApplicationIdExtensions.cs @@ -0,0 +1,17 @@ +namespace Speckle.Converters.Civil3dShared.Extensions; + +public static class SpeckleApplicationIdExtensions +{ + /// + /// Retrieves the Speckle object application id + /// + public static string GetSpeckleApplicationId(this CDB.Entity entity) => entity.Handle.Value.ToString(); + + /// + /// Retrieves the Speckle application id from an ObjectId. + /// This is used primarily when storing civil entity relationships, eg alignments used for sites and corridors + /// + /// + /// + public static string GetSpeckleApplicationId(this ADB.ObjectId objectId) => objectId.Handle.Value.ToString(); +} diff --git a/Converters/Civil3d/Speckle.Converters.Civil3dShared/GlobalUsings.cs b/Converters/Civil3d/Speckle.Converters.Civil3dShared/GlobalUsings.cs index a1041daae..a4bc49b0f 100644 --- a/Converters/Civil3d/Speckle.Converters.Civil3dShared/GlobalUsings.cs +++ b/Converters/Civil3d/Speckle.Converters.Civil3dShared/GlobalUsings.cs @@ -1,3 +1,3 @@ global using AAEC = Autodesk.Aec; +global using AAECPDB = Autodesk.Aec.PropertyData.DatabaseServices; global using CDB = Autodesk.Civil.DatabaseServices; -global using SOBE = Speckle.Objects.BuiltElements; diff --git a/Converters/Civil3d/Speckle.Converters.Civil3dShared/Helpers/BaseCurveExtractor.cs b/Converters/Civil3d/Speckle.Converters.Civil3dShared/Helpers/BaseCurveExtractor.cs new file mode 100644 index 000000000..02acc8130 --- /dev/null +++ b/Converters/Civil3d/Speckle.Converters.Civil3dShared/Helpers/BaseCurveExtractor.cs @@ -0,0 +1,55 @@ +using Microsoft.Extensions.Logging; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Objects; + +namespace Speckle.Converters.Civil3dShared.Helpers; + +public sealed class BaseCurveExtractor +{ + private readonly ILogger _logger; + private readonly IConverterSettingsStore _converterSettings; + private readonly ITypedConverter _lineConverter; + + //private readonly ITypedConverter _arcConverter; + private readonly ITypedConverter _curveConverter; + + public BaseCurveExtractor( + ITypedConverter lineConverter, + //ITypedConverter arcConverter, + ITypedConverter curveConverter, + ILogger logger, + IConverterSettingsStore converterSettings + ) + { + _lineConverter = lineConverter; + //_arcConverter = arcConverter; + _curveConverter = curveConverter; + _logger = logger; + _converterSettings = converterSettings; + } + + public List? GetBaseCurve(CDB.Entity entity) + { + switch (entity) + { + // rant: if this is a pipe, the BaseCurve prop is fake news && will return a DB.line with start and endpoints set to [0,0,0] & [0,0,1] + // do not use basecurve for pipes 😡 + // currently not handling arc pipes due to lack of CircularArc2D converter, and also way to properly retrieve 2d arc curve + case CDB.Pipe pipe: + ICurve pipeCurve = + //pipe.SubEntityType == PipeSubEntityType.Straight ? + _lineConverter.Convert(new AG.LineSegment3d(pipe.StartPoint, pipe.EndPoint)); + //: _arcConverter.Convert(pipe.Curve2d); + return new() { pipeCurve }; + + case CDB.Alignment: + ICurve baseCurve = _curveConverter.Convert(entity.BaseCurve); + return new() { baseCurve }; + + // for any entities that don't use their basecurve prop + default: + return null; + } + } +} diff --git a/Converters/Civil3d/Speckle.Converters.Civil3dShared/Helpers/CatchmentGroupHandler.cs b/Converters/Civil3d/Speckle.Converters.Civil3dShared/Helpers/CatchmentGroupHandler.cs new file mode 100644 index 000000000..fd0fbf76c --- /dev/null +++ b/Converters/Civil3d/Speckle.Converters.Civil3dShared/Helpers/CatchmentGroupHandler.cs @@ -0,0 +1,61 @@ +using Speckle.Converters.Civil3dShared.Extensions; +using Speckle.Converters.Common; +using Speckle.Sdk.Models.Proxies; + +namespace Speckle.Converters.Civil3dShared.Helpers; + +public sealed class CatchmentGroupHandler +{ + /// + /// Keeps track of all catchment groups used by catchments in the current send operation. + /// (catchmentGroup objectId, catchmentGroupProxy). + /// This should be added to the root commit object post conversion. + /// + /// POC: Using group proxies for now + public Dictionary CatchmentGroupProxiesCache { get; } = new(); + + private readonly IConverterSettingsStore _converterSettings; + + public CatchmentGroupHandler(IConverterSettingsStore converterSettings) + { + _converterSettings = converterSettings; + } + + /// + /// Extracts the Catchment group from a catchment and stores in the appId of the catchment. + /// + /// + /// + public void HandleCatchmentGroup(CDB.Catchment catchment) + { + ADB.ObjectId catchmentGroupId = catchment.ContainingGroupId; + + if (catchmentGroupId == ADB.ObjectId.Null) + { + return; + } + + string catchmentApplicationId = catchment.GetSpeckleApplicationId(); + if (CatchmentGroupProxiesCache.TryGetValue(catchmentGroupId, out GroupProxy? value)) + { + value.objects.Add(catchmentApplicationId); + } + else + { + using (var tr = _converterSettings.Current.Document.Database.TransactionManager.StartTransaction()) + { + var catchmentGroup = (CDB.CatchmentGroup)tr.GetObject(catchmentGroupId, ADB.OpenMode.ForRead); + + CatchmentGroupProxiesCache[catchmentGroupId] = new() + { + name = catchmentGroup.Name, + objects = new() { catchmentApplicationId }, + applicationId = catchmentGroup.Handle.Value.ToString() + }; + + tr.Commit(); + } + } + return; + } +} diff --git a/Converters/Civil3d/Speckle.Converters.Civil3dShared/Helpers/DisplayValueExtractor.cs b/Converters/Civil3d/Speckle.Converters.Civil3dShared/Helpers/DisplayValueExtractor.cs new file mode 100644 index 000000000..0ab46cbb4 --- /dev/null +++ b/Converters/Civil3d/Speckle.Converters.Civil3dShared/Helpers/DisplayValueExtractor.cs @@ -0,0 +1,54 @@ +using Microsoft.Extensions.Logging; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Civil3dShared.Helpers; + +public sealed class DisplayValueExtractor +{ + private readonly ITypedConverter _solidConverter; + private readonly ITypedConverter _tinSurfaceConverter; + private readonly ITypedConverter _gridSurfaceConverter; + private readonly ILogger _logger; + private readonly IConverterSettingsStore _converterSettings; + + public DisplayValueExtractor( + ITypedConverter solidConverter, + ITypedConverter tinSurfaceConverter, + ITypedConverter gridSurfaceConverter, + ILogger logger, + IConverterSettingsStore converterSettings + ) + { + _solidConverter = solidConverter; + _tinSurfaceConverter = tinSurfaceConverter; + _gridSurfaceConverter = gridSurfaceConverter; + _logger = logger; + _converterSettings = converterSettings; + } + + public List GetDisplayValue(CDB.Entity entity) + { + List result = new(); + switch (entity) + { + // pipe networks: https://help.autodesk.com/view/CIV3D/2025/ENU/?guid=ade47b62-debf-f899-9b94-5645a620ab4f + case CDB.Part part: + SOG.Mesh partMesh = _solidConverter.Convert(part.Solid3dBody); + result.Add(partMesh); + break; + + // surfaces: https://help.autodesk.com/view/CIV3D/2025/ENU/?guid=d741aa49-e7da-9513-6b0b-226ebe3fa43f + // POC: volume surfaces not supported + case CDB.TinSurface tinSurface: + SOG.Mesh tinSurfaceMesh = _tinSurfaceConverter.Convert(tinSurface); + result.Add(tinSurfaceMesh); + break; + case CDB.GridSurface gridSurface: + SOG.Mesh gridSurfaceMesh = _gridSurfaceConverter.Convert(gridSurface); + result.Add(gridSurfaceMesh); + break; + } + return result; + } +} diff --git a/Converters/Civil3d/Speckle.Converters.Civil3dShared/Helpers/PipeNetworkHandler.cs b/Converters/Civil3d/Speckle.Converters.Civil3dShared/Helpers/PipeNetworkHandler.cs new file mode 100644 index 000000000..8ed0f2a9c --- /dev/null +++ b/Converters/Civil3d/Speckle.Converters.Civil3dShared/Helpers/PipeNetworkHandler.cs @@ -0,0 +1,60 @@ +using Speckle.Converters.Civil3dShared.Extensions; +using Speckle.Converters.Common; +using Speckle.Sdk.Models.Proxies; + +namespace Speckle.Converters.Civil3dShared.Helpers; + +public sealed class PipeNetworkHandler +{ + /// + /// Keeps track of all networks used by parts in the current send operation. + /// (network app id, network Proxy). + /// This should be added to the root commit object post conversion. + /// + /// POC: Using group proxies for now + public Dictionary PipeNetworkProxiesCache { get; } = new(); + + private readonly IConverterSettingsStore _converterSettings; + + public PipeNetworkHandler(IConverterSettingsStore converterSettings) + { + _converterSettings = converterSettings; + } + + /// + /// Extracts the pipe network from a part and stores in the appId of the part. + /// + /// + /// + public void HandlePipeNetwork(CDB.Part part) + { + if (part.NetworkId == ADB.ObjectId.Null) + { + return; + } + + string networkApplicationId = part.NetworkId.GetSpeckleApplicationId(); + string partApplicationId = part.GetSpeckleApplicationId(); + if (PipeNetworkProxiesCache.TryGetValue(networkApplicationId, out GroupProxy? value)) + { + value.objects.Add(partApplicationId); + } + else + { + using (var tr = _converterSettings.Current.Document.Database.TransactionManager.StartTransaction()) + { + var network = (CDB.Network)tr.GetObject(part.NetworkId, ADB.OpenMode.ForRead); + + PipeNetworkProxiesCache[networkApplicationId] = new() + { + name = network.Name, + objects = new() { partApplicationId }, + applicationId = networkApplicationId + }; + + tr.Commit(); + } + } + return; + } +} diff --git a/Converters/Civil3d/Speckle.Converters.Civil3dShared/ServiceRegistration.cs b/Converters/Civil3d/Speckle.Converters.Civil3dShared/ServiceRegistration.cs index 0b001f819..e65e403f1 100644 --- a/Converters/Civil3d/Speckle.Converters.Civil3dShared/ServiceRegistration.cs +++ b/Converters/Civil3d/Speckle.Converters.Civil3dShared/ServiceRegistration.cs @@ -1,10 +1,12 @@ -using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection; using Speckle.Converters.Autocad; +using Speckle.Converters.Civil3dShared.Helpers; +using Speckle.Converters.Civil3dShared.ToSpeckle; using Speckle.Converters.Common; using Speckle.Converters.Common.Registration; using Speckle.Sdk; -namespace Speckle.Converters.Civil3d; +namespace Speckle.Converters.Civil3dShared; public static class ServiceRegistration { @@ -16,7 +18,7 @@ public static void AddCivil3dConverters(this IServiceCollection serviceCollectio serviceCollection.AddMatchingInterfacesAsTransient(civil3dAssembly); serviceCollection.AddMatchingInterfacesAsTransient(autocadAssembly); // Register single root - serviceCollection.AddRootCommon(civil3dAssembly); + serviceCollection.AddRootCommon(civil3dAssembly); // register all application converters serviceCollection.AddApplicationConverters( @@ -31,5 +33,16 @@ public static void AddCivil3dConverters(this IServiceCollection serviceCollectio IConverterSettingsStore, ConverterSettingsStore >(); + + // add other classes + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); } } diff --git a/Converters/Civil3d/Speckle.Converters.Civil3dShared/Speckle.Converters.Civil3dShared.projitems b/Converters/Civil3d/Speckle.Converters.Civil3dShared/Speckle.Converters.Civil3dShared.projitems index 117817a09..eb1c74523 100644 --- a/Converters/Civil3d/Speckle.Converters.Civil3dShared/Speckle.Converters.Civil3dShared.projitems +++ b/Converters/Civil3d/Speckle.Converters.Civil3dShared/Speckle.Converters.Civil3dShared.projitems @@ -10,11 +10,24 @@ - + + + + + + + - - + + + + + + + + + \ No newline at end of file diff --git a/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/BuiltElements/CivilEntityToSpeckleTopLevelConverter.cs b/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/BuiltElements/CivilEntityToSpeckleTopLevelConverter.cs new file mode 100644 index 000000000..f2433fa62 --- /dev/null +++ b/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/BuiltElements/CivilEntityToSpeckleTopLevelConverter.cs @@ -0,0 +1,65 @@ +using Speckle.Converters.Civil3dShared.Helpers; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Objects; +using Speckle.Sdk.Models; + +namespace Speckle.Converters.Civil3dShared.ToSpeckle.BuiltElements; + +[NameAndRankValue(nameof(CDB.Entity), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class CivilEntityToSpeckleTopLevelConverter : IToSpeckleTopLevelConverter +{ + private readonly IConverterSettingsStore _settingsStore; + private readonly DisplayValueExtractor _displayValueExtractor; + private readonly BaseCurveExtractor _baseCurveExtractor; + private readonly ClassPropertiesExtractor _classPropertiesExtractor; + + public CivilEntityToSpeckleTopLevelConverter( + IConverterSettingsStore settingsStore, + DisplayValueExtractor displayValueExtractor, + BaseCurveExtractor baseCurveExtractor, + ClassPropertiesExtractor classPropertiesExtractor + ) + { + _settingsStore = settingsStore; + _displayValueExtractor = displayValueExtractor; + _baseCurveExtractor = baseCurveExtractor; + _classPropertiesExtractor = classPropertiesExtractor; + } + + public Base Convert(object target) => Convert((CDB.Entity)target); + + public Base Convert(CDB.Entity target) + { + Base civilObject = new(); + civilObject["type"] = target.GetType().ToString().Split('.').Last(); + civilObject["name"] = target.Name; + civilObject["units"] = _settingsStore.Current.SpeckleUnits; + + // get basecurve + List? baseCurves = _baseCurveExtractor.GetBaseCurve(target); + if (baseCurves is not null) + { + civilObject["baseCurves"] = baseCurves; + } + + // extract display value + List display = _displayValueExtractor.GetDisplayValue(target); + if (display.Count > 0) + { + civilObject["displayValue"] = display; + } + + // add any additional class properties + Dictionary? classProperties = _classPropertiesExtractor.GetClassProperties(target); + if (classProperties is not null) + { + foreach (string key in classProperties.Keys) + { + civilObject[$"{key}"] = classProperties[key]; + } + } + + return civilObject; + } +} diff --git a/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/BuiltElements/PipeToSpeckleConverter.cs b/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/BuiltElements/PipeToSpeckleConverter.cs deleted file mode 100644 index d79ea563f..000000000 --- a/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/BuiltElements/PipeToSpeckleConverter.cs +++ /dev/null @@ -1,60 +0,0 @@ -using Speckle.Converters.Common; -using Speckle.Converters.Common.Objects; -using Speckle.Objects; -using Speckle.Objects.Other; -using Speckle.Sdk.Models; -using AECPropDB = Autodesk.Aec.PropertyData.DatabaseServices; - -namespace Speckle.Converters.Civil3d.ToSpeckle.BuiltElements; - -[NameAndRankValue(nameof(CDB.Pipe), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] -public class PipeToSpeckleConverter : IToSpeckleTopLevelConverter -{ - private readonly ITypedConverter _pointConverter; - private readonly ITypedConverter _curveConverter; - private readonly ITypedConverter _boxConverter; - private readonly ITypedConverter _solidConverter; - private readonly ITypedConverter> _propertySetConverter; - private readonly IConverterSettingsStore _settingsStore; - - public PipeToSpeckleConverter( - ITypedConverter pointConverter, - ITypedConverter curveConverter, - ITypedConverter boxConverter, - ITypedConverter solidConverter, - ITypedConverter> propertySetConverter, - IConverterSettingsStore settingsStore - ) - { - _pointConverter = pointConverter; - _curveConverter = curveConverter; - _boxConverter = boxConverter; - _solidConverter = solidConverter; - _propertySetConverter = propertySetConverter; - _settingsStore = settingsStore; - } - - public Base Convert(object target) => Convert((CDB.Pipe)target); - - public SOBE.Pipe Convert(CDB.Pipe target) - { - ICurve curve = _curveConverter.Convert(target.BaseCurve); - SOG.Mesh pipeMesh = _solidConverter.Convert(target.Solid3dBody); - - SOBE.Pipe specklePipe = - new() - { - baseCurve = curve, - diameter = target.InnerDiameterOrWidth, - length = target.Length3DToInsideEdge, - displayValue = new List { pipeMesh }, - units = _settingsStore.Current.SpeckleUnits - }; - - // POC: not setting property sets yet, need to determine connector parameter interoperability - // POC: not setting part data yet, same reason as above - // POC: not setting additional pipe properties, probably should scope a CivilPipe class - - return specklePipe; - } -} diff --git a/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Properties/ClassPropertiesExtractor.cs b/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Properties/ClassPropertiesExtractor.cs new file mode 100644 index 000000000..f7ce2f20f --- /dev/null +++ b/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Properties/ClassPropertiesExtractor.cs @@ -0,0 +1,208 @@ +using Autodesk.Civil.DatabaseServices; +using Speckle.Converters.Civil3dShared.Extensions; +using Speckle.Converters.Civil3dShared.Helpers; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Civil3dShared.ToSpeckle; + +/// +/// Extracts class properties deemed important from a civil entity. +/// Should not repeat any data that would be included on property sets and general properties on the object. +/// Expects to be scoped per operation. +/// +public class ClassPropertiesExtractor +{ + private readonly IConverterSettingsStore _settingsStore; + private readonly ITypedConverter _point3dCollectionConverter; + private readonly ITypedConverter _pointConverter; + private readonly CatchmentGroupHandler _catchmentGroupHandler; + private readonly PipeNetworkHandler _pipeNetworkHandler; + + public ClassPropertiesExtractor( + IConverterSettingsStore settingsStore, + ITypedConverter point3dCollectionConverter, + ITypedConverter pointConverter, + CatchmentGroupHandler catchmentGroupHandler, + PipeNetworkHandler pipeNetworkHandler + ) + { + _point3dCollectionConverter = point3dCollectionConverter; + _pointConverter = pointConverter; + _settingsStore = settingsStore; + _catchmentGroupHandler = catchmentGroupHandler; + _pipeNetworkHandler = pipeNetworkHandler; + } + + /// + /// Extracts general properties from a civil entity. Expects to be scoped per operation. + /// + /// + /// + public Dictionary? GetClassProperties(CDB.Entity entity) + { + switch (entity) + { + case CDB.Catchment catchment: + return ExtractCatchmentProperties(catchment); + case CDB.Site site: + return ExtractSiteProperties(site); + case CDB.Pipe pipe: + return ExtractPipeProperties(pipe); + case CDB.Structure structure: + return ExtractStructureProperties(structure); + + default: + return null; + } + } + + // For more info on how points are used: https://help.autodesk.com/view/CIV3D/2024/ENU/?guid=GUID-CBABE972-D690-49AE-A7DE-60F2E1B0675D + private Dictionary ExtractPointProperties(CDB.Point point) + { + Dictionary pointProperties = + new() + { + ["elevation"] = point.Elevation, + ["station"] = point.Station, + ["isLoopPoint"] = point.IsLoopPoint + }; + + if (point.Codes.Count > 0) + { + pointProperties["codes"] = point.Codes.ToList(); + } + + return pointProperties; + } + + private Dictionary ExtractPipeProperties(CDB.Pipe pipe) + { + Dictionary pipeProperties = + new() + { + ["innerDiameterOrWidth"] = pipe.InnerDiameterOrWidth, + ["innerHeight"] = pipe.InnerHeight, + ["slope"] = pipe.Slope, + ["shape"] = pipe.CrossSectionalShape.ToString(), + ["length2d"] = pipe.Length2D, + ["minimumCover"] = pipe.MinimumCover, + ["maximumCover"] = pipe.MaximumCover, + ["junctionLoss"] = pipe.JunctionLoss, + ["flowDirection"] = pipe.FlowDirection.ToString(), + ["flowRate"] = pipe.FlowRate + }; + + if (pipe.StartStructureId != ADB.ObjectId.Null) + { + pipeProperties["startStructureId"] = pipe.StartStructureId.GetSpeckleApplicationId(); + } + + if (pipe.EndStructureId != ADB.ObjectId.Null) + { + pipeProperties["endStructureId"] = pipe.EndStructureId.GetSpeckleApplicationId(); + } + + ExtractPartProperties(pipe, pipeProperties); + + return pipeProperties; + } + + private Dictionary ExtractStructureProperties(CDB.Structure structure) + { + var location = _pointConverter.Convert(structure.Location); + + Dictionary structureProperties = + new() + { + ["location"] = location, + ["northing"] = structure.Northing, + ["rotation"] = structure.Rotation, + ["sumpDepth"] = structure.SumpDepth, + ["sumpElevation"] = structure.SumpElevation, + ["innerDiameterOrWidth"] = structure.InnerDiameterOrWidth + }; + + if (structure.BoundingShape == BoundingShapeType.Box) + { + structureProperties["innerLength"] = structure.InnerLength; + structureProperties["length"] = structure.Length; + } + + ExtractPartProperties(structure, structureProperties); + + return structureProperties; + } + + private void ExtractPartProperties(CDB.Part part, Dictionary dict) + { + // process the part's pipe network with the pipe network handler + _pipeNetworkHandler.HandlePipeNetwork(part); + + dict["domain"] = part.Domain.ToString(); + dict["partType"] = part.PartType.ToString(); + if (part.RefSurfaceId != ADB.ObjectId.Null) + { + dict["surfaceId"] = part.RefSurfaceId.GetSpeckleApplicationId(); + } + + return; + } + + private Dictionary ExtractSiteProperties(CDB.Site site) + { + Dictionary catchmentProperties = new(); + + if (site.GetAlignmentIds().Count > 0) + { + catchmentProperties["alignmentIds"] = GetSpeckleApplicationIdsFromCollection(site.GetAlignmentIds()); + } + + if (site.GetFeatureLineIds().Count > 0) + { + catchmentProperties["featureLineIds"] = GetSpeckleApplicationIdsFromCollection(site.GetFeatureLineIds()); + } + + if (site.GetParcelIds().Count > 0) + { + catchmentProperties["parcelIds"] = GetSpeckleApplicationIdsFromCollection(site.GetParcelIds()); + } + + return catchmentProperties; + } + + private Dictionary ExtractCatchmentProperties(CDB.Catchment catchment) + { + // get the bounding curve of the catchment + SOG.Polyline boundary = _point3dCollectionConverter.Convert(catchment.BoundaryPolyline3d); + + // use the catchment group handler to process the catchment's group + _catchmentGroupHandler.HandleCatchmentGroup(catchment); + + return new() + { + ["antecedentWetness"] = catchment.AntecedentWetness, + ["area"] = catchment.Area, + ["area2d"] = catchment.Area2d, + ["boundary"] = boundary, + ["exclusionary"] = catchment.Exclusionary, + ["hydrologicalSoilGroup"] = catchment.HydrologicalSoilGroup.ToString(), + ["imperviousArea"] = catchment.ImperviousArea, + ["manningsCoefficient"] = catchment.ManningsCoefficient, + ["perimeter2d"] = catchment.Perimeter2d, + ["runoffCoefficient"] = catchment.RunoffCoefficient, + ["timeOfConcentration"] = catchment.TimeOfConcentration + }; + } + + private List GetSpeckleApplicationIdsFromCollection(ADB.ObjectIdCollection collection) + { + List speckleAppIds = new(collection.Count); + foreach (ADB.ObjectId parcelId in collection) + { + speckleAppIds.Add(parcelId.GetSpeckleApplicationId()); + } + + return speckleAppIds; + } +} diff --git a/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Properties/GeneralPropertiesExtractor.cs b/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Properties/GeneralPropertiesExtractor.cs new file mode 100644 index 000000000..fd8ed7a3e --- /dev/null +++ b/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Properties/GeneralPropertiesExtractor.cs @@ -0,0 +1,76 @@ +using System.Reflection; + +namespace Speckle.Converters.Civil3dShared.ToSpeckle; + +/// +/// Extracts general properties related to analysis, statistics, and calculations out from a civil entity. Expects to be scoped per operation. +/// +public class GeneralPropertiesExtractor +{ + public GeneralPropertiesExtractor() { } + + /// + /// Extracts general properties from a civil entity. Expects to be scoped per operation. + /// + /// + /// + public Dictionary? GetGeneralProperties(CDB.Entity entity) + { + Dictionary? generalPropertiesDict = null; + switch (entity) + { + // surface -> properties -> statistics -> general, extended, and tin/grid properties + case CDB.Surface surface: + generalPropertiesDict = ExtractSurfaceProperties(surface); + break; + } + + return generalPropertiesDict; + } + + private Dictionary ExtractSurfaceProperties(CDB.Surface surface) + { + Dictionary generalPropertiesDict = new(); + + // get statistics props + Dictionary statisticsDict = new(); + statisticsDict["General"] = ExtractPropertiesGeneric(surface.GetGeneralProperties()); + switch (surface) + { + case CDB.TinSurface tinSurface: + statisticsDict["TIN"] = ExtractPropertiesGeneric(tinSurface.GetTinProperties()); + break; + case CDB.TinVolumeSurface tinVolumeSurface: + statisticsDict["TIN"] = ExtractPropertiesGeneric(tinVolumeSurface.GetTinProperties()); + break; + case CDB.GridSurface gridSurface: + statisticsDict["Grid"] = ExtractPropertiesGeneric(gridSurface.GetGridProperties()); + break; + case CDB.GridVolumeSurface gridVolumeSurface: + statisticsDict["Grid"] = ExtractPropertiesGeneric( + gridVolumeSurface.GetGridProperties() + ); + break; + } + + // set all general props + generalPropertiesDict["Statistics"] = statisticsDict; + return generalPropertiesDict; + } + + // A generic method to create a dictionary from an object types's properties + private Dictionary ExtractPropertiesGeneric(T obj) + { + Dictionary propertiesDict = new(); + + var type = typeof(T); + PropertyInfo[] properties = type.GetProperties(); + foreach (PropertyInfo? property in properties) + { + var value = property.GetValue(obj); + propertiesDict[property.Name] = value; + } + + return propertiesDict; + } +} diff --git a/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Properties/PartDataExtractor.cs b/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Properties/PartDataExtractor.cs new file mode 100644 index 000000000..338ca6650 --- /dev/null +++ b/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Properties/PartDataExtractor.cs @@ -0,0 +1,117 @@ +namespace Speckle.Converters.Civil3dShared.ToSpeckle; + +/// +/// Extracts parameters out from an element. Expects to be scoped per operation. +/// +public class PartDataExtractor +{ + /// POC: Note that we're abusing dictionaries in here because we've yet to have a simple way to serialize non-base derived classes (or structs?) + + public PartDataExtractor() { } + + /// + /// Extracts part data out from an entity. Expects to be scoped per operation. + /// + /// + /// + public Dictionary? GetPartData(CDB.Entity entity) + { + if (entity is CDB.Part part) + { + return ParsePartData(part.PartData); + } + + return null; + } + + private Dictionary ParsePartData(CDB.PartDataRecord partData) + { + var result = new Dictionary(); + + foreach (CDB.PartDataField field in partData.GetAllDataFields()) + { + var value = GetValue(field); + string fieldName = field.Context.ToString(); // we're using the context for the field name because it is more human-readable than the name prop + + var fieldDictionary = new Dictionary() + { + ["value"] = value, + ["name"] = field.Name, + ["context"] = fieldName, + ["units"] = field.Units + }; + + if (!result.ContainsKey(fieldName)) + { + result.Add(fieldName, fieldDictionary); + } + } + + return result; + } + + private object? GetValue(CDB.PartDataField field) + { + switch (field.DataType) + { + case CDB.PartCatalogDataType.Double: + return field.IsFromList + ? GetValueListGeneric(field.ValueList) + : field.IsFromRange + ? GetValueRangeGeneric(field.ValueRange) + : field.Value as double?; + case CDB.PartCatalogDataType.Int: + return field.IsFromList + ? GetValueListGeneric(field.ValueList) + : field.IsFromRange + ? GetValueRangeGeneric(field.ValueRange) + : field.Value as int?; + case CDB.PartCatalogDataType.Bool: + return field.IsFromList + ? GetValueListGeneric(field.ValueList) + : field.IsFromRange + ? GetValueRangeGeneric(field.ValueRange) + : field.Value as bool?; + default: + return field.IsFromList + ? GetValueListGeneric(field.ValueList) + : field.IsFromRange + ? GetValueRangeGeneric(field.ValueRange) + : field.Value.ToString(); + } + } + + private List? GetValueListGeneric(CDB.PartDataList list) + { + if (list == null || list.Count == 0) + { + return default; + } + + List result = new(); + for (int i = 0; i < list.Count; i++) + { + if (list[i] is TResult item) + { + result.Add(item); + } + } + + return result; + } + + private List? GetValueRangeGeneric(CDB.PartDataRange range) + { + if (range == null) + { + return default; + } + + if (range.RangeMin is TResult min && range.RangeMax is TResult max) + { + return new() { min, max }; + } + + return default; + } +} diff --git a/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Properties/PropertySetDefinitionHandler.cs b/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Properties/PropertySetDefinitionHandler.cs new file mode 100644 index 000000000..73f210461 --- /dev/null +++ b/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Properties/PropertySetDefinitionHandler.cs @@ -0,0 +1,67 @@ +using Speckle.Sdk; + +namespace Speckle.Converters.Civil3dShared.ToSpeckle; + +/// +/// Keeps track during a send conversion operation of the property set definitions used. +/// +public class PropertySetDefinitionHandler +{ + /// + /// Keeps track of all property set definitions used in the current send operation. This should be added to the properties dict on the root commit object post conversion. + /// + /// POC: Note that we're abusing dictionaries in here because we've yet to have a simple way to serialize non-base derived classes (or structs?) + /// POC: We're storing these by property set def name atm. There is a decent change different property sets can have the same name, need to validate this. + public Dictionary> Definitions { get; } = new(); + + /// + /// Extracts out and stores in the property set definition. + /// + /// The property set definition. Assumes this is opened for Read already. + /// + public (Dictionary propertyDefinitionNames, string name) HandleDefinition( + AAECPDB.PropertySetDefinition setDefinition + ) + { + Dictionary propertyDefinitionsDict = new(); // this is used to store on the property set definition + Dictionary propertyDefinitionNames = new(); // this is used to pass to the instance for property value retrieval + foreach (AAECPDB.PropertyDefinition propertyDefinition in setDefinition.Definitions) + { + string propertyName = propertyDefinition.Name; + propertyDefinitionNames[propertyDefinition.Id] = propertyName; + var propertyDict = new Dictionary() + { + ["name"] = propertyName, + ["description"] = propertyDefinition.Description, + ["id"] = propertyDefinition.Id, + ["isReadOnly"] = propertyDefinition.IsReadOnly, + ["dataType"] = propertyDefinition.DataType.ToString(), + ["defaultValue"] = propertyDefinition.DefaultData + }; + + try + { + // accessing unit type prop can be expected to throw if it's not applicable to the definition + propertyDict["units"] = propertyDefinition.UnitType.GetTypeDisplayName(true); + } + catch (Exception e) when (!e.IsFatal()) { } + + propertyDefinitionsDict[propertyName] = propertyDict; + } + + var name = setDefinition.Name; + + if (Definitions.ContainsKey(name)) + { + return (propertyDefinitionNames, name); + } + + Definitions[name] = new Dictionary() + { + ["name"] = name, + ["propertyDefinitions"] = propertyDefinitionsDict + }; + + return (propertyDefinitionNames, name); + } +} diff --git a/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Properties/PropertySetExtractor.cs b/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Properties/PropertySetExtractor.cs new file mode 100644 index 000000000..6c56bae7b --- /dev/null +++ b/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Properties/PropertySetExtractor.cs @@ -0,0 +1,151 @@ +using Microsoft.Extensions.Logging; +using Speckle.Converters.Common; +using Speckle.Sdk; + +namespace Speckle.Converters.Civil3dShared.ToSpeckle; + +/// +/// Extracts property sets out from a dbobject. Expects to be scoped per operation. +/// +public class PropertySetExtractor +{ + /// POC: Note that we're abusing dictionaries in here because we've yet to have a simple way to serialize non-base derived classes (or structs?) + private readonly PropertySetDefinitionHandler _propertySetDefinitionHandler; + private readonly IConverterSettingsStore _settingsStore; + private readonly ILogger _logger; + + public PropertySetExtractor( + PropertySetDefinitionHandler propertySetDefinitionHandler, + IConverterSettingsStore settingsStore, + ILogger logger + ) + { + _propertySetDefinitionHandler = propertySetDefinitionHandler; + _settingsStore = settingsStore; + _logger = logger; + } + + /// + /// Extracts property sets out from a dbObject. Expects to be scoped per operation. + /// + /// + /// + public Dictionary? GetPropertySets(ADB.DBObject dbObject) + { + ADB.ObjectIdCollection? propertySetIds = null; + + try + { + propertySetIds = AAECPDB.PropertyDataServices.GetPropertySets(dbObject); + } + catch (Exception e) when (!e.IsFatal()) + { + _logger.LogWarning(e, $"Failed to retrieve property sets on object {dbObject.Handle.Value}"); + } + + if (propertySetIds is null || propertySetIds.Count == 0) + { + return null; + } + + using (var tr = _settingsStore.Current.Document.Database.TransactionManager.StartTransaction()) + { + Dictionary propertySets = new(); + foreach (ADB.ObjectId id in propertySetIds) + { + AAECPDB.PropertySet propertySet = (AAECPDB.PropertySet)tr.GetObject(id, ADB.OpenMode.ForRead); + + // parse property sets within this transaction, since we'll need it for retrieving the definition as well + if (ParsePropertySet(propertySet, tr) is (string propertySetName, Dictionary propertySetValue)) + { + propertySets[propertySetName] = propertySetValue; + } + } + + tr.Commit(); + return propertySets; + } + } + + private (string, Dictionary)? ParsePropertySet(AAECPDB.PropertySet propertySet, ADB.Transaction tr) + { + try + { + // var isNullOrEmpty = value == null || (value is string s && string.IsNullOrEmpty(s)); + // POC: should add same check as in revit for sending null or empty values + + var setDefinition = (AAECPDB.PropertySetDefinition) + tr.GetObject(propertySet.PropertySetDefinition, ADB.OpenMode.ForRead); + + (Dictionary propertyDefinitionNames, string name) = _propertySetDefinitionHandler.HandleDefinition( + setDefinition + ); + + // get all property values in the propertyset + Dictionary properties = new(); + foreach (AAECPDB.PropertySetData data in propertySet.PropertySetData) + { + string dataName = propertyDefinitionNames.TryGetValue(data.Id, out string propertyDefinitionName) + ? propertyDefinitionName + : data.FieldBucketId; + + var value = GetValue(data); + + var propertyValueDict = new Dictionary() { ["value"] = value, ["name"] = dataName }; + + try + { + // accessing unit type prop can be expected to throw if it's not applicable to the definition + propertyValueDict["units"] = data.UnitType.GetTypeDisplayName(true); + } + catch (Exception e) when (!e.IsFatal()) { } + + properties[dataName] = propertyValueDict; + } + + // add property set to dict + Dictionary propertySetDict = + new() + { + ["name"] = name, + ["properties"] = properties, + ["definitionName"] = name + }; + + return (name, propertySetDict); + } + catch (Exception e) when (!e.IsFatal()) + { + _logger.LogWarning(e, $"Failed to convert property set {propertySet.Name}"); + } + + return null; + } + + private object? GetValue(AAECPDB.PropertySetData data) + { + object fieldData = data.GetData(data.UnitType); + + switch (data.DataType) + { + case AAEC.PropertyData.DataType.Integer: + return fieldData as int?; + case AAEC.PropertyData.DataType.Real: + return fieldData as double?; + case AAEC.PropertyData.DataType.TrueFalse: + return fieldData as bool?; + case AAEC.PropertyData.DataType.Graphic: // POC: not sure how to support atm + return null; + case AAEC.PropertyData.DataType.List: + return fieldData as List; + case AAEC.PropertyData.DataType.AutoIncrement: + return fieldData as int?; + case AAEC.PropertyData.DataType.AlphaIncrement: // POC: not sure what this is + return fieldData; + case AAEC.PropertyData.DataType.Text: + return fieldData as string; + default: + return fieldData; + } + } +} diff --git a/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Raw/GridSurfaceToSpeckleMeshRawConverter.cs b/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Raw/GridSurfaceToSpeckleMeshRawConverter.cs new file mode 100644 index 000000000..1c874940a --- /dev/null +++ b/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Raw/GridSurfaceToSpeckleMeshRawConverter.cs @@ -0,0 +1,71 @@ +using Autodesk.AutoCAD.Geometry; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Civil3dShared.ToSpeckle.Raw; + +public class GridSurfaceToSpeckleMeshRawConverter : ITypedConverter +{ + private readonly IConverterSettingsStore _settingsStore; + + public GridSurfaceToSpeckleMeshRawConverter(IConverterSettingsStore settingsStore) + { + _settingsStore = settingsStore; + } + + public SOG.Mesh Convert(object target) => Convert((CDB.GridSurface)target); + + public SOG.Mesh Convert(CDB.GridSurface target) + { + List vertices = new(); + List faces = new(); + Dictionary indices = new(); + + int indexCounter = 0; + foreach (var cell in target.GetCells(false)) + { + try + { + Point3d[] cellVertices = + { + cell.BottomLeftVertex.Location, + cell.BottomRightVertex.Location, + cell.TopLeftVertex.Location, + cell.TopRightVertex.Location + }; + + foreach (Point3d p in cellVertices) + { + if (!indices.ContainsKey(p)) + { + vertices.Add(p.X); + vertices.Add(p.Y); + vertices.Add(p.Z); + indices.Add(p, indexCounter); + indexCounter++; + } + } + + faces.Add(4); + faces.Add(indices[cellVertices[0]]); + faces.Add(indices[cellVertices[1]]); + faces.Add(indices[cellVertices[2]]); + faces.Add(indices[cellVertices[3]]); + } + finally + { + cell.Dispose(); + } + } + + SOG.Mesh mesh = + new() + { + vertices = vertices, + faces = faces, + units = _settingsStore.Current.SpeckleUnits + }; + + return mesh; + } +} diff --git a/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Raw/Point3dCollectionToSpeckleRawConverter.cs b/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Raw/Point3dCollectionToSpeckleRawConverter.cs new file mode 100644 index 000000000..8cb13973a --- /dev/null +++ b/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Raw/Point3dCollectionToSpeckleRawConverter.cs @@ -0,0 +1,47 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Civil3dShared.ToSpeckle.Raw; + +public class Point3dCollectionToSpeckleRawConverter : ITypedConverter +{ + private readonly ITypedConverter _pointConverter; + private readonly IConverterSettingsStore _settingsStore; + + public Point3dCollectionToSpeckleRawConverter( + ITypedConverter pointConverter, + IConverterSettingsStore settingsStore + ) + { + _pointConverter = pointConverter; + _settingsStore = settingsStore; + } + + public SOG.Polyline Convert(object target) => Convert((AG.Point3dCollection)target); + + public SOG.Polyline Convert(AG.Point3dCollection target) + { + List value = new(); + double length = 0; + AG.Point3d? previousPoint = null; + foreach (AG.Point3d point in target) + { + value.Add(point.X); + value.Add(point.Y); + value.Add(point.Z); + if (previousPoint is AG.Point3d p) + { + length += point.DistanceTo(p); + } + previousPoint = point; + } + + return new() + { + value = value, + units = _settingsStore.Current.SpeckleUnits, + closed = true, + length = length + }; + } +} diff --git a/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Raw/PropertySetToSpeckleRawConverter.cs b/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Raw/PropertySetToSpeckleRawConverter.cs deleted file mode 100644 index c2517f70b..000000000 --- a/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Raw/PropertySetToSpeckleRawConverter.cs +++ /dev/null @@ -1,56 +0,0 @@ -using Speckle.Converters.Common; -using Speckle.Converters.Common.Objects; -using Speckle.Objects.Other; -using AECPropDB = Autodesk.Aec.PropertyData.DatabaseServices; - -namespace Speckle.Converters.Civil3d.ToSpeckle.Raw; - -public class PropertySetToSpeckleRawConverter : ITypedConverter> -{ - private readonly ITypedConverter _vectorConverter; - private readonly ITypedConverter _pointConverter; - private readonly IConverterSettingsStore _settingsStore; - - public PropertySetToSpeckleRawConverter( - ITypedConverter vectorConverter, - ITypedConverter pointConverter, - IConverterSettingsStore settingsStore - ) - { - _vectorConverter = vectorConverter; - _pointConverter = pointConverter; - _settingsStore = settingsStore; - } - - public List Convert(object target) => Convert((AECPropDB.PropertySet)target); - - public List Convert(AECPropDB.PropertySet target) - { - List properties = new(); - - //TODO using? - ADB.Transaction tr = _settingsStore.Current.Document.TransactionManager.TopTransaction; - AECPropDB.PropertySetDefinition setDef = (AECPropDB.PropertySetDefinition) - tr.GetObject(target.PropertySetDefinition, ADB.OpenMode.ForRead); - - // get property definitions - var propDefs = new Dictionary(); - foreach (AECPropDB.PropertyDefinition def in setDef.Definitions) - { - propDefs.Add(def.Id, def); - } - - foreach (AECPropDB.PropertySetData data in target.PropertySetData) - { - string fieldName = propDefs.TryGetValue(data.Id, out AECPropDB.PropertyDefinition value) - ? value.Name - : data.FieldBucketId; - - object fieldData = data.GetData(); - DataField field = new(fieldName, fieldData.GetType().Name, fieldData, data.UnitType.PluralName(false)); - properties.Add(field); - } - - return properties; - } -} diff --git a/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Raw/TinSurfaceToSpeckleMeshRawConverter.cs b/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Raw/TinSurfaceToSpeckleMeshRawConverter.cs new file mode 100644 index 000000000..aaf607bd9 --- /dev/null +++ b/Converters/Civil3d/Speckle.Converters.Civil3dShared/ToSpeckle/Raw/TinSurfaceToSpeckleMeshRawConverter.cs @@ -0,0 +1,67 @@ +using Autodesk.AutoCAD.Geometry; +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Civil3dShared.ToSpeckle.Raw; + +public class TinSurfaceToSpeckleMeshRawConverter : ITypedConverter +{ + private readonly IConverterSettingsStore _settingsStore; + + public TinSurfaceToSpeckleMeshRawConverter(IConverterSettingsStore settingsStore) + { + _settingsStore = settingsStore; + } + + public SOG.Mesh Convert(object target) => Convert((CDB.TinSurface)target); + + public SOG.Mesh Convert(CDB.TinSurface target) + { + List vertices = new(); + List faces = new(); + Dictionary indices = new(); + + int indexCounter = 0; + foreach (var triangle in target.GetTriangles(false)) + { + try + { + Point3d[] triangleVertices = + { + triangle.Vertex1.Location, + triangle.Vertex2.Location, + triangle.Vertex3.Location + }; + foreach (Point3d p in triangleVertices) + { + if (!indices.ContainsKey(p)) + { + vertices.Add(p.X); + vertices.Add(p.Y); + vertices.Add(p.Z); + indices.Add(p, indexCounter); + indexCounter++; + } + } + faces.Add(3); + faces.Add(indices[triangleVertices[0]]); + faces.Add(indices[triangleVertices[1]]); + faces.Add(indices[triangleVertices[2]]); + } + finally + { + triangle.Dispose(); + } + } + + SOG.Mesh mesh = + new() + { + faces = faces, + vertices = vertices, + units = _settingsStore.Current.SpeckleUnits + }; + + return mesh; + } +} diff --git a/Converters/Revit/Speckle.Converters.Revit2022.Tests/Speckle.Converters.Revit2022.Tests.csproj b/Converters/Revit/Speckle.Converters.Revit2022.Tests/Speckle.Converters.Revit2022.Tests.csproj index 43338bfd8..2f4cc84f7 100644 --- a/Converters/Revit/Speckle.Converters.Revit2022.Tests/Speckle.Converters.Revit2022.Tests.csproj +++ b/Converters/Revit/Speckle.Converters.Revit2022.Tests/Speckle.Converters.Revit2022.Tests.csproj @@ -25,6 +25,6 @@ - + diff --git a/Converters/Revit/Speckle.Converters.Revit2022.Tests/packages.lock.json b/Converters/Revit/Speckle.Converters.Revit2022.Tests/packages.lock.json index 3dbff6d8e..693ece02c 100644 --- a/Converters/Revit/Speckle.Converters.Revit2022.Tests/packages.lock.json +++ b/Converters/Revit/Speckle.Converters.Revit2022.Tests/packages.lock.json @@ -27,6 +27,15 @@ "Microsoft.TestPlatform.TestHost": "17.10.0" } }, + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3" + } + }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -78,9 +87,9 @@ }, "Speckle.Revit2022.Fakes": { "type": "Direct", - "requested": "[0.3.0, 1.0.0)", - "resolved": "0.3.0", - "contentHash": "aAy+bmuXn7oFyUyuvwe8Aw0SfuQoX23jTDI1v6ZrTDSVvRxsmJLBZVYcT9V0CuGiP3JwqM5gmy9CkVBfSm+DcA==", + "requested": "[0.3.1, )", + "resolved": "0.3.1", + "contentHash": "kz/taszZjEUEnqJM4qjRqEYdsrD6s9X1N56SXiB2fUiwc7MeM6H1LZaAdRYM/70tukq8OshL5fiWIQZrXrLaAg==", "dependencies": { "System.Drawing.Common": "8.0.6" } @@ -204,6 +213,11 @@ "System.Runtime.CompilerServices.Unsafe": "4.5.1" } }, + "Microsoft.NETFramework.ReferenceAssemblies.net461": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA==" + }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", @@ -349,7 +363,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )" } }, "speckle.testing": { @@ -379,18 +393,18 @@ }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "gpENXDB2i8SiXJZwrLVfB3BwKF1FHUSpPiw0BQ3Hl0V3821UWZjPYw5XK694oxJlVAeEMRvrMZMGsWOcK+3nMw==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "1fyaPjC1kVtroKfoXI7edJX0Uho/TNRWG9mtmIQ7jKhX1zElrJPSoH8zEIMqckZhGmwUrYjlQFPssASNCP721Q==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.145" + "Speckle.Sdk": "3.1.0-dev.162" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "YZLtitiVIxl12pNTXaB9gU+paNwFe++WBw6oIN6Rs5hFPialUgeKbpjh16Zq6wcptjBpvg0CHq29+d456QUAJQ==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "ulT3XNgqIQSmpQFK5YEEwRq4RA6emXBKQHnZxtCz37SJin1PnVpH9m8vOAF9FmaWsgSAfjhhjIZDruMu1oKxSg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", diff --git a/Converters/Revit/Speckle.Converters.Revit2022/packages.lock.json b/Converters/Revit/Speckle.Converters.Revit2022/packages.lock.json index 864ede376..4f91063c7 100644 --- a/Converters/Revit/Speckle.Converters.Revit2022/packages.lock.json +++ b/Converters/Revit/Speckle.Converters.Revit2022/packages.lock.json @@ -2,6 +2,15 @@ "version": 2, "dependencies": { ".NETFramework,Version=v4.8": { + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3" + } + }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -137,6 +146,11 @@ "System.Runtime.CompilerServices.Unsafe": "4.5.1" } }, + "Microsoft.NETFramework.ReferenceAssemblies.net48": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ==" + }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", @@ -259,7 +273,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )" } }, "Microsoft.Extensions.Logging": { @@ -282,18 +296,18 @@ }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "gpENXDB2i8SiXJZwrLVfB3BwKF1FHUSpPiw0BQ3Hl0V3821UWZjPYw5XK694oxJlVAeEMRvrMZMGsWOcK+3nMw==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "1fyaPjC1kVtroKfoXI7edJX0Uho/TNRWG9mtmIQ7jKhX1zElrJPSoH8zEIMqckZhGmwUrYjlQFPssASNCP721Q==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.145" + "Speckle.Sdk": "3.1.0-dev.162" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "YZLtitiVIxl12pNTXaB9gU+paNwFe++WBw6oIN6Rs5hFPialUgeKbpjh16Zq6wcptjBpvg0CHq29+d456QUAJQ==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "ulT3XNgqIQSmpQFK5YEEwRq4RA6emXBKQHnZxtCz37SJin1PnVpH9m8vOAF9FmaWsgSAfjhhjIZDruMu1oKxSg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", diff --git a/Converters/Revit/Speckle.Converters.Revit2023.Tests/Speckle.Converters.Revit2023.Tests.csproj b/Converters/Revit/Speckle.Converters.Revit2023.Tests/Speckle.Converters.Revit2023.Tests.csproj index d6f5c8eaa..f2c9896b8 100644 --- a/Converters/Revit/Speckle.Converters.Revit2023.Tests/Speckle.Converters.Revit2023.Tests.csproj +++ b/Converters/Revit/Speckle.Converters.Revit2023.Tests/Speckle.Converters.Revit2023.Tests.csproj @@ -25,6 +25,6 @@ - + diff --git a/Converters/Revit/Speckle.Converters.Revit2023.Tests/packages.lock.json b/Converters/Revit/Speckle.Converters.Revit2023.Tests/packages.lock.json index 68a841dfb..70c7e011c 100644 --- a/Converters/Revit/Speckle.Converters.Revit2023.Tests/packages.lock.json +++ b/Converters/Revit/Speckle.Converters.Revit2023.Tests/packages.lock.json @@ -27,6 +27,15 @@ "Microsoft.TestPlatform.TestHost": "17.10.0" } }, + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3" + } + }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -78,9 +87,9 @@ }, "Speckle.Revit2023.Fakes": { "type": "Direct", - "requested": "[0.3.0, 1.0.0)", - "resolved": "0.3.0", - "contentHash": "kMNRHztk5lncfCJxaxM73UtYIq7f0ekT8Zq99CXrHSYDEKif4tUga8re1uNv2rT1x25CvVtnvyTiNOtrs1yd0g==", + "requested": "[0.3.1, )", + "resolved": "0.3.1", + "contentHash": "uHYAqg2ljL5WpYYAQujgiS6olkGD31lXToyc0yIugAi0cnHtP5z95hjzg3QBbmFizVYyD5n2IwFnR49UiSVfJg==", "dependencies": { "System.Drawing.Common": "8.0.6" } @@ -204,6 +213,11 @@ "System.Runtime.CompilerServices.Unsafe": "4.5.1" } }, + "Microsoft.NETFramework.ReferenceAssemblies.net461": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA==" + }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", @@ -349,7 +363,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )" } }, "speckle.testing": { @@ -379,18 +393,18 @@ }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "gpENXDB2i8SiXJZwrLVfB3BwKF1FHUSpPiw0BQ3Hl0V3821UWZjPYw5XK694oxJlVAeEMRvrMZMGsWOcK+3nMw==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "1fyaPjC1kVtroKfoXI7edJX0Uho/TNRWG9mtmIQ7jKhX1zElrJPSoH8zEIMqckZhGmwUrYjlQFPssASNCP721Q==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.145" + "Speckle.Sdk": "3.1.0-dev.162" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "YZLtitiVIxl12pNTXaB9gU+paNwFe++WBw6oIN6Rs5hFPialUgeKbpjh16Zq6wcptjBpvg0CHq29+d456QUAJQ==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "ulT3XNgqIQSmpQFK5YEEwRq4RA6emXBKQHnZxtCz37SJin1PnVpH9m8vOAF9FmaWsgSAfjhhjIZDruMu1oKxSg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", diff --git a/Converters/Revit/Speckle.Converters.Revit2023/packages.lock.json b/Converters/Revit/Speckle.Converters.Revit2023/packages.lock.json index 9d9b1da8e..22fd4ef63 100644 --- a/Converters/Revit/Speckle.Converters.Revit2023/packages.lock.json +++ b/Converters/Revit/Speckle.Converters.Revit2023/packages.lock.json @@ -2,6 +2,15 @@ "version": 2, "dependencies": { ".NETFramework,Version=v4.8": { + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3" + } + }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -137,6 +146,11 @@ "System.Runtime.CompilerServices.Unsafe": "4.5.1" } }, + "Microsoft.NETFramework.ReferenceAssemblies.net48": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ==" + }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", @@ -259,7 +273,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )" } }, "Microsoft.Extensions.Logging": { @@ -282,18 +296,18 @@ }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "gpENXDB2i8SiXJZwrLVfB3BwKF1FHUSpPiw0BQ3Hl0V3821UWZjPYw5XK694oxJlVAeEMRvrMZMGsWOcK+3nMw==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "1fyaPjC1kVtroKfoXI7edJX0Uho/TNRWG9mtmIQ7jKhX1zElrJPSoH8zEIMqckZhGmwUrYjlQFPssASNCP721Q==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.145" + "Speckle.Sdk": "3.1.0-dev.162" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "YZLtitiVIxl12pNTXaB9gU+paNwFe++WBw6oIN6Rs5hFPialUgeKbpjh16Zq6wcptjBpvg0CHq29+d456QUAJQ==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "ulT3XNgqIQSmpQFK5YEEwRq4RA6emXBKQHnZxtCz37SJin1PnVpH9m8vOAF9FmaWsgSAfjhhjIZDruMu1oKxSg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", diff --git a/Converters/Revit/Speckle.Converters.Revit2024.Tests/Speckle.Converters.Revit2024.Tests.csproj b/Converters/Revit/Speckle.Converters.Revit2024.Tests/Speckle.Converters.Revit2024.Tests.csproj index 58a09acda..54573dcd1 100644 --- a/Converters/Revit/Speckle.Converters.Revit2024.Tests/Speckle.Converters.Revit2024.Tests.csproj +++ b/Converters/Revit/Speckle.Converters.Revit2024.Tests/Speckle.Converters.Revit2024.Tests.csproj @@ -25,6 +25,6 @@ - + diff --git a/Converters/Revit/Speckle.Converters.Revit2024.Tests/packages.lock.json b/Converters/Revit/Speckle.Converters.Revit2024.Tests/packages.lock.json index cdf30fe53..2112e0a11 100644 --- a/Converters/Revit/Speckle.Converters.Revit2024.Tests/packages.lock.json +++ b/Converters/Revit/Speckle.Converters.Revit2024.Tests/packages.lock.json @@ -27,6 +27,15 @@ "Microsoft.TestPlatform.TestHost": "17.10.0" } }, + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3" + } + }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -78,9 +87,9 @@ }, "Speckle.Revit2024.Fakes": { "type": "Direct", - "requested": "[0.3.0, 1.0.0)", - "resolved": "0.3.0", - "contentHash": "gBwoXQDphcbfexbNojI+9Isht8M3citP9j3cMMsnDJPlj7l1rXHzFmzZ7sn0OzuKzmcG92sGvyR1RvsGUtfqYw==", + "requested": "[0.3.1, )", + "resolved": "0.3.1", + "contentHash": "01T15eXABKlHKNBKMfehlnH+Ki5r/0Ozx6sFMCTSnFSF5u12g+Dkefo+GsEIiS1GxuMlEb+BKZbCrFmp8GLaHQ==", "dependencies": { "System.Drawing.Common": "8.0.6" } @@ -204,6 +213,11 @@ "System.Runtime.CompilerServices.Unsafe": "4.5.1" } }, + "Microsoft.NETFramework.ReferenceAssemblies.net461": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA==" + }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", @@ -349,7 +363,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )" } }, "speckle.testing": { @@ -379,18 +393,18 @@ }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "gpENXDB2i8SiXJZwrLVfB3BwKF1FHUSpPiw0BQ3Hl0V3821UWZjPYw5XK694oxJlVAeEMRvrMZMGsWOcK+3nMw==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "1fyaPjC1kVtroKfoXI7edJX0Uho/TNRWG9mtmIQ7jKhX1zElrJPSoH8zEIMqckZhGmwUrYjlQFPssASNCP721Q==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.145" + "Speckle.Sdk": "3.1.0-dev.162" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "YZLtitiVIxl12pNTXaB9gU+paNwFe++WBw6oIN6Rs5hFPialUgeKbpjh16Zq6wcptjBpvg0CHq29+d456QUAJQ==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "ulT3XNgqIQSmpQFK5YEEwRq4RA6emXBKQHnZxtCz37SJin1PnVpH9m8vOAF9FmaWsgSAfjhhjIZDruMu1oKxSg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", diff --git a/Converters/Revit/Speckle.Converters.Revit2024/packages.lock.json b/Converters/Revit/Speckle.Converters.Revit2024/packages.lock.json index 401fd38c2..2b3064cd4 100644 --- a/Converters/Revit/Speckle.Converters.Revit2024/packages.lock.json +++ b/Converters/Revit/Speckle.Converters.Revit2024/packages.lock.json @@ -2,6 +2,15 @@ "version": 2, "dependencies": { ".NETFramework,Version=v4.8": { + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3" + } + }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -137,6 +146,11 @@ "System.Runtime.CompilerServices.Unsafe": "4.5.1" } }, + "Microsoft.NETFramework.ReferenceAssemblies.net48": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ==" + }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", @@ -259,7 +273,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )" } }, "Microsoft.Extensions.Logging": { @@ -282,18 +296,18 @@ }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "gpENXDB2i8SiXJZwrLVfB3BwKF1FHUSpPiw0BQ3Hl0V3821UWZjPYw5XK694oxJlVAeEMRvrMZMGsWOcK+3nMw==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "1fyaPjC1kVtroKfoXI7edJX0Uho/TNRWG9mtmIQ7jKhX1zElrJPSoH8zEIMqckZhGmwUrYjlQFPssASNCP721Q==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.145" + "Speckle.Sdk": "3.1.0-dev.162" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "YZLtitiVIxl12pNTXaB9gU+paNwFe++WBw6oIN6Rs5hFPialUgeKbpjh16Zq6wcptjBpvg0CHq29+d456QUAJQ==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "ulT3XNgqIQSmpQFK5YEEwRq4RA6emXBKQHnZxtCz37SJin1PnVpH9m8vOAF9FmaWsgSAfjhhjIZDruMu1oKxSg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", diff --git a/Converters/Revit/Speckle.Converters.Revit2025/packages.lock.json b/Converters/Revit/Speckle.Converters.Revit2025/packages.lock.json index e72b2bd01..0c3e5dbe5 100644 --- a/Converters/Revit/Speckle.Converters.Revit2025/packages.lock.json +++ b/Converters/Revit/Speckle.Converters.Revit2025/packages.lock.json @@ -2,6 +2,15 @@ "version": 2, "dependencies": { "net8.0-windows7.0": { + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3" + } + }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -136,6 +145,11 @@ "System.Runtime.CompilerServices.Unsafe": "4.5.1" } }, + "Microsoft.NETFramework.ReferenceAssemblies.net461": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA==" + }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", @@ -223,7 +237,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )" } }, "Microsoft.Extensions.Logging": { @@ -246,18 +260,18 @@ }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "gpENXDB2i8SiXJZwrLVfB3BwKF1FHUSpPiw0BQ3Hl0V3821UWZjPYw5XK694oxJlVAeEMRvrMZMGsWOcK+3nMw==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "1fyaPjC1kVtroKfoXI7edJX0Uho/TNRWG9mtmIQ7jKhX1zElrJPSoH8zEIMqckZhGmwUrYjlQFPssASNCP721Q==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.145" + "Speckle.Sdk": "3.1.0-dev.162" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "YZLtitiVIxl12pNTXaB9gU+paNwFe++WBw6oIN6Rs5hFPialUgeKbpjh16Zq6wcptjBpvg0CHq29+d456QUAJQ==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "ulT3XNgqIQSmpQFK5YEEwRq4RA6emXBKQHnZxtCz37SJin1PnVpH9m8vOAF9FmaWsgSAfjhhjIZDruMu1oKxSg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", diff --git a/Converters/Revit/Speckle.Converters.RevitShared.DependencyInjection/RevitConverterModule.cs b/Converters/Revit/Speckle.Converters.RevitShared.DependencyInjection/RevitConverterModule.cs index d2daf71af..1d0a98d23 100644 --- a/Converters/Revit/Speckle.Converters.RevitShared.DependencyInjection/RevitConverterModule.cs +++ b/Converters/Revit/Speckle.Converters.RevitShared.DependencyInjection/RevitConverterModule.cs @@ -24,7 +24,8 @@ public void Load(SpeckleContainerBuilder builder) builder.AddScoped(); builder.AddSingleton(new RevitContext()); - builder.AddSingleton(new RevitMaterialCacheSingleton()); + builder.AddSingleton(new RevitToHostCacheSingleton()); + builder.AddSingleton(new RevitToSpeckleCacheSingleton()); // POC: do we need ToSpeckleScalingService as is, do we need to interface it out? builder.AddScoped(); diff --git a/Converters/Revit/Speckle.Converters.RevitShared/Helpers/RevitToHostCacheSingleton.cs b/Converters/Revit/Speckle.Converters.RevitShared/Helpers/RevitToHostCacheSingleton.cs new file mode 100644 index 000000000..ae32f20e9 --- /dev/null +++ b/Converters/Revit/Speckle.Converters.RevitShared/Helpers/RevitToHostCacheSingleton.cs @@ -0,0 +1,12 @@ +namespace Speckle.Converters.RevitShared.Helpers; + +public class RevitToHostCacheSingleton +{ + /// + /// POC: Not sure is there a way to create it on "RevitHostObjectBuilder" with a scope instead singleton. For now we fill this dictionary and clear it on "RevitHostObjectBuilder". + /// Map extracted by revit material baker to be able to use it in converter. + /// This is needed because we cannot set materials for meshes in connector. + /// They needed to be set while creating "TessellatedFace". + /// + public Dictionary MaterialsByObjectId { get; } = new(); +} diff --git a/Converters/Revit/Speckle.Converters.RevitShared/Helpers/RevitMaterialCacheSingleton.cs b/Converters/Revit/Speckle.Converters.RevitShared/Helpers/RevitToSpeckleCacheSingleton.cs similarity index 54% rename from Converters/Revit/Speckle.Converters.RevitShared/Helpers/RevitMaterialCacheSingleton.cs rename to Converters/Revit/Speckle.Converters.RevitShared/Helpers/RevitToSpeckleCacheSingleton.cs index 2a441f241..9cd5ce502 100644 --- a/Converters/Revit/Speckle.Converters.RevitShared/Helpers/RevitMaterialCacheSingleton.cs +++ b/Converters/Revit/Speckle.Converters.RevitShared/Helpers/RevitToSpeckleCacheSingleton.cs @@ -1,37 +1,34 @@ -using Speckle.Objects.Other; +using Speckle.Objects.Other; using Speckle.Objects.Other.Revit; namespace Speckle.Converters.RevitShared.Helpers; /// -/// Persistent cache (across conversions) for all generated render material proxies. This cache stores a list of render material proxies per element id, and provides a method to get out the merged render material proxy list from a set of object ids for setting on the root commit object post conversion phase. /// -/// Why is this needed? Because two reasons: send caching bypasses converter and revit conversions typically generate multiple display values per element. Ask dim for more and he might start crying. +/// Lifetime of this singleton should be per document. +/// +/// +/// Why is this needed? Because two reasons: send caching bypasses converter and revit conversions typically generate multiple display values per element. +/// Ask dim for more and he might start crying. /// -/// TODO: this dude needs to be split into single responsability (render materials and material quantities), and removed from the context - as it's not needed for it to be there. It can be DI'ed as appropriate (see ParameterDefinitionHandler) /// -public class RevitMaterialCacheSingleton +public class RevitToSpeckleCacheSingleton { /// - /// map(object id, ( map (materialId, proxy) ) ) - /// a per object map of material proxies. not the best way??? + /// (DB.Material id, RevitMaterial). This can be generated from converting render materials or material quantities. /// - public Dictionary> ObjectRenderMaterialProxiesMap { get; } = new(); + public Dictionary RevitMaterialCache { get; } = new(); /// - /// POC: The map we mutate PER RECEIVE operation, this smells a LOT! Once we have better conversion context stack that we can manage our data between connector - converter, this property must go away! + /// (DB.Material id, RenderMaterial). This can be generated from converting render materials or material quantities. /// - public Dictionary ObjectIdAndMaterialIndexMap { get; } = new(); + public Dictionary SpeckleRenderMaterialCache { get; } = new(); /// - /// map (DB.Material id, RevitMaterial). This can be generated from converting render materials or material quantities. - /// - public Dictionary ConvertedRevitMaterialMap { get; } = new(); - - /// - /// map (DB.Material id, RenderMaterial). This can be generated from converting render materials or material quantities. + /// map(object id, ( map (materialId, proxy) ) ) + /// a per object map of material proxies. not the best way??? /// - public Dictionary ConvertedRenderMaterialMap { get; } = new(); + public Dictionary> ObjectRenderMaterialProxiesMap { get; } = new(); /// /// Returns the merged material proxy list for the given object ids. Use this to get post conversion a correct list of material proxies for setting on the root commit object. diff --git a/Converters/Revit/Speckle.Converters.RevitShared/RevitRootToHostConverter.cs b/Converters/Revit/Speckle.Converters.RevitShared/RevitRootToHostConverter.cs index 505ca0d9e..ec53dd0fb 100644 --- a/Converters/Revit/Speckle.Converters.RevitShared/RevitRootToHostConverter.cs +++ b/Converters/Revit/Speckle.Converters.RevitShared/RevitRootToHostConverter.cs @@ -6,6 +6,8 @@ namespace Speckle.Converters.RevitShared; +public record DirectShapeDefinitionWrapper(string DefinitionId, List Geometries); + public class RevitRootToHostConverter : IRootToHostConverter { private readonly IConverterSettingsStore _converterSettings; @@ -22,47 +24,18 @@ IConverterSettingsStore converterSettings public object Convert(Base target) { - List geometryObjects = _baseToGeometryConverter.Convert(target); + List geometryObjects = _baseToGeometryConverter.Convert(target); if (geometryObjects.Count == 0) { throw new SpeckleConversionException($"No supported conversion for {target.speckle_type} found."); } - // create direct shape from geometries - DB.DirectShape result = CreateDirectShape(geometryObjects, target["category"] as string); - - return result; - } - - private DB.DirectShape CreateDirectShape(List geometry, string? category) - { - // set ds category - var dsCategory = BuiltInCategory.OST_GenericModel; - if (category is string categoryString) - { - var res = Enum.TryParse($"OST_{categoryString}", out DB.BuiltInCategory cat); - if (res) - { - var c = Category.GetCategory(_converterSettings.Current.Document, cat); - if (c is not null && DirectShape.IsValidCategoryId(c.Id, _converterSettings.Current.Document)) - { - dsCategory = cat; - } - } - } - - var result = DirectShape.CreateElement(_converterSettings.Current.Document, new DB.ElementId(dsCategory)); - - // check for valid geometry - if (!result.IsValidShape(geometry)) - { - _converterSettings.Current.Document.Delete(result.Id); - throw new SpeckleConversionException("Invalid geometry (eg unbounded curves) found for creating directshape."); - } - - result.SetShape(geometry); + var definitionId = target.applicationId ?? target.id; + DirectShapeLibrary + .GetDirectShapeLibrary(_converterSettings.Current.Document) + .AddDefinition(definitionId, geometryObjects); - return result; + return new DirectShapeDefinitionWrapper(definitionId, geometryObjects); } } diff --git a/Converters/Revit/Speckle.Converters.RevitShared/RevitRootToSpeckleConverter.cs b/Converters/Revit/Speckle.Converters.RevitShared/RevitRootToSpeckleConverter.cs index 120f81c37..86378ccc9 100644 --- a/Converters/Revit/Speckle.Converters.RevitShared/RevitRootToSpeckleConverter.cs +++ b/Converters/Revit/Speckle.Converters.RevitShared/RevitRootToSpeckleConverter.cs @@ -13,7 +13,7 @@ namespace Speckle.Converters.RevitShared; public class RevitRootToSpeckleConverter : IRootToSpeckleConverter { private readonly IConverterManager _toSpeckle; - private readonly ITypedConverter>> _materialQuantityConverter; + private readonly ITypedConverter> _materialQuantityConverter; private readonly IConverterSettingsStore _converterSettings; private readonly ParameterExtractor _parameterExtractor; private readonly ILogger _logger; @@ -22,7 +22,7 @@ public class RevitRootToSpeckleConverter : IRootToSpeckleConverter public RevitRootToSpeckleConverter( IConverterManager toSpeckle, - ITypedConverter>> materialQuantityConverter, + ITypedConverter> materialQuantityConverter, IConverterSettingsStore converterSettings, ParameterExtractor parameterExtractor, ILogger logger @@ -55,6 +55,9 @@ public Base Convert(object target) result.applicationId = element.UniqueId; + // Add ElementID to the converted objects + result["elementId"] = element.Id.ToString()!; + // POC DirectShapes have RevitCategory enum as the type or the category property, DS category property is already set in the converter // trying to set the category as a string will throw // the category should be moved to be set in each converter instead of the root to speckle converter diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ServiceRegistration.cs b/Converters/Revit/Speckle.Converters.RevitShared/ServiceRegistration.cs index 787d41326..ed644f76c 100644 --- a/Converters/Revit/Speckle.Converters.RevitShared/ServiceRegistration.cs +++ b/Converters/Revit/Speckle.Converters.RevitShared/ServiceRegistration.cs @@ -26,7 +26,8 @@ public static IServiceCollection AddRevitConverters(this IServiceCollection serv serviceCollection.AddScoped(); serviceCollection.AddSingleton(new RevitContext()); - serviceCollection.AddSingleton(new RevitMaterialCacheSingleton()); + serviceCollection.AddSingleton(new RevitToHostCacheSingleton()); + serviceCollection.AddSingleton(new RevitToSpeckleCacheSingleton()); // POC: do we need ToSpeckleScalingService as is, do we need to interface it out? serviceCollection.AddScoped(); diff --git a/Converters/Revit/Speckle.Converters.RevitShared/Speckle.Converters.RevitShared.projitems b/Converters/Revit/Speckle.Converters.RevitShared/Speckle.Converters.RevitShared.projitems index c33cdb8a9..25c8e14a2 100644 --- a/Converters/Revit/Speckle.Converters.RevitShared/Speckle.Converters.RevitShared.projitems +++ b/Converters/Revit/Speckle.Converters.RevitShared/Speckle.Converters.RevitShared.projitems @@ -12,7 +12,8 @@ - + + @@ -38,11 +39,14 @@ + + + @@ -69,7 +73,8 @@ - + + @@ -99,7 +104,4 @@ - - - \ No newline at end of file diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/BaseToHostGeometryObjectConverter.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/BaseToHostGeometryObjectConverter.cs index ac4685557..81eca9cdc 100644 --- a/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/BaseToHostGeometryObjectConverter.cs +++ b/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/BaseToHostGeometryObjectConverter.cs @@ -11,16 +11,19 @@ public class BaseToHostGeometryObjectConverter : ITypedConverter _pointConverter; private readonly ITypedConverter _curveConverter; private readonly ITypedConverter> _meshConverter; + private readonly ITypedConverter> _encodedObjectConverter; public BaseToHostGeometryObjectConverter( ITypedConverter pointConverter, ITypedConverter curveConverter, - ITypedConverter> meshConverter + ITypedConverter> meshConverter, + ITypedConverter> encodedObjectConverter ) { _pointConverter = pointConverter; _curveConverter = curveConverter; _meshConverter = meshConverter; + _encodedObjectConverter = encodedObjectConverter; } public List Convert(Base target) @@ -41,6 +44,10 @@ public BaseToHostGeometryObjectConverter( var meshes = _meshConverter.Convert(mesh).Cast(); result.AddRange(meshes); break; + case SOG.IRawEncodedObject elon: + var res = _encodedObjectConverter.Convert(elon); + result.AddRange(res); + break; default: var displayValue = target.TryGetDisplayValue(); if ((displayValue is IList && !displayValue.Any()) || displayValue is null) @@ -52,7 +59,6 @@ public BaseToHostGeometryObjectConverter( { result.AddRange(Convert(display)); } - break; } diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/IRawEncodedObjectConverter.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/IRawEncodedObjectConverter.cs new file mode 100644 index 000000000..ac98b4d25 --- /dev/null +++ b/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/IRawEncodedObjectConverter.cs @@ -0,0 +1,76 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.FileOps; +using Speckle.Converters.Common.Objects; +using Speckle.Converters.RevitShared.Helpers; +using Speckle.Converters.RevitShared.Settings; +using Speckle.Sdk.Common; +using Speckle.Sdk.Models; +using Speckle.Sdk.Models.Extensions; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +public class IRawEncodedObjectConverter : ITypedConverter> +{ + private readonly IConverterSettingsStore _settings; + private readonly ITypedConverter> _meshConverter; + private readonly RevitToHostCacheSingleton _revitToHostCacheSingleton; + + public IRawEncodedObjectConverter( + IConverterSettingsStore settings, + ITypedConverter> meshConverter, + RevitToHostCacheSingleton revitToHostCacheSingleton + ) + { + _settings = settings; + _meshConverter = meshConverter; + _revitToHostCacheSingleton = revitToHostCacheSingleton; + } + + public List Convert(SOG.IRawEncodedObject target) + { + var targetAsBase = (Base)target; + var raw = target.encodedValue.contents; + var bytes = System.Convert.FromBase64String(raw!); + var filePath = TempFileProvider.GetTempFile("RevitX", target.encodedValue.format); + File.WriteAllBytes(filePath, bytes); + + using var importer = new DB.ShapeImporter(); + var shapeImportResult = importer.Convert(_settings.Current.Document, filePath); + + DB.ElementId materialId = DB.ElementId.InvalidElementId; + if ( + _revitToHostCacheSingleton.MaterialsByObjectId.TryGetValue( + targetAsBase.applicationId ?? targetAsBase.id, + out var mappedElementId + ) + ) + { + materialId = mappedElementId; + } + + if (materialId == DB.ElementId.InvalidElementId) + { + return shapeImportResult.ToList(); // exit fast if there's no material id associated with this object + } + + // check whether the results have any meshes inside - if yes, it means the shape importer produced a subpar result. + // as we cannot paint meshes later (as you can solid faces), we need to create them now. + // we'll default to using the display value of the original object as it's a better fallback. + // note: if you're tempted to try and re-mesh the shape importer's meshes, don't - they are garbage. + var hasMesh = shapeImportResult.Any(o => o is DB.Mesh); + if (!hasMesh) + { + return shapeImportResult.ToList(); + } + + var displayValue = targetAsBase.TryGetDisplayValue().NotNull(); + var returnList = new List(); + foreach (var mesh in displayValue) + { + mesh.applicationId = targetAsBase.applicationId ?? targetAsBase.id; // to properly map materials + returnList.AddRange(_meshConverter.Convert(mesh)); + } + + return returnList; + } +} diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/MeshConverterToHost.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/MeshConverterToHost.cs index a5652fb60..cd6d9be81 100644 --- a/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/MeshConverterToHost.cs +++ b/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/MeshConverterToHost.cs @@ -2,22 +2,23 @@ using Speckle.Converters.Common; using Speckle.Converters.Common.Objects; using Speckle.Converters.RevitShared.Helpers; +using Speckle.Converters.RevitShared.Services; using Speckle.DoubleNumerics; namespace Speckle.Converters.RevitShared.ToHost.TopLevel; public class MeshConverterToHost : ITypedConverter> { - private readonly ITypedConverter _pointConverter; - private readonly RevitMaterialCacheSingleton _revitMaterialCacheSingleton; + private readonly RevitToHostCacheSingleton _revitToHostCacheSingleton; + private readonly ScalingServiceToHost _scalingServiceToHost; public MeshConverterToHost( - ITypedConverter pointConverter, - RevitMaterialCacheSingleton revitMaterialCacheSingleton + RevitToHostCacheSingleton revitToHostCacheSingleton, + ScalingServiceToHost scalingServiceToHost ) { - _pointConverter = pointConverter; - _revitMaterialCacheSingleton = revitMaterialCacheSingleton; + _revitToHostCacheSingleton = revitToHostCacheSingleton; + _scalingServiceToHost = scalingServiceToHost; } public List Convert(SOG.Mesh mesh) @@ -32,17 +33,12 @@ RevitMaterialCacheSingleton revitMaterialCacheSingleton GraphicsStyleId = ElementId.InvalidElementId }; - var valid = tsb.AreTargetAndFallbackCompatible(target, fallback); tsb.OpenConnectedFaceSet(false); var vertices = ArrayToPoints(mesh.vertices, mesh.units); ElementId materialId = ElementId.InvalidElementId; - if ( - _revitMaterialCacheSingleton.ObjectIdAndMaterialIndexMap.TryGetValue( - mesh.applicationId ?? mesh.id, - out var mappedElementId - ) + _revitToHostCacheSingleton.MaterialsByObjectId.TryGetValue(mesh.applicationId ?? mesh.id, out var mappedElementId) ) { materialId = mappedElementId; @@ -61,8 +57,8 @@ out var mappedElementId if (IsNonPlanarQuad(points)) { - //Non-planar quads will be triangulated as it's more desirable than `TessellatedShapeBuilder.Build`'s attempt to make them planar. - //TODO consider triangulating all n > 3 polygons that are non-planar + // Non-planar quads will be triangulated as it's more desirable than `TessellatedShapeBuilder.Build`'s attempt to make them planar. + // TODO consider triangulating all n > 3 polygons that are non-planar var triPoints = new List { points[0], points[1], points[3] }; var face1 = new TessellatedFace(triPoints, materialId); tsb.AddFace(face1); @@ -125,11 +121,15 @@ private XYZ[] ArrayToPoints(IList arr, string units) } XYZ[] points = new XYZ[arr.Count / 3]; + var fTypeId = _scalingServiceToHost.UnitsToNative(units) ?? UnitTypeId.Meters; for (int i = 2, k = 0; i < arr.Count; i += 3) { - var point = new SOG.Point(arr[i - 2], arr[i - 1], arr[i], units); - points[k++] = _pointConverter.Convert(point); + points[k++] = new XYZ( + _scalingServiceToHost.ScaleToNative(arr[i - 2], fTypeId), + _scalingServiceToHost.ScaleToNative(arr[i - 1], fTypeId), + _scalingServiceToHost.ScaleToNative(arr[i], fTypeId) + ); } return points; diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/TransformConverterToHost.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/TransformConverterToHost.cs new file mode 100644 index 000000000..8b29bb880 --- /dev/null +++ b/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/Geometry/TransformConverterToHost.cs @@ -0,0 +1,47 @@ +using Speckle.Converters.Common.Objects; +using Speckle.Converters.RevitShared.Services; +using Speckle.DoubleNumerics; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +public class TransformConverterToHost : ITypedConverter<(Matrix4x4 matrix, string units), DB.Transform> +{ + private readonly ScalingServiceToHost _scalingService; + + public TransformConverterToHost(ScalingServiceToHost scalingService) + { + _scalingService = scalingService; + } + + public DB.Transform Convert((Matrix4x4 matrix, string units) target) + { + var transform = DB.Transform.Identity; + if (target.matrix.M44 == 0 || target.units is null) // TODO: check target.units nullability? + { + return transform; + } + + var tX = _scalingService.ScaleToNative(target.matrix.M14 / target.matrix.M44, target.units); + var tY = _scalingService.ScaleToNative(target.matrix.M24 / target.matrix.M44, target.units); + var tZ = _scalingService.ScaleToNative(target.matrix.M34 / target.matrix.M44, target.units); + var t = new DB.XYZ(tX, tY, tZ); + + // basis vectors + DB.XYZ vX = new(target.matrix.M11, target.matrix.M21, target.matrix.M31); + DB.XYZ vY = new(target.matrix.M12, target.matrix.M22, target.matrix.M32); + DB.XYZ vZ = new(target.matrix.M13, target.matrix.M23, target.matrix.M33); + + // apply to new transform + transform.Origin = t; + transform.BasisX = vX.Normalize(); + transform.BasisY = vY.Normalize(); + transform.BasisZ = vZ.Normalize(); + + // TODO: check below needed? + // // apply doc transform + // var docTransform = GetDocReferencePointTransform(Doc); + // var internalTransform = docTransform.Multiply(_transform); + + return transform; + } +} diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/LocalToGlobalToDirectShapeConverter.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/LocalToGlobalToDirectShapeConverter.cs new file mode 100644 index 000000000..c33972225 --- /dev/null +++ b/Converters/Revit/Speckle.Converters.RevitShared/ToHost/Raw/LocalToGlobalToDirectShapeConverter.cs @@ -0,0 +1,82 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Converters.RevitShared.Settings; +using Speckle.DoubleNumerics; +using Speckle.Sdk.Models; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +/// +/// Converts local to global maps to direct shapes. +/// Spirit of the LocalToGlobalMap, we can't pass that object directly here bc it lives in Connectors.Common which I (ogu) don't want to bother with it. +/// All this is poc that should be burned, once we enable proper block support to revit. +/// +public class LocalToGlobalToDirectShapeConverter + : ITypedConverter<(Base atomicObject, List matrix), DB.DirectShape> +{ + private readonly IConverterSettingsStore _converterSettings; + private readonly ITypedConverter<(Matrix4x4 matrix, string units), DB.Transform> _transformConverter; + + public LocalToGlobalToDirectShapeConverter( + IConverterSettingsStore converterSettings, + ITypedConverter<(Matrix4x4 matrix, string units), DB.Transform> transformConverter + ) + { + _converterSettings = converterSettings; + _transformConverter = transformConverter; + } + + public DB.DirectShape Convert((Base atomicObject, List matrix) target) + { + // 1- set ds category + var category = target.atomicObject["category"] as string; + var dsCategory = DB.BuiltInCategory.OST_GenericModel; + if (category is not null) + { + var res = Enum.TryParse($"OST_{category}", out DB.BuiltInCategory cat); + if (res) + { + var c = DB.Category.GetCategory(_converterSettings.Current.Document, cat); + if (c is not null && DB.DirectShape.IsValidCategoryId(c.Id, _converterSettings.Current.Document)) + { + dsCategory = cat; + } + } + } + + // 2 - init DirectShape + var result = DB.DirectShape.CreateElement(_converterSettings.Current.Document, new DB.ElementId(dsCategory)); + + // If there is no transforms to be applied, use the simple way of creating direct shapes + if (target.matrix.Count == 0) + { + var def = DB + .DirectShapeLibrary.GetDirectShapeLibrary(_converterSettings.Current.Document) + .FindDefinition(target.atomicObject.applicationId ?? target.atomicObject.id); + result.SetShape(def); + return result; // note fast exit here + } + + // 3 - Transform the geometries + DB.Transform combinedTransform = DB.Transform.Identity; + + // existence of units is must, to be able to scale the transform correctly + if (target.atomicObject["units"] is string units) + { + foreach (Matrix4x4 matrix in target.matrix) + { + DB.Transform revitTransform = _transformConverter.Convert((matrix, units)); + combinedTransform = combinedTransform.Multiply(revitTransform); + } + } + + var transformedGeometries = DB.DirectShape.CreateGeometryInstance( + _converterSettings.Current.Document, + target.atomicObject.applicationId ?? target.atomicObject.id, + combinedTransform + ); + + result.SetShape(transformedGeometries); + return result; + } +} diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Parameters/ParameterDefinitionHandler.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Parameters/ParameterDefinitionHandler.cs index f556656d4..19eacbace 100644 --- a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Parameters/ParameterDefinitionHandler.cs +++ b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Parameters/ParameterDefinitionHandler.cs @@ -16,7 +16,7 @@ public class ParameterDefinitionHandler /// /// /// - public (string internalDefinitionName, string humanReadableName, string groupName) HandleDefinition( + public (string internalDefinitionName, string humanReadableName, string groupName, string? units) HandleDefinition( DB.Parameter parameter ) { @@ -42,7 +42,12 @@ definition is DB.InternalDefinition internalDefinition #pragma warning restore CA1854 { var def = Definitions[internalDefinitionName]; - return (internalDefinitionName, humanReadableName, def["group"]! as string ?? "unknown group"); + return ( + internalDefinitionName, + humanReadableName, + def["group"]! as string ?? "unknown group", + def["units"]! as string + ); } string? units = null; @@ -63,6 +68,6 @@ definition is DB.InternalDefinition internalDefinition ["group"] = group }; - return (internalDefinitionName, humanReadableName, group); + return (internalDefinitionName, humanReadableName, group, units); } } diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Parameters/ParameterExtractor.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Parameters/ParameterExtractor.cs index c554eaf75..6f93643b4 100644 --- a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Parameters/ParameterExtractor.cs +++ b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Parameters/ParameterExtractor.cs @@ -65,12 +65,42 @@ ILogger logger return CreateParameterDictionary(instanceParameterDictionary, null); } - typeParameterDictionary = ParseParameterSet(type.Parameters); + typeParameterDictionary = ParseParameterSet(type.Parameters); // NOTE: type parameters should be ideally proxied out for a better data layout. + if (type is DB.HostObjAttributes hostObjectAttr) + { + // NOTE: this could be paired up and merged with material quantities - they're pretty much the same :/ + var factor = _scalingServiceToSpeckle.ScaleLength(1); + var structureDictionary = new Dictionary(); + var structure = hostObjectAttr.GetCompoundStructure(); + var layers = structure.GetLayers(); + foreach (var layer in layers) + { + if (_settingsStore.Current.Document.GetElement(layer.MaterialId) is DB.Material material) + { + structureDictionary[material.Name] = new Dictionary() + { + ["material"] = material.Name, + ["function"] = layer.Function.ToString(), + ["thickness"] = layer.Width * factor, + ["units"] = _settingsStore.Current.SpeckleUnits + }; + } + } + + typeParameterDictionary["Structure"] = structureDictionary; + } + _typeParameterCache[typeId] = typeParameterDictionary; return CreateParameterDictionary(instanceParameterDictionary, typeParameterDictionary); } + /// + /// Internal utility to create the default parameter structure we expect all elements to have. + /// + /// + /// + /// private Dictionary CreateParameterDictionary( Dictionary> instanceParams, Dictionary>? typeParams @@ -90,16 +120,39 @@ ILogger logger { try { + var (internalDefinitionName, humanReadableName, groupName, units) = + _parameterDefinitionHandler.HandleDefinition(parameter); + + // NOTE: ids don't really have much meaning; if we discover the opposite, we can bring them back. See [CNX-556: All ID Parameters are send as Name](https://linear.app/speckle/issue/CNX-556/all-id-parameters-are-send-as-name) + if (internalDefinitionName.Contains("_ID")) + { + continue; + } + var value = GetValue(parameter); + var isNullOrEmpty = value == null || (value is string s && string.IsNullOrEmpty(s)); + if (!_settingsStore.Current.SendParameterNullOrEmptyStrings && isNullOrEmpty) { continue; } - var (internalDefinitionName, humanReadableName, groupName) = _parameterDefinitionHandler.HandleDefinition( - parameter - ); + if (value is (string typeName, string familyName)) // element type: same element, different expected values depending on the param definition + { + if (internalDefinitionName == "ELEM_FAMILY_PARAM") // Probably should be using the BUILTINPARAM whatever + { + value = familyName; + } + else if (internalDefinitionName == "ELEM_TYPE_PARAM") + { + value = typeName; + } + else + { + value = familyName + " " + typeName; + } + } var param = new Dictionary() { @@ -108,6 +161,11 @@ ILogger logger ["internalDefinitionName"] = internalDefinitionName }; + if (units is not null) + { + param["units"] = units; + } + if (!dict.TryGetValue(groupName, out Dictionary? paramGroup)) { paramGroup = new Dictionary(); @@ -131,7 +189,7 @@ ILogger logger return dict; } - private readonly Dictionary _elementNameCache = new(); + private readonly Dictionary _elementNameCache = new(); private object? GetValue(DB.Parameter parameter) { @@ -140,15 +198,35 @@ ILogger logger case DB.StorageType.Double: return _scalingServiceToSpeckle.Scale(parameter.AsDouble(), parameter.GetUnitTypeId()); case DB.StorageType.Integer: - return parameter.AsInteger(); + return parameter.AsInteger().ToString() == parameter.AsValueString() + ? parameter.AsInteger() + : parameter.AsValueString(); case DB.StorageType.ElementId: var elId = parameter.AsElementId()!; - if (_elementNameCache.TryGetValue(elId, out string? value)) + if (elId == DB.ElementId.InvalidElementId) + { + return null; + } + + if (_elementNameCache.TryGetValue(elId, out object? value)) { return value; } + var docElement = _settingsStore.Current.Document.GetElement(elId); - var docElementName = docElement?.Name ?? elId.ToString(); + object? docElementName; + + // Note: for element types, different params point at the same element. We're getting the right value out in the parent function + // based on what the actual built in param name is. + if (docElement is DB.ElementType elementType) + { + docElementName = (elementType.Name, elementType.FamilyName); + } + else + { + docElementName = docElement?.Name ?? null; + } + _elementNameCache[parameter.AsElementId()] = docElementName; return docElementName; case DB.StorageType.String: diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/MeshByMaterialDictionaryToSpeckle.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/MeshByMaterialDictionaryToSpeckle.cs index 40c60762d..93c346434 100644 --- a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/MeshByMaterialDictionaryToSpeckle.cs +++ b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/Geometry/MeshByMaterialDictionaryToSpeckle.cs @@ -3,7 +3,6 @@ using Speckle.Converters.RevitShared.Helpers; using Speckle.Converters.RevitShared.Settings; using Speckle.Objects.Other; -using Speckle.Objects.Other.Revit; using Speckle.Sdk.Common; namespace Speckle.Converters.RevitShared.ToSpeckle; @@ -12,21 +11,21 @@ public class MeshByMaterialDictionaryToSpeckle : ITypedConverter<(Dictionary> target, DB.ElementId parentElementId), List> { private readonly IConverterSettingsStore _converterSettings; - private readonly ITypedConverter _materialConverter; + private readonly ITypedConverter _speckleRenderMaterialConverter; private readonly ITypedConverter, SOG.Mesh> _meshListConverter; - private readonly RevitMaterialCacheSingleton _revitMaterialCacheSingleton; + private readonly RevitToSpeckleCacheSingleton _revitToSpeckleCacheSingleton; public MeshByMaterialDictionaryToSpeckle( - ITypedConverter materialConverter, ITypedConverter, SOG.Mesh> meshListConverter, IConverterSettingsStore converterSettings, - RevitMaterialCacheSingleton revitMaterialCacheSingleton + RevitToSpeckleCacheSingleton revitToSpeckleCacheSingleton, + ITypedConverter speckleRenderMaterialConverter ) { - _materialConverter = materialConverter; _meshListConverter = meshListConverter; _converterSettings = converterSettings; - _revitMaterialCacheSingleton = revitMaterialCacheSingleton; + _revitToSpeckleCacheSingleton = revitToSpeckleCacheSingleton; + _speckleRenderMaterialConverter = speckleRenderMaterialConverter; } /// @@ -46,7 +45,7 @@ RevitMaterialCacheSingleton revitMaterialCacheSingleton public List Convert((Dictionary> target, DB.ElementId parentElementId) args) { var result = new List(args.target.Keys.Count); - var objectRenderMaterialProxiesMap = _revitMaterialCacheSingleton.ObjectRenderMaterialProxiesMap; + var objectRenderMaterialProxiesMap = _revitToSpeckleCacheSingleton.ObjectRenderMaterialProxiesMap; var materialProxyMap = new Dictionary(); objectRenderMaterialProxiesMap[args.parentElementId.ToString().NotNull()] = materialProxyMap; @@ -69,7 +68,7 @@ RevitMaterialCacheSingleton revitMaterialCacheSingleton // get the render material if any if (_converterSettings.Current.Document.GetElement(materialId) is DB.Material material) { - (RevitMaterial _, RenderMaterial convertedRenderMaterial) = _materialConverter.Convert(material); + RenderMaterial convertedRenderMaterial = _speckleRenderMaterialConverter.Convert(material); if (!materialProxyMap.TryGetValue(materialIdString, out RenderMaterialProxy? renderMaterialProxy)) { diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/MaterialAsRevitMaterialConversionToSpeckle.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/MaterialAsRevitMaterialConversionToSpeckle.cs new file mode 100644 index 000000000..3d125e652 --- /dev/null +++ b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/MaterialAsRevitMaterialConversionToSpeckle.cs @@ -0,0 +1,55 @@ +using Speckle.Converters.Common.Objects; +using Speckle.Converters.RevitShared.Helpers; +using Speckle.Objects.Other.Revit; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +public class MaterialAsRevitMaterialConversionToSpeckle : ITypedConverter +{ + private readonly RevitToSpeckleCacheSingleton _revitToSpeckleCacheSingleton; + + public MaterialAsRevitMaterialConversionToSpeckle(RevitToSpeckleCacheSingleton revitToSpeckleCacheSingleton) + { + _revitToSpeckleCacheSingleton = revitToSpeckleCacheSingleton; + } + + public RevitMaterial Convert(DB.Material target) + { + RevitMaterial material; + if ( + _revitToSpeckleCacheSingleton.RevitMaterialCache.TryGetValue(target.UniqueId, out RevitMaterial? cachedMaterial) + ) + { + material = cachedMaterial; + } + else + { + material = ConvertToRevitMaterial(target); + _revitToSpeckleCacheSingleton.RevitMaterialCache.Add(target.UniqueId, material); + } + + return material; + } + + private RevitMaterial ConvertToRevitMaterial(DB.Material target) + { + // POC: we need to validate these properties on the RevitMaterial class, ie are they used? + RevitMaterial material = + new( + target.Name, + target.MaterialCategory, + target.MaterialClass, + target.Shininess, + target.Smoothness, + target.Transparency + ) + { + applicationId = target.UniqueId + }; + + // POC: I'm removing material parameter assigning here as we're exploding a bit the whole space with too many parameters. + // We can bring this back if needed/requested by our end users. + // _parameterObjectAssigner.AssignParametersToBase(target, material); + return material; + } +} diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/MaterialAsSpeckleMaterialConversionToSpeckle.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/MaterialAsSpeckleMaterialConversionToSpeckle.cs new file mode 100644 index 000000000..1a1b8144b --- /dev/null +++ b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/MaterialAsSpeckleMaterialConversionToSpeckle.cs @@ -0,0 +1,50 @@ +using Speckle.Converters.Common.Objects; +using Speckle.Converters.RevitShared.Helpers; +using Speckle.Objects.Other; + +namespace Speckle.Converters.RevitShared.ToSpeckle; + +public class MaterialAsSpeckleMaterialConversionToSpeckle : ITypedConverter +{ + private readonly RevitToSpeckleCacheSingleton _revitToSpeckleCacheSingleton; + + public MaterialAsSpeckleMaterialConversionToSpeckle(RevitToSpeckleCacheSingleton revitToSpeckleCacheSingleton) + { + _revitToSpeckleCacheSingleton = revitToSpeckleCacheSingleton; + } + + public RenderMaterial Convert(DB.Material target) + { + RenderMaterial renderMaterial; + if ( + _revitToSpeckleCacheSingleton.SpeckleRenderMaterialCache.TryGetValue( + target.UniqueId, + out RenderMaterial? cachedRenderMaterial + ) + ) + { + renderMaterial = cachedRenderMaterial; + } + else + { + renderMaterial = ConvertToRenderMaterial(target); + _revitToSpeckleCacheSingleton.SpeckleRenderMaterialCache.Add(target.UniqueId, renderMaterial); + } + + return renderMaterial; + } + + private RenderMaterial ConvertToRenderMaterial(DB.Material target) + { + RenderMaterial renderMaterial = + new() + { + name = target.Name, + opacity = 1 - target.Transparency / 100d, + diffuse = System.Drawing.Color.FromArgb(target.Color.Red, target.Color.Green, target.Color.Blue).ToArgb(), + applicationId = target.UniqueId + }; + + return renderMaterial; + } +} diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/MaterialConversionToSpeckle.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/MaterialConversionToSpeckle.cs deleted file mode 100644 index 247e997b0..000000000 --- a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/MaterialConversionToSpeckle.cs +++ /dev/null @@ -1,86 +0,0 @@ -using Speckle.Converters.Common.Objects; -using Speckle.Converters.RevitShared.Helpers; -using Speckle.Objects.Other; -using Speckle.Objects.Other.Revit; - -namespace Speckle.Converters.RevitShared.ToSpeckle; - -public class MaterialConversionToSpeckle : ITypedConverter -{ - private readonly RevitMaterialCacheSingleton _materialCacheSingleton; - - public MaterialConversionToSpeckle(RevitMaterialCacheSingleton materialCacheSingleton) - { - _materialCacheSingleton = materialCacheSingleton; - } - - public (RevitMaterial, RenderMaterial) Convert(DB.Material target) - { - RevitMaterial material; - if ( - _materialCacheSingleton.ConvertedRevitMaterialMap.TryGetValue(target.UniqueId, out RevitMaterial? cachedMaterial) - ) - { - material = cachedMaterial; - } - else - { - material = ConvertToRevitMaterial(target); - _materialCacheSingleton.ConvertedRevitMaterialMap.Add(target.UniqueId, material); - } - - RenderMaterial renderMaterial; - if ( - _materialCacheSingleton.ConvertedRenderMaterialMap.TryGetValue( - target.UniqueId, - out RenderMaterial? cachedRenderMaterial - ) - ) - { - renderMaterial = cachedRenderMaterial; - } - else - { - renderMaterial = ConvertToRenderMaterial(target); - _materialCacheSingleton.ConvertedRenderMaterialMap.Add(target.UniqueId, renderMaterial); - } - - return (material, renderMaterial); - } - - private RevitMaterial ConvertToRevitMaterial(DB.Material target) - { - // POC: we need to validate these properties on the RevitMaterial class, ie are they used? - RevitMaterial material = - new( - target.Name, - target.MaterialCategory, - target.MaterialClass, - target.Shininess, - target.Smoothness, - target.Transparency - ) - { - applicationId = target.UniqueId - }; - - // POC: I'm removing material parameter assigning here as we're exploding a bit the whole space with too many parameters. - // We can bring this back if needed/requested by our end users. - // _parameterObjectAssigner.AssignParametersToBase(target, material); - return material; - } - - private RenderMaterial ConvertToRenderMaterial(DB.Material target) - { - RenderMaterial renderMaterial = - new() - { - name = target.Name, - opacity = 1 - target.Transparency / 100d, - diffuse = System.Drawing.Color.FromArgb(target.Color.Red, target.Color.Green, target.Color.Blue).ToArgb(), - applicationId = target.UniqueId - }; - - return renderMaterial; - } -} diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/MaterialQuantitiesToSpeckle.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/MaterialQuantitiesToSpeckle.cs index 37c32a36b..6defa25b0 100644 --- a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/MaterialQuantitiesToSpeckle.cs +++ b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/Raw/MaterialQuantitiesToSpeckle.cs @@ -11,7 +11,7 @@ namespace Speckle.Converters.RevitShared.ToSpeckle; /// Lighter converter for material quantities. It basically returns a For each material quantity available on the target element, it will return a dictionary containing: area, volume, units, material name, material class and material category. /// POC: we need to validate this with user needs. It currently does not include material parameters or any other more complex props to ensure speedy sending of data and a lighter payload. We're though keen to re-add more data provided we can validate it. /// -public class MaterialQuantitiesToSpeckleLite : ITypedConverter>> +public class MaterialQuantitiesToSpeckleLite : ITypedConverter> { private readonly ScalingServiceToSpeckle _scalingService; private readonly IConverterSettingsStore _converterSettings; @@ -30,10 +30,9 @@ IConverterSettingsStore converterSettings /// /// /// - public List> Convert(DB.Element target) + public Dictionary Convert(DB.Element target) { - List> quantities = new(); - + Dictionary quantities = new(); if (target.Category.HasMaterialQuantities) { foreach (DB.ElementId matId in target.GetMaterialIds(false)) @@ -55,7 +54,7 @@ public List> Convert(DB.Element target) materialQuantity["materialName"] = material.Name; materialQuantity["materialCategory"] = material.MaterialCategory; materialQuantity["materialClass"] = material.MaterialClass; - quantities.Add(materialQuantity); + quantities[material.Name] = materialQuantity; } } } @@ -70,19 +69,19 @@ public List> Convert(DB.Element target) [Obsolete("Creates a rather bloated data structure - 2.0 style. More in the comment above.")] public class MaterialQuantitiesToSpeckle : ITypedConverter> { - private readonly ITypedConverter _materialConverter; + private readonly ITypedConverter _revitMaterialConverter; private readonly ScalingServiceToSpeckle _scalingService; private readonly IConverterSettingsStore _converterSettings; public MaterialQuantitiesToSpeckle( - ITypedConverter materialConverter, ScalingServiceToSpeckle scalingService, - IConverterSettingsStore converterSettings + IConverterSettingsStore converterSettings, + ITypedConverter revitMaterialConverter ) { - _materialConverter = materialConverter; _scalingService = scalingService; _converterSettings = converterSettings; + _revitMaterialConverter = revitMaterialConverter; } /// @@ -117,7 +116,7 @@ public List Convert(DB.Element target) if (_converterSettings.Current.Document.GetElement(matId) is DB.Material material) { - (RevitMaterial convertedMaterial, RenderMaterial _) = _materialConverter.Convert(material); + RevitMaterial convertedMaterial = _revitMaterialConverter.Convert(material); // NOTE: the RevitMaterial class is semi useless, and it used to extract parameters out too for each material. Overkill. quantities.Add(new(convertedMaterial, volume, area, _converterSettings.Current.SpeckleUnits)); } diff --git a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/FootPrintRoofToSpeckleTopLevelConverter.cs b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/FootPrintRoofToSpeckleTopLevelConverter.cs index 85e8f7dbb..3234b6c7f 100644 --- a/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/FootPrintRoofToSpeckleTopLevelConverter.cs +++ b/Converters/Revit/Speckle.Converters.RevitShared/ToSpeckle/TopLevel/FootPrintRoofToSpeckleTopLevelConverter.cs @@ -55,10 +55,6 @@ public override RevitFootprintRoof Convert(FootPrintRoof target) out var topLevel ); - //POC: CNX-9403 can be null if the sides have different slopes. - //We currently don't validate the success or failure of this TryGet as it's not necessary, but will be once we start the above ticket. - _parameterValueExtractor.TryGetValueAsDouble(target, DB.BuiltInParameter.ROOF_SLOPE, out var slope); - var elementType = (ElementType)target.Document.GetElement(target.GetTypeId()); List displayValue = _displayValueExtractor.GetDisplayValue(target); @@ -69,7 +65,6 @@ out var topLevel family = elementType.FamilyName, level = _levelConverter.Convert(baseLevel), cutOffLevel = topLevel is not null ? _levelConverter.Convert(topLevel) : null, - slope = slope, displayValue = displayValue, units = _converterSettings.Current.SpeckleUnits }; diff --git a/Converters/Rhino/Speckle.Converters.Rhino7.Tests/Speckle.Converters.Rhino7.Tests.csproj b/Converters/Rhino/Speckle.Converters.Rhino7.Tests/Speckle.Converters.Rhino7.Tests.csproj index b2b3653be..a7c0bf00e 100644 --- a/Converters/Rhino/Speckle.Converters.Rhino7.Tests/Speckle.Converters.Rhino7.Tests.csproj +++ b/Converters/Rhino/Speckle.Converters.Rhino7.Tests/Speckle.Converters.Rhino7.Tests.csproj @@ -23,6 +23,6 @@ - + diff --git a/Converters/Rhino/Speckle.Converters.Rhino7.Tests/packages.lock.json b/Converters/Rhino/Speckle.Converters.Rhino7.Tests/packages.lock.json index 146519ee8..f160cf303 100644 --- a/Converters/Rhino/Speckle.Converters.Rhino7.Tests/packages.lock.json +++ b/Converters/Rhino/Speckle.Converters.Rhino7.Tests/packages.lock.json @@ -27,6 +27,15 @@ "Microsoft.TestPlatform.TestHost": "17.10.0" } }, + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3" + } + }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -78,9 +87,9 @@ }, "Speckle.Rhino7.Fakes": { "type": "Direct", - "requested": "[0.3.0, 1.0.0)", - "resolved": "0.3.0", - "contentHash": "6nDQudPvrsaVgrGWicQ50fb3LzyEp8QMCltJZs6itzyYlHYC8xjmCHK1Z305f3y9QfK9QQa93sBrEtS7xIhcEQ==", + "requested": "[0.3.1, )", + "resolved": "0.3.1", + "contentHash": "II0zR/4QfPC/j44zfVTSqplZsd/GXHEa4eKRZFdjHuXnXBcd80uObWAb0C2Wm88KSlKOnSYAeQLEMmPjK5Kw3w==", "dependencies": { "System.Drawing.Common": "8.0.6" } @@ -204,6 +213,11 @@ "System.Runtime.CompilerServices.Unsafe": "4.5.1" } }, + "Microsoft.NETFramework.ReferenceAssemblies.net461": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA==" + }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", @@ -349,7 +363,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )" } }, "speckle.testing": { @@ -379,18 +393,18 @@ }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "gpENXDB2i8SiXJZwrLVfB3BwKF1FHUSpPiw0BQ3Hl0V3821UWZjPYw5XK694oxJlVAeEMRvrMZMGsWOcK+3nMw==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "1fyaPjC1kVtroKfoXI7edJX0Uho/TNRWG9mtmIQ7jKhX1zElrJPSoH8zEIMqckZhGmwUrYjlQFPssASNCP721Q==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.145" + "Speckle.Sdk": "3.1.0-dev.162" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "YZLtitiVIxl12pNTXaB9gU+paNwFe++WBw6oIN6Rs5hFPialUgeKbpjh16Zq6wcptjBpvg0CHq29+d456QUAJQ==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "ulT3XNgqIQSmpQFK5YEEwRq4RA6emXBKQHnZxtCz37SJin1PnVpH9m8vOAF9FmaWsgSAfjhhjIZDruMu1oKxSg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", diff --git a/Converters/Rhino/Speckle.Converters.Rhino7/packages.lock.json b/Converters/Rhino/Speckle.Converters.Rhino7/packages.lock.json index a8ac32461..36eff02fd 100644 --- a/Converters/Rhino/Speckle.Converters.Rhino7/packages.lock.json +++ b/Converters/Rhino/Speckle.Converters.Rhino7/packages.lock.json @@ -2,6 +2,15 @@ "version": 2, "dependencies": { ".NETFramework,Version=v4.8": { + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3" + } + }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -137,6 +146,11 @@ "System.Runtime.CompilerServices.Unsafe": "4.5.1" } }, + "Microsoft.NETFramework.ReferenceAssemblies.net48": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ==" + }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", @@ -259,7 +273,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )" } }, "Microsoft.Extensions.Logging": { @@ -282,18 +296,18 @@ }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "gpENXDB2i8SiXJZwrLVfB3BwKF1FHUSpPiw0BQ3Hl0V3821UWZjPYw5XK694oxJlVAeEMRvrMZMGsWOcK+3nMw==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "1fyaPjC1kVtroKfoXI7edJX0Uho/TNRWG9mtmIQ7jKhX1zElrJPSoH8zEIMqckZhGmwUrYjlQFPssASNCP721Q==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.145" + "Speckle.Sdk": "3.1.0-dev.162" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "YZLtitiVIxl12pNTXaB9gU+paNwFe++WBw6oIN6Rs5hFPialUgeKbpjh16Zq6wcptjBpvg0CHq29+d456QUAJQ==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "ulT3XNgqIQSmpQFK5YEEwRq4RA6emXBKQHnZxtCz37SJin1PnVpH9m8vOAF9FmaWsgSAfjhhjIZDruMu1oKxSg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", diff --git a/Converters/Rhino/Speckle.Converters.Rhino8/packages.lock.json b/Converters/Rhino/Speckle.Converters.Rhino8/packages.lock.json index 793da53a3..5fa802b9a 100644 --- a/Converters/Rhino/Speckle.Converters.Rhino8/packages.lock.json +++ b/Converters/Rhino/Speckle.Converters.Rhino8/packages.lock.json @@ -2,6 +2,15 @@ "version": 2, "dependencies": { ".NETFramework,Version=v4.8": { + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3" + } + }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -137,6 +146,11 @@ "System.Runtime.CompilerServices.Unsafe": "4.5.1" } }, + "Microsoft.NETFramework.ReferenceAssemblies.net48": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ==" + }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", @@ -259,7 +273,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )" } }, "Microsoft.Extensions.Logging": { @@ -282,18 +296,18 @@ }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "gpENXDB2i8SiXJZwrLVfB3BwKF1FHUSpPiw0BQ3Hl0V3821UWZjPYw5XK694oxJlVAeEMRvrMZMGsWOcK+3nMw==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "1fyaPjC1kVtroKfoXI7edJX0Uho/TNRWG9mtmIQ7jKhX1zElrJPSoH8zEIMqckZhGmwUrYjlQFPssASNCP721Q==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.145" + "Speckle.Sdk": "3.1.0-dev.162" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "YZLtitiVIxl12pNTXaB9gU+paNwFe++WBw6oIN6Rs5hFPialUgeKbpjh16Zq6wcptjBpvg0CHq29+d456QUAJQ==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "ulT3XNgqIQSmpQFK5YEEwRq4RA6emXBKQHnZxtCz37SJin1PnVpH9m8vOAF9FmaWsgSAfjhhjIZDruMu1oKxSg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/GlobalUsings.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/GlobalUsings.cs index a26246d61..1880b8c40 100644 --- a/Converters/Rhino/Speckle.Converters.RhinoShared/GlobalUsings.cs +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/GlobalUsings.cs @@ -1,3 +1,4 @@ global using RG = Rhino.Geometry; +global using SO = Speckle.Objects.Other; global using SOG = Speckle.Objects.Geometry; global using SOP = Speckle.Objects.Primitive; diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/SpeckleToHostGeometryBaseTopLevelConverter.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/SpeckleToHostGeometryBaseTopLevelConverter.cs index a7f4914e7..c2b0030ce 100644 --- a/Converters/Rhino/Speckle.Converters.RhinoShared/SpeckleToHostGeometryBaseTopLevelConverter.cs +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/SpeckleToHostGeometryBaseTopLevelConverter.cs @@ -7,9 +7,8 @@ namespace Speckle.Converters.Rhino; public abstract class SpeckleToHostGeometryBaseTopLevelConverter : IToHostTopLevelConverter where TIn : Base - where TOut : RG.GeometryBase { - protected IConverterSettingsStore SettingsStore { get; private set; } + private readonly IConverterSettingsStore _settingsStore; private readonly ITypedConverter _geometryBaseConverter; protected SpeckleToHostGeometryBaseTopLevelConverter( @@ -17,7 +16,7 @@ protected SpeckleToHostGeometryBaseTopLevelConverter( ITypedConverter geometryBaseConverter ) { - SettingsStore = settingsStore; + _settingsStore = settingsStore; _geometryBaseConverter = geometryBaseConverter; } @@ -26,18 +25,38 @@ public object Convert(Base target) var castedBase = (TIn)target; var result = _geometryBaseConverter.Convert(castedBase); - /* - * POC: CNX-9270 Looking at a simpler, more performant way of doing unit scaling on `ToNative` - * by fully relying on the transform capabilities of the HostApp, and only transforming top-level stuff. - * This may not hold when adding more complex conversions, but it works for now! - */ - if (castedBase["units"] is string units) + if (result is null) { - var scaleFactor = Units.GetConversionFactor(units, SettingsStore.Current.SpeckleUnits); - var scale = RG.Transform.Scale(RG.Point3d.Origin, scaleFactor); - result.Transform(scale); + throw new SpeckleConversionException( + $"Geometry base converter returned null for base object of type {target.speckle_type}" + ); + } + + var units = castedBase["units"] as string; + if (result is RG.GeometryBase geometryBase && units is not null) + { + geometryBase.Transform(GetScaleTransform(units)); + return geometryBase; + } + + if (result is List geometryBases && units is not null) + { + var t = GetScaleTransform(units); + foreach (var gb in geometryBases) + { + gb.Transform(t); + } + + return geometryBases; } return result; } + + private RG.Transform GetScaleTransform(string from) + { + var scaleFactor = Units.GetConversionFactor(from, _settingsStore.Current.SpeckleUnits); + var scale = RG.Transform.Scale(RG.Point3d.Origin, scaleFactor); + return scale; + } } diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/Helpers/RawEncodingToHost.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/Helpers/RawEncodingToHost.cs new file mode 100644 index 000000000..ca59d5b23 --- /dev/null +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/Helpers/RawEncodingToHost.cs @@ -0,0 +1,30 @@ +using Rhino.FileIO; +using Speckle.Converters.Common; + +namespace Speckle.Converters.Rhino.ToHost.Helpers; + +/// +/// Top level handler for raw encoded objects. +/// +public static class RawEncodingToHost +{ + public static List Convert(SOG.IRawEncodedObject target) + { + // note: I am not sure that we're going to have other encoding formats, but who knows. + switch (target.encodedValue.format) + { + case SO.RawEncodingFormats.RHINO_3DM: + return Handle3dm(target); + default: + throw new SpeckleConversionException($"Unsupported brep encoding format: {target.encodedValue.format}"); + } + } + + private static List Handle3dm(SOG.IRawEncodedObject target) + { + var bytes = System.Convert.FromBase64String(target.encodedValue.contents); + var file = File3dm.FromByteArray(bytes); + var brepObject = file.Objects.Select(o => o.Geometry); + return brepObject.ToList(); + } +} diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/Raw/BrepXToHostConverter.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/Raw/BrepXToHostConverter.cs new file mode 100644 index 000000000..d4d39b3f5 --- /dev/null +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/Raw/BrepXToHostConverter.cs @@ -0,0 +1,9 @@ +using Speckle.Converters.Common.Objects; +using Speckle.Converters.Rhino.ToHost.Helpers; + +namespace Speckle.Converters.Rhino.ToHost.Raw; + +public class BrepXToHostConverter : ITypedConverter> +{ + public List Convert(SOG.BrepX target) => RawEncodingToHost.Convert(target); +} diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/Raw/ExtrusionXToHostConverter.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/Raw/ExtrusionXToHostConverter.cs new file mode 100644 index 000000000..713c697b1 --- /dev/null +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/Raw/ExtrusionXToHostConverter.cs @@ -0,0 +1,9 @@ +using Speckle.Converters.Common.Objects; +using Speckle.Converters.Rhino.ToHost.Helpers; + +namespace Speckle.Converters.Rhino.ToHost.Raw; + +public class ExtrusionXToHostConverter : ITypedConverter> +{ + public List Convert(SOG.ExtrusionX target) => RawEncodingToHost.Convert(target); +} diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/Raw/PolyCurveToHostConverter.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/Raw/PolyCurveToHostConverter.cs index 0c21f87d2..9ea6a97ed 100644 --- a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/Raw/PolyCurveToHostConverter.cs +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/Raw/PolyCurveToHostConverter.cs @@ -1,7 +1,7 @@ using Microsoft.Extensions.DependencyInjection; using Speckle.Converters.Common.Objects; using Speckle.Objects; -using Speckle.Sdk.Common; +using Speckle.Sdk.Common.Exceptions; namespace Speckle.Converters.Rhino.ToHost.Raw; diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/Raw/SubDXToHostConverter.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/Raw/SubDXToHostConverter.cs new file mode 100644 index 000000000..19f56337f --- /dev/null +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/Raw/SubDXToHostConverter.cs @@ -0,0 +1,9 @@ +using Speckle.Converters.Common.Objects; +using Speckle.Converters.Rhino.ToHost.Helpers; + +namespace Speckle.Converters.Rhino.ToHost.Raw; + +public class SubDXToHostConverter : ITypedConverter> +{ + public List Convert(SOG.SubDX target) => RawEncodingToHost.Convert(target); +} diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/BrepXToHostTopLevelConverter.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/BrepXToHostTopLevelConverter.cs new file mode 100644 index 000000000..08f1c6902 --- /dev/null +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/BrepXToHostTopLevelConverter.cs @@ -0,0 +1,35 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; + +namespace Speckle.Converters.Rhino.ToHost.TopLevel; + +[NameAndRankValue(nameof(SOG.BrepX), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class BrepXToHostTopLevelConverter : SpeckleToHostGeometryBaseTopLevelConverter> +{ + public BrepXToHostTopLevelConverter( + IConverterSettingsStore settingsStore, + ITypedConverter> geometryBaseConverter + ) + : base(settingsStore, geometryBaseConverter) { } +} + +[NameAndRankValue(nameof(SOG.SubDX), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class SubDXToHostTopLevelConverter : SpeckleToHostGeometryBaseTopLevelConverter> +{ + public SubDXToHostTopLevelConverter( + IConverterSettingsStore settingsStore, + ITypedConverter> geometryBaseConverter + ) + : base(settingsStore, geometryBaseConverter) { } +} + +[NameAndRankValue(nameof(SOG.ExtrusionX), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class ExtrusionXToHostTopLevelConverter + : SpeckleToHostGeometryBaseTopLevelConverter> +{ + public ExtrusionXToHostTopLevelConverter( + IConverterSettingsStore settingsStore, + ITypedConverter> geometryBaseConverter + ) + : base(settingsStore, geometryBaseConverter) { } +} diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/Encoding/RawEncodingCreator.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/Encoding/RawEncodingCreator.cs new file mode 100644 index 000000000..c8475723b --- /dev/null +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/Encoding/RawEncodingCreator.cs @@ -0,0 +1,50 @@ +using Rhino; +using Rhino.FileIO; +using Speckle.Converters.Common; + +namespace Speckle.Converters.Rhino.ToSpeckle.Encoding; + +internal static class RawEncodingCreator +{ + public static SO.RawEncoding Encode(RG.GeometryBase target, RhinoDoc doc) + { + // note: this way works too, but we need to write the file to disk before reading it back out. + // using var doc = RhinoDoc.CreateHeadless(default); + // doc.ModelUnitSystem = _settingsStore.Current.Document.ModelUnitSystem; + // doc.ModelAbsoluteTolerance = _settingsStore.Current.Document.ModelAbsoluteTolerance; + // doc.ModelAngleToleranceRadians = _settingsStore.Current.Document.ModelAngleToleranceRadians; + // doc.Objects.Add(target); + + // var tempFile = TempFileProvider.GetTempFile(_speckleApplication.Slug, "3dm"); + // doc.Write3dmFile(tempFile, new FileWriteOptions() { IncludeRenderMeshes = false, WriteGeometryOnly = true, IncludeHistory = false, WriteUserData = false}); + // var fileBytes = System.Convert.ToBase64String(File.ReadAllBytes(tempFile)); + // var brepXEncoding = new SOG.BrepXEncoding() { contents = fileBytes, format = "3dm" }; + // return brepXEncoding; + + // note: this way works probably better as we don't need to write the file to disk and read it back in. + using var file = new File3dm(); + switch (target) + { + case RG.Brep b: + file.Objects.AddBrep(b); + break; + case RG.Extrusion e: + file.Objects.AddExtrusion(e); + break; + case RG.SubD d: + file.Objects.AddSubD(d); + break; + default: + throw new SpeckleConversionException($"Unsupported type for encoding: {target.GetType().FullName}"); + } + + file.Settings.ModelUnitSystem = doc.ModelUnitSystem; + file.Settings.ModelAbsoluteTolerance = doc.ModelAbsoluteTolerance; + file.Settings.ModelAngleToleranceRadians = doc.ModelAngleToleranceRadians; + + var fb = file.ToByteArray(new File3dmWriteOptions() { SaveUserData = false, Version = 7 }); + var fbString = System.Convert.ToBase64String(fb); + var bxe = new SO.RawEncoding() { contents = fbString, format = SO.RawEncodingFormats.RHINO_3DM }; + return bxe; + } +} diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/Meshing/DisplayMeshExtractor.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/Meshing/DisplayMeshExtractor.cs new file mode 100644 index 000000000..63e9023b5 --- /dev/null +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/Meshing/DisplayMeshExtractor.cs @@ -0,0 +1,39 @@ +using Rhino.DocObjects; +using Speckle.Converters.Common; + +namespace Speckle.Converters.Rhino.ToSpeckle.Meshing; + +public static class DisplayMeshExtractor +{ + public static RG.Mesh GetDisplayMesh(RhinoObject obj) + { + // note: unsure this is nice, we get bigger meshes - we should to benchmark (conversion time vs size tradeoffs) + var renderMeshes = obj.GetMeshes(RG.MeshType.Render); + if (renderMeshes.Length == 0) + { + switch (obj) + { + case BrepObject brep: + renderMeshes = RG.Mesh.CreateFromBrep(brep.BrepGeometry, new(0.05, 0.05)); + break; + case ExtrusionObject extrusion: + renderMeshes = RG.Mesh.CreateFromBrep(extrusion.ExtrusionGeometry.ToBrep(), new(0.05, 0.05)); + break; + case SubDObject subDObject: +#pragma warning disable CA2000 + var mesh = RG.Mesh.CreateFromSubD(subDObject.Geometry as RG.SubD, 0); +#pragma warning restore CA2000 + renderMeshes = [mesh]; + break; + default: + throw new SpeckleConversionException( + $"Unsupported object for display mesh generation {obj.GetType().FullName}" + ); + } + } + + var joinedMesh = new RG.Mesh(); + joinedMesh.Append(renderMeshes); + return joinedMesh; + } +} diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/Raw/BrepToSpeckleConverter.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/Raw/BrepToSpeckleConverter.cs index d373e10bb..e17527044 100644 --- a/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/Raw/BrepToSpeckleConverter.cs +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/Raw/BrepToSpeckleConverter.cs @@ -43,7 +43,7 @@ IConverterSettingsStore settingsStore public SOG.Brep Convert(RG.Brep target) { var tol = _settingsStore.Current.Document.ModelAbsoluteTolerance; - target.Repair(tol); + target.Repair(tol); // NOTE: for objects far-ish (not that far imho) from origin, this call nukes performance and takes ages. // POC: CNX-9276 This should come as part of the user settings in the context object. // if (PreprocessGeometry) @@ -65,6 +65,7 @@ public SOG.Brep Convert(RG.Brep target) // displayValue["renderMaterial"] = mat; // } + // Vertices, uv curves, 3d curves and surfaces List vertices = new(target.Vertices.Count); vertices.AddRange(target.Vertices.Select(v => _pointConverter.Convert(v.Location))); diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/TopLevel/BrepObjectToSpeckleTopLevelConverter.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/TopLevel/BrepObjectToSpeckleTopLevelConverter.cs index e14a019ca..c79440bfe 100644 --- a/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/TopLevel/BrepObjectToSpeckleTopLevelConverter.cs +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/TopLevel/BrepObjectToSpeckleTopLevelConverter.cs @@ -1,6 +1,8 @@ using Rhino.DocObjects; using Speckle.Converters.Common; using Speckle.Converters.Common.Objects; +using Speckle.Converters.Rhino.ToSpeckle.Encoding; +using Speckle.Converters.Rhino.ToSpeckle.Meshing; using Speckle.Sdk.Models; namespace Speckle.Converters.Rhino.ToSpeckle.TopLevel; @@ -8,17 +10,33 @@ namespace Speckle.Converters.Rhino.ToSpeckle.TopLevel; [NameAndRankValue(nameof(BrepObject), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class BrepObjectToSpeckleTopLevelConverter : IToSpeckleTopLevelConverter { - private readonly ITypedConverter _curveConverter; + private readonly ITypedConverter _meshConverter; + private readonly IConverterSettingsStore _settingsStore; - public BrepObjectToSpeckleTopLevelConverter(ITypedConverter curveConverter) + public BrepObjectToSpeckleTopLevelConverter( + ITypedConverter meshConverter, + IConverterSettingsStore settingsStore + ) { - _curveConverter = curveConverter; + _meshConverter = meshConverter; + _settingsStore = settingsStore; } public Base Convert(object target) { - var curveObject = (BrepObject)target; - var speckleCurve = _curveConverter.Convert(curveObject.BrepGeometry); - return speckleCurve; + var brepObject = (BrepObject)target; + var brepEncoding = RawEncodingCreator.Encode(brepObject.Geometry, _settingsStore.Current.Document); + + var mesh = DisplayMeshExtractor.GetDisplayMesh(brepObject); + var displayValue = new List { _meshConverter.Convert(mesh) }; + + var bx = new SOG.BrepX() + { + displayValue = displayValue, + encodedValue = brepEncoding, + units = _settingsStore.Current.SpeckleUnits + }; + + return bx; } } diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/TopLevel/ExtrusionObjectToSpeckleTopLevelConverter.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/TopLevel/ExtrusionObjectToSpeckleTopLevelConverter.cs index 9ea18de4b..255ed0162 100644 --- a/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/TopLevel/ExtrusionObjectToSpeckleTopLevelConverter.cs +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/TopLevel/ExtrusionObjectToSpeckleTopLevelConverter.cs @@ -1,6 +1,8 @@ using Rhino.DocObjects; using Speckle.Converters.Common; using Speckle.Converters.Common.Objects; +using Speckle.Converters.Rhino.ToSpeckle.Encoding; +using Speckle.Converters.Rhino.ToSpeckle.Meshing; using Speckle.Sdk.Models; namespace Speckle.Converters.Rhino.ToSpeckle.TopLevel; @@ -8,17 +10,33 @@ namespace Speckle.Converters.Rhino.ToSpeckle.TopLevel; [NameAndRankValue(nameof(ExtrusionObject), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class ExtrusionObjectToSpeckleTopLevelConverter : IToSpeckleTopLevelConverter { - private readonly ITypedConverter _curveConverter; + private readonly ITypedConverter _meshConverter; + private readonly IConverterSettingsStore _settingsStore; - public ExtrusionObjectToSpeckleTopLevelConverter(ITypedConverter curveConverter) + public ExtrusionObjectToSpeckleTopLevelConverter( + ITypedConverter meshConverter, + IConverterSettingsStore settingsStore + ) { - _curveConverter = curveConverter; + _meshConverter = meshConverter; + _settingsStore = settingsStore; } public Base Convert(object target) { - var curveObject = (ExtrusionObject)target; - var speckleCurve = _curveConverter.Convert(curveObject.ExtrusionGeometry.ToBrep()); - return speckleCurve; + var extrusionObject = (ExtrusionObject)target; + var extrusionEncoding = RawEncodingCreator.Encode(extrusionObject.Geometry, _settingsStore.Current.Document); + + var mesh = DisplayMeshExtractor.GetDisplayMesh(extrusionObject); + var displayValue = new List { _meshConverter.Convert(mesh) }; + + var bx = new SOG.ExtrusionX() + { + displayValue = displayValue, + encodedValue = extrusionEncoding, + units = _settingsStore.Current.SpeckleUnits + }; + + return bx; } } diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/TopLevel/SubDObjectToSpeckleTopLevelConverter.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/TopLevel/SubDObjectToSpeckleTopLevelConverter.cs index 479ff68b2..f86839467 100644 --- a/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/TopLevel/SubDObjectToSpeckleTopLevelConverter.cs +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/ToSpeckle/TopLevel/SubDObjectToSpeckleTopLevelConverter.cs @@ -1,6 +1,8 @@ using Rhino.DocObjects; using Speckle.Converters.Common; using Speckle.Converters.Common.Objects; +using Speckle.Converters.Rhino.ToSpeckle.Encoding; +using Speckle.Converters.Rhino.ToSpeckle.Meshing; using Speckle.Sdk.Models; namespace Speckle.Converters.Rhino.ToSpeckle.TopLevel; @@ -8,18 +10,33 @@ namespace Speckle.Converters.Rhino.ToSpeckle.TopLevel; [NameAndRankValue(nameof(SubDObject), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] public class SubDObjectToSpeckleTopLevelConverter : IToSpeckleTopLevelConverter { - private readonly ITypedConverter _brepConverter; + private readonly ITypedConverter _meshConverter; + private readonly IConverterSettingsStore _settingsStore; - public SubDObjectToSpeckleTopLevelConverter(ITypedConverter curveConverter) + public SubDObjectToSpeckleTopLevelConverter( + ITypedConverter meshConverter, + IConverterSettingsStore settingsStore + ) { - _brepConverter = curveConverter; + _meshConverter = meshConverter; + _settingsStore = settingsStore; } public Base Convert(object target) { var subDObject = (SubDObject)target; - var subD = (RG.SubD)subDObject.Geometry; - var speckleCurve = _brepConverter.Convert(subD.ToBrep()); - return speckleCurve; + var subdEncoding = RawEncodingCreator.Encode(subDObject.Geometry, _settingsStore.Current.Document); + + var mesh = DisplayMeshExtractor.GetDisplayMesh(subDObject); + var displayValue = new List { _meshConverter.Convert(mesh) }; + + var bx = new SOG.SubDX() + { + displayValue = displayValue, + encodedValue = subdEncoding, + units = _settingsStore.Current.SpeckleUnits + }; + + return bx; } } diff --git a/DUI3/Speckle.Connectors.DUI.Tests/Bridge/IdleCallManagerTests.cs b/DUI3/Speckle.Connectors.DUI.Tests/Bridge/IdleCallManagerTests.cs index 57cd12dc3..ecbb6ebaa 100644 --- a/DUI3/Speckle.Connectors.DUI.Tests/Bridge/IdleCallManagerTests.cs +++ b/DUI3/Speckle.Connectors.DUI.Tests/Bridge/IdleCallManagerTests.cs @@ -15,7 +15,7 @@ public void SubscribeToIdleTest() var sut = new IdleCallManager(handler.Object); var action = Create(); var addEvent = Create(); - handler.Setup(x => x.CatchUnhandled(It.IsAny())).Returns(new Result()); + handler.Setup(x => x.CatchUnhandled(It.IsAny())); sut.SubscribeToIdle("id", action.Object, addEvent.Object); } @@ -24,7 +24,7 @@ public void SubscribeInternalTest() { var handler = Create(); var sut = new IdleCallManager(handler.Object); - var action = Create(); + var action = Create>(); var addEvent = Create(); addEvent.Setup(x => x.Invoke()); @@ -41,19 +41,22 @@ public void AppOnIdleTest() var handler = Create(); var sut = new IdleCallManager(handler.Object); var removeEvent = Create(); - handler.Setup(x => x.CatchUnhandled(It.IsAny())).Returns(new Result()); + handler.Setup(x => x.FireAndForget(It.IsAny>())); sut.AppOnIdle(removeEvent.Object); } [Test] - public void AppOnIdleInternalTest() + public async Task AppOnIdleInternalTest() { var handler = Create(); var sut = new IdleCallManager(handler.Object); - var expectedAction = Create(); - expectedAction.Setup(x => x.Invoke()); + var expectedAction = Create>(); + expectedAction.Setup(x => x.Invoke()).Returns(Task.CompletedTask); - handler.Setup(m => m.CatchUnhandled(It.IsAny())).Callback(a => a.Invoke()).Returns(new Result()); + handler + .Setup(m => m.CatchUnhandledAsync(It.IsAny>())) + .Callback>(a => a.Invoke()) + .Returns(Task.CompletedTask); var removeEvent = Create(); removeEvent.Setup(x => x.Invoke()); @@ -62,7 +65,7 @@ public void AppOnIdleInternalTest() sut.IdleSubscriptionCalled.Should().BeTrue(); sut.Calls.Count.Should().Be(1); - sut.AppOnIdleInternal(removeEvent.Object); + await sut.AppOnIdleInternal(removeEvent.Object); sut.Calls.Count.Should().Be(0); sut.IdleSubscriptionCalled.Should().BeFalse(); expectedAction.Verify(a => a(), Times.Once); diff --git a/DUI3/Speckle.Connectors.DUI.Tests/Bridge/TopLevelExceptionHandlerTests.cs b/DUI3/Speckle.Connectors.DUI.Tests/Bridge/TopLevelExceptionHandlerTests.cs index 3ef8a0e15..8ba16dc14 100644 --- a/DUI3/Speckle.Connectors.DUI.Tests/Bridge/TopLevelExceptionHandlerTests.cs +++ b/DUI3/Speckle.Connectors.DUI.Tests/Bridge/TopLevelExceptionHandlerTests.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.Extensions.Logging; using Moq; using NUnit.Framework; @@ -26,7 +26,9 @@ public void CatchUnhandledAction_Exception() var logger = Create>(MockBehavior.Loose); var bridge = Create(); - bridge.Setup(x => x.Send(BasicConnectorBindingCommands.SET_GLOBAL_NOTIFICATION, It.IsAny())); + bridge + .Setup(x => x.Send(BasicConnectorBindingCommands.SET_GLOBAL_NOTIFICATION, It.IsAny(), default)) + .Returns(Task.CompletedTask); var sut = new TopLevelExceptionHandler(logger.Object, bridge.Object); @@ -53,7 +55,9 @@ public void CatchUnhandledFunc_Exception() var logger = Create>(MockBehavior.Loose); var bridge = Create(); - bridge.Setup(x => x.Send(BasicConnectorBindingCommands.SET_GLOBAL_NOTIFICATION, It.IsAny())); + bridge + .Setup(x => x.Send(BasicConnectorBindingCommands.SET_GLOBAL_NOTIFICATION, It.IsAny(), default)) + .Returns(Task.CompletedTask); var sut = new TopLevelExceptionHandler(logger.Object, bridge.Object); @@ -70,11 +74,9 @@ public void CatchUnhandledFunc_Exception_Fatal() var bridge = Create(); var sut = new TopLevelExceptionHandler(logger.Object, bridge.Object); -#pragma warning disable CA2201 var exception = Assert.Throws( () => sut.CatchUnhandled(new Func(() => throw new AppDomainUnloadedException())) ); -#pragma warning restore CA2201 exception.InnerExceptions.Single().Should().BeOfType(); } @@ -86,7 +88,7 @@ public async Task CatchUnhandledFuncAsync_Happy() var bridge = Create(); var sut = new TopLevelExceptionHandler(logger.Object, bridge.Object); - var returnVal = await sut.CatchUnhandled(() => Task.FromResult(val)); + var returnVal = await sut.CatchUnhandledAsync(() => Task.FromResult(val)); returnVal.Value.Should().Be(val); returnVal.Exception.Should().BeNull(); returnVal.IsSuccess.Should().BeTrue(); @@ -98,11 +100,13 @@ public async Task CatchUnhandledFuncAsync_Exception() var logger = Create>(MockBehavior.Loose); var bridge = Create(); - bridge.Setup(x => x.Send(BasicConnectorBindingCommands.SET_GLOBAL_NOTIFICATION, It.IsAny())); + bridge + .Setup(x => x.Send(BasicConnectorBindingCommands.SET_GLOBAL_NOTIFICATION, It.IsAny(), default)) + .Returns(Task.CompletedTask); var sut = new TopLevelExceptionHandler(logger.Object, bridge.Object); - var returnVal = await sut.CatchUnhandled(new Func>(() => throw new InvalidOperationException())); + var returnVal = await sut.CatchUnhandledAsync(new Func>(() => throw new InvalidOperationException())); returnVal.Value.Should().BeNull(); returnVal.Exception.Should().BeOfType(); returnVal.IsSuccess.Should().BeFalse(); @@ -115,11 +119,9 @@ public void CatchUnhandledFuncAsync_Exception_Fatal() var bridge = Create(); var sut = new TopLevelExceptionHandler(logger.Object, bridge.Object); -#pragma warning disable CA2201 var exception = Assert.ThrowsAsync( - async () => await sut.CatchUnhandled(new Func>(() => throw new AppDomainUnloadedException())) + async () => await sut.CatchUnhandledAsync(new Func>(() => throw new AppDomainUnloadedException())) ); -#pragma warning restore CA2201 exception.Should().BeOfType(); } } diff --git a/DUI3/Speckle.Connectors.DUI.Tests/packages.lock.json b/DUI3/Speckle.Connectors.DUI.Tests/packages.lock.json index 50599e569..e2c78991c 100644 --- a/DUI3/Speckle.Connectors.DUI.Tests/packages.lock.json +++ b/DUI3/Speckle.Connectors.DUI.Tests/packages.lock.json @@ -27,6 +27,15 @@ "Microsoft.TestPlatform.TestHost": "17.10.0" } }, + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3" + } + }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -195,6 +204,11 @@ "System.Runtime.CompilerServices.Unsafe": "4.5.1" } }, + "Microsoft.NETFramework.ReferenceAssemblies.net461": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA==" + }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", @@ -328,8 +342,8 @@ "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )", - "Speckle.Sdk": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )", + "Speckle.Sdk": "[3.1.0-dev.162, )" } }, "speckle.connectors.dui": { @@ -337,7 +351,7 @@ "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", "Speckle.Connectors.Common": "[1.0.0, )", - "Speckle.Sdk": "[3.1.0-dev.145, )", + "Speckle.Sdk": "[3.1.0-dev.162, )", "System.Threading.Tasks.Dataflow": "[6.0.0, )" } }, @@ -380,18 +394,18 @@ }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "gpENXDB2i8SiXJZwrLVfB3BwKF1FHUSpPiw0BQ3Hl0V3821UWZjPYw5XK694oxJlVAeEMRvrMZMGsWOcK+3nMw==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "1fyaPjC1kVtroKfoXI7edJX0Uho/TNRWG9mtmIQ7jKhX1zElrJPSoH8zEIMqckZhGmwUrYjlQFPssASNCP721Q==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.145" + "Speckle.Sdk": "3.1.0-dev.162" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "YZLtitiVIxl12pNTXaB9gU+paNwFe++WBw6oIN6Rs5hFPialUgeKbpjh16Zq6wcptjBpvg0CHq29+d456QUAJQ==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "ulT3XNgqIQSmpQFK5YEEwRq4RA6emXBKQHnZxtCz37SJin1PnVpH9m8vOAF9FmaWsgSAfjhhjIZDruMu1oKxSg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", diff --git a/DUI3/Speckle.Connectors.DUI.WebView/DUI3ControlWebView.xaml.cs b/DUI3/Speckle.Connectors.DUI.WebView/DUI3ControlWebView.xaml.cs index 979045d39..56ff2b85d 100644 --- a/DUI3/Speckle.Connectors.DUI.WebView/DUI3ControlWebView.xaml.cs +++ b/DUI3/Speckle.Connectors.DUI.WebView/DUI3ControlWebView.xaml.cs @@ -26,14 +26,57 @@ public DUI3ControlWebView(IServiceProvider serviceProvider) public object BrowserElement => Browser; - public void ExecuteScriptAsyncMethod(string script) + // { + // if (!Browser.IsInitialized) + // { + // throw new InvalidOperationException("Failed to execute script, Webview2 is not initialized yet."); + // } + // + // var t = Browser.Dispatcher.Invoke( + // async () => + // { + // var res = await Browser.ExecuteScriptAsync(script).ConfigureAwait(true); + // await Task.Delay(100).ConfigureAwait(true); + // return res; + // }, + // DispatcherPriority.Background + // ); + // + // _ = t.IsCompleted; + + // bool isAlreadyMainThread = Browser.Dispatcher.CheckAccess(); + // if (isAlreadyMainThread) + // { + // Browser.ExecuteScriptAsync(script); + // } + // else + // { + // Browser.Dispatcher.Invoke( + // () => + // { + // return Browser.ExecuteScriptAsync(script); + // }, + // DispatcherPriority.Background + // ); + // } + // } + + public async Task ExecuteScriptAsyncMethod(string script, CancellationToken cancellationToken) { if (!Browser.IsInitialized) { throw new InvalidOperationException("Failed to execute script, Webview2 is not initialized yet."); } - Browser.Dispatcher.Invoke(() => Browser.ExecuteScriptAsync(script), DispatcherPriority.Background); + var callbackTask = await Browser + .Dispatcher.InvokeAsync( + async () => await Browser.ExecuteScriptAsync(script).ConfigureAwait(false), + DispatcherPriority.Background, + cancellationToken + ) + .Task.ConfigureAwait(false); + + _ = await callbackTask.ConfigureAwait(false); } private void OnInitialized(object? sender, CoreWebView2InitializationCompletedEventArgs e) @@ -51,6 +94,9 @@ private void OnInitialized(object? sender, CoreWebView2InitializationCompletedEv } } + /// + /// This must be called on the Main thread + /// private void SetupBinding(IBinding binding) { binding.Parent.AssociateWithBinding(binding); diff --git a/DUI3/Speckle.Connectors.DUI.WebView/packages.lock.json b/DUI3/Speckle.Connectors.DUI.WebView/packages.lock.json index d9998e0f7..914064d33 100644 --- a/DUI3/Speckle.Connectors.DUI.WebView/packages.lock.json +++ b/DUI3/Speckle.Connectors.DUI.WebView/packages.lock.json @@ -2,6 +2,15 @@ "version": 2, "dependencies": { ".NETFramework,Version=v4.8": { + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3" + } + }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -137,6 +146,11 @@ "System.Runtime.CompilerServices.Unsafe": "4.5.1" } }, + "Microsoft.NETFramework.ReferenceAssemblies.net48": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ==" + }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", @@ -260,8 +274,8 @@ "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )", - "Speckle.Sdk": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )", + "Speckle.Sdk": "[3.1.0-dev.162, )" } }, "speckle.connectors.dui": { @@ -269,7 +283,7 @@ "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", "Speckle.Connectors.Common": "[1.0.0, )", - "Speckle.Sdk": "[3.1.0-dev.145, )", + "Speckle.Sdk": "[3.1.0-dev.162, )", "System.Threading.Tasks.Dataflow": "[6.0.0, )" } }, @@ -305,18 +319,18 @@ }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "gpENXDB2i8SiXJZwrLVfB3BwKF1FHUSpPiw0BQ3Hl0V3821UWZjPYw5XK694oxJlVAeEMRvrMZMGsWOcK+3nMw==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "1fyaPjC1kVtroKfoXI7edJX0Uho/TNRWG9mtmIQ7jKhX1zElrJPSoH8zEIMqckZhGmwUrYjlQFPssASNCP721Q==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.145" + "Speckle.Sdk": "3.1.0-dev.162" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "YZLtitiVIxl12pNTXaB9gU+paNwFe++WBw6oIN6Rs5hFPialUgeKbpjh16Zq6wcptjBpvg0CHq29+d456QUAJQ==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "ulT3XNgqIQSmpQFK5YEEwRq4RA6emXBKQHnZxtCz37SJin1PnVpH9m8vOAF9FmaWsgSAfjhhjIZDruMu1oKxSg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", @@ -338,6 +352,15 @@ } }, "net6.0-windows7.0": { + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3" + } + }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -472,6 +495,11 @@ "System.Runtime.CompilerServices.Unsafe": "4.5.1" } }, + "Microsoft.NETFramework.ReferenceAssemblies.net461": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA==" + }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", @@ -560,8 +588,8 @@ "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )", - "Speckle.Sdk": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )", + "Speckle.Sdk": "[3.1.0-dev.162, )" } }, "speckle.connectors.dui": { @@ -569,7 +597,7 @@ "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", "Speckle.Connectors.Common": "[1.0.0, )", - "Speckle.Sdk": "[3.1.0-dev.145, )", + "Speckle.Sdk": "[3.1.0-dev.162, )", "System.Threading.Tasks.Dataflow": "[6.0.0, )" } }, @@ -605,18 +633,18 @@ }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "gpENXDB2i8SiXJZwrLVfB3BwKF1FHUSpPiw0BQ3Hl0V3821UWZjPYw5XK694oxJlVAeEMRvrMZMGsWOcK+3nMw==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "1fyaPjC1kVtroKfoXI7edJX0Uho/TNRWG9mtmIQ7jKhX1zElrJPSoH8zEIMqckZhGmwUrYjlQFPssASNCP721Q==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.145" + "Speckle.Sdk": "3.1.0-dev.162" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "YZLtitiVIxl12pNTXaB9gU+paNwFe++WBw6oIN6Rs5hFPialUgeKbpjh16Zq6wcptjBpvg0CHq29+d456QUAJQ==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "ulT3XNgqIQSmpQFK5YEEwRq4RA6emXBKQHnZxtCz37SJin1PnVpH9m8vOAF9FmaWsgSAfjhhjIZDruMu1oKxSg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", diff --git a/DUI3/Speckle.Connectors.DUI/Bindings/ConfigBinding.cs b/DUI3/Speckle.Connectors.DUI/Bindings/ConfigBinding.cs index b8d86603a..50eeb162f 100644 --- a/DUI3/Speckle.Connectors.DUI/Bindings/ConfigBinding.cs +++ b/DUI3/Speckle.Connectors.DUI/Bindings/ConfigBinding.cs @@ -37,7 +37,7 @@ JsonSerializerSettings serializerOptions public bool GetIsDevMode() #pragma warning restore CA1024 { -#if DEBUG +#if DEBUG || LOCAL return true; #else return false; @@ -80,6 +80,39 @@ public void UpdateConfig(ConnectorConfig config) var str = JsonConvert.SerializeObject(config, _serializerOptions); ConfigStorage.UpdateObject(_speckleApplication.HostApplication, str); } + + public void SetUserSelectedAccountId(string userSelectedAccountId) + { + var str = JsonConvert.SerializeObject( + new AccountsConfig() { UserSelectedAccountId = userSelectedAccountId }, + _serializerOptions + ); + ConfigStorage.UpdateObject("accounts", str); + } + + public async Task GetUserSelectedAccountId() + { + var rawConfig = await ConfigStorage.GetObject("accounts").ConfigureAwait(false); + if (rawConfig is null) + { + return null; + } + + try + { + var config = JsonConvert.DeserializeObject(rawConfig, _serializerOptions); + if (config is null) + { + throw new SerializationException("Failed to deserialize accounts config"); + } + + return config; + } + catch (SerializationException) + { + return null; + } + } } /// @@ -89,3 +122,8 @@ public class ConnectorConfig { public bool DarkTheme { get; set; } = true; } + +public class AccountsConfig +{ + public string? UserSelectedAccountId { get; set; } +} diff --git a/DUI3/Speckle.Connectors.DUI/Bindings/IBasicConnectorBinding.cs b/DUI3/Speckle.Connectors.DUI/Bindings/IBasicConnectorBinding.cs index 4641720e5..2677926d4 100644 --- a/DUI3/Speckle.Connectors.DUI/Bindings/IBasicConnectorBinding.cs +++ b/DUI3/Speckle.Connectors.DUI/Bindings/IBasicConnectorBinding.cs @@ -19,9 +19,9 @@ public interface IBasicConnectorBinding : IBinding /// Highlights the objects attached to this sender in the host application. /// /// - public void HighlightModel(string modelCardId); + public Task HighlightModel(string modelCardId); - public void HighlightObjects(List objectIds); + public Task HighlightObjects(IReadOnlyList objectIds); public BasicConnectorBindingCommands Commands { get; } } @@ -43,7 +43,6 @@ public enum ToastNotificationType 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"; public const string SET_GLOBAL_NOTIFICATION = "setGlobalNotification"; @@ -54,7 +53,8 @@ public BasicConnectorBindingCommands(IBrowserBridge bridge) Bridge = bridge; } - public void NotifyDocumentChanged() => Bridge.Send(NOTIFY_DOCUMENT_CHANGED_EVENT_NAME); + public async Task NotifyDocumentChanged() => + await Bridge.Send(NOTIFY_DOCUMENT_CHANGED_EVENT_NAME).ConfigureAwait(false); /// /// Use it whenever you want to send global toast notification to UI. @@ -63,29 +63,27 @@ public BasicConnectorBindingCommands(IBrowserBridge bridge) /// Title of the notification /// Message in the toast notification. /// Closes toast notification in set timeout in UI. Default is true. - public void SetGlobalNotification(ToastNotificationType type, string title, string message, bool autoClose = true) => - Bridge.Send( - SET_GLOBAL_NOTIFICATION, - new - { - type, - title, - description = message, - autoClose - } - ); + public async Task SetGlobalNotification( + ToastNotificationType type, + string title, + string message, + bool autoClose = true + ) => + await Bridge + .Send( + SET_GLOBAL_NOTIFICATION, + new + { + type, + title, + description = message, + autoClose + } + ) + .ConfigureAwait(false); - public void SetModelProgress(string modelCardId, ModelCardProgress progress, CancellationTokenSource cts) - { - // NOTE: To prevent potential race condition - // After cancelling operation some parts could still send last progress update which was set progress on UI - // after it forced to be undefined. This is the safest way to prevent any case like this. - if (!cts.IsCancellationRequested) - { - Bridge.Send(SET_MODEL_PROGRESS_UI_COMMAND_NAME, new { modelCardId, progress }); - } - } - - public void SetModelError(string modelCardId, Exception error) => - Bridge.Send(SET_MODEL_ERROR_UI_COMMAND_NAME, new { modelCardId, error = error.Message }); + public async Task SetModelError(string modelCardId, Exception error) => + await Bridge + .Send(SET_MODEL_ERROR_UI_COMMAND_NAME, new { modelCardId, error = error.Message }) + .ConfigureAwait(false); } diff --git a/DUI3/Speckle.Connectors.DUI/Bindings/OperationProgressManager.cs b/DUI3/Speckle.Connectors.DUI/Bindings/OperationProgressManager.cs index bdf790ca9..992aaf669 100644 --- a/DUI3/Speckle.Connectors.DUI/Bindings/OperationProgressManager.cs +++ b/DUI3/Speckle.Connectors.DUI/Bindings/OperationProgressManager.cs @@ -1,4 +1,5 @@ using System.Collections.Concurrent; +using Speckle.Connectors.Common.Operations; using Speckle.Connectors.DUI.Bridge; using Speckle.Connectors.DUI.Models.Card; using Speckle.InterfaceGenerator; @@ -12,12 +13,37 @@ namespace Speckle.Connectors.DUI.Bindings; [GenerateAutoInterface] public class OperationProgressManager : IOperationProgressManager { + private class NonUIThreadProgress(Action handler) : IProgress + { + public void Report(T value) => handler(value); + } + private const string SET_MODEL_PROGRESS_UI_COMMAND_NAME = "setModelProgress"; private static readonly ConcurrentDictionary s_lastProgressValues = new(); - private const int THROTTLE_INTERVAL_MS = 50; + private const int THROTTLE_INTERVAL_MS = 200; + + public IProgress CreateOperationProgressEventHandler( + IBrowserBridge bridge, + string modelCardId, + CancellationToken cancellationToken + ) + { + var progress = new NonUIThreadProgress(args => + bridge.TopLevelExceptionHandler.FireAndForget( + () => + SetModelProgress( + bridge, + modelCardId, + new ModelCardProgress(modelCardId, args.Status, args.Progress), + cancellationToken + ) + ) + ); + return progress; + } - public void SetModelProgress( + public async Task SetModelProgress( IBrowserBridge bridge, string modelCardId, ModelCardProgress progress, @@ -34,20 +60,21 @@ CancellationToken cancellationToken t.Item1 = DateTime.Now; s_lastProgressValues[modelCardId] = (t.Item1, progress.Status); // Since it's the first time we get a call for this model card, we should send it out - SendProgress(bridge, modelCardId, progress); + await SendProgress(bridge, modelCardId, progress).ConfigureAwait(false); return; } - var elapsedMs = (DateTime.Now - t.Item1).Milliseconds; + var currentTime = DateTime.Now; + var elapsedMs = (currentTime - t.Item1).Milliseconds; if (elapsedMs < THROTTLE_INTERVAL_MS && t.Item2 == progress.Status) { return; } - SendProgress(bridge, modelCardId, progress); - s_lastProgressValues[modelCardId] = (DateTime.Now, progress.Status); + s_lastProgressValues[modelCardId] = (currentTime, progress.Status); + await SendProgress(bridge, modelCardId, progress).ConfigureAwait(false); } - private void SendProgress(IBrowserBridge bridge, string modelCardId, ModelCardProgress progress) => - bridge.Send(SET_MODEL_PROGRESS_UI_COMMAND_NAME, new { modelCardId, progress }); + private static async Task SendProgress(IBrowserBridge bridge, string modelCardId, ModelCardProgress progress) => + await bridge.Send(SET_MODEL_PROGRESS_UI_COMMAND_NAME, new { modelCardId, progress }).ConfigureAwait(false); } diff --git a/DUI3/Speckle.Connectors.DUI/Bindings/ReceiveBindingUICommands.cs b/DUI3/Speckle.Connectors.DUI/Bindings/ReceiveBindingUICommands.cs index 7b9d17497..0c7bd3428 100644 --- a/DUI3/Speckle.Connectors.DUI/Bindings/ReceiveBindingUICommands.cs +++ b/DUI3/Speckle.Connectors.DUI/Bindings/ReceiveBindingUICommands.cs @@ -3,7 +3,7 @@ namespace Speckle.Connectors.DUI.Bindings; -public class ReceiveBindingUICommands : BasicConnectorBindingCommands +public sealed class ReceiveBindingUICommands : BasicConnectorBindingCommands { // POC: put here events once we needed for receive specific private const string SET_MODEL_RECEIVE_RESULT_UI_COMMAND_NAME = "setModelReceiveResult"; @@ -11,20 +11,22 @@ public class ReceiveBindingUICommands : BasicConnectorBindingCommands public ReceiveBindingUICommands(IBrowserBridge bridge) : base(bridge) { } - public void SetModelReceiveResult( + public async Task SetModelReceiveResult( string modelCardId, IEnumerable bakedObjectIds, IEnumerable conversionResults ) { - Bridge.Send( - SET_MODEL_RECEIVE_RESULT_UI_COMMAND_NAME, - new - { - ModelCardId = modelCardId, - bakedObjectIds, - conversionResults - } - ); + await Bridge + .Send( + SET_MODEL_RECEIVE_RESULT_UI_COMMAND_NAME, + new + { + ModelCardId = modelCardId, + bakedObjectIds, + conversionResults + } + ) + .ConfigureAwait(false); } } diff --git a/DUI3/Speckle.Connectors.DUI/Bindings/SendBindingUICommands.cs b/DUI3/Speckle.Connectors.DUI/Bindings/SendBindingUICommands.cs index 2341b46aa..75b892fc2 100644 --- a/DUI3/Speckle.Connectors.DUI/Bindings/SendBindingUICommands.cs +++ b/DUI3/Speckle.Connectors.DUI/Bindings/SendBindingUICommands.cs @@ -4,33 +4,33 @@ namespace Speckle.Connectors.DUI.Bindings; // POC: Send Commands share all commands from BasicBindings + some, this pattern should be revised -public class SendBindingUICommands : BasicConnectorBindingCommands +public class SendBindingUICommands(IBrowserBridge bridge) : BasicConnectorBindingCommands(bridge) { private const string REFRESH_SEND_FILTERS_UI_COMMAND_NAME = "refreshSendFilters"; private const string SET_MODELS_EXPIRED_UI_COMMAND_NAME = "setModelsExpired"; private const string SET_MODEL_SEND_RESULT_UI_COMMAND_NAME = "setModelSendResult"; - public SendBindingUICommands(IBrowserBridge bridge) - : base(bridge) { } - // POC.. the only reasons this needs the bridge is to send? realtionship to these messages and the bridge is unclear - public void RefreshSendFilters() => Bridge.Send(REFRESH_SEND_FILTERS_UI_COMMAND_NAME); + public async Task RefreshSendFilters() => + await Bridge.Send(REFRESH_SEND_FILTERS_UI_COMMAND_NAME).ConfigureAwait(false); - public void SetModelsExpired(IEnumerable expiredModelIds) => - Bridge.Send(SET_MODELS_EXPIRED_UI_COMMAND_NAME, expiredModelIds); + public async Task SetModelsExpired(IEnumerable expiredModelIds) => + await Bridge.Send(SET_MODELS_EXPIRED_UI_COMMAND_NAME, expiredModelIds).ConfigureAwait(false); - public void SetModelSendResult( + public async Task SetModelSendResult( string modelCardId, string versionId, IEnumerable sendConversionResults ) => - Bridge.Send( - SET_MODEL_SEND_RESULT_UI_COMMAND_NAME, - new - { - modelCardId, - versionId, - sendConversionResults - } - ); + await Bridge + .Send( + SET_MODEL_SEND_RESULT_UI_COMMAND_NAME, + new + { + modelCardId, + versionId, + sendConversionResults + } + ) + .ConfigureAwait(false); } diff --git a/DUI3/Speckle.Connectors.DUI/Bindings/TestBinding.cs b/DUI3/Speckle.Connectors.DUI/Bindings/TestBinding.cs index 7231b2f2e..a5e095fbc 100644 --- a/DUI3/Speckle.Connectors.DUI/Bindings/TestBinding.cs +++ b/DUI3/Speckle.Connectors.DUI/Bindings/TestBinding.cs @@ -41,24 +41,27 @@ public object GetComplexType() => thisIsABoolean = false }; - public void TriggerEvent(string eventName) + public async Task TriggerEvent(string eventName) { switch (eventName) { case "emptyTestEvent": - Parent.Send("emptyTestEvent"); + await Parent.Send("emptyTestEvent").ConfigureAwait(false); + break; case "testEvent": default: - Parent.Send( - "testEvent", - new - { - IsOk = true, - Name = "foo", - Count = 42 - } - ); + await Parent + .Send( + "testEvent", + new + { + IsOk = true, + Name = "foo", + Count = 42 + } + ) + .ConfigureAwait(false); break; } } diff --git a/DUI3/Speckle.Connectors.DUI/Bridge/AppIdleManager.cs b/DUI3/Speckle.Connectors.DUI/Bridge/AppIdleManager.cs new file mode 100644 index 000000000..12ecee416 --- /dev/null +++ b/DUI3/Speckle.Connectors.DUI/Bridge/AppIdleManager.cs @@ -0,0 +1,33 @@ +using Speckle.InterfaceGenerator; + +namespace Speckle.Connectors.DUI.Bridge; + +[GenerateAutoInterface] +public abstract class AppIdleManager : IAppIdleManager +{ + private readonly IIdleCallManager _idleCallManager; + + protected AppIdleManager(IIdleCallManager idleCallManager) + { + _idleCallManager = idleCallManager; + } + + /// + /// Subscribe deferred action to Idling event to run it whenever Revit becomes idle. + /// + /// Action to call whenever the host app becomes Idle. + /// some events in host app are triggered many times, we might get 10x per object + /// Making this more like a deferred action, so we don't update the UI many times + public void SubscribeToIdle(string id, Action action) + { + _idleCallManager.SubscribeToIdle(id, action, AddEvent); + } + + /// + public void SubscribeToIdle(string id, Func asyncAction) + { + _idleCallManager.SubscribeToIdle(id, asyncAction, AddEvent); + } + + protected abstract void AddEvent(); +} diff --git a/DUI3/Speckle.Connectors.DUI/Bridge/BrowserBridge.cs b/DUI3/Speckle.Connectors.DUI/Bridge/BrowserBridge.cs index d610e1e01..376070bfc 100644 --- a/DUI3/Speckle.Connectors.DUI/Bridge/BrowserBridge.cs +++ b/DUI3/Speckle.Connectors.DUI/Bridge/BrowserBridge.cs @@ -114,14 +114,14 @@ public void AssociateWithBinding(IBinding binding) private async Task OnActionBlock(RunMethodArgs args) { Result result = await TopLevelExceptionHandler - .CatchUnhandled(async () => await ExecuteMethod(args.MethodName, args.MethodArgs).ConfigureAwait(false)) + .CatchUnhandledAsync(async () => await ExecuteMethod(args.MethodName, args.MethodArgs).ConfigureAwait(false)) .ConfigureAwait(false); string resultJson = result.IsSuccess ? JsonConvert.SerializeObject(result.Value, _serializerOptions) : SerializeFormattedException(result.Exception); - NotifyUIMethodCallResultReady(args.RequestId, resultJson); + await NotifyUIMethodCallResultReady(args.RequestId, resultJson).ConfigureAwait(false); } /// @@ -165,10 +165,6 @@ void Post() } } - /// - /// Run actions on main thread. - /// - /// Action to run on main thread. public void RunOnMainThread(Action action) { _mainThreadContext.Post( @@ -181,6 +177,40 @@ public void RunOnMainThread(Action action) ); } + public async Task RunOnMainThreadAsync(Func action) + { + await RunOnMainThreadAsync(async () => + { + await action.Invoke().ConfigureAwait(false); + return null; + }) + .ConfigureAwait(false); + } + + [SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "TaskCompletionSource")] + public Task RunOnMainThreadAsync(Func> action) + { + TaskCompletionSource tcs = new(); + + _mainThreadContext.Post( + async _ => + { + try + { + T result = await action.Invoke().ConfigureAwait(false); + tcs.SetResult(result); + } + catch (Exception ex) + { + tcs.SetException(ex); + } + }, + null + ); + + return tcs.Task; + } + /// /// Used by the action block to invoke the actual method called by the UI. /// @@ -272,11 +302,15 @@ private string SerializeFormattedException(Exception e) /// /// /// - private void NotifyUIMethodCallResultReady(string requestId, string? serializedData = null) + private async Task NotifyUIMethodCallResultReady( + string requestId, + string? serializedData = null, + CancellationToken cancellationToken = default + ) { _resultsStore[requestId] = serializedData; string script = $"{FrontendBoundName}.responseReady('{requestId}')"; - _browserScriptExecutor.ExecuteScriptAsyncMethod(script); + await _browserScriptExecutor.ExecuteScriptAsyncMethod(script, cancellationToken).ConfigureAwait(false); } /// @@ -310,7 +344,7 @@ public void OpenUrl(string url) Process.Start(new ProcessStartInfo { FileName = url, UseShellExecute = true }); } - public void Send(string eventName) + public async Task Send(string eventName, CancellationToken cancellationToken = default) { if (_binding is null) { @@ -319,10 +353,10 @@ public void Send(string eventName) var script = $"{FrontendBoundName}.emit('{eventName}')"; - _browserScriptExecutor.ExecuteScriptAsyncMethod(script); + await _browserScriptExecutor.ExecuteScriptAsyncMethod(script, cancellationToken).ConfigureAwait(false); } - public void Send(string eventName, T data) + public async Task Send(string eventName, T data, CancellationToken cancellationToken = default) where T : class { if (_binding is null) @@ -334,6 +368,6 @@ public void Send(string eventName, T data) string requestId = $"{Guid.NewGuid()}_{eventName}"; _resultsStore[requestId] = payload; var script = $"{FrontendBoundName}.emitResponseReady('{eventName}', '{requestId}')"; - _browserScriptExecutor.ExecuteScriptAsyncMethod(script); + await _browserScriptExecutor.ExecuteScriptAsyncMethod(script, cancellationToken).ConfigureAwait(false); } } diff --git a/DUI3/Speckle.Connectors.DUI/Bridge/IBrowserBridge.cs b/DUI3/Speckle.Connectors.DUI/Bridge/IBrowserBridge.cs index 41075f246..7bd772978 100644 --- a/DUI3/Speckle.Connectors.DUI/Bridge/IBrowserBridge.cs +++ b/DUI3/Speckle.Connectors.DUI/Bridge/IBrowserBridge.cs @@ -30,24 +30,30 @@ public interface IBrowserBridge public void RunMethod(string methodName, string requestId, string args); /// - /// Run actions on main thread. + /// Posts an onto the main thread /// Some applications might need to run some operations on main thread as deferred actions. /// - /// - /// Exceptions will be caught by - /// - /// Action to run on main thread. - public void RunOnMainThread(Action action); + /// An awaitable + /// Action to run on the main thread + public Task RunOnMainThreadAsync(Func> action); + + /// + /// Posts an onto the main thread + /// Some applications might need to run some operations on main thread as deferred actions. + /// + /// An awaitable + /// Action to run on the main thread + public Task RunOnMainThreadAsync(Func action); /// /// Bridge was not initialized with a binding - public void Send(string eventName); + public Task Send(string eventName, CancellationToken cancellationToken = default); - /// + /// /// data to store /// /// Bridge was not initialized with a binding - public void Send(string eventName, T data) + public Task Send(string eventName, T data, CancellationToken cancellationToken = default) where T : class; public ITopLevelExceptionHandler TopLevelExceptionHandler { get; } diff --git a/DUI3/Speckle.Connectors.DUI/Bridge/IBrowserScriptExecutor.cs b/DUI3/Speckle.Connectors.DUI/Bridge/IBrowserScriptExecutor.cs index 6f3e79c34..6c3fb69f9 100644 --- a/DUI3/Speckle.Connectors.DUI/Bridge/IBrowserScriptExecutor.cs +++ b/DUI3/Speckle.Connectors.DUI/Bridge/IBrowserScriptExecutor.cs @@ -4,7 +4,7 @@ public interface IBrowserScriptExecutor { /// thrown when is /// The (constant string) script to execute on the browser - public void ExecuteScriptAsyncMethod(string script); + public Task ExecuteScriptAsyncMethod(string script, CancellationToken cancellationToken); public bool IsBrowserInitialized { get; } diff --git a/DUI3/Speckle.Connectors.DUI/Bridge/IdleCallManager.cs b/DUI3/Speckle.Connectors.DUI/Bridge/IdleCallManager.cs index 2336ae7b0..0b3e83504 100644 --- a/DUI3/Speckle.Connectors.DUI/Bridge/IdleCallManager.cs +++ b/DUI3/Speckle.Connectors.DUI/Bridge/IdleCallManager.cs @@ -6,14 +6,15 @@ namespace Speckle.Connectors.DUI.Bridge; public interface IIdleCallManager { void SubscribeToIdle(string id, Action action, Action addEvent); + void SubscribeToIdle(string id, Func asyncAction, Action addEvent); void AppOnIdle(Action removeEvent); } //should be registered as singleton [SuppressMessage("ReSharper", "InconsistentlySynchronizedField")] -public class IdleCallManager : IIdleCallManager +public sealed class IdleCallManager : IIdleCallManager { - public ConcurrentDictionary Calls { get; } = new(); + internal ConcurrentDictionary> Calls { get; } = new(); private readonly object _lock = new(); public bool IdleSubscriptionCalled { get; private set; } @@ -26,9 +27,20 @@ public IdleCallManager(ITopLevelExceptionHandler topLevelExceptionHandler) } public void SubscribeToIdle(string id, Action action, Action addEvent) => - _topLevelExceptionHandler.CatchUnhandled(() => SubscribeInternal(id, action, addEvent)); + SubscribeToIdle( + id, + () => + { + action.Invoke(); + return Task.CompletedTask; + }, + addEvent + ); + + public void SubscribeToIdle(string id, Func asyncAction, Action addEvent) => + _topLevelExceptionHandler.CatchUnhandled(() => SubscribeInternal(id, asyncAction, addEvent)); - public void SubscribeInternal(string id, Action action, Action addEvent) + internal void SubscribeInternal(string id, Func action, Action addEvent) { Calls.TryAdd(id, action); if (!IdleSubscriptionCalled) @@ -45,13 +57,13 @@ public void SubscribeInternal(string id, Action action, Action addEvent) } public void AppOnIdle(Action removeEvent) => - _topLevelExceptionHandler.CatchUnhandled(() => AppOnIdleInternal(removeEvent)); + _topLevelExceptionHandler.FireAndForget(async () => await AppOnIdleInternal(removeEvent).ConfigureAwait(false)); - public void AppOnIdleInternal(Action removeEvent) + internal async Task AppOnIdleInternal(Action removeEvent) { - foreach (KeyValuePair kvp in Calls) + foreach (KeyValuePair> kvp in Calls) { - _topLevelExceptionHandler.CatchUnhandled(() => kvp.Value.Invoke()); + await _topLevelExceptionHandler.CatchUnhandledAsync(kvp.Value).ConfigureAwait(false); } Calls.Clear(); diff --git a/DUI3/Speckle.Connectors.DUI/Bridge/SyncToCurrentThread.cs b/DUI3/Speckle.Connectors.DUI/Bridge/SyncToCurrentThread.cs deleted file mode 100644 index eaac8b7b3..000000000 --- a/DUI3/Speckle.Connectors.DUI/Bridge/SyncToCurrentThread.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Speckle.Connectors.Common.Operations; - -namespace Speckle.Connectors.DUI.Bridge; - -/// -/// Implements the interface and runs a given function on the current thread using Task.Run. -/// -public class SyncToCurrentThread : ISyncToThread -{ - /// - /// Executes a given function on the current thread using Task.Run. - /// - /// The return type of the function. - /// The function to execute. - /// A Task object representing the asynchronous operation. - public Task RunOnThread(Func func) => Task.FromResult(func.Invoke()); -} diff --git a/DUI3/Speckle.Connectors.DUI/Bridge/SyncToUIThread.cs b/DUI3/Speckle.Connectors.DUI/Bridge/SyncToUIThread.cs index 3ae77f62f..bb7bbe0c3 100644 --- a/DUI3/Speckle.Connectors.DUI/Bridge/SyncToUIThread.cs +++ b/DUI3/Speckle.Connectors.DUI/Bridge/SyncToUIThread.cs @@ -13,23 +13,6 @@ public SyncToUIThread(IBrowserBridge bridge) } [SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Task Completion Source")] - public Task RunOnThread(Func func) - { - TaskCompletionSource tcs = new(); - - _bridge.RunOnMainThread(() => - { - try - { - T result = func.Invoke(); - tcs.SetResult(result); - } - catch (Exception ex) - { - tcs.SetException(ex); - } - }); - - return tcs.Task; - } + public async Task RunOnThread(Func> func) => + await _bridge.RunOnMainThreadAsync(func).ConfigureAwait(false); } diff --git a/DUI3/Speckle.Connectors.DUI/Bridge/TopLevelExceptionHandler.cs b/DUI3/Speckle.Connectors.DUI/Bridge/TopLevelExceptionHandler.cs index 7c3f004ab..a39ff0bc2 100644 --- a/DUI3/Speckle.Connectors.DUI/Bridge/TopLevelExceptionHandler.cs +++ b/DUI3/Speckle.Connectors.DUI/Bridge/TopLevelExceptionHandler.cs @@ -41,43 +41,35 @@ internal TopLevelExceptionHandler(ILogger logger, IBro /// The function to invoke and provide error handling for /// will be rethrown, these should be allowed to bubble up to the host app /// - public Result CatchUnhandled(Action function) + public void CatchUnhandled(Action function) { - try - { - try - { - function.Invoke(); - return new(); - } - catch (Exception ex) when (!ex.IsFatal()) - { - _logger.LogError(ex, UNHANDLED_LOGGER_TEMPLATE); - - SetGlobalNotification( - ToastNotificationType.DANGER, - "Unhandled Exception Occured", - ex.ToFormattedString(), - false - ); - return new(ex); - } - } - catch (Exception ex) + _ = CatchUnhandled(() => { - _logger.LogCritical(ex, UNHANDLED_LOGGER_TEMPLATE); - throw; - } + function(); + return null; + }); } /// /// return type /// A result pattern struct (where exceptions have been handled) public Result CatchUnhandled(Func function) => - CatchUnhandled(() => Task.FromResult(function.Invoke())).Result; + CatchUnhandledAsync(() => Task.FromResult(function.Invoke())).Result; //Safe to do a .Result because this as an already completed and non-async Task from the Task.FromResult + + /// + /// A result pattern struct (where exceptions have been handled) + public async Task CatchUnhandledAsync(Func function) + { + _ = await CatchUnhandledAsync(async () => + { + await function().ConfigureAwait(false); + return null; + }) + .ConfigureAwait(false); + } /// - public async Task> CatchUnhandled(Func> function) + public async Task> CatchUnhandledAsync(Func> function) { try { @@ -88,12 +80,13 @@ public async Task> CatchUnhandled(Func> function) catch (Exception ex) when (!ex.IsFatal()) { _logger.LogError(ex, UNHANDLED_LOGGER_TEMPLATE); - SetGlobalNotification( - ToastNotificationType.DANGER, - "Unhandled Exception Occured", - ex.ToFormattedString(), - false - ); + await SetGlobalNotification( + ToastNotificationType.DANGER, + "Unhandled Exception Occured", + ex.ToFormattedString(), + false + ) + .ConfigureAwait(false); return new(ex); } } @@ -104,15 +97,28 @@ public async Task> CatchUnhandled(Func> function) } } - private void SetGlobalNotification(ToastNotificationType type, string title, string message, bool autoClose) => - Parent.Send( - BasicConnectorBindingCommands.SET_GLOBAL_NOTIFICATION, //TODO: We could move these constants into a DUI3 constants static class - new - { - type, - title, - description = message, - autoClose - } - ); + /// + /// Triggers an async action without explicitly needing to await it.
+ /// Any thrown by invoking will be handled by the
+ ///
+ /// + /// This function should only be used as an event handler that doesn't allow for handlers to return a + /// In cases where you can use keyword, you should prefer using + /// + /// + public async void FireAndForget(Func function) => await CatchUnhandledAsync(function).ConfigureAwait(false); + + private async Task SetGlobalNotification(ToastNotificationType type, string title, string message, bool autoClose) => + await Parent + .Send( + BasicConnectorBindingCommands.SET_GLOBAL_NOTIFICATION, //TODO: We could move these constants into a DUI3 constants static class + new + { + type, + title, + description = message, + autoClose + } + ) + .ConfigureAwait(false); } diff --git a/DUI3/Speckle.Connectors.DUI/packages.lock.json b/DUI3/Speckle.Connectors.DUI/packages.lock.json index 05acaaa5f..b88c79359 100644 --- a/DUI3/Speckle.Connectors.DUI/packages.lock.json +++ b/DUI3/Speckle.Connectors.DUI/packages.lock.json @@ -8,6 +8,15 @@ "resolved": "2.2.0", "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" }, + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3" + } + }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -41,9 +50,9 @@ }, "Speckle.Sdk": { "type": "Direct", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "YZLtitiVIxl12pNTXaB9gU+paNwFe++WBw6oIN6Rs5hFPialUgeKbpjh16Zq6wcptjBpvg0CHq29+d456QUAJQ==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "ulT3XNgqIQSmpQFK5YEEwRq4RA6emXBKQHnZxtCz37SJin1PnVpH9m8vOAF9FmaWsgSAfjhhjIZDruMu1oKxSg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", @@ -179,6 +188,11 @@ "resolved": "1.1.0", "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" }, + "Microsoft.NETFramework.ReferenceAssemblies.net461": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA==" + }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", @@ -311,8 +325,8 @@ "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )", - "Speckle.Sdk": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )", + "Speckle.Sdk": "[3.1.0-dev.162, )" } }, "speckle.connectors.logging": { @@ -341,11 +355,11 @@ }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "gpENXDB2i8SiXJZwrLVfB3BwKF1FHUSpPiw0BQ3Hl0V3821UWZjPYw5XK694oxJlVAeEMRvrMZMGsWOcK+3nMw==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "1fyaPjC1kVtroKfoXI7edJX0Uho/TNRWG9mtmIQ7jKhX1zElrJPSoH8zEIMqckZhGmwUrYjlQFPssASNCP721Q==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.145" + "Speckle.Sdk": "3.1.0-dev.162" } } } diff --git a/Directory.Build.props b/Directory.Build.props index 858d82c2e..858928479 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -10,7 +10,6 @@ true true false - false diff --git a/Directory.Build.targets b/Directory.Build.targets index 4ad4f99dc..cabf959a2 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -4,6 +4,7 @@ + @@ -17,6 +18,7 @@ + @@ -24,17 +26,11 @@ - - true - - - Program - $(ProgramW6432)\Autodesk\AutoCAD $(AutoCADVersion)\acad.exe - + @@ -42,32 +38,14 @@ - - true - - - Program - $(ProgramW6432)\Autodesk\AutoCAD $(Civil3DVersion)\acad.exe - + + + - - .rhp - $(ProgramFiles)\Rhino $(RhinoVersion)\System\Rhino.exe - - - .rhp - true - true - true - - - - - diff --git a/Directory.Packages.props b/Directory.Packages.props index 3b0a94aaa..88a142aae 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -20,23 +20,31 @@ + + + + - - - - + + + + + + + + diff --git a/Local.sln b/Local.sln index 7becde7cc..3e877e6fc 100644 --- a/Local.sln +++ b/Local.sln @@ -160,6 +160,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Connectors.Common", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Converters.Common.Tests", "Sdk\Speckle.Converters.Common.Tests\Speckle.Converters.Common.Tests.csproj", "{13225611-10EE-41BB-9198-C88D6E978DA6}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tekla", "Tekla", "{AB1AD13B-163E-45F8-8F96-52A921501FA0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Connector.Tekla2024", "Connectors\Tekla\Speckle.Connector.Tekla2024\Speckle.Connector.Tekla2024.csproj", "{8077B6A7-86BD-4E87-8B23-6D168D0E14D9}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Local|Any CPU = Local|Any CPU @@ -245,6 +249,8 @@ Global {3747C01B-343E-4425-A973-4D9D181BE468}.Local|Any CPU.Build.0 = Local|Any CPU {13225611-10EE-41BB-9198-C88D6E978DA6}.Local|Any CPU.ActiveCfg = Local|Any CPU {13225611-10EE-41BB-9198-C88D6E978DA6}.Local|Any CPU.Build.0 = Local|Any CPU + {8077B6A7-86BD-4E87-8B23-6D168D0E14D9}.Local|Any CPU.ActiveCfg = Debug|Any CPU + {8077B6A7-86BD-4E87-8B23-6D168D0E14D9}.Local|Any CPU.Build.0 = Debug|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -315,6 +321,8 @@ Global {38497468-6B28-4F8F-A9E0-3167E90DDD89} = {2E00592E-558D-492D-88F9-3ECEE4C0C7DA} {3747C01B-343E-4425-A973-4D9D181BE468} = {2E00592E-558D-492D-88F9-3ECEE4C0C7DA} {13225611-10EE-41BB-9198-C88D6E978DA6} = {2E00592E-558D-492D-88F9-3ECEE4C0C7DA} + {AB1AD13B-163E-45F8-8F96-52A921501FA0} = {42826721-9A18-4762-8BA9-F1429DD5C5B1} + {8077B6A7-86BD-4E87-8B23-6D168D0E14D9} = {AB1AD13B-163E-45F8-8F96-52A921501FA0} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {EE253116-7070-4E9A-BCE8-2911C251B8C8} diff --git a/Sdk/Speckle.Connectors.Common/Builders/IHostObjectBuilder.cs b/Sdk/Speckle.Connectors.Common/Builders/IHostObjectBuilder.cs index 9cfa1113b..7d9e150dc 100644 --- a/Sdk/Speckle.Connectors.Common/Builders/IHostObjectBuilder.cs +++ b/Sdk/Speckle.Connectors.Common/Builders/IHostObjectBuilder.cs @@ -1,4 +1,5 @@ -using Speckle.Connectors.Common.Conversion; +using Speckle.Connectors.Common.Conversion; +using Speckle.Connectors.Common.Operations; using Speckle.Sdk.Models; namespace Speckle.Connectors.Common.Builders; @@ -21,7 +22,7 @@ Task Build( Base rootObject, string projectName, string modelName, - Action? onOperationProgressed, + IProgress onOperationProgressed, CancellationToken cancellationToken ); } diff --git a/Sdk/Speckle.Connectors.Common/Builders/IRootObjectBuilder.cs b/Sdk/Speckle.Connectors.Common/Builders/IRootObjectBuilder.cs index f70f6437e..f3584c726 100644 --- a/Sdk/Speckle.Connectors.Common/Builders/IRootObjectBuilder.cs +++ b/Sdk/Speckle.Connectors.Common/Builders/IRootObjectBuilder.cs @@ -9,7 +9,7 @@ public interface IRootObjectBuilder public Task Build( IReadOnlyList objects, SendInfo sendInfo, - Action? onOperationProgressed = null, + IProgress onOperationProgressed, CancellationToken ct = default ); } diff --git a/Sdk/Speckle.Connectors.Common/Builders/TraversalExtensions.cs b/Sdk/Speckle.Connectors.Common/Builders/TraversalExtensions.cs deleted file mode 100644 index 68e50e178..000000000 --- a/Sdk/Speckle.Connectors.Common/Builders/TraversalExtensions.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Speckle.Sdk.Models; -using Speckle.Sdk.Models.GraphTraversal; - -namespace Speckle.Connectors.Common.Builders; - -public static class TraversalExtensions -{ - [Obsolete( - "Do not use - we're doing multi stage receives and this confuses things. Report progress as appropriate from the connector side.", - true - )] - public static IEnumerable TraverseWithProgress( - this GraphTraversal traversalFunction, - Base rootObject, - Action? onOperationProgressed, - CancellationToken cancellationToken = default - ) - { - var traversalGraph = traversalFunction.Traverse(rootObject).ToArray(); - int count = 0; - foreach (var tc in traversalGraph) - { - cancellationToken.ThrowIfCancellationRequested(); - - yield return tc; - - onOperationProgressed?.Invoke("Converting", (double)++count / traversalGraph.Length); - } - } -} diff --git a/Sdk/Speckle.Connectors.Common/Connector.cs b/Sdk/Speckle.Connectors.Common/Connector.cs index fbe971ac6..40c1fefc7 100644 --- a/Sdk/Speckle.Connectors.Common/Connector.cs +++ b/Sdk/Speckle.Connectors.Common/Connector.cs @@ -12,10 +12,19 @@ namespace Speckle.Connectors.Common; public static class Connector { + private sealed record LoggingDisposable(IDisposable Tracing, IDisposable Metrics) : IDisposable + { + public void Dispose() + { + Tracing.Dispose(); + Metrics.Dispose(); + } + } + public static readonly string TabName = "Speckle"; public static readonly string TabTitle = "Speckle (Beta)"; - public static IDisposable? Initialize( + public static IDisposable Initialize( this IServiceCollection serviceCollection, HostApplication application, HostAppVersion version @@ -23,36 +32,47 @@ HostAppVersion version { TypeLoader.Initialize(typeof(Base).Assembly, typeof(Point).Assembly); - var (logging, tracing) = Observability.Initialize( + var (logging, tracing, metrics) = Observability.Initialize( + application.Name + " " + HostApplications.GetVersion(version), application.Slug, - HostApplications.GetVersion(version), Assembly.GetExecutingAssembly().GetVersion(), - new( #if DEBUG || LOCAL - new SpeckleLogging(Console: true, MinimumLevel: SpeckleLogLevel.Debug), new SpeckleTracing(Console: false) + new( + new SpeckleLogging(Console: true, File: new(), MinimumLevel: SpeckleLogLevel.Debug), + new SpeckleTracing(Console: false), + new SpeckleMetrics(Console: false) + ) #else + new( new SpeckleLogging( Console: true, - Otel: new( - Endpoint: "https://seq-dev.speckle.systems/ingest/otlp/v1/logs", - Headers: new() { { "X-Seq-ApiKey", "y5YnBp12ZE1Czh4tzZWn" } } - ), + File: new(), + Otel: + [ + new( + Endpoint: "https://seq-dev.speckle.systems/ingest/otlp/v1/logs", + Headers: new() { { "X-Seq-ApiKey", "y5YnBp12ZE1Czh4tzZWn" } } + ) + ], MinimumLevel: SpeckleLogLevel.Warning ), new SpeckleTracing( Console: false, - Otel: new( - Endpoint: "https://seq-dev.speckle.systems/ingest/otlp/v1/traces", - Headers: new() { { "X-Seq-ApiKey", "y5YnBp12ZE1Czh4tzZWn" } } - ) + Otel: + [ + new( + Endpoint: "https://seq-dev.speckle.systems/ingest/otlp/v1/traces", + Headers: new() { { "X-Seq-ApiKey", "y5YnBp12ZE1Czh4tzZWn" } } + ) + ] ) -#endif ) +#endif ); serviceCollection.AddLogging(x => x.AddProvider(new SpeckleLogProvider(logging))); serviceCollection.AddSpeckleSdk(application, version, Assembly.GetExecutingAssembly().GetVersion()); serviceCollection.AddSingleton(); - return tracing; + return new LoggingDisposable(tracing, metrics); } } diff --git a/Sdk/Speckle.Connectors.Common/ConnectorActivityFactory.cs b/Sdk/Speckle.Connectors.Common/ConnectorActivityFactory.cs index ea1955793..417361f59 100644 --- a/Sdk/Speckle.Connectors.Common/ConnectorActivityFactory.cs +++ b/Sdk/Speckle.Connectors.Common/ConnectorActivityFactory.cs @@ -1,18 +1,22 @@ -using Speckle.Connectors.Logging; +using System.Runtime.CompilerServices; +using Speckle.Connectors.Logging; +using Speckle.Sdk; using Speckle.Sdk.Common; using Speckle.Sdk.Logging; namespace Speckle.Connectors.Common; -public sealed class ConnectorActivityFactory : ISdkActivityFactory, IDisposable +public sealed class ConnectorActivityFactory(ISpeckleApplication application) : ISdkActivityFactory, IDisposable { private readonly LoggingActivityFactory _loggingActivityFactory = new(); + public void SetTag(string key, object? value) => _loggingActivityFactory.SetTag(key, value); + public void Dispose() => _loggingActivityFactory.Dispose(); - public ISdkActivity? Start(string? name = default, string source = "") + public ISdkActivity? Start(string? name = default, [CallerMemberName] string source = "") { - var activity = _loggingActivityFactory?.Start(name, source); + var activity = _loggingActivityFactory.Start(application.ApplicationAndVersion + " " + (name ?? source)); if (activity is null) { return null; @@ -40,5 +44,7 @@ public void SetStatus(SdkActivityStatusCode code) => _ => throw new ArgumentOutOfRangeException(nameof(code), code, null) } ); + + public void InjectHeaders(Action header) => activity.InjectHeaders(header); } } diff --git a/Sdk/Speckle.Connectors.Common/ConnectorMetricsFactory.cs b/Sdk/Speckle.Connectors.Common/ConnectorMetricsFactory.cs new file mode 100644 index 000000000..c85a8ef37 --- /dev/null +++ b/Sdk/Speckle.Connectors.Common/ConnectorMetricsFactory.cs @@ -0,0 +1,34 @@ +using Speckle.Connectors.Logging; +using Speckle.Sdk.Logging; + +namespace Speckle.Connectors.Common; + +public sealed class ConnectorMetricsFactory : ISdkMetricsFactory, IDisposable +{ + private readonly LoggingMetricsFactory _loggingMetricsFactory = new(); + + public void Dispose() => _loggingMetricsFactory.Dispose(); + + public ISdkCounter CreateCounter(string name, string? unit = default, string? description = default) + where T : struct => new ConnectorCounter(_loggingMetricsFactory.CreateCounter(name, unit, description)); + + private readonly struct ConnectorCounter(LoggingCounter counter) : ISdkCounter + where T : struct + { + public void Add(T value) => counter.Add(value); + + public void Add(T value, KeyValuePair tag) => counter.Add(value, tag); + + public void Add(T value, KeyValuePair tag1, KeyValuePair tag2) => + counter.Add(value, tag1, tag2); + + public void Add( + T value, + KeyValuePair tag1, + KeyValuePair tag2, + KeyValuePair tag3 + ) => counter.Add(value, tag1, tag2, tag3); + + public void Add(T value, params KeyValuePair[] tags) => counter.Add(value, tags); + } +} diff --git a/Sdk/Speckle.Connectors.Common/Instances/IInstanceBaker.cs b/Sdk/Speckle.Connectors.Common/Instances/IInstanceBaker.cs index 6fa1dfa5b..fe6fa4af7 100644 --- a/Sdk/Speckle.Connectors.Common/Instances/IInstanceBaker.cs +++ b/Sdk/Speckle.Connectors.Common/Instances/IInstanceBaker.cs @@ -1,3 +1,4 @@ +using Speckle.Connectors.Common.Operations; using Speckle.Sdk.Models.Collections; using Speckle.Sdk.Models.Instances; @@ -13,11 +14,11 @@ public interface IInstanceBaker /// /// /// - BakeResult BakeInstances( - List<(Collection[] collectionPath, IInstanceComponent obj)> instanceComponents, + Task BakeInstances( + IReadOnlyCollection<(Collection[] collectionPath, IInstanceComponent obj)> instanceComponents, Dictionary applicationIdMap, string baseLayerName, - Action? onOperationProgressed + IProgress onOperationProgressed ); /// diff --git a/Sdk/Speckle.Connectors.Common/Instances/LocalToGlobalMap.cs b/Sdk/Speckle.Connectors.Common/Instances/LocalToGlobalMap.cs index dbc88aea2..9e20fc111 100644 --- a/Sdk/Speckle.Connectors.Common/Instances/LocalToGlobalMap.cs +++ b/Sdk/Speckle.Connectors.Common/Instances/LocalToGlobalMap.cs @@ -4,4 +4,19 @@ namespace Speckle.Connectors.Common.Instances; -public record LocalToGlobalMap(TraversalContext TraversalContext, Base AtomicObject, List Matrix); +// Note: this was changed to a class with mutable props as in revit we need to pre-transform curves (native revit scaling does not support curves). +// public record LocalToGlobalMap(TraversalContext TraversalContext, Base AtomicObject, List Matrix); + +public class LocalToGlobalMap +{ + public LocalToGlobalMap(TraversalContext traversalContext, Base atomicObject, List matrix) + { + TraversalContext = traversalContext; + AtomicObject = atomicObject; + Matrix = matrix; + } + + public TraversalContext TraversalContext { get; set; } + public Base AtomicObject { get; set; } + public List Matrix { get; set; } +} diff --git a/Sdk/Speckle.Connectors.Common/Operations/CardProgress.cs b/Sdk/Speckle.Connectors.Common/Operations/CardProgress.cs new file mode 100644 index 000000000..d1fbd4a8f --- /dev/null +++ b/Sdk/Speckle.Connectors.Common/Operations/CardProgress.cs @@ -0,0 +1,3 @@ +namespace Speckle.Connectors.Common.Operations; + +public readonly record struct CardProgress(string Status, double? Progress); diff --git a/Sdk/Speckle.Connectors.Common/Operations/ISyncToThread.cs b/Sdk/Speckle.Connectors.Common/Operations/ISyncToThread.cs index 795d928d9..2440e642f 100644 --- a/Sdk/Speckle.Connectors.Common/Operations/ISyncToThread.cs +++ b/Sdk/Speckle.Connectors.Common/Operations/ISyncToThread.cs @@ -2,5 +2,5 @@ public interface ISyncToThread { - public Task RunOnThread(Func func); + public Task RunOnThread(Func> func); } diff --git a/Sdk/Speckle.Connectors.Common/Operations/PassthroughProgress.cs b/Sdk/Speckle.Connectors.Common/Operations/PassthroughProgress.cs new file mode 100644 index 000000000..b21833e02 --- /dev/null +++ b/Sdk/Speckle.Connectors.Common/Operations/PassthroughProgress.cs @@ -0,0 +1,8 @@ +using Speckle.Sdk.Transports; + +namespace Speckle.Connectors.Common.Operations; + +public class PassthroughProgress(Action progressCallback) : IProgress +{ + public void Report(ProgressArgs value) => progressCallback.Invoke(value); +} diff --git a/Sdk/Speckle.Connectors.Common/Operations/ProxyKeys.cs b/Sdk/Speckle.Connectors.Common/Operations/ProxyKeys.cs index f5ad1b9d3..a0abff694 100644 --- a/Sdk/Speckle.Connectors.Common/Operations/ProxyKeys.cs +++ b/Sdk/Speckle.Connectors.Common/Operations/ProxyKeys.cs @@ -1,4 +1,4 @@ -namespace Speckle.Connectors.Common.Operations; +namespace Speckle.Connectors.Common.Operations; public static class ProxyKeys { @@ -7,4 +7,5 @@ public static class ProxyKeys public const string INSTANCE_DEFINITION = "instanceDefinitionProxies"; public const string GROUP = "groupProxies"; public const string PARAMETER_DEFINITIONS = "parameterDefinitions"; + public const string PROPERTYSET_DEFINITIONS = "propertySetDefinitions"; } diff --git a/Sdk/Speckle.Connectors.Common/Operations/ReceiveOperation.cs b/Sdk/Speckle.Connectors.Common/Operations/ReceiveOperation.cs index 575cd070d..a680d64f1 100644 --- a/Sdk/Speckle.Connectors.Common/Operations/ReceiveOperation.cs +++ b/Sdk/Speckle.Connectors.Common/Operations/ReceiveOperation.cs @@ -1,8 +1,10 @@ using Speckle.Connectors.Common.Builders; +using Speckle.Connectors.Logging; using Speckle.Sdk.Api; using Speckle.Sdk.Credentials; using Speckle.Sdk.Logging; using Speckle.Sdk.Models; +using Speckle.Sdk.Models.Extensions; using Speckle.Sdk.Transports; namespace Speckle.Connectors.Common.Operations; @@ -38,82 +40,72 @@ IClientFactory clientFactory public async Task Execute( ReceiveInfo receiveInfo, - CancellationToken cancellationToken, - Action? onOperationProgressed = null + IProgress onOperationProgressed, + CancellationToken cancellationToken ) { - using var execute = _activityFactory.Start(); - Speckle.Sdk.Api.GraphQL.Models.Version? version; - Base? commitObject; - HostObjectBuilderResult? res; + using var execute = _activityFactory.Start("Receive Operation"); + execute?.SetTag("receiveInfo", receiveInfo); // 2 - Check account exist Account account = _accountService.GetAccountWithServerUrlFallback(receiveInfo.AccountId, receiveInfo.ServerUrl); using Client apiClient = _clientFactory.Create(account); + using var userScope = ActivityScope.SetTag(Consts.USER_ID, account.GetHashedEmail()); - using (var _ = _activityFactory.Start("Receive version")) - { - version = await apiClient - .Version.Get(receiveInfo.SelectedVersionId, receiveInfo.ModelId, receiveInfo.ProjectId, cancellationToken) - .ConfigureAwait(false); - } - - int totalCount = 1; + var version = await apiClient + .Version.Get(receiveInfo.SelectedVersionId, receiveInfo.ModelId, receiveInfo.ProjectId, cancellationToken) + .ConfigureAwait(false); using var transport = _serverTransportFactory.Create(account, receiveInfo.ProjectId); - using (var _ = _activityFactory.Start("Receive objects")) - { - _progressDisplayManager.Begin(); - commitObject = await _operations - .Receive( - version.referencedObject, - transport, - onProgressAction: dict => + + _progressDisplayManager.Begin(); + Base? commitObject = await _operations + .Receive( + version.referencedObject, + transport, + onProgressAction: new PassthroughProgress(args => + { + if (!_progressDisplayManager.ShouldUpdate()) { - if (!_progressDisplayManager.ShouldUpdate()) - { - return; - } - // NOTE: this looks weird for the user, as when deserialization kicks in, the progress bar will go down, and then start progressing again. - // This is something we're happy to live with until we refactor the whole receive pipeline. - var args = dict.FirstOrDefault(); - if (args is null) - { - return; - } - switch (args.ProgressEvent) - { - case ProgressEvent.DownloadBytes: - onOperationProgressed?.Invoke( + return; + } + + switch (args.ProgressEvent) + { + case ProgressEvent.DownloadBytes: //TODO: OnOperationProgress is not awaited here. + onOperationProgressed.Report( + new( $"Downloading ({_progressDisplayManager.CalculateSpeed(args)})", _progressDisplayManager.CalculatePercentage(args) - ); - break; - case ProgressEvent.DownloadObject: - onOperationProgressed?.Invoke("Downloading Root Object...", null); - break; - case ProgressEvent.DeserializeObject: - onOperationProgressed?.Invoke( + ) + ); + break; + case ProgressEvent.DownloadObject: + onOperationProgressed.Report(new("Downloading Root Object...", null)); + break; + case ProgressEvent.DeserializeObject: + onOperationProgressed.Report( + new( $"Deserializing ({_progressDisplayManager.CalculateSpeed(args)})", _progressDisplayManager.CalculatePercentage(args) - ); - break; - } - }, - onTotalChildrenCountKnown: c => totalCount = c, - cancellationToken: cancellationToken - ) - .ConfigureAwait(false); + ) + ); + break; + } + }), + cancellationToken: cancellationToken + ) + .ConfigureAwait(false); - cancellationToken.ThrowIfCancellationRequested(); - } + cancellationToken.ThrowIfCancellationRequested(); // 4 - Convert objects - using (var _ = _activityFactory.Start("Convert")) - { - res = await _hostObjectBuilder - .Build(commitObject, receiveInfo.ProjectName, receiveInfo.ModelName, onOperationProgressed, cancellationToken) - .ConfigureAwait(false); - } + HostObjectBuilderResult? res = await ConvertObjects( + commitObject, + receiveInfo, + onOperationProgressed, + cancellationToken + ) + .ConfigureAwait(false); await apiClient .Version.Received(new(version.id, receiveInfo.ProjectId, receiveInfo.SourceApplication), cancellationToken) @@ -121,4 +113,35 @@ await apiClient return res; } + + private async Task ConvertObjects( + Base commitObject, + ReceiveInfo receiveInfo, + IProgress onOperationProgressed, + CancellationToken cancellationToken + ) + { + using var conversionActivity = _activityFactory.Start("ReceiveOperation.ConvertObjects"); + conversionActivity?.SetTag("smellsLikeV2Data", commitObject.SmellsLikeV2Data()); + conversionActivity?.SetTag("receiveInfo.serverUrl", receiveInfo.ServerUrl); + conversionActivity?.SetTag("receiveInfo.projectId", receiveInfo.ProjectId); + conversionActivity?.SetTag("receiveInfo.modelId", receiveInfo.ModelId); + conversionActivity?.SetTag("receiveInfo.selectedVersionId", receiveInfo.SelectedVersionId); + conversionActivity?.SetTag("receiveInfo.sourceApplication", receiveInfo.SourceApplication); + + try + { + HostObjectBuilderResult res = await _hostObjectBuilder + .Build(commitObject, receiveInfo.ProjectName, receiveInfo.ModelName, onOperationProgressed, cancellationToken) + .ConfigureAwait(false); + conversionActivity?.SetStatus(SdkActivityStatusCode.Ok); + return res; + } + catch (Exception ex) + { + conversionActivity?.RecordException(ex); + conversionActivity?.SetStatus(SdkActivityStatusCode.Error); + throw; + } + } } diff --git a/Sdk/Speckle.Connectors.Common/Operations/RootObjectSender.cs b/Sdk/Speckle.Connectors.Common/Operations/RootObjectSender.cs index 23f6337cc..cffee904c 100644 --- a/Sdk/Speckle.Connectors.Common/Operations/RootObjectSender.cs +++ b/Sdk/Speckle.Connectors.Common/Operations/RootObjectSender.cs @@ -1,8 +1,10 @@ using Speckle.Connectors.Common.Caching; +using Speckle.Connectors.Logging; using Speckle.InterfaceGenerator; using Speckle.Sdk.Api; using Speckle.Sdk.Api.GraphQL.Inputs; using Speckle.Sdk.Credentials; +using Speckle.Sdk.Logging; using Speckle.Sdk.Models; using Speckle.Sdk.Transports; @@ -24,6 +26,7 @@ public sealed class RootObjectSender : IRootObjectSender private readonly IProgressDisplayManager _progressDisplayManager; private readonly IOperations _operations; private readonly IClientFactory _clientFactory; + private readonly ISdkActivityFactory _activityFactory; public RootObjectSender( IServerTransportFactory transportFactory, @@ -31,7 +34,8 @@ public RootObjectSender( AccountService accountService, IProgressDisplayManager progressDisplayManager, IOperations operations, - IClientFactory clientFactory + IClientFactory clientFactory, + ISdkActivityFactory activityFactory ) { _transportFactory = transportFactory; @@ -40,6 +44,7 @@ IClientFactory clientFactory _progressDisplayManager = progressDisplayManager; _operations = operations; _clientFactory = clientFactory; + _activityFactory = activityFactory; } /// @@ -50,15 +55,17 @@ IClientFactory clientFactory public async Task<(string rootObjId, IReadOnlyDictionary convertedReferences)> Send( Base commitObject, SendInfo sendInfo, - Action? onOperationProgressed = null, + IProgress onOperationProgressed, CancellationToken ct = default ) { ct.ThrowIfCancellationRequested(); - onOperationProgressed?.Invoke("Uploading...", null); + onOperationProgressed.Report(new("Uploading...", null)); Account account = _accountService.GetAccountWithServerUrlFallback(sendInfo.AccountId, sendInfo.ServerUrl); + using var userScope = ActivityScope.SetTag(Consts.USER_ID, account.GetHashedEmail()); + using var activity = _activityFactory.Start("SendOperation"); using var transport = _transportFactory.Create(account, sendInfo.ProjectId, 60, null); @@ -68,40 +75,36 @@ IClientFactory clientFactory commitObject, transport, true, - onProgressAction: dict => + onProgressAction: new PassthroughProgress(args => { if (!_progressDisplayManager.ShouldUpdate()) { return; } - // NOTE: this looks weird for the user, as when deserialization kicks in, the progress bar will go down, and then start progressing again. - // This is something we're happy to live with until we refactor the whole receive pipeline. - var args = dict.FirstOrDefault(); - if (args is null) - { - return; - } - switch (args.ProgressEvent) { - case ProgressEvent.UploadBytes: - onOperationProgressed?.Invoke( - $"Uploading ({_progressDisplayManager.CalculateSpeed(args)})", - _progressDisplayManager.CalculatePercentage(args) + case ProgressEvent.UploadBytes: //TODO: These progress calls are not awaited + onOperationProgressed.Report( + new( + $"Uploading ({_progressDisplayManager.CalculateSpeed(args)})", + _progressDisplayManager.CalculatePercentage(args) + ) ); break; case ProgressEvent.UploadObject: - onOperationProgressed?.Invoke("Uploading Root Object...", null); + onOperationProgressed.Report(new("Uploading Root Object...", null)); break; case ProgressEvent.SerializeObject: - onOperationProgressed?.Invoke( - $"Serializing ({_progressDisplayManager.CalculateSpeed(args)})", - _progressDisplayManager.CalculatePercentage(args) + onOperationProgressed.Report( + new( + $"Serializing ({_progressDisplayManager.CalculateSpeed(args)})", + _progressDisplayManager.CalculatePercentage(args) + ) ); break; } - }, + }), ct ) .ConfigureAwait(false); @@ -110,7 +113,7 @@ IClientFactory clientFactory ct.ThrowIfCancellationRequested(); - onOperationProgressed?.Invoke("Linking version to model...", null); + onOperationProgressed.Report(new("Linking version to model...", null)); // 8 - Create the version (commit) using var apiClient = _clientFactory.Create(account); diff --git a/Sdk/Speckle.Connectors.Common/Operations/SendOperation.cs b/Sdk/Speckle.Connectors.Common/Operations/SendOperation.cs index 52b6fa277..7ca24667d 100644 --- a/Sdk/Speckle.Connectors.Common/Operations/SendOperation.cs +++ b/Sdk/Speckle.Connectors.Common/Operations/SendOperation.cs @@ -1,6 +1,5 @@ using Speckle.Connectors.Common.Builders; using Speckle.Connectors.Common.Conversion; -using Speckle.Sdk.Logging; using Speckle.Sdk.Models; namespace Speckle.Connectors.Common.Operations; @@ -9,27 +8,20 @@ public sealed class SendOperation { private readonly IRootObjectBuilder _rootObjectBuilder; private readonly IRootObjectSender _baseObjectSender; - private readonly ISdkActivityFactory _activityFactory; - public SendOperation( - IRootObjectBuilder rootObjectBuilder, - IRootObjectSender baseObjectSender, - ISdkActivityFactory activityFactory - ) + public SendOperation(IRootObjectBuilder rootObjectBuilder, IRootObjectSender baseObjectSender) { _rootObjectBuilder = rootObjectBuilder; _baseObjectSender = baseObjectSender; - _activityFactory = activityFactory; } public async Task Execute( IReadOnlyList objects, SendInfo sendInfo, - Action? onOperationProgressed = null, + IProgress onOperationProgressed, CancellationToken ct = default ) { - using var activity = _activityFactory.Start("SendOperation"); var buildResult = await _rootObjectBuilder .Build(objects, sendInfo, onOperationProgressed, ct) .ConfigureAwait(false); diff --git a/Sdk/Speckle.Connectors.Common/Speckle.Connectors.Common.csproj b/Sdk/Speckle.Connectors.Common/Speckle.Connectors.Common.csproj index c59c169e3..3b90f2d22 100644 --- a/Sdk/Speckle.Connectors.Common/Speckle.Connectors.Common.csproj +++ b/Sdk/Speckle.Connectors.Common/Speckle.Connectors.Common.csproj @@ -12,7 +12,7 @@ - + diff --git a/Sdk/Speckle.Connectors.Common/SpeckleLogProvider.cs b/Sdk/Speckle.Connectors.Common/SpeckleLogProvider.cs index 1c0848b62..c78a1290a 100644 --- a/Sdk/Speckle.Connectors.Common/SpeckleLogProvider.cs +++ b/Sdk/Speckle.Connectors.Common/SpeckleLogProvider.cs @@ -3,9 +3,9 @@ namespace Speckle.Connectors.Common; -public sealed class SpeckleLogProvider(Logger speckleLogger) : ILoggerProvider +public sealed class SpeckleLogProvider(LoggerProvider speckleLogger) : ILoggerProvider { public void Dispose() { } - public ILogger CreateLogger(string categoryName) => new SpeckleLogger(speckleLogger); + public ILogger CreateLogger(string categoryName) => new SpeckleLogger(speckleLogger.CreateLogger(categoryName)); } diff --git a/Sdk/Speckle.Connectors.Common/SpeckleLogger.cs b/Sdk/Speckle.Connectors.Common/SpeckleLogger.cs index 13c714c3c..140cf4b27 100644 --- a/Sdk/Speckle.Connectors.Common/SpeckleLogger.cs +++ b/Sdk/Speckle.Connectors.Common/SpeckleLogger.cs @@ -16,22 +16,22 @@ public void Log( switch (logLevel) { case LogLevel.Critical: - logger.Write(SpeckleLogLevel.Fatal, exception, formatter(state, exception)); + logger.Write(SpeckleLogLevel.Fatal, eventId.Id, state, exception, formatter); break; case LogLevel.Trace: - logger.Write(SpeckleLogLevel.Verbose, exception, formatter(state, exception)); + logger.Write(SpeckleLogLevel.Verbose, eventId.Id, state, exception, formatter); break; case LogLevel.Debug: - logger.Write(SpeckleLogLevel.Debug, exception, formatter(state, exception)); + logger.Write(SpeckleLogLevel.Debug, eventId.Id, state, exception, formatter); break; case LogLevel.Information: - logger.Write(SpeckleLogLevel.Information, exception, formatter(state, exception)); + logger.Write(SpeckleLogLevel.Information, eventId.Id, state, exception, formatter); break; case LogLevel.Warning: - logger.Write(SpeckleLogLevel.Warning, exception, formatter(state, exception)); + logger.Write(SpeckleLogLevel.Warning, eventId.Id, state, exception, formatter); break; case LogLevel.Error: - logger.Write(SpeckleLogLevel.Error, exception, formatter(state, exception)); + logger.Write(SpeckleLogLevel.Error, eventId.Id, state, exception, formatter); break; case LogLevel.None: default: diff --git a/Sdk/Speckle.Connectors.Common/packages.lock.json b/Sdk/Speckle.Connectors.Common/packages.lock.json index f8cbfc650..5b10bc71e 100644 --- a/Sdk/Speckle.Connectors.Common/packages.lock.json +++ b/Sdk/Speckle.Connectors.Common/packages.lock.json @@ -11,6 +11,15 @@ "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0" } }, + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3" + } + }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -44,18 +53,18 @@ }, "Speckle.Objects": { "type": "Direct", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "gpENXDB2i8SiXJZwrLVfB3BwKF1FHUSpPiw0BQ3Hl0V3821UWZjPYw5XK694oxJlVAeEMRvrMZMGsWOcK+3nMw==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "1fyaPjC1kVtroKfoXI7edJX0Uho/TNRWG9mtmIQ7jKhX1zElrJPSoH8zEIMqckZhGmwUrYjlQFPssASNCP721Q==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.145" + "Speckle.Sdk": "3.1.0-dev.162" } }, "Speckle.Sdk": { "type": "Direct", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "YZLtitiVIxl12pNTXaB9gU+paNwFe++WBw6oIN6Rs5hFPialUgeKbpjh16Zq6wcptjBpvg0CHq29+d456QUAJQ==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "ulT3XNgqIQSmpQFK5YEEwRq4RA6emXBKQHnZxtCz37SJin1PnVpH9m8vOAF9FmaWsgSAfjhhjIZDruMu1oKxSg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", @@ -185,6 +194,11 @@ "resolved": "1.1.0", "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" }, + "Microsoft.NETFramework.ReferenceAssemblies.net461": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA==" + }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", diff --git a/Sdk/Speckle.Connectors.Logging/ActivityScopeActivityProcessor.cs b/Sdk/Speckle.Connectors.Logging/ActivityScopeActivityProcessor.cs new file mode 100644 index 000000000..33d205bd1 --- /dev/null +++ b/Sdk/Speckle.Connectors.Logging/ActivityScopeActivityProcessor.cs @@ -0,0 +1,15 @@ +using System.Diagnostics; +using OpenTelemetry; + +namespace Speckle.Connectors.Logging; + +internal sealed class ActivityScopeActivityProcessor : BaseProcessor +{ + public override void OnEnd(Activity data) + { + foreach (KeyValuePair keyValuePair in ActivityScope.Tags) + { + data.SetTag(keyValuePair.Key, keyValuePair.Value); + } + } +} diff --git a/Sdk/Speckle.Connectors.Logging/ActivityScopeExtensions.cs b/Sdk/Speckle.Connectors.Logging/ActivityScopeExtensions.cs new file mode 100644 index 000000000..2cb549fef --- /dev/null +++ b/Sdk/Speckle.Connectors.Logging/ActivityScopeExtensions.cs @@ -0,0 +1,21 @@ +namespace Speckle.Connectors.Logging; + +public static class ActivityScope +{ + private static readonly AsyncLocal> s_tags = new() { Value = new() }; + public static IReadOnlyDictionary Tags => s_tags.Value ?? []; + public static IReadOnlyList> TagsList { get; } = + new List>(s_tags.Value); + + public static IDisposable SetTag(string key, string value) + { + s_tags.Value ??= new(); + s_tags.Value[key] = value; + return new TagScope(key); + } + + private sealed class TagScope(string key) : IDisposable + { + public void Dispose() => s_tags.Value.Remove(key); + } +} diff --git a/Sdk/Speckle.Connectors.Logging/ActivityScopeLogProcessor.cs b/Sdk/Speckle.Connectors.Logging/ActivityScopeLogProcessor.cs new file mode 100644 index 000000000..857785a43 --- /dev/null +++ b/Sdk/Speckle.Connectors.Logging/ActivityScopeLogProcessor.cs @@ -0,0 +1,22 @@ +using OpenTelemetry; +using OpenTelemetry.Logs; + +namespace Speckle.Connectors.Logging; + +internal sealed class ActivityScopeLogProcessor : BaseProcessor +{ + public override void OnEnd(LogRecord data) + { + if (ActivityScope.Tags.Count > 0) + { + if (data.Attributes is null) + { + data.Attributes = ActivityScope.TagsList; + } + else if (data.Attributes.Count > 0) + { + data.Attributes = data.Attributes.Concat(ActivityScope.Tags).ToList(); + } + } + } +} diff --git a/Sdk/Speckle.Connectors.Logging/Consts.cs b/Sdk/Speckle.Connectors.Logging/Consts.cs index 1dcb0ae6f..53d9bda08 100644 --- a/Sdk/Speckle.Connectors.Logging/Consts.cs +++ b/Sdk/Speckle.Connectors.Logging/Consts.cs @@ -1,3 +1,5 @@ +using System.Reflection; + namespace Speckle.Connectors.Logging; public static class Consts @@ -8,4 +10,28 @@ public static class Consts public const string OS_TYPE = "os.type"; public const string OS_SLUG = "os.slug"; public const string RUNTIME_NAME = "runtime.name"; + public const string USER_ID = "user.id"; + public const string TRACING_SOURCE = "speckle"; + + public static string GetPackageVersion(Assembly assembly) + { + // MinVer https://github.com/adamralph/minver?tab=readme-ov-file#version-numbers + // together with Microsoft.SourceLink.GitHub https://github.com/dotnet/sourcelink + // fills AssemblyInformationalVersionAttribute by + // {majorVersion}.{minorVersion}.{patchVersion}.{pre-release label}.{pre-release version}.{gitHeight}+{Git SHA of current commit} + // Ex: 1.5.0-alpha.1.40+807f703e1b4d9874a92bd86d9f2d4ebe5b5d52e4 + // The following parts are optional: pre-release label, pre-release version, git height, Git SHA of current commit + // For package version, value of AssemblyInformationalVersionAttribute without commit hash is returned. + + var informationalVersion = assembly + .GetCustomAttribute() + ?.InformationalVersion; + if (informationalVersion is null) + { + return string.Empty; + } + + var indexOfPlusSign = informationalVersion.IndexOf('+'); + return indexOfPlusSign > 0 ? informationalVersion[..indexOfPlusSign] : informationalVersion; + } } diff --git a/Sdk/Speckle.Connectors.Logging/Internal/LogBuilder.cs b/Sdk/Speckle.Connectors.Logging/Internal/LogBuilder.cs index b53ab41b5..cb7fd96fd 100644 --- a/Sdk/Speckle.Connectors.Logging/Internal/LogBuilder.cs +++ b/Sdk/Speckle.Connectors.Logging/Internal/LogBuilder.cs @@ -1,104 +1,101 @@ -using System.Diagnostics; -using System.Reflection; -using System.Runtime.InteropServices; +using System.Text; +using Microsoft.Extensions.Logging; +using OpenTelemetry.Exporter; +using OpenTelemetry.Logs; using OpenTelemetry.Resources; using Serilog; using Serilog.Exceptions; -using Serilog.Sinks.OpenTelemetry; namespace Speckle.Connectors.Logging.Internal; internal static class LogBuilder { - public static Logger Initialize( + public static LoggerProvider Initialize( string applicationAndVersion, string connectorVersion, SpeckleLogging? speckleLogging, ResourceBuilder resourceBuilder ) { - var fileVersionInfo = GetFileVersionInfo(); - var serilogLogConfiguration = new LoggerConfiguration() - .MinimumLevel.Is(SpeckleLogLevelUtility.GetLevel(speckleLogging?.MinimumLevel ?? SpeckleLogLevel.Warning)) - .Enrich.FromLogContext() - .Enrich.WithProperty("version", fileVersionInfo.FileVersion) - .Enrich.WithProperty("productVersion", connectorVersion) - .Enrich.WithProperty("hostOs", DetermineHostOsSlug()) - .Enrich.WithProperty("hostOsVersion", Environment.OSVersion) - .Enrich.WithProperty("hostOsArchitecture", RuntimeInformation.ProcessArchitecture.ToString()) - .Enrich.WithProperty("runtime", RuntimeInformation.FrameworkDescription) - .Enrich.WithExceptionDetails(); - - if (speckleLogging?.File is not null) - { - // TODO: check if we have write permissions to the file. - var logFilePath = SpecklePathProvider.LogFolderPath(applicationAndVersion); - logFilePath = Path.Combine(logFilePath, speckleLogging.File.Path ?? "SpeckleCoreLog.txt"); - serilogLogConfiguration = serilogLogConfiguration.WriteTo.File( - logFilePath, - rollingInterval: RollingInterval.Day, - retainedFileCountLimit: 10 - ); - } - - if (speckleLogging?.Console ?? false) + var factory = LoggerFactory.Create(loggingBuilder => { - serilogLogConfiguration = serilogLogConfiguration.WriteTo.Console(); - } + if (speckleLogging?.File is not null || speckleLogging?.Console is not null) + { + var serilogLogConfiguration = new LoggerConfiguration() + .MinimumLevel.Is(SpeckleLogLevelUtility.GetLevel(speckleLogging.MinimumLevel)) + .Enrich.FromLogContext() + .Enrich.WithExceptionDetails(); - if (speckleLogging?.Otel is not null) - { - serilogLogConfiguration = InitializeOtelLogging(serilogLogConfiguration, speckleLogging.Otel, resourceBuilder); - } - var logger = serilogLogConfiguration.CreateLogger(); - - logger - .ForContext("hostApplication", applicationAndVersion) - .ForContext("userApplicationDataPath", SpecklePathProvider.UserApplicationDataPath()) - .ForContext("installApplicationDataPath", SpecklePathProvider.InstallApplicationDataPath) - .Information( - "Initialized logger inside {hostApplication}/{productVersion}/{version} for user {id}. Path info {userApplicationDataPath} {installApplicationDataPath}." - ); - - return new Logger(logger); - } + if (speckleLogging.File is not null) + { + // TODO: check if we have write permissions to the file. + var logFilePath = SpecklePathProvider.LogFolderPath(applicationAndVersion); + logFilePath = Path.Combine(logFilePath, speckleLogging.File.Path ?? "SpeckleCoreLog.txt"); + serilogLogConfiguration = serilogLogConfiguration.WriteTo.File( + logFilePath, + rollingInterval: RollingInterval.Day, + retainedFileCountLimit: 10 + ); + } - private static FileVersionInfo GetFileVersionInfo() - { - var assembly = Assembly.GetExecutingAssembly().Location; - return FileVersionInfo.GetVersionInfo(assembly); - } + if (speckleLogging.Console) + { + serilogLogConfiguration.WriteTo.Console(); + } - private static string DetermineHostOsSlug() - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - return "Windows"; - } + var serilogLogger = serilogLogConfiguration.CreateLogger(); + if (speckleLogging.File is not null) + { + serilogLogger + .ForContext("applicationAndVersion", applicationAndVersion) + .ForContext("connectorVersion", connectorVersion) + .ForContext("userApplicationDataPath", SpecklePathProvider.UserApplicationDataPath()) + .ForContext("installApplicationDataPath", SpecklePathProvider.InstallApplicationDataPath) + .Information( + "Initialized logger inside {applicationAndVersion}/{connectorVersion}. Path info {userApplicationDataPath} {installApplicationDataPath}." + ); + } - if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - { - return "MacOS"; - } + loggingBuilder.AddSerilog(serilogLogger); + } - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - return "Linux"; - } + foreach (var otel in speckleLogging?.Otel ?? []) + { + InitializeOtelLogging(loggingBuilder, otel, resourceBuilder); + } + }); - return RuntimeInformation.OSDescription; + return new LoggerProvider(factory); } - private static LoggerConfiguration InitializeOtelLogging( - LoggerConfiguration serilogLogConfiguration, + private static void InitializeOtelLogging( + ILoggingBuilder loggingBuilder, SpeckleOtelLogging speckleOtelLogging, ResourceBuilder resourceBuilder ) => - serilogLogConfiguration.WriteTo.OpenTelemetry(o => + loggingBuilder.AddOpenTelemetry(x => { - o.Protocol = OtlpProtocol.HttpProtobuf; - o.LogsEndpoint = speckleOtelLogging.Endpoint; - o.Headers = speckleOtelLogging.Headers ?? o.Headers; - o.ResourceAttributes = resourceBuilder.Build().Attributes.ToDictionary(x => x.Key, x => x.Value); + x.AddOtlpExporter(y => + { + y.Protocol = OtlpExportProtocol.HttpProtobuf; + y.Endpoint = new Uri(speckleOtelLogging.Endpoint); + var sb = new StringBuilder(); + bool appendSemicolon = false; + foreach (var kvp in speckleOtelLogging.Headers ?? []) + { + sb.Append(kvp.Key).Append('=').Append(kvp.Value); + if (appendSemicolon) + { + sb.Append(','); + } + else + { + appendSemicolon = true; + } + } + y.Headers = sb.ToString(); + }) + .AddProcessor(new ActivityScopeLogProcessor()) + .SetResourceBuilder(resourceBuilder); }); } diff --git a/Sdk/Speckle.Connectors.Logging/Internal/MetricsBuilder.cs b/Sdk/Speckle.Connectors.Logging/Internal/MetricsBuilder.cs new file mode 100644 index 000000000..daf109aaa --- /dev/null +++ b/Sdk/Speckle.Connectors.Logging/Internal/MetricsBuilder.cs @@ -0,0 +1,42 @@ +using OpenTelemetry.Exporter; +using OpenTelemetry.Metrics; +using OpenTelemetry.Resources; +using OpenTelemetry.Trace; + +namespace Speckle.Connectors.Logging.Internal; + +internal static class MetricsBuilder +{ + public static IDisposable Initialize(SpeckleMetrics? metricsConfiguration, ResourceBuilder resourceBuilder) + { + var metricsProviderBuilder = OpenTelemetry.Sdk.CreateMeterProviderBuilder().AddMeter(Consts.TRACING_SOURCE); + foreach (var metrics in metricsConfiguration?.Otel ?? []) + { + metricsProviderBuilder = metricsProviderBuilder.AddOtlpExporter(x => ProcessOptions(metrics, x)); + } + + if (metricsConfiguration?.Console ?? false) + { + metricsProviderBuilder = metricsProviderBuilder.AddConsoleExporter(); + } + + metricsProviderBuilder = metricsProviderBuilder.AddHttpClientInstrumentation().SetResourceBuilder(resourceBuilder); + + return metricsProviderBuilder.Build(); + } + + private static void ProcessOptions(SpeckleOtelMetrics metrics, OtlpExporterOptions options) + { + options.Protocol = OtlpExportProtocol.HttpProtobuf; + var headers = string.Join(",", metrics.Headers?.Select(x => x.Key + "=" + x.Value) ?? []); + if (headers.Length != 0) + { + options.Headers = headers; + } + + if (metrics.Endpoint is not null) + { + options.Endpoint = new Uri(metrics.Endpoint); + } + } +} diff --git a/Sdk/Speckle.Connectors.Logging/Internal/ResourceCreator.cs b/Sdk/Speckle.Connectors.Logging/Internal/ResourceCreator.cs index 7de798c48..22f2ab9b7 100644 --- a/Sdk/Speckle.Connectors.Logging/Internal/ResourceCreator.cs +++ b/Sdk/Speckle.Connectors.Logging/Internal/ResourceCreator.cs @@ -8,7 +8,7 @@ internal static class ResourceCreator internal static ResourceBuilder Create(string applicationAndVersion, string slug, string connectorVersion) => ResourceBuilder .CreateEmpty() - .AddService(serviceName: LoggingActivityFactory.TRACING_SOURCE, serviceVersion: connectorVersion) + .AddService(serviceName: Consts.TRACING_SOURCE, serviceVersion: connectorVersion) .AddAttributes( new List> { diff --git a/Sdk/Speckle.Connectors.Logging/Internal/TracingBuilder.cs b/Sdk/Speckle.Connectors.Logging/Internal/TracingBuilder.cs index 183d165fa..42eeb2655 100644 --- a/Sdk/Speckle.Connectors.Logging/Internal/TracingBuilder.cs +++ b/Sdk/Speckle.Connectors.Logging/Internal/TracingBuilder.cs @@ -6,46 +6,42 @@ namespace Speckle.Connectors.Logging.Internal; internal static class TracingBuilder { - public static IDisposable? Initialize(SpeckleTracing? logConfiguration, ResourceBuilder resourceBuilder) + public static IDisposable Initialize(SpeckleTracing? logConfiguration, ResourceBuilder resourceBuilder) { - var consoleEnabled = logConfiguration?.Console ?? false; - var otelEnabled = logConfiguration?.Otel?.Enabled ?? false; - if (!consoleEnabled && !otelEnabled) - { - return null; - } - var tracerProviderBuilder = OpenTelemetry .Sdk.CreateTracerProviderBuilder() - .AddSource(LoggingActivityFactory.TRACING_SOURCE); - tracerProviderBuilder = tracerProviderBuilder.AddHttpClientInstrumentation(); - if (otelEnabled) + .AddSource(Consts.TRACING_SOURCE) + .AddHttpClientInstrumentation(); + foreach (var tracing in logConfiguration?.Otel ?? []) { - tracerProviderBuilder = tracerProviderBuilder.AddOtlpExporter(x => ProcessOptions(logConfiguration!, x)); + tracerProviderBuilder = tracerProviderBuilder.AddOtlpExporter(x => ProcessOptions(tracing, x)); } - if (consoleEnabled) + if (logConfiguration?.Console ?? false) { tracerProviderBuilder = tracerProviderBuilder.AddConsoleExporter(); } - tracerProviderBuilder = tracerProviderBuilder.SetResourceBuilder(resourceBuilder).SetSampler(); + tracerProviderBuilder = tracerProviderBuilder + .SetResourceBuilder(resourceBuilder) + .SetSampler() + .AddProcessor(new ActivityScopeActivityProcessor()); return tracerProviderBuilder.Build(); } - private static void ProcessOptions(SpeckleTracing logConfiguration, OtlpExporterOptions options) + private static void ProcessOptions(SpeckleOtelTracing tracing, OtlpExporterOptions options) { options.Protocol = OtlpExportProtocol.HttpProtobuf; - var headers = string.Join(",", logConfiguration.Otel?.Headers?.Select(x => x.Key + "=" + x.Value) ?? []); + var headers = string.Join(",", tracing.Headers?.Select(x => x.Key + "=" + x.Value) ?? []); if (headers.Length != 0) { options.Headers = headers; } - if (logConfiguration.Otel?.Endpoint is not null) + if (tracing.Endpoint is not null) { - options.Endpoint = new Uri(logConfiguration.Otel.Endpoint); + options.Endpoint = new Uri(tracing.Endpoint); } } } diff --git a/Sdk/Speckle.Connectors.Logging/LoggerProvider.cs b/Sdk/Speckle.Connectors.Logging/LoggerProvider.cs new file mode 100644 index 000000000..bf037bf46 --- /dev/null +++ b/Sdk/Speckle.Connectors.Logging/LoggerProvider.cs @@ -0,0 +1,14 @@ +using Microsoft.Extensions.Logging; + +namespace Speckle.Connectors.Logging; + +public sealed class LoggerProvider : IDisposable +{ + private readonly ILoggerFactory _provider; + + internal LoggerProvider(ILoggerFactory provider) => _provider = provider; + + public Logger CreateLogger(string categoryName) => new(_provider.CreateLogger(categoryName)); + + public void Dispose() => _provider.Dispose(); +} diff --git a/Sdk/Speckle.Connectors.Logging/LoggingActivity.cs b/Sdk/Speckle.Connectors.Logging/LoggingActivity.cs index 7fbe82a2e..d8e6073bf 100644 --- a/Sdk/Speckle.Connectors.Logging/LoggingActivity.cs +++ b/Sdk/Speckle.Connectors.Logging/LoggingActivity.cs @@ -30,4 +30,17 @@ public void SetStatus(LoggingActivityStatusCode code) => _ => throw new ArgumentOutOfRangeException(nameof(code), code, null) } ); + + public void InjectHeaders(Action header) => + DistributedContextPropagator.Current.Inject( + _activity, + header, + static (carrier, key, value) => + { + if (carrier is Action request) + { + request.Invoke(key, value); + } + } + ); } diff --git a/Sdk/Speckle.Connectors.Logging/LoggingActivityFactory.cs b/Sdk/Speckle.Connectors.Logging/LoggingActivityFactory.cs index dff87de43..82223f5b8 100644 --- a/Sdk/Speckle.Connectors.Logging/LoggingActivityFactory.cs +++ b/Sdk/Speckle.Connectors.Logging/LoggingActivityFactory.cs @@ -1,18 +1,21 @@ -using System.Diagnostics; +using System.Diagnostics; using System.Reflection; -using System.Runtime.CompilerServices; namespace Speckle.Connectors.Logging; public sealed class LoggingActivityFactory : IDisposable { - public const string TRACING_SOURCE = "speckle-connectors"; - private readonly ActivitySource? _activitySource = - new(TRACING_SOURCE, GetPackageVersion(Assembly.GetExecutingAssembly())); + private readonly ActivitySource _activitySource = + new(Consts.TRACING_SOURCE, Consts.GetPackageVersion(Assembly.GetExecutingAssembly())); - public LoggingActivity? Start(string? name = null, [CallerMemberName] string source = "") + private readonly Dictionary _tags = new(); + + public void SetTag(string key, object? value) => _tags[key] = value; + + public LoggingActivity? Start(string name) { - var activity = _activitySource?.StartActivity(name ?? source, ActivityKind.Client); + //If you get a MissingManifestResourceException, Likely source or name is empty string, which is no good. + var activity = _activitySource.StartActivity(name: name, kind: ActivityKind.Client, tags: _tags); if (activity is null) { return null; @@ -20,27 +23,5 @@ public sealed class LoggingActivityFactory : IDisposable return new LoggingActivity(activity); } - public void Dispose() => _activitySource?.Dispose(); - - private static string GetPackageVersion(Assembly assembly) - { - // MinVer https://github.com/adamralph/minver?tab=readme-ov-file#version-numbers - // together with Microsoft.SourceLink.GitHub https://github.com/dotnet/sourcelink - // fills AssemblyInformationalVersionAttribute by - // {majorVersion}.{minorVersion}.{patchVersion}.{pre-release label}.{pre-release version}.{gitHeight}+{Git SHA of current commit} - // Ex: 1.5.0-alpha.1.40+807f703e1b4d9874a92bd86d9f2d4ebe5b5d52e4 - // The following parts are optional: pre-release label, pre-release version, git height, Git SHA of current commit - // For package version, value of AssemblyInformationalVersionAttribute without commit hash is returned. - - var informationalVersion = assembly - .GetCustomAttribute() - ?.InformationalVersion; - if (informationalVersion is null) - { - return String.Empty; - } - - var indexOfPlusSign = informationalVersion.IndexOf('+'); - return indexOfPlusSign > 0 ? informationalVersion[..indexOfPlusSign] : informationalVersion; - } + public void Dispose() => _activitySource.Dispose(); } diff --git a/Sdk/Speckle.Connectors.Logging/LoggingMetricsFactory.cs b/Sdk/Speckle.Connectors.Logging/LoggingMetricsFactory.cs new file mode 100644 index 000000000..0e66e11bc --- /dev/null +++ b/Sdk/Speckle.Connectors.Logging/LoggingMetricsFactory.cs @@ -0,0 +1,42 @@ +using System.Diagnostics.Metrics; +using System.Reflection; + +namespace Speckle.Connectors.Logging; + +public sealed class LoggingMetricsFactory : IDisposable +{ + private readonly Meter _meterSource = + new(Consts.TRACING_SOURCE, Consts.GetPackageVersion(Assembly.GetExecutingAssembly())); + + public LoggingCounter CreateCounter(string name, string? unit = null, string? description = null) + where T : struct => new(_meterSource.CreateCounter(name, unit, description)); + + public void Dispose() => _meterSource.Dispose(); +} + +public readonly struct LoggingCounter + where T : struct +{ + private readonly Counter _counter; + + internal LoggingCounter(Counter counter) + { + _counter = counter; + } + + public void Add(T value) => _counter.Add(value); + + public void Add(T value, KeyValuePair tag) => _counter.Add(value, tag); + + public void Add(T value, KeyValuePair tag1, KeyValuePair tag2) => + _counter.Add(value, tag1, tag2); + + public void Add( + T value, + KeyValuePair tag1, + KeyValuePair tag2, + KeyValuePair tag3 + ) => _counter.Add(value, tag1, tag2, tag3); + + public void Add(T value, params KeyValuePair[] tags) => _counter.Add(value, tags); +} diff --git a/Sdk/Speckle.Connectors.Logging/Observability.cs b/Sdk/Speckle.Connectors.Logging/Observability.cs index 2a1db2c92..59405aed4 100644 --- a/Sdk/Speckle.Connectors.Logging/Observability.cs +++ b/Sdk/Speckle.Connectors.Logging/Observability.cs @@ -4,7 +4,7 @@ namespace Speckle.Connectors.Logging; public static class Observability { - public static (Logger, IDisposable?) Initialize( + public static (LoggerProvider, IDisposable, IDisposable) Initialize( string applicationAndVersion, string slug, string connectorVersion, @@ -19,6 +19,7 @@ SpeckleObservability observability resourceBuilder ); var tracing = TracingBuilder.Initialize(observability.Tracing, resourceBuilder); - return (logging, tracing); + var metrics = MetricsBuilder.Initialize(observability.Metrics, resourceBuilder); + return (logging, tracing, metrics); } } diff --git a/Sdk/Speckle.Connectors.Logging/Speckle.Connectors.Logging.csproj b/Sdk/Speckle.Connectors.Logging/Speckle.Connectors.Logging.csproj index 379d32e88..e3100f21b 100644 --- a/Sdk/Speckle.Connectors.Logging/Speckle.Connectors.Logging.csproj +++ b/Sdk/Speckle.Connectors.Logging/Speckle.Connectors.Logging.csproj @@ -18,8 +18,8 @@ + - diff --git a/Sdk/Speckle.Connectors.Logging/SpeckleLogger.cs b/Sdk/Speckle.Connectors.Logging/SpeckleLogger.cs index 5360221e8..6015138a0 100644 --- a/Sdk/Speckle.Connectors.Logging/SpeckleLogger.cs +++ b/Sdk/Speckle.Connectors.Logging/SpeckleLogger.cs @@ -1,58 +1,26 @@ -using Serilog.Events; +using Microsoft.Extensions.Logging; namespace Speckle.Connectors.Logging; -public sealed class Logger +public sealed class Logger(ILogger logger) { - private readonly Serilog.ILogger _logger; - - public Logger(Serilog.ILogger logger) - { - _logger = logger; - } - - private static LogEventLevel GetLevel(SpeckleLogLevel speckleLogLevel) => + private static LogLevel GetLevel(SpeckleLogLevel speckleLogLevel) => speckleLogLevel switch { - SpeckleLogLevel.Debug => LogEventLevel.Debug, - SpeckleLogLevel.Verbose => LogEventLevel.Verbose, - SpeckleLogLevel.Information => LogEventLevel.Information, - SpeckleLogLevel.Warning => LogEventLevel.Warning, - SpeckleLogLevel.Error => LogEventLevel.Error, - SpeckleLogLevel.Fatal => LogEventLevel.Fatal, + SpeckleLogLevel.Debug => LogLevel.Debug, + SpeckleLogLevel.Verbose => LogLevel.Trace, + SpeckleLogLevel.Information => LogLevel.Information, + SpeckleLogLevel.Warning => LogLevel.Warning, + SpeckleLogLevel.Error => LogLevel.Error, + SpeckleLogLevel.Fatal => LogLevel.Critical, _ => throw new ArgumentOutOfRangeException(nameof(speckleLogLevel), speckleLogLevel, null) }; - public void Write(SpeckleLogLevel speckleLogLevel, string message, params object?[] arguments) => - _logger.Write(GetLevel(speckleLogLevel), message, arguments); - - public void Write( + public void Write( SpeckleLogLevel speckleLogLevel, + int eventId, + TState state, Exception? exception, - string message, - params object?[] arguments - ) => _logger.Write(GetLevel(speckleLogLevel), exception, message, arguments); - - public void Debug(string message, params object?[] arguments) => _logger.Debug(message, arguments); - - public void Debug(Exception? exception, string message, params object?[] arguments) => - _logger.Debug(exception, message, arguments); - - public void Warning(string message, params object?[] arguments) => _logger.Warning(message, arguments); - - public void Warning(Exception? exception, string message, params object?[] arguments) => - _logger.Warning(exception, message, arguments); - - public void Information(string message, params object?[] arguments) => _logger.Information(message, arguments); - - public void Information(Exception? exception, string message, params object?[] arguments) => - _logger.Information(exception, message, arguments); - - public void LogError(string message, params object?[] arguments) => _logger.Error(message, arguments); - - public void LogError(Exception? exception, string message, params object?[] arguments) => - _logger.Error(exception, message, arguments); - - public void Fatal(Exception? exception, string message, params object?[] arguments) => - _logger.Fatal(exception, message, arguments); + Func formatter + ) => logger.Log(GetLevel(speckleLogLevel), new EventId(eventId), state, exception, formatter); } diff --git a/Sdk/Speckle.Connectors.Logging/SpeckleObservability.cs b/Sdk/Speckle.Connectors.Logging/SpeckleObservability.cs index 9f98f5ebc..ba14a59ec 100644 --- a/Sdk/Speckle.Connectors.Logging/SpeckleObservability.cs +++ b/Sdk/Speckle.Connectors.Logging/SpeckleObservability.cs @@ -4,23 +4,56 @@ namespace Speckle.Connectors.Logging; /// Configuration object for the Speckle logging system. /// -public record SpeckleObservability(SpeckleLogging? Logging = null, SpeckleTracing? Tracing = null); +public record SpeckleObservability( + SpeckleLogging? Logging = null, + SpeckleTracing? Tracing = null, + SpeckleMetrics? Metrics = null +); public record SpeckleLogging( SpeckleLogLevel MinimumLevel = SpeckleLogLevel.Warning, bool Console = true, SpeckleFileLogging? File = null, - SpeckleOtelLogging? Otel = null -); + IEnumerable? Otel = null +) +{ + public SpeckleLogging( + SpeckleLogLevel minimumLevel = SpeckleLogLevel.Warning, + bool console = true, + SpeckleFileLogging? file = null, + SpeckleOtelLogging? otel = null + ) + : this(minimumLevel, console, file, otel is null ? null : [otel]) { } +} public record SpeckleFileLogging(string? Path = null, bool Enabled = true); -public record SpeckleOtelLogging(string Endpoint, bool Enabled = true, Dictionary? Headers = null); +public record SpeckleOtelLogging( + string? Endpoint = null, + bool Enabled = true, + Dictionary? Headers = null +); -public record SpeckleTracing(bool Console = false, SpeckleOtelTracing? Otel = null); +public record SpeckleTracing(bool Console = false, IEnumerable? Otel = null) +{ + public SpeckleTracing(bool console = true, SpeckleOtelTracing? otel = null) + : this(console, otel is null ? null : [otel]) { } +} public record SpeckleOtelTracing( string? Endpoint = null, bool Enabled = true, Dictionary? Headers = null ); + +public record SpeckleMetrics(bool Console = false, IEnumerable? Otel = null) +{ + public SpeckleMetrics(bool console = true, SpeckleOtelMetrics? otel = null) + : this(console, otel is null ? null : [otel]) { } +} + +public record SpeckleOtelMetrics( + string? Endpoint = null, + bool Enabled = true, + Dictionary? Headers = null +); diff --git a/Sdk/Speckle.Connectors.Logging/packages.lock.json b/Sdk/Speckle.Connectors.Logging/packages.lock.json index d0fa75701..21def2c52 100644 --- a/Sdk/Speckle.Connectors.Logging/packages.lock.json +++ b/Sdk/Speckle.Connectors.Logging/packages.lock.json @@ -11,6 +11,15 @@ "ILRepack": "2.0.33" } }, + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3" + } + }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -90,6 +99,16 @@ "System.Reflection.TypeExtensions": "4.7.0" } }, + "Serilog.Extensions.Logging": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "YEAMWu1UnWgf1c1KP85l1SgXGfiVo0Rz6x08pCiPOIBt2Qe18tcZLvdBUuV5o1QHvrs8FAry9wTIhgBRtjIlEg==", + "dependencies": { + "Microsoft.Extensions.Logging": "8.0.0", + "Serilog": "3.1.1" + } + }, "Serilog.Sinks.Console": { "type": "Direct", "requested": "[6.0.0, )", @@ -108,17 +127,6 @@ "Serilog": "4.0.0" } }, - "Serilog.Sinks.OpenTelemetry": { - "type": "Direct", - "requested": "[4.0.0, )", - "resolved": "4.0.0", - "contentHash": "M4WKDojg8a+msY5qhNGg5PYZ9ZVelcqipkP/CrfDAOvGCZ3MYj+cGXgnTanffYyIoC2onabaOtydqW6lqT/P/w==", - "dependencies": { - "Google.Protobuf": "3.26.1", - "Grpc.Net.Client": "2.62.0", - "Serilog": "4.0.0" - } - }, "Speckle.InterfaceGenerator": { "type": "Direct", "requested": "[0.9.6, )", @@ -127,8 +135,8 @@ }, "Google.Protobuf": { "type": "Transitive", - "resolved": "3.26.1", - "contentHash": "CHZX8zXqhF/fdUtd+AYzew8T2HFkAoe5c7lbGxZY/qryAlQXckDvM5BfOJjXlMS7kyICqQTMszj4w1bX5uBJ/w==", + "resolved": "3.22.5", + "contentHash": "tTMtDZPbLxJew8pk7NBdqhLqC4OipfkZdwPuCEUNr2AoDo1siUGcxFqJK0wDewTL8ge5Cjrb16CToMPxBUHMGA==", "dependencies": { "System.Memory": "4.5.3", "System.Runtime.CompilerServices.Unsafe": "4.5.2" @@ -153,30 +161,12 @@ }, "Grpc.Core.Api": { "type": "Transitive", - "resolved": "2.62.0", - "contentHash": "q4Jj6bRZHNnE4CMLqgjiBUCKLit+tRr0simZsS2W6U++akd7CzXByeKy2tddqT68hFzP2XzceXA2YtBTfWtixA==", + "resolved": "2.44.0", + "contentHash": "FBfPMvKwT8q98T8lWa5z6nBMLdH/Mmo5g4yyYYMvbXLWDzo4beqa7CUU5QH3PKvo2X6/b+UAZ2IymXlrYG3IXg==", "dependencies": { "System.Memory": "4.5.3" } }, - "Grpc.Net.Client": { - "type": "Transitive", - "resolved": "2.62.0", - "contentHash": "C7HxLt+wWPTpPFORRHkxxtDLL+K/jXSmZBaPLhFM8AEkN0bYjklIfCwnzajn1gcbRcEETBb0WnRgHJdVzpwbCg==", - "dependencies": { - "Grpc.Net.Common": "2.62.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "System.Diagnostics.DiagnosticSource": "6.0.1" - } - }, - "Grpc.Net.Common": { - "type": "Transitive", - "resolved": "2.62.0", - "contentHash": "eBv5I4RPWfdezGXqooU5hs3+XcfVMLk5XDlA4G/Nd9TMX78ZGrFl/lM1Ad187zgBLmH7WPAgfjKRWLBwaa1Wbw==", - "dependencies": { - "Grpc.Core.Api": "2.62.0" - } - }, "ILRepack": { "type": "Transitive", "resolved": "2.0.33", @@ -292,6 +282,11 @@ "resolved": "1.1.0", "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" }, + "Microsoft.NETFramework.ReferenceAssemblies.net461": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA==" + }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", diff --git a/Sdk/Speckle.Connectors.Tests/ActivityScopeTests.cs b/Sdk/Speckle.Connectors.Tests/ActivityScopeTests.cs new file mode 100644 index 000000000..3305d84f4 --- /dev/null +++ b/Sdk/Speckle.Connectors.Tests/ActivityScopeTests.cs @@ -0,0 +1,16 @@ +using FluentAssertions; +using NUnit.Framework; + +namespace Speckle.Connectors.Tests; + +public class ActivityScopeTests +{ + [Test] + public async Task TestAsyncLocal() + { + Logging.ActivityScope.SetTag("test", "me"); + await Task.Delay(10).ConfigureAwait(false); + Logging.ActivityScope.Tags.ContainsKey("test").Should().BeTrue(); + Logging.ActivityScope.Tags["test"].Should().Be("me"); + } +} diff --git a/Sdk/Speckle.Connectors.Tests/packages.lock.json b/Sdk/Speckle.Connectors.Tests/packages.lock.json index f16451831..89be98716 100644 --- a/Sdk/Speckle.Connectors.Tests/packages.lock.json +++ b/Sdk/Speckle.Connectors.Tests/packages.lock.json @@ -27,6 +27,15 @@ "Microsoft.TestPlatform.TestHost": "17.10.0" } }, + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3" + } + }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -195,6 +204,11 @@ "System.Runtime.CompilerServices.Unsafe": "4.5.1" } }, + "Microsoft.NETFramework.ReferenceAssemblies.net461": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA==" + }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", @@ -328,8 +342,8 @@ "dependencies": { "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", "Speckle.Connectors.Logging": "[1.0.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )", - "Speckle.Sdk": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )", + "Speckle.Sdk": "[3.1.0-dev.162, )" } }, "speckle.connectors.logging": { @@ -371,18 +385,18 @@ }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "gpENXDB2i8SiXJZwrLVfB3BwKF1FHUSpPiw0BQ3Hl0V3821UWZjPYw5XK694oxJlVAeEMRvrMZMGsWOcK+3nMw==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "1fyaPjC1kVtroKfoXI7edJX0Uho/TNRWG9mtmIQ7jKhX1zElrJPSoH8zEIMqckZhGmwUrYjlQFPssASNCP721Q==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.145" + "Speckle.Sdk": "3.1.0-dev.162" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "YZLtitiVIxl12pNTXaB9gU+paNwFe++WBw6oIN6Rs5hFPialUgeKbpjh16Zq6wcptjBpvg0CHq29+d456QUAJQ==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "ulT3XNgqIQSmpQFK5YEEwRq4RA6emXBKQHnZxtCz37SJin1PnVpH9m8vOAF9FmaWsgSAfjhhjIZDruMu1oKxSg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", diff --git a/Sdk/Speckle.Connectors.Utils/Operations/ReceiveOperation.cs b/Sdk/Speckle.Connectors.Utils/Operations/ReceiveOperation.cs new file mode 100644 index 000000000..e69de29bb diff --git a/Sdk/Speckle.Converters.Common.Tests/packages.lock.json b/Sdk/Speckle.Converters.Common.Tests/packages.lock.json index 3d9dc7029..f87ed6f29 100644 --- a/Sdk/Speckle.Converters.Common.Tests/packages.lock.json +++ b/Sdk/Speckle.Converters.Common.Tests/packages.lock.json @@ -36,6 +36,15 @@ "Microsoft.TestPlatform.TestHost": "17.10.0" } }, + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3" + } + }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -204,6 +213,11 @@ "System.Runtime.CompilerServices.Unsafe": "4.5.1" } }, + "Microsoft.NETFramework.ReferenceAssemblies.net461": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA==" + }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", @@ -336,7 +350,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )", - "Speckle.Objects": "[3.1.0-dev.145, )" + "Speckle.Objects": "[3.1.0-dev.162, )" } }, "speckle.testing": { @@ -366,18 +380,18 @@ }, "Speckle.Objects": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "gpENXDB2i8SiXJZwrLVfB3BwKF1FHUSpPiw0BQ3Hl0V3821UWZjPYw5XK694oxJlVAeEMRvrMZMGsWOcK+3nMw==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "1fyaPjC1kVtroKfoXI7edJX0Uho/TNRWG9mtmIQ7jKhX1zElrJPSoH8zEIMqckZhGmwUrYjlQFPssASNCP721Q==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.145" + "Speckle.Sdk": "3.1.0-dev.162" } }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "YZLtitiVIxl12pNTXaB9gU+paNwFe++WBw6oIN6Rs5hFPialUgeKbpjh16Zq6wcptjBpvg0CHq29+d456QUAJQ==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "ulT3XNgqIQSmpQFK5YEEwRq4RA6emXBKQHnZxtCz37SJin1PnVpH9m8vOAF9FmaWsgSAfjhhjIZDruMu1oKxSg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", diff --git a/Sdk/Speckle.Converters.Common/FileOps/TempFileProvider.cs b/Sdk/Speckle.Converters.Common/FileOps/TempFileProvider.cs new file mode 100644 index 000000000..6d627e151 --- /dev/null +++ b/Sdk/Speckle.Converters.Common/FileOps/TempFileProvider.cs @@ -0,0 +1,24 @@ +namespace Speckle.Converters.Common.FileOps; + +public static class TempFileProvider // note should be in connector, and connector should nuke its folder on startup +{ + public static string GetTempFile(string appSlug, string extension) + { + var folderPath = GetTempFolderPath(appSlug); + var filePath = Path.Combine(folderPath, $"{Guid.NewGuid():N}.{extension}"); + return filePath; + } + + public static void CleanTempFolder(string appSlug) // note, not used? + { + var folderPath = GetTempFolderPath(appSlug); + Directory.Delete(folderPath, true); + } + + private static string GetTempFolderPath(string appSlug) + { + var folderPath = Path.Combine(Path.GetTempPath(), "Speckle", appSlug); + Directory.CreateDirectory(folderPath); + return folderPath; + } +} diff --git a/Sdk/Speckle.Converters.Common/packages.lock.json b/Sdk/Speckle.Converters.Common/packages.lock.json index 2cf3d50e4..04a7d94ad 100644 --- a/Sdk/Speckle.Converters.Common/packages.lock.json +++ b/Sdk/Speckle.Converters.Common/packages.lock.json @@ -8,6 +8,15 @@ "resolved": "2.2.0", "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" }, + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3" + } + }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -41,11 +50,11 @@ }, "Speckle.Objects": { "type": "Direct", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "gpENXDB2i8SiXJZwrLVfB3BwKF1FHUSpPiw0BQ3Hl0V3821UWZjPYw5XK694oxJlVAeEMRvrMZMGsWOcK+3nMw==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "1fyaPjC1kVtroKfoXI7edJX0Uho/TNRWG9mtmIQ7jKhX1zElrJPSoH8zEIMqckZhGmwUrYjlQFPssASNCP721Q==", "dependencies": { - "Speckle.Sdk": "3.1.0-dev.145" + "Speckle.Sdk": "3.1.0-dev.162" } }, "GraphQL.Client": { @@ -164,6 +173,11 @@ "resolved": "1.1.0", "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" }, + "Microsoft.NETFramework.ReferenceAssemblies.net461": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA==" + }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", @@ -305,9 +319,9 @@ }, "Speckle.Sdk": { "type": "CentralTransitive", - "requested": "[3.1.0-dev.145, )", - "resolved": "3.1.0-dev.145", - "contentHash": "YZLtitiVIxl12pNTXaB9gU+paNwFe++WBw6oIN6Rs5hFPialUgeKbpjh16Zq6wcptjBpvg0CHq29+d456QUAJQ==", + "requested": "[3.1.0-dev.162, )", + "resolved": "3.1.0-dev.162", + "contentHash": "ulT3XNgqIQSmpQFK5YEEwRq4RA6emXBKQHnZxtCz37SJin1PnVpH9m8vOAF9FmaWsgSAfjhhjIZDruMu1oKxSg==", "dependencies": { "GraphQL.Client": "6.0.0", "Microsoft.CSharp": "4.7.0", diff --git a/Sdk/Speckle.Testing/packages.lock.json b/Sdk/Speckle.Testing/packages.lock.json index 3c8bf9004..f26206fc4 100644 --- a/Sdk/Speckle.Testing/packages.lock.json +++ b/Sdk/Speckle.Testing/packages.lock.json @@ -2,6 +2,15 @@ "version": 2, "dependencies": { "net8.0": { + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3" + } + }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -52,6 +61,11 @@ "resolved": "8.0.0", "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" }, + "Microsoft.NETFramework.ReferenceAssemblies.net461": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA==" + }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", diff --git a/Speckle.Connectors.sln b/Speckle.Connectors.sln index b08e0767a..7775ff145 100644 --- a/Speckle.Connectors.sln +++ b/Speckle.Connectors.sln @@ -155,8 +155,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connectors.Tests", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connectors.Logging", "Sdk\Speckle.Connectors.Logging\Speckle.Connectors.Logging.csproj", "{8098BAFC-DF1C-4AFA-A93E-08121E6D09D4}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tekla", "Tekla", "{696086E4-D8CC-4FE0-A9B3-5F10B9089B55}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connector.Tekla2024", "Connectors\Tekla\Speckle.Connector.Tekla2024\Speckle.Connector.Tekla2024.csproj", "{2319C00F-B268-4E4C-9F88-6B379E2BBD22}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Common.Tests", "Sdk\Speckle.Converters.Common.Tests\Speckle.Converters.Common.Tests.csproj", "{9EF292C6-1333-4502-AD9C-224D99847185}" EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Connectors.Civil3dShared", "Connectors\Autocad\Speckle.Connectors.Civil3dShared\Speckle.Connectors.Civil3dShared.shproj", "{A1B72EC1-5BC4-41FD-850F-5DA87DF49616}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -311,6 +317,10 @@ Global {8098BAFC-DF1C-4AFA-A93E-08121E6D09D4}.Debug|Any CPU.Build.0 = Debug|Any CPU {8098BAFC-DF1C-4AFA-A93E-08121E6D09D4}.Release|Any CPU.ActiveCfg = Release|Any CPU {8098BAFC-DF1C-4AFA-A93E-08121E6D09D4}.Release|Any CPU.Build.0 = Release|Any CPU + {2319C00F-B268-4E4C-9F88-6B379E2BBD22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2319C00F-B268-4E4C-9F88-6B379E2BBD22}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2319C00F-B268-4E4C-9F88-6B379E2BBD22}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2319C00F-B268-4E4C-9F88-6B379E2BBD22}.Release|Any CPU.Build.0 = Release|Any CPU {9EF292C6-1333-4502-AD9C-224D99847185}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9EF292C6-1333-4502-AD9C-224D99847185}.Debug|Any CPU.Build.0 = Debug|Any CPU {9EF292C6-1333-4502-AD9C-224D99847185}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -384,7 +394,10 @@ Global {9D66EDE4-AFC2-4F00-B40C-A7E878A2972F} = {B2BF1FAE-D0F4-4961-84CB-A00D3CABD236} {5B9A550A-9314-4E91-884E-E54960F589FB} = {2E00592E-558D-492D-88F9-3ECEE4C0C7DA} {8098BAFC-DF1C-4AFA-A93E-08121E6D09D4} = {2E00592E-558D-492D-88F9-3ECEE4C0C7DA} + {696086E4-D8CC-4FE0-A9B3-5F10B9089B55} = {42826721-9A18-4762-8BA9-F1429DD5C5B1} + {2319C00F-B268-4E4C-9F88-6B379E2BBD22} = {696086E4-D8CC-4FE0-A9B3-5F10B9089B55} {9EF292C6-1333-4502-AD9C-224D99847185} = {2E00592E-558D-492D-88F9-3ECEE4C0C7DA} + {A1B72EC1-5BC4-41FD-850F-5DA87DF49616} = {4721AA15-AF6E-4A62-A2C3-65564DC563E6} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {EE253116-7070-4E9A-BCE8-2911C251B8C8} @@ -416,6 +429,7 @@ Global Connectors\Autocad\Speckle.Connectors.AutocadShared\Speckle.Connectors.AutocadShared.projitems*{9166cc10-12e1-4a0f-916b-61f6f2004f5a}*SharedItemsImports = 5 Converters\Autocad\Speckle.Converters.AutocadShared\Speckle.Converters.AutocadShared.projitems*{9add1b7a-6401-4202-8613-f668e2fbc0a4}*SharedItemsImports = 13 Converters\Autocad\Speckle.Converters.AutocadShared\Speckle.Converters.AutocadShared.projitems*{9d66ede4-afc2-4f00-b40c-a7e878a2972f}*SharedItemsImports = 5 + Connectors\Autocad\Speckle.Connectors.Civil3dShared\Speckle.Connectors.Civil3dShared.projitems*{a1b72ec1-5bc4-41fd-850f-5da87df49616}*SharedItemsImports = 13 Connectors\Revit\Speckle.Connectors.RevitShared.Cef\Speckle.Connectors.RevitShared.Cef.projitems*{a6de3da0-b242-4f49-aef0-4e26af92d16c}*SharedItemsImports = 5 Connectors\Revit\Speckle.Connectors.RevitShared\Speckle.Connectors.RevitShared.projitems*{a6de3da0-b242-4f49-aef0-4e26af92d16c}*SharedItemsImports = 5 Connectors\Rhino\Speckle.Connectors.RhinoShared\Speckle.Connectors.RhinoShared.projitems*{a6e3a82f-4696-4d92-aba1-38aa80752067}*SharedItemsImports = 5 @@ -427,6 +441,7 @@ Global Converters\Revit\Speckle.Converters.RevitShared\Speckle.Converters.RevitShared.projitems*{c32274d9-1b66-4d5c-82f9-eb3f10f46752}*SharedItemsImports = 5 Connectors\Autocad\Speckle.Connectors.AutocadShared\Speckle.Connectors.AutocadShared.projitems*{c70ebb84-ba5b-4f2f-819e-25e0985ba13c}*SharedItemsImports = 5 Connectors\Autocad\Speckle.Connectors.AutocadShared\Speckle.Connectors.AutocadShared.projitems*{ca8eae01-ab9f-4ec1-b6f3-73721487e9e1}*SharedItemsImports = 5 + Connectors\Autocad\Speckle.Connectors.Civil3dShared\Speckle.Connectors.Civil3dShared.projitems*{ca8eae01-ab9f-4ec1-b6f3-73721487e9e1}*SharedItemsImports = 5 Converters\Revit\Speckle.Converters.RevitShared.Tests\Speckle.Converters.RevitShared.Tests.projitems*{d8069a23-ad2e-4c9e-8574-7e8c45296a46}*SharedItemsImports = 5 Converters\Revit\Speckle.Converters.RevitShared\Speckle.Converters.RevitShared.projitems*{d8069a23-ad2e-4c9e-8574-7e8c45296a46}*SharedItemsImports = 5 Connectors\Revit\Speckle.Connectors.RevitShared\Speckle.Connectors.RevitShared.projitems*{dc570fff-6fe5-47bd-8bc1-b471a6067786}*SharedItemsImports = 13