Compare commits

..

12 Commits

Author SHA1 Message Date
Oğuzhan Koral 370588fa89 Merge pull request #461 from specklesystems/dev
.NET Build and Publish / build (push) Has been cancelled
.NET Build and Publish / test (push) Has been cancelled
.NET Build and Publish / deploy-installers (push) Has been cancelled
Update dev to release
2024-12-20 16:14:59 +03:00
Oğuzhan Koral c9802396f5 Merge pull request #448 from specklesystems/dev
.NET Build and Publish / build (push) Has been cancelled
.NET Build and Publish / test (push) Has been cancelled
.NET Build and Publish / deploy-installers (push) Has been cancelled
Update dev to release
2024-12-13 01:54:05 +03:00
Oğuzhan Koral 9644444ea7 Merge pull request #437 from specklesystems/dev
.NET Build and Publish / build (push) Has been cancelled
.NET Build and Publish / test (push) Has been cancelled
.NET Build and Publish / deploy-installers (push) Has been cancelled
Update dev to release
2024-12-04 22:38:16 +03:00
Alan Rynne bb0fffd4d7 Merge pull request #414 from specklesystems/dev
.NET Build and Publish / build (push) Has been cancelled
.NET Build and Publish / test (push) Has been cancelled
.NET Build and Publish / deploy-installers (push) Has been cancelled
Update `release/3.0.0` with changes from `dev`
2024-11-27 16:23:50 +01:00
Alan Rynne c719cfd66f Merge pull request #383 from specklesystems/dev
.NET Build and Publish / build (push) Has been cancelled
.NET Build and Publish / test (push) Has been cancelled
.NET Build and Publish / deploy-installers (push) Has been cancelled
Update `release/3.0.0` with changes from `dev`
2024-11-12 15:56:02 +01:00
Oğuzhan Koral 92435ce1a1 Merge pull request #376 from specklesystems/dev
.NET Build and Publish / build (push) Has been cancelled
.NET Build and Publish / test (push) Has been cancelled
.NET Build and Publish / deploy-installers (push) Has been cancelled
Update release/3.0.0 with changes from dev
2024-11-08 18:16:16 +00:00
Oğuzhan Koral 71409e8af8 Merge pull request #374 from specklesystems/dev
.NET Build and Publish / build (push) Has been cancelled
.NET Build and Publish / test (push) Has been cancelled
.NET Build and Publish / deploy-installers (push) Has been cancelled
Update release/3.0.0 with changes from dev
2024-11-08 15:13:47 +00:00
Alan Rynne 8060f46882 Merge pull request #359 from specklesystems/dev
Update `release/3.0.0` with changes from `dev`
2024-11-06 18:49:52 +01:00
Alan Rynne 6671edd36e Merge pull request #354 from specklesystems/dev
.NET Build and Publish / build (push) Has been cancelled
.NET Build and Publish / test (push) Has been cancelled
.NET Build and Publish / deploy-installers (push) Has been cancelled
Update `release/3.0.0` with changes from `dev`
2024-11-06 14:22:08 +01:00
Alan Rynne bc15ff3a34 Merge pull request #347 from specklesystems/dev
Update `release/3.0.0` with changes from `dev`
2024-11-05 10:30:20 +01:00
Alan Rynne de275dcf02 Merge pull request #337 from specklesystems/dev
.NET Build and Publish / build (push) Has been cancelled
.NET Build and Publish / test (push) Has been cancelled
.NET Build and Publish / deploy-installers (push) Has been cancelled
Update `release/3.0.0` with changes from `dev`
2024-10-31 12:17:57 +01:00
Alan Rynne 6158739df0 Merge pull request #331 from specklesystems/dev
Update `release/3.0.0` with changes from `dev`
2024-10-28 13:05:26 +01:00
608 changed files with 7706 additions and 23845 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
@@ -254,9 +254,6 @@ dotnet_diagnostic.ca1508.severity = warning # Avoid dead conditional code
dotnet_diagnostic.ca1509.severity = warning # Invalid entry in code metrics configuration file
dotnet_diagnostic.ca1861.severity = none # Prefer 'static readonly' fields over constant array arguments if the called method is called repeatedly and is not mutating the passed array (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1861)
# CA2007: Consider calling ConfigureAwait on the awaited task (this is not needed for application code, in fact we don't want to call anything but ConfigureAwait(true) which is the default)
dotnet_diagnostic.CA2007.severity = none
dotnet_diagnostic.cs8618.severity = suggestion # nullable problem
+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
+38 -29
View File
@@ -1,46 +1,30 @@
<!---
Provide a short summary in the Title above. Use the following template:
Provide a short summary in the Title above. Examples of good PR titles:
category(project): summary
* "Feature: adds metrics to component"
Categories:
* "Fix: resolves duplication in comment thread"
* feat: (new feature for the user, not a new feature for build script)
* fix: (bug fix for the user, not a fix to a build script)
* docs: (changes to the documentation)
* style: (formatting, missing semi colons, etc; no production code change)
* refactor: (refactoring production code, eg. renaming a variable)
* test: (adding missing tests, refactoring tests; no production code change)
* chore: (updating grunt tasks etc; no production code change)
Example:
feat(revit): added category filter to send
* "Update: apollo v2.34.0"
-->
## Description
## Description & motivation
<!---
Describe your changes, and why you're making them.
Describe your changes, and why you're making them. What benefit will this have to others?
Link related github issues here ->
Fixes #85, Fixes #22, Connects #123
Is this linked to an open Github issue, a thread in Speckle community,
or another pull request? Link it here.
If it is related to a Github issue, and resolves it, please link to the issue number, e.g.:
Fixes #85, Fixes #22, Fixes username/repo#123
Connects #123
-->
## User Value
<!---
Describe in 1 sentence the user value.
This can also be a link to the relevant thread in Speckle community, or a link to the Linear issue.
-->
## Changes:
<!---
@@ -84,10 +68,35 @@ Describe what tests have been added or amended, and why these demonstrate it wor
<!---
This checklist is a useful reminder of related tasks to uphold our repo quality. Amend this list as needed for the pr.
This checklist is mostly useful as a reminder of small things that can easily be
forgotten it is meant as a helpful tool rather than hoops to jump through.
Put an `x` between the square brackets, e.g. [x], for all the items that apply,
make notes next to any that haven't been addressed, and remove any items that are not relevant to this PR.
-->
- [ ] My pull request follows the guidelines in the [Contributing guide](https://github.com/specklesystems/speckle-server/blob/main/CONTRIBUTING.md)?
- [ ] My pull request does not duplicate any other open [Pull Requests](../../pulls) for the same update/change?
- [ ] My commits are related to the pull request and do not amend unrelated code or documentation.
- [ ] My code follows a similar style to existing code.
- [ ] I have added appropriate tests.
- [ ] I have updated or added relevant documentation.
## References
<!---
(Optional -- remove this section if not needed )
Include **important** links regarding the implementation of this PR.
This usually includes a RFC or an aggregation of issues and/or individual conversations
that helped put this solution together. This helps ensure we retain and share knowledge
regarding the implementation, and may help others understand motivation and design decisions etc..
-->
@@ -1,7 +1,9 @@
name: .NET Build
on:
pull_request
pull_request: # Run build on every pull request that is not to main
branches-ignore:
- main
jobs:
build:
@@ -32,8 +34,6 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup .NET
uses: actions/setup-dotnet@v4
@@ -46,10 +46,7 @@ jobs:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
- name: ⚒️ Run Build on Linux
run: ./build.sh build-linux
- name: ⚒️ Run tests
- name: ⚒️ Run build
run: ./build.sh test-only
- name: Upload coverage reports to Codecov with GitHub Action
@@ -2,14 +2,12 @@ name: .NET Build and Publish
on:
push:
branches: ["main", "installer-test/**"]
branches: ["main", "dev", "release/*"] # Continuous delivery on every long-lived branch
tags: ["v3.*"] # Manual delivery on every 3.x tag
jobs:
build-windows:
build:
runs-on: windows-latest
env:
SPECKLE_VERSION: "unset"
outputs:
version: ${{ steps.set-version.outputs.version }}
steps:
@@ -29,35 +27,38 @@ jobs:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
- name: ⚒️ Run build on Windows
run: ./build.ps1 zip
- name: ⚒️ Run GitVersion
run: ./build.ps1 build-server-version
- name: ⚒️ Run build
run: ./build.ps1
- name: ⬆️ Upload artifacts
uses: actions/upload-artifact@v4
with:
name: output-${{ env.SPECKLE_VERSION }}
name: output-${{ env.GitVersion_FullSemVer }}
path: output/*.*
if-no-files-found: error
retention-days: 1
compression-level: 0 # no compression
compression-level: 0 # no compression
- id: set-version
name: Set version to output
run: echo "version=${{ env.SPECKLE_VERSION }}" >> "$Env:GITHUB_OUTPUT"
run: echo "version=${{ env.GitVersion_FullSemVer }}" >> "$Env:GITHUB_OUTPUT"
deploy-installers:
runs-on: ubuntu-latest
needs: build-windows
needs: build
env:
IS_PUBLIC_RELEASE: ${{ github.ref_type == 'tag' }}
IS_TEST_INSTALLER: ${{ 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: 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 }}", "version": "${{ needs.build-windows.outputs.version }}", "public_release": ${{ env.IS_PUBLIC_RELEASE }}, "store_artifacts": ${{ env.IS_TEST_INSTALLER }} }'
inputs: '{ "run_id": "${{ github.run_id }}", "version": "${{ needs.build.outputs.version }}", "public_release": ${{ env.IS_TAG_BUILD }}, "store_artifacts": ${{ env.IS_RELEASE_BRANCH }} }'
ref: main
wait-for-completion: true
wait-for-completion-interval: 10s
@@ -69,13 +70,11 @@ jobs:
with:
name: output-*
build-linux:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup .NET
uses: actions/setup-dotnet@v4
@@ -88,18 +87,11 @@ jobs:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
- name: ⚒️ Run tests on Linux
- name: ⚒️ Run build
run: ./build.sh test-only
- name: ⚒️ Run Build and Pack on Linux
run: ./build.sh build-linux
- 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 }}
- 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
-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;
}
}
-1
View File
@@ -10,7 +10,6 @@
<PackageReference Include="Bullseye" />
<PackageReference Include="Glob" />
<PackageReference Include="Microsoft.Build" />
<PackageReference Include="Semver" />
<PackageReference Include="SimpleExec" />
</ItemGroup>
</Project>
+4 -12
View File
@@ -4,7 +4,7 @@ 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(
@@ -49,28 +49,20 @@ 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(
"etabs",
[
new("Connectors/CSi/Speckle.Connectors.ETABS21", "net48"),
new("Connectors/CSi/Speckle.Connectors.ETABS22", "net8.0-windows"),
]
)
};
}
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}";
}
+2 -2
View File
@@ -28,11 +28,11 @@ public static class Github
Content = content
};
request.Headers.Add("X-GitHub-Api-Version", "2022-11-28");
var response = await client.SendAsync(request);
var response = await client.SendAsync(request).ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
{
throw new InvalidOperationException(
$"{response.StatusCode} {response.ReasonPhrase} {await response.Content.ReadAsStringAsync()}"
$"{response.StatusCode} {response.ReasonPhrase} {await response.Content.ReadAsStringAsync().ConfigureAwait(false)}"
);
}
}
+93 -150
View File
@@ -7,17 +7,16 @@ using static SimpleExec.Command;
const string CLEAN = "clean";
const string RESTORE = "restore";
const string BUILD = "build";
const string BUILD_LINUX = "build-linux";
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 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>();
@@ -27,59 +26,18 @@ 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");
}
void DeleteFiles(string pattern)
{
foreach (var f in Glob.Files(".", pattern))
{
Console.WriteLine("Found and will delete: " + f);
File.Delete(f);
}
}
void DeleteDirectories(string pattern)
{
foreach (var f in Glob.Directories(".", pattern))
{
if (f.StartsWith("Build"))
{
continue;
}
Console.WriteLine("Found and will delete: " + f);
Directory.Delete(f, true);
}
}
void CleanSolution(string solution, string configuration)
{
Console.WriteLine("Cleaning solution: " + solution);
DeleteDirectories("**/bin");
DeleteDirectories("**/obj");
DeleteFiles("**/*.lock.json");
Restore(solution);
Build(solution, configuration);
}
Target(
CLEAN_LOCKS,
() =>
{
DeleteFiles("**/*.lock.json");
Restore("Speckle.Connectors.sln");
foreach (var f in Glob.Files(".", "**/*.lock.json"))
{
Console.WriteLine("Found and will delete: " + f);
File.Delete(f);
}
Console.WriteLine("Running restore now.");
Run("dotnet", "restore .\\Speckle.Connectors.sln --no-cache");
}
);
@@ -87,14 +45,26 @@ Target(
DEEP_CLEAN,
() =>
{
CleanSolution("Speckle.Connectors.sln", "debug");
}
);
Target(
DEEP_CLEAN_LOCAL,
() =>
{
CleanSolution("Local.sln", "local");
foreach (var f in Glob.Directories(".", "**/bin"))
{
if (f.StartsWith("Build"))
{
continue;
}
Console.WriteLine("Found and will delete: " + f);
Directory.Delete(f, true);
}
foreach (var f in Glob.Directories(".", "**/obj"))
{
if (f.StartsWith("Build"))
{
continue;
}
Console.WriteLine("Found and will delete: " + f);
Directory.Delete(f, true);
}
Console.WriteLine("Running restore now.");
Run("dotnet", "restore .\\Speckle.Connectors.sln --no-cache");
}
);
@@ -125,22 +95,21 @@ Target(
);
Target(
RESTORE_TOOLS,
() =>
VERSION,
async () =>
{
Run("dotnet", "tool restore");
var (output, _) = await ReadAsync("dotnet", "minver -v w").ConfigureAwait(false);
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");
}
);
@@ -155,14 +124,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");
}
);
@@ -170,14 +145,14 @@ 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"
);
}
);
@@ -187,48 +162,23 @@ Target(CHECK_SOLUTIONS, Solutions.CompareConnectorsToLocal);
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", $"restore {file} --locked-mode");
Run(
"dotnet",
$"test \"{file}\" -c Release --no-build --verbosity=minimal /p:AltCover=true /p:AltCoverAttributeFilter=ExcludeFromCodeCoverage /p:AltCoverVerbosity=Warning"
);
}
);
Target(
BUILD_LINUX,
DependsOn(FORMAT),
Glob.Files(".", "**/Speckle.Importers.Ifc.csproj"),
async file =>
{
await RunAsync("dotnet", $"restore \"{file}\" --locked-mode");
var version = await Versions.ComputeVersion();
var fileVersion = await Versions.ComputeFileVersion();
Console.WriteLine($"Version: {version} & {fileVersion}");
await RunAsync(
"dotnet",
$"build \"{file}\" -c Release --no-restore -warnaserror -p:Version={version} -p:FileVersion={fileVersion} -v:m"
);
await RunAsync(
"dotnet",
$"pack \"{file}\" -c Release -o output --no-build -p:Version={version} -p:FileVersion={fileVersion} -v:m"
$"test {file} -c Release --no-restore --verbosity=minimal /p:AltCover=true /p:AltCoverAttributeFilter=ExcludeFromCodeCoverage /p:AltCoverVerbosity=Warning"
);
}
);
@@ -236,54 +186,47 @@ Target(
Target(
ZIP,
DependsOn(TEST),
async () =>
Consts.InstallerManifests,
x =>
{
var version = await Versions.ComputeVersion();
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, $"SPECKLE_VERSION={version}{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);
-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;
}
}
-14
View File
@@ -53,15 +53,6 @@
"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, )",
@@ -84,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",
@@ -19,7 +19,7 @@ namespace Speckle.Connectors.ArcGIS.Bindings;
public sealed class ArcGISReceiveBinding : IReceiveBinding
{
public string Name { get; } = "receiveBinding";
private readonly ICancellationManager _cancellationManager;
private readonly CancellationManager _cancellationManager;
private readonly DocumentModelStore _store;
private readonly IServiceProvider _serviceProvider;
private readonly IOperationProgressManager _operationProgressManager;
@@ -32,7 +32,7 @@ public sealed class ArcGISReceiveBinding : IReceiveBinding
public ArcGISReceiveBinding(
DocumentModelStore store,
IBrowserBridge parent,
ICancellationManager cancellationManager,
CancellationManager cancellationManager,
IServiceProvider serviceProvider,
IOperationProgressManager operationProgressManager,
ILogger<ArcGISReceiveBinding> logger,
@@ -60,7 +60,7 @@ public sealed class ArcGISReceiveBinding : IReceiveBinding
throw new InvalidOperationException("No download model card was found.");
}
using var cancellationItem = _cancellationManager.GetCancellationItem(modelCardId);
CancellationToken cancellationToken = _cancellationManager.InitCancellationTokenSource(modelCardId);
using var scope = _serviceProvider.CreateScope();
scope
.ServiceProvider.GetRequiredService<IConverterSettingsStore<ArcGISConversionSettings>>()
@@ -76,16 +76,19 @@ public sealed class ArcGISReceiveBinding : IReceiveBinding
.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
);
_operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationToken),
cancellationToken
)
.ConfigureAwait(false);
modelCard.BakedObjectIds = receiveOperationResults.BakedObjectIds.ToList();
await Commands.SetModelReceiveResult(
modelCardId,
receiveOperationResults.BakedObjectIds,
receiveOperationResults.ConversionResults
);
await Commands
.SetModelReceiveResult(
modelCardId,
receiveOperationResults.BakedObjectIds,
receiveOperationResults.ConversionResults
)
.ConfigureAwait(false);
}
catch (OperationCanceledException)
{
@@ -97,7 +100,7 @@ public sealed class ArcGISReceiveBinding : IReceiveBinding
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);
await Commands.SetModelError(modelCardId, ex).ConfigureAwait(false);
}
}
@@ -12,18 +12,15 @@ public class ArcGISSelectionBinding : ISelectionBinding
public string Name => "selectionBinding";
public IBrowserBridge Parent { get; }
public ArcGISSelectionBinding(
IBrowserBridge parent,
MapMembersUtils mapMemberUtils,
ITopLevelExceptionHandler topLevelExceptionHandler
)
public ArcGISSelectionBinding(IBrowserBridge parent, MapMembersUtils mapMemberUtils)
{
_mapMemberUtils = mapMemberUtils;
Parent = parent;
var topLevelHandler = parent.TopLevelExceptionHandler;
// 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);
TOCSelectionChangedEvent.Subscribe(_ => topLevelHandler.CatchUnhandled(OnSelectionChanged), true);
}
private void OnSelectionChanged()
@@ -13,7 +13,6 @@ 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.Exceptions;
@@ -39,14 +38,12 @@ public sealed class ArcGISSendBinding : ISendBinding
private readonly DocumentModelStore _store;
private readonly IServiceProvider _serviceProvider;
private readonly List<ISendFilter> _sendFilters;
private readonly ICancellationManager _cancellationManager;
private readonly CancellationManager _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;
private readonly ISpeckleApplication _speckleApplication;
/// <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:
@@ -65,15 +62,12 @@ public sealed class ArcGISSendBinding : ISendBinding
IBrowserBridge parent,
IEnumerable<ISendFilter> sendFilters,
IServiceProvider serviceProvider,
ICancellationManager cancellationManager,
CancellationManager cancellationManager,
ISendConversionCache sendConversionCache,
IOperationProgressManager operationProgressManager,
ILogger<ArcGISSendBinding> logger,
IArcGISConversionSettingsFactory arcGisConversionSettingsFactory,
MapMembersUtils mapMemberUtils,
IThreadContext threadContext,
ISpeckleApplication speckleApplication,
ITopLevelExceptionHandler topLevelExceptionHandler
MapMembersUtils mapMemberUtils
)
{
_store = store;
@@ -83,11 +77,9 @@ public sealed class ArcGISSendBinding : ISendBinding
_sendConversionCache = sendConversionCache;
_operationProgressManager = operationProgressManager;
_logger = logger;
_topLevelExceptionHandler = topLevelExceptionHandler;
_topLevelExceptionHandler = parent.TopLevelExceptionHandler;
_arcGISConversionSettingsFactory = arcGisConversionSettingsFactory;
_mapMemberUtils = mapMemberUtils;
_threadContext = threadContext;
_speckleApplication = speckleApplication;
Parent = parent;
Commands = new SendBindingUICommands(parent);
@@ -98,22 +90,18 @@ public sealed class ArcGISSendBinding : ISendBinding
};
}
private void OnDocumentStoreChangedEvent(object _) => _sendConversionCache.ClearCache();
private void SubscribeToArcGISEvents()
{
LayersRemovedEvent.Subscribe(
a =>
_topLevelExceptionHandler.FireAndForget(
async () => await QueuedTask.Run(async () => await GetIdsForLayersRemovedEvent(a))
),
_topLevelExceptionHandler.FireAndForget(async () => await GetIdsForLayersRemovedEvent(a).ConfigureAwait(false)),
true
);
StandaloneTablesRemovedEvent.Subscribe(
a =>
_topLevelExceptionHandler.FireAndForget(
async () => await QueuedTask.Run(async () => await GetIdsForStandaloneTablesRemovedEvent(a))
async () => await GetIdsForStandaloneTablesRemovedEvent(a).ConfigureAwait(false)
),
true
);
@@ -121,7 +109,7 @@ public sealed class ArcGISSendBinding : ISendBinding
MapPropertyChangedEvent.Subscribe(
a =>
_topLevelExceptionHandler.FireAndForget(
async () => await QueuedTask.Run(async () => await GetIdsForMapPropertyChangedEvent(a))
async () => await GetIdsForMapPropertyChangedEvent(a).ConfigureAwait(false)
),
true
); // Map units, CRS etc.
@@ -129,17 +117,13 @@ public sealed class ArcGISSendBinding : ISendBinding
MapMemberPropertiesChangedEvent.Subscribe(
a =>
_topLevelExceptionHandler.FireAndForget(
async () => await QueuedTask.Run(async () => await GetIdsForMapMemberPropertiesChangedEvent(a))
async () => await GetIdsForMapMemberPropertiesChangedEvent(a).ConfigureAwait(false)
),
true
); // e.g. Layer name
ActiveMapViewChangedEvent.Subscribe(
_ =>
_topLevelExceptionHandler.FireAndForget(async () =>
{
await QueuedTask.Run(SubscribeToMapMembersDataSourceChange);
}),
_ => _topLevelExceptionHandler.CatchUnhandled(SubscribeToMapMembersDataSourceChange),
true
);
@@ -155,24 +139,28 @@ public sealed class ArcGISSendBinding : ISendBinding
private void SubscribeToMapMembersDataSourceChange()
{
if (MapView.Active == null)
var task = QueuedTask.Run(() =>
{
return;
}
// subscribe to layers
foreach (Layer layer in MapView.Active.Map.Layers)
{
if (layer is FeatureLayer featureLayer)
if (MapView.Active == null)
{
SubscribeToFeatureLayerDataSourceChange(featureLayer);
return;
}
}
// subscribe to tables
foreach (StandaloneTable table in MapView.Active.Map.StandaloneTables)
{
SubscribeToTableDataSourceChange(table);
}
// subscribe to layers
foreach (Layer layer in MapView.Active.Map.Layers)
{
if (layer is FeatureLayer featureLayer)
{
SubscribeToFeatureLayerDataSourceChange(featureLayer);
}
}
// subscribe to tables
foreach (StandaloneTable table in MapView.Active.Map.StandaloneTables)
{
SubscribeToTableDataSourceChange(table);
}
});
task.Wait();
}
private void SubscribeToFeatureLayerDataSourceChange(FeatureLayer layer)
@@ -207,25 +195,25 @@ public sealed class ArcGISSendBinding : ISendBinding
{
RowCreatedEvent.Subscribe(
(args) =>
_topLevelExceptionHandler.FireAndForget(async () =>
Parent.TopLevelExceptionHandler.FireAndForget(async () =>
{
await OnRowChanged(args);
await OnRowChanged(args).ConfigureAwait(false);
}),
layerTable
);
RowChangedEvent.Subscribe(
(args) =>
_topLevelExceptionHandler.FireAndForget(async () =>
Parent.TopLevelExceptionHandler.FireAndForget(async () =>
{
await OnRowChanged(args);
await OnRowChanged(args).ConfigureAwait(false);
}),
layerTable
);
RowDeletedEvent.Subscribe(
(args) =>
_topLevelExceptionHandler.FireAndForget(async () =>
Parent.TopLevelExceptionHandler.FireAndForget(async () =>
{
await OnRowChanged(args);
await OnRowChanged(args).ConfigureAwait(false);
}),
layerTable
);
@@ -270,7 +258,7 @@ public sealed class ArcGISSendBinding : ISendBinding
}
}
await RunExpirationChecks(false);
await RunExpirationChecks(false).ConfigureAwait(false);
}
private async Task GetIdsForLayersRemovedEvent(LayerEventsArgs args)
@@ -279,7 +267,7 @@ public sealed class ArcGISSendBinding : ISendBinding
{
ChangedObjectIds[layer.URI] = 1;
}
await RunExpirationChecks(true);
await RunExpirationChecks(true).ConfigureAwait(false);
}
private async Task GetIdsForStandaloneTablesRemovedEvent(StandaloneTableEventArgs args)
@@ -288,7 +276,7 @@ public sealed class ArcGISSendBinding : ISendBinding
{
ChangedObjectIds[table.URI] = 1;
}
await RunExpirationChecks(true);
await RunExpirationChecks(true).ConfigureAwait(false);
}
private async Task GetIdsForMapPropertyChangedEvent(MapPropertyChangedEventArgs args)
@@ -301,7 +289,7 @@ public sealed class ArcGISSendBinding : ISendBinding
ChangedObjectIds[member.URI] = 1;
}
}
await RunExpirationChecks(false);
await RunExpirationChecks(false).ConfigureAwait(false);
}
private void GetIdsForLayersAddedEvent(LayerEventsArgs args)
@@ -351,7 +339,7 @@ public sealed class ArcGISSendBinding : ISendBinding
{
ChangedObjectIds[member.URI] = 1;
}
await RunExpirationChecks(false);
await RunExpirationChecks(false).ConfigureAwait(false);
}
}
@@ -376,61 +364,66 @@ public sealed class ArcGISSendBinding : ISendBinding
throw new InvalidOperationException("No publish model card was found.");
}
using var cancellationItem = _cancellationManager.GetCancellationItem(modelCardId);
CancellationToken cancellationToken = _cancellationManager.InitCancellationTokenSource(modelCardId);
using var scope = _serviceProvider.CreateScope();
List<MapMember> mapMembers = await QueuedTask.Run(() =>
{
scope
.ServiceProvider.GetRequiredService<IConverterSettingsStore<ArcGISConversionSettings>>()
.Initialize(
_arcGISConversionSettingsFactory.Create(
Project.Current,
MapView.Active.Map,
new CRSoffsetRotation(MapView.Active.Map)
)
);
return 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!");
}
await QueuedTask.Run(() =>
{
// subscribe to the selected layer events
foreach (MapMember mapMember in mapMembers)
var sendResult = await QueuedTask
.Run(async () =>
{
if (mapMember is FeatureLayer featureLayer)
{
SubscribeToFeatureLayerDataSourceChange(featureLayer);
}
else if (mapMember is StandaloneTable table)
{
SubscribeToTableDataSourceChange(table);
}
}
});
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();
var sendResult = await scope
.ServiceProvider.GetRequiredService<SendOperation<MapMember>>()
.Execute(
mapMembers,
modelCard.GetSendInfo(_speckleApplication.ApplicationAndVersion),
_operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationItem.Token),
cancellationItem.Token
);
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!"
);
}
await Commands.SetModelSendResult(modelCardId, sendResult.RootObjId, sendResult.ConversionResults);
// 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 result = 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, cancellationToken),
cancellationToken
)
.ConfigureAwait(false);
return result;
})
.ConfigureAwait(false);
await Commands
.SetModelSendResult(modelCardId, sendResult.RootObjId, sendResult.ConversionResults)
.ConfigureAwait(false);
}
catch (OperationCanceledException)
{
@@ -442,7 +435,7 @@ public sealed class ArcGISSendBinding : ISendBinding
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);
await Commands.SetModelError(modelCardId, ex).ConfigureAwait(false);
}
}
@@ -477,7 +470,7 @@ public sealed class ArcGISSendBinding : ISendBinding
}
}
await Commands.SetModelsExpired(expiredSenderIds);
await Commands.SetModelsExpired(expiredSenderIds).ConfigureAwait(false);
ChangedObjectIds = new();
}
}
@@ -21,25 +21,18 @@ public class BasicConnectorBinding : IBasicConnectorBinding
public BasicConnectorBindingCommands Commands { get; }
private readonly DocumentModelStore _store;
private readonly ISpeckleApplication _speckleApplication;
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
public BasicConnectorBinding(
DocumentModelStore store,
IBrowserBridge parent,
ISpeckleApplication speckleApplication,
ITopLevelExceptionHandler topLevelExceptionHandler
)
public BasicConnectorBinding(DocumentModelStore store, IBrowserBridge parent, ISpeckleApplication speckleApplication)
{
_store = store;
_speckleApplication = speckleApplication;
_topLevelExceptionHandler = topLevelExceptionHandler;
Parent = parent;
Commands = new BasicConnectorBindingCommands(parent);
_store.DocumentChanged += (_, _) =>
_topLevelExceptionHandler.FireAndForget(async () =>
parent.TopLevelExceptionHandler.FireAndForget(async () =>
{
await Commands.NotifyDocumentChanged();
await Commands.NotifyDocumentChanged().ConfigureAwait(false);
});
}
@@ -67,12 +60,8 @@ public class BasicConnectorBinding : 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)
{
await HighlightObjectsOnView(objectIds.Select(x => new ObjectID(x)).ToList());
}
public async Task HighlightObjects(IReadOnlyList<string> objectIds) =>
await HighlightObjectsOnView(objectIds.Select(x => new ObjectID(x)).ToList()).ConfigureAwait(false);
public async Task HighlightModel(string modelCardId)
{
@@ -99,22 +88,24 @@ public class BasicConnectorBinding : IBasicConnectorBinding
{
return;
}
await HighlightObjectsOnView(objectIds);
await HighlightObjectsOnView(objectIds).ConfigureAwait(false);
}
private async Task HighlightObjectsOnView(IReadOnlyList<ObjectID> objectIds)
{
await QueuedTask.Run(() =>
{
MapView mapView = MapView.Active;
MapView mapView = MapView.Active;
List<MapMemberFeature> mapMembersFeatures = GetMapMembers(objectIds, mapView);
ClearSelectionInTOC();
ClearSelection();
SelectMapMembersInTOC(mapMembersFeatures);
SelectMapMembersAndFeatures(mapMembersFeatures);
mapView.ZoomToSelected();
});
await QueuedTask
.Run(async () =>
{
List<MapMemberFeature> mapMembersFeatures = GetMapMembers(objectIds, mapView);
ClearSelectionInTOC();
ClearSelection();
await SelectMapMembersInTOC(mapMembersFeatures).ConfigureAwait(false);
SelectMapMembersAndFeatures(mapMembersFeatures);
mapView.ZoomToSelected();
})
.ConfigureAwait(false);
}
private List<MapMemberFeature> GetMapMembers(IReadOnlyList<ObjectID> objectIds, MapView mapView)
@@ -180,7 +171,7 @@ public class BasicConnectorBinding : IBasicConnectorBinding
}
}
private void SelectMapMembersInTOC(IReadOnlyList<MapMemberFeature> mapMembersFeatures)
private async Task SelectMapMembersInTOC(IReadOnlyList<MapMemberFeature> mapMembersFeatures)
{
List<Layer> layers = new();
List<StandaloneTable> tables = new();
@@ -196,7 +187,7 @@ public class BasicConnectorBinding : IBasicConnectorBinding
}
else
{
layer.SetExpanded(true);
await QueuedTask.Run(() => layer.SetExpanded(true)).ConfigureAwait(false);
}
}
else if (member is StandaloneTable table)
@@ -20,7 +20,7 @@
<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>
<Description>Speckle connector for ArcGIS</Description>
<Image>Images\AddinDesktop32.png</Image>
<Author>Speckle Systems</Author>
<Company>Speckle Systems</Company>
@@ -33,14 +33,14 @@
<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">
<!--<tab id="Speckle_Tab1" caption="New Tab">
<group refID="Speckle_Group1"/>
</tab>
</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">
an empty group-->
<group id="Speckle_Group1" caption="Speckle" appearsOnAddInTab="true" keytip="G1">
<!-- host controls within groups -->
<button refID="SpeckleDUI3_SpeckleDUI3OpenButton" size="large" />
</group>
@@ -59,7 +59,7 @@
</controls>
<dockPanes>
<dockPane id="SpeckleDUI3_SpeckleDUI3" caption="Speckle (Beta)" className="SpeckleDUI3ViewModel" keytip="DockPane" initiallyVisible="true" dock="group" dockWith="esri_core_projectDockPane">
<dockPane id="SpeckleDUI3_SpeckleDUI3" caption="Speckle (Beta) for ArcGIS" className="SpeckleDUI3ViewModel" keytip="DockPane" initiallyVisible="true" dock="group" dockWith="esri_core_projectDockPane">
<content className="SpeckleDUI3Wrapper" />
</dockPane>
</dockPanes>
@@ -10,7 +10,6 @@ using Speckle.Connectors.Common;
using Speckle.Connectors.Common.Builders;
using Speckle.Connectors.Common.Caching;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.DUI;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Models.Card.SendFilter;
@@ -28,7 +27,7 @@ public static class ArcGISConnectorModule
public static void AddArcGIS(this IServiceCollection serviceCollection)
{
serviceCollection.AddConnectorUtils();
serviceCollection.AddDUI<DefaultThreadContext, ArcGISDocumentStore>();
serviceCollection.AddDUI<ArcGISDocumentStore>();
serviceCollection.AddDUIView();
// Register bindings
@@ -38,6 +37,7 @@ public static class ArcGISConnectorModule
serviceCollection.AddSingleton<IBinding>(sp => sp.GetRequiredService<IBasicConnectorBinding>());
serviceCollection.AddSingleton<IBasicConnectorBinding, BasicConnectorBinding>();
serviceCollection.RegisterTopLevelExceptionHandler();
serviceCollection.AddSingleton(DefaultTraversal.CreateTraversalFunc());
// register send operation and dependencies
@@ -25,7 +25,7 @@ public class ArcGISColorManager
/// </summary>
/// <param name="colorProxies"></param>
/// <param name="onOperationProgressed"></param>
public void ParseColors(List<ColorProxy> colorProxies, IProgress<CardProgress> onOperationProgressed)
public async Task ParseColors(List<ColorProxy> colorProxies, IProgress<CardProgress> onOperationProgressed)
{
// injected as Singleton, so we need to clean existing proxies first
ObjectColorsIdMap = new();
@@ -33,6 +33,7 @@ public class ArcGISColorManager
foreach (ColorProxy colorProxy in colorProxies)
{
onOperationProgressed.Report(new("Converting colors", (double)++count / colorProxies.Count));
await Task.Yield();
foreach (string objectId in colorProxy.objects)
{
Color convertedColor = Color.FromArgb(colorProxy.value);
@@ -46,7 +47,10 @@ public class ArcGISColorManager
/// </summary>
/// <param name="materialProxies"></param>
/// <param name="onOperationProgressed"></param>
public void ParseMaterials(List<RenderMaterialProxy> materialProxies, IProgress<CardProgress> onOperationProgressed)
public async Task ParseMaterials(
List<RenderMaterialProxy> materialProxies,
IProgress<CardProgress> onOperationProgressed
)
{
// injected as Singleton, so we need to clean existing proxies first
ObjectMaterialsIdMap = new();
@@ -54,6 +58,7 @@ public class ArcGISColorManager
foreach (RenderMaterialProxy colorProxy in materialProxies)
{
onOperationProgressed.Report(new("Converting materials", (double)++count / materialProxies.Count));
await Task.Yield();
foreach (string objectId in colorProxy.objects)
{
Color convertedColor = Color.FromArgb(colorProxy.value.diffuse);
@@ -184,7 +184,6 @@ public class ArcGISColorUnpacker
if (StoredColor is int existingColorProxyId)
{
AddObjectIdToColorProxyCache(rowApplicationId, existingColorProxyId);
return;
}
// get the color from the renderer and row
@@ -18,7 +18,7 @@ public class ArcGISLayerUnpacker
/// <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(
public async Task<List<ADM.MapMember>> UnpackSelectionAsync(
IEnumerable<ADM.MapMember> mapMembers,
Collection parentCollection,
List<ADM.MapMember>? objects = null
@@ -37,7 +37,7 @@ public class ArcGISLayerUnpacker
Collection containerCollection = CreateAndCacheMapMemberCollection(mapMember, true);
parentCollection.elements.Add(containerCollection);
UnpackSelection(container.Layers, containerCollection, objects);
await UnpackSelectionAsync(container.Layers, containerCollection, objects).ConfigureAwait(false);
break;
default:
@@ -54,20 +54,7 @@ public class ArcGISHostObjectBuilder : IHostObjectBuilder
_colorManager = colorManager;
}
public Task<HostObjectBuilderResult> Build(
Base rootObject,
string projectName,
string modelName,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
{
return QueuedTask.Run(
() => BuildInternal(rootObject, projectName, modelName, onOperationProgressed, cancellationToken)
);
}
private HostObjectBuilderResult BuildInternal(
public async Task<HostObjectBuilderResult> Build(
Base rootObject,
string projectName,
string modelName,
@@ -87,14 +74,14 @@ public class ArcGISHostObjectBuilder : IHostObjectBuilder
.ToList();
if (materials != null)
{
_colorManager.ParseMaterials(materials, onOperationProgressed);
await _colorManager.ParseMaterials(materials, onOperationProgressed).ConfigureAwait(false);
}
// get colors
List<ColorProxy>? colors = (rootObject[ProxyKeys.COLOR] as List<object>)?.Cast<ColorProxy>().ToList();
if (colors != null)
{
_colorManager.ParseColors(colors, onOperationProgressed);
await _colorManager.ParseColors(colors, onOperationProgressed).ConfigureAwait(false);
}
int count = 0;
@@ -113,7 +100,7 @@ public class ArcGISHostObjectBuilder : IHostObjectBuilder
try
{
obj = _localToGlobalConverterUtils.TransformObjects(objectToConvert.AtomicObject, objectToConvert.Matrix);
object conversionResult = _converter.Convert(obj);
object? conversionResult = await QueuedTask.Run(() => _converter.Convert(obj)).ConfigureAwait(false);
string nestedLayerPath = $"{string.Join("\\", path)}";
@@ -141,20 +128,29 @@ public class ArcGISHostObjectBuilder : IHostObjectBuilder
// 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))
);
Dictionary<string, List<(TraversalContext, ObjectConversionTracker)>> convertedGroups = await QueuedTask
.Run(async () =>
{
return await _featureClassUtils
.GroupConversionTrackers(conversionTracker, (s, progres) => onOperationProgressed.Report(new(s, progres)))
.ConfigureAwait(false);
})
.ConfigureAwait(false);
// 2.2. Write groups of objects to Datasets
onOperationProgressed.Report(new("Writing to Database", null));
_featureClassUtils.CreateDatasets(
conversionTracker,
convertedGroups,
(s, progres) => onOperationProgressed.Report(new(s, progres))
);
await QueuedTask
.Run(async () =>
{
await _featureClassUtils
.CreateDatasets(
conversionTracker,
convertedGroups,
(s, progres) => onOperationProgressed.Report(new(s, progres))
)
.ConfigureAwait(false);
})
.ConfigureAwait(false);
// 3. add layer and tables to the Map and Table Of Content
@@ -206,7 +202,8 @@ public class ArcGISHostObjectBuilder : IHostObjectBuilder
else
{
// no layer yet, create and add layer to Map
MapMember mapMember = AddDatasetsToMap(trackerItem, createdLayerGroups, projectName, modelName);
MapMember mapMember = await AddDatasetsToMap(trackerItem, createdLayerGroups, projectName, modelName)
.ConfigureAwait(false);
// add layer and layer URI to tracker
trackerItem.AddConvertedMapMember(mapMember);
@@ -234,13 +231,13 @@ public class ArcGISHostObjectBuilder : IHostObjectBuilder
if (bakedMember.Value.Item1 is FeatureLayer fLayer)
{
// Set the feature layer's renderer.
fLayer.SetRenderer(bakedMember.Value.Item2);
await QueuedTask.Run(() => fLayer.SetRenderer(bakedMember.Value.Item2)).ConfigureAwait(false);
}
}
bakedObjectIds.AddRange(createdLayerGroups.Values.Select(x => x.URI));
// TODO: validated a correct set regarding bakedobject ids
return new HostObjectBuilderResult(bakedObjectIds, results);
return new(bakedObjectIds, results);
}
private IReadOnlyCollection<LocalToGlobalMap> GetObjectsToConvert(Base rootObject)
@@ -301,72 +298,80 @@ public class ArcGISHostObjectBuilder : IHostObjectBuilder
}
}
private MapMember AddDatasetsToMap(
private async Task<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)
return await QueuedTask
.Run(() =>
{
throw new SpeckleException($"Layer '{shortName}' was not created");
}
layer.SetExpanded(false);
// 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;
// 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, };
// add group for the current layer
string shortName = nestedLayerName.Split("\\")[^1];
string nestedLayerPath = string.Join("\\", nestedLayerName.Split("\\").SkipLast(1));
// for Feature Layers
if (layer.GetDefinition() is CIMFeatureLayer cimLyr)
// if no general group layer found
if (createdLayerGroups.Count == 0)
{
cimLyr.LayerElevation = layerElevationSurface;
layer.SetDefinition(cimLyr);
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
}
}
return layer;
}
catch (ArgumentException)
{
StandaloneTable table = StandaloneTableFactory.Instance.CreateStandaloneTable(
uri,
groupLayer,
tableName: shortName
);
return table;
}
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 (MapMember)layer;
}
catch (ArgumentException)
{
StandaloneTable table = StandaloneTableFactory.Instance.CreateStandaloneTable(
uri,
groupLayer,
tableName: shortName
);
return table;
}
})
.ConfigureAwait(false);
}
private GroupLayer CreateNestedGroupLayer(string nestedLayerPath, Dictionary<string, GroupLayer> createdLayerGroups)
@@ -6,6 +6,7 @@ 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.Caching;
using Speckle.Connectors.Common.Conversion;
using Speckle.Connectors.Common.Extensions;
using Speckle.Connectors.Common.Operations;
@@ -24,6 +25,7 @@ namespace Speckle.Connectors.ArcGis.Operations.Send;
public class ArcGISRootObjectBuilder : IRootObjectBuilder<ADM.MapMember>
{
private readonly IRootToSpeckleConverter _rootToSpeckleConverter;
private readonly ISendConversionCache _sendConversionCache;
private readonly ArcGISLayerUnpacker _layerUnpacker;
private readonly ArcGISColorUnpacker _colorUnpacker;
private readonly IConverterSettingsStore<ArcGISConversionSettings> _converterSettings;
@@ -32,6 +34,7 @@ public class ArcGISRootObjectBuilder : IRootObjectBuilder<ADM.MapMember>
private readonly MapMembersUtils _mapMemberUtils;
public ArcGISRootObjectBuilder(
ISendConversionCache sendConversionCache,
ArcGISLayerUnpacker layerUnpacker,
ArcGISColorUnpacker colorUnpacker,
IConverterSettingsStore<ArcGISConversionSettings> converterSettings,
@@ -41,6 +44,7 @@ public class ArcGISRootObjectBuilder : IRootObjectBuilder<ADM.MapMember>
MapMembersUtils mapMemberUtils
)
{
_sendConversionCache = sendConversionCache;
_layerUnpacker = layerUnpacker;
_colorUnpacker = colorUnpacker;
_converterSettings = converterSettings;
@@ -50,18 +54,11 @@ public class ArcGISRootObjectBuilder : IRootObjectBuilder<ADM.MapMember>
_mapMemberUtils = mapMemberUtils;
}
public Task<RootObjectBuilderResult> Build(
public async Task<RootObjectBuilderResult> Build(
IReadOnlyList<ADM.MapMember> layers,
SendInfo __,
SendInfo sendInfo,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
) => QueuedTask.Run(() => BuildInternal(layers, __, onOperationProgressed, cancellationToken));
private async Task<RootObjectBuilderResult> BuildInternal(
IReadOnlyList<ADM.MapMember> layers,
SendInfo __,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
CancellationToken ct = default
)
{
// TODO: add a warning if Geographic CRS is set
@@ -102,28 +99,23 @@ public class ArcGISRootObjectBuilder : IRootObjectBuilder<ADM.MapMember>
// 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;
Dictionary<ADM.MapMember, long> layersWithFeatureCount;
long allFeaturesCount;
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);
// count number of features to convert. Raster layers are counter as 1 feature for now (not ideal)
layersWithFeatureCount = CountAllFeaturesInLayers(unpackedLayers);
allFeaturesCount = layersWithFeatureCount.Values.Sum();
unpackedLayers = await QueuedTask
.Run(() => _layerUnpacker.UnpackSelectionAsync(layersOrdered, rootCollection))
.ConfigureAwait(false);
}
List<SendConversionResult> results = new(unpackedLayers.Count);
onOperationProgressed.Report(new("Converting", null));
using (var convertingActivity = _activityFactory.Start("Converting objects"))
{
long count = 0;
foreach (var (layer, layerFeatureCount) in layersWithFeatureCount)
int count = 0;
foreach (ADM.MapMember layer in unpackedLayers)
{
cancellationToken.ThrowIfCancellationRequested();
ct.ThrowIfCancellationRequested();
string layerApplicationId = layer.GetSpeckleApplicationId();
try
@@ -149,33 +141,21 @@ public class ArcGISRootObjectBuilder : IRootObjectBuilder<ADM.MapMember>
switch (layer)
{
case ADM.FeatureLayer featureLayer:
List<Base> convertedFeatureLayerObjects = ConvertFeatureLayerObjects(
featureLayer,
count,
allFeaturesCount,
onOperationProgressed,
cancellationToken
);
List<Base> convertedFeatureLayerObjects = await QueuedTask
.Run(() => ConvertFeatureLayerObjectsAsync(featureLayer))
.ConfigureAwait(false);
layerCollection.elements.AddRange(convertedFeatureLayerObjects);
break;
case ADM.RasterLayer rasterLayer:
List<Base> convertedRasterLayerObjects = ConvertRasterLayerObjects(
rasterLayer,
count,
allFeaturesCount,
onOperationProgressed,
cancellationToken
);
List<Base> convertedRasterLayerObjects = await QueuedTask
.Run(() => ConvertRasterLayerObjectsAsync(rasterLayer))
.ConfigureAwait(false);
layerCollection.elements.AddRange(convertedRasterLayerObjects);
break;
case ADM.LasDatasetLayer lasDatasetLayer:
List<Base> convertedLasDatasetObjects = ConvertLasDatasetLayerObjects(
lasDatasetLayer,
count,
allFeaturesCount,
onOperationProgressed,
cancellationToken
);
List<Base> convertedLasDatasetObjects = await QueuedTask
.Run(() => ConvertLasDatasetLayerObjectsAsync(lasDatasetLayer))
.ConfigureAwait(false);
layerCollection.elements.AddRange(convertedLasDatasetObjects);
break;
default:
@@ -183,8 +163,6 @@ public class ArcGISRootObjectBuilder : IRootObjectBuilder<ADM.MapMember>
sdkStatus = SdkActivityStatusCode.Error;
break;
}
count += layerFeatureCount;
results.Add(new(status, layerApplicationId, layer.GetType().Name, layerCollection));
convertingActivity?.SetStatus(sdkStatus);
}
@@ -201,7 +179,7 @@ public class ArcGISRootObjectBuilder : IRootObjectBuilder<ADM.MapMember>
convertingActivity?.RecordException(ex);
}
await Task.Yield();
onOperationProgressed.Report(new("Converting", (double)++count / layers.Count));
}
}
@@ -216,141 +194,96 @@ public class ArcGISRootObjectBuilder : IRootObjectBuilder<ADM.MapMember>
return new RootObjectBuilderResult(rootCollection, results);
}
private Dictionary<ADM.MapMember, long> CountAllFeaturesInLayers(List<ADM.MapMember> unpackedLayers)
{
Dictionary<ADM.MapMember, long> layersFeatureCount = new();
foreach (ADM.MapMember layer in unpackedLayers)
{
switch (layer)
{
case ADM.FeatureLayer featureLayer:
layersFeatureCount.Add(featureLayer, featureLayer.GetFeatureClass().GetCount());
break;
case ADM.RasterLayer rasterLayer:
// count Raster layer as 1 feature: not optimal but this is the approach for now
layersFeatureCount.Add(rasterLayer, 1);
break;
case ADM.LasDatasetLayer lasDatasetLayer:
var dataset = lasDatasetLayer.GetLasDataset();
// simple dataset.GetPointCount() keeps returning null, so switched to EstimatePointCount
layersFeatureCount.Add(
lasDatasetLayer,
(long)dataset.EstimatePointCount(dataset.GetDefinition().GetExtent())
);
break;
}
}
return layersFeatureCount;
}
private List<Base> ConvertFeatureLayerObjects(
ADM.FeatureLayer featureLayer,
long count,
long allFeaturesCount,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
private async Task<List<Base>> ConvertFeatureLayerObjectsAsync(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())
await QueuedTask
.Run(() =>
{
// allow cancellation before every feature
cancellationToken.ThrowIfCancellationRequested();
// store the layer renderer for color unpacking
_colorUnpacker.StoreRendererAndFields(featureLayer);
// 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)
// 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())
{
// get application id. test for subtypes before defaulting to base type.
Base converted = _rootToSpeckleConverter.Convert(row);
string applicationId = row.GetSpeckleApplicationId(layerApplicationId);
converted.applicationId = applicationId;
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);
convertedObjects.Add(converted);
// process the object color
_colorUnpacker.ProcessFeatureLayerColor(row, applicationId);
// process the object color
_colorUnpacker.ProcessFeatureLayerColor(row, applicationId);
}
}
}
// update report
onOperationProgressed.Report(new("Converting", (double)++count / allFeaturesCount));
}
}
})
.ConfigureAwait(false);
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,
long count,
long allFeaturesCount,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
private async Task<List<Base>> ConvertRasterLayerObjectsAsync(ADM.RasterLayer rasterLayer)
{
string layerApplicationId = rasterLayer.GetSpeckleApplicationId();
List<Base> convertedObjects = new();
Raster raster = rasterLayer.GetRaster();
// check cancellation token before conversion
cancellationToken.ThrowIfCancellationRequested();
Base converted = _rootToSpeckleConverter.Convert(raster);
string applicationId = raster.GetSpeckleApplicationId(layerApplicationId);
converted.applicationId = applicationId;
convertedObjects.Add(converted);
// update report
onOperationProgressed.Report(new("Converting", (double)++count / allFeaturesCount));
await QueuedTask
.Run(() =>
{
Raster raster = rasterLayer.GetRaster();
Base converted = _rootToSpeckleConverter.Convert(raster);
string applicationId = raster.GetSpeckleApplicationId(layerApplicationId);
converted.applicationId = applicationId;
convertedObjects.Add(converted);
})
.ConfigureAwait(false);
return convertedObjects;
}
private List<Base> ConvertLasDatasetLayerObjects(
ADM.LasDatasetLayer lasDatasetLayer,
long count,
long allFeaturesCount,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
private async Task<List<Base>> ConvertLasDatasetLayerObjectsAsync(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())
await QueuedTask
.Run(() =>
{
// allow cancellation before every point
cancellationToken.ThrowIfCancellationRequested();
// store the layer renderer for color unpacking
_colorUnpacker.StoreRenderer(lasDatasetLayer);
using (ACD.Analyst3D.LasPoint pt = ptCursor.Current)
using (
ACD.Analyst3D.LasPointCursor ptCursor = lasDatasetLayer.SearchPoints(new ACD.Analyst3D.LasPointFilter())
)
{
Base converted = _rootToSpeckleConverter.Convert(pt);
string applicationId = pt.GetSpeckleApplicationId(layerApplicationId);
converted.applicationId = applicationId;
convertedObjects.Add(converted);
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);
// process the object color
_colorUnpacker.ProcessLasLayerColor(pt, applicationId);
}
}
}
// update report
onOperationProgressed.Report(new("Converting", (double)++count / allFeaturesCount));
}
}
})
.ConfigureAwait(false);
}
catch (ACD.Exceptions.TinException ex)
{
@@ -18,7 +18,7 @@ internal sealed class SpeckleDUI3ViewModel : DockPane
/// </summary>
protected override async Task InitializeAsync()
{
await base.InitializeAsync();
await base.InitializeAsync().ConfigureAwait(false);
}
/// <summary>
@@ -26,7 +26,7 @@ internal sealed class SpeckleDUI3ViewModel : DockPane
/// </summary>
protected override async Task UninitializeAsync()
{
await base.UninitializeAsync();
await base.UninitializeAsync().ConfigureAwait(false);
}
}
@@ -3,7 +3,6 @@ using ArcGIS.Desktop.Core.Events;
using ArcGIS.Desktop.Framework.Threading.Tasks;
using ArcGIS.Desktop.Mapping;
using ArcGIS.Desktop.Mapping.Events;
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Utils;
@@ -12,16 +11,9 @@ namespace Speckle.Connectors.ArcGIS.Utils;
public class ArcGISDocumentStore : DocumentModelStore
{
private readonly IThreadContext _threadContext;
public ArcGISDocumentStore(
IJsonSerializer jsonSerializer,
ITopLevelExceptionHandler topLevelExceptionHandler,
IThreadContext threadContext
)
public ArcGISDocumentStore(IJsonSerializer jsonSerializer, ITopLevelExceptionHandler topLevelExceptionHandler)
: base(jsonSerializer)
{
_threadContext = threadContext;
ActiveMapViewChangedEvent.Subscribe(a => topLevelExceptionHandler.CatchUnhandled(() => OnMapViewChanged(a)), true);
ProjectSavingEvent.Subscribe(
_ =>
@@ -82,52 +74,52 @@ public class ArcGISDocumentStore : DocumentModelStore
OnDocumentChanged();
}
protected override void HostAppSaveState(string modelCardState) =>
QueuedTask
.Run(() =>
protected override void HostAppSaveState(string modelCardState)
{
Map map = MapView.Active.Map;
QueuedTask.Run(() =>
{
// Read existing metadata - To prevent messing existing metadata. 🤞 Hope other add-in developers will do same :D
var existingMetadata = map.GetMetadata();
// Parse existing metadata
XDocument existingXmlDocument = !string.IsNullOrEmpty(existingMetadata)
? XDocument.Parse(existingMetadata)
: new XDocument(new XElement("metadata"));
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)
{
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() =>
QueuedTask
.Run(() =>
speckleModelCardsElement.ReplaceWith(xmlModelCards);
}
else
{
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;
}
existingXmlDocument.Root?.Add(xmlModelCards);
}
string modelsString = element.Value;
LoadFromString(modelsString);
})
.FireAndForget();
map.SetMetadata(existingXmlDocument.ToString());
});
}
protected override void LoadState()
{
Map map = MapView.Active.Map;
QueuedTask.Run(() =>
{
var metadata = map.GetMetadata();
var root = XDocument.Parse(metadata).Root;
var element = root?.Element("SpeckleModelCards");
if (element is null)
{
ClearAndSave();
return;
}
string modelsString = element.Value;
LoadFromString(modelsString);
});
}
}
@@ -166,6 +166,11 @@
"resolved": "8.0.0",
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
},
"Speckle.DoubleNumerics": {
"type": "Transitive",
"resolved": "4.0.1",
"contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w=="
},
"Speckle.Newtonsoft.Json": {
"type": "Transitive",
"resolved": "13.0.2",
@@ -221,21 +226,14 @@
"resolved": "4.5.1",
"contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw=="
},
"speckle.common.meshtriangulation": {
"type": "Project",
"dependencies": {
"LibTessDotNet": "[1.1.15, )",
"Speckle.DoubleNumerics": "[4.1.0, )"
}
},
"speckle.connectors.common": {
"type": "Project",
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.1.8, )",
"Speckle.Sdk": "[3.1.8, )",
"Speckle.Sdk.Dependencies": "[3.1.8, )"
"Speckle.Objects": "[3.1.0-dev.219, )",
"Speckle.Sdk": "[3.1.0-dev.219, )",
"Speckle.Sdk.Dependencies": "[3.1.0-dev.219, )"
}
},
"speckle.connectors.dui": {
@@ -243,8 +241,9 @@
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.8, )",
"Speckle.Sdk.Dependencies": "[3.1.8, )"
"Speckle.Sdk": "[3.1.0-dev.219, )",
"Speckle.Sdk.Dependencies": "[3.1.0-dev.219, )",
"System.Threading.Tasks.Dataflow": "[6.0.0, )"
}
},
"speckle.connectors.dui.webview": {
@@ -261,7 +260,6 @@
"type": "Project",
"dependencies": {
"Esri.ArcGISPro.Extensions30": "[3.2.0.49743, )",
"Speckle.Common.MeshTriangulation": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )"
}
},
@@ -269,15 +267,9 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.1.8, )"
"Speckle.Objects": "[3.1.0-dev.219, )"
}
},
"LibTessDotNet": {
"type": "CentralTransitive",
"requested": "[1.1.15, )",
"resolved": "1.1.15",
"contentHash": "KuA7N3Nv/lIeawJdQBQJR6oqWD9KETHLbWzBqapwFs+Tby+R5I4crkKujKMm5bXcSuFZ8LNtflFQVadsWCbBjg=="
},
"Microsoft.Extensions.DependencyInjection": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -305,26 +297,20 @@
"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.1.8, )",
"resolved": "3.1.8",
"contentHash": "rM1t+E8NIWFCPC3ndnMAI71B9ONiPFsz6GuIIdc5kth5AggQRGArPCalJBwA33ith2WtbjWOanI+PhHmpGQgGw==",
"requested": "[3.1.0-dev.219, )",
"resolved": "3.1.0-dev.219",
"contentHash": "2thsMUDkRr7lR0PXZJ8k3KWZLWjz/aBY0KaO2SuXM0lIKyJw1APMJ7lXJ89e6JAjBzEbK8yLHBb0qtfcc4bNRw==",
"dependencies": {
"Speckle.Sdk": "3.1.8"
"Speckle.Sdk": "3.1.0-dev.219"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.1.8, )",
"resolved": "3.1.8",
"contentHash": "qIYRiYHO2UkKuy8ZbjYltKCqLesIcJOcCGTEzyTCbyij5aC1x8HnwFjGMeonUCPaeipEKhI/4HidEZ0CwWvnXg==",
"requested": "[3.1.0-dev.219, )",
"resolved": "3.1.0-dev.219",
"contentHash": "UTgz9UgkMFGqRLmCq1HPo74MngvKbSZOFWaMRzASHrrnsBtzZatBXjoJBlvVoeBCoiea5Jj+BgZg6RvlV2m3sg==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -332,16 +318,22 @@
"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.DoubleNumerics": "4.0.1",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.1.8"
"Speckle.Sdk.Dependencies": "3.1.0-dev.219"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.1.8, )",
"resolved": "3.1.8",
"contentHash": "+MWIemub3nlPnDJ7sHl5nDGFDIIfiBU2VxqNmTGKzWNtCjsDi6KYw5zpbvXnaNcIqFoexv3TKCurjvT2ejzyQw=="
"requested": "[3.1.0-dev.219, )",
"resolved": "3.1.0-dev.219",
"contentHash": "5hqBd9whAs5cC8S/6YUgKFOivjntAkVutKhz06XD/f3Kj6xScT3hwcUaWBxiwK3Mw7FLtiVuU1uG777pjn0PKQ=="
},
"System.Threading.Tasks.Dataflow": {
"type": "CentralTransitive",
"requested": "[6.0.0, )",
"resolved": "6.0.0",
"contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA=="
}
},
"net6.0-windows7.0/win-x64": {
@@ -1,26 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project>
<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'">
<ItemGroup>
<AutoCADDLLs Include="$(TargetDir)\**\*.*" />
</ItemGroup>
<Message Text="AutoCAD Version $(AutoCADVersion)" Importance="high"/>
<Copy DestinationFolder="$(AppData)\Autodesk\ApplicationPlugins\Speckle.Connectors.Autocad$(AutoCADVersion)\%(RecursiveDir)" SourceFiles="@(AutoCADDLLs)" />
</Target>
<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'">
<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>
</Project>
@@ -11,7 +11,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2022.0.2" ExcludeAssets="runtime"/>
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2022.0.2" />
</ItemGroup>
<ItemGroup>
@@ -164,6 +164,11 @@
"resolved": "8.0.0",
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
},
"Speckle.DoubleNumerics": {
"type": "Transitive",
"resolved": "4.0.1",
"contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w=="
},
"Speckle.Newtonsoft.Json": {
"type": "Transitive",
"resolved": "13.0.2",
@@ -259,9 +264,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.1.8, )",
"Speckle.Sdk": "[3.1.8, )",
"Speckle.Sdk.Dependencies": "[3.1.8, )"
"Speckle.Objects": "[3.1.0-dev.219, )",
"Speckle.Sdk": "[3.1.0-dev.219, )",
"Speckle.Sdk.Dependencies": "[3.1.0-dev.219, )"
}
},
"speckle.connectors.dui": {
@@ -269,8 +274,9 @@
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.8, )",
"Speckle.Sdk.Dependencies": "[3.1.8, )"
"Speckle.Sdk": "[3.1.0-dev.219, )",
"Speckle.Sdk.Dependencies": "[3.1.0-dev.219, )",
"System.Threading.Tasks.Dataflow": "[6.0.0, )"
}
},
"speckle.connectors.dui.webview": {
@@ -294,7 +300,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.1.8, )"
"Speckle.Objects": "[3.1.0-dev.219, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -330,26 +336,20 @@
"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.1.8, )",
"resolved": "3.1.8",
"contentHash": "rM1t+E8NIWFCPC3ndnMAI71B9ONiPFsz6GuIIdc5kth5AggQRGArPCalJBwA33ith2WtbjWOanI+PhHmpGQgGw==",
"requested": "[3.1.0-dev.219, )",
"resolved": "3.1.0-dev.219",
"contentHash": "2thsMUDkRr7lR0PXZJ8k3KWZLWjz/aBY0KaO2SuXM0lIKyJw1APMJ7lXJ89e6JAjBzEbK8yLHBb0qtfcc4bNRw==",
"dependencies": {
"Speckle.Sdk": "3.1.8"
"Speckle.Sdk": "3.1.0-dev.219"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.1.8, )",
"resolved": "3.1.8",
"contentHash": "qIYRiYHO2UkKuy8ZbjYltKCqLesIcJOcCGTEzyTCbyij5aC1x8HnwFjGMeonUCPaeipEKhI/4HidEZ0CwWvnXg==",
"requested": "[3.1.0-dev.219, )",
"resolved": "3.1.0-dev.219",
"contentHash": "UTgz9UgkMFGqRLmCq1HPo74MngvKbSZOFWaMRzASHrrnsBtzZatBXjoJBlvVoeBCoiea5Jj+BgZg6RvlV2m3sg==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -357,16 +357,22 @@
"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.DoubleNumerics": "4.0.1",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.1.8"
"Speckle.Sdk.Dependencies": "3.1.0-dev.219"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.1.8, )",
"resolved": "3.1.8",
"contentHash": "+MWIemub3nlPnDJ7sHl5nDGFDIIfiBU2VxqNmTGKzWNtCjsDi6KYw5zpbvXnaNcIqFoexv3TKCurjvT2ejzyQw=="
"requested": "[3.1.0-dev.219, )",
"resolved": "3.1.0-dev.219",
"contentHash": "5hqBd9whAs5cC8S/6YUgKFOivjntAkVutKhz06XD/f3Kj6xScT3hwcUaWBxiwK3Mw7FLtiVuU1uG777pjn0PKQ=="
},
"System.Threading.Tasks.Dataflow": {
"type": "CentralTransitive",
"requested": "[6.0.0, )",
"resolved": "6.0.0",
"contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA=="
}
}
}
@@ -11,7 +11,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2023.0.0" ExcludeAssets="runtime"/>
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2023.0.0" />
</ItemGroup>
<ItemGroup>
@@ -164,6 +164,11 @@
"resolved": "8.0.0",
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
},
"Speckle.DoubleNumerics": {
"type": "Transitive",
"resolved": "4.0.1",
"contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w=="
},
"Speckle.Newtonsoft.Json": {
"type": "Transitive",
"resolved": "13.0.2",
@@ -259,9 +264,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.1.8, )",
"Speckle.Sdk": "[3.1.8, )",
"Speckle.Sdk.Dependencies": "[3.1.8, )"
"Speckle.Objects": "[3.1.0-dev.219, )",
"Speckle.Sdk": "[3.1.0-dev.219, )",
"Speckle.Sdk.Dependencies": "[3.1.0-dev.219, )"
}
},
"speckle.connectors.dui": {
@@ -269,8 +274,9 @@
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.8, )",
"Speckle.Sdk.Dependencies": "[3.1.8, )"
"Speckle.Sdk": "[3.1.0-dev.219, )",
"Speckle.Sdk.Dependencies": "[3.1.0-dev.219, )",
"System.Threading.Tasks.Dataflow": "[6.0.0, )"
}
},
"speckle.connectors.dui.webview": {
@@ -294,7 +300,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.1.8, )"
"Speckle.Objects": "[3.1.0-dev.219, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -330,26 +336,20 @@
"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.1.8, )",
"resolved": "3.1.8",
"contentHash": "rM1t+E8NIWFCPC3ndnMAI71B9ONiPFsz6GuIIdc5kth5AggQRGArPCalJBwA33ith2WtbjWOanI+PhHmpGQgGw==",
"requested": "[3.1.0-dev.219, )",
"resolved": "3.1.0-dev.219",
"contentHash": "2thsMUDkRr7lR0PXZJ8k3KWZLWjz/aBY0KaO2SuXM0lIKyJw1APMJ7lXJ89e6JAjBzEbK8yLHBb0qtfcc4bNRw==",
"dependencies": {
"Speckle.Sdk": "3.1.8"
"Speckle.Sdk": "3.1.0-dev.219"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.1.8, )",
"resolved": "3.1.8",
"contentHash": "qIYRiYHO2UkKuy8ZbjYltKCqLesIcJOcCGTEzyTCbyij5aC1x8HnwFjGMeonUCPaeipEKhI/4HidEZ0CwWvnXg==",
"requested": "[3.1.0-dev.219, )",
"resolved": "3.1.0-dev.219",
"contentHash": "UTgz9UgkMFGqRLmCq1HPo74MngvKbSZOFWaMRzASHrrnsBtzZatBXjoJBlvVoeBCoiea5Jj+BgZg6RvlV2m3sg==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -357,16 +357,22 @@
"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.DoubleNumerics": "4.0.1",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.1.8"
"Speckle.Sdk.Dependencies": "3.1.0-dev.219"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.1.8, )",
"resolved": "3.1.8",
"contentHash": "+MWIemub3nlPnDJ7sHl5nDGFDIIfiBU2VxqNmTGKzWNtCjsDi6KYw5zpbvXnaNcIqFoexv3TKCurjvT2ejzyQw=="
"requested": "[3.1.0-dev.219, )",
"resolved": "3.1.0-dev.219",
"contentHash": "5hqBd9whAs5cC8S/6YUgKFOivjntAkVutKhz06XD/f3Kj6xScT3hwcUaWBxiwK3Mw7FLtiVuU1uG777pjn0PKQ=="
},
"System.Threading.Tasks.Dataflow": {
"type": "CentralTransitive",
"requested": "[6.0.0, )",
"resolved": "6.0.0",
"contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA=="
}
}
}
@@ -11,7 +11,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2024.0.0" ExcludeAssets="runtime"/>
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2024.0.0" />
</ItemGroup>
<ItemGroup>
@@ -164,6 +164,11 @@
"resolved": "8.0.0",
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
},
"Speckle.DoubleNumerics": {
"type": "Transitive",
"resolved": "4.0.1",
"contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w=="
},
"Speckle.Newtonsoft.Json": {
"type": "Transitive",
"resolved": "13.0.2",
@@ -259,9 +264,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.1.8, )",
"Speckle.Sdk": "[3.1.8, )",
"Speckle.Sdk.Dependencies": "[3.1.8, )"
"Speckle.Objects": "[3.1.0-dev.219, )",
"Speckle.Sdk": "[3.1.0-dev.219, )",
"Speckle.Sdk.Dependencies": "[3.1.0-dev.219, )"
}
},
"speckle.connectors.dui": {
@@ -269,8 +274,9 @@
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.8, )",
"Speckle.Sdk.Dependencies": "[3.1.8, )"
"Speckle.Sdk": "[3.1.0-dev.219, )",
"Speckle.Sdk.Dependencies": "[3.1.0-dev.219, )",
"System.Threading.Tasks.Dataflow": "[6.0.0, )"
}
},
"speckle.connectors.dui.webview": {
@@ -295,7 +301,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.1.8, )"
"Speckle.Objects": "[3.1.0-dev.219, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -331,26 +337,20 @@
"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.1.8, )",
"resolved": "3.1.8",
"contentHash": "rM1t+E8NIWFCPC3ndnMAI71B9ONiPFsz6GuIIdc5kth5AggQRGArPCalJBwA33ith2WtbjWOanI+PhHmpGQgGw==",
"requested": "[3.1.0-dev.219, )",
"resolved": "3.1.0-dev.219",
"contentHash": "2thsMUDkRr7lR0PXZJ8k3KWZLWjz/aBY0KaO2SuXM0lIKyJw1APMJ7lXJ89e6JAjBzEbK8yLHBb0qtfcc4bNRw==",
"dependencies": {
"Speckle.Sdk": "3.1.8"
"Speckle.Sdk": "3.1.0-dev.219"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.1.8, )",
"resolved": "3.1.8",
"contentHash": "qIYRiYHO2UkKuy8ZbjYltKCqLesIcJOcCGTEzyTCbyij5aC1x8HnwFjGMeonUCPaeipEKhI/4HidEZ0CwWvnXg==",
"requested": "[3.1.0-dev.219, )",
"resolved": "3.1.0-dev.219",
"contentHash": "UTgz9UgkMFGqRLmCq1HPo74MngvKbSZOFWaMRzASHrrnsBtzZatBXjoJBlvVoeBCoiea5Jj+BgZg6RvlV2m3sg==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -358,16 +358,22 @@
"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.DoubleNumerics": "4.0.1",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.1.8"
"Speckle.Sdk.Dependencies": "3.1.0-dev.219"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.1.8, )",
"resolved": "3.1.8",
"contentHash": "+MWIemub3nlPnDJ7sHl5nDGFDIIfiBU2VxqNmTGKzWNtCjsDi6KYw5zpbvXnaNcIqFoexv3TKCurjvT2ejzyQw=="
"requested": "[3.1.0-dev.219, )",
"resolved": "3.1.0-dev.219",
"contentHash": "5hqBd9whAs5cC8S/6YUgKFOivjntAkVutKhz06XD/f3Kj6xScT3hwcUaWBxiwK3Mw7FLtiVuU1uG777pjn0PKQ=="
},
"System.Threading.Tasks.Dataflow": {
"type": "CentralTransitive",
"requested": "[6.0.0, )",
"resolved": "6.0.0",
"contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA=="
}
}
}
@@ -13,7 +13,8 @@
</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>
@@ -155,6 +155,11 @@
"resolved": "8.0.0",
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
},
"Speckle.DoubleNumerics": {
"type": "Transitive",
"resolved": "4.0.1",
"contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w=="
},
"Speckle.Newtonsoft.Json": {
"type": "Transitive",
"resolved": "13.0.2",
@@ -215,9 +220,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.1.8, )",
"Speckle.Sdk": "[3.1.8, )",
"Speckle.Sdk.Dependencies": "[3.1.8, )"
"Speckle.Objects": "[3.1.0-dev.219, )",
"Speckle.Sdk": "[3.1.0-dev.219, )",
"Speckle.Sdk.Dependencies": "[3.1.0-dev.219, )"
}
},
"speckle.connectors.dui": {
@@ -225,8 +230,9 @@
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.8, )",
"Speckle.Sdk.Dependencies": "[3.1.8, )"
"Speckle.Sdk": "[3.1.0-dev.219, )",
"Speckle.Sdk.Dependencies": "[3.1.0-dev.219, )",
"System.Threading.Tasks.Dataflow": "[6.0.0, )"
}
},
"speckle.connectors.dui.webview": {
@@ -251,7 +257,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.1.8, )"
"Speckle.Objects": "[3.1.0-dev.219, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -287,42 +293,42 @@
"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.1.8, )",
"resolved": "3.1.8",
"contentHash": "rM1t+E8NIWFCPC3ndnMAI71B9ONiPFsz6GuIIdc5kth5AggQRGArPCalJBwA33ith2WtbjWOanI+PhHmpGQgGw==",
"requested": "[3.1.0-dev.219, )",
"resolved": "3.1.0-dev.219",
"contentHash": "2thsMUDkRr7lR0PXZJ8k3KWZLWjz/aBY0KaO2SuXM0lIKyJw1APMJ7lXJ89e6JAjBzEbK8yLHBb0qtfcc4bNRw==",
"dependencies": {
"Speckle.Sdk": "3.1.8"
"Speckle.Sdk": "3.1.0-dev.219"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.1.8, )",
"resolved": "3.1.8",
"contentHash": "qIYRiYHO2UkKuy8ZbjYltKCqLesIcJOcCGTEzyTCbyij5aC1x8HnwFjGMeonUCPaeipEKhI/4HidEZ0CwWvnXg==",
"requested": "[3.1.0-dev.219, )",
"resolved": "3.1.0-dev.219",
"contentHash": "UTgz9UgkMFGqRLmCq1HPo74MngvKbSZOFWaMRzASHrrnsBtzZatBXjoJBlvVoeBCoiea5Jj+BgZg6RvlV2m3sg==",
"dependencies": {
"GraphQL.Client": "6.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",
"Speckle.DoubleNumerics": "4.0.1",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.1.8"
"Speckle.Sdk.Dependencies": "3.1.0-dev.219"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.1.8, )",
"resolved": "3.1.8",
"contentHash": "+MWIemub3nlPnDJ7sHl5nDGFDIIfiBU2VxqNmTGKzWNtCjsDi6KYw5zpbvXnaNcIqFoexv3TKCurjvT2ejzyQw=="
"requested": "[3.1.0-dev.219, )",
"resolved": "3.1.0-dev.219",
"contentHash": "5hqBd9whAs5cC8S/6YUgKFOivjntAkVutKhz06XD/f3Kj6xScT3hwcUaWBxiwK3Mw7FLtiVuU1uG777pjn0PKQ=="
},
"System.Threading.Tasks.Dataflow": {
"type": "CentralTransitive",
"requested": "[6.0.0, )",
"resolved": "6.0.0",
"contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -1,7 +1,6 @@
using Autodesk.AutoCAD.DatabaseServices;
using Microsoft.Extensions.Logging;
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.Models;
@@ -20,8 +19,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; }
@@ -31,9 +28,7 @@ public class AutocadBasicConnectorBinding : IBasicConnectorBinding
IBrowserBridge parent,
IAccountManager accountManager,
ISpeckleApplication speckleApplication,
ILogger<AutocadBasicConnectorBinding> logger,
IThreadContext threadContext,
ITopLevelExceptionHandler topLevelExceptionHandler
ILogger<AutocadBasicConnectorBinding> logger
)
{
_store = store;
@@ -41,14 +36,12 @@ public class AutocadBasicConnectorBinding : IBasicConnectorBinding
_accountManager = accountManager;
_speckleApplication = speckleApplication;
Commands = new BasicConnectorBindingCommands(parent);
_logger = logger;
_threadContext = threadContext;
_topLevelExceptionHandler = topLevelExceptionHandler;
_store.DocumentChanged += (_, _) =>
_topLevelExceptionHandler.FireAndForget(async () =>
parent.TopLevelExceptionHandler.FireAndForget(async () =>
{
await Commands.NotifyDocumentChanged();
await Commands.NotifyDocumentChanged().ConfigureAwait(false);
});
_logger = logger;
}
public string GetConnectorVersion() => _speckleApplication.SpeckleVersion;
@@ -79,8 +72,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!
@@ -88,7 +79,7 @@ public class AutocadBasicConnectorBinding : IBasicConnectorBinding
var dbObjects = doc.GetObjects(objectIds);
var acadObjectIds = dbObjects.Select(tuple => tuple.Root.Id).ToArray();
await HighlightObjectsOnView(acadObjectIds);
await HighlightObjectsOnView(acadObjectIds).ConfigureAwait(false);
}
public async Task HighlightModel(string modelCardId)
@@ -125,73 +116,79 @@ public class AutocadBasicConnectorBinding : IBasicConnectorBinding
if (objectIds.Length == 0)
{
await Commands.SetModelError(modelCardId, new OperationCanceledException("No objects found to highlight."));
await Commands
.SetModelError(modelCardId, new OperationCanceledException("No objects found to highlight."))
.ConfigureAwait(false);
return;
}
await HighlightObjectsOnView(objectIds, modelCardId);
await HighlightObjectsOnView(objectIds, modelCardId).ConfigureAwait(false);
}
private async Task HighlightObjectsOnView(ObjectId[] objectIds, string? modelCardId = null)
{
var doc = Application.DocumentManager.MdiActiveDocument;
await _threadContext.RunOnMainAsync(async () =>
{
try
await Parent
.RunOnMainThreadAsync(async () =>
{
doc.Editor.SetImpliedSelection([]); // Deselects
try
{
doc.Editor.SetImpliedSelection(objectIds);
}
catch (Exception e) when (!e.IsFatal())
{
// SWALLOW REASON:
// If the objects under the blocks, it won't be able to select them.
// If we try, API will throw the invalid input error, because we request something from API that Autocad doesn't
// handle it on its current canvas. Block elements only selectable when in its scope.
}
doc.Editor.UpdateScreen();
Extents3d selectedExtents = new();
var tr = doc.TransactionManager.StartTransaction();
foreach (ObjectId objectId in objectIds)
{
doc.Editor.SetImpliedSelection(Array.Empty<ObjectId>()); // Deselects
try
{
var entity = (Entity?)tr.GetObject(objectId, OpenMode.ForRead);
if (entity?.GeometricExtents != null)
{
selectedExtents.AddExtents(entity.GeometricExtents);
}
doc.Editor.SetImpliedSelection(objectIds);
}
catch (Exception e) when (!e.IsFatal())
{
// Note: we're swallowing exeptions here because of a weird case when receiving blocks, we would have
// acad api throw an error on accessing entity.GeometricExtents.
// may also throw Autodesk.AutoCAD.Runtime.Exception for invalid extents on objects like rays and xlines
// SWALLOW REASON:
// If the objects under the blocks, it won't be able to select them.
// If we try, API will throw the invalid input error, because we request something from API that Autocad doesn't
// handle it on its current canvas. Block elements only selectable when in its scope.
}
doc.Editor.UpdateScreen();
Extents3d selectedExtents = new();
var tr = doc.TransactionManager.StartTransaction();
foreach (ObjectId objectId in objectIds)
{
try
{
var entity = (Entity?)tr.GetObject(objectId, OpenMode.ForRead);
if (entity?.GeometricExtents != null)
{
selectedExtents.AddExtents(entity.GeometricExtents);
}
}
catch (Exception e) when (!e.IsFatal())
{
// Note: we're swallowing exeptions here because of a weird case when receiving blocks, we would have
// acad api throw an error on accessing entity.GeometricExtents.
// may also throw Autodesk.AutoCAD.Runtime.Exception for invalid extents on objects like rays and xlines
}
}
doc.Editor.Zoom(selectedExtents);
tr.Commit();
Autodesk.AutoCAD.Internal.Utils.FlushGraphics();
}
catch (Exception ex) when (!ex.IsFatal())
{
if (modelCardId != null)
{
await Commands
.SetModelError(modelCardId, new OperationCanceledException("Failed to highlight objects."))
.ConfigureAwait(false);
}
else
{
// This will happen, in some cases, where we highlight individual objects. Should be caught by the top level handler and not
// crash the host app.
throw;
}
}
doc.Editor.Zoom(selectedExtents);
tr.Commit();
Autodesk.AutoCAD.Internal.Utils.FlushGraphics();
}
catch (Exception ex) when (!ex.IsFatal())
{
if (modelCardId != null)
{
await Commands.SetModelError(modelCardId, new OperationCanceledException("Failed to highlight objects."));
}
else
{
// This will happen, in some cases, where we highlight individual objects. Should be caught by the top level handler and not
// crash the host app.
throw;
}
}
});
})
.ConfigureAwait(false);
}
}
@@ -1,119 +0,0 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
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.Logging;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Sdk;
namespace Speckle.Connectors.Autocad.Bindings;
public abstract class AutocadReceiveBaseBinding : 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 ISpeckleApplication _speckleApplication;
private readonly IThreadContext _threadContext;
private ReceiveBindingUICommands Commands { get; }
protected AutocadReceiveBaseBinding(
DocumentModelStore store,
IBrowserBridge parent,
ICancellationManager cancellationManager,
IServiceProvider serviceProvider,
IOperationProgressManager operationProgressManager,
ILogger<AutocadReceiveBinding> logger,
ISpeckleApplication speckleApplication,
IThreadContext threadContext
)
{
_store = store;
_cancellationManager = cancellationManager;
_serviceProvider = serviceProvider;
_operationProgressManager = operationProgressManager;
_logger = logger;
_speckleApplication = speckleApplication;
_threadContext = threadContext;
Parent = parent;
Commands = new ReceiveBindingUICommands(parent);
}
protected abstract void InitializeSettings(IServiceProvider serviceProvider);
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 scope = _serviceProvider.CreateScope();
InitializeSettings(scope.ServiceProvider);
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;
// 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,49 +1,109 @@
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 CancellationManager _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,
CancellationManager cancellationManager,
IServiceProvider serviceProvider,
IOperationProgressManager operationProgressManager,
ILogger<AutocadReceiveBinding> logger,
IAutocadConversionSettingsFactory autocadConversionSettingsFactory,
ISpeckleApplication speckleApplication,
IThreadContext threadContext
ISpeckleApplication speckleApplication
)
: base(
store,
parent,
cancellationManager,
serviceProvider,
operationProgressManager,
logger,
speckleApplication,
threadContext
)
{
_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)
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.");
}
CancellationToken cancellationToken = _cancellationManager.InitCancellationTokenSource(modelCardId);
// Disable document activation (document creation and document switch)
// Not disabling results in DUI model card being out of sync with the active document
// The DocumentActivated event isn't usable probably because it is pushed to back of main thread queue
Application.DocumentManager.DocumentActivationEnabled = false;
// Receive host objects
var operationResults = await scope
.ServiceProvider.GetRequiredService<ReceiveOperation>()
.Execute(
modelCard.GetReceiveInfo(_speckleApplication.Slug),
_operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationToken),
cancellationToken
)
.ConfigureAwait(false);
await Commands
.SetModelReceiveResult(modelCardId, operationResults.BakedObjectIds, operationResults.ConversionResults)
.ConfigureAwait(false);
}
catch (OperationCanceledException)
{
// SWALLOW -> UI handles it immediately, so we do not need to handle anything for now!
// Idea for later -> when cancel called, create promise from UI to solve it later with this catch block.
// So have 3 state on UI -> Cancellation clicked -> Cancelling -> Cancelled
return;
}
catch (Exception ex) when (!ex.IsFatal()) // UX reasons - we will report operation exceptions as model card error. We may change this later when we have more exception documentation
{
_logger.LogModelCardHandledError(ex);
await Commands.SetModelError(modelCardId, ex).ConfigureAwait(false);
}
finally
{
// reenable document activation
Application.DocumentManager.DocumentActivationEnabled = true;
}
}
}
@@ -1,7 +1,6 @@
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Speckle.Connectors.Autocad.HostApp.Extensions;
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
@@ -9,23 +8,17 @@ 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 ITopLevelExceptionHandler _topLevelExceptionHandler;
private readonly HashSet<Document> _visitedDocuments = new();
public string Name => "selectionBinding";
public IBrowserBridge Parent { get; }
public AutocadSelectionBinding(
IBrowserBridge parent,
ITopLevelExceptionHandler topLevelExceptionHandler,
IThreadContext threadContext
)
public AutocadSelectionBinding(IBrowserBridge parent)
{
_topLevelExceptionHandler = topLevelExceptionHandler;
_threadContext = threadContext;
_topLevelExceptionHandler = parent.TopLevelExceptionHandler;
Parent = parent;
// POC: Use here Context for doc. In converters it's OK but we are still lacking to use context into bindings.
@@ -48,7 +41,9 @@ public class AutocadSelectionBinding : ISelectionBinding
if (!_visitedDocuments.Contains(document))
{
document.ImpliedSelectionChanged += (_, _) =>
_topLevelExceptionHandler.FireAndForget(async () => await _threadContext.RunOnMainAsync(OnSelectionChanged));
_topLevelExceptionHandler.FireAndForget(
async () => await Parent.RunOnMainThreadAsync(OnSelectionChanged).ConfigureAwait(false)
);
_visitedDocuments.Add(document);
}
@@ -62,7 +57,7 @@ public class AutocadSelectionBinding : ISelectionBinding
private async Task OnSelectionChanged()
{
_selectionInfo = GetSelectionInternal();
await Parent.Send(SELECTION_EVENT, _selectionInfo);
await Parent.Send(SELECTION_EVENT, _selectionInfo).ConfigureAwait(false);
}
public SelectionInfo GetSelection() => _selectionInfo;
@@ -1,14 +1,13 @@
using System.Collections.Concurrent;
using System.Diagnostics.CodeAnalysis;
using System.Collections.Concurrent;
using Autodesk.AutoCAD.DatabaseServices;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Autocad.HostApp;
using Speckle.Connectors.Autocad.HostApp.Extensions;
using Speckle.Connectors.Autocad.Operations.Send;
using Speckle.Connectors.Common.Caching;
using Speckle.Connectors.Common.Cancellation;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Exceptions;
@@ -22,7 +21,6 @@ using Speckle.Sdk.Common;
namespace Speckle.Connectors.Autocad.Bindings;
[SuppressMessage("ReSharper", "AsyncVoidMethod")]
public abstract class AutocadSendBaseBinding : ISendBinding
{
public string Name => "sendBinding";
@@ -31,16 +29,15 @@ public abstract class AutocadSendBaseBinding : ISendBinding
public IBrowserBridge Parent { get; }
private readonly DocumentModelStore _store;
private readonly IAutocadIdleManager _idleManager;
private readonly List<ISendFilter> _sendFilters;
private readonly ICancellationManager _cancellationManager;
private readonly CancellationManager _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 ISpeckleApplication _speckleApplication;
/// <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:
@@ -48,24 +45,23 @@ 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,
IAutocadIdleManager idleManager,
IBrowserBridge parent,
IEnumerable<ISendFilter> sendFilters,
ICancellationManager cancellationManager,
CancellationManager cancellationManager,
IServiceProvider serviceProvider,
ISendConversionCache sendConversionCache,
IOperationProgressManager operationProgressManager,
ILogger<AutocadSendBinding> logger,
ISpeckleApplication speckleApplication,
IThreadContext threadContext,
ITopLevelExceptionHandler topLevelExceptionHandler,
IAppIdleManager idleManager
ISpeckleApplication speckleApplication
)
{
_store = store;
_idleManager = idleManager;
_serviceProvider = serviceProvider;
_cancellationManager = cancellationManager;
_sendFilters = sendFilters.ToList();
@@ -73,9 +69,7 @@ public abstract class AutocadSendBaseBinding : ISendBinding
_operationProgressManager = operationProgressManager;
_logger = logger;
_speckleApplication = speckleApplication;
_threadContext = threadContext;
_topLevelExceptionHandler = topLevelExceptionHandler;
_idleManager = idleManager;
_topLevelExceptionHandler = parent.TopLevelExceptionHandler;
Parent = parent;
Commands = new SendBindingUICommands(parent);
@@ -109,25 +103,31 @@ public abstract class AutocadSendBaseBinding : ISendBinding
doc.Database.ObjectModified += (_, e) => OnObjectChanged(e.DBObject);
}
private void OnObjectChanged(DBObject dbObject) =>
private void OnObjectChanged(DBObject dbObject)
{
_topLevelExceptionHandler.CatchUnhandled(() => OnChangeChangedObjectIds(dbObject));
}
private void OnChangeChangedObjectIds(DBObject dBObject)
{
ChangedObjectIds.Add(dBObject.GetSpeckleApplicationId());
_idleManager.SubscribeToIdle(nameof(RunExpirationChecks), async () => await RunExpirationChecks());
ChangedObjectIds[dBObject.GetSpeckleApplicationId()] = 1;
_idleManager.SubscribeToIdle(
nameof(AutocadSendBinding),
async () => await RunExpirationChecks().ConfigureAwait(false)
);
}
private async Task RunExpirationChecks()
{
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)
{
@@ -135,7 +135,7 @@ public abstract class AutocadSendBaseBinding : ISendBinding
}
}
await Commands.SetModelsExpired(expiredSenderIds);
await Commands.SetModelsExpired(expiredSenderIds).ConfigureAwait(false);
ChangedObjectIds = new();
}
@@ -144,7 +144,9 @@ public abstract class AutocadSendBaseBinding : ISendBinding
public List<ICardSetting> GetSendSettings() => [];
public async Task Send(string modelCardId) =>
await _threadContext.RunOnMainAsync(async () => await SendInternal(modelCardId));
await Parent
.RunOnMainThreadAsync(async () => await SendInternal(modelCardId).ConfigureAwait(false))
.ConfigureAwait(false);
protected abstract void InitializeSettings(IServiceProvider serviceProvider);
@@ -161,7 +163,7 @@ public abstract class AutocadSendBaseBinding : ISendBinding
using var scope = _serviceProvider.CreateScope();
InitializeSettings(scope.ServiceProvider);
using var cancellationItem = _cancellationManager.GetCancellationItem(modelCardId);
CancellationToken cancellationToken = _cancellationManager.InitCancellationTokenSource(modelCardId);
// Disable document activation (document creation and document switch)
// Not disabling results in DUI model card being out of sync with the active document
@@ -183,12 +185,15 @@ public abstract class AutocadSendBaseBinding : ISendBinding
.ServiceProvider.GetRequiredService<SendOperation<AutocadRootObject>>()
.Execute(
autocadObjects,
modelCard.GetSendInfo(_speckleApplication.ApplicationAndVersion),
_operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationItem.Token),
cancellationItem.Token
);
modelCard.GetSendInfo(_speckleApplication.Slug),
_operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationToken),
cancellationToken
)
.ConfigureAwait(false);
await Commands.SetModelSendResult(modelCardId, sendResult.RootObjId, sendResult.ConversionResults);
await Commands
.SetModelSendResult(modelCardId, sendResult.RootObjId, sendResult.ConversionResults)
.ConfigureAwait(false);
}
catch (OperationCanceledException)
{
@@ -200,7 +205,7 @@ public abstract class AutocadSendBaseBinding : ISendBinding
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);
await Commands.SetModelError(modelCardId, ex).ConfigureAwait(false);
}
finally
{
@@ -1,8 +1,8 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Autocad.HostApp;
using Speckle.Connectors.Common.Caching;
using Speckle.Connectors.Common.Cancellation;
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Models;
@@ -19,21 +19,20 @@ public sealed class AutocadSendBinding : AutocadSendBaseBinding
public AutocadSendBinding(
DocumentModelStore store,
IAutocadIdleManager idleManager,
IBrowserBridge parent,
IEnumerable<ISendFilter> sendFilters,
ICancellationManager cancellationManager,
CancellationManager cancellationManager,
IServiceProvider serviceProvider,
ISendConversionCache sendConversionCache,
IOperationProgressManager operationProgressManager,
ILogger<AutocadSendBinding> logger,
IAutocadConversionSettingsFactory autocadConversionSettingsFactory,
ISpeckleApplication speckleApplication,
IThreadContext threadContext,
ITopLevelExceptionHandler topLevelExceptionHandler,
IAppIdleManager appIdleManager
ISpeckleApplication speckleApplication
)
: base(
store,
idleManager,
parent,
sendFilters,
cancellationManager,
@@ -41,10 +40,7 @@ public sealed class AutocadSendBinding : AutocadSendBaseBinding
sendConversionCache,
operationProgressManager,
logger,
speckleApplication,
threadContext,
topLevelExceptionHandler,
appIdleManager
speckleApplication
)
{
_autocadConversionSettingsFactory = autocadConversionSettingsFactory;
@@ -10,7 +10,6 @@ using Speckle.Connectors.Common.Builders;
using Speckle.Connectors.Common.Caching;
using Speckle.Connectors.Common.Instances;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.DUI;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
@@ -25,7 +24,7 @@ public static class SharedRegistration
public static void AddAutocadBase(this IServiceCollection serviceCollection)
{
serviceCollection.AddConnectorUtils();
serviceCollection.AddDUI<DefaultThreadContext, AutocadDocumentStore>();
serviceCollection.AddDUI<AutocadDocumentStore>();
serviceCollection.AddDUIView();
// Register other connector specific types
@@ -44,10 +43,10 @@ public static class SharedRegistration
serviceCollection.AddScoped<AutocadGroupBaker>();
serviceCollection.AddScoped<AutocadColorUnpacker>();
serviceCollection.AddScoped<IAutocadColorBaker, AutocadColorBaker>();
serviceCollection.AddScoped<AutocadColorBaker>();
serviceCollection.AddScoped<AutocadMaterialUnpacker>();
serviceCollection.AddScoped<IAutocadMaterialBaker, AutocadMaterialBaker>();
serviceCollection.AddScoped<AutocadMaterialBaker>();
serviceCollection.AddSingleton<IAppIdleManager, AutocadIdleManager>();
@@ -61,6 +60,8 @@ public static class SharedRegistration
serviceCollection.AddSingleton<IBinding>(sp => sp.GetRequiredService<IBasicConnectorBinding>());
serviceCollection.AddSingleton<IBasicConnectorBinding, AutocadBasicConnectorBinding>();
serviceCollection.AddSingleton<IBinding, ConfigBinding>();
serviceCollection.RegisterTopLevelExceptionHandler();
}
public static void LoadSend(this IServiceCollection serviceCollection)
@@ -1,7 +1,6 @@
using Autodesk.AutoCAD.Colors;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Common.Operations;
using Speckle.InterfaceGenerator;
using Speckle.Sdk;
using Speckle.Sdk.Models.Proxies;
using AutocadColor = Autodesk.AutoCAD.Colors.Color;
@@ -11,9 +10,15 @@ namespace Speckle.Connectors.Autocad.HostApp;
/// <summary>
/// Expects to be a scoped dependency for a given operation and helps with layer creation and cleanup.
/// </summary>
[GenerateAutoInterface]
public class AutocadColorBaker(ILogger<AutocadColorBaker> logger) : IAutocadColorBaker
public class AutocadColorBaker
{
private readonly ILogger<AutocadColorBaker> _logger;
public AutocadColorBaker(ILogger<AutocadColorBaker> logger)
{
_logger = logger;
}
/// <summary>
/// For receive operations
/// </summary>
@@ -54,7 +59,7 @@ public class AutocadColorBaker(ILogger<AutocadColorBaker> logger) : IAutocadColo
}
catch (Exception ex) when (!ex.IsFatal())
{
logger.LogError(ex, "Failed parsing color proxy");
_logger.LogError(ex, "Failed parsing color proxy");
}
}
}
@@ -6,10 +6,9 @@ namespace Speckle.Connectors.Autocad.HostApp;
public class AutocadDocumentStore : DocumentModelStore
{
private const string NULL_DOCUMENT_NAME = "Null Doc";
private readonly string _nullDocumentName = "Null Doc";
private string _previousDocName;
private readonly AutocadDocumentManager _autocadDocumentManager;
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
public AutocadDocumentStore(
IJsonSerializer jsonSerializer,
@@ -19,8 +18,7 @@ public class AutocadDocumentStore : DocumentModelStore
: base(jsonSerializer)
{
_autocadDocumentManager = autocadDocumentManager;
_topLevelExceptionHandler = topLevelExceptionHandler;
_previousDocName = NULL_DOCUMENT_NAME;
_previousDocName = _nullDocumentName;
// POC: Will be addressed to move it into AutocadContext!
if (Application.DocumentManager.MdiActiveDocument != null)
@@ -42,7 +40,7 @@ public class AutocadDocumentStore : DocumentModelStore
private void OnDocChangeInternal(Document? doc)
{
var currentDocName = doc != null ? doc.Name : NULL_DOCUMENT_NAME;
var currentDocName = doc != null ? doc.Name : _nullDocumentName;
if (_previousDocName == currentDocName)
{
return;
@@ -12,7 +12,10 @@ public sealed class AutocadIdleManager(IIdleCallManager idleCallManager)
{
private readonly IIdleCallManager _idleCallManager = idleCallManager;
protected override void AddEvent() => Application.Idle += AutocadAppOnIdle;
protected override void AddEvent()
{
Application.Idle += AutocadAppOnIdle;
}
private void AutocadAppOnIdle(object? sender, EventArgs e) =>
_idleCallManager.AppOnIdle(() => Application.Idle -= AutocadAppOnIdle);
@@ -26,16 +26,16 @@ namespace Speckle.Connectors.Autocad.HostApp;
public class AutocadInstanceBaker : IInstanceBaker<IReadOnlyCollection<Entity>>
{
private readonly AutocadLayerBaker _layerBaker;
private readonly IAutocadColorBaker _colorBaker;
private readonly IAutocadMaterialBaker _materialBaker;
private readonly AutocadColorBaker _colorBaker;
private readonly AutocadMaterialBaker _materialBaker;
private readonly AutocadContext _autocadContext;
private readonly ILogger<AutocadInstanceBaker> _logger;
private readonly IConverterSettingsStore<AutocadConversionSettings> _converterSettings;
public AutocadInstanceBaker(
AutocadLayerBaker layerBaker,
IAutocadColorBaker colorBaker,
IAutocadMaterialBaker materialBaker,
AutocadColorBaker colorBaker,
AutocadMaterialBaker materialBaker,
AutocadContext autocadContext,
ILogger<AutocadInstanceBaker> logger,
IConverterSettingsStore<AutocadConversionSettings> converterSettings
@@ -12,15 +12,15 @@ public class AutocadLayerBaker : TraversalContextUnpacker
{
private readonly string _layerFilterName = "Speckle";
private readonly AutocadContext _autocadContext;
private readonly IAutocadMaterialBaker _materialBaker;
private readonly IAutocadColorBaker _colorBaker;
private readonly AutocadMaterialBaker _materialBaker;
private readonly AutocadColorBaker _colorBaker;
private Document Doc => Application.DocumentManager.MdiActiveDocument;
private readonly HashSet<string> _uniqueLayerNames = new();
public AutocadLayerBaker(
AutocadContext autocadContext,
IAutocadMaterialBaker materialBaker,
IAutocadColorBaker colorBaker
AutocadMaterialBaker materialBaker,
AutocadColorBaker colorBaker
)
{
_autocadContext = autocadContext;
@@ -4,7 +4,6 @@ using Autodesk.AutoCAD.GraphicsInterface;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Common.Conversion;
using Speckle.Connectors.Common.Operations;
using Speckle.InterfaceGenerator;
using Speckle.Objects.Other;
using Speckle.Sdk;
using Speckle.Sdk.Common;
@@ -17,8 +16,7 @@ namespace Speckle.Connectors.Autocad.HostApp;
/// <summary>
/// Expects to be a scoped dependency for a given operation and helps with layer creation and cleanup.
/// </summary>
[GenerateAutoInterface]
public class AutocadMaterialBaker : IAutocadMaterialBaker
public class AutocadMaterialBaker
{
private readonly ILogger<AutocadMaterialBaker> _logger;
private readonly AutocadContext _autocadContext;
@@ -20,44 +20,86 @@ namespace Speckle.Connectors.Autocad.Operations.Receive;
/// <summary>
/// <para>Expects to be a scoped dependency per receive operation.</para>
/// </summary>
public class AutocadHostObjectBuilder(
IRootToHostConverter converter,
AutocadLayerBaker layerBaker,
AutocadGroupBaker groupBaker,
AutocadInstanceBaker instanceBaker,
IAutocadMaterialBaker materialBaker,
IAutocadColorBaker colorBaker,
AutocadContext autocadContext,
RootObjectUnpacker rootObjectUnpacker
) : IHostObjectBuilder
public class AutocadHostObjectBuilder : IHostObjectBuilder
{
public Task<HostObjectBuilderResult> Build(
private readonly AutocadLayerBaker _layerBaker;
private readonly IRootToHostConverter _converter;
private readonly ISyncToThread _syncToThread;
private readonly AutocadGroupBaker _groupBaker;
private readonly AutocadMaterialBaker _materialBaker;
private readonly AutocadColorBaker _colorBaker;
private readonly AutocadInstanceBaker _instanceBaker;
private readonly AutocadContext _autocadContext;
private readonly RootObjectUnpacker _rootObjectUnpacker;
public AutocadHostObjectBuilder(
IRootToHostConverter converter,
AutocadLayerBaker layerBaker,
AutocadGroupBaker groupBaker,
AutocadInstanceBaker instanceBaker,
AutocadMaterialBaker materialBaker,
AutocadColorBaker colorBaker,
ISyncToThread syncToThread,
AutocadContext autocadContext,
RootObjectUnpacker rootObjectUnpacker
)
{
_converter = converter;
_layerBaker = layerBaker;
_groupBaker = groupBaker;
_instanceBaker = instanceBaker;
_materialBaker = materialBaker;
_colorBaker = colorBaker;
_syncToThread = syncToThread;
_autocadContext = autocadContext;
_rootObjectUnpacker = rootObjectUnpacker;
}
public async Task<HostObjectBuilderResult> Build(
Base rootObject,
string projectName,
string modelName,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
CancellationToken _
)
{
// NOTE: This is the only place we apply ISyncToThread across connectors. We need to sync up with main thread here
// after GetObject and Deserialization. It is anti-pattern now. Happiness level 3/10 but works.
return await _syncToThread
.RunOnThread(async () =>
{
await Task.CompletedTask.ConfigureAwait(true);
return BuildSync(rootObject, projectName, modelName, onOperationProgressed);
})
.ConfigureAwait(false);
}
private HostObjectBuilderResult BuildSync(
Base rootObject,
string projectName,
string modelName,
IProgress<CardProgress> onOperationProgressed
)
{
// Prompt the UI conversion started. Progress bar will swoosh.
onOperationProgressed.Report(new("Converting", null));
// Layer filter for received commit with project and model name
layerBaker.CreateLayerFilter(projectName, modelName);
_layerBaker.CreateLayerFilter(projectName, modelName);
// 0 - Clean then Rock n Roll!
string baseLayerPrefix = autocadContext.RemoveInvalidChars($"SPK-{projectName}-{modelName}-");
string baseLayerPrefix = _autocadContext.RemoveInvalidChars($"SPK-{projectName}-{modelName}-");
PreReceiveDeepClean(baseLayerPrefix);
// 1 - Unpack objects and proxies from root commit object
var unpackedRoot = rootObjectUnpacker.Unpack(rootObject);
var unpackedRoot = _rootObjectUnpacker.Unpack(rootObject);
// 2 - Split atomic objects and instance components with their path
var (atomicObjects, instanceComponents) = rootObjectUnpacker.SplitAtomicObjectsAndInstances(
var (atomicObjects, instanceComponents) = _rootObjectUnpacker.SplitAtomicObjectsAndInstances(
unpackedRoot.ObjectsToConvert
);
var atomicObjectsWithPath = layerBaker.GetAtomicObjectsWithPath(atomicObjects);
var instanceComponentsWithPath = layerBaker.GetInstanceComponentsWithPath(instanceComponents);
var atomicObjectsWithPath = _layerBaker.GetAtomicObjectsWithPath(atomicObjects);
var instanceComponentsWithPath = _layerBaker.GetInstanceComponentsWithPath(instanceComponents);
// POC: these are not captured by traversal, so we need to re-add them here
if (unpackedRoot.DefinitionProxies != null && unpackedRoot.DefinitionProxies.Count > 0)
@@ -71,7 +113,7 @@ public class AutocadHostObjectBuilder(
// 3 - Bake materials and colors, as they are used later down the line by layers and objects
if (unpackedRoot.RenderMaterialProxies != null)
{
materialBaker.ParseAndBakeRenderMaterials(
_materialBaker.ParseAndBakeRenderMaterials(
unpackedRoot.RenderMaterialProxies,
baseLayerPrefix,
onOperationProgressed
@@ -80,10 +122,10 @@ public class AutocadHostObjectBuilder(
if (unpackedRoot.ColorProxies != null)
{
colorBaker.ParseColors(unpackedRoot.ColorProxies, onOperationProgressed);
_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();
@@ -92,7 +134,6 @@ public class AutocadHostObjectBuilder(
{
string objectId = atomicObject.applicationId ?? atomicObject.id.NotNull();
onOperationProgressed.Report(new("Converting objects", (double)++count / atomicObjects.Count));
cancellationToken.ThrowIfCancellationRequested();
try
{
IReadOnlyCollection<Entity> convertedObjects = ConvertObject(atomicObject, layerPath, baseLayerPrefix);
@@ -116,8 +157,8 @@ public class AutocadHostObjectBuilder(
}
}
// 5 - Convert instances
var (createdInstanceIds, consumedObjectIds, instanceConversionResults) = instanceBaker.BakeInstances(
// 6 - Convert instances
var (createdInstanceIds, consumedObjectIds, instanceConversionResults) = _instanceBaker.BakeInstances(
instanceComponentsWithPath,
applicationIdMap,
baseLayerPrefix,
@@ -129,52 +170,46 @@ 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(
IReadOnlyCollection<ReceiveConversionResult> groupResults = _groupBaker.CreateGroups(
unpackedRoot.GroupProxies,
applicationIdMap
);
results.UnionWith(groupResults);
}
return Task.FromResult(new HostObjectBuilderResult(bakedObjectIds, results));
return new HostObjectBuilderResult(bakedObjectIds, results);
}
private void PreReceiveDeepClean(string baseLayerPrefix)
{
layerBaker.DeleteAllLayersByPrefix(baseLayerPrefix);
instanceBaker.PurgeInstances(baseLayerPrefix);
materialBaker.PurgeMaterials(baseLayerPrefix);
_layerBaker.DeleteAllLayersByPrefix(baseLayerPrefix);
_instanceBaker.PurgeInstances(baseLayerPrefix);
_materialBaker.PurgeMaterials(baseLayerPrefix);
}
private IReadOnlyCollection<Entity> ConvertObject(Base obj, Collection[] layerPath, string baseLayerNamePrefix)
{
string layerName = layerBaker.CreateLayerForReceive(layerPath, baseLayerNamePrefix);
string layerName = _layerBaker.CreateLayerForReceive(layerPath, baseLayerNamePrefix);
var convertedEntities = new HashSet<Entity>();
using var tr = Application.DocumentManager.CurrentDocument.Database.TransactionManager.StartTransaction();
// 1: convert
var converted = converter.Convert(obj);
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();
@@ -184,12 +219,12 @@ public class AutocadHostObjectBuilder(
private Entity BakeObject(Entity entity, Base originalObject, string layerName, Base? parentObject = null)
{
var objId = originalObject.applicationId ?? originalObject.id.NotNull();
if (colorBaker.ObjectColorsIdMap.TryGetValue(objId, out AutocadColor? color))
if (_colorBaker.ObjectColorsIdMap.TryGetValue(objId, out AutocadColor? color))
{
entity.Color = color;
}
if (materialBaker.TryGetMaterialId(originalObject, parentObject, out ObjectId matId))
if (_materialBaker.TryGetMaterialId(originalObject, parentObject, out ObjectId matId))
{
entity.MaterialId = matId;
}
@@ -214,16 +249,11 @@ 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);
var groupName = autocadContext.RemoveInvalidChars(
var groupName = _autocadContext.RemoveInvalidChars(
$@"{parentObject.speckle_type.Split('.').Last()} - {parentObject.applicationId ?? parentObject.id} ({baseLayerName})"
);
@@ -49,6 +49,13 @@ public abstract class AutocadRootObjectBaseBuilder : IRootObjectBuilder<AutocadR
_activityFactory = activityFactory;
}
public Task<RootObjectBuilderResult> Build(
IReadOnlyList<AutocadRootObject> objects,
SendInfo sendInfo,
IProgress<CardProgress> onOperationProgressed,
CancellationToken ct = default
) => Task.FromResult(BuildSync(objects, sendInfo, onOperationProgressed, ct));
[SuppressMessage(
"Maintainability",
"CA1506:Avoid excessive class coupling",
@@ -58,11 +65,11 @@ public abstract class AutocadRootObjectBaseBuilder : IRootObjectBuilder<AutocadR
proxy classes yet. So I'm supressing this one now!!!
"""
)]
public Task<RootObjectBuilderResult> Build(
private RootObjectBuilderResult BuildSync(
IReadOnlyList<AutocadRootObject> objects,
SendInfo sendInfo,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
CancellationToken ct = default
)
{
// 0 - Init the root
@@ -94,7 +101,7 @@ public abstract class AutocadRootObjectBaseBuilder : IRootObjectBuilder<AutocadR
int count = 0;
foreach (var (entity, applicationId) in atomicObjects)
{
cancellationToken.ThrowIfCancellationRequested();
ct.ThrowIfCancellationRequested();
using (var convertActivity = _activityFactory.Start("Converting object"))
{
// Create and add a collection for this entity if not done so already.
@@ -133,7 +140,7 @@ public abstract class AutocadRootObjectBaseBuilder : IRootObjectBuilder<AutocadR
// add any additional properties (most likely from verticals)
AddAdditionalProxiesToRoot(root);
return Task.FromResult(new RootObjectBuilderResult(root, results));
return new RootObjectBuilderResult(root, results);
}
}
@@ -31,7 +31,7 @@ public class AutocadCommand
return;
}
PaletteSet = new PaletteSet($"Speckle (Beta)", s_id)
PaletteSet = new PaletteSet($"Speckle (Beta) for {AppUtils.App.Name}", s_id)
{
Size = new Size(400, 500),
DockEnabled = (DockSides)((int)DockSides.Left + (int)DockSides.Right)
@@ -52,7 +52,7 @@ public class AutocadCommand
var panelWebView = Container.GetRequiredService<DUI3ControlWebView>();
PaletteSet.AddVisual("Speckle (Beta)", panelWebView);
PaletteSet.AddVisual($"Speckle (Beta) for {AppUtils.App.Name} WebView", panelWebView);
FocusPalette();
}
@@ -46,20 +46,20 @@ public class AutocadRibbon
private void Create()
{
RibbonTab tab = FindOrMakeTab("Speckle");
RibbonPanelSource source = new() { Title = "Speckle (Beta)" };
RibbonTab tab = FindOrMakeTab("Add-ins");
RibbonPanelSource source = new() { Title = "Speckle 2 (New Beta)" };
RibbonPanel panel = new() { Source = source };
tab.Panels.Add(panel);
RibbonToolTip speckleToolTip =
new()
{
Title = "Speckle (Beta)",
Content = $"Next Gen Speckle Connector (Beta) for {AppUtils.App.Name}",
Title = "Speckle 2 (New Beta)",
Content = "Speckle Connector for " + AppUtils.App.Name,
IsHelpEnabled = true // Without this "Press F1 for help" does not appear in the tooltip
};
_ = CreateSpeckleButton("Speckle (Beta)", source, null, speckleToolTip, "logo");
_ = CreateSpeckleButton("Connector " + AppUtils.App.Name + " (New)", source, null, speckleToolTip, "logo");
}
private void ComponentManager_ItemInitialized(object? sender, RibbonItemEventArgs e)
@@ -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" />
@@ -31,6 +29,7 @@
<Compile Include="$(MSBuildThisFileDirectory)HostApp\AutocadContext.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\AutocadDocumentManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\AutocadDocumentModelStore.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\AutocadIdleManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\AutocadMaterialUnpacker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Extensions\DatabaseExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Extensions\DocumentExtensions.cs" />
@@ -10,8 +10,8 @@
<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>
@@ -173,6 +173,11 @@
"resolved": "8.0.0",
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
},
"Speckle.DoubleNumerics": {
"type": "Transitive",
"resolved": "4.0.1",
"contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w=="
},
"Speckle.Newtonsoft.Json": {
"type": "Transitive",
"resolved": "13.0.2",
@@ -268,9 +273,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.1.8, )",
"Speckle.Sdk": "[3.1.8, )",
"Speckle.Sdk.Dependencies": "[3.1.8, )"
"Speckle.Objects": "[3.1.0-dev.219, )",
"Speckle.Sdk": "[3.1.0-dev.219, )",
"Speckle.Sdk.Dependencies": "[3.1.0-dev.219, )"
}
},
"speckle.connectors.dui": {
@@ -278,8 +283,9 @@
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.8, )",
"Speckle.Sdk.Dependencies": "[3.1.8, )"
"Speckle.Sdk": "[3.1.0-dev.219, )",
"Speckle.Sdk.Dependencies": "[3.1.0-dev.219, )",
"System.Threading.Tasks.Dataflow": "[6.0.0, )"
}
},
"speckle.connectors.dui.webview": {
@@ -304,7 +310,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.1.8, )"
"Speckle.Objects": "[3.1.0-dev.219, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -340,26 +346,20 @@
"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.1.8, )",
"resolved": "3.1.8",
"contentHash": "rM1t+E8NIWFCPC3ndnMAI71B9ONiPFsz6GuIIdc5kth5AggQRGArPCalJBwA33ith2WtbjWOanI+PhHmpGQgGw==",
"requested": "[3.1.0-dev.219, )",
"resolved": "3.1.0-dev.219",
"contentHash": "2thsMUDkRr7lR0PXZJ8k3KWZLWjz/aBY0KaO2SuXM0lIKyJw1APMJ7lXJ89e6JAjBzEbK8yLHBb0qtfcc4bNRw==",
"dependencies": {
"Speckle.Sdk": "3.1.8"
"Speckle.Sdk": "3.1.0-dev.219"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.1.8, )",
"resolved": "3.1.8",
"contentHash": "qIYRiYHO2UkKuy8ZbjYltKCqLesIcJOcCGTEzyTCbyij5aC1x8HnwFjGMeonUCPaeipEKhI/4HidEZ0CwWvnXg==",
"requested": "[3.1.0-dev.219, )",
"resolved": "3.1.0-dev.219",
"contentHash": "UTgz9UgkMFGqRLmCq1HPo74MngvKbSZOFWaMRzASHrrnsBtzZatBXjoJBlvVoeBCoiea5Jj+BgZg6RvlV2m3sg==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -367,16 +367,22 @@
"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.DoubleNumerics": "4.0.1",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.1.8"
"Speckle.Sdk.Dependencies": "3.1.0-dev.219"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.1.8, )",
"resolved": "3.1.8",
"contentHash": "+MWIemub3nlPnDJ7sHl5nDGFDIIfiBU2VxqNmTGKzWNtCjsDi6KYw5zpbvXnaNcIqFoexv3TKCurjvT2ejzyQw=="
"requested": "[3.1.0-dev.219, )",
"resolved": "3.1.0-dev.219",
"contentHash": "5hqBd9whAs5cC8S/6YUgKFOivjntAkVutKhz06XD/f3Kj6xScT3hwcUaWBxiwK3Mw7FLtiVuU1uG777pjn0PKQ=="
},
"System.Threading.Tasks.Dataflow": {
"type": "CentralTransitive",
"requested": "[6.0.0, )",
"resolved": "6.0.0",
"contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA=="
}
}
}
@@ -11,8 +11,8 @@
</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>
@@ -173,6 +173,11 @@
"resolved": "8.0.0",
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
},
"Speckle.DoubleNumerics": {
"type": "Transitive",
"resolved": "4.0.1",
"contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w=="
},
"Speckle.Newtonsoft.Json": {
"type": "Transitive",
"resolved": "13.0.2",
@@ -268,9 +273,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.1.8, )",
"Speckle.Sdk": "[3.1.8, )",
"Speckle.Sdk.Dependencies": "[3.1.8, )"
"Speckle.Objects": "[3.1.0-dev.219, )",
"Speckle.Sdk": "[3.1.0-dev.219, )",
"Speckle.Sdk.Dependencies": "[3.1.0-dev.219, )"
}
},
"speckle.connectors.dui": {
@@ -278,8 +283,9 @@
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.8, )",
"Speckle.Sdk.Dependencies": "[3.1.8, )"
"Speckle.Sdk": "[3.1.0-dev.219, )",
"Speckle.Sdk.Dependencies": "[3.1.0-dev.219, )",
"System.Threading.Tasks.Dataflow": "[6.0.0, )"
}
},
"speckle.connectors.dui.webview": {
@@ -304,7 +310,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.1.8, )"
"Speckle.Objects": "[3.1.0-dev.219, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -340,26 +346,20 @@
"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.1.8, )",
"resolved": "3.1.8",
"contentHash": "rM1t+E8NIWFCPC3ndnMAI71B9ONiPFsz6GuIIdc5kth5AggQRGArPCalJBwA33ith2WtbjWOanI+PhHmpGQgGw==",
"requested": "[3.1.0-dev.219, )",
"resolved": "3.1.0-dev.219",
"contentHash": "2thsMUDkRr7lR0PXZJ8k3KWZLWjz/aBY0KaO2SuXM0lIKyJw1APMJ7lXJ89e6JAjBzEbK8yLHBb0qtfcc4bNRw==",
"dependencies": {
"Speckle.Sdk": "3.1.8"
"Speckle.Sdk": "3.1.0-dev.219"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.1.8, )",
"resolved": "3.1.8",
"contentHash": "qIYRiYHO2UkKuy8ZbjYltKCqLesIcJOcCGTEzyTCbyij5aC1x8HnwFjGMeonUCPaeipEKhI/4HidEZ0CwWvnXg==",
"requested": "[3.1.0-dev.219, )",
"resolved": "3.1.0-dev.219",
"contentHash": "UTgz9UgkMFGqRLmCq1HPo74MngvKbSZOFWaMRzASHrrnsBtzZatBXjoJBlvVoeBCoiea5Jj+BgZg6RvlV2m3sg==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -367,16 +367,22 @@
"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.DoubleNumerics": "4.0.1",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.1.8"
"Speckle.Sdk.Dependencies": "3.1.0-dev.219"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.1.8, )",
"resolved": "3.1.8",
"contentHash": "+MWIemub3nlPnDJ7sHl5nDGFDIIfiBU2VxqNmTGKzWNtCjsDi6KYw5zpbvXnaNcIqFoexv3TKCurjvT2ejzyQw=="
"requested": "[3.1.0-dev.219, )",
"resolved": "3.1.0-dev.219",
"contentHash": "5hqBd9whAs5cC8S/6YUgKFOivjntAkVutKhz06XD/f3Kj6xScT3hwcUaWBxiwK3Mw7FLtiVuU1uG777pjn0PKQ=="
},
"System.Threading.Tasks.Dataflow": {
"type": "CentralTransitive",
"requested": "[6.0.0, )",
"resolved": "6.0.0",
"contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA=="
}
}
}
@@ -11,8 +11,8 @@
</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>
@@ -173,6 +173,11 @@
"resolved": "8.0.0",
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
},
"Speckle.DoubleNumerics": {
"type": "Transitive",
"resolved": "4.0.1",
"contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w=="
},
"Speckle.Newtonsoft.Json": {
"type": "Transitive",
"resolved": "13.0.2",
@@ -268,9 +273,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.1.8, )",
"Speckle.Sdk": "[3.1.8, )",
"Speckle.Sdk.Dependencies": "[3.1.8, )"
"Speckle.Objects": "[3.1.0-dev.219, )",
"Speckle.Sdk": "[3.1.0-dev.219, )",
"Speckle.Sdk.Dependencies": "[3.1.0-dev.219, )"
}
},
"speckle.connectors.dui": {
@@ -278,8 +283,9 @@
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.8, )",
"Speckle.Sdk.Dependencies": "[3.1.8, )"
"Speckle.Sdk": "[3.1.0-dev.219, )",
"Speckle.Sdk.Dependencies": "[3.1.0-dev.219, )",
"System.Threading.Tasks.Dataflow": "[6.0.0, )"
}
},
"speckle.connectors.dui.webview": {
@@ -304,7 +310,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.1.8, )"
"Speckle.Objects": "[3.1.0-dev.219, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -340,26 +346,20 @@
"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.1.8, )",
"resolved": "3.1.8",
"contentHash": "rM1t+E8NIWFCPC3ndnMAI71B9ONiPFsz6GuIIdc5kth5AggQRGArPCalJBwA33ith2WtbjWOanI+PhHmpGQgGw==",
"requested": "[3.1.0-dev.219, )",
"resolved": "3.1.0-dev.219",
"contentHash": "2thsMUDkRr7lR0PXZJ8k3KWZLWjz/aBY0KaO2SuXM0lIKyJw1APMJ7lXJ89e6JAjBzEbK8yLHBb0qtfcc4bNRw==",
"dependencies": {
"Speckle.Sdk": "3.1.8"
"Speckle.Sdk": "3.1.0-dev.219"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.1.8, )",
"resolved": "3.1.8",
"contentHash": "qIYRiYHO2UkKuy8ZbjYltKCqLesIcJOcCGTEzyTCbyij5aC1x8HnwFjGMeonUCPaeipEKhI/4HidEZ0CwWvnXg==",
"requested": "[3.1.0-dev.219, )",
"resolved": "3.1.0-dev.219",
"contentHash": "UTgz9UgkMFGqRLmCq1HPo74MngvKbSZOFWaMRzASHrrnsBtzZatBXjoJBlvVoeBCoiea5Jj+BgZg6RvlV2m3sg==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -367,16 +367,22 @@
"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.DoubleNumerics": "4.0.1",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.1.8"
"Speckle.Sdk.Dependencies": "3.1.0-dev.219"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.1.8, )",
"resolved": "3.1.8",
"contentHash": "+MWIemub3nlPnDJ7sHl5nDGFDIIfiBU2VxqNmTGKzWNtCjsDi6KYw5zpbvXnaNcIqFoexv3TKCurjvT2ejzyQw=="
"requested": "[3.1.0-dev.219, )",
"resolved": "3.1.0-dev.219",
"contentHash": "5hqBd9whAs5cC8S/6YUgKFOivjntAkVutKhz06XD/f3Kj6xScT3hwcUaWBxiwK3Mw7FLtiVuU1uG777pjn0PKQ=="
},
"System.Threading.Tasks.Dataflow": {
"type": "CentralTransitive",
"requested": "[6.0.0, )",
"resolved": "6.0.0",
"contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA=="
}
}
}
@@ -13,8 +13,9 @@
</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>
@@ -164,6 +164,11 @@
"resolved": "8.0.0",
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
},
"Speckle.DoubleNumerics": {
"type": "Transitive",
"resolved": "4.0.1",
"contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w=="
},
"Speckle.Newtonsoft.Json": {
"type": "Transitive",
"resolved": "13.0.2",
@@ -224,9 +229,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.1.8, )",
"Speckle.Sdk": "[3.1.8, )",
"Speckle.Sdk.Dependencies": "[3.1.8, )"
"Speckle.Objects": "[3.1.0-dev.219, )",
"Speckle.Sdk": "[3.1.0-dev.219, )",
"Speckle.Sdk.Dependencies": "[3.1.0-dev.219, )"
}
},
"speckle.connectors.dui": {
@@ -234,8 +239,9 @@
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.8, )",
"Speckle.Sdk.Dependencies": "[3.1.8, )"
"Speckle.Sdk": "[3.1.0-dev.219, )",
"Speckle.Sdk.Dependencies": "[3.1.0-dev.219, )",
"System.Threading.Tasks.Dataflow": "[6.0.0, )"
}
},
"speckle.connectors.dui.webview": {
@@ -261,7 +267,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.1.8, )"
"Speckle.Objects": "[3.1.0-dev.219, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -297,42 +303,42 @@
"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.1.8, )",
"resolved": "3.1.8",
"contentHash": "rM1t+E8NIWFCPC3ndnMAI71B9ONiPFsz6GuIIdc5kth5AggQRGArPCalJBwA33ith2WtbjWOanI+PhHmpGQgGw==",
"requested": "[3.1.0-dev.219, )",
"resolved": "3.1.0-dev.219",
"contentHash": "2thsMUDkRr7lR0PXZJ8k3KWZLWjz/aBY0KaO2SuXM0lIKyJw1APMJ7lXJ89e6JAjBzEbK8yLHBb0qtfcc4bNRw==",
"dependencies": {
"Speckle.Sdk": "3.1.8"
"Speckle.Sdk": "3.1.0-dev.219"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.1.8, )",
"resolved": "3.1.8",
"contentHash": "qIYRiYHO2UkKuy8ZbjYltKCqLesIcJOcCGTEzyTCbyij5aC1x8HnwFjGMeonUCPaeipEKhI/4HidEZ0CwWvnXg==",
"requested": "[3.1.0-dev.219, )",
"resolved": "3.1.0-dev.219",
"contentHash": "UTgz9UgkMFGqRLmCq1HPo74MngvKbSZOFWaMRzASHrrnsBtzZatBXjoJBlvVoeBCoiea5Jj+BgZg6RvlV2m3sg==",
"dependencies": {
"GraphQL.Client": "6.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",
"Speckle.DoubleNumerics": "4.0.1",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.1.8"
"Speckle.Sdk.Dependencies": "3.1.0-dev.219"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.1.8, )",
"resolved": "3.1.8",
"contentHash": "+MWIemub3nlPnDJ7sHl5nDGFDIIfiBU2VxqNmTGKzWNtCjsDi6KYw5zpbvXnaNcIqFoexv3TKCurjvT2ejzyQw=="
"requested": "[3.1.0-dev.219, )",
"resolved": "3.1.0-dev.219",
"contentHash": "5hqBd9whAs5cC8S/6YUgKFOivjntAkVutKhz06XD/f3Kj6xScT3hwcUaWBxiwK3Mw7FLtiVuU1uG777pjn0PKQ=="
},
"System.Threading.Tasks.Dataflow": {
"type": "CentralTransitive",
"requested": "[6.0.0, )",
"resolved": "6.0.0",
"contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -1,60 +0,0 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
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;
using Speckle.Converters.Autocad;
using Speckle.Converters.Civil3dShared;
using Speckle.Converters.Common;
using Speckle.Sdk;
namespace Speckle.Connectors.Civil3dShared.Bindings;
public sealed class Civil3dReceiveBinding : AutocadReceiveBaseBinding
{
private readonly ICivil3dConversionSettingsFactory _civil3dConversionSettingsFactory;
private readonly IAutocadConversionSettingsFactory _autocadConversionSettingsFactory;
public Civil3dReceiveBinding(
DocumentModelStore store,
IBrowserBridge parent,
ICancellationManager cancellationManager,
IServiceProvider serviceProvider,
IOperationProgressManager operationProgressManager,
ILogger<AutocadReceiveBinding> logger,
ICivil3dConversionSettingsFactory civil3dConversionSettingsFactory,
IAutocadConversionSettingsFactory autocadConversionSettingsFactory,
ISpeckleApplication speckleApplication,
IThreadContext threadContext
)
: base(
store,
parent,
cancellationManager,
serviceProvider,
operationProgressManager,
logger,
speckleApplication,
threadContext
)
{
_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)
{
serviceProvider
.GetRequiredService<IConverterSettingsStore<Civil3dConversionSettings>>()
.Initialize(_civil3dConversionSettingsFactory.Create(Application.DocumentManager.CurrentDocument));
serviceProvider
.GetRequiredService<IConverterSettingsStore<AutocadConversionSettings>>()
.Initialize(_autocadConversionSettingsFactory.Create(Application.DocumentManager.CurrentDocument));
}
}
@@ -1,9 +1,9 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Autocad.Bindings;
using Speckle.Connectors.Autocad.HostApp;
using Speckle.Connectors.Common.Caching;
using Speckle.Connectors.Common.Cancellation;
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Models;
@@ -22,22 +22,21 @@ public sealed class Civil3dSendBinding : AutocadSendBaseBinding
public Civil3dSendBinding(
DocumentModelStore store,
IAutocadIdleManager idleManager,
IBrowserBridge parent,
IEnumerable<ISendFilter> sendFilters,
ICancellationManager cancellationManager,
CancellationManager cancellationManager,
IServiceProvider serviceProvider,
ISendConversionCache sendConversionCache,
IOperationProgressManager operationProgressManager,
ILogger<AutocadSendBinding> logger,
ICivil3dConversionSettingsFactory civil3dConversionSettingsFactory,
IAutocadConversionSettingsFactory autocadConversionSettingsFactory,
ISpeckleApplication speckleApplication,
IThreadContext threadContext,
ITopLevelExceptionHandler topLevelExceptionHandler,
IAppIdleManager appIdleManager
ISpeckleApplication speckleApplication
)
: base(
store,
idleManager,
parent,
sendFilters,
cancellationManager,
@@ -45,10 +44,7 @@ public sealed class Civil3dSendBinding : AutocadSendBaseBinding
sendConversionCache,
operationProgressManager,
logger,
speckleApplication,
threadContext,
topLevelExceptionHandler,
appIdleManager
speckleApplication
)
{
_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,5 +1,4 @@
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Models.Card;
@@ -11,8 +10,7 @@ public class CsiSharedBasicConnectorBinding : IBasicConnectorBinding
{
private readonly ISpeckleApplication _speckleApplication;
private readonly DocumentModelStore _store;
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
private readonly IThreadContext _threadContext;
public string Name => "baseBinding";
public IBrowserBridge Parent { get; }
public BasicConnectorBindingCommands Commands { get; }
@@ -20,27 +18,13 @@ public class CsiSharedBasicConnectorBinding : IBasicConnectorBinding
public CsiSharedBasicConnectorBinding(
IBrowserBridge parent,
ISpeckleApplication speckleApplication,
DocumentModelStore store,
ITopLevelExceptionHandler topLevelExceptionHandler,
IThreadContext threadContext
DocumentModelStore store
)
{
_threadContext = threadContext;
Parent = parent;
_speckleApplication = speckleApplication;
_store = store;
_topLevelExceptionHandler = topLevelExceptionHandler;
Commands = new BasicConnectorBindingCommands(Parent);
_store.DocumentChanged += (_, _) =>
_topLevelExceptionHandler.FireAndForget(async () =>
{
// enforce main thread
await _threadContext.RunOnMainAsync(async () =>
{
await Commands.NotifyDocumentChanged();
});
});
Commands = new BasicConnectorBindingCommands(parent);
}
public string GetConnectorVersion() => _speckleApplication.SpeckleVersion;
@@ -49,24 +33,15 @@ public class CsiSharedBasicConnectorBinding : IBasicConnectorBinding
public string GetSourceApplicationVersion() => _speckleApplication.HostApplicationVersion;
public DocumentInfo? GetDocumentInfo() => new("ETABS Model", "ETABS Model", "1");
public DocumentInfo? GetDocumentInfo() => new DocumentInfo("ETABS Model", "ETABS Model", "1");
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,78 +1,20 @@
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.CSiShared.HostApp;
using Speckle.Connectors.CSiShared.HostApp;
using Speckle.Connectors.CSiShared.Utils;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Converters.CSiShared.Utils;
using Timer = System.Timers.Timer;
namespace Speckle.Connectors.CSiShared.Bindings;
public class CsiSharedSelectionBinding : ISelectionBinding, IDisposable
public 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";
public IBrowserBridge Parent { get; }
private readonly ICsiApplicationService _csiApplicationService;
public CsiSharedSelectionBinding(
IBrowserBridge parent,
ICsiApplicationService csiApplicationService,
ITopLevelExceptionHandler topLevelExceptionHandler,
IThreadContext threadContext
)
public CsiSharedSelectionBinding(IBrowserBridge parent, ICsiApplicationService csiApplicationService)
{
_threadContext = threadContext;
Parent = parent;
_csiApplicationService = csiApplicationService;
_topLevelExceptionHandler = topLevelExceptionHandler;
_selectionTimer = new Timer(1000);
_selectionTimer.Elapsed += (_, _) =>
_topLevelExceptionHandler.CatchUnhandled(() => _threadContext.RunOnMain(CheckSelectionChanged));
_selectionTimer.Start();
}
private void CheckSelectionChanged()
{
// 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)
)
);
}
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_selectionTimer?.Dispose();
}
_disposed = true;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
@@ -83,26 +25,42 @@ public class CsiSharedSelectionBinding : ISelectionBinding, IDisposable
/// </remarks>
public SelectionInfo GetSelection()
{
// TODO: Since this is standard across CSi Suite - better stored in an enum?
var objectTypeMap = new Dictionary<int, string>
{
{ 1, "Point" },
{ 2, "Frame" },
{ 3, "Cable" },
{ 4, "Tendon" },
{ 5, "Area" },
{ 6, "Solid" },
{ 7, "Link" }
};
int numberItems = 0;
int[] objectType = [];
string[] objectName = [];
int[] objectType = Array.Empty<int>();
string[] objectName = Array.Empty<string>();
_csiApplicationService.SapModel.SelectObj.GetSelected(ref numberItems, ref objectType, ref objectName);
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]));
var typeKey = objectType[i];
var typeName = objectTypeMap.TryGetValue(typeKey, out var name) ? name : $"Unknown ({typeKey})";
encodedIds.Add(ObjectIdentifier.Encode(typeKey, objectName[i]));
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);
}
}
@@ -27,9 +27,10 @@ public sealed class CsiSharedSendBinding : ISendBinding
public IBrowserBridge Parent { get; }
private readonly DocumentModelStore _store;
private readonly IAppIdleManager _idleManager;
private readonly IServiceProvider _serviceProvider;
private readonly List<ISendFilter> _sendFilters;
private readonly ICancellationManager _cancellationManager;
private readonly CancellationManager _cancellationManager;
private readonly IOperationProgressManager _operationProgressManager;
private readonly ILogger<CsiSharedSendBinding> _logger;
private readonly ICsiApplicationService _csiApplicationService;
@@ -39,10 +40,11 @@ public sealed class CsiSharedSendBinding : ISendBinding
public CsiSharedSendBinding(
DocumentModelStore store,
IAppIdleManager idleManager,
IBrowserBridge parent,
IEnumerable<ISendFilter> sendFilters,
IServiceProvider serviceProvider,
ICancellationManager cancellationManager,
CancellationManager cancellationManager,
IOperationProgressManager operationProgressManager,
ILogger<CsiSharedSendBinding> logger,
ICsiConversionSettingsFactory csiConversionSettingsFactory,
@@ -52,6 +54,7 @@ public sealed class CsiSharedSendBinding : ISendBinding
)
{
_store = store;
_idleManager = idleManager;
_serviceProvider = serviceProvider;
_sendFilters = sendFilters.ToList();
_cancellationManager = cancellationManager;
@@ -84,7 +87,7 @@ public sealed class CsiSharedSendBinding : ISendBinding
.ServiceProvider.GetRequiredService<IConverterSettingsStore<CsiConversionSettings>>()
.Initialize(_csiConversionSettingsFactory.Create(_csiApplicationService.SapModel));
using var cancellationItem = _cancellationManager.GetCancellationItem(modelCardId);
CancellationToken cancellationToken = _cancellationManager.InitCancellationTokenSource(modelCardId);
List<ICsiWrapper> wrappers = modelCard
.SendFilter.NotNull()
@@ -101,12 +104,15 @@ public sealed class CsiSharedSendBinding : ISendBinding
.ServiceProvider.GetRequiredService<SendOperation<ICsiWrapper>>()
.Execute(
wrappers,
modelCard.GetSendInfo(_speckleApplication.ApplicationAndVersion),
_operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationItem.Token),
cancellationItem.Token
);
modelCard.GetSendInfo(_speckleApplication.Slug),
_operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationToken),
cancellationToken
)
.ConfigureAwait(false);
await Commands.SetModelSendResult(modelCardId, sendResult.RootObjId, sendResult.ConversionResults);
await Commands
.SetModelSendResult(modelCardId, sendResult.RootObjId, sendResult.ConversionResults)
.ConfigureAwait(false);
}
catch (OperationCanceledException)
{
@@ -115,7 +121,7 @@ public sealed class CsiSharedSendBinding : ISendBinding
catch (Exception ex) when (!ex.IsFatal())
{
_logger.LogModelCardHandledError(ex);
await Commands.SetModelError(modelCardId, ex);
await Commands.SetModelError(modelCardId, ex).ConfigureAwait(false);
}
}
@@ -13,4 +13,22 @@ namespace Speckle.Connectors.CSiShared.HostApp;
public interface ICsiApplicationService
{
cSapModel SapModel { get; }
void Initialize(cSapModel sapModel, cPluginCallback pluginCallback);
}
public class CsiApplicationService : ICsiApplicationService
{
public cSapModel SapModel { get; private set; }
private cPluginCallback _pluginCallback;
public CsiApplicationService()
{
SapModel = null!;
}
public void Initialize(cSapModel sapModel, cPluginCallback pluginCallback)
{
SapModel = sapModel;
_pluginCallback = pluginCallback;
}
}
@@ -1,99 +1,46 @@
using System.IO;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Utils;
using Speckle.Sdk;
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 string _lastModelFilename = string.Empty;
private bool _disposed;
private string HostAppUserDataPath { get; set; }
private string DocumentStateFile { get; set; }
private string ModelPathHash { get; set; }
public CsiDocumentModelStore(
IJsonSerializer jsonSerializer,
IJsonSerializer jsonSerializerSettings,
ISpeckleApplication speckleApplication,
ILogger<CsiDocumentModelStore> logger,
ICsiApplicationService csiApplicationService,
ITopLevelExceptionHandler topLevelExceptionHandler,
IThreadContext threadContext
ICsiApplicationService csiApplicationService
)
: base(jsonSerializer)
: base(jsonSerializerSettings)
{
_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();
}
private void CheckModelChanges()
{
string currentFilename = _csiApplicationService.SapModel.GetModelFilename();
if (string.IsNullOrEmpty(currentFilename) || currentFilename == _lastModelFilename)
{
return;
}
_lastModelFilename = currentFilename;
SetPaths();
LoadState();
OnDocumentChanged();
}
public override Task OnDocumentStoreInitialized()
{
var currentFilename = _csiApplicationService.SapModel.GetModelFilename();
if (!string.IsNullOrEmpty(currentFilename))
{
_lastModelFilename = currentFilename;
SetPaths();
LoadState();
}
return Task.CompletedTask;
}
private void SetPaths()
{
try
{
ModelPathHash = Crypt.Md5(_csiApplicationService.SapModel.GetModelFilename(), length: 32);
HostAppUserDataPath = Path.Combine(
SpecklePathProvider.UserSpeckleFolderPath,
"ConnectorsFileData",
_speckleApplication.Slug
);
DocumentStateFile = Path.Combine(HostAppUserDataPath, $"{ModelPathHash}.json");
_logger.LogDebug($"Paths set - Hash: {ModelPathHash}, File: {DocumentStateFile}");
}
catch (Exception ex) when (!ex.IsFatal())
{
_logger.LogError(ex, "Error in setting paths for CsiDocumentModelStore");
}
ModelPathHash = Crypt.Md5(_csiApplicationService.SapModel.GetModelFilepath(), length: 32);
HostAppUserDataPath = Path.Combine(
SpecklePathProvider.UserSpeckleFolderPath,
"ConnectorsFileData",
_speckleApplication.Slug
);
DocumentStateFile = Path.Combine(HostAppUserDataPath, $"{ModelPathHash}.json");
}
protected override void HostAppSaveState(string modelCardState)
@@ -104,53 +51,29 @@ public class CsiDocumentModelStore : DocumentModelStore, IDisposable
{
Directory.CreateDirectory(HostAppUserDataPath);
}
File.WriteAllText(DocumentStateFile, modelCardState);
}
catch (Exception ex) when (!ex.IsFatal())
{
_logger.LogError(ex, "Failed to save state");
_logger.LogError(ex.Message);
}
}
protected override void LoadState()
{
try
if (!Directory.Exists(HostAppUserDataPath))
{
if (!File.Exists(DocumentStateFile))
{
ClearAndSave();
return;
}
string serializedState = File.ReadAllText(DocumentStateFile);
LoadFromString(serializedState);
}
catch (Exception ex) when (!ex.IsFatal())
{
_logger.LogError(ex, "Failed to load state, initializing empty state");
ClearAndSave();
}
}
protected virtual void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
if (disposing)
if (!File.Exists(DocumentStateFile))
{
_modelCheckTimer.Dispose();
ClearAndSave();
return;
}
_disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
string serializedState = File.ReadAllText(DocumentStateFile);
LoadFromString(serializedState);
}
}
@@ -0,0 +1,20 @@
using Speckle.Connectors.DUI.Bridge;
namespace Speckle.Connectors.CSiShared.HostApp;
public sealed class CsiIdleManager : AppIdleManager
{
private readonly IIdleCallManager _idleCallManager;
public CsiIdleManager(IIdleCallManager idleCallManager)
: base(idleCallManager)
{
_idleCallManager = idleCallManager;
}
protected override void AddEvent()
{
// TODO: CSi specific idle handling can be added here if needed
_idleCallManager.AppOnIdle(() => { });
}
}
@@ -1,115 +0,0 @@
using Speckle.Converters.Common;
using Speckle.Converters.CSiShared;
using Speckle.Converters.CSiShared.Utils;
namespace Speckle.Connectors.CSiShared.HostApp.Helpers;
/// <summary>
/// Base frame section property extractor for CSi products.
/// </summary>
/// <remarks>
/// Handles common Csi API calls for frame section properties
/// Provides foundation for application-specific extractors.
/// </remarks>
public class CsiFrameSectionPropertyExtractor : IFrameSectionPropertyExtractor
{
private readonly IConverterSettingsStore<CsiConversionSettings> _settingsStore;
public CsiFrameSectionPropertyExtractor(IConverterSettingsStore<CsiConversionSettings> settingsStore)
{
_settingsStore = settingsStore;
}
public void ExtractProperties(string sectionName, Dictionary<string, object?> properties)
{
GetMaterialName(sectionName, properties);
GetSectionProperties(sectionName, properties);
GetPropertyModifiers(sectionName, properties);
}
private void GetMaterialName(string sectionName, Dictionary<string, object?> properties)
{
// get material name
string materialName = string.Empty;
_settingsStore.Current.SapModel.PropFrame.GetMaterial(sectionName, ref materialName);
// append to General Data of properties dictionary
Dictionary<string, object?> generalData = properties.EnsureNested(SectionPropertyCategory.GENERAL_DATA);
generalData["Material"] = materialName;
}
private void GetSectionProperties(string sectionName, Dictionary<string, object?> properties)
{
double crossSectionalArea = 0,
shearAreaInMajorAxisDirection = 0,
shearAreaInMinorAxisDirection = 0,
torsionalConstant = 0,
momentOfInertiaAboutMajorAxis = 0,
momentOfInertiaAboutMinorAxis = 0,
sectionModulusAboutMajorAxis = 0,
sectionModulusAboutMinorAxis = 0,
plasticModulusAboutMajorAxis = 0,
plasticModulusAboutMinorAxis = 0,
radiusOfGyrationAboutMajorAxis = 0,
radiusOfGyrationAboutMinorAxis = 0;
_settingsStore.Current.SapModel.PropFrame.GetSectProps(
sectionName,
ref crossSectionalArea,
ref shearAreaInMajorAxisDirection,
ref shearAreaInMinorAxisDirection,
ref torsionalConstant,
ref momentOfInertiaAboutMajorAxis,
ref momentOfInertiaAboutMinorAxis,
ref sectionModulusAboutMajorAxis,
ref sectionModulusAboutMinorAxis,
ref plasticModulusAboutMajorAxis,
ref plasticModulusAboutMinorAxis,
ref radiusOfGyrationAboutMajorAxis,
ref radiusOfGyrationAboutMinorAxis
);
string distanceUnit = _settingsStore.Current.SpeckleUnits;
string areaUnit = $"{distanceUnit}²"; // // TODO: Formalize this better
string modulusUnit = $"{distanceUnit}³"; // // TODO: Formalize this better
string inertiaUnit = $"{distanceUnit}\u2074"; // TODO: Formalize this better
Dictionary<string, object?> mechanicalProperties = properties.EnsureNested(
SectionPropertyCategory.SECTION_PROPERTIES
);
mechanicalProperties.AddWithUnits("Area", crossSectionalArea, areaUnit);
mechanicalProperties.AddWithUnits("As2", shearAreaInMajorAxisDirection, areaUnit);
mechanicalProperties.AddWithUnits("As3", shearAreaInMinorAxisDirection, areaUnit);
mechanicalProperties.AddWithUnits("J", torsionalConstant, inertiaUnit);
mechanicalProperties.AddWithUnits("I22", momentOfInertiaAboutMajorAxis, inertiaUnit);
mechanicalProperties.AddWithUnits("I33", momentOfInertiaAboutMinorAxis, inertiaUnit);
mechanicalProperties.AddWithUnits("S22", sectionModulusAboutMajorAxis, modulusUnit);
mechanicalProperties.AddWithUnits("S33", sectionModulusAboutMinorAxis, modulusUnit);
mechanicalProperties.AddWithUnits("Z22", plasticModulusAboutMajorAxis, modulusUnit);
mechanicalProperties.AddWithUnits("Z33", plasticModulusAboutMinorAxis, modulusUnit);
mechanicalProperties.AddWithUnits("R22", radiusOfGyrationAboutMajorAxis, distanceUnit);
mechanicalProperties.AddWithUnits("R33", radiusOfGyrationAboutMinorAxis, distanceUnit);
}
private void GetPropertyModifiers(string sectionName, Dictionary<string, object?> properties)
{
double[] stiffnessModifiersArray = [];
_settingsStore.Current.SapModel.PropFrame.GetModifiers(sectionName, ref stiffnessModifiersArray);
Dictionary<string, object?> modifiers =
new()
{
["Cross-section (Axial) Area"] = stiffnessModifiersArray[0],
["Shear Area in 2 Direction"] = stiffnessModifiersArray[1],
["Shear Area in 3 Direction"] = stiffnessModifiersArray[2],
["Torsional Constant"] = stiffnessModifiersArray[3],
["Moment of Inertia about 2 Axis"] = stiffnessModifiersArray[4],
["Moment of Inertia about 3 Axis"] = stiffnessModifiersArray[5],
["Mass"] = stiffnessModifiersArray[6],
["Weight"] = stiffnessModifiersArray[7],
};
Dictionary<string, object?> generalData = properties.EnsureNested(SectionPropertyCategory.GENERAL_DATA);
generalData["Modifiers"] = modifiers;
}
}
@@ -1,212 +0,0 @@
using Speckle.Converters.Common;
using Speckle.Converters.CSiShared;
using Speckle.Converters.CSiShared.Utils;
namespace Speckle.Connectors.CSiShared.HostApp.Helpers;
/// <summary>
/// Base material property extractor for CSi products.
/// </summary>
/// <remarks>
/// Currently, all material property extraction can happen on a CsiShared level which simplifies things a lot.
/// Properties depend on the directional symmetry of the material, hence the switch statements.
/// </remarks>
public class CsiMaterialPropertyExtractor
{
/// <summary>
/// Property strings for all mechanical properties, used by numerous methods.
/// </summary>
private static class MechanicalPropertyNames
{
public const string MODULUS_OF_ELASTICITY = "Modulus of Elasticity, E";
public const string MODULUS_OF_ELASTICITY_ARRAY = "Modulus of Elasticity Array, E";
public const string POISSON_RATIO = "Poisson's Ratio, U";
public const string POISSON_RATIO_ARRAY = "Poisson's Ratio Array, U";
public const string THERMAL_COEFFICIENT = "Coefficient of Thermal Expansion, A";
public const string THERMAL_COEFFICIENT_ARRAY = "Coefficient of Thermal Expansion Array, A";
public const string SHEAR_MODULUS = "Shear Modulus, G";
public const string SHEAR_MODULUS_ARRAY = "Shear Modulus Array, G";
}
private readonly IConverterSettingsStore<CsiConversionSettings> _settingsStore;
public CsiMaterialPropertyExtractor(IConverterSettingsStore<CsiConversionSettings> settingsStore)
{
_settingsStore = settingsStore;
}
public void ExtractProperties(string materialName, Dictionary<string, object?> properties)
{
GetGeneralProperties(materialName, properties);
GetWeightAndMassProperties(materialName, properties); // TODO: Add units
GetMechanicalProperties(materialName, properties); // TODO: Add units
}
private void GetGeneralProperties(string materialName, Dictionary<string, object?> properties)
{
{
eMatType materialType = default;
int materialColor = 0;
string materialNotes = string.Empty;
string materialGuid = string.Empty;
_settingsStore.Current.SapModel.PropMaterial.GetMaterial(
materialName,
ref materialType,
ref materialColor,
ref materialNotes,
ref materialGuid
);
var generalData = properties.EnsureNested(SectionPropertyCategory.GENERAL_DATA);
generalData["Name"] = materialName;
generalData["Type"] = materialType.ToString();
generalData["Notes"] = materialNotes;
}
}
private void GetWeightAndMassProperties(string materialName, Dictionary<string, object?> properties)
{
double weightPerUnitVolume = double.NaN;
double massPerUnitVolume = double.NaN;
_settingsStore.Current.SapModel.PropMaterial.GetWeightAndMass(
materialName,
ref weightPerUnitVolume,
ref massPerUnitVolume
);
var weightAndMass = properties.EnsureNested("Weight and Mass");
weightAndMass["Weight per Unit Volume"] = weightPerUnitVolume;
weightAndMass["Mass per Unit Volume"] = massPerUnitVolume;
}
private void GetMechanicalProperties(string materialName, Dictionary<string, object?> properties)
{
int materialDirectionalSymmetryKey = 0;
eMatType materialType = default;
_settingsStore.Current.SapModel.PropMaterial.GetTypeOAPI(
materialName,
ref materialType,
ref materialDirectionalSymmetryKey
);
var materialDirectionalSymmetryValue = materialDirectionalSymmetryKey switch
{
0 => DirectionalSymmetryType.ISOTROPIC,
1 => DirectionalSymmetryType.ORTHOTROPIC,
2 => DirectionalSymmetryType.ANISOTROPIC,
3 => DirectionalSymmetryType.UNIAXIAL,
_ => throw new ArgumentException($"Unknown symmetry type: {materialDirectionalSymmetryKey}")
};
var mechanicalProperties = properties.EnsureNested("Mechanical Properties");
mechanicalProperties["Directional Symmetry Type"] = materialDirectionalSymmetryValue.ToString();
GetMechanicalPropertiesByType(materialName, materialDirectionalSymmetryValue, mechanicalProperties);
}
private void GetMechanicalPropertiesByType(
string materialName,
DirectionalSymmetryType directionalSymmetryType,
Dictionary<string, object?> mechanicalProperties
)
{
switch (directionalSymmetryType)
{
case DirectionalSymmetryType.ISOTROPIC:
ExtractIsotropicProperties(materialName, mechanicalProperties);
break;
case DirectionalSymmetryType.ORTHOTROPIC:
ExtractOrthotropicProperties(materialName, mechanicalProperties);
break;
case DirectionalSymmetryType.ANISOTROPIC:
ExtractAnisotropicProperties(materialName, mechanicalProperties);
break;
case DirectionalSymmetryType.UNIAXIAL:
ExtractUniaxialProperties(materialName, mechanicalProperties);
break;
default:
throw new ArgumentException($"Unknown directional symmetry type: {directionalSymmetryType}");
}
}
private void ExtractIsotropicProperties(string materialName, Dictionary<string, object?> mechanicalProperties)
{
double modulusOfElasticity = double.NaN;
double poissonRatio = double.NaN;
double thermalCoefficient = double.NaN;
double shearModulus = double.NaN;
_settingsStore.Current.SapModel.PropMaterial.GetMPIsotropic(
materialName,
ref modulusOfElasticity,
ref poissonRatio,
ref thermalCoefficient,
ref shearModulus
);
mechanicalProperties[MechanicalPropertyNames.MODULUS_OF_ELASTICITY] = modulusOfElasticity;
mechanicalProperties[MechanicalPropertyNames.POISSON_RATIO] = poissonRatio;
mechanicalProperties[MechanicalPropertyNames.THERMAL_COEFFICIENT] = thermalCoefficient;
mechanicalProperties[MechanicalPropertyNames.SHEAR_MODULUS] = shearModulus;
}
private void ExtractOrthotropicProperties(string materialName, Dictionary<string, object?> mechanicalProperties)
{
double[] modulusOfElasticityArray = [];
double[] poissonRatioArray = [];
double[] thermalCoefficientArray = [];
double[] shearModulusArray = [];
_settingsStore.Current.SapModel.PropMaterial.GetMPOrthotropic(
materialName,
ref modulusOfElasticityArray,
ref poissonRatioArray,
ref thermalCoefficientArray,
ref shearModulusArray
);
mechanicalProperties[MechanicalPropertyNames.MODULUS_OF_ELASTICITY_ARRAY] = modulusOfElasticityArray;
mechanicalProperties[MechanicalPropertyNames.POISSON_RATIO_ARRAY] = poissonRatioArray;
mechanicalProperties[MechanicalPropertyNames.THERMAL_COEFFICIENT_ARRAY] = thermalCoefficientArray;
mechanicalProperties[MechanicalPropertyNames.SHEAR_MODULUS_ARRAY] = shearModulusArray;
}
private void ExtractAnisotropicProperties(string materialName, Dictionary<string, object?> mechanicalProperties)
{
double[] modulusOfElasticityArray = [];
double[] poissonRatioArray = [];
double[] thermalCoefficientArray = [];
double[] shearModulusArray = [];
_settingsStore.Current.SapModel.PropMaterial.GetMPAnisotropic(
materialName,
ref modulusOfElasticityArray,
ref poissonRatioArray,
ref thermalCoefficientArray,
ref shearModulusArray
);
mechanicalProperties[MechanicalPropertyNames.MODULUS_OF_ELASTICITY_ARRAY] = modulusOfElasticityArray;
mechanicalProperties[MechanicalPropertyNames.POISSON_RATIO_ARRAY] = poissonRatioArray;
mechanicalProperties[MechanicalPropertyNames.THERMAL_COEFFICIENT_ARRAY] = thermalCoefficientArray;
mechanicalProperties[MechanicalPropertyNames.SHEAR_MODULUS_ARRAY] = shearModulusArray;
}
private void ExtractUniaxialProperties(string materialName, Dictionary<string, object?> mechanicalProperties)
{
double modulusOfElasticity = double.NaN;
double thermalCoefficient = double.NaN;
_settingsStore.Current.SapModel.PropMaterial.GetMPUniaxial(
materialName,
ref modulusOfElasticity,
ref thermalCoefficient
);
mechanicalProperties[MechanicalPropertyNames.MODULUS_OF_ELASTICITY] = modulusOfElasticity;
mechanicalProperties[MechanicalPropertyNames.THERMAL_COEFFICIENT] = thermalCoefficient;
}
}
@@ -1,68 +0,0 @@
using Speckle.Converters.Common;
using Speckle.Converters.CSiShared;
using Speckle.Converters.CSiShared.Utils;
namespace Speckle.Connectors.CSiShared.HostApp.Helpers;
/// <summary>
/// Base shell section property extractor for CSi products.
/// </summary>
/// <remarks>
/// Handles common Csi API calls for shell section properties.
/// Provides foundation for application-specific extractors.
/// </remarks>
public class CsiShellSectionPropertyExtractor : IShellSectionPropertyExtractor
{
private readonly IConverterSettingsStore<CsiConversionSettings> _settingsStore;
public CsiShellSectionPropertyExtractor(IConverterSettingsStore<CsiConversionSettings> settingsStore)
{
_settingsStore = settingsStore;
}
public void ExtractProperties(string sectionName, Dictionary<string, object?> properties)
{
GetPropertyType(sectionName, properties);
GetPropertyModifiers(sectionName, properties);
}
private void GetPropertyType(string sectionName, Dictionary<string, object?> properties)
{
int propertyTypeKey = 1;
_settingsStore.Current.SapModel.PropArea.GetTypeOAPI(sectionName, ref propertyTypeKey);
var propertyTypeValue = propertyTypeKey switch
{
1 => AreaPropertyType.SHELL,
2 => AreaPropertyType.PLANE,
3 => AreaPropertyType.ASOLID,
_ => throw new ArgumentException($"Unknown property type: {propertyTypeKey}"),
};
var generalData = properties.EnsureNested(SectionPropertyCategory.GENERAL_DATA);
generalData["Property Type"] = propertyTypeValue.ToString();
}
private void GetPropertyModifiers(string sectionName, Dictionary<string, object?> properties)
{
double[] stiffnessModifiersArray = [];
_settingsStore.Current.SapModel.PropArea.GetModifiers(sectionName, ref stiffnessModifiersArray);
Dictionary<string, object?> modifiers =
new()
{
["Membrane f11 Direction"] = stiffnessModifiersArray[0],
["Membrane f22 Direction"] = stiffnessModifiersArray[1],
["Membrane f12 Direction"] = stiffnessModifiersArray[2],
["Bending m11 Direction"] = stiffnessModifiersArray[3],
["Bending m22 Direction"] = stiffnessModifiersArray[3],
["Bending m12 Direction"] = stiffnessModifiersArray[4],
["Shear v13 Direction"] = stiffnessModifiersArray[5],
["Shear v23 Direction"] = stiffnessModifiersArray[6],
["Mass"] = stiffnessModifiersArray[7],
["Weight"] = stiffnessModifiersArray[8]
};
var generalData = properties.EnsureNested(SectionPropertyCategory.GENERAL_DATA);
generalData["Modifiers"] = modifiers;
}
}
@@ -1,18 +0,0 @@
namespace Speckle.Connectors.CSiShared.HostApp.Helpers;
/// <summary>
/// Contract for host application specific section property extraction.
/// </summary>
/// <remarks>
/// Mirrors property extraction system pattern by composing with base extractor.
/// Enables both shared and application-specific property extraction in one call.
/// </remarks>
public interface IApplicationSectionPropertyExtractor
{
void ExtractProperties(string sectionName, Dictionary<string, object?> properties);
}
// NOTE: Seemingly silly, but allows us to register the correct extractor for the correct type.
public interface IApplicationFrameSectionPropertyExtractor : IApplicationSectionPropertyExtractor { }
public interface IApplicationShellSectionPropertyExtractor : IApplicationSectionPropertyExtractor { }
@@ -1,14 +0,0 @@
namespace Speckle.Connectors.CSiShared.HostApp.Helpers;
/// <summary>
/// Core contract for section property extraction common across CSi products.
/// </summary>
public interface ISectionPropertyExtractor
{
void ExtractProperties(string sectionName, Dictionary<string, object?> properties);
}
// NOTE: Seemingly silly, but allows us to register the correct extractor for the correct type.
public interface IFrameSectionPropertyExtractor : ISectionPropertyExtractor { }
public interface IShellSectionPropertyExtractor : ISectionPropertyExtractor { }
@@ -1,10 +0,0 @@
using Speckle.Sdk.Models.Proxies;
namespace Speckle.Connectors.CSiShared.HostApp.Helpers;
// NOTE: Interface because Etabs and Sap2000 section unpacking and extraction is different.
// At ServiceRegistration, we inject the correct implementation of the ISectionUnpacker
public interface ISectionUnpacker
{
IEnumerable<GroupProxy> UnpackSections();
}
@@ -1,51 +0,0 @@
using Speckle.Connectors.CSiShared.HostApp.Helpers;
using Speckle.Converters.CSiShared.ToSpeckle.Helpers;
using Speckle.Sdk.Models.Proxies;
namespace Speckle.Connectors.CSiShared.HostApp;
/// <summary>
/// Creates material proxies based on stored entries from the materials cache
/// </summary>
public class MaterialUnpacker
{
private readonly CsiMaterialPropertyExtractor _propertyExtractor;
private readonly CsiToSpeckleCacheSingleton _csiToSpeckleCacheSingleton;
public MaterialUnpacker(
CsiMaterialPropertyExtractor propertyExtractor,
CsiToSpeckleCacheSingleton csiToSpeckleCacheSingleton
)
{
_propertyExtractor = propertyExtractor;
_csiToSpeckleCacheSingleton = csiToSpeckleCacheSingleton;
}
// Creates a list of material proxies from the csi materials cache
public IEnumerable<IProxyCollection> UnpackMaterials()
{
foreach (var kvp in _csiToSpeckleCacheSingleton.MaterialCache)
{
// get the cached entry
string materialName = kvp.Key;
List<string> sectionIds = kvp.Value;
// get the properties of the material
Dictionary<string, object?> properties = new(); // create empty dictionary
_propertyExtractor.ExtractProperties(materialName, properties); // dictionary mutated with respective properties
// create the material proxy
GroupProxy materialProxy =
new()
{
id = materialName,
name = materialName,
applicationId = materialName,
objects = sectionIds,
["properties"] = properties
};
yield return materialProxy;
}
}
}
@@ -1,12 +1,11 @@
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Common.Builders;
using Speckle.Connectors.Common.Caching;
using Speckle.Connectors.Common.Conversion;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.CSiShared.HostApp;
using Speckle.Connectors.CSiShared.HostApp.Helpers;
using Speckle.Converters.Common;
using Speckle.Converters.CSiShared;
using Speckle.Converters.CSiShared.Extensions;
using Speckle.Sdk;
using Speckle.Sdk.Logging;
using Speckle.Sdk.Models;
@@ -14,72 +13,47 @@ using Speckle.Sdk.Models.Collections;
namespace Speckle.Connectors.CSiShared.Builders;
/// <summary>
/// Manages the conversion of CSi model objects and establishes proxy-based relationships.
/// </summary>
/// <remarks>
/// Core responsibilities:
/// - Converts ICsiWrappers to Speckle objects through caching-aware conversion
/// - Creates proxy objects for materials and sections from model data
/// - Establishes relationships between objects and their dependencies
///
/// The builder follows a two-phase process:
/// 1. Conversion Phase: ICsiWrappers → Speckle objects with cached results handling
/// 2. Relationship Phase: Material/section proxy creation and relationship mapping
/// </remarks>
public class CsiRootObjectBuilder : IRootObjectBuilder<ICsiWrapper>
{
private readonly IRootToSpeckleConverter _rootToSpeckleConverter;
private readonly ISendConversionCache _sendConversionCache;
private readonly IConverterSettingsStore<CsiConversionSettings> _converterSettings;
private readonly CsiSendCollectionManager _sendCollectionManager;
private readonly MaterialUnpacker _materialUnpacker;
private readonly ISectionUnpacker _sectionUnpacker;
private readonly ILogger<CsiRootObjectBuilder> _logger;
private readonly ISdkActivityFactory _activityFactory;
private readonly ICsiApplicationService _csiApplicationService;
public CsiRootObjectBuilder(
IRootToSpeckleConverter rootToSpeckleConverter,
ISendConversionCache sendConversionCache,
IConverterSettingsStore<CsiConversionSettings> converterSettings,
CsiSendCollectionManager sendCollectionManager,
MaterialUnpacker materialUnpacker,
ISectionUnpacker sectionUnpacker,
ILogger<CsiRootObjectBuilder> logger,
ISdkActivityFactory activityFactory,
ICsiApplicationService csiApplicationService
)
{
_sendConversionCache = sendConversionCache;
_converterSettings = converterSettings;
_sendCollectionManager = sendCollectionManager;
_materialUnpacker = materialUnpacker;
_sectionUnpacker = sectionUnpacker;
_rootToSpeckleConverter = rootToSpeckleConverter;
_logger = logger;
_activityFactory = activityFactory;
_csiApplicationService = csiApplicationService;
}
/// <summary>
/// Converts Csi objects into a Speckle-compatible object hierarchy with established relationships.
/// </summary>
/// <remarks>
/// Operation sequence:
/// 1. Creates root collection with model metadata
/// 2. Converts each object with caching and progress tracking
/// 3. Creates proxies for materials and sections
/// </remarks>
public async Task<RootObjectBuilderResult> Build(
IReadOnlyList<ICsiWrapper> csiObjects,
SendInfo sendInfo,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
CancellationToken cancellationToken = default
)
{
using var activity = _activityFactory.Start("Build");
string modelFileName = _csiApplicationService.SapModel.GetModelFilename(false) ?? "Unnamed model";
Collection rootObjectCollection =
new() { name = modelFileName, ["units"] = _converterSettings.Current.SpeckleUnits };
Collection rootObjectCollection = new() { name = modelFileName };
rootObjectCollection["units"] = _converterSettings.Current.SpeckleUnits;
List<SendConversionResult> results = new(csiObjects.Count);
int count = 0;
@@ -88,15 +62,14 @@ public class CsiRootObjectBuilder : IRootObjectBuilder<ICsiWrapper>
{
foreach (ICsiWrapper csiObject in csiObjects)
{
cancellationToken.ThrowIfCancellationRequested();
using var _2 = _activityFactory.Start("Convert");
cancellationToken.ThrowIfCancellationRequested();
var result = ConvertCsiObject(csiObject, rootObjectCollection);
var result = ConvertCSiObject(csiObject, rootObjectCollection, sendInfo.ProjectId);
results.Add(result);
count++;
onOperationProgressed.Report(new("Converting", (double)count / csiObjects.Count));
await Task.Yield();
}
}
@@ -105,55 +78,33 @@ public class CsiRootObjectBuilder : IRootObjectBuilder<ICsiWrapper>
throw new SpeckleException("Failed to convert all objects.");
}
using (var _ = _activityFactory.Start("Process Proxies"))
{
// Create and add material proxies
rootObjectCollection[ProxyKeys.MATERIAL] = _materialUnpacker.UnpackMaterials().ToList();
// Create and all section proxies (frame and shell)
rootObjectCollection[ProxyKeys.SECTION] = _sectionUnpacker.UnpackSections().ToList();
}
await Task.Yield();
return new RootObjectBuilderResult(rootObjectCollection, results);
}
/// <summary>
/// Converts a single Csi wrapper "object" to a data object with appropriate collection management.
/// </summary>
private SendConversionResult ConvertCsiObject(ICsiWrapper csiObject, Collection typeCollection)
private SendConversionResult ConvertCSiObject(ICsiWrapper csiObject, Collection typeCollection, string projectId)
{
string applicationId = $"{csiObject.ObjectType}{csiObject.Name}"; // TODO: NO! Use GUID
string sourceType = csiObject.ObjectName;
string applicationId = csiObject switch
{
CsiJointWrapper jointWrapper => jointWrapper.GetSpeckleApplicationId(_csiApplicationService.SapModel),
CsiFrameWrapper frameWrapper => frameWrapper.GetSpeckleApplicationId(_csiApplicationService.SapModel),
CsiCableWrapper cableWrapper => cableWrapper.GetSpeckleApplicationId(_csiApplicationService.SapModel),
CsiTendonWrapper tendonWrapper => tendonWrapper.ObjectName, // No GetGUID method in the Csi API available
CsiShellWrapper shellWrapper => shellWrapper.GetSpeckleApplicationId(_csiApplicationService.SapModel),
CsiSolidWrapper solidWrapper => solidWrapper.GetSpeckleApplicationId(_csiApplicationService.SapModel),
CsiLinkWrapper linkWrapper => linkWrapper.GetSpeckleApplicationId(_csiApplicationService.SapModel),
_ => throw new ArgumentException($"Unsupported wrapper type: {csiObject.GetType()}", nameof(csiObject))
};
try
{
Base converted = _rootToSpeckleConverter.Convert(csiObject);
Base converted;
if (_sendConversionCache.TryGetValue(projectId, applicationId, out ObjectReference? value))
{
converted = value;
}
else
{
converted = _rootToSpeckleConverter.Convert(csiObject);
}
var collection = _sendCollectionManager.AddObjectCollectionToRoot(converted, typeCollection);
collection.elements ??= new List<Base>();
collection.elements.Add(converted);
return new(Status.SUCCESS, applicationId, sourceType, converted);
}
// Expected not implemented:
// TODO: SAP 2000: CsiCableWrapper, CsiSolidWrapper
// TODO: ETABS: CsiLinkWrapper, CsiTendonWrapper
// NOTE: CsiLinkWrapper - not important to data extraction workflow
// NOTE: CsiTendonWrapper - not typically modelled in ETABS, rather SAFE
catch (NotImplementedException ex)
{
_logger.LogError(ex, sourceType);
return new(Status.WARNING, applicationId, sourceType, null, ex);
}
catch (Exception ex) when (!ex.IsFatal())
{
_logger.LogError(ex, sourceType);
@@ -10,8 +10,8 @@ public abstract class CSiPluginBase : cPluginContract, IDisposable
public void Main(ref cSapModel sapModel, ref cPluginCallback pluginCallback)
{
_panel = CreateForm();
_panel.Initialize(ref sapModel, ref pluginCallback);
_panel.FormClosed += (_, _) => Dispose();
_panel.SetSapModel(ref sapModel, ref pluginCallback);
_panel.FormClosed += (s, e) => Dispose();
if (string.Equals(s_modality, "Non-Modal", StringComparison.OrdinalIgnoreCase))
{
@@ -1,10 +1,8 @@
using System.ComponentModel;
using System.Reflection;
using System.Windows.Forms.Integration;
using Microsoft.Extensions.DependencyInjection;
using Speckle.Connectors.Common;
using Speckle.Connectors.CSiShared.HostApp;
using Speckle.Connectors.DUI;
using Speckle.Connectors.DUI.WebView;
using Speckle.Converters.CSiShared;
using Speckle.Sdk.Host;
@@ -12,22 +10,27 @@ using Speckle.Sdk.Host;
namespace Speckle.Connectors.CSiShared;
[DesignerCategory("")]
public abstract class SpeckleFormBase : Form, ICsiApplicationService
public abstract class SpeckleFormBase : Form
{
private ElementHost Host { get; set; }
protected ElementHost Host { get; set; }
public static new ServiceProvider? Container { get; set; }
private cSapModel _sapModel;
private cPluginCallback _pluginCallback;
private bool _disposed;
#pragma warning disable CA2213
private ServiceProvider _container;
#pragma warning restore CA2213
protected SpeckleFormBase()
{
Text = "Speckle (Beta)";
Size = new System.Drawing.Size(400, 600);
}
public cSapModel SapModel { get; private set; }
var services = new ServiceCollection();
ConfigureServices(services);
Container = services.BuildServiceProvider();
var webview = Container.GetRequiredService<DUI3ControlWebView>();
Host = new() { Child = webview, Dock = DockStyle.Fill };
Controls.Add(Host);
FormClosing += Form1Closing;
}
protected virtual void ConfigureServices(IServiceCollection services)
{
@@ -40,61 +43,23 @@ public abstract class SpeckleFormBase : Form, ICsiApplicationService
protected abstract HostAppVersion GetVersion();
public void Initialize(ref cSapModel sapModel, ref cPluginCallback pluginCallback)
public void SetSapModel(ref cSapModel sapModel, ref cPluginCallback pluginCallback)
{
// store app-specific model and callback references (callback if at all possible?)
SapModel = sapModel;
_sapModel = sapModel;
_pluginCallback = pluginCallback;
string assemblyName =
Assembly.GetExecutingAssembly().GetName().Name
?? throw new InvalidOperationException("Could not determine executing assembly name");
string resourcePath = $"{assemblyName}.Resources.et_element_Speckle.bmp";
// load and set the speckle icon from embedded resources
using (var stream = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(resourcePath))
{
if (stream == null)
{
throw new InvalidOperationException($"Could not find resource: {resourcePath}");
}
using var bmp = new Bitmap(stream);
Icon = Icon.FromHandle(bmp.GetHicon());
}
// configure dependency injection services
var services = new ServiceCollection();
services.AddSingleton<ICsiApplicationService>(this);
ConfigureServices(services);
// build service container and initialize ui framework
_container = services.BuildServiceProvider();
_container.UseDUI();
// setup webview control and form properties
var webview = _container.GetRequiredService<DUI3ControlWebView>();
Host = new() { Child = webview, Dock = DockStyle.Fill };
Controls.Add(Host);
FormBorderStyle = FormBorderStyle.Sizable;
// this.TopLevel = true;
// TODO: Get IntrPtr for Csi window
FormClosing += Form1Closing;
var csiService = Container.GetRequiredService<ICsiApplicationService>();
csiService.Initialize(sapModel, pluginCallback);
}
private void Form1Closing(object? sender, FormClosingEventArgs e) => _pluginCallback.Finish(0);
protected override void Dispose(bool disposing)
protected void Form1Closing(object? sender, FormClosingEventArgs e)
{
if (!_disposed)
{
if (disposing)
{
_container.Dispose();
Host.Dispose();
base.Dispose(disposing);
}
_disposed = true;
}
Host.Dispose();
_pluginCallback.Finish(0);
}
public new void ShowDialog()
{
base.ShowDialog();
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 352 KiB

@@ -1,20 +1,18 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Speckle.Connectors.Common;
using Speckle.Connectors.Common.Builders;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.CSiShared.Bindings;
using Speckle.Connectors.CSiShared.Builders;
using Speckle.Connectors.CSiShared.Filters;
using Speckle.Connectors.CSiShared.HostApp;
using Speckle.Connectors.CSiShared.HostApp.Helpers;
using Speckle.Connectors.DUI;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Models.Card.SendFilter;
using Speckle.Connectors.DUI.WebView;
using Speckle.Converters.CSiShared;
using Speckle.Converters.CSiShared.ToSpeckle.Helpers;
namespace Speckle.Connectors.CSiShared;
@@ -23,17 +21,21 @@ public static class ServiceRegistration
public static IServiceCollection AddCsi(this IServiceCollection services)
{
services.AddSingleton<IBrowserBridge, BrowserBridge>();
services.AddSingleton<ICsiApplicationService, CsiApplicationService>();
services.AddConnectorUtils();
services.AddDUI<DefaultThreadContext, CsiDocumentModelStore>();
services.AddDUI<CsiDocumentModelStore>();
services.AddDUIView();
services.AddSingleton<DocumentModelStore, CsiDocumentModelStore>();
services.AddSingleton<IBinding, TestBinding>();
services.AddSingleton<IBinding, ConfigBinding>();
services.AddSingleton<IBinding, AccountBinding>();
services.AddSingleton<IBinding>(sp => sp.GetRequiredService<IBasicConnectorBinding>());
services.AddSingleton<IBasicConnectorBinding, CsiSharedBasicConnectorBinding>();
services.AddSingleton<IAppIdleManager, CsiIdleManager>();
services.AddSingleton<IBinding, CsiSharedSelectionBinding>();
services.AddSingleton<IBinding, CsiSharedSendBinding>();
@@ -43,13 +45,7 @@ public static class ServiceRegistration
services.AddScoped<IRootObjectBuilder<ICsiWrapper>, CsiRootObjectBuilder>();
services.AddScoped<SendOperation<ICsiWrapper>>();
services.AddScoped<CsiMaterialPropertyExtractor>();
services.AddScoped<MaterialUnpacker>();
services.AddScoped<IFrameSectionPropertyExtractor, CsiFrameSectionPropertyExtractor>();
services.AddScoped<IShellSectionPropertyExtractor, CsiShellSectionPropertyExtractor>();
// add converter caches
services.AddScoped<CsiToSpeckleCacheSingleton>();
services.RegisterTopLevelExceptionHandler();
return services;
}
@@ -8,34 +8,20 @@
<PropertyGroup Label="Configuration">
<Import_RootNamespace>Speckle.Connectors.CSiShared</Import_RootNamespace>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Resources\et_element_Speckle.bmp">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)Bindings\CsiSharedBasicConnectorBinding.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bindings\CsiSharedSelectionBinding.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bindings\CsiSharedSendBinding.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Filters\CsiSharedSelectionFilter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\MaterialUnpacker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\CsiSendCollectionManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\CsiFrameSectionPropertyExtractor.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\CsiMaterialPropertyExtractor.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\CsiShellSectionPropertyExtractor.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\IApplicationSectionPropertyExtractor.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\ISectionPropertyExtractor.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\ISectionUnpacker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\CsiRootObjectBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Plugin\CsiPluginBase.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Plugin\SpeckleFormBase.cs" />
<Compile Include="$(MSBuildThisFileDirectory)GlobalUsing.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\CsiApplicationService.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\CsiDocumentModelStore.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\CsiIdleManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ServiceRegistration.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Utils\ObjectIdentifiers.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="$(MSBuildThisFileDirectory)Resources\et_element_Speckle.bmp" />
</ItemGroup>
</Project>
@@ -8,5 +8,5 @@ namespace Speckle.Connectors.ETABS21;
public class SpeckleForm : EtabsSpeckleFormBase
{
protected override HostAppVersion GetVersion() => HostAppVersion.v21;
protected override HostAppVersion GetVersion() => HostAppVersion.v2021; // TODO: We need a v21
}
@@ -2,13 +2,14 @@
<PropertyGroup>
<TargetFramework>net48</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Platforms>AnyCPU</Platforms>
<UseWindowsForms>true</UseWindowsForms>
<UseWPF>true</UseWPF>
<ETABSVersion>21</ETABSVersion>
<DefineConstants>$(DefineConstants);ETABS21</DefineConstants>
<EnableDynamicLoading>true</EnableDynamicLoading>
<Configurations>Debug;Release;Local</Configurations>
</PropertyGroup>
<ItemGroup>
@@ -164,6 +164,11 @@
"resolved": "8.0.0",
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
},
"Speckle.DoubleNumerics": {
"type": "Transitive",
"resolved": "4.0.1",
"contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w=="
},
"Speckle.Newtonsoft.Json": {
"type": "Transitive",
"resolved": "13.0.2",
@@ -259,9 +264,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.1.8, )",
"Speckle.Sdk": "[3.1.8, )",
"Speckle.Sdk.Dependencies": "[3.1.8, )"
"Speckle.Objects": "[3.1.0-dev.219, )",
"Speckle.Sdk": "[3.1.0-dev.219, )",
"Speckle.Sdk.Dependencies": "[3.1.0-dev.219, )"
}
},
"speckle.connectors.dui": {
@@ -269,8 +274,9 @@
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.8, )",
"Speckle.Sdk.Dependencies": "[3.1.8, )"
"Speckle.Sdk": "[3.1.0-dev.219, )",
"Speckle.Sdk.Dependencies": "[3.1.0-dev.219, )",
"System.Threading.Tasks.Dataflow": "[6.0.0, )"
}
},
"speckle.connectors.dui.webview": {
@@ -287,7 +293,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.1.8, )"
"Speckle.Objects": "[3.1.0-dev.219, )"
}
},
"speckle.converters.etabs21": {
@@ -329,26 +335,20 @@
"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.1.8, )",
"resolved": "3.1.8",
"contentHash": "rM1t+E8NIWFCPC3ndnMAI71B9ONiPFsz6GuIIdc5kth5AggQRGArPCalJBwA33ith2WtbjWOanI+PhHmpGQgGw==",
"requested": "[3.1.0-dev.219, )",
"resolved": "3.1.0-dev.219",
"contentHash": "2thsMUDkRr7lR0PXZJ8k3KWZLWjz/aBY0KaO2SuXM0lIKyJw1APMJ7lXJ89e6JAjBzEbK8yLHBb0qtfcc4bNRw==",
"dependencies": {
"Speckle.Sdk": "3.1.8"
"Speckle.Sdk": "3.1.0-dev.219"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.1.8, )",
"resolved": "3.1.8",
"contentHash": "qIYRiYHO2UkKuy8ZbjYltKCqLesIcJOcCGTEzyTCbyij5aC1x8HnwFjGMeonUCPaeipEKhI/4HidEZ0CwWvnXg==",
"requested": "[3.1.0-dev.219, )",
"resolved": "3.1.0-dev.219",
"contentHash": "UTgz9UgkMFGqRLmCq1HPo74MngvKbSZOFWaMRzASHrrnsBtzZatBXjoJBlvVoeBCoiea5Jj+BgZg6RvlV2m3sg==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -356,16 +356,22 @@
"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.DoubleNumerics": "4.0.1",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.1.8"
"Speckle.Sdk.Dependencies": "3.1.0-dev.219"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.1.8, )",
"resolved": "3.1.8",
"contentHash": "+MWIemub3nlPnDJ7sHl5nDGFDIIfiBU2VxqNmTGKzWNtCjsDi6KYw5zpbvXnaNcIqFoexv3TKCurjvT2ejzyQw=="
"requested": "[3.1.0-dev.219, )",
"resolved": "3.1.0-dev.219",
"contentHash": "5hqBd9whAs5cC8S/6YUgKFOivjntAkVutKhz06XD/f3Kj6xScT3hwcUaWBxiwK3Mw7FLtiVuU1uG777pjn0PKQ=="
},
"System.Threading.Tasks.Dataflow": {
"type": "CentralTransitive",
"requested": "[6.0.0, )",
"resolved": "6.0.0",
"contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA=="
}
}
}
@@ -8,5 +8,5 @@ namespace Speckle.Connectors.ETABS22;
public class SpeckleForm : EtabsSpeckleFormBase
{
protected override HostAppVersion GetVersion() => HostAppVersion.v22;
protected override HostAppVersion GetVersion() => HostAppVersion.v2021; // TODO: v22
}
@@ -2,13 +2,14 @@
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Platforms>AnyCPU</Platforms>
<UseWindowsForms>true</UseWindowsForms>
<UseWPF>true</UseWPF>
<ETABSVersion>22</ETABSVersion>
<DefineConstants>$(DefineConstants);ETABS22;ETABS22_OR_GREATER</DefineConstants>
<EnableDynamicLoading>true</EnableDynamicLoading>
<Configurations>Debug;Release;Local</Configurations>
</PropertyGroup>
<ItemGroup>
@@ -155,6 +155,11 @@
"resolved": "8.0.0",
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
},
"Speckle.DoubleNumerics": {
"type": "Transitive",
"resolved": "4.0.1",
"contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w=="
},
"Speckle.Newtonsoft.Json": {
"type": "Transitive",
"resolved": "13.0.2",
@@ -215,9 +220,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.1.8, )",
"Speckle.Sdk": "[3.1.8, )",
"Speckle.Sdk.Dependencies": "[3.1.8, )"
"Speckle.Objects": "[3.1.0-dev.219, )",
"Speckle.Sdk": "[3.1.0-dev.219, )",
"Speckle.Sdk.Dependencies": "[3.1.0-dev.219, )"
}
},
"speckle.connectors.dui": {
@@ -225,8 +230,9 @@
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.8, )",
"Speckle.Sdk.Dependencies": "[3.1.8, )"
"Speckle.Sdk": "[3.1.0-dev.219, )",
"Speckle.Sdk.Dependencies": "[3.1.0-dev.219, )",
"System.Threading.Tasks.Dataflow": "[6.0.0, )"
}
},
"speckle.connectors.dui.webview": {
@@ -243,7 +249,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.1.8, )"
"Speckle.Objects": "[3.1.0-dev.219, )"
}
},
"speckle.converters.etabs22": {
@@ -285,42 +291,42 @@
"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.1.8, )",
"resolved": "3.1.8",
"contentHash": "rM1t+E8NIWFCPC3ndnMAI71B9ONiPFsz6GuIIdc5kth5AggQRGArPCalJBwA33ith2WtbjWOanI+PhHmpGQgGw==",
"requested": "[3.1.0-dev.219, )",
"resolved": "3.1.0-dev.219",
"contentHash": "2thsMUDkRr7lR0PXZJ8k3KWZLWjz/aBY0KaO2SuXM0lIKyJw1APMJ7lXJ89e6JAjBzEbK8yLHBb0qtfcc4bNRw==",
"dependencies": {
"Speckle.Sdk": "3.1.8"
"Speckle.Sdk": "3.1.0-dev.219"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.1.8, )",
"resolved": "3.1.8",
"contentHash": "qIYRiYHO2UkKuy8ZbjYltKCqLesIcJOcCGTEzyTCbyij5aC1x8HnwFjGMeonUCPaeipEKhI/4HidEZ0CwWvnXg==",
"requested": "[3.1.0-dev.219, )",
"resolved": "3.1.0-dev.219",
"contentHash": "UTgz9UgkMFGqRLmCq1HPo74MngvKbSZOFWaMRzASHrrnsBtzZatBXjoJBlvVoeBCoiea5Jj+BgZg6RvlV2m3sg==",
"dependencies": {
"GraphQL.Client": "6.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",
"Speckle.DoubleNumerics": "4.0.1",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.1.8"
"Speckle.Sdk.Dependencies": "3.1.0-dev.219"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.1.8, )",
"resolved": "3.1.8",
"contentHash": "+MWIemub3nlPnDJ7sHl5nDGFDIIfiBU2VxqNmTGKzWNtCjsDi6KYw5zpbvXnaNcIqFoexv3TKCurjvT2ejzyQw=="
"requested": "[3.1.0-dev.219, )",
"resolved": "3.1.0-dev.219",
"contentHash": "5hqBd9whAs5cC8S/6YUgKFOivjntAkVutKhz06XD/f3Kj6xScT3hwcUaWBxiwK3Mw7FLtiVuU1uG777pjn0PKQ=="
},
"System.Threading.Tasks.Dataflow": {
"type": "CentralTransitive",
"requested": "[6.0.0, )",
"resolved": "6.0.0",
"contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA=="
}
}
}
@@ -1,110 +0,0 @@
using Speckle.Connectors.CSiShared.HostApp.Helpers;
using Speckle.Connectors.ETABSShared.HostApp.Helpers;
using Speckle.Converters.CSiShared.ToSpeckle.Helpers;
using Speckle.Sdk.Models.Proxies;
namespace Speckle.Connectors.ETABSShared.HostApp;
/// <summary>
/// Unpacks and creates proxies for frame and shell sections from the model.
/// </summary>
/// <remarks>
/// Provides a unified approach to section extraction across different section types.
/// Leverages specialized extractors to handle complex property retrieval. Centralizes
/// section proxy creation with robust error handling and logging mechanisms.
/// </remarks>
public class EtabsSectionUnpacker : ISectionUnpacker
{
private readonly EtabsSectionPropertyExtractor _propertyExtractor;
private readonly CsiToSpeckleCacheSingleton _csiToSpeckleCacheSingleton;
public EtabsSectionUnpacker(
EtabsSectionPropertyExtractor propertyExtractor,
CsiToSpeckleCacheSingleton csiToSpeckleCacheSingleton
)
{
_propertyExtractor = propertyExtractor;
_csiToSpeckleCacheSingleton = csiToSpeckleCacheSingleton;
}
public IEnumerable<GroupProxy> UnpackSections()
{
foreach (GroupProxy frameSectionProxy in UnpackFrameSections())
{
yield return frameSectionProxy;
}
foreach (GroupProxy shellSectionProxy in UnpackShellSections())
{
yield return shellSectionProxy;
}
}
private IEnumerable<GroupProxy> UnpackFrameSections()
{
foreach (var entry in _csiToSpeckleCacheSingleton.FrameSectionCache)
{
string sectionName = entry.Key;
List<string> frameIds = entry.Value;
// Initialize properties outside the if statement
Dictionary<string, object?> properties = new Dictionary<string, object?>();
// get the properties of the section
// openings will have objects assigned to them, but won't have properties
// sectionName is initialized with string.Empty, but api ref returns string "None"
if (sectionName != "None")
{
properties = _propertyExtractor.ExtractFrameSectionProperties(sectionName);
}
// create the section proxy
GroupProxy sectionProxy =
new()
{
id = sectionName,
name = sectionName,
applicationId = sectionName,
objects = frameIds,
["type"] = "Frame Section", // since sectionProxies are a flat list, need some way to distinguish from shell
["properties"] = properties // openings will just have an empty dict here
};
yield return sectionProxy;
}
}
private IEnumerable<GroupProxy> UnpackShellSections()
{
foreach (var entry in _csiToSpeckleCacheSingleton.ShellSectionCache)
{
string sectionName = entry.Key;
List<string> frameIds = entry.Value;
// Initialize properties outside the if statement
Dictionary<string, object?> properties = new Dictionary<string, object?>();
// get the properties of the section
// openings will have objects assigned to them, but won't have properties
// sectionName is initialized with string.Empty, but api ref returns string "None"
if (sectionName != "None")
{
properties = _propertyExtractor.ExtractShellSectionProperties(sectionName);
}
// create the section proxy
GroupProxy sectionProxy =
new()
{
id = sectionName,
name = sectionName,
applicationId = sectionName,
objects = frameIds,
["type"] = "Shell Section", // since sectionProxies are a flat list, need some way to distinguish from frame
["properties"] = properties // openings will just have an empty dict here
};
yield return sectionProxy;
}
}
}
@@ -1,7 +1,6 @@
using Speckle.Connectors.CSiShared.HostApp;
using Speckle.Converters.Common;
using Speckle.Converters.CSiShared;
using Speckle.Converters.CSiShared.Utils;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
@@ -48,14 +47,9 @@ public class EtabsSendCollectionManager : CsiSendCollectionManager
return DEFAULT_LEVEL;
}
if (
properties.TryGetValue(ObjectPropertyCategory.OBJECT_ID, out var objectId)
&& objectId is Dictionary<string, object> parameters
)
if (properties.TryGetValue("Object ID", out var objectId) && objectId is Dictionary<string, object> parameters)
{
return parameters.TryGetValue(CommonObjectProperty.LEVEL, out var level)
? level?.ToString() ?? DEFAULT_LEVEL
: DEFAULT_LEVEL;
return parameters.TryGetValue("level", out var level) ? level?.ToString() ?? DEFAULT_LEVEL : DEFAULT_LEVEL;
}
return DEFAULT_LEVEL;
@@ -72,17 +66,11 @@ public class EtabsSendCollectionManager : CsiSendCollectionManager
}
// For frames and shells, get design orientation from Object ID
if (
(type == ModelObjectType.FRAME.ToString() || type == ModelObjectType.SHELL.ToString())
&& obj["properties"] is Dictionary<string, object> properties
)
if ((type == "Frame" || type == "Shell") && obj["properties"] is Dictionary<string, object> properties)
{
if (
properties.TryGetValue(ObjectPropertyCategory.OBJECT_ID, out var objectId)
&& objectId is Dictionary<string, object> parameters
)
if (properties.TryGetValue("Object ID", out var objectId) && objectId is Dictionary<string, object> parameters)
{
if (parameters.TryGetValue(CommonObjectProperty.DESIGN_ORIENTATION, out var orientation))
if (parameters.TryGetValue("designOrientation", out var orientation))
{
return GetCategoryFromDesignOrientation(orientation?.ToString(), type);
}
@@ -90,7 +78,7 @@ public class EtabsSendCollectionManager : CsiSendCollectionManager
}
// For joints, simply categorize as joints
return type == ModelObjectType.JOINT.ToString() ? ElementCategory.JOINT : ElementCategory.OTHER;
return type == "Joint" ? ElementCategory.JOINT : ElementCategory.OTHER;
}
private ElementCategory GetCategoryFromDesignOrientation(string? orientation, string type)
@@ -102,12 +90,12 @@ public class EtabsSendCollectionManager : CsiSendCollectionManager
return (orientation, type) switch
{
("Column", nameof(ModelObjectType.FRAME)) => ElementCategory.COLUMN,
("Beam", nameof(ModelObjectType.FRAME)) => ElementCategory.BEAM,
("Brace", nameof(ModelObjectType.FRAME)) => ElementCategory.BRACE,
("Wall", nameof(ModelObjectType.SHELL)) => ElementCategory.WALL,
("Floor", nameof(ModelObjectType.SHELL)) => ElementCategory.FLOOR,
("Ramp", nameof(ModelObjectType.SHELL)) => ElementCategory.RAMP,
("Column", "Frame") => ElementCategory.COLUMN,
("Beam", "Frame") => ElementCategory.BEAM,
("Brace", "Frame") => ElementCategory.BRACE,
("Wall", "Shell") => ElementCategory.WALL,
("Floor", "Shell") => ElementCategory.FLOOR,
("Ramp", "Shell") => ElementCategory.RAMP,
_ => ElementCategory.OTHER
};
}
@@ -150,4 +138,16 @@ public class EtabsSendCollectionManager : CsiSendCollectionManager
levelCollection.elements.Add(categoryCollection);
return categoryCollection;
}
private enum ElementCategory
{
COLUMN,
BEAM,
BRACE,
WALL,
FLOOR,
RAMP,
JOINT,
OTHER
}
}
@@ -1,76 +0,0 @@
using Speckle.Connectors.CSiShared.HostApp.Helpers;
using Speckle.Converters.Common;
using Speckle.Converters.CSiShared;
using Speckle.Converters.CSiShared.Utils;
namespace Speckle.Connectors.ETABSShared.HostApp.Helpers;
/// <summary>
/// Extracts ETABS-specific frame section properties.
/// </summary>
public class EtabsFrameSectionPropertyExtractor : IApplicationFrameSectionPropertyExtractor
{
private readonly IConverterSettingsStore<CsiConversionSettings> _settingsStore;
public EtabsFrameSectionPropertyExtractor(IConverterSettingsStore<CsiConversionSettings> settingsStore)
{
_settingsStore = settingsStore;
}
/// <summary>
/// Gets generalised frame section properties
/// </summary>
/// <remarks>
/// Sap2000 doesn't support this method, unfortunately
/// Alternative is to account for extraction according to section type - we're talking over 40 section types!
/// This way, we get basic information with minimal computational costs.
/// </remarks>
public void ExtractProperties(string sectionName, Dictionary<string, object?> properties)
{
// Get all frame properties
int numberOfNames = 0;
string[] names = [];
eFramePropType[] propTypes = [];
double[] t3 = [],
t2 = [],
tf = [],
tw = [],
t2b = [],
tfb = [],
area = [];
_settingsStore.Current.SapModel.PropFrame.GetAllFrameProperties_2(
ref numberOfNames,
ref names,
ref propTypes,
ref t3,
ref t2,
ref tf,
ref tw,
ref t2b,
ref tfb,
ref area
);
// Find the index of the current section
int sectionIndex = Array.IndexOf(names, sectionName);
if (sectionIndex != -1)
{
// General Data
var generalData = properties.EnsureNested(SectionPropertyCategory.GENERAL_DATA);
generalData["Section Shape"] = propTypes[sectionIndex].ToString();
// Section Dimensions
string unit = _settingsStore.Current.SpeckleUnits;
var sectionDimensions = properties.EnsureNested(SectionPropertyCategory.SECTION_DIMENSIONS);
sectionDimensions.AddWithUnits("t3", t3[sectionIndex], unit);
sectionDimensions.AddWithUnits("t2", t2[sectionIndex], unit);
sectionDimensions.AddWithUnits("tf", tf[sectionIndex], unit);
sectionDimensions.AddWithUnits("tw", tw[sectionIndex], unit);
sectionDimensions.AddWithUnits("t2b", t2b[sectionIndex], unit);
sectionDimensions.AddWithUnits("tfb", tfb[sectionIndex], unit);
sectionDimensions.AddWithUnits("Area", area[sectionIndex], $"{unit}²");
}
}
}
@@ -1,53 +0,0 @@
using Speckle.Connectors.CSiShared.HostApp.Helpers;
namespace Speckle.Connectors.ETABSShared.HostApp.Helpers;
/// <summary>
/// Coordinates property extraction combining base CSi and ETABS-specific properties.
/// </summary>
/// <remarks>
/// Mirrors property extraction system pattern used in EtabsPropertiesExtractor.
/// Composition handled at coordinator level rather than individual extractors.
/// </remarks>
public class EtabsSectionPropertyExtractor
{
private readonly IFrameSectionPropertyExtractor _csiFrameExtractor;
private readonly IShellSectionPropertyExtractor _csiShellExtractor;
private readonly IApplicationFrameSectionPropertyExtractor _etabsFrameExtractor;
private readonly IApplicationShellSectionPropertyExtractor _etabsShellExtractor;
public EtabsSectionPropertyExtractor(
IFrameSectionPropertyExtractor csiFrameExtractor,
IShellSectionPropertyExtractor csiShellExtractor,
IApplicationFrameSectionPropertyExtractor etabsFrameExtractor,
IApplicationShellSectionPropertyExtractor etabsShellExtractor
)
{
_csiFrameExtractor = csiFrameExtractor;
_csiShellExtractor = csiShellExtractor;
_etabsFrameExtractor = etabsFrameExtractor;
_etabsShellExtractor = etabsShellExtractor;
}
/// <summary>
/// Extract the frame section properties on both a Csi and app-specific level
/// </summary>
public Dictionary<string, object?> ExtractFrameSectionProperties(string sectionName)
{
Dictionary<string, object?> properties = new();
_csiFrameExtractor.ExtractProperties(sectionName, properties);
_etabsFrameExtractor.ExtractProperties(sectionName, properties);
return properties;
}
/// <summary>
/// Extract the shell section properties on both a Csi and app-specific level
/// </summary>
public Dictionary<string, object?> ExtractShellSectionProperties(string sectionName)
{
Dictionary<string, object?> properties = new();
_csiShellExtractor.ExtractProperties(sectionName, properties);
_etabsShellExtractor.ExtractProperties(sectionName, properties);
return properties;
}
}
@@ -1,64 +0,0 @@
using Microsoft.Extensions.Logging;
using Speckle.Connectors.CSiShared.HostApp.Helpers;
using Speckle.Converters.Common;
using Speckle.Converters.CSiShared;
using Speckle.Converters.CSiShared.Utils;
namespace Speckle.Connectors.ETABSShared.HostApp.Helpers;
/// <summary>
/// Extracts ETABS-specific shell section properties.
/// </summary>
public class EtabsShellSectionPropertyExtractor : IApplicationShellSectionPropertyExtractor
{
private readonly IConverterSettingsStore<CsiConversionSettings> _settingsStore;
private readonly ILogger<EtabsShellSectionPropertyExtractor> _logger;
private readonly EtabsShellSectionResolver _etabsShellSectionResolver;
public EtabsShellSectionPropertyExtractor(
IConverterSettingsStore<CsiConversionSettings> settingsStore,
ILogger<EtabsShellSectionPropertyExtractor> logger,
EtabsShellSectionResolver etabsShellSectionResolver
)
{
_settingsStore = settingsStore;
_logger = logger;
_etabsShellSectionResolver = etabsShellSectionResolver;
}
/// <summary>
/// Extract shell section properties
/// </summary>
/// <remarks>
/// sectionName is unique across all types (Wall, Slab and Deck)
/// There is no general query such as PropArea.GetShell() - rather we have to be specific on the type, for example
/// PropArea.GetWall() or PropArea.GetDeck() BUT we can't get the building type given a SectionName.
/// Hence the introduction of ResolveSection.
/// </remarks>
public void ExtractProperties(string sectionName, Dictionary<string, object?> properties)
{
// Step 01: Finding the appropriate api query for the unknown section type (wall, deck or slab)
Dictionary<string, object?> resolvedProperties = _etabsShellSectionResolver.ResolveSection(sectionName);
// Step 02: Mutate properties dictionary with resolved properties
foreach (var nestedDictionary in resolvedProperties)
{
if (nestedDictionary.Value is not Dictionary<string, object?> nestedValues)
{
_logger.LogWarning(
"Unexpected value type for key {Key} in section {SectionName}. Expected Dictionary<string, object?>, got {ActualType}",
nestedDictionary.Key,
sectionName,
nestedDictionary.Value?.GetType().Name ?? "null"
);
continue;
}
var nestedProperties = properties.EnsureNested(nestedDictionary.Key);
foreach (var kvp in nestedValues)
{
nestedProperties[kvp.Key] = kvp.Value;
}
}
}
}
@@ -1,182 +0,0 @@
using Speckle.Converters.Common;
using Speckle.Converters.CSiShared;
using Speckle.Converters.CSiShared.Utils;
namespace Speckle.Connectors.ETABSShared.HostApp.Helpers;
/// <summary>
/// Attempts to resolve the section type and retrieve its properties by trying different section resolvers.
/// </summary>
/// <remarks>
/// This service focuses solely on determining the correct section type and returning its properties.
/// Since section names are unique across different types (Wall, Slab, Deck), it uses a try-and-fail approach
/// rather than attempting to predetermine the type. The first successful resolution is returned.
/// </remarks>
public record AreaSectionResult
{
public bool Success { get; init; }
public Dictionary<string, object?> Properties { get; init; }
}
public interface IAreaSectionResolver
{
AreaSectionResult TryResolveSection(string sectionName);
}
public class EtabsShellSectionResolver
{
private readonly IConverterSettingsStore<CsiConversionSettings> _settingsStore;
private readonly IEnumerable<IAreaSectionResolver> _resolvers;
public EtabsShellSectionResolver(IConverterSettingsStore<CsiConversionSettings> settingsStore)
{
_settingsStore = settingsStore;
_resolvers =
[
new WallSectionResolver(_settingsStore),
new SlabSectionResolver(_settingsStore),
new DeckSectionResolver(_settingsStore)
];
}
public Dictionary<String, object?> ResolveSection(string sectionName)
{
foreach (var resolver in _resolvers)
{
var result = resolver.TryResolveSection(sectionName);
if (result.Success)
{
return result.Properties;
}
}
throw new InvalidOperationException($"Section '{sectionName}' could not be resolved to any known type.");
}
}
public class WallSectionResolver(IConverterSettingsStore<CsiConversionSettings> settingsStore) : IAreaSectionResolver
{
public AreaSectionResult TryResolveSection(string sectionName)
{
eWallPropType wallPropType = default;
eShellType shellType = default;
string matProp = string.Empty;
double thickness = 0.0;
int color = 0;
string notes = string.Empty;
string guid = string.Empty;
var result = settingsStore.Current.SapModel.PropArea.GetWall(
sectionName,
ref wallPropType,
ref shellType,
ref matProp,
ref thickness,
ref color,
ref notes,
ref guid
);
Dictionary<string, object?> generalData = [];
generalData["Property Name"] = sectionName;
generalData["Property Type"] = wallPropType.ToString();
generalData["Material"] = matProp;
generalData["Modeling Type"] = shellType.ToString();
generalData["Display Color"] = color;
generalData["Notes"] = notes;
Dictionary<string, object?> propertyData = [];
propertyData["Type"] = "Wall";
propertyData.AddWithUnits("Thickness", thickness, settingsStore.Current.SpeckleUnits);
Dictionary<string, object?> properties = [];
properties[SectionPropertyCategory.GENERAL_DATA] = generalData;
properties[SectionPropertyCategory.PROPERTY_DATA] = propertyData;
return new AreaSectionResult { Success = result == 0, Properties = properties };
}
}
public class SlabSectionResolver(IConverterSettingsStore<CsiConversionSettings> settingsStore) : IAreaSectionResolver
{
public AreaSectionResult TryResolveSection(string sectionName)
{
eSlabType slabType = default;
eShellType shellType = default;
string matProp = string.Empty;
double thickness = 0.0;
int color = 0;
string notes = string.Empty;
string guid = string.Empty;
var result = settingsStore.Current.SapModel.PropArea.GetSlab(
sectionName,
ref slabType,
ref shellType,
ref matProp,
ref thickness,
ref color,
ref notes,
ref guid
);
Dictionary<string, object?> generalData = [];
generalData["Property Name"] = sectionName;
generalData["Material"] = matProp;
generalData["Modeling Type"] = shellType.ToString();
generalData["Display Color"] = color;
generalData["Notes"] = notes;
Dictionary<string, object?> propertyData = [];
propertyData["Type"] = slabType.ToString();
propertyData.AddWithUnits("Thickness", thickness, settingsStore.Current.SpeckleUnits);
Dictionary<string, object?> properties = [];
properties[SectionPropertyCategory.GENERAL_DATA] = generalData;
properties[SectionPropertyCategory.PROPERTY_DATA] = propertyData;
return new AreaSectionResult { Success = result == 0, Properties = properties };
}
}
public class DeckSectionResolver(IConverterSettingsStore<CsiConversionSettings> settingsStore) : IAreaSectionResolver
{
public AreaSectionResult TryResolveSection(string sectionName)
{
eDeckType deckType = default;
eShellType shellType = default;
string deckMatProp = string.Empty;
double thickness = 0.0;
int color = 0;
string notes = string.Empty;
string guid = string.Empty;
var result = settingsStore.Current.SapModel.PropArea.GetDeck(
sectionName,
ref deckType,
ref shellType,
ref deckMatProp,
ref thickness,
ref color,
ref notes,
ref guid
);
Dictionary<string, object?> generalData = [];
generalData["Property Name"] = sectionName;
generalData["Property Type"] = deckType.ToString();
generalData["Material"] = deckMatProp;
generalData["Modeling Type"] = shellType.ToString();
generalData["Display Color"] = color;
generalData["Notes"] = notes;
Dictionary<string, object?> propertyData = [];
propertyData.AddWithUnits("Thickness", thickness, settingsStore.Current.SpeckleUnits);
Dictionary<string, object?> properties = [];
properties[SectionPropertyCategory.GENERAL_DATA] = generalData;
properties[SectionPropertyCategory.PROPERTY_DATA] = propertyData;
return new AreaSectionResult { Success = result == 0, Properties = properties };
}
}
@@ -7,7 +7,7 @@ public abstract class EtabsPluginBase : CSiPluginBase
{
public override int Info(ref string text)
{
text = "Next Gen Speckle Connector for ETABS";
text = "Hey Speckler! This is our next-gen ETABS Connector.";
return 0;
}
@@ -1,8 +1,6 @@
using Microsoft.Extensions.DependencyInjection;
using Speckle.Connectors.CSiShared.HostApp;
using Speckle.Connectors.CSiShared.HostApp.Helpers;
using Speckle.Connectors.ETABSShared.HostApp;
using Speckle.Connectors.ETABSShared.HostApp.Helpers;
using Speckle.Converters.ETABSShared;
namespace Speckle.Connectors.ETABSShared;
@@ -12,12 +10,7 @@ public static class ServiceRegistration
public static IServiceCollection AddEtabs(this IServiceCollection services)
{
services.AddEtabsConverters();
services.AddScoped<IApplicationFrameSectionPropertyExtractor, EtabsFrameSectionPropertyExtractor>();
services.AddScoped<IApplicationShellSectionPropertyExtractor, EtabsShellSectionPropertyExtractor>();
services.AddScoped<EtabsSectionPropertyExtractor>();
services.AddScoped<EtabsShellSectionResolver>();
services.AddScoped<CsiSendCollectionManager, EtabsSendCollectionManager>();
services.AddScoped<ISectionUnpacker, EtabsSectionUnpacker>();
return services;
}

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