Initial commit from alpha

This commit is contained in:
Adam Hathcock
2024-07-04 11:56:34 +01:00
commit 60499e3a34
524 changed files with 36133 additions and 0 deletions
+12
View File
@@ -0,0 +1,12 @@
{
"version": 1,
"isRoot": true,
"tools": {
"csharpier": {
"version": "0.28.2",
"commands": [
"dotnet-csharpier"
]
}
}
}
+7
View File
@@ -0,0 +1,7 @@
printWidth: 120
useTabs: false
tabWidth: 2
preprocessorSymbolSets:
- ""
- "DEBUG"
- "DEBUG,CODE_STYLE"
+309
View File
@@ -0,0 +1,309 @@
root = true
# Don't use tabs for indentation.
[*]
indent_style = space
# Microsoft .NET properties
csharp_using_directive_placement = outside_namespace:silent
dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:none
dotnet_style_parentheses_in_other_binary_operators = never_if_unnecessary:none
dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:none
# Standard properties
insert_final_newline = true
# (Please don't specify an indent_size here; that has too many unintended consequences.)
# Code files
[*.{cs,csx,vb,vbx}]
indent_size = 2
charset = utf-8
# Xml project files
[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]
indent_size = 2
space_after_last_pi_attribute = false
# Xml config files
[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}]
indent_size = 2
space_after_last_pi_attribute = false
# JSON files
[*.json]
indent_size = 2
# Dotnet code style settings:
[*.{cs,vb}]
# Sort using and Import directives with System.* appearing first
dotnet_sort_system_directives_first = true
dotnet_separate_import_directive_groups = false
# Avoid "this." and "Me." if not necessary
dotnet_style_qualification_for_field = false:suggestion
dotnet_style_qualification_for_property = false:suggestion
dotnet_style_qualification_for_method = false:suggestion
dotnet_style_qualification_for_event = false:suggestion
# Use language keywords instead of framework type names for type references
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
dotnet_style_predefined_type_for_member_access = true:suggestion
# Parentheses preferences
dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:silent
dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:silent
dotnet_style_parentheses_in_other_binary_operators = never_if_unnecessary:silent
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
# Modifier preferences
dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
dotnet_style_readonly_field = true:suggestion
# Expression-level preferences
dotnet_style_object_initializer = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_explicit_tuple_names = true:suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent
dotnet_style_prefer_inferred_tuple_names = true:suggestion
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
dotnet_style_prefer_auto_properties = true:warning
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
dotnet_style_prefer_conditional_expression_over_return = true:silent
# CSharp code style settings:
[*.cs]
# Prefer "var" everywhere
csharp_style_var_elsewhere = false:none
csharp_style_var_for_built_in_types = false:none
csharp_style_var_when_type_is_apparent = false:none
# Prefer method-like constructs to have a block body
csharp_style_expression_bodied_methods = true:suggestion
csharp_style_expression_bodied_constructors = false:suggestion
csharp_style_expression_bodied_operators = true:suggestion
# Prefer property-like constructs to have an expression-body
csharp_style_expression_bodied_properties = true:suggestion
csharp_style_expression_bodied_indexers = true:suggestion
csharp_style_expression_bodied_accessors = true:suggestion
# Suggest more modern language features when available
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
csharp_style_inlined_variable_declaration = true:suggestion
csharp_style_throw_expression = true:suggestion
csharp_style_conditional_delegate_call = true:suggestion
csharp_style_namespace_declarations = file_scoped
# Newline settings
csharp_new_line_before_open_brace = all
csharp_new_line_before_else = true
csharp_new_line_before_catch = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_members_in_anonymous_types = true
# Space preferences
csharp_space_after_cast = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_around_binary_operators = before_and_after
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_between_method_call_empty_parameter_list_parentheses = false
# Wrapping preferences
csharp_preserve_single_line_statements = true
csharp_preserve_single_line_blocks = true
# SYMBOL NAMING RULES
# Copied from https://github.com/dotnet/roslyn/blob/main/.editorconfig
# Adapted rules:
# - Constants are ALL_UPPER
# - Non-private fields are PascalCase
# Non-private fields are PascalCase
dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.severity = warning
dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.symbols = non_private_readonly_fields
dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.style = non_private_readonly_field_style
dotnet_naming_symbols.non_private_readonly_fields.applicable_kinds = field
dotnet_naming_symbols.non_private_readonly_fields.applicable_accessibilities = public, protected, internal, protected_internal, private_protected
dotnet_naming_style.non_private_readonly_field_style.capitalization = pascal_case
# Constants are ALL_UPPER
dotnet_naming_rule.constants_should_be_all_upper.severity = warning
dotnet_naming_rule.constants_should_be_all_upper.symbols = constants
dotnet_naming_rule.constants_should_be_all_upper.style = constant_style
dotnet_naming_symbols.constants.applicable_kinds = field, local
dotnet_naming_symbols.constants.required_modifiers = const
dotnet_naming_style.constant_style.capitalization = all_upper
# Private static fields are camelCase and start with s_
dotnet_naming_rule.static_fields_should_be_camel_case.severity = warning
dotnet_naming_rule.static_fields_should_be_camel_case.symbols = static_fields
dotnet_naming_rule.static_fields_should_be_camel_case.style = static_field_style
dotnet_naming_symbols.static_fields.applicable_accessibilities = private
dotnet_naming_symbols.static_fields.applicable_kinds = field
dotnet_naming_symbols.static_fields.required_modifiers = static
dotnet_naming_style.static_field_style.capitalization = camel_case
dotnet_naming_style.static_field_style.required_prefix = s_
# Instance fields are camelCase and start with _
dotnet_naming_rule.instance_fields_should_be_camel_case.severity = warning
dotnet_naming_rule.instance_fields_should_be_camel_case.symbols = instance_fields
dotnet_naming_rule.instance_fields_should_be_camel_case.style = instance_field_style
dotnet_naming_symbols.instance_fields.applicable_kinds = field
dotnet_naming_style.instance_field_style.capitalization = camel_case
dotnet_naming_style.instance_field_style.required_prefix = _
# Locals and parameters are camelCase
dotnet_naming_rule.locals_should_be_camel_case.severity = warning
dotnet_naming_rule.locals_should_be_camel_case.symbols = locals_and_parameters
dotnet_naming_rule.locals_should_be_camel_case.style = camel_case_style
dotnet_naming_symbols.locals_and_parameters.applicable_kinds = parameter, local
dotnet_naming_style.camel_case_style.capitalization = camel_case
# Local functions are PascalCase
dotnet_naming_rule.local_functions_should_be_pascal_case.severity = warning
dotnet_naming_rule.local_functions_should_be_pascal_case.symbols = local_functions
dotnet_naming_rule.local_functions_should_be_pascal_case.style = local_function_style
dotnet_naming_symbols.local_functions.applicable_kinds = local_function
dotnet_naming_style.local_function_style.capitalization = pascal_case
# By default, name items with PascalCase
dotnet_naming_rule.members_should_be_pascal_case.severity = warning
dotnet_naming_rule.members_should_be_pascal_case.symbols = all_members
dotnet_naming_rule.members_should_be_pascal_case.style = pascal_case_style
dotnet_naming_symbols.all_members.applicable_kinds = *
dotnet_naming_style.pascal_case_style.capitalization = pascal_case
# Analyzer settings
dotnet_analyzer_diagnostic.category-Style.severity = warning # All rules will use this severity unless overriden
dotnet_diagnostic.ide0055.severity = none # Formatting rule: Incompatible with CSharpier
dotnet_diagnostic.ide0007.severity = none # Use var instead of explicit type: Preference
dotnet_diagnostic.ide0009.severity = none # Add this or Me qualification: Preference
dotnet_diagnostic.ide0200.severity = none # Remove unnecessary lambda expression: may be performance reasons not to
dotnet_diagnostic.ide0058.severity = none # Remove unnecessary expression value: Subjective
dotnet_diagnostic.ide0010.severity = none # Add missing cases to switch statement: Too verbose
dotnet_diagnostic.ide0200.severity = none # Remove unnecessary lambda expression: may be performance reasons not to
dotnet_diagnostic.ide0058.severity = none # Remove unnecessary expression value: Subjective
dotnet_diagnostic.ide0001.severity = suggestion # Name can be simplified: Non enforceable in build
dotnet_diagnostic.ide0046.severity = suggestion # Use conditional expression for return: Subjective
dotnet_diagnostic.ide0045.severity = suggestion # Use conditional expression for assignment: Subjective
dotnet_diagnostic.ide0078.severity = suggestion # Use pattern matching: Subjective
dotnet_diagnostic.ide0260.severity = suggestion # Use pattern matching: Subjective
dotnet_diagnostic.ide0022.severity = suggestion # Use expression body for method: Subjective
dotnet_diagnostic.ide0061.severity = suggestion # Use expression body for local functions: Subjective
dotnet_diagnostic.ide0063.severity = suggestion # Using directive can be simplified
dotnet_diagnostic.ide0066.severity = suggestion # Use switch expression: Subjective
dotnet_diagnostic.ide0029.severity = suggestion # Null check can be simplified: Subjective
dotnet_diagnostic.ide0030.severity = suggestion # Null check can be simplified: Subjective
dotnet_diagnostic.ide0270.severity = suggestion # Null check can be simplified: Subjective
dotnet_diagnostic.ide0042.severity = suggestion # Deconstruct variable declaration: Subjective
dotnet_diagnostic.ide0039.severity = suggestion # Use local function instead of lambda: Subjective
dotnet_diagnostic.ide0029.severity = suggestion # Null check can be simplified: Subjective
dotnet_diagnostic.ide0030.severity = suggestion # Null check can be simplified: Subjective
dotnet_diagnostic.ide0270.severity = suggestion # Null check can be simplified: Subjective
dotnet_diagnostic.ide0042.severity = suggestion # Deconstruct variable declaration: Subjective
dotnet_diagnostic.ide0028.severity = suggestion # Use collection initializers: Subjective
dotnet_diagnostic.ide0072.severity = suggestion # Populate switch statement: Subjective
dotnet_diagnostic.ide0074.severity = suggestion # Use compound assignment: Subjective
# Maintainability rules
dotnet_diagnostic.ca1501.severity = warning # Avoid excessive inheritance
dotnet_diagnostic.ca1502.severity = warning # Avoid excessive complexity
dotnet_diagnostic.ca1505.severity = warning # Avoid unmaintainable code
dotnet_diagnostic.ca1506.severity = warning # Avoid excessive class coupling
dotnet_diagnostic.ca1507.severity = warning # Use nameof in place of string
dotnet_diagnostic.ca1508.severity = warning # Avoid dead conditional code
dotnet_diagnostic.ca1509.severity = warning # Invalid entry in code metrics configuration file
dotnet_diagnostic.ca1861.severity = none # Prefer 'static readonly' fields over constant array arguments if the called method is called repeatedly and is not mutating the passed array (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1861)
dotnet_diagnostic.cs8618.severity = suggestion # nullable problem
dotnet_diagnostic.CS0809.severity = suggestion # obsolete errors
dotnet_diagnostic.CS0618.severity = suggestion # obsolete errors
# Performance rules
dotnet_diagnostic.ca1849.severity = suggestion # Call async methods when in an async method: May decrease performance
dotnet_diagnostic.ca1822.severity = suggestion # Mark member as static
dotnet_diagnostic.ca1859.severity = suggestion # Use concrete types when possible for improved performance
# Design rule
dotnet_diagnostic.ca1002.severity = suggestion # Do not expose generic lists
dotnet_diagnostic.ca1051.severity = warning # Do not declare visible instance fields
dotnet_diagnostic.ca1056.severity = suggestion # URI properties should not be strings
dotnet_diagnostic.ca1062.severity = none # Public method must check all parameters for null
# Naming
dotnet_diagnostic.ca1707.severity = none # Remove underscores in names
# Usage
dotnet_diagnostic.ca2227.severity = suggestion # Collection props should be read-only
dotnet_code_quality.ca1051.exclude_structs = true # CA1051 is excluded in structs
dotnet_code_quality.dispose_ownership_transfer_at_constructor = true # CA2000 has a lot of false positives without this
dotnet_code_quality.dispose_ownership_transfer_at_method_call = true # CA2000 has a lot of false positives without this
dotnet_code_quality.dispose_analysis_kind = NonExceptionPathsOnlyNotDisposed # CA2000 has a lot of false positives without this
# NUnit
dotnet_diagnostic.NUnit2001.severity = warning # Consider using Assert.That(expr, Is.False) instead of Assert.False(expr)
dotnet_diagnostic.NUnit2002.severity = warning # Consider using Assert.That(expr, Is.False) instead of Assert.IsFalse(expr)
dotnet_diagnostic.NUnit2003.severity = warning # Consider using Assert.That(expr, Is.True) instead of Assert.IsTrue(expr)
dotnet_diagnostic.NUnit2004.severity = warning # Consider using Assert.That(expr, Is.True) instead of Assert.True(expr)
dotnet_diagnostic.NUnit2005.severity = warning # Consider using Assert.That(actual, Is.EqualTo(expected)) instead of Assert.AreEqual(expected, actual)
dotnet_diagnostic.NUnit2006.severity = warning # Consider using Assert.That(actual, Is.Not.EqualTo(expected)) instead of Assert.AreNotEqual(expected, actual)
dotnet_diagnostic.NUnit2010.severity = warning # Use EqualConstraint for better assertion messages in case of failure
dotnet_diagnostic.NUnit2011.severity = warning # Use ContainsConstraint for better assertion messages in case of failure
dotnet_diagnostic.NUnit2011.severity = warning # Use StartsWithConstraint for better assertion messages in case of failure
dotnet_diagnostic.NUnit2011.severity = warning # Use EndsWithConstraint for better assertion messages in case of failure
dotnet_diagnostic.NUnit2014.severity = warning # Use SomeItemsConstraint for better assertion messages in case of failure
dotnet_diagnostic.NUnit2015.severity = warning # Consider using Assert.That(actual, Is.SameAs(expected)) instead of Assert.AreSame(expected, actual)
dotnet_diagnostic.NUnit2016.severity = warning # Consider using Assert.That(expr, Is.Null) instead of Assert.Null(expr)
dotnet_diagnostic.NUnit2017.severity = warning # Consider using Assert.That(expr, Is.Null) instead of Assert.IsNull(expr)
dotnet_diagnostic.NUnit2018.severity = warning # Consider using Assert.That(expr, Is.Not.Null) instead of Assert.NotNull(expr)
dotnet_diagnostic.NUnit2028.severity = warning # Consider using Assert.That(actual, Is.GreaterThanOrEqualTo(expected)) instead of Assert.GreaterOrEqual(actual, expected)
dotnet_diagnostic.NUnit2027.severity = warning # Consider using Assert.That(actual, Is.GreaterThan(expected)) instead of Assert.Greater(actual, expected)
dotnet_diagnostic.NUnit2029.severity = warning # Consider using Assert.That(actual, Is.LessThan(expected)) instead of Assert.Less(actual, expected)
dotnet_diagnostic.NUnit2030.severity = warning # Consider using Assert.That(actual, Is.LessThanOrEqualTo(expected)) instead of Assert.LessOrEqual(actual, expected)
dotnet_diagnostic.NUnit2031.severity = warning # Consider using Assert.That(actual, Is.Not.SameAs(expected)) instead of Assert.AreNotSame(expected, actual)
dotnet_diagnostic.NUnit2032.severity = warning # Consider using Assert.That(expr, Is.Zero) instead of Assert.Zero(expr)
dotnet_diagnostic.NUnit2033.severity = warning # Consider using Assert.That(expr, Is.Not.Zero) instead of Assert.NotZero(expr)
dotnet_diagnostic.NUnit2034.severity = warning # Consider using Assert.That(expr, Is.NaN) instead of Assert.IsNaN(expr)
dotnet_diagnostic.NUnit2035.severity = warning # Consider using Assert.That(collection, Is.Empty) instead of Assert.IsEmpty(collection)
dotnet_diagnostic.NUnit2036.severity = warning # Consider using Assert.That(collection, Is.Not.Empty) instead of Assert.IsNotEmpty(collection)
dotnet_diagnostic.NUnit2037.severity = warning # Consider using Assert.That(collection, Does.Contain(instance)) instead of Assert.Contains(instance, collection)
dotnet_diagnostic.NUnit2038.severity = warning # Consider using Assert.That(actual, Is.InstanceOf(expected)) instead of Assert.IsInstanceOf(expected, actual)
dotnet_diagnostic.NUnit2039.severity = warning # Consider using Assert.That(actual, Is.Not.InstanceOf(expected)) instead of Assert.IsNotInstanceOf(expected, actual)
[*.{appxmanifest,asax,ascx,aspx,axaml,build,c,c++,cc,cginc,compute,cp,cpp,cs,cshtml,cu,cuh,cxx,dtd,fs,fsi,fsscript,fsx,fx,fxh,h,hh,hlsl,hlsli,hlslinc,hpp,hxx,inc,inl,ino,ipp,ixx,master,ml,mli,mpp,mq4,mq5,mqh,nuspec,paml,razor,resw,resx,shader,skin,tpp,usf,ush,vb,xaml,xamlx,xoml,xsd}]
indent_style = space
indent_size = 2
tab_width = 2
+5
View File
@@ -0,0 +1,5 @@
# Set the default behavior, in case people don't have core.autocrlf set.
* text=auto
# need original files to be windows
*.txt text eol=crlf
+21
View File
@@ -0,0 +1,21 @@
**/bin/*
**/obj/*
_ReSharper.SharpCompress/
bin/
*.suo
*.user
TestArchives/Scratch/
TestArchives/Scratch2/
TestResults/
*.nupkg
packages/*/
project.lock.json
tests/TestArchives/Scratch
.vs
tools
.vscode
.idea/
.DS_Store
*.snupkg
coverage.xml
@@ -0,0 +1,82 @@
using Speckle.Autofac.DependencyInjection;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Connectors.Utils;
using Speckle.Connectors.Utils.Cancellation;
using Speckle.Connectors.Utils.Operations;
namespace Speckle.Connectors.ArcGIS.Bindings;
public sealed class ArcGISReceiveBinding : IReceiveBinding
{
public string Name { get; } = "receiveBinding";
private readonly CancellationManager _cancellationManager;
private readonly DocumentModelStore _store;
private readonly IUnitOfWorkFactory _unitOfWorkFactory;
public ReceiveBindingUICommands Commands { get; }
public IBridge Parent { get; }
public ArcGISReceiveBinding(
DocumentModelStore store,
IBridge parent,
CancellationManager cancellationManager,
IUnitOfWorkFactory unitOfWorkFactory
)
{
_store = store;
_cancellationManager = cancellationManager;
Parent = parent;
Commands = new ReceiveBindingUICommands(parent);
_unitOfWorkFactory = unitOfWorkFactory;
}
public async Task Receive(string modelCardId)
{
try
{
// Get receiver card
if (_store.GetModelById(modelCardId) is not ReceiverModelCard modelCard)
{
// Handle as GLOBAL ERROR at BrowserBridge
throw new InvalidOperationException("No download model card was found.");
}
// Init cancellation token source -> Manager also cancel it if exist before
CancellationTokenSource cts = _cancellationManager.InitCancellationTokenSource(modelCardId);
using IUnitOfWork<ReceiveOperation> unitOfWork = _unitOfWorkFactory.Resolve<ReceiveOperation>();
// Receive host objects
var receiveOperationResults = await unitOfWork.Service
.Execute(
modelCard.AccountId.NotNull(), // POC: I hear -you are saying why we're passing them separately. Not sure pass the DUI3-> Connectors.DUI project dependency to the SDK-> Connector.Utils
modelCard.ProjectId.NotNull(),
modelCard.ProjectName.NotNull(),
modelCard.ModelName.NotNull(),
modelCard.SelectedVersionId.NotNull(),
cts.Token,
(status, progress) =>
Commands.SetModelProgress(modelCardId, new ModelCardProgress(modelCardId, status, progress), cts)
)
.ConfigureAwait(false);
modelCard.BakedObjectIds = receiveOperationResults.BakedObjectIds.ToList();
Commands.SetModelReceiveResult(
modelCardId,
receiveOperationResults.BakedObjectIds,
receiveOperationResults.ConversionResults
);
}
// Catch here specific exceptions if they related to model card.
catch (OperationCanceledException)
{
// SWALLOW -> UI handles it immediately, so we do not need to handle anything
return;
}
}
public void CancelReceive(string modelCardId) => _cancellationManager.CancelOperation(modelCardId);
}
@@ -0,0 +1,44 @@
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using ArcGIS.Desktop.Mapping.Events;
using ArcGIS.Desktop.Mapping;
namespace Speckle.Connectors.ArcGIS.Bindings;
public class ArcGISSelectionBinding : ISelectionBinding
{
public string Name => "selectionBinding";
public IBridge Parent { get; }
public ArcGISSelectionBinding(IBridge parent, ITopLevelExceptionHandler topLevelHandler)
{
Parent = parent;
// example: https://github.com/Esri/arcgis-pro-sdk-community-samples/blob/master/Map-Authoring/QueryBuilderControl/DefinitionQueryDockPaneViewModel.cs
// MapViewEventArgs args = new(MapView.Active);
TOCSelectionChangedEvent.Subscribe(_ => topLevelHandler.CatchUnhandled(OnSelectionChanged), true);
}
private void OnSelectionChanged()
{
SelectionInfo selInfo = GetSelection();
Parent.Send(SelectionBindingEvents.SET_SELECTION, selInfo);
}
public SelectionInfo GetSelection()
{
MapView mapView = MapView.Active;
List<MapMember> selectedMembers = new();
selectedMembers.AddRange(mapView.GetSelectedLayers());
selectedMembers.AddRange(mapView.GetSelectedStandaloneTables());
List<string> objectTypes = selectedMembers
.Select(o => o.GetType().ToString().Split(".").Last())
.Distinct()
.ToList();
return new SelectionInfo(
selectedMembers.Select(x => x.URI).ToList(),
$"{selectedMembers.Count} layers ({string.Join(", ", objectTypes)})"
);
}
}
@@ -0,0 +1,411 @@
using System.Diagnostics.CodeAnalysis;
using Speckle.Autofac.DependencyInjection;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.Utils.Cancellation;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Connectors.DUI.Models.Card.SendFilter;
using Speckle.Connectors.DUI.Settings;
using ArcGIS.Desktop.Mapping.Events;
using ArcGIS.Desktop.Mapping;
using Speckle.Connectors.ArcGIS.Filters;
using ArcGIS.Desktop.Editing.Events;
using ArcGIS.Desktop.Framework.Threading.Tasks;
using ArcGIS.Core.Data;
using Speckle.Connectors.DUI.Exceptions;
using Speckle.Connectors.Utils;
using Speckle.Connectors.Utils.Caching;
using Speckle.Connectors.Utils.Operations;
namespace Speckle.Connectors.ArcGIS.Bindings;
public sealed class ArcGISSendBinding : ISendBinding
{
public string Name => "sendBinding";
public SendBindingUICommands Commands { get; }
public IBridge Parent { get; }
private readonly DocumentModelStore _store;
private readonly IUnitOfWorkFactory _unitOfWorkFactory; // POC: unused? :D
private readonly List<ISendFilter> _sendFilters;
private readonly CancellationManager _cancellationManager;
private readonly ISendConversionCache _sendConversionCache;
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
/// <summary>
/// Used internally to aggregate the changed objects' id.
/// </summary>
private HashSet<string> ChangedObjectIds { get; set; } = new();
private List<FeatureLayer> SubscribedLayers { get; set; } = new();
private List<StandaloneTable> SubscribedTables { get; set; } = new();
public ArcGISSendBinding(
DocumentModelStore store,
IBridge parent,
IEnumerable<ISendFilter> sendFilters,
IUnitOfWorkFactory unitOfWorkFactory,
CancellationManager cancellationManager,
ISendConversionCache sendConversionCache,
ITopLevelExceptionHandler topLevelExceptionHandler
)
{
_store = store;
_unitOfWorkFactory = unitOfWorkFactory;
_sendFilters = sendFilters.ToList();
_cancellationManager = cancellationManager;
_sendConversionCache = sendConversionCache;
_topLevelExceptionHandler = topLevelExceptionHandler;
Parent = parent;
Commands = new SendBindingUICommands(parent);
SubscribeToArcGISEvents();
}
private void SubscribeToArcGISEvents()
{
LayersRemovedEvent.Subscribe(
a => _topLevelExceptionHandler.CatchUnhandled(() => GetIdsForLayersRemovedEvent(a)),
true
);
StandaloneTablesRemovedEvent.Subscribe(
a => _topLevelExceptionHandler.CatchUnhandled(() => GetIdsForStandaloneTablesRemovedEvent(a)),
true
);
MapPropertyChangedEvent.Subscribe(
a => _topLevelExceptionHandler.CatchUnhandled(() => GetIdsForMapPropertyChangedEvent(a)),
true
); // Map units, CRS etc.
MapMemberPropertiesChangedEvent.Subscribe(
a => _topLevelExceptionHandler.CatchUnhandled(() => GetIdsForMapMemberPropertiesChangedEvent(a)),
true
); // e.g. Layer name
ActiveMapViewChangedEvent.Subscribe(
_ => _topLevelExceptionHandler.CatchUnhandled(SubscribeToMapMembersDataSourceChange),
true
);
LayersAddedEvent.Subscribe(a => _topLevelExceptionHandler.CatchUnhandled(() => GetIdsForLayersAddedEvent(a)), true);
StandaloneTablesAddedEvent.Subscribe(
a => _topLevelExceptionHandler.CatchUnhandled(() => GetIdsForStandaloneTablesAddedEvent(a)),
true
);
}
private void SubscribeToMapMembersDataSourceChange()
{
var task = QueuedTask.Run(() =>
{
if (MapView.Active == null)
{
return;
}
// subscribe to layers
foreach (Layer layer in MapView.Active.Map.Layers)
{
if (layer is FeatureLayer featureLayer)
{
SubscribeToFeatureLayerDataSourceChange(featureLayer);
}
}
// subscribe to tables
foreach (StandaloneTable table in MapView.Active.Map.StandaloneTables)
{
SubscribeToTableDataSourceChange(table);
}
});
task.Wait();
}
private void SubscribeToFeatureLayerDataSourceChange(FeatureLayer layer)
{
if (SubscribedLayers.Contains(layer))
{
return;
}
Table layerTable = layer.GetTable();
if (layerTable != null)
{
SubscribeToAnyDataSourceChange(layerTable);
SubscribedLayers.Add(layer);
}
}
private void SubscribeToTableDataSourceChange(StandaloneTable table)
{
if (SubscribedTables.Contains(table))
{
return;
}
Table layerTable = table.GetTable();
if (layerTable != null)
{
SubscribeToAnyDataSourceChange(layerTable);
SubscribedTables.Add(table);
}
}
private void SubscribeToAnyDataSourceChange(Table layerTable)
{
RowCreatedEvent.Subscribe(
(args) =>
{
OnRowChanged(args);
},
layerTable
);
RowChangedEvent.Subscribe(
(args) =>
{
OnRowChanged(args);
},
layerTable
);
RowDeletedEvent.Subscribe(
(args) =>
{
OnRowChanged(args);
},
layerTable
);
}
private void OnRowChanged(RowChangedEventArgs args)
{
if (args == null || MapView.Active == null)
{
return;
}
// get the path of the edited dataset
var datasetURI = args.Row.GetTable().GetPath();
// find all layers & tables reading from the dataset
foreach (Layer layer in MapView.Active.Map.Layers)
{
if (layer.GetPath() == datasetURI)
{
ChangedObjectIds.Add(layer.URI);
}
}
foreach (StandaloneTable table in MapView.Active.Map.StandaloneTables)
{
if (table.GetPath() == datasetURI)
{
ChangedObjectIds.Add(table.URI);
}
}
RunExpirationChecks(false);
}
private void GetIdsForLayersRemovedEvent(LayerEventsArgs args)
{
foreach (Layer layer in args.Layers)
{
ChangedObjectIds.Add(layer.URI);
}
RunExpirationChecks(true);
}
private void GetIdsForStandaloneTablesRemovedEvent(StandaloneTableEventArgs args)
{
foreach (StandaloneTable table in args.Tables)
{
ChangedObjectIds.Add(table.URI);
}
RunExpirationChecks(true);
}
private void GetIdsForMapPropertyChangedEvent(MapPropertyChangedEventArgs args)
{
foreach (Map map in args.Maps)
{
foreach (MapMember member in map.Layers)
{
ChangedObjectIds.Add(member.URI);
}
}
RunExpirationChecks(false);
}
private void GetIdsForLayersAddedEvent(LayerEventsArgs args)
{
foreach (Layer layer in args.Layers)
{
if (layer is FeatureLayer featureLayer)
{
SubscribeToFeatureLayerDataSourceChange(featureLayer);
}
}
}
private void GetIdsForStandaloneTablesAddedEvent(StandaloneTableEventArgs args)
{
foreach (StandaloneTable table in args.Tables)
{
SubscribeToTableDataSourceChange(table);
}
}
private void GetIdsForMapMemberPropertiesChangedEvent(MapMemberPropertiesChangedEventArgs args)
{
// don't subscribe to all events (e.g. expanding group, changing visibility etc.)
bool validEvent = false;
foreach (var hint in args.EventHints)
{
if (
hint == MapMemberEventHint.DataSource
|| hint == MapMemberEventHint.DefinitionQuery
|| hint == MapMemberEventHint.LabelClasses
|| hint == MapMemberEventHint.LabelVisibility
|| hint == MapMemberEventHint.Name
|| hint == MapMemberEventHint.Renderer
|| hint == MapMemberEventHint.SceneLayerType
|| hint == MapMemberEventHint.URL
)
{
validEvent = true;
break;
}
}
if (validEvent)
{
foreach (MapMember member in args.MapMembers)
{
ChangedObjectIds.Add(member.URI);
}
RunExpirationChecks(false);
}
}
public List<ISendFilter> GetSendFilters() => _sendFilters;
// POC: delete this
public List<CardSetting> GetSendSettings()
{
return new List<CardSetting>
{
new()
{
Id = "includeAttributes",
Title = "Include Attributes",
Value = true,
Type = "boolean"
},
};
}
[SuppressMessage(
"Maintainability",
"CA1506:Avoid excessive class coupling",
Justification = "Being refactored on in parallel, muting this issue so CI can pass initially."
)]
public async Task Send(string modelCardId)
{
//poc: dupe code between connectors
using var unitOfWork = _unitOfWorkFactory.Resolve<SendOperation<MapMember>>();
try
{
if (_store.GetModelById(modelCardId) is not SenderModelCard modelCard)
{
// Handle as GLOBAL ERROR at BrowserBridge
throw new InvalidOperationException("No publish model card was found.");
}
// Init cancellation token source -> Manager also cancel it if exist before
CancellationTokenSource cts = _cancellationManager.InitCancellationTokenSource(modelCardId);
var sendInfo = new SendInfo(
modelCard.AccountId.NotNull(),
modelCard.ProjectId.NotNull(),
modelCard.ModelId.NotNull(),
"ArcGIS"
);
var sendResult = await QueuedTask
.Run(async () =>
{
List<MapMember> mapMembers = modelCard.SendFilter
.NotNull()
.GetObjectIds()
.Select(id => (MapMember)MapView.Active.Map.FindLayer(id) ?? MapView.Active.Map.FindStandaloneTable(id))
.Where(obj => obj != null)
.ToList();
if (mapMembers.Count == 0)
{
// Handle as CARD ERROR in this function
throw new SpeckleSendFilterException(
"No objects were found to convert. Please update your publish filter!"
);
}
var result = await unitOfWork.Service
.Execute(
mapMembers,
sendInfo,
(status, progress) =>
Commands.SetModelProgress(modelCardId, new ModelCardProgress(modelCardId, status, progress), cts),
cts.Token
)
.ConfigureAwait(false);
return result;
})
.ConfigureAwait(false);
Commands.SetModelSendResult(modelCardId, sendResult.RootObjId, sendResult.ConversionResults);
}
// Catch here specific exceptions if they related to model card.
catch (SpeckleSendFilterException e)
{
Commands.SetModelError(modelCardId, e);
}
catch (OperationCanceledException)
{
// SWALLOW -> UI handles it immediately, so we do not need to handle anything
return;
}
}
public void CancelSend(string modelCardId) => _cancellationManager.CancelOperation(modelCardId);
/// <summary>
/// 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.
/// </summary>
private void RunExpirationChecks(bool idsDeleted)
{
var senders = _store.GetSenders();
List<string> expiredSenderIds = new();
string[] objectIdsList = ChangedObjectIds.ToArray();
_sendConversionCache.EvictObjects(objectIdsList);
foreach (SenderModelCard sender in senders)
{
var objIds = sender.SendFilter.NotNull().GetObjectIds();
var intersection = objIds.Intersect(objectIdsList).ToList();
bool isExpired = sender.SendFilter.NotNull().CheckExpiry(ChangedObjectIds.ToArray());
if (isExpired)
{
expiredSenderIds.Add(sender.ModelCardId.NotNull());
// Update the model card object Ids
if (idsDeleted && sender.SendFilter is ArcGISSelectionFilter filter)
{
List<string> remainingObjIds = objIds.SkipWhile(x => intersection.Contains(x)).ToList();
filter.SelectedObjectIds = remainingObjIds;
}
}
}
Commands.SetModelsExpired(expiredSenderIds);
ChangedObjectIds = new HashSet<string>();
}
}
@@ -0,0 +1,203 @@
using System.Reflection;
using ArcGIS.Core.Data;
using ArcGIS.Desktop.Framework.Threading.Tasks;
using ArcGIS.Desktop.Mapping;
using Speckle.Connectors.ArcGIS.HostApp;
using Speckle.Connectors.ArcGIS.Utils;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Connectors.Utils;
using Speckle.Connectors.Utils.Reflection;
namespace Speckle.Connectors.ArcGIS.Bindings;
//poc: dupe code between connectors
public class BasicConnectorBinding : IBasicConnectorBinding
{
public string Name => "baseBinding";
public IBridge Parent { get; }
public BasicConnectorBindingCommands Commands { get; }
private readonly DocumentModelStore _store;
private readonly ArcGISSettings _settings;
public BasicConnectorBinding(DocumentModelStore store, ArcGISSettings settings, IBridge parent)
{
_store = store;
_settings = settings;
Parent = parent;
Commands = new BasicConnectorBindingCommands(parent);
_store.DocumentChanged += (_, _) =>
{
Commands.NotifyDocumentChanged();
};
}
public string GetSourceApplicationName() => _settings.HostAppInfo.Slug;
public string GetSourceApplicationVersion() => _settings.HostAppInfo.GetVersion(_settings.HostAppVersion);
public string GetConnectorVersion() => Assembly.GetAssembly(GetType()).NotNull().GetVersion();
public DocumentInfo? GetDocumentInfo()
{
if (MapView.Active is null)
{
return null;
}
return new DocumentInfo(MapView.Active.Map.URI, MapView.Active.Map.Name, MapView.Active.Map.Name);
}
public DocumentModelStore GetDocumentState() => _store;
public void AddModel(ModelCard model) => _store.Models.Add(model);
public void UpdateModel(ModelCard model) => _store.UpdateModel(model);
public void RemoveModel(ModelCard model) => _store.RemoveModel(model);
public void HighlightObjects(List<string> objectIds) =>
HighlightObjectsOnView(objectIds.Select(x => new ObjectID(x)).ToList());
public void HighlightModel(string modelCardId)
{
var model = _store.GetModelById(modelCardId);
if (model is null)
{
return;
}
var objectIds = new List<ObjectID>();
if (model is SenderModelCard senderModelCard)
{
objectIds = senderModelCard.SendFilter.NotNull().GetObjectIds().Select(x => new ObjectID(x)).ToList();
}
if (model is ReceiverModelCard receiverModelCard)
{
objectIds = receiverModelCard.BakedObjectIds.NotNull().Select(x => new ObjectID(x)).ToList();
}
if (objectIds is null)
{
return;
}
HighlightObjectsOnView(objectIds);
}
private async void HighlightObjectsOnView(List<ObjectID> objectIds)
{
MapView mapView = MapView.Active;
await QueuedTask
.Run(() =>
{
List<MapMemberFeature> mapMembersFeatures = GetMapMembers(objectIds, mapView);
ClearSelectionInTOC();
ClearSelection();
SelectMapMembersInTOC(mapMembersFeatures);
SelectMapMembersAndFeatures(mapMembersFeatures);
mapView.ZoomToSelected();
})
.ConfigureAwait(false);
}
private List<MapMemberFeature> GetMapMembers(List<ObjectID> objectIds, MapView mapView)
{
// find the layer on the map (from the objectID) and add the featureID is available
List<MapMemberFeature> mapMembersFeatures = new();
foreach (ObjectID objectId in objectIds)
{
MapMember mapMember = mapView.Map.FindLayer(objectId.MappedLayerURI, true);
if (mapMember is null)
{
mapMember = mapView.Map.FindStandaloneTable(objectId.MappedLayerURI);
}
if (mapMember is not null)
{
MapMemberFeature mapMembersFeat = new(mapMember, objectId.FeatureId);
mapMembersFeatures.Add(mapMembersFeat);
}
}
return mapMembersFeatures;
}
private void ClearSelection()
{
List<Layer> mapMembers = MapView.Active.Map.GetLayersAsFlattenedList().ToList();
foreach (var member in mapMembers)
{
if (member is FeatureLayer featureLayer)
{
featureLayer.ClearSelection();
}
}
}
private void ClearSelectionInTOC()
{
MapView.Active.ClearTOCSelection();
}
private void SelectMapMembersAndFeatures(List<MapMemberFeature> mapMembersFeatures)
{
foreach (MapMemberFeature mapMemberFeat in mapMembersFeatures)
{
MapMember member = mapMemberFeat.MapMember;
if (member is FeatureLayer layer)
{
if (mapMemberFeat.FeatureId == null)
{
// select full layer if featureID not specified
layer.Select();
}
else
{
// query features by ID
var objectIDfield = layer.GetFeatureClass().GetDefinition().GetObjectIDField();
// FeatureID range starts from 0, but auto-assigned IDs in the layer start from 1
QueryFilter anotherQueryFilter = new() { WhereClause = $"{objectIDfield} = {mapMemberFeat.FeatureId + 1}" };
using (Selection onlyOneSelection = layer.Select(anotherQueryFilter, SelectionCombinationMethod.New)) { }
}
}
}
}
private void SelectMapMembersInTOC(List<MapMemberFeature> mapMembersFeatures)
{
List<Layer> layers = new();
List<StandaloneTable> tables = new();
foreach (MapMemberFeature mapMemberFeat in mapMembersFeatures)
{
MapMember member = mapMemberFeat.MapMember;
if (member is Layer layer)
{
if (member is not GroupLayer) // group layer selection clears other layers selection
{
layers.Add(layer);
}
}
else if (member is StandaloneTable table)
{
tables.Add(table);
}
}
MapView.Active.SelectLayers(layers);
// this step clears previous selection, not clear how to ADD selection instead
// this is why, activating it only if no layers are selected
if (layers.Count == 0)
{
MapView.Active.SelectStandaloneTables(tables);
}
}
}
@@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8" ?>
<!--
Copyright 2022 Esri
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<ArcGIS defaultAssembly="Speckle.Connectors.ArcGIS3.dll" defaultNamespace="Speckle.Connectors.ArcGIS" xmlns="http://schemas.esri.com/DADF/Registry" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://schemas.esri.com/DADF/Registry file:///C:/Program%20Files/ArcGIS/Pro/bin/ArcGIS.Desktop.Framework.xsd">
<AddInInfo id="{6CB1D25C-B8BF-4A33-9099-C1F8D1B32EFC}" version="1.0" desktopVersion="3.0.34047">
<Name>Speckle</Name>
<Description>Speckle connector for ArcGIS</Description>
<Image>Images\AddinDesktop32.png</Image>
<Author>Speckle Systems</Author>
<Company>Speckle Systems</Company>
<Date>8/5/2021 12:24:21 PM</Date>
<Subject>Framework</Subject>
<!-- Note subject can be one or more of these topics:
Content, Framework, Editing, Geodatabase, Geometry, Geoprocessing, Layouts, Map Authoring, Map Exploration -->
</AddInInfo>
<modules>
<insertModule id="ConnectorArcGIS_Module" className="SpeckleModule" autoLoad="false" caption="SpeckleModule">
<!-- uncomment to have the control hosted on a separate tab-->
<tabs>
<!--<tab id="Speckle_Tab1" caption="New Tab">
<group refID="Speckle_Group1"/>
</tab>-->
</tabs>
<groups>
<!-- comment this out if you have no controls on the Addin tab to avoid
an empty group-->
<group id="Speckle_Group1" caption="Speckle" appearsOnAddInTab="true" keytip="G1">
<!-- host controls within groups -->
<button refID="SpeckleDUI3_SpeckleDUI3OpenButton" size="large" />
</group>
</groups>
<controls>
<!-- add your controls here -->
<button id="SpeckleDUI3_SpeckleDUI3OpenButton" caption="SpeckleNewUI"
className="SpeckleDUI3OpenButton" loadOnClick="true"
keytip="B1"
smallImage="Images/s2logo_16.png"
largeImage="Images/s2logo_32.png">
<tooltip heading="Speckle Connector for ArcGIS">
<disabledText />
</tooltip>
</button>
</controls>
<dockPanes>
<dockPane id="SpeckleDUI3_SpeckleDUI3" caption="SpeckleNewUI" className="SpeckleDUI3ViewModel" keytip="DockPane" initiallyVisible="true" dock="group" dockWith="esri_core_contentsDockPane">
<content className="SpeckleDUI3Wrapper" />
</dockPane>
</dockPanes>
</insertModule>
</modules>
</ArcGIS>
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 714 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

@@ -0,0 +1,63 @@
using ArcGIS.Desktop.Mapping;
using Speckle.Autofac.DependencyInjection;
using Speckle.Connectors.ArcGIS.Bindings;
using Speckle.Connectors.ArcGis.Operations.Send;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.ArcGIS.Utils;
using Speckle.Connectors.ArcGIS.Operations.Receive;
using Speckle.Connectors.DUI.Models.Card.SendFilter;
using Speckle.Connectors.DUI.WebView;
using Speckle.Connectors.Utils.Builders;
using Speckle.Autofac;
using Speckle.Connectors.ArcGIS.Filters;
using Speckle.Connectors.ArcGIS.HostApp;
using Speckle.Connectors.DUI;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.Utils;
using Speckle.Connectors.Utils.Caching;
using Speckle.Connectors.Utils.Operations;
using Speckle.Core.Models.GraphTraversal;
// POC: This is a temp reference to root object senders to tweak CI failing after having generic interfaces into common project.
// This should go whenever it is aligned.
namespace Speckle.Connectors.ArcGIS.DependencyInjection;
public class ArcGISConnectorModule : ISpeckleModule
{
public void Load(SpeckleContainerBuilder builder)
{
builder.AddAutofac();
builder.AddConnectorUtils();
builder.AddDUI();
builder.AddDUIView();
// POC: Overwriting the SyncToMainThread to SyncToCurrentThread for ArcGIS only!
// On SendOperation, once we called QueuedTask, it expect to run everything on same thread.
builder.AddSingletonInstance<ISyncToThread, SyncToQueuedTask>();
builder.AddSingleton<DocumentModelStore, ArcGISDocumentStore>();
// Register bindings
builder.AddSingleton<IBinding, TestBinding>();
builder.AddSingleton<IBinding, ConfigBinding>("connectorName", "ArcGIS"); // POC: Easier like this for now, should be cleaned up later
builder.AddSingleton<IBinding, AccountBinding>();
builder.AddSingleton<IBinding, BasicConnectorBinding>();
builder.AddSingleton<IBasicConnectorBinding, BasicConnectorBinding>();
builder.AddSingleton<IBinding, ArcGISSelectionBinding>();
builder.AddSingleton<IBinding, ArcGISSendBinding>();
builder.AddSingleton<IBinding, ArcGISReceiveBinding>();
builder.AddTransient<ISendFilter, ArcGISSelectionFilter>();
builder.AddScoped<IHostObjectBuilder, ArcGISHostObjectBuilder>();
builder.AddSingleton(DefaultTraversal.CreateTraversalFunc());
// register send operation and dependencies
builder.AddScoped<SendOperation<MapMember>>();
builder.AddScoped<ArcGISRootObjectBuilder>();
builder.AddScoped<IRootObjectBuilder<MapMember>, ArcGISRootObjectBuilder>();
// register send conversion cache
builder.AddSingleton<ISendConversionCache, SendConversionCache>();
}
}
@@ -0,0 +1,352 @@
<?xml version="1.0" encoding="utf-8" ?>
<Project>
<!-- Code to zip up the files-->
<UsingTask TaskName="PackageAddIn" TaskFactory="RoslynCodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
<ParameterGroup>
<ZipIntermediatePath ParameterType="System.String" Required="true" />
<PackageType ParameterType="System.String" Required="true" />
<TargetFolder ParameterType="System.String" Required="true" />
<TargetFileName ParameterType="System.String" Required="true" />
<RootNamespace ParameterType="System.String" Required="true" />
<PackageOutputPath ParameterType="System.String" Output="true"/>
</ParameterGroup>
<Task>
<Using Namespace="System"/>
<Using Namespace="System.IO"/>
<Using Namespace="System.Xml.Linq"/>
<Using Namespace="System.Linq"/>
<Using Namespace="System.IO.Compression"/>
<Code Type="Fragment" Language="cs">
<![CDATA[
Success = false;
string ConfigNotFound = "{0} was not found. File must be present in the root of the project and its build action set to AddInContent.";
string ZipIntermediatePathNotFound = "{0} was not found.";
string DefaultAssemblyDoesNotMatch = "Your value of '{0}' for the '{1}' attribute in the {2} does not match the assembly name '{3}' set for your project.";
string DefaultNSDoesNotMatch = "Your value of '{0}' for the '{1}' attribute in the {2} does not match the default namespace '{3}' set for your project.";
//Create the name of the Config File and extension
string extension = "";
string config = "";
string attrib_asm = "";
string attrib_ns = "";
var assemblyValMissing = "";
var nsValMissing = "";
if (PackageType.ToLower() == "plugin")
{
Log.LogMessage(MessageImportance.Low, "This is an plugin");
config = "Config.xml";
extension = ".esriPlugin";
attrib_asm = "library";
attrib_ns = "namespace";
assemblyValMissing = "AddIn element 'library' attribute not found";
nsValMissing = "AddIn element 'namespace' attribute not found";
}
else if (PackageType.ToLower() == "configuration")
{
Log.LogMessage(MessageImportance.Low, "This is an configuration");
config = "Config.daml";
extension = ".proConfigX";
attrib_asm = "defaultAssembly";
attrib_ns = "defaultNamespace";
assemblyValMissing = "ArcGIS element 'defaultAssembly' attribute not found";
nsValMissing = "ArcGIS element 'defaultNamespace' attribute not found";
}
else
{
Log.LogMessage(MessageImportance.Low, "This is an addin");
config = "Config.daml";
bool proSDKProject = File.Exists(Path.Combine(ZipIntermediatePath, config));
if (!proSDKProject) //This might be a class library that uses the Pro references only
return true;
extension = ".esriAddinX";
attrib_asm = "defaultAssembly";
attrib_ns = "defaultNamespace";
assemblyValMissing = "ArcGIS element 'defaultAssembly' attribute not found";
nsValMissing = "ArcGIS element 'defaultNamespace' attribute not found";
}
// Check if Config.daml exists in ZipFolder
ZipIntermediatePath = Path.GetFullPath(ZipIntermediatePath);
if (!Directory.Exists(ZipIntermediatePath))
{
Log.LogError(ZipIntermediatePathNotFound, ZipIntermediatePath);
return false;
}
var addInXML = Path.Combine(ZipIntermediatePath, config);
Log.LogMessage(MessageImportance.Low, "addInXML: " + addInXML);
Log.LogMessage(MessageImportance.High, "PackageType: " + PackageType);
if (!File.Exists(addInXML))
{
Log.LogError(ConfigNotFound, config);
return false;
}
//Verfiy that an assembly with the name defined in the Config.daml
//matches the default assembly set in the project. Ditto for the
//namespace
string DefaultAssembly = "";
string DefaultNamespace = "";
XDocument xdoc = XDocument.Load(addInXML);
XNamespace DefaultNS = "http://schemas.esri.com/DADF/Registry";
if (PackageType.ToLower() == "plugin")
{
var addin = xdoc.Root.Element(DefaultNS + "AddIn");
if (addin != null)
{
var val = addin.Attribute("library");
if (val != null)
DefaultAssembly = val.Value;
val = addin.Attribute("namespace");
if (val != null)
DefaultNamespace = val.Value;
}
}
else
{
var val = xdoc.Root.Attribute("defaultAssembly");
if (val != null)
DefaultAssembly = val.Value;
val = xdoc.Root.Attribute("defaultNamespace");
if (val != null)
DefaultNamespace = val.Value;
}
if (string.IsNullOrEmpty(DefaultAssembly))
{
Log.LogError(assemblyValMissing);
return false;
}
if (string.IsNullOrEmpty(DefaultNamespace))
{
Log.LogError(nsValMissing);
return false;
}
//check that the addin assembly and default assembly names match
if (DefaultAssembly.ToLower() != TargetFileName.ToLower())
{
Log.LogWarning(DefaultAssemblyDoesNotMatch, DefaultAssembly, attrib_asm, config, TargetFileName);
}
//Ditto for namespace
if (DefaultNamespace.ToLower() != RootNamespace.ToLower())
{
Log.LogWarning(DefaultNSDoesNotMatch, DefaultNamespace, attrib_ns, config, RootNamespace);
}
if (!Directory.Exists(TargetFolder))
{
Directory.CreateDirectory(TargetFolder);
}
string addInAssembly = System.IO.Path.GetFileNameWithoutExtension(DefaultAssembly);
string archiveName = addInAssembly + extension;
try
{
string file = Path.Combine(TargetFolder, archiveName);
if (File.Exists(file))
File.Delete(file);
System.IO.Compression.ZipFile.CreateFromDirectory(ZipIntermediatePath, file);
PackageOutputPath = Path.GetFullPath(file);
Success = true;
}
catch (Exception ex)
{
Log.LogErrorFromException(ex);
return false;
}
return Success;
]]>
</Code>
</Task>
</UsingTask>
<!-- Code to find relative path-->
<UsingTask TaskName="ConvertToRelativePath" TaskFactory="RoslynCodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
<ParameterGroup>
<RelativeTo ParameterType="System.String" Required="true"/>
<Paths ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
<RelativePaths ParameterType="Microsoft.Build.Framework.ITaskItem[]" Output="true"/>
</ParameterGroup>
<Task>
<Using Namespace="System"/>
<Using Namespace="System.IO"/>
<Using Namespace="System.Linq"/>
<Code Type="Fragment" Language="cs">
<![CDATA[
var result = new List<ITaskItem>();
System.Uri relativeTo = new Uri(this.RelativeTo);
foreach (var i in Paths) {
try {
System.Uri itemFullPath = new Uri(i.GetMetadata("FullPath"));
var relativeUri = relativeTo.MakeRelativeUri(itemFullPath);
result.Add(new TaskItem(Uri.UnescapeDataString(relativeUri.ToString())));
}
catch {
return false;
}
}
RelativePaths = result.ToArray();
foreach (var i in RelativePaths)
{
Log.LogMessage(MessageImportance.Low, "RelativePaths: " + i.ToString());
}
return true;
]]>
</Code>
</Task>
</UsingTask>
<UsingTask TaskName="CleanAddIn" TaskFactory="RoslynCodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
<ParameterGroup>
<ProjectDir ParameterType="System.String" Required="true"/>
<AssemblyName ParameterType="System.String" Required="true"/>
<PackageType ParameterType="System.String" Required="true"/>
<!--<ArcGISFolder ParameterType="System.String" Output="true" /> -->
<CleanInfo ParameterType="System.String" Output="true"/>
</ParameterGroup>
<Task>
<Using Namespace="System"/>
<Using Namespace="System.IO"/>
<Using Namespace="System.Xml.Linq"/>
<Using Namespace="System.Linq"/>
<Code Type="Fragment" Language="cs">
<![CDATA[
Success = false;
string ConfigNotFound = "{0} was not found. File must be present in the root of the project and its build action set to AddInContent.";
//Create the name of the Config File and extension
string extension = "";
string config = "";
if (PackageType.ToLower() == "plugin")
{
config = "Config.xml";
extension = ".esriPlugin";
}
else if (PackageType.ToLower() == "configuration")
{
config = "Config.daml";
extension = ".proConfigX";
}
else
{
config = "Config.daml";
bool proSDKProject = File.Exists(Path.Combine(ProjectDir, config));
if (!proSDKProject) //This might be a class library that uses the Pro references only
return true;
extension = ".esriAddinX";
}
var addInXML = Path.Combine(ProjectDir, config);
if (!File.Exists(addInXML))
{
Log.LogError(ConfigNotFound, config);
return false;
}
//Get the add-in id
XDocument xdoc = XDocument.Load(addInXML);
XNamespace DefaultNS = "http://schemas.esri.com/DADF/Registry";
if (PackageType.ToLower() == "plugin")
{
Log.LogMessage("process plugin");
var addInID = xdoc.Root.Element(DefaultNS + "AddInID");
CleanInfo = addInID.Value;//let it error if it's missing
}
else if (PackageType.ToLower() == "addin")
{
Log.LogMessage("process addin");
var addinInfo = xdoc.Root.Element(DefaultNS + "AddInInfo");
CleanInfo = addinInfo.Attribute("id").Value;//let it error if it's missing
}
else
{
Log.LogMessage("process configuration");
CleanInfo = AssemblyName + extension;
}
Success = true;
return Success;
]]>
</Code>
</Task>
</UsingTask>
<!-- Define additional BuildAction option -->
<!-- Set up default zip properties -->
<PropertyGroup>
<PackageType Condition="'$(PackageType)' == ''">Addin</PackageType>
</PropertyGroup>
<PropertyGroup>
<ArcGISFolder>$([MSBuild]::GetRegistryValueFromView('HKEY_LOCAL_MACHINE\SOFTWARE\ESRI\ArcGISPro', 'InstallDir', null, RegistryView.Registry64))</ArcGISFolder>
<ArcGISFolder Condition="'$(ArcGISFolder)' == ''">$(registry:HKEY_CURRENT_USER\SOFTWARE\ESRI\ArcGISPro@InstallDir)</ArcGISFolder>
</PropertyGroup>
<Target Name="ArcGISInstallOutput" AfterTargets="Build">
<Message Text="IntermediateOutputPath Name: $(IntermediateOutputPath)..." Importance="High"/>
<Message Text="CleanFile Name: $(CleanFile)..." Importance="High"/>
<Message Text="ProjectDir Name: $(ProjectDir)..." Importance="High"/>
<Message Text="AssemblyName Name: $(AssemblyName)..." Importance="High"/>
<Message Text="TargetFileName Name: $(TargetFileName)..." Importance="High"/>
<Message Text="RootNamespace: $(RootNamespace)..." Importance="High"/>
<Message Text="TargetFolder Name: $(OutDir)..." Importance="High"/>
<Message Text="PackageType Name: $(PackageType)..." Importance="High"/>
<Message Text="Install dir: $(ArcGISFolder)" Importance="High"/>
<!-- Get a list of project outputs from the cache file and FileWritesXXX item,
excluding those in intermediate output directory -->
<!-- Note clean file may miss listing CopyLocal reference -->
<ReadLinesFromFile File="$(IntermediateOutputPath)$(CleanFile)">
<Output TaskParameter="Lines" ItemName="CacheOutputFiles" />
</ReadLinesFromFile>
<FindUnderPath Files="@(CacheOutputFiles)" Path="$(OutDir)">
<Output TaskParameter="InPath" ItemName="PackageOutputFiles" />
</FindUnderPath>
<FindUnderPath Files="@(FileWrites->'%(FullPath)')" Path="$(OutDir)">
<Output TaskParameter="InPath" ItemName="PackageOutputFiles" />
</FindUnderPath>
<FindUnderPath Files="@(FileWritesShareable->'%(FullPath)')" Path="$(OutDir)">
<Output TaskParameter="InPath" ItemName="PackageOutputFiles" />
</FindUnderPath>
<RemoveDuplicates Inputs="@(PackageOutputFiles)">
<Output TaskParameter="Filtered" ItemName="FilteredPackageOutputFiles" />
</RemoveDuplicates>
<ConvertToRelativePath Paths="@(FilteredPackageOutputFiles)" RelativeTo="$(TargetDir)">
<Output TaskParameter="RelativePaths" ItemName="ConfigBinaries" />
</ConvertToRelativePath>
<Message Text="ConvertToRelativePath Task, TargetDir: $(TargetDir) " Importance="High"/>
</Target>
<Target Name="PackageArcGISContents" AfterTargets="ArcGISInstallOutput">
<Message Text="Running PackageArcGISContents..." Importance="High"/>
<RemoveDir Condition="Exists('$(ZipIntermediatePath)')" Directories="$(ZipIntermediatePath)" />
<Message Text="ZipIntermediatePath: $(ZipIntermediatePath)Install..." Importance="High"/>
<!-- Copy project output files, preserving folder structure -->
<Copy SourceFiles="@(ConfigBinaries->'$(OutDir)%(Identity)')" ContinueOnError="true" DestinationFolder="$(IntermediateOutputPath)temp_archive\Install\%(RelativeDir)" />
<!-- Copy items marked with Content as BuildAction, preserving folder structure & handling linked items -->
<!-- Only include items that have CopyToOutputDirectory as Never -->
<Copy SourceFiles="@(Content)" Condition="'%(Content.Link)' == '' And ('%(Content.CopyToOutputDirectory)' == 'Never' Or '%(Content.CopyToOutputDirectory)' == '')" DestinationFolder="$(IntermediateOutputPath)temp_archive\%(RelativeDir)" ContinueOnError="true" />
<Copy SourceFiles="@(Content)" Condition="'%(Content.Link)' != '' And ('%(Content.CopyToOutputDirectory)' == 'Never' Or '%(Content.CopyToOutputDirectory)' == '')" DestinationFiles="$(IntermediateOutputPath)temp_archive\%(Content.Link)" ContinueOnError="true"/>
<!-- Zipping up add-in resources -->
<PackageAddIn ZipIntermediatePath="$(IntermediateOutputPath)temp_archive\"
PackageType="$(PackageType)"
TargetFolder="$(OutDir)"
TargetFileName="$(TargetFileName)"
RootNamespace="$(RootNamespace)">
<Output TaskParameter="PackageOutputPath" PropertyName="PackageFile" />
</PackageAddIn>
<!-- Shell out to RegisterAddIn.exe to install the package -->
<Message Text="Deploying $(PackageType)..." Importance="High"/>
<Message Text="ArcGISFolder Name: $(ArcGISFolder)..." Importance="High"/>
<Message Text="Unable to execute RegisterAddIn.exe. ArcGIS Pro is not installed." Importance="High" Condition="'$(ArcGISFolder)' == ''"/>
<Message Text="Execute RegisterAddIn.exe &quot;$(PackageFile)&quot; /s..." Importance="High" Condition="'$(ArcGISFolder)' != ''"/>
<Exec IgnoreExitCode="true" WorkingDirectory="$(ArcGISFolder)bin" Command="RegisterAddIn.exe &quot;$(PackageFile)&quot; /s" Condition="'$(ArcGISFolder)' != '' AND $(PackageFile) != '' ">
<Output TaskParameter="ExitCode" PropertyName="ESRIRegAddinExitCode" />
</Exec>
<RemoveDir Condition="Exists('$(ZipIntermediatePath)')" Directories="$(ZipIntermediatePath)" />
</Target>
<Target Name="CleanArcGISContents" AfterTargets="Clean">
<CleanAddIn ProjectDir="$(ProjectDir)"
AssemblyName="$(AssemblyName)"
PackageType="$(PackageType)">
<Output TaskParameter="CleanInfo" PropertyName="CleanInfo" />
</CleanAddIn>
<Message Text="Clean $(PackageType)..." Importance="High"/>
<Message Text="Execute RegisterAddIn.exe &quot;$(CleanInfo)&quot; /u..." Importance="High" Condition="'$(ArcGISFolder)' != ''"/>
<Message Text="Unable to execute RegisterAddIn.exe. ArcGIS Pro is not installed." Importance="High" Condition="'$(ArcGISFolder)' == ''"/>
<Exec IgnoreExitCode="true" WorkingDirectory="$(ArcGISFolder)bin" Command="RegisterAddIn.exe &quot;$(CleanInfo)&quot; /u /s" Condition="'$(ArcGISFolder)' != '' AND $(CleanInfo) != ''">
<Output TaskParameter="ExitCode" PropertyName="ESRIRegAddinExitCode" />
</Exec>
</Target>
</Project>
@@ -0,0 +1,10 @@
using Speckle.Connectors.DUI.Models.Card.SendFilter;
namespace Speckle.Connectors.ArcGIS.Filters;
public class ArcGISEverythingFilter : EverythingSendFilter
{
public override List<string> GetObjectIds() => new(); // TODO
public override bool CheckExpiry(string[] changedObjectIds) => true;
}
@@ -0,0 +1,10 @@
using Speckle.Connectors.DUI.Models.Card.SendFilter;
namespace Speckle.Connectors.ArcGIS.Filters;
public class ArcGISSelectionFilter : DirectSelectionSendFilter
{
public override List<string> GetObjectIds() => SelectedObjectIds;
public override bool CheckExpiry(string[] changedObjectIds) => SelectedObjectIds.Intersect(changedObjectIds).Any();
}
@@ -0,0 +1,21 @@
using System.IO;
using Speckle.Connectors.Utils;
using Speckle.Core.Kits;
namespace Speckle.Connectors.ArcGIS.HostApp;
//poc: dupe code bewtween connectors
public class ArcGISSettings
{
public ArcGISSettings(HostApplication hostAppInfo, HostAppVersion hostAppVersion)
{
HostAppInfo = hostAppInfo;
HostAppVersion = hostAppVersion;
Modules = new[] { new DirectoryInfo(typeof(ArcGISSettings).Assembly.Location).Parent.NotNull().FullName }; //poc: Net6 requires us to use this `location` property rather than ToString, should we use this everywhere?
}
public HostApplication HostAppInfo { get; private set; }
public HostAppVersion HostAppVersion { get; private set; }
public IReadOnlyList<string> Modules { get; private set; }
}
@@ -0,0 +1,9 @@
using ArcGIS.Desktop.Framework.Threading.Tasks;
using Speckle.Connectors.Utils.Operations;
namespace Speckle.Connectors.ArcGIS.HostApp;
public class SyncToQueuedTask : ISyncToThread
{
public Task<T> RunOnThread<T>(Func<T> func) => QueuedTask.Run(func);
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 524 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 948 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 714 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

@@ -0,0 +1,293 @@
using System.Diagnostics.Contracts;
using ArcGIS.Desktop.Mapping;
using Speckle.Connectors.Utils.Builders;
using Speckle.Converters.Common;
using Speckle.Core.Logging;
using Speckle.Core.Models;
using Speckle.Converters.ArcGIS3.Utils;
using ArcGIS.Core.Geometry;
using Objects.GIS;
using Speckle.Connectors.Utils.Conversion;
using Speckle.Core.Models.GraphTraversal;
using Speckle.Converters.ArcGIS3;
using RasterLayer = Objects.GIS.RasterLayer;
using Speckle.Connectors.ArcGIS.Utils;
namespace Speckle.Connectors.ArcGIS.Operations.Receive;
public class ArcGISHostObjectBuilder : IHostObjectBuilder
{
private readonly IRootToHostConverter _converter;
private readonly INonNativeFeaturesUtils _nonGisFeaturesUtils;
// POC: figure out the correct scope to only initialize on Receive
private readonly IConversionContextStack<ArcGISDocument, Unit> _contextStack;
private readonly GraphTraversal _traverseFunction;
public ArcGISHostObjectBuilder(
IRootToHostConverter converter,
IConversionContextStack<ArcGISDocument, Unit> contextStack,
INonNativeFeaturesUtils nonGisFeaturesUtils,
GraphTraversal traverseFunction
)
{
_converter = converter;
_contextStack = contextStack;
_nonGisFeaturesUtils = nonGisFeaturesUtils;
_traverseFunction = traverseFunction;
}
public HostObjectBuilderResult Build(
Base rootObject,
string projectName,
string modelName,
Action<string, double?>? onOperationProgressed,
CancellationToken cancellationToken
)
{
// Prompt the UI conversion started. Progress bar will swoosh.
onOperationProgressed?.Invoke("Converting", null);
var objectsToConvert = _traverseFunction
.Traverse(rootObject)
.Where(ctx => ctx.Current is not Collection || IsGISType(ctx.Current))
.Where(ctx => HasGISParent(ctx) is false)
.ToList();
int allCount = objectsToConvert.Count;
int count = 0;
Dictionary<TraversalContext, ObjectConversionTracker> conversionTracker = new();
// 1. convert everything
List<ReceiveConversionResult> results = new(objectsToConvert.Count);
List<string> bakedObjectIds = new();
foreach (TraversalContext ctx in objectsToConvert)
{
string[] path = GetLayerPath(ctx);
Base obj = ctx.Current;
cancellationToken.ThrowIfCancellationRequested();
try
{
if (IsGISType(obj))
{
string nestedLayerPath = $"{string.Join("\\", path)}";
string datasetId = (string)_converter.Convert(obj);
conversionTracker[ctx] = new ObjectConversionTracker(obj, nestedLayerPath, datasetId);
}
else
{
string nestedLayerPath = $"{string.Join("\\", path)}\\{obj.speckle_type.Split(".")[^1]}";
Geometry converted = (Geometry)_converter.Convert(obj);
conversionTracker[ctx] = new ObjectConversionTracker(obj, nestedLayerPath, converted);
}
}
catch (Exception ex) when (!ex.IsFatal()) // DO NOT CATCH SPECIFIC STUFF, conversion errors should be recoverable
{
results.Add(new(Status.ERROR, obj, null, null, ex));
}
onOperationProgressed?.Invoke("Converting", (double)++count / allCount);
}
// 2. convert Database entries with non-GIS geometry datasets
onOperationProgressed?.Invoke("Writing to Database", null);
_nonGisFeaturesUtils.WriteGeometriesToDatasets(conversionTracker);
// Create main group layer
Dictionary<string, GroupLayer> createdLayerGroups = new();
Map map = _contextStack.Current.Document.Map;
GroupLayer groupLayer = LayerFactory.Instance.CreateGroupLayer(map, 0, $"{projectName}: {modelName}");
createdLayerGroups["Basic Speckle Group"] = groupLayer; // key doesn't really matter here
// 3. add layer and tables to the Table Of Content
int bakeCount = 0;
Dictionary<string, MapMember> bakedMapMembers = new();
onOperationProgressed?.Invoke("Adding to Map", bakeCount);
foreach (var item in conversionTracker)
{
cancellationToken.ThrowIfCancellationRequested();
var trackerItem = conversionTracker[item.Key]; // updated tracker object
// BAKE OBJECTS HERE
if (trackerItem.Exception != null)
{
results.Add(new(Status.ERROR, trackerItem.Base, null, null, trackerItem.Exception));
}
else if (trackerItem.DatasetId == null)
{
results.Add(
new(
Status.ERROR,
trackerItem.Base,
null,
null,
new ArgumentException($"Unknown error: Dataset not created for {trackerItem.Base.speckle_type}")
)
);
}
else if (bakedMapMembers.TryGetValue(trackerItem.DatasetId, out MapMember? value))
{
// add layer and layer URI to tracker
trackerItem.AddConvertedMapMember(value);
trackerItem.AddLayerURI(value.URI);
conversionTracker[item.Key] = trackerItem; // not necessary atm, but needed if we use conversionTracker further
// only add a report item
AddResultsFromTracker(trackerItem, results);
}
else
{
// add layer to Map
MapMember mapMember = AddDatasetsToMap(trackerItem, createdLayerGroups);
// add layer and layer URI to tracker
trackerItem.AddConvertedMapMember(mapMember);
trackerItem.AddLayerURI(mapMember.URI);
conversionTracker[item.Key] = trackerItem; // not necessary atm, but needed if we use conversionTracker further
// add layer URI to bakedIds
bakedObjectIds.Add(trackerItem.MappedLayerURI == null ? "" : trackerItem.MappedLayerURI);
// mark dataset as already created
bakedMapMembers[trackerItem.DatasetId] = mapMember;
// add report item
AddResultsFromTracker(trackerItem, results);
}
onOperationProgressed?.Invoke("Adding to Map", (double)++bakeCount / conversionTracker.Count);
}
bakedObjectIds.AddRange(createdLayerGroups.Values.Select(x => x.URI));
// TODO: validated a correct set regarding bakedobject ids
return new(bakedObjectIds, results);
}
private void AddResultsFromTracker(ObjectConversionTracker trackerItem, List<ReceiveConversionResult> results)
{
if (trackerItem.MappedLayerURI == null) // should not happen
{
results.Add(
new(
Status.ERROR,
trackerItem.Base,
null,
null,
new ArgumentException($"Created Layer URI not found for {trackerItem.Base.speckle_type}")
)
);
}
else
{
// encode layer ID and ID of its feature in 1 object represented as string
ObjectID objectId = new(trackerItem.MappedLayerURI, trackerItem.DatasetRow);
if (trackerItem.HostAppGeom != null) // individual hostAppGeometry
{
results.Add(
new(
Status.SUCCESS,
trackerItem.Base,
objectId.ObjectIdToString(),
trackerItem.HostAppGeom.GetType().ToString()
)
);
}
else // hostApp Layers
{
results.Add(
new(
Status.SUCCESS,
trackerItem.Base,
objectId.ObjectIdToString(),
trackerItem.HostAppMapMember?.GetType().ToString()
)
);
}
}
}
private MapMember AddDatasetsToMap(
ObjectConversionTracker trackerItem,
Dictionary<string, GroupLayer> createdLayerGroups
)
{
// get layer details
string? datasetId = trackerItem.DatasetId; // should not ne null here
Uri uri = new($"{_contextStack.Current.Document.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));
GroupLayer 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 layer = LayerFactory.Instance.CreateLayer(uri, groupLayer, layerName: shortName);
layer.SetExpanded(true);
return layer;
}
catch (ArgumentException)
{
var table = StandaloneTableFactory.Instance.CreateStandaloneTable(uri, groupLayer, tableName: shortName);
return table;
}
}
private GroupLayer CreateNestedGroupLayer(string nestedLayerPath, Dictionary<string, GroupLayer> createdLayerGroups)
{
GroupLayer lastGroup = createdLayerGroups.FirstOrDefault().Value;
if (lastGroup == null) // if layer not found
{
throw new InvalidOperationException("Speckle Layer Group not found");
}
// iterate through each nested level
string createdGroupPath = "";
var allPathElements = nestedLayerPath.Split("\\").Where(x => !string.IsNullOrEmpty(x));
foreach (string pathElement in allPathElements)
{
createdGroupPath += "\\" + pathElement;
if (createdLayerGroups.TryGetValue(createdGroupPath, out var existingGroupLayer))
{
lastGroup = existingGroupLayer;
}
else
{
// create new GroupLayer under last found Group, named with last pathElement
lastGroup = LayerFactory.Instance.CreateGroupLayer(lastGroup, 0, pathElement);
lastGroup.SetExpanded(true);
}
createdLayerGroups[createdGroupPath] = lastGroup;
}
return lastGroup;
}
[Pure]
private static string[] GetLayerPath(TraversalContext context)
{
string[] collectionBasedPath = context.GetAscendantOfType<Collection>().Select(c => c.name).ToArray();
string[] reverseOrderPath =
collectionBasedPath.Length != 0 ? collectionBasedPath : context.GetPropertyPath().ToArray();
var originalPath = reverseOrderPath.Reverse().ToArray();
return originalPath.Where(x => !string.IsNullOrEmpty(x)).ToArray();
}
[Pure]
private static bool HasGISParent(TraversalContext context)
{
List<Base> gisLayers = context.GetAscendants().Where(IsGISType).Where(obj => obj != context.Current).ToList();
return gisLayers.Count > 0;
}
[Pure]
private static bool IsGISType(Base obj)
{
return obj is RasterLayer or VectorLayer;
}
}
@@ -0,0 +1,87 @@
using System.Diagnostics;
using ArcGIS.Desktop.Mapping;
using Speckle.Autofac.DependencyInjection;
using Speckle.Connectors.Utils.Builders;
using Speckle.Connectors.Utils.Caching;
using Speckle.Connectors.Utils.Conversion;
using Speckle.Connectors.Utils.Operations;
using Speckle.Converters.Common;
using Speckle.Core.Logging;
using Speckle.Core.Models;
namespace Speckle.Connectors.ArcGis.Operations.Send;
/// <summary>
/// Stateless builder object to turn an ISendFilter into a <see cref="Base"/> object
/// </summary>
public class ArcGISRootObjectBuilder : IRootObjectBuilder<MapMember>
{
private readonly IUnitOfWorkFactory _unitOfWorkFactory;
private readonly ISendConversionCache _sendConversionCache;
public ArcGISRootObjectBuilder(IUnitOfWorkFactory unitOfWorkFactory, ISendConversionCache sendConversionCache)
{
_unitOfWorkFactory = unitOfWorkFactory;
_sendConversionCache = sendConversionCache;
}
public RootObjectBuilderResult Build(
IReadOnlyList<MapMember> objects,
SendInfo sendInfo,
Action<string, double?>? onOperationProgressed = null,
CancellationToken ct = default
)
{
// POC: does this feel like the right place? I am wondering if this should be called from within send/rcv?
// begin the unit of work
using var uow = _unitOfWorkFactory.Resolve<IRootToSpeckleConverter>();
var converter = uow.Service;
int count = 0;
Collection rootObjectCollection = new(); //TODO: Collections
List<SendConversionResult> results = new(objects.Count);
var cacheHitCount = 0;
foreach (MapMember mapMember in objects)
{
ct.ThrowIfCancellationRequested();
var collectionHost = rootObjectCollection;
var applicationId = mapMember.URI;
try
{
Base converted;
if (_sendConversionCache.TryGetValue(sendInfo.ProjectId, applicationId, out ObjectReference value))
{
converted = value;
cacheHitCount++;
}
else
{
converted = converter.Convert(mapMember);
converted.applicationId = applicationId;
}
// add to host
collectionHost.elements.Add(converted);
results.Add(new(Status.SUCCESS, applicationId, mapMember.GetType().Name, converted));
}
catch (Exception ex) when (!ex.IsFatal())
{
results.Add(new(Status.ERROR, applicationId, mapMember.GetType().Name, null, ex));
// POC: add logging
}
onOperationProgressed?.Invoke("Converting", (double)++count / objects.Count);
}
// POC: Log would be nice, or can be removed.
Debug.WriteLine(
$"Cache hit count {cacheHitCount} out of {objects.Count} ({(double)cacheHitCount / objects.Count})"
);
return new(rootObjectCollection, results);
}
}
@@ -0,0 +1,9 @@
{
"profiles": {
"Speckle.Connectors.ArcGIS3": {
"commandName": "Executable",
"executablePath": "C:\\Program Files\\ArcGIS\\Pro\\bin\\ArcGISPro.exe",
"commandLineArgs": ""
}
}
}
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
<PlatformTarget>x64</PlatformTarget>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<RootNameSpace>Speckle.Connectors.ArcGIS</RootNameSpace>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
</PropertyGroup>
<ItemGroup>
<Content Include="Config.daml"/>
<Content Include="Images\s2logo_16.png"/>
<Content Include="Images\s2logo_32.png"/>
<Content Include="DarkImages\s2logo_16.png"/>
<Content Include="DarkImages\s2logo_32.png"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Esri.ArcGISPro.Extensions30" IncludeAssets="compile"/>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
</ItemGroup>
<ItemGroup>
<ProjectReference
Include="..\..\..\Converters\ArcGIS\Speckle.Converters.ArcGIS3.DependencyInjection\Speckle.Converters.ArcGIS3.DependencyInjection.csproj"/>
<ProjectReference
Include="..\..\..\DUI3\Speckle.Connectors.DUI.WebView\Speckle.Connectors.DUI.WebView.csproj"/>
<ProjectReference
Include="..\..\..\Sdk\Speckle.Connectors.Utils\Speckle.Connectors.Utils.csproj"/>
</ItemGroup>
<Import Project="Esri.ArcGISPro.Extensions30.Speckle.targets"/>
</Project>
@@ -0,0 +1,42 @@
using ArcGIS.Desktop.Framework;
using ArcGIS.Desktop.Framework.Contracts;
namespace Speckle.Connectors.ArcGIS;
internal sealed class SpeckleDUI3ViewModel : DockPane
{
private const string DOCKPANE_ID = "SpeckleDUI3_SpeckleDUI3";
internal static void Create()
{
var pane = FrameworkApplication.DockPaneManager.Find(DOCKPANE_ID);
pane?.Activate();
}
/// <summary>
/// Called when the pane is initialized.
/// </summary>
protected override async Task InitializeAsync()
{
await base.InitializeAsync().ConfigureAwait(false);
}
/// <summary>
/// Called when the pane is uninitialized.
/// </summary>
protected override async Task UninitializeAsync()
{
await base.UninitializeAsync().ConfigureAwait(false);
}
}
/// <summary>
/// Button implementation to create a new instance of the pane and activate it.
/// </summary>
internal sealed class SpeckleDUI3OpenButton : Button
{
protected override void OnClick()
{
SpeckleDUI3ViewModel.Create();
}
}
@@ -0,0 +1,17 @@
using System.Windows.Controls;
using Speckle.Connectors.DUI.WebView;
namespace Speckle.Connectors.ArcGIS;
public class SpeckleDUI3Wrapper : UserControl
{
public SpeckleDUI3Wrapper()
{
Initialize();
}
private void Initialize()
{
Content = SpeckleModule.Current.Container.Resolve<DUI3ControlWebView>();
}
}
@@ -0,0 +1,51 @@
using System.Reflection;
using ArcGIS.Desktop.Framework;
using Speckle.Autofac;
using Speckle.Autofac.DependencyInjection;
using Speckle.Connectors.ArcGIS.HostApp;
using Speckle.Core.Kits;
using Module = ArcGIS.Desktop.Framework.Contracts.Module;
namespace Speckle.Connectors.ArcGIS;
/// <summary>
/// This sample shows how to implement pane that contains an Edge WebView2 control using the built-in ArcGIS Pro SDK's WebBrowser control. For details on how to utilize the WebBrowser control in an add-in see here: https://github.com/Esri/arcgis-pro-sdk/wiki/ProConcepts-Framework#webbrowser For details on how to utilize the Microsoft Edge web browser control in an add-in see here: https://github.com/Esri/arcgis-pro-sdk/wiki/ProConcepts-Framework#webbrowser-control
/// </summary>
internal sealed class SpeckleModule : Module
{
private static SpeckleModule? s_this;
/// <summary>
/// Retrieve the singleton instance to this module here
/// </summary>
public static SpeckleModule Current =>
s_this ??= (SpeckleModule)FrameworkApplication.FindModule("ConnectorArcGIS_Module");
public SpeckleContainer Container { get; }
public SpeckleModule()
{
AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolver.OnAssemblyResolve<SpeckleModule>;
var builder = SpeckleContainerBuilder.CreateInstance();
// Register Settings
var arcgisSettings = new ArcGISSettings(HostApplications.ArcGIS, HostAppVersion.v3);
Container = builder
.LoadAutofacModules(Assembly.GetExecutingAssembly(), arcgisSettings.Modules)
.AddSingleton(arcgisSettings)
.Build();
}
/// <summary>
/// Called by Framework when ArcGIS Pro is closing
/// </summary>
/// <returns>False to prevent Pro from closing, otherwise True</returns>
protected override bool CanUnload()
{
//TODO - add your business logic
//return false to ~cancel~ Application close
return true;
}
}
@@ -0,0 +1,131 @@
using System.Xml.Linq;
using ArcGIS.Desktop.Core.Events;
using ArcGIS.Desktop.Framework.Threading.Tasks;
using ArcGIS.Desktop.Mapping;
using ArcGIS.Desktop.Mapping.Events;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.Utils;
using Speckle.Newtonsoft.Json;
namespace Speckle.Connectors.ArcGIS.Utils;
public class ArcGISDocumentStore : DocumentModelStore
{
public ArcGISDocumentStore(
JsonSerializerSettings serializerOption,
ITopLevelExceptionHandler topLevelExceptionHandler
)
: base(serializerOption, true)
{
ActiveMapViewChangedEvent.Subscribe(a => topLevelExceptionHandler.CatchUnhandled(() => OnMapViewChanged(a)), true);
ProjectSavingEvent.Subscribe(
_ =>
{
topLevelExceptionHandler.CatchUnhandled(OnProjectSaving);
return Task.CompletedTask;
},
true
);
ProjectClosingEvent.Subscribe(
_ =>
{
topLevelExceptionHandler.CatchUnhandled(OnProjectClosing);
return Task.CompletedTask;
},
true
);
// in case plugin was loaded into already opened Map, read metadata from the current Map
if (IsDocumentInit == false && MapView.Active != null)
{
IsDocumentInit = true;
ReadFromFile();
OnDocumentChanged();
}
}
private void OnProjectClosing()
{
if (MapView.Active is null)
{
return;
}
WriteToFile();
}
private void OnProjectSaving()
{
if (MapView.Active is not null)
{
WriteToFile();
}
}
/// <summary>
/// On map view switch, this event trigger twice, first for outgoing view, second for incoming view.
/// </summary>
private void OnMapViewChanged(ActiveMapViewChangedEventArgs args)
{
if (args.IncomingView is null)
{
return;
}
IsDocumentInit = true;
ReadFromFile();
OnDocumentChanged();
}
public override void WriteToFile()
{
Map map = MapView.Active.Map;
QueuedTask.Run(() =>
{
// Read existing metadata - To prevent messing existing metadata. 🤞 Hope other add-in developers will do same :D
var existingMetadata = map.GetMetadata();
// Parse existing metadata
XDocument existingXmlDocument = !string.IsNullOrEmpty(existingMetadata)
? XDocument.Parse(existingMetadata)
: new XDocument(new XElement("metadata"));
string serializedModels = Serialize();
XElement xmlModelCards = new("SpeckleModelCards", serializedModels);
// Check if SpeckleModelCards element already exists at root and update it
var speckleModelCardsElement = existingXmlDocument.Root?.Element("SpeckleModelCards");
if (speckleModelCardsElement != null)
{
speckleModelCardsElement.ReplaceWith(xmlModelCards);
}
else
{
existingXmlDocument.Root?.Add(xmlModelCards);
}
map.SetMetadata(existingXmlDocument.ToString());
});
}
public override void ReadFromFile()
{
Map map = MapView.Active.Map;
QueuedTask.Run(() =>
{
var metadata = map.GetMetadata();
var root = XDocument.Parse(metadata).Root;
var element = root?.Element("SpeckleModelCards");
if (element is null)
{
Models = new();
return;
}
string modelsString = element.Value;
Models = Deserialize(modelsString).NotNull();
});
}
}
@@ -0,0 +1,16 @@
using ArcGIS.Desktop.Mapping;
namespace Speckle.Connectors.ArcGIS.Utils;
// bind together a layer object on the map, and auto-assigned ID if the specific feature
public readonly struct MapMemberFeature
{
public int? FeatureId { get; } // unique feature id (start from 0) of a feature in the layer
public MapMember MapMember { get; } // layer object on the Map
public MapMemberFeature(MapMember mapMember, int? featureId)
{
MapMember = mapMember;
FeatureId = featureId;
}
}
@@ -0,0 +1,38 @@
namespace Speckle.Connectors.ArcGIS.Utils;
// this struct is needed to be able to parse single-string value into IDs of both a layer, and it's individual feature
public struct ObjectID
{
private const string FEATURE_ID_SEPARATOR = "__speckleFeatureId__";
public string MappedLayerURI { get; } // unique ID of the layer on the map
public int? FeatureId { get; } // unique feature id (start from 0) of a feature in the layer
public ObjectID(string encodedId)
{
List<string> stringParts = encodedId.Split(FEATURE_ID_SEPARATOR).ToList();
MappedLayerURI = stringParts[0];
FeatureId = null;
if (stringParts.Count > 1)
{
FeatureId = Convert.ToInt32(stringParts[1]);
}
}
public ObjectID(string layerId, int? featureId)
{
MappedLayerURI = layerId;
FeatureId = featureId;
}
public readonly string ObjectIdToString()
{
if (FeatureId == null)
{
return $"{MappedLayerURI}";
}
else
{
return $"{MappedLayerURI}{FEATURE_ID_SEPARATOR}{FeatureId}";
}
}
}
@@ -0,0 +1,510 @@
{
"version": 2,
"dependencies": {
"net6.0-windows7.0": {
"Esri.ArcGISPro.Extensions30": {
"type": "Direct",
"requested": "[3.2.0.49743, )",
"resolved": "3.2.0.49743",
"contentHash": "fmnYm+mD14Cz0Uqh1ij37SfLJerkyFHK5581y5tXT/l3H2ZvUmVuuxjYquXzyzj9p7IexQzMW4xCpxe+mD922g=="
},
"Microsoft.Extensions.Logging.Abstractions": {
"type": "Direct",
"requested": "[7.0.0, )",
"resolved": "7.0.0",
"contentHash": "kmn78+LPVMOWeITUjIlfxUPDsI0R6G0RkeAMBmQxAJ7vBJn4q2dTva7pWi65ceN5vPGjJ9q/Uae2WKgvfktJAw=="
},
"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.5, )",
"resolved": "0.9.5",
"contentHash": "oU/L7pN1R7q8KkbrpQ3WJnHirPHqn+9DEA7asOcUiggV5dzVg1A/VYs7GOSusD24njxXh03tE3a2oTLOjt3cVg=="
},
"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.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.AspNetCore.Http": {
"type": "Transitive",
"resolved": "2.2.2",
"contentHash": "BAibpoItxI5puk7YJbIGj95arZueM8B8M5xT1fXBn3hb3L2G3ucrZcYXv1gXdaroLbntUs8qeV8iuBrpjQsrKw==",
"dependencies": {
"Microsoft.AspNetCore.Http.Abstractions": "2.2.0",
"Microsoft.AspNetCore.WebUtilities": "2.2.0",
"Microsoft.Extensions.ObjectPool": "2.2.0",
"Microsoft.Extensions.Options": "2.2.0",
"Microsoft.Net.Http.Headers": "2.2.0"
}
},
"Microsoft.AspNetCore.Http.Abstractions": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "Nxs7Z1q3f1STfLYKJSVXCs1iBl+Ya6E8o4Oy1bCxJ/rNI44E/0f6tbsrVqAWfB7jlnJfyaAtIalBVxPKUPQb4Q==",
"dependencies": {
"Microsoft.AspNetCore.Http.Features": "2.2.0",
"System.Text.Encodings.Web": "4.5.0"
}
},
"Microsoft.AspNetCore.Http.Features": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "ziFz5zH8f33En4dX81LW84I6XrYXKf9jg6aM39cM+LffN9KJahViKZ61dGMSO2gd3e+qe5yBRwsesvyqlZaSMg==",
"dependencies": {
"Microsoft.Extensions.Primitives": "2.2.0"
}
},
"Microsoft.AspNetCore.WebUtilities": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "9ErxAAKaDzxXASB/b5uLEkLgUWv1QbeVxyJYEHQwMaxXOeFFVkQxiq8RyfVcifLU7NR0QY0p3acqx4ZpYfhHDg==",
"dependencies": {
"Microsoft.Net.Http.Headers": "2.2.0",
"System.Text.Encodings.Web": "4.5.0"
}
},
"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.5",
"contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==",
"dependencies": {
"Microsoft.Data.Sqlite.Core": "7.0.5",
"SQLitePCLRaw.bundle_e_sqlite3": "2.1.4"
}
},
"Microsoft.Data.Sqlite.Core": {
"type": "Transitive",
"resolved": "7.0.5",
"contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==",
"dependencies": {
"SQLitePCLRaw.core": "2.1.4"
}
},
"Microsoft.Extensions.DependencyInjection": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "elNeOmkeX3eDVG6pYVeV82p29hr+UKDaBhrZyWvWLw/EVZSYEkZlQdkp0V39k/Xehs2Qa0mvoCvkVj3eQxNQ1Q==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "h3j/QfmFN4S0w4C2A6X7arXij/M/OVw3uQHSOFxnND4DyAzO1F9eMX7Eti7lU/OkSthEE0WzRsfT/Dmx86jzCw=="
},
"Microsoft.Extensions.Logging": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "Nw2muoNrOG5U5qa2ZekXwudUn2BJcD41e65zwmDHb1fQegTX66UokLWZkJRpqSSHXDOWZ5V0iqhbxOEky91atA==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "7.0.0",
"Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0",
"Microsoft.Extensions.Logging.Abstractions": "7.0.0",
"Microsoft.Extensions.Options": "7.0.0"
}
},
"Microsoft.Extensions.ObjectPool": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "gA8H7uQOnM5gb+L0uTNjViHYr+hRDqCdfugheGo/MxQnuHzmhhzCBTIPm19qL1z1Xe0NEMabfcOBGv9QghlZ8g=="
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "lP1yBnTTU42cKpMozuafbvNtQ7QcBjr/CcK3bYOGEMH55Fjt+iecXjT6chR7vbgCMqy3PG3aNQSZgo/EuY/9qQ==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0",
"Microsoft.Extensions.Primitives": "7.0.0"
}
},
"Microsoft.Extensions.Primitives": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "um1KU5kxcRp3CNuI8o/GrZtD4AIOXDk+RLsytjZ9QPok3ttLUelLKpilVPuaFT3TFjOhSibUAso0odbOaCDj3Q==",
"dependencies": {
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"Microsoft.Net.Http.Headers": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "iZNkjYqlo8sIOI0bQfpsSoMTmB/kyvmV2h225ihyZT33aTp48ZpF6qYnXxzSXmHt8DpBAwBTX+1s1UFLbYfZKg==",
"dependencies": {
"Microsoft.Extensions.Primitives": "2.2.0",
"System.Buffers": "4.5.0"
}
},
"Microsoft.SourceLink.Common": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
},
"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"
}
},
"Sentry": {
"type": "Transitive",
"resolved": "3.33.0",
"contentHash": "8vbD2o6IR2wrRrkSiRbnodWGWUOqIlwYtzpjvPNOb5raJdOf+zxMwfS8f6nx9bmrTTfDj7KrCB8C/5OuicAc8A=="
},
"Sentry.Serilog": {
"type": "Transitive",
"resolved": "3.33.0",
"contentHash": "V8BU7QGWg2qLYfNPqtuTBhC1opysny5l+Ifp6J6PhOeAxU0FssR7nYfbJVetrnLIoh2rd3DlJ6hHYYQosQYcUQ==",
"dependencies": {
"Sentry": "3.33.0",
"Serilog": "2.10.0"
}
},
"Serilog": {
"type": "Transitive",
"resolved": "2.12.0",
"contentHash": "xaiJLIdu6rYMKfQMYUZgTy8YK7SMZjB4Yk50C/u//Z4OsvxkUfSPJy4nknfvwAC34yr13q7kcyh4grbwhSxyZg=="
},
"Serilog.Enrichers.ClientInfo": {
"type": "Transitive",
"resolved": "1.3.0",
"contentHash": "mTc7PM+wC9Hr7LWSwqt5mmnlAr7RJs+eTb3PGPRhwdOackk95MkhUZognuxXEdlW19HAFNmEBTSBY5DfLwM8jQ==",
"dependencies": {
"Microsoft.AspNetCore.Http": "2.2.2",
"Serilog": "2.9.0"
}
},
"Serilog.Exceptions": {
"type": "Transitive",
"resolved": "8.4.0",
"contentHash": "nc/+hUw3lsdo0zCj0KMIybAu7perMx79vu72w0za9Nsi6mWyNkGXxYxakAjWB7nEmYL6zdmhEQRB4oJ2ALUeug==",
"dependencies": {
"Serilog": "2.8.0",
"System.Reflection.TypeExtensions": "4.7.0"
}
},
"Serilog.Formatting.Compact": {
"type": "Transitive",
"resolved": "1.1.0",
"contentHash": "pNroKVjo+rDqlxNG5PXkRLpfSCuDOBY0ri6jp9PLe505ljqwhwZz8ospy2vWhQlFu5GkIesh3FcDs4n7sWZODA==",
"dependencies": {
"Serilog": "2.8.0"
}
},
"Serilog.Sinks.Console": {
"type": "Transitive",
"resolved": "4.1.0",
"contentHash": "K6N5q+5fetjnJPvCmkWOpJ/V8IEIoMIB1s86OzBrbxwTyHxdx3pmz4H+8+O/Dc/ftUX12DM1aynx/dDowkwzqg==",
"dependencies": {
"Serilog": "2.10.0"
}
},
"Serilog.Sinks.File": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==",
"dependencies": {
"Serilog": "2.10.0"
}
},
"Serilog.Sinks.PeriodicBatching": {
"type": "Transitive",
"resolved": "3.1.0",
"contentHash": "NDWR7m3PalVlGEq3rzoktrXikjFMLmpwF0HI4sowo8YDdU+gqPlTHlDQiOGxHfB0sTfjPA9JjA7ctKG9zqjGkw==",
"dependencies": {
"Serilog": "2.0.0"
}
},
"Serilog.Sinks.Seq": {
"type": "Transitive",
"resolved": "5.2.2",
"contentHash": "1Csmo5ua7NKUe0yXUx+zsRefjAniPWcXFhUXxXG8pwo0iMiw2gjn9SOkgYnnxbgWqmlGv236w0N/dHc2v5XwMg==",
"dependencies": {
"Serilog": "2.12.0",
"Serilog.Formatting.Compact": "1.1.0",
"Serilog.Sinks.File": "5.0.0",
"Serilog.Sinks.PeriodicBatching": "3.1.0"
}
},
"SerilogTimings": {
"type": "Transitive",
"resolved": "3.0.1",
"contentHash": "Zs28eTgszAMwpIrbBnWHBI50yuxL50p/dmAUWmy75+axdZYK/Sjm5/5m1N/CisR8acJUhTVcjPZrsB1P5iv0Uw==",
"dependencies": {
"Serilog": "2.10.0"
}
},
"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.e_sqlite3": "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.e_sqlite3": {
"type": "Transitive",
"resolved": "2.1.4",
"contentHash": "CSlb5dUp1FMIkez9Iv5EXzpeq7rHryVNqwJMWnpq87j9zWZexaEMdisDktMsnnrzKM6ahNrsTkjqNodTBPBxtQ==",
"dependencies": {
"SQLitePCLRaw.core": "2.1.4"
}
},
"System.Buffers": {
"type": "Transitive",
"resolved": "4.5.0",
"contentHash": "pL2ChpaRRWI/p4LXyy4RgeWlYF2sgfj/pnVMvBqwNFr5cXg7CXNnWZWxrOONLg8VGdFB8oB+EG2Qw4MLgTOe+A=="
},
"System.DoubleNumerics": {
"type": "Transitive",
"resolved": "3.1.3",
"contentHash": "KRKEM/L3KBodjA9VOg3EifFVWUY6EOqaMB05UvPEDm7Zeby/kZW+4kdWUEPzW6xtkwf46p661L9NrbeeQhtLzw==",
"dependencies": {
"NETStandard.Library": "1.6.1"
}
},
"System.Memory": {
"type": "Transitive",
"resolved": "4.5.3",
"contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA=="
},
"System.Reactive": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ=="
},
"System.Reflection.TypeExtensions": {
"type": "Transitive",
"resolved": "4.7.0",
"contentHash": "VybpaOQQhqE6siHppMktjfGBw1GCwvCqiufqmP8F1nj7fTUNtW35LOEt3UZTEsECfo+ELAl/9o9nJx3U91i7vA=="
},
"System.Runtime.CompilerServices.Unsafe": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
},
"System.Text.Encodings.Web": {
"type": "Transitive",
"resolved": "4.5.0",
"contentHash": "Xg4G4Indi4dqP1iuAiMSwpiWS54ZghzR644OtsRCm/m/lBMG8dUBhLVN7hLm8NNrNTR+iGbshCPTwrvxZPlm4g=="
},
"speckle.autofac": {
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[7.0.0, )"
}
},
"speckle.connectors.dui": {
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[7.0.0, )",
"Speckle.Autofac": "[2.0.999-local, )",
"Speckle.Connectors.Utils": "[2.0.999-local, )",
"Speckle.Core": "[3.0.1-alpha.14, )",
"System.Threading.Tasks.Dataflow": "[6.0.0, )"
}
},
"speckle.connectors.dui.webview": {
"type": "Project",
"dependencies": {
"Microsoft.Web.WebView2": "[1.0.1823.32, )",
"Speckle.Connectors.DUI": "[2.0.999-local, )"
}
},
"speckle.connectors.utils": {
"type": "Project",
"dependencies": {
"Serilog.Extensions.Logging": "[7.0.0, )",
"Speckle.Autofac": "[2.0.999-local, )",
"Speckle.Core": "[3.0.1-alpha.14, )"
}
},
"speckle.converters.arcgis3": {
"type": "Project",
"dependencies": {
"Esri.ArcGISPro.Extensions30": "[3.2.0.49743, )",
"Speckle.Converters.Common": "[2.0.999-local, )"
}
},
"speckle.converters.arcgis3.dependencyinjection": {
"type": "Project",
"dependencies": {
"Autofac": "[5.2.0, )",
"Speckle.Converters.ArcGIS3": "[2.0.999-local, )",
"Speckle.Converters.Common.DependencyInjection": "[2.0.999-local, )"
}
},
"speckle.converters.common": {
"type": "Project",
"dependencies": {
"Speckle.Autofac": "[2.0.999-local, )",
"Speckle.Objects": "[3.0.1-alpha.14, )"
}
},
"speckle.converters.common.dependencyinjection": {
"type": "Project",
"dependencies": {
"Speckle.Autofac": "[2.0.999-local, )",
"Speckle.Converters.Common": "[2.0.999-local, )"
}
},
"Autofac": {
"type": "CentralTransitive",
"requested": "[5.2.0, )",
"resolved": "5.2.0",
"contentHash": "V8dBH0dsv75uDzl7Sw+HkhKDPUw2eXnlMjcSVMH+tLo2s67MpTKGyDj1pDcpR+IF2u4YRs0s3/x7R88YJzIWvg=="
},
"Microsoft.Web.WebView2": {
"type": "CentralTransitive",
"requested": "[1.0.1823.32, )",
"resolved": "1.0.1823.32",
"contentHash": "ppRcWBUNggFIqyJp7PfgS4Oe8/7yli/mHcTNDOaqo3ZzJWnv9AOr0WE9ceEP5SPs1M7CQ3QvdGMR7KNz0v09EA=="
},
"Serilog.Extensions.Logging": {
"type": "CentralTransitive",
"requested": "[7.0.0, )",
"resolved": "7.0.0",
"contentHash": "9faU0zNQqU7I6soVhLUMYaGNpgWv6cKlKb2S5AnS8gXxzW/em5Ladm/6FMrWTnX41cdbdGPOWNAo6adi4WaJ6A==",
"dependencies": {
"Microsoft.Extensions.Logging": "7.0.0",
"Serilog": "2.12.0"
}
},
"Speckle.Core": {
"type": "CentralTransitive",
"requested": "[3.0.1-alpha.14, )",
"resolved": "3.0.1-alpha.14",
"contentHash": "RzQPVIGFFkKvG56YLr8ACtiwdWJE6IJ9vCQ4qHa0PIsUEpfzAIAi59jnzqtByOFC0FiFrFPow9bkfzylaZorAA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Polly": "7.2.3",
"Polly.Contrib.WaitAndRetry": "1.1.1",
"Polly.Extensions.Http": "3.0.0",
"Sentry": "3.33.0",
"Sentry.Serilog": "3.33.0",
"Serilog": "2.12.0",
"Serilog.Enrichers.ClientInfo": "1.3.0",
"Serilog.Exceptions": "8.4.0",
"Serilog.Sinks.Console": "4.1.0",
"Serilog.Sinks.Seq": "5.2.2",
"SerilogTimings": "3.0.1",
"Speckle.Newtonsoft.Json": "13.0.2",
"System.DoubleNumerics": "3.1.3"
}
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.0.1-alpha.14, )",
"resolved": "3.0.1-alpha.14",
"contentHash": "z38LGryMvh7iU1uBW+4uo5DwsB3CwRgLt2uFexWFx3mPSid+A0l5XcJzOgLwgFhNl6B42Ryz4ezBsddTp1Uc/g==",
"dependencies": {
"Speckle.Core": "3.0.1-alpha.14"
}
},
"System.Threading.Tasks.Dataflow": {
"type": "CentralTransitive",
"requested": "[6.0.0, )",
"resolved": "6.0.0",
"contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA=="
}
},
"net6.0-windows7.0/win-x64": {
"SQLitePCLRaw.lib.e_sqlite3": {
"type": "Transitive",
"resolved": "2.1.4",
"contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg=="
},
"Microsoft.Web.WebView2": {
"type": "CentralTransitive",
"requested": "[1.0.1823.32, )",
"resolved": "1.0.1823.32",
"contentHash": "ppRcWBUNggFIqyJp7PfgS4Oe8/7yli/mHcTNDOaqo3ZzJWnv9AOr0WE9ceEP5SPs1M7CQ3QvdGMR7KNz0v09EA=="
}
}
}
}
@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RootNamespace>Speckle.Connectors.Autocad</RootNamespace>
<TargetFramework>net48</TargetFramework>
<PlatformTarget>x64</PlatformTarget>
<UseWpf>true</UseWpf>
<StartAction>Program</StartAction>
<StartProgram>$(ProgramW6432)\Autodesk\AutoCAD 2023\acad.exe</StartProgram>
<DefineConstants>$(DefineConstants);AUTOCAD2023;AUTOCAD</DefineConstants>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Converters\Autocad\2023\Speckle.Converters.Autocad2023.DependencyInjection\Speckle.Converters.Autocad2023.DependencyInjection.csproj" />
<ProjectReference Include="..\..\..\DUI3\Speckle.Connectors.DUI.WebView\Speckle.Connectors.DUI.WebView.csproj" />
<ProjectReference Include="..\..\..\Sdk\Speckle.Autofac\Speckle.Autofac.csproj" />
<ProjectReference Include="..\..\..\Sdk\Speckle.Connectors.Utils\Speckle.Connectors.Utils.csproj" />
<ProjectReference Include="..\..\..\Sdk\Speckle.Converters.Common.DependencyInjection\Speckle.Converters.Common.DependencyInjection.csproj" />
<ProjectReference Include="..\..\..\Sdk\Speckle.Converters.Common\Speckle.Converters.Common.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2023.0.0" />
</ItemGroup>
<Import Project="..\Speckle.Connectors.AutocadShared\Speckle.Connectors.AutocadShared.projitems" Label="Shared" />
</Project>
@@ -0,0 +1,553 @@
{
"version": 2,
"dependencies": {
".NETFramework,Version=v4.8": {
"Microsoft.Extensions.Logging.Abstractions": {
"type": "Direct",
"requested": "[7.0.0, )",
"resolved": "7.0.0",
"contentHash": "kmn78+LPVMOWeITUjIlfxUPDsI0R6G0RkeAMBmQxAJ7vBJn4q2dTva7pWi65ceN5vPGjJ9q/Uae2WKgvfktJAw==",
"dependencies": {
"System.Buffers": "4.5.1",
"System.Memory": "4.5.5"
}
},
"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.AutoCAD.API": {
"type": "Direct",
"requested": "[2023.0.0, )",
"resolved": "2023.0.0",
"contentHash": "aNfiNw9zRW8pCl8AAQK7afEJuea4bJ4sFNsGVSDrdq1egaonZrwALU01dSyFNCE8tne86eVjlprpOGG6r0+G/A=="
},
"Speckle.InterfaceGenerator": {
"type": "Direct",
"requested": "[0.9.5, )",
"resolved": "0.9.5",
"contentHash": "oU/L7pN1R7q8KkbrpQ3WJnHirPHqn+9DEA7asOcUiggV5dzVg1A/VYs7GOSusD24njxXh03tE3a2oTLOjt3cVg=="
},
"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.Bcl.AsyncInterfaces": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "3aeMZ1N0lJoSyzqiP03hqemtb1BijhsJADdobn/4nsMJ8V1H+CrpuduUe4hlRdx+ikBQju1VGjMD1GJ3Sk05Eg==",
"dependencies": {
"System.Threading.Tasks.Extensions": "4.5.4"
}
},
"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.5",
"contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==",
"dependencies": {
"Microsoft.Data.Sqlite.Core": "7.0.5",
"SQLitePCLRaw.bundle_e_sqlite3": "2.1.4"
}
},
"Microsoft.Data.Sqlite.Core": {
"type": "Transitive",
"resolved": "7.0.5",
"contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==",
"dependencies": {
"SQLitePCLRaw.core": "2.1.4"
}
},
"Microsoft.Extensions.DependencyInjection": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "elNeOmkeX3eDVG6pYVeV82p29hr+UKDaBhrZyWvWLw/EVZSYEkZlQdkp0V39k/Xehs2Qa0mvoCvkVj3eQxNQ1Q==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "7.0.0",
"Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0",
"System.Threading.Tasks.Extensions": "4.5.4"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "h3j/QfmFN4S0w4C2A6X7arXij/M/OVw3uQHSOFxnND4DyAzO1F9eMX7Eti7lU/OkSthEE0WzRsfT/Dmx86jzCw==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "7.0.0",
"System.Threading.Tasks.Extensions": "4.5.4"
}
},
"Microsoft.Extensions.Logging": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "Nw2muoNrOG5U5qa2ZekXwudUn2BJcD41e65zwmDHb1fQegTX66UokLWZkJRpqSSHXDOWZ5V0iqhbxOEky91atA==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "7.0.0",
"Microsoft.Extensions.DependencyInjection": "7.0.0",
"Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0",
"Microsoft.Extensions.Logging.Abstractions": "7.0.0",
"Microsoft.Extensions.Options": "7.0.0",
"System.Diagnostics.DiagnosticSource": "7.0.0",
"System.ValueTuple": "4.5.0"
}
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "lP1yBnTTU42cKpMozuafbvNtQ7QcBjr/CcK3bYOGEMH55Fjt+iecXjT6chR7vbgCMqy3PG3aNQSZgo/EuY/9qQ==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0",
"Microsoft.Extensions.Primitives": "7.0.0"
}
},
"Microsoft.Extensions.Primitives": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "um1KU5kxcRp3CNuI8o/GrZtD4AIOXDk+RLsytjZ9QPok3ttLUelLKpilVPuaFT3TFjOhSibUAso0odbOaCDj3Q==",
"dependencies": {
"System.Memory": "4.5.5",
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"Microsoft.SourceLink.Common": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
},
"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"
}
},
"Sentry": {
"type": "Transitive",
"resolved": "3.33.0",
"contentHash": "8vbD2o6IR2wrRrkSiRbnodWGWUOqIlwYtzpjvPNOb5raJdOf+zxMwfS8f6nx9bmrTTfDj7KrCB8C/5OuicAc8A==",
"dependencies": {
"System.Reflection.Metadata": "5.0.0",
"System.Runtime.InteropServices.RuntimeInformation": "4.3.0",
"System.Text.Json": "5.0.2"
}
},
"Sentry.Serilog": {
"type": "Transitive",
"resolved": "3.33.0",
"contentHash": "V8BU7QGWg2qLYfNPqtuTBhC1opysny5l+Ifp6J6PhOeAxU0FssR7nYfbJVetrnLIoh2rd3DlJ6hHYYQosQYcUQ==",
"dependencies": {
"Sentry": "3.33.0",
"Serilog": "2.7.1"
}
},
"Serilog": {
"type": "Transitive",
"resolved": "2.12.0",
"contentHash": "xaiJLIdu6rYMKfQMYUZgTy8YK7SMZjB4Yk50C/u//Z4OsvxkUfSPJy4nknfvwAC34yr13q7kcyh4grbwhSxyZg=="
},
"Serilog.Enrichers.ClientInfo": {
"type": "Transitive",
"resolved": "1.3.0",
"contentHash": "mTc7PM+wC9Hr7LWSwqt5mmnlAr7RJs+eTb3PGPRhwdOackk95MkhUZognuxXEdlW19HAFNmEBTSBY5DfLwM8jQ==",
"dependencies": {
"Serilog": "2.4.0"
}
},
"Serilog.Exceptions": {
"type": "Transitive",
"resolved": "8.4.0",
"contentHash": "nc/+hUw3lsdo0zCj0KMIybAu7perMx79vu72w0za9Nsi6mWyNkGXxYxakAjWB7nEmYL6zdmhEQRB4oJ2ALUeug==",
"dependencies": {
"Serilog": "2.8.0"
}
},
"Serilog.Formatting.Compact": {
"type": "Transitive",
"resolved": "1.1.0",
"contentHash": "pNroKVjo+rDqlxNG5PXkRLpfSCuDOBY0ri6jp9PLe505ljqwhwZz8ospy2vWhQlFu5GkIesh3FcDs4n7sWZODA==",
"dependencies": {
"Serilog": "2.8.0"
}
},
"Serilog.Sinks.Console": {
"type": "Transitive",
"resolved": "4.1.0",
"contentHash": "K6N5q+5fetjnJPvCmkWOpJ/V8IEIoMIB1s86OzBrbxwTyHxdx3pmz4H+8+O/Dc/ftUX12DM1aynx/dDowkwzqg==",
"dependencies": {
"Serilog": "2.10.0"
}
},
"Serilog.Sinks.File": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==",
"dependencies": {
"Serilog": "2.10.0"
}
},
"Serilog.Sinks.PeriodicBatching": {
"type": "Transitive",
"resolved": "3.1.0",
"contentHash": "NDWR7m3PalVlGEq3rzoktrXikjFMLmpwF0HI4sowo8YDdU+gqPlTHlDQiOGxHfB0sTfjPA9JjA7ctKG9zqjGkw==",
"dependencies": {
"Serilog": "2.0.0"
}
},
"Serilog.Sinks.Seq": {
"type": "Transitive",
"resolved": "5.2.2",
"contentHash": "1Csmo5ua7NKUe0yXUx+zsRefjAniPWcXFhUXxXG8pwo0iMiw2gjn9SOkgYnnxbgWqmlGv236w0N/dHc2v5XwMg==",
"dependencies": {
"Serilog": "2.12.0",
"Serilog.Formatting.Compact": "1.1.0",
"Serilog.Sinks.File": "5.0.0",
"Serilog.Sinks.PeriodicBatching": "3.1.0"
}
},
"SerilogTimings": {
"type": "Transitive",
"resolved": "3.0.1",
"contentHash": "Zs28eTgszAMwpIrbBnWHBI50yuxL50p/dmAUWmy75+axdZYK/Sjm5/5m1N/CisR8acJUhTVcjPZrsB1P5iv0Uw==",
"dependencies": {
"Serilog": "2.10.0"
}
},
"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.Collections.Immutable": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "FXkLXiK0sVVewcso0imKQoOxjoPAj42R8HtjjbSjVPAzwDfzoyoznWxgA3c38LDbN9SJux1xXoXYAhz98j7r2g==",
"dependencies": {
"System.Memory": "4.5.4"
}
},
"System.Diagnostics.DiagnosticSource": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "9W0ewWDuAyDqS2PigdTxk6jDKonfgscY/hP8hm7VpxYhNHZHKvZTdRckberlFk3VnCmr3xBUyMBut12Q+T2aOw==",
"dependencies": {
"System.Memory": "4.5.5",
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"System.DoubleNumerics": {
"type": "Transitive",
"resolved": "3.1.3",
"contentHash": "KRKEM/L3KBodjA9VOg3EifFVWUY6EOqaMB05UvPEDm7Zeby/kZW+4kdWUEPzW6xtkwf46p661L9NrbeeQhtLzw==",
"dependencies": {
"NETStandard.Library": "1.6.1"
}
},
"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.Metadata": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "5NecZgXktdGg34rh1OenY1rFNDCI8xSjFr+Z4OU4cU06AQHUdRnIIEeWENu3Wl4YowbzkymAIMvi3WyK9U53pQ==",
"dependencies": {
"System.Collections.Immutable": "5.0.0"
}
},
"System.Runtime.CompilerServices.Unsafe": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
},
"System.Runtime.InteropServices.RuntimeInformation": {
"type": "Transitive",
"resolved": "4.3.0",
"contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw=="
},
"System.Text.Encodings.Web": {
"type": "Transitive",
"resolved": "5.0.1",
"contentHash": "KmJ+CJXizDofbq6mpqDoRRLcxgOd2z9X3XoFNULSbvbqVRZkFX3istvr+MUjL6Zw1RT+RNdoI4GYidIINtgvqQ==",
"dependencies": {
"System.Buffers": "4.5.1",
"System.Memory": "4.5.4"
}
},
"System.Text.Json": {
"type": "Transitive",
"resolved": "5.0.2",
"contentHash": "I47dVIGiV6SfAyppphxqupertT/5oZkYLDCX6vC3HpOI4ZLjyoKAreUoem2ie6G0RbRuFrlqz/PcTQjfb2DOfQ==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"System.Buffers": "4.5.1",
"System.Memory": "4.5.4",
"System.Numerics.Vectors": "4.5.0",
"System.Runtime.CompilerServices.Unsafe": "5.0.0",
"System.Text.Encodings.Web": "5.0.1",
"System.Threading.Tasks.Extensions": "4.5.4",
"System.ValueTuple": "4.5.0"
}
},
"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"
}
},
"System.ValueTuple": {
"type": "Transitive",
"resolved": "4.5.0",
"contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ=="
},
"speckle.autofac": {
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[7.0.0, )"
}
},
"speckle.connectors.dui": {
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[7.0.0, )",
"Speckle.Autofac": "[2.0.999-local, )",
"Speckle.Connectors.Utils": "[2.0.999-local, )",
"Speckle.Core": "[3.0.1-alpha.14, )",
"System.Threading.Tasks.Dataflow": "[6.0.0, )"
}
},
"speckle.connectors.dui.webview": {
"type": "Project",
"dependencies": {
"Microsoft.Web.WebView2": "[1.0.1823.32, )",
"Speckle.Connectors.DUI": "[2.0.999-local, )"
}
},
"speckle.connectors.utils": {
"type": "Project",
"dependencies": {
"Serilog.Extensions.Logging": "[7.0.0, )",
"Speckle.Autofac": "[2.0.999-local, )",
"Speckle.Core": "[3.0.1-alpha.14, )"
}
},
"speckle.converters.autocad2023": {
"type": "Project",
"dependencies": {
"Speckle.AutoCAD.API": "[2023.0.0, )",
"Speckle.Converters.Common": "[2.0.999-local, )"
}
},
"speckle.converters.autocad2023.dependencyinjection": {
"type": "Project",
"dependencies": {
"Autofac": "[5.2.0, )",
"Speckle.Converters.Autocad2023": "[2.0.999-local, )",
"Speckle.Converters.Common.DependencyInjection": "[2.0.999-local, )"
}
},
"speckle.converters.common": {
"type": "Project",
"dependencies": {
"Speckle.Autofac": "[2.0.999-local, )",
"Speckle.Objects": "[3.0.1-alpha.14, )"
}
},
"speckle.converters.common.dependencyinjection": {
"type": "Project",
"dependencies": {
"Speckle.Autofac": "[2.0.999-local, )",
"Speckle.Converters.Common": "[2.0.999-local, )"
}
},
"Autofac": {
"type": "CentralTransitive",
"requested": "[5.2.0, )",
"resolved": "5.2.0",
"contentHash": "V8dBH0dsv75uDzl7Sw+HkhKDPUw2eXnlMjcSVMH+tLo2s67MpTKGyDj1pDcpR+IF2u4YRs0s3/x7R88YJzIWvg==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "1.1.0"
}
},
"Microsoft.Web.WebView2": {
"type": "CentralTransitive",
"requested": "[1.0.1823.32, )",
"resolved": "1.0.1823.32",
"contentHash": "ppRcWBUNggFIqyJp7PfgS4Oe8/7yli/mHcTNDOaqo3ZzJWnv9AOr0WE9ceEP5SPs1M7CQ3QvdGMR7KNz0v09EA=="
},
"Serilog.Extensions.Logging": {
"type": "CentralTransitive",
"requested": "[7.0.0, )",
"resolved": "7.0.0",
"contentHash": "9faU0zNQqU7I6soVhLUMYaGNpgWv6cKlKb2S5AnS8gXxzW/em5Ladm/6FMrWTnX41cdbdGPOWNAo6adi4WaJ6A==",
"dependencies": {
"Microsoft.Extensions.Logging": "7.0.0",
"Serilog": "2.12.0"
}
},
"Speckle.Core": {
"type": "CentralTransitive",
"requested": "[3.0.1-alpha.14, )",
"resolved": "3.0.1-alpha.14",
"contentHash": "RzQPVIGFFkKvG56YLr8ACtiwdWJE6IJ9vCQ4qHa0PIsUEpfzAIAi59jnzqtByOFC0FiFrFPow9bkfzylaZorAA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Polly": "7.2.3",
"Polly.Contrib.WaitAndRetry": "1.1.1",
"Polly.Extensions.Http": "3.0.0",
"Sentry": "3.33.0",
"Sentry.Serilog": "3.33.0",
"Serilog": "2.12.0",
"Serilog.Enrichers.ClientInfo": "1.3.0",
"Serilog.Exceptions": "8.4.0",
"Serilog.Sinks.Console": "4.1.0",
"Serilog.Sinks.Seq": "5.2.2",
"SerilogTimings": "3.0.1",
"Speckle.Newtonsoft.Json": "13.0.2",
"System.DoubleNumerics": "3.1.3"
}
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.0.1-alpha.14, )",
"resolved": "3.0.1-alpha.14",
"contentHash": "z38LGryMvh7iU1uBW+4uo5DwsB3CwRgLt2uFexWFx3mPSid+A0l5XcJzOgLwgFhNl6B42Ryz4ezBsddTp1Uc/g==",
"dependencies": {
"Speckle.Core": "3.0.1-alpha.14"
}
},
"System.Threading.Tasks.Dataflow": {
"type": "CentralTransitive",
"requested": "[6.0.0, )",
"resolved": "6.0.0",
"contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA=="
}
}
}
}
@@ -0,0 +1,158 @@
using Autodesk.AutoCAD.DatabaseServices;
using Sentry.Reflection;
using Speckle.Connectors.Autocad.HostApp;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Core.Credentials;
using Speckle.Connectors.Autocad.HostApp.Extensions;
using Speckle.Connectors.Utils;
using Speckle.Core.Logging;
namespace Speckle.Connectors.Autocad.Bindings;
public class AutocadBasicConnectorBinding : IBasicConnectorBinding
{
public string Name { get; set; } = "baseBinding";
public IBridge Parent { get; }
private readonly DocumentModelStore _store;
private readonly AutocadSettings _settings;
public BasicConnectorBindingCommands Commands { get; }
public AutocadBasicConnectorBinding(DocumentModelStore store, AutocadSettings settings, IBridge parent)
{
_store = store;
_settings = settings;
Parent = parent;
Commands = new BasicConnectorBindingCommands(parent);
_store.DocumentChanged += (_, _) =>
{
Commands.NotifyDocumentChanged();
};
}
public string GetConnectorVersion() =>
typeof(AutocadBasicConnectorBinding).Assembly.GetNameAndVersion().Version ?? "No version";
public string GetSourceApplicationName() => _settings.HostAppInfo.Slug;
public string GetSourceApplicationVersion() => _settings.HostAppVersion.ToString();
public Account[] GetAccounts() => AccountManager.GetAccounts().ToArray();
public DocumentInfo? GetDocumentInfo()
{
// POC: Will be addressed to move it into AutocadContext!
var doc = Application.DocumentManager.MdiActiveDocument;
if (doc is null)
{
return null;
}
string name = doc.Name.Split(System.IO.Path.PathSeparator).Last();
return new DocumentInfo(doc.Name, name, doc.GetHashCode().ToString());
}
public DocumentModelStore GetDocumentState() => _store;
public void AddModel(ModelCard model) => _store.Models.Add(model);
public void UpdateModel(ModelCard model) => _store.UpdateModel(model);
public void RemoveModel(ModelCard model) => _store.RemoveModel(model);
public void HighlightObjects(List<string> objectIds)
{
// POC: Will be addressed to move it into AutocadContext!
var doc = Application.DocumentManager.MdiActiveDocument;
var dbObjects = doc.GetObjects(objectIds);
var acadObjectIds = dbObjects.Select(tuple => tuple.Root.Id).ToArray();
HighlightObjectsOnView(acadObjectIds);
}
public void HighlightModel(string modelCardId)
{
// POC: Will be addressed to move it into AutocadContext!
var doc = Application.DocumentManager.MdiActiveDocument;
if (doc == null)
{
return;
}
var objectIds = Array.Empty<ObjectId>();
var model = _store.GetModelById(modelCardId);
if (model == null)
{
return;
}
if (model is SenderModelCard senderModelCard)
{
var dbObjects = doc.GetObjects(senderModelCard.SendFilter.NotNull().GetObjectIds());
objectIds = dbObjects.Select(tuple => tuple.Root.Id).ToArray();
}
if (model is ReceiverModelCard receiverModelCard)
{
var dbObjects = doc.GetObjects(receiverModelCard.BakedObjectIds.NotNull());
objectIds = dbObjects.Select(tuple => tuple.Root.Id).ToArray();
}
if (objectIds.Length == 0)
{
Commands.SetModelError(modelCardId, new OperationCanceledException("No objects found to highlight."));
return;
}
HighlightObjectsOnView(objectIds, modelCardId);
}
private void HighlightObjectsOnView(ObjectId[] objectIds, string? modelCardId = null)
{
var doc = Application.DocumentManager.MdiActiveDocument;
Parent.RunOnMainThread(() =>
{
try
{
doc.Editor.SetImpliedSelection(Array.Empty<ObjectId>()); // Deselects
doc.Editor.SetImpliedSelection(objectIds); // Selects
doc.Editor.UpdateScreen();
Extents3d selectedExtents = new();
var tr = doc.TransactionManager.StartTransaction();
foreach (ObjectId objectId in objectIds)
{
var entity = (Entity)tr.GetObject(objectId, OpenMode.ForRead);
if (entity != null)
{
selectedExtents.AddExtents(entity.GeometricExtents);
}
}
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."));
}
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;
}
}
});
}
}
@@ -0,0 +1,87 @@
using Speckle.Autofac.DependencyInjection;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.Utils.Cancellation;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Connectors.Utils;
using Speckle.Connectors.Utils.Operations;
namespace Speckle.Connectors.Autocad.Bindings;
public sealed class AutocadReceiveBinding : IReceiveBinding
{
public string Name => "receiveBinding";
public IBridge Parent { get; }
private readonly DocumentModelStore _store;
private readonly CancellationManager _cancellationManager;
private readonly IUnitOfWorkFactory _unitOfWorkFactory;
public ReceiveBindingUICommands Commands { get; }
public AutocadReceiveBinding(
DocumentModelStore store,
IBridge parent,
CancellationManager cancellationManager,
IUnitOfWorkFactory unitOfWorkFactory
)
{
_store = store;
_cancellationManager = cancellationManager;
_unitOfWorkFactory = unitOfWorkFactory;
Parent = parent;
Commands = new ReceiveBindingUICommands(parent);
}
public void CancelReceive(string modelCardId) => _cancellationManager.CancelOperation(modelCardId);
public async Task Receive(string modelCardId)
{
using var unitOfWork = _unitOfWorkFactory.Resolve<ReceiveOperation>();
try
{
// Get receiver card
if (_store.GetModelById(modelCardId) is not ReceiverModelCard modelCard)
{
// Handle as GLOBAL ERROR at BrowserBridge
throw new InvalidOperationException("No download model card was found.");
}
// Init cancellation token source -> Manager also cancel it if exist before
CancellationTokenSource cts = _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;
// Receive host objects
var operationResults = await unitOfWork.Service
.Execute(
modelCard.AccountId.NotNull(), // POC: I hear -you are saying why we're passing them separately. Not sure pass the DUI3-> Connectors.DUI project dependency to the SDK-> Connector.Utils
modelCard.ProjectId.NotNull(),
modelCard.ProjectName.NotNull(),
modelCard.ModelName.NotNull(),
modelCard.SelectedVersionId.NotNull(),
cts.Token,
(status, progress) =>
Commands.SetModelProgress(modelCardId, new ModelCardProgress(modelCardId, status, progress), cts)
)
.ConfigureAwait(false);
Commands.SetModelReceiveResult(modelCardId, operationResults.BakedObjectIds, operationResults.ConversionResults);
}
// Catch here specific exceptions if they related to model card.
catch (OperationCanceledException)
{
// SWALLOW -> UI handles it immediately, so we do not need to handle anything
return;
}
finally
{
// renable document activation
Application.DocumentManager.DocumentActivationEnabled = true;
}
}
}
@@ -0,0 +1,86 @@
using Autodesk.AutoCAD.DatabaseServices;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Autodesk.AutoCAD.EditorInput;
namespace Speckle.Connectors.Autocad.Bindings;
public class AutocadSelectionBinding : ISelectionBinding
{
private const string SELECTION_EVENT = "setSelection";
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
private readonly HashSet<Document> _visitedDocuments = new();
public string Name => "selectionBinding";
public IBridge Parent { get; }
public AutocadSelectionBinding(IBridge parent, ITopLevelExceptionHandler topLevelExceptionHandler)
{
_topLevelExceptionHandler = topLevelExceptionHandler;
Parent = parent;
// POC: Use here Context for doc. In converters it's OK but we are still lacking to use context into bindings.
// It is with the case of if binding created with already a document
// This is valid when user opens acad file directly double clicking
TryRegisterDocumentForSelection(Application.DocumentManager.MdiActiveDocument);
Application.DocumentManager.DocumentActivated += (_, e) =>
_topLevelExceptionHandler.CatchUnhandled(() => OnDocumentChanged(e.Document));
}
private void OnDocumentChanged(Document? document) => TryRegisterDocumentForSelection(document);
private void TryRegisterDocumentForSelection(Document? document)
{
if (document == null)
{
return;
}
if (!_visitedDocuments.Contains(document))
{
document.ImpliedSelectionChanged += (_, _) =>
_topLevelExceptionHandler.CatchUnhandled(() => Parent.RunOnMainThread(OnSelectionChanged));
_visitedDocuments.Add(document);
}
}
private void OnSelectionChanged()
{
SelectionInfo selInfo = GetSelection();
Parent.Send(SELECTION_EVENT, selInfo);
}
public SelectionInfo GetSelection()
{
// POC: Will be addressed to move it into AutocadContext! https://spockle.atlassian.net/browse/CNX-9319
Document? doc = Application.DocumentManager.MdiActiveDocument;
List<string> objs = new();
List<string> objectTypes = new();
if (doc != null)
{
PromptSelectionResult selection = doc.Editor.SelectImplied();
if (selection.Status == PromptStatus.OK)
{
using var tr = doc.TransactionManager.StartTransaction();
foreach (SelectedObject obj in selection.Value)
{
var dbObject = tr.GetObject(obj.ObjectId, OpenMode.ForRead);
if (dbObject == null)
{
continue;
}
var handleString = dbObject.Handle.Value.ToString();
objectTypes.Add(dbObject.GetType().Name);
objs.Add(handleString);
}
tr.Commit();
}
}
List<string> flatObjectTypes = objectTypes.Select(o => o).Distinct().ToList();
return new SelectionInfo(objs, $"{objs.Count} objects ({string.Join(", ", flatObjectTypes)})");
}
}
@@ -0,0 +1,196 @@
using Autodesk.AutoCAD.DatabaseServices;
using Speckle.Connectors.Autocad.HostApp;
using Speckle.Connectors.Autocad.HostApp.Extensions;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Connectors.Utils.Cancellation;
using Speckle.Autofac.DependencyInjection;
using Speckle.Connectors.Autocad.Operations.Send;
using Speckle.Connectors.DUI.Exceptions;
using Speckle.Connectors.Utils.Operations;
using Speckle.Connectors.DUI.Models.Card.SendFilter;
using Speckle.Connectors.Utils;
using Speckle.Connectors.Utils.Caching;
namespace Speckle.Connectors.Autocad.Bindings;
public sealed class AutocadSendBinding : ISendBinding
{
public string Name => "sendBinding";
public SendBindingUICommands Commands { get; }
public IBridge Parent { get; }
private readonly DocumentModelStore _store;
private readonly AutocadIdleManager _idleManager;
private readonly List<ISendFilter> _sendFilters;
private readonly CancellationManager _cancellationManager;
private readonly IUnitOfWorkFactory _unitOfWorkFactory;
private readonly AutocadSettings _autocadSettings;
private readonly ISendConversionCache _sendConversionCache;
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
/// <summary>
/// Used internally to aggregate the changed objects' id.
/// </summary>
private HashSet<string> ChangedObjectIds { get; set; } = new();
public AutocadSendBinding(
DocumentModelStore store,
AutocadIdleManager idleManager,
IBridge parent,
IEnumerable<ISendFilter> sendFilters,
CancellationManager cancellationManager,
AutocadSettings autocadSettings,
IUnitOfWorkFactory unitOfWorkFactory,
ISendConversionCache sendConversionCache,
ITopLevelExceptionHandler topLevelExceptionHandler
)
{
_store = store;
_idleManager = idleManager;
_unitOfWorkFactory = unitOfWorkFactory;
_autocadSettings = autocadSettings;
_cancellationManager = cancellationManager;
_sendFilters = sendFilters.ToList();
_sendConversionCache = sendConversionCache;
_topLevelExceptionHandler = 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);
}
}
private readonly List<string> _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);
}
void OnObjectChanged(DBObject dbObject)
{
_topLevelExceptionHandler.CatchUnhandled(() => OnChangeChangedObjectIds(dbObject));
}
private void OnChangeChangedObjectIds(DBObject dBObject)
{
ChangedObjectIds.Add(dBObject.Handle.Value.ToString());
_idleManager.SubscribeToIdle(RunExpirationChecks);
}
private void RunExpirationChecks()
{
var senders = _store.GetSenders();
string[] objectIdsList = ChangedObjectIds.ToArray();
List<string> 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 HashSet<string>();
}
public List<ISendFilter> GetSendFilters() => _sendFilters;
public Task Send(string modelCardId)
{
Parent.RunOnMainThread(async () => await SendInternal(modelCardId).ConfigureAwait(false));
return Task.CompletedTask;
}
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 uow = _unitOfWorkFactory.Resolve<SendOperation<AutocadRootObject>>();
// Init cancellation token source -> Manager also cancel it if exist before
CancellationTokenSource cts = _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<AutocadRootObject> 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 sendInfo = new SendInfo(
modelCard.AccountId.NotNull(),
modelCard.ProjectId.NotNull(),
modelCard.ModelId.NotNull(),
_autocadSettings.HostAppInfo.Name
);
var sendResult = await uow.Service
.Execute(
autocadObjects,
sendInfo,
(status, progress) =>
Commands.SetModelProgress(modelCardId, new ModelCardProgress(modelCardId, status, progress), cts),
cts.Token
)
.ConfigureAwait(false);
Commands.SetModelSendResult(modelCardId, sendResult.RootObjId, sendResult.ConversionResults);
}
// Catch here specific exceptions if they related to model card.
catch (OperationCanceledException)
{
// SWALLOW -> UI handles it immediately, so we do not need to handle anything
return;
}
catch (SpeckleSendFilterException e)
{
Commands.SetModelError(modelCardId, e);
}
finally
{
// renable document activation
Application.DocumentManager.DocumentActivationEnabled = true;
}
}
public void CancelSend(string modelCardId) => _cancellationManager.CancelOperation(modelCardId);
}
@@ -0,0 +1,69 @@
#if AUTOCAD
using Autodesk.AutoCAD.DatabaseServices;
using Speckle.Autofac;
using Speckle.Autofac.DependencyInjection;
using Speckle.Connectors.Autocad.Bindings;
using Speckle.Connectors.Autocad.Filters;
using Speckle.Connectors.Autocad.HostApp;
using Speckle.Connectors.Autocad.Interfaces;
using Speckle.Connectors.Autocad.Operations.Receive;
using Speckle.Connectors.Autocad.Operations.Send;
using Speckle.Connectors.Autocad.Plugin;
using Speckle.Connectors.DUI;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Models.Card.SendFilter;
using Speckle.Connectors.DUI.WebView;
using Speckle.Connectors.Utils;
using Speckle.Connectors.Utils.Builders;
using Speckle.Connectors.Utils.Caching;
using Speckle.Connectors.Utils.Instances;
using Speckle.Connectors.Utils.Operations;
using Speckle.Core.Models.GraphTraversal;
namespace Speckle.Connectors.Autocad.DependencyInjection;
public class AutocadConnectorModule : ISpeckleModule
{
public void Load(SpeckleContainerBuilder builder)
{
builder.AddAutofac();
builder.AddConnectorUtils();
builder.AddDUI();
builder.AddDUIView();
// Register other connector specific types
builder.AddSingleton<IAutocadPlugin, AutocadPlugin>();
builder.AddTransient<TransactionContext>();
builder.AddSingleton(new AutocadDocumentManager()); // TODO: Dependent to TransactionContext, can be moved to AutocadContext
builder.AddSingleton<DocumentModelStore, AutocadDocumentStore>();
builder.AddSingleton<AutocadContext>();
builder.AddSingleton<AutocadIdleManager>();
SharedConnectorModule.LoadShared(builder);
builder.AddScoped<AutocadLayerManager>();
// Operations
builder.AddScoped<SendOperation<AutocadRootObject>>();
builder.AddSingleton(DefaultTraversal.CreateTraversalFunc());
// Object Builders
builder.AddScoped<IHostObjectBuilder, AutocadHostObjectBuilder>();
builder.AddScoped<IRootObjectBuilder<AutocadRootObject>, AutocadRootObjectBuilder>();
// Register bindings
builder.AddSingleton<IBinding, ConfigBinding>("connectorName", "Autocad"); // POC: Easier like this for now, should be cleaned up later
builder.AddSingleton<IBinding, AutocadSendBinding>();
builder.AddSingleton<IBinding, AutocadReceiveBinding>();
// register send filters
builder.AddTransient<ISendFilter, AutocadSelectionFilter>();
// register send conversion cache
builder.AddSingleton<ISendConversionCache, SendConversionCache>();
builder.AddScoped<IInstanceObjectsManager<AutocadRootObject, List<Entity>>, AutocadInstanceObjectManager>();
}
}
#endif
@@ -0,0 +1,37 @@
#if CIVIL3D
using Speckle.Autofac.DependencyInjection;
using Speckle.Connectors.Autocad.Bindings;
using Speckle.Connectors.Autocad.Filters;
using Speckle.Connectors.Autocad.Operations.Send;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Models.Card.SendFilter;
using Speckle.Connectors.Utils.Builders;
using Speckle.Connectors.Utils.Caching;
using Speckle.Connectors.Utils.Operations;
namespace Speckle.Connectors.Autocad.DependencyInjection;
public class Civil3dConnectorModule : ISpeckleModule
{
public void Load(SpeckleContainerBuilder builder)
{
SharedConnectorModule.LoadShared(builder);
// Operations
builder.AddScoped<SendOperation<AutocadRootObject>>();
// Object Builders
builder.AddScoped<IRootObjectBuilder<AutocadRootObject>, AutocadRootObjectBuilder>();
// Register bindings
builder.AddSingleton<IBinding, ConfigBinding>("connectorName", "Civil3d"); // POC: Easier like this for now, should be cleaned up later
builder.AddSingleton<IBinding, AutocadSendBinding>();
// register send filters
builder.AddTransient<ISendFilter, AutocadSelectionFilter>();
// register send conversion cache
builder.AddSingleton<ISendConversionCache, SendConversionCache>();
}
}
#endif
@@ -0,0 +1,40 @@
using Speckle.Autofac;
using Speckle.Autofac.DependencyInjection;
using Speckle.Connectors.Autocad.Bindings;
using Speckle.Connectors.Autocad.HostApp;
using Speckle.Connectors.Autocad.Interfaces;
using Speckle.Connectors.Autocad.Plugin;
using Speckle.Connectors.DUI;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.WebView;
using Speckle.Connectors.Utils;
namespace Speckle.Connectors.Autocad.DependencyInjection;
public static class SharedConnectorModule
{
public static void LoadShared(SpeckleContainerBuilder builder)
{
builder.AddAutofac();
builder.AddConnectorUtils();
builder.AddDUI();
builder.AddDUIView();
// Register other connector specific types
builder.AddSingleton<IAutocadPlugin, AutocadPlugin>();
builder.AddTransient<TransactionContext>();
builder.AddSingleton(new AutocadDocumentManager()); // TODO: Dependent to TransactionContext, can be moved to AutocadContext
builder.AddSingleton<DocumentModelStore, AutocadDocumentStore>();
builder.AddSingleton<AutocadContext>();
builder.AddSingleton<AutocadLayerManager>();
builder.AddSingleton<AutocadIdleManager>();
// Register bindings
builder.AddSingleton<IBinding, TestBinding>();
builder.AddSingleton<IBinding, AccountBinding>();
builder.AddSingleton<IBinding, AutocadBasicConnectorBinding>();
builder.AddSingleton<IBasicConnectorBinding, AutocadBasicConnectorBinding>();
builder.AddSingleton<IBinding, AutocadSelectionBinding>();
}
}
@@ -0,0 +1,10 @@
using Speckle.Connectors.DUI.Models.Card.SendFilter;
namespace Speckle.Connectors.Autocad.Filters;
public class AutocadSelectionFilter : DirectSelectionSendFilter
{
public override List<string> GetObjectIds() => SelectedObjectIds;
public override bool CheckExpiry(string[] changedObjectIds) => SelectedObjectIds.Intersect(changedObjectIds).Any();
}
@@ -0,0 +1,2 @@
global using Document = Autodesk.AutoCAD.ApplicationServices.Document;
global using Application = Autodesk.AutoCAD.ApplicationServices.Core.Application;
@@ -0,0 +1,19 @@
namespace Speckle.Connectors.Autocad.HostApp;
public class AutocadContext
{
// POC: we may want to inject this autocadcontext with the active doc?
private const string INVALID_CHARS = @"<>/\:;""?*|=,";
// POC: we can move this function into more relevant/general place. Not sure other connectors need similar functionality like this.
public string RemoveInvalidChars(string str)
{
foreach (char c in INVALID_CHARS)
{
str = str.Replace(c.ToString(), string.Empty);
}
return str;
}
}
@@ -0,0 +1,144 @@
using System.Diagnostics;
using System.Text;
using Autodesk.AutoCAD.DatabaseServices;
namespace Speckle.Connectors.Autocad.HostApp;
public class AutocadDocumentManager
{
private const string SPECKLE_KEY = "Speckle_DUI3";
private const string SPECKLE_MODEL_CARDS_KEY = "Speckle_DUI3_Model_Cards";
/// <summary>
/// Returns all the speckle model cards present in the current document.
/// </summary>
/// <param name="doc"></param>
/// <returns></returns>
public string? ReadModelCards(Document doc)
{
using (TransactionContext.StartTransaction(doc))
{
Transaction tr = doc.Database.TransactionManager.TopTransaction;
var nod = (DBDictionary)tr.GetObject(doc.Database.NamedObjectsDictionaryId, OpenMode.ForRead);
if (!nod.Contains(SPECKLE_KEY))
{
return null;
}
var speckleDict = (DBDictionary)tr.GetObject(nod.GetAt(SPECKLE_KEY), OpenMode.ForRead);
if (speckleDict.Count == 0)
{
return null;
}
ObjectId id = speckleDict.GetAt(SPECKLE_MODEL_CARDS_KEY);
if (id == ObjectId.Null)
{
return null;
}
var record = (Xrecord)tr.GetObject(id, OpenMode.ForRead);
string value = GetXrecordData(record);
try
{
//Try to decode here because there is old data
return Base64Decode(value);
}
catch (ApplicationException e)
{
Debug.WriteLine(e);
return null;
}
}
}
/// <summary>
/// Writes the model cards to the current document.
/// </summary>
/// <param name="doc"></param>
/// <param name="modelCardsString"></param>
public void WriteModelCards(Document doc, string modelCardsString)
{
if (doc == null)
{
return;
}
using (TransactionContext.StartTransaction(doc))
{
Transaction tr = doc.Database.TransactionManager.TopTransaction;
var nod = (DBDictionary)tr.GetObject(doc.Database.NamedObjectsDictionaryId, OpenMode.ForRead);
DBDictionary speckleDict;
if (nod.Contains(SPECKLE_KEY))
{
speckleDict = (DBDictionary)tr.GetObject(nod.GetAt(SPECKLE_KEY), OpenMode.ForWrite);
}
else
{
speckleDict = new DBDictionary();
nod.UpgradeOpen();
nod.SetAt(SPECKLE_KEY, speckleDict);
tr.AddNewlyCreatedDBObject(speckleDict, true);
}
Xrecord xRec = new() { Data = CreateResultBuffer(modelCardsString) };
speckleDict.SetAt(SPECKLE_MODEL_CARDS_KEY, xRec);
tr.AddNewlyCreatedDBObject(xRec, true);
}
}
private ResultBuffer CreateResultBuffer(string value)
{
int size = 1024;
var valueEncoded = Base64Encode(value);
var valueEncodedList = SplitString(valueEncoded, size);
ResultBuffer rb = new();
foreach (string valueEncodedSplit in valueEncodedList)
{
rb.Add(new TypedValue((int)DxfCode.Text, valueEncodedSplit));
}
return rb;
}
private string GetXrecordData(Xrecord pXrecord)
{
StringBuilder valueEncoded = new();
foreach (TypedValue typedValue in pXrecord.Data)
{
if (typedValue.TypeCode == (int)DxfCode.Text)
{
valueEncoded.Append(typedValue.Value.ToString());
}
}
return valueEncoded.ToString();
}
private string Base64Encode(string plainText)
{
var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
return Convert.ToBase64String(plainTextBytes);
}
private string Base64Decode(string base64EncodedData)
{
var base64EncodedBytes = Convert.FromBase64String(base64EncodedData);
return Encoding.UTF8.GetString(base64EncodedBytes);
}
private IEnumerable<string> SplitString(string text, int chunkSize)
{
for (int offset = 0; offset < text.Length; offset += chunkSize)
{
int size = Math.Min(chunkSize, text.Length - offset);
yield return text.Substring(offset, size);
}
}
}
@@ -0,0 +1,89 @@
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.Utils;
using Speckle.Newtonsoft.Json;
namespace Speckle.Connectors.Autocad.HostApp;
public class AutocadDocumentStore : DocumentModelStore
{
private readonly string _nullDocumentName = "Null Doc";
private string _previousDocName;
private readonly AutocadDocumentManager _autocadDocumentManager;
public AutocadDocumentStore(
JsonSerializerSettings jsonSerializerSettings,
AutocadDocumentManager autocadDocumentManager,
ITopLevelExceptionHandler topLevelExceptionHandler
)
: base(jsonSerializerSettings, true)
{
_autocadDocumentManager = autocadDocumentManager;
_previousDocName = _nullDocumentName;
// POC: Will be addressed to move it into AutocadContext!
if (Application.DocumentManager.MdiActiveDocument != null)
{
IsDocumentInit = true;
// POC: this logic might go when we have document management in context
// It is with the case of if binding created with already a document
// This is valid when user opens acad file directly double clicking
OnDocChangeInternal(Application.DocumentManager.MdiActiveDocument);
}
Application.DocumentManager.DocumentActivated += (_, e) =>
topLevelExceptionHandler.CatchUnhandled(() => OnDocChangeInternal(e.Document));
// since below event triggered as secondary, it breaks the logic in OnDocChangeInternal function, leaving it here for now.
// Autodesk.AutoCAD.ApplicationServices.Application.DocumentWindowCollection.DocumentWindowActivated += (_, args) =>
// OnDocChangeInternal((Document)args.DocumentWindow.Document);
}
private void OnDocChangeInternal(Document? doc)
{
var currentDocName = doc != null ? doc.Name : _nullDocumentName;
if (_previousDocName == currentDocName)
{
return;
}
_previousDocName = currentDocName;
ReadFromFile();
OnDocumentChanged();
}
public override void ReadFromFile()
{
Models = new();
// POC: Will be addressed to move it into AutocadContext!
Document? doc = Application.DocumentManager.MdiActiveDocument;
if (doc == null)
{
return;
}
string? serializedModelCards = _autocadDocumentManager.ReadModelCards(doc);
if (serializedModelCards == null)
{
return;
}
Models = Deserialize(serializedModelCards).NotNull();
}
public override void WriteToFile()
{
// POC: Will be addressed to move it into AutocadContext!
Document doc = Application.DocumentManager.MdiActiveDocument;
if (doc == null)
{
return;
}
string modelCardsString = Serialize();
_autocadDocumentManager.WriteModelCards(doc, modelCardsString);
}
}
@@ -0,0 +1,38 @@
using System.Collections.Concurrent;
namespace Speckle.Connectors.Autocad.HostApp;
public class AutocadIdleManager
{
private readonly ConcurrentDictionary<string, Action> _sCalls = new();
private bool _hasSubscribed;
/// <summary>
/// Subscribe deferred action to AutocadIdle event to run it whenever Autocad become idle.
/// </summary>
/// <param name="action"> Action to call whenever Autocad become Idle.</param>
public void SubscribeToIdle(Action action)
{
_sCalls[action.Method.Name] = action;
if (_hasSubscribed)
{
return;
}
_hasSubscribed = true;
Application.Idle += OnIdleHandler;
}
private void OnIdleHandler(object sender, EventArgs e)
{
foreach (var kvp in _sCalls)
{
kvp.Value();
}
_sCalls.Clear();
_hasSubscribed = false;
Application.Idle -= OnIdleHandler;
}
}
@@ -0,0 +1,368 @@
using System.DoubleNumerics;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Speckle.Connectors.Autocad.HostApp.Extensions;
using Speckle.Connectors.Autocad.Operations.Send;
using Speckle.Connectors.Utils.Conversion;
using Speckle.Connectors.Utils.Instances;
using Speckle.Core.Kits;
using Speckle.Core.Logging;
using Speckle.Core.Models;
using Speckle.Core.Models.Instances;
namespace Speckle.Connectors.Autocad.HostApp;
/// <summary>
/// <inheritdoc/>
/// Expects to be a scoped dependency per send or receive operation.
/// </summary>
public class AutocadInstanceObjectManager : IInstanceObjectsManager<AutocadRootObject, List<Entity>>
{
private readonly AutocadLayerManager _autocadLayerManager;
private Dictionary<string, InstanceProxy> InstanceProxies { get; set; } = new();
private Dictionary<string, List<InstanceProxy>> InstanceProxiesByDefinitionId { get; set; } = new();
private Dictionary<string, InstanceDefinitionProxy> DefinitionProxies { get; set; } = new();
private Dictionary<string, AutocadRootObject> FlatAtomicObjects { get; set; } = new();
public AutocadInstanceObjectManager(AutocadLayerManager autocadLayerManager)
{
_autocadLayerManager = autocadLayerManager;
}
public UnpackResult<AutocadRootObject> UnpackSelection(IEnumerable<AutocadRootObject> objects)
{
using var transaction = Application.DocumentManager.CurrentDocument.Database.TransactionManager.StartTransaction();
foreach (var obj in objects)
{
if (obj.Root is BlockReference blockReference && !blockReference.IsDynamicBlock)
{
UnpackInstance(blockReference, 0, transaction);
}
FlatAtomicObjects[obj.ApplicationId] = obj;
}
return new(FlatAtomicObjects.Values.ToList(), InstanceProxies, DefinitionProxies.Values.ToList());
}
private void UnpackInstance(BlockReference instance, int depth, Transaction transaction)
{
var instanceIdString = instance.Handle.Value.ToString();
var definitionId = instance.BlockTableRecord;
InstanceProxies[instanceIdString] = new InstanceProxy()
{
applicationId = instanceIdString,
DefinitionId = definitionId.ToString(),
MaxDepth = depth,
Transform = GetMatrix(instance.BlockTransform.ToArray()),
Units = Application.DocumentManager.CurrentDocument.Database.Insunits.ToSpeckleString()
};
// For each block instance that has the same definition, we need to keep track of the "maximum depth" at which is found.
// This will enable on receive to create them in the correct order (descending by max depth, interleaved definitions and instances).
// We need to interleave the creation of definitions and instances, as some definitions may depend on instances.
if (
!InstanceProxiesByDefinitionId.TryGetValue(
definitionId.ToString(),
out List<InstanceProxy> instanceProxiesWithSameDefinition
)
)
{
instanceProxiesWithSameDefinition = new List<InstanceProxy>();
InstanceProxiesByDefinitionId[definitionId.ToString()] = instanceProxiesWithSameDefinition;
}
// We ensure that all previous instance proxies that have the same definition are at this max depth. I kind of have a feeling this can be done more elegantly, but YOLO
foreach (var instanceProxy in instanceProxiesWithSameDefinition)
{
instanceProxy.MaxDepth = depth;
}
instanceProxiesWithSameDefinition.Add(InstanceProxies[instanceIdString]);
if (DefinitionProxies.TryGetValue(definitionId.ToString(), out InstanceDefinitionProxy value))
{
value.MaxDepth = depth;
return; // exit fast - we've parsed this one so no need to go further
}
var definition = (BlockTableRecord)transaction.GetObject(definitionId, OpenMode.ForRead);
// definition.Origin
var definitionProxy = new InstanceDefinitionProxy()
{
applicationId = definitionId.ToString(),
Objects = new(),
MaxDepth = depth,
["name"] = definition.Name,
["comments"] = definition.Comments,
["units"] = definition.Units // ? not sure needed?
};
// Go through each definition object
foreach (ObjectId id in definition)
{
var obj = transaction.GetObject(id, OpenMode.ForRead);
var handleIdString = obj.Handle.Value.ToString();
definitionProxy.Objects.Add(handleIdString);
if (obj is BlockReference blockReference && !blockReference.IsDynamicBlock)
{
UnpackInstance(blockReference, depth + 1, transaction);
}
FlatAtomicObjects[handleIdString] = new(obj, handleIdString);
}
DefinitionProxies[definitionId.ToString()] = definitionProxy;
}
public BakeResult BakeInstances(
List<(string[] layerPath, IInstanceComponent obj)> instanceComponents,
Dictionary<string, List<Entity>> applicationIdMap,
string baseLayerName,
Action<string, double?>? onOperationProgressed
)
{
var sortedInstanceComponents = instanceComponents
.OrderByDescending(x => x.obj.MaxDepth) // Sort by max depth, so we start baking from the deepest element first
.ThenBy(x => x.obj is InstanceDefinitionProxy ? 0 : 1) // Ensure we bake the deepest definition first, then any instances that depend on it
.ToList();
var definitionIdAndApplicationIdMap = new Dictionary<string, ObjectId>();
using var transaction = Application.DocumentManager.CurrentDocument.Database.TransactionManager.StartTransaction();
var conversionResults = new List<ReceiveConversionResult>();
var createdObjectIds = new List<string>();
var consumedObjectIds = new List<string>();
var count = 0;
foreach (var (path, instanceOrDefinition) in sortedInstanceComponents)
{
try
{
onOperationProgressed?.Invoke("Converting blocks", (double)++count / sortedInstanceComponents.Count);
if (instanceOrDefinition is InstanceDefinitionProxy { applicationId: not null } definitionProxy)
{
// TODO: create definition (block table record)
var constituentEntities = definitionProxy.Objects
.Select(id => applicationIdMap.TryGetValue(id, out List<Entity> value) ? value : null)
.Where(x => x is not null)
.SelectMany(ent => ent)
.ToList();
var record = new BlockTableRecord();
var objectIds = new ObjectIdCollection();
record.Name = baseLayerName;
if (definitionProxy["name"] is string name)
{
record.Name += name;
}
else
{
record.Name += definitionProxy.applicationId;
}
foreach (var entity in constituentEntities)
{
// record.AppendEntity(entity);
objectIds.Add(entity.ObjectId);
}
using var blockTable = (BlockTable)
transaction.GetObject(Application.DocumentManager.CurrentDocument.Database.BlockTableId, OpenMode.ForWrite);
var id = blockTable.Add(record);
record.AssumeOwnershipOf(objectIds);
definitionIdAndApplicationIdMap[definitionProxy.applicationId] = id;
transaction.AddNewlyCreatedDBObject(record, true);
var consumedEntitiesHandleValues = constituentEntities.Select(ent => ent.Handle.Value.ToString()).ToArray();
consumedObjectIds.AddRange(consumedEntitiesHandleValues);
createdObjectIds.RemoveAll(newId => consumedEntitiesHandleValues.Contains(newId));
}
else if (
instanceOrDefinition is InstanceProxy instanceProxy
&& definitionIdAndApplicationIdMap.TryGetValue(instanceProxy.DefinitionId, out ObjectId definitionId)
)
{
var matrix3d = GetMatrix3d(instanceProxy.Transform, instanceProxy.Units);
var insertionPoint = Point3d.Origin.TransformBy(matrix3d);
var modelSpaceBlockTableRecord = Application.DocumentManager.CurrentDocument.Database.GetModelSpace(
OpenMode.ForWrite
);
_autocadLayerManager.CreateLayerForReceive(path[0]);
var blockRef = new BlockReference(insertionPoint, definitionId)
{
BlockTransform = matrix3d,
Layer = path[0],
};
modelSpaceBlockTableRecord.AppendEntity(blockRef);
if (instanceProxy.applicationId != null)
{
applicationIdMap[instanceProxy.applicationId] = new List<Entity> { blockRef };
}
transaction.AddNewlyCreatedDBObject(blockRef, true);
conversionResults.Add(
new(Status.SUCCESS, instanceProxy, blockRef.Handle.Value.ToString(), "Instance (Block)")
);
createdObjectIds.Add(blockRef.Handle.Value.ToString());
}
}
catch (Exception ex) when (!ex.IsFatal())
{
conversionResults.Add(new(Status.ERROR, instanceOrDefinition as Base ?? new Base(), null, null, ex));
}
}
transaction.Commit();
return new(createdObjectIds, consumedObjectIds, conversionResults);
}
/// <summary>
/// Cleans up any previously created instances.
/// POC: This function will not be able to delete block definitions if the user creates a new one composed out of received definitions.
/// </summary>
/// <param name="namePrefix"></param>
public void PurgeInstances(string namePrefix)
{
using var transaction = Application.DocumentManager.CurrentDocument.Database.TransactionManager.StartTransaction();
var instanceDefinitionsToDelete = new Dictionary<string, BlockTableRecord>();
// Helper function that recurses through a given block table record's constituent objects and purges inner instances as required.
void TraverseAndClean(BlockTableRecord btr)
{
foreach (var objectId in btr)
{
var obj = transaction.GetObject(objectId, OpenMode.ForRead) as BlockReference;
if (obj == null)
{
continue;
}
var definition = (BlockTableRecord)transaction.GetObject(obj.BlockTableRecord, OpenMode.ForRead);
if (obj.IsErased)
{
TraverseAndClean(definition);
continue;
}
obj.UpgradeOpen();
obj.Erase();
TraverseAndClean(definition);
instanceDefinitionsToDelete[obj.BlockTableRecord.ToString()] = definition;
}
}
using var blockTable = (BlockTable)
transaction.GetObject(Application.DocumentManager.CurrentDocument.Database.BlockTableId, OpenMode.ForRead);
// deep clean definitions
foreach (var btrId in blockTable)
{
var btr = (BlockTableRecord)transaction.GetObject(btrId, OpenMode.ForRead);
if (btr.Name.Contains(namePrefix)) // POC: this is tightly coupled with a naming convention for definitions in the instance object manager
{
TraverseAndClean(btr);
instanceDefinitionsToDelete[btr.Name] = btr;
}
}
foreach (var def in instanceDefinitionsToDelete.Values)
{
def.UpgradeOpen();
def.Erase();
}
transaction.Commit();
}
private Matrix4x4 GetMatrix(double[] t)
{
return new Matrix4x4(
t[0],
t[1],
t[2],
t[3],
t[4],
t[5],
t[6],
t[7],
t[8],
t[9],
t[10],
t[11],
t[12],
t[13],
t[14],
t[15]
);
}
private Matrix3d GetMatrix3d(Matrix4x4 matrix, string units)
{
var sf = Units.GetConversionFactor(
units,
Application.DocumentManager.CurrentDocument.Database.Insunits.ToSpeckleString()
);
var scaledTransform = new[]
{
matrix.M11,
matrix.M12,
matrix.M13,
matrix.M14 * sf,
matrix.M21,
matrix.M22,
matrix.M23,
matrix.M24 * sf,
matrix.M31,
matrix.M32,
matrix.M33,
matrix.M34 * sf,
matrix.M41,
matrix.M42,
matrix.M43,
matrix.M44
};
var m3d = new Matrix3d(scaledTransform);
if (!m3d.IsScaledOrtho())
{
m3d = new Matrix3d(MakePerpendicular(m3d));
}
return m3d;
}
// https://forums.autodesk.com/t5/net/set-blocktransform-values/m-p/6452121#M49479
private static double[] MakePerpendicular(Matrix3d matrix)
{
// Get the basis vectors of the matrix
Vector3d right = new(matrix[0, 0], matrix[1, 0], matrix[2, 0]);
Vector3d up = new(matrix[0, 1], matrix[1, 1], matrix[2, 1]);
Vector3d newForward = right.CrossProduct(up).GetNormal();
Vector3d newUp = newForward.CrossProduct(right).GetNormal();
return new[]
{
right.X,
newUp.X,
newForward.X,
matrix[0, 3],
right.Y,
newUp.Y,
newForward.Y,
matrix[1, 3],
right.Z,
newUp.Z,
newForward.Z,
matrix[2, 3],
0.0,
0.0,
0.0,
matrix[3, 3],
};
}
}
@@ -0,0 +1,160 @@
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.LayerManager;
using Speckle.Core.Models;
using Speckle.Core.Models.GraphTraversal;
namespace Speckle.Connectors.Autocad.HostApp;
/// <summary>
/// Expects to be a scoped dependency for a given operation and helps with layer creation and cleanup.
/// </summary>
public class AutocadLayerManager
{
private readonly AutocadContext _autocadContext;
private readonly string _layerFilterName = "Speckle";
// POC: Will be addressed to move it into AutocadContext!
private Document Doc => Application.DocumentManager.MdiActiveDocument;
private readonly HashSet<string> _uniqueLayerNames = new();
public AutocadLayerManager(AutocadContext autocadContext)
{
_autocadContext = autocadContext;
}
/// <summary>
/// Will create a layer with the provided name, or, if it finds an existing one, will "purge" all objects from it.
/// This ensures we're creating the new objects we've just received rather than overlaying them.
/// </summary>
/// <param name="layerName">Name to search layer for purge and create.</param>
public void CreateLayerForReceive(string layerName)
{
if (!_uniqueLayerNames.Add(layerName))
{
return;
}
Doc.LockDocument();
using Transaction transaction = Doc.TransactionManager.StartTransaction();
LayerTable? layerTable =
transaction.TransactionManager.GetObject(Doc.Database.LayerTableId, OpenMode.ForRead) as LayerTable;
LayerTableRecord layerTableRecord = new() { Name = layerName };
bool hasLayer = layerTable != null && layerTable.Has(layerName);
if (hasLayer)
{
TypedValue[] tvs = { new((int)DxfCode.LayerName, layerName) };
SelectionFilter selectionFilter = new(tvs);
SelectionSet selectionResult = Doc.Editor.SelectAll(selectionFilter).Value;
if (selectionResult == null)
{
return;
}
foreach (SelectedObject selectedObject in selectionResult)
{
transaction.GetObject(selectedObject.ObjectId, OpenMode.ForWrite).Erase();
}
return;
}
layerTable?.UpgradeOpen();
layerTable?.Add(layerTableRecord);
transaction.AddNewlyCreatedDBObject(layerTableRecord, true);
transaction.Commit();
}
public void DeleteAllLayersByPrefix(string prefix)
{
Doc.LockDocument();
using Transaction transaction = Doc.TransactionManager.StartTransaction();
var layerTable = (LayerTable)transaction.TransactionManager.GetObject(Doc.Database.LayerTableId, OpenMode.ForRead);
foreach (var layerId in layerTable)
{
var layer = (LayerTableRecord)transaction.GetObject(layerId, OpenMode.ForRead);
var layerName = layer.Name;
if (layer.Name.Contains(prefix))
{
// Delete objects from this layer
TypedValue[] tvs = { new((int)DxfCode.LayerName, layerName) };
SelectionFilter selectionFilter = new(tvs);
SelectionSet selectionResult = Doc.Editor.SelectAll(selectionFilter).Value;
if (selectionResult == null)
{
return;
}
foreach (SelectedObject selectedObject in selectionResult)
{
transaction.GetObject(selectedObject.ObjectId, OpenMode.ForWrite).Erase();
}
// Delete layer
layer.UpgradeOpen();
layer.Erase();
}
}
transaction.Commit();
}
/// <summary>
/// Creates a layer filter for the just received model, grouped under a top level filter "Speckle". Note: manual close and open of the layer properties panel required (it's an acad thing).
/// This comes in handy to quickly access the layers created for this specific model.
/// </summary>
/// <param name="projectName"></param>
/// <param name="modelName"></param>
public void CreateLayerFilter(string projectName, string modelName)
{
using var docLock = Doc.LockDocument();
string filterName = _autocadContext.RemoveInvalidChars($@"{projectName}-{modelName}");
LayerFilterTree layerFilterTree = Doc.Database.LayerFilters;
LayerFilterCollection? layerFilterCollection = layerFilterTree.Root.NestedFilters;
LayerFilter? groupFilter = null;
// Find existing layer filter if exists
foreach (LayerFilter existingFilter in layerFilterCollection)
{
if (existingFilter.Name == _layerFilterName)
{
groupFilter = existingFilter;
break;
}
}
// Create new one unless exists
if (groupFilter == null)
{
groupFilter = new LayerFilter() { Name = "Speckle", FilterExpression = $"NAME==\"SPK-*\"" };
layerFilterCollection.Add(groupFilter);
}
string layerFilterExpression = $"NAME==\"SPK-{filterName}*\"";
foreach (LayerFilter lf in groupFilter.NestedFilters)
{
if (lf.Name == filterName)
{
lf.FilterExpression = layerFilterExpression;
return;
}
}
var layerFilter = new LayerFilter() { Name = filterName, FilterExpression = layerFilterExpression };
groupFilter.NestedFilters.Add(layerFilter);
Doc.Database.LayerFilters = layerFilterTree;
}
/// <summary>
/// Gets a valid layer name for a given context.
/// </summary>
/// <param name="context"></param>
/// <param name="baseLayerPrefix"></param>
/// <returns></returns>
public string GetLayerPath(TraversalContext context, string baseLayerPrefix)
{
string[] collectionBasedPath = context.GetAscendantOfType<Collection>().Select(c => c.name).Reverse().ToArray();
string[] path = collectionBasedPath.Length != 0 ? collectionBasedPath : context.GetPropertyPath().ToArray();
var name = baseLayerPrefix + string.Join("-", path);
return _autocadContext.RemoveInvalidChars(name);
}
}
@@ -0,0 +1,19 @@
using System.IO;
using Speckle.Core.Kits; // POC: Must go https://spockle.atlassian.net/browse/CNX-9325
namespace Speckle.Connectors.Autocad.HostApp;
public class AutocadSettings
{
public AutocadSettings(HostApplication hostAppInfo, HostAppVersion hostAppVersion)
{
HostAppInfo = hostAppInfo;
HostAppVersion = hostAppVersion;
Modules = new[] { new DirectoryInfo(typeof(AutocadSettings).Assembly.Location).Parent.FullName };
}
public HostApplication HostAppInfo { get; private set; }
public HostAppVersion HostAppVersion { get; private set; }
public IReadOnlyList<string> Modules { get; private set; }
}
@@ -0,0 +1,39 @@
using Autodesk.AutoCAD.DatabaseServices;
using Speckle.Core.Kits;
using Speckle.Core.Logging;
namespace Speckle.Connectors.Autocad.HostApp.Extensions;
public static class AcadUnitsExtension
{
public static string ToSpeckleString(this UnitsValue units)
{
switch (units)
{
case UnitsValue.Millimeters:
return Units.Millimeters;
case UnitsValue.Centimeters:
return Units.Centimeters;
case UnitsValue.Meters:
return Units.Meters;
case UnitsValue.Kilometers:
return Units.Kilometers;
case UnitsValue.Inches:
case UnitsValue.USSurveyInch:
return Units.Inches;
case UnitsValue.Feet:
case UnitsValue.USSurveyFeet:
return Units.Feet;
case UnitsValue.Yards:
case UnitsValue.USSurveyYard:
return Units.Yards;
case UnitsValue.Miles:
case UnitsValue.USSurveyMile:
return Units.Miles;
case UnitsValue.Undefined:
return Units.None;
default:
throw new SpeckleException($"The Unit System \"{units}\" is unsupported.");
}
}
}
@@ -0,0 +1,15 @@
using Autodesk.AutoCAD.DatabaseServices;
namespace Speckle.Connectors.Autocad.HostApp.Extensions;
public static class DatabaseExtensions
{
/// <summary>
/// Gets the document model space
/// </summary>
/// <param name="db"></param>
/// <param name="mode"></param>
/// <returns></returns>
public static BlockTableRecord GetModelSpace(this Database db, OpenMode mode = OpenMode.ForRead) =>
(BlockTableRecord)SymbolUtilityServices.GetBlockModelSpaceId(db).GetObject(mode);
}
@@ -0,0 +1,47 @@
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Runtime;
using Speckle.Connectors.Autocad.Operations.Send;
namespace Speckle.Connectors.Autocad.HostApp.Extensions;
public static class DocumentExtensions
{
public static List<AutocadRootObject> GetObjects(this Document doc, IEnumerable<string> objectIds)
{
List<AutocadRootObject> objects = new();
using (TransactionContext.StartTransaction(doc))
{
Transaction tr = doc.Database.TransactionManager.TopTransaction;
foreach (string objectIdHandle in objectIds)
{
if (long.TryParse(objectIdHandle, out long parsedId))
{
Handle handle = new(parsedId);
// Note: Fatal crash happens here when objects are deleted, so we need to catch it.
try
{
if (doc.Database.TryGetObjectId(handle, out ObjectId myObjectId))
{
if (tr.GetObject(myObjectId, OpenMode.ForRead) is DBObject dbObject)
{
objects.Add(new(dbObject, objectIdHandle));
}
}
}
catch (Autodesk.AutoCAD.Runtime.Exception e)
{
if (e.ErrorStatus == ErrorStatus.WasErased) // Note: TBD if we want to catch more things in here. For now maybe not, but it does seem like this function gets into "crashes the host app territory"
{
continue;
}
throw;
}
}
}
}
return objects;
}
}
@@ -0,0 +1,29 @@
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
namespace Speckle.Connectors.Autocad.HostApp.Extensions;
public static class EditorExtensions
{
public static void Zoom(this Editor editor, Extents3d ext)
{
if (editor == null)
{
throw new ArgumentNullException(nameof(editor));
}
using ViewTableRecord view = editor.GetCurrentView();
Matrix3d worldToEye =
Matrix3d.WorldToPlane(view.ViewDirection)
* Matrix3d.Displacement(Point3d.Origin - view.Target)
* Matrix3d.Rotation(view.ViewTwist, view.ViewDirection, view.Target);
ext.TransformBy(worldToEye);
view.Width = ext.MaxPoint.X - ext.MinPoint.X;
view.Height = ext.MaxPoint.Y - ext.MinPoint.Y;
view.CenterPoint = new Point2d((ext.MaxPoint.X + ext.MinPoint.X) / 2.0, (ext.MaxPoint.Y + ext.MinPoint.Y) / 2.0);
editor.SetCurrentView(view);
}
}
@@ -0,0 +1,45 @@
using Autodesk.AutoCAD.DatabaseServices;
namespace Speckle.Connectors.Autocad.HostApp.Extensions;
public static class EntityExtensions
{
/// <summary>
/// Adds an entity to the autocad database model space record
/// </summary>
/// <param name="entity">Entity to add into database.</param>
/// <param name="layer"> Layer to append object.</param>
/// <exception cref="InvalidOperationException">Throws when there is no top transaction in the document.</exception>
public static ObjectId AppendToDb(this Entity entity, string? layer = null)
{
// POC: Will be addressed to move it into AutocadContext!
var db = entity.Database ?? Application.DocumentManager.MdiActiveDocument.Database;
Transaction tr = db.TransactionManager.TopTransaction;
if (tr == null)
{
throw new InvalidOperationException($"Document does not have a top transaction.");
}
BlockTableRecord btr = db.GetModelSpace(OpenMode.ForWrite);
if (entity.IsNewObject)
{
if (layer != null)
{
entity.Layer = layer;
}
var id = btr.AppendEntity(entity);
tr.AddNewlyCreatedDBObject(entity, true);
return id;
}
else
{
if (layer != null)
{
entity.Layer = layer;
}
return entity.Id;
}
}
}
@@ -0,0 +1,32 @@
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
namespace Speckle.Connectors.Autocad.HostApp;
[System.Diagnostics.CodeAnalysis.SuppressMessage(
"Usage",
"CA2213:Disposable fields should be disposed",
Justification = "Analyzer false positive with Autocad classes"
)]
public sealed class TransactionContext : IDisposable
{
private DocumentLock? _documentLock;
private Transaction? _transaction;
public static TransactionContext StartTransaction(Document document) => new(document);
private TransactionContext(Document document)
{
_documentLock = document.LockDocument();
_transaction = document.Database.TransactionManager.StartTransaction();
}
public void Dispose()
{
_transaction?.Commit();
_transaction = null;
_documentLock?.Dispose();
_documentLock = null;
}
}
@@ -0,0 +1,7 @@
namespace Speckle.Connectors.Autocad.Interfaces;
internal interface IAutocadPlugin
{
void Initialise();
void Shutdown();
}
@@ -0,0 +1,181 @@
using Autodesk.AutoCAD.DatabaseServices;
using Speckle.Connectors.Autocad.HostApp;
using Speckle.Connectors.Autocad.HostApp.Extensions;
using Speckle.Connectors.Autocad.Operations.Send;
using Speckle.Core.Models;
using Speckle.Connectors.Utils.Builders;
using Speckle.Connectors.Utils.Conversion;
using Speckle.Connectors.Utils.Instances;
using Speckle.Converters.Common;
using Speckle.Core.Logging;
using Speckle.Core.Models.GraphTraversal;
using Speckle.Core.Models.Instances;
namespace Speckle.Connectors.Autocad.Operations.Receive;
/// <summary>
/// <para>Expects to be a scoped dependency per receive operation.</para>
/// </summary>
public class AutocadHostObjectBuilder : IHostObjectBuilder
{
private readonly AutocadLayerManager _autocadLayerManager;
private readonly IRootToHostConverter _converter;
private readonly GraphTraversal _traversalFunction;
// private readonly HashSet<string> _uniqueLayerNames = new();
private readonly IInstanceObjectsManager<AutocadRootObject, List<Entity>> _instanceObjectsManager;
public AutocadHostObjectBuilder(
IRootToHostConverter converter,
GraphTraversal traversalFunction,
AutocadLayerManager autocadLayerManager,
IInstanceObjectsManager<AutocadRootObject, List<Entity>> instanceObjectsManager
)
{
_converter = converter;
_traversalFunction = traversalFunction;
_autocadLayerManager = autocadLayerManager;
_instanceObjectsManager = instanceObjectsManager;
}
public HostObjectBuilderResult Build(
Base rootObject,
string projectName,
string modelName,
Action<string, double?>? onOperationProgressed,
CancellationToken cancellationToken
)
{
// Prompt the UI conversion started. Progress bar will swoosh.
onOperationProgressed?.Invoke("Converting", null);
// Layer filter for received commit with project and model name
_autocadLayerManager.CreateLayerFilter(projectName, modelName);
//TODO: make the layerManager handle \/ ?
string baseLayerPrefix = $"SPK-{projectName}-{modelName}-";
PreReceiveDeepClean(baseLayerPrefix);
List<ReceiveConversionResult> results = new();
List<string> bakedObjectIds = new();
// return new(bakedObjectIds, results);
var objectGraph = _traversalFunction.Traverse(rootObject).Where(obj => obj.Current is not Collection);
// POC: these are not captured by traversal, so we need to re-add them here
var instanceDefinitionProxies = (rootObject["instanceDefinitionProxies"] as List<object>)
?.Cast<InstanceDefinitionProxy>()
.ToList();
var instanceComponents = new List<(string[] path, IInstanceComponent obj)>();
// POC: these are not captured by traversal, so we need to re-add them here
if (instanceDefinitionProxies != null && instanceDefinitionProxies.Count > 0)
{
var transformed = instanceDefinitionProxies.Select(proxy => (Array.Empty<string>(), proxy as IInstanceComponent));
instanceComponents.AddRange(transformed);
}
var atomicObjects = new List<(string layerName, Base obj)>();
foreach (TraversalContext tc in objectGraph)
{
var layerName = _autocadLayerManager.GetLayerPath(tc, baseLayerPrefix);
if (tc.Current is IInstanceComponent instanceComponent)
{
instanceComponents.Add((new string[] { layerName }, instanceComponent));
}
else
{
atomicObjects.Add((layerName, tc.Current));
}
}
// Stage 1: Convert atomic objects
Dictionary<string, List<Entity>> applicationIdMap = new();
var count = 0;
foreach (var (layerName, atomicObject) in atomicObjects)
{
onOperationProgressed?.Invoke("Converting objects", (double)++count / atomicObjects.Count);
try
{
var convertedObjects = ConvertObject(atomicObject, layerName).ToList();
if (atomicObject.applicationId != null)
{
applicationIdMap[atomicObject.applicationId] = convertedObjects;
}
results.AddRange(
convertedObjects.Select(
e =>
new ReceiveConversionResult(
Status.SUCCESS,
atomicObject,
e.Handle.Value.ToString(),
e.GetType().ToString()
)
)
);
bakedObjectIds.AddRange(convertedObjects.Select(e => e.Handle.Value.ToString()));
}
catch (Exception ex) when (!ex.IsFatal())
{
results.Add(new(Status.ERROR, atomicObject, null, null, ex));
}
}
// Stage 2: Convert instances
var (createdInstanceIds, consumedObjectIds, instanceConversionResults) = _instanceObjectsManager.BakeInstances(
instanceComponents,
applicationIdMap,
baseLayerPrefix,
onOperationProgressed
);
bakedObjectIds.RemoveAll(id => consumedObjectIds.Contains(id));
bakedObjectIds.AddRange(createdInstanceIds);
results.RemoveAll(result => result.ResultId != null && consumedObjectIds.Contains(result.ResultId));
results.AddRange(instanceConversionResults);
return new(bakedObjectIds, results);
}
private void PreReceiveDeepClean(string baseLayerPrefix)
{
_autocadLayerManager.DeleteAllLayersByPrefix(baseLayerPrefix);
_instanceObjectsManager.PurgeInstances(baseLayerPrefix);
}
private IEnumerable<Entity> ConvertObject(Base obj, string layerName)
{
using TransactionContext transactionContext = TransactionContext.StartTransaction(
Application.DocumentManager.MdiActiveDocument
);
_autocadLayerManager.CreateLayerForReceive(layerName);
object converted;
using (var tr = Application.DocumentManager.CurrentDocument.Database.TransactionManager.StartTransaction())
{
converted = _converter.Convert(obj);
tr.Commit();
}
IEnumerable<Entity?> flattened = Utilities.FlattenToHostConversionResult(converted).Cast<Entity>();
foreach (Entity? conversionResult in flattened)
{
if (conversionResult == null)
{
// POC: This needed to be double checked why we check null and continue
continue;
}
conversionResult.AppendToDb(layerName);
yield return conversionResult;
}
}
}
@@ -0,0 +1,6 @@
using Autodesk.AutoCAD.DatabaseServices;
namespace Speckle.Connectors.Autocad.Operations.Send;
// Note: naming is a bit confusing, Root is similar to base commit object, or root commit object, etc. It might be just in my head (dim)
public record AutocadRootObject(DBObject Root, string ApplicationId);
@@ -0,0 +1,113 @@
using System.Diagnostics;
using Autodesk.AutoCAD.DatabaseServices;
using Speckle.Connectors.Utils.Builders;
using Speckle.Connectors.Utils.Caching;
using Speckle.Connectors.Utils.Conversion;
using Speckle.Connectors.Utils.Instances;
using Speckle.Connectors.Utils.Operations;
using Speckle.Converters.Common;
using Speckle.Core.Logging;
using Speckle.Core.Models;
using Speckle.Core.Models.Instances;
namespace Speckle.Connectors.Autocad.Operations.Send;
public class AutocadRootObjectBuilder : IRootObjectBuilder<AutocadRootObject>
{
private readonly IRootToSpeckleConverter _converter;
private readonly string[] _documentPathSeparator = { "\\" };
private readonly ISendConversionCache _sendConversionCache;
private readonly IInstanceObjectsManager<AutocadRootObject, List<Entity>> _instanceObjectsManager;
public AutocadRootObjectBuilder(
IRootToSpeckleConverter converter,
ISendConversionCache sendConversionCache,
IInstanceObjectsManager<AutocadRootObject, List<Entity>> instanceObjectManager
)
{
_converter = converter;
_sendConversionCache = sendConversionCache;
_instanceObjectsManager = instanceObjectManager;
}
public RootObjectBuilderResult Build(
IReadOnlyList<AutocadRootObject> objects,
SendInfo sendInfo,
Action<string, double?>? onOperationProgressed = null,
CancellationToken ct = default
)
{
Collection modelWithLayers =
new()
{
name = Application.DocumentManager.CurrentDocument.Name // POC: https://spockle.atlassian.net/browse/CNX-9319
.Split(_documentPathSeparator, StringSplitOptions.None)
.Reverse()
.First(),
collectionType = "root"
};
// Cached dictionary to create Collection for autocad entity layers. We first look if collection exists. If so use it otherwise create new one for that layer.
Dictionary<string, Collection> collectionCache = new();
int count = 0;
var (atomicObjects, instanceProxies, instanceDefinitionProxies) = _instanceObjectsManager.UnpackSelection(objects);
// POC: until we formalise a bit more the root object
modelWithLayers["instanceDefinitionProxies"] = instanceDefinitionProxies;
List<SendConversionResult> results = new();
var cacheHitCount = 0;
foreach (var (dbObject, applicationId) in atomicObjects)
{
ct.ThrowIfCancellationRequested();
try
{
Base converted;
if (dbObject is BlockReference && instanceProxies.TryGetValue(applicationId, out InstanceProxy instanceProxy))
{
converted = instanceProxy;
}
else if (_sendConversionCache.TryGetValue(sendInfo.ProjectId, applicationId, out ObjectReference value))
{
converted = value;
cacheHitCount++;
}
else
{
converted = _converter.Convert(dbObject);
converted.applicationId = applicationId;
}
// Create and add a collection for each layer if not done so already.
if ((dbObject as Entity)?.Layer is string layer)
{
if (!collectionCache.TryGetValue(layer, out Collection? collection))
{
collection = new Collection() { name = layer, collectionType = "layer" };
collectionCache[layer] = collection;
modelWithLayers.elements.Add(collectionCache[layer]);
}
collection.elements.Add(converted);
}
results.Add(new(Status.SUCCESS, applicationId, dbObject.GetType().ToString(), converted));
}
catch (Exception ex) when (!ex.IsFatal())
{
results.Add(new(Status.ERROR, applicationId, dbObject.GetType().ToString(), null, ex));
// POC: add logging
}
onOperationProgressed?.Invoke("Converting", (double)++count / atomicObjects.Count);
}
// POC: Log would be nice, or can be removed.
Debug.WriteLine(
$"Cache hit count {cacheHitCount} out of {objects.Count} ({(double)cacheHitCount / objects.Count})"
);
return new(modelWithLayers, results);
}
}
@@ -0,0 +1,69 @@
using System.Drawing;
using System.Reflection;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.Windows;
using Speckle.Autofac.DependencyInjection;
using Speckle.Connectors.Autocad.HostApp;
using Speckle.Core.Kits;
using Speckle.Connectors.Autocad.Interfaces;
using Speckle.Connectors.DUI.WebView;
namespace Speckle.Connectors.Autocad.Plugin;
public class AutocadCommand
{
private static PaletteSet? PaletteSet { get; set; }
private static readonly Guid s_id = new("3223E594-1B09-4E54-B3DD-8EA0BECE7BA5");
private IAutocadPlugin? _autocadPlugin;
public SpeckleContainer? Container { get; private set; }
[CommandMethod("SpeckleNewUI")]
public void Command()
{
if (PaletteSet != null)
{
FocusPalette();
return;
}
PaletteSet = new PaletteSet("Speckle DUI3", s_id)
{
Size = new Size(400, 500),
DockEnabled = (DockSides)((int)DockSides.Left + (int)DockSides.Right)
};
var builder = SpeckleContainerBuilder.CreateInstance();
#if CIVIL3D2024
AutocadSettings autocadSettings = new (HostApplications.Civil3D, HostAppVersion.v2024);
#elif AUTOCAD2023
AutocadSettings autocadSettings = new(HostApplications.AutoCAD, HostAppVersion.v2023);
#else
AutocadSettings autocadSettings = new(HostApplications.AutoCAD, HostAppVersion.v2023);
#endif
Container = builder
.LoadAutofacModules(Assembly.GetExecutingAssembly(), autocadSettings.Modules)
.AddSingleton(autocadSettings)
.Build();
// Resolve root plugin object and initialise.
_autocadPlugin = Container.Resolve<IAutocadPlugin>();
_autocadPlugin.Initialise();
var panelWebView = Container.Resolve<DUI3ControlWebView>();
PaletteSet.AddVisual("Speckle DUI3 WebView", panelWebView);
FocusPalette();
}
private void FocusPalette()
{
if (PaletteSet != null)
{
PaletteSet.KeepFocus = true;
PaletteSet.Visible = true;
}
}
}
@@ -0,0 +1,12 @@
using Autodesk.AutoCAD.Runtime;
using Speckle.Autofac;
namespace Speckle.Connectors.Autocad.Plugin;
public class AutocadExtensionApplication : IExtensionApplication
{
public void Initialize() =>
AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolver.OnAssemblyResolve<AutocadExtensionApplication>;
public void Terminate() { }
}
@@ -0,0 +1,23 @@
using Speckle.Connectors.Autocad.HostApp;
using Speckle.Connectors.Autocad.Interfaces;
using Speckle.Connectors.DUI.WebView;
namespace Speckle.Connectors.Autocad.Plugin;
public class AutocadPlugin : IAutocadPlugin
{
private readonly AutocadIdleManager _idleManager;
private readonly DUI3ControlWebView _panel;
private readonly AutocadSettings _settings;
public AutocadPlugin(DUI3ControlWebView panel, AutocadSettings settings, AutocadIdleManager idleManager)
{
_panel = panel;
_settings = settings;
_idleManager = idleManager;
}
public void Initialise() { }
public void Shutdown() { }
}
@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
<HasSharedItems>true</HasSharedItems>
<SharedGUID>55E65D72-2FE8-4E61-891C-16A4D551CCF7</SharedGUID>
</PropertyGroup>
<PropertyGroup Label="Configuration">
<Import_RootNamespace>Speckle.Connectors.AutocadShared</Import_RootNamespace>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)Bindings\AutocadSelectionBinding.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bindings\AutocadReceiveBinding.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bindings\AutocadSendBinding.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bindings\AutocadBasicConnectorBinding.cs" />
<Compile Include="$(MSBuildThisFileDirectory)DependencyInjection\SharedConnectorModule.cs" />
<Compile Include="$(MSBuildThisFileDirectory)DependencyInjection\AutocadConnectorModule.cs" />
<Compile Include="$(MSBuildThisFileDirectory)DependencyInjection\Civil3dConnectorModule.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Filters\AutocadSelectionFilter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\AutocadInstanceObjectManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\AutocadContext.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\AutocadDocumentManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\AutocadDocumentModelStore.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\AutocadIdleManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\AutocadLayerManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\AutocadSettings.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Extensions\AcadUnitsExtension.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Extensions\DatabaseExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Extensions\DocumentExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Extensions\EditorExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Extensions\EntityExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\TransactionContext.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\AutocadHostObjectBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\AutocadRootObject.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\AutocadRootObjectBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Plugin\AutocadExtensionApplication.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Plugin\AutocadPlugin.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interfaces\IAutocadPlugin.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Plugin\AutocadCommand.cs" />
<Compile Include="$(MSBuildThisFileDirectory)GlobalUsings.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="$(MSBuildThisFileDirectory)Interfaces\" />
</ItemGroup>
</Project>
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Globals">
<ProjectGuid>{41BC679F-887F-44CF-971D-A5502EE87DB0}</ProjectGuid>
</PropertyGroup>
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props"
Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')"/>
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.Default.props"/>
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.props"/>
<Import Project="Speckle.Connectors.AutocadShared.projitems" Label="Shared"/>
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.CSharp.targets"/>
</Project>
@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RootNamespace>Speckle.Connectors.Civil3d</RootNamespace>
<TargetFramework>net48</TargetFramework>
<PlatformTarget>x64</PlatformTarget>
<UseWpf>true</UseWpf>
<StartAction>Program</StartAction>
<StartProgram>$(ProgramW6432)\Autodesk\AutoCAD 2024\acad.exe</StartProgram>
<DefineConstants>$(DefineConstants);CIVIL3D2024;CIVIL3D;</DefineConstants>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Converters\Autocad\2024\Speckle.Converters.Autocad2024.DependencyInjection\Speckle.Converters.Autocad2024.DependencyInjection.csproj" />
<ProjectReference Include="..\..\..\DUI3\Speckle.Connectors.DUI.WebView\Speckle.Connectors.DUI.WebView.csproj" />
<ProjectReference Include="..\..\..\Sdk\Speckle.Autofac\Speckle.Autofac.csproj" />
<ProjectReference Include="..\..\..\Sdk\Speckle.Connectors.Utils\Speckle.Connectors.Utils.csproj" />
<ProjectReference Include="..\..\..\Sdk\Speckle.Converters.Common.DependencyInjection\Speckle.Converters.Common.DependencyInjection.csproj" />
<ProjectReference Include="..\..\..\Sdk\Speckle.Converters.Common\Speckle.Converters.Common.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
<PackageReference Include="Speckle.Civil3D.API" />
</ItemGroup>
<Import Project="..\..\Autocad\Speckle.Connectors.AutocadShared\Speckle.Connectors.AutocadShared.projitems" Label="Shared" />
</Project>
@@ -0,0 +1,562 @@
{
"version": 2,
"dependencies": {
".NETFramework,Version=v4.8": {
"Microsoft.Extensions.Logging.Abstractions": {
"type": "Direct",
"requested": "[7.0.0, )",
"resolved": "7.0.0",
"contentHash": "kmn78+LPVMOWeITUjIlfxUPDsI0R6G0RkeAMBmQxAJ7vBJn4q2dTva7pWi65ceN5vPGjJ9q/Uae2WKgvfktJAw==",
"dependencies": {
"System.Buffers": "4.5.1",
"System.Memory": "4.5.5"
}
},
"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.Civil3D.API": {
"type": "Direct",
"requested": "[2024.0.0, )",
"resolved": "2024.0.0",
"contentHash": "9Q7M1k0DotN8w7MkiScQezErRdnZ4dAkxBMcPNhHSWoth/lSaT6UPV1aYEdl90RhehJWG4l3O7U2e3OXvVSFdw==",
"dependencies": {
"Speckle.AutoCAD.API": "2024.0.0"
}
},
"Speckle.InterfaceGenerator": {
"type": "Direct",
"requested": "[0.9.5, )",
"resolved": "0.9.5",
"contentHash": "oU/L7pN1R7q8KkbrpQ3WJnHirPHqn+9DEA7asOcUiggV5dzVg1A/VYs7GOSusD24njxXh03tE3a2oTLOjt3cVg=="
},
"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.Bcl.AsyncInterfaces": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "3aeMZ1N0lJoSyzqiP03hqemtb1BijhsJADdobn/4nsMJ8V1H+CrpuduUe4hlRdx+ikBQju1VGjMD1GJ3Sk05Eg==",
"dependencies": {
"System.Threading.Tasks.Extensions": "4.5.4"
}
},
"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.5",
"contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==",
"dependencies": {
"Microsoft.Data.Sqlite.Core": "7.0.5",
"SQLitePCLRaw.bundle_e_sqlite3": "2.1.4"
}
},
"Microsoft.Data.Sqlite.Core": {
"type": "Transitive",
"resolved": "7.0.5",
"contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==",
"dependencies": {
"SQLitePCLRaw.core": "2.1.4"
}
},
"Microsoft.Extensions.DependencyInjection": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "elNeOmkeX3eDVG6pYVeV82p29hr+UKDaBhrZyWvWLw/EVZSYEkZlQdkp0V39k/Xehs2Qa0mvoCvkVj3eQxNQ1Q==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "7.0.0",
"Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0",
"System.Threading.Tasks.Extensions": "4.5.4"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "h3j/QfmFN4S0w4C2A6X7arXij/M/OVw3uQHSOFxnND4DyAzO1F9eMX7Eti7lU/OkSthEE0WzRsfT/Dmx86jzCw==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "7.0.0",
"System.Threading.Tasks.Extensions": "4.5.4"
}
},
"Microsoft.Extensions.Logging": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "Nw2muoNrOG5U5qa2ZekXwudUn2BJcD41e65zwmDHb1fQegTX66UokLWZkJRpqSSHXDOWZ5V0iqhbxOEky91atA==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "7.0.0",
"Microsoft.Extensions.DependencyInjection": "7.0.0",
"Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0",
"Microsoft.Extensions.Logging.Abstractions": "7.0.0",
"Microsoft.Extensions.Options": "7.0.0",
"System.Diagnostics.DiagnosticSource": "7.0.0",
"System.ValueTuple": "4.5.0"
}
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "lP1yBnTTU42cKpMozuafbvNtQ7QcBjr/CcK3bYOGEMH55Fjt+iecXjT6chR7vbgCMqy3PG3aNQSZgo/EuY/9qQ==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0",
"Microsoft.Extensions.Primitives": "7.0.0"
}
},
"Microsoft.Extensions.Primitives": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "um1KU5kxcRp3CNuI8o/GrZtD4AIOXDk+RLsytjZ9QPok3ttLUelLKpilVPuaFT3TFjOhSibUAso0odbOaCDj3Q==",
"dependencies": {
"System.Memory": "4.5.5",
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"Microsoft.SourceLink.Common": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
},
"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"
}
},
"Sentry": {
"type": "Transitive",
"resolved": "3.33.0",
"contentHash": "8vbD2o6IR2wrRrkSiRbnodWGWUOqIlwYtzpjvPNOb5raJdOf+zxMwfS8f6nx9bmrTTfDj7KrCB8C/5OuicAc8A==",
"dependencies": {
"System.Reflection.Metadata": "5.0.0",
"System.Runtime.InteropServices.RuntimeInformation": "4.3.0",
"System.Text.Json": "5.0.2"
}
},
"Sentry.Serilog": {
"type": "Transitive",
"resolved": "3.33.0",
"contentHash": "V8BU7QGWg2qLYfNPqtuTBhC1opysny5l+Ifp6J6PhOeAxU0FssR7nYfbJVetrnLIoh2rd3DlJ6hHYYQosQYcUQ==",
"dependencies": {
"Sentry": "3.33.0",
"Serilog": "2.7.1"
}
},
"Serilog": {
"type": "Transitive",
"resolved": "2.12.0",
"contentHash": "xaiJLIdu6rYMKfQMYUZgTy8YK7SMZjB4Yk50C/u//Z4OsvxkUfSPJy4nknfvwAC34yr13q7kcyh4grbwhSxyZg=="
},
"Serilog.Enrichers.ClientInfo": {
"type": "Transitive",
"resolved": "1.3.0",
"contentHash": "mTc7PM+wC9Hr7LWSwqt5mmnlAr7RJs+eTb3PGPRhwdOackk95MkhUZognuxXEdlW19HAFNmEBTSBY5DfLwM8jQ==",
"dependencies": {
"Serilog": "2.4.0"
}
},
"Serilog.Exceptions": {
"type": "Transitive",
"resolved": "8.4.0",
"contentHash": "nc/+hUw3lsdo0zCj0KMIybAu7perMx79vu72w0za9Nsi6mWyNkGXxYxakAjWB7nEmYL6zdmhEQRB4oJ2ALUeug==",
"dependencies": {
"Serilog": "2.8.0"
}
},
"Serilog.Formatting.Compact": {
"type": "Transitive",
"resolved": "1.1.0",
"contentHash": "pNroKVjo+rDqlxNG5PXkRLpfSCuDOBY0ri6jp9PLe505ljqwhwZz8ospy2vWhQlFu5GkIesh3FcDs4n7sWZODA==",
"dependencies": {
"Serilog": "2.8.0"
}
},
"Serilog.Sinks.Console": {
"type": "Transitive",
"resolved": "4.1.0",
"contentHash": "K6N5q+5fetjnJPvCmkWOpJ/V8IEIoMIB1s86OzBrbxwTyHxdx3pmz4H+8+O/Dc/ftUX12DM1aynx/dDowkwzqg==",
"dependencies": {
"Serilog": "2.10.0"
}
},
"Serilog.Sinks.File": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==",
"dependencies": {
"Serilog": "2.10.0"
}
},
"Serilog.Sinks.PeriodicBatching": {
"type": "Transitive",
"resolved": "3.1.0",
"contentHash": "NDWR7m3PalVlGEq3rzoktrXikjFMLmpwF0HI4sowo8YDdU+gqPlTHlDQiOGxHfB0sTfjPA9JjA7ctKG9zqjGkw==",
"dependencies": {
"Serilog": "2.0.0"
}
},
"Serilog.Sinks.Seq": {
"type": "Transitive",
"resolved": "5.2.2",
"contentHash": "1Csmo5ua7NKUe0yXUx+zsRefjAniPWcXFhUXxXG8pwo0iMiw2gjn9SOkgYnnxbgWqmlGv236w0N/dHc2v5XwMg==",
"dependencies": {
"Serilog": "2.12.0",
"Serilog.Formatting.Compact": "1.1.0",
"Serilog.Sinks.File": "5.0.0",
"Serilog.Sinks.PeriodicBatching": "3.1.0"
}
},
"SerilogTimings": {
"type": "Transitive",
"resolved": "3.0.1",
"contentHash": "Zs28eTgszAMwpIrbBnWHBI50yuxL50p/dmAUWmy75+axdZYK/Sjm5/5m1N/CisR8acJUhTVcjPZrsB1P5iv0Uw==",
"dependencies": {
"Serilog": "2.10.0"
}
},
"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.Collections.Immutable": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "FXkLXiK0sVVewcso0imKQoOxjoPAj42R8HtjjbSjVPAzwDfzoyoznWxgA3c38LDbN9SJux1xXoXYAhz98j7r2g==",
"dependencies": {
"System.Memory": "4.5.4"
}
},
"System.Diagnostics.DiagnosticSource": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "9W0ewWDuAyDqS2PigdTxk6jDKonfgscY/hP8hm7VpxYhNHZHKvZTdRckberlFk3VnCmr3xBUyMBut12Q+T2aOw==",
"dependencies": {
"System.Memory": "4.5.5",
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"System.DoubleNumerics": {
"type": "Transitive",
"resolved": "3.1.3",
"contentHash": "KRKEM/L3KBodjA9VOg3EifFVWUY6EOqaMB05UvPEDm7Zeby/kZW+4kdWUEPzW6xtkwf46p661L9NrbeeQhtLzw==",
"dependencies": {
"NETStandard.Library": "1.6.1"
}
},
"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.Metadata": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "5NecZgXktdGg34rh1OenY1rFNDCI8xSjFr+Z4OU4cU06AQHUdRnIIEeWENu3Wl4YowbzkymAIMvi3WyK9U53pQ==",
"dependencies": {
"System.Collections.Immutable": "5.0.0"
}
},
"System.Runtime.CompilerServices.Unsafe": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
},
"System.Runtime.InteropServices.RuntimeInformation": {
"type": "Transitive",
"resolved": "4.3.0",
"contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw=="
},
"System.Text.Encodings.Web": {
"type": "Transitive",
"resolved": "5.0.1",
"contentHash": "KmJ+CJXizDofbq6mpqDoRRLcxgOd2z9X3XoFNULSbvbqVRZkFX3istvr+MUjL6Zw1RT+RNdoI4GYidIINtgvqQ==",
"dependencies": {
"System.Buffers": "4.5.1",
"System.Memory": "4.5.4"
}
},
"System.Text.Json": {
"type": "Transitive",
"resolved": "5.0.2",
"contentHash": "I47dVIGiV6SfAyppphxqupertT/5oZkYLDCX6vC3HpOI4ZLjyoKAreUoem2ie6G0RbRuFrlqz/PcTQjfb2DOfQ==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"System.Buffers": "4.5.1",
"System.Memory": "4.5.4",
"System.Numerics.Vectors": "4.5.0",
"System.Runtime.CompilerServices.Unsafe": "5.0.0",
"System.Text.Encodings.Web": "5.0.1",
"System.Threading.Tasks.Extensions": "4.5.4",
"System.ValueTuple": "4.5.0"
}
},
"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"
}
},
"System.ValueTuple": {
"type": "Transitive",
"resolved": "4.5.0",
"contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ=="
},
"speckle.autofac": {
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[7.0.0, )"
}
},
"speckle.connectors.dui": {
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[7.0.0, )",
"Speckle.Autofac": "[2.0.999-local, )",
"Speckle.Connectors.Utils": "[2.0.999-local, )",
"Speckle.Core": "[3.0.1-alpha.14, )",
"System.Threading.Tasks.Dataflow": "[6.0.0, )"
}
},
"speckle.connectors.dui.webview": {
"type": "Project",
"dependencies": {
"Microsoft.Web.WebView2": "[1.0.1823.32, )",
"Speckle.Connectors.DUI": "[2.0.999-local, )"
}
},
"speckle.connectors.utils": {
"type": "Project",
"dependencies": {
"Serilog.Extensions.Logging": "[7.0.0, )",
"Speckle.Autofac": "[2.0.999-local, )",
"Speckle.Core": "[3.0.1-alpha.14, )"
}
},
"speckle.converters.autocad2024": {
"type": "Project",
"dependencies": {
"Speckle.AutoCAD.API": "[2024.0.0, )",
"Speckle.Converters.Common": "[2.0.999-local, )"
}
},
"speckle.converters.autocad2024.dependencyinjection": {
"type": "Project",
"dependencies": {
"Autofac": "[5.2.0, )",
"Speckle.Converters.Autocad2024": "[2.0.999-local, )",
"Speckle.Converters.Common.DependencyInjection": "[2.0.999-local, )"
}
},
"speckle.converters.common": {
"type": "Project",
"dependencies": {
"Speckle.Autofac": "[2.0.999-local, )",
"Speckle.Objects": "[3.0.1-alpha.14, )"
}
},
"speckle.converters.common.dependencyinjection": {
"type": "Project",
"dependencies": {
"Speckle.Autofac": "[2.0.999-local, )",
"Speckle.Converters.Common": "[2.0.999-local, )"
}
},
"Autofac": {
"type": "CentralTransitive",
"requested": "[5.2.0, )",
"resolved": "5.2.0",
"contentHash": "V8dBH0dsv75uDzl7Sw+HkhKDPUw2eXnlMjcSVMH+tLo2s67MpTKGyDj1pDcpR+IF2u4YRs0s3/x7R88YJzIWvg==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "1.1.0"
}
},
"Microsoft.Web.WebView2": {
"type": "CentralTransitive",
"requested": "[1.0.1823.32, )",
"resolved": "1.0.1823.32",
"contentHash": "ppRcWBUNggFIqyJp7PfgS4Oe8/7yli/mHcTNDOaqo3ZzJWnv9AOr0WE9ceEP5SPs1M7CQ3QvdGMR7KNz0v09EA=="
},
"Serilog.Extensions.Logging": {
"type": "CentralTransitive",
"requested": "[7.0.0, )",
"resolved": "7.0.0",
"contentHash": "9faU0zNQqU7I6soVhLUMYaGNpgWv6cKlKb2S5AnS8gXxzW/em5Ladm/6FMrWTnX41cdbdGPOWNAo6adi4WaJ6A==",
"dependencies": {
"Microsoft.Extensions.Logging": "7.0.0",
"Serilog": "2.12.0"
}
},
"Speckle.AutoCAD.API": {
"type": "CentralTransitive",
"requested": "[2023.0.0, )",
"resolved": "2024.0.0",
"contentHash": "pZZ5uI+NXhZaQnsqRkgp/rywqBAjDObDJ9XNFGJvemT5k2OthDpHzlK/mKxz8QDCYie7uImQ8dv3uWj2QUFDPw=="
},
"Speckle.Core": {
"type": "CentralTransitive",
"requested": "[3.0.1-alpha.14, )",
"resolved": "3.0.1-alpha.14",
"contentHash": "RzQPVIGFFkKvG56YLr8ACtiwdWJE6IJ9vCQ4qHa0PIsUEpfzAIAi59jnzqtByOFC0FiFrFPow9bkfzylaZorAA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Polly": "7.2.3",
"Polly.Contrib.WaitAndRetry": "1.1.1",
"Polly.Extensions.Http": "3.0.0",
"Sentry": "3.33.0",
"Sentry.Serilog": "3.33.0",
"Serilog": "2.12.0",
"Serilog.Enrichers.ClientInfo": "1.3.0",
"Serilog.Exceptions": "8.4.0",
"Serilog.Sinks.Console": "4.1.0",
"Serilog.Sinks.Seq": "5.2.2",
"SerilogTimings": "3.0.1",
"Speckle.Newtonsoft.Json": "13.0.2",
"System.DoubleNumerics": "3.1.3"
}
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.0.1-alpha.14, )",
"resolved": "3.0.1-alpha.14",
"contentHash": "z38LGryMvh7iU1uBW+4uo5DwsB3CwRgLt2uFexWFx3mPSid+A0l5XcJzOgLwgFhNl6B42Ryz4ezBsddTp1Uc/g==",
"dependencies": {
"Speckle.Core": "3.0.1-alpha.14"
}
},
"System.Threading.Tasks.Dataflow": {
"type": "CentralTransitive",
"requested": "[6.0.0, )",
"resolved": "6.0.0",
"contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA=="
}
}
}
}
@@ -0,0 +1,8 @@
{
"profiles": {
"ConnectorRevit2023": {
"commandName": "Executable",
"executablePath": "C:\\Program Files\\Autodesk\\Revit 2023\\Revit.exe"
}
}
}
@@ -0,0 +1,54 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<TargetFramework>net48</TargetFramework>
<PlatformTarget>x64</PlatformTarget>
<UseWpf>true</UseWpf>
<GenerateTargetFrameworkAttribute>false</GenerateTargetFrameworkAttribute>
</PropertyGroup>
<Import Project="..\Speckle.Connectors.RevitShared\Speckle.Connectors.RevitShared.projitems" Label="Shared" />
<ItemGroup>
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.Xaml" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Net.Http" />
<Reference Include="WindowsBase" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Converters\Revit\Speckle.Converters.Revit2023.DependencyInjection\Speckle.Converters.Revit2023.DependencyInjection.csproj" />
<ProjectReference Include="..\..\..\Converters\Revit\Speckle.Converters.Revit2023\Speckle.Converters.Revit2023.csproj" />
<ProjectReference Include="..\..\..\DUI3\Speckle.Connectors.DUI\Speckle.Connectors.DUI.csproj" />
<ProjectReference Include="..\..\..\Sdk\Speckle.Converters.Common.DependencyInjection\Speckle.Converters.Common.DependencyInjection.csproj" />
<ProjectReference Include="..\..\..\Sdk\Speckle.Converters.Common\Speckle.Converters.Common.csproj" />
<ProjectReference Include="..\..\..\Sdk\Speckle.Autofac\Speckle.Autofac.csproj" />
<ProjectReference Include="..\..\..\Sdk\Speckle.Connectors.Utils\Speckle.Connectors.Utils.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="CefSharp.Wpf" IncludeAssets="compile" NoWarn="NU1903" />
<PackageReference Include="Revit.Async" />
</ItemGroup>
<Target AfterTargets="Clean" Name="CleanAddinFolder">
<RemoveDir Directories="$(TargetDir);$(ProjectDir)\..\Release\Release2023;$(AppData)\Autodesk\Revit\Addins\2023\Speckle.Connectors.Revit2023;" />
<Delete Files="$(AppData)\Autodesk\Revit\Addins\2023\Speckle.Connectors.Revit2023.addin" />
</Target>
<Target Name="AfterBuildMigrated" AfterTargets="Build" Condition="$([MSBuild]::IsOsPlatform('Windows'))">
<CallTarget Condition="'$(IsDesktopBuild)' == true" Targets="AfterBuildDebug" />
</Target>
<Target Name="AfterBuildDebug">
<ItemGroup>
<RevitDLLs Include="$(TargetDir)\**\*.*" Exclude="$(TargetDir)*.addin" />
<SourceManifest Include="$(TargetDir)\Plugin\*.addin" />
</ItemGroup>
<Copy DestinationFolder="$(AppData)\Autodesk\REVIT\Addins\2023\Speckle.Connectors.Revit2023\%(RecursiveDir)" SourceFiles="@(RevitDLLs)" />
<Copy DestinationFolder="$(AppData)\Autodesk\REVIT\Addins\2023\" SourceFiles="@(SourceManifest)" />
</Target>
</Project>
@@ -0,0 +1,565 @@
{
"version": 2,
"dependencies": {
".NETFramework,Version=v4.8": {
"CefSharp.Wpf": {
"type": "Direct",
"requested": "[92.0.260, )",
"resolved": "92.0.260",
"contentHash": "b0TW/HpHsvz2x3M1IQa/8fbCIRKShOgpmSqj5gKiibXY0v2cHgnWssfEX+3Q3UCpizkBJBQ+0fVp+fN8JCPcNQ==",
"dependencies": {
"CefSharp.Common": "[92.0.260]"
}
},
"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=="
},
"Revit.Async": {
"type": "Direct",
"requested": "[2.0.1, )",
"resolved": "2.0.1",
"contentHash": "B7D5zXznqgxMryBYdGgWob20ALfGSP7hJ6+bh9JdLM/LRkYN5dNf0AaI0+7VID9X7e8MA0koAxv9fIijJnmSnw=="
},
"Speckle.InterfaceGenerator": {
"type": "Direct",
"requested": "[0.9.5, )",
"resolved": "0.9.5",
"contentHash": "oU/L7pN1R7q8KkbrpQ3WJnHirPHqn+9DEA7asOcUiggV5dzVg1A/VYs7GOSusD24njxXh03tE3a2oTLOjt3cVg=="
},
"cef.redist.x64": {
"type": "Transitive",
"resolved": "92.0.26",
"contentHash": "wwwSGVgO9sdlj5TRnjVdzuCkNuYpTOXT90Z+dasnqGrnihrxKU+1UvulxxJa83O2k+l3kaNlAU9uBhiwGX7YyQ=="
},
"cef.redist.x86": {
"type": "Transitive",
"resolved": "92.0.26",
"contentHash": "VT93sU6hH7LoUILjfaAxWbLc+m+b9QafeeRkuulnj3twJnb0PQ7nXd8PMUeOmYg96NzZi14G7qBYSY0dkxqztg=="
},
"CefSharp.Common": {
"type": "Transitive",
"resolved": "92.0.260",
"contentHash": "0KS5z+kqg+mmB1X2KM17w3S7KQI5UTuJv+UOIQauJPvFYfRGWwTqmBbBxL3kCklGx8WSkaeBjPdSFxdzzXCSew==",
"dependencies": {
"cef.redist.x64": "[92.0.26]",
"cef.redist.x86": "[92.0.26]"
}
},
"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.Bcl.AsyncInterfaces": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "3aeMZ1N0lJoSyzqiP03hqemtb1BijhsJADdobn/4nsMJ8V1H+CrpuduUe4hlRdx+ikBQju1VGjMD1GJ3Sk05Eg==",
"dependencies": {
"System.Threading.Tasks.Extensions": "4.5.4"
}
},
"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.5",
"contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==",
"dependencies": {
"Microsoft.Data.Sqlite.Core": "7.0.5",
"SQLitePCLRaw.bundle_e_sqlite3": "2.1.4"
}
},
"Microsoft.Data.Sqlite.Core": {
"type": "Transitive",
"resolved": "7.0.5",
"contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==",
"dependencies": {
"SQLitePCLRaw.core": "2.1.4"
}
},
"Microsoft.Extensions.DependencyInjection": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "elNeOmkeX3eDVG6pYVeV82p29hr+UKDaBhrZyWvWLw/EVZSYEkZlQdkp0V39k/Xehs2Qa0mvoCvkVj3eQxNQ1Q==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "7.0.0",
"Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0",
"System.Threading.Tasks.Extensions": "4.5.4"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "h3j/QfmFN4S0w4C2A6X7arXij/M/OVw3uQHSOFxnND4DyAzO1F9eMX7Eti7lU/OkSthEE0WzRsfT/Dmx86jzCw==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "7.0.0",
"System.Threading.Tasks.Extensions": "4.5.4"
}
},
"Microsoft.Extensions.Logging": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "Nw2muoNrOG5U5qa2ZekXwudUn2BJcD41e65zwmDHb1fQegTX66UokLWZkJRpqSSHXDOWZ5V0iqhbxOEky91atA==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "7.0.0",
"Microsoft.Extensions.DependencyInjection": "7.0.0",
"Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0",
"Microsoft.Extensions.Logging.Abstractions": "7.0.0",
"Microsoft.Extensions.Options": "7.0.0",
"System.Diagnostics.DiagnosticSource": "7.0.0",
"System.ValueTuple": "4.5.0"
}
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "lP1yBnTTU42cKpMozuafbvNtQ7QcBjr/CcK3bYOGEMH55Fjt+iecXjT6chR7vbgCMqy3PG3aNQSZgo/EuY/9qQ==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0",
"Microsoft.Extensions.Primitives": "7.0.0"
}
},
"Microsoft.Extensions.Primitives": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "um1KU5kxcRp3CNuI8o/GrZtD4AIOXDk+RLsytjZ9QPok3ttLUelLKpilVPuaFT3TFjOhSibUAso0odbOaCDj3Q==",
"dependencies": {
"System.Memory": "4.5.5",
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"Microsoft.SourceLink.Common": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
},
"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"
}
},
"Sentry": {
"type": "Transitive",
"resolved": "3.33.0",
"contentHash": "8vbD2o6IR2wrRrkSiRbnodWGWUOqIlwYtzpjvPNOb5raJdOf+zxMwfS8f6nx9bmrTTfDj7KrCB8C/5OuicAc8A==",
"dependencies": {
"System.Reflection.Metadata": "5.0.0",
"System.Runtime.InteropServices.RuntimeInformation": "4.3.0",
"System.Text.Json": "5.0.2"
}
},
"Sentry.Serilog": {
"type": "Transitive",
"resolved": "3.33.0",
"contentHash": "V8BU7QGWg2qLYfNPqtuTBhC1opysny5l+Ifp6J6PhOeAxU0FssR7nYfbJVetrnLIoh2rd3DlJ6hHYYQosQYcUQ==",
"dependencies": {
"Sentry": "3.33.0",
"Serilog": "2.7.1"
}
},
"Serilog": {
"type": "Transitive",
"resolved": "2.12.0",
"contentHash": "xaiJLIdu6rYMKfQMYUZgTy8YK7SMZjB4Yk50C/u//Z4OsvxkUfSPJy4nknfvwAC34yr13q7kcyh4grbwhSxyZg=="
},
"Serilog.Enrichers.ClientInfo": {
"type": "Transitive",
"resolved": "1.3.0",
"contentHash": "mTc7PM+wC9Hr7LWSwqt5mmnlAr7RJs+eTb3PGPRhwdOackk95MkhUZognuxXEdlW19HAFNmEBTSBY5DfLwM8jQ==",
"dependencies": {
"Serilog": "2.4.0"
}
},
"Serilog.Exceptions": {
"type": "Transitive",
"resolved": "8.4.0",
"contentHash": "nc/+hUw3lsdo0zCj0KMIybAu7perMx79vu72w0za9Nsi6mWyNkGXxYxakAjWB7nEmYL6zdmhEQRB4oJ2ALUeug==",
"dependencies": {
"Serilog": "2.8.0"
}
},
"Serilog.Formatting.Compact": {
"type": "Transitive",
"resolved": "1.1.0",
"contentHash": "pNroKVjo+rDqlxNG5PXkRLpfSCuDOBY0ri6jp9PLe505ljqwhwZz8ospy2vWhQlFu5GkIesh3FcDs4n7sWZODA==",
"dependencies": {
"Serilog": "2.8.0"
}
},
"Serilog.Sinks.Console": {
"type": "Transitive",
"resolved": "4.1.0",
"contentHash": "K6N5q+5fetjnJPvCmkWOpJ/V8IEIoMIB1s86OzBrbxwTyHxdx3pmz4H+8+O/Dc/ftUX12DM1aynx/dDowkwzqg==",
"dependencies": {
"Serilog": "2.10.0"
}
},
"Serilog.Sinks.File": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==",
"dependencies": {
"Serilog": "2.10.0"
}
},
"Serilog.Sinks.PeriodicBatching": {
"type": "Transitive",
"resolved": "3.1.0",
"contentHash": "NDWR7m3PalVlGEq3rzoktrXikjFMLmpwF0HI4sowo8YDdU+gqPlTHlDQiOGxHfB0sTfjPA9JjA7ctKG9zqjGkw==",
"dependencies": {
"Serilog": "2.0.0"
}
},
"Serilog.Sinks.Seq": {
"type": "Transitive",
"resolved": "5.2.2",
"contentHash": "1Csmo5ua7NKUe0yXUx+zsRefjAniPWcXFhUXxXG8pwo0iMiw2gjn9SOkgYnnxbgWqmlGv236w0N/dHc2v5XwMg==",
"dependencies": {
"Serilog": "2.12.0",
"Serilog.Formatting.Compact": "1.1.0",
"Serilog.Sinks.File": "5.0.0",
"Serilog.Sinks.PeriodicBatching": "3.1.0"
}
},
"SerilogTimings": {
"type": "Transitive",
"resolved": "3.0.1",
"contentHash": "Zs28eTgszAMwpIrbBnWHBI50yuxL50p/dmAUWmy75+axdZYK/Sjm5/5m1N/CisR8acJUhTVcjPZrsB1P5iv0Uw==",
"dependencies": {
"Serilog": "2.10.0"
}
},
"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.Collections.Immutable": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "FXkLXiK0sVVewcso0imKQoOxjoPAj42R8HtjjbSjVPAzwDfzoyoznWxgA3c38LDbN9SJux1xXoXYAhz98j7r2g==",
"dependencies": {
"System.Memory": "4.5.4"
}
},
"System.Diagnostics.DiagnosticSource": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "9W0ewWDuAyDqS2PigdTxk6jDKonfgscY/hP8hm7VpxYhNHZHKvZTdRckberlFk3VnCmr3xBUyMBut12Q+T2aOw==",
"dependencies": {
"System.Memory": "4.5.5",
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"System.DoubleNumerics": {
"type": "Transitive",
"resolved": "3.1.3",
"contentHash": "KRKEM/L3KBodjA9VOg3EifFVWUY6EOqaMB05UvPEDm7Zeby/kZW+4kdWUEPzW6xtkwf46p661L9NrbeeQhtLzw==",
"dependencies": {
"NETStandard.Library": "1.6.1"
}
},
"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.Metadata": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "5NecZgXktdGg34rh1OenY1rFNDCI8xSjFr+Z4OU4cU06AQHUdRnIIEeWENu3Wl4YowbzkymAIMvi3WyK9U53pQ==",
"dependencies": {
"System.Collections.Immutable": "5.0.0"
}
},
"System.Runtime.CompilerServices.Unsafe": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
},
"System.Runtime.InteropServices.RuntimeInformation": {
"type": "Transitive",
"resolved": "4.3.0",
"contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw=="
},
"System.Text.Encodings.Web": {
"type": "Transitive",
"resolved": "5.0.1",
"contentHash": "KmJ+CJXizDofbq6mpqDoRRLcxgOd2z9X3XoFNULSbvbqVRZkFX3istvr+MUjL6Zw1RT+RNdoI4GYidIINtgvqQ==",
"dependencies": {
"System.Buffers": "4.5.1",
"System.Memory": "4.5.4"
}
},
"System.Text.Json": {
"type": "Transitive",
"resolved": "5.0.2",
"contentHash": "I47dVIGiV6SfAyppphxqupertT/5oZkYLDCX6vC3HpOI4ZLjyoKAreUoem2ie6G0RbRuFrlqz/PcTQjfb2DOfQ==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"System.Buffers": "4.5.1",
"System.Memory": "4.5.4",
"System.Numerics.Vectors": "4.5.0",
"System.Runtime.CompilerServices.Unsafe": "5.0.0",
"System.Text.Encodings.Web": "5.0.1",
"System.Threading.Tasks.Extensions": "4.5.4",
"System.ValueTuple": "4.5.0"
}
},
"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"
}
},
"System.ValueTuple": {
"type": "Transitive",
"resolved": "4.5.0",
"contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ=="
},
"speckle.autofac": {
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[7.0.0, )"
}
},
"speckle.connectors.dui": {
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[7.0.0, )",
"Speckle.Autofac": "[2.0.999-local, )",
"Speckle.Connectors.Utils": "[2.0.999-local, )",
"Speckle.Core": "[3.0.1-alpha.14, )",
"System.Threading.Tasks.Dataflow": "[6.0.0, )"
}
},
"speckle.connectors.utils": {
"type": "Project",
"dependencies": {
"Serilog.Extensions.Logging": "[7.0.0, )",
"Speckle.Autofac": "[2.0.999-local, )",
"Speckle.Core": "[3.0.1-alpha.14, )"
}
},
"speckle.converters.common": {
"type": "Project",
"dependencies": {
"Speckle.Autofac": "[2.0.999-local, )",
"Speckle.Objects": "[3.0.1-alpha.14, )"
}
},
"speckle.converters.common.dependencyinjection": {
"type": "Project",
"dependencies": {
"Speckle.Autofac": "[2.0.999-local, )",
"Speckle.Converters.Common": "[2.0.999-local, )"
}
},
"speckle.converters.revit2023": {
"type": "Project",
"dependencies": {
"Speckle.Converters.Common": "[2.0.999-local, )",
"Speckle.Revit.API": "[2023.0.0, )"
}
},
"speckle.converters.revit2023.dependencyinjection": {
"type": "Project",
"dependencies": {
"Speckle.Converters.Common": "[2.0.999-local, )",
"Speckle.Converters.Common.DependencyInjection": "[2.0.999-local, )",
"Speckle.Converters.Revit2023": "[2.0.999-local, )"
}
},
"Microsoft.Extensions.Logging.Abstractions": {
"type": "CentralTransitive",
"requested": "[7.0.0, )",
"resolved": "7.0.0",
"contentHash": "kmn78+LPVMOWeITUjIlfxUPDsI0R6G0RkeAMBmQxAJ7vBJn4q2dTva7pWi65ceN5vPGjJ9q/Uae2WKgvfktJAw==",
"dependencies": {
"System.Buffers": "4.5.1",
"System.Memory": "4.5.5"
}
},
"Serilog.Extensions.Logging": {
"type": "CentralTransitive",
"requested": "[7.0.0, )",
"resolved": "7.0.0",
"contentHash": "9faU0zNQqU7I6soVhLUMYaGNpgWv6cKlKb2S5AnS8gXxzW/em5Ladm/6FMrWTnX41cdbdGPOWNAo6adi4WaJ6A==",
"dependencies": {
"Microsoft.Extensions.Logging": "7.0.0",
"Serilog": "2.12.0"
}
},
"Speckle.Core": {
"type": "CentralTransitive",
"requested": "[3.0.1-alpha.14, )",
"resolved": "3.0.1-alpha.14",
"contentHash": "RzQPVIGFFkKvG56YLr8ACtiwdWJE6IJ9vCQ4qHa0PIsUEpfzAIAi59jnzqtByOFC0FiFrFPow9bkfzylaZorAA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Polly": "7.2.3",
"Polly.Contrib.WaitAndRetry": "1.1.1",
"Polly.Extensions.Http": "3.0.0",
"Sentry": "3.33.0",
"Sentry.Serilog": "3.33.0",
"Serilog": "2.12.0",
"Serilog.Enrichers.ClientInfo": "1.3.0",
"Serilog.Exceptions": "8.4.0",
"Serilog.Sinks.Console": "4.1.0",
"Serilog.Sinks.Seq": "5.2.2",
"SerilogTimings": "3.0.1",
"Speckle.Newtonsoft.Json": "13.0.2",
"System.DoubleNumerics": "3.1.3"
}
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.0.1-alpha.14, )",
"resolved": "3.0.1-alpha.14",
"contentHash": "z38LGryMvh7iU1uBW+4uo5DwsB3CwRgLt2uFexWFx3mPSid+A0l5XcJzOgLwgFhNl6B42Ryz4ezBsddTp1Uc/g==",
"dependencies": {
"Speckle.Core": "3.0.1-alpha.14"
}
},
"Speckle.Revit.API": {
"type": "CentralTransitive",
"requested": "[2023.0.0, )",
"resolved": "2023.0.0",
"contentHash": "tq40eD7psgTbV+epNouYyqfo6+hEi7FmXZqcxEOsAV7zfYyWhL6Rt3vmojkWGNuerGbH6oRI6KIIxrnlCNb8Hw=="
},
"System.Threading.Tasks.Dataflow": {
"type": "CentralTransitive",
"requested": "[6.0.0, )",
"resolved": "6.0.0",
"contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA=="
}
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 714 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

@@ -0,0 +1,117 @@
using System.Reflection;
using Autodesk.Revit.DB;
using Revit.Async;
using Speckle.Connectors.Utils.Reflection;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Connectors.Revit.Plugin;
using Speckle.Connectors.Utils;
using Speckle.Converters.RevitShared.Helpers;
using Speckle.Core.Logging;
namespace Speckle.Connectors.DUI.Bindings;
internal sealed class BasicConnectorBindingRevit : IBasicConnectorBinding
{
// POC: name and bridge might be better for them to be protected props?
public string Name { get; private set; }
public IBridge Parent { get; private set; }
public BasicConnectorBindingCommands Commands { get; }
private readonly DocumentModelStore _store;
private readonly RevitContext _revitContext;
private readonly RevitSettings _revitSettings;
public BasicConnectorBindingRevit(
DocumentModelStore store,
RevitSettings revitSettings,
IBridge parent,
RevitContext revitContext
)
{
Name = "baseBinding";
Parent = parent;
_store = store;
_revitContext = revitContext;
_revitSettings = revitSettings;
Commands = new BasicConnectorBindingCommands(parent);
// POC: event binding?
_store.DocumentChanged += (_, _) =>
{
Commands.NotifyDocumentChanged();
};
}
public string GetConnectorVersion()
{
return Assembly.GetAssembly(GetType()).GetVersion();
}
public string GetSourceApplicationName() => _revitSettings.HostSlug.ToLower(); // POC: maybe not right place but... // ANOTHER POC: We should align this naming from somewhere in common DUI projects instead old structs. I know there are other POC comments around this
public string GetSourceApplicationVersion() => _revitSettings.HostAppVersion; // POC: maybe not right place but...
public DocumentInfo? GetDocumentInfo()
{
// POC: not sure why this would ever be null, is this needed?
_revitContext.UIApplication.NotNull();
var doc = _revitContext.UIApplication.ActiveUIDocument?.Document;
if (doc is null)
{
return null;
}
if (doc.IsFamilyDocument)
{
return new DocumentInfo("", "", "") { Message = "Family environment files not supported by Speckle." };
}
var info = new DocumentInfo(doc.PathName, doc.Title, doc.GetHashCode().ToString());
return info;
}
public DocumentModelStore GetDocumentState() => _store;
public void AddModel(ModelCard model) => _store.Models.Add(model);
public void UpdateModel(ModelCard model) => _store.UpdateModel(model);
public void RemoveModel(ModelCard model) => _store.RemoveModel(model);
public void HighlightModel(string modelCardId)
{
SenderModelCard model = (SenderModelCard)_store.GetModelById(modelCardId);
var elementIds = model.SendFilter.NotNull().GetObjectIds().Select(ElementId.Parse).ToList();
if (elementIds.Count == 0)
{
Commands.SetModelError(modelCardId, new InvalidOperationException("No objects found to highlight."));
return;
}
HighlightObjectsOnView(elementIds);
}
public void HighlightObjects(List<string> objectIds) =>
HighlightObjectsOnView(objectIds.Select(ElementId.Parse).ToList());
private void HighlightObjectsOnView(List<ElementId> objectIds)
{
// POC: don't know if we can rely on storing the ActiveUIDocument, hence getting it each time
var activeUIDoc =
_revitContext.UIApplication?.ActiveUIDocument
?? 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);
});
}
}
@@ -0,0 +1,30 @@
using Speckle.Connectors.DUI.Models.Card.SendFilter;
namespace Speckle.Connectors.Revit.Bindings;
public class RevitEverythingFilter : EverythingSendFilter
{
public override List<string> GetObjectIds()
{
// TODO
return new List<string>();
}
public override bool CheckExpiry(string[] changedObjectIds)
{
return true;
}
}
public class RevitSelectionFilter : DirectSelectionSendFilter
{
public override List<string> GetObjectIds()
{
return SelectedObjectIds;
}
public override bool CheckExpiry(string[] changedObjectIds)
{
return SelectedObjectIds.Intersect(changedObjectIds).Any();
}
}
@@ -0,0 +1,24 @@
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Models;
using Speckle.Converters.RevitShared.Helpers;
namespace Speckle.Connectors.Revit.Bindings;
internal abstract class RevitBaseBinding : IBinding
{
// POC: name and bridge might be better for them to be protected props?
public string Name { get; }
public IBridge Parent { get; }
protected readonly DocumentModelStore Store;
protected readonly RevitContext RevitContext;
protected RevitBaseBinding(string name, DocumentModelStore store, IBridge bridge, RevitContext revitContext)
{
Name = name;
Parent = bridge;
Store = store;
RevitContext = revitContext;
}
}
@@ -0,0 +1,82 @@
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.Utils.Builders;
using Speckle.Connectors.Utils.Cancellation;
using Speckle.Connectors.Utils.Operations;
using Speckle.Autofac.DependencyInjection;
using Speckle.Connectors.Utils;
namespace Speckle.Connectors.Revit.Bindings;
internal class RevitReceiveBinding : IReceiveBinding
{
public string Name => "receiveBinding";
public IBridge Parent { get; }
private readonly CancellationManager _cancellationManager;
private readonly DocumentModelStore _store;
private readonly IUnitOfWorkFactory _unitOfWorkFactory;
public ReceiveBindingUICommands Commands { get; }
public RevitReceiveBinding(
DocumentModelStore store,
CancellationManager cancellationManager,
IBridge parent,
IUnitOfWorkFactory unitOfWorkFactory
)
{
Parent = parent;
_store = store;
_unitOfWorkFactory = unitOfWorkFactory;
_cancellationManager = cancellationManager;
Commands = new ReceiveBindingUICommands(parent);
}
public void CancelReceive(string modelCardId) => _cancellationManager.CancelOperation(modelCardId);
public async Task Receive(string modelCardId)
{
using var unitOfWork = _unitOfWorkFactory.Resolve<ReceiveOperation>();
try
{
// Get receiver card
if (_store.GetModelById(modelCardId) is not ReceiverModelCard modelCard)
{
// Handle as GLOBAL ERROR at BrowserBridge
throw new InvalidOperationException("No download model card was found.");
}
// Init cancellation token source -> Manager also cancel it if exist before
CancellationTokenSource cts = _cancellationManager.InitCancellationTokenSource(modelCardId);
// Receive host objects
HostObjectBuilderResult conversionResults = await unitOfWork.Service
.Execute(
modelCard.AccountId.NotNull(), // POC: I hear -you are saying why we're passing them separately. Not sure pass the DUI3-> Connectors.DUI project dependency to the SDK-> Connector.Utils
modelCard.ProjectId.NotNull(),
modelCard.ProjectName.NotNull(),
modelCard.ModelName.NotNull(),
modelCard.SelectedVersionId.NotNull(),
cts.Token,
(status, progress) =>
Commands.SetModelProgress(modelCardId, new ModelCardProgress(modelCardId, status, progress), cts)
)
.ConfigureAwait(false);
modelCard.BakedObjectIds = conversionResults.BakedObjectIds.ToList();
Commands.SetModelReceiveResult(
modelCardId,
conversionResults.BakedObjectIds,
conversionResults.ConversionResults
);
}
// Catch here specific exceptions if they related to model card.
catch (OperationCanceledException)
{
// SWALLOW -> UI handles it immediately, so we do not need to handle anything
return;
}
}
}
@@ -0,0 +1,200 @@
using Autodesk.Revit.DB;
using Speckle.Connectors.DUI.Models.Card.SendFilter;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.Revit.Plugin;
using Speckle.Connectors.Utils;
using Speckle.Converters.RevitShared.Helpers;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Autofac.DependencyInjection;
using Speckle.Connectors.DUI.Exceptions;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.Utils.Caching;
using Speckle.Connectors.Utils.Cancellation;
using Speckle.Connectors.Utils.Operations;
namespace Speckle.Connectors.Revit.Bindings;
internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
{
// POC:does it need injecting?
// POC: does it need injecting?
private HashSet<string> ChangedObjectIds { get; set; } = new();
private readonly RevitSettings _revitSettings;
private readonly IRevitIdleManager _idleManager;
private readonly CancellationManager _cancellationManager;
private readonly IUnitOfWorkFactory _unitOfWorkFactory;
private readonly ISendConversionCache _sendConversionCache;
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
public RevitSendBinding(
IRevitIdleManager idleManager,
RevitContext revitContext,
DocumentModelStore store,
CancellationManager cancellationManager,
IBridge bridge,
IUnitOfWorkFactory unitOfWorkFactory,
RevitSettings revitSettings,
ISendConversionCache sendConversionCache,
ITopLevelExceptionHandler topLevelExceptionHandler
)
: base("sendBinding", store, bridge, revitContext)
{
_idleManager = idleManager;
_cancellationManager = cancellationManager;
_unitOfWorkFactory = unitOfWorkFactory;
_revitSettings = revitSettings;
_sendConversionCache = sendConversionCache;
_topLevelExceptionHandler = topLevelExceptionHandler;
Commands = new SendBindingUICommands(bridge);
// TODO expiry events
// TODO filters need refresh events
revitContext.UIApplication.NotNull().Application.DocumentChanged += (_, e) =>
_topLevelExceptionHandler.CatchUnhandled(() => DocChangeHandler(e));
Store.DocumentChanged += (_, _) => _topLevelExceptionHandler.CatchUnhandled(OnDocumentChanged);
}
public List<ISendFilter> GetSendFilters()
{
return new List<ISendFilter> { new RevitSelectionFilter() { IsDefault = true } };
}
public void CancelSend(string modelCardId)
{
_cancellationManager.CancelOperation(modelCardId);
}
public SendBindingUICommands Commands { get; }
public async Task Send(string modelCardId)
{
// Note: removed top level handling thing as it was confusing me
try
{
if (Store.GetModelById(modelCardId) is not SenderModelCard modelCard)
{
// Handle as GLOBAL ERROR at BrowserBridge
throw new InvalidOperationException("No publish model card was found.");
}
// POC: probably the CTS SHOULD be injected as InstancePerLifetimeScope and then
// it can be injected where needed instead of passing it around like a bomb :D
CancellationTokenSource cts = _cancellationManager.InitCancellationTokenSource(modelCardId);
using IUnitOfWork<SendOperation<ElementId>> sendOperation = _unitOfWorkFactory.Resolve<
SendOperation<ElementId>
>();
List<ElementId> revitObjects = modelCard.SendFilter
.NotNull()
.GetObjectIds()
.Select(id => ElementId.Parse(id))
.ToList();
if (revitObjects.Count == 0)
{
// Handle as CARD ERROR in this function
throw new SpeckleSendFilterException("No objects were found to convert. Please update your publish filter!");
}
var sendInfo = new SendInfo(
modelCard.AccountId.NotNull(),
modelCard.ProjectId.NotNull(),
modelCard.ModelId.NotNull(),
_revitSettings.HostSlug.NotNull()
);
var sendResult = await sendOperation.Service
.Execute(
revitObjects,
sendInfo,
(status, progress) =>
Commands.SetModelProgress(modelCardId, new ModelCardProgress(modelCardId, status, progress), cts),
cts.Token
)
.ConfigureAwait(false);
Commands.SetModelSendResult(modelCardId, sendResult.RootObjId, sendResult.ConversionResults);
}
// Catch here specific exceptions if they related to model card.
catch (SpeckleSendFilterException e)
{
Commands.SetModelError(modelCardId, e);
}
catch (OperationCanceledException)
{
return;
}
}
/// <summary>
/// Keeps track of the changed element ids as well as checks if any of them need to trigger
/// a filter refresh (e.g., views being added).
/// </summary>
/// <param name="e"></param>
private void DocChangeHandler(Autodesk.Revit.DB.Events.DocumentChangedEventArgs e)
{
ICollection<ElementId> addedElementIds = e.GetAddedElementIds();
ICollection<ElementId> deletedElementIds = e.GetDeletedElementIds();
ICollection<ElementId> modifiedElementIds = e.GetModifiedElementIds();
foreach (ElementId elementId in addedElementIds)
{
ChangedObjectIds.Add(elementId.ToString());
}
foreach (ElementId elementId in deletedElementIds)
{
ChangedObjectIds.Add(elementId.ToString());
}
foreach (ElementId elementId in modifiedElementIds)
{
ChangedObjectIds.Add(elementId.ToString());
}
// TODO: CHECK IF ANY OF THE ABOVE ELEMENTS NEED TO TRIGGER A FILTER REFRESH
_idleManager.SubscribeToIdle(RunExpirationChecks);
}
private void RunExpirationChecks()
{
var senders = Store.GetSenders();
string[] objectIdsList = ChangedObjectIds.ToArray();
List<string> 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 HashSet<string>();
}
// POC: Will be re-addressed later with better UX with host apps that are friendly on async doc operations.
// That's why don't bother for now how to get rid of from dup logic in other bindings.
private void OnDocumentChanged()
{
if (_cancellationManager.NumberOfOperations > 0)
{
_cancellationManager.CancelAllOperations();
Commands.SetGlobalNotification(
ToastNotificationType.INFO,
"Document Switch",
"Operations cancelled because of document swap!"
);
}
}
}
@@ -0,0 +1,49 @@
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.Revit.Plugin;
using Speckle.Converters.Common;
using Speckle.Converters.RevitShared.Helpers;
namespace Speckle.Connectors.Revit.Bindings;
// POC: we need a base a RevitBaseBinding
internal sealed class SelectionBinding : RevitBaseBinding, ISelectionBinding
{
private readonly IRevitIdleManager _revitIdleManager;
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
public SelectionBinding(
RevitContext revitContext,
DocumentModelStore store,
IRevitIdleManager idleManager,
IBridge bridge,
ITopLevelExceptionHandler topLevelExceptionHandler
)
: base("selectionBinding", store, bridge, revitContext)
{
_revitIdleManager = idleManager;
_topLevelExceptionHandler = topLevelExceptionHandler;
// POC: we can inject the solution here
// TODO: Need to figure it out equivalent of SelectionChanged for Revit2020
RevitContext.UIApplication.NotNull().SelectionChanged += (_, _) =>
topLevelExceptionHandler.CatchUnhandled(() => _revitIdleManager.SubscribeToIdle(OnSelectionChanged));
}
private void OnSelectionChanged()
{
Parent.Send(SelectionBindingEvents.SET_SELECTION, GetSelection());
}
public SelectionInfo GetSelection()
{
// POC: this was also being called on shutdown
// probably the bridge needs to be able to know if the plugin has been terminated
// also on termination the OnSelectionChanged event needs unwinding
var selectionIds = (RevitContext.UIApplication?.ActiveUIDocument?.Selection.GetElementIds())
.NotNull()
.Select(id => id.ToString())
.ToList();
return new SelectionInfo(selectionIds, $"{selectionIds.Count} objects selected.");
}
}
@@ -0,0 +1,72 @@
using Autodesk.Revit.DB;
using CefSharp;
using Speckle.Autofac;
using Speckle.Autofac.DependencyInjection;
using Speckle.Connectors.DUI;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.Revit.Bindings;
using Speckle.Connectors.Revit.HostApp;
using Speckle.Connectors.Revit.Operations.Receive;
using Speckle.Connectors.Revit.Operations.Send;
using Speckle.Connectors.Revit.Plugin;
using Speckle.Connectors.Utils;
using Speckle.Connectors.Utils.Builders;
using Speckle.Connectors.Utils.Caching;
using Speckle.Connectors.Utils.Operations;
using Speckle.Core.Models.GraphTraversal;
namespace Speckle.Connectors.Revit.DependencyInjection;
// POC: should interface out things that are not
public class RevitConnectorModule : ISpeckleModule
{
public void Load(SpeckleContainerBuilder builder)
{
builder.AddAutofac();
builder.AddConnectorUtils();
builder.AddDUI();
//builder.AddDUIView();
builder.AddSingletonInstance<ISyncToThread, RevitContextAccessor>();
// POC: different versons for different versions of CEF
builder.AddSingleton(BindingOptions.DefaultBinder);
var panel = new CefSharpPanel();
panel.Browser.JavascriptObjectRepository.NameConverter = null;
builder.AddSingleton(panel);
builder.AddSingleton<IRevitPlugin, RevitPlugin>();
// register
builder.AddSingleton<DocumentModelStore, RevitDocumentStore>();
// Storage Schema
builder.AddScoped<DocumentModelStorageSchema>();
builder.AddScoped<IdStorageSchema>();
// POC: we need to review the scopes and create a document on what the policy is
// and where the UoW should be
// register UI bindings
builder.AddSingleton<IBinding, TestBinding>();
builder.AddSingleton<IBinding, ConfigBinding>("connectorName", "Revit"); // POC: Easier like this for now, should be cleaned up later
builder.AddSingleton<IBinding, AccountBinding>();
builder.AddSingleton<IBinding, BasicConnectorBindingRevit>();
builder.AddSingleton<IBasicConnectorBinding, BasicConnectorBindingRevit>();
builder.AddSingleton<IBinding, SelectionBinding>();
builder.AddSingleton<IBinding, RevitSendBinding>();
builder.AddSingleton<IBinding, RevitReceiveBinding>();
builder.AddSingleton<IRevitIdleManager, RevitIdleManager>();
// send operation and dependencies
builder.AddScoped<SendOperation<ElementId>>();
builder.AddScoped<IRootObjectBuilder<ElementId>, RevitRootObjectBuilder>();
builder.AddSingleton<ISendConversionCache, SendConversionCache>();
// receive operation and dependencies
builder.AddScoped<IHostObjectBuilder, RevitHostObjectBuilder>();
builder.AddScoped<ITransactionManager, TransactionManager>();
builder.AddSingleton(DefaultTraversal.CreateTraversalFunc());
}
}
@@ -0,0 +1,22 @@
using Autodesk.Revit.DB.ExtensibleStorage;
namespace Speckle.Connectors.Revit.HostApp;
public class DocumentModelStorageSchema : IStorageSchema
{
private readonly Guid _schemaGuid = new("D690F2B4-BDB0-4CB4-8657-17844ADF42AA");
public Schema GetSchema()
{
Schema schema = Schema.Lookup(_schemaGuid);
if (schema != null)
{
return schema;
}
using SchemaBuilder builder = new(_schemaGuid);
builder.SetSchemaName("DUI3State");
builder.AddSimpleField("contents", typeof(string));
return builder.Finish();
}
}
@@ -0,0 +1,12 @@
using Autodesk.Revit.DB;
namespace Speckle.Connectors.Revit.HostApp;
// POC: is this really better than injection? :/
public static class Elements
{
public static IEnumerable<Element> GetElements(this Document doc, IEnumerable<string> objectIds)
{
return objectIds.Select(doc.GetElement).Where(x => x != null);
}
}
@@ -0,0 +1,8 @@
using Autodesk.Revit.DB.ExtensibleStorage;
namespace Speckle.Connectors.Revit.HostApp;
public interface IStorageSchema
{
Schema GetSchema();
}
@@ -0,0 +1,22 @@
using Autodesk.Revit.DB.ExtensibleStorage;
namespace Speckle.Connectors.Revit.HostApp;
public class IdStorageSchema : IStorageSchema
{
private readonly Guid _schemaGuid = new("D0E2AD18-0DE0-41CF-A2B7-5384267061D7");
public Schema GetSchema()
{
Schema schema = Schema.Lookup(_schemaGuid);
if (schema != null)
{
return schema;
}
using SchemaBuilder builder = new(_schemaGuid);
builder.SetSchemaName("DataStorageUniqueId");
builder.AddSimpleField("Id", typeof(Guid));
return builder.Finish();
}
}
@@ -0,0 +1,183 @@
using System.Diagnostics;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.ExtensibleStorage;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Events;
using Revit.Async;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.Revit.Plugin;
using Speckle.Connectors.Utils;
using Speckle.Converters.RevitShared.Helpers;
using Speckle.Core.Logging;
using Speckle.Newtonsoft.Json;
namespace Speckle.Connectors.Revit.HostApp;
// POC: should be interfaced out
internal sealed class RevitDocumentStore : DocumentModelStore
{
// POC: move to somewhere central?
private static readonly Guid s_revitDocumentStoreId = new("D35B3695-EDC9-4E15-B62A-D3FC2CB83FA3");
private readonly RevitContext _revitContext;
private readonly IRevitIdleManager _idleManager;
private readonly DocumentModelStorageSchema _documentModelStorageSchema;
private readonly IdStorageSchema _idStorageSchema;
public RevitDocumentStore(
IRevitIdleManager idleManager,
RevitContext revitContext,
JsonSerializerSettings serializerSettings,
DocumentModelStorageSchema documentModelStorageSchema,
IdStorageSchema idStorageSchema,
ITopLevelExceptionHandler topLevelExceptionHandler
)
: base(serializerSettings, true)
{
_idleManager = idleManager;
_revitContext = revitContext;
_documentModelStorageSchema = documentModelStorageSchema;
_idStorageSchema = idStorageSchema;
UIApplication uiApplication = _revitContext.UIApplication.NotNull();
uiApplication.ViewActivated += (s, e) => topLevelExceptionHandler.CatchUnhandled(() => OnViewActivated(s, e));
uiApplication.Application.DocumentOpening += (_, _) =>
topLevelExceptionHandler.CatchUnhandled(() => IsDocumentInit = false);
uiApplication.Application.DocumentOpened += (_, _) =>
topLevelExceptionHandler.CatchUnhandled(() => IsDocumentInit = false);
Models.CollectionChanged += (_, _) => topLevelExceptionHandler.CatchUnhandled(WriteToFile);
// There is no event that we can hook here for double-click file open...
// It is kind of harmless since we create this object as "SingleInstance".
ReadFromFile();
OnDocumentChanged();
}
/// <summary>
/// This is the place where we track document switch for new document -> Responsible to Read from new doc
/// </summary>
private void OnViewActivated(object sender, ViewActivatedEventArgs e)
{
if (e.Document == null)
{
return;
}
// Return only if we are switching views that belongs to same document
if (e.PreviousActiveView?.Document != null && e.PreviousActiveView.Document.Equals(e.CurrentActiveView.Document))
{
return;
}
IsDocumentInit = true;
_idleManager.SubscribeToIdle(() =>
{
ReadFromFile();
OnDocumentChanged();
});
}
public override void WriteToFile()
{
var doc = _revitContext.UIApplication?.ActiveUIDocument.Document;
// POC: this can happen? A: Not really, imho (dim)
if (doc == null)
{
return;
}
RevitTask.RunAsync(() =>
{
using Transaction t = new(doc, "Speckle Write State");
t.Start();
using DataStorage ds = GetSettingsDataStorage(doc) ?? DataStorage.Create(doc);
using Entity stateEntity = new(_documentModelStorageSchema.GetSchema());
string serializedModels = Serialize();
stateEntity.Set("contents", serializedModels);
using Entity idEntity = new(_idStorageSchema.GetSchema());
idEntity.Set("Id", s_revitDocumentStoreId);
ds.SetEntity(idEntity);
ds.SetEntity(stateEntity);
t.Commit();
});
}
public override void ReadFromFile()
{
try
{
var stateEntity = GetSpeckleEntity(_revitContext.UIApplication?.ActiveUIDocument?.Document);
if (stateEntity == null || !stateEntity.IsValid())
{
Models = new();
return;
}
string modelsString = stateEntity.Get<string>("contents");
Models = Deserialize(modelsString).NotNull();
}
catch (Exception ex) when (!ex.IsFatal())
{
Models = new();
Debug.WriteLine(ex.Message); // POC: Log here error and notify UI that cards not read succesfully
}
}
private DataStorage? GetSettingsDataStorage(Document doc)
{
using FilteredElementCollector collector = new(doc);
FilteredElementCollector dataStorages = collector.OfClass(typeof(DataStorage));
foreach (Element element in dataStorages)
{
DataStorage dataStorage = (DataStorage)element;
Entity settingIdEntity = dataStorage.GetEntity(_idStorageSchema.GetSchema());
if (!settingIdEntity.IsValid())
{
continue;
}
Guid id = settingIdEntity.Get<Guid>("Id");
if (!id.Equals(s_revitDocumentStoreId))
{
continue;
}
return dataStorage;
}
return null;
}
private Entity? GetSpeckleEntity(Document? doc)
{
if (doc is null)
{
return null;
}
using FilteredElementCollector collector = new(doc);
FilteredElementCollector dataStorages = collector.OfClass(typeof(DataStorage));
foreach (Element element in dataStorages)
{
DataStorage dataStorage = (DataStorage)element;
Entity settingEntity = dataStorage.GetEntity(_documentModelStorageSchema.GetSchema());
if (!settingEntity.IsValid())
{
continue;
}
return settingEntity;
}
return null;
}
}
@@ -0,0 +1,13 @@
using Autodesk.Revit.DB;
namespace Speckle.Connectors.Revit.Operations.Receive;
public interface ITransactionManager : IDisposable
{
TransactionStatus CommitSubtransaction();
TransactionStatus CommitTransaction();
void RollbackSubTransaction();
void RollbackTransaction();
void StartSubtransaction();
void StartTransaction();
}
@@ -0,0 +1,9 @@
using Revit.Async;
using Speckle.Connectors.Utils.Operations;
namespace Speckle.Connectors.Revit.Operations.Receive;
internal class RevitContextAccessor : ISyncToThread
{
public Task<T> RunOnThread<T>(Func<T> func) => RevitTask.RunAsync(func);
}
@@ -0,0 +1,85 @@
using Speckle.Connectors.Utils.Builders;
using Speckle.Connectors.Utils.Conversion;
using Speckle.Converters.Common;
using Speckle.Core.Logging;
using Speckle.Core.Models.GraphTraversal;
using Speckle.Core.Models;
using Speckle.Converters.RevitShared.Helpers;
using Autodesk.Revit.DB;
namespace Speckle.Connectors.Revit.Operations.Receive;
/// <summary>
/// Potentially consolidate all application specific IHostObjectBuilders
/// https://spockle.atlassian.net/browse/DUI3-465
/// </summary>
internal class RevitHostObjectBuilder : IHostObjectBuilder, IDisposable
{
private readonly IRootToHostConverter _converter;
private readonly IRevitConversionContextStack _contextStack;
private readonly GraphTraversal _traverseFunction;
private readonly ITransactionManager _transactionManager;
public RevitHostObjectBuilder(
IRootToHostConverter converter,
IRevitConversionContextStack contextStack,
GraphTraversal traverseFunction,
ITransactionManager transactionManager
)
{
_converter = converter;
_contextStack = contextStack;
_traverseFunction = traverseFunction;
_transactionManager = transactionManager;
}
public HostObjectBuilderResult Build(
Base rootObject,
string projectName,
string modelName,
Action<string, double?>? onOperationProgressed,
CancellationToken cancellationToken
)
{
var objectsToConvert = _traverseFunction
.TraverseWithProgress(rootObject, onOperationProgressed, cancellationToken)
.Where(obj => obj.Current is not Collection);
using TransactionGroup transactionGroup = new(_contextStack.Current.Document, $"Received data from {projectName}");
transactionGroup.Start();
_transactionManager.StartTransaction();
var conversionResults = BakeObjects(objectsToConvert);
_transactionManager.CommitTransaction();
transactionGroup.Assimilate();
return conversionResults;
}
// POC: Potentially refactor out into an IObjectBaker.
private HostObjectBuilderResult BakeObjects(IEnumerable<TraversalContext> objectsGraph)
{
var conversionResults = new List<ReceiveConversionResult>();
var bakedObjectIds = new List<string>();
foreach (TraversalContext tc in objectsGraph)
{
try
{
var result = _converter.Convert(tc.Current);
}
catch (Exception ex) when (!ex.IsFatal())
{
conversionResults.Add(new(Status.ERROR, tc.Current, null, null, ex));
}
}
return new(bakedObjectIds, conversionResults);
}
public void Dispose()
{
_transactionManager?.Dispose();
}
}
@@ -0,0 +1,124 @@
using Autodesk.Revit.DB;
using Speckle.Converters.RevitShared.Helpers;
namespace Speckle.Connectors.Revit.Operations.Receive;
/// <summary>
/// Is responsible for all functionality regarding subtransactions, transactions, and transaction groups.
/// This includes starting, pausing, committing, and rolling back transactions
/// </summary>
public sealed class TransactionManager : ITransactionManager
{
private readonly IRevitConversionContextStack _contextStack;
private Document Document => _contextStack.Current.Document;
public TransactionManager(IRevitConversionContextStack contextStack)
{
_contextStack = contextStack;
}
// poc : these are being disposed. I'm not sure why I need to supress this warning
#pragma warning disable CA2213 // Disposable fields should be disposed
private Transaction? _transaction;
private SubTransaction? _subTransaction;
#pragma warning restore CA2213 // Disposable fields should be disposed
public void StartTransaction()
{
if (_transaction == null || !_transaction.IsValidObject || _transaction.GetStatus() != TransactionStatus.Started)
{
_transaction = new Transaction(Document, "Speckle Transaction");
var failOpts = _transaction.GetFailureHandlingOptions();
// POC: make sure to implement and add the failure preprocessor
// https://spockle.atlassian.net/browse/DUI3-461
//failOpts.SetFailuresPreprocessor(_errorPreprocessingService);
failOpts.SetClearAfterRollback(true);
_transaction.SetFailureHandlingOptions(failOpts);
_transaction.Start();
}
}
public TransactionStatus CommitTransaction()
{
if (
_subTransaction != null
&& _subTransaction.IsValidObject
&& _subTransaction.GetStatus() == TransactionStatus.Started
)
{
var status = _subTransaction.Commit();
if (status != TransactionStatus.Committed)
{
// POC: handle failed commit
//HandleFailedCommit(status);
}
}
if (_transaction != null && _transaction.IsValidObject && _transaction.GetStatus() == TransactionStatus.Started)
{
var status = _transaction.Commit();
if (status != TransactionStatus.Committed)
{
// POC: handle failed commit
//HandleFailedCommit(status);
}
return status;
}
return TransactionStatus.Uninitialized;
}
public void RollbackTransaction()
{
RollbackSubTransaction();
if (_transaction != null && _transaction.IsValidObject && _transaction.GetStatus() == TransactionStatus.Started)
{
_transaction.RollBack();
}
}
public void StartSubtransaction()
{
StartTransaction();
if (
_subTransaction == null
|| !_subTransaction.IsValidObject
|| _subTransaction.GetStatus() != TransactionStatus.Started
)
{
_subTransaction = new SubTransaction(Document);
_subTransaction.Start();
}
}
public TransactionStatus CommitSubtransaction()
{
if (_subTransaction != null && _subTransaction.IsValidObject)
{
var status = _subTransaction.Commit();
if (status != TransactionStatus.Committed)
{
// POC: handle failed commit
//HandleFailedCommit(status);
}
return status;
}
return TransactionStatus.Uninitialized;
}
public void RollbackSubTransaction()
{
if (
_subTransaction != null
&& _subTransaction.IsValidObject
&& _subTransaction.GetStatus() == TransactionStatus.Started
)
{
_subTransaction.RollBack();
}
}
public void Dispose()
{
_subTransaction?.Dispose();
_transaction?.Dispose();
}
}
@@ -0,0 +1,156 @@
using System.Diagnostics;
using Speckle.Converters.Common;
using Speckle.Core.Models;
using Autodesk.Revit.DB;
using Speckle.Connectors.DUI.Exceptions;
using Speckle.Converters.RevitShared.Helpers;
using Speckle.Connectors.Utils.Builders;
using Speckle.Connectors.Utils.Caching;
using Speckle.Connectors.Utils.Conversion;
using Speckle.Connectors.Utils.Operations;
using Speckle.Core.Logging;
namespace Speckle.Connectors.Revit.Operations.Send;
public class RevitRootObjectBuilder : IRootObjectBuilder<ElementId>
{
// POC: SendSelection and RevitConversionContextStack should be interfaces, former needs interfaces
private readonly IRootToSpeckleConverter _converter;
private readonly IRevitConversionContextStack _contextStack;
private readonly Dictionary<string, Collection> _collectionCache;
private readonly Collection _rootObject;
private readonly ISendConversionCache _sendConversionCache;
public RevitRootObjectBuilder(
IRootToSpeckleConverter converter,
IRevitConversionContextStack contextStack,
ISendConversionCache sendConversionCache
)
{
_converter = converter;
_contextStack = contextStack;
_sendConversionCache = sendConversionCache;
// Note, this class is instantiated per unit of work (aka per send operation), so we can safely initialize what we need in here.
_collectionCache = new Dictionary<string, Collection>();
_rootObject = new Collection()
{
name = _contextStack.Current.Document.PathName.Split('\\').Last().Split('.').First()
};
}
public RootObjectBuilderResult Build(
IReadOnlyList<ElementId> objects,
SendInfo sendInfo,
Action<string, double?>? onOperationProgressed = null,
CancellationToken ct = default
)
{
var doc = _contextStack.Current.Document;
if (doc.IsFamilyDocument)
{
throw new SpeckleException("Family Environment documents are not supported.");
}
var revitElements = new List<Element>();
foreach (var id in objects)
{
var el = _contextStack.Current.Document.GetElement(id);
if (el != null)
{
revitElements.Add(el);
}
}
if (revitElements.Count == 0)
{
throw new SpeckleSendFilterException("No objects were found. Please update your send filter!");
}
var countProgress = 0; // because for(int i = 0; ...) loops are so last year
var cacheHitCount = 0;
List<SendConversionResult> results = new(revitElements.Count);
var path = new string[2];
foreach (Element revitElement in revitElements)
{
ct.ThrowIfCancellationRequested();
var cat = revitElement.Category.Name;
path[0] = doc.GetElement(revitElement.LevelId) is not Level level ? "No level" : level.Name;
path[1] = cat;
var collection = GetAndCreateObjectHostCollection(path);
var applicationId = revitElement.Id.ToString();
try
{
Base converted;
if (_sendConversionCache.TryGetValue(sendInfo.ProjectId, applicationId, out ObjectReference value))
{
converted = value;
cacheHitCount++;
}
else
{
converted = _converter.Convert(revitElement);
converted.applicationId = applicationId;
}
collection.elements.Add(converted);
results.Add(new(Status.SUCCESS, applicationId, revitElement.GetType().Name, converted));
}
catch (Exception ex) when (!ex.IsFatal())
{
results.Add(new(Status.ERROR, applicationId, revitElement.GetType().Name, null, ex));
// POC: add logging
}
onOperationProgressed?.Invoke("Converting", (double)++countProgress / revitElements.Count);
}
// POC: Log would be nice, or can be removed.
Debug.WriteLine(
$"Cache hit count {cacheHitCount} out of {objects.Count} ({(double)cacheHitCount / objects.Count})"
);
return new(_rootObject, results);
}
/// <summary>
/// Creates and nests collections based on the provided path within the root collection provided. This will not return a new collection each time is called, but an existing one if one is found.
/// For example, you can use this to use (or re-use) a new collection for a path of (level, category) as it's currently implemented.
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
private Collection GetAndCreateObjectHostCollection(string[] path)
{
string fullPathName = string.Concat(path);
if (_collectionCache.TryGetValue(fullPathName, out Collection value))
{
return value;
}
string flatPathName = "";
Collection previousCollection = _rootObject;
foreach (var pathItem in path)
{
flatPathName += pathItem;
Collection childCollection;
if (_collectionCache.TryGetValue(flatPathName, out Collection? collection))
{
childCollection = collection;
}
else
{
childCollection = new Collection(pathItem, "layer");
previousCollection.elements.Add(childCollection);
_collectionCache[flatPathName] = childCollection;
}
previousCollection = childCollection;
}
return previousCollection;
}
}
@@ -0,0 +1,18 @@
<Page x:Class="Speckle.Connectors.Revit.CefSharpPanel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:cefSharp="clr-namespace:CefSharp.Wpf;assembly=CefSharp.Wpf"
xmlns:local="clr-namespace:Speckle.Connectors.DUI;assembly=Speckle.Connectors.DUI"
mc:Ignorable="d"
Title="Panel" Height="450" Width="800">
<Grid>
<cefSharp:ChromiumWebBrowser
Name="Browser"
Grid.Row="0"
Address="{x:Static local:Url.NetlifyString}" />
</Grid>
</Page>
@@ -0,0 +1,29 @@
using System.Windows.Controls;
using Autodesk.Revit.UI;
using CefSharp;
using System.Windows.Threading;
namespace Speckle.Connectors.Revit;
public partial class CefSharpPanel : Page, Autodesk.Revit.UI.IDockablePaneProvider
{
public CefSharpPanel()
{
InitializeComponent();
}
public void ExecuteScriptAsync(string script) =>
Browser.Dispatcher.Invoke(() => Browser.ExecuteScriptAsync(script), DispatcherPriority.Background);
public void ShowDevTools() => Browser.ShowDevTools();
public void SetupDockablePane(Autodesk.Revit.UI.DockablePaneProviderData data)
{
data.FrameworkElement = this;
data.InitialState = new Autodesk.Revit.UI.DockablePaneState
{
DockPosition = DockPosition.Tabbed,
TabBehind = Autodesk.Revit.UI.DockablePanes.BuiltInDockablePanes.ProjectBrowser
};
}
}
@@ -0,0 +1,14 @@
namespace Speckle.Connectors.Revit.Plugin;
// POC: needs interface
// is probably misnamed, perhaps OnIdleCallbackManager
internal interface IRevitIdleManager
{
/// <summary>
/// Subscribe deferred action to Idling event to run it whenever Revit becomes idle.
/// </summary>
/// <param name="action"> Action to call whenever Revit becomes Idle.</param>
/// 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
void SubscribeToIdle(Action action);
}
@@ -0,0 +1,7 @@
namespace Speckle.Connectors.Revit.Plugin;
internal interface IRevitPlugin
{
void Initialise();
void Shutdown();
}
@@ -0,0 +1,17 @@
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
namespace Speckle.Connectors.Revit.Plugin;
[Transaction(TransactionMode.Manual)]
internal sealed class SpeckleRevitCommand : IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
DockablePane panel = commandData.Application.GetDockablePane(RevitExternalApplication.DoackablePanelId);
panel.Show();
return Result.Succeeded;
}
}
@@ -0,0 +1,85 @@
using Autodesk.Revit.UI;
using Speckle.Autofac.DependencyInjection;
using System.IO;
using System.Reflection;
using Speckle.Autofac;
using Speckle.Connectors.Utils;
namespace Speckle.Connectors.Revit.Plugin;
internal sealed class RevitExternalApplication : IExternalApplication
{
private IRevitPlugin? _revitPlugin;
private SpeckleContainer? _container;
// POC: this is getting hard coded - need a way of injecting it
// I am beginning to think the shared project is not the way
// and an assembly which is invoked with some specialisation is the right way to go
// maybe subclassing, or some hook to inject som configuration
private readonly RevitSettings _revitSettings;
// POC: move to somewhere central?
public static readonly DockablePaneId DoackablePanelId = new(new Guid("{f7b5da7c-366c-4b13-8455-b56f433f461e}"));
public RevitExternalApplication()
{
// POC: load from JSON file?
_revitSettings = new RevitSettings(
"Speckle New UI",
"Speckle",
"Speckle New UI",
"2023",
"Speckle New UI",
"Revit",
new[] { Path.GetDirectoryName(typeof(RevitExternalApplication).Assembly.Location) },
"Revit Connector",
"2023" //POC: app version?
);
}
public Result OnStartup(UIControlledApplication application)
{
try
{
// POC: not sure what this is doing... could be messing up our Aliasing????
AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolver.OnAssemblyResolve<RevitExternalApplication>;
var containerBuilder = SpeckleContainerBuilder.CreateInstance();
// init DI
_container = containerBuilder
.LoadAutofacModules(Assembly.GetExecutingAssembly(), _revitSettings.ModuleFolders.NotNull())
.AddSingleton(_revitSettings) // apply revit settings into DI
.AddSingleton(application) // inject UIControlledApplication application
.Build();
// resolve root object
_revitPlugin = _container.Resolve<IRevitPlugin>();
_revitPlugin.Initialise();
}
catch (Exception e) when (!e.IsFatal())
{
// POC: feedback?
return Result.Failed;
}
return Result.Succeeded;
}
public Result OnShutdown(UIControlledApplication application)
{
try
{
// POC: could this be more a generic Connector Init() Shutdown()
// possibly with injected pieces or with some abstract methods?
// need to look for commonality
_revitPlugin?.Shutdown();
}
catch (Exception e) when (!e.IsFatal())
{
// POC: feedback?
return Result.Failed;
}
return Result.Succeeded;
}
}
@@ -0,0 +1,66 @@
using System.Collections.Concurrent;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Events;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Converters.RevitShared.Helpers;
namespace Speckle.Connectors.Revit.Plugin;
// POC: needs interface
// is probably misnamed, perhaps OnIdleCallbackManager
internal sealed class RevitIdleManager : IRevitIdleManager
{
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
private readonly UIApplication _uiApplication;
private readonly ConcurrentDictionary<string, Action> _calls = new();
// POC: still not thread safe
private volatile bool _hasSubscribed;
public RevitIdleManager(RevitContext revitContext, ITopLevelExceptionHandler topLevelExceptionHandler)
{
_topLevelExceptionHandler = topLevelExceptionHandler;
_uiApplication = revitContext.UIApplication!;
}
/// <summary>
/// Subscribe deferred action to Idling event to run it whenever Revit becomes idle.
/// </summary>
/// <param name="action"> Action to call whenever Revit becomes Idle.</param>
/// 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(Action action)
{
// POC: key for method is brittle | thread safe is not this is
// I want to be called back ONCE when the host app has become idle once more
// would this work "action.Method.Name" with anonymous function, including the SAME function
// does this work across class instances? Should it? What about functions of the same name? Fully qualified name might be better
_calls[action.Method.Name] = action;
if (_hasSubscribed)
{
return;
}
_hasSubscribed = true;
_uiApplication.Idling += RevitAppOnIdle;
}
private void RevitAppOnIdle(object sender, IdlingEventArgs e)
{
_topLevelExceptionHandler.CatchUnhandled(() =>
{
foreach (KeyValuePair<string, Action> kvp in _calls)
{
kvp.Value.Invoke();
}
_calls.Clear();
_uiApplication.Idling -= RevitAppOnIdle;
// setting last will delay ntering re-subscritption
_hasSubscribed = false;
});
}
}
@@ -0,0 +1,198 @@
using Autodesk.Revit.UI;
using Autodesk.Revit.ApplicationServices;
using Revit.Async;
using CefSharp;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Bindings;
using System.Diagnostics;
using Speckle.Converters.RevitShared.Helpers;
using Speckle.Core.Logging;
using System.Reflection;
using System.Windows.Media.Imaging;
using System.Windows.Media;
using System.IO;
namespace Speckle.Connectors.Revit.Plugin;
internal sealed class RevitPlugin : IRevitPlugin
{
private readonly UIControlledApplication _uIControlledApplication;
private readonly RevitSettings _revitSettings;
private readonly IEnumerable<Lazy<IBinding>> _bindings; // should be lazy to ensure the bindings are not created too early
private readonly BindingOptions _bindingOptions;
private readonly RevitContext _revitContext;
private readonly CefSharpPanel _cefSharpPanel;
public RevitPlugin(
UIControlledApplication uIControlledApplication,
RevitSettings revitSettings,
IEnumerable<Lazy<IBinding>> bindings,
BindingOptions bindingOptions,
RevitContext revitContext,
CefSharpPanel cefSharpPanel
)
{
_uIControlledApplication = uIControlledApplication;
_revitSettings = revitSettings;
_bindings = bindings;
_bindingOptions = bindingOptions;
_revitContext = revitContext;
_cefSharpPanel = cefSharpPanel;
}
public void Initialise()
{
// Create and register panels before app initialized. this is needed for double-click file open
CreateTabAndRibbonPanel(_uIControlledApplication);
RegisterDockablePane();
_uIControlledApplication.ControlledApplication.ApplicationInitialized += OnApplicationInitialized;
}
public void Shutdown()
{
// POC: should we be cleaning up the RibbonPanel etc...
// Should we be indicating to any active in-flight functions that we are being closed?
}
// POC: Could be injected but maybe not worthwhile
private void CreateTabAndRibbonPanel(UIControlledApplication application)
{
// POC: some top-level handling and feedback here
try
{
application.CreateRibbonTab(_revitSettings.RevitTabName);
}
catch (ArgumentException)
{
// exception occurs when the speckle tab has already been created.
// this happens when both the dui2 and the dui3 connectors are installed. Can be safely ignored.
}
RibbonPanel specklePanel = application.CreateRibbonPanel(_revitSettings.RevitTabName, _revitSettings.RevitTabTitle);
var dui3Button = (PushButton)
specklePanel.AddItem(
new PushButtonData(
_revitSettings.RevitButtonName,
_revitSettings.RevitButtonText,
typeof(RevitExternalApplication).Assembly.Location,
typeof(SpeckleRevitCommand).FullName
)
);
string path = typeof(RevitPlugin).Assembly.Location;
dui3Button.Image = LoadPngImgSource(
$"Speckle.Connectors.Revit{_revitSettings.RevitVersionName}.Assets.logo16.png",
path
);
dui3Button.LargeImage = LoadPngImgSource(
$"Speckle.Connectors.Revit{_revitSettings.RevitVersionName}.Assets.logo32.png",
path
);
dui3Button.ToolTipImage = LoadPngImgSource(
$"Speckle.Connectors.Revit{_revitSettings.RevitVersionName}.Assets.logo32.png",
path
);
dui3Button.ToolTip = "Speckle Connector for Revit New UI";
//dui3Button.AvailabilityClassName = typeof(CmdAvailabilityViews).FullName;
dui3Button.SetContextualHelp(new ContextualHelp(ContextualHelpType.Url, "https://speckle.systems"));
}
private void OnApplicationInitialized(object sender, Autodesk.Revit.DB.Events.ApplicationInitializedEventArgs e)
{
var uiApplication = new UIApplication(sender as Application);
_revitContext.UIApplication = uiApplication;
// POC: might be worth to interface this out, we shall see...
RevitTask.Initialize(uiApplication);
PostApplicationInit(); // for double-click file open
}
/// <summary>
/// Actions to run after UiApplication initialized. This is needed for double-click file open issue.
/// </summary>
private void PostApplicationInit()
{
// binding the bindings to each bridge
foreach (IBinding binding in _bindings.Select(x => x.Value))
{
Debug.WriteLine(binding.Name);
binding.Parent.AssociateWithBinding(
binding,
_cefSharpPanel.ExecuteScriptAsync,
_cefSharpPanel,
_cefSharpPanel.ShowDevTools
);
}
_cefSharpPanel.Browser.IsBrowserInitializedChanged += (sender, e) =>
{
if (e.NewValue is false)
{
return;
}
foreach (IBinding binding in _bindings.Select(x => x.Value))
{
IBridge bridge = binding.Parent;
_cefSharpPanel.Browser.JavascriptObjectRepository.Register(
bridge.FrontendBoundName,
bridge,
true,
_bindingOptions
);
}
// POC: Below line seems unneccesary but not removing just in case we did it like this? Maybe check it later
// with some other revit connectors again since CefSharp version is different
// _cefSharpPanel.Browser.Load("https://boisterous-douhua-e3cefb.netlify.app/");
// POC: not sure where this comes from
#if REVIT2020
// NOTE: Cef65 does not work with DUI3 in yarn dev mode. To test things you need to do `yarn build` and serve the build
// folder at port 3000 (or change it to something else if you want to). Guru meditation: Je sais, pas ideal. Mais q'est que nous pouvons faire? Rien. C'est l'autodesk vie.
// NOTE: To run the ui from a build, follow these steps:
// - run `yarn build` in the DUI3 folder
// - run ` PORT=3003 node .output/server/index.mjs` after the build
CefSharpPanel.Browser.Load("http://localhost:3003");
CefSharpPanel.Browser.ShowDevTools();
#endif
#if REVIT2023
CefSharpPanel.Browser.Load("http://localhost:8082");
#endif
};
}
private void RegisterDockablePane()
{
CefSharpSettings.ConcurrentTaskExecution = true;
// Registering dockable pane should happen before UiApplication is initialized with RevitTask.
// Otherwise pane cannot be registered for double-click file open.
_uIControlledApplication.RegisterDockablePane(
RevitExternalApplication.DoackablePanelId,
_revitSettings.RevitPanelName,
_cefSharpPanel
);
}
private ImageSource? LoadPngImgSource(string sourceName, string path)
{
try
{
var assembly = Assembly.LoadFrom(Path.Combine(path));
var icon = assembly.GetManifestResourceStream(sourceName);
PngBitmapDecoder decoder = new(icon, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
ImageSource source = decoder.Frames[0];
return source;
}
catch (Exception ex) when (!ex.IsFatal())
{
// POC: logging
}
return null;
}
}

Some files were not shown because too many files have changed in this diff Show More