Compare commits

...

12 Commits

Author SHA1 Message Date
Björn Steinhagen 3bb0860d14 feat(connectors): disable cache config (#1349)
.NET Test / test (push) Has been cancelled
.NET Build and Publish / build-connectors (push) Has been cancelled
.NET Build and Publish / deploy-installers (push) Has been cancelled
* feat(all): adds disable cache functionality

* feat(connectors): prevents StoreSendResult if cache disabled

* fix(connectors): restores IsBypassed state

* Explicit flag instead implicit

* Delete unused flag

* Add packfile support

---------

Co-authored-by: Oğuzhan Koral <45078678+oguzhankoral@users.noreply.github.com>
Co-authored-by: oguzhankoral <oguzhankoral@gmail.com>
Co-authored-by: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com>
2026-04-08 11:27:22 +01:00
Oğuzhan Koral 0441cf7e3c Merge pull request #1356 from specklesystems/dev
UPDATE DEV INTO MAIN
2026-04-08 12:10:25 +03:00
Jedd Morgan 7860c44f4e feat(api)!: Implement new packfile based sends via SendPipline (aka DuckDB changes) (#1277)
* Dim/quack lets go (#1275)

* Add model ingestion to sharp connectors

* correct ingestion message

* Progress

* grasshopper

* GH exception messages

* fix GH

* file names

* revit file name

* grasshopper file names

* etabs file names

* delete tests

* tekla maybe

* ingestion  scope

* bad boolean logic

* Longer TimeSpan

* wip upload pipe

* 10s

* passthrough ingestion id

* happy hack time: prevent ingestion completion

this is handled server-side in the processing logic.

* add packfile send endpoint detection and routing

Route to SendViaPackfile when the server supports the upload-signing
endpoint (POST probe, 404 = unsupported) and a continuous traversal
builder is registered.

* Adds Continuous Traversal Builder

Introduces a Continuous Traversal Builder to manage the conversion and processing of Revit elements within a Send Pipeline.

---------

Co-authored-by: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com>

* feat(api): DI Refactor for Duck DB + Gergo's API endpoint changes (#1282)

* Di

* undo accidental change

* Feat (duck): dui ingestion updates post upload (#1295)

* Pass optional ingestion id to DUI

* Make ingestion id null for the SendViaIngestion, see the note :)

* feat!: Duckdev progress reporitng (#1296)

* Di

* throwaway from laptop

* Progress reporting

* Use matching logger

* Revit and revert rhino unpacker progress

* more revertion

* make pr even cleaner

* and this one

* fix build issues with other connectors

* SDK nuget (#1299)

* Bump to 3.14.0-alpha.2

* Feat(duck): grasshopper (#1297)

* Duck x Grasshopper - who would win?

* Fix registration for new builder

* missing imports

* return version id grasshopper

* Align sync resource to sync

---------

Co-authored-by: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com>

* Bump SDK

* feat(importer): rhino file importer changes for packfile (#1301)

* rhino importer changes

* correct deps

* Bump SDK

* Fix build issues

* ditto

* Fix build issue

* Lower standards

* Fix build

* feat: duck for acad, civil, navis, tekla, etabs (#1300)

* duck: acad, civil, etabs, tekla, navis and bump channels to 10.0.0

* notes

* fix conflicts

* more conflicts

* Ready for testing

* fix(duck): Fix send caching (#1302)

* potential fix

* undo-rhino chnages

* fix xml comment

* amended comment

* revit

* Fix build

* Aligned converting message

* fix: reoccurring object references

* Bump sdk and resolve merge conflict issues

* Merge pull request #1317 from specklesystems/jrm/importer-tracing

feat(otel): Tracing and OTEL changes for Rhino importer

* Fix revit linked model progress (#1312)

* Revert otel packages

* bump SDK

* Trace unpacking groups

* Align trace context nullability with app

* Disable send caching in Navisworks

* comments

* Update FileimportPayload.cs

* fix using directive

---------

Co-authored-by: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com>

* Fix merge conflicts

---------

Co-authored-by: Dimitrie Stefanescu <didimitrie@gmail.com>
Co-authored-by: Oğuzhan Koral <45078678+oguzhankoral@users.noreply.github.com>
Co-authored-by: Björn Steinhagen <88777268+bjoernsteinhagen@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Sebastian Witt <sebastian.witt@rwth-aachen.de>
2026-04-08 10:07:56 +01:00
Jedd Morgan 38b9415e81 Update Plant3dSendBinding.cs (#1355)
.NET Build and Publish / build-connectors (push) Has been cancelled
.NET Build and Publish / deploy-installers (push) Has been cancelled
2026-04-02 17:44:57 +01:00
Mucahit Bilal GOKER a1f392a33b feat: plant 3d connector (#1344)
.NET Build and Publish / build-connectors (push) Has been cancelled
.NET Build and Publish / deploy-installers (push) Has been cancelled
* wip

* feat: data extraction from db

* Fallback conversions

* Fix line segments

* Fix: do not skip empty values

* Remove claude generated receive boilerplates and civil related extractor

* Fix compile errors and custom assembly resolver

* Guids for bundle

* Nuget

* Use TagValue as object name

* add plant3d to slnx (#1347)

* add plant3d to slnx

* format

* and the local (#1348)

* Resolve comments

* final comments

* lockfiles

* don't swallow image exception

---------

Co-authored-by: oguzhankoral <oguzhankoral@gmail.com>
Co-authored-by: Oğuzhan Koral <45078678+oguzhankoral@users.noreply.github.com>
Co-authored-by: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com>
2026-04-02 16:20:49 +01:00
Jedd Morgan 24442ab0a9 chore(sdk): bump SDK (#1352)
* Align tracing with server

* Bump sdk
2026-04-02 13:58:17 +02:00
Sebastian Witt 47344e0af3 Add dwg/dxf config to include material layer color conversion (#1351)
* Add dwg/dxf config to include material layer color conversion

* cleanup formatting
2026-04-02 10:12:03 +00:00
Björn Steinhagen 0d09ea5158 fix(rhino): resolve material type editing (#1345)
* fix(rhino): prevent material duplication on type change

* fix(rhino): modernise material baking and use RenderMaterial Guid for assignment

* chore(rhino): changes for rh7

* fix(rhino): resolve null reference and incorrect material assignment for layers

* refactor(rhino): simplifies material naming
2026-04-01 14:24:44 +02:00
dependabot[bot] 3be24d5b15 Merge pull request #1346 from specklesystems/dependabot/github_actions/codecov/codecov-action-6
chore(deps): bump codecov/codecov-action from 5 to 6
2026-03-30 22:10:33 +01:00
Björn Steinhagen 8bbb9b893e feat(revit): handles linked model elements and adds logging (#1333) 2026-03-27 19:35:06 +03:00
Jedd Morgan 85b2b21294 Merge pull request #1343 from specklesystems/dev
Dev -> Main
2026-03-27 10:56:43 +00:00
Björn Steinhagen 63d6d1a52b feat(revit): add shared location reference point setting (#1337) 2026-03-27 10:42:13 +02:00
218 changed files with 5358 additions and 1388 deletions
+1 -1
View File
@@ -29,7 +29,7 @@ jobs:
run: ./build.sh test-and-pack
- name: Upload coverage reports to Codecov with GitHub Action
uses: codecov/codecov-action@v5
uses: codecov/codecov-action@v6
with:
files: Converters/**/coverage.xml
token: ${{ secrets.CODECOV_TOKEN }}
+2
View File
@@ -21,3 +21,5 @@ tools
coverage.xml
output/
Images/Thumbs.db
.claude/
+1
View File
@@ -43,6 +43,7 @@ public static class Consts
new("Connectors/Autocad/Speckle.Connectors.Civil3d2026", "net8.0-windows")
]
),
new("plant3d", [new("Connectors/Autocad/Speckle.Connectors.Plant3d2026", "net8.0-windows")]),
new(
"navisworks",
[
+1 -1
View File
@@ -1,4 +1,4 @@
CA1502: 25
CA1501: 5
CA1506(Method): 50
CA1506(Method): 60
CA1506(Type): 95
@@ -35,4 +35,23 @@
<StartProgram>$(ProgramW6432)\Autodesk\AutoCAD $(Civil3DVersion)\acad.exe</StartProgram>
<StartArguments>/product C3D</StartArguments>
</PropertyGroup>
<Target AfterTargets="Clean" Name="CleanAddinPlant3D" Condition="'$(Plant3DVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true' And '$(OS)' == 'Windows_NT'">
<RemoveDir Directories="$(AppData)\Autodesk\ApplicationPlugins\Speckle.Connectors.Plant3d.bundle\Contents\Windows\Speckle.Connectors.Plant3d$(Plant3DVersion);" />
</Target>
<Target AfterTargets="Build" Name="AfterBuildPlant3D" Condition="'$(Plant3DVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true' And '$(OS)' == 'Windows_NT'">
<ItemGroup>
<Plant3DDLLs Include="$(TargetDir)\**\*.*" />
</ItemGroup>
<Message Text="Plant3D Version $(Plant3DVersion)" Importance="high"/>
<Copy DestinationFolder="$(AppData)\Autodesk\ApplicationPlugins\Speckle.Connectors.Plant3d.bundle\Contents\Windows\Speckle.Connectors.Plant3d$(Plant3DVersion)\%(RecursiveDir)" SourceFiles="@(Plant3DDLLs)" />
<Copy DestinationFolder="$(AppData)\Autodesk\ApplicationPlugins\Speckle.Connectors.Plant3d.bundle\" SourceFiles="$(TargetDir)\Plugin\BundlePlant3D\PackageContents.xml" />
</Target>
<PropertyGroup Condition="'$(Plant3DVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true' And '$(OS)' == 'Windows_NT'">
<StartAction>Program</StartAction>
<StartProgram>$(ProgramW6432)\Autodesk\AutoCAD $(Plant3DVersion)\acad.exe</StartProgram>
<StartArguments>/product PLNT3D</StartArguments>
</PropertyGroup>
</Project>
@@ -171,8 +171,8 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "Gt1qAZrMflLMOjDy4cQzJX5pdq5WokphYdsry7pMBlRHRdx9imQhJUPCg528Y2iVSMUnRKvj3A3cHtBeUxysEQ==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.CSharp": "4.7.0",
@@ -181,13 +181,13 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.15.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "OUOnt4Vnd1J838LTWEpP0ge8WIwjhx8O94kuTbC6AJRfOx9Lw78SG6XtVCE+WXb9zC5h3tzOV9YOng8Ke5yXMg==",
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
@@ -283,7 +283,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -314,7 +314,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -358,11 +358,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.15.1, )",
"resolved": "3.15.1",
"contentHash": "vFIf01jNVD69N6wE5iYVM7TKA6AbSTZxq1EedJCVZxhj5pkhHBw+23fyO+wz7Cd9ggqsiBUJMuKxUBHaDphGWQ==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.15.1"
"Speckle.Sdk": "3.15.3"
}
}
}
@@ -171,8 +171,8 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "Gt1qAZrMflLMOjDy4cQzJX5pdq5WokphYdsry7pMBlRHRdx9imQhJUPCg528Y2iVSMUnRKvj3A3cHtBeUxysEQ==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.CSharp": "4.7.0",
@@ -181,13 +181,13 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.15.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "OUOnt4Vnd1J838LTWEpP0ge8WIwjhx8O94kuTbC6AJRfOx9Lw78SG6XtVCE+WXb9zC5h3tzOV9YOng8Ke5yXMg==",
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
@@ -283,7 +283,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -314,7 +314,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -358,11 +358,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.15.1, )",
"resolved": "3.15.1",
"contentHash": "vFIf01jNVD69N6wE5iYVM7TKA6AbSTZxq1EedJCVZxhj5pkhHBw+23fyO+wz7Cd9ggqsiBUJMuKxUBHaDphGWQ==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.15.1"
"Speckle.Sdk": "3.15.3"
}
}
}
@@ -171,8 +171,8 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "Gt1qAZrMflLMOjDy4cQzJX5pdq5WokphYdsry7pMBlRHRdx9imQhJUPCg528Y2iVSMUnRKvj3A3cHtBeUxysEQ==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.CSharp": "4.7.0",
@@ -181,13 +181,13 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.15.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "OUOnt4Vnd1J838LTWEpP0ge8WIwjhx8O94kuTbC6AJRfOx9Lw78SG6XtVCE+WXb9zC5h3tzOV9YOng8Ke5yXMg==",
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
@@ -283,7 +283,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -315,7 +315,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -359,11 +359,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.15.1, )",
"resolved": "3.15.1",
"contentHash": "vFIf01jNVD69N6wE5iYVM7TKA6AbSTZxq1EedJCVZxhj5pkhHBw+23fyO+wz7Cd9ggqsiBUJMuKxUBHaDphGWQ==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.15.1"
"Speckle.Sdk": "3.15.3"
}
}
}
@@ -157,8 +157,8 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "Gt1qAZrMflLMOjDy4cQzJX5pdq5WokphYdsry7pMBlRHRdx9imQhJUPCg528Y2iVSMUnRKvj3A3cHtBeUxysEQ==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
@@ -166,13 +166,13 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.15.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "OUOnt4Vnd1J838LTWEpP0ge8WIwjhx8O94kuTbC6AJRfOx9Lw78SG6XtVCE+WXb9zC5h3tzOV9YOng8Ke5yXMg=="
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w=="
},
"SQLitePCLRaw.bundle_e_sqlite3": {
"type": "Transitive",
@@ -230,7 +230,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -262,7 +262,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -306,11 +306,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.15.1, )",
"resolved": "3.15.1",
"contentHash": "vFIf01jNVD69N6wE5iYVM7TKA6AbSTZxq1EedJCVZxhj5pkhHBw+23fyO+wz7Cd9ggqsiBUJMuKxUBHaDphGWQ==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.15.1"
"Speckle.Sdk": "3.15.3"
}
}
},
@@ -157,8 +157,8 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "Gt1qAZrMflLMOjDy4cQzJX5pdq5WokphYdsry7pMBlRHRdx9imQhJUPCg528Y2iVSMUnRKvj3A3cHtBeUxysEQ==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
@@ -166,13 +166,13 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.15.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "OUOnt4Vnd1J838LTWEpP0ge8WIwjhx8O94kuTbC6AJRfOx9Lw78SG6XtVCE+WXb9zC5h3tzOV9YOng8Ke5yXMg=="
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w=="
},
"SQLitePCLRaw.bundle_e_sqlite3": {
"type": "Transitive",
@@ -230,7 +230,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -262,7 +262,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -306,11 +306,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.15.1, )",
"resolved": "3.15.1",
"contentHash": "vFIf01jNVD69N6wE5iYVM7TKA6AbSTZxq1EedJCVZxhj5pkhHBw+23fyO+wz7Cd9ggqsiBUJMuKxUBHaDphGWQ==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.15.1"
"Speckle.Sdk": "3.15.3"
}
}
},
@@ -18,6 +18,10 @@ public static class AutocadConnectorModule
// Send
serviceCollection.LoadSend();
serviceCollection.AddScoped<IRootObjectBuilder<AutocadRootObject>, AutocadRootObjectBuilder>();
serviceCollection.AddScoped<
IRootContinuousTraversalBuilder<AutocadRootObject>,
AutocadContinuousTraversalBuilder
>();
// Receive
serviceCollection.LoadReceive();
@@ -1,9 +1,9 @@
using Autodesk.AutoCAD.Colors;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Common.Operations;
using Speckle.InterfaceGenerator;
using Speckle.Sdk;
using Speckle.Sdk.Models.Proxies;
using Speckle.Sdk.Pipelines.Progress;
using AutocadColor = Autodesk.AutoCAD.Colors.Color;
namespace Speckle.Connectors.Autocad.HostApp;
@@ -5,7 +5,6 @@ using Microsoft.Extensions.Logging;
using Speckle.Connectors.Autocad.HostApp.Extensions;
using Speckle.Connectors.Common.Conversion;
using Speckle.Connectors.Common.Instances;
using Speckle.Connectors.Common.Operations;
using Speckle.Converters.Autocad;
using Speckle.Converters.Common;
using Speckle.DoubleNumerics;
@@ -16,6 +15,7 @@ using Speckle.Sdk.Dependencies;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Models.Instances;
using Speckle.Sdk.Pipelines.Progress;
using AutocadColor = Autodesk.AutoCAD.Colors.Color;
namespace Speckle.Connectors.Autocad.HostApp;
@@ -3,12 +3,12 @@ using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.GraphicsInterface;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Common.Conversion;
using Speckle.Connectors.Common.Operations;
using Speckle.InterfaceGenerator;
using Speckle.Objects.Other;
using Speckle.Sdk;
using Speckle.Sdk.Common;
using Speckle.Sdk.Models;
using Speckle.Sdk.Pipelines.Progress;
using Material = Autodesk.AutoCAD.DatabaseServices.Material;
using RenderMaterial = Speckle.Objects.Other.RenderMaterial;
@@ -12,6 +12,7 @@ using Speckle.Sdk.Dependencies;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Models.Instances;
using Speckle.Sdk.Pipelines.Progress;
using AutocadColor = Autodesk.AutoCAD.Colors.Color;
namespace Speckle.Connectors.Autocad.Operations.Receive;
@@ -0,0 +1,197 @@
using System.Diagnostics.CodeAnalysis;
using Autodesk.AutoCAD.DatabaseServices;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Autocad.HostApp;
using Speckle.Connectors.Common.Builders;
using Speckle.Connectors.Common.Caching;
using Speckle.Connectors.Common.Conversion;
using Speckle.Connectors.Common.Extensions;
using Speckle.Connectors.Common.Operations;
using Speckle.Converters.Common;
using Speckle.Sdk;
using Speckle.Sdk.Logging;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Models.Instances;
using Speckle.Sdk.Pipelines.Progress;
using Speckle.Sdk.Pipelines.Send;
namespace Speckle.Connectors.Autocad.Operations.Send;
/// <summary>
/// Abstract base class for AutoCAD continuous traversal builders that stream objects through a
/// <see cref="SendPipeline"/> for packfile-based uploads. Same conversion logic as
/// <see cref="AutocadRootObjectBaseBuilder"/>, but processes elements through the pipeline.
/// </summary>
public abstract class AutocadContinuousTraversalBaseBuilder : IRootContinuousTraversalBuilder<AutocadRootObject>
{
private readonly IRootToSpeckleConverter _converter;
private readonly string[] _documentPathSeparator = ["\\"];
private readonly ISendConversionCache _sendConversionCache;
private readonly AutocadInstanceUnpacker _instanceUnpacker;
private readonly AutocadMaterialUnpacker _materialUnpacker;
private readonly AutocadColorUnpacker _colorUnpacker;
private readonly AutocadGroupUnpacker _groupUnpacker;
private readonly ILogger<AutocadRootObjectBuilder> _logger;
private readonly ISdkActivityFactory _activityFactory;
protected AutocadContinuousTraversalBaseBuilder(
IRootToSpeckleConverter converter,
ISendConversionCache sendConversionCache,
AutocadInstanceUnpacker instanceObjectManager,
AutocadMaterialUnpacker materialUnpacker,
AutocadColorUnpacker colorUnpacker,
AutocadGroupUnpacker groupUnpacker,
ILogger<AutocadRootObjectBuilder> logger,
ISdkActivityFactory activityFactory
)
{
_converter = converter;
_sendConversionCache = sendConversionCache;
_instanceUnpacker = instanceObjectManager;
_materialUnpacker = materialUnpacker;
_colorUnpacker = colorUnpacker;
_groupUnpacker = groupUnpacker;
_logger = logger;
_activityFactory = activityFactory;
}
[SuppressMessage(
"Maintainability",
"CA1506:Avoid excessive class coupling",
Justification = """
It is already simplified but has many different references since it is a builder. Do not know can we simplify it now.
Later we might consider to refactor proxies from one proxy manager? but we do not know the shape of it all potential
proxy classes yet. So I'm supressing this one now!!!
"""
)]
public async Task<RootObjectBuilderResult> Build(
IReadOnlyList<AutocadRootObject> objects,
string projectId,
SendPipeline sendPipeline,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
{
// 0 - Init the root
Collection root =
new()
{
name = Application
.DocumentManager.CurrentDocument.Name.Split(_documentPathSeparator, StringSplitOptions.None)
.Reverse()
.First()
};
Document doc = Application.DocumentManager.CurrentDocument;
using Transaction tr = doc.Database.TransactionManager.StartTransaction();
// 1 - Unpack the instances
var (atomicObjects, _, instanceProxies, instanceDefinitionProxies) = _instanceUnpacker.UnpackSelection(objects);
root[ProxyKeys.INSTANCE_DEFINITION] = instanceDefinitionProxies;
// 2 - Unpack the groups
root[ProxyKeys.GROUP] = _groupUnpacker.UnpackGroups(atomicObjects);
using (var _ = _activityFactory.Start("Converting objects"))
{
// 3 - Convert atomic objects and process through pipeline
List<LayerTableRecord> usedAcadLayers = new();
List<SendConversionResult> results = new();
int count = 0;
foreach (var (entity, applicationId) in atomicObjects)
{
cancellationToken.ThrowIfCancellationRequested();
(Collection objectCollection, LayerTableRecord? autocadLayer) = CreateObjectCollection(entity, tr);
if (autocadLayer is not null)
{
usedAcadLayers.Add(autocadLayer);
root.elements.Add(objectCollection);
}
var result = await ConvertAutocadEntity(
entity,
applicationId,
objectCollection,
instanceProxies,
projectId,
sendPipeline
);
results.Add(result);
onOperationProgressed.Report(
new($"Converting objects... ({count:N0} / {atomicObjects.Count:N0})", (double)++count / atomicObjects.Count)
);
}
if (results.All(x => x.Status == Status.ERROR))
{
throw new SpeckleException("Failed to convert all objects.");
}
// 4 - Unpack the render material proxies
root[ProxyKeys.RENDER_MATERIAL] = _materialUnpacker.UnpackMaterials(atomicObjects, usedAcadLayers);
// 5 - Unpack the color proxies
root[ProxyKeys.COLOR] = _colorUnpacker.UnpackColors(atomicObjects, usedAcadLayers);
// add any additional properties (most likely from verticals)
AddAdditionalProxiesToRoot(root);
// Process root collection and wait for all uploads
await sendPipeline.Process(root);
await sendPipeline.WaitForUpload();
return new RootObjectBuilderResult(root, results);
}
}
public virtual (Collection, LayerTableRecord?) CreateObjectCollection(Entity entity, Transaction tr)
{
return (new(), null);
}
public virtual void AddAdditionalProxiesToRoot(Collection rootCollection)
{
return;
}
private async Task<SendConversionResult> ConvertAutocadEntity(
Entity entity,
string applicationId,
Collection collectionHost,
IReadOnlyDictionary<string, InstanceProxy> instanceProxies,
string projectId,
SendPipeline sendPipeline
)
{
string sourceType = entity.GetType().ToString();
try
{
Base converted;
if (entity is BlockReference && instanceProxies.TryGetValue(applicationId, out InstanceProxy? instanceProxy))
{
converted = instanceProxy;
}
else if (_sendConversionCache.TryGetValue(projectId, applicationId, out ObjectReference? value))
{
converted = value;
}
else
{
converted = _converter.Convert(entity);
converted.applicationId = applicationId;
}
// NOTE: this is the main part that differentiate from the main root object builder
var reference = await sendPipeline.Process(converted).ConfigureAwait(false);
collectionHost.elements.Add(reference);
return new(Status.SUCCESS, applicationId, sourceType, reference);
}
catch (Exception ex) when (!ex.IsFatal())
{
_logger.LogSendConversionError(ex, sourceType);
return new(Status.ERROR, applicationId, sourceType, null, ex);
}
}
}
@@ -0,0 +1,46 @@
using Autodesk.AutoCAD.DatabaseServices;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Autocad.HostApp;
using Speckle.Connectors.Common.Caching;
using Speckle.Converters.Common;
using Speckle.Sdk.Logging;
using Speckle.Sdk.Models.Collections;
namespace Speckle.Connectors.Autocad.Operations.Send;
public sealed class AutocadContinuousTraversalBuilder : AutocadContinuousTraversalBaseBuilder
{
private readonly AutocadLayerUnpacker _layerUnpacker;
public AutocadContinuousTraversalBuilder(
AutocadLayerUnpacker layerUnpacker,
IRootToSpeckleConverter converter,
ISendConversionCache sendConversionCache,
AutocadInstanceUnpacker instanceObjectManager,
AutocadMaterialUnpacker materialUnpacker,
AutocadColorUnpacker colorUnpacker,
AutocadGroupUnpacker groupUnpacker,
ILogger<AutocadRootObjectBuilder> logger,
ISdkActivityFactory activityFactory
)
: base(
converter,
sendConversionCache,
instanceObjectManager,
materialUnpacker,
colorUnpacker,
groupUnpacker,
logger,
activityFactory
)
{
_layerUnpacker = layerUnpacker;
}
public override (Collection, LayerTableRecord?) CreateObjectCollection(Entity entity, Transaction tr)
{
Layer layer = _layerUnpacker.GetOrCreateSpeckleLayer(entity, tr, out LayerTableRecord? autocadLayer);
return (layer, autocadLayer);
}
}
@@ -16,6 +16,7 @@ using Speckle.Sdk.Logging;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Models.Instances;
using Speckle.Sdk.Pipelines.Progress;
namespace Speckle.Connectors.Autocad.Operations.Send;
@@ -7,6 +7,8 @@ public static class AppUtils
public static Speckle.Sdk.Application App =>
#if CIVIL3D
HostApplications.Civil3D;
#elif PLANT3D
HostApplications.Plant3D;
#elif AUTOCAD
HostApplications.AutoCAD;
#else
@@ -14,11 +16,11 @@ public static class AppUtils
#endif
public static HostAppVersion Version =>
#if AUTOCAD2026 || CIVIL3D2026
#if AUTOCAD2026 || CIVIL3D2026 || PLANT3D2026
HostAppVersion.v2026;
#elif AUTOCAD2025 || CIVIL3D2025
#elif AUTOCAD2025 || CIVIL3D2025 || PLANT3D2025
HostAppVersion.v2025;
#elif AUTOCAD2024 || CIVIL3D2024
#elif AUTOCAD2024 || CIVIL3D2024 || PLANT3D2024
HostAppVersion.v2024;
#elif AUTOCAD2023|| CIVIL3D2023
HostAppVersion.v2023;
@@ -11,6 +11,9 @@ using Speckle.Converters.Autocad;
#elif CIVIL3D
using Speckle.Converters.Civil3dShared;
using Speckle.Connectors.Civil3dShared.DependencyInjection;
#elif PLANT3D
using Speckle.Connectors.Plant3dShared.DependencyInjection;
using Speckle.Converters.Plant3dShared;
#endif
namespace Speckle.Connectors.Autocad.Plugin;
@@ -39,13 +42,18 @@ public class AutocadCommand
// init DI
var services = new ServiceCollection();
_disposableLogger = services.Initialize(AppUtils.App, AppUtils.Version);
#if AUTOCAD
services.AddAutocad();
services.AddAutocadConverters();
#elif CIVIL3D
services.AddCivil3d();
services.AddCivil3dConverters();
#elif PLANT3D
services.AddPlant3d();
services.AddPlant3dConverters();
#endif
Container = services.BuildServiceProvider();
Container.UseDUI();
@@ -3,7 +3,7 @@ using System.Windows.Media;
using System.Windows.Media.Imaging;
using Autodesk.Windows;
using Speckle.Sdk;
#if !AUTOCAD2025_OR_GREATER && !CIVIL3D2025_OR_GREATER
#if !AUTOCAD2025_OR_GREATER && !CIVIL3D2025_OR_GREATER && !PLANT3D2025_OR_GREATER
using System.IO;
#endif
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8" ?>
<ApplicationPackage
SchemaVersion="1.0"
AppVersion="1.0"
Author="AEC SYSTEMS LTD"
Name="Speckle for Plant 3D"
Description="Speckle for Plant 3D"
Icon="./Contents/Resources/Logo.ico"
ProductCode="{b91352b6-01db-4084-93c8-684bb231947e}"
UpgradeCode="{91c7e43f-e962-40e9-85bd-d6bd733b6964}">
<CompanyDetails
Name="Speckle"
Url="https://speckle.systems"
Email="support@speckle.systems" />
<Components>
<!-- Plant 3D 2026 -->
<ComponentEntry
AppName="Speckle.Connectors.Plant3d2026"
ModuleName="./Contents/Windows/Speckle.Connectors.Plant3d2026/Speckle.Connectors.Plant3d2026.dll"
LoadOnAutoCADStartup="True">
<RuntimeRequirements
OS="Win64"
Platform="Plant3D"
SeriesMin="R25.1"
SeriesMax="R25.1" />
</ComponentEntry>
</Components>
</ApplicationPackage>
@@ -41,6 +41,8 @@
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\AutocadHostObjectBaseBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\AutocadHostObjectBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\AutocadRootObject.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\AutocadContinuousTraversalBaseBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\AutocadContinuousTraversalBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\AutocadRootObjectBaseBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\AutocadRootObjectBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Plugin\AutocadRibbon.cs" />
@@ -60,5 +62,8 @@
<Content Include="$(MSBuildThisFileDirectory)Plugin\BundleAutoCAD\PackageContents.xml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Plugin\BundlePlant3D\PackageContents.xml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>
@@ -180,8 +180,8 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "Gt1qAZrMflLMOjDy4cQzJX5pdq5WokphYdsry7pMBlRHRdx9imQhJUPCg528Y2iVSMUnRKvj3A3cHtBeUxysEQ==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.CSharp": "4.7.0",
@@ -190,13 +190,13 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.15.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "OUOnt4Vnd1J838LTWEpP0ge8WIwjhx8O94kuTbC6AJRfOx9Lw78SG6XtVCE+WXb9zC5h3tzOV9YOng8Ke5yXMg==",
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
@@ -292,7 +292,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -324,7 +324,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -368,11 +368,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.15.1, )",
"resolved": "3.15.1",
"contentHash": "vFIf01jNVD69N6wE5iYVM7TKA6AbSTZxq1EedJCVZxhj5pkhHBw+23fyO+wz7Cd9ggqsiBUJMuKxUBHaDphGWQ==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.15.1"
"Speckle.Sdk": "3.15.3"
}
}
}
@@ -180,8 +180,8 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "Gt1qAZrMflLMOjDy4cQzJX5pdq5WokphYdsry7pMBlRHRdx9imQhJUPCg528Y2iVSMUnRKvj3A3cHtBeUxysEQ==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.CSharp": "4.7.0",
@@ -190,13 +190,13 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.15.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "OUOnt4Vnd1J838LTWEpP0ge8WIwjhx8O94kuTbC6AJRfOx9Lw78SG6XtVCE+WXb9zC5h3tzOV9YOng8Ke5yXMg==",
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
@@ -292,7 +292,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -324,7 +324,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -368,11 +368,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.15.1, )",
"resolved": "3.15.1",
"contentHash": "vFIf01jNVD69N6wE5iYVM7TKA6AbSTZxq1EedJCVZxhj5pkhHBw+23fyO+wz7Cd9ggqsiBUJMuKxUBHaDphGWQ==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.15.1"
"Speckle.Sdk": "3.15.3"
}
}
}
@@ -180,8 +180,8 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "Gt1qAZrMflLMOjDy4cQzJX5pdq5WokphYdsry7pMBlRHRdx9imQhJUPCg528Y2iVSMUnRKvj3A3cHtBeUxysEQ==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.CSharp": "4.7.0",
@@ -190,13 +190,13 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.15.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "OUOnt4Vnd1J838LTWEpP0ge8WIwjhx8O94kuTbC6AJRfOx9Lw78SG6XtVCE+WXb9zC5h3tzOV9YOng8Ke5yXMg==",
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
@@ -292,7 +292,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -324,7 +324,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -368,11 +368,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.15.1, )",
"resolved": "3.15.1",
"contentHash": "vFIf01jNVD69N6wE5iYVM7TKA6AbSTZxq1EedJCVZxhj5pkhHBw+23fyO+wz7Cd9ggqsiBUJMuKxUBHaDphGWQ==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.15.1"
"Speckle.Sdk": "3.15.3"
}
}
}
@@ -166,8 +166,8 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "Gt1qAZrMflLMOjDy4cQzJX5pdq5WokphYdsry7pMBlRHRdx9imQhJUPCg528Y2iVSMUnRKvj3A3cHtBeUxysEQ==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
@@ -175,13 +175,13 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.15.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "OUOnt4Vnd1J838LTWEpP0ge8WIwjhx8O94kuTbC6AJRfOx9Lw78SG6XtVCE+WXb9zC5h3tzOV9YOng8Ke5yXMg=="
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w=="
},
"SQLitePCLRaw.bundle_e_sqlite3": {
"type": "Transitive",
@@ -239,7 +239,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -272,7 +272,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -316,11 +316,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.15.1, )",
"resolved": "3.15.1",
"contentHash": "vFIf01jNVD69N6wE5iYVM7TKA6AbSTZxq1EedJCVZxhj5pkhHBw+23fyO+wz7Cd9ggqsiBUJMuKxUBHaDphGWQ==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.15.1"
"Speckle.Sdk": "3.15.3"
}
}
},
@@ -166,8 +166,8 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "Gt1qAZrMflLMOjDy4cQzJX5pdq5WokphYdsry7pMBlRHRdx9imQhJUPCg528Y2iVSMUnRKvj3A3cHtBeUxysEQ==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
@@ -175,13 +175,13 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.15.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "OUOnt4Vnd1J838LTWEpP0ge8WIwjhx8O94kuTbC6AJRfOx9Lw78SG6XtVCE+WXb9zC5h3tzOV9YOng8Ke5yXMg=="
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w=="
},
"SQLitePCLRaw.bundle_e_sqlite3": {
"type": "Transitive",
@@ -239,7 +239,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -272,7 +272,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -316,11 +316,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.15.1, )",
"resolved": "3.15.1",
"contentHash": "vFIf01jNVD69N6wE5iYVM7TKA6AbSTZxq1EedJCVZxhj5pkhHBw+23fyO+wz7Cd9ggqsiBUJMuKxUBHaDphGWQ==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.15.1"
"Speckle.Sdk": "3.15.3"
}
}
},
@@ -22,6 +22,10 @@ public static class Civil3dConnectorModule
// add send
serviceCollection.LoadSend();
serviceCollection.AddScoped<IRootObjectBuilder<AutocadRootObject>, Civil3dRootObjectBuilder>();
serviceCollection.AddScoped<
IRootContinuousTraversalBuilder<AutocadRootObject>,
Civil3dContinuousTraversalBuilder
>();
serviceCollection.AddSingleton<IBinding, Civil3dSendBinding>();
// add receive
@@ -0,0 +1,57 @@
using Autodesk.AutoCAD.DatabaseServices;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Autocad.HostApp;
using Speckle.Connectors.Autocad.Operations.Send;
using Speckle.Connectors.Common.Caching;
using Speckle.Connectors.Common.Operations;
using Speckle.Converters.Civil3dShared.ToSpeckle;
using Speckle.Converters.Common;
using Speckle.Sdk.Logging;
using Speckle.Sdk.Models.Collections;
namespace Speckle.Connectors.Civil3dShared.Operations.Send;
public sealed class Civil3dContinuousTraversalBuilder : AutocadContinuousTraversalBaseBuilder
{
private readonly AutocadLayerUnpacker _layerUnpacker;
private readonly PropertySetDefinitionHandler _propertySetDefinitionHandler;
public Civil3dContinuousTraversalBuilder(
AutocadLayerUnpacker layerUnpacker,
PropertySetDefinitionHandler propertySetDefinitionHandler,
IRootToSpeckleConverter converter,
ISendConversionCache sendConversionCache,
AutocadInstanceUnpacker instanceObjectManager,
AutocadMaterialUnpacker materialUnpacker,
AutocadColorUnpacker colorUnpacker,
AutocadGroupUnpacker groupUnpacker,
ILogger<AutocadRootObjectBuilder> logger,
ISdkActivityFactory activityFactory
)
: base(
converter,
sendConversionCache,
instanceObjectManager,
materialUnpacker,
colorUnpacker,
groupUnpacker,
logger,
activityFactory
)
{
_layerUnpacker = layerUnpacker;
_propertySetDefinitionHandler = propertySetDefinitionHandler;
}
public override (Collection, LayerTableRecord?) CreateObjectCollection(Entity entity, Transaction tr)
{
Layer layer = _layerUnpacker.GetOrCreateSpeckleLayer(entity, tr, out LayerTableRecord? autocadLayer);
return (layer, autocadLayer);
}
public override void AddAdditionalProxiesToRoot(Collection rootObject)
{
rootObject[ProxyKeys.PROPERTYSET_DEFINITIONS] = _propertySetDefinitionHandler.Definitions;
}
}
@@ -13,6 +13,7 @@
<Compile Include="$(MSBuildThisFileDirectory)DependencyInjection\Civil3dConnectorModule.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\PropertySetBaker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\Civil3dHostObjectBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Civil3dContinuousTraversalBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Civil3dRootObjectBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bindings\Civil3dSendBinding.cs" />
</ItemGroup>
@@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<Plant3DVersion>2026</Plant3DVersion>
<DefineConstants>$(DefineConstants);PLANT3D2026;PLANT3D;PLANT3D2024_OR_GREATER;PLANT3D2025_OR_GREATER;PLANT3D2026_OR_GREATER</DefineConstants>
<Configurations>Debug;Release;Local</Configurations>
</PropertyGroup>
<PropertyGroup>
<!-- .NET Core uses this to move native dependencies into a root for runtime selection and usage for non-windows development https://learn.microsoft.com/en-us/dotnet/core/project-sdk/msbuild-props#enablewindowstargeting -->
<RuntimeIdentifier>win-x64</RuntimeIdentifier> <!--This is needed for managed dependencies-->
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> <!--This is needed for the rest-->
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath> <!--This is needed just to keep folder paths the same as the netframework versions-->
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2026.0.0" ExcludeAssets="runtime"/>
<PackageReference Include="Speckle.Plant3D.API" VersionOverride="2026.0.0" ExcludeAssets="runtime"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Converters\Plant3d\Speckle.Converters.Plant3d2026\Speckle.Converters.Plant3d2026.csproj" />
<ProjectReference Include="..\..\..\DUI3\Speckle.Connectors.DUI.WebView\Speckle.Connectors.DUI.WebView.csproj" />
<ProjectReference Include="..\..\..\Sdk\Speckle.Converters.Common\Speckle.Converters.Common.csproj" />
</ItemGroup>
<Import Project="..\Speckle.Connectors.Plant3dShared\Speckle.Connectors.Plant3dShared.projitems" Label="Shared" />
<Import Project="..\Speckle.Connectors.AutocadShared\Speckle.Connectors.AutocadShared.projitems" Label="Shared" />
</Project>
@@ -0,0 +1,341 @@
{
"version": 2,
"dependencies": {
"net8.0-windows7.0": {
"Microsoft.NETFramework.ReferenceAssemblies": {
"type": "Direct",
"requested": "[1.0.3, )",
"resolved": "1.0.3",
"contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
"dependencies": {
"Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3"
}
},
"Microsoft.SourceLink.GitHub": {
"type": "Direct",
"requested": "[8.0.0, )",
"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": "[2026.0.0, )",
"resolved": "2026.0.0",
"contentHash": "WlkV81PmbK/ftoM7aGpU6LGosKbePBQej9MO/m63rFMozX89tsitEhE12o58wu7K/4FmRUdAMolYtdK20EDBnw=="
},
"Speckle.InterfaceGenerator": {
"type": "Direct",
"requested": "[0.9.6, )",
"resolved": "0.9.6",
"contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w=="
},
"Speckle.Plant3D.API": {
"type": "Direct",
"requested": "[2026.0.0, )",
"resolved": "2026.0.0",
"contentHash": "VT8M6CGQl1mk3/5Ro3AEAOVrOs/cX58YRwYZnu+6xeHvsazK07Et/6kMJsde4Y7Xrpox7eBLKgYeOspiRVvX5g==",
"dependencies": {
"Speckle.AutoCAD.API": "2026.0.0"
}
},
"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.Build.Tasks.Git": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
},
"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.Configuration": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==",
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "2.2.0"
}
},
"Microsoft.Extensions.Configuration.Abstractions": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==",
"dependencies": {
"Microsoft.Extensions.Primitives": "2.2.0"
}
},
"Microsoft.Extensions.Configuration.Binder": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==",
"dependencies": {
"Microsoft.Extensions.Configuration": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "UpZLNLBpIZ0GTebShui7xXYh6DmBHjWM8NxGxZbdQh/bPZ5e6YswqI+bru6BnEL5eWiOdodsXtEz3FROcgi/qg==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Primitives": "2.2.0",
"System.ComponentModel.Annotations": "4.5.0"
}
},
"Microsoft.Extensions.Primitives": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==",
"dependencies": {
"System.Memory": "4.5.1",
"System.Runtime.CompilerServices.Unsafe": "4.5.1"
}
},
"Microsoft.NETFramework.ReferenceAssemblies.net461": {
"type": "Transitive",
"resolved": "1.0.3",
"contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA=="
},
"Microsoft.SourceLink.Common": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
},
"Speckle.Newtonsoft.Json": {
"type": "Transitive",
"resolved": "13.0.2",
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w=="
},
"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.ComponentModel.Annotations": {
"type": "Transitive",
"resolved": "4.5.0",
"contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg=="
},
"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.Runtime.CompilerServices.Unsafe": {
"type": "Transitive",
"resolved": "4.5.1",
"contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw=="
},
"speckle.connectors.common": {
"type": "Project",
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )"
}
},
"speckle.connectors.dui.webview": {
"type": "Project",
"dependencies": {
"Microsoft.Web.WebView2": "[1.0.1938.49, )",
"Speckle.Connectors.DUI": "[1.0.0, )"
}
},
"speckle.connectors.logging": {
"type": "Project"
},
"speckle.converters.common": {
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.converters.plant3d2026": {
"type": "Project",
"dependencies": {
"Speckle.AutoCAD.API": "[2026.0.0, )",
"Speckle.Connectors.DUI.WebView": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Plant3D.API": "[2026.0.0, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
"resolved": "2.2.0",
"contentHash": "MZtBIwfDFork5vfjpJdG5g8wuJFt7d/y3LOSVVtDK/76wlbtz6cjltfKHqLx2TKVqTj5/c41t77m1+h20zqtPA==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
}
},
"Microsoft.Extensions.Logging": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
"resolved": "2.2.0",
"contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==",
"dependencies": {
"Microsoft.Extensions.Configuration.Binder": "2.2.0",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging.Abstractions": "2.2.0",
"Microsoft.Extensions.Options": "2.2.0"
}
},
"Microsoft.Extensions.Logging.Abstractions": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
"resolved": "2.2.0",
"contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A=="
},
"Microsoft.Web.WebView2": {
"type": "CentralTransitive",
"requested": "[1.0.1938.49, )",
"resolved": "1.0.1938.49",
"contentHash": "z8KnFnaTYzhA/ZnyRX0qGfS1NU5ZBJeClAH64F0fVDvdDJTvME7xl6zTJ0Jlfe1BtL3C0NH9xTy64shg2baKdw=="
},
"Speckle.DoubleNumerics": {
"type": "CentralTransitive",
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.15.3"
}
}
},
"net8.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.1938.49, )",
"resolved": "1.0.1938.49",
"contentHash": "z8KnFnaTYzhA/ZnyRX0qGfS1NU5ZBJeClAH64F0fVDvdDJTvME7xl6zTJ0Jlfe1BtL3C0NH9xTy64shg2baKdw=="
}
}
}
}
@@ -0,0 +1,61 @@
using Microsoft.Extensions.DependencyInjection;
using Speckle.Connectors.Autocad.Bindings;
using Speckle.Connectors.Common.Caching;
using Speckle.Connectors.Common.Cancellation;
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Models.Card.SendFilter;
using Speckle.Converters.Autocad;
using Speckle.Converters.Common;
using Speckle.Converters.Plant3dShared;
namespace Speckle.Connectors.Plant3dShared.Bindings;
public sealed class Plant3dSendBinding : AutocadSendBaseBinding
{
private readonly IPlant3dConversionSettingsFactory _plant3dConversionSettingsFactory;
private readonly IAutocadConversionSettingsFactory _autocadConversionSettingsFactory;
public Plant3dSendBinding(
DocumentModelStore store,
IBrowserBridge parent,
IEnumerable<ISendFilter> sendFilters,
ICancellationManager cancellationManager,
ISendConversionCache sendConversionCache,
IPlant3dConversionSettingsFactory plant3dConversionSettingsFactory,
IAutocadConversionSettingsFactory autocadConversionSettingsFactory,
IThreadContext threadContext,
ITopLevelExceptionHandler topLevelExceptionHandler,
IAppIdleManager appIdleManager,
ISendOperationManagerFactory sendOperationManagerFactory
)
: base(
store,
parent,
sendFilters,
cancellationManager,
sendConversionCache,
threadContext,
topLevelExceptionHandler,
appIdleManager,
sendOperationManagerFactory
)
{
_plant3dConversionSettingsFactory = plant3dConversionSettingsFactory;
_autocadConversionSettingsFactory = autocadConversionSettingsFactory;
}
// We need a separate send binding for Plant 3D due to using a different unit converter (needed for conversion settings construction)
protected override void InitializeSettings(IServiceProvider serviceProvider)
{
serviceProvider
.GetRequiredService<IConverterSettingsStore<Plant3dConversionSettings>>()
.Initialize(_plant3dConversionSettingsFactory.Create(Application.DocumentManager.CurrentDocument));
serviceProvider
.GetRequiredService<IConverterSettingsStore<AutocadConversionSettings>>()
.Initialize(_autocadConversionSettingsFactory.Create(Application.DocumentManager.CurrentDocument));
}
}
@@ -0,0 +1,27 @@
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using Speckle.Connectors.Autocad.DependencyInjection;
using Speckle.Connectors.Autocad.Operations.Send;
using Speckle.Connectors.Common.Builders;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.Plant3dShared.Bindings;
using Speckle.Connectors.Plant3dShared.Operations.Send;
using Speckle.Sdk;
namespace Speckle.Connectors.Plant3dShared.DependencyInjection;
public static class Plant3dConnectorModule
{
public static void AddPlant3d(this IServiceCollection serviceCollection)
{
serviceCollection.AddAutocadBase();
// add send
serviceCollection.LoadSend();
serviceCollection.AddScoped<IRootObjectBuilder<AutocadRootObject>, Plant3dRootObjectBuilder>();
serviceCollection.AddSingleton<IBinding, Plant3dSendBinding>();
// automatically detects the Class:IClass interface pattern to register all generated interfaces
serviceCollection.AddMatchingInterfacesAsTransient(Assembly.GetExecutingAssembly());
}
}
@@ -0,0 +1,50 @@
using Autodesk.AutoCAD.DatabaseServices;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Autocad.HostApp;
using Speckle.Connectors.Autocad.Operations.Send;
using Speckle.Connectors.Common.Caching;
using Speckle.Converters.Autocad;
using Speckle.Converters.Common;
using Speckle.Sdk.Logging;
using Speckle.Sdk.Models.Collections;
namespace Speckle.Connectors.Plant3dShared.Operations.Send;
public sealed class Plant3dRootObjectBuilder : AutocadRootObjectBaseBuilder
{
private readonly AutocadLayerUnpacker _layerUnpacker;
public Plant3dRootObjectBuilder(
AutocadLayerUnpacker layerUnpacker,
IRootToSpeckleConverter converter,
IConverterSettingsStore<AutocadConversionSettings> converterSettings,
ISendConversionCache sendConversionCache,
AutocadInstanceUnpacker instanceObjectManager,
AutocadMaterialUnpacker materialUnpacker,
AutocadColorUnpacker colorUnpacker,
AutocadGroupUnpacker groupUnpacker,
ILogger<AutocadRootObjectBuilder> logger,
ISdkActivityFactory activityFactory
)
: base(
converter,
converterSettings,
sendConversionCache,
instanceObjectManager,
materialUnpacker,
colorUnpacker,
groupUnpacker,
logger,
activityFactory
)
{
_layerUnpacker = layerUnpacker;
}
public override (Collection, LayerTableRecord?) CreateObjectCollection(Entity entity, Transaction tr)
{
Layer layer = _layerUnpacker.GetOrCreateSpeckleLayer(entity, tr, out LayerTableRecord? autocadLayer);
return (layer, autocadLayer);
}
}
@@ -0,0 +1,20 @@
<?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>B1C4A14C-2F4E-4C3D-9B71-A3F5D8E6C912</SharedGUID>
</PropertyGroup>
<PropertyGroup Label="Configuration">
<Import_RootNamespace>Speckle.Connectors.Plant3dShared</Import_RootNamespace>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)DependencyInjection\Plant3dConnectorModule.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Plant3dRootObjectBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bindings\Plant3dSendBinding.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="$(MSBuildThisFileDirectory)DependencyInjection\" />
<Folder Include="$(MSBuildThisFileDirectory)Operations\Send\" />
</ItemGroup>
</Project>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Globals">
<ProjectGuid>{B1C4A14C-2F4E-4C3D-9B71-A3F5D8E6C912}</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.Plant3dShared.projitems" Label="Shared" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.CSharp.targets" />
</Project>
@@ -0,0 +1,211 @@
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Common.Builders;
using Speckle.Connectors.Common.Conversion;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.CSiShared.HostApp;
using Speckle.Connectors.CSiShared.HostApp.Helpers;
using Speckle.Connectors.CSiShared.Utils;
using Speckle.Converters.Common;
using Speckle.Converters.CSiShared;
using Speckle.Converters.CSiShared.Extensions;
using Speckle.Converters.CSiShared.Utils;
using Speckle.Sdk;
using Speckle.Sdk.Logging;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Pipelines.Progress;
using Speckle.Sdk.Pipelines.Send;
namespace Speckle.Connectors.CSiShared.Builders;
/// <summary>
/// Continuous traversal builder for CSi that streams objects through a <see cref="SendPipeline"/>
/// for packfile-based uploads. Same conversion logic as <see cref="CsiRootObjectBuilder"/>.
/// </summary>
public class CsiContinuousTraversalBuilder : IRootContinuousTraversalBuilder<ICsiWrapper>
{
private readonly IRootToSpeckleConverter _rootToSpeckleConverter;
private readonly IConverterSettingsStore<CsiConversionSettings> _converterSettings;
private readonly CsiSendCollectionManager _sendCollectionManager;
private readonly IMaterialUnpacker _materialUnpacker;
private readonly ISectionUnpacker _sectionUnpacker;
private readonly ILogger<CsiRootObjectBuilder> _logger;
private readonly ISdkActivityFactory _activityFactory;
private readonly ICsiApplicationService _csiApplicationService;
private readonly AnalysisResultsExtractor _analysisResultsExtractor;
public CsiContinuousTraversalBuilder(
IRootToSpeckleConverter rootToSpeckleConverter,
IConverterSettingsStore<CsiConversionSettings> converterSettings,
CsiSendCollectionManager sendCollectionManager,
IMaterialUnpacker materialUnpacker,
ISectionUnpacker sectionUnpacker,
ILogger<CsiRootObjectBuilder> logger,
ISdkActivityFactory activityFactory,
ICsiApplicationService csiApplicationService,
AnalysisResultsExtractor analysisResultsExtractor
)
{
_converterSettings = converterSettings;
_sendCollectionManager = sendCollectionManager;
_materialUnpacker = materialUnpacker;
_sectionUnpacker = sectionUnpacker;
_rootToSpeckleConverter = rootToSpeckleConverter;
_logger = logger;
_activityFactory = activityFactory;
_csiApplicationService = csiApplicationService;
_analysisResultsExtractor = analysisResultsExtractor;
}
public async Task<RootObjectBuilderResult> Build(
IReadOnlyList<ICsiWrapper> csiObjects,
string projectId,
SendPipeline sendPipeline,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
{
using var activity = _activityFactory.Start("Build");
string modelFileName = _csiApplicationService.SapModel.GetModelFilename(false) ?? "Unnamed model";
(string forceUnit, string tempUnit) = GetForceAndTemperatureUnits();
Collection rootObjectCollection =
new()
{
name = modelFileName,
["units"] = _converterSettings.Current.SpeckleUnits,
["forceUnits"] = forceUnit,
["temperatureUnits"] = tempUnit
};
List<SendConversionResult> results = new(csiObjects.Count);
int count = 0;
using (var _ = _activityFactory.Start("Convert all"))
{
foreach (ICsiWrapper csiObject in csiObjects)
{
cancellationToken.ThrowIfCancellationRequested();
var result = await ConvertCsiObject(csiObject, rootObjectCollection, sendPipeline);
results.Add(result);
count++;
onOperationProgressed.Report(
new($"Converting objects... ({count:N0} / {csiObjects.Count:N0})", (double)count / csiObjects.Count)
);
await Task.Yield();
}
}
if (results.All(x => x.Status == Status.ERROR))
{
throw new SpeckleException("Failed to convert all objects");
}
using (var _ = _activityFactory.Start("Process Proxies"))
{
rootObjectCollection[ProxyKeys.MATERIAL] = _materialUnpacker.UnpackMaterials().ToList();
rootObjectCollection[ProxyKeys.SECTION] = _sectionUnpacker.UnpackSections().ToList();
}
// Extract analysis results (if applicable)
var objectSelectionSummary = GetObjectSummary(csiObjects);
var selectedCasesAndCombinations = _converterSettings.Current.SelectedLoadCasesAndCombinations;
var requestedResultTypes = _converterSettings.Current.SelectedResultTypes;
if (selectedCasesAndCombinations?.Count > 0)
{
if (requestedResultTypes == null || requestedResultTypes.Count == 0)
{
throw new SpeckleException(
"Adjust publish settings - no result type input for the requested load cases and combinations"
);
}
if (!_csiApplicationService.SapModel.GetModelIsLocked())
{
throw new SpeckleException("Model unlocked, no access to analysis results");
}
try
{
var analysisResults = _analysisResultsExtractor.ExtractAnalysisResults(
selectedCasesAndCombinations,
requestedResultTypes,
objectSelectionSummary
);
rootObjectCollection[RootKeys.ANALYSIS_RESULTS] = analysisResults;
}
catch (Exception ex)
{
throw new SpeckleException("Analysis result extraction failed", ex);
}
}
// Process root collection and wait for all uploads
await sendPipeline.Process(rootObjectCollection);
await sendPipeline.WaitForUpload();
return new RootObjectBuilderResult(rootObjectCollection, results);
}
private async Task<SendConversionResult> ConvertCsiObject(
ICsiWrapper csiObject,
Collection typeCollection,
SendPipeline sendPipeline
)
{
string sourceType = csiObject.ObjectName;
string applicationId = csiObject switch
{
CsiJointWrapper jointWrapper => jointWrapper.GetSpeckleApplicationId(_csiApplicationService.SapModel),
CsiFrameWrapper frameWrapper => frameWrapper.GetSpeckleApplicationId(_csiApplicationService.SapModel),
CsiCableWrapper cableWrapper => cableWrapper.GetSpeckleApplicationId(_csiApplicationService.SapModel),
CsiTendonWrapper tendonWrapper => tendonWrapper.ObjectName,
CsiShellWrapper shellWrapper => shellWrapper.GetSpeckleApplicationId(_csiApplicationService.SapModel),
CsiSolidWrapper solidWrapper => solidWrapper.GetSpeckleApplicationId(_csiApplicationService.SapModel),
CsiLinkWrapper linkWrapper => linkWrapper.GetSpeckleApplicationId(_csiApplicationService.SapModel),
_ => throw new ArgumentException($"Unsupported wrapper type: {csiObject.GetType()}", nameof(csiObject))
};
try
{
Base converted = _rootToSpeckleConverter.Convert(csiObject);
var collection = _sendCollectionManager.AddObjectCollectionToRoot(converted, typeCollection);
// NOTE: this is the main part that differentiate from the main root object builder
var reference = await sendPipeline.Process(converted).ConfigureAwait(false);
collection.elements.Add(reference);
return new(Status.SUCCESS, applicationId, sourceType, reference);
}
catch (NotImplementedException ex)
{
_logger.LogError(ex, "Failed to convert object {sourceType}", sourceType);
return new(Status.WARNING, applicationId, sourceType, null, ex);
}
catch (Exception ex) when (!ex.IsFatal())
{
_logger.LogError(ex, "Failed to convert object {sourceType}", sourceType);
return new(Status.ERROR, applicationId, sourceType, null, ex);
}
}
private Dictionary<ModelObjectType, List<string>> GetObjectSummary(IReadOnlyList<ICsiWrapper> csiObjects) =>
csiObjects
.GroupBy(csiObject => csiObject.ObjectType)
.ToDictionary(group => group.Key, group => group.Select(obj => obj.Name).ToList());
private (string, string) GetForceAndTemperatureUnits()
{
var forceUnit = eForce.NotApplicable;
var lengthUnit = eLength.NotApplicable;
var temperatureUnit = eTemperature.NotApplicable;
_converterSettings.Current.SapModel.GetDatabaseUnits_2(ref forceUnit, ref lengthUnit, ref temperatureUnit);
return (forceUnit.ToString(), temperatureUnit.ToString());
}
}
@@ -13,6 +13,7 @@ using Speckle.Sdk;
using Speckle.Sdk.Logging;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Pipelines.Progress;
namespace Speckle.Connectors.CSiShared.Builders;
@@ -43,6 +43,7 @@ public static class ServiceRegistration
services.AddScoped<ISendFilter, CsiSharedSelectionFilter>();
services.AddScoped<CsiSendCollectionManager>();
services.AddScoped<IRootObjectBuilder<ICsiWrapper>, CsiRootObjectBuilder>();
services.AddScoped<IRootContinuousTraversalBuilder<ICsiWrapper>, CsiContinuousTraversalBuilder>();
services.AddScoped<SendOperation<ICsiWrapper>>();
services.AddScoped<CsiMaterialPropertyExtractor>();
@@ -29,6 +29,7 @@
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\IApplicationSectionPropertyExtractor.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\ISectionPropertyExtractor.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\ISectionUnpacker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\CsiContinuousTraversalBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\CsiRootObjectBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\ToSpeckleSettingsManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Plugin\CsiPluginBase.cs" />
@@ -171,8 +171,8 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "Gt1qAZrMflLMOjDy4cQzJX5pdq5WokphYdsry7pMBlRHRdx9imQhJUPCg528Y2iVSMUnRKvj3A3cHtBeUxysEQ==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.CSharp": "4.7.0",
@@ -181,13 +181,13 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.15.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "OUOnt4Vnd1J838LTWEpP0ge8WIwjhx8O94kuTbC6AJRfOx9Lw78SG6XtVCE+WXb9zC5h3tzOV9YOng8Ke5yXMg==",
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
@@ -283,7 +283,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -307,7 +307,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.converters.etabs21": {
@@ -357,11 +357,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.15.1, )",
"resolved": "3.15.1",
"contentHash": "vFIf01jNVD69N6wE5iYVM7TKA6AbSTZxq1EedJCVZxhj5pkhHBw+23fyO+wz7Cd9ggqsiBUJMuKxUBHaDphGWQ==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.15.1"
"Speckle.Sdk": "3.15.3"
}
}
}
@@ -157,8 +157,8 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "Gt1qAZrMflLMOjDy4cQzJX5pdq5WokphYdsry7pMBlRHRdx9imQhJUPCg528Y2iVSMUnRKvj3A3cHtBeUxysEQ==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
@@ -166,13 +166,13 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.15.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "OUOnt4Vnd1J838LTWEpP0ge8WIwjhx8O94kuTbC6AJRfOx9Lw78SG6XtVCE+WXb9zC5h3tzOV9YOng8Ke5yXMg=="
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w=="
},
"SQLitePCLRaw.bundle_e_sqlite3": {
"type": "Transitive",
@@ -230,7 +230,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -254,7 +254,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.converters.etabs22": {
@@ -304,11 +304,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.15.1, )",
"resolved": "3.15.1",
"contentHash": "vFIf01jNVD69N6wE5iYVM7TKA6AbSTZxq1EedJCVZxhj5pkhHBw+23fyO+wz7Cd9ggqsiBUJMuKxUBHaDphGWQ==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.15.1"
"Speckle.Sdk": "3.15.3"
}
}
}
@@ -171,8 +171,8 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "Gt1qAZrMflLMOjDy4cQzJX5pdq5WokphYdsry7pMBlRHRdx9imQhJUPCg528Y2iVSMUnRKvj3A3cHtBeUxysEQ==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.CSharp": "4.7.0",
@@ -181,13 +181,13 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.15.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "OUOnt4Vnd1J838LTWEpP0ge8WIwjhx8O94kuTbC6AJRfOx9Lw78SG6XtVCE+WXb9zC5h3tzOV9YOng8Ke5yXMg==",
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
@@ -283,7 +283,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -307,7 +307,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.converters.navisworks2020": {
@@ -359,11 +359,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.15.1, )",
"resolved": "3.15.1",
"contentHash": "vFIf01jNVD69N6wE5iYVM7TKA6AbSTZxq1EedJCVZxhj5pkhHBw+23fyO+wz7Cd9ggqsiBUJMuKxUBHaDphGWQ==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.15.1"
"Speckle.Sdk": "3.15.3"
}
}
},
@@ -171,8 +171,8 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "Gt1qAZrMflLMOjDy4cQzJX5pdq5WokphYdsry7pMBlRHRdx9imQhJUPCg528Y2iVSMUnRKvj3A3cHtBeUxysEQ==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.CSharp": "4.7.0",
@@ -181,13 +181,13 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.15.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "OUOnt4Vnd1J838LTWEpP0ge8WIwjhx8O94kuTbC6AJRfOx9Lw78SG6XtVCE+WXb9zC5h3tzOV9YOng8Ke5yXMg==",
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
@@ -283,7 +283,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -307,7 +307,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.converters.navisworks2021": {
@@ -359,11 +359,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.15.1, )",
"resolved": "3.15.1",
"contentHash": "vFIf01jNVD69N6wE5iYVM7TKA6AbSTZxq1EedJCVZxhj5pkhHBw+23fyO+wz7Cd9ggqsiBUJMuKxUBHaDphGWQ==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.15.1"
"Speckle.Sdk": "3.15.3"
}
}
},
@@ -171,8 +171,8 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "Gt1qAZrMflLMOjDy4cQzJX5pdq5WokphYdsry7pMBlRHRdx9imQhJUPCg528Y2iVSMUnRKvj3A3cHtBeUxysEQ==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.CSharp": "4.7.0",
@@ -181,13 +181,13 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.15.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "OUOnt4Vnd1J838LTWEpP0ge8WIwjhx8O94kuTbC6AJRfOx9Lw78SG6XtVCE+WXb9zC5h3tzOV9YOng8Ke5yXMg==",
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
@@ -283,7 +283,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -307,7 +307,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.converters.navisworks2022": {
@@ -359,11 +359,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.15.1, )",
"resolved": "3.15.1",
"contentHash": "vFIf01jNVD69N6wE5iYVM7TKA6AbSTZxq1EedJCVZxhj5pkhHBw+23fyO+wz7Cd9ggqsiBUJMuKxUBHaDphGWQ==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.15.1"
"Speckle.Sdk": "3.15.3"
}
}
},
@@ -171,8 +171,8 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "Gt1qAZrMflLMOjDy4cQzJX5pdq5WokphYdsry7pMBlRHRdx9imQhJUPCg528Y2iVSMUnRKvj3A3cHtBeUxysEQ==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.CSharp": "4.7.0",
@@ -181,13 +181,13 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.15.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "OUOnt4Vnd1J838LTWEpP0ge8WIwjhx8O94kuTbC6AJRfOx9Lw78SG6XtVCE+WXb9zC5h3tzOV9YOng8Ke5yXMg==",
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
@@ -283,7 +283,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -307,7 +307,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.converters.navisworks2023": {
@@ -359,11 +359,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.15.1, )",
"resolved": "3.15.1",
"contentHash": "vFIf01jNVD69N6wE5iYVM7TKA6AbSTZxq1EedJCVZxhj5pkhHBw+23fyO+wz7Cd9ggqsiBUJMuKxUBHaDphGWQ==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.15.1"
"Speckle.Sdk": "3.15.3"
}
}
},
@@ -171,8 +171,8 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "Gt1qAZrMflLMOjDy4cQzJX5pdq5WokphYdsry7pMBlRHRdx9imQhJUPCg528Y2iVSMUnRKvj3A3cHtBeUxysEQ==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.CSharp": "4.7.0",
@@ -181,13 +181,13 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.15.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "OUOnt4Vnd1J838LTWEpP0ge8WIwjhx8O94kuTbC6AJRfOx9Lw78SG6XtVCE+WXb9zC5h3tzOV9YOng8Ke5yXMg==",
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
@@ -283,7 +283,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -307,7 +307,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.converters.navisworks2024": {
@@ -359,11 +359,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.15.1, )",
"resolved": "3.15.1",
"contentHash": "vFIf01jNVD69N6wE5iYVM7TKA6AbSTZxq1EedJCVZxhj5pkhHBw+23fyO+wz7Cd9ggqsiBUJMuKxUBHaDphGWQ==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.15.1"
"Speckle.Sdk": "3.15.3"
}
}
},
@@ -177,8 +177,8 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "Gt1qAZrMflLMOjDy4cQzJX5pdq5WokphYdsry7pMBlRHRdx9imQhJUPCg528Y2iVSMUnRKvj3A3cHtBeUxysEQ==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.CSharp": "4.7.0",
@@ -187,13 +187,13 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.15.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "OUOnt4Vnd1J838LTWEpP0ge8WIwjhx8O94kuTbC6AJRfOx9Lw78SG6XtVCE+WXb9zC5h3tzOV9YOng8Ke5yXMg==",
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
@@ -289,7 +289,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -313,7 +313,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.converters.navisworks2025": {
@@ -359,11 +359,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.15.1, )",
"resolved": "3.15.1",
"contentHash": "vFIf01jNVD69N6wE5iYVM7TKA6AbSTZxq1EedJCVZxhj5pkhHBw+23fyO+wz7Cd9ggqsiBUJMuKxUBHaDphGWQ==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.15.1"
"Speckle.Sdk": "3.15.3"
}
}
},
@@ -186,8 +186,8 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "Gt1qAZrMflLMOjDy4cQzJX5pdq5WokphYdsry7pMBlRHRdx9imQhJUPCg528Y2iVSMUnRKvj3A3cHtBeUxysEQ==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.CSharp": "4.7.0",
@@ -196,13 +196,13 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.15.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "OUOnt4Vnd1J838LTWEpP0ge8WIwjhx8O94kuTbC6AJRfOx9Lw78SG6XtVCE+WXb9zC5h3tzOV9YOng8Ke5yXMg==",
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
@@ -290,7 +290,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -314,7 +314,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.converters.navisworks2026": {
@@ -361,11 +361,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.15.1, )",
"resolved": "3.15.1",
"contentHash": "vFIf01jNVD69N6wE5iYVM7TKA6AbSTZxq1EedJCVZxhj5pkhHBw+23fyO+wz7Cd9ggqsiBUJMuKxUBHaDphGWQ==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.15.1"
"Speckle.Sdk": "3.15.3"
}
}
},
@@ -5,7 +5,6 @@ using Speckle.Connector.Navisworks.Operations.Send.Filters;
using Speckle.Connector.Navisworks.Operations.Send.Settings;
using Speckle.Connector.Navisworks.Services;
using Speckle.Connectors.Common.Cancellation;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
@@ -18,6 +17,7 @@ using Speckle.Converter.Navisworks.Services;
using Speckle.Converter.Navisworks.Settings;
using Speckle.Converters.Common;
using Speckle.Sdk.Common;
using Speckle.Sdk.Pipelines.Progress;
namespace Speckle.Connector.Navisworks.Bindings;
@@ -57,6 +57,7 @@ public static class NavisworksConnectorServiceRegistration
// Sending operations
serviceCollection.AddScoped<IRootObjectBuilder<NAV.ModelItem>, NavisworksRootObjectBuilder>();
serviceCollection.AddScoped<IRootContinuousTraversalBuilder<NAV.ModelItem>, NavisworksContinuousTraversalBuilder>();
serviceCollection.AddScoped<SendOperation<NAV.ModelItem>>();
serviceCollection.AddSingleton(DefaultTraversal.CreateTraversalFunc());
serviceCollection.AddSingleton<IOperationProgressManager, OperationProgressManager>();
@@ -0,0 +1,400 @@
using Microsoft.Extensions.Logging;
using Speckle.Connector.Navisworks.HostApp;
using Speckle.Connectors.Common.Builders;
using Speckle.Connectors.Common.Conversion;
using Speckle.Converter.Navisworks.Helpers;
using Speckle.Converter.Navisworks.Services;
using Speckle.Converter.Navisworks.Settings;
using Speckle.Converters.Common;
using Speckle.Objects.Data;
using Speckle.Sdk;
using Speckle.Sdk.Logging;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Models.Instances;
using Speckle.Sdk.Pipelines.Progress;
using Speckle.Sdk.Pipelines.Send;
using static Speckle.Connector.Navisworks.Operations.Send.GeometryNodeMerger;
using static Speckle.Connectors.Common.Operations.ProxyKeys;
using static Speckle.Converter.Navisworks.Constants.InstanceConstants;
namespace Speckle.Connector.Navisworks.Operations.Send;
/// <summary>
/// Continuous traversal builder for Navisworks that streams objects through a <see cref="SendPipeline"/>
/// for packfile-based uploads. Same conversion/grouping logic as <see cref="NavisworksRootObjectBuilder"/>,
/// but processes final elements through the pipeline after all post-processing is complete.
/// </summary>
public class NavisworksContinuousTraversalBuilder(
IRootToSpeckleConverter rootToSpeckleConverter,
IConverterSettingsStore<NavisworksConversionSettings> converterSettings,
ILogger<NavisworksContinuousTraversalBuilder> logger,
ISdkActivityFactory activityFactory,
NavisworksMaterialUnpacker materialUnpacker,
NavisworksColorUnpacker colorUnpacker,
Speckle.Converter.Navisworks.Constants.Registers.IInstanceFragmentRegistry instanceRegistry,
IElementSelectionService elementSelectionService,
IUiUnitsCache uiUnitsCache
) : IRootContinuousTraversalBuilder<NAV.ModelItem>
{
private bool SkipNodeMerging { get; set; }
private bool DisableGroupingForInstanceTesting { get; set; }
public async Task<RootObjectBuilderResult> Build(
IReadOnlyList<NAV.ModelItem> navisworksModelItems,
string projectId,
SendPipeline sendPipeline,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
{
#if DEBUG
SkipNodeMerging = false;
DisableGroupingForInstanceTesting = false;
#endif
using var activity = activityFactory.Start("Build");
ValidateInputs(navisworksModelItems, projectId, onOperationProgressed);
var rootCollection = InitializeRootCollection();
(Dictionary<string, Base?> convertedElements, List<SendConversionResult> conversionResults) =
await ConvertModelItemsAsync(navisworksModelItems, onOperationProgressed, cancellationToken);
ValidateConversionResults(conversionResults);
var groupedNodes = SkipNodeMerging ? [] : GroupSiblingGeometryNodes(navisworksModelItems);
var finalElements = BuildFinalElements(convertedElements, groupedNodes);
await AddProxiesToCollection(rootCollection, navisworksModelItems, groupedNodes);
AddInstanceDefinitionsToCollection(rootCollection, ref finalElements);
// Process each final element through the send pipeline
var processedElements = new List<Base>(finalElements.Count);
foreach (var element in finalElements)
{
cancellationToken.ThrowIfCancellationRequested();
// NOTE: this is the main part that differentiate from the main root object builder
var reference = await sendPipeline.Process(element).ConfigureAwait(false);
processedElements.Add(reference);
}
rootCollection.elements = processedElements;
// Process the root collection and wait for all uploads to complete
await sendPipeline.Process(rootCollection);
await sendPipeline.WaitForUpload();
return new RootObjectBuilderResult(rootCollection, conversionResults);
}
private static void ValidateInputs(
IReadOnlyList<NAV.ModelItem> navisworksModelItems,
string projectId,
IProgress<CardProgress> onOperationProgressed
)
{
if (!navisworksModelItems.Any())
{
throw new SpeckleException("No objects to convert");
}
if (navisworksModelItems == null)
{
throw new ArgumentNullException(nameof(navisworksModelItems));
}
if (onOperationProgressed == null || projectId == null)
{
throw new ArgumentNullException(
onOperationProgressed == null ? nameof(onOperationProgressed) : nameof(projectId)
);
}
}
private Collection InitializeRootCollection() =>
new()
{
name = NavisworksApp.ActiveDocument.Title ?? "Unnamed model",
["units"] = converterSettings.Current.Derived.SpeckleUnits
};
private Task<(Dictionary<string, Base?> converted, List<SendConversionResult> results)> ConvertModelItemsAsync(
IReadOnlyList<NAV.ModelItem> navisworksModelItems,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
{
var results = new List<SendConversionResult>(navisworksModelItems.Count);
var convertedBases = new Dictionary<string, Base?>();
int processedCount = 0;
int totalCount = navisworksModelItems.Count;
foreach (var item in navisworksModelItems)
{
cancellationToken.ThrowIfCancellationRequested();
var converted = ConvertNavisworksItem(item, convertedBases);
results.Add(converted);
processedCount++;
onOperationProgressed.Report(new CardProgress("Converting", (double)processedCount / totalCount));
}
return Task.FromResult((convertedBases, results));
}
private static void ValidateConversionResults(List<SendConversionResult> results)
{
if (results.All(x => x.Status == Status.ERROR))
{
throw new SpeckleException("Failed to convert all objects.");
}
}
private List<Base> BuildFinalElements(
Dictionary<string, Base?> convertedBases,
Dictionary<string, List<NAV.ModelItem>> groupedNodes
)
{
var finalElements = new List<Base>();
var processedPaths = new HashSet<string>();
if (!DisableGroupingForInstanceTesting)
{
AddGroupedElements(finalElements, convertedBases, groupedNodes, processedPaths);
logger.LogInformation(
"After grouping: {grouped} paths processed, {elements} elements in collection",
processedPaths.Count,
finalElements.Count
);
}
else
{
logger.LogInformation("Grouping disabled for instance testing");
}
if (converterSettings.Current.User.PreserveModelHierarchy)
{
logger.LogInformation("Building hierarchy (PreserveModelHierarchy=true)");
var hierarchyBuilder = new NavisworksHierarchyBuilder(
convertedBases,
rootToSpeckleConverter,
elementSelectionService
);
return hierarchyBuilder.BuildHierarchy();
}
logger.LogInformation("Adding remaining elements (flat mode)");
AddRemainingElements(finalElements, convertedBases, processedPaths);
logger.LogInformation("Final elements count: {count}", finalElements.Count);
return finalElements;
}
private void AddGroupedElements(
List<Base> finalElements,
Dictionary<string, Base?> convertedBases,
Dictionary<string, List<NAV.ModelItem>> groupedNodes,
HashSet<string> processedPaths
)
{
foreach (var group in groupedNodes)
{
var siblingBases = new List<Base>(group.Value.Count);
foreach (var itemPath in group.Value.Select(elementSelectionService.GetModelItemPath))
{
processedPaths.Add(itemPath);
if (convertedBases.TryGetValue(itemPath, out var convertedBase) && convertedBase != null)
{
siblingBases.Add(convertedBase);
}
}
if (siblingBases.Count > 0)
{
finalElements.Add(CreateNavisworksObject(group.Key, siblingBases));
}
}
}
private void AddRemainingElements(
List<Base> finalElements,
Dictionary<string, Base?> convertedBases,
HashSet<string> processedPaths
)
{
foreach (var kvp in convertedBases.Where(kvp => !processedPaths.Contains(kvp.Key)))
{
switch (kvp.Value)
{
case null:
continue;
case Collection collection:
finalElements.Add(collection);
break;
default:
if (CreateNavisworksObject(kvp.Value) is { } navisworksObject)
{
finalElements.Add(navisworksObject);
}
break;
}
}
}
private (string name, string path) GetElementNameAndPath(string applicationId)
{
var modelItem = elementSelectionService.GetModelItemFromPath(applicationId);
var context = HierarchyHelper.ExtractContext(modelItem);
return (context.Name, context.Path);
}
private NavisworksObject CreateNavisworksObject(string groupKey, List<Base> siblingBases)
{
string cleanParentPath = ElementSelectionHelper.GetCleanPath(groupKey);
(string name, string path) = GetElementNameAndPath(cleanParentPath);
int estimatedCapacity = siblingBases.Sum(b => (b["displayValue"] as List<Base>)?.Count ?? 0);
var displayValues = new List<Base>(estimatedCapacity);
displayValues.AddRange(
siblingBases
.Where(sibling => sibling["displayValue"] is List<Base>)
.SelectMany(sibling => (List<Base>)sibling["displayValue"]!)
);
return new NavisworksObject
{
name = name,
displayValue = displayValues,
properties = siblingBases.First()["properties"] as Dictionary<string, object?> ?? [],
units = converterSettings.Current.Derived.SpeckleUnits,
applicationId = groupKey,
["path"] = path
};
}
private NavisworksObject? CreateNavisworksObject(Base convertedBase)
{
if (convertedBase.applicationId == null)
{
return null;
}
(string name, string path) = GetElementNameAndPath(convertedBase.applicationId);
var units = uiUnitsCache.Ensure();
return new NavisworksObject
{
name = name,
displayValue = convertedBase["displayValue"] as List<Base> ?? [],
properties = convertedBase["properties"] as Dictionary<string, object?> ?? [],
units = units.ToString(),
applicationId = convertedBase.applicationId,
["path"] = path
};
}
private Task AddProxiesToCollection(
Collection rootCollection,
IReadOnlyList<NAV.ModelItem> navisworksModelItems,
Dictionary<string, List<NAV.ModelItem>> groupedNodes
)
{
using var _ = activityFactory.Start("UnpackProxies");
var renderMaterials = materialUnpacker.UnpackRenderMaterial(navisworksModelItems, groupedNodes);
if (renderMaterials.Count > 0)
{
rootCollection[RENDER_MATERIAL] = renderMaterials;
}
var colors = colorUnpacker.UnpackColor(navisworksModelItems, groupedNodes);
if (colors.Count > 0)
{
rootCollection[COLOR] = colors;
}
return Task.CompletedTask;
}
private void AddInstanceDefinitionsToCollection(Collection rootCollection, ref List<Base> finalElements)
{
using var _ = activityFactory.Start("BuildInstanceDefinitions");
var allDefinitions = instanceRegistry.GetAllDefinitionGeometries();
if (allDefinitions.Count == 0)
{
logger.LogInformation("No instance definitions found - instancing may be disabled");
return;
}
logger.LogInformation("Building instance structure for {count} definition groups", allDefinitions.Count);
var instanceDefinitionProxies = new List<InstanceDefinitionProxy>(allDefinitions.Count);
int estimatedGeometryCount = allDefinitions.Sum(kvp => kvp.Value.Count);
var allDefinitionGeometries = new List<Base>(estimatedGeometryCount);
foreach (var kvp in allDefinitions)
{
var groupKey = kvp.Key;
var geometries = kvp.Value;
var groupKeyPath = groupKey.ToPathString();
var defProxy = new InstanceDefinitionProxy
{
name = $"Shared Geometry {groupKeyPath}",
objects = geometries.Select(g => g.applicationId ?? "").Where(id => !string.IsNullOrEmpty(id)).ToList(),
applicationId = $"{DEFINITION_ID_PREFIX}{groupKeyPath}",
maxDepth = 0
};
instanceDefinitionProxies.Add(defProxy);
allDefinitionGeometries.AddRange(geometries);
}
rootCollection[INSTANCE_DEFINITION] = instanceDefinitionProxies;
var geometryDefinitionsCollection = new Collection
{
name = "Geometry Definitions",
elements = allDefinitionGeometries
};
var objectCollection = new Collection { name = "", elements = finalElements };
finalElements = [geometryDefinitionsCollection, objectCollection];
logger.LogInformation(
"Added {proxyCount} instance definition proxies and {geomCount} definition geometries",
instanceDefinitionProxies.Count,
allDefinitionGeometries.Count
);
}
private SendConversionResult ConvertNavisworksItem(
NAV.ModelItem navisworksItem,
Dictionary<string, Base?> convertedBases
)
{
string applicationId = elementSelectionService.GetModelItemPath(navisworksItem);
string sourceType = navisworksItem.GetType().Name;
try
{
Base converted = rootToSpeckleConverter.Convert(navisworksItem);
convertedBases[applicationId] = converted;
return new SendConversionResult(Status.SUCCESS, applicationId, sourceType, converted);
}
catch (Exception ex) when (!ex.IsFatal())
{
logger.LogError(ex, "Failed to convert model item {id}", applicationId);
return new SendConversionResult(Status.ERROR, applicationId, "ModelItem", null, ex);
}
}
}
@@ -3,7 +3,6 @@ using Speckle.Connector.Navisworks.HostApp;
using Speckle.Connectors.Common.Builders;
using Speckle.Connectors.Common.Caching;
using Speckle.Connectors.Common.Conversion;
using Speckle.Connectors.Common.Operations;
using Speckle.Converter.Navisworks.Helpers;
using Speckle.Converter.Navisworks.Services;
using Speckle.Converter.Navisworks.Settings;
@@ -14,6 +13,7 @@ using Speckle.Sdk.Logging;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Models.Instances;
using Speckle.Sdk.Pipelines.Progress;
using static Speckle.Connector.Navisworks.Operations.Send.GeometryNodeMerger;
using static Speckle.Connectors.Common.Operations.ProxyKeys;
using static Speckle.Converter.Navisworks.Constants.InstanceConstants;
@@ -24,6 +24,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Filters\SavedItemHelpers.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\GeometryNodeMerger.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\NavisworksHierarchyBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\NavisworksContinuousTraversalBuilder.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\NavisworksRootObjectBuilder.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\ConvertHiddenElementsSetting.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\IncludeInternalPropertiesSetting.cs"/>
@@ -193,8 +193,8 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "Gt1qAZrMflLMOjDy4cQzJX5pdq5WokphYdsry7pMBlRHRdx9imQhJUPCg528Y2iVSMUnRKvj3A3cHtBeUxysEQ==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.CSharp": "4.7.0",
@@ -203,13 +203,13 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.15.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "OUOnt4Vnd1J838LTWEpP0ge8WIwjhx8O94kuTbC6AJRfOx9Lw78SG6XtVCE+WXb9zC5h3tzOV9YOng8Ke5yXMg==",
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
@@ -312,7 +312,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -335,7 +335,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.converters.revit2022": {
@@ -387,11 +387,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.15.1, )",
"resolved": "3.15.1",
"contentHash": "vFIf01jNVD69N6wE5iYVM7TKA6AbSTZxq1EedJCVZxhj5pkhHBw+23fyO+wz7Cd9ggqsiBUJMuKxUBHaDphGWQ==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.15.1"
"Speckle.Sdk": "3.15.3"
}
},
"Speckle.Revit.API": {
@@ -193,8 +193,8 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "Gt1qAZrMflLMOjDy4cQzJX5pdq5WokphYdsry7pMBlRHRdx9imQhJUPCg528Y2iVSMUnRKvj3A3cHtBeUxysEQ==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.CSharp": "4.7.0",
@@ -203,13 +203,13 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.15.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "OUOnt4Vnd1J838LTWEpP0ge8WIwjhx8O94kuTbC6AJRfOx9Lw78SG6XtVCE+WXb9zC5h3tzOV9YOng8Ke5yXMg==",
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
@@ -312,7 +312,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -335,7 +335,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.converters.revit2023": {
@@ -387,11 +387,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.15.1, )",
"resolved": "3.15.1",
"contentHash": "vFIf01jNVD69N6wE5iYVM7TKA6AbSTZxq1EedJCVZxhj5pkhHBw+23fyO+wz7Cd9ggqsiBUJMuKxUBHaDphGWQ==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.15.1"
"Speckle.Sdk": "3.15.3"
}
},
"Speckle.Revit.API": {
@@ -193,8 +193,8 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "Gt1qAZrMflLMOjDy4cQzJX5pdq5WokphYdsry7pMBlRHRdx9imQhJUPCg528Y2iVSMUnRKvj3A3cHtBeUxysEQ==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.CSharp": "4.7.0",
@@ -203,13 +203,13 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.15.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "OUOnt4Vnd1J838LTWEpP0ge8WIwjhx8O94kuTbC6AJRfOx9Lw78SG6XtVCE+WXb9zC5h3tzOV9YOng8Ke5yXMg==",
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
@@ -312,7 +312,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -335,7 +335,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.converters.revit2024": {
@@ -387,11 +387,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.15.1, )",
"resolved": "3.15.1",
"contentHash": "vFIf01jNVD69N6wE5iYVM7TKA6AbSTZxq1EedJCVZxhj5pkhHBw+23fyO+wz7Cd9ggqsiBUJMuKxUBHaDphGWQ==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.15.1"
"Speckle.Sdk": "3.15.3"
}
},
"Speckle.Revit.API": {
@@ -173,8 +173,8 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "Gt1qAZrMflLMOjDy4cQzJX5pdq5WokphYdsry7pMBlRHRdx9imQhJUPCg528Y2iVSMUnRKvj3A3cHtBeUxysEQ==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
@@ -182,13 +182,13 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.15.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "OUOnt4Vnd1J838LTWEpP0ge8WIwjhx8O94kuTbC6AJRfOx9Lw78SG6XtVCE+WXb9zC5h3tzOV9YOng8Ke5yXMg=="
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w=="
},
"SQLitePCLRaw.bundle_e_sqlite3": {
"type": "Transitive",
@@ -253,7 +253,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -276,7 +276,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.converters.revit2025": {
@@ -328,11 +328,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.15.1, )",
"resolved": "3.15.1",
"contentHash": "vFIf01jNVD69N6wE5iYVM7TKA6AbSTZxq1EedJCVZxhj5pkhHBw+23fyO+wz7Cd9ggqsiBUJMuKxUBHaDphGWQ==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.15.1"
"Speckle.Sdk": "3.15.3"
}
},
"Speckle.Revit.API": {
@@ -166,8 +166,8 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "Gt1qAZrMflLMOjDy4cQzJX5pdq5WokphYdsry7pMBlRHRdx9imQhJUPCg528Y2iVSMUnRKvj3A3cHtBeUxysEQ==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
@@ -175,13 +175,13 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.15.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "OUOnt4Vnd1J838LTWEpP0ge8WIwjhx8O94kuTbC6AJRfOx9Lw78SG6XtVCE+WXb9zC5h3tzOV9YOng8Ke5yXMg=="
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w=="
},
"SQLitePCLRaw.bundle_e_sqlite3": {
"type": "Transitive",
@@ -246,7 +246,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -269,7 +269,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.converters.revit2026": {
@@ -312,11 +312,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.15.1, )",
"resolved": "3.15.1",
"contentHash": "vFIf01jNVD69N6wE5iYVM7TKA6AbSTZxq1EedJCVZxhj5pkhHBw+23fyO+wz7Cd9ggqsiBUJMuKxUBHaDphGWQ==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.15.1"
"Speckle.Sdk": "3.15.3"
}
},
"Speckle.Revit.API": {
@@ -1,4 +1,5 @@
using Autodesk.Revit.DB;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Utils;
@@ -34,6 +35,7 @@ internal sealed class RevitParametersBinding : IParametersBinding
private readonly ParameterUpdater _parameterUpdater;
private readonly IJsonSerializer _jsonSerializer;
private readonly IBasicConnectorBinding _baseBinding;
private readonly ILogger<RevitParametersBinding> _logger;
public RevitParametersBinding(
IBrowserBridge parent,
@@ -42,7 +44,8 @@ internal sealed class RevitParametersBinding : IParametersBinding
IRevitTask revitTask,
ParameterUpdater parameterUpdater,
IJsonSerializer jsonSerializer,
IBasicConnectorBinding baseBinding
IBasicConnectorBinding baseBinding,
ILogger<RevitParametersBinding> logger
)
{
Parent = parent;
@@ -52,6 +55,7 @@ internal sealed class RevitParametersBinding : IParametersBinding
_parameterUpdater = parameterUpdater;
_jsonSerializer = jsonSerializer;
_baseBinding = baseBinding;
_logger = logger;
}
public async Task Update(string payload)
@@ -123,19 +127,36 @@ internal sealed class RevitParametersBinding : IParametersBinding
if (errors.Count > 0)
{
var groupedErrors = errors.GroupBy(e => e).Select(g => g.Count() > 1 ? $"{g.Count()} x {g.Key}" : g.Key);
await _baseBinding.Commands.SetGlobalNotification(
ToastNotificationType.WARNING,
"Update Completed with Issues",
$"Applied {successCount} updates. Encountered {errors.Count} errors: {string.Join(" | ", groupedErrors)}",
autoClose: false
);
var groupedErrors = errors.GroupBy(e => e).Select(g => $"{g.Count()} x {g.Key}");
var errorString = string.Join(", ", groupedErrors);
if (successCount > 0)
{
// Partial Success (Some worked, some failed)
await _baseBinding.Commands.SetGlobalNotification(
ToastNotificationType.WARNING,
"Parameters updated with errors",
$"Applied {successCount} updates. Encountered {errors.Count} errors: {errorString}",
autoClose: false
);
}
else
{
// Total Failure (None worked)
await _baseBinding.Commands.SetGlobalNotification(
ToastNotificationType.DANGER,
"No parameters updated",
$"All {errors.Count} updates failed: {errorString}",
autoClose: false
);
}
}
else
else if (successCount > 0)
{
// Total Success
await _baseBinding.Commands.SetGlobalNotification(
ToastNotificationType.SUCCESS,
"Parameters Updated",
"All parameters updated",
$"Successfully applied {successCount} updates."
);
}
@@ -162,28 +183,35 @@ internal sealed class RevitParametersBinding : IParametersBinding
if (string.IsNullOrEmpty(request.ApplicationId))
{
errorMessage = "Missing ApplicationId.";
errorMessage = "Missing ApplicationId";
return false;
}
if (ContainsLinkedModelTransformHash(request.ApplicationId))
{
errorMessage = "Cannot modify elements from a linked model";
return false;
}
var elementId = ElementIdHelper.GetElementIdFromUniqueId(doc, request.ApplicationId);
if (elementId == null)
{
errorMessage = $"Element UniqueId not found: {request.ApplicationId}";
errorMessage = "Element(s) not found in document";
return false;
}
element = doc.GetElement(elementId);
if (element == null)
{
errorMessage = $"Element is null for Id: {elementId}";
errorMessage = "Element(s) not found in document";
return false;
}
var rawPath = request.Path;
if (string.IsNullOrEmpty(rawPath))
{
errorMessage = "Path is missing.";
_logger.LogError("Widget / DUI payload error: parameter path missing");
errorMessage = "Parameter path is missing";
return false;
}
@@ -200,13 +228,21 @@ internal sealed class RevitParametersBinding : IParametersBinding
var pathParts = rawPath.Split(['.'], 3);
if (pathParts.Length != 3)
{
errorMessage = $"Path must have 3 parts. Got: '{rawPath}'";
_logger.LogError(
"Path format error: Expected exactly 3 parts (Scope.Category.Name) but received '{RawPath}' for element",
rawPath
);
errorMessage = "Parameter path is incorrectly formatted";
return false;
}
parsedPath = new ParsedParameterPath(pathParts[0], pathParts[1], pathParts[2]);
return true;
}
private static bool ContainsLinkedModelTransformHash(string applicationId) =>
// Evaluates if the ID contains the standard transform hash for linked elements
System.Text.RegularExpressions.Regex.IsMatch(applicationId, @"_t[a-f0-9]+$");
}
public class ParameterChangesWrapper
@@ -65,6 +65,7 @@ public static class ServiceRegistration
serviceCollection.AddScoped<ViewUnpacker>();
serviceCollection.AddScoped<SendCollectionManager>();
serviceCollection.AddScoped<IRootObjectBuilder<DocumentToConvert>, RevitRootObjectBuilder>();
serviceCollection.AddScoped<IRootContinuousTraversalBuilder<DocumentToConvert>, RevitContinuousTraversalBuilder>();
serviceCollection.AddSingleton<ISendConversionCache, SendConversionCache>();
serviceCollection.AddSingleton<ToSpeckleSettingsManager>();
serviceCollection.AddSingleton<ToHostSettingsManager>();
@@ -4,7 +4,6 @@ using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Structure;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Common.Conversion;
using Speckle.Connectors.Common.Operations;
using Speckle.Converters.Common;
using Speckle.Converters.Common.Objects;
using Speckle.Converters.RevitShared.Helpers;
@@ -18,6 +17,7 @@ using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Models.GraphTraversal;
using Speckle.Sdk.Models.Instances;
using Speckle.Sdk.Pipelines.Progress;
using DB = Autodesk.Revit.DB;
using Document = Autodesk.Revit.DB.Document;
@@ -24,6 +24,7 @@ using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Models.GraphTraversal;
using Speckle.Sdk.Models.Instances;
using Speckle.Sdk.Pipelines.Progress;
namespace Speckle.Connectors.Revit.Operations.Receive;
@@ -75,7 +75,7 @@ public class ToHostSettingsManager : IToHostSettingsManager
if (_revitContext.UIApplication is UIApplication uiApplication)
{
// first get the main doc base points and reference setting transform
// first get the main doc base points
using FilteredElementCollector filteredElementCollector = new(uiApplication.ActiveUIDocument.Document);
var points = filteredElementCollector.OfClass(typeof(BasePoint)).Cast<BasePoint>().ToList();
BasePoint? projectPoint = points.FirstOrDefault(o => !o.IsShared);
@@ -95,26 +95,23 @@ public class ToHostSettingsManager : IToHostSettingsManager
}
break;
// note that the project base (ui) rotation is registered on the survey pt, not on the base point
case ReceiveReferencePointType.Survey:
if (surveyPoint is not null && projectPoint is not null)
if (surveyPoint is not null)
{
// POC: should a null angle resolve to 0?
// retrieve the survey point rotation from the project point
var angle = projectPoint.get_Parameter(BuiltInParameter.BASEPOINT_ANGLETON_PARAM)?.AsDouble() ?? 0;
// POC: following disposed incorrectly or early or maybe a false negative?
ProjectPosition projectPosition =
uiApplication.ActiveUIDocument.Document.ActiveProjectLocation.GetProjectPosition(XYZ.Zero);
double angleToTrueNorth = projectPosition.Angle;
using Transform translation = Transform.CreateTranslation(surveyPoint.Position);
referencePointTransform = translation.Multiply(Transform.CreateRotation(XYZ.BasisZ, angle));
using Transform rotation = Transform.CreateRotation(XYZ.BasisZ, angleToTrueNorth);
referencePointTransform = translation.Multiply(rotation);
}
else
{
throw new InvalidOperationException("Couldn't retrieve Survey and Project Point from document");
throw new InvalidOperationException("Couldn't retrieve Survey Point from document");
}
break;
case ReceiveReferencePointType.Source:
break;
case ReceiveReferencePointType.InternalOrigin:
break;
}
@@ -0,0 +1,318 @@
using System.Diagnostics.CodeAnalysis;
using Autodesk.Revit.DB;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Common.Builders;
using Speckle.Connectors.Common.Caching;
using Speckle.Connectors.Common.Conversion;
using Speckle.Connectors.Common.Extensions;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.DUI.Exceptions;
using Speckle.Connectors.DUI.Settings;
using Speckle.Connectors.Revit.HostApp;
using Speckle.Converters.Common;
using Speckle.Converters.RevitShared.Helpers;
using Speckle.Converters.RevitShared.Settings;
using Speckle.Sdk;
using Speckle.Sdk.Common;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Pipelines.Progress;
using Speckle.Sdk.Pipelines.Send;
namespace Speckle.Connectors.Revit.Operations.Send;
public class RevitContinuousTraversalBuilder(
IRootToSpeckleConverter converter,
IConverterSettingsStore<RevitConversionSettings> converterSettings,
ISendConversionCache sendConversionCache,
ElementUnpacker elementUnpacker,
LevelUnpacker levelUnpacker,
ViewUnpacker viewUnpacker,
IThreadContext threadContext,
SendCollectionManager sendCollectionManager,
ILogger<RevitRootObjectBuilder> logger,
RevitToSpeckleCacheSingleton revitToSpeckleCacheSingleton,
LinkedModelHandler linkedModelHandler,
IConfigStore configStore
) : IRootContinuousTraversalBuilder<DocumentToConvert>
{
public async Task<RootObjectBuilderResult> Build(
IReadOnlyList<DocumentToConvert> documentElementContexts,
string projectId,
SendPipeline sendPipeline,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
{
return await threadContext.RunOnMainAsync(
async () =>
await BuildMainThread(
documentElementContexts,
projectId,
sendPipeline,
onOperationProgressed,
cancellationToken
)
);
}
[SuppressMessage("Maintainability", "CA1502:Avoid excessive class coupling")]
[SuppressMessage("Maintainability", "CA1506:Avoid excessive class coupling")]
private async Task<RootObjectBuilderResult> BuildMainThread(
IReadOnlyList<DocumentToConvert> documentElementContexts,
string projectId,
SendPipeline sendPipeline,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
{
var doc = converterSettings.Current.Document;
if (doc.IsFamilyDocument)
{
throw new SpeckleException("Family Environment documents are not supported.");
}
// init the root
Collection rootObject =
new() { name = converterSettings.Current.Document.PathName.Split('\\').Last().Split('.').First() };
rootObject["units"] = converterSettings.Current.SpeckleUnits;
var filteredDocumentsToConvert = new List<DocumentToConvert>();
bool sendWithLinkedModels = converterSettings.Current.SendLinkedModels;
List<SendConversionResult> results = new();
// Prepare linked model display names if needed
if (sendWithLinkedModels)
{
linkedModelHandler.PrepareLinkedModelNames(documentElementContexts);
}
foreach (var documentElementContext in documentElementContexts)
{
// add appropriate warnings for linked documents
if (documentElementContext.Doc.IsLinked && !sendWithLinkedModels)
{
results.Add(
new(
Status.WARNING,
documentElementContext.Doc.PathName,
typeof(RevitLinkInstance).ToString(),
null,
new SpeckleException("Enable linked model support from the settings to send this object")
)
);
continue;
}
// filter for valid elements
// if send linked models setting is disabled List<Elements> will be empty, and we won't enter foreach loop
var elementsInTransform = new List<Element>();
foreach (var el in documentElementContext.Elements)
{
if (el == null || el.Category == null)
{
continue;
}
elementsInTransform.Add(el);
}
// only add contexts with elements
if (elementsInTransform.Count > 0)
{
filteredDocumentsToConvert.Add(documentElementContext with { Elements = elementsInTransform });
}
}
// TODO: check the exception!!!!
if (filteredDocumentsToConvert.Count == 0)
{
throw new SpeckleSendFilterException("No objects were found. Please update your publish filter!");
}
// Unpack groups (& other complex data structures)
var atomicObjectsByDocumentAndTransform = new List<DocumentToConvert>();
var atomicObjectCount = 0;
foreach (var filteredDocumentToConvert in filteredDocumentsToConvert)
{
using (
converterSettings.Push(currentSettings => currentSettings with { Document = filteredDocumentToConvert.Doc })
)
{
var atomicObjects = elementUnpacker
.UnpackSelectionForConversion(filteredDocumentToConvert.Elements, filteredDocumentToConvert.Doc)
.ToList();
atomicObjectsByDocumentAndTransform.Add(filteredDocumentToConvert with { Elements = atomicObjects });
atomicObjectCount += atomicObjects.Count;
}
}
var count = 0;
var cacheHitCount = 0;
var skippedObjectCount = 0;
var config = configStore.GetConnectorConfig();
foreach (var atomicObjectByDocumentAndTransform in atomicObjectsByDocumentAndTransform)
{
string? modelDisplayName = null;
if (atomicObjectByDocumentAndTransform.Doc.IsLinked)
{
string id = linkedModelHandler.GetIdFromDocumentToConvert(atomicObjectByDocumentAndTransform);
linkedModelHandler.LinkedModelDisplayNames.TryGetValue(id, out modelDisplayName);
}
// here we do magic for changing the transform and the related document according to model. first one is always the main model.
using (
converterSettings.Push(currentSettings =>
currentSettings with
{
ReferencePointTransform = atomicObjectByDocumentAndTransform.Transform,
Document = atomicObjectByDocumentAndTransform.Doc,
}
)
)
{
var atomicObjects = atomicObjectByDocumentAndTransform.Elements;
foreach (Element revitElement in atomicObjects)
{
cancellationToken.ThrowIfCancellationRequested();
string applicationId = revitElement.UniqueId;
string sourceType = revitElement.GetType().Name;
try
{
if (!SupportedCategoriesUtils.IsSupportedCategory(revitElement.Category))
{
var cat = revitElement.Category != null ? revitElement.Category.Name : "No category";
results.Add(
new(
Status.WARNING,
revitElement.UniqueId,
cat,
null,
new SpeckleException($"Category {cat} is not supported.")
)
);
skippedObjectCount++;
continue;
}
Base converted;
bool hasTransform = atomicObjectByDocumentAndTransform.Transform != null;
// non-transformed elements can safely rely on cache
// TODO: Potential here to transform cached objects and NOT reconvert,
// TODO: we wont do !hasTransform here, and re-set application id before this
if (
!hasTransform
&& !config.DocumentChangeListeningDisabled //This is experimental
&& sendConversionCache.TryGetValue(projectId, applicationId, out ObjectReference? value)
)
{
converted = value;
cacheHitCount++;
}
// not in cache means we convert
else
{
// if it has a transform we append transform hash to the applicationId to distinguish the elements from other instances
if (hasTransform)
{
string transformHash = linkedModelHandler.GetTransformHash(
atomicObjectByDocumentAndTransform.Transform.NotNull()
);
applicationId = $"{applicationId}_t{transformHash}";
}
// normal conversions
converted = converter.Convert(revitElement);
converted.applicationId = applicationId;
}
// NOTE: this is the main part that differentiate from the main root object builder
var reference = await sendPipeline.Process(converted).ConfigureAwait(true);
var collection = sendCollectionManager.GetAndCreateObjectHostCollection(
revitElement,
rootObject,
sendWithLinkedModels,
modelDisplayName
);
collection.elements.Add(reference);
results.Add(new(Status.SUCCESS, applicationId, sourceType, reference));
}
catch (Exception ex) when (!ex.IsFatal())
{
logger.LogSendConversionError(ex, sourceType);
results.Add(new(Status.ERROR, applicationId, sourceType, null, ex));
}
count++;
onOperationProgressed.Report(
new($"Converting objects... ({count:N0} / {atomicObjectCount:N0})", (double)count / atomicObjectCount)
);
}
}
}
// if we ended up skipping everything, there is a reason for this, that users can diagnose themselves
// this can occur if a published view contains only unsupported objects or if user trying to ONLY send linked model
// docs but the setting is disabled
if (skippedObjectCount == atomicObjectCount)
{
throw new SpeckleException("No supported objects visible. Update publish filter or check publish settings.");
}
// this is, I suppose, fully on us?
if (results.All(x => x.Status == Status.ERROR))
{
throw new SpeckleException("Failed to convert all objects.");
}
// STEP 5: Unpack proxies to attach to root collection
var flatElements = atomicObjectsByDocumentAndTransform.SelectMany(t => t.Elements).ToList();
var idsAndSubElementIds = elementUnpacker.GetElementsAndSubelementIdsFromAtomicObjects(flatElements);
var renderMaterialProxies = revitToSpeckleCacheSingleton.GetRenderMaterialProxyListForObjects(idsAndSubElementIds);
rootObject[ProxyKeys.RENDER_MATERIAL] = renderMaterialProxies;
var levelProxies = levelUnpacker.Unpack(flatElements);
rootObject[ProxyKeys.LEVEL] = levelProxies;
rootObject[ProxyKeys.INSTANCE_DEFINITION] = revitToSpeckleCacheSingleton.GetInstanceDefinitionProxiesForObjects(
idsAndSubElementIds
);
rootObject.elements.Add(
new Collection()
{
elements = revitToSpeckleCacheSingleton.GetBaseObjectsForObjects(idsAndSubElementIds),
name = "definitionGeometry"
}
);
// STEP 6: Unpack all other objects to attach to root collection
List<Objects.Other.Camera> views = viewUnpacker.Unpack(converterSettings.Current.Document);
if (views.Count > 0)
{
rootObject[RootKeys.VIEW] = views;
}
// NOTE: these are currently not used anywhere, we'll skip them until someone calls for it back
// rootObject[ProxyKeys.PARAMETER_DEFINITIONS] = _parameterDefinitionHandler.Definitions;
// we want to store transform data for chosen reference point setting
if (converterSettings.Current.ReferencePointTransform is Transform transform)
{
var transformMatrix = ReferencePointHelper.CreateTransformDataForRootObject(transform);
rootObject[RootKeys.REFERENCE_POINT_TRANSFORM] = transformMatrix;
}
await sendPipeline.Process(rootObject);
await sendPipeline.WaitForUpload();
return new RootObjectBuilderResult(rootObject, results);
}
}
@@ -17,6 +17,7 @@ using Speckle.Sdk;
using Speckle.Sdk.Common;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Pipelines.Progress;
namespace Speckle.Connectors.Revit.Operations.Send;
@@ -39,7 +40,7 @@ public class RevitRootObjectBuilder(
IReadOnlyList<DocumentToConvert> documentElementContexts,
string projectId,
IProgress<CardProgress> onOperationProgressed,
CancellationToken ct = default
CancellationToken ct
) =>
threadContext.RunOnMainAsync(
() => Task.FromResult(BuildSync(documentElementContexts, projectId, onOperationProgressed, ct))
@@ -213,21 +213,18 @@ public class ToSpeckleSettingsManager(
}
break;
// note that the project base (ui) rotation is registered on the survey pt, not on the base point
case ReferencePointType.Survey:
if (surveyPoint is not null && projectPoint is not null)
if (surveyPoint is not null)
{
// POC: should a null angle resolve to 0?
// retrieve the survey point rotation from the project point
var angle = projectPoint.get_Parameter(BuiltInParameter.BASEPOINT_ANGLETON_PARAM)?.AsDouble() ?? 0;
// POC: following disposed incorrectly or early or maybe a false negative?
ProjectPosition projectPosition = document.ActiveProjectLocation.GetProjectPosition(XYZ.Zero);
double angleToTrueNorth = projectPosition.Angle;
using Transform translation = Transform.CreateTranslation(surveyPoint.Position);
referencePointTransform = translation.Multiply(Transform.CreateRotation(XYZ.BasisZ, angle));
using Transform rotation = Transform.CreateRotation(XYZ.BasisZ, angleToTrueNorth);
referencePointTransform = translation.Multiply(rotation);
}
else
{
throw new InvalidOperationException("Couldn't retrieve Survey and Project Point from document");
throw new InvalidOperationException("Couldn't retrieve Survey Point from document");
}
break;
@@ -60,6 +60,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Filters\RevitCategoriesFilter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Filters\RevitSelectionFilter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Filters\RevitViewsFilter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\RevitContinuousTraversalBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\RevitRootObjectBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\LinkedModelsSetting.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\SendParameterNullOrEmptyStringsSetting.cs" />
@@ -204,8 +204,8 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "Gt1qAZrMflLMOjDy4cQzJX5pdq5WokphYdsry7pMBlRHRdx9imQhJUPCg528Y2iVSMUnRKvj3A3cHtBeUxysEQ==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.CSharp": "4.7.0",
@@ -214,13 +214,13 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.15.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "OUOnt4Vnd1J838LTWEpP0ge8WIwjhx8O94kuTbC6AJRfOx9Lw78SG6XtVCE+WXb9zC5h3tzOV9YOng8Ke5yXMg==",
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
@@ -349,7 +349,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.logging": {
@@ -359,7 +359,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.converters.rhino7": {
@@ -404,11 +404,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.15.1, )",
"resolved": "3.15.1",
"contentHash": "vFIf01jNVD69N6wE5iYVM7TKA6AbSTZxq1EedJCVZxhj5pkhHBw+23fyO+wz7Cd9ggqsiBUJMuKxUBHaDphGWQ==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.15.1"
"Speckle.Sdk": "3.15.3"
}
}
}
@@ -204,8 +204,8 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "Gt1qAZrMflLMOjDy4cQzJX5pdq5WokphYdsry7pMBlRHRdx9imQhJUPCg528Y2iVSMUnRKvj3A3cHtBeUxysEQ==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.CSharp": "4.7.0",
@@ -214,13 +214,13 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.15.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "OUOnt4Vnd1J838LTWEpP0ge8WIwjhx8O94kuTbC6AJRfOx9Lw78SG6XtVCE+WXb9zC5h3tzOV9YOng8Ke5yXMg==",
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
@@ -349,7 +349,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.logging": {
@@ -359,7 +359,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.converters.rhino8": {
@@ -403,11 +403,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.15.1, )",
"resolved": "3.15.1",
"contentHash": "vFIf01jNVD69N6wE5iYVM7TKA6AbSTZxq1EedJCVZxhj5pkhHBw+23fyO+wz7Cd9ggqsiBUJMuKxUBHaDphGWQ==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.15.1"
"Speckle.Sdk": "3.15.3"
}
}
}
@@ -8,7 +8,6 @@ using GrasshopperAsyncComponent;
using Rhino;
using Speckle.Connectors.Common;
using Speckle.Connectors.Common.Analytics;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Common.Operations.Receive;
using Speckle.Connectors.GrasshopperShared.HostApp;
using Speckle.Connectors.GrasshopperShared.Operations.Receive;
@@ -22,6 +21,7 @@ using Speckle.Sdk.Credentials;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Models.Extensions;
using Speckle.Sdk.Pipelines.Progress;
namespace Speckle.Connectors.GrasshopperShared.Components.Operations.Receive;
@@ -3,7 +3,6 @@ using Microsoft.Extensions.DependencyInjection;
using Rhino;
using Speckle.Connectors.Common;
using Speckle.Connectors.Common.Analytics;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Common.Operations.Receive;
using Speckle.Connectors.GrasshopperShared.Components.BaseComponents;
using Speckle.Connectors.GrasshopperShared.HostApp;
@@ -16,6 +15,7 @@ using Speckle.Sdk.Api;
using Speckle.Sdk.Api.GraphQL.Models;
using Speckle.Sdk.Credentials;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Pipelines.Progress;
namespace Speckle.Connectors.GrasshopperShared.Components.Operations.Receive;
@@ -0,0 +1,56 @@
using Speckle.Sdk;
using Speckle.Sdk.Api;
using Speckle.Sdk.Api.GraphQL.Enums;
using Speckle.Sdk.Common;
namespace Speckle.Connectors.GrasshopperShared.Components.Operations.Send;
/// <summary>
/// Polls ingestion status via the SDK's GraphQL query API
/// and blocks until the ingestion reaches a terminal state (success/failed/cancelled).
/// </summary>
/// <remarks>
/// We use polling instead of subscriptions because GH components call WaitForIngestionCompletion
/// after SendViaPackfile returns — by that point the server may have already completed
/// the ingestion. Setting up a new WebSocket subscription is too slow to catch fast completions.
/// Polling with Ingestion.Get() is reliable regardless of timing.
/// </remarks>
public class IngestionTracker
{
private static readonly TimeSpan s_pollInterval = TimeSpan.FromSeconds(1);
public async Task<string> WaitForIngestionCompletion(
IClient client,
string projectId,
string ingestionId,
Action<string, double>? reportProgress,
string? reportProgressId,
CancellationToken cancellationToken
)
{
// NOTE: before start hating from this - read the class description
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
var ingestion = await client.Ingestion.Get(ingestionId, projectId, cancellationToken).ConfigureAwait(false);
var status = ingestion.statusData.status;
switch (status)
{
case ModelIngestionStatus.success:
return ingestion.statusData.versionId.NotNull();
case ModelIngestionStatus.failed:
throw new SpeckleException($"Server processing failed: {ingestion.statusData.progressMessage}");
case ModelIngestionStatus.cancelled:
throw new OperationCanceledException("Ingestion was cancelled by the server");
case ModelIngestionStatus.processing:
case ModelIngestionStatus.queued:
reportProgress?.Invoke(reportProgressId ?? "Server", 0);
break;
}
await Task.Delay(s_pollInterval, cancellationToken).ConfigureAwait(false);
}
}
}
@@ -20,6 +20,7 @@ using Speckle.Sdk.Api;
using Speckle.Sdk.Common;
using Speckle.Sdk.Credentials;
using Speckle.Sdk.Models.Extensions;
using Speckle.Sdk.Pipelines.Progress;
namespace Speckle.Connectors.GrasshopperShared.Components.Operations.Send;
@@ -503,10 +504,35 @@ public class SendComponentWorker : WorkerInstance<SendAsyncComponent>
});
using var scope = PriorityLoader.CreateScopeForActiveDocument();
var sendOperation = scope.ServiceProvider.GetRequiredService<SendOperation<SpeckleCollectionWrapperGoo>>();
(SendOperationResult result, string versionId) = await sendOperation
.Send([rootCollectionWrapper], sendInfo, fileName, fileBytes, Parent.VersionMessage, progress, CancellationToken)
(SendOperationResult result, string versionId, string? ingestionId) = await sendOperation
.Send(
[rootCollectionWrapper],
sendInfo,
fileName,
fileBytes,
Parent.VersionMessage,
progress,
true,
CancellationToken
)
.ConfigureAwait(false);
if (ingestionId != null)
{
Parent.Message = "Remote processing";
var ingestionTracker = scope.ServiceProvider.GetRequiredService<IngestionTracker>();
versionId = await ingestionTracker
.WaitForIngestionCompletion(
Parent.ApiClient,
sendInfo.ProjectId,
ingestionId,
reportProgress,
Id,
CancellationToken
)
.ConfigureAwait(false);
}
// TODO: If we have NodeRun events later, better to have `ComponentTracker` to use across components
var customProperties = new Dictionary<string, object>() { { "isAsync", true }, { "auto", Parent.AutoSend } };
if (sendInfo.WorkspaceId != null)
@@ -15,6 +15,7 @@ using Speckle.Sdk.Api;
using Speckle.Sdk.Common;
using Speckle.Sdk.Credentials;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Pipelines.Progress;
namespace Speckle.Connectors.GrasshopperShared.Components.Operations.Send;
@@ -281,10 +282,26 @@ public class SendComponent : SpeckleTaskCapableComponent<SendComponentInput, Sen
using var client = clientFactory.Create(account);
var sendInfo = await input.Resource.GetSendInfo(client, cancellationToken).ConfigureAwait(false);
var (result, versionId) = await sendOperation
.Send([collectionToSend], sendInfo, fileName, fileBytes, VersionMessage, progress, cancellationToken)
var (result, versionId, ingestionId) = await sendOperation
.Send([collectionToSend], sendInfo, fileName, fileBytes, VersionMessage, progress, true, cancellationToken)
.ConfigureAwait(false);
if (ingestionId != null)
{
Message = "Remote processing";
var ingestionTracker = scope.ServiceProvider.GetRequiredService<IngestionTracker>();
versionId = await ingestionTracker
.WaitForIngestionCompletion(
client,
sendInfo.ProjectId,
ingestionId,
reportProgress: null,
reportProgressId: null,
cancellationToken
)
.ConfigureAwait(false);
}
// TODO: If we have NodeRun events later, better to have `ComponentTracker` to use across components
var customProperties = new Dictionary<string, object> { { "isAsync", false } };
if (sendInfo.WorkspaceId != null)
@@ -295,12 +312,13 @@ public class SendComponent : SpeckleTaskCapableComponent<SendComponentInput, Sen
var mixpanel = PriorityLoader.Container.GetRequiredService<IMixPanelManager>();
await mixpanel.TrackEvent(MixPanelEvents.Send, account, customProperties);
SpeckleUrlLatestModelVersionResource createdVersionResource =
SpeckleUrlModelVersionResource createdVersionResource =
new(
new(sendInfo.Account.id, null, sendInfo.Account.serverInfo.url),
sendInfo.WorkspaceId,
sendInfo.ProjectId,
sendInfo.ModelId
sendInfo.ModelId,
versionId
);
Url = $"{sendInfo.Account.serverInfo.url}/projects/{sendInfo.ProjectId}/models/{sendInfo.ModelId}";
return new SendComponentOutput(createdVersionResource, versionId);
@@ -3,6 +3,7 @@ using Speckle.Connectors.Common.Operations;
using Speckle.Sdk.Api;
using Speckle.Sdk.Logging;
using Speckle.Sdk.Models;
using Speckle.Sdk.Pipelines.Progress;
using Speckle.Sdk.Transports;
namespace Speckle.Connectors.GrasshopperShared.Operations.Receive;
@@ -0,0 +1,221 @@
using Speckle.Connectors.Common.Builders;
using Speckle.Connectors.Common.Instances;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.GrasshopperShared.HostApp;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Pipelines.Progress;
using Speckle.Sdk.Pipelines.Send;
using DataObject = Speckle.Objects.Data.DataObject;
namespace Speckle.Connectors.GrasshopperShared.Operations.Send;
/// <summary>
/// Continuous traversal builder for Grasshopper that processes each object through the <see cref="SendPipeline"/>
/// as it unwraps. This enables the packfile send path (streaming objects to S3 during build).
/// </summary>
public class GrasshopperContinuousTraversalBuilder(
IInstanceObjectsManager<SpeckleGeometryWrapper, List<string>> instanceObjectsManager
) : IRootContinuousTraversalBuilder<SpeckleCollectionWrapperGoo>
{
public async Task<RootObjectBuilderResult> Build(
IReadOnlyList<SpeckleCollectionWrapperGoo> objects,
string projectId,
SendPipeline sendPipeline,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
{
// create root collection
var rootCollectionGoo = (SpeckleRootCollectionWrapperGoo)objects[0].Duplicate();
rootCollectionGoo.Value.Name = "Grasshopper Model";
RootCollection rootCollection =
new(rootCollectionGoo.Value.Name)
{
applicationId = rootCollectionGoo.Value.ApplicationId,
properties = rootCollectionGoo.Value.Properties ?? new()
};
// create packers for colors and render materials
GrasshopperColorPacker colorPacker = new();
GrasshopperMaterialPacker materialPacker = new();
GrasshopperBlockPacker blockPacker = new(instanceObjectsManager);
// unwrap the input collection, processing each object through the send pipeline
await Unwrap(
rootCollectionGoo.Value,
rootCollection,
colorPacker,
materialPacker,
blockPacker,
sendPipeline,
cancellationToken
)
.ConfigureAwait(false);
// add proxies
rootCollection[ProxyKeys.COLOR] = colorPacker.ColorProxies.Values.ToList();
rootCollection[ProxyKeys.RENDER_MATERIAL] = materialPacker.RenderMaterialProxies.Values.ToList();
rootCollection[ProxyKeys.INSTANCE_DEFINITION] = blockPacker.InstanceDefinitionProxies.Values.ToList();
// process the root collection through the pipeline and wait for all uploads
await sendPipeline.Process(rootCollection).ConfigureAwait(false);
await sendPipeline.WaitForUpload().ConfigureAwait(false);
// TODO: Not getting any conversion results yet
return new RootObjectBuilderResult(rootCollection, []);
}
private async Task Unwrap(
SpeckleCollectionWrapper wrapper,
Collection targetCollection,
GrasshopperColorPacker colorPacker,
GrasshopperMaterialPacker materialPacker,
GrasshopperBlockPacker blockPacker,
SendPipeline sendPipeline,
CancellationToken cancellationToken
)
{
colorPacker.ProcessColor(wrapper.ApplicationId, wrapper.Color);
materialPacker.ProcessMaterial(wrapper.ApplicationId, wrapper.Material);
int skippedNulls = 0;
foreach (ISpeckleCollectionObject? element in wrapper.Elements)
{
cancellationToken.ThrowIfCancellationRequested();
switch (element)
{
case null:
skippedNulls++;
continue;
case SpeckleCollectionWrapper collWrapper:
collWrapper.ApplicationId ??= collWrapper.GetSpeckleApplicationId();
targetCollection.elements.Add(collWrapper.Collection);
await Unwrap(
collWrapper,
collWrapper.Collection,
colorPacker,
materialPacker,
blockPacker,
sendPipeline,
cancellationToken
)
.ConfigureAwait(false);
break;
case SpeckleGeometryWrapper so:
Base objectBase = UnwrapGeometry(so);
string applicationId = objectBase.applicationId!;
// NOTE: This is how it differentiate from 'GrasshopperSendOperation'
// It process through send pipeline before adding to collection
var reference = await sendPipeline.Process(objectBase).ConfigureAwait(false);
targetCollection.elements.Add(reference);
if (so is SpeckleBlockInstanceWrapper blockInstance)
{
await ProcessBlockInstanceDefinition(
blockInstance,
colorPacker,
materialPacker,
blockPacker,
targetCollection,
sendPipeline,
cancellationToken
)
.ConfigureAwait(false);
}
colorPacker.ProcessColor(applicationId, so.Color);
materialPacker.ProcessMaterial(applicationId, so.Material);
break;
case SpeckleDataObjectWrapper dataObjectWrapper:
DataObject dataObject = UnwrapDataObject(dataObjectWrapper, colorPacker, materialPacker);
// process data object through send pipeline
var dataRef = await sendPipeline.Process(dataObject).ConfigureAwait(false);
targetCollection.elements.Add(dataRef);
break;
}
}
// clear topology when nulls are present (CNX-2855)
if (skippedNulls > 0)
{
targetCollection[Constants.TOPOLOGY_PROP] = null;
}
}
private Base UnwrapGeometry(SpeckleGeometryWrapper wrapper)
{
Dictionary<string, object?> props = [];
Base baseObject = wrapper.Base;
if (wrapper.Properties.CastTo(ref props))
{
baseObject["properties"] = props;
}
return baseObject;
}
private async Task ProcessBlockInstanceDefinition(
SpeckleBlockInstanceWrapper blockInstance,
GrasshopperColorPacker colorPacker,
GrasshopperMaterialPacker materialPacker,
GrasshopperBlockPacker blockPacker,
Collection currentColl,
SendPipeline sendPipeline,
CancellationToken cancellationToken
)
{
cancellationToken.ThrowIfCancellationRequested();
var definitionObjects = blockPacker.ProcessInstance(blockInstance);
if (definitionObjects != null)
{
foreach (var definitionObject in definitionObjects)
{
Base defObjectBase = UnwrapGeometry(definitionObject);
string applicationId = defObjectBase.applicationId!;
var reference = await sendPipeline.Process(defObjectBase).ConfigureAwait(false);
currentColl.elements.Add(reference);
colorPacker.ProcessColor(applicationId, definitionObject.Color);
materialPacker.ProcessMaterial(applicationId, definitionObject.Material);
}
}
}
private DataObject UnwrapDataObject(
SpeckleDataObjectWrapper wrapper,
GrasshopperColorPacker colorPacker,
GrasshopperMaterialPacker materialPacker
)
{
DataObject dataObject = wrapper.DataObject;
var displayValue = new List<Base>();
foreach (var geometryWrapper in wrapper.Geometries)
{
Base geometryBase = UnwrapGeometry(geometryWrapper);
displayValue.Add(geometryBase);
if (geometryWrapper.ApplicationId != null)
{
colorPacker.ProcessColor(geometryWrapper.ApplicationId, geometryWrapper.Color);
materialPacker.ProcessMaterial(geometryWrapper.ApplicationId, geometryWrapper.Material);
}
}
dataObject.displayValue = displayValue;
return dataObject;
}
}
@@ -5,6 +5,7 @@ using Speckle.Connectors.GrasshopperShared.HostApp;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Pipelines.Progress;
using DataObject = Speckle.Objects.Data.DataObject;
namespace Speckle.Connectors.GrasshopperShared.Operations.Send;
@@ -9,6 +9,7 @@ using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Common.Operations.Receive;
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.GrasshopperShared.Components;
using Speckle.Connectors.GrasshopperShared.Components.Operations.Send;
using Speckle.Connectors.GrasshopperShared.HostApp;
using Speckle.Connectors.GrasshopperShared.Operations.Receive;
using Speckle.Connectors.GrasshopperShared.Operations.Send;
@@ -61,9 +62,14 @@ public class PriorityLoader : GH_AssemblyPriority
services.AddTransient<TraversalContextUnpacker>();
services.AddScoped<IDataObjectInstanceRegistry, DataObjectInstanceRegistry>();
services.AddTransient<LocalToGlobalMapHandler>();
services.AddTransient<IngestionTracker>();
// send
services.AddTransient<IRootObjectBuilder<SpeckleCollectionWrapperGoo>, GrasshopperRootObjectBuilder>();
services.AddTransient<
IRootContinuousTraversalBuilder<SpeckleCollectionWrapperGoo>,
GrasshopperContinuousTraversalBuilder
>();
services.AddTransient<SendOperation<SpeckleCollectionWrapperGoo>>();
services.AddSingleton<IThreadContext>(new DefaultThreadContext());
services.AddScoped<
@@ -58,7 +58,9 @@
<Compile Include="$(MSBuildThisFileDirectory)Components\Operations\Receive\ReceiveComponent.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\GrasshopperBlockPacker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\GrasshopperMaterialPacker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\GrasshopperContinuousTraversalBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\GrasshopperColorPacker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Components\Operations\Send\IngestionTracker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Components\Operations\Send\SendAsyncComponent.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Components\Operations\Send\SendComponent.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Components\Operations\SpeckleSelectModelComponent.cs" />
@@ -185,8 +185,8 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "Gt1qAZrMflLMOjDy4cQzJX5pdq5WokphYdsry7pMBlRHRdx9imQhJUPCg528Y2iVSMUnRKvj3A3cHtBeUxysEQ==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.CSharp": "4.7.0",
@@ -195,13 +195,13 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.15.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "OUOnt4Vnd1J838LTWEpP0ge8WIwjhx8O94kuTbC6AJRfOx9Lw78SG6XtVCE+WXb9zC5h3tzOV9YOng8Ke5yXMg==",
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
@@ -330,7 +330,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -363,7 +363,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.converters.rhino7": {
@@ -423,11 +423,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.15.1, )",
"resolved": "3.15.1",
"contentHash": "vFIf01jNVD69N6wE5iYVM7TKA6AbSTZxq1EedJCVZxhj5pkhHBw+23fyO+wz7Cd9ggqsiBUJMuKxUBHaDphGWQ==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.15.1"
"Speckle.Sdk": "3.15.3"
}
},
"System.Resources.Extensions": {
@@ -185,8 +185,8 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "Gt1qAZrMflLMOjDy4cQzJX5pdq5WokphYdsry7pMBlRHRdx9imQhJUPCg528Y2iVSMUnRKvj3A3cHtBeUxysEQ==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.CSharp": "4.7.0",
@@ -195,13 +195,13 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.15.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "OUOnt4Vnd1J838LTWEpP0ge8WIwjhx8O94kuTbC6AJRfOx9Lw78SG6XtVCE+WXb9zC5h3tzOV9YOng8Ke5yXMg==",
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
@@ -330,7 +330,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -363,7 +363,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.converters.rhino8": {
@@ -422,11 +422,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.15.1, )",
"resolved": "3.15.1",
"contentHash": "vFIf01jNVD69N6wE5iYVM7TKA6AbSTZxq1EedJCVZxhj5pkhHBw+23fyO+wz7Cd9ggqsiBUJMuKxUBHaDphGWQ==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.15.1"
"Speckle.Sdk": "3.15.3"
}
},
"System.Resources.Extensions": {
@@ -29,20 +29,20 @@
},
"RhinoCommon": {
"type": "Direct",
"requested": "[8.25.25328.11001, )",
"resolved": "8.25.25328.11001",
"contentHash": "PDKR9GwqyUXUkTulV4J0dzDIf/aWqSJkL7nkS8ReAx8xhnt/+RQpE8gTjOSCmkSU2tjG6WzclowbTxwMTU7VAA==",
"requested": "[8.28.26041.11001, )",
"resolved": "8.28.26041.11001",
"contentHash": "5mByZF+IdHvRLbYvr6Ek95Pwam6otewAyVHIGSC0sUE1+OscSpd9gbrFWnzo1arfCYmUJcUdsGhf7VayW09fQA==",
"dependencies": {
"System.Drawing.Common": "7.0.0"
}
},
"RhinoWindows": {
"type": "Direct",
"requested": "[8.25.25328.11001, )",
"resolved": "8.25.25328.11001",
"contentHash": "I/+++piwtYTue+iAAQqcMF5QlontqwNnC7Leyhiv2FiF8JpAl6K44ZsJqB7ZEUC6ns0LDfa3mbFzQwUfHwYumQ==",
"requested": "[8.28.26041.11001, )",
"resolved": "8.28.26041.11001",
"contentHash": "7MBG231k5c0/V/USTzbXpaTCiZCECQOuB80DnpgEvvEA3r//IQQDTbrqawDYmN9BEw/FHiFn82bsf6tV+SS5Iw==",
"dependencies": {
"RhinoCommon": "[8.25.25328.11001]"
"RhinoCommon": "[8.28.26041.11001]"
}
},
"Speckle.InterfaceGenerator": {
@@ -174,8 +174,8 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "Gt1qAZrMflLMOjDy4cQzJX5pdq5WokphYdsry7pMBlRHRdx9imQhJUPCg528Y2iVSMUnRKvj3A3cHtBeUxysEQ==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
@@ -183,13 +183,13 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.15.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "OUOnt4Vnd1J838LTWEpP0ge8WIwjhx8O94kuTbC6AJRfOx9Lw78SG6XtVCE+WXb9zC5h3tzOV9YOng8Ke5yXMg=="
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w=="
},
"SQLitePCLRaw.bundle_e_sqlite3": {
"type": "Transitive",
@@ -255,7 +255,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -279,7 +279,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.converters.rhino8": {
@@ -329,11 +329,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.15.1, )",
"resolved": "3.15.1",
"contentHash": "vFIf01jNVD69N6wE5iYVM7TKA6AbSTZxq1EedJCVZxhj5pkhHBw+23fyO+wz7Cd9ggqsiBUJMuKxUBHaDphGWQ==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.15.1"
"Speckle.Sdk": "3.15.3"
}
}
},
@@ -2,9 +2,9 @@ using Microsoft.Extensions.Logging;
using Rhino;
using Rhino.DocObjects;
using Rhino.Geometry;
using Rhino.Render;
using Speckle.Connectors.Common.Conversion;
using Speckle.Connectors.Common.Instances;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Rhino.Extensions;
using Speckle.Converters.Common.ToHost;
using Speckle.DoubleNumerics;
@@ -14,6 +14,7 @@ using Speckle.Sdk.Common.Exceptions;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Models.Instances;
using Speckle.Sdk.Pipelines.Progress;
namespace Speckle.Connectors.Rhino.HostApp;
@@ -137,9 +138,11 @@ public class RhinoInstanceBaker : IInstanceBaker<IReadOnlyCollection<string>>
// create attributes
ObjectAttributes atts = instanceProxy.GetAttributes();
atts.LayerIndex = layerIndex;
if (_materialBaker.ObjectIdAndMaterialIndexMap.TryGetValue(instanceProxyId, out int mIndex))
// set material using Guid
if (_materialBaker.ObjectIdAndMaterialIdMap.TryGetValue(instanceProxyId, out Guid materialGuid))
{
atts.MaterialIndex = mIndex;
atts.RenderMaterial = RenderContent.FromId(doc, materialGuid) as RenderMaterial;
atts.MaterialSource = ObjectMaterialSource.MaterialFromObject;
}
@@ -1,5 +1,6 @@
using Rhino;
using Rhino.DocObjects;
using Rhino.Render;
using Speckle.Connectors.Common.Operations.Receive;
using Speckle.Sdk;
using Speckle.Sdk.Common;
@@ -104,8 +105,7 @@ public class RhinoLayerBaker : TraversalContextUnpacker
/// </summary>
/// <param name="collectionPath">An array of Collection objects representing the path to create the layer.</param>
/// <param name="baseLayerName">The base layer name to start creating the new layer.</param>
/// <returns>The index of the last created layer.</returns>
private int CreateLayerFromPath(Collection[] collectionPath, string baseLayerName)
private void CreateLayerFromPath(Collection[] collectionPath, string baseLayerName)
{
var currentLayerName = baseLayerName;
var currentDocument = RhinoDoc.ActiveDoc; // POC: too much effort right now to wrap around the interfaced layers
@@ -135,13 +135,17 @@ public class RhinoLayerBaker : TraversalContextUnpacker
// set material
if (
_materialBaker.ObjectIdAndMaterialIndexMap.TryGetValue(
_materialBaker.ObjectIdAndMaterialIdMap.TryGetValue(
collection.applicationId ?? collection.id.NotNull(),
out int mIndex
out Guid materialGuid
)
)
{
newLayer.RenderMaterialIndex = mIndex;
var rc = RenderContent.FromId(currentDocument, materialGuid);
if (rc is RenderMaterial rm)
{
newLayer.RenderMaterialIndex = rm.DocumentAssoc.Materials.CurrentMaterialIndex;
}
}
// set color
@@ -164,7 +168,5 @@ public class RhinoLayerBaker : TraversalContextUnpacker
_hostLayerCache.Add(currentLayerName, index);
previousLayer = currentDocument.Layers.FindIndex(index); // note we need to get the correct id out, hence why we're double calling this
}
return previousLayer.Index;
}
}
@@ -3,9 +3,9 @@ using Speckle.Converters.Common;
using Speckle.Converters.Rhino;
using Speckle.Objects.Other;
using Speckle.Sdk;
using Speckle.Sdk.Common;
using Speckle.Sdk.Common.Exceptions;
using Material = Rhino.DocObjects.Material;
using RenderMaterial = Rhino.Render.RenderMaterial;
namespace Speckle.Connectors.Rhino.HostApp;
@@ -24,11 +24,12 @@ public class RhinoMaterialBaker
}
/// <summary>
/// A map keeping track of ids, <b>either layer id or object id</b>, and their material index. It's generated from the material proxy list as we bake materials; <see cref="BakeMaterials"/> must be called in advance for this to be populated with the correct data.
/// A map keeping track of ids, either layer id or object id, and their Render Material Guid.
/// It's generated from the material proxy list as we <see cref="BakeMaterials"/>.
/// </summary>
public Dictionary<string, int> ObjectIdAndMaterialIndexMap { get; } = new();
public Dictionary<string, Guid> ObjectIdAndMaterialIdMap { get; } = [];
public void BakeMaterials(IReadOnlyCollection<RenderMaterialProxy> speckleRenderMaterialProxies, string baseLayerName)
public void BakeMaterials(IReadOnlyCollection<RenderMaterialProxy> speckleRenderMaterialProxies)
{
var doc = _converterSettings.Current.Document; // POC: too much right now to interface around
// List<ReceiveConversionResult> conversionResults = new(); // TODO: return this guy
@@ -40,15 +41,14 @@ public class RhinoMaterialBaker
try
{
// POC: Currently we're relying on the render material name for identification if it's coming from speckle and from which model; could we do something else?
string materialId = speckleRenderMaterial.applicationId ?? speckleRenderMaterial.id.NotNull();
string matName = $"{speckleRenderMaterial.name}-({materialId})-{baseLayerName}";
string matName = speckleRenderMaterial.name;
matName = matName.Replace("[", "").Replace("]", ""); // "Material" doesn't like square brackets if we create from here. Once they created from Rhino UI, all good..
// Check if material with this name already exists in the document
int matIndex = doc.Materials.Find(matName, ignoreDeletedMaterials: true);
var existingRenderMaterial = doc.RenderMaterials.FirstOrDefault(m => m.Name == matName);
Guid materialGuid;
// If material doesn't exist, create it
if (matIndex == -1)
if (existingRenderMaterial == null)
{
Color diffuse = Color.FromArgb(speckleRenderMaterial.diffuse);
Color emissive = Color.FromArgb(speckleRenderMaterial.emissive);
@@ -73,24 +73,32 @@ public class RhinoMaterialBaker
rhinoMaterial.Shine = shine;
}
matIndex = doc.Materials.Add(rhinoMaterial);
// create RenderMaterial wrapper (CNX-2896)
var renderMaterial = RenderMaterial.CreateBasicMaterial(rhinoMaterial, doc);
// POC: check on matIndex -1, means we haven't created anything - this is most likely an recoverable error at this stage
if (matIndex == -1)
{
throw new ConversionException($"Failed to add a material to the document: '{matName}' (ID: {materialId})");
}
// add to RenderMaterial table. From my understanding, this internally manages the legacy Material table entry
doc.RenderMaterials.Add(renderMaterial);
materialGuid = renderMaterial.Id;
}
else
{
materialGuid = existingRenderMaterial.Id;
}
// Create the object <> material index map
if (materialGuid == Guid.Empty)
{
throw new ConversionException($"Failed to create or retrieve RenderMaterial Guid for: '{matName}'");
}
// map object ID to Material Guid
foreach (var objectId in proxy.objects)
{
ObjectIdAndMaterialIndexMap[objectId] = matIndex;
ObjectIdAndMaterialIdMap[objectId] = materialGuid;
}
}
catch (Exception ex) when (!ex.IsFatal())
{
_logger.LogError(ex, "Failed to add a material to the document");
_logger.LogError(ex, "Failed to add a modern RenderMaterial to the document");
}
}
}
@@ -1,6 +1,7 @@
using Rhino;
using Rhino.DocObjects;
using Rhino.Geometry;
using Rhino.Render;
using Speckle.Connectors.Common.Builders;
using Speckle.Connectors.Common.Conversion;
using Speckle.Connectors.Common.Extensions;
@@ -18,6 +19,7 @@ using Speckle.Sdk.Logging;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Models.Instances;
using Speckle.Sdk.Pipelines.Progress;
namespace Speckle.Connectors.Rhino.Operations.Receive;
@@ -119,7 +121,7 @@ public class RhinoHostObjectBuilder : IHostObjectBuilder
using var _ = _activityFactory.Start("Render Materials");
_threadContext.RunOnMain(() =>
{
_materialBaker.BakeMaterials(unpackedRoot.RenderMaterialProxies, baseLayerName);
_materialBaker.BakeMaterials(unpackedRoot.RenderMaterialProxies);
});
}
@@ -330,17 +332,17 @@ public class RhinoHostObjectBuilder : IHostObjectBuilder
{
var objectId = originalObject.applicationId ?? originalObject.id.NotNull();
if (_materialBaker.ObjectIdAndMaterialIndexMap.TryGetValue(objectId, out int mIndex))
if (_materialBaker.ObjectIdAndMaterialIdMap.TryGetValue(objectId, out Guid materialGuid))
{
atts.MaterialIndex = mIndex;
atts.RenderMaterial = RenderContent.FromId(_converterSettings.Current.Document, materialGuid) as RenderMaterial;
atts.MaterialSource = ObjectMaterialSource.MaterialFromObject;
}
else if (
parentObjectId is not null
&& (_materialBaker.ObjectIdAndMaterialIndexMap.TryGetValue(parentObjectId, out int mIndexSpeckleObj))
&& (_materialBaker.ObjectIdAndMaterialIdMap.TryGetValue(parentObjectId, out Guid parentGuid))
)
{
atts.MaterialIndex = mIndexSpeckleObj;
atts.RenderMaterial = RenderContent.FromId(_converterSettings.Current.Document, parentGuid) as RenderMaterial;
atts.MaterialSource = ObjectMaterialSource.MaterialFromObject;
}
@@ -0,0 +1,220 @@
using Microsoft.Extensions.Logging;
using Rhino.DocObjects;
using Speckle.Connectors.Common.Builders;
using Speckle.Connectors.Common.Caching;
using Speckle.Connectors.Common.Conversion;
using Speckle.Connectors.Common.Extensions;
using Speckle.Connectors.Common.Instances;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.DUI.Models.Card.SendFilter;
using Speckle.Connectors.Rhino.HostApp;
using Speckle.Connectors.Rhino.HostApp.Properties;
using Speckle.Converters.Common;
using Speckle.Converters.Rhino;
using Speckle.Sdk;
using Speckle.Sdk.Logging;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Models.Instances;
using Speckle.Sdk.Pipelines.Progress;
using Speckle.Sdk.Pipelines.Send;
using Layer = Rhino.DocObjects.Layer;
namespace Speckle.Connectors.Rhino.Operations.Send;
/// <summary>
/// NOTE: I am not happy this is a mostly copy paste of the Root object builder, but i'm also not too worried. The main (hot) path
/// should be this one going forward, so we should not touch the og root object builder besides to delete it.
/// Stateless builder object to turn an <see cref="ISendFilter"/> into a <see cref="Base"/> object
/// </summary>
public class RhinoContinuousTraversalBuilder : IRootContinuousTraversalBuilder<RhinoObject>
{
private readonly IRootToSpeckleConverter _rootToSpeckleConverter;
private readonly ISendConversionCache _sendConversionCache;
private readonly IConverterSettingsStore<RhinoConversionSettings> _converterSettings;
private readonly RhinoLayerUnpacker _layerUnpacker;
private readonly RhinoInstanceUnpacker _instanceUnpacker;
private readonly RhinoGroupUnpacker _groupUnpacker;
private readonly RhinoMaterialUnpacker _materialUnpacker;
private readonly RhinoColorUnpacker _colorUnpacker;
private readonly RhinoViewUnpacker _viewUnpacker;
private readonly PropertiesExtractor _propertiesExtractor;
private readonly ILogger<RhinoContinuousTraversalBuilder> _logger;
private readonly ISdkActivityFactory _activityFactory;
public RhinoContinuousTraversalBuilder(
IRootToSpeckleConverter rootToSpeckleConverter,
ISendConversionCache sendConversionCache,
IConverterSettingsStore<RhinoConversionSettings> converterSettings,
RhinoLayerUnpacker layerUnpacker,
RhinoInstanceUnpacker instanceUnpacker,
RhinoGroupUnpacker groupUnpacker,
RhinoMaterialUnpacker materialUnpacker,
RhinoColorUnpacker colorUnpacker,
RhinoViewUnpacker viewUnpacker,
PropertiesExtractor propertiesExtractor,
ILogger<RhinoContinuousTraversalBuilder> logger,
ISdkActivityFactory activityFactory
)
{
_sendConversionCache = sendConversionCache;
_converterSettings = converterSettings;
_layerUnpacker = layerUnpacker;
_instanceUnpacker = instanceUnpacker;
_groupUnpacker = groupUnpacker;
_rootToSpeckleConverter = rootToSpeckleConverter;
_materialUnpacker = materialUnpacker;
_colorUnpacker = colorUnpacker;
_viewUnpacker = viewUnpacker;
_propertiesExtractor = propertiesExtractor;
_logger = logger;
_activityFactory = activityFactory;
}
public async Task<RootObjectBuilderResult> Build(
IReadOnlyList<RhinoObject> rhinoObjects,
string projectId,
SendPipeline sendPipeline,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
{
using var activity = _activityFactory.Start("Build");
// 0 - Init the root
Collection rootObjectCollection = new() { name = _converterSettings.Current.Document.Name ?? "Unnamed document" };
rootObjectCollection["units"] = _converterSettings.Current.SpeckleUnits;
// 1 - Unpack the instances
UnpackResult<RhinoObject> unpackResults;
using (var _ = _activityFactory.Start("UnpackSelection"))
{
unpackResults = _instanceUnpacker.UnpackSelection(rhinoObjects);
}
var (atomicObjects, _, instanceProxies, instanceDefinitionProxies) = unpackResults;
// POC: we should formalise this, sooner or later - or somehow fix it a bit more
rootObjectCollection[ProxyKeys.INSTANCE_DEFINITION] = instanceDefinitionProxies; // this won't work re traversal on receive
// 2 - Unpack the groups
using (var _ = _activityFactory.Start("Unpack Groups"))
{
_groupUnpacker.UnpackGroups(rhinoObjects);
}
rootObjectCollection[ProxyKeys.GROUP] = _groupUnpacker.GroupProxies.Values;
// 3 - Convert atomic objects
List<SendConversionResult> results = new(atomicObjects.Count);
int count = 0;
using (var _ = _activityFactory.Start("Convert all"))
{
foreach (RhinoObject rhinoObject in atomicObjects)
{
cancellationToken.ThrowIfCancellationRequested();
// handle layer and store object layer *and all layer parents* to the version layers
// this is important because we need to unpack colors and materials on intermediate layers that do not have objects as well.
Layer layer = _converterSettings.Current.Document.Layers[rhinoObject.Attributes.LayerIndex];
Collection collectionHost = _layerUnpacker.GetHostObjectCollection(layer, rootObjectCollection);
var result = await ConvertRhinoObject(rhinoObject, collectionHost, instanceProxies, projectId, sendPipeline);
results.Add(result);
count++;
onOperationProgressed.Report(
new($"Converting objects... ({count:N0} / {atomicObjects.Count:N0})", (double)count / atomicObjects.Count)
);
}
}
if (results.All(x => x.Status == Status.ERROR))
{
throw new SpeckleException("Failed to convert all objects."); // fail fast instead creating empty commit! It will appear as model card error with red color.
}
// 4 - Unpack all proxies for the root
// Get all layers from the created collections on the root object commit for proxy processing
List<Layer> layers = _layerUnpacker.GetUsedLayers().ToList();
using (var _ = _activityFactory.Start("UnpackRenderMaterials"))
{
rootObjectCollection[ProxyKeys.RENDER_MATERIAL] = _materialUnpacker.UnpackRenderMaterials(atomicObjects, layers);
}
using (var _ = _activityFactory.Start("UnpackColors"))
{
rootObjectCollection[ProxyKeys.COLOR] = _colorUnpacker.UnpackColors(atomicObjects, layers);
}
// 5 - Unpack all other objects for the root
using (var _ = _activityFactory.Start("UnpackViews"))
{
List<Objects.Other.Camera> views = _viewUnpacker.UnpackViews(_converterSettings.Current.Document.NamedViews);
if (views.Count > 0)
{
rootObjectCollection[RootKeys.VIEW] = views;
}
}
await sendPipeline.Process(rootObjectCollection);
await sendPipeline.WaitForUpload();
return new RootObjectBuilderResult(rootObjectCollection, results);
}
private async Task<SendConversionResult> ConvertRhinoObject(
RhinoObject rhinoObject,
Collection collectionHost,
IReadOnlyDictionary<string, InstanceProxy> instanceProxies,
string projectId,
SendPipeline sendPipeline
)
{
string applicationId = rhinoObject.Id.ToString();
string sourceType = rhinoObject.ObjectType.ToString();
try
{
// get from cache or convert:
// What we actually do here is check if the object has been previously converted AND has not changed.
// If that's the case, we insert in the host collection just its object reference which has been saved from the prior conversion.
Base converted;
if (rhinoObject is InstanceObject)
{
converted = instanceProxies[applicationId];
}
else if (_sendConversionCache.TryGetValue(projectId, applicationId, out ObjectReference? value))
{
converted = value;
}
else
{
converted = _rootToSpeckleConverter.Convert(rhinoObject);
converted.applicationId = applicationId;
}
// add name and properties
// POC: this is NOT done in the converter because we don't have a RootToSpeckle converter that captures all top level converters
if (!string.IsNullOrEmpty(rhinoObject.Attributes.Name))
{
converted["name"] = rhinoObject.Attributes.Name;
}
var properties = _propertiesExtractor.GetProperties(rhinoObject);
if (properties.Count > 0)
{
converted["properties"] = properties;
}
// NOTE: this is the main part that differentiate from the main root object builder
var reference = await sendPipeline.Process(converted).ConfigureAwait(false);
// add to host
collectionHost.elements.Add(reference);
return new(Status.SUCCESS, applicationId, sourceType, reference);
}
catch (Exception ex) when (!ex.IsFatal())
{
_logger.LogSendConversionError(ex, sourceType);
return new(Status.ERROR, applicationId, sourceType, null, ex);
}
}
}
@@ -16,6 +16,7 @@ using Speckle.Sdk.Logging;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Models.Instances;
using Speckle.Sdk.Pipelines.Progress;
using Layer = Rhino.DocObjects.Layer;
namespace Speckle.Connectors.Rhino.Operations.Send;
@@ -71,6 +71,7 @@ public static class ServiceRegistration
serviceCollection.AddSingleton(DefaultTraversal.CreateTraversalFunc());
serviceCollection.AddScoped<IRootObjectBuilder<RhinoObject>, RhinoRootObjectBuilder>();
serviceCollection.AddScoped<IRootContinuousTraversalBuilder<RhinoObject>, RhinoContinuousTraversalBuilder>();
serviceCollection.AddScoped<
IInstanceObjectsManager<RhinoObject, List<string>>,
InstanceObjectsManager<RhinoObject, List<string>>
@@ -38,6 +38,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Mapper\Revit\RevitMappingResolver.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Filters\RhinoSelectionFilter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Filters\RhinoLayersFilter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\RhinoContinousTraversalBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\AddVisualizationProperties.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\ToSpeckleSettingsManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)RhinoEvents.cs" />
@@ -210,8 +210,8 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "Gt1qAZrMflLMOjDy4cQzJX5pdq5WokphYdsry7pMBlRHRdx9imQhJUPCg528Y2iVSMUnRKvj3A3cHtBeUxysEQ==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.CSharp": "4.7.0",
@@ -220,13 +220,13 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.15.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "OUOnt4Vnd1J838LTWEpP0ge8WIwjhx8O94kuTbC6AJRfOx9Lw78SG6XtVCE+WXb9zC5h3tzOV9YOng8Ke5yXMg==",
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
@@ -349,7 +349,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -382,7 +382,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"LibTessDotNet": {
@@ -432,11 +432,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.15.1, )",
"resolved": "3.15.1",
"contentHash": "vFIf01jNVD69N6wE5iYVM7TKA6AbSTZxq1EedJCVZxhj5pkhHBw+23fyO+wz7Cd9ggqsiBUJMuKxUBHaDphGWQ==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.15.1"
"Speckle.Sdk": "3.15.3"
}
}
}
@@ -229,8 +229,8 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "Gt1qAZrMflLMOjDy4cQzJX5pdq5WokphYdsry7pMBlRHRdx9imQhJUPCg528Y2iVSMUnRKvj3A3cHtBeUxysEQ==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.CSharp": "4.7.0",
@@ -239,13 +239,13 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.15.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "OUOnt4Vnd1J838LTWEpP0ge8WIwjhx8O94kuTbC6AJRfOx9Lw78SG6XtVCE+WXb9zC5h3tzOV9YOng8Ke5yXMg==",
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
@@ -430,7 +430,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -463,7 +463,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"LibTessDotNet": {
@@ -513,11 +513,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.15.1, )",
"resolved": "3.15.1",
"contentHash": "vFIf01jNVD69N6wE5iYVM7TKA6AbSTZxq1EedJCVZxhj5pkhHBw+23fyO+wz7Cd9ggqsiBUJMuKxUBHaDphGWQ==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.15.1"
"Speckle.Sdk": "3.15.3"
}
}
}
@@ -229,8 +229,8 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "Gt1qAZrMflLMOjDy4cQzJX5pdq5WokphYdsry7pMBlRHRdx9imQhJUPCg528Y2iVSMUnRKvj3A3cHtBeUxysEQ==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.CSharp": "4.7.0",
@@ -239,13 +239,13 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.15.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.15.1",
"contentHash": "OUOnt4Vnd1J838LTWEpP0ge8WIwjhx8O94kuTbC6AJRfOx9Lw78SG6XtVCE+WXb9zC5h3tzOV9YOng8Ke5yXMg==",
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
@@ -430,7 +430,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -463,7 +463,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.15.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"LibTessDotNet": {
@@ -513,11 +513,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.15.1, )",
"resolved": "3.15.1",
"contentHash": "vFIf01jNVD69N6wE5iYVM7TKA6AbSTZxq1EedJCVZxhj5pkhHBw+23fyO+wz7Cd9ggqsiBUJMuKxUBHaDphGWQ==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.15.1"
"Speckle.Sdk": "3.15.3"
}
}
}
@@ -0,0 +1,141 @@
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Common.Builders;
using Speckle.Connectors.Common.Caching;
using Speckle.Connectors.Common.Conversion;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.TeklaShared.Extensions;
using Speckle.Connectors.TeklaShared.HostApp;
using Speckle.Converters.Common;
using Speckle.Converters.TeklaShared;
using Speckle.Sdk;
using Speckle.Sdk.Logging;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Pipelines.Progress;
using Speckle.Sdk.Pipelines.Send;
namespace Speckle.Connectors.TeklaShared.Operations.Send;
/// <summary>
/// Continuous traversal builder for Tekla that streams objects through a <see cref="SendPipeline"/>
/// for packfile-based uploads. Same conversion logic as <see cref="TeklaRootObjectBuilder"/>.
/// </summary>
public class TeklaContinuousTraversalBuilder : IRootContinuousTraversalBuilder<TSM.ModelObject>
{
private readonly IRootToSpeckleConverter _rootToSpeckleConverter;
private readonly ISendConversionCache _sendConversionCache;
private readonly IConverterSettingsStore<TeklaConversionSettings> _converterSettings;
private readonly SendCollectionManager _sendCollectionManager;
private readonly ILogger<TeklaRootObjectBuilder> _logger;
private readonly ISdkActivityFactory _activityFactory;
private readonly TeklaMaterialUnpacker _materialUnpacker;
public TeklaContinuousTraversalBuilder(
IRootToSpeckleConverter rootToSpeckleConverter,
ISendConversionCache sendConversionCache,
IConverterSettingsStore<TeklaConversionSettings> converterSettings,
SendCollectionManager sendCollectionManager,
ILogger<TeklaRootObjectBuilder> logger,
ISdkActivityFactory activityFactory,
TeklaMaterialUnpacker materialUnpacker
)
{
_sendConversionCache = sendConversionCache;
_converterSettings = converterSettings;
_sendCollectionManager = sendCollectionManager;
_rootToSpeckleConverter = rootToSpeckleConverter;
_logger = logger;
_activityFactory = activityFactory;
_materialUnpacker = materialUnpacker;
}
public async Task<RootObjectBuilderResult> Build(
IReadOnlyList<TSM.ModelObject> teklaObjects,
string projectId,
SendPipeline sendPipeline,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
{
using var activity = _activityFactory.Start("Build");
var model = new TSM.Model();
string modelName = model.GetInfo().ModelName ?? "Unnamed model";
Collection rootObjectCollection = new() { name = modelName };
rootObjectCollection["units"] = _converterSettings.Current.SpeckleUnits;
List<SendConversionResult> results = new(teklaObjects.Count);
int count = 0;
using (var _ = _activityFactory.Start("Convert all"))
{
foreach (TSM.ModelObject teklaObject in teklaObjects)
{
cancellationToken.ThrowIfCancellationRequested();
var result = await ConvertTeklaObject(teklaObject, rootObjectCollection, projectId, sendPipeline);
results.Add(result);
++count;
onOperationProgressed.Report(
new($"Converting objects... ({count:N0} / {teklaObjects.Count:N0})", (double)count / teklaObjects.Count)
);
await Task.Yield();
}
}
if (results.All(x => x.Status == Status.ERROR))
{
throw new SpeckleException("Failed to convert all objects.");
}
var renderMaterialProxies = _materialUnpacker.UnpackRenderMaterial(teklaObjects.ToList());
if (renderMaterialProxies.Count > 0)
{
rootObjectCollection[ProxyKeys.RENDER_MATERIAL] = renderMaterialProxies;
}
// Process root collection and wait for all uploads
await sendPipeline.Process(rootObjectCollection);
await sendPipeline.WaitForUpload();
return new RootObjectBuilderResult(rootObjectCollection, results);
}
private async Task<SendConversionResult> ConvertTeklaObject(
TSM.ModelObject teklaObject,
Collection collectionHost,
string projectId,
SendPipeline sendPipeline
)
{
string applicationId = teklaObject.GetSpeckleApplicationId();
string sourceType = teklaObject.GetType().Name;
try
{
Base converted;
if (_sendConversionCache.TryGetValue(projectId, applicationId, out ObjectReference? value))
{
converted = value;
}
else
{
converted = _rootToSpeckleConverter.Convert(teklaObject);
}
var collection = _sendCollectionManager.GetAndCreateObjectHostCollection(teklaObject, collectionHost);
// NOTE: this is the main part that differentiate from the main root object builder
var reference = await sendPipeline.Process(converted).ConfigureAwait(false);
collection.elements.Add(reference);
return new(Status.SUCCESS, applicationId, sourceType, reference);
}
catch (Exception ex) when (!ex.IsFatal())
{
_logger.LogError(ex, "Failed to convert object {SourceType}", sourceType);
return new(Status.ERROR, applicationId, sourceType, null, ex);
}
}
}
@@ -11,6 +11,7 @@ using Speckle.Sdk;
using Speckle.Sdk.Logging;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Pipelines.Progress;
namespace Speckle.Connectors.TeklaShared.Operations.Send;
@@ -54,6 +54,7 @@ public static class ServiceRegistration
services.AddSingleton(DefaultTraversal.CreateTraversalFunc());
services.AddScoped<SendCollectionManager>();
services.AddScoped<IRootObjectBuilder<ModelObject>, TeklaRootObjectBuilder>();
services.AddScoped<IRootContinuousTraversalBuilder<ModelObject>, TeklaContinuousTraversalBuilder>();
services.AddScoped<SendOperation<ModelObject>>();
services.AddSingleton<ToSpeckleSettingsManager>();
@@ -37,6 +37,7 @@
<Compile Include="$(MSBuildThisFileDirectory)HostApp\TeklaMaterialUnpacker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\SendRebarsAsSolidSetting.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\ToSpeckleSettingsManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\TeklaContinuousTraversalBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\TeklaRootObjectBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Plugin\TeklaPlugin.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ServiceRegistration.cs" />

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