Compare commits

..

6 Commits

Author SHA1 Message Date
oguzhankoral 055cdb67a7 revert rest 2025-02-06 15:54:02 +00:00
oguzhankoral bc57632f3d revert autocad 2025-02-06 15:51:11 +00:00
oguzhankoral d443d2149b revert rhino 2025-02-06 15:49:01 +00:00
oguzhankoral b8edac1072 Merge remote-tracking branch 'origin/dev' into oguzhan/fix-threading 2025-02-06 15:19:08 +00:00
oguzhankoral 155d53c740 untangle revit 2025-02-06 15:18:27 +00:00
oguzhankoral 90c9308b8b Fix threading bug 2025-02-06 14:17:00 +00:00
906 changed files with 17521 additions and 45316 deletions
+3 -3
View File
@@ -9,10 +9,10 @@
],
"rollForward": false
},
"dotnet-affected": {
"version": "5.0.0",
"gitversion.tool": {
"version": "6.0.2",
"commands": [
"dotnet-affected"
"dotnet-gitversion"
],
"rollForward": false
}
-3
View File
@@ -314,9 +314,6 @@ dotnet_diagnostic.NUnit2037.severity = warning # Consider using Assert.That(coll
dotnet_diagnostic.NUnit2038.severity = warning # Consider using Assert.That(actual, Is.InstanceOf(expected)) instead of Assert.IsInstanceOf(expected, actual)
dotnet_diagnostic.NUnit2039.severity = warning # Consider using Assert.That(actual, Is.Not.InstanceOf(expected)) instead of Assert.IsNotInstanceOf(expected, actual)
# note: added to allow the copy paste from rhino inside of the ValueSet component
dotnet_diagnostic.CA1033.severity = none
[*.{appxmanifest,asax,ascx,aspx,axaml,build,c,c++,cc,cginc,compute,cp,cpp,cs,cshtml,cu,cuh,cxx,dtd,fs,fsi,fsscript,fsx,fx,fxh,h,hh,hlsl,hlsli,hlslinc,hpp,hxx,inc,inl,ino,ipp,ixx,master,ml,mli,mpp,mq4,mq5,mqh,nuspec,paml,razor,resw,resx,shader,skin,tpp,usf,ush,vb,xaml,xamlx,xoml,xsd}]
indent_style = space
indent_size = 2
+26 -29
View File
@@ -4,36 +4,33 @@
# * @specklesystems/connectors
# Core
# Not needed, falls back on team approval
# Objects
# Converters require product owner approval, anything else falls back to team approval
Objects/ConverterAutocadCivil/* @clairekuang @connorivy
Objects/ConverterCSI/* @connorivy
Objects/ConverterDynamo/* @teocomi @alanrynne
Objects/ConverterMicrostation/* @connorivy
Objects/ConverterRevit/* @connorivy @teocomi
Objects/ConverterRhinoGh/* @alanrynne @clairekuang
Objects/ConverterTeklaStructures/* @connorivy
Objects/StructuralUtilities/PolygonMesher/* @connorivy
# Connectors
/Connectors/ArcGIS/* @KatKatKateryna
/Connectors/Autocad/* @clairekuang @oguzhankoral @didimitrie
/Connectors/Civil3d/* @clairekuang @oguzhankoral @didimitrie
/Connectors/CSi/* @bjoernsteinhagen @dogukankaratas
/Connectors/Navisworks/* @jsdbroughton
/Connectors/Revit/* @clairekuang @oguzhankoral @didimitrie
/Connectors/Rhino/* @clairekuang @oguzhankoral @didimitrie
/Connectors/Tekla/* @bjoernsteinhagen @dogukankaratas
ConnectorAutocadCivil/* @clairekuang
ConnectorArchicad/* @jozseflkiss
ConnectorCSI/* @connorivy
ConnectorDynamo/* @teocomi @alanrynne
ConnectorGrasshopper/* @alanrynne @clairekuang
ConnectorMicrostation/* @clairekuang
ConnectorRevit/* @teocomi @connorivy
ConnectorRhino/* @clairekuang @alanrynne
ConnectorTeklaStructures/* @connorivy
# Converters
/Convertors/ArcGIS/* @KatKatKateryna
/Convertors/Autocad/* @clairekuang @oguzhankoral @didimitrie
/Convertors/Civil3d/* @clairekuang @oguzhankoral @didimitrie
/Convertors/CSi/* @bjoernsteinhagen @dogukankaratas
/Convertors/Navisworks/* @jsdbroughton
/Convertors/Revit/* @clairekuang @oguzhankoral @didimitrie
/Convertors/Rhino/* @clairekuang @oguzhankoral @didimitrie
/Convertors/Tekla/* @bjoernsteinhagen @dogukankaratas
# DesktopUI
# DUI
/DUI3/* @clairekuang @oguzhankoral @didimitrie
# Importers
/Importers/* @JR-Morgan @didimitrie @oguzhankoral @adamhathcock
# SDK
/SDK/* @JR-Morgan @clairekuang @didimitrie @oguzhankoral @adamhathcock
# Build
/Build/* @JR-Morgan @oguzhankoral @adamhathcock
DesktopUI2/* @teocomi @clairekuang
+17 -14
View File
@@ -1,22 +1,27 @@
name: .NET Build
on: pull_request
on:
pull_request: # Run build on every pull request that is not to main
branches-ignore:
- main
jobs:
jobs:
build:
runs-on: windows-latest
outputs:
version: ${{ steps.set-version.outputs.version }}
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup .NET
uses: actions/setup-dotnet@v5
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.4xx # Align with global.json (including roll forward rules)
- name: Cache Nuget
- name: Cache Nuget
uses: actions/cache@v4
with:
path: ~/.nuget/packages
@@ -28,16 +33,14 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
with:
fetch-depth: 0
uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v5
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.4xx # Align with global.json (including roll forward rules)
- name: Cache Nuget
- name: Cache Nuget
uses: actions/cache@v4
with:
path: ~/.nuget/packages
@@ -48,9 +51,9 @@ jobs:
- name: ⚒️ Run tests
run: ./build.sh test-only
- name: Upload coverage reports to Codecov with GitHub Action
uses: codecov/codecov-action@v5
with:
files: Converters/**/coverage.xml
file: Converters/**/coverage.xml
token: ${{ secrets.CODECOV_TOKEN }}
+21 -28
View File
@@ -2,26 +2,22 @@ name: .NET Build and Publish
on:
push:
branches: ["main", "installer-test/**"]
tags: ["v3.*.*"] # Manual delivery on every 3.x tag
branches: ["main", "dev", "release/*"] # Continuous delivery on every long-lived branch
tags: ["v3.*"] # Manual delivery on every 3.x tag
jobs:
build-windows:
runs-on: windows-latest
env:
SEMVER: "unset"
FILE_VERSION: "unset"
outputs:
semver: ${{ steps.set-version.outputs.semver }}
file_version: ${{ steps.set-version.outputs.file_version }}
version: ${{ steps.set-version.outputs.version }}
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup .NET
uses: actions/setup-dotnet@v5
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.4xx # Align with global.json (including roll forward rules)
@@ -31,43 +27,38 @@ jobs:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
- name: ⚒️ Run GitVersion on Windows
run: ./build.ps1 build-server-version
- name: ⚒️ Run build on Windows
run: ./build.ps1 zip
run: ./build.ps1
- name: ⬆️ Upload artifacts
uses: actions/upload-artifact@v4
with:
name: output-${{ env.SEMVER }}
name: output-${{ env.GitVersion_FullSemVer }}
path: output/*.*
if-no-files-found: error
retention-days: 1
compression-level: 0 # no compression
- id: set-version
name: Set version to output
run: |
echo "semver=${{ env.SEMVER }}" >> "$Env:GITHUB_OUTPUT"
echo "file_version=${{ env.FILE_VERSION }}" >> "$Env:GITHUB_OUTPUT"
run: echo "version=${{ env.GitVersion_FullSemVer }}" >> "$Env:GITHUB_OUTPUT"
deploy-installers:
runs-on: ubuntu-latest
needs: build-windows
env:
IS_PUBLIC_RELEASE: ${{ github.ref_type == 'tag' }}
IS_TAG_BUILD: ${{ github.ref_type == 'tag' }}
IS_RELEASE_BRANCH: ${{ startsWith(github.ref_name, 'release/') || github.ref_name == 'main'}}
steps:
- name: 🔫 Trigger Build Installers
uses: the-actions-org/workflow-dispatch@v4.0.0
uses: ALEEF02/workflow-dispatch@v3.0.0
continue-on-error: true
with:
workflow: Build Installers
repo: specklesystems/connector-installers
token: ${{ secrets.CONNECTORS_GH_TOKEN }}
inputs: '{
"run_id": "${{ github.run_id }}",
"semver": "${{ needs.build-windows.outputs.semver }}",
"file_version": "${{ needs.build-windows.outputs.file_version }}",
"repo": "${{ github.repository }}",
"is_public_release": ${{ env.IS_PUBLIC_RELEASE }}
}'
inputs: '{ "run_id": "${{ github.run_id }}", "version": "${{ needs.build-windows.outputs.version }}", "public_release": ${{ env.IS_TAG_BUILD }}, "store_artifacts": ${{ env.IS_RELEASE_BRANCH }} }'
ref: main
wait-for-completion: true
wait-for-completion-interval: 10s
@@ -83,12 +74,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup .NET
uses: actions/setup-dotnet@v5
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.4xx # Align with global.json (including roll forward rules)
@@ -98,6 +89,9 @@ jobs:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
- name: ⚒️ Run GitVersion on Linux
run: ./build.sh build-server-version
- name: ⚒️ Run tests on Linux
run: ./build.sh test-only
@@ -111,5 +105,4 @@ jobs:
token: ${{ secrets.CODECOV_TOKEN }}
- name: Push to nuget.org
if: (github.ref_type == 'tag')
run: dotnet nuget push output/*.nupkg --source "https://api.nuget.org/v3/index.json" --api-key ${{secrets.CONNECTORS_NUGET_TOKEN }} --skip-duplicate
+424
View File
@@ -0,0 +1,424 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.10.35027.167
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Config", "Config", "{85A13E25-EB29-4F31-8853-B9EE83275B3D}"
ProjectSection(SolutionItems) = preProject
.csharpierrc.yaml = .csharpierrc.yaml
.editorconfig = .editorconfig
codecov.yml = codecov.yml
Directory.Build.props = Directory.Build.props
Directory.Build.targets = Directory.Build.targets
Directory.Packages.props = Directory.Packages.props
global.json = global.json
README.md = README.md
CodeMetricsConfig.txt = CodeMetricsConfig.txt
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DUI3", "DUI3", "{FD4D6594-D81E-456F-8F2E-35B09E04A755}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Revit", "Revit", "{D92751C8-1039-4005-90B2-913E55E0B8BD}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sdk", "Sdk", "{2E00592E-558D-492D-88F9-3ECEE4C0C7DA}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connectors.Revit2023", "Connectors\Revit\Speckle.Connectors.Revit2023\Speckle.Connectors.Revit2023.csproj", "{01F98733-7352-47AD-A594-537D979DE3DE}"
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Connectors.RevitShared", "Connectors\Revit\Speckle.Connectors.RevitShared\Speckle.Connectors.RevitShared.shproj", "{DC570FFF-6FE5-47BD-8BC1-B471A6067786}"
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Converters.RevitShared", "Converters\Revit\Speckle.Converters.RevitShared\Speckle.Converters.RevitShared.shproj", "{E1C43415-3200-45F4-8BF9-A4DD7D7F2ED6}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Revit2023.DependencyInjection", "Converters\Revit\Speckle.Converters.Revit2023.DependencyInjection\Speckle.Converters.Revit2023.DependencyInjection.csproj", "{83EAD6F0-3CB3-456A-AD81-072127D0DE0E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Revit2023", "Converters\Revit\Speckle.Converters.Revit2023\Speckle.Converters.Revit2023.csproj", "{26391930-F86F-47E0-A5F6-B89919E38CE1}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connectors.DUI", "DUI3\Speckle.Connectors.DUI\Speckle.Connectors.DUI.csproj", "{D81C0B87-F0C1-4297-A147-02F001FB7E1E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Autofac", "Sdk\Speckle.Autofac\Speckle.Autofac.csproj", "{C9D4CA21-182B-4ED2-81BB-280A6FD713F6}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connectors.Utils", "Sdk\Speckle.Connectors.Utils\Speckle.Connectors.Utils.csproj", "{7291B93C-615D-42DE-B8C1-3F9DF643E0FC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Common", "Sdk\Speckle.Converters.Common\Speckle.Converters.Common.csproj", "{8AEF06C0-CA5C-4460-BC2D-ADE5F35D0434}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Rhino", "Rhino", "{9584AEE5-CD59-46E6-93E6-2DC2B5285B75}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connectors.Rhino7", "Connectors\Rhino\Speckle.Connectors.Rhino7\Speckle.Connectors.Rhino7.csproj", "{1E2644A9-6B31-4350-8772-CEAAD6EE0B21}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Rhino7", "Converters\Rhino\Speckle.Converters.Rhino7\Speckle.Converters.Rhino7.csproj", "{65A2F556-F14A-49F3-8A92-7F2E1E7ED3B5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Rhino7.DependencyInjection", "Converters\Rhino\Speckle.Converters.Rhino7.DependencyInjection\Speckle.Converters.Rhino7.DependencyInjection.csproj", "{9C1ECA1D-DE98-4FB7-92D0-FC45BB308E5D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connectors.ArcGIS3", "Connectors\ArcGIS\Speckle.Connectors.ArcGIS3\Speckle.Connectors.ArcGIS3.csproj", "{A97F7177-86C7-4B38-A6ED-DA51BF762471}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.ArcGIS3", "Converters\ArcGIS\Speckle.Converters.ArcGIS3\Speckle.Converters.ArcGIS3.csproj", "{139F7A79-69E4-4B8A-B2A5-6A30A66C495C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.ArcGIS3.DependencyInjection", "Converters\ArcGIS\Speckle.Converters.ArcGIS3.DependencyInjection\Speckle.Converters.ArcGIS3.DependencyInjection.csproj", "{7DFF1591-237D-499E-A767-EE37B93FB958}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ArcGIS", "ArcGIS", "{CCF48B65-33D1-4E8B-A57B-E03394730B21}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connectors.Autocad2023", "Connectors\Autocad\Speckle.Connectors.Autocad2023\Speckle.Connectors.Autocad2023.csproj", "{89C4CBC7-1606-40DE-B6DA-FBE3AAC98395}"
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Connectors.AutocadShared", "Connectors\Autocad\Speckle.Connectors.AutocadShared\Speckle.Connectors.AutocadShared.shproj", "{41BC679F-887F-44CF-971D-A5502EE87DB0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Common.DependencyInjection", "Sdk\Speckle.Converters.Common.DependencyInjection\Speckle.Converters.Common.DependencyInjection.csproj", "{11F7D41B-AFCA-4D29-BC08-285A14BF3A3B}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Autocad", "Autocad", "{804E065F-914C-414A-AF84-009312C3CFF6}"
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Converters.AutocadShared", "Converters\Autocad\Speckle.Converters.AutocadShared\Speckle.Converters.AutocadShared.shproj", "{9ADD1B7A-6401-4202-8613-F668E2FBC0A4}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Autocad2023", "Converters\Autocad\Speckle.Converters.Autocad2023\Speckle.Converters.Autocad2023.csproj", "{631C295A-7CCF-4B42-8686-7034E31469E7}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Autocad2023.DependencyInjection", "Converters\Autocad\Speckle.Converters.Autocad2023.DependencyInjection\Speckle.Converters.Autocad2023.DependencyInjection.csproj", "{D940853C-003A-482C-BDB0-665367F274A0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connectors.DUI.WebView", "DUI3\Speckle.Connectors.DUI.WebView\Speckle.Connectors.DUI.WebView.csproj", "{7420652C-3046-4F38-BE64-9B9E69D76FA2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Build", "Build\Build.csproj", "{C50AA3E3-8C31-4131-9DEC-1D8B377D5A89}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "HostApps", "HostApps", "{42826721-9A18-4762-8BA9-F1429DD5C5B1}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{59E8E8F3-4E42-4E92-83B3-B1C2AB901D18}"
ProjectSection(SolutionItems) = preProject
.github\workflows\ci.yml = .github\workflows\ci.yml
.github\workflows\main.yml = .github\workflows\main.yml
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connectors.Civil3d2024", "Connectors\Autocad\Speckle.Connectors.Civil3d2024\Speckle.Connectors.Civil3d2024.csproj", "{CA8EAE01-AB9F-4EC1-B6F3-73721487E9E1}"
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Converters.Civil3dShared", "Converters\Civil3d\Speckle.Converters.Civil3dShared\Speckle.Converters.Civil3dShared.shproj", "{35175682-DA83-4C0A-A49D-B191F5885D8E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Civil3d2024.DependencyInjection", "Converters\Civil3d\Speckle.Converters.Civil3d2024.DependencyInjection\Speckle.Converters.Civil3d2024.DependencyInjection.csproj", "{80F965C4-E2A8-4F54-985D-73D06E45F9CE}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Autocad2024", "Converters\Autocad\Speckle.Converters.Autocad2024\Speckle.Converters.Autocad2024.csproj", "{C2DE264A-AA87-4012-B954-17E3F403A237}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Autocad2024.DependencyInjection", "Converters\Autocad\Speckle.Converters.Autocad2024.DependencyInjection\Speckle.Converters.Autocad2024.DependencyInjection.csproj", "{AF507D61-6766-4C20-9F58-23DC29508219}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Civil3d2024", "Converters\Civil3d\Speckle.Converters.Civil3d2024\Speckle.Converters.Civil3d2024.csproj", "{25172C49-7AA4-4739-BB07-69785094C379}"
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Converters.RhinoShared", "Converters\Rhino\Speckle.Converters.RhinoShared\Speckle.Converters.RhinoShared.shproj", "{E1C43415-3200-45F4-8BF9-A4DD7D7F2ED9}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Rhino7.Tests", "Converters\Rhino\Speckle.Converters.Rhino7.Tests\Speckle.Converters.Rhino7.Tests.csproj", "{AC2DB416-F05C-4296-9040-56D6AD4FCD27}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Revit2023.Tests", "Converters\Revit\Speckle.Converters.Revit2023.Tests\Speckle.Converters.Revit2023.Tests.csproj", "{68CF9BDF-94AC-4D2D-A7BD-D1C064F97051}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Testing", "Sdk\Speckle.Testing\Speckle.Testing.csproj", "{A3869243-B462-4986-914B-94E407D8D20F}"
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Converters.RevitShared.DependencyInjection", "Converters\Revit\Speckle.Converters.RevitShared.DependencyInjection\Speckle.Converters.RevitShared.DependencyInjection.shproj", "{6067BA60-D279-4156-8AE1-6B44E2D19187}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connectors.Revit2024", "Connectors\Revit\Speckle.Connectors.Revit2024\Speckle.Connectors.Revit2024.csproj", "{617BD3C7-87D9-4D28-8AC9-4910945BB9FC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Revit2024", "Converters\Revit\Speckle.Converters.Revit2024\Speckle.Converters.Revit2024.csproj", "{67B888D9-C6C4-49F1-883C-5B964151D889}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Revit2024.DependencyInjection", "Converters\Revit\Speckle.Converters.Revit2024.DependencyInjection\Speckle.Converters.Revit2024.DependencyInjection.csproj", "{7F3055BA-70AA-424C-8748-345AF35127E9}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2023", "2023", "{E9DEBA00-50A4-485D-BA65-D8AB3E3467AB}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2024", "2024", "{57F59C0C-5687-4AF9-AE1C-1933B539F0E4}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{FC224610-32D3-454E-9BC1-1219FE8ACD5F}"
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Converters.RevitShared.Tests", "Converters\Revit\Speckle.Converters.RevitShared.Tests\Speckle.Converters.RevitShared.Tests.shproj", "{E1C43415-3202-45F4-8BF9-A4DD7D7F2ED6}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Revit2024.Tests", "Converters\Revit\Speckle.Converters.Revit2024.Tests\Speckle.Converters.Revit2024.Tests.csproj", "{C32274D9-1B66-4D5C-82F9-EB3F10F46752}"
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Connectors.RevitShared.Cef", "Connectors\Revit\Speckle.Connectors.RevitShared.Cef\Speckle.Connectors.RevitShared.Cef.shproj", "{6A40CBE4-ECAB-4CED-9917-5C64CBF75DA6}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2025", "2025", "{8AC2AD6D-6C74-4B24-8DF6-42717FC9B804}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connectors.Revit2025", "Connectors\Revit\Speckle.Connectors.Revit2025\Speckle.Connectors.Revit2025.csproj", "{A6DE3DA0-B242-4F49-AEF0-4E26AF92D16C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Revit2025", "Converters\Revit\Speckle.Converters.Revit2025\Speckle.Converters.Revit2025.csproj", "{4D40A101-07E6-4FF2-8934-83434932591D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Revit2025.DependencyInjection", "Converters\Revit\Speckle.Converters.Revit2025.DependencyInjection\Speckle.Converters.Revit2025.DependencyInjection.csproj", "{20751904-0DFC-4126-BF2A-834B53841010}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connectors.Revit2022", "Connectors\Revit\Speckle.Connectors.Revit2022\Speckle.Connectors.Revit2022.csproj", "{7F1FDCF2-0CE8-4119-B3C1-F2CC6D7E1C36}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Revit2022", "Converters\Revit\Speckle.Converters.Revit2022\Speckle.Converters.Revit2022.csproj", "{19424B55-058C-4E9C-B86F-700AEF9EAEC3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Revit2022.DependencyInjection", "Converters\Revit\Speckle.Converters.Revit2022.DependencyInjection\Speckle.Converters.Revit2022.DependencyInjection.csproj", "{881D71A3-D276-4108-98C6-0FFD32129B9C}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2022", "2022", "{0AF38BA3-65A0-481B-8CBB-B82E406E1575}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Connectors.DUI.Tests", "DUI3\Speckle.Connectors.DUI.Tests\Speckle.Connectors.DUI.Tests.csproj", "{EB83A3A3-F9B6-4281-8EBF-F7289FB5D885}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Revit2022.Tests", "Converters\Revit\Speckle.Converters.Revit2022.Tests\Speckle.Converters.Revit2022.Tests.csproj", "{D8069A23-AD2E-4C9E-8574-7E8C45296A46}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2023", "2023", "{2D5AE63D-85C0-43D1-84BF-04418ED93F63}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2024", "2024", "{2F45036E-D817-41E9-B82F-DBE013EC95D0}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Civil3d", "Civil3d", "{91D70DE1-DC8E-4AE1-B100-0671D6263FC5}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{4721AA15-AF6E-4A62-A2C3-65564DC563E6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{01F98733-7352-47AD-A594-537D979DE3DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{01F98733-7352-47AD-A594-537D979DE3DE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{01F98733-7352-47AD-A594-537D979DE3DE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{01F98733-7352-47AD-A594-537D979DE3DE}.Release|Any CPU.Build.0 = Release|Any CPU
{83EAD6F0-3CB3-456A-AD81-072127D0DE0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{83EAD6F0-3CB3-456A-AD81-072127D0DE0E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{83EAD6F0-3CB3-456A-AD81-072127D0DE0E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{83EAD6F0-3CB3-456A-AD81-072127D0DE0E}.Release|Any CPU.Build.0 = Release|Any CPU
{26391930-F86F-47E0-A5F6-B89919E38CE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{26391930-F86F-47E0-A5F6-B89919E38CE1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{26391930-F86F-47E0-A5F6-B89919E38CE1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{26391930-F86F-47E0-A5F6-B89919E38CE1}.Release|Any CPU.Build.0 = Release|Any CPU
{D81C0B87-F0C1-4297-A147-02F001FB7E1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D81C0B87-F0C1-4297-A147-02F001FB7E1E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D81C0B87-F0C1-4297-A147-02F001FB7E1E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D81C0B87-F0C1-4297-A147-02F001FB7E1E}.Release|Any CPU.Build.0 = Release|Any CPU
{C9D4CA21-182B-4ED2-81BB-280A6FD713F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C9D4CA21-182B-4ED2-81BB-280A6FD713F6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C9D4CA21-182B-4ED2-81BB-280A6FD713F6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C9D4CA21-182B-4ED2-81BB-280A6FD713F6}.Release|Any CPU.Build.0 = Release|Any CPU
{7291B93C-615D-42DE-B8C1-3F9DF643E0FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7291B93C-615D-42DE-B8C1-3F9DF643E0FC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7291B93C-615D-42DE-B8C1-3F9DF643E0FC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7291B93C-615D-42DE-B8C1-3F9DF643E0FC}.Release|Any CPU.Build.0 = Release|Any CPU
{8AEF06C0-CA5C-4460-BC2D-ADE5F35D0434}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8AEF06C0-CA5C-4460-BC2D-ADE5F35D0434}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8AEF06C0-CA5C-4460-BC2D-ADE5F35D0434}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8AEF06C0-CA5C-4460-BC2D-ADE5F35D0434}.Release|Any CPU.Build.0 = Release|Any CPU
{1E2644A9-6B31-4350-8772-CEAAD6EE0B21}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1E2644A9-6B31-4350-8772-CEAAD6EE0B21}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1E2644A9-6B31-4350-8772-CEAAD6EE0B21}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1E2644A9-6B31-4350-8772-CEAAD6EE0B21}.Release|Any CPU.Build.0 = Release|Any CPU
{65A2F556-F14A-49F3-8A92-7F2E1E7ED3B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{65A2F556-F14A-49F3-8A92-7F2E1E7ED3B5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{65A2F556-F14A-49F3-8A92-7F2E1E7ED3B5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{65A2F556-F14A-49F3-8A92-7F2E1E7ED3B5}.Release|Any CPU.Build.0 = Release|Any CPU
{9C1ECA1D-DE98-4FB7-92D0-FC45BB308E5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9C1ECA1D-DE98-4FB7-92D0-FC45BB308E5D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9C1ECA1D-DE98-4FB7-92D0-FC45BB308E5D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9C1ECA1D-DE98-4FB7-92D0-FC45BB308E5D}.Release|Any CPU.Build.0 = Release|Any CPU
{A97F7177-86C7-4B38-A6ED-DA51BF762471}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A97F7177-86C7-4B38-A6ED-DA51BF762471}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A97F7177-86C7-4B38-A6ED-DA51BF762471}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A97F7177-86C7-4B38-A6ED-DA51BF762471}.Release|Any CPU.Build.0 = Release|Any CPU
{139F7A79-69E4-4B8A-B2A5-6A30A66C495C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{139F7A79-69E4-4B8A-B2A5-6A30A66C495C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{139F7A79-69E4-4B8A-B2A5-6A30A66C495C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{139F7A79-69E4-4B8A-B2A5-6A30A66C495C}.Release|Any CPU.Build.0 = Release|Any CPU
{7DFF1591-237D-499E-A767-EE37B93FB958}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7DFF1591-237D-499E-A767-EE37B93FB958}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7DFF1591-237D-499E-A767-EE37B93FB958}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7DFF1591-237D-499E-A767-EE37B93FB958}.Release|Any CPU.Build.0 = Release|Any CPU
{89C4CBC7-1606-40DE-B6DA-FBE3AAC98395}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{89C4CBC7-1606-40DE-B6DA-FBE3AAC98395}.Debug|Any CPU.Build.0 = Debug|Any CPU
{89C4CBC7-1606-40DE-B6DA-FBE3AAC98395}.Release|Any CPU.ActiveCfg = Release|Any CPU
{89C4CBC7-1606-40DE-B6DA-FBE3AAC98395}.Release|Any CPU.Build.0 = Release|Any CPU
{11F7D41B-AFCA-4D29-BC08-285A14BF3A3B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{11F7D41B-AFCA-4D29-BC08-285A14BF3A3B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{11F7D41B-AFCA-4D29-BC08-285A14BF3A3B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{11F7D41B-AFCA-4D29-BC08-285A14BF3A3B}.Release|Any CPU.Build.0 = Release|Any CPU
{631C295A-7CCF-4B42-8686-7034E31469E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{631C295A-7CCF-4B42-8686-7034E31469E7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{631C295A-7CCF-4B42-8686-7034E31469E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{631C295A-7CCF-4B42-8686-7034E31469E7}.Release|Any CPU.Build.0 = Release|Any CPU
{D940853C-003A-482C-BDB0-665367F274A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D940853C-003A-482C-BDB0-665367F274A0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D940853C-003A-482C-BDB0-665367F274A0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D940853C-003A-482C-BDB0-665367F274A0}.Release|Any CPU.Build.0 = Release|Any CPU
{7420652C-3046-4F38-BE64-9B9E69D76FA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7420652C-3046-4F38-BE64-9B9E69D76FA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7420652C-3046-4F38-BE64-9B9E69D76FA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7420652C-3046-4F38-BE64-9B9E69D76FA2}.Release|Any CPU.Build.0 = Release|Any CPU
{C50AA3E3-8C31-4131-9DEC-1D8B377D5A89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C50AA3E3-8C31-4131-9DEC-1D8B377D5A89}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C50AA3E3-8C31-4131-9DEC-1D8B377D5A89}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C50AA3E3-8C31-4131-9DEC-1D8B377D5A89}.Release|Any CPU.Build.0 = Release|Any CPU
{CA8EAE01-AB9F-4EC1-B6F3-73721487E9E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CA8EAE01-AB9F-4EC1-B6F3-73721487E9E1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CA8EAE01-AB9F-4EC1-B6F3-73721487E9E1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CA8EAE01-AB9F-4EC1-B6F3-73721487E9E1}.Release|Any CPU.Build.0 = Release|Any CPU
{80F965C4-E2A8-4F54-985D-73D06E45F9CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{80F965C4-E2A8-4F54-985D-73D06E45F9CE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{80F965C4-E2A8-4F54-985D-73D06E45F9CE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{80F965C4-E2A8-4F54-985D-73D06E45F9CE}.Release|Any CPU.Build.0 = Release|Any CPU
{C2DE264A-AA87-4012-B954-17E3F403A237}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C2DE264A-AA87-4012-B954-17E3F403A237}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C2DE264A-AA87-4012-B954-17E3F403A237}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C2DE264A-AA87-4012-B954-17E3F403A237}.Release|Any CPU.Build.0 = Release|Any CPU
{AF507D61-6766-4C20-9F58-23DC29508219}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AF507D61-6766-4C20-9F58-23DC29508219}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AF507D61-6766-4C20-9F58-23DC29508219}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AF507D61-6766-4C20-9F58-23DC29508219}.Release|Any CPU.Build.0 = Release|Any CPU
{25172C49-7AA4-4739-BB07-69785094C379}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{25172C49-7AA4-4739-BB07-69785094C379}.Debug|Any CPU.Build.0 = Debug|Any CPU
{25172C49-7AA4-4739-BB07-69785094C379}.Release|Any CPU.ActiveCfg = Release|Any CPU
{25172C49-7AA4-4739-BB07-69785094C379}.Release|Any CPU.Build.0 = Release|Any CPU
{AC2DB416-F05C-4296-9040-56D6AD4FCD27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AC2DB416-F05C-4296-9040-56D6AD4FCD27}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AC2DB416-F05C-4296-9040-56D6AD4FCD27}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AC2DB416-F05C-4296-9040-56D6AD4FCD27}.Release|Any CPU.Build.0 = Release|Any CPU
{68CF9BDF-94AC-4D2D-A7BD-D1C064F97051}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{68CF9BDF-94AC-4D2D-A7BD-D1C064F97051}.Debug|Any CPU.Build.0 = Debug|Any CPU
{68CF9BDF-94AC-4D2D-A7BD-D1C064F97051}.Release|Any CPU.ActiveCfg = Release|Any CPU
{68CF9BDF-94AC-4D2D-A7BD-D1C064F97051}.Release|Any CPU.Build.0 = Release|Any CPU
{A3869243-B462-4986-914B-94E407D8D20F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A3869243-B462-4986-914B-94E407D8D20F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A3869243-B462-4986-914B-94E407D8D20F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A3869243-B462-4986-914B-94E407D8D20F}.Release|Any CPU.Build.0 = Release|Any CPU
{617BD3C7-87D9-4D28-8AC9-4910945BB9FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{617BD3C7-87D9-4D28-8AC9-4910945BB9FC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{617BD3C7-87D9-4D28-8AC9-4910945BB9FC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{617BD3C7-87D9-4D28-8AC9-4910945BB9FC}.Release|Any CPU.Build.0 = Release|Any CPU
{67B888D9-C6C4-49F1-883C-5B964151D889}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{67B888D9-C6C4-49F1-883C-5B964151D889}.Debug|Any CPU.Build.0 = Debug|Any CPU
{67B888D9-C6C4-49F1-883C-5B964151D889}.Release|Any CPU.ActiveCfg = Release|Any CPU
{67B888D9-C6C4-49F1-883C-5B964151D889}.Release|Any CPU.Build.0 = Release|Any CPU
{7F3055BA-70AA-424C-8748-345AF35127E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7F3055BA-70AA-424C-8748-345AF35127E9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7F3055BA-70AA-424C-8748-345AF35127E9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7F3055BA-70AA-424C-8748-345AF35127E9}.Release|Any CPU.Build.0 = Release|Any CPU
{C32274D9-1B66-4D5C-82F9-EB3F10F46752}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C32274D9-1B66-4D5C-82F9-EB3F10F46752}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C32274D9-1B66-4D5C-82F9-EB3F10F46752}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C32274D9-1B66-4D5C-82F9-EB3F10F46752}.Release|Any CPU.Build.0 = Release|Any CPU
{A6DE3DA0-B242-4F49-AEF0-4E26AF92D16C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A6DE3DA0-B242-4F49-AEF0-4E26AF92D16C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A6DE3DA0-B242-4F49-AEF0-4E26AF92D16C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A6DE3DA0-B242-4F49-AEF0-4E26AF92D16C}.Release|Any CPU.Build.0 = Release|Any CPU
{4D40A101-07E6-4FF2-8934-83434932591D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4D40A101-07E6-4FF2-8934-83434932591D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4D40A101-07E6-4FF2-8934-83434932591D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4D40A101-07E6-4FF2-8934-83434932591D}.Release|Any CPU.Build.0 = Release|Any CPU
{20751904-0DFC-4126-BF2A-834B53841010}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{20751904-0DFC-4126-BF2A-834B53841010}.Debug|Any CPU.Build.0 = Debug|Any CPU
{20751904-0DFC-4126-BF2A-834B53841010}.Release|Any CPU.ActiveCfg = Release|Any CPU
{20751904-0DFC-4126-BF2A-834B53841010}.Release|Any CPU.Build.0 = Release|Any CPU
{7F1FDCF2-0CE8-4119-B3C1-F2CC6D7E1C36}.Debug|Any CPU.ActiveCfg = Debug|x64
{7F1FDCF2-0CE8-4119-B3C1-F2CC6D7E1C36}.Debug|Any CPU.Build.0 = Debug|x64
{7F1FDCF2-0CE8-4119-B3C1-F2CC6D7E1C36}.Release|Any CPU.ActiveCfg = Debug|x64
{7F1FDCF2-0CE8-4119-B3C1-F2CC6D7E1C36}.Release|Any CPU.Build.0 = Debug|x64
{19424B55-058C-4E9C-B86F-700AEF9EAEC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{19424B55-058C-4E9C-B86F-700AEF9EAEC3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{19424B55-058C-4E9C-B86F-700AEF9EAEC3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{19424B55-058C-4E9C-B86F-700AEF9EAEC3}.Release|Any CPU.Build.0 = Release|Any CPU
{881D71A3-D276-4108-98C6-0FFD32129B9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{881D71A3-D276-4108-98C6-0FFD32129B9C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{881D71A3-D276-4108-98C6-0FFD32129B9C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{881D71A3-D276-4108-98C6-0FFD32129B9C}.Release|Any CPU.Build.0 = Release|Any CPU
{EB83A3A3-F9B6-4281-8EBF-F7289FB5D885}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EB83A3A3-F9B6-4281-8EBF-F7289FB5D885}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EB83A3A3-F9B6-4281-8EBF-F7289FB5D885}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EB83A3A3-F9B6-4281-8EBF-F7289FB5D885}.Release|Any CPU.Build.0 = Release|Any CPU
{D8069A23-AD2E-4C9E-8574-7E8C45296A46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D8069A23-AD2E-4C9E-8574-7E8C45296A46}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D8069A23-AD2E-4C9E-8574-7E8C45296A46}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D8069A23-AD2E-4C9E-8574-7E8C45296A46}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{D92751C8-1039-4005-90B2-913E55E0B8BD} = {42826721-9A18-4762-8BA9-F1429DD5C5B1}
{01F98733-7352-47AD-A594-537D979DE3DE} = {E9DEBA00-50A4-485D-BA65-D8AB3E3467AB}
{DC570FFF-6FE5-47BD-8BC1-B471A6067786} = {FC224610-32D3-454E-9BC1-1219FE8ACD5F}
{E1C43415-3200-45F4-8BF9-A4DD7D7F2ED6} = {FC224610-32D3-454E-9BC1-1219FE8ACD5F}
{83EAD6F0-3CB3-456A-AD81-072127D0DE0E} = {E9DEBA00-50A4-485D-BA65-D8AB3E3467AB}
{26391930-F86F-47E0-A5F6-B89919E38CE1} = {E9DEBA00-50A4-485D-BA65-D8AB3E3467AB}
{D81C0B87-F0C1-4297-A147-02F001FB7E1E} = {FD4D6594-D81E-456F-8F2E-35B09E04A755}
{C9D4CA21-182B-4ED2-81BB-280A6FD713F6} = {2E00592E-558D-492D-88F9-3ECEE4C0C7DA}
{7291B93C-615D-42DE-B8C1-3F9DF643E0FC} = {2E00592E-558D-492D-88F9-3ECEE4C0C7DA}
{8AEF06C0-CA5C-4460-BC2D-ADE5F35D0434} = {2E00592E-558D-492D-88F9-3ECEE4C0C7DA}
{9584AEE5-CD59-46E6-93E6-2DC2B5285B75} = {42826721-9A18-4762-8BA9-F1429DD5C5B1}
{1E2644A9-6B31-4350-8772-CEAAD6EE0B21} = {9584AEE5-CD59-46E6-93E6-2DC2B5285B75}
{65A2F556-F14A-49F3-8A92-7F2E1E7ED3B5} = {9584AEE5-CD59-46E6-93E6-2DC2B5285B75}
{9C1ECA1D-DE98-4FB7-92D0-FC45BB308E5D} = {9584AEE5-CD59-46E6-93E6-2DC2B5285B75}
{A97F7177-86C7-4B38-A6ED-DA51BF762471} = {CCF48B65-33D1-4E8B-A57B-E03394730B21}
{139F7A79-69E4-4B8A-B2A5-6A30A66C495C} = {CCF48B65-33D1-4E8B-A57B-E03394730B21}
{7DFF1591-237D-499E-A767-EE37B93FB958} = {CCF48B65-33D1-4E8B-A57B-E03394730B21}
{CCF48B65-33D1-4E8B-A57B-E03394730B21} = {42826721-9A18-4762-8BA9-F1429DD5C5B1}
{11F7D41B-AFCA-4D29-BC08-285A14BF3A3B} = {2E00592E-558D-492D-88F9-3ECEE4C0C7DA}
{804E065F-914C-414A-AF84-009312C3CFF6} = {42826721-9A18-4762-8BA9-F1429DD5C5B1}
{7420652C-3046-4F38-BE64-9B9E69D76FA2} = {FD4D6594-D81E-456F-8F2E-35B09E04A755}
{C50AA3E3-8C31-4131-9DEC-1D8B377D5A89} = {59E8E8F3-4E42-4E92-83B3-B1C2AB901D18}
{E1C43415-3200-45F4-8BF9-A4DD7D7F2ED9} = {9584AEE5-CD59-46E6-93E6-2DC2B5285B75}
{AC2DB416-F05C-4296-9040-56D6AD4FCD27} = {9584AEE5-CD59-46E6-93E6-2DC2B5285B75}
{68CF9BDF-94AC-4D2D-A7BD-D1C064F97051} = {E9DEBA00-50A4-485D-BA65-D8AB3E3467AB}
{A3869243-B462-4986-914B-94E407D8D20F} = {2E00592E-558D-492D-88F9-3ECEE4C0C7DA}
{6067BA60-D279-4156-8AE1-6B44E2D19187} = {FC224610-32D3-454E-9BC1-1219FE8ACD5F}
{617BD3C7-87D9-4D28-8AC9-4910945BB9FC} = {57F59C0C-5687-4AF9-AE1C-1933B539F0E4}
{67B888D9-C6C4-49F1-883C-5B964151D889} = {57F59C0C-5687-4AF9-AE1C-1933B539F0E4}
{7F3055BA-70AA-424C-8748-345AF35127E9} = {57F59C0C-5687-4AF9-AE1C-1933B539F0E4}
{E9DEBA00-50A4-485D-BA65-D8AB3E3467AB} = {D92751C8-1039-4005-90B2-913E55E0B8BD}
{57F59C0C-5687-4AF9-AE1C-1933B539F0E4} = {D92751C8-1039-4005-90B2-913E55E0B8BD}
{FC224610-32D3-454E-9BC1-1219FE8ACD5F} = {D92751C8-1039-4005-90B2-913E55E0B8BD}
{E1C43415-3202-45F4-8BF9-A4DD7D7F2ED6} = {FC224610-32D3-454E-9BC1-1219FE8ACD5F}
{C32274D9-1B66-4D5C-82F9-EB3F10F46752} = {57F59C0C-5687-4AF9-AE1C-1933B539F0E4}
{6A40CBE4-ECAB-4CED-9917-5C64CBF75DA6} = {FC224610-32D3-454E-9BC1-1219FE8ACD5F}
{8AC2AD6D-6C74-4B24-8DF6-42717FC9B804} = {D92751C8-1039-4005-90B2-913E55E0B8BD}
{A6DE3DA0-B242-4F49-AEF0-4E26AF92D16C} = {8AC2AD6D-6C74-4B24-8DF6-42717FC9B804}
{4D40A101-07E6-4FF2-8934-83434932591D} = {8AC2AD6D-6C74-4B24-8DF6-42717FC9B804}
{20751904-0DFC-4126-BF2A-834B53841010} = {8AC2AD6D-6C74-4B24-8DF6-42717FC9B804}
{7F1FDCF2-0CE8-4119-B3C1-F2CC6D7E1C36} = {0AF38BA3-65A0-481B-8CBB-B82E406E1575}
{19424B55-058C-4E9C-B86F-700AEF9EAEC3} = {0AF38BA3-65A0-481B-8CBB-B82E406E1575}
{881D71A3-D276-4108-98C6-0FFD32129B9C} = {0AF38BA3-65A0-481B-8CBB-B82E406E1575}
{0AF38BA3-65A0-481B-8CBB-B82E406E1575} = {D92751C8-1039-4005-90B2-913E55E0B8BD}
{EB83A3A3-F9B6-4281-8EBF-F7289FB5D885} = {FD4D6594-D81E-456F-8F2E-35B09E04A755}
{D8069A23-AD2E-4C9E-8574-7E8C45296A46} = {0AF38BA3-65A0-481B-8CBB-B82E406E1575}
{2D5AE63D-85C0-43D1-84BF-04418ED93F63} = {804E065F-914C-414A-AF84-009312C3CFF6}
{89C4CBC7-1606-40DE-B6DA-FBE3AAC98395} = {2D5AE63D-85C0-43D1-84BF-04418ED93F63}
{631C295A-7CCF-4B42-8686-7034E31469E7} = {2D5AE63D-85C0-43D1-84BF-04418ED93F63}
{D940853C-003A-482C-BDB0-665367F274A0} = {2D5AE63D-85C0-43D1-84BF-04418ED93F63}
{2F45036E-D817-41E9-B82F-DBE013EC95D0} = {804E065F-914C-414A-AF84-009312C3CFF6}
{C2DE264A-AA87-4012-B954-17E3F403A237} = {2F45036E-D817-41E9-B82F-DBE013EC95D0}
{AF507D61-6766-4C20-9F58-23DC29508219} = {2F45036E-D817-41E9-B82F-DBE013EC95D0}
{91D70DE1-DC8E-4AE1-B100-0671D6263FC5} = {804E065F-914C-414A-AF84-009312C3CFF6}
{CA8EAE01-AB9F-4EC1-B6F3-73721487E9E1} = {91D70DE1-DC8E-4AE1-B100-0671D6263FC5}
{25172C49-7AA4-4739-BB07-69785094C379} = {91D70DE1-DC8E-4AE1-B100-0671D6263FC5}
{80F965C4-E2A8-4F54-985D-73D06E45F9CE} = {91D70DE1-DC8E-4AE1-B100-0671D6263FC5}
{35175682-DA83-4C0A-A49D-B191F5885D8E} = {91D70DE1-DC8E-4AE1-B100-0671D6263FC5}
{4721AA15-AF6E-4A62-A2C3-65564DC563E6} = {804E065F-914C-414A-AF84-009312C3CFF6}
{41BC679F-887F-44CF-971D-A5502EE87DB0} = {4721AA15-AF6E-4A62-A2C3-65564DC563E6}
{9ADD1B7A-6401-4202-8613-F668E2FBC0A4} = {4721AA15-AF6E-4A62-A2C3-65564DC563E6}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {EE253116-7070-4E9A-BCE8-2911C251B8C8}
EndGlobalSection
GlobalSection(SharedMSBuildProjectFiles) = preSolution
Connectors\Revit\Speckle.Connectors.RevitShared.Cef\Speckle.Connectors.RevitShared.Cef.projitems*{01f98733-7352-47ad-a594-537d979de3de}*SharedItemsImports = 5
Connectors\Revit\Speckle.Connectors.RevitShared\Speckle.Connectors.RevitShared.projitems*{01f98733-7352-47ad-a594-537d979de3de}*SharedItemsImports = 5
Converters\Revit\Speckle.Converters.RevitShared\Speckle.Converters.RevitShared.projitems*{19424b55-058c-4e9c-b86f-700aef9eaec3}*SharedItemsImports = 5
Converters\Revit\Speckle.Converters.RevitShared.DependencyInjection\Speckle.Converters.RevitShared.DependencyInjection.projitems*{20751904-0dfc-4126-bf2a-834b53841010}*SharedItemsImports = 5
Converters\Autocad\Speckle.Converters.AutocadShared\Speckle.Converters.AutocadShared.projitems*{25172c49-7aa4-4739-bb07-69785094c379}*SharedItemsImports = 5
Converters\Civil3d\Speckle.Converters.Civil3dShared\Speckle.Converters.Civil3dShared.projitems*{25172c49-7aa4-4739-bb07-69785094c379}*SharedItemsImports = 5
Converters\Revit\Speckle.Converters.RevitShared\Speckle.Converters.RevitShared.projitems*{26391930-f86f-47e0-a5f6-b89919e38ce1}*SharedItemsImports = 5
Converters\Civil3d\Speckle.Converters.Civil3dShared\Speckle.Converters.Civil3dShared.projitems*{35175682-da83-4c0a-a49d-b191f5885d8e}*SharedItemsImports = 13
Connectors\Autocad\Speckle.Connectors.AutocadShared\Speckle.Connectors.AutocadShared.projitems*{41bc679f-887f-44cf-971d-a5502ee87db0}*SharedItemsImports = 13
Converters\Revit\Speckle.Converters.RevitShared\Speckle.Converters.RevitShared.projitems*{4d40a101-07e6-4ff2-8934-83434932591d}*SharedItemsImports = 5
Converters\Revit\Speckle.Converters.RevitShared.DependencyInjection\Speckle.Converters.RevitShared.DependencyInjection.projitems*{6067ba60-d279-4156-8ae1-6b44e2d19187}*SharedItemsImports = 13
Connectors\Revit\Speckle.Connectors.RevitShared.Cef\Speckle.Connectors.RevitShared.Cef.projitems*{617bd3c7-87d9-4d28-8ac9-4910945bb9fc}*SharedItemsImports = 5
Connectors\Revit\Speckle.Connectors.RevitShared\Speckle.Connectors.RevitShared.projitems*{617bd3c7-87d9-4d28-8ac9-4910945bb9fc}*SharedItemsImports = 5
Converters\Autocad\Speckle.Converters.AutocadShared\Speckle.Converters.AutocadShared.projitems*{631c295a-7ccf-4b42-8686-7034e31469e7}*SharedItemsImports = 5
Converters\Rhino\Speckle.Converters.RhinoShared\Speckle.Converters.RhinoShared.projitems*{65a2f556-f14a-49f3-8a92-7f2e1e7ed3b5}*SharedItemsImports = 5
Converters\Revit\Speckle.Converters.RevitShared\Speckle.Converters.RevitShared.projitems*{67b888d9-c6c4-49f1-883c-5b964151d889}*SharedItemsImports = 5
Converters\Revit\Speckle.Converters.RevitShared.Tests\Speckle.Converters.RevitShared.Tests.projitems*{68cf9bdf-94ac-4d2d-a7bd-d1c064f97051}*SharedItemsImports = 5
Converters\Revit\Speckle.Converters.RevitShared\Speckle.Converters.RevitShared.projitems*{68cf9bdf-94ac-4d2d-a7bd-d1c064f97051}*SharedItemsImports = 5
Connectors\Revit\Speckle.Connectors.RevitShared.Cef\Speckle.Connectors.RevitShared.Cef.projitems*{6a40cbe4-ecab-4ced-9917-5c64cbf75da6}*SharedItemsImports = 13
Connectors\Revit\Speckle.Connectors.RevitShared.Cef\Speckle.Connectors.RevitShared.Cef.projitems*{7f1fdcf2-0ce8-4119-b3c1-f2cc6d7e1c36}*SharedItemsImports = 5
Connectors\Revit\Speckle.Connectors.RevitShared\Speckle.Connectors.RevitShared.projitems*{7f1fdcf2-0ce8-4119-b3c1-f2cc6d7e1c36}*SharedItemsImports = 5
Converters\Revit\Speckle.Converters.RevitShared.DependencyInjection\Speckle.Converters.RevitShared.DependencyInjection.projitems*{7f3055ba-70aa-424c-8748-345af35127e9}*SharedItemsImports = 5
Converters\Revit\Speckle.Converters.RevitShared.DependencyInjection\Speckle.Converters.RevitShared.DependencyInjection.projitems*{83ead6f0-3cb3-456a-ad81-072127d0de0e}*SharedItemsImports = 5
Converters\Revit\Speckle.Converters.RevitShared.DependencyInjection\Speckle.Converters.RevitShared.DependencyInjection.projitems*{881d71a3-d276-4108-98c6-0ffd32129b9c}*SharedItemsImports = 5
Connectors\Autocad\Speckle.Connectors.AutocadShared\Speckle.Connectors.AutocadShared.projitems*{89c4cbc7-1606-40de-b6da-fbe3aac98395}*SharedItemsImports = 5
Converters\Autocad\Speckle.Converters.AutocadShared\Speckle.Converters.AutocadShared.projitems*{9add1b7a-6401-4202-8613-f668e2fbc0a4}*SharedItemsImports = 13
Connectors\Revit\Speckle.Connectors.RevitShared\Speckle.Connectors.RevitShared.projitems*{a6de3da0-b242-4f49-aef0-4e26af92d16c}*SharedItemsImports = 5
Converters\Rhino\Speckle.Converters.RhinoShared\Speckle.Converters.RhinoShared.projitems*{ac2db416-f05c-4296-9040-56d6ad4fcd27}*SharedItemsImports = 5
Converters\Autocad\Speckle.Converters.AutocadShared\Speckle.Converters.AutocadShared.projitems*{c2de264a-aa87-4012-b954-17e3f403a237}*SharedItemsImports = 5
Converters\Revit\Speckle.Converters.RevitShared.Tests\Speckle.Converters.RevitShared.Tests.projitems*{c32274d9-1b66-4d5c-82f9-eb3f10f46752}*SharedItemsImports = 5
Converters\Revit\Speckle.Converters.RevitShared\Speckle.Converters.RevitShared.projitems*{c32274d9-1b66-4d5c-82f9-eb3f10f46752}*SharedItemsImports = 5
Connectors\Autocad\Speckle.Connectors.AutocadShared\Speckle.Connectors.AutocadShared.projitems*{ca8eae01-ab9f-4ec1-b6f3-73721487e9e1}*SharedItemsImports = 5
Converters\Revit\Speckle.Converters.RevitShared.Tests\Speckle.Converters.RevitShared.Tests.projitems*{d8069a23-ad2e-4c9e-8574-7e8c45296a46}*SharedItemsImports = 5
Converters\Revit\Speckle.Converters.RevitShared\Speckle.Converters.RevitShared.projitems*{d8069a23-ad2e-4c9e-8574-7e8c45296a46}*SharedItemsImports = 5
Connectors\Revit\Speckle.Connectors.RevitShared\Speckle.Connectors.RevitShared.projitems*{dc570fff-6fe5-47bd-8bc1-b471a6067786}*SharedItemsImports = 13
Converters\Revit\Speckle.Converters.RevitShared\Speckle.Converters.RevitShared.projitems*{e1c43415-3200-45f4-8bf9-a4dd7d7f2ed6}*SharedItemsImports = 13
Converters\Rhino\Speckle.Converters.RhinoShared\Speckle.Converters.RhinoShared.projitems*{e1c43415-3200-45f4-8bf9-a4dd7d7f2ed9}*SharedItemsImports = 13
Converters\Revit\Speckle.Converters.RevitShared.Tests\Speckle.Converters.RevitShared.Tests.projitems*{e1c43415-3202-45f4-8bf9-a4dd7d7f2ed6}*SharedItemsImports = 13
EndGlobalSection
EndGlobal
-116
View File
@@ -1,116 +0,0 @@
using GlobExpressions;
using Microsoft.Build.Construction;
using static SimpleExec.Command;
namespace Build;
public static class Affected
{
public static readonly string Root = Environment.CurrentDirectory;
public const string AFFECTED_PROJECT = "affected.proj";
private static IEnumerable<string> GetAffectedProjects()
{
var projFile = Path.Combine(Root, AFFECTED_PROJECT);
Console.WriteLine("Affected project file: " + projFile);
var project = ProjectRootElement.Open(projFile) ?? throw new InvalidOperationException();
var references = project.ItemGroups.SelectMany(x => x.Items).Where(x => x.ItemType == "ProjectReference");
foreach (var refe in references)
{
var referencePath = refe.Include[(Root.Length + 1)..];
referencePath = Path.GetDirectoryName(referencePath) ?? throw new InvalidOperationException();
if (Path.DirectorySeparatorChar != '/')
{
referencePath = referencePath.Replace(Path.DirectorySeparatorChar, '/');
}
yield return referencePath;
}
}
public static async Task<IEnumerable<string>> GetTestProjects()
{
await ComputeAffected();
var projFile = Path.Combine(Root, AFFECTED_PROJECT);
if (File.Exists(projFile))
{
var references = GetAffectedProjects();
return references.Where(x => x.Contains("Tests"));
}
return Glob.Files(Root, "**/*.Tests.csproj");
}
public static async Task<ProjectGroup[]> GetAffectedProjectGroups()
{
await ComputeAffected();
var projFile = Path.Combine(Root, AFFECTED_PROJECT);
if (File.Exists(projFile))
{
var references = GetAffectedProjects().ToList();
var groups = new List<ProjectGroup>();
foreach (var projectGroup in Consts.ProjectGroups)
{
foreach (var referencePath in references)
{
if (projectGroup.Projects.Any(x => x.ProjectPath.Contains(referencePath)))
{
groups.Add(projectGroup);
break;
}
}
}
foreach (var group in groups)
{
Console.WriteLine("Affected project group being built: " + group.HostAppSlug);
}
return groups.ToArray();
}
Console.WriteLine("Using all project groups: " + string.Join(',', Consts.ProjectGroups));
return Consts.ProjectGroups;
}
private static bool s_affectedComputed;
public static async Task ComputeAffected()
{
if (s_affectedComputed)
{
return;
}
var currentTag = await Versions.GetCurrentTag();
var currentVersion = await Versions.ComputeVersion();
var lastTag = await Versions.GetPreviousTag(currentTag);
var lastVersion = await Versions.ComputePreviousVersion(currentTag);
Console.WriteLine($"Last tag: {lastTag}, Current tag: {currentTag}");
Console.WriteLine($"Last parsed version: {lastVersion}, Current parsed version: {currentVersion}");
var sort = currentVersion.CompareSortOrderTo(lastVersion);
if (sort == -1)
{
Console.WriteLine($"Current version {currentVersion} is less than: {lastVersion}");
s_affectedComputed = true;
return;
}
var majorEquals = currentVersion.Major == lastVersion.Major;
if (!majorEquals)
{
Console.WriteLine($"Current version {currentVersion} is not matching major version: {lastVersion}");
s_affectedComputed = true;
return;
}
//use tags no matter the version if major versions match
var (currentCommit, _) = await ReadAsync("git", $"rev-list -n 1 {currentTag}");
var (lastCommit, _) = await ReadAsync("git", $"rev-list -n 1 {lastTag}");
await RunAsync("dotnet", $"affected -v --from {currentCommit.Trim()} --to {lastCommit.Trim()}", Root);
s_affectedComputed = true;
}
}
-2
View File
@@ -10,8 +10,6 @@
<PackageReference Include="Bullseye" />
<PackageReference Include="Glob" />
<PackageReference Include="Microsoft.Build" />
<PackageReference Include="Microsoft.VisualStudio.SolutionPersistence" />
<PackageReference Include="Semver" />
<PackageReference Include="SimpleExec" />
</ItemGroup>
</Project>
+10 -14
View File
@@ -4,13 +4,14 @@ public static class Consts
{
public static readonly string[] Solutions = ["Speckle.Connectors.sln"];
public static readonly ProjectGroup[] ProjectGroups =
public static readonly InstallerProject[] InstallerManifests =
{
new("arcgis", [new("Connectors/ArcGIS/Speckle.Connectors.ArcGIS3", "net6.0-windows")]),
new(
"rhino",
[
new("Connectors/Rhino/Speckle.Connectors.Rhino7", "net48"),
new("Connectors/Rhino/Speckle.Connectors.Rhino8", "net48"),
new("Connectors/Rhino/Speckle.Connectors.Rhino8", "net48")
]
),
new(
@@ -19,8 +20,7 @@ public static class Consts
new("Connectors/Revit/Speckle.Connectors.Revit2022", "net48"),
new("Connectors/Revit/Speckle.Connectors.Revit2023", "net48"),
new("Connectors/Revit/Speckle.Connectors.Revit2024", "net48"),
new("Connectors/Revit/Speckle.Connectors.Revit2025", "net8.0-windows"),
new("Connectors/Revit/Speckle.Connectors.Revit2026", "net8.0-windows")
new("Connectors/Revit/Speckle.Connectors.Revit2025", "net8.0-windows")
]
),
new(
@@ -29,8 +29,7 @@ public static class Consts
new("Connectors/Autocad/Speckle.Connectors.Autocad2022", "net48"),
new("Connectors/Autocad/Speckle.Connectors.Autocad2023", "net48"),
new("Connectors/Autocad/Speckle.Connectors.Autocad2024", "net48"),
new("Connectors/Autocad/Speckle.Connectors.Autocad2025", "net8.0-windows"),
new("Connectors/Autocad/Speckle.Connectors.Autocad2026", "net8.0-windows")
new("Connectors/Autocad/Speckle.Connectors.Autocad2025", "net8.0-windows")
]
),
new(
@@ -39,8 +38,7 @@ public static class Consts
new("Connectors/Autocad/Speckle.Connectors.Civil3d2022", "net48"),
new("Connectors/Autocad/Speckle.Connectors.Civil3d2023", "net48"),
new("Connectors/Autocad/Speckle.Connectors.Civil3d2024", "net48"),
new("Connectors/Autocad/Speckle.Connectors.Civil3d2025", "net8.0-windows"),
new("Connectors/Autocad/Speckle.Connectors.Civil3d2026", "net8.0-windows")
new("Connectors/Autocad/Speckle.Connectors.Civil3d2025", "net8.0-windows")
]
),
new(
@@ -51,16 +49,14 @@ public static class Consts
new("Connectors/Navisworks/Speckle.Connectors.Navisworks2022", "net48"),
new("Connectors/Navisworks/Speckle.Connectors.Navisworks2023", "net48"),
new("Connectors/Navisworks/Speckle.Connectors.Navisworks2024", "net48"),
new("Connectors/Navisworks/Speckle.Connectors.Navisworks2025", "net48"),
new("Connectors/Navisworks/Speckle.Connectors.Navisworks2026", "net48")
new("Connectors/Navisworks/Speckle.Connectors.Navisworks2025", "net48")
]
),
new(
"teklastructures",
"tekla-structures",
[
new("Connectors/Tekla/Speckle.Connector.Tekla2023", "net48"),
new("Connectors/Tekla/Speckle.Connector.Tekla2024", "net48"),
new("Connectors/Tekla/Speckle.Connector.Tekla2025", "net48")
new("Connectors/Tekla/Speckle.Connector.Tekla2024", "net48")
]
),
new(
@@ -73,7 +69,7 @@ public static class Consts
};
}
public readonly record struct ProjectGroup(string HostAppSlug, IReadOnlyList<InstallerAsset> Projects)
public readonly record struct InstallerProject(string HostAppSlug, IReadOnlyList<InstallerAsset> Projects)
{
public override string ToString() => $"{HostAppSlug}";
}
+89 -91
View File
@@ -12,13 +12,13 @@ const string TEST = "test";
const string TEST_ONLY = "test-only";
const string FORMAT = "format";
const string ZIP = "zip";
const string VERSION = "version";
const string RESTORE_TOOLS = "restore-tools";
const string BUILD_SERVER_VERSION = "build-server-version";
const string CLEAN_LOCKS = "clean-locks";
const string CHECK_SOLUTIONS = "check-solutions";
const string GEN_SOLUTIONS = "generate-solutions";
const string DEEP_CLEAN = "deep-clean";
const string DEEP_CLEAN_LOCAL = "deep-clean-local";
const string DETECT_AFFECTED = "detect-affected";
//need to pass arguments
/*var arguments = new List<string>();
@@ -28,13 +28,20 @@ if (args.Length > 1)
args = new[] { arguments.First() };
//arguments = arguments.Skip(1).ToList();
}*/
void Build(string solution, string configuration)
{
Console.WriteLine();
Console.WriteLine();
Console.WriteLine($"Building solution '{solution}' as '{configuration}'");
Console.WriteLine();
Run("dotnet", $"build .\\{solution} --configuration {configuration} --no-restore");
}
void Restore(string solution)
{
Console.WriteLine();
Console.WriteLine($"Restoring solution '{solution}'");
Console.WriteLine();
Run("dotnet", $"restore \".\\{solution}\" --no-cache");
Run("dotnet", $"restore .\\{solution} --no-cache");
}
void DeleteFiles(string pattern)
{
@@ -65,31 +72,30 @@ void CleanSolution(string solution, string configuration)
DeleteDirectories("**/obj");
DeleteFiles("**/*.lock.json");
Restore(solution);
Build(solution, configuration);
}
Target(
CLEAN_LOCKS,
Consts.Solutions,
s =>
() =>
{
DeleteFiles("**/*.lock.json");
Restore(s);
Restore("Speckle.Connectors.sln");
}
);
Target(
DEEP_CLEAN,
Consts.Solutions,
s =>
() =>
{
CleanSolution(s, "debug");
CleanSolution("Speckle.Connectors.sln", "debug");
}
);
Target(
DEEP_CLEAN_LOCAL,
() =>
{
CleanSolution("Local.sln", "Local");
CleanSolution("Local.sln", "local");
}
);
@@ -120,22 +126,21 @@ Target(
);
Target(
RESTORE_TOOLS,
() =>
VERSION,
async () =>
{
Run("dotnet", "tool restore");
var (output, _) = await ReadAsync("dotnet", "minver -v w");
output = output.Trim();
Console.WriteLine($"Version: {output}");
Run("echo", $"\"version={output}\" >> $GITHUB_OUTPUT");
}
);
Target(
DETECT_AFFECTED,
DependsOn(RESTORE_TOOLS),
async () =>
RESTORE_TOOLS,
() =>
{
foreach (var group in await Affected.GetAffectedProjectGroups())
{
Console.WriteLine("Affected project group being built: " + group.HostAppSlug);
}
Run("dotnet", "tool restore");
}
);
@@ -150,14 +155,20 @@ Target(
Target(
RESTORE,
DependsOn(FORMAT, DETECT_AFFECTED),
DependsOn(FORMAT),
Consts.Solutions,
async s =>
s =>
{
var version = await Versions.ComputeVersion();
var fileVersion = await Versions.ComputeFileVersion();
Console.WriteLine($"Restoring: {s} - Version: {version} & {fileVersion}");
await RunAsync("dotnet", $"restore \"{s}\" --locked-mode");
Run("dotnet", $"restore {s} --locked-mode");
}
);
Target(
BUILD_SERVER_VERSION,
DependsOn(RESTORE_TOOLS),
() =>
{
Run("dotnet", "tool run dotnet-gitversion /output json /output buildserver");
}
);
@@ -165,44 +176,40 @@ Target(
BUILD,
DependsOn(RESTORE),
Consts.Solutions,
async s =>
s =>
{
var version = await Versions.ComputeVersion();
var fileVersion = await Versions.ComputeFileVersion();
Console.WriteLine($"Restoring: {s} - Version: {version} & {fileVersion}");
await RunAsync(
var version = Environment.GetEnvironmentVariable("GitVersion_FullSemVer") ?? "3.0.0-localBuild";
var fileVersion = Environment.GetEnvironmentVariable("GitVersion_AssemblySemFileVer") ?? "3.0.0.0";
Console.WriteLine($"Version: {version} & {fileVersion}");
Run(
"dotnet",
$"build \"{s}\" -c Release --no-restore -warnaserror -p:Version={version} -p:FileVersion={fileVersion} -v:m"
$"build {s} -c Release --no-restore -warnaserror -p:Version={version} -p:FileVersion={fileVersion} -v:m"
);
}
);
Target(CHECK_SOLUTIONS, Solutions.CompareConnectorsToLocal);
Target(GEN_SOLUTIONS, Solutions.GenerateSolutions);
Target(
TEST,
DependsOn(BUILD, CHECK_SOLUTIONS),
async () =>
Glob.Files(".", "**/*.Tests.csproj"),
file =>
{
foreach (var s in await Affected.GetTestProjects())
{
await RunAsync("dotnet", $"test \"{s}\" -c Release --no-build --no-restore --verbosity=minimal");
}
Run("dotnet", $"test {file} -c Release --no-build --no-restore --verbosity=minimal");
}
);
//all tests on purpose
Target(
TEST_ONLY,
DependsOn(FORMAT),
Glob.Files(".", "**/*.Tests.csproj"),
file =>
{
Run("dotnet", $"build \"{file}\" -c Release --no-incremental");
Run("dotnet", $"build {file} -c Release --no-incremental");
Run(
"dotnet",
$"test \"{file}\" -c Release --no-build --verbosity=minimal /p:AltCover=true /p:AltCoverAttributeFilter=ExcludeFromCodeCoverage /p:AltCoverVerbosity=Warning"
$"test {file} -c Release --no-build --verbosity=minimal /p:AltCover=true /p:AltCoverAttributeFilter=ExcludeFromCodeCoverage /p:AltCoverVerbosity=Warning"
);
}
);
@@ -211,20 +218,20 @@ Target(
BUILD_LINUX,
DependsOn(FORMAT),
Glob.Files(".", "**/Speckle.Importers.Ifc.csproj"),
async file =>
file =>
{
await RunAsync("dotnet", $"restore \"{file}\" --locked-mode");
var version = await Versions.ComputeVersion();
var fileVersion = await Versions.ComputeFileVersion();
Run("dotnet", $"restore {file} --locked-mode");
var version = Environment.GetEnvironmentVariable("GitVersion_FullSemVer") ?? "3.0.0-localBuild";
var fileVersion = Environment.GetEnvironmentVariable("GitVersion_AssemblySemFileVer") ?? "3.0.0.0";
Console.WriteLine($"Version: {version} & {fileVersion}");
await RunAsync(
Run(
"dotnet",
$"build \"{file}\" -c Release --no-restore -warnaserror -p:Version={version} -p:FileVersion={fileVersion} -v:m"
$"build {file} -c Release --no-restore -warnaserror -p:Version={version} -p:FileVersion={fileVersion} -v:m"
);
await RunAsync(
RunAsync(
"dotnet",
$"pack \"{file}\" -c Release -o output --no-build -p:Version={version} -p:FileVersion={fileVersion} -v:m"
$"pack {file} -c Release -o output --no-build -p:Version={version} -p:FileVersion={fileVersion} -v:m"
);
}
);
@@ -232,56 +239,47 @@ Target(
Target(
ZIP,
DependsOn(TEST),
async () =>
Consts.InstallerManifests,
x =>
{
var version = await Versions.ComputeVersion();
var fileVersion = await Versions.ComputeFileVersion();
foreach (var group in await Affected.GetAffectedProjectGroups())
var outputDir = Path.Combine(".", "output");
var slugDir = Path.Combine(outputDir, x.HostAppSlug);
Directory.CreateDirectory(outputDir);
Directory.CreateDirectory(slugDir);
foreach (var asset in x.Projects)
{
Console.WriteLine($"Zipping: {group.HostAppSlug} as {version}");
var outputDir = Path.Combine(".", "output");
var slugDir = Path.Combine(outputDir, group.HostAppSlug);
Directory.CreateDirectory(outputDir);
Directory.CreateDirectory(slugDir);
foreach (var asset in group.Projects)
var fullPath = Path.Combine(".", asset.ProjectPath, "bin", "Release", asset.TargetName);
if (!Directory.Exists(fullPath))
{
var fullPath = Path.Combine(".", asset.ProjectPath, "bin", "Release", asset.TargetName);
if (!Directory.Exists(fullPath))
{
throw new InvalidOperationException("Could not find: " + fullPath);
}
var assetName = Path.GetFileName(asset.ProjectPath);
var connectorDir = Path.Combine(slugDir, assetName);
Directory.CreateDirectory(connectorDir);
foreach (var directory in Directory.EnumerateDirectories(fullPath, "*", SearchOption.AllDirectories))
{
Directory.CreateDirectory(directory.Replace(fullPath, connectorDir));
}
foreach (var file in Directory.EnumerateFiles(fullPath, "*", SearchOption.AllDirectories))
{
Console.WriteLine(file);
File.Copy(file, file.Replace(fullPath, connectorDir), true);
}
throw new InvalidOperationException("Could not find: " + fullPath);
}
var outputPath = Path.Combine(outputDir, $"{group.HostAppSlug}.zip");
File.Delete(outputPath);
Console.WriteLine($"Zipping: '{slugDir}' to '{outputPath}'");
ZipFile.CreateFromDirectory(slugDir, outputPath);
var assetName = Path.GetFileName(asset.ProjectPath);
var connectorDir = Path.Combine(slugDir, assetName);
Directory.CreateDirectory(connectorDir);
foreach (var directory in Directory.EnumerateDirectories(fullPath, "*", SearchOption.AllDirectories))
{
Directory.CreateDirectory(directory.Replace(fullPath, connectorDir));
}
foreach (var file in Directory.EnumerateFiles(fullPath, "*", SearchOption.AllDirectories))
{
Console.WriteLine(file);
File.Copy(file, file.Replace(fullPath, connectorDir), true);
}
}
string githubEnv = Environment.GetEnvironmentVariable("GITHUB_ENV") ?? "Unset";
Console.WriteLine($"GITHUB_ENV: {githubEnv}");
File.AppendAllText(githubEnv, $"SEMVER={version}{Environment.NewLine}");
File.AppendAllText(githubEnv, $"FILE_VERSION={fileVersion}{Environment.NewLine}");
var outputPath = Path.Combine(outputDir, $"{x.HostAppSlug}.zip");
File.Delete(outputPath);
Console.WriteLine($"Zipping: '{slugDir}' to '{outputPath}'");
ZipFile.CreateFromDirectory(slugDir, outputPath);
// Directory.Delete(slugDir, true);
}
);
Target("default", DependsOn(TEST), () => Console.WriteLine("Done!"));
Target("default", DependsOn(FORMAT, ZIP), () => Console.WriteLine("Done!"));
await RunTargetsAndExitAsync(args).ConfigureAwait(true);
+19 -80
View File
@@ -1,49 +1,46 @@
using Microsoft.VisualStudio.SolutionPersistence.Model;
using Microsoft.VisualStudio.SolutionPersistence.Serializer;
using Microsoft.Build.Construction;
namespace Build;
public static class Solutions
{
#pragma warning disable CA1802
#pragma warning disable IDE1006
private static readonly string DIRECTORY = Environment.CurrentDirectory;
#pragma warning restore IDE1006
#pragma warning restore CA1802
public static async Task CompareConnectorsToLocal()
{
var localSln = await GetSolution("Local.sln");
var connectorsSln = await GetSolution("Speckle.Connectors.sln");
var localProjects = localSln.SolutionProjects.ToList();
private static bool ValidProjects(KeyValuePair<string, ProjectInSolution> projectInSolution) =>
projectInSolution.Value.ProjectType == SolutionProjectType.KnownToBeMSBuildFormat;
foreach (var value in connectorsSln.SolutionProjects)
public static void CompareConnectorsToLocal()
{
var localSln = SolutionFile.Parse(Path.Combine(Environment.CurrentDirectory, "Local.sln"));
var connectorsSln = SolutionFile.Parse(Path.Combine(Environment.CurrentDirectory, "Speckle.Connectors.sln"));
var localProjects = localSln.ProjectsByGuid.Where(ValidProjects).ToDictionary();
foreach ((string? _, ProjectInSolution? value) in connectorsSln.ProjectsByGuid.Where(ValidProjects))
{
var localProject = localProjects.FirstOrDefault(x => x.ActualDisplayName == value.ActualDisplayName);
var localProject = localProjects.Values.FirstOrDefault(x => x.ProjectName == value.ProjectName);
if (localProject is null)
{
throw new InvalidOperationException($"Could not find in LOCAL solution: {value.ActualDisplayName}");
throw new InvalidOperationException($"Could not find in LOCAL solution: {value.ProjectName}");
}
if (value.ActualDisplayName != localProject.ActualDisplayName)
if (value.ProjectName != localProject.ProjectName)
{
throw new InvalidOperationException(
"Projects with different names have same Guid in solution: "
+ value.ActualDisplayName
+ value.ProjectName
+ " and "
+ localProject.ActualDisplayName
+ localProject.ProjectName
);
}
localProjects.Remove(localProjects.Single(x => x.ActualDisplayName == value.ActualDisplayName));
localProjects.Remove(localProjects.Single(x => x.Value.ProjectName == value.ProjectName).Key);
}
void CheckAndRemoveKnown(string projectName)
{
var localProject = localProjects.FirstOrDefault(x => x.ActualDisplayName == projectName);
var localProject = localProjects.Values.FirstOrDefault(x => x.ProjectName == projectName);
if (localProject is null)
{
throw new InvalidOperationException($"Could not find in LOCAL solution: {projectName}");
}
localProjects.Remove(localProjects.Single(x => x.ActualDisplayName == projectName));
localProjects.Remove(localProjects.Single(x => x.Value.ProjectName == projectName).Key);
}
CheckAndRemoveKnown("Speckle.Objects");
@@ -52,66 +49,8 @@ public static class Solutions
if (localProjects.Count != 0)
{
throw new InvalidOperationException(
"Could not find in CONNECTOR solution: " + localProjects.First().ActualDisplayName
"Could not find in CONNECTOR solution: " + localProjects.First().Value.ProjectName
);
}
}
public static async Task GenerateSolutions()
{
await GenerateLocalSlnx();
foreach (var group in Consts.ProjectGroups)
{
var connectors = await GetFullSlnx();
var slug = group.HostAppSlug;
await GenerateConnector(connectors, group, string.Concat(slug[0].ToString().ToUpper(), slug.AsSpan(1)));
}
}
public static async Task GenerateLocalSlnx()
{
var connectors = await GetFullSlnx();
connectors.AddProject("..\\speckle-sharp-sdk\\src\\Speckle.Objects\\Speckle.Objects.csproj");
connectors.AddProject("..\\speckle-sharp-sdk\\src\\Speckle.Sdk\\Speckle.Sdk.csproj");
connectors.AddProject("..\\speckle-sharp-sdk\\src\\Speckle.Sdk.Dependencies\\Speckle.Sdk.Dependencies.csproj");
var sln = Path.Combine(DIRECTORY, "Local.slnx");
await SolutionSerializers.SlnXml.SaveAsync(sln, connectors, default);
sln = Path.Combine(DIRECTORY, "Local.sln");
await SolutionSerializers.SlnFileV12.SaveAsync(sln, connectors, default);
var revit = Consts.ProjectGroups.Single(x => x.HostAppSlug.Equals("revit"));
await GenerateConnector(connectors, revit, "Revit.Local");
}
public static async Task GenerateConnector(SolutionModel connectors, ProjectGroup group, string? name)
{
var path = group.Projects[0].ProjectPath.Split('/');
var folder = $"/{path[0]}/{path[1]}/";
var foldersToRemove = connectors
.SolutionFolders.Where(x =>
//need base folder
!x.Path.Equals("/Connectors/")
//don't grab all
&& (x.Path.StartsWith("/Connectors/") && !x.Path.StartsWith(folder))
)
.ToList();
foreach (var folderToRemove in foldersToRemove)
{
connectors.RemoveFolder(folderToRemove);
}
var sln = Path.Combine(DIRECTORY, $"Speckle.{name}.slnx");
await SolutionSerializers.SlnXml.SaveAsync(sln, connectors, default);
}
public static async Task<SolutionModel> GetFullSlnx()
{
var connectorsSln = Path.Combine(DIRECTORY, "Speckle.Connectors.slnx");
return await SolutionSerializers.SlnXml.OpenAsync(connectorsSln, default);
}
public static async Task<SolutionModel> GetSolution(string solutionName)
{
var connectorsSln = Path.Combine(DIRECTORY, solutionName);
return await SolutionSerializers.SlnFileV12.OpenAsync(connectorsSln, default);
}
}
-79
View File
@@ -1,79 +0,0 @@
using Semver;
using static SimpleExec.Command;
namespace Build;
public static class Versions
{
private static string? s_currentTag;
private static SemVersion? s_currentVersion;
public static async Task<string> GetCurrentTag()
{
if (s_currentTag is not null)
{
return s_currentTag;
}
//finds current tag or makes one
var (currentTag, _) = await ReadAsync("git", "describe --tags");
currentTag = currentTag.Trim();
s_currentTag = currentTag;
return s_currentTag;
}
public static async Task<SemVersion> ComputeVersion()
{
if (s_currentVersion is not null)
{
return s_currentVersion;
}
var currentTag = await GetCurrentTag();
if (!SemVersion.TryParse(currentTag, SemVersionStyles.AllowLowerV, out var currentVersion))
{
throw new InvalidOperationException($"Could not parse version: '{currentTag}'");
}
s_currentVersion = currentVersion;
return s_currentVersion;
}
private static string? s_currentFileVersion;
public static async Task<string> ComputeFileVersion()
{
if (s_currentFileVersion is not null)
{
return s_currentFileVersion;
}
var currentVersion = await ComputeVersion();
s_currentFileVersion = currentVersion.WithoutPrereleaseOrMetadata() + ".0";
return s_currentFileVersion;
}
public static async Task<string> GetPreviousTag(string currentTag)
{
//finds a tag starting with current tag and adds no abbrevation
var (lastTag, _) = await ReadAsync("git", $"describe --abbrev=0 --tags {currentTag}^");
lastTag = lastTag.Trim();
return lastTag;
}
private static SemVersion? s_previousVersion;
public static async Task<SemVersion> ComputePreviousVersion(string currentTag)
{
if (s_previousVersion is not null)
{
return s_previousVersion;
}
var lastTag = await GetPreviousTag(currentTag);
if (!SemVersion.TryParse(lastTag, SemVersionStyles.AllowLowerV, out var lastVersion))
{
throw new InvalidOperationException($"Could not parse version: '{lastTag}'");
}
s_previousVersion = lastVersion;
return s_previousVersion;
}
}
-20
View File
@@ -47,27 +47,12 @@
"Microsoft.SourceLink.Common": "8.0.0"
}
},
"Microsoft.VisualStudio.SolutionPersistence": {
"type": "Direct",
"requested": "[1.0.52, )",
"resolved": "1.0.52",
"contentHash": "oNv2JtYXhpdJrX63nibx1JT3uCESOBQ1LAk7Dtz/sr0+laW0KRM6eKp4CZ3MHDR2siIkKsY8MmUkeP5DKkQQ5w=="
},
"PolySharp": {
"type": "Direct",
"requested": "[1.14.1, )",
"resolved": "1.14.1",
"contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ=="
},
"Semver": {
"type": "Direct",
"requested": "[3.0.0, )",
"resolved": "3.0.0",
"contentHash": "9jZCicsVgTebqkAujRWtC9J1A5EQVlu0TVKHcgoCuv345ve5DYf4D1MjhKEnQjdRZo6x/vdv6QQrYFs7ilGzLA==",
"dependencies": {
"Microsoft.Extensions.Primitives": "5.0.1"
}
},
"SimpleExec": {
"type": "Direct",
"requested": "[12.0.0, )",
@@ -90,11 +75,6 @@
"resolved": "8.0.0",
"contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
},
"Microsoft.Extensions.Primitives": {
"type": "Transitive",
"resolved": "5.0.1",
"contentHash": "5WPSmL4YeP7eW+Vc8XZ4DwjYWBAiSwDV9Hm63JJWcz1Ie3Xjv4KuJXzgCstj48LkLfVCYa7mLcx7y+q6yqVvtw=="
},
"Microsoft.NET.StringTools": {
"type": "Transitive",
"resolved": "17.11.4",
@@ -0,0 +1,105 @@
using ArcGIS.Desktop.Core;
using ArcGIS.Desktop.Mapping;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Common.Cancellation;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Logging;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Converters.ArcGIS3;
using Speckle.Converters.ArcGIS3.Utils;
using Speckle.Converters.Common;
using Speckle.Sdk;
namespace Speckle.Connectors.ArcGIS.Bindings;
public sealed class ArcGISReceiveBinding : IReceiveBinding
{
public string Name { get; } = "receiveBinding";
private readonly ICancellationManager _cancellationManager;
private readonly DocumentModelStore _store;
private readonly IServiceProvider _serviceProvider;
private readonly IOperationProgressManager _operationProgressManager;
private readonly ILogger<ArcGISReceiveBinding> _logger;
private readonly IArcGISConversionSettingsFactory _arcGISConversionSettingsFactory;
private ReceiveBindingUICommands Commands { get; }
public IBrowserBridge Parent { get; }
public ArcGISReceiveBinding(
DocumentModelStore store,
IBrowserBridge parent,
ICancellationManager cancellationManager,
IServiceProvider serviceProvider,
IOperationProgressManager operationProgressManager,
ILogger<ArcGISReceiveBinding> logger,
IArcGISConversionSettingsFactory arcGisConversionSettingsFactory
)
{
_store = store;
_cancellationManager = cancellationManager;
Parent = parent;
Commands = new ReceiveBindingUICommands(parent);
_serviceProvider = serviceProvider;
_operationProgressManager = operationProgressManager;
_logger = logger;
_arcGISConversionSettingsFactory = arcGisConversionSettingsFactory;
}
public async Task Receive(string modelCardId)
{
try
{
// Get receiver card
if (_store.GetModelById(modelCardId) is not ReceiverModelCard modelCard)
{
// Handle as GLOBAL ERROR at BrowserBridge
throw new InvalidOperationException("No download model card was found.");
}
using var cancellationItem = _cancellationManager.GetCancellationItem(modelCardId);
using var scope = _serviceProvider.CreateScope();
scope
.ServiceProvider.GetRequiredService<IConverterSettingsStore<ArcGISConversionSettings>>()
.Initialize(
_arcGISConversionSettingsFactory.Create(
Project.Current,
MapView.Active.Map,
new CRSoffsetRotation(MapView.Active.Map)
)
);
// Receive host objects
var receiveOperationResults = await scope
.ServiceProvider.GetRequiredService<ReceiveOperation>()
.Execute(
modelCard.GetReceiveInfo("ArcGIS"), // POC: get host app name from settings? same for GetSendInfo
_operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationItem.Token),
cancellationItem.Token
);
modelCard.BakedObjectIds = receiveOperationResults.BakedObjectIds.ToList();
await Commands.SetModelReceiveResult(
modelCardId,
receiveOperationResults.BakedObjectIds,
receiveOperationResults.ConversionResults
);
}
catch (OperationCanceledException)
{
// SWALLOW -> UI handles it immediately, so we do not need to handle anything for now!
// Idea for later -> when cancel called, create promise from UI to solve it later with this catch block.
// So have 3 state on UI -> Cancellation clicked -> Cancelling -> Cancelled
return;
}
catch (Exception ex) when (!ex.IsFatal()) // UX reasons - we will report operation exceptions as model card error. We may change this later when we have more exception documentation
{
_logger.LogModelCardHandledError(ex);
await Commands.SetModelError(modelCardId, ex);
}
}
public void CancelReceive(string modelCardId) => _cancellationManager.CancelOperation(modelCardId);
}
@@ -0,0 +1,71 @@
using ArcGIS.Desktop.Mapping;
using ArcGIS.Desktop.Mapping.Events;
using Speckle.Connectors.ArcGIS.Utils;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
namespace Speckle.Connectors.ArcGIS.Bindings;
public class ArcGISSelectionBinding : ISelectionBinding
{
private readonly MapMembersUtils _mapMemberUtils;
public string Name => "selectionBinding";
public IBrowserBridge Parent { get; }
public ArcGISSelectionBinding(
IBrowserBridge parent,
MapMembersUtils mapMemberUtils,
ITopLevelExceptionHandler topLevelExceptionHandler
)
{
_mapMemberUtils = mapMemberUtils;
Parent = parent;
// example: https://github.com/Esri/arcgis-pro-sdk-community-samples/blob/master/Map-Authoring/QueryBuilderControl/DefinitionQueryDockPaneViewModel.cs
// MapViewEventArgs args = new(MapView.Active);
TOCSelectionChangedEvent.Subscribe(_ => topLevelExceptionHandler.CatchUnhandled(OnSelectionChanged), true);
}
private void OnSelectionChanged()
{
SelectionInfo selInfo = GetSelection();
Parent.Send(SelectionBindingEvents.SET_SELECTION, selInfo);
}
private void GetLayersFromGroup(GroupLayer group, List<MapMember> nestedLayers)
{
nestedLayers.Add(group);
foreach (MapMember member in group.Layers)
{
if (member is GroupLayer subGroup)
{
GetLayersFromGroup(subGroup, nestedLayers);
}
else
{
nestedLayers.Add(member);
}
}
}
public SelectionInfo GetSelection()
{
MapView mapView = MapView.Active;
List<MapMember> selectedMembers = new();
selectedMembers.AddRange(mapView.GetSelectedLayers());
selectedMembers.AddRange(mapView.GetSelectedStandaloneTables());
List<MapMember> allNestedMembers = new();
var layerMapMembers = _mapMemberUtils.UnpackMapLayers(selectedMembers);
allNestedMembers.AddRange(layerMapMembers);
List<string> objectTypes = allNestedMembers
.Select(o => o.GetType().ToString().Split(".").Last())
.Distinct()
.ToList();
return new SelectionInfo(
allNestedMembers.Select(x => x.URI).ToList(),
$"{allNestedMembers.Count} layers ({string.Join(", ", objectTypes)})"
);
}
}
@@ -0,0 +1,472 @@
using System.Collections.Concurrent;
using System.Diagnostics.CodeAnalysis;
using ArcGIS.Core.Data;
using ArcGIS.Desktop.Core;
using ArcGIS.Desktop.Editing.Events;
using ArcGIS.Desktop.Mapping;
using ArcGIS.Desktop.Mapping.Events;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.ArcGIS.Filters;
using Speckle.Connectors.ArcGIS.Utils;
using Speckle.Connectors.Common.Caching;
using Speckle.Connectors.Common.Cancellation;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Eventing;
using Speckle.Connectors.DUI.Exceptions;
using Speckle.Connectors.DUI.Logging;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Connectors.DUI.Models.Card.SendFilter;
using Speckle.Connectors.DUI.Settings;
using Speckle.Converters.ArcGIS3;
using Speckle.Converters.ArcGIS3.Utils;
using Speckle.Converters.Common;
using Speckle.Sdk;
using Speckle.Sdk.Common;
namespace Speckle.Connectors.ArcGIS.Bindings;
public sealed class ArcGISSendBinding : ISendBinding
{
public string Name => "sendBinding";
public SendBindingUICommands Commands { get; }
public IBrowserBridge Parent { get; }
private readonly DocumentModelStore _store;
private readonly IServiceProvider _serviceProvider;
private readonly List<ISendFilter> _sendFilters;
private readonly ICancellationManager _cancellationManager;
private readonly ISendConversionCache _sendConversionCache;
private readonly IOperationProgressManager _operationProgressManager;
private readonly ILogger<ArcGISSendBinding> _logger;
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
private readonly IArcGISConversionSettingsFactory _arcGISConversionSettingsFactory;
private readonly IThreadContext _threadContext;
/// <summary>
/// Used internally to aggregate the changed objects' id. Note we're using a concurrent dictionary here as the expiry check method is not thread safe, and this was causing problems. See:
/// [CNX-202: Unhandled Exception Occurred when receiving in Rhino](https://linear.app/speckle/issue/CNX-202/unhandled-exception-occurred-when-receiving-in-rhino)
/// As to why a concurrent dictionary, it's because it's the cheapest/easiest way to do so.
/// https://stackoverflow.com/questions/18922985/concurrent-hashsett-in-net-framework
/// </summary>
private ConcurrentDictionary<string, byte> ChangedObjectIds { get; set; } = new();
private List<FeatureLayer> SubscribedLayers { get; set; } = new();
private List<StandaloneTable> SubscribedTables { get; set; } = new();
private readonly MapMembersUtils _mapMemberUtils;
public ArcGISSendBinding(
DocumentModelStore store,
IBrowserBridge parent,
IEnumerable<ISendFilter> sendFilters,
IServiceProvider serviceProvider,
ICancellationManager cancellationManager,
ISendConversionCache sendConversionCache,
IOperationProgressManager operationProgressManager,
ILogger<ArcGISSendBinding> logger,
IArcGISConversionSettingsFactory arcGisConversionSettingsFactory,
MapMembersUtils mapMemberUtils,
IThreadContext threadContext,
IEventAggregator eventAggregator,
ITopLevelExceptionHandler topLevelExceptionHandler
)
{
_store = store;
_serviceProvider = serviceProvider;
_sendFilters = sendFilters.ToList();
_cancellationManager = cancellationManager;
_sendConversionCache = sendConversionCache;
_operationProgressManager = operationProgressManager;
_logger = logger;
_topLevelExceptionHandler = topLevelExceptionHandler;
_arcGISConversionSettingsFactory = arcGisConversionSettingsFactory;
_mapMemberUtils = mapMemberUtils;
_threadContext = threadContext;
Parent = parent;
Commands = new SendBindingUICommands(parent);
SubscribeToArcGISEvents();
eventAggregator.GetEvent<DocumentStoreChangedEvent>().Subscribe(OnDocumentStoreChangedEvent);
}
private void OnDocumentStoreChangedEvent(object _) => _sendConversionCache.ClearCache();
private void SubscribeToArcGISEvents()
{
LayersRemovedEvent.Subscribe(
a =>
_topLevelExceptionHandler.FireAndForget(
async () => await _threadContext.RunOnWorkerAsync(async () => await GetIdsForLayersRemovedEvent(a))
),
true
);
StandaloneTablesRemovedEvent.Subscribe(
a =>
_topLevelExceptionHandler.FireAndForget(
async () => await _threadContext.RunOnWorkerAsync(async () => await GetIdsForStandaloneTablesRemovedEvent(a))
),
true
);
MapPropertyChangedEvent.Subscribe(
a =>
_topLevelExceptionHandler.FireAndForget(
async () => await _threadContext.RunOnWorkerAsync(async () => await GetIdsForMapPropertyChangedEvent(a))
),
true
); // Map units, CRS etc.
MapMemberPropertiesChangedEvent.Subscribe(
a =>
_topLevelExceptionHandler.FireAndForget(
async () =>
await _threadContext.RunOnWorkerAsync(async () => await GetIdsForMapMemberPropertiesChangedEvent(a))
),
true
); // e.g. Layer name
ActiveMapViewChangedEvent.Subscribe(
_ =>
_topLevelExceptionHandler.FireAndForget(async () =>
{
await _threadContext.RunOnWorker(SubscribeToMapMembersDataSourceChange);
}),
true
);
/*
LayersAddedEvent.Subscribe(a => _topLevelExceptionHandler.CatchUnhandled(() => GetIdsForLayersAddedEvent(a)), true);
StandaloneTablesAddedEvent.Subscribe(
a => _topLevelExceptionHandler.CatchUnhandled(() => GetIdsForStandaloneTablesAddedEvent(a)),
true
);
*/
}
private void SubscribeToMapMembersDataSourceChange()
{
if (MapView.Active == null)
{
return;
}
// subscribe to layers
foreach (Layer layer in MapView.Active.Map.Layers)
{
if (layer is FeatureLayer featureLayer)
{
SubscribeToFeatureLayerDataSourceChange(featureLayer);
}
}
// subscribe to tables
foreach (StandaloneTable table in MapView.Active.Map.StandaloneTables)
{
SubscribeToTableDataSourceChange(table);
}
}
private void SubscribeToFeatureLayerDataSourceChange(FeatureLayer layer)
{
if (SubscribedLayers.Contains(layer))
{
return;
}
Table layerTable = layer.GetTable();
if (layerTable != null)
{
SubscribeToAnyDataSourceChange(layerTable);
SubscribedLayers.Add(layer);
}
}
private void SubscribeToTableDataSourceChange(StandaloneTable table)
{
if (SubscribedTables.Contains(table))
{
return;
}
Table layerTable = table.GetTable();
if (layerTable != null)
{
SubscribeToAnyDataSourceChange(layerTable);
SubscribedTables.Add(table);
}
}
private void SubscribeToAnyDataSourceChange(Table layerTable)
{
RowCreatedEvent.Subscribe(
(args) =>
_topLevelExceptionHandler.FireAndForget(async () =>
{
await OnRowChanged(args);
}),
layerTable
);
RowChangedEvent.Subscribe(
(args) =>
_topLevelExceptionHandler.FireAndForget(async () =>
{
await OnRowChanged(args);
}),
layerTable
);
RowDeletedEvent.Subscribe(
(args) =>
_topLevelExceptionHandler.FireAndForget(async () =>
{
await OnRowChanged(args);
}),
layerTable
);
}
private async Task OnRowChanged(RowChangedEventArgs args)
{
if (args == null || MapView.Active == null)
{
return;
}
// get the path of the edited dataset
Uri datasetPath = args.Row.GetTable().GetPath();
foreach (Layer layer in MapView.Active.Map.Layers)
{
try
{
if (layer.GetPath() == datasetPath)
{
ChangedObjectIds[layer.URI] = 1;
}
}
catch (UriFormatException) // layer.GetPath() or table.GetPath() can throw this error, if data source was removed from the hard drive
{
// ignore layers with invalid source URI
}
}
foreach (StandaloneTable table in MapView.Active.Map.StandaloneTables)
{
try
{
if (table.GetPath() == datasetPath)
{
ChangedObjectIds[table.URI] = 1;
}
}
catch (UriFormatException) // layer.GetPath() or table.GetPath() can throw this error, if data source was removed from the hard drive
{
// ignore layers with invalid source URI
}
}
await RunExpirationChecks(false);
}
private async Task GetIdsForLayersRemovedEvent(LayerEventsArgs args)
{
foreach (Layer layer in args.Layers)
{
ChangedObjectIds[layer.URI] = 1;
}
await RunExpirationChecks(true);
}
private async Task GetIdsForStandaloneTablesRemovedEvent(StandaloneTableEventArgs args)
{
foreach (StandaloneTable table in args.Tables)
{
ChangedObjectIds[table.URI] = 1;
}
await RunExpirationChecks(true);
}
private async Task GetIdsForMapPropertyChangedEvent(MapPropertyChangedEventArgs args)
{
foreach (Map map in args.Maps)
{
List<MapMember> allMapMembers = _mapMemberUtils.GetAllMapMembers(map);
foreach (MapMember member in allMapMembers)
{
ChangedObjectIds[member.URI] = 1;
}
}
await RunExpirationChecks(false);
}
private void GetIdsForLayersAddedEvent(LayerEventsArgs args)
{
foreach (Layer layer in args.Layers)
{
if (layer is FeatureLayer featureLayer)
{
SubscribeToFeatureLayerDataSourceChange(featureLayer);
}
}
}
private void GetIdsForStandaloneTablesAddedEvent(StandaloneTableEventArgs args)
{
foreach (StandaloneTable table in args.Tables)
{
SubscribeToTableDataSourceChange(table);
}
}
private async Task GetIdsForMapMemberPropertiesChangedEvent(MapMemberPropertiesChangedEventArgs args)
{
// don't subscribe to all events (e.g. expanding group, changing visibility etc.)
bool validEvent = false;
foreach (var hint in args.EventHints)
{
if (
hint == MapMemberEventHint.DataSource
|| hint == MapMemberEventHint.DefinitionQuery
|| hint == MapMemberEventHint.LabelClasses
|| hint == MapMemberEventHint.LabelVisibility
|| hint == MapMemberEventHint.Name
|| hint == MapMemberEventHint.Renderer
|| hint == MapMemberEventHint.SceneLayerType
|| hint == MapMemberEventHint.URL
)
{
validEvent = true;
break;
}
}
if (validEvent)
{
foreach (MapMember member in args.MapMembers)
{
ChangedObjectIds[member.URI] = 1;
}
await RunExpirationChecks(false);
}
}
public List<ISendFilter> GetSendFilters() => _sendFilters;
public List<ICardSetting> GetSendSettings() => [];
[SuppressMessage(
"Maintainability",
"CA1506:Avoid excessive class coupling",
Justification = "Being refactored on in parallel, muting this issue so CI can pass initially."
)]
public async Task Send(string modelCardId)
{
//poc: dupe code between connectors
try
{
if (_store.GetModelById(modelCardId) is not SenderModelCard modelCard)
{
// Handle as GLOBAL ERROR at BrowserBridge
throw new InvalidOperationException("No publish model card was found.");
}
using var cancellationItem = _cancellationManager.GetCancellationItem(modelCardId);
using var scope = _serviceProvider.CreateScope();
scope
.ServiceProvider.GetRequiredService<IConverterSettingsStore<ArcGISConversionSettings>>()
.Initialize(
_arcGISConversionSettingsFactory.Create(
Project.Current,
MapView.Active.Map,
new CRSoffsetRotation(MapView.Active.Map)
)
);
List<MapMember> mapMembers = modelCard
.SendFilter.NotNull()
.RefreshObjectIds()
.Select(id => (MapMember)MapView.Active.Map.FindLayer(id) ?? MapView.Active.Map.FindStandaloneTable(id))
.Where(obj => obj != null)
.ToList();
if (mapMembers.Count == 0)
{
// Handle as CARD ERROR in this function
throw new SpeckleSendFilterException("No objects were found to convert. Please update your publish filter!");
}
// subscribe to the selected layer events
foreach (MapMember mapMember in mapMembers)
{
if (mapMember is FeatureLayer featureLayer)
{
SubscribeToFeatureLayerDataSourceChange(featureLayer);
}
else if (mapMember is StandaloneTable table)
{
SubscribeToTableDataSourceChange(table);
}
}
var sendResult = await scope
.ServiceProvider.GetRequiredService<SendOperation<MapMember>>()
.Execute(
mapMembers,
modelCard.GetSendInfo("ArcGIS"), // POC: get host app name from settings? same for GetReceiveInfo
_operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationItem.Token),
cancellationItem.Token
);
await Commands.SetModelSendResult(modelCardId, sendResult.RootObjId, sendResult.ConversionResults);
}
catch (OperationCanceledException)
{
// SWALLOW -> UI handles it immediately, so we do not need to handle anything for now!
// Idea for later -> when cancel called, create promise from UI to solve it later with this catch block.
// So have 3 state on UI -> Cancellation clicked -> Cancelling -> Cancelled
return;
}
catch (Exception ex) when (!ex.IsFatal()) // UX reasons - we will report operation exceptions as model card error. We may change this later when we have more exception documentation
{
_logger.LogModelCardHandledError(ex);
await Commands.SetModelError(modelCardId, ex);
}
}
public void CancelSend(string modelCardId) => _cancellationManager.CancelOperation(modelCardId);
/// <summary>
/// Checks if any sender model cards contain any of the changed objects. If so, also updates the changed objects hashset for each model card - this last part is important for on send change detection.
/// </summary>
private async Task RunExpirationChecks(bool idsDeleted)
{
var senders = _store.GetSenders();
List<string> expiredSenderIds = new();
string[] objectIdsList = ChangedObjectIds.Keys.ToArray();
_sendConversionCache.EvictObjects(objectIdsList);
foreach (SenderModelCard sender in senders)
{
var objIds = sender.SendFilter.NotNull().RefreshObjectIds();
var intersection = objIds.Intersect(objectIdsList).ToList();
bool isExpired = intersection.Count != 0;
if (isExpired)
{
expiredSenderIds.Add(sender.ModelCardId.NotNull());
// Update the model card object Ids
if (idsDeleted && sender.SendFilter is ArcGISSelectionFilter filter)
{
List<string> remainingObjIds = objIds.SkipWhile(x => intersection.Contains(x)).ToList();
filter.SelectedObjectIds = remainingObjIds;
}
}
}
await Commands.SetModelsExpired(expiredSenderIds);
ChangedObjectIds = new();
}
}
@@ -0,0 +1,209 @@
using ArcGIS.Core.Data;
using ArcGIS.Desktop.Mapping;
using Speckle.Connectors.ArcGIS.Utils;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Eventing;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Sdk;
using Speckle.Sdk.Common;
using ArcProject = ArcGIS.Desktop.Core.Project;
namespace Speckle.Connectors.ArcGIS.Bindings;
//poc: dupe code between connectors
public class BasicConnectorBinding : IBasicConnectorBinding
{
public string Name => "baseBinding";
public IBrowserBridge Parent { get; }
public BasicConnectorBindingCommands Commands { get; }
private readonly DocumentModelStore _store;
private readonly ISpeckleApplication _speckleApplication;
public BasicConnectorBinding(
DocumentModelStore store,
IBrowserBridge parent,
ISpeckleApplication speckleApplication,
IEventAggregator eventAggregator
)
{
_store = store;
_speckleApplication = speckleApplication;
Parent = parent;
Commands = new BasicConnectorBindingCommands(parent);
eventAggregator.GetEvent<DocumentStoreChangedEvent>().Subscribe(OnDocumentStoreChangedEvent);
}
private async Task OnDocumentStoreChangedEvent(object _) => await Commands.NotifyDocumentChanged();
public string GetSourceApplicationName() => _speckleApplication.Slug;
public string GetSourceApplicationVersion() => _speckleApplication.HostApplicationVersion;
public string GetConnectorVersion() => _speckleApplication.SpeckleVersion;
public DocumentInfo? GetDocumentInfo()
{
if (MapView.Active is null)
{
return null;
}
return new DocumentInfo(ArcProject.Current.URI, MapView.Active.Map.Name, MapView.Active.Map.URI);
}
public DocumentModelStore GetDocumentState() => _store;
public void AddModel(ModelCard model) => _store.AddModel(model);
public void UpdateModel(ModelCard model) => _store.UpdateModel(model);
public void RemoveModel(ModelCard model) => _store.RemoveModel(model);
public Task HighlightObjects(IReadOnlyList<string> objectIds)
{
HighlightObjectsOnView(objectIds.Select(x => new ObjectID(x)).ToList());
return Task.CompletedTask;
}
public Task HighlightModel(string modelCardId)
{
var model = _store.GetModelById(modelCardId);
if (model is null)
{
return Task.CompletedTask;
}
var objectIds = new List<ObjectID>();
if (model is SenderModelCard senderModelCard)
{
objectIds = senderModelCard.SendFilter.NotNull().RefreshObjectIds().Select(x => new ObjectID(x)).ToList();
}
if (model is ReceiverModelCard receiverModelCard)
{
objectIds = receiverModelCard.BakedObjectIds.NotNull().Select(x => new ObjectID(x)).ToList();
}
if (objectIds is null)
{
return Task.CompletedTask;
}
HighlightObjectsOnView(objectIds);
return Task.CompletedTask;
}
private void HighlightObjectsOnView(IReadOnlyList<ObjectID> objectIds)
{
MapView mapView = MapView.Active;
List<MapMemberFeature> mapMembersFeatures = GetMapMembers(objectIds, mapView);
ClearSelectionInTOC();
ClearSelection();
SelectMapMembersInTOC(mapMembersFeatures);
SelectMapMembersAndFeatures(mapMembersFeatures);
mapView.ZoomToSelected();
}
private List<MapMemberFeature> GetMapMembers(IReadOnlyList<ObjectID> objectIds, MapView mapView)
{
// find the layer on the map (from the objectID) and add the featureID is available
List<MapMemberFeature> mapMembersFeatures = new();
foreach (ObjectID objectId in objectIds)
{
MapMember mapMember = mapView.Map.FindLayer(objectId.MappedLayerURI, true);
if (mapMember is null)
{
mapMember = mapView.Map.FindStandaloneTable(objectId.MappedLayerURI);
}
if (mapMember is not null)
{
MapMemberFeature mapMembersFeat = new(mapMember, objectId.FeatureId);
mapMembersFeatures.Add(mapMembersFeat);
}
}
return mapMembersFeatures;
}
private void ClearSelection()
{
List<Layer> mapMembers = MapView.Active.Map.GetLayersAsFlattenedList().ToList();
foreach (var member in mapMembers)
{
if (member is FeatureLayer featureLayer)
{
featureLayer.ClearSelection();
}
}
}
private void ClearSelectionInTOC()
{
MapView.Active.ClearTOCSelection();
}
private void SelectMapMembersAndFeatures(IReadOnlyList<MapMemberFeature> mapMembersFeatures)
{
foreach (MapMemberFeature mapMemberFeat in mapMembersFeatures)
{
MapMember member = mapMemberFeat.MapMember;
if (member is FeatureLayer layer)
{
if (mapMemberFeat.FeatureId == null)
{
// select full layer if featureID not specified
layer.Select();
}
else
{
// query features by ID
var objectIDfield = layer.GetFeatureClass().GetDefinition().GetObjectIDField();
// FeatureID range starts from 0, but auto-assigned IDs in the layer start from 1
QueryFilter anotherQueryFilter = new() { WhereClause = $"{objectIDfield} = {mapMemberFeat.FeatureId + 1}" };
using (Selection onlyOneSelection = layer.Select(anotherQueryFilter, SelectionCombinationMethod.New)) { }
}
}
}
}
private void SelectMapMembersInTOC(IReadOnlyList<MapMemberFeature> mapMembersFeatures)
{
List<Layer> layers = new();
List<StandaloneTable> tables = new();
foreach (MapMemberFeature mapMemberFeat in mapMembersFeatures)
{
MapMember member = mapMemberFeat.MapMember;
if (member is Layer layer)
{
if (member is not GroupLayer) // group layer selection clears other layers selection
{
layers.Add(layer);
}
else
{
layer.SetExpanded(true);
}
}
else if (member is StandaloneTable table)
{
tables.Add(table);
}
}
MapView.Active.SelectLayers(layers);
// this step clears previous selection, not clear how to ADD selection instead
// this is why, activating it only if no layers are selected
if (layers.Count == 0)
{
MapView.Active.SelectStandaloneTables(tables);
}
}
}
@@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8" ?>
<!--
Copyright 2022 Esri
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<ArcGIS defaultAssembly="Speckle.Connectors.ArcGIS3.dll" defaultNamespace="Speckle.Connectors.ArcGIS" xmlns="http://schemas.esri.com/DADF/Registry" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://schemas.esri.com/DADF/Registry file:///C:/Program%20Files/ArcGIS/Pro/bin/ArcGIS.Desktop.Framework.xsd">
<AddInInfo id="{6CB1D25C-B8BF-4A33-9099-C1F8D1B32EFC}" version="1.0" desktopVersion="3.0.34047">
<Name>Speckle</Name>
<Description>Next Gen Speckle Connector (Beta) for ArcGIS</Description>
<Image>Images\AddinDesktop32.png</Image>
<Author>Speckle Systems</Author>
<Company>Speckle Systems</Company>
<Date>8/5/2021 12:24:21 PM</Date>
<Subject>Framework</Subject>
<!-- Note subject can be one or more of these topics:
Content, Framework, Editing, Geodatabase, Geometry, Geoprocessing, Layouts, Map Authoring, Map Exploration -->
</AddInInfo>
<modules>
<insertModule id="ConnectorArcGIS_Module" className="SpeckleModule" autoLoad="false" caption="SpeckleModule">
<!-- uncomment to have the control hosted on a separate tab-->
<tabs>
<tab id="Speckle_Tab1" caption="Speckle">
<group refID="Speckle_Group1"/>
</tab>
</tabs>
<groups>
<!-- comment this out if you have no controls on the Addin tab to avoid
an empty group. change appearsOnAddinTab to "True" if control is to be in the addin tab-->
<group id="Speckle_Group1" caption="Speckle" appearsOnAddInTab="false" keytip="G1">
<!-- host controls within groups -->
<button refID="SpeckleDUI3_SpeckleDUI3OpenButton" size="large" />
</group>
</groups>
<controls>
<!-- add your controls here -->
<button id="SpeckleDUI3_SpeckleDUI3OpenButton" caption="Speckle (Beta)"
className="SpeckleDUI3OpenButton" loadOnClick="true"
keytip="B1"
smallImage="Images/s2logo_16.png"
largeImage="Images/s2logo_32.png">
<tooltip heading="Speckle Connector for ArcGIS">
<disabledText />
</tooltip>
</button>
</controls>
<dockPanes>
<dockPane id="SpeckleDUI3_SpeckleDUI3" caption="Speckle (Beta)" className="SpeckleDUI3ViewModel" keytip="DockPane" initiallyVisible="true" dock="group" dockWith="esri_core_projectDockPane">
<content className="SpeckleDUI3Wrapper" />
</dockPane>
</dockPanes>
</insertModule>
</modules>
</ArcGIS>
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 714 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

@@ -0,0 +1,65 @@
using ArcGIS.Desktop.Mapping;
using Microsoft.Extensions.DependencyInjection;
using Speckle.Connectors.ArcGIS.Bindings;
using Speckle.Connectors.ArcGIS.Filters;
using Speckle.Connectors.ArcGIS.HostApp;
using Speckle.Connectors.ArcGIS.Operations.Receive;
using Speckle.Connectors.ArcGis.Operations.Send;
using Speckle.Connectors.ArcGIS.Utils;
using Speckle.Connectors.Common;
using Speckle.Connectors.Common.Builders;
using Speckle.Connectors.Common.Caching;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.DUI;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Models.Card.SendFilter;
using Speckle.Connectors.DUI.WebView;
using Speckle.Converters.Common;
using Speckle.Sdk.Models.GraphTraversal;
// POC: This is a temp reference to root object senders to tweak CI failing after having generic interfaces into common project.
// This should go whenever it is aligned.
namespace Speckle.Connectors.ArcGIS.DependencyInjection;
public static class ArcGISConnectorModule
{
public static void AddArcGIS(this IServiceCollection serviceCollection)
{
serviceCollection.AddConnectorUtils();
serviceCollection.AddDUI<ArcGISThreadContext, ArcGISDocumentStore>();
serviceCollection.AddDUIView();
// Register bindings
serviceCollection.AddSingleton<IBinding, TestBinding>();
serviceCollection.AddSingleton<IBinding, ConfigBinding>();
serviceCollection.AddSingleton<IBinding, AccountBinding>();
serviceCollection.AddSingleton<IBinding>(sp => sp.GetRequiredService<IBasicConnectorBinding>());
serviceCollection.AddSingleton<IBasicConnectorBinding, BasicConnectorBinding>();
serviceCollection.AddSingleton(DefaultTraversal.CreateTraversalFunc());
// register send operation and dependencies
serviceCollection.AddSingleton<IBinding, ArcGISSendBinding>();
serviceCollection.AddScoped<SendOperation<MapMember>>();
serviceCollection.AddSingleton<IBinding, ArcGISSelectionBinding>();
serviceCollection.AddTransient<ISendFilter, ArcGISSelectionFilter>();
serviceCollection.AddScoped<ArcGISRootObjectBuilder>();
serviceCollection.AddScoped<IRootObjectBuilder<MapMember>, ArcGISRootObjectBuilder>();
serviceCollection.AddScoped<ArcGISLayerUnpacker>();
serviceCollection.AddScoped<ArcGISColorUnpacker>();
// register send conversion cache
serviceCollection.AddSingleton<ISendConversionCache, SendConversionCache>();
// register receive operation and dependencies
// serviceCollection.AddSingleton<IBinding, ArcGISReceiveBinding>(); // POC: disabled until receive code is refactored
serviceCollection.AddScoped<LocalToGlobalConverterUtils>();
serviceCollection.AddScoped<ArcGISColorManager>();
serviceCollection.AddScoped<IHostObjectBuilder, ArcGISHostObjectBuilder>();
serviceCollection.AddScoped<MapMembersUtils>();
// operation progress manager
serviceCollection.AddSingleton<IOperationProgressManager, OperationProgressManager>();
}
}
@@ -0,0 +1,356 @@
<?xml version="1.0" encoding="utf-8" ?>
<Project>
<!-- Code to zip up the files-->
<UsingTask TaskName="PackageAddIn" TaskFactory="RoslynCodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
<ParameterGroup>
<ZipIntermediatePath ParameterType="System.String" Required="true" />
<PackageType ParameterType="System.String" Required="true" />
<TargetFolder ParameterType="System.String" Required="true" />
<TargetFileName ParameterType="System.String" Required="true" />
<RootNamespace ParameterType="System.String" Required="true" />
<PackageOutputPath ParameterType="System.String" Output="true"/>
</ParameterGroup>
<Task>
<!-- <Reference Include="System.IO.Compression.FileSystem"/>-->
<!-- <Reference Include="System.Xml.Linq"/>-->
<!-- <Reference Include="System.Xml"/>-->
<Using Namespace="System"/>
<Using Namespace="System.IO"/>
<Using Namespace="System.Xml.Linq"/>
<Using Namespace="System.Linq"/>
<Using Namespace="System.IO.Compression"/>
<Code Type="Fragment" Language="cs">
<![CDATA[
Success = false;
string ConfigNotFound = "{0} was not found. File must be present in the root of the project and its build action set to AddInContent.";
string ZipIntermediatePathNotFound = "{0} was not found.";
string DefaultAssemblyDoesNotMatch = "Your value of '{0}' for the '{1}' attribute in the {2} does not match the assembly name '{3}' set for your project.";
string DefaultNSDoesNotMatch = "Your value of '{0}' for the '{1}' attribute in the {2} does not match the default namespace '{3}' set for your project.";
//Create the name of the Config File and extension
string extension = "";
string config = "";
string attrib_asm = "";
string attrib_ns = "";
var assemblyValMissing = "";
var nsValMissing = "";
if (PackageType.ToLower() == "plugin")
{
Log.LogMessage(MessageImportance.Low, "This is an plugin");
config = "Config.xml";
extension = ".esriPlugin";
attrib_asm = "library";
attrib_ns = "namespace";
assemblyValMissing = "AddIn element 'library' attribute not found";
nsValMissing = "AddIn element 'namespace' attribute not found";
}
else if (PackageType.ToLower() == "configuration")
{
Log.LogMessage(MessageImportance.Low, "This is an configuration");
config = "Config.daml";
extension = ".proConfigX";
attrib_asm = "defaultAssembly";
attrib_ns = "defaultNamespace";
assemblyValMissing = "ArcGIS element 'defaultAssembly' attribute not found";
nsValMissing = "ArcGIS element 'defaultNamespace' attribute not found";
}
else
{
Log.LogMessage(MessageImportance.Low, "This is an addin");
config = "Config.daml";
bool proSDKProject = File.Exists(Path.Combine(ZipIntermediatePath, config));
if (!proSDKProject) //This might be a class library that uses the Pro references only
return true;
extension = ".esriAddinX";
attrib_asm = "defaultAssembly";
attrib_ns = "defaultNamespace";
assemblyValMissing = "ArcGIS element 'defaultAssembly' attribute not found";
nsValMissing = "ArcGIS element 'defaultNamespace' attribute not found";
}
// Check if Config.daml exists in ZipFolder
ZipIntermediatePath = Path.GetFullPath(ZipIntermediatePath);
if (!Directory.Exists(ZipIntermediatePath))
{
Log.LogError(ZipIntermediatePathNotFound, ZipIntermediatePath);
return false;
}
var addInXML = Path.Combine(ZipIntermediatePath, config);
Log.LogMessage(MessageImportance.Low, "addInXML: " + addInXML);
Log.LogMessage(MessageImportance.High, "PackageType: " + PackageType);
if (!File.Exists(addInXML))
{
Log.LogError(ConfigNotFound, config);
return false;
}
//Verfiy that an assembly with the name defined in the Config.daml
//matches the default assembly set in the project. Ditto for the
//namespace
string DefaultAssembly = "";
string DefaultNamespace = "";
XDocument xdoc = XDocument.Load(addInXML);
XNamespace DefaultNS = "http://schemas.esri.com/DADF/Registry";
if (PackageType.ToLower() == "plugin")
{
var addin = xdoc.Root.Element(DefaultNS + "AddIn");
if (addin != null)
{
var val = addin.Attribute("library");
if (val != null)
DefaultAssembly = val.Value;
val = addin.Attribute("namespace");
if (val != null)
DefaultNamespace = val.Value;
}
}
else
{
var val = xdoc.Root.Attribute("defaultAssembly");
if (val != null)
DefaultAssembly = val.Value;
val = xdoc.Root.Attribute("defaultNamespace");
if (val != null)
DefaultNamespace = val.Value;
}
if (string.IsNullOrEmpty(DefaultAssembly))
{
Log.LogError(assemblyValMissing);
return false;
}
if (string.IsNullOrEmpty(DefaultNamespace))
{
Log.LogError(nsValMissing);
return false;
}
//check that the addin assembly and default assembly names match
if (DefaultAssembly.ToLower() != TargetFileName.ToLower())
{
Log.LogWarning(DefaultAssemblyDoesNotMatch, DefaultAssembly, attrib_asm, config, TargetFileName);
}
//Ditto for namespace
if (DefaultNamespace.ToLower() != RootNamespace.ToLower())
{
Log.LogWarning(DefaultNSDoesNotMatch, DefaultNamespace, attrib_ns, config, RootNamespace);
}
if (!Directory.Exists(TargetFolder))
{
Directory.CreateDirectory(TargetFolder);
}
string addInAssembly = System.IO.Path.GetFileNameWithoutExtension(DefaultAssembly);
string archiveName = addInAssembly + extension;
try
{
string file = Path.Combine(TargetFolder, archiveName);
if (File.Exists(file))
File.Delete(file);
System.IO.Compression.ZipFile.CreateFromDirectory(ZipIntermediatePath, file);
PackageOutputPath = Path.GetFullPath(file);
Success = true;
}
catch (Exception ex)
{
Log.LogErrorFromException(ex);
return false;
}
]]>
</Code>
</Task>
</UsingTask>
<!-- Code to find relative path-->
<UsingTask TaskName="ConvertToRelativePath" TaskFactory="RoslynCodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
<ParameterGroup>
<RelativeTo ParameterType="System.String" Required="true"/>
<Paths ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
<RelativePaths ParameterType="Microsoft.Build.Framework.ITaskItem[]" Output="true"/>
</ParameterGroup>
<Task>
<Using Namespace="System"/>
<Using Namespace="System.IO"/>
<Using Namespace="System.Linq"/>
<Code Type="Fragment" Language="cs">
<![CDATA[
var result = new List<ITaskItem>();
System.Uri relativeTo = new Uri(this.RelativeTo);
foreach (var i in Paths) {
try {
System.Uri itemFullPath = new Uri(i.GetMetadata("FullPath"));
var relativeUri = relativeTo.MakeRelativeUri(itemFullPath);
result.Add(new TaskItem(Uri.UnescapeDataString(relativeUri.ToString())));
}
catch {
return false;
}
}
RelativePaths = result.ToArray();
foreach (var i in RelativePaths)
{
Log.LogMessage(MessageImportance.Low, "RelativePaths: " + i.ToString());
}
Success = true;
]]>
</Code>
</Task>
</UsingTask>
<UsingTask TaskName="CleanAddIn" TaskFactory="RoslynCodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
<ParameterGroup>
<ProjectDir ParameterType="System.String" Required="true"/>
<AssemblyName ParameterType="System.String" Required="true"/>
<PackageType ParameterType="System.String" Required="true"/>
<!--<ArcGISFolder ParameterType="System.String" Output="true" /> -->
<CleanInfo ParameterType="System.String" Output="true"/>
</ParameterGroup>
<Task>
<!-- <Reference Include="System.Xml.Linq"/>-->
<!-- <Reference Include="System.Xml"/>-->
<Using Namespace="System"/>
<Using Namespace="System.IO"/>
<Using Namespace="System.Xml.Linq"/>
<Using Namespace="System.Linq"/>
<Code Type="Fragment" Language="cs">
<![CDATA[
Success = false;
string ConfigNotFound = "{0} was not found. File must be present in the root of the project and its build action set to AddInContent.";
//Create the name of the Config File and extension
string extension = "";
string config = "";
if (PackageType.ToLower() == "plugin")
{
config = "Config.xml";
extension = ".esriPlugin";
}
else if (PackageType.ToLower() == "configuration")
{
config = "Config.daml";
extension = ".proConfigX";
}
else
{
config = "Config.daml";
bool proSDKProject = File.Exists(Path.Combine(ProjectDir, config));
if (!proSDKProject) //This might be a class library that uses the Pro references only
return true;
extension = ".esriAddinX";
}
var addInXML = Path.Combine(ProjectDir, config);
if (!File.Exists(addInXML))
{
Log.LogError(ConfigNotFound, config);
return false;
}
//Get the add-in id
XDocument xdoc = XDocument.Load(addInXML);
XNamespace DefaultNS = "http://schemas.esri.com/DADF/Registry";
if (PackageType.ToLower() == "plugin")
{
Log.LogMessage("process plugin");
var addInID = xdoc.Root.Element(DefaultNS + "AddInID");
CleanInfo = addInID.Value;//let it error if it's missing
}
else if (PackageType.ToLower() == "addin")
{
Log.LogMessage("process addin");
var addinInfo = xdoc.Root.Element(DefaultNS + "AddInInfo");
CleanInfo = addinInfo.Attribute("id").Value;//let it error if it's missing
}
else
{
Log.LogMessage("process configuration");
CleanInfo = AssemblyName + extension;
}
Success = true;
]]>
</Code>
</Task>
</UsingTask>
<!-- Define additional BuildAction option -->
<!-- Set up default zip properties -->
<PropertyGroup>
<PackageType Condition="'$(PackageType)' == ''">Addin</PackageType>
</PropertyGroup>
<PropertyGroup>
<ArcGISFolder>$([MSBuild]::GetRegistryValueFromView('HKEY_LOCAL_MACHINE\SOFTWARE\ESRI\ArcGISPro', 'InstallDir', null, RegistryView.Registry64))\bin</ArcGISFolder>
<ArcGISFolder Condition="'$(ArcGISFolder)' == ''">$(registry:HKEY_CURRENT_USER\SOFTWARE\ESRI\ArcGISPro@InstallDir)\bin</ArcGISFolder>
<ArcGISFolder Condition="'$(ArcGISFolder)' == '' Or !Exists('$(ArcGISFolder)\RegisterAddIn.exe')">$(ProgramData)\EsriProCommon\</ArcGISFolder>
</PropertyGroup>
<Target Name="ArcGISInstallOutput" AfterTargets="Build">
<Message Text="IntermediateOutputPath Name: $(IntermediateOutputPath)..." Importance="High"/>
<Message Text="CleanFile Name: $(CleanFile)..." Importance="High"/>
<Message Text="ProjectDir Name: $(ProjectDir)..." Importance="High"/>
<Message Text="AssemblyName Name: $(AssemblyName)..." Importance="High"/>
<Message Text="TargetFileName Name: $(TargetFileName)..." Importance="High"/>
<Message Text="RootNamespace: $(RootNamespace)..." Importance="High"/>
<Message Text="TargetFolder Name: $(OutDir)..." Importance="High"/>
<Message Text="PackageType Name: $(PackageType)..." Importance="High"/>
<Message Text="Install dir: $(ArcGISFolder)" Importance="High"/>
<!-- Get a list of project outputs from the cache file and FileWritesXXX item,
excluding those in intermediate output directory -->
<!-- Note clean file may miss listing CopyLocal reference -->
<ReadLinesFromFile File="$(IntermediateOutputPath)$(CleanFile)">
<Output TaskParameter="Lines" ItemName="CacheOutputFiles" />
</ReadLinesFromFile>
<FindUnderPath Files="@(CacheOutputFiles)" Path="$(OutDir)">
<Output TaskParameter="InPath" ItemName="PackageOutputFiles" />
</FindUnderPath>
<FindUnderPath Files="@(FileWrites->'%(FullPath)')" Path="$(OutDir)">
<Output TaskParameter="InPath" ItemName="PackageOutputFiles" />
</FindUnderPath>
<FindUnderPath Files="@(FileWritesShareable->'%(FullPath)')" Path="$(OutDir)">
<Output TaskParameter="InPath" ItemName="PackageOutputFiles" />
</FindUnderPath>
<RemoveDuplicates Inputs="@(PackageOutputFiles)">
<Output TaskParameter="Filtered" ItemName="FilteredPackageOutputFiles" />
</RemoveDuplicates>
<ConvertToRelativePath Paths="@(FilteredPackageOutputFiles)" RelativeTo="$(TargetDir)">
<Output TaskParameter="RelativePaths" ItemName="ConfigBinaries" />
</ConvertToRelativePath>
<Message Text="ConvertToRelativePath Task, TargetDir: $(TargetDir) " Importance="High"/>
</Target>
<Target Name="PackageArcGISContents" AfterTargets="ArcGISInstallOutput">
<Message Text="Running PackageArcGISContents..." Importance="High"/>
<RemoveDir Condition="Exists('$(ZipIntermediatePath)')" Directories="$(ZipIntermediatePath)" />
<Message Text="ZipIntermediatePath: $(ZipIntermediatePath)Install..." Importance="High"/>
<!-- Copy project output files, preserving folder structure -->
<Copy SourceFiles="@(ConfigBinaries->'$(OutDir)%(Identity)')" ContinueOnError="true" DestinationFolder="$(IntermediateOutputPath)temp_archive\Install\%(RelativeDir)" />
<!-- Copy items marked with Content as BuildAction, preserving folder structure & handling linked items -->
<!-- Only include items that have CopyToOutputDirectory as Never -->
<Copy SourceFiles="@(Content)" Condition="'%(Content.Link)' == '' And ('%(Content.CopyToOutputDirectory)' == 'Never' Or '%(Content.CopyToOutputDirectory)' == '')" DestinationFolder="$(IntermediateOutputPath)temp_archive\%(RelativeDir)" ContinueOnError="true" />
<Copy SourceFiles="@(Content)" Condition="'%(Content.Link)' != '' And ('%(Content.CopyToOutputDirectory)' == 'Never' Or '%(Content.CopyToOutputDirectory)' == '')" DestinationFiles="$(IntermediateOutputPath)temp_archive\%(Content.Link)" ContinueOnError="true"/>
<!-- Zipping up add-in resources -->
<PackageAddIn ZipIntermediatePath="$(IntermediateOutputPath)temp_archive\"
PackageType="$(PackageType)"
TargetFolder="$(OutDir)"
TargetFileName="$(TargetFileName)"
RootNamespace="$(RootNamespace)">
<Output TaskParameter="PackageOutputPath" PropertyName="PackageFile" />
</PackageAddIn>
<!-- Shell out to RegisterAddIn.exe to install the package -->
<Message Text="Deploying $(PackageType)..." Importance="High"/>
<Message Text="ArcGISFolder Name: $(ArcGISFolder)..." Importance="High"/>
<Message Text="Unable to execute RegisterAddIn.exe. ArcGIS Pro is not installed." Importance="High" Condition="!Exists('$(ArcGISFolder)')"/>
<Message Text="Execute RegisterAddIn.exe &quot;$(PackageFile)&quot; /s..." Importance="High" Condition="Exists('$(ArcGISFolder)')"/>
<Exec IgnoreExitCode="true" WorkingDirectory="$(ArcGISFolder)" Command="RegisterAddIn.exe &quot;$(PackageFile)&quot; /s" Condition="Exists('$(ArcGISFolder)') AND $(PackageFile) != '' ">
<Output TaskParameter="ExitCode" PropertyName="ESRIRegAddinExitCode" />
</Exec>
<RemoveDir Condition="Exists('$(ZipIntermediatePath)')" Directories="$(ZipIntermediatePath)" />
</Target>
<Target Name="CleanArcGISContents" AfterTargets="Clean">
<CleanAddIn ProjectDir="$(ProjectDir)"
AssemblyName="$(AssemblyName)"
PackageType="$(PackageType)">
<Output TaskParameter="CleanInfo" PropertyName="CleanInfo" />
</CleanAddIn>
<Message Text="Clean $(PackageType).$(ArcGISFolder).." Importance="High"/>
<Message Text="Execute RegisterAddIn.exe &quot;$(CleanInfo)&quot; /u..." Importance="High" Condition="Exists('$(ArcGISFolder)')"/>
<Message Text="Unable to execute RegisterAddIn.exe. ArcGIS Pro is not installed." Importance="High" Condition="!Exists('$(ArcGISFolder)')"/>
<Exec IgnoreExitCode="true" WorkingDirectory="$(ArcGISFolder)" Command="RegisterAddIn.exe &quot;$(CleanInfo)&quot; /u /s" Condition="Exists('$(ArcGISFolder)') AND $(CleanInfo) != ''">
<Output TaskParameter="ExitCode" PropertyName="ESRIRegAddinExitCode" />
</Exec>
</Target>
</Project>
@@ -1,10 +1,10 @@
using Speckle.Connectors.DUI.Models.Card.SendFilter;
namespace Speckle.Connectors.Rhino.Operations.Send.Filters;
namespace Speckle.Connectors.ArcGIS.Filters;
public class RhinoSelectionFilter : DirectSelectionSendFilter
public class ArcGISSelectionFilter : DirectSelectionSendFilter
{
public RhinoSelectionFilter()
public ArcGISSelectionFilter()
{
IsDefault = true;
}
@@ -0,0 +1,3 @@
global using AC = ArcGIS.Core;
global using ACD = ArcGIS.Core.Data;
global using ADM = ArcGIS.Desktop.Mapping;
@@ -0,0 +1,265 @@
using System.Drawing;
using ArcGIS.Core.CIM;
using ArcGIS.Desktop.Mapping;
using Speckle.Connectors.Common.Operations;
using Speckle.Converters.ArcGIS3.Utils;
using Speckle.Objects;
using Speckle.Objects.Other;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Models.Extensions;
using Speckle.Sdk.Models.GraphTraversal;
using Speckle.Sdk.Models.Proxies;
namespace Speckle.Connectors.ArcGIS.HostApp;
/// <summary>
/// TODO: definitely need to refactor this, probably will collect colors during layer iteration in the root object builder.
/// </summary>
public class ArcGISColorManager
{
public Dictionary<string, Color> ObjectColorsIdMap { get; set; } = new();
public Dictionary<string, Color> ObjectMaterialsIdMap { get; set; } = new();
/// <summary>
/// Parse Color Proxies and stores in ObjectColorsIdMap the relationship between object ids and colors
/// </summary>
/// <param name="colorProxies"></param>
/// <param name="onOperationProgressed"></param>
public void ParseColors(List<ColorProxy> colorProxies, IProgress<CardProgress> onOperationProgressed)
{
// injected as Singleton, so we need to clean existing proxies first
ObjectColorsIdMap = new();
var count = 0;
foreach (ColorProxy colorProxy in colorProxies)
{
onOperationProgressed.Report(new("Converting colors", (double)++count / colorProxies.Count));
foreach (string objectId in colorProxy.objects)
{
Color convertedColor = Color.FromArgb(colorProxy.value);
ObjectColorsIdMap.TryAdd(objectId, convertedColor);
}
}
}
/// <summary>
/// Parse Color renderMaterials and stores in ObjectMaterialsIdMap the relationship between object ids and colors
/// </summary>
/// <param name="materialProxies"></param>
/// <param name="onOperationProgressed"></param>
public void ParseMaterials(List<RenderMaterialProxy> materialProxies, IProgress<CardProgress> onOperationProgressed)
{
// injected as Singleton, so we need to clean existing proxies first
ObjectMaterialsIdMap = new();
var count = 0;
foreach (RenderMaterialProxy colorProxy in materialProxies)
{
onOperationProgressed.Report(new("Converting materials", (double)++count / materialProxies.Count));
foreach (string objectId in colorProxy.objects)
{
Color convertedColor = Color.FromArgb(colorProxy.value.diffuse);
ObjectMaterialsIdMap.TryAdd(objectId, convertedColor);
}
}
}
public int CIMColorToInt(CIMColor color)
{
return (255 << 24)
| ((int)Math.Round(color.Values[0]) << 16)
| ((int)Math.Round(color.Values[1]) << 8)
| (int)Math.Round(color.Values[2]);
}
/// <summary>
/// Create a new CIMUniqueValueClass for UniqueRenderer per each object ID
/// </summary>
/// <param name="tc"></param>
/// <param name="speckleGeometryType"></param>
private CIMUniqueValueClass CreateColorCategory(
TraversalContext tc,
esriGeometryType speckleGeometryType,
string uniqueLabel
)
{
// declare default white color
Color color = Color.FromArgb(255, 255, 255, 255);
bool colorFound = false;
// get color moving upwards from the object
foreach (var parent in tc.GetAscendants())
{
if (parent.applicationId is string appId)
{
if (ObjectMaterialsIdMap.TryGetValue(appId, out Color objColorMaterial))
{
color = objColorMaterial;
colorFound = true;
break;
}
if (ObjectColorsIdMap.TryGetValue(appId, out Color objColor))
{
color = objColor;
colorFound = true;
break;
}
}
}
// handling Revit case, where child objects have separate colors/materials
if (!colorFound && tc.Current is IDataObject)
{
var displayable = tc.Current.TryGetDisplayValue();
if (displayable != null)
{
foreach (var childObj in displayable)
{
if (childObj.applicationId is string appId)
{
if (ObjectMaterialsIdMap.TryGetValue(appId, out Color objColorMaterial))
{
color = objColorMaterial;
break;
}
if (ObjectColorsIdMap.TryGetValue(appId, out Color objColor))
{
color = objColor;
break;
}
}
}
}
}
CIMSymbolReference symbol = CreateSymbol(speckleGeometryType, color);
// First create a "CIMUniqueValueClass"
List<CIMUniqueValue> listUniqueValues = new() { new CIMUniqueValue { FieldValues = new string[] { uniqueLabel } } };
CIMUniqueValueClass newUniqueValueClass =
new()
{
Editable = true,
Label = uniqueLabel,
Patch = PatchShape.Default,
Symbol = symbol,
Visible = true,
Values = listUniqueValues.ToArray()
};
return newUniqueValueClass;
}
/// <summary>
/// Create a Symbol from GeometryType and Color
/// </summary>
/// <param name="speckleGeometryType"></param>
/// <param name="color"></param>
private CIMSymbolReference CreateSymbol(esriGeometryType speckleGeometryType, Color color)
{
var symbol = SymbolFactory
.Instance.ConstructPointSymbol(ColorFactory.Instance.CreateColor(color))
.MakeSymbolReference();
switch (speckleGeometryType)
{
case esriGeometryType.esriGeometryLine:
case esriGeometryType.esriGeometryPolyline:
symbol = SymbolFactory
.Instance.ConstructLineSymbol(ColorFactory.Instance.CreateColor(color))
.MakeSymbolReference();
break;
case esriGeometryType.esriGeometryPolygon:
case esriGeometryType.esriGeometryMultiPatch:
symbol = SymbolFactory
.Instance.ConstructPolygonSymbol(ColorFactory.Instance.CreateColor(color))
.MakeSymbolReference();
break;
}
return symbol;
}
/// <summary>
/// Add CIMUniqueValueClass to Layer Renderer (if exists); apply Renderer to Layer (again)
/// </summary>
/// <param name="tc"></param>
/// <param name="trackerItem"></param>
public CIMUniqueValueRenderer? CreateOrEditLayerRenderer(
TraversalContext tc,
ObjectConversionTracker trackerItem,
CIMRenderer? existingRenderer
)
{
if (trackerItem.HostAppMapMember is not FeatureLayer fLayer)
{
// do nothing with non-feature layers
return null;
}
// declare default grey color, create default symbol for the given layer geometry type
var color = Color.FromArgb(CIMColorToInt(ColorFactory.Instance.GreyRGB));
CIMSymbolReference defaultSymbol = CreateSymbol(fLayer.ShapeType, color);
// get existing renderer classes
List<CIMUniqueValueClass> listUniqueValueClasses = new() { };
if (existingRenderer is CIMUniqueValueRenderer uniqueRenderer)
{
if (uniqueRenderer.Groups[0].Classes != null)
{
listUniqueValueClasses.AddRange(uniqueRenderer.Groups[0].Classes.ToList());
}
}
// Add new CIMUniqueValueClass (or multiple, if it's a Collection with elements, e.g. VectorLayer)
List<TraversalContext> traversalContexts = new();
if (tc.Current is Collection collection)
{
foreach (var element in collection.elements)
{
TraversalContext newTc = new(element, "elements", tc);
traversalContexts.Add(newTc);
}
}
else
{
traversalContexts.Add(tc);
}
foreach (var tContext in traversalContexts)
{
// get unique label
string? uniqueLabel = tContext.Current?.id;
// remove any GIS-specific classes for now
/*
if (tContext.Current is IGisFeature gisFeat)
{
var existingLabel = gisFeat.attributes["Speckle_ID"];
if (existingLabel is string stringLabel)
{
uniqueLabel = stringLabel;
}
}*/
if (uniqueLabel is not null && !listUniqueValueClasses.Select(x => x.Label).Contains(uniqueLabel))
{
CIMUniqueValueClass newUniqueValueClass = CreateColorCategory(tContext, fLayer.ShapeType, uniqueLabel);
listUniqueValueClasses.Add(newUniqueValueClass);
}
}
// Create a list of CIMUniqueValueGroup
CIMUniqueValueGroup uvg = new() { Classes = listUniqueValueClasses.ToArray(), Heading = "Speckle_ID" };
List<CIMUniqueValueGroup> listUniqueValueGroups = new() { uvg };
// Create the CIMUniqueValueRenderer
CIMUniqueValueRenderer uvr =
new()
{
UseDefaultSymbol = true,
DefaultLabel = "all other values",
DefaultSymbol = defaultSymbol,
Groups = listUniqueValueGroups.ToArray(),
Fields = new string[] { "Speckle_ID" }
};
return uvr;
}
}
@@ -0,0 +1,460 @@
using ArcGIS.Desktop.Mapping;
using Speckle.Sdk.Models.Proxies;
namespace Speckle.Connectors.ArcGIS.HostApp;
public class ArcGISColorUnpacker
{
/// <summary>
/// Cache of all color proxies for converted features. Key is the Color proxy argb value.
/// </summary>
public Dictionary<int, ColorProxy> ColorProxyCache { get; } = new();
/// <summary>
/// Stores the current renderer (determined by mapMember)
/// </summary>
private AC.CIM.CIMRenderer? StoredRenderer { get; set; }
/// <summary>
/// Stores the current renderer (determined by tin mapmember)
/// </summary>
private AC.CIM.CIMTinRenderer? StoredTinRenderer { get; set; }
/// <summary>
/// Stores the used renderer fields from the layer
/// </summary>
private List<string> StoredRendererFields { get; set; }
/// <summary>
/// Stores an already processed color for current mapMember, to dbe used by all mapMember objects. Only applies to simple type renderers
/// </summary>
private int? StoredColor { get; set; }
/// <summary>
/// Stores a feature layer renderer to be used by <see cref="ProcessFeatureLayerColor"/> in <see cref="StoredRenderer"/>, any fields used by the renderer from the layer, and resets the <see cref="StoredColor"/> and <see cref="StoredRendererFields"/>
/// </summary>
/// <param name="featureLayer"></param>
/// <exception cref="AC.CalledOnWrongThreadException">Must be called on MCT.</exception>
public void StoreRendererAndFields(ADM.FeatureLayer featureLayer)
{
// field names are unique, but often their alias is used instead by renderer headings
// so we are storing both names and alias in this dictionary for fast lookup
// POC: adding aliases are not optimal, because they do not need to be unique && they can be the same as the name of another field
Dictionary<string, string> layerFieldDictionary = new();
foreach (ADM.FieldDescription field in featureLayer.GetFieldDescriptions())
{
layerFieldDictionary.TryAdd(field.Name, field.Name);
layerFieldDictionary.TryAdd(field.Alias, field.Name);
}
// clear stored values
StoredRendererFields = new();
StoredColor = null;
StoredRenderer = null;
AC.CIM.CIMRenderer layerRenderer = featureLayer.GetRenderer();
List<string> fields = new();
bool isSupported = false;
switch (layerRenderer)
{
case AC.CIM.CIMSimpleRenderer:
isSupported = true;
break;
case AC.CIM.CIMUniqueValueRenderer uniqueValueRenderer:
isSupported = true;
fields = uniqueValueRenderer.Fields.ToList();
break;
case AC.CIM.CIMClassBreaksRenderer classBreaksRenderer:
isSupported = true;
fields.Add(classBreaksRenderer.Field);
break;
default:
// TODO: log error here that a renderer is unsupported
break;
}
if (isSupported)
{
StoredRenderer = layerRenderer;
foreach (string field in fields)
{
if (layerFieldDictionary.TryGetValue(field, out string? fieldName))
{
StoredRendererFields.Add(fieldName);
}
}
}
}
/// <summary>
/// Stores a las layer renderer to be used by <see cref="ProcessLasLayerColor"/> in <see cref="StoredTinRenderer"/>
/// </summary>
/// <param name="lasLayer"></param>
/// <exception cref="AC.CalledOnWrongThreadException">Must be called on MCT.</exception>
public void StoreRenderer(ADM.LasDatasetLayer lasLayer)
{
// clear stored values
StoredTinRenderer = null;
// POC: not sure why we are only using the first renderer here
AC.CIM.CIMTinRenderer layerRenderer = lasLayer.GetRenderers()[0];
bool isSupported = false;
switch (layerRenderer)
{
case AC.CIM.CIMTinUniqueValueRenderer:
isSupported = true;
break;
default:
// TODO: log error here that a renderer is unsupported
break;
}
if (isSupported)
{
StoredTinRenderer = layerRenderer;
}
}
/// <summary>
/// Processes a las layer's point color by the stored <see cref="StoredRenderer"/>, and stores the point's id and color proxy to the <see cref="ColorProxyCache"/>.
/// POC: logic probably can be combined with ProcessFeatureLayerColor.
/// </summary>
/// <param name="point"></param>
public void ProcessLasLayerColor(ACD.Analyst3D.LasPoint point, string pointApplicationId)
{
// get the color from the renderer and point
AC.CIM.CIMColor? color;
switch (StoredTinRenderer)
{
case AC.CIM.CIMTinUniqueValueRenderer uniqueValueRenderer:
color = GetPointColorByUniqueValueRenderer(uniqueValueRenderer, point);
break;
default:
return;
}
// get or create the color proxy for the point
int argb = CIMColorToInt(color ?? point.RGBColor);
AddObjectIdToColorProxyCache(pointApplicationId, argb);
}
// Retrieves the las point color from a unique value renderer
// unique renderers have groups of conditions that may affect the color of a feature
// resulting in a different color than the default renderer symbol color
private AC.CIM.CIMColor? GetPointColorByUniqueValueRenderer(
AC.CIM.CIMTinUniqueValueRenderer renderer,
ACD.Analyst3D.LasPoint point
)
{
foreach (AC.CIM.CIMUniqueValueGroup group in renderer.Groups)
{
foreach (AC.CIM.CIMUniqueValueClass groupClass in group.Classes)
{
foreach (AC.CIM.CIMUniqueValue value in groupClass.Values)
{
// all field values have to match the row values
for (int i = 0; i < value.FieldValues.Length; i++)
{
string groupValue = value.FieldValues[i].Replace("<Null>", "");
object? pointValue = point.ClassCode;
if (ValuesAreEqual(groupValue, pointValue))
{
return groupClass.Symbol.Symbol.GetColor();
}
}
}
}
}
return null;
}
/// <summary>
/// Processes a feature layer's row color by the stored <see cref="StoredRenderer"/>, and stores the row's id and color proxy to the <see cref="ColorProxyCache"/>.
/// </summary>
/// <param name="row"></param>
/// <returns></returns>
/// <exception cref="ACD.Exceptions.GeodatabaseException"></exception>
public void ProcessFeatureLayerColor(ACD.Row row, string rowApplicationId)
{
// if stored color is not null, this means the renderer was a simple renderer that applies to the entire layer, and was already created.
// just add the row application id to the color proxy.
if (StoredColor is int existingColorProxyId)
{
AddObjectIdToColorProxyCache(rowApplicationId, existingColorProxyId);
}
// get the color from the renderer and row
AC.CIM.CIMColor? color = null;
switch (StoredRenderer)
{
// simple renderers do not rely on fields, so the color can be retrieved from the renderer directly
case AC.CIM.CIMSimpleRenderer simpleRenderer:
color = simpleRenderer.Symbol.Symbol.GetColor();
break;
case AC.CIM.CIMUniqueValueRenderer uniqueValueRenderer:
color = GetRowColorByUniqueValueRenderer(uniqueValueRenderer, row);
break;
case AC.CIM.CIMClassBreaksRenderer classBreaksRenderer:
color = GetRowColorByClassBreaksRenderer(classBreaksRenderer, row);
break;
}
if (color is null)
{
// TODO: log error or throw exception that color could not be retrieved
return;
}
// get or create the color proxy for the row
int argb = CIMColorToInt(color);
AddObjectIdToColorProxyCache(rowApplicationId, argb);
// store color if from simple renderer
if (StoredRenderer is AC.CIM.CIMSimpleRenderer)
{
StoredColor = argb;
}
}
// Retrieves the row color from a class breaks renderer
// unique renderers have groups of conditions that may affect the color of a feature
// resulting in a different color than the default renderer symbol color
private AC.CIM.CIMColor? GetRowColorByClassBreaksRenderer(AC.CIM.CIMClassBreaksRenderer renderer, ACD.Row row)
{
AC.CIM.CIMColor? color = null;
// get the default symbol color
if (renderer.DefaultSymbol?.Symbol.GetColor() is AC.CIM.CIMColor defaultColor)
{
color = defaultColor;
}
// get the first stored field, since this renderer should only have 1 field
double storedFieldValue = Convert.ToDouble(row[StoredRendererFields.First()]);
List<AC.CIM.CIMClassBreak> reversedBreaks = new(renderer.Breaks);
reversedBreaks.Reverse();
foreach (var rBreak in reversedBreaks)
{
// keep looping until the last matching condition
if (storedFieldValue <= rBreak.UpperBound)
{
if (rBreak.Symbol.Symbol.GetColor() is AC.CIM.CIMColor breakColor)
{
color = breakColor;
}
else
{
// TODO: log error here, could not retrieve break color from symbol
}
}
}
return color;
}
// Retrieves the row color from a unique value renderer
// unique renderers have groups of conditions that may affect the color of a feature
// resulting in a different color than the default renderer symbol color
private AC.CIM.CIMColor? GetRowColorByUniqueValueRenderer(AC.CIM.CIMUniqueValueRenderer renderer, ACD.Row row)
{
AC.CIM.CIMColor? color = null;
// get the default symbol color
if (renderer.DefaultSymbol?.Symbol.GetColor() is AC.CIM.CIMColor defaultColor)
{
color = defaultColor;
}
// note: usually there is only 1 group
foreach (AC.CIM.CIMUniqueValueGroup group in renderer.Groups)
{
// loop through all values in groups to see if any have met conditions that result in a different color
foreach (AC.CIM.CIMUniqueValueClass groupClass in group.Classes)
{
bool groupConditionsMet = true;
foreach (AC.CIM.CIMUniqueValue value in groupClass.Values)
{
// all field values have to match the row values
for (int i = 0; i < StoredRendererFields.Count; i++)
{
string groupValue = value.FieldValues[i];
object? rowValue = row[StoredRendererFields[i]];
if (!ValuesAreEqual(groupValue, rowValue))
{
groupConditionsMet = false;
break;
}
}
}
// set the group color to class symbol color if conditions are met
if (groupConditionsMet)
{
if (groupClass.Symbol.Symbol.GetColor() is AC.CIM.CIMColor groupColor)
{
color = groupColor;
}
else
{
// TODO: log error here, could not retrieve group color from symbol
}
}
}
}
return color;
}
/// <summary>
/// Compares the label string of a UniqueValueRenderer (groupValue), and an object value (row, las point), to determine if they are equal
/// </summary>
/// <param name="objectValue"></param>
/// <param name="groupValue"></param>
private bool ValuesAreEqual(string groupValue, object? objectValue)
{
switch (objectValue)
{
case int:
case short:
case long:
case byte:
string objectValueString = Convert.ToString(objectValue) ?? "";
return groupValue.Equals(objectValueString);
case string:
return groupValue.Equals(objectValue);
// POC: these are tricky to compare with the label strings accurately, so will trim both values to 5 decimal places.
case double d:
return double.TryParse(groupValue, out double groupDouble) && groupDouble - d < 0.000001;
case float f:
return float.TryParse(groupValue, out float groupFloat) && groupFloat - f < 0.000001;
default:
return false;
}
}
private void AddObjectIdToColorProxyCache(string objectId, int argb)
{
if (ColorProxyCache.TryGetValue(argb, out ColorProxy? colorProxy))
{
colorProxy.objects.Add(objectId);
}
else
{
ColorProxy newColorProxy =
new()
{
name = argb.ToString(),
objects = new() { objectId },
value = argb,
applicationId = argb.ToString()
};
ColorProxyCache.Add(argb, newColorProxy);
}
}
private int ArgbToInt(int a, int r, int g, int b)
{
return (a << 24) | (r << 16) | (g << 8) | b;
}
// Gets the argb int from a CIMColor
// Defaults to assuming CIMColor.Values represent the red, green, and blue channels.
private int CIMColorToInt(AC.CIM.CIMColor color)
{
switch (color)
{
case AC.CIM.CIMHSVColor hsv:
(float hsvR, float hsvG, float hsvB) = RgbFromHsv(hsv.H, hsv.S, hsv.V);
return ArgbToInt(
(int)Math.Round(hsv.Alpha),
(int)Math.Round(hsvR * 255),
(int)Math.Round(hsvG * 255),
(int)Math.Round(hsvB * 255)
);
case AC.CIM.CIMCMYKColor cmyk:
float k = cmyk.K;
int cmykR = Convert.ToInt32(255 * (1 - cmyk.C) * (1 - k));
int cmykG = Convert.ToInt32(255 * (1 - cmyk.M) * (1 - k));
int cmykB = Convert.ToInt32(255 * (1 - cmyk.Y) * (1 - k));
return ArgbToInt((int)Math.Round(cmyk.Alpha), cmykR, cmykG, cmykB);
default:
return ArgbToInt(
(int)Math.Round(color.Alpha),
(int)Math.Round(color.Values[0]),
(int)Math.Round(color.Values[1]),
(int)Math.Round(color.Values[2])
);
}
}
private (float, float, float) RgbFromHsv(float hue, float saturation, float value)
{
// Translates HSV color to RGB color
// H: 0.0 - 360.0, S: 0.0 - 100.0, V: 0.0 - 100.0
// R, G, B: 0.0 - 1.0
float c = (value / 100) * (saturation / 100);
float x = c * (1 - Math.Abs(((hue / 60) % 2) - 1));
float m = (value / 100) - c;
float r = 0;
float g = 0;
float b = 0;
if (hue >= 0 && hue < 60)
{
r = c;
g = x;
b = 0;
}
else if (hue >= 60 && hue < 120)
{
r = x;
g = c;
b = 0;
}
else if (hue >= 120 && hue < 180)
{
r = 0;
g = c;
b = x;
}
else if (hue >= 180 && hue < 240)
{
r = 0;
g = x;
b = c;
}
else if (hue >= 240 && hue < 300)
{
r = x;
g = 0;
b = c;
}
else if (hue >= 300 && hue < 360)
{
r = c;
g = 0;
b = x;
}
r += m;
g += m;
b += m;
return (r, g, b);
}
}
@@ -0,0 +1,97 @@
using Speckle.Connectors.ArcGIS.HostApp.Extensions;
using Speckle.Sdk.Models.Collections;
namespace Speckle.Connectors.ArcGIS.HostApp;
public class ArcGISLayerUnpacker
{
/// <summary>
/// Cache of all collections created by unpacked Layer MapMembers. Key is the Speckle applicationId (Layer URI).
/// </summary>
public Dictionary<string, Collection> CollectionCache { get; } = new();
/// <summary>
/// Mapmembers can be layers containing objects, or LayerContainers containing other layers.
/// Unpacks selected mapMembers and creates their corresponding collection on the root collection.
/// </summary>
/// <param name="mapMembers"></param>
/// <param name="parentCollection"></param>
/// <returns>List of layers containing objects.</returns>
/// <exception cref="AC.CalledOnWrongThreadException">Thrown when this method is *not* called on the MCT, because this method accesses mapmember fields</exception>
public List<ADM.MapMember> UnpackSelection(
IEnumerable<ADM.MapMember> mapMembers,
Collection parentCollection,
List<ADM.MapMember>? objects = null
)
{
if (objects is null)
{
objects = new();
}
foreach (ADM.MapMember mapMember in mapMembers)
{
switch (mapMember)
{
case ADM.ILayerContainer container:
Collection containerCollection = CreateAndCacheMapMemberCollection(mapMember, true);
parentCollection.elements.Add(containerCollection);
UnpackSelection(container.Layers, containerCollection, objects);
break;
default:
if (!(objects.Contains(mapMember)))
{
Collection collection = CreateAndCacheMapMemberCollection(mapMember);
parentCollection.elements.Add(collection);
objects.Add(mapMember);
}
break;
}
}
return objects;
}
private Collection CreateAndCacheMapMemberCollection(ADM.MapMember mapMember, bool isLayerContainer = false)
{
string mapMemberApplicationId = mapMember.GetSpeckleApplicationId();
Collection collection =
new()
{
name = mapMember.Name,
applicationId = mapMemberApplicationId,
["type"] = mapMember.GetType().Name
};
switch (mapMember)
{
case ADM.IDisplayTable displayTable: // get fields from layers that implement IDisplayTable, eg FeatureLayer or StandaloneTable
Dictionary<string, string>? fields = displayTable
.GetFieldDescriptions()
.ToDictionary(field => field.Name, field => field.Type.ToString());
collection["fields"] = fields;
if (mapMember is ADM.BasicFeatureLayer basicFeatureLayer)
{
collection["shapeType"] = basicFeatureLayer.ShapeType.ToString();
}
break;
case ADM.Layer layer:
collection["mapLayerType"] = layer.MapLayerType.ToString();
break;
case ADM.ILayerContainer:
default:
break;
}
if (!isLayerContainer) // do not cache layer containers, since these won't contain any objects
{
CollectionCache.Add(mapMemberApplicationId, collection);
}
return collection;
}
}
@@ -0,0 +1,31 @@
using ArcGIS.Core.Data.Raster;
namespace Speckle.Connectors.ArcGIS.HostApp.Extensions;
public static class SpeckleApplicationIdExtensions
{
/// <summary>
/// Retrieves the Speckle application id for map members
/// </summary>
public static string GetSpeckleApplicationId(this ADM.MapMember mapMember) => mapMember.URI;
/// <summary>
/// Constructs the Speckle application id for Features as a concatenation of the layer URI (applicationId)
/// and the row OID (index of row in layer).
/// </summary>
/// <exception cref="ACD.Exceptions.GeodatabaseException">Throws when this is *not* called on MCT. Use QueuedTask.Run.</exception>
public static string GetSpeckleApplicationId(this ACD.Row row, string layerApplicationId) =>
$"{layerApplicationId}_{row.GetObjectID()}";
/// <summary>
/// Constructs the Speckle application id for Raster as a concatenation of the layer URI (applicationId) and 0-index
/// </summary>
public static string GetSpeckleApplicationId(this Raster _, string layerApplicationId) => $"{layerApplicationId}_0";
/// <summary>
/// Constructs the Speckle application id for LasDatasets as a concatenation of the layer URI (applicationId)
/// and point OID.
/// </summary>
public static string GetSpeckleApplicationId(this ACD.Analyst3D.LasPoint point, string layerApplicationId) =>
$"{layerApplicationId}_{point.PointID}";
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 524 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 948 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 714 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

@@ -0,0 +1,397 @@
using System.Diagnostics.Contracts;
using ArcGIS.Core.CIM;
using ArcGIS.Core.Geometry;
using ArcGIS.Desktop.Mapping;
using Speckle.Connectors.ArcGIS.HostApp;
using Speckle.Connectors.ArcGIS.Utils;
using Speckle.Connectors.Common.Builders;
using Speckle.Connectors.Common.Conversion;
using Speckle.Connectors.Common.Instances;
using Speckle.Connectors.Common.Operations;
using Speckle.Converters.ArcGIS3;
using Speckle.Converters.ArcGIS3.Utils;
using Speckle.Converters.Common;
using Speckle.Objects.Data;
using Speckle.Objects.Other;
using Speckle.Sdk;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Models.GraphTraversal;
using Speckle.Sdk.Models.Instances;
using Speckle.Sdk.Models.Proxies;
namespace Speckle.Connectors.ArcGIS.Operations.Receive;
public class ArcGISHostObjectBuilder : IHostObjectBuilder
{
private readonly IRootToHostConverter _converter;
private readonly IFeatureClassUtils _featureClassUtils;
private readonly ILocalToGlobalUnpacker _localToGlobalUnpacker;
private readonly LocalToGlobalConverterUtils _localToGlobalConverterUtils;
// POC: figure out the correct scope to only initialize on Receive
private readonly IConverterSettingsStore<ArcGISConversionSettings> _settingsStore;
private readonly GraphTraversal _traverseFunction;
private readonly ArcGISColorManager _colorManager;
public ArcGISHostObjectBuilder(
IRootToHostConverter converter,
IConverterSettingsStore<ArcGISConversionSettings> settingsStore,
IFeatureClassUtils featureClassUtils,
ILocalToGlobalUnpacker localToGlobalUnpacker,
LocalToGlobalConverterUtils localToGlobalConverterUtils,
GraphTraversal traverseFunction,
ArcGISColorManager colorManager
)
{
_converter = converter;
_settingsStore = settingsStore;
_featureClassUtils = featureClassUtils;
_localToGlobalUnpacker = localToGlobalUnpacker;
_localToGlobalConverterUtils = localToGlobalConverterUtils;
_traverseFunction = traverseFunction;
_colorManager = colorManager;
}
public Task<HostObjectBuilderResult> Build(
Base rootObject,
string projectName,
string modelName,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
{
// TODO get spatialRef and offsets & rotation from ProjectInfo in CommitObject
// ATM, GIS commit CRS is stored per layer (in FeatureClass converter), but should be moved to the Root level too
// Prompt the UI conversion started. Progress bar will swoosh.
onOperationProgressed.Report(new("Converting", null));
// get materials
List<RenderMaterialProxy>? materials = (rootObject[ProxyKeys.RENDER_MATERIAL] as List<object>)
?.Cast<RenderMaterialProxy>()
.ToList();
if (materials != null)
{
_colorManager.ParseMaterials(materials, onOperationProgressed);
}
// get colors
List<ColorProxy>? colors = (rootObject[ProxyKeys.COLOR] as List<object>)?.Cast<ColorProxy>().ToList();
if (colors != null)
{
_colorManager.ParseColors(colors, onOperationProgressed);
}
int count = 0;
IReadOnlyCollection<LocalToGlobalMap> objectsToConvert = GetObjectsToConvert(rootObject);
Dictionary<TraversalContext, ObjectConversionTracker> conversionTracker = new();
// 1. convert everything
List<ReceiveConversionResult> results = new(objectsToConvert.Count);
List<string> bakedObjectIds = new();
foreach (LocalToGlobalMap objectToConvert in objectsToConvert)
{
string[] path = GetLayerPath(objectToConvert.TraversalContext);
Base obj = objectToConvert.AtomicObject;
cancellationToken.ThrowIfCancellationRequested();
try
{
obj = _localToGlobalConverterUtils.TransformObjects(objectToConvert.AtomicObject, objectToConvert.Matrix);
object conversionResult = _converter.Convert(obj);
string nestedLayerPath = $"{string.Join("\\", path)}";
if (obj is ArcgisObject gisObj)
{
nestedLayerPath += $"\\{gisObj.name}";
}
else
{
nestedLayerPath += $"\\{obj.speckle_type.Split(".")[^1]}"; // add sub-layer by speckleType, for non-GIS objects
}
conversionTracker[objectToConvert.TraversalContext] = new ObjectConversionTracker(
obj,
(Geometry?)conversionResult,
nestedLayerPath
);
}
catch (Exception ex) when (!ex.IsFatal()) // DO NOT CATCH SPECIFIC STUFF, conversion errors should be recoverable
{
results.Add(new(Status.ERROR, obj, null, null, ex));
}
onOperationProgressed.Report(new("Converting", (double)++count / objectsToConvert.Count));
}
// 2.1. Group conversionTrackers (to write into datasets)
onOperationProgressed.Report(new("Grouping features into layers", null));
Dictionary<string, List<(TraversalContext, ObjectConversionTracker)>> convertedGroups =
_featureClassUtils.GroupConversionTrackers(
conversionTracker,
(s, progres) => onOperationProgressed.Report(new(s, progres))
);
// 2.2. Write groups of objects to Datasets
onOperationProgressed.Report(new("Writing to Database", null));
_featureClassUtils.CreateDatasets(
conversionTracker,
convertedGroups,
(s, progres) => onOperationProgressed.Report(new(s, progres))
);
// 3. add layer and tables to the Map and Table Of Content
// Create placeholder for GroupLayers
Dictionary<string, GroupLayer> createdLayerGroups = new();
int bakeCount = 0;
Dictionary<string, (MapMember, CIMUniqueValueRenderer?)> bakedMapMembers = new();
onOperationProgressed.Report(new("Adding to Map", bakeCount));
foreach (var item in conversionTracker)
{
cancellationToken.ThrowIfCancellationRequested();
var trackerItem = conversionTracker[item.Key]; // updated tracker object
// BAKE OBJECTS HERE
if (trackerItem.Exception != null)
{
results.Add(new(Status.ERROR, trackerItem.Base, null, null, trackerItem.Exception));
}
else if (trackerItem.DatasetId == null)
{
results.Add(
new(
Status.ERROR,
trackerItem.Base,
null,
null,
new ArgumentException($"Unknown error: Dataset not created for {trackerItem.Base.speckle_type}")
)
);
}
else if (bakedMapMembers.TryGetValue(trackerItem.DatasetId, out var value))
{
// if the layer already created, just add more features to report, and more color categories
// add layer and layer URI to tracker
trackerItem.AddConvertedMapMember(value.Item1);
trackerItem.AddLayerURI(value.Item1.URI);
conversionTracker[item.Key] = trackerItem; // not necessary atm, but needed if we use conversionTracker further
// add color category
CIMUniqueValueRenderer? uvr = _colorManager.CreateOrEditLayerRenderer(item.Key, trackerItem, value.Item2);
// replace renderer
bakedMapMembers[trackerItem.DatasetId] = (value.Item1, uvr);
// only add a report item
AddResultsFromTracker(trackerItem, results);
}
else
{
// no layer yet, create and add layer to Map
MapMember mapMember = AddDatasetsToMap(trackerItem, createdLayerGroups, projectName, modelName);
// add layer and layer URI to tracker
trackerItem.AddConvertedMapMember(mapMember);
trackerItem.AddLayerURI(mapMember.URI);
conversionTracker[item.Key] = trackerItem; // not necessary atm, but needed if we use conversionTracker further
// add layer URI to bakedIds
bakedObjectIds.Add(trackerItem.MappedLayerURI == null ? "" : trackerItem.MappedLayerURI);
// add color category
CIMUniqueValueRenderer? uvr = _colorManager.CreateOrEditLayerRenderer(item.Key, trackerItem, null);
// mark dataset as already created
bakedMapMembers[trackerItem.DatasetId] = (mapMember, uvr);
// add report item
AddResultsFromTracker(trackerItem, results);
}
onOperationProgressed.Report(new("Adding to Map", (double)++bakeCount / conversionTracker.Count));
}
// apply renderers to baked layers
foreach (var bakedMember in bakedMapMembers)
{
if (bakedMember.Value.Item1 is FeatureLayer fLayer)
{
// Set the feature layer's renderer.
fLayer.SetRenderer(bakedMember.Value.Item2);
}
}
bakedObjectIds.AddRange(createdLayerGroups.Values.Select(x => x.URI));
// TODO: validated a correct set regarding bakedobject ids
return Task.FromResult(new HostObjectBuilderResult(bakedObjectIds, results));
}
private IReadOnlyCollection<LocalToGlobalMap> GetObjectsToConvert(Base rootObject)
{
// keep GISlayers in the list, because they are still needed to extract CRS of the commit (code below)
List<TraversalContext> objectsToConvertTc = _traverseFunction.Traverse(rootObject).ToList();
// now filter the objects
objectsToConvertTc = objectsToConvertTc.Where(ctx => ctx.Current is not Collection).ToList();
var instanceDefinitionProxies = (rootObject[ProxyKeys.INSTANCE_DEFINITION] as List<object>)
?.Cast<InstanceDefinitionProxy>()
.ToList();
return _localToGlobalUnpacker.Unpack(instanceDefinitionProxies, objectsToConvertTc);
}
private void AddResultsFromTracker(ObjectConversionTracker trackerItem, List<ReceiveConversionResult> results)
{
if (trackerItem.MappedLayerURI == null) // should not happen
{
results.Add(
new(
Status.ERROR,
trackerItem.Base,
null,
null,
new ArgumentException($"Created Layer URI not found for {trackerItem.Base.speckle_type}")
)
);
}
else
{
// encode layer ID and ID of its feature in 1 object represented as string
ObjectID objectId = new(trackerItem.MappedLayerURI, trackerItem.DatasetRow);
if (trackerItem.HostAppGeom != null) // individual hostAppGeometry
{
results.Add(
new(
Status.SUCCESS,
trackerItem.Base,
objectId.ObjectIdToString(),
trackerItem.HostAppGeom.GetType().ToString()
)
);
}
else // hostApp Layers
{
results.Add(
new(
Status.SUCCESS,
trackerItem.Base,
objectId.ObjectIdToString(),
trackerItem.HostAppMapMember?.GetType().ToString()
)
);
}
}
}
private MapMember AddDatasetsToMap(
ObjectConversionTracker trackerItem,
Dictionary<string, GroupLayer> createdLayerGroups,
string projectName,
string modelName
)
{
// get layer details
string? datasetId = trackerItem.DatasetId; // should not be null here
Uri uri = new($"{_settingsStore.Current.SpeckleDatabasePath.AbsolutePath.Replace('/', '\\')}\\{datasetId}");
string nestedLayerName = trackerItem.NestedLayerName;
// add group for the current layer
string shortName = nestedLayerName.Split("\\")[^1];
string nestedLayerPath = string.Join("\\", nestedLayerName.Split("\\").SkipLast(1));
// if no general group layer found
if (createdLayerGroups.Count == 0)
{
Map map = _settingsStore.Current.Map;
GroupLayer mainGroupLayer = LayerFactory.Instance.CreateGroupLayer(map, 0, $"{projectName}: {modelName}");
mainGroupLayer.SetExpanded(true);
createdLayerGroups["Basic Speckle Group"] = mainGroupLayer; // key doesn't really matter here
}
var groupLayer = CreateNestedGroupLayer(nestedLayerPath, createdLayerGroups);
// Most of the Speckle-written datasets will be containing geometry and added as Layers
// although, some datasets might be just tables (e.g. native GIS Tables, in the future maybe Revit schedules etc.
// We can create a connection to the dataset in advance and determine its type, but this will be more
// expensive, than assuming by default that it's a layer with geometry (which in most cases it's expected to be)
try
{
var layer = LayerFactory.Instance.CreateLayer(uri, groupLayer, layerName: shortName);
if (layer == null)
{
throw new SpeckleException($"Layer '{shortName}' was not created");
}
layer.SetExpanded(false);
// if Scene
// https://community.esri.com/t5/arcgis-pro-sdk-questions/sdk-equivalent-to-changing-layer-s-elevation/td-p/1346139
if (_settingsStore.Current.Map.IsScene)
{
var groundSurfaceLayer = _settingsStore.Current.Map.GetGroundElevationSurfaceLayer();
var layerElevationSurface = new CIMLayerElevationSurface { ElevationSurfaceLayerURI = groundSurfaceLayer.URI, };
// for Feature Layers
if (layer.GetDefinition() is CIMFeatureLayer cimLyr)
{
cimLyr.LayerElevation = layerElevationSurface;
layer.SetDefinition(cimLyr);
}
}
return layer;
}
catch (ArgumentException)
{
StandaloneTable table = StandaloneTableFactory.Instance.CreateStandaloneTable(
uri,
groupLayer,
tableName: shortName
);
return table;
}
}
private GroupLayer CreateNestedGroupLayer(string nestedLayerPath, Dictionary<string, GroupLayer> createdLayerGroups)
{
GroupLayer lastGroup = createdLayerGroups.FirstOrDefault().Value;
if (lastGroup == null) // if layer not found
{
throw new InvalidOperationException("Speckle Layer Group not found");
}
// iterate through each nested level
string createdGroupPath = "";
var allPathElements = nestedLayerPath.Split("\\").Where(x => !string.IsNullOrEmpty(x));
foreach (string pathElement in allPathElements)
{
createdGroupPath += "\\" + pathElement;
if (createdLayerGroups.TryGetValue(createdGroupPath, out var existingGroupLayer))
{
lastGroup = existingGroupLayer;
}
else
{
// create new GroupLayer under last found Group, named with last pathElement
lastGroup = LayerFactory.Instance.CreateGroupLayer(lastGroup, 0, pathElement);
lastGroup.SetExpanded(true);
}
createdLayerGroups[createdGroupPath] = lastGroup;
}
return lastGroup;
}
[Pure]
private static string[] GetLayerPath(TraversalContext context)
{
string[] collectionBasedPath = context.GetAscendantOfType<Collection>().Select(c => c.name).ToArray();
string[] reverseOrderPath =
collectionBasedPath.Length != 0 ? collectionBasedPath : context.GetPropertyPath().ToArray();
var originalPath = reverseOrderPath.Reverse().ToArray();
return originalPath.Where(x => !string.IsNullOrEmpty(x)).ToArray();
}
}
@@ -0,0 +1,265 @@
using ArcGIS.Core.Data.Raster;
using ArcGIS.Core.Geometry;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.ArcGIS.HostApp;
using Speckle.Connectors.ArcGIS.HostApp.Extensions;
using Speckle.Connectors.ArcGIS.Utils;
using Speckle.Connectors.Common.Builders;
using Speckle.Connectors.Common.Conversion;
using Speckle.Connectors.Common.Extensions;
using Speckle.Connectors.Common.Operations;
using Speckle.Converters.ArcGIS3;
using Speckle.Converters.Common;
using Speckle.Sdk;
using Speckle.Sdk.Logging;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
namespace Speckle.Connectors.ArcGis.Operations.Send;
/// <summary>
/// Stateless builder object to turn an ISendFilter into a <see cref="Base"/> object
/// </summary>
public class ArcGISRootObjectBuilder : IRootObjectBuilder<ADM.MapMember>
{
private readonly IRootToSpeckleConverter _rootToSpeckleConverter;
private readonly ArcGISLayerUnpacker _layerUnpacker;
private readonly ArcGISColorUnpacker _colorUnpacker;
private readonly IConverterSettingsStore<ArcGISConversionSettings> _converterSettings;
private readonly ILogger<ArcGISRootObjectBuilder> _logger;
private readonly ISdkActivityFactory _activityFactory;
private readonly MapMembersUtils _mapMemberUtils;
public ArcGISRootObjectBuilder(
ArcGISLayerUnpacker layerUnpacker,
ArcGISColorUnpacker colorUnpacker,
IConverterSettingsStore<ArcGISConversionSettings> converterSettings,
IRootToSpeckleConverter rootToSpeckleConverter,
ILogger<ArcGISRootObjectBuilder> logger,
ISdkActivityFactory activityFactory,
MapMembersUtils mapMemberUtils
)
{
_layerUnpacker = layerUnpacker;
_colorUnpacker = colorUnpacker;
_converterSettings = converterSettings;
_rootToSpeckleConverter = rootToSpeckleConverter;
_logger = logger;
_activityFactory = activityFactory;
_mapMemberUtils = mapMemberUtils;
}
public async Task<RootObjectBuilderResult> Build(
IReadOnlyList<ADM.MapMember> layers,
SendInfo __,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
{
// TODO: add a warning if Geographic CRS is set
// "Data has been sent in the units 'degrees'. It is advisable to set the project CRS to Projected type (e.g. EPSG:32631) to be able to receive geometry correctly in CAD/BIM software"
// 0 - Create Root collection and attach CRS properties
// CRS properties are useful for data based workflows coming out of gis applications
SpatialReference sr = _converterSettings.Current.ActiveCRSoffsetRotation.SpatialReference;
Dictionary<string, object?> spatialReference =
new()
{
["name"] = sr.Name,
["unit"] = sr.Unit.Name,
["wkid"] = sr.Wkid,
["wkt"] = sr.Wkt,
};
Dictionary<string, object?> crs =
new()
{
["trueNorthRadians"] = _converterSettings.Current.ActiveCRSoffsetRotation.TrueNorthRadians,
["latOffset"] = _converterSettings.Current.ActiveCRSoffsetRotation.LatOffset,
["lonOffset"] = _converterSettings.Current.ActiveCRSoffsetRotation.LonOffset,
["spatialReference"] = spatialReference
};
Collection rootCollection =
new()
{
name = ADM.MapView.Active.Map.Name,
["units"] = _converterSettings.Current.SpeckleUnits,
["crs"] = crs
};
// 1 - Unpack the selected mapmembers
// In Arcgis, mapmembers are collections of other mapmember or objects.
// We need to unpack the selected mapmembers into all leaf-level mapmembers (containing just objects) and build the root collection structure during unpacking.
// Mapmember dynamically attached properties are also added at this step.
List<ADM.MapMember> unpackedLayers;
ADM.Map map = ADM.MapView.Active.Map;
IEnumerable<ADM.MapMember> layersOrdered = _mapMemberUtils.GetMapMembersInOrder(map, layers);
using (var _ = _activityFactory.Start("Unpacking selection"))
{
unpackedLayers = _layerUnpacker.UnpackSelection(layersOrdered, rootCollection);
}
List<SendConversionResult> results = new(unpackedLayers.Count);
onOperationProgressed.Report(new("Converting", null));
using (var convertingActivity = _activityFactory.Start("Converting objects"))
{
int count = 0;
foreach (ADM.MapMember layer in unpackedLayers)
{
cancellationToken.ThrowIfCancellationRequested();
string layerApplicationId = layer.GetSpeckleApplicationId();
try
{
// get the corresponding collection for this layer - we'll add all converted objects to the collection
if (_layerUnpacker.CollectionCache.TryGetValue(layerApplicationId, out Collection? layerCollection))
{
var status = Status.SUCCESS;
var sdkStatus = SdkActivityStatusCode.Ok;
// TODO: check cache first to see if this layer was previously converted
/*
if (_sendConversionCache.TryGetValue(
sendInfo.ProjectId,
layerApplicationId,
out ObjectReference? value
))
{
}
*/
switch (layer)
{
case ADM.FeatureLayer featureLayer:
List<Base> convertedFeatureLayerObjects = ConvertFeatureLayerObjects(featureLayer);
layerCollection.elements.AddRange(convertedFeatureLayerObjects);
break;
case ADM.RasterLayer rasterLayer:
List<Base> convertedRasterLayerObjects = ConvertRasterLayerObjects(rasterLayer);
layerCollection.elements.AddRange(convertedRasterLayerObjects);
break;
case ADM.LasDatasetLayer lasDatasetLayer:
List<Base> convertedLasDatasetObjects = ConvertLasDatasetLayerObjects(lasDatasetLayer);
layerCollection.elements.AddRange(convertedLasDatasetObjects);
break;
default:
status = Status.ERROR;
sdkStatus = SdkActivityStatusCode.Error;
break;
}
results.Add(new(status, layerApplicationId, layer.GetType().Name, layerCollection));
convertingActivity?.SetStatus(sdkStatus);
}
else
{
throw new SpeckleException($"No converted Collection found for layer {layerApplicationId}.");
}
}
catch (Exception ex) when (!ex.IsFatal())
{
_logger.LogSendConversionError(ex, layer.GetType().Name);
results.Add(new(Status.ERROR, layerApplicationId, layer.GetType().Name, null, ex));
convertingActivity?.SetStatus(SdkActivityStatusCode.Error);
convertingActivity?.RecordException(ex);
}
onOperationProgressed.Report(new("Converting", (double)++count / layers.Count));
await Task.Yield();
}
}
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.
}
// 3 - Add Color Proxies
rootCollection[ProxyKeys.COLOR] = _colorUnpacker.ColorProxyCache.Values.ToList();
return new RootObjectBuilderResult(rootCollection, results);
}
private List<Base> ConvertFeatureLayerObjects(ADM.FeatureLayer featureLayer)
{
string layerApplicationId = featureLayer.GetSpeckleApplicationId();
List<Base> convertedObjects = new();
// store the layer renderer for color unpacking
_colorUnpacker.StoreRendererAndFields(featureLayer);
// search the rows of the layer, where each row is treated like an object
// RowCursor is IDisposable but is not being correctly picked up by IDE warnings.
// This means we need to be carefully adding using statements based on the API documentation coming from each method/class
using (ACD.RowCursor rowCursor = featureLayer.Search())
{
while (rowCursor.MoveNext())
{
// Same IDisposable issue appears to happen on Row class too. Docs say it should always be disposed of manually by the caller.
using (ACD.Row row = rowCursor.Current)
{
// get application id. test for subtypes before defaulting to base type.
Base converted = _rootToSpeckleConverter.Convert(row);
string applicationId = row.GetSpeckleApplicationId(layerApplicationId);
converted.applicationId = applicationId;
convertedObjects.Add(converted);
// process the object color
_colorUnpacker.ProcessFeatureLayerColor(row, applicationId);
}
}
}
return convertedObjects;
}
// POC: raster colors are stored as mesh vertex colors in RasterToSpeckleConverter. Should probably move to color unpacker.
private List<Base> ConvertRasterLayerObjects(ADM.RasterLayer rasterLayer)
{
string layerApplicationId = rasterLayer.GetSpeckleApplicationId();
List<Base> convertedObjects = new();
Raster raster = rasterLayer.GetRaster();
Base converted = _rootToSpeckleConverter.Convert(raster);
string applicationId = raster.GetSpeckleApplicationId(layerApplicationId);
converted.applicationId = applicationId;
convertedObjects.Add(converted);
return convertedObjects;
}
private List<Base> ConvertLasDatasetLayerObjects(ADM.LasDatasetLayer lasDatasetLayer)
{
string layerApplicationId = lasDatasetLayer.GetSpeckleApplicationId();
List<Base> convertedObjects = new();
try
{
// store the layer renderer for color unpacking
_colorUnpacker.StoreRenderer(lasDatasetLayer);
using (ACD.Analyst3D.LasPointCursor ptCursor = lasDatasetLayer.SearchPoints(new ACD.Analyst3D.LasPointFilter()))
{
while (ptCursor.MoveNext())
{
using (ACD.Analyst3D.LasPoint pt = ptCursor.Current)
{
Base converted = _rootToSpeckleConverter.Convert(pt);
string applicationId = pt.GetSpeckleApplicationId(layerApplicationId);
converted.applicationId = applicationId;
convertedObjects.Add(converted);
// process the object color
_colorUnpacker.ProcessLasLayerColor(pt, applicationId);
}
}
}
}
catch (ACD.Exceptions.TinException ex)
{
throw new SpeckleException("3D analyst extension is not enabled for .las layer operations", ex);
}
return convertedObjects;
}
}
@@ -0,0 +1,14 @@
{
"profiles": {
"Speckle.Connectors.ArcGIS3_all_users": {
"commandName": "Executable",
"executablePath": "C:\\Program Files\\ArcGIS\\Pro\\bin\\ArcGISPro.exe",
"commandLineArgs": ""
},
"Speckle.Connectors.ArcGIS3_user": {
"commandName": "Executable",
"executablePath": "C:\\Users\\%USERNAME%\\AppData\\Local\\Programs\\ArcGIS\\Pro\\bin\\ArcGISPro.exe",
"commandLineArgs": ""
}
}
}
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
<PlatformTarget>x64</PlatformTarget>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<RootNameSpace>Speckle.Connectors.ArcGIS</RootNameSpace>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
<DefineConstants>$(DefineConstants);ARCGIS3</DefineConstants>
<Configurations>Debug;Release;Local</Configurations>
</PropertyGroup>
<ItemGroup>
<Content Include="Config.daml" />
<Content Include="Images\s2logo_16.png" />
<Content Include="Images\s2logo_32.png" />
<Content Include="DarkImages\s2logo_16.png" />
<Content Include="DarkImages\s2logo_32.png" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Esri.ArcGISPro.Extensions30" IncludeAssets="compile" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Converters\ArcGIS\Speckle.Converters.ArcGIS3\Speckle.Converters.ArcGIS3.csproj" />
<ProjectReference Include="..\..\..\DUI3\Speckle.Connectors.DUI.WebView\Speckle.Connectors.DUI.WebView.csproj" />
<ProjectReference Include="..\..\..\Sdk\Speckle.Connectors.Common\Speckle.Connectors.Common.csproj" />
</ItemGroup>
<Import Project="Esri.ArcGISPro.Extensions30.Speckle.targets" />
</Project>
@@ -0,0 +1,42 @@
using ArcGIS.Desktop.Framework;
using ArcGIS.Desktop.Framework.Contracts;
namespace Speckle.Connectors.ArcGIS;
internal sealed class SpeckleDUI3ViewModel : DockPane
{
private const string DOCKPANE_ID = "SpeckleDUI3_SpeckleDUI3";
internal static void Create()
{
var pane = FrameworkApplication.DockPaneManager.Find(DOCKPANE_ID);
pane?.Activate();
}
/// <summary>
/// Called when the pane is initialized.
/// </summary>
protected override async Task InitializeAsync()
{
await base.InitializeAsync();
}
/// <summary>
/// Called when the pane is uninitialized.
/// </summary>
protected override async Task UninitializeAsync()
{
await base.UninitializeAsync();
}
}
/// <summary>
/// Button implementation to create a new instance of the pane and activate it.
/// </summary>
internal sealed class SpeckleDUI3OpenButton : Button
{
protected override void OnClick()
{
SpeckleDUI3ViewModel.Create();
}
}
@@ -0,0 +1,18 @@
using System.Windows.Controls;
using Microsoft.Extensions.DependencyInjection;
using Speckle.Connectors.DUI.WebView;
namespace Speckle.Connectors.ArcGIS;
public class SpeckleDUI3Wrapper : UserControl
{
public SpeckleDUI3Wrapper()
{
Initialize();
}
private void Initialize()
{
Content = SpeckleModule.Current.Container.GetRequiredService<DUI3ControlWebView>();
}
}
@@ -0,0 +1,62 @@
using ArcGIS.Desktop.Framework;
using Microsoft.Extensions.DependencyInjection;
using Speckle.Connectors.ArcGIS.DependencyInjection;
using Speckle.Connectors.Common;
using Speckle.Connectors.DUI;
using Speckle.Converters.ArcGIS3;
using Speckle.Sdk.Host;
using Module = ArcGIS.Desktop.Framework.Contracts.Module;
namespace Speckle.Connectors.ArcGIS;
/// <summary>
/// This sample shows how to implement pane that contains an Edge WebView2 control using the built-in ArcGIS Pro SDK's WebBrowser control. For details on how to utilize the WebBrowser control in an add-in see here: https://github.com/Esri/arcgis-pro-sdk/wiki/ProConcepts-Framework#webbrowser For details on how to utilize the Microsoft Edge web browser control in an add-in see here: https://github.com/Esri/arcgis-pro-sdk/wiki/ProConcepts-Framework#webbrowser-control
/// </summary>
internal sealed class SpeckleModule : Module
{
private static SpeckleModule? s_this;
private readonly IDisposable? _disposableLogger;
/// <summary>
/// Retrieve the singleton instance to this module here
/// </summary>
public static SpeckleModule Current =>
s_this ??= (SpeckleModule)FrameworkApplication.FindModule("ConnectorArcGIS_Module");
public ServiceProvider Container { get; }
public SpeckleModule()
{
AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolver.OnAssemblyResolve<SpeckleModule>;
var services = new ServiceCollection();
// init DI
_disposableLogger = services.Initialize(HostApplications.ArcGIS, GetVersion());
services.AddArcGIS();
services.AddArcGISConverters();
Container = services.BuildServiceProvider();
Container.UseDUI();
}
private HostAppVersion GetVersion()
{
#if ARCGIS3
return HostAppVersion.v3;
#else
throw new NotImplementedException();
#endif
}
/// <summary>
/// Called by Framework when ArcGIS Pro is closing
/// </summary>
/// <returns>False to prevent Pro from closing, otherwise True</returns>
protected override bool CanUnload()
{
//TODO - add your business logic
//return false to ~cancel~ Application close
_disposableLogger?.Dispose();
Container.Dispose();
return true;
}
}
@@ -0,0 +1,36 @@
using ArcGIS.Desktop.Framework.Threading.Tasks;
using Speckle.Connectors.Common.Threading;
namespace Speckle.Connectors.ArcGIS.Utils;
//don't check for GUI as it's the same check we do in ThreadContext
public class ArcGISThreadContext : ThreadContext
{
protected override Task<T> MainToWorkerAsync<T>(Func<Task<T>> action)
{
if (QueuedTask.OnWorker)
{
return action();
}
else
{
return QueuedTask.Run(async () => await action());
}
}
protected override Task<T> WorkerToMainAsync<T>(Func<Task<T>> action) => QueuedTask.Run(async () => await action());
protected override Task<T> MainToWorker<T>(Func<T> action)
{
if (QueuedTask.OnWorker)
{
return Task.FromResult(action());
}
else
{
return QueuedTask.Run(action);
}
}
protected override Task<T> WorkerToMain<T>(Func<T> action) => QueuedTask.Run(action);
}
@@ -0,0 +1,139 @@
using System.Xml.Linq;
using ArcGIS.Desktop.Core.Events;
using ArcGIS.Desktop.Mapping;
using ArcGIS.Desktop.Mapping.Events;
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Eventing;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Utils;
namespace Speckle.Connectors.ArcGIS.Utils;
public class ArcGISDocumentStore : DocumentModelStore
{
private readonly IThreadContext _threadContext;
private readonly IEventAggregator _eventAggregator;
public ArcGISDocumentStore(
IJsonSerializer jsonSerializer,
ITopLevelExceptionHandler topLevelExceptionHandler,
IThreadContext threadContext,
IEventAggregator eventAggregator
)
: base(jsonSerializer)
{
_threadContext = threadContext;
_eventAggregator = eventAggregator;
ActiveMapViewChangedEvent.Subscribe(a => topLevelExceptionHandler.CatchUnhandled(() => OnMapViewChanged(a)), true);
ProjectSavingEvent.Subscribe(
_ =>
{
topLevelExceptionHandler.CatchUnhandled(OnProjectSaving);
return Task.CompletedTask;
},
true
);
ProjectClosingEvent.Subscribe(
_ =>
{
topLevelExceptionHandler.CatchUnhandled(OnProjectClosing);
return Task.CompletedTask;
},
true
);
}
public override async Task OnDocumentStoreInitialized()
{
// in case plugin was loaded into already opened Map, read metadata from the current Map
if (!IsDocumentInit && MapView.Active != null)
{
IsDocumentInit = true;
LoadState();
await _eventAggregator.GetEvent<DocumentStoreChangedEvent>().PublishAsync(new object());
}
}
private void OnProjectClosing()
{
if (MapView.Active is null)
{
return;
}
SaveState();
}
private void OnProjectSaving()
{
if (MapView.Active is not null)
{
SaveState();
}
}
/// <summary>
/// On map view switch, this event trigger twice, first for outgoing view, second for incoming view.
/// </summary>
private async void OnMapViewChanged(ActiveMapViewChangedEventArgs args)
{
if (args.IncomingView is null)
{
return;
}
IsDocumentInit = true;
LoadState();
await _eventAggregator.GetEvent<DocumentStoreChangedEvent>().PublishAsync(new object());
}
protected override void HostAppSaveState(string modelCardState) =>
_threadContext
.RunOnWorker(() =>
{
Map map = MapView.Active.Map;
// Read existing metadata - To prevent messing existing metadata. 🤞 Hope other add-in developers will do same :D
var existingMetadata = map.GetMetadata();
// Parse existing metadata
XDocument existingXmlDocument = !string.IsNullOrEmpty(existingMetadata)
? XDocument.Parse(existingMetadata)
: new XDocument(new XElement("metadata"));
XElement xmlModelCards = new("SpeckleModelCards", modelCardState);
// Check if SpeckleModelCards element already exists at root and update it
var speckleModelCardsElement = existingXmlDocument.Root?.Element("SpeckleModelCards");
if (speckleModelCardsElement != null)
{
speckleModelCardsElement.ReplaceWith(xmlModelCards);
}
else
{
existingXmlDocument.Root?.Add(xmlModelCards);
}
map.SetMetadata(existingXmlDocument.ToString());
})
.FireAndForget();
protected override void LoadState() =>
_threadContext
.RunOnWorker(() =>
{
Map map = MapView.Active.Map;
var metadata = map.GetMetadata();
var root = XDocument.Parse(metadata).Root;
var element = root?.Element("SpeckleModelCards");
if (element is null)
{
ClearAndSave();
return;
}
string modelsString = element.Value;
LoadFromString(modelsString);
})
.FireAndForget();
}
@@ -0,0 +1,16 @@
using ArcGIS.Desktop.Mapping;
namespace Speckle.Connectors.ArcGIS.Utils;
// bind together a layer object on the map, and auto-assigned ID if the specific feature
public readonly struct MapMemberFeature
{
public int? FeatureId { get; } // unique feature id (start from 0) of a feature in the layer
public MapMember MapMember { get; } // layer object on the Map
public MapMemberFeature(MapMember mapMember, int? featureId)
{
MapMember = mapMember;
FeatureId = featureId;
}
}
@@ -0,0 +1,61 @@
using ArcGIS.Desktop.Mapping;
namespace Speckle.Connectors.ArcGIS.Utils;
public class MapMembersUtils
{
/// <summary>
/// Returns all Layers and Standalone Tables present on the Map
/// </summary>
/// <param name="map"></param>
/// <returns></returns>
public List<MapMember> GetAllMapMembers(Map map)
{
// first get all map layers
List<MapMember> mapMembers = new();
var layerMapMembers = UnpackMapLayers(map.Layers);
mapMembers.AddRange(layerMapMembers);
// add tables
var standaloneTableMapMembers = UnpackMapLayers(map.StandaloneTables);
mapMembers.AddRange(standaloneTableMapMembers);
return mapMembers;
}
public List<MapMember> UnpackMapLayers(IEnumerable<MapMember> mapMembersToUnpack)
{
List<MapMember> mapMembers = new();
foreach (var layer in mapMembersToUnpack)
{
mapMembers.Add(layer);
switch (layer)
{
case ILayerContainer subGroup:
var subLayerMapMembers = UnpackMapLayers(subGroup.Layers);
mapMembers.AddRange(subLayerMapMembers);
break;
}
}
return mapMembers;
}
/// <summary>
/// Sorts the selected mapmembers into the same order as they appear in the Table of Contents (TOC) bar in the file.
/// This is a required step before unpacking layers, because depending on the user selection order, some children layers may appear before their container layer if both the container and children layers are selected.
/// </summary>
public IEnumerable<MapMember> GetMapMembersInOrder(Map map, IReadOnlyList<MapMember> selectedMapMembers)
{
// first get all map layers
List<MapMember> allMapMembers = GetAllMapMembers(map);
// recalculate selected layer priority from all map layers
foreach (MapMember mapMember in allMapMembers)
{
if (selectedMapMembers.Contains(mapMember))
{
yield return mapMember;
}
}
}
}
@@ -0,0 +1,38 @@
namespace Speckle.Connectors.ArcGIS.Utils;
// this struct is needed to be able to parse single-string value into IDs of both a layer, and it's individual feature
public struct ObjectID
{
private const string FEATURE_ID_SEPARATOR = "__speckleFeatureId__";
public string MappedLayerURI { get; } // unique ID of the layer on the map
public int? FeatureId { get; } // unique feature id (start from 0) of a feature in the layer
public ObjectID(string encodedId)
{
List<string> stringParts = encodedId.Split(FEATURE_ID_SEPARATOR).ToList();
MappedLayerURI = stringParts[0];
FeatureId = null;
if (stringParts.Count > 1)
{
FeatureId = Convert.ToInt32(stringParts[1]);
}
}
public ObjectID(string layerId, int? featureId)
{
MappedLayerURI = layerId;
FeatureId = featureId;
}
public readonly string ObjectIdToString()
{
if (FeatureId == null)
{
return $"{MappedLayerURI}";
}
else
{
return $"{MappedLayerURI}{FEATURE_ID_SEPARATOR}{FeatureId}";
}
}
}
@@ -1,7 +1,19 @@
{
"version": 2,
"dependencies": {
"net8.0-windows7.0": {
"net6.0-windows7.0": {
"Esri.ArcGISPro.Extensions30": {
"type": "Direct",
"requested": "[3.2.0.49743, )",
"resolved": "3.2.0.49743",
"contentHash": "fmnYm+mD14Cz0Uqh1ij37SfLJerkyFHK5581y5tXT/l3H2ZvUmVuuxjYquXzyzj9p7IexQzMW4xCpxe+mD922g=="
},
"Microsoft.Extensions.Logging.Abstractions": {
"type": "Direct",
"requested": "[2.2.0, )",
"resolved": "2.2.0",
"contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A=="
},
"Microsoft.NETFramework.ReferenceAssemblies": {
"type": "Direct",
"requested": "[1.0.3, )",
@@ -27,21 +39,6 @@
"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.Civil3D.API": {
"type": "Direct",
"requested": "[2026.0.0, )",
"resolved": "2026.0.0",
"contentHash": "JcQvKvA3KC+9hzJiWlaZ3REtvqJV+AFHPIU5J6Xp7JHlNyhnaalN37WXpWIKhNAUwL9ppUBOXMZpQupbFytUwg==",
"dependencies": {
"Speckle.AutoCAD.API": "2026.0.0"
}
},
"Speckle.InterfaceGenerator": {
"type": "Direct",
"requested": "[0.9.6, )",
@@ -79,11 +76,21 @@
"resolved": "6.0.0",
"contentHash": "yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA=="
},
"Microsoft.Bcl.AsyncInterfaces": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ=="
},
"Microsoft.Build.Tasks.Git": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
},
"Microsoft.CSharp": {
"type": "Transitive",
"resolved": "4.7.0",
"contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA=="
},
"Microsoft.Data.Sqlite": {
"type": "Transitive",
"resolved": "7.0.5",
@@ -219,16 +226,18 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.3, )",
"Speckle.Sdk": "[3.5.3, )",
"Speckle.Sdk.Dependencies": "[3.5.3, )"
"Speckle.Objects": "[3.1.0-dev.255, )",
"Speckle.Sdk": "[3.1.0-dev.255, )",
"Speckle.Sdk.Dependencies": "[3.1.0-dev.255, )"
}
},
"speckle.connectors.dui": {
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )"
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.0-dev.255, )",
"Speckle.Sdk.Dependencies": "[3.1.0-dev.255, )"
}
},
"speckle.connectors.dui.webview": {
@@ -241,12 +250,10 @@
"speckle.connectors.logging": {
"type": "Project"
},
"speckle.converters.civil3d2026": {
"speckle.converters.arcgis3": {
"type": "Project",
"dependencies": {
"Speckle.AutoCAD.API": "[2026.0.0, )",
"Speckle.Civil3d.API": "[2026.0.0, )",
"Speckle.Connectors.DUI.WebView": "[1.0.0, )",
"Esri.ArcGISPro.Extensions30": "[3.2.0.49743, )",
"Speckle.Converters.Common": "[1.0.0, )"
}
},
@@ -254,7 +261,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.3, )"
"Speckle.Objects": "[3.1.0-dev.255, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -278,12 +285,6 @@
"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, )",
@@ -293,41 +294,43 @@
"Speckle.DoubleNumerics": {
"type": "CentralTransitive",
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
"resolved": "4.0.1",
"contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"requested": "[3.1.0-dev.255, )",
"resolved": "3.1.0-dev.255",
"contentHash": "0S9YVdu5nx7SYS0y5Gq1nwCbxfYB2UxsUUCskaGmzRAm+QzHGqCLYHgadz7Sw5mNAF3bOTLVrOYMzyLAWFWxJA==",
"dependencies": {
"Speckle.Sdk": "3.5.3"
"Speckle.Sdk": "3.1.0-dev.255"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
"requested": "[3.1.0-dev.255, )",
"resolved": "3.1.0-dev.255",
"contentHash": "TsFpIv8ipvOD/r0se1XmcNPo7t5dWVadwV32Z6vUqoW3s94VIE54/ZTHNkRCEYHFhfOyTcFfbLegiei+0D13bQ==",
"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",
"Microsoft.Bcl.AsyncInterfaces": "[5.0.0]",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "[7.0.5]",
"Microsoft.Extensions.DependencyInjection.Abstractions": "[2.2.0]",
"Microsoft.Extensions.Logging": "[2.2.0]",
"Speckle.DoubleNumerics": "4.0.1",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.3"
"Speckle.Sdk.Dependencies": "3.1.0-dev.255"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
"requested": "[3.1.0-dev.255, )",
"resolved": "3.1.0-dev.255",
"contentHash": "koFeWc/EB2XrMH2aKl2Hqwrqk/fAGYEVKgxFlFpnEAsbZf7Kk57gBQ3ViV5l6X9vqMHGWFc4dWUp24diq/XPtQ=="
}
},
"net8.0-windows7.0/win-x64": {
"net6.0-windows7.0/win-x64": {
"SQLitePCLRaw.lib.e_sqlite3": {
"type": "Transitive",
"resolved": "2.1.4",
+4 -14
View File
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<Project>
<Target AfterTargets="Clean" Name="CleanAddinAutocad" Condition="'$(AutoCADVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true' And '$(OS)' == 'Windows_NT'">
<Target AfterTargets="Clean" Name="CleanAddinAutocad" Condition="'$(AutoCADVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true'">
<RemoveDir Directories="$(AppData)\Autodesk\ApplicationPlugins\Speckle.Connectors.Autocad$(AutoCADVersion);" />
</Target>
<Target AfterTargets="Build" Name="AfterBuildAutoCAD" Condition="'$(AutoCADVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true' And '$(OS)' == 'Windows_NT'">
<Target AfterTargets="Build" Name="AfterBuildAutoCAD" Condition="'$(AutoCADVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true'">
<ItemGroup>
<AutoCADDLLs Include="$(TargetDir)\**\*.*" />
</ItemGroup>
@@ -12,25 +12,15 @@
<Copy DestinationFolder="$(AppData)\Autodesk\ApplicationPlugins\Speckle.Connectors.Autocad$(AutoCADVersion)\%(RecursiveDir)" SourceFiles="@(AutoCADDLLs)" />
</Target>
<Target AfterTargets="Clean" Name="CleanAddinCivil3D" Condition="'$(Civil3DVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true' And '$(OS)' == 'Windows_NT'">
<Target AfterTargets="Clean" Name="CleanAddinCivil3D" Condition="'$(Civil3DVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true'">
<RemoveDir Directories="$(AppData)\Autodesk\ApplicationPlugins\Speckle.Connectors.Civil3d$(Civil3DVersion);" />
</Target>
<Target AfterTargets="Build" Name="AfterBuildCivil3D" Condition="'$(Civil3DVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true' And '$(OS)' == 'Windows_NT'">
<Target AfterTargets="Build" Name="AfterBuildCivil3D" Condition="'$(Civil3DVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true'">
<ItemGroup>
<Civil3DDLLs Include="$(TargetDir)\**\*.*" />
</ItemGroup>
<Message Text="Civil3D Version $(Civil3DVersion)" Importance="high"/>
<Copy DestinationFolder="$(AppData)\Autodesk\ApplicationPlugins\Speckle.Connectors.Civil3d$(Civil3DVersion)\%(RecursiveDir)" SourceFiles="@(Civil3DDLLs)" />
</Target>
<PropertyGroup Condition="'$(AutoCADVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true' And '$(OS)' == 'Windows_NT'">
<StartAction>Program</StartAction>
<StartProgram>$(ProgramW6432)\Autodesk\AutoCAD $(AutoCADVersion)\acad.exe</StartProgram>
</PropertyGroup>
<PropertyGroup Condition="'$(Civil3DVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true' And '$(OS)' == 'Windows_NT'">
<StartAction>Program</StartAction>
<StartProgram>$(ProgramW6432)\Autodesk\AutoCAD $(Civil3DVersion)\acad.exe</StartProgram>
<StartArguments>/product C3D</StartArguments>
</PropertyGroup>
</Project>
@@ -6,10 +6,12 @@
<AutoCADVersion>2022</AutoCADVersion>
<DefineConstants>$(DefineConstants);AUTOCAD;AUTOCAD2022;AUTOCAD2022_OR_GREATER</DefineConstants>
<Configurations>Debug;Release;Local</Configurations>
<StartAction>Program</StartAction>
<StartProgram>$(ProgramW6432)\Autodesk\AutoCAD $(AutoCADVersion)\acad.exe</StartProgram>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2022.0.2" ExcludeAssets="runtime"/>
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2022.0.2" />
</ItemGroup>
<ItemGroup>
@@ -259,16 +259,18 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.3, )",
"Speckle.Sdk": "[3.5.3, )",
"Speckle.Sdk.Dependencies": "[3.5.3, )"
"Speckle.Objects": "[3.1.0-dev.255, )",
"Speckle.Sdk": "[3.1.0-dev.255, )",
"Speckle.Sdk.Dependencies": "[3.1.0-dev.255, )"
}
},
"speckle.connectors.dui": {
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )"
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.0-dev.255, )",
"Speckle.Sdk.Dependencies": "[3.1.0-dev.255, )"
}
},
"speckle.connectors.dui.webview": {
@@ -292,7 +294,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.3, )"
"Speckle.Objects": "[3.1.0-dev.255, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -331,40 +333,40 @@
"Speckle.DoubleNumerics": {
"type": "CentralTransitive",
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
"resolved": "4.0.1",
"contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"requested": "[3.1.0-dev.255, )",
"resolved": "3.1.0-dev.255",
"contentHash": "0S9YVdu5nx7SYS0y5Gq1nwCbxfYB2UxsUUCskaGmzRAm+QzHGqCLYHgadz7Sw5mNAF3bOTLVrOYMzyLAWFWxJA==",
"dependencies": {
"Speckle.Sdk": "3.5.3"
"Speckle.Sdk": "3.1.0-dev.255"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
"requested": "[3.1.0-dev.255, )",
"resolved": "3.1.0-dev.255",
"contentHash": "TsFpIv8ipvOD/r0se1XmcNPo7t5dWVadwV32Z6vUqoW3s94VIE54/ZTHNkRCEYHFhfOyTcFfbLegiei+0D13bQ==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.Bcl.AsyncInterfaces": "[5.0.0]",
"Microsoft.CSharp": "4.7.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",
"Microsoft.Data.Sqlite": "[7.0.5]",
"Microsoft.Extensions.DependencyInjection.Abstractions": "[2.2.0]",
"Microsoft.Extensions.Logging": "[2.2.0]",
"Speckle.DoubleNumerics": "4.0.1",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.3"
"Speckle.Sdk.Dependencies": "3.1.0-dev.255"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
"requested": "[3.1.0-dev.255, )",
"resolved": "3.1.0-dev.255",
"contentHash": "koFeWc/EB2XrMH2aKl2Hqwrqk/fAGYEVKgxFlFpnEAsbZf7Kk57gBQ3ViV5l6X9vqMHGWFc4dWUp24diq/XPtQ=="
}
}
}
@@ -6,10 +6,12 @@
<AutoCADVersion>2023</AutoCADVersion>
<DefineConstants>$(DefineConstants);AUTOCAD;AUTOCAD2023;AUTOCAD2022_OR_GREATER;AUTOCAD2023_OR_GREATER</DefineConstants>
<Configurations>Debug;Release;Local</Configurations>
<StartAction>Program</StartAction>
<StartProgram>$(ProgramW6432)\Autodesk\AutoCAD $(AutoCADVersion)\acad.exe</StartProgram>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2023.0.0" ExcludeAssets="runtime"/>
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2023.0.0" />
</ItemGroup>
<ItemGroup>
@@ -259,16 +259,18 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.3, )",
"Speckle.Sdk": "[3.5.3, )",
"Speckle.Sdk.Dependencies": "[3.5.3, )"
"Speckle.Objects": "[3.1.0-dev.255, )",
"Speckle.Sdk": "[3.1.0-dev.255, )",
"Speckle.Sdk.Dependencies": "[3.1.0-dev.255, )"
}
},
"speckle.connectors.dui": {
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )"
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.0-dev.255, )",
"Speckle.Sdk.Dependencies": "[3.1.0-dev.255, )"
}
},
"speckle.connectors.dui.webview": {
@@ -292,7 +294,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.3, )"
"Speckle.Objects": "[3.1.0-dev.255, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -331,40 +333,40 @@
"Speckle.DoubleNumerics": {
"type": "CentralTransitive",
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
"resolved": "4.0.1",
"contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"requested": "[3.1.0-dev.255, )",
"resolved": "3.1.0-dev.255",
"contentHash": "0S9YVdu5nx7SYS0y5Gq1nwCbxfYB2UxsUUCskaGmzRAm+QzHGqCLYHgadz7Sw5mNAF3bOTLVrOYMzyLAWFWxJA==",
"dependencies": {
"Speckle.Sdk": "3.5.3"
"Speckle.Sdk": "3.1.0-dev.255"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
"requested": "[3.1.0-dev.255, )",
"resolved": "3.1.0-dev.255",
"contentHash": "TsFpIv8ipvOD/r0se1XmcNPo7t5dWVadwV32Z6vUqoW3s94VIE54/ZTHNkRCEYHFhfOyTcFfbLegiei+0D13bQ==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.Bcl.AsyncInterfaces": "[5.0.0]",
"Microsoft.CSharp": "4.7.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",
"Microsoft.Data.Sqlite": "[7.0.5]",
"Microsoft.Extensions.DependencyInjection.Abstractions": "[2.2.0]",
"Microsoft.Extensions.Logging": "[2.2.0]",
"Speckle.DoubleNumerics": "4.0.1",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.3"
"Speckle.Sdk.Dependencies": "3.1.0-dev.255"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
"requested": "[3.1.0-dev.255, )",
"resolved": "3.1.0-dev.255",
"contentHash": "koFeWc/EB2XrMH2aKl2Hqwrqk/fAGYEVKgxFlFpnEAsbZf7Kk57gBQ3ViV5l6X9vqMHGWFc4dWUp24diq/XPtQ=="
}
}
}
@@ -1,15 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net48</TargetFramework>
<AutoCADVersion>2024</AutoCADVersion>
<PlatformTarget>x64</PlatformTarget>
<UseWpf>true</UseWpf>
<AutoCADVersion>2024</AutoCADVersion>
<DefineConstants>$(DefineConstants);AUTOCAD;AUTOCAD2024;AUTOCAD2022_OR_GREATER;AUTOCAD2023_OR_GREATER;AUTOCAD2024_OR_GREATER</DefineConstants>
<Configurations>Debug;Release;Local</Configurations>
<StartAction>Program</StartAction>
<StartProgram>$(ProgramW6432)\Autodesk\AutoCAD $(AutoCADVersion)\acad.exe</StartProgram>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2024.0.0" ExcludeAssets="runtime"/>
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2024.0.0" />
</ItemGroup>
<ItemGroup>
@@ -259,16 +259,18 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.3, )",
"Speckle.Sdk": "[3.5.3, )",
"Speckle.Sdk.Dependencies": "[3.5.3, )"
"Speckle.Objects": "[3.1.0-dev.255, )",
"Speckle.Sdk": "[3.1.0-dev.255, )",
"Speckle.Sdk.Dependencies": "[3.1.0-dev.255, )"
}
},
"speckle.connectors.dui": {
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )"
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.0-dev.255, )",
"Speckle.Sdk.Dependencies": "[3.1.0-dev.255, )"
}
},
"speckle.connectors.dui.webview": {
@@ -293,7 +295,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.3, )"
"Speckle.Objects": "[3.1.0-dev.255, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -332,40 +334,40 @@
"Speckle.DoubleNumerics": {
"type": "CentralTransitive",
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
"resolved": "4.0.1",
"contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"requested": "[3.1.0-dev.255, )",
"resolved": "3.1.0-dev.255",
"contentHash": "0S9YVdu5nx7SYS0y5Gq1nwCbxfYB2UxsUUCskaGmzRAm+QzHGqCLYHgadz7Sw5mNAF3bOTLVrOYMzyLAWFWxJA==",
"dependencies": {
"Speckle.Sdk": "3.5.3"
"Speckle.Sdk": "3.1.0-dev.255"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
"requested": "[3.1.0-dev.255, )",
"resolved": "3.1.0-dev.255",
"contentHash": "TsFpIv8ipvOD/r0se1XmcNPo7t5dWVadwV32Z6vUqoW3s94VIE54/ZTHNkRCEYHFhfOyTcFfbLegiei+0D13bQ==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.Bcl.AsyncInterfaces": "[5.0.0]",
"Microsoft.CSharp": "4.7.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",
"Microsoft.Data.Sqlite": "[7.0.5]",
"Microsoft.Extensions.DependencyInjection.Abstractions": "[2.2.0]",
"Microsoft.Extensions.Logging": "[2.2.0]",
"Speckle.DoubleNumerics": "4.0.1",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.3"
"Speckle.Sdk.Dependencies": "3.1.0-dev.255"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
"requested": "[3.1.0-dev.255, )",
"resolved": "3.1.0-dev.255",
"contentHash": "koFeWc/EB2XrMH2aKl2Hqwrqk/fAGYEVKgxFlFpnEAsbZf7Kk57gBQ3ViV5l6X9vqMHGWFc4dWUp24diq/XPtQ=="
}
}
}
@@ -1,20 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<PlatformTarget>x64</PlatformTarget>
<UseWpf>true</UseWpf>
<AutoCADVersion>2025</AutoCADVersion>
<DefineConstants>$(DefineConstants);AUTOCAD;AUTOCAD2025;AUTOCAD2022_OR_GREATER;AUTOCAD2023_OR_GREATER;AUTOCAD2024_OR_GREATER;AUTOCAD2025_OR_GREATER</DefineConstants>
<DefineConstants>$(DefineConstants);AUTOCAD2025;AUTOCAD</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 of autocad/civil-->
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<RuntimeIdentifier>win-x64</RuntimeIdentifier> <!-- .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 -->
<StartAction>Program</StartAction>
<StartProgram>$(ProgramW6432)\Autodesk\AutoCAD $(AutoCADVersion)\acad.exe</StartProgram>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2025.0.0" ExcludeAssets="runtime"/>
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2025.0.0" />
<FrameworkReference Include="Microsoft.WindowsDesktop.App"/>
</ItemGroup>
<ItemGroup>
@@ -75,6 +75,11 @@
"resolved": "8.0.0",
"contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
},
"Microsoft.CSharp": {
"type": "Transitive",
"resolved": "4.7.0",
"contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA=="
},
"Microsoft.Data.Sqlite": {
"type": "Transitive",
"resolved": "7.0.5",
@@ -210,16 +215,18 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.3, )",
"Speckle.Sdk": "[3.5.3, )",
"Speckle.Sdk.Dependencies": "[3.5.3, )"
"Speckle.Objects": "[3.1.0-dev.255, )",
"Speckle.Sdk": "[3.1.0-dev.255, )",
"Speckle.Sdk.Dependencies": "[3.1.0-dev.255, )"
}
},
"speckle.connectors.dui": {
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )"
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.0-dev.255, )",
"Speckle.Sdk.Dependencies": "[3.1.0-dev.255, )"
}
},
"speckle.connectors.dui.webview": {
@@ -244,7 +251,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.3, )"
"Speckle.Objects": "[3.1.0-dev.255, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -283,38 +290,39 @@
"Speckle.DoubleNumerics": {
"type": "CentralTransitive",
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
"resolved": "4.0.1",
"contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"requested": "[3.1.0-dev.255, )",
"resolved": "3.1.0-dev.255",
"contentHash": "0S9YVdu5nx7SYS0y5Gq1nwCbxfYB2UxsUUCskaGmzRAm+QzHGqCLYHgadz7Sw5mNAF3bOTLVrOYMzyLAWFWxJA==",
"dependencies": {
"Speckle.Sdk": "3.5.3"
"Speckle.Sdk": "3.1.0-dev.255"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
"requested": "[3.1.0-dev.255, )",
"resolved": "3.1.0-dev.255",
"contentHash": "TsFpIv8ipvOD/r0se1XmcNPo7t5dWVadwV32Z6vUqoW3s94VIE54/ZTHNkRCEYHFhfOyTcFfbLegiei+0D13bQ==",
"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",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "[7.0.5]",
"Microsoft.Extensions.DependencyInjection.Abstractions": "[2.2.0]",
"Microsoft.Extensions.Logging": "[2.2.0]",
"Speckle.DoubleNumerics": "4.0.1",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.3"
"Speckle.Sdk.Dependencies": "3.1.0-dev.255"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
"requested": "[3.1.0-dev.255, )",
"resolved": "3.1.0-dev.255",
"contentHash": "koFeWc/EB2XrMH2aKl2Hqwrqk/fAGYEVKgxFlFpnEAsbZf7Kk57gBQ3ViV5l6X9vqMHGWFc4dWUp24diq/XPtQ=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -1,27 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<AutoCADVersion>2026</AutoCADVersion>
<DefineConstants>$(DefineConstants);AUTOCAD;AUTOCAD2026;AUTOCAD2022_OR_GREATER;AUTOCAD2023_OR_GREATER;AUTOCAD2024_OR_GREATER;AUTOCAD2025_OR_GREATER;AUTOCAD2026_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 of autocad/civil-->
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2026.0.0" ExcludeAssets="runtime"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Converters\Autocad\Speckle.Converters.Autocad2026\Speckle.Converters.Autocad2026.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.AutocadShared\Speckle.Connectors.AutocadShared.projitems" Label="Shared" />
</Project>
@@ -1,334 +0,0 @@
{
"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=="
},
"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=="
},
"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.Objects": "[3.5.3, )",
"Speckle.Sdk": "[3.5.3, )",
"Speckle.Sdk.Dependencies": "[3.5.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.autocad2026": {
"type": "Project",
"dependencies": {
"Speckle.AutoCAD.API": "[2026.0.0, )",
"Speckle.Connectors.DUI.WebView": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )"
}
},
"speckle.converters.common": {
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.3, )"
}
},
"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.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
"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.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
}
},
"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=="
}
}
}
}
@@ -4,6 +4,7 @@ using Speckle.Connectors.Autocad.HostApp.Extensions;
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Eventing;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Sdk;
@@ -21,7 +22,6 @@ public class AutocadBasicConnectorBinding : IBasicConnectorBinding
private readonly DocumentModelStore _store;
private readonly ISpeckleApplication _speckleApplication;
private readonly IThreadContext _threadContext;
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
private readonly ILogger<AutocadBasicConnectorBinding> _logger;
public BasicConnectorBindingCommands Commands { get; }
@@ -32,8 +32,8 @@ public class AutocadBasicConnectorBinding : IBasicConnectorBinding
IAccountManager accountManager,
ISpeckleApplication speckleApplication,
ILogger<AutocadBasicConnectorBinding> logger,
IThreadContext threadContext,
ITopLevelExceptionHandler topLevelExceptionHandler
IEventAggregator eventAggregator,
IThreadContext threadContext
)
{
_store = store;
@@ -41,16 +41,13 @@ public class AutocadBasicConnectorBinding : IBasicConnectorBinding
_accountManager = accountManager;
_speckleApplication = speckleApplication;
Commands = new BasicConnectorBindingCommands(parent);
eventAggregator.GetEvent<DocumentStoreChangedEvent>().Subscribe(OnDocumentStoreChangedEvent);
_logger = logger;
_threadContext = threadContext;
_topLevelExceptionHandler = topLevelExceptionHandler;
_store.DocumentChanged += (_, _) =>
_topLevelExceptionHandler.FireAndForget(async () =>
{
await Commands.NotifyDocumentChanged();
});
}
private async Task OnDocumentStoreChangedEvent(object _) => await Commands.NotifyDocumentChanged();
public string GetConnectorVersion() => _speckleApplication.SpeckleVersion;
public string GetSourceApplicationName() => _speckleApplication.Slug;
@@ -79,8 +76,6 @@ public class AutocadBasicConnectorBinding : IBasicConnectorBinding
public void RemoveModel(ModelCard model) => _store.RemoveModel(model);
public void RemoveModels(List<ModelCard> models) => _store.RemoveModels(models);
public async Task HighlightObjects(IReadOnlyList<string> objectIds)
{
// POC: Will be addressed to move it into AutocadContext!
@@ -1,59 +0,0 @@
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.Card;
namespace Speckle.Connectors.Autocad.Bindings;
public abstract class AutocadReceiveBaseBinding(
IBrowserBridge parent,
ICancellationManager cancellationManager,
IThreadContext threadContext,
IReceiveOperationManagerFactory receiveOperationManagerFactory
) : IReceiveBinding
{
public string Name => "receiveBinding";
public IBrowserBridge Parent { get; } = parent;
private ReceiveBindingUICommands Commands { get; } = new(parent);
protected abstract void InitializeSettings(IServiceProvider serviceProvider, ModelCard mc);
public void CancelReceive(string modelCardId) => cancellationManager.CancelOperation(modelCardId);
public async Task Receive(string modelCardId) =>
await threadContext.RunOnMainAsync(async () => await ReceiveInternal(modelCardId));
private async Task ReceiveInternal(string modelCardId)
{
using var manager = receiveOperationManagerFactory.Create();
await manager.Process(
Commands,
modelCardId,
InitializeSettings,
async (_, processor) =>
{
try
{
// Disable document activation (document creation and document switch)
// Not disabling results in DUI model card being out of sync with the active document
// The DocumentActivated event isn't usable probably because it is pushed to back of main thread queue
Application.DocumentManager.DocumentActivationEnabled = false;
return await processor();
}
finally
{
// reenable document activation
Application.DocumentManager.DocumentActivationEnabled = true;
// regenerate doc to flush graphics, sometimes some objects (ellipses, nurbs curves) do not appear fully visible after receive.
// Adding a regen (must be run on main thread) here, but it doesn't seem to work:
// it's run on main thread, tried sending the "regen" string to execute, also tried regen after every object bake, but still can't fix.
// the objects should appear visible if you manually call the "regen" command after the operation finishes, or click on a view on the view cube which also calls regen.
Application.DocumentManager.CurrentDocument.Editor.Regen();
}
}
);
}
}
@@ -1,34 +1,110 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Common.Cancellation;
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Logging;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Converters.Autocad;
using Speckle.Converters.Common;
using Speckle.Sdk;
namespace Speckle.Connectors.Autocad.Bindings;
public sealed class AutocadReceiveBinding : AutocadReceiveBaseBinding
public sealed class AutocadReceiveBinding : IReceiveBinding
{
public string Name => "receiveBinding";
public IBrowserBridge Parent { get; }
private readonly DocumentModelStore _store;
private readonly ICancellationManager _cancellationManager;
private readonly IServiceProvider _serviceProvider;
private readonly IOperationProgressManager _operationProgressManager;
private readonly ILogger<AutocadReceiveBinding> _logger;
private readonly IAutocadConversionSettingsFactory _autocadConversionSettingsFactory;
private readonly ISpeckleApplication _speckleApplication;
private ReceiveBindingUICommands Commands { get; }
public AutocadReceiveBinding(
DocumentModelStore store,
IBrowserBridge parent,
ICancellationManager cancellationManager,
IServiceProvider serviceProvider,
IOperationProgressManager operationProgressManager,
ILogger<AutocadReceiveBinding> logger,
IAutocadConversionSettingsFactory autocadConversionSettingsFactory,
IThreadContext threadContext,
IReceiveOperationManagerFactory receiveOperationManagerFactory
ISpeckleApplication speckleApplication
)
: base(parent, cancellationManager, threadContext, receiveOperationManagerFactory)
{
_store = store;
_cancellationManager = cancellationManager;
_serviceProvider = serviceProvider;
_operationProgressManager = operationProgressManager;
_logger = logger;
_autocadConversionSettingsFactory = autocadConversionSettingsFactory;
_speckleApplication = speckleApplication;
Parent = parent;
Commands = new ReceiveBindingUICommands(parent);
}
protected override void InitializeSettings(IServiceProvider serviceProvider, ModelCard mc)
public void CancelReceive(string modelCardId) => _cancellationManager.CancelOperation(modelCardId);
public async Task Receive(string modelCardId)
{
serviceProvider
.GetRequiredService<IConverterSettingsStore<AutocadConversionSettings>>()
using var scope = _serviceProvider.CreateScope();
scope
.ServiceProvider.GetRequiredService<IConverterSettingsStore<AutocadConversionSettings>>()
.Initialize(_autocadConversionSettingsFactory.Create(Application.DocumentManager.CurrentDocument));
try
{
// Get receiver card
if (_store.GetModelById(modelCardId) is not ReceiverModelCard modelCard)
{
// Handle as GLOBAL ERROR at BrowserBridge
throw new InvalidOperationException("No download model card was found.");
}
using var cancellationItem = _cancellationManager.GetCancellationItem(modelCardId);
// Disable document activation (document creation and document switch)
// Not disabling results in DUI model card being out of sync with the active document
// The DocumentActivated event isn't usable probably because it is pushed to back of main thread queue
Application.DocumentManager.DocumentActivationEnabled = false;
// Receive host objects
var operationResults = await scope
.ServiceProvider.GetRequiredService<ReceiveOperation>()
.Execute(
modelCard.GetReceiveInfo(_speckleApplication.Slug),
_operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationItem.Token),
cancellationItem.Token
);
await Commands.SetModelReceiveResult(
modelCardId,
operationResults.BakedObjectIds,
operationResults.ConversionResults
);
}
catch (OperationCanceledException)
{
// SWALLOW -> UI handles it immediately, so we do not need to handle anything for now!
// Idea for later -> when cancel called, create promise from UI to solve it later with this catch block.
// So have 3 state on UI -> Cancellation clicked -> Cancelling -> Cancelled
return;
}
catch (Exception ex) when (!ex.IsFatal()) // UX reasons - we will report operation exceptions as model card error. We may change this later when we have more exception documentation
{
_logger.LogModelCardHandledError(ex);
await Commands.SetModelError(modelCardId, ex);
}
finally
{
// reenable document activation
Application.DocumentManager.DocumentActivationEnabled = true;
}
}
}
@@ -1,42 +1,48 @@
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Speckle.Connectors.Autocad.HostApp.Extensions;
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.Autocad.Plugin;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Eventing;
namespace Speckle.Connectors.Autocad.Bindings;
public class AutocadSelectionBinding : ISelectionBinding
{
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
private readonly IThreadContext _threadContext;
private const string SELECTION_EVENT = "setSelection";
private readonly HashSet<Document> _visitedDocuments = new();
private readonly IEventAggregator _eventAggregator;
private readonly HashSet<string> _visitedDocuments = new();
public string Name => "selectionBinding";
public IBrowserBridge Parent { get; }
public AutocadSelectionBinding(
IBrowserBridge parent,
ITopLevelExceptionHandler topLevelExceptionHandler,
IThreadContext threadContext
)
public AutocadSelectionBinding(IBrowserBridge parent, IEventAggregator eventAggregator)
{
_topLevelExceptionHandler = topLevelExceptionHandler;
_threadContext = threadContext;
_eventAggregator = eventAggregator;
Parent = parent;
// POC: Use here Context for doc. In converters it's OK but we are still lacking to use context into bindings.
// It is with the case of if binding created with already a document
// This is valid when user opens acad file directly double clicking
TryRegisterDocumentForSelection(Application.DocumentManager.MdiActiveDocument);
Application.DocumentManager.DocumentActivated += (_, e) =>
_topLevelExceptionHandler.CatchUnhandled(() => OnDocumentChanged(e.Document));
eventAggregator.GetEvent<DocumentActivatedEvent>().Subscribe(OnDocumentChanged);
eventAggregator.GetEvent<ImpliedSelectionChangedEvent>().Subscribe(OnSelectionChanged);
eventAggregator.GetEvent<DocumentToBeDestroyedEvent>().Subscribe(OnDocumentDestroyed);
}
private void OnDocumentChanged(Document? document) => TryRegisterDocumentForSelection(document);
private void OnDocumentDestroyed(DocumentCollectionEventArgs e)
{
if (!_visitedDocuments.Contains(e.Document.Name))
{
e.Document.ImpliedSelectionChanged -= DocumentOnImpliedSelectionChanged;
_visitedDocuments.Remove(e.Document.Name);
}
}
private void OnDocumentChanged(DocumentCollectionEventArgs e) => TryRegisterDocumentForSelection(e.Document);
private void TryRegisterDocumentForSelection(Document? document)
{
@@ -45,21 +51,24 @@ public class AutocadSelectionBinding : ISelectionBinding
return;
}
if (!_visitedDocuments.Contains(document))
if (!_visitedDocuments.Contains(document.Name))
{
document.ImpliedSelectionChanged += (_, _) =>
_topLevelExceptionHandler.FireAndForget(async () => await _threadContext.RunOnMainAsync(OnSelectionChanged));
document.ImpliedSelectionChanged += DocumentOnImpliedSelectionChanged;
_visitedDocuments.Add(document);
_visitedDocuments.Add(document.Name);
}
}
// ReSharper disable once AsyncVoidMethod
private async void DocumentOnImpliedSelectionChanged(object? sender, EventArgs e) =>
await _eventAggregator.GetEvent<ImpliedSelectionChangedEvent>().PublishAsync(e);
// NOTE: Autocad 2022 caused problems, so we need to refactor things a bit in here to always store
// selection info locally (and get it updated by the event, which we can control to run on the main thread).
// Ui requests to GetSelection() should just return this local copy that is kept up to date by the event handler.
private SelectionInfo _selectionInfo;
private async Task OnSelectionChanged()
private async Task OnSelectionChanged(EventArgs _)
{
_selectionInfo = GetSelectionInternal();
await Parent.Send(SELECTION_EVENT, _selectionInfo);
@@ -89,17 +98,6 @@ public class AutocadSelectionBinding : ISelectionBinding
objectTypes.Add(dbObject.GetType().Name);
objs.Add(dbObject.GetSpeckleApplicationId());
// do the same also for each AttributeReference inside the BlockReference (attribute change is not affecting the block otherwise)
if (dbObject is BlockReference blockReference)
{
foreach (ObjectId id in blockReference.AttributeCollection)
{
var attr = (AttributeReference)tr.GetObject(id, OpenMode.ForRead);
objectTypes.Add(attr.GetType().Name);
objs.Add(attr.GetSpeckleApplicationId());
}
}
}
tr.Commit();
@@ -1,16 +1,26 @@
using System.Collections.Concurrent;
using System.Diagnostics.CodeAnalysis;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Autocad.HostApp.Extensions;
using Speckle.Connectors.Autocad.Operations.Send;
using Speckle.Connectors.Autocad.Plugin;
using Speckle.Connectors.Common.Caching;
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;
using Speckle.Connectors.DUI.Eventing;
using Speckle.Connectors.DUI.Exceptions;
using Speckle.Connectors.DUI.Logging;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Connectors.DUI.Models.Card.SendFilter;
using Speckle.Connectors.DUI.Settings;
using Speckle.Sdk;
using Speckle.Sdk.Common;
namespace Speckle.Connectors.Autocad.Bindings;
@@ -26,11 +36,13 @@ public abstract class AutocadSendBaseBinding : ISendBinding
private readonly DocumentModelStore _store;
private readonly List<ISendFilter> _sendFilters;
private readonly ICancellationManager _cancellationManager;
private readonly IServiceProvider _serviceProvider;
private readonly ISendConversionCache _sendConversionCache;
private readonly IOperationProgressManager _operationProgressManager;
private readonly ILogger<AutocadSendBinding> _logger;
private readonly ISpeckleApplication _speckleApplication;
private readonly IThreadContext _threadContext;
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
private readonly IAppIdleManager _idleManager;
private readonly ISendOperationManagerFactory _sendOperationManagerFactory;
private readonly IEventAggregator _eventAggregator;
/// <summary>
/// Used internally to aggregate the changed objects' id. Note we're using a concurrent dictionary here as the expiry check method is not thread safe, and this was causing problems. See:
@@ -38,49 +50,71 @@ public abstract class AutocadSendBaseBinding : ISendBinding
/// As to why a concurrent dictionary, it's because it's the cheapest/easiest way to do so.
/// https://stackoverflow.com/questions/18922985/concurrent-hashsett-in-net-framework
/// </summary>
private ConcurrentBag<string> ChangedObjectIds { get; set; } = new();
private ConcurrentDictionary<string, byte> ChangedObjectIds { get; set; } = new();
protected AutocadSendBaseBinding(
DocumentModelStore store,
IBrowserBridge parent,
IEnumerable<ISendFilter> sendFilters,
ICancellationManager cancellationManager,
IServiceProvider serviceProvider,
ISendConversionCache sendConversionCache,
IOperationProgressManager operationProgressManager,
ILogger<AutocadSendBinding> logger,
ISpeckleApplication speckleApplication,
IThreadContext threadContext,
ITopLevelExceptionHandler topLevelExceptionHandler,
IAppIdleManager idleManager,
ISendOperationManagerFactory sendOperationManagerFactory
IEventAggregator eventAggregator
)
{
_store = store;
_serviceProvider = serviceProvider;
_cancellationManager = cancellationManager;
_sendFilters = sendFilters.ToList();
_sendConversionCache = sendConversionCache;
_operationProgressManager = operationProgressManager;
_logger = logger;
_speckleApplication = speckleApplication;
_threadContext = threadContext;
_topLevelExceptionHandler = topLevelExceptionHandler;
_idleManager = idleManager;
_sendOperationManagerFactory = sendOperationManagerFactory;
_eventAggregator = eventAggregator;
Parent = parent;
Commands = new SendBindingUICommands(parent);
Application.DocumentManager.DocumentActivated += (_, args) =>
_topLevelExceptionHandler.CatchUnhandled(() => SubscribeToObjectChanges(args.Document));
if (Application.DocumentManager.CurrentDocument != null)
{
// catches the case when autocad just opens up with a blank new doc
SubscribeToObjectChanges(Application.DocumentManager.CurrentDocument);
TryRegisterSubscribeToObjectChanges(Application.DocumentManager.CurrentDocument);
}
// Since ids of the objects generates from same seed, we should clear the cache always whenever doc swapped.
_store.DocumentChanged += (_, _) =>
{
_sendConversionCache.ClearCache();
};
eventAggregator.GetEvent<DocumentActivatedEvent>().Subscribe(SubscribeToObjectChanges);
eventAggregator.GetEvent<DocumentStoreChangedEvent>().Subscribe(OnDocumentStoreChangedEvent);
eventAggregator.GetEvent<DocumentToBeDestroyedEvent>().Subscribe(OnDocumentDestroyed);
eventAggregator.GetEvent<ObjectAppendedEvent>().Subscribe(OnObjectAppended);
eventAggregator.GetEvent<ObjectErasedEvent>().Subscribe(ObjectErased);
eventAggregator.GetEvent<ObjectModifiedEvent>().Subscribe(ObjectModified);
}
private void OnDocumentDestroyed(DocumentCollectionEventArgs args)
{
Document doc = args.Document;
if (!_docSubsTracker.Contains(doc.Name))
{
doc.Database.ObjectAppended -= DatabaseOnObjectAppended;
doc.Database.ObjectErased -= DatabaseOnObjectErased;
doc.Database.ObjectModified -= DatabaseObjectModified;
_docSubsTracker.Remove(doc.Name);
}
}
private void OnDocumentStoreChangedEvent(object _) => _sendConversionCache.ClearCache();
private readonly List<string> _docSubsTracker = new();
private void SubscribeToObjectChanges(Document doc)
private void SubscribeToObjectChanges(DocumentCollectionEventArgs e) =>
TryRegisterSubscribeToObjectChanges(e.Document);
private void TryRegisterSubscribeToObjectChanges(Document? doc)
{
if (doc == null || doc.Database == null || _docSubsTracker.Contains(doc.Name))
{
@@ -88,30 +122,43 @@ public abstract class AutocadSendBaseBinding : ISendBinding
}
_docSubsTracker.Add(doc.Name);
doc.Database.ObjectAppended += (_, e) => OnObjectChanged(e.DBObject);
doc.Database.ObjectErased += (_, e) => OnObjectChanged(e.DBObject);
doc.Database.ObjectModified += (_, e) => OnObjectChanged(e.DBObject);
doc.Database.ObjectAppended += DatabaseOnObjectAppended;
doc.Database.ObjectErased += DatabaseOnObjectErased;
doc.Database.ObjectModified += DatabaseObjectModified;
}
private void OnObjectChanged(DBObject dbObject) =>
_topLevelExceptionHandler.CatchUnhandled(() => OnChangeChangedObjectIds(dbObject));
private async void DatabaseOnObjectAppended(object sender, ObjectEventArgs e) =>
await _eventAggregator.GetEvent<ObjectAppendedEvent>().PublishAsync(e);
private async void DatabaseOnObjectErased(object sender, ObjectErasedEventArgs e) =>
await _eventAggregator.GetEvent<ObjectErasedEvent>().PublishAsync(e);
private async void DatabaseObjectModified(object sender, ObjectEventArgs e) =>
await _eventAggregator.GetEvent<ObjectModifiedEvent>().PublishAsync(e);
private void OnObjectAppended(ObjectEventArgs e) => OnChangeChangedObjectIds(e.DBObject);
private void ObjectErased(ObjectErasedEventArgs e) => OnChangeChangedObjectIds(e.DBObject);
private void ObjectModified(ObjectEventArgs e) => OnChangeChangedObjectIds(e.DBObject);
private void OnChangeChangedObjectIds(DBObject dBObject)
{
ChangedObjectIds.Add(dBObject.GetSpeckleApplicationId());
_idleManager.SubscribeToIdle(nameof(RunExpirationChecks), async () => await RunExpirationChecks());
ChangedObjectIds[dBObject.GetSpeckleApplicationId()] = 1;
_eventAggregator.GetEvent<IdleEvent>().OneTimeSubscribe(nameof(AutocadSendBinding), RunExpirationChecks);
}
private async Task RunExpirationChecks()
private async Task RunExpirationChecks(object _)
{
var senders = _store.GetSenders();
string[] objectIdsList = ChangedObjectIds.Keys.ToArray();
List<string> expiredSenderIds = new();
_sendConversionCache.EvictObjects(ChangedObjectIds);
_sendConversionCache.EvictObjects(objectIdsList);
foreach (SenderModelCard modelCard in senders)
{
var intersection = modelCard.SendFilter.NotNull().RefreshObjectIds().Intersect(ChangedObjectIds).ToList();
var intersection = modelCard.SendFilter.NotNull().RefreshObjectIds().Intersect(objectIdsList).ToList();
bool isExpired = intersection.Count != 0;
if (isExpired)
{
@@ -128,7 +175,7 @@ public abstract class AutocadSendBaseBinding : ISendBinding
public List<ICardSetting> GetSendSettings() => [];
public async Task Send(string modelCardId) =>
await _threadContext.RunOnMainAsync(async () => await SendInternal(modelCardId));
await _threadContext.RunOnWorkerAsync(async () => await SendInternal(modelCardId));
protected abstract void InitializeSettings(IServiceProvider serviceProvider);
@@ -136,17 +183,55 @@ public abstract class AutocadSendBaseBinding : ISendBinding
{
try
{
using var manager = _sendOperationManagerFactory.Create();
if (_store.GetModelById(modelCardId) is not SenderModelCard modelCard)
{
// Handle as GLOBAL ERROR at BrowserBridge
throw new InvalidOperationException("No publish model card was found.");
}
using var scope = _serviceProvider.CreateScope();
InitializeSettings(scope.ServiceProvider);
using var cancellationItem = _cancellationManager.GetCancellationItem(modelCardId);
// Disable document activation (document creation and document switch)
// Not disabling results in DUI model card being out of sync with the active document
// The DocumentActivated event isn't usable probably because it is pushed to back of main thread queue
Application.DocumentManager.DocumentActivationEnabled = false;
await manager.Process(
Commands,
modelCardId,
(sp, card) => InitializeSettings(sp),
card => Application.DocumentManager.CurrentDocument.GetObjects(card.SendFilter.NotNull().RefreshObjectIds())
// Get elements to convert
List<AutocadRootObject> autocadObjects = Application.DocumentManager.CurrentDocument.GetObjects(
modelCard.SendFilter.NotNull().RefreshObjectIds()
);
if (autocadObjects.Count == 0)
{
// Handle as CARD ERROR in this function
throw new SpeckleSendFilterException("No objects were found to convert. Please update your publish filter!");
}
var sendResult = await scope
.ServiceProvider.GetRequiredService<SendOperation<AutocadRootObject>>()
.Execute(
autocadObjects,
modelCard.GetSendInfo(_speckleApplication.Slug),
_operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationItem.Token),
cancellationItem.Token
);
await Commands.SetModelSendResult(modelCardId, sendResult.RootObjId, sendResult.ConversionResults);
}
catch (OperationCanceledException)
{
// SWALLOW -> UI handles it immediately, so we do not need to handle anything for now!
// Idea for later -> when cancel called, create promise from UI to solve it later with this catch block.
// So have 3 state on UI -> Cancellation clicked -> Cancelling -> Cancelled
return;
}
catch (Exception ex) when (!ex.IsFatal()) // UX reasons - we will report operation exceptions as model card error. We may change this later when we have more exception documentation
{
_logger.LogModelCardHandledError(ex);
await Commands.SetModelError(modelCardId, ex);
}
finally
{
@@ -1,13 +1,16 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
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.Eventing;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Models.Card.SendFilter;
using Speckle.Converters.Autocad;
using Speckle.Converters.Common;
using Speckle.Sdk;
namespace Speckle.Connectors.Autocad.Bindings;
@@ -20,23 +23,27 @@ public sealed class AutocadSendBinding : AutocadSendBaseBinding
IBrowserBridge parent,
IEnumerable<ISendFilter> sendFilters,
ICancellationManager cancellationManager,
IServiceProvider serviceProvider,
ISendConversionCache sendConversionCache,
IOperationProgressManager operationProgressManager,
ILogger<AutocadSendBinding> logger,
IAutocadConversionSettingsFactory autocadConversionSettingsFactory,
ISpeckleApplication speckleApplication,
IThreadContext threadContext,
ITopLevelExceptionHandler topLevelExceptionHandler,
IAppIdleManager appIdleManager,
ISendOperationManagerFactory sendOperationManagerFactory
IEventAggregator eventAggregator
)
: base(
store,
parent,
sendFilters,
cancellationManager,
serviceProvider,
sendConversionCache,
operationProgressManager,
logger,
speckleApplication,
threadContext,
topLevelExceptionHandler,
appIdleManager,
sendOperationManagerFactory
eventAggregator
)
{
_autocadConversionSettingsFactory = autocadConversionSettingsFactory;
@@ -13,7 +13,6 @@ using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.DUI;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Models.Card.SendFilter;
using Speckle.Connectors.DUI.WebView;
using Speckle.Sdk.Models.GraphTraversal;
@@ -24,7 +23,7 @@ public static class SharedRegistration
{
public static void AddAutocadBase(this IServiceCollection serviceCollection)
{
serviceCollection.AddConnectors();
serviceCollection.AddConnectorUtils();
serviceCollection.AddDUI<DefaultThreadContext, AutocadDocumentStore>();
serviceCollection.AddDUIView();
@@ -49,8 +48,6 @@ public static class SharedRegistration
serviceCollection.AddScoped<AutocadMaterialUnpacker>();
serviceCollection.AddScoped<IAutocadMaterialBaker, AutocadMaterialBaker>();
serviceCollection.AddSingleton<IAppIdleManager, AutocadIdleManager>();
// operation progress manager
serviceCollection.AddSingleton<IOperationProgressManager, OperationProgressManager>();
@@ -1,5 +1,6 @@
using Microsoft.Extensions.Logging;
using Speckle.Connectors.DUI.Bridge;
using Autodesk.AutoCAD.ApplicationServices;
using Speckle.Connectors.Autocad.Plugin;
using Speckle.Connectors.DUI.Eventing;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Utils;
@@ -10,18 +11,28 @@ public class AutocadDocumentStore : DocumentModelStore
private const string NULL_DOCUMENT_NAME = "Null Doc";
private string _previousDocName;
private readonly AutocadDocumentManager _autocadDocumentManager;
private readonly IEventAggregator _eventAggregator;
public AutocadDocumentStore(
ILogger<DocumentModelStore> logger,
IJsonSerializer jsonSerializer,
AutocadDocumentManager autocadDocumentManager,
ITopLevelExceptionHandler topLevelExceptionHandler
IEventAggregator eventAggregator
)
: base(logger, jsonSerializer)
: base(jsonSerializer)
{
_autocadDocumentManager = autocadDocumentManager;
_eventAggregator = eventAggregator;
_previousDocName = NULL_DOCUMENT_NAME;
eventAggregator.GetEvent<DocumentActivatedEvent>().Subscribe(DocChanged);
// since below event triggered as secondary, it breaks the logic in OnDocChangeInternal function, leaving it here for now.
// Autodesk.AutoCAD.ApplicationServices.Application.DocumentWindowCollection.DocumentWindowActivated += (_, args) =>
// OnDocChangeInternal((Document)args.DocumentWindow.Document);
}
public override async Task OnDocumentStoreInitialized()
{
// POC: Will be addressed to move it into AutocadContext!
if (Application.DocumentManager.MdiActiveDocument != null)
{
@@ -29,18 +40,13 @@ public class AutocadDocumentStore : DocumentModelStore
// POC: this logic might go when we have document management in context
// It is with the case of if binding created with already a document
// This is valid when user opens acad file directly double clicking
OnDocChangeInternal(Application.DocumentManager.MdiActiveDocument);
await TryDocChanged(Application.DocumentManager.MdiActiveDocument);
}
Application.DocumentManager.DocumentActivated += (_, e) =>
topLevelExceptionHandler.CatchUnhandled(() => OnDocChangeInternal(e.Document));
// since below event triggered as secondary, it breaks the logic in OnDocChangeInternal function, leaving it here for now.
// Autodesk.AutoCAD.ApplicationServices.Application.DocumentWindowCollection.DocumentWindowActivated += (_, args) =>
// OnDocChangeInternal((Document)args.DocumentWindow.Document);
}
private void OnDocChangeInternal(Document? doc)
private async Task DocChanged(DocumentCollectionEventArgs e) => await TryDocChanged(e.Document);
private async Task TryDocChanged(Document? doc)
{
var currentDocName = doc != null ? doc.Name : NULL_DOCUMENT_NAME;
if (_previousDocName == currentDocName)
@@ -50,7 +56,7 @@ public class AutocadDocumentStore : DocumentModelStore
_previousDocName = currentDocName;
LoadState();
OnDocumentChanged();
await _eventAggregator.GetEvent<DocumentStoreChangedEvent>().PublishAsync(new object());
}
protected override void LoadState()
@@ -1,19 +0,0 @@
using Speckle.Connectors.DUI.Bridge;
using Speckle.InterfaceGenerator;
namespace Speckle.Connectors.Autocad.HostApp;
public partial interface IAutocadIdleManager : IAppIdleManager;
[GenerateAutoInterface]
public sealed class AutocadIdleManager(IIdleCallManager idleCallManager)
: AppIdleManager(idleCallManager),
IAutocadIdleManager
{
private readonly IIdleCallManager _idleCallManager = idleCallManager;
protected override void AddEvent() => Application.Idle += AutocadAppOnIdle;
private void AutocadAppOnIdle(object? sender, EventArgs e) =>
_idleCallManager.AppOnIdle(() => Application.Idle -= AutocadAppOnIdle);
}
@@ -3,7 +3,6 @@ using Microsoft.Extensions.Logging;
using Speckle.Connectors.Autocad.HostApp.Extensions;
using Speckle.Connectors.Autocad.Operations.Send;
using Speckle.Connectors.Common.Instances;
using Speckle.Converters.AutocadShared.ToSpeckle;
using Speckle.Converters.Common;
using Speckle.DoubleNumerics;
using Speckle.Sdk;
@@ -18,19 +17,16 @@ public class AutocadInstanceUnpacker : IInstanceUnpacker<AutocadRootObject>
{
private readonly IHostToSpeckleUnitConverter<UnitsValue> _unitsConverter;
private readonly IInstanceObjectsManager<AutocadRootObject, List<Entity>> _instanceObjectsManager;
private readonly IPropertiesExtractor _propertiesExtractor;
private readonly ILogger<AutocadInstanceUnpacker> _logger;
public AutocadInstanceUnpacker(
IHostToSpeckleUnitConverter<UnitsValue> unitsConverter,
IInstanceObjectsManager<AutocadRootObject, List<Entity>> instanceObjectsManager,
IPropertiesExtractor propertiesExtractor,
ILogger<AutocadInstanceUnpacker> logger
)
{
_unitsConverter = unitsConverter;
_instanceObjectsManager = instanceObjectsManager;
_propertiesExtractor = propertiesExtractor;
_logger = logger;
}
@@ -75,13 +71,6 @@ public class AutocadInstanceUnpacker : IInstanceUnpacker<AutocadRootObject>
transform = GetMatrix(instance.BlockTransform.ToArray()),
units = _unitsConverter.ConvertOrThrow(Application.DocumentManager.CurrentDocument.Database.Insunits)
};
var properties = _propertiesExtractor.GetProperties(instance);
if (properties?.Count > 0)
{
instanceProxy["properties"] = properties;
}
_instanceObjectsManager.AddInstanceProxy(instanceId, instanceProxy);
// For each block instance that has the same definition, we need to keep track of the "maximum depth" at which is found.
@@ -112,23 +101,6 @@ public class AutocadInstanceUnpacker : IInstanceUnpacker<AutocadRootObject>
instanceProxiesWithSameDefinition.Add(_instanceObjectsManager.GetInstanceProxy(instanceId));
// Add text attributes from Instances as separate atomic objects:
// AttributeReferences found on Instances are just a text, not a part of the Instance
// They are not actually references and are not linked to AttributeDefinition (as one would expect),
// and already have the correct position (no need for transforms).
// We don't want to create a new BlockDefinition for every changed text for now, because AutoCAD API doesn't provide one,
// e.g. AnonymousBlockTableRecord is provided for each dynamic blocks with geometry changes, but not for Attribute changes.
// Docs on AttributeReference usage (used totally independent of AttributeDefinition): https://help.autodesk.com/view/OARX/2025/ENU/?guid=GUID-BA69D85A-2AED-43C2-B5B7-73022B5F28F8
// Case of trying to match AttributeDefinition with AttributeReference via Tag value (which is not unique): https://forums.autodesk.com/t5/net-forum/get-the-value-of-an-attribute-in-c/td-p/9060940
foreach (ObjectId id in instance.AttributeCollection)
{
var reference = (AttributeReference)transaction.GetObject(id, OpenMode.ForRead);
string refAppId = reference.GetSpeckleApplicationId();
_instanceObjectsManager.AddAtomicObject(refAppId, new(reference, refAppId));
}
// rely on already converted Definition
if (
_instanceObjectsManager.TryGetInstanceDefinitionProxy(
definitionId.ToString(),
@@ -159,12 +131,12 @@ public class AutocadInstanceUnpacker : IInstanceUnpacker<AutocadRootObject>
{
Entity obj = (Entity)transaction.GetObject(id, OpenMode.ForRead);
// In the case of dynamic blocks, this prevents sending objects that are not visible in its current state.
// Also skipping AttributeDefinition because it only contains default text values. We convert AttributeReference above instead, as a separate object.
if (!obj.Visible || obj is AttributeDefinition)
// In the case of dynamic blocks, this prevents sending objects that are not visibile in its current state.
if (!obj.Visible)
{
continue;
}
string appId = obj.GetSpeckleApplicationId();
definitionProxy.objects.Add(appId);
@@ -7,6 +7,7 @@ using Speckle.Connectors.Common.Extensions;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Common.Operations.Receive;
using Speckle.Converters.Common;
using Speckle.Sdk;
using Speckle.Sdk.Common;
using Speckle.Sdk.Dependencies;
using Speckle.Sdk.Models;
@@ -27,8 +28,7 @@ public class AutocadHostObjectBuilder(
IAutocadMaterialBaker materialBaker,
IAutocadColorBaker colorBaker,
AutocadContext autocadContext,
RootObjectUnpacker rootObjectUnpacker,
IReceiveConversionHandler conversionHandler
RootObjectUnpacker rootObjectUnpacker
) : IHostObjectBuilder
{
public Task<HostObjectBuilderResult> Build(
@@ -83,18 +83,18 @@ public class AutocadHostObjectBuilder(
colorBaker.ParseColors(unpackedRoot.ColorProxies, onOperationProgressed);
}
// 4 - Convert atomic objects
// 5 - Convert atomic objects
HashSet<ReceiveConversionResult> results = new();
HashSet<string> bakedObjectIds = new();
Dictionary<string, IReadOnlyCollection<Entity>> applicationIdMap = new();
var count = 0;
foreach (var (layerPath, atomicObject) in atomicObjectsWithPath)
{
string objectId = atomicObject.applicationId ?? atomicObject.id.NotNull();
onOperationProgressed.Report(new("Converting objects", (double)++count / atomicObjects.Count));
var ex = conversionHandler.TryConvert(() =>
cancellationToken.ThrowIfCancellationRequested();
try
{
cancellationToken.ThrowIfCancellationRequested();
string objectId = atomicObject.applicationId ?? atomicObject.id.NotNull();
IReadOnlyCollection<Entity> convertedObjects = ConvertObject(atomicObject, layerPath, baseLayerPrefix);
applicationIdMap[objectId] = convertedObjects;
@@ -109,14 +109,14 @@ public class AutocadHostObjectBuilder(
);
bakedObjectIds.UnionWith(convertedObjects.Select(e => e.GetSpeckleApplicationId()));
});
if (ex != null)
}
catch (Exception ex) when (!ex.IsFatal())
{
results.Add(new(Status.ERROR, atomicObject, null, null, ex));
}
}
// 5 - Convert instances
// 6 - Convert instances
var (createdInstanceIds, consumedObjectIds, instanceConversionResults) = instanceBaker.BakeInstances(
instanceComponentsWithPath,
applicationIdMap,
@@ -129,7 +129,7 @@ public class AutocadHostObjectBuilder(
results.RemoveWhere(result => result.ResultId != null && consumedObjectIds.Contains(result.ResultId));
results.UnionWith(instanceConversionResults);
// 6 - Create groups
// 7 - Create groups
if (unpackedRoot.GroupProxies != null)
{
IReadOnlyCollection<ReceiveConversionResult> groupResults = groupBaker.CreateGroups(
@@ -160,21 +160,15 @@ public class AutocadHostObjectBuilder(
var converted = converter.Convert(obj);
// 2: handle result
switch (converted)
if (converted is Entity entity)
{
case Entity entity:
var bakedEntity = BakeObject(entity, obj, layerName);
convertedEntities.Add(bakedEntity);
break;
case List<(Entity, Base)> listConversionResult: // this is from fallback conversion for brep/brepx/subdx/extrusionx/polycurve
var bakedFallbackEntities = BakeObjectsAsGroup(listConversionResult, obj, layerName, baseLayerNamePrefix);
convertedEntities.UnionWith(bakedFallbackEntities);
break;
default:
// TODO: capture defualt case with report object here? Same as in Rhino
break;
var bakedEntity = BakeObject(entity, obj, layerName);
convertedEntities.Add(bakedEntity);
}
else if (converted is List<(Entity, Base)> fallbackConversionResult)
{
var bakedFallbackEntities = BakeObjectsAsGroup(fallbackConversionResult, obj, layerName, baseLayerNamePrefix);
convertedEntities.UnionWith(bakedFallbackEntities);
}
tr.Commit();
@@ -214,11 +208,6 @@ public class AutocadHostObjectBuilder(
entities.Add(conversionResult);
}
if (entities.Count <= 1) // return if empty list or only one, because we don't want to create empty or single item groups.
{
return entities;
}
var tr = Application.DocumentManager.CurrentDocument.Database.TransactionManager.TopTransaction;
var groupDictionary = (DBDictionary)
tr.GetObject(Application.DocumentManager.CurrentDocument.Database.GroupDictionaryId, OpenMode.ForWrite);
@@ -60,7 +60,7 @@ public abstract class AutocadRootObjectBaseBuilder : IRootObjectBuilder<AutocadR
)]
public Task<RootObjectBuilderResult> Build(
IReadOnlyList<AutocadRootObject> objects,
string projectId,
SendInfo sendInfo,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
@@ -95,19 +95,28 @@ public abstract class AutocadRootObjectBaseBuilder : IRootObjectBuilder<AutocadR
foreach (var (entity, applicationId) in atomicObjects)
{
cancellationToken.ThrowIfCancellationRequested();
// Create and add a collection for this entity if not done so already.
(Collection objectCollection, LayerTableRecord? autocadLayer) = CreateObjectCollection(entity, tr);
if (autocadLayer is not null)
using (var convertActivity = _activityFactory.Start("Converting object"))
{
usedAcadLayers.Add(autocadLayer);
root.elements.Add(objectCollection);
// Create and add a collection for this entity if not done so already.
(Collection objectCollection, LayerTableRecord? autocadLayer) = CreateObjectCollection(entity, tr);
if (autocadLayer is not null)
{
usedAcadLayers.Add(autocadLayer);
root.elements.Add(objectCollection);
}
var result = ConvertAutocadEntity(
entity,
applicationId,
objectCollection,
instanceProxies,
sendInfo.ProjectId
);
results.Add(result);
onOperationProgressed.Report(new("Converting", (double)++count / atomicObjects.Count));
}
var result = ConvertAutocadEntity(entity, applicationId, objectCollection, instanceProxies, projectId);
results.Add(result);
onOperationProgressed.Report(new("Converting", (double)++count / atomicObjects.Count));
}
if (results.All(x => x.Status == Status.ERROR))
@@ -1,10 +1,10 @@
using Speckle.Connectors.Common;
using Speckle.Sdk.Host;
namespace Speckle.Connectors.Autocad.Plugin;
public static class AppUtils
{
public static Speckle.Sdk.Application App =>
public static HostApplication App =>
#if CIVIL3D
HostApplications.Civil3D;
#elif AUTOCAD
@@ -14,9 +14,7 @@ public static class AppUtils
#endif
public static HostAppVersion Version =>
#if AUTOCAD2026 || CIVIL3D2026
HostAppVersion.v2026;
#elif AUTOCAD2025 || CIVIL3D2025
#if AUTOCAD2025 || CIVIL3D2025
HostAppVersion.v2025;
#elif AUTOCAD2024 || CIVIL3D2024
HostAppVersion.v2024;
@@ -4,6 +4,7 @@ using Autodesk.AutoCAD.Windows;
using Microsoft.Extensions.DependencyInjection;
using Speckle.Connectors.Common;
using Speckle.Connectors.DUI;
using Speckle.Connectors.DUI.Eventing;
using Speckle.Connectors.DUI.WebView;
#if AUTOCAD
using Speckle.Connectors.Autocad.DependencyInjection;
@@ -17,10 +18,10 @@ namespace Speckle.Connectors.Autocad.Plugin;
public class AutocadCommand
{
private static PaletteSet? PaletteSet { get; set; }
private static readonly Guid s_id = new("7C27DD2B-86E8-4D31-B3DE-B34B267B1DC8");
private static readonly Guid s_id = new("3223E594-1B09-4E54-B3DD-8EA0BECE7BA5");
public ServiceProvider? Container { get; private set; }
private IDisposable? _disposableLogger;
public const string COMMAND_STRING = "Speckle";
public const string COMMAND_STRING = "SpeckleBeta";
[CommandMethod(COMMAND_STRING)]
public void Command()
@@ -31,7 +32,7 @@ public class AutocadCommand
return;
}
PaletteSet = new PaletteSet($"Speckle", s_id)
PaletteSet = new PaletteSet($"Speckle (Beta)", s_id)
{
Size = new Size(400, 500),
DockEnabled = (DockSides)((int)DockSides.Left + (int)DockSides.Right)
@@ -48,11 +49,12 @@ public class AutocadCommand
services.AddCivil3dConverters();
#endif
Container = services.BuildServiceProvider();
AutocadEvents.Register(Container.GetRequiredService<IEventAggregator>());
Container.UseDUI();
var panelWebView = Container.GetRequiredService<DUI3ControlWebView>();
PaletteSet.AddVisual("Speckle", panelWebView);
PaletteSet.AddVisual("Speckle (Beta)", panelWebView);
FocusPalette();
}
@@ -0,0 +1,38 @@
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Eventing;
namespace Speckle.Connectors.Autocad.Plugin;
public class DocumentActivatedEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
: ThreadedEvent<DocumentCollectionEventArgs>(threadContext, exceptionHandler);
public class DocumentToBeDestroyedEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
: ThreadedEvent<DocumentCollectionEventArgs>(threadContext, exceptionHandler);
public class ImpliedSelectionChangedEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
: ThreadedEvent<EventArgs>(threadContext, exceptionHandler);
public class ObjectAppendedEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
: ThreadedEvent<ObjectEventArgs>(threadContext, exceptionHandler);
public class ObjectErasedEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
: ThreadedEvent<ObjectErasedEventArgs>(threadContext, exceptionHandler);
public class ObjectModifiedEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
: ThreadedEvent<ObjectEventArgs>(threadContext, exceptionHandler);
public static class AutocadEvents
{
public static void Register(IEventAggregator eventAggregator)
{
Application.Idle += async (_, e) => await eventAggregator.GetEvent<IdleEvent>().PublishAsync(e);
Application.DocumentManager.DocumentActivated += async (_, e) =>
await eventAggregator.GetEvent<DocumentActivatedEvent>().PublishAsync(e);
Application.DocumentManager.DocumentToBeDestroyed += async (_, e) =>
await eventAggregator.GetEvent<DocumentToBeDestroyedEvent>().PublishAsync(e);
}
}
@@ -1,11 +1,9 @@
using System.IO;
using System.Reflection;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Autodesk.Windows;
using Speckle.Sdk;
#if !AUTOCAD2025_OR_GREATER && !CIVIL3D2025_OR_GREATER
using System.IO;
#endif
namespace Speckle.Connectors.Autocad.Plugin;
@@ -49,19 +47,19 @@ public class AutocadRibbon
private void Create()
{
RibbonTab tab = FindOrMakeTab("Speckle");
RibbonPanelSource source = new() { Title = "Speckle" };
RibbonPanelSource source = new() { Title = "Speckle (Beta)" };
RibbonPanel panel = new() { Source = source };
tab.Panels.Add(panel);
RibbonToolTip speckleToolTip =
new()
{
Title = "Speckle",
Content = $"Next Gen Speckle Connector for {AppUtils.App.Name}",
Title = "Speckle (Beta)",
Content = $"Next Gen Speckle Connector (Beta) for {AppUtils.App.Name}",
IsHelpEnabled = true // Without this "Press F1 for help" does not appear in the tooltip
};
_ = CreateSpeckleButton("Speckle", source, null, speckleToolTip, "logo");
_ = CreateSpeckleButton("Speckle (Beta)", source, null, speckleToolTip, "logo");
}
private void ComponentManager_ItemInitialized(object? sender, RibbonItemEventArgs e)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 302 B

After

Width:  |  Height:  |  Size: 714 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 430 B

After

Width:  |  Height:  |  Size: 1.6 KiB

@@ -9,7 +9,6 @@
<Import_RootNamespace>Speckle.Connectors.AutocadShared</Import_RootNamespace>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)Bindings\AutocadReceiveBaseBinding.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bindings\AutocadSelectionBinding.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bindings\AutocadReceiveBinding.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bindings\AutocadSendBinding.cs" />
@@ -22,7 +21,6 @@
<Compile Include="$(MSBuildThisFileDirectory)HostApp\AutocadColorUnpacker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\AutocadGroupBaker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\AutocadGroupUnpacker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\AutocadIdleManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\AutocadInstanceBaker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\AutocadInstanceUnpacker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\AutocadLayerBaker.cs" />
@@ -42,6 +40,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\AutocadRootObject.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\AutocadRootObjectBaseBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\AutocadRootObjectBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Plugin\AutocadEvents.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Plugin\AutocadRibbon.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Plugin\AutocadExtensionApplication.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Plugin\AutocadCommand.cs" />
@@ -3,15 +3,6 @@
<PropertyGroup Label="Globals">
<ProjectGuid>{41BC679F-887F-44CF-971D-A5502EE87DB0}</ProjectGuid>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<OutputPath>bin\Debug\</OutputPath>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Local|AnyCPU' ">
<OutputPath>bin\Local\</OutputPath>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<OutputPath>bin\Release\</OutputPath>
</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"/>
@@ -6,10 +6,12 @@
<Civil3DVersion>2022</Civil3DVersion>
<DefineConstants>$(DefineConstants);CIVIL3D;CIVIL3D2022;CIVIL3D2022_OR_GREATER</DefineConstants>
<Configurations>Debug;Release;Local</Configurations>
<StartAction>Program</StartAction>
<StartProgram>$(ProgramW6432)\Autodesk\AutoCAD $(Civil3DVersion)\acad.exe</StartProgram>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2022.0.2" ExcludeAssets="runtime"/>
<PackageReference Include="Speckle.Civil3D.API" VersionOverride="2022.0.2" ExcludeAssets="runtime"/>
<PackageReference Include="Speckle.AutoCAD.API" />
<PackageReference Include="Speckle.Civil3D.API" />
</ItemGroup>
<ItemGroup>
@@ -268,16 +268,18 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.3, )",
"Speckle.Sdk": "[3.5.3, )",
"Speckle.Sdk.Dependencies": "[3.5.3, )"
"Speckle.Objects": "[3.1.0-dev.255, )",
"Speckle.Sdk": "[3.1.0-dev.255, )",
"Speckle.Sdk.Dependencies": "[3.1.0-dev.255, )"
}
},
"speckle.connectors.dui": {
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )"
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.0-dev.255, )",
"Speckle.Sdk.Dependencies": "[3.1.0-dev.255, )"
}
},
"speckle.connectors.dui.webview": {
@@ -302,7 +304,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.3, )"
"Speckle.Objects": "[3.1.0-dev.255, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -341,40 +343,40 @@
"Speckle.DoubleNumerics": {
"type": "CentralTransitive",
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
"resolved": "4.0.1",
"contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"requested": "[3.1.0-dev.255, )",
"resolved": "3.1.0-dev.255",
"contentHash": "0S9YVdu5nx7SYS0y5Gq1nwCbxfYB2UxsUUCskaGmzRAm+QzHGqCLYHgadz7Sw5mNAF3bOTLVrOYMzyLAWFWxJA==",
"dependencies": {
"Speckle.Sdk": "3.5.3"
"Speckle.Sdk": "3.1.0-dev.255"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
"requested": "[3.1.0-dev.255, )",
"resolved": "3.1.0-dev.255",
"contentHash": "TsFpIv8ipvOD/r0se1XmcNPo7t5dWVadwV32Z6vUqoW3s94VIE54/ZTHNkRCEYHFhfOyTcFfbLegiei+0D13bQ==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.Bcl.AsyncInterfaces": "[5.0.0]",
"Microsoft.CSharp": "4.7.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",
"Microsoft.Data.Sqlite": "[7.0.5]",
"Microsoft.Extensions.DependencyInjection.Abstractions": "[2.2.0]",
"Microsoft.Extensions.Logging": "[2.2.0]",
"Speckle.DoubleNumerics": "4.0.1",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.3"
"Speckle.Sdk.Dependencies": "3.1.0-dev.255"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
"requested": "[3.1.0-dev.255, )",
"resolved": "3.1.0-dev.255",
"contentHash": "koFeWc/EB2XrMH2aKl2Hqwrqk/fAGYEVKgxFlFpnEAsbZf7Kk57gBQ3ViV5l6X9vqMHGWFc4dWUp24diq/XPtQ=="
}
}
}
@@ -6,11 +6,13 @@
<Civil3DVersion>2023</Civil3DVersion>
<DefineConstants>$(DefineConstants);CIVIL3D;CIVIL3D2023;CIVIL3D2022_OR_GREATER;CIVIL3D2023_OR_GREATER</DefineConstants>
<Configurations>Debug;Release;Local</Configurations>
<StartAction>Program</StartAction>
<StartProgram>$(ProgramW6432)\Autodesk\AutoCAD $(Civil3DVersion)\acad.exe</StartProgram>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2023.0.0" ExcludeAssets="runtime"/>
<PackageReference Include="Speckle.Civil3D.API" VersionOverride="2023.0.0" ExcludeAssets="runtime"/>
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2023.0.0" />
<PackageReference Include="Speckle.Civil3D.API" VersionOverride="2023.0.0" />
</ItemGroup>
<ItemGroup>
@@ -268,16 +268,18 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.3, )",
"Speckle.Sdk": "[3.5.3, )",
"Speckle.Sdk.Dependencies": "[3.5.3, )"
"Speckle.Objects": "[3.1.0-dev.255, )",
"Speckle.Sdk": "[3.1.0-dev.255, )",
"Speckle.Sdk.Dependencies": "[3.1.0-dev.255, )"
}
},
"speckle.connectors.dui": {
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )"
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.0-dev.255, )",
"Speckle.Sdk.Dependencies": "[3.1.0-dev.255, )"
}
},
"speckle.connectors.dui.webview": {
@@ -302,7 +304,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.3, )"
"Speckle.Objects": "[3.1.0-dev.255, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -341,40 +343,40 @@
"Speckle.DoubleNumerics": {
"type": "CentralTransitive",
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
"resolved": "4.0.1",
"contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"requested": "[3.1.0-dev.255, )",
"resolved": "3.1.0-dev.255",
"contentHash": "0S9YVdu5nx7SYS0y5Gq1nwCbxfYB2UxsUUCskaGmzRAm+QzHGqCLYHgadz7Sw5mNAF3bOTLVrOYMzyLAWFWxJA==",
"dependencies": {
"Speckle.Sdk": "3.5.3"
"Speckle.Sdk": "3.1.0-dev.255"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
"requested": "[3.1.0-dev.255, )",
"resolved": "3.1.0-dev.255",
"contentHash": "TsFpIv8ipvOD/r0se1XmcNPo7t5dWVadwV32Z6vUqoW3s94VIE54/ZTHNkRCEYHFhfOyTcFfbLegiei+0D13bQ==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.Bcl.AsyncInterfaces": "[5.0.0]",
"Microsoft.CSharp": "4.7.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",
"Microsoft.Data.Sqlite": "[7.0.5]",
"Microsoft.Extensions.DependencyInjection.Abstractions": "[2.2.0]",
"Microsoft.Extensions.Logging": "[2.2.0]",
"Speckle.DoubleNumerics": "4.0.1",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.3"
"Speckle.Sdk.Dependencies": "3.1.0-dev.255"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
"requested": "[3.1.0-dev.255, )",
"resolved": "3.1.0-dev.255",
"contentHash": "koFeWc/EB2XrMH2aKl2Hqwrqk/fAGYEVKgxFlFpnEAsbZf7Kk57gBQ3ViV5l6X9vqMHGWFc4dWUp24diq/XPtQ=="
}
}
}
@@ -6,11 +6,13 @@
<Civil3DVersion>2024</Civil3DVersion>
<DefineConstants>$(DefineConstants);CIVIL3D;CIVIL3D2024;CIVIL3D2022_OR_GREATER;CIVIL3D2023_OR_GREATER;CIVIL3D2024_OR_GREATER</DefineConstants>
<Configurations>Debug;Release;Local</Configurations>
<StartAction>Program</StartAction>
<StartProgram>$(ProgramW6432)\Autodesk\AutoCAD $(Civil3DVersion)\acad.exe</StartProgram>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2024.0.0" ExcludeAssets="runtime"/>
<PackageReference Include="Speckle.Civil3D.API" VersionOverride="2024.0.0" ExcludeAssets="runtime"/>
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2024.0.0" />
<PackageReference Include="Speckle.Civil3D.API" VersionOverride="2024.0.0" />
</ItemGroup>
<ItemGroup>
@@ -268,16 +268,18 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.3, )",
"Speckle.Sdk": "[3.5.3, )",
"Speckle.Sdk.Dependencies": "[3.5.3, )"
"Speckle.Objects": "[3.1.0-dev.255, )",
"Speckle.Sdk": "[3.1.0-dev.255, )",
"Speckle.Sdk.Dependencies": "[3.1.0-dev.255, )"
}
},
"speckle.connectors.dui": {
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )"
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.0-dev.255, )",
"Speckle.Sdk.Dependencies": "[3.1.0-dev.255, )"
}
},
"speckle.connectors.dui.webview": {
@@ -302,7 +304,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.3, )"
"Speckle.Objects": "[3.1.0-dev.255, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -341,40 +343,40 @@
"Speckle.DoubleNumerics": {
"type": "CentralTransitive",
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
"resolved": "4.0.1",
"contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"requested": "[3.1.0-dev.255, )",
"resolved": "3.1.0-dev.255",
"contentHash": "0S9YVdu5nx7SYS0y5Gq1nwCbxfYB2UxsUUCskaGmzRAm+QzHGqCLYHgadz7Sw5mNAF3bOTLVrOYMzyLAWFWxJA==",
"dependencies": {
"Speckle.Sdk": "3.5.3"
"Speckle.Sdk": "3.1.0-dev.255"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
"requested": "[3.1.0-dev.255, )",
"resolved": "3.1.0-dev.255",
"contentHash": "TsFpIv8ipvOD/r0se1XmcNPo7t5dWVadwV32Z6vUqoW3s94VIE54/ZTHNkRCEYHFhfOyTcFfbLegiei+0D13bQ==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.Bcl.AsyncInterfaces": "[5.0.0]",
"Microsoft.CSharp": "4.7.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",
"Microsoft.Data.Sqlite": "[7.0.5]",
"Microsoft.Extensions.DependencyInjection.Abstractions": "[2.2.0]",
"Microsoft.Extensions.Logging": "[2.2.0]",
"Speckle.DoubleNumerics": "4.0.1",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.3"
"Speckle.Sdk.Dependencies": "3.1.0-dev.255"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
"requested": "[3.1.0-dev.255, )",
"resolved": "3.1.0-dev.255",
"contentHash": "koFeWc/EB2XrMH2aKl2Hqwrqk/fAGYEVKgxFlFpnEAsbZf7Kk57gBQ3ViV5l6X9vqMHGWFc4dWUp24diq/XPtQ=="
}
}
}
@@ -1,21 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<PlatformTarget>x64</PlatformTarget>
<UseWpf>true</UseWpf>
<Civil3DVersion>2025</Civil3DVersion>
<DefineConstants>$(DefineConstants);CIVIL3D2025;CIVIL3D;CIVIL3D2022_OR_GREATER;CIVIL3D2023_OR_GREATER;CIVIL3D2024_OR_GREATER;CIVIL3D2025_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 of autocad/civil-->
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<RuntimeIdentifier>win-x64</RuntimeIdentifier> <!-- .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 -->
<StartAction>Program</StartAction>
<StartProgram>$(ProgramW6432)\Autodesk\AutoCAD $(Civil3DVersion)\acad.exe</StartProgram>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2025.0.0" ExcludeAssets="runtime"/>
<PackageReference Include="Speckle.Civil3d.API" VersionOverride="2025.0.0" ExcludeAssets="runtime"/>
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2025.0.0" />
<PackageReference Include="Speckle.Civil3d.API" VersionOverride="2025.0.0" />
<FrameworkReference Include="Microsoft.WindowsDesktop.App" />
</ItemGroup>
<ItemGroup>
@@ -84,6 +84,11 @@
"resolved": "8.0.0",
"contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
},
"Microsoft.CSharp": {
"type": "Transitive",
"resolved": "4.7.0",
"contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA=="
},
"Microsoft.Data.Sqlite": {
"type": "Transitive",
"resolved": "7.0.5",
@@ -219,16 +224,18 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.3, )",
"Speckle.Sdk": "[3.5.3, )",
"Speckle.Sdk.Dependencies": "[3.5.3, )"
"Speckle.Objects": "[3.1.0-dev.255, )",
"Speckle.Sdk": "[3.1.0-dev.255, )",
"Speckle.Sdk.Dependencies": "[3.1.0-dev.255, )"
}
},
"speckle.connectors.dui": {
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )"
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.0-dev.255, )",
"Speckle.Sdk.Dependencies": "[3.1.0-dev.255, )"
}
},
"speckle.connectors.dui.webview": {
@@ -254,7 +261,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.3, )"
"Speckle.Objects": "[3.1.0-dev.255, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -293,38 +300,39 @@
"Speckle.DoubleNumerics": {
"type": "CentralTransitive",
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
"resolved": "4.0.1",
"contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"requested": "[3.1.0-dev.255, )",
"resolved": "3.1.0-dev.255",
"contentHash": "0S9YVdu5nx7SYS0y5Gq1nwCbxfYB2UxsUUCskaGmzRAm+QzHGqCLYHgadz7Sw5mNAF3bOTLVrOYMzyLAWFWxJA==",
"dependencies": {
"Speckle.Sdk": "3.5.3"
"Speckle.Sdk": "3.1.0-dev.255"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
"requested": "[3.1.0-dev.255, )",
"resolved": "3.1.0-dev.255",
"contentHash": "TsFpIv8ipvOD/r0se1XmcNPo7t5dWVadwV32Z6vUqoW3s94VIE54/ZTHNkRCEYHFhfOyTcFfbLegiei+0D13bQ==",
"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",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "[7.0.5]",
"Microsoft.Extensions.DependencyInjection.Abstractions": "[2.2.0]",
"Microsoft.Extensions.Logging": "[2.2.0]",
"Speckle.DoubleNumerics": "4.0.1",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.3"
"Speckle.Sdk.Dependencies": "3.1.0-dev.255"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
"requested": "[3.1.0-dev.255, )",
"resolved": "3.1.0-dev.255",
"contentHash": "koFeWc/EB2XrMH2aKl2Hqwrqk/fAGYEVKgxFlFpnEAsbZf7Kk57gBQ3ViV5l6X9vqMHGWFc4dWUp24diq/XPtQ=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -1,30 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<Civil3DVersion>2026</Civil3DVersion>
<DefineConstants>$(DefineConstants);CIVIL3D2026;CIVIL3D;CIVIL3D2022_OR_GREATER;CIVIL3D2023_OR_GREATER;CIVIL3D2024_OR_GREATER;CIVIL3D2025_OR_GREATER;CIVIL3D2026_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 of autocad/civil-->
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2026.0.0" ExcludeAssets="runtime"/>
<PackageReference Include="Speckle.Civil3d.API" VersionOverride="2026.0.0" ExcludeAssets="runtime"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Converters\Civil3d\Speckle.Converters.Civil3d2026\Speckle.Converters.Civil3d2026.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.Civil3dShared\Speckle.Connectors.Civil3dShared.projitems" Label="Shared" />
<Import Project="..\Speckle.Connectors.AutocadShared\Speckle.Connectors.AutocadShared.projitems" Label="Shared" />
</Project>
@@ -1,45 +0,0 @@
using Microsoft.Extensions.DependencyInjection;
using Speckle.Connectors.Autocad.Bindings;
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.Card;
using Speckle.Converters.Autocad;
using Speckle.Converters.Civil3dShared;
using Speckle.Converters.Common;
namespace Speckle.Connectors.Civil3dShared.Bindings;
public sealed class Civil3dReceiveBinding : AutocadReceiveBaseBinding
{
private readonly ICivil3dConversionSettingsFactory _civil3dConversionSettingsFactory;
private readonly IAutocadConversionSettingsFactory _autocadConversionSettingsFactory;
public Civil3dReceiveBinding(
IBrowserBridge parent,
ICancellationManager cancellationManager,
ICivil3dConversionSettingsFactory civil3dConversionSettingsFactory,
IAutocadConversionSettingsFactory autocadConversionSettingsFactory,
IThreadContext threadContext,
IReceiveOperationManagerFactory receiveOperationManagerFactory
)
: base(parent, cancellationManager, threadContext, receiveOperationManagerFactory)
{
_civil3dConversionSettingsFactory = civil3dConversionSettingsFactory;
_autocadConversionSettingsFactory = autocadConversionSettingsFactory;
}
// POC: we're registering the conversion settings for autocad here because we need the autocad conversion settings to be able to use the autocad typed converters.
// POC: We need a separate receive binding for civil3d due to using a different unit converter (needed for conversion settings construction)
protected override void InitializeSettings(IServiceProvider serviceProvider, ModelCard mc)
{
serviceProvider
.GetRequiredService<IConverterSettingsStore<Civil3dConversionSettings>>()
.Initialize(_civil3dConversionSettingsFactory.Create(Application.DocumentManager.CurrentDocument));
serviceProvider
.GetRequiredService<IConverterSettingsStore<AutocadConversionSettings>>()
.Initialize(_autocadConversionSettingsFactory.Create(Application.DocumentManager.CurrentDocument));
}
}
@@ -1,15 +1,18 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
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.Eventing;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Models.Card.SendFilter;
using Speckle.Converters.Autocad;
using Speckle.Converters.Civil3dShared;
using Speckle.Converters.Common;
using Speckle.Sdk;
namespace Speckle.Connectors.Civil3dShared.Bindings;
@@ -23,24 +26,28 @@ public sealed class Civil3dSendBinding : AutocadSendBaseBinding
IBrowserBridge parent,
IEnumerable<ISendFilter> sendFilters,
ICancellationManager cancellationManager,
IServiceProvider serviceProvider,
ISendConversionCache sendConversionCache,
IOperationProgressManager operationProgressManager,
ILogger<AutocadSendBinding> logger,
ICivil3dConversionSettingsFactory civil3dConversionSettingsFactory,
IAutocadConversionSettingsFactory autocadConversionSettingsFactory,
ISpeckleApplication speckleApplication,
IThreadContext threadContext,
ITopLevelExceptionHandler topLevelExceptionHandler,
IAppIdleManager appIdleManager,
ISendOperationManagerFactory sendOperationManagerFactory
IEventAggregator eventAggregator
)
: base(
store,
parent,
sendFilters,
cancellationManager,
serviceProvider,
sendConversionCache,
operationProgressManager,
logger,
speckleApplication,
threadContext,
topLevelExceptionHandler,
appIdleManager,
sendOperationManagerFactory
eventAggregator
)
{
_civil3dConversionSettingsFactory = civil3dConversionSettingsFactory;
@@ -6,6 +6,7 @@ using Speckle.Connectors.Civil3dShared.Bindings;
using Speckle.Connectors.Civil3dShared.Operations.Send;
using Speckle.Connectors.Common.Builders;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Converters.Civil3dShared.Helpers;
using Speckle.Converters.Civil3dShared.ToSpeckle;
using Speckle.Sdk;
@@ -16,20 +17,18 @@ public static class Civil3dConnectorModule
public static void AddCivil3d(this IServiceCollection serviceCollection)
{
serviceCollection.AddAutocadBase();
// add send
serviceCollection.LoadSend();
// register civil specific send classes
serviceCollection.AddScoped<IRootObjectBuilder<AutocadRootObject>, Civil3dRootObjectBuilder>();
serviceCollection.AddSingleton<IBinding, Civil3dSendBinding>();
// add receive
serviceCollection.LoadReceive();
serviceCollection.AddSingleton<IBinding, Civil3dReceiveBinding>();
// automatically detects the Class:IClass interface pattern to register all generated interfaces
serviceCollection.AddMatchingInterfacesAsTransient(Assembly.GetExecutingAssembly());
// additional classes
serviceCollection.AddScoped<PropertySetDefinitionHandler>();
// automatically detects the Class:IClass interface pattern to register all generated interfaces
serviceCollection.AddMatchingInterfacesAsTransient(Assembly.GetExecutingAssembly());
serviceCollection.AddScoped<CatchmentGroupHandler>();
serviceCollection.AddScoped<PipeNetworkHandler>();
}
}
@@ -4,6 +4,7 @@ using Speckle.Connectors.Autocad.HostApp;
using Speckle.Connectors.Autocad.Operations.Send;
using Speckle.Connectors.Common.Caching;
using Speckle.Connectors.Common.Operations;
using Speckle.Converters.Civil3dShared.Helpers;
using Speckle.Converters.Civil3dShared.ToSpeckle;
using Speckle.Converters.Common;
using Speckle.Sdk.Logging;
@@ -15,10 +16,14 @@ public sealed class Civil3dRootObjectBuilder : AutocadRootObjectBaseBuilder
{
private readonly AutocadLayerUnpacker _layerUnpacker;
private readonly PropertySetDefinitionHandler _propertySetDefinitionHandler;
private readonly CatchmentGroupHandler _catchmentGroupHandler;
private readonly PipeNetworkHandler _pipeNetworkHandler;
public Civil3dRootObjectBuilder(
AutocadLayerUnpacker layerUnpacker,
PropertySetDefinitionHandler propertySetDefinitionHandler,
CatchmentGroupHandler catchmentGroupHandler,
PipeNetworkHandler pipeNetworkHandler,
IRootToSpeckleConverter converter,
ISendConversionCache sendConversionCache,
AutocadInstanceUnpacker instanceObjectManager,
@@ -41,6 +46,8 @@ public sealed class Civil3dRootObjectBuilder : AutocadRootObjectBaseBuilder
{
_layerUnpacker = layerUnpacker;
_propertySetDefinitionHandler = propertySetDefinitionHandler;
_catchmentGroupHandler = catchmentGroupHandler;
_pipeNetworkHandler = pipeNetworkHandler;
}
public override (Collection, LayerTableRecord?) CreateObjectCollection(Entity entity, Transaction tr)
@@ -50,8 +57,11 @@ public sealed class Civil3dRootObjectBuilder : AutocadRootObjectBaseBuilder
return (layer, autocadLayer);
}
// POC: probably will need to add Network proxies as well
public override void AddAdditionalProxiesToRoot(Collection rootObject)
{
rootObject[ProxyKeys.PROPERTYSET_DEFINITIONS] = _propertySetDefinitionHandler.Definitions;
rootObject["catchmentGroupProxies"] = _catchmentGroupHandler.CatchmentGroupProxiesCache.Values.ToList();
rootObject["pipeNetworkProxies"] = _pipeNetworkHandler.PipeNetworkProxiesCache.Values.ToList();
}
}
@@ -9,13 +9,13 @@
<Import_RootNamespace>Speckle.Connectors.Civil3dShared</Import_RootNamespace>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)Bindings\Civil3dReceiveBinding.cs" />
<Compile Include="$(MSBuildThisFileDirectory)DependencyInjection\Civil3dConnectorModule.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Civil3dRootObjectBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bindings\Civil3dSendBinding.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="$(MSBuildThisFileDirectory)DependencyInjection\" />
<Folder Include="$(MSBuildThisFileDirectory)Bindings\" />
<Folder Include="$(MSBuildThisFileDirectory)Operations\Send\" />
</ItemGroup>
</Project>
@@ -1,6 +1,6 @@
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Eventing;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Sdk;
@@ -11,8 +11,7 @@ public class CsiSharedBasicConnectorBinding : IBasicConnectorBinding
{
private readonly ISpeckleApplication _speckleApplication;
private readonly DocumentModelStore _store;
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
private readonly IThreadContext _threadContext;
private readonly IEventAggregator _eventAggregator;
public string Name => "baseBinding";
public IBrowserBridge Parent { get; }
public BasicConnectorBindingCommands Commands { get; }
@@ -21,28 +20,20 @@ public class CsiSharedBasicConnectorBinding : IBasicConnectorBinding
IBrowserBridge parent,
ISpeckleApplication speckleApplication,
DocumentModelStore store,
ITopLevelExceptionHandler topLevelExceptionHandler,
IThreadContext threadContext
IEventAggregator eventAggregator
)
{
_threadContext = threadContext;
Parent = parent;
_speckleApplication = speckleApplication;
_store = store;
_topLevelExceptionHandler = topLevelExceptionHandler;
Commands = new BasicConnectorBindingCommands(Parent);
_eventAggregator = eventAggregator;
_store.DocumentChanged += (_, _) =>
_topLevelExceptionHandler.FireAndForget(async () =>
{
// enforce main thread
await _threadContext.RunOnMainAsync(async () =>
{
await Commands.NotifyDocumentChanged();
});
});
_eventAggregator.GetEvent<DocumentStoreChangedEvent>().Subscribe(OnDocumentStoreChangedEvent);
}
private async Task OnDocumentStoreChangedEvent(object _) => await Commands.NotifyDocumentChanged();
public string GetConnectorVersion() => _speckleApplication.SpeckleVersion;
public string GetSourceApplicationName() => _speckleApplication.Slug;
@@ -53,20 +44,11 @@ public class CsiSharedBasicConnectorBinding : IBasicConnectorBinding
public DocumentModelStore GetDocumentState() => _store;
/// <remarks>Operations must run on the main thread for ETABS and SAP 2000</remarks>
public void AddModel(ModelCard model) =>
_topLevelExceptionHandler.CatchUnhandled(() => _threadContext.RunOnThread(() => _store.AddModel(model), true));
public void AddModel(ModelCard model) => _store.AddModel(model);
/// <remarks>Operations must run on the main thread for ETABS and SAP 2000</remarks>
public void UpdateModel(ModelCard model) =>
_topLevelExceptionHandler.CatchUnhandled(() => _threadContext.RunOnThread(() => _store.UpdateModel(model), true));
public void UpdateModel(ModelCard model) => _store.UpdateModel(model);
/// <remarks>Operations must run on the main thread for ETABS and SAP 2000</remarks>
public void RemoveModel(ModelCard model) =>
_topLevelExceptionHandler.CatchUnhandled(() => _threadContext.RunOnThread(() => _store.RemoveModel(model), true));
public void RemoveModels(List<ModelCard> models) =>
_topLevelExceptionHandler.CatchUnhandled(() => _threadContext.RunOnThread(() => _store.RemoveModels(models), true));
public void RemoveModel(ModelCard model) => _store.RemoveModel(model);
public Task HighlightModel(string modelCardId) => Task.CompletedTask;
@@ -1,21 +1,17 @@
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.CSiShared.Events;
using Speckle.Connectors.CSiShared.HostApp;
using Speckle.Connectors.CSiShared.Utils;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Eventing;
using Speckle.Converters.CSiShared.Utils;
using Timer = System.Timers.Timer;
namespace Speckle.Connectors.CSiShared.Bindings;
public class CsiSharedSelectionBinding : ISelectionBinding, IDisposable
public sealed class CsiSharedSelectionBinding : ISelectionBinding
{
private bool _disposed;
private readonly Timer _selectionTimer;
private readonly ICsiApplicationService _csiApplicationService;
private readonly IThreadContext _threadContext;
private HashSet<string> _lastSelection = new();
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
public IBrowserBridge Parent { get; }
public string Name => "selectionBinding";
@@ -23,58 +19,27 @@ public class CsiSharedSelectionBinding : ISelectionBinding, IDisposable
public CsiSharedSelectionBinding(
IBrowserBridge parent,
ICsiApplicationService csiApplicationService,
ITopLevelExceptionHandler topLevelExceptionHandler,
IThreadContext threadContext
IEventAggregator eventAggregator
)
{
_threadContext = threadContext;
Parent = parent;
_csiApplicationService = csiApplicationService;
_topLevelExceptionHandler = topLevelExceptionHandler;
_selectionTimer = new Timer(1000);
_selectionTimer.Elapsed += (_, _) =>
_topLevelExceptionHandler.CatchUnhandled(() => _threadContext.RunOnMain(CheckSelectionChanged));
_selectionTimer.Start();
eventAggregator.GetEvent<SelectionBindingEvent>().SubscribePeriodic(TimeSpan.FromSeconds(1), CheckSelectionChanged);
}
private void CheckSelectionChanged()
private void CheckSelectionChanged(object _)
{
// timer callbacks are on a background thread, but CSI API calls must be on main thread
var currentSelection = GetSelection();
var currentIds = new HashSet<string>(currentSelection.SelectedObjectIds);
if (!_lastSelection.SetEquals(currentIds))
{
_lastSelection = currentIds;
// ensure UI updates also run on main thread
_threadContext.RunOnMain(
() =>
_topLevelExceptionHandler.CatchUnhandled(
() => Parent.Send(SelectionBindingEvents.SET_SELECTION, currentSelection)
)
);
Parent.Send(SelectionBindingEvents.SET_SELECTION, currentSelection);
}
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_selectionTimer?.Dispose();
}
_disposed = true;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Gets the selection and creates an encoded ID (objectType and objectName).
/// </summary>
@@ -91,18 +56,22 @@ public class CsiSharedSelectionBinding : ISelectionBinding, IDisposable
var encodedIds = new List<string>(numberItems);
var typeCounts = new Dictionary<string, int>();
for (int i = 0; i < numberItems; i++)
{
var typeKey = (ModelObjectType)objectType[i];
var typeName = typeKey.ToString();
encodedIds.Add(ObjectIdentifier.Encode(objectType[i], objectName[i]));
typeCounts[typeName] = (typeCounts.TryGetValue(typeName, out var count) ? count : 0) + 1;
typeCounts[typeName] = (typeCounts.TryGetValue(typeName, out var count) ? count : 0) + 1; // NOTE: Cross-framework compatibility (net 48 and net8)
}
var summary =
encodedIds.Count == 0
? "No objects selected."
: $"{encodedIds.Count} objects ({string.Join(", ",
typeCounts.Select(kv => $"{kv.Value} {kv.Key}"))})";
return new SelectionInfo(encodedIds, summary);
}
}
@@ -1,16 +1,22 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Common.Cancellation;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.CSiShared.HostApp;
using Speckle.Connectors.CSiShared.Operations.Send.Settings;
using Speckle.Connectors.CSiShared.Settings;
using Speckle.Connectors.CSiShared.Utils;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Exceptions;
using Speckle.Connectors.DUI.Logging;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Connectors.DUI.Models.Card.SendFilter;
using Speckle.Connectors.DUI.Settings;
using Speckle.Converters.Common;
using Speckle.Converters.CSiShared;
using Speckle.Sdk;
using Speckle.Sdk.Common;
using Speckle.Sdk.Logging;
namespace Speckle.Connectors.CSiShared.Bindings;
@@ -20,57 +26,97 @@ public sealed class CsiSharedSendBinding : ISendBinding
public SendBindingUICommands Commands { get; }
public IBrowserBridge Parent { get; }
private readonly DocumentModelStore _store;
private readonly IServiceProvider _serviceProvider;
private readonly List<ISendFilter> _sendFilters;
private readonly ICancellationManager _cancellationManager;
private readonly IOperationProgressManager _operationProgressManager;
private readonly ILogger<CsiSharedSendBinding> _logger;
private readonly ICsiApplicationService _csiApplicationService;
private readonly ICsiConversionSettingsFactory _csiConversionSettingsFactory;
private readonly ISendOperationManagerFactory _sendOperationManagerFactory;
private readonly ToSpeckleSettingsManager _toSpeckleSettingsManager;
private readonly ISpeckleApplication _speckleApplication;
private readonly ISdkActivityFactory _activityFactory;
public CsiSharedSendBinding(
DocumentModelStore store,
IBrowserBridge parent,
IEnumerable<ISendFilter> sendFilters,
IServiceProvider serviceProvider,
ICancellationManager cancellationManager,
IOperationProgressManager operationProgressManager,
ILogger<CsiSharedSendBinding> logger,
ICsiConversionSettingsFactory csiConversionSettingsFactory,
ICsiApplicationService csiApplicationService,
ISendOperationManagerFactory sendOperationManagerFactory,
ToSpeckleSettingsManager toSpeckleSettingsManager
ISpeckleApplication speckleApplication,
ISdkActivityFactory activityFactory,
ICsiApplicationService csiApplicationService
)
{
_store = store;
_serviceProvider = serviceProvider;
_sendFilters = sendFilters.ToList();
_cancellationManager = cancellationManager;
_operationProgressManager = operationProgressManager;
_logger = logger;
Parent = parent;
Commands = new SendBindingUICommands(parent);
_csiConversionSettingsFactory = csiConversionSettingsFactory;
_speckleApplication = speckleApplication;
_activityFactory = activityFactory;
_csiApplicationService = csiApplicationService;
_sendOperationManagerFactory = sendOperationManagerFactory;
_toSpeckleSettingsManager = toSpeckleSettingsManager;
}
public List<ISendFilter> GetSendFilters() => _sendFilters;
public List<ICardSetting> GetSendSettings() =>
[new LoadCaseCombinationSetting([], _csiApplicationService.SapModel), new ResultTypeSetting([])];
public List<ICardSetting> GetSendSettings() => [];
public async Task Send(string modelCardId)
{
using var manager = _sendOperationManagerFactory.Create();
await manager.Process(
Commands,
modelCardId,
(sp, card) =>
using var activity = _activityFactory.Start();
try
{
if (_store.GetModelById(modelCardId) is not SenderModelCard modelCard)
{
sp.GetRequiredService<IConverterSettingsStore<CsiConversionSettings>>()
.Initialize(
_csiConversionSettingsFactory.Create(
_csiApplicationService.SapModel,
_toSpeckleSettingsManager.GetLoadCasesAndCombinations(card),
_toSpeckleSettingsManager.GetResultTypes(card)
)
);
},
card => card.SendFilter.NotNull().RefreshObjectIds().Select(DecodeObjectIdentifier).ToList()
);
throw new InvalidOperationException("No publish model card was found.");
}
using var scope = _serviceProvider.CreateScope();
scope
.ServiceProvider.GetRequiredService<IConverterSettingsStore<CsiConversionSettings>>()
.Initialize(_csiConversionSettingsFactory.Create(_csiApplicationService.SapModel));
using var cancellationItem = _cancellationManager.GetCancellationItem(modelCardId);
List<ICsiWrapper> wrappers = modelCard
.SendFilter.NotNull()
.RefreshObjectIds()
.Select(DecodeObjectIdentifier)
.ToList();
if (wrappers.Count == 0)
{
throw new SpeckleSendFilterException("No objects were found to convert. Please update your publish filter!");
}
var sendResult = await scope
.ServiceProvider.GetRequiredService<SendOperation<ICsiWrapper>>()
.Execute(
wrappers,
modelCard.GetSendInfo(_speckleApplication.Slug),
_operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationItem.Token),
cancellationItem.Token
);
await Commands.SetModelSendResult(modelCardId, sendResult.RootObjId, sendResult.ConversionResults);
}
catch (OperationCanceledException)
{
return;
}
catch (Exception ex) when (!ex.IsFatal())
{
_logger.LogModelCardHandledError(ex);
await Commands.SetModelError(modelCardId, ex);
}
}
private ICsiWrapper DecodeObjectIdentifier(string encodedId)
@@ -0,0 +1,8 @@
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Eventing;
namespace Speckle.Connectors.CSiShared.Events;
public class ModelChangedEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
: PeriodicThreadedEvent(threadContext, exceptionHandler);
@@ -0,0 +1,8 @@
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Eventing;
namespace Speckle.Connectors.CSiShared.Events;
public class SelectionBindingEvent(IThreadContext threadContext, ITopLevelExceptionHandler exceptionHandler)
: PeriodicThreadedEvent(threadContext, exceptionHandler);
@@ -1,57 +1,43 @@
using System.IO;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.CSiShared.Events;
using Speckle.Connectors.DUI.Eventing;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Utils;
using Speckle.Sdk;
using Speckle.Sdk.Common;
using Speckle.Sdk.Helpers;
using Speckle.Sdk.Logging;
using Timer = System.Timers.Timer;
namespace Speckle.Connectors.CSiShared.HostApp;
public class CsiDocumentModelStore : DocumentModelStore, IDisposable
public class CsiDocumentModelStore : DocumentModelStore
{
private readonly ISpeckleApplication _speckleApplication;
private readonly ILogger<CsiDocumentModelStore> _logger;
private readonly ICsiApplicationService _csiApplicationService;
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
private readonly IThreadContext _threadContext;
private readonly Timer _modelCheckTimer;
private readonly IEventAggregator _eventAggregator;
private string _lastModelFilename = string.Empty;
private bool _disposed;
private string HostAppUserDataPath { get; set; }
private string DocumentStateFile { get; set; }
private string ModelPathHash { get; set; }
public CsiDocumentModelStore(
ILogger<DocumentModelStore> baseLogger,
IJsonSerializer jsonSerializer,
ISpeckleApplication speckleApplication,
ILogger<CsiDocumentModelStore> logger,
ICsiApplicationService csiApplicationService,
ITopLevelExceptionHandler topLevelExceptionHandler,
IThreadContext threadContext
IEventAggregator eventAggregator
)
: base(baseLogger, jsonSerializer)
: base(jsonSerializer)
{
_threadContext = threadContext;
_speckleApplication = speckleApplication;
_logger = logger;
_csiApplicationService = csiApplicationService;
_topLevelExceptionHandler = topLevelExceptionHandler;
// initialize timer to check for model changes
_modelCheckTimer = new Timer(1000);
// timer runs on background thread but model checks must be on main thread
_modelCheckTimer.Elapsed += (_, _) =>
_topLevelExceptionHandler.CatchUnhandled(() => _threadContext.RunOnMain(CheckModelChanges));
_modelCheckTimer.Start();
_eventAggregator = eventAggregator;
eventAggregator.GetEvent<ModelChangedEvent>().SubscribePeriodic(TimeSpan.FromSeconds(1), CheckModelChanges);
}
private void CheckModelChanges()
private async Task CheckModelChanges(object _)
{
string currentFilename = _csiApplicationService.SapModel.GetModelFilename();
@@ -63,7 +49,8 @@ public class CsiDocumentModelStore : DocumentModelStore, IDisposable
_lastModelFilename = currentFilename;
SetPaths();
LoadState();
OnDocumentChanged();
await _eventAggregator.GetEvent<DocumentStoreChangedEvent>().PublishAsync(new object());
}
public override Task OnDocumentStoreInitialized()
@@ -82,7 +69,7 @@ public class CsiDocumentModelStore : DocumentModelStore, IDisposable
{
try
{
ModelPathHash = Md5.GetString(_csiApplicationService.SapModel.GetModelFilename());
ModelPathHash = Crypt.Md5(_csiApplicationService.SapModel.GetModelFilename(), length: 32);
HostAppUserDataPath = Path.Combine(
SpecklePathProvider.UserSpeckleFolderPath,
"ConnectorsFileData",
@@ -133,25 +120,4 @@ public class CsiDocumentModelStore : DocumentModelStore, IDisposable
ClearAndSave();
}
}
protected virtual void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
if (disposing)
{
_modelCheckTimer.Dispose();
}
_disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
@@ -1,27 +0,0 @@
using Microsoft.Extensions.DependencyInjection;
using Speckle.Converters.CSiShared.ToSpeckle.Helpers;
using Speckle.Converters.CSiShared.Utils;
namespace Speckle.Connectors.CSiShared.HostApp.Helpers;
public class CsiResultsExtractorFactory
{
private readonly IServiceProvider _serviceProvider;
public CsiResultsExtractorFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public IApplicationResultsExtractor GetExtractor(string resultsKey) =>
resultsKey switch
{
ResultsKey.BASE_REACT => _serviceProvider.GetRequiredService<CsiBaseReactResultsExtractor>(),
ResultsKey.FRAME_FORCES => _serviceProvider.GetRequiredService<CsiFrameForceResultsExtractor>(),
ResultsKey.JOINT_REACT => _serviceProvider.GetRequiredService<CsiJointReactResultsExtractor>(),
ResultsKey.PIER_FORCES => _serviceProvider.GetRequiredService<CsiPierForceResultsExtractor>(),
ResultsKey.SPANDREL_FORCES => _serviceProvider.GetRequiredService<CsiSpandrelForceResultsExtractor>(),
ResultsKey.STORY_DRIFTS => _serviceProvider.GetRequiredService<CsiStoryDriftsResultsExtractor>(),
_ => throw new InvalidOperationException($"{resultsKey} not accounted for in CsiResultsExtractorFactory")
};
}
@@ -4,11 +4,9 @@ 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;
@@ -39,7 +37,6 @@ public class CsiRootObjectBuilder : IRootObjectBuilder<ICsiWrapper>
private readonly ILogger<CsiRootObjectBuilder> _logger;
private readonly ISdkActivityFactory _activityFactory;
private readonly ICsiApplicationService _csiApplicationService;
private readonly AnalysisResultsExtractor _analysisResultsExtractor;
public CsiRootObjectBuilder(
IRootToSpeckleConverter rootToSpeckleConverter,
@@ -49,8 +46,7 @@ public class CsiRootObjectBuilder : IRootObjectBuilder<ICsiWrapper>
ISectionUnpacker sectionUnpacker,
ILogger<CsiRootObjectBuilder> logger,
ISdkActivityFactory activityFactory,
ICsiApplicationService csiApplicationService,
AnalysisResultsExtractor analysisResultsExtractor
ICsiApplicationService csiApplicationService
)
{
_converterSettings = converterSettings;
@@ -61,7 +57,6 @@ public class CsiRootObjectBuilder : IRootObjectBuilder<ICsiWrapper>
_logger = logger;
_activityFactory = activityFactory;
_csiApplicationService = csiApplicationService;
_analysisResultsExtractor = analysisResultsExtractor;
}
/// <summary>
@@ -75,7 +70,7 @@ public class CsiRootObjectBuilder : IRootObjectBuilder<ICsiWrapper>
/// </remarks>
public async Task<RootObjectBuilderResult> Build(
IReadOnlyList<ICsiWrapper> csiObjects,
string projectId,
SendInfo sendInfo,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
@@ -94,6 +89,8 @@ public class CsiRootObjectBuilder : IRootObjectBuilder<ICsiWrapper>
foreach (ICsiWrapper csiObject in csiObjects)
{
cancellationToken.ThrowIfCancellationRequested();
using var _2 = _activityFactory.Start("Convert");
var result = ConvertCsiObject(csiObject, rootObjectCollection);
results.Add(result);
@@ -117,34 +114,6 @@ public class CsiRootObjectBuilder : IRootObjectBuilder<ICsiWrapper>
rootObjectCollection[ProxyKeys.SECTION] = _sectionUnpacker.UnpackSections().ToList();
}
// Extract analysis results (if applicable)
// NOTE: objectSelectionSummary used to extract results for objects being published ONLY
// NOTE: etabs is complicated and we can't get specifics from original selection
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(
"No result type input for the requested load cases and combinations. Adjust publish settings."
);
}
if (!_csiApplicationService.SapModel.GetModelIsLocked())
{
throw new SpeckleException("Model unlocked. No access to analysis results.");
}
var analysisResults = _analysisResultsExtractor.ExtractAnalysisResults(
selectedCasesAndCombinations,
requestedResultTypes,
objectSelectionSummary
);
rootObjectCollection["analysisResults"] = analysisResults;
}
return new RootObjectBuilderResult(rootObjectCollection, results);
}
@@ -191,22 +160,4 @@ public class CsiRootObjectBuilder : IRootObjectBuilder<ICsiWrapper>
return new(Status.ERROR, applicationId, sourceType, null, ex);
}
}
/// <summary>
/// Generates a summary of object types and their associated names from the collection of CSI wrappers.
/// </summary>
/// <remarks>
/// A summary of object names for each object type is needed for getting analysis results of the selected objects only.
/// During object conversion, however, we lose the selection (like a clear selection)(presumably because of other api calls).
/// This has to be recreated since GetSelection() return type is bound by the interface.
/// The LINQ-based implementation is computationally inexpensive as it operates on an already-loaded collection without additional API calls.
/// Also, we don't want to rely on user selection remaining active, what if someone re-publishes using model card cache?
/// </remarks>
private Dictionary<ModelObjectType, List<string>> GetObjectSummary(IReadOnlyList<ICsiWrapper> csiObjects) =>
csiObjects
.GroupBy(csiObject => csiObject.ObjectType)
.ToDictionary(
group => group.Key, // ModelObjectType (FRAME, JOINT, etc.)
group => group.Select(obj => obj.Name).ToList() // Extract Name from each ICsiWrapper and convert to List<string>
);
}

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