From 30ef48389cec6331c607f042d6437febc25b353b Mon Sep 17 00:00:00 2001 From: connorivy <43247197+connorivy@users.noreply.github.com> Date: Thu, 16 May 2024 14:41:44 -0500 Subject: [PATCH 01/52] CNX 9442 Incorrect Export of 'Room Type' Parameter from Revit to Speckle (#3422) send element name instead of id (if possible) Co-authored-by: Connor Ivy --- .../ConverterRevitShared/Extensions/ParameterExtensions.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Objects/Converters/ConverterRevit/ConverterRevitShared/Extensions/ParameterExtensions.cs b/Objects/Converters/ConverterRevit/ConverterRevitShared/Extensions/ParameterExtensions.cs index 0103f15db5..319a46ac65 100644 --- a/Objects/Converters/ConverterRevit/ConverterRevitShared/Extensions/ParameterExtensions.cs +++ b/Objects/Converters/ConverterRevit/ConverterRevitShared/Extensions/ParameterExtensions.cs @@ -49,7 +49,9 @@ internal static class ParameterExtensions case StorageType.String: return parameter.AsString(); case StorageType.ElementId: - return parameter.AsElementId().ToString(); + ElementId id = parameter.AsElementId(); + Element el = parameter.Element.Document.GetElement(id); + return el?.Name ?? id.ToString(); default: return null; } From c4b928c6a39945b4549428965cdcc45111df6430 Mon Sep 17 00:00:00 2001 From: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com> Date: Thu, 30 May 2024 15:17:19 +0100 Subject: [PATCH 02/52] Promoted CS8618 to warning (dev) (#3453) Promoted CS8616 from `NoWarn` to `Warning` --- Automate/Speckle.Automate.Sdk/Speckle.Automate.Sdk.csproj | 4 ++-- .../EventEmitter/DllConflictEventEmitter.cs | 6 +++--- Core/Core/Core.csproj | 2 +- Directory.Build.props | 2 -- Directory.Build.targets | 2 +- Objects/Objects/Objects.csproj | 2 +- 6 files changed, 8 insertions(+), 10 deletions(-) diff --git a/Automate/Speckle.Automate.Sdk/Speckle.Automate.Sdk.csproj b/Automate/Speckle.Automate.Sdk/Speckle.Automate.Sdk.csproj index 23712d34b6..bbdb0d9a65 100644 --- a/Automate/Speckle.Automate.Sdk/Speckle.Automate.Sdk.csproj +++ b/Automate/Speckle.Automate.Sdk/Speckle.Automate.Sdk.csproj @@ -13,7 +13,7 @@ true - $(WarningsNotAsErrors);NU5104 + $(WarningsNotAsErrors);CS8618; @@ -22,7 +22,7 @@ - + diff --git a/ConnectorCore/DllConflictManagement/EventEmitter/DllConflictEventEmitter.cs b/ConnectorCore/DllConflictManagement/EventEmitter/DllConflictEventEmitter.cs index 3c7ffdb69b..7feda8a3b8 100644 --- a/ConnectorCore/DllConflictManagement/EventEmitter/DllConflictEventEmitter.cs +++ b/ConnectorCore/DllConflictManagement/EventEmitter/DllConflictEventEmitter.cs @@ -9,9 +9,9 @@ namespace Speckle.DllConflictManagement.EventEmitter; /// public class DllConflictEventEmitter { - public event EventHandler OnError; - public event EventHandler OnInfo; - public event EventHandler OnAction; + public event EventHandler? OnError; + public event EventHandler? OnInfo; + public event EventHandler? OnAction; private bool _shouldEmitEvents; private readonly List _savedErrorEvents = new(); diff --git a/Core/Core/Core.csproj b/Core/Core/Core.csproj index 2b74ff5528..33510411b4 100644 --- a/Core/Core/Core.csproj +++ b/Core/Core/Core.csproj @@ -23,7 +23,7 @@ CA1502; CA1506; CA1708; CA1710; CA1711; CA1716; CA1720; CA1721; CA1724; CA1816; CA1851; CA1861; - CA2201; + CA2201; CS8618; CS0419; CS0618; CS0659; CS0809; CS8600; CS8602; CS8603; CS8604; IDE0032; IDE0059; IDE0130; IDE1006; diff --git a/Directory.Build.props b/Directory.Build.props index dbaa3aea2d..e0b95b5d9c 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -34,8 +34,6 @@ CA5399;CA1862; CS1591;CS1573; - - CS8618; CA1303;CA1304;CA1305;CA1307;CA1308;CA1309;CA1310;CA1311;CA2101; NU1701; diff --git a/Directory.Build.targets b/Directory.Build.targets index bfe533e029..432400ed48 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -74,7 +74,7 @@ CS0618;CA1034;CA2201;CA1051;CA1040;CA1724; IDE0044;IDE0130;CA1508; - CA5394;CA2007;CA1852;CA1819;CA1711;CA1063;CA1816;CA2234; + CA5394;CA2007;CA1852;CA1819;CA1711;CA1063;CA1816;CA2234;CS8618; false diff --git a/Objects/Objects/Objects.csproj b/Objects/Objects/Objects.csproj index 77a9ad6bbd..52f203fa66 100644 --- a/Objects/Objects/Objects.csproj +++ b/Objects/Objects/Objects.csproj @@ -21,7 +21,7 @@ CA1708; CA1711; CA1716; CA1724; CA1725; CA1819; CA2201; CA2225; - CS0659; CS0661; CS0728; + CS0659; CS0661; CS0728; CS8618; IDE0041; IDE0060; IDE1006; From a40569e335c0478409d5f343f039184a47e6ec38 Mon Sep 17 00:00:00 2001 From: Claire Kuang Date: Mon, 3 Jun 2024 04:09:10 -0700 Subject: [PATCH 03/52] CNX-9752 add parts data to pipes and structures (#3457) add parts class and property to pipes and structures --- .../ConverterAutocadCivil.Civil.cs | 30 +++++++++++++------ Objects/Objects/Other/Civil/CivilDataField.cs | 20 +++++++++++++ Objects/Objects/Other/DataField.cs | 27 +++++++++++++++++ 3 files changed, 68 insertions(+), 9 deletions(-) create mode 100644 Objects/Objects/Other/Civil/CivilDataField.cs create mode 100644 Objects/Objects/Other/DataField.cs diff --git a/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.Civil.cs b/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.Civil.cs index 20ede66c9c..a5d50b5efc 100644 --- a/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.Civil.cs +++ b/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.Civil.cs @@ -17,6 +17,7 @@ using Objects.BuiltElements.Civil; using Alignment = Objects.BuiltElements.Alignment; using Arc = Objects.Geometry.Arc; +using CivilDataField = Objects.Other.Civil.CivilDataField; using Polycurve = Objects.Geometry.Polycurve; using Featureline = Objects.BuiltElements.Featureline; using Line = Objects.Geometry.Line; @@ -1015,20 +1016,30 @@ public Structure StructureToSpeckle(CivilDB.Structure structure) }; // assign additional structure props + try { speckleStructure["grate"] = structure.Grate; } catch (Exception ex) when (!ex.IsFatal()) { } + try { speckleStructure["station"] = structure.Station; } catch (Exception ex) when (!ex.IsFatal()) { } + try { speckleStructure["network"] = structure.NetworkName; } catch (Exception ex) when (!ex.IsFatal()) { } AddNameAndDescriptionProperty(structure.Name, structure.Description, speckleStructure); + speckleStructure["partData"] = PartDataRecordToSpeckle(structure.PartData); - try - { - speckleStructure["grate"] = structure.Grate; - speckleStructure["station"] = structure.Station; - speckleStructure["network"] = structure.NetworkName; - } - catch (Exception e) when (!e.IsFatal()) + return speckleStructure; + } + + // part data + /// + /// Converts PartData into a list of DataField + /// + private List PartDataRecordToSpeckle(PartDataRecord partData) + { + List fields = new(); + + foreach (PartDataField partField in partData.GetAllDataFields()) { - // Couldn't set non-essential structure properties + CivilDataField field = new(partField.Name, partField.DataType.ToString(), partField.Units.ToString(), partField.Context.ToString(), partField.Value); + fields.Add(field); } - return speckleStructure; + return fields; } // pipes @@ -1059,6 +1070,7 @@ public Pipe PipeToSpeckle(CivilDB.Pipe pipe) // assign additional pipe props AddNameAndDescriptionProperty(pipe.Name, pipe.Description, specklePipe); + specklePipe["partData"] = PartDataRecordToSpeckle(pipe.PartData); try { specklePipe["shape"] = pipe.CrossSectionalShape.ToString(); } catch(Exception ex) when(!ex.IsFatal()) { } try { specklePipe["slope"] = pipe.Slope; } catch(Exception ex) when(!ex.IsFatal()) { } diff --git a/Objects/Objects/Other/Civil/CivilDataField.cs b/Objects/Objects/Other/Civil/CivilDataField.cs new file mode 100644 index 0000000000..48e1a2ad83 --- /dev/null +++ b/Objects/Objects/Other/Civil/CivilDataField.cs @@ -0,0 +1,20 @@ +namespace Objects.Other.Civil; + +public class CivilDataField : DataField +{ + public CivilDataField() { } + + public CivilDataField(string name, string type, string units, string context, object? value = null) + { + this.name = name; + this.type = type; + this.units = units; + this.context = context; + this.value = value; + } + + /// + /// The context type of the Civil3D part + /// + public string context { get; set; } +} diff --git a/Objects/Objects/Other/DataField.cs b/Objects/Objects/Other/DataField.cs new file mode 100644 index 0000000000..6770b3a8ef --- /dev/null +++ b/Objects/Objects/Other/DataField.cs @@ -0,0 +1,27 @@ +using Speckle.Core.Models; + +namespace Objects.Other; + +/// +/// Generic class for a data field +/// +public class DataField : Base +{ + public DataField() { } + + public DataField(string name, string type, string units, object? value = null) + { + this.name = name; + this.type = type; + this.units = units; + this.value = value; + } + + public string name { get; set; } + + public string type { get; set; } + + public object? value { get; set; } + + public string units { get; set; } +} From f29aaebd064fc449c5bcb040429130ac57a08e46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B3zsef=20L=2E=20Kiss?= <50739844+jozseflkiss@users.noreply.github.com> Date: Wed, 5 Jun 2024 10:54:07 +0200 Subject: [PATCH 04/52] feat(Archicad): Add default commit message (#3473) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit add default commit message Co-authored-by: József L. Kiss <> --- ConnectorArchicad/ConnectorArchicad/ConnectorBinding.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ConnectorArchicad/ConnectorArchicad/ConnectorBinding.cs b/ConnectorArchicad/ConnectorArchicad/ConnectorBinding.cs index 8a34225426..650f5c2000 100644 --- a/ConnectorArchicad/ConnectorArchicad/ConnectorBinding.cs +++ b/ConnectorArchicad/ConnectorArchicad/ConnectorBinding.cs @@ -220,7 +220,8 @@ public override async Task SendStream(StreamState state, ProgressViewMod return await Speckle.Core.Api.Helpers.Send( IdentifyStream(state), commitObject, - state.CommitMessage, + state.CommitMessage + ?? $"Sent {progress.Report.ReportObjects.Count} objects from {HostApplications.Archicad.Name + " " + archicadVersion}.", HostApplications.Archicad.Name ); } From c92da2d771b822c4b17f05231c62b5d89b1d0330 Mon Sep 17 00:00:00 2001 From: Claire Kuang Date: Thu, 6 Jun 2024 06:01:24 -0700 Subject: [PATCH 05/52] CNX-9760 [Rhino]: add gis attributes as user strings (#3477) * adds gis attributes as user strings * adds custom conversion for GIS elements * adds generic gis attribute case for fallback conversion * Update ConnectorBindingsRhino.Receive.cs * add GisFeature class * adds gisfeature direct conversion and refactors attributes creation * Update ConverterRhinoGh.GIS.cs * updates gisfeature conversion * Update ConnectorBindingsRhino.Previews.cs * Update ConnectorBindingsRhino.Receive.cs --------- Co-authored-by: KatKatKateryna Co-authored-by: Alan Rynne --- .../UI/ConnectorBindingsRhino.Previews.cs | 4 +- .../UI/ConnectorBindingsRhino.Receive.cs | 138 +++++++++++----- .../ConverterRhinoGh.GIS.cs | 151 ++++++++++++++++++ .../ConverterRhinoGh.cs | 11 ++ .../ConverterRhinoGhShared.projitems | 1 + Objects/Objects/GIS/GisFeature.cs | 43 +++++ 6 files changed, 310 insertions(+), 38 deletions(-) create mode 100644 Objects/Converters/ConverterRhinoGh/ConverterRhinoGhShared/ConverterRhinoGh.GIS.cs create mode 100644 Objects/Objects/GIS/GisFeature.cs diff --git a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Previews.cs b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Previews.cs index 00e1bb4df1..cd551584f8 100644 --- a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Previews.cs +++ b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Previews.cs @@ -100,7 +100,9 @@ private static bool IsPreviewIgnore(Base @object) || @object.speckle_type.Contains("View") || @object.speckle_type.Contains("Level") || @object.speckle_type.Contains("GridLine") - || @object.speckle_type.Contains("Collection"); + || @object.speckle_type.Contains("Collection") + || @object.speckle_type.Contains("PolygonElement") + || @object.speckle_type.Contains("GisFeature"); } public override async Task PreviewReceive(StreamState state, ProgressViewModel progress) diff --git a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Receive.cs b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Receive.cs index e85475969d..944d35a57c 100644 --- a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Receive.cs +++ b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Receive.cs @@ -415,8 +415,8 @@ ApplicationObject NewAppObj() return null; } - // get parameters - var parameters = current["parameters"] as Base; + // get parameters and attributes from revit/gis + var parameters = current["parameters"] as Base ?? current["attributes"] as Base; //Handle convertable objects if (converter.CanConvertToNative(current)) @@ -569,29 +569,15 @@ private void BakeObject( continue; } - var attributes = new ObjectAttributes(); - // handle display style - Base display = obj["displayStyle"] as Base ?? obj["@displayStyle"] as Base; - Base render = obj["renderMaterial"] as Base ?? obj["@renderMaterial"] as Base; - if (display != null) - { - var convertedDisplay = converter.ConvertToNative(display) as ObjectAttributes; - if (convertedDisplay is not null) - { - attributes = convertedDisplay; - } - } - else if (render != null) - { - attributes.ColorSource = ObjectColorSource.ColorFromMaterial; - } - - // assign layer - attributes.LayerIndex = layer.Index; - - // handle user info, application id, revit parameters - SetUserInfo(obj, attributes, converter, parent); + // create attributes with layer, display and render, user info, application id, revit/gis parameters + ObjectAttributes attributes = CreateAttributesFromObject( + obj, + layer.Index, + converter, + out RenderMaterial renderMaterial, + parent + ); Guid id = Doc.Objects.Add(o, attributes); if (id == Guid.Empty) @@ -621,22 +607,26 @@ private void BakeObject( bakedCount++; // handle render material - if (render != null) + if (renderMaterial is not null) { - var convertedMaterial = converter.ConvertToNative(render) as RenderMaterial; - if (convertedMaterial is not null) - { - RhinoObject rhinoObject = Doc.Objects.FindId(id); - rhinoObject.RenderMaterial = convertedMaterial; - rhinoObject.CommitChanges(); - } + RhinoObject rhinoObject = Doc.Objects.FindId(id); + rhinoObject.RenderMaterial = renderMaterial; + rhinoObject.CommitChanges(); } break; case RhinoObject o: // this was prbly a block instance, baked during conversion - o.Attributes.LayerIndex = layer.Index; // assign layer - SetUserInfo(obj, o.Attributes, converter, parent); // handle user info, including application id + // create attributes with layer, display and render, user info, application id, revit/gis parameters + ObjectAttributes objectAttributes = CreateAttributesFromObject( + obj, + layer.Index, + converter, + out RenderMaterial objectRenderMaterial, + parent + ); + + o.Attributes = objectAttributes; o.CommitChanges(); if (parent != null) { @@ -650,6 +640,42 @@ private void BakeObject( bakedCount++; break; + case Group o: // this is a GIS object + // create attributes with layer, display and render, user info, application id, revit/gis parameters + ObjectAttributes groupAttributes = CreateAttributesFromObject( + obj, + layer.Index, + converter, + out RenderMaterial groupRenderMaterial, + parent + ); + + groupAttributes.AddToGroup(o.Index); + + foreach (RhinoObject groupObject in Doc.Objects.FindByGroup(o.Index)) + { + groupObject.Attributes = groupAttributes; + + // handle render material + if (groupRenderMaterial is not null) + { + groupObject.RenderMaterial = groupRenderMaterial; + } + + groupObject.CommitChanges(); + } + + if (parent != null) + { + parent.Update(o.Id.ToString()); + } + else + { + appObj.Update(o.Id.ToString()); + } + bakedCount++; + break; + case ViewInfo o: // this is a view, baked during conversion appObj.Update(o.Name); bakedCount++; @@ -675,13 +701,36 @@ private void BakeObject( } } - private void SetUserInfo( + private ObjectAttributes CreateAttributesFromObject( Base obj, - ObjectAttributes attributes, + int layerIndex, ISpeckleConverter converter, + out RenderMaterial renderMaterial, ApplicationObject parent = null ) { + ObjectAttributes attributes = new(); + renderMaterial = null; + + // handle display style + Base display = obj["displayStyle"] as Base ?? obj["@displayStyle"] as Base; + Base render = obj["renderMaterial"] as Base ?? obj["@renderMaterial"] as Base; + if (display != null) + { + var convertedDisplay = converter.ConvertToNative(display) as ObjectAttributes; + if (convertedDisplay is not null) + { + attributes = convertedDisplay; + } + } + else if (render != null) + { + attributes.ColorSource = ObjectColorSource.ColorFromMaterial; + } + + // assign layer + attributes.LayerIndex = layerIndex; + // set user strings if (obj[UserStrings] is Base userStrings) { @@ -708,12 +757,13 @@ private void SetUserInfo( attributes.Name = name; } - // set revit parameters as user strings + // set revit/gis parameters as user strings var paramId = parent != null ? parent.OriginalId : obj.id; if (StoredObjectParams.TryGetValue(paramId, out Base parameters)) { foreach (var member in parameters.GetMembers(DynamicBaseMemberType.Dynamic)) { + // parameters coming from revit, value Base is Objects.BuiltElements.Revit.Parameter if (member.Value is Base parameter) { var convertedParameter = converter.ConvertToNative(parameter) as Tuple; @@ -723,8 +773,22 @@ private void SetUserInfo( attributes.SetUserString(paramName, convertedParameter.Item2); } } + // attributes coming from GIS + else + { + string userStringValue = member.Value is object value ? value.ToString() : string.Empty; + attributes.SetUserString(member.Key, userStringValue); + } } } + + // render material + if (render is not null) + { + renderMaterial = converter.ConvertToNative(render) as RenderMaterial; + } + + return attributes; } // Clears the stored objects, params, and preview objects diff --git a/Objects/Converters/ConverterRhinoGh/ConverterRhinoGhShared/ConverterRhinoGh.GIS.cs b/Objects/Converters/ConverterRhinoGh/ConverterRhinoGhShared/ConverterRhinoGh.GIS.cs new file mode 100644 index 0000000000..cc766f2784 --- /dev/null +++ b/Objects/Converters/ConverterRhinoGh/ConverterRhinoGhShared/ConverterRhinoGh.GIS.cs @@ -0,0 +1,151 @@ +using System; +using System.Collections.Generic; +using Objects.GIS; +using Speckle.Core.Models; +using RH = Rhino.DocObjects; +using System.Linq; +using Rhino.Geometry; + +namespace Objects.Converter.RhinoGh; + +public partial class ConverterRhinoGh +{ + // polygon element + // NOTE: class no longer in use? from 2.19 + public ApplicationObject PolygonElementToNative(PolygonElement poly) + { + var appObj = new ApplicationObject(poly.id, poly.speckle_type) { applicationId = poly.applicationId }; + + // get the group name + var commitInfo = GetCommitInfo(); + string groupName = $"{commitInfo} - " + poly.id; + if (Doc.Groups.FindName(groupName) is RH.Group existingGroup) + { + Doc.Groups.Delete(existingGroup); + } + + List addedGeometry = new(); + foreach (object geo in poly.geometry) + { + if (geo is Base geoBase) + { + var display = geoBase["displayValue"] as List ?? geoBase["@displayValue"] as List; + if (display is null) + { + continue; + } + + foreach (object displayObject in display) + { + if (displayObject is Base baseObj) + { + if (ConvertToNative(baseObj) is GeometryBase convertedObject) + { + Guid id = Doc.Objects.Add(convertedObject); + if (id != Guid.Empty) + { + addedGeometry.Add(id); + } + } + } + } + } + } + + if (addedGeometry.Count == 0) + { + appObj.Update(status: ApplicationObject.State.Failed, logItem: "No objects were created for group"); + return appObj; + } + + int groupIndex = Doc.Groups.Add(groupName, addedGeometry); + if (groupIndex == -1) + { + appObj.Update(status: ApplicationObject.State.Failed, logItem: "Could not add group to doc"); + return appObj; + } + + RH.Group convertedGroup = Doc.Groups.FindIndex(groupIndex); + + // update appobj + appObj.Update(convertedItem: convertedGroup, createdIds: addedGeometry.Select(o => o.ToString()).ToList()); + + return appObj; + } + + // gis feature + public ApplicationObject GisFeatureToNative(GisFeature feature) + { + var appObj = new ApplicationObject(feature.id, feature.speckle_type) { applicationId = feature.applicationId }; + + // get the group name + var commitInfo = GetCommitInfo(); + string groupName = $"{commitInfo} - " + feature.id; + if (Doc.Groups.FindName(groupName) is RH.Group existingGroup) + { + Doc.Groups.Delete(existingGroup); + } + + // for gis features, we are assuming that the `displayValue prop` should be converted first + // if there are no objects in `displayValue`, then we will fall back to check for convertible objects in `geometries` + List convertedObjects = new(); + if (feature.displayValue is List displayValue && displayValue.Count > 0) + { + foreach (Base displayObj in displayValue) + { + if (ConvertToNative(displayObj) is GeometryBase convertedObject) + { + convertedObjects.Add(convertedObject); + } + } + } + else if (feature.geometry is List geometries && geometries.Count > 0) + { + foreach (Base displayObj in geometries) + { + if (ConvertToNative(displayObj) is GeometryBase convertedObject) + { + convertedObjects.Add(convertedObject); + } + } + } + else + { + appObj.Update( + status: ApplicationObject.State.Failed, + logItem: "No objects in displayValue or geometries was found" + ); + return appObj; + } + + List addedGeometry = new(); + foreach (GeometryBase convertedObject in convertedObjects) + { + Guid id = Doc.Objects.Add(convertedObject); + if (id != Guid.Empty) + { + addedGeometry.Add(id); + } + } + + if (addedGeometry.Count == 0) + { + appObj.Update(status: ApplicationObject.State.Failed, logItem: "No objects were created for group"); + return appObj; + } + + int groupIndex = Doc.Groups.Add(groupName, addedGeometry); + if (groupIndex == -1) + { + appObj.Update(status: ApplicationObject.State.Failed, logItem: "Could not add group to doc"); + return appObj; + } + + RH.Group convertedGroup = Doc.Groups.FindIndex(groupIndex); + + // update appobj + appObj.Update(convertedItem: convertedGroup, createdIds: addedGeometry.Select(o => o.ToString()).ToList()); + + return appObj; + } +} diff --git a/Objects/Converters/ConverterRhinoGh/ConverterRhinoGhShared/ConverterRhinoGh.cs b/Objects/Converters/ConverterRhinoGh/ConverterRhinoGhShared/ConverterRhinoGh.cs index b467aad06a..7530e0e981 100644 --- a/Objects/Converters/ConverterRhinoGh/ConverterRhinoGhShared/ConverterRhinoGh.cs +++ b/Objects/Converters/ConverterRhinoGh/ConverterRhinoGhShared/ConverterRhinoGh.cs @@ -7,6 +7,7 @@ using Objects.BuiltElements.Revit; using Objects.BuiltElements.Revit.Curve; using Objects.Geometry; +using Objects.GIS; using Objects.Other; using Objects.Primitive; using Objects.Structural.Geometry; @@ -513,6 +514,14 @@ public object ConvertToNative(Base @object) rhinoObj = AlignmentToNative(o); break; + case PolygonElement o: + rhinoObj = PolygonElementToNative(o); + break; + + case GisFeature o: + rhinoObj = GisFeatureToNative(o); + break; + case Level o: rhinoObj = LevelToNative(o); break; @@ -735,6 +744,8 @@ public bool CanConvertToNative(Base @object) case Instance _: case GridLine _: case Alignment _: + case PolygonElement _: + case GisFeature _: case Level _: case Dimension _: case Collection c when !c.collectionType.ToLower().Contains("model"): diff --git a/Objects/Converters/ConverterRhinoGh/ConverterRhinoGhShared/ConverterRhinoGhShared.projitems b/Objects/Converters/ConverterRhinoGh/ConverterRhinoGhShared/ConverterRhinoGhShared.projitems index c0bb3fc5d2..67a382db59 100644 --- a/Objects/Converters/ConverterRhinoGh/ConverterRhinoGhShared/ConverterRhinoGhShared.projitems +++ b/Objects/Converters/ConverterRhinoGh/ConverterRhinoGhShared/ConverterRhinoGhShared.projitems @@ -9,6 +9,7 @@ ConverterRhinoGhShared + diff --git a/Objects/Objects/GIS/GisFeature.cs b/Objects/Objects/GIS/GisFeature.cs new file mode 100644 index 0000000000..1a5f4dc5ac --- /dev/null +++ b/Objects/Objects/GIS/GisFeature.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using Speckle.Core.Models; + +namespace Objects.GIS; + +public class GisFeature : Base +{ + public GisFeature() + { + attributes = new Base(); + } + + public GisFeature(Base attributes) + { + this.attributes = attributes; + } + + public GisFeature(List geometry, Base attributes) + { + this.geometry = geometry; + this.attributes = attributes; + } + + public GisFeature(Base attributes, List displayValue) + { + this.attributes = attributes; + this.displayValue = displayValue; + } + + public GisFeature(List geometry, Base attributes, List displayValue) + { + this.geometry = geometry; + this.attributes = attributes; + this.displayValue = displayValue; + } + + [DetachProperty] + public List? geometry { get; set; } + + [DetachProperty] + public List? displayValue { get; set; } + public Base attributes { get; set; } +} From 460e9acb8a8b780f8cab091c275ec24be1c569d1 Mon Sep 17 00:00:00 2001 From: Claire Kuang Date: Fri, 7 Jun 2024 06:52:44 -0700 Subject: [PATCH 06/52] CNX-9753: Add assembly and subassembly objects to Corridors (#3475) * adds additional corridor classes * updates with subassembly info and featureline based baselines * warnings fix * adds additional props to corridor sub elements --- .../ConverterAutocadCivil.Civil.cs | 192 ++++++++++++++---- .../ConverterAutocadCivil.cs | 2 +- Objects/Objects/BuiltElements/Baseline.cs | 88 ++++++++ .../Civil/CivilAppliedAssembly.cs | 26 +++ .../Civil/CivilAppliedSubassembly.cs | 37 ++++ .../BuiltElements/Civil/CivilBaseline.cs | 54 +++++ .../Civil/CivilBaselineRegion.cs | 45 ++++ .../Civil/CivilCalculatedLink.cs | 20 ++ .../Civil/CivilCalculatedPoint.cs | 35 ++++ .../Civil/CivilCalculatedShape.cs | 26 +++ Objects/Objects/Interfaces.cs | 11 + Objects/Objects/Other/Civil/CivilDataField.cs | 16 +- Objects/Objects/Other/DataField.cs | 6 +- 13 files changed, 509 insertions(+), 49 deletions(-) create mode 100644 Objects/Objects/BuiltElements/Baseline.cs create mode 100644 Objects/Objects/BuiltElements/Civil/CivilAppliedAssembly.cs create mode 100644 Objects/Objects/BuiltElements/Civil/CivilAppliedSubassembly.cs create mode 100644 Objects/Objects/BuiltElements/Civil/CivilBaseline.cs create mode 100644 Objects/Objects/BuiltElements/Civil/CivilBaselineRegion.cs create mode 100644 Objects/Objects/BuiltElements/Civil/CivilCalculatedLink.cs create mode 100644 Objects/Objects/BuiltElements/Civil/CivilCalculatedPoint.cs create mode 100644 Objects/Objects/BuiltElements/Civil/CivilCalculatedShape.cs diff --git a/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.Civil.cs b/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.Civil.cs index a5d50b5efc..fce119ee81 100644 --- a/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.Civil.cs +++ b/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.Civil.cs @@ -30,6 +30,7 @@ using SpiralType = Objects.Geometry.SpiralType; using Station = Objects.BuiltElements.Station; using Structure = Objects.BuiltElements.Structure; +using Vector = Objects.Geometry.Vector; using Speckle.Core.Logging; namespace Objects.Converter.AutocadCivil; @@ -640,7 +641,7 @@ private Line ProfileGenericToSpeckle(double startStation, double startElevation, } // featurelines - public Featureline FeatureLineToSpeckle(CivilDB.FeatureLine featureline) + public Featureline FeaturelineToSpeckle(CivilDB.FeatureLine featureline) { // get all points var points = new List(); @@ -666,25 +667,18 @@ public Featureline FeatureLineToSpeckle(CivilDB.FeatureLine featureline) piPoints.Add(allPoints.IndexOf(piPoint)); } - /* - // get bulges at pi point indices - int count = (featureline.Closed) ? featureline.PointsCount : featureline.PointsCount - 1; - List bulges = new List(); - for (int i = 0; i < count; i++) bulges.Add(featureline.GetBulge(i)); - var piBulges = new List(); - foreach (var index in indices) piBulges.Add(bulges[index]); - */ - // get displayvalue var polyline = PolylineToSpeckle(new Polyline3d(Poly3dType.SimplePoly, intersectionPoints, false)); // featureline Featureline speckleFeatureline = new() { + points = points, curve = CurveToSpeckle(featureline.BaseCurve, ModelUnits), units = ModelUnits, displayValue = new List() { polyline } }; + AddNameAndDescriptionProperty(featureline.Name, featureline.Description, speckleFeatureline); speckleFeatureline["@piPoints"] = piPoints; speckleFeatureline["@elevationPoints"] = ePoints; @@ -1035,7 +1029,7 @@ private List PartDataRecordToSpeckle(PartDataRecord partData) foreach (PartDataField partField in partData.GetAllDataFields()) { - CivilDataField field = new(partField.Name, partField.DataType.ToString(), partField.Units.ToString(), partField.Context.ToString(), partField.Value); + CivilDataField field = new(partField.Name, partField.DataType.ToString(), partField.Value, partField.Units.ToString(),partField.Context.ToString(), null); fields.Add(field); } @@ -1128,13 +1122,154 @@ public Pipe PipeToSpeckle(PressurePipe pipe) // corridors // this is composed of assemblies, alignments, and profiles, use point codes to generate featurelines (which will have the 3d curve) + + private CivilDataField AppliedSubassemblyParamToSpeckle(IAppliedSubassemblyParam param) + { + CivilDataField baseParam = new(param.KeyName, param.ValueType.Name, param.ValueAsObject, null, null, param.DisplayName); + return baseParam; + } + + private CivilAppliedSubassembly AppliedSubassemblyToSpeckle(AppliedSubassembly appliedSubassembly) + { + // retrieve subassembly name + Subassembly subassembly = Trans.GetObject(appliedSubassembly.SubassemblyId, OpenMode.ForRead) as Subassembly; + + // get the calculated shapes + List speckleShapes = new(); + foreach (CalculatedShape shape in appliedSubassembly.Shapes) + { + CivilCalculatedShape speckleShape = CalculatedShapeToSpeckle(shape); + speckleShapes.Add(speckleShape); + } + + Point soePoint = PointToSpeckle(appliedSubassembly.OriginStationOffsetElevationToBaseline); + List speckleParameters = appliedSubassembly.Parameters.Select(p => AppliedSubassemblyParamToSpeckle(p)).ToList(); + + CivilAppliedSubassembly speckleAppliedSubassembly = new(appliedSubassembly.SubassemblyId.ToString(), subassembly.Name, speckleShapes, soePoint, speckleParameters); + return speckleAppliedSubassembly; + } + + private CivilAppliedAssembly AppliedAssemblyToSpeckle(AppliedAssembly appliedAssembly) + { + // get the applied subassemblies + List speckleSubassemblies = new(); + foreach (AppliedSubassembly appliedSubassembly in appliedAssembly.GetAppliedSubassemblies()) + { + CivilAppliedSubassembly speckleSubassembly = AppliedSubassemblyToSpeckle(appliedSubassembly); + speckleSubassemblies.Add(speckleSubassembly); + } + + CivilAppliedAssembly speckleAppliedAssembly = new(speckleSubassemblies, appliedAssembly.AdjustedElevation, ModelUnits); + return speckleAppliedAssembly; + } + + private CivilBaselineRegion BaselineRegionToSpeckle(BaselineRegion region) + { + // get the region assembly + Assembly assembly = Trans.GetObject(region.AssemblyId, OpenMode.ForRead) as Assembly; + + // get the applied assemblies by station + List speckleAppliedAssemblies = new(); + double[] sortedStations = region.SortedStations(); + for (int i = 0; i < sortedStations.Length; i++) + { + double station = sortedStations[i]; + CivilAppliedAssembly speckleAssembly = AppliedAssemblyToSpeckle(region.AppliedAssemblies[i]); + speckleAssembly["station"] = station; + speckleAppliedAssemblies.Add(speckleAssembly); + } + + // create the speckle region + CivilBaselineRegion speckleRegion = new(region.Name, region.StartStation, region.EndStation, assembly.Id.ToString(), assembly.Name, speckleAppliedAssemblies); + return speckleRegion; + } + + private CivilCalculatedShape CalculatedShapeToSpeckle(CalculatedShape shape) + { + List codes = shape.CorridorCodes.ToList(); + List speckleLinks = new(); + foreach (CalculatedLink link in shape.CalculatedLinks) + { + CivilCalculatedLink speckleLink = CalculatedLinkToSpeckle(link); + speckleLinks.Add(speckleLink); + } + + CivilCalculatedShape speckleCalculatedShape = new(codes, speckleLinks, shape.Area, ModelUnits); + return speckleCalculatedShape; + } + + private CivilCalculatedLink CalculatedLinkToSpeckle(CalculatedLink link) + { + List codes = link.CorridorCodes.ToList(); + List specklePoints = new(); + foreach (CalculatedPoint point in link.CalculatedPoints) + { + CivilCalculatedPoint specklePoint = CalculatedPointToSpeckle(point); + specklePoints.Add(specklePoint); + } + + CivilCalculatedLink speckleLink = new(codes, specklePoints); + return speckleLink; + } + + private CivilCalculatedPoint CalculatedPointToSpeckle(CalculatedPoint point) + { + Point specklePoint = PointToSpeckle(point.XYZ); + List codes = point.CorridorCodes.ToList(); + Vector normalBaseline = VectorToSpeckle(point.NormalToBaseline); + Vector normalSubAssembly = VectorToSpeckle(point.NormalToSubassembly); + Point soePoint = PointToSpeckle(point.StationOffsetElevationToBaseline); + CivilCalculatedPoint speckleCalculatedPoint = new(specklePoint, codes, normalBaseline, normalSubAssembly, soePoint); + return speckleCalculatedPoint; + } + + private CivilBaseline BaselineToSpeckle(CivilDB.Baseline baseline) + { + CivilBaseline speckleBaseline = null; + + // get the speckle regions + List speckleRegions = new(); + foreach (BaselineRegion region in baseline.BaselineRegions) + { + CivilBaselineRegion speckleRegion = BaselineRegionToSpeckle(region); + speckleRegions.Add(speckleRegion); + } + + // get profile and alignment if nonfeaturelinebased + // for featureline based corridors, accessing AlignmentId and ProfileId will return NULL + // and throw an exception ""This operation on feature line based baseline is invalid". + if (!baseline.IsFeatureLineBased()) + { + // get the speckle alignment + var alignment = Trans.GetObject(baseline.AlignmentId, OpenMode.ForRead) as CivilDB.Alignment; + CivilAlignment speckleAlignment = AlignmentToSpeckle(alignment); + + // get the speckle profile + var profile = Trans.GetObject(baseline.ProfileId, OpenMode.ForRead) as CivilDB.Profile; + CivilProfile speckleProfile = ProfileToSpeckle(profile); + + speckleBaseline = new(baseline.Name, speckleRegions, baseline.SortedStations().ToList(), baseline.StartStation, baseline.EndStation, speckleAlignment, speckleProfile); + } + else + { + // get the baseline featureline + var featureline = Trans.GetObject(baseline.FeatureLineId, OpenMode.ForRead) as CivilDB.FeatureLine; + Featureline speckleFeatureline = FeaturelineToSpeckle(featureline); + + speckleBaseline = new(baseline.Name, speckleRegions, baseline.SortedStations().ToList(), baseline.StartStation, baseline.EndStation, speckleFeatureline); + } + + return speckleBaseline; + } + public Base CorridorToSpeckle(Corridor corridor) { - List alignments = new(); - List profiles = new(); List featurelines = new(); - foreach (Baseline baseline in corridor.Baselines) + List baselines = new(); + foreach (CivilDB.Baseline baseline in corridor.Baselines) { + CivilBaseline speckleBaseline = BaselineToSpeckle(baseline); + baselines.Add(speckleBaseline); // get the collection of featurelines for this baseline foreach (FeatureLineCollection mainFeaturelineCollection in baseline.MainBaselineFeatureLines.FeatureLineCollectionMap) // main featurelines @@ -1155,32 +1290,6 @@ public Base CorridorToSpeckle(Corridor corridor) } } } - - // get alignment and profile if relevant - // for featureline based corridors, accessing AlignmentId and ProfileId will return NULL - // and throw an exception ""This operation on feature line based baseline is invalid". - if (!baseline.IsFeatureLineBased()) - { - if (baseline.AlignmentId is ObjectId alignmentId) - { - var alignment = Trans.GetObject(alignmentId, OpenMode.ForRead) as CivilDB.Alignment; - var convertedAlignment = AlignmentToSpeckle(alignment); - if (convertedAlignment != null) - { - alignments.Add(convertedAlignment); - } - } - - if (baseline.ProfileId is ObjectId profileId) - { - var profile = Trans.GetObject(profileId, OpenMode.ForRead) as CivilDB.Profile; - var convertedProfile = ProfileToSpeckle(profile); - if (convertedProfile != null) - { - profiles.Add(convertedProfile); - } - } - } } // get corridor surfaces @@ -1202,9 +1311,8 @@ public Base CorridorToSpeckle(Corridor corridor) } var corridorBase = new Base(); - corridorBase["@alignments"] = alignments; - corridorBase["@profiles"] = profiles; corridorBase["@featurelines"] = featurelines; + corridorBase["@baselines"] = baselines; AddNameAndDescriptionProperty(corridor.Name, corridor.Description, corridorBase); corridorBase["units"] = ModelUnits; if (surfaces.Count > 0) diff --git a/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.cs b/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.cs index 957fff734b..96bd06d9f1 100644 --- a/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.cs +++ b/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.cs @@ -206,7 +206,7 @@ public Base ConvertToSpeckle(object @object) @base = CorridorToSpeckle(o); break; case CivilDB.FeatureLine o: - @base = FeatureLineToSpeckle(o); + @base = FeaturelineToSpeckle(o); break; case CivilDB.Structure o: @base = StructureToSpeckle(o); diff --git a/Objects/Objects/BuiltElements/Baseline.cs b/Objects/Objects/BuiltElements/Baseline.cs new file mode 100644 index 0000000000..a78fc9b069 --- /dev/null +++ b/Objects/Objects/BuiltElements/Baseline.cs @@ -0,0 +1,88 @@ +using Speckle.Core.Models; +using Speckle.Newtonsoft.Json; + +namespace Objects.BuiltElements; + +public abstract class Baseline : Base +{ + protected Baseline() { } + + protected Baseline(string name, bool isFeaturelineBased) + { + this.name = name; + this.isFeaturelineBased = isFeaturelineBased; + } + + /// + /// The name of this baseline + /// + public string name { get; set; } + + /// + /// The horizontal component of this baseline + /// + public abstract Alignment? alignment { get; internal set; } + + /// + /// The vertical component of this baseline + /// + public abstract Profile? profile { get; internal set; } + + [DetachProperty] + public Featureline? featureline { get; internal set; } + + public bool isFeaturelineBased { get; set; } +} + +/// +/// Generic instance class +/// +public abstract class Baseline : Baseline + where TA : Alignment + where TP : Profile +{ + protected Baseline(string name, TA alignment, TP profile, Featureline? featureline, bool isFeaturelineBased) + : base(name, isFeaturelineBased) + { + this.name = name; + typedAlignment = alignment; + typedProfile = profile; + this.featureline = featureline; + this.isFeaturelineBased = isFeaturelineBased; + } + + protected Baseline() + : base(string.Empty, false) { } + + [JsonIgnore] + public TA typedAlignment { get; set; } + + [JsonIgnore] + public TP typedProfile { get; set; } + + [DetachProperty] + public override Alignment? alignment + { + get => typedAlignment; + internal set + { + if (value is TA typeA) + { + typedAlignment = typeA; + } + } + } + + [DetachProperty] + public override Profile? profile + { + get => typedProfile; + internal set + { + if (value is TP typeP) + { + typedProfile = typeP; + } + } + } +} diff --git a/Objects/Objects/BuiltElements/Civil/CivilAppliedAssembly.cs b/Objects/Objects/BuiltElements/Civil/CivilAppliedAssembly.cs new file mode 100644 index 0000000000..f490c643de --- /dev/null +++ b/Objects/Objects/BuiltElements/Civil/CivilAppliedAssembly.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using Speckle.Core.Models; + +namespace Objects.BuiltElements.Civil; + +public class CivilAppliedAssembly : Base +{ + public CivilAppliedAssembly() { } + + public CivilAppliedAssembly( + List appliedSubassemblies, + double adjustedElevation, + string units + ) + { + this.appliedSubassemblies = appliedSubassemblies; + this.adjustedElevation = adjustedElevation; + this.units = units; + } + + public List appliedSubassemblies { get; set; } + + public double adjustedElevation { get; set; } + + public string units { get; set; } +} diff --git a/Objects/Objects/BuiltElements/Civil/CivilAppliedSubassembly.cs b/Objects/Objects/BuiltElements/Civil/CivilAppliedSubassembly.cs new file mode 100644 index 0000000000..f4beb7a3d0 --- /dev/null +++ b/Objects/Objects/BuiltElements/Civil/CivilAppliedSubassembly.cs @@ -0,0 +1,37 @@ +using System.Collections.Generic; +using Objects.Geometry; +using Objects.Other.Civil; +using Speckle.Core.Models; + +namespace Objects.BuiltElements.Civil; + +public class CivilAppliedSubassembly : Base +{ + public CivilAppliedSubassembly() { } + + public CivilAppliedSubassembly( + string subassemblyId, + string subassemblyName, + List shapes, + Point stationOffsetElevationToBaseline, + List parameters + ) + { + this.subassemblyId = subassemblyId; + this.subassemblyName = subassemblyName; + this.shapes = shapes; + this.stationOffsetElevationToBaseline = stationOffsetElevationToBaseline; + this.parameters = parameters; + } + + public string subassemblyId { get; set; } + + public string subassemblyName { get; set; } + + public List shapes { get; set; } + + public Point stationOffsetElevationToBaseline { get; set; } + + [DetachProperty] + public List parameters { get; set; } +} diff --git a/Objects/Objects/BuiltElements/Civil/CivilBaseline.cs b/Objects/Objects/BuiltElements/Civil/CivilBaseline.cs new file mode 100644 index 0000000000..826ef8df2b --- /dev/null +++ b/Objects/Objects/BuiltElements/Civil/CivilBaseline.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; + +namespace Objects.BuiltElements.Civil; + +public class CivilBaseline : Baseline +{ + public CivilBaseline() { } + + public CivilBaseline( + string name, + List regions, + List stations, + double startStation, + double endStation, + CivilAlignment alignment, + CivilProfile profile + ) + { + this.name = name; + this.regions = regions; + this.stations = stations; + this.startStation = startStation; + this.endStation = endStation; + this.alignment = alignment; + this.profile = profile; + isFeaturelineBased = false; + } + + public CivilBaseline( + string name, + List regions, + List stations, + double startStation, + double endStation, + Featureline featureline + ) + { + this.name = name; + this.regions = regions; + this.stations = stations; + this.startStation = startStation; + this.endStation = endStation; + this.featureline = featureline; + isFeaturelineBased = true; + } + + public List regions { get; set; } + + public List stations { get; set; } + + public double startStation { get; set; } + + public double endStation { get; set; } +} diff --git a/Objects/Objects/BuiltElements/Civil/CivilBaselineRegion.cs b/Objects/Objects/BuiltElements/Civil/CivilBaselineRegion.cs new file mode 100644 index 0000000000..5aabffd3f7 --- /dev/null +++ b/Objects/Objects/BuiltElements/Civil/CivilBaselineRegion.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; +using Speckle.Core.Models; + +namespace Objects.BuiltElements.Civil; + +public class CivilBaselineRegion : Base +{ + public CivilBaselineRegion() { } + + public CivilBaselineRegion( + string name, + double startStation, + double endStation, + string assemblyId, + string? assemblyName, + List appliedAssemblies + ) + { + this.name = name; + this.startStation = startStation; + this.endStation = endStation; + this.assemblyId = assemblyId; + this.assemblyName = assemblyName; + this.appliedAssemblies = appliedAssemblies; + } + + /// + /// The name of the region + /// + public string name { get; set; } + + /// + /// The id of the assembly of the region + /// + public string assemblyId { get; set; } + + public string? assemblyName { get; set; } + + public double startStation { get; set; } + + public double endStation { get; set; } + + [DetachProperty] + public List appliedAssemblies { get; set; } +} diff --git a/Objects/Objects/BuiltElements/Civil/CivilCalculatedLink.cs b/Objects/Objects/BuiltElements/Civil/CivilCalculatedLink.cs new file mode 100644 index 0000000000..aa0bc94979 --- /dev/null +++ b/Objects/Objects/BuiltElements/Civil/CivilCalculatedLink.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using Speckle.Core.Models; + +namespace Objects.BuiltElements.Civil; + +public class CivilCalculatedLink : Base, ICivilCalculatedObject +{ + public CivilCalculatedLink() { } + + public CivilCalculatedLink(List codes, List points) + { + this.codes = codes; + this.points = points; + } + + public List codes { get; set; } + + [DetachProperty] + public List points { get; set; } +} diff --git a/Objects/Objects/BuiltElements/Civil/CivilCalculatedPoint.cs b/Objects/Objects/BuiltElements/Civil/CivilCalculatedPoint.cs new file mode 100644 index 0000000000..c6ed147b4f --- /dev/null +++ b/Objects/Objects/BuiltElements/Civil/CivilCalculatedPoint.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using Objects.Geometry; +using Speckle.Core.Models; + +namespace Objects.BuiltElements.Civil; + +public class CivilCalculatedPoint : Base, ICivilCalculatedObject +{ + public CivilCalculatedPoint() { } + + public CivilCalculatedPoint( + Point point, + List codes, + Vector normalToBaseline, + Vector normalToSubassembly, + Point stationOffsetElevationToBaseline + ) + { + this.point = point; + this.codes = codes; + this.normalToBaseline = normalToBaseline; + this.normalToSubassembly = normalToSubassembly; + this.stationOffsetElevationToBaseline = stationOffsetElevationToBaseline; + } + + public Point point { get; set; } + + public List codes { get; set; } + + public Vector normalToBaseline { get; set; } + + public Vector normalToSubassembly { get; set; } + + public Point stationOffsetElevationToBaseline { get; set; } +} diff --git a/Objects/Objects/BuiltElements/Civil/CivilCalculatedShape.cs b/Objects/Objects/BuiltElements/Civil/CivilCalculatedShape.cs new file mode 100644 index 0000000000..1e6a3fb7b6 --- /dev/null +++ b/Objects/Objects/BuiltElements/Civil/CivilCalculatedShape.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using Speckle.Core.Models; + +namespace Objects.BuiltElements.Civil; + +public class CivilCalculatedShape : Base, ICivilCalculatedObject +{ + public CivilCalculatedShape() { } + + public CivilCalculatedShape(List codes, List links, double area, string units) + { + this.codes = codes; + this.links = links; + this.area = area; + this.units = units; + } + + public List codes { get; set; } + + [DetachProperty] + public List links { get; set; } + + public double area { get; set; } + + public string units { get; set; } +} diff --git a/Objects/Objects/Interfaces.cs b/Objects/Objects/Interfaces.cs index f732d0d64f..4a4ff2d789 100644 --- a/Objects/Objects/Interfaces.cs +++ b/Objects/Objects/Interfaces.cs @@ -110,5 +110,16 @@ public interface IDisplayValue T displayValue { get; set; } } +/// +/// Represents a calculated object for civil disciplines +/// +public interface ICivilCalculatedObject +{ + /// + /// for this calculated object. + /// + List codes { get; set; } +} + #endregion diff --git a/Objects/Objects/Other/Civil/CivilDataField.cs b/Objects/Objects/Other/Civil/CivilDataField.cs index 48e1a2ad83..6919669d85 100644 --- a/Objects/Objects/Other/Civil/CivilDataField.cs +++ b/Objects/Objects/Other/Civil/CivilDataField.cs @@ -4,17 +4,27 @@ public class CivilDataField : DataField { public CivilDataField() { } - public CivilDataField(string name, string type, string units, string context, object? value = null) + public CivilDataField( + string name, + string type, + object? value, + string? units = null, + string? context = null, + string? displayName = null + ) { this.name = name; this.type = type; + this.value = value; this.units = units; this.context = context; - this.value = value; + this.displayName = displayName; } /// /// The context type of the Civil3D part /// - public string context { get; set; } + public string? context { get; set; } + + public string? displayName { get; set; } } diff --git a/Objects/Objects/Other/DataField.cs b/Objects/Objects/Other/DataField.cs index 6770b3a8ef..4d3fd5a1ea 100644 --- a/Objects/Objects/Other/DataField.cs +++ b/Objects/Objects/Other/DataField.cs @@ -9,12 +9,12 @@ public class DataField : Base { public DataField() { } - public DataField(string name, string type, string units, object? value = null) + public DataField(string name, string type, object? value, string? units = null) { this.name = name; this.type = type; - this.units = units; this.value = value; + this.units = units; } public string name { get; set; } @@ -23,5 +23,5 @@ public DataField(string name, string type, string units, object? value = null) public object? value { get; set; } - public string units { get; set; } + public string? units { get; set; } } From 8fe7878225ede1ac248c85b094360377da047e10 Mon Sep 17 00:00:00 2001 From: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com> Date: Fri, 7 Jun 2024 16:57:28 +0100 Subject: [PATCH 07/52] CNX-9439 interacting with structural gh models in the viewer (#3451) * seperated Revit wall subclasses into own files * Updated build element constructors to use baseCurve if no displayValue is provided * Hopefully fixed all the problems * Beam * Reverted changes to revit wall subtypes * fix warnings * reverted some uneeded changes * Column * Schema info Beam call --- .../SchemaBuilder/CreateSchemaObject.cs | 3 +- .../PartialClasses/ConvertBrace.cs | 21 +- .../PartialClasses/ConvertColumn.cs | 57 +++--- .../PartialClasses/ConvertWall.cs | 27 +-- .../PartialClasses/ConvertBeam.cs | 14 +- .../BuiltElements/AdvanceSteel/AsteelBeam.cs | 4 +- .../BuiltElements/Archicad/ElementShape.cs | 16 +- Objects/Objects/BuiltElements/Beam.cs | 19 +- Objects/Objects/BuiltElements/Brace.cs | 15 +- Objects/Objects/BuiltElements/Column.cs | 21 +- Objects/Objects/BuiltElements/Duct.cs | 72 ++++--- .../Objects/BuiltElements/Revit/RevitBeam.cs | 25 ++- .../Objects/BuiltElements/Revit/RevitBrace.cs | 32 ++- .../BuiltElements/Revit/RevitColumn.cs | 115 ++++++----- .../Objects/BuiltElements/Revit/RevitDuct.cs | 193 +++++++++++------- .../Objects/BuiltElements/Revit/RevitWall.cs | 146 ++++++------- Objects/Objects/BuiltElements/Wall.cs | 41 ++-- Objects/Objects/Interfaces.cs | 14 +- Objects/Objects/Objects.csproj | 2 +- .../Objects/Structural/Geometry/Element1D.cs | 157 +++++++------- 20 files changed, 574 insertions(+), 420 deletions(-) diff --git a/ConnectorGrasshopper/ConnectorGrasshopperShared/SchemaBuilder/CreateSchemaObject.cs b/ConnectorGrasshopper/ConnectorGrasshopperShared/SchemaBuilder/CreateSchemaObject.cs index 3749d94648..67f00ce746 100644 --- a/ConnectorGrasshopper/ConnectorGrasshopperShared/SchemaBuilder/CreateSchemaObject.cs +++ b/ConnectorGrasshopper/ConnectorGrasshopperShared/SchemaBuilder/CreateSchemaObject.cs @@ -252,13 +252,14 @@ public void SwitchConstructor(ConstructorInfo constructor) RegisterPropertyAsInputParameter(p, k++); } + SelectedConstructor = constructor; + UserInterfaceUtils.CreateCanvasDropdownForAllEnumInputs(this, props); Name = constructor.GetCustomAttribute().Name; Description = constructor.GetCustomAttribute().Description; Message = constructor.DeclaringType.FullName.Split('.')[0]; - SelectedConstructor = constructor; Params.Output[0].NickName = constructor.DeclaringType.Name; Params.OnParametersChanged(); diff --git a/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertBrace.cs b/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertBrace.cs index 0613b4d8ab..8de864d03a 100644 --- a/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertBrace.cs +++ b/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertBrace.cs @@ -1,8 +1,10 @@ +using System; using Autodesk.Revit.DB.Structure; using Objects.BuiltElements; using Objects.BuiltElements.Revit; using Speckle.Core.Models; using System.Collections.Generic; +using Objects.Geometry; using DB = Autodesk.Revit.DB; namespace Objects.Converter.Revit; @@ -37,18 +39,21 @@ public ApplicationObject BraceToNative(Brace speckleBrace) private Base BraceToSpeckle(DB.FamilyInstance myFamily, out List notes) { - notes = new List(); - var myBeam = BeamToSpeckle(myFamily, out notes) as RevitBeam; + var myBeam = (RevitBeam)BeamToSpeckle(myFamily, out notes); - var myBrace = new RevitBrace() + var myBrace = new RevitBrace( + myBeam.family, + myBeam.type, + myBeam.baseLine, + myBeam.level, + myBeam.units, + myBeam.elementId, + Array.Empty() + ) { + displayValue = myBeam.displayValue, applicationId = myBeam.applicationId, - type = myBeam.type, - baseLine = myBeam.baseLine, - level = myBeam.level, - family = myBeam.family, parameters = myBeam.parameters, - displayValue = myBeam.displayValue, }; var dynamicProps = myBeam.GetMembers(DynamicBaseMemberType.Dynamic); diff --git a/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertColumn.cs b/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertColumn.cs index 0f813d6165..94efd55a14 100644 --- a/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertColumn.cs +++ b/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertColumn.cs @@ -249,39 +249,29 @@ double topOffset public Base ColumnToSpeckle(DB.FamilyInstance revitColumn, out List notes) { notes = new List(); - var symbol = revitColumn.Document.GetElement(revitColumn.GetTypeId()) as FamilySymbol; - - var speckleColumn = new RevitColumn(); - speckleColumn.family = symbol.FamilyName; - speckleColumn.type = revitColumn.Document.GetElement(revitColumn.GetTypeId()).Name; - speckleColumn.level = ConvertAndCacheLevel(revitColumn, BuiltInParameter.FAMILY_BASE_LEVEL_PARAM); - speckleColumn.topLevel = ConvertAndCacheLevel(revitColumn, BuiltInParameter.FAMILY_TOP_LEVEL_PARAM); - speckleColumn.baseOffset = GetParamValue(revitColumn, BuiltInParameter.FAMILY_BASE_LEVEL_OFFSET_PARAM); - speckleColumn.topOffset = GetParamValue(revitColumn, BuiltInParameter.FAMILY_TOP_LEVEL_OFFSET_PARAM); - speckleColumn.facingFlipped = revitColumn.FacingFlipped; - speckleColumn.handFlipped = revitColumn.HandFlipped; - speckleColumn.isSlanted = revitColumn.IsSlantedColumn; - //speckleColumn.structural = revitColumn.StructuralType == StructuralType.Column; + var symbol = (FamilySymbol)revitColumn.Document.GetElement(revitColumn.GetTypeId()); + + RevitLevel level = ConvertAndCacheLevel(revitColumn, BuiltInParameter.FAMILY_BASE_LEVEL_PARAM); + RevitLevel topLevel = ConvertAndCacheLevel(revitColumn, BuiltInParameter.FAMILY_TOP_LEVEL_PARAM); + double baseOffset = GetParamValue(revitColumn, BuiltInParameter.FAMILY_BASE_LEVEL_OFFSET_PARAM); + double topOffset = GetParamValue(revitColumn, BuiltInParameter.FAMILY_TOP_LEVEL_OFFSET_PARAM); //geometry var baseGeometry = LocationToSpeckle(revitColumn); var baseLine = baseGeometry as ICurve; - //make line from point and height if (baseLine == null && baseGeometry is Point basePoint) + //make line from point and height { - if ( - symbol.Family.FamilyPlacementType == FamilyPlacementType.OneLevelBased - || symbol.Family.FamilyPlacementType == FamilyPlacementType.WorkPlaneBased - ) + if (symbol.Family.FamilyPlacementType is FamilyPlacementType.OneLevelBased or FamilyPlacementType.WorkPlaneBased) { return RevitInstanceToSpeckle(revitColumn, out notes, null); } - var elevation = speckleColumn.topLevel.elevation; + var elevation = topLevel.elevation; baseLine = new Line( basePoint, - new Point(basePoint.x, basePoint.y, elevation + speckleColumn.topOffset, ModelUnits), + new Point(basePoint.x, basePoint.y, elevation + topOffset, ModelUnits), ModelUnits ); } @@ -291,7 +281,25 @@ public Base ColumnToSpeckle(DB.FamilyInstance revitColumn, out List note return RevitElementToSpeckle(revitColumn, out notes); } - speckleColumn.baseLine = baseLine; //all speckle columns should be line based + double rotation = revitColumn.Location is LocationPoint location ? location.Rotation : 0; + + var speckleColumn = new RevitColumn( + symbol.FamilyName, + revitColumn.Document.GetElement(revitColumn.GetTypeId()).Name, + baseLine, //all speckle columns should be line based + level, + topLevel, + ModelUnits, + revitColumn.Id.ToString(), + baseOffset, + topOffset, + revitColumn.FacingFlipped, + revitColumn.HandFlipped, + revitColumn.IsSlantedColumn, + rotation, + GetElementDisplayValue(revitColumn) + //structural: revitColumn.StructuralType == StructuralType.Column; + ); GetAllRevitParamsAndIds( speckleColumn, @@ -307,13 +315,6 @@ public Base ColumnToSpeckle(DB.FamilyInstance revitColumn, out List note } ); - if (revitColumn.Location is LocationPoint) - { - speckleColumn.rotation = ((LocationPoint)revitColumn.Location).Rotation; - } - - speckleColumn.displayValue = GetElementDisplayValue(revitColumn); - return speckleColumn; } } diff --git a/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertWall.cs b/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertWall.cs index 11bbd0ceaf..ef493e7fa9 100644 --- a/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertWall.cs +++ b/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertWall.cs @@ -187,18 +187,21 @@ public Base WallToSpeckle(DB.Wall revitWall, out List notes) return RevitElementToSpeckle(revitWall, out notes); } - RevitWall speckleWall = new(); - speckleWall.family = revitWall.WallType.FamilyName.ToString(); - speckleWall.type = revitWall.WallType.Name; - speckleWall.baseLine = (ICurve)baseGeometry; - speckleWall.level = ConvertAndCacheLevel(revitWall, BuiltInParameter.WALL_BASE_CONSTRAINT); - speckleWall.topLevel = ConvertAndCacheLevel(revitWall, BuiltInParameter.WALL_HEIGHT_TYPE); - speckleWall.height = GetParamValue(revitWall, BuiltInParameter.WALL_USER_HEIGHT_PARAM); - speckleWall.baseOffset = GetParamValue(revitWall, BuiltInParameter.WALL_BASE_OFFSET); - speckleWall.topOffset = GetParamValue(revitWall, BuiltInParameter.WALL_TOP_OFFSET); - speckleWall.structural = GetParamValue(revitWall, BuiltInParameter.WALL_STRUCTURAL_SIGNIFICANT); - speckleWall.flipped = revitWall.Flipped; - + RevitWall speckleWall = + new( + revitWall.WallType.FamilyName, + revitWall.WallType.Name, + (ICurve)baseGeometry, + ConvertAndCacheLevel(revitWall, BuiltInParameter.WALL_BASE_CONSTRAINT), + ConvertAndCacheLevel(revitWall, BuiltInParameter.WALL_HEIGHT_TYPE), + GetParamValue(revitWall, BuiltInParameter.WALL_USER_HEIGHT_PARAM), + ModelUnits, + revitWall.Id.ToString(), + GetParamValue(revitWall, BuiltInParameter.WALL_BASE_OFFSET), + GetParamValue(revitWall, BuiltInParameter.WALL_TOP_OFFSET), + revitWall.Flipped, + GetParamValue(revitWall, BuiltInParameter.WALL_STRUCTURAL_SIGNIFICANT) + ); //CreateVoids(revitWall, speckleWall); if (revitWall.CurtainGrid is not CurtainGrid grid) diff --git a/Objects/Converters/ConverterTeklaStructures/ConverterTeklaStructuresShared/PartialClasses/ConvertBeam.cs b/Objects/Converters/ConverterTeklaStructures/ConverterTeklaStructuresShared/PartialClasses/ConvertBeam.cs index 8ad1e75f55..1c9f0980ff 100644 --- a/Objects/Converters/ConverterTeklaStructures/ConverterTeklaStructuresShared/PartialClasses/ConvertBeam.cs +++ b/Objects/Converters/ConverterTeklaStructures/ConverterTeklaStructuresShared/PartialClasses/ConvertBeam.cs @@ -192,12 +192,14 @@ public TeklaBeam BeamToSpeckle(Tekla.Structures.Model.Beam beam) Point speckleStartPoint = new(startPoint.X, startPoint.Y, startPoint.Z, units); Point speckleEndPoint = new(endPoint.X, endPoint.Y, endPoint.Z, units); - speckleBeam.baseLine = new Line(speckleStartPoint, speckleEndPoint, units); - speckleBeam.baseLine.length = Math.Sqrt( - Math.Pow((startPoint.X - endPoint.X), 2) - + Math.Pow((startPoint.Y - endPoint.Y), 2) - + Math.Pow((startPoint.Z - endPoint.Z), 2) - ); + speckleBeam.baseLine = new Line(speckleStartPoint, speckleEndPoint, units) + { + length = Math.Sqrt( + Math.Pow((startPoint.X - endPoint.X), 2) + + Math.Pow((startPoint.Y - endPoint.Y), 2) + + Math.Pow((startPoint.Z - endPoint.Z), 2) + ), + }; speckleBeam.profile = GetBeamProfile(beam.Profile.ProfileString); speckleBeam.material = GetMaterial(beam.Material.MaterialString); var beamCS = beam.GetCoordinateSystem(); diff --git a/Objects/Objects/BuiltElements/AdvanceSteel/AsteelBeam.cs b/Objects/Objects/BuiltElements/AdvanceSteel/AsteelBeam.cs index 83db3e1c81..af9fde1bba 100644 --- a/Objects/Objects/BuiltElements/AdvanceSteel/AsteelBeam.cs +++ b/Objects/Objects/BuiltElements/AdvanceSteel/AsteelBeam.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; -using Objects.Geometry; using Objects.Structural.Materials; using Objects.Structural.Properties.Profiles; using Speckle.Core.Kits; @@ -7,7 +5,7 @@ namespace Objects.BuiltElements.AdvanceSteel; -public class AsteelBeam : Beam, IDisplayValue>, IHasVolume, IHasArea, IAsteelObject +public class AsteelBeam : Beam, IHasVolume, IHasArea, IAsteelObject { [DetachProperty] public SectionProfile profile { get; set; } diff --git a/Objects/Objects/BuiltElements/Archicad/ElementShape.cs b/Objects/Objects/BuiltElements/Archicad/ElementShape.cs index 4da45cb3ae..cd78ab4717 100644 --- a/Objects/Objects/BuiltElements/Archicad/ElementShape.cs +++ b/Objects/Objects/BuiltElements/Archicad/ElementShape.cs @@ -1,7 +1,9 @@ using System.Collections.Generic; using Objects.Geometry; using Objects.Primitive; +using Speckle.Core.Kits; using Speckle.Core.Models; +using Speckle.Newtonsoft.Json; namespace Objects.BuiltElements.Archicad; @@ -19,6 +21,9 @@ public ElementShape(Polyline contourPolyline, List? holePolylines = nu public List? holePolylines { get; set; } + /// + /// This class is only used for Archicad interop + /// public sealed class PolylineSegment : Base, ICurve { public PolylineSegment() { } @@ -33,21 +38,30 @@ public PolylineSegment(Point startPoint, Point endPoint, double? arcAngle = null public Point startPoint { get; set; } public Point endPoint { get; set; } + + [JsonIgnore] + public string units => Units.Meters; public double arcAngle { get; set; } public bool? bodyFlag { get; set; } public double length { get; set; } public Interval domain { get; set; } = new(0, 1); } + /// + /// This class is only used for Archicad interop + /// public sealed class Polyline : Base, ICurve { public Polyline() { } public Polyline(List segments) { - polylineSegments = segments; + this.polylineSegments = segments; } + [JsonIgnore] + public string units => Units.Meters; + public List polylineSegments { get; set; } = new(); public double length { get; set; } public Interval domain { get; set; } = new(0, 1); diff --git a/Objects/Objects/BuiltElements/Beam.cs b/Objects/Objects/BuiltElements/Beam.cs index 7d917c6c93..d868921632 100644 --- a/Objects/Objects/BuiltElements/Beam.cs +++ b/Objects/Objects/BuiltElements/Beam.cs @@ -5,22 +5,31 @@ namespace Objects.BuiltElements; -public class Beam : Base, IDisplayValue> +public class Beam : Base, IDisplayValue> { public Beam() { } - [SchemaInfo("Beam", "Creates a Speckle beam", "BIM", "Structure")] - public Beam([SchemaMainParam] ICurve baseLine) + public Beam(ICurve baseLine, Level? level, string? units, IReadOnlyList? displayValue = null) { this.baseLine = baseLine; + this.level = level; + this.units = units; + this.displayValue = ((IReadOnlyList?)displayValue) ?? new[] { (Base)baseLine }; } public ICurve baseLine { get; set; } public virtual Level? level { get; internal set; } - public string units { get; set; } + public string? units { get; set; } [DetachProperty] - public List displayValue { get; set; } + public IReadOnlyList displayValue { get; set; } + + #region Schema Info Constructors + [SchemaInfo("Beam", "Creates a Speckle beam", "BIM", "Structure")] + public Beam([SchemaMainParam] ICurve baseLine) + : this(baseLine, null, null) { } + + #endregion } diff --git a/Objects/Objects/BuiltElements/Brace.cs b/Objects/Objects/BuiltElements/Brace.cs index c51cd56828..c8a90447ee 100644 --- a/Objects/Objects/BuiltElements/Brace.cs +++ b/Objects/Objects/BuiltElements/Brace.cs @@ -5,20 +5,25 @@ namespace Objects.BuiltElements; -public class Brace : Base, IDisplayValue> +public class Brace : Base, IDisplayValue> { public Brace() { } - [SchemaInfo("Brace", "Creates a Speckle brace", "BIM", "Structure")] - public Brace([SchemaMainParam] ICurve baseLine) + public Brace(ICurve baseLine, string? units, IReadOnlyList? displayValue = null) { this.baseLine = baseLine; + this.units = units; + this.displayValue = ((IReadOnlyList?)displayValue) ?? new[] { (Base)baseLine }; } public ICurve baseLine { get; set; } - public string units { get; set; } + public string? units { get; set; } [DetachProperty] - public List displayValue { get; set; } + public IReadOnlyList displayValue { get; set; } + + [SchemaInfo("Brace", "Creates a Speckle brace", "BIM", "Structure")] + public Brace([SchemaMainParam] ICurve baseLine) + : this(baseLine, null) { } } diff --git a/Objects/Objects/BuiltElements/Column.cs b/Objects/Objects/BuiltElements/Column.cs index e13e054eaa..3b4d405575 100644 --- a/Objects/Objects/BuiltElements/Column.cs +++ b/Objects/Objects/BuiltElements/Column.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using Objects.Geometry; using Speckle.Core.Kits; @@ -5,22 +6,32 @@ namespace Objects.BuiltElements; -public class Column : Base, IDisplayValue> +public class Column : Base, IDisplayValue> { public Column() { } - [SchemaInfo("Column", "Creates a Speckle column", "BIM", "Structure")] - public Column([SchemaMainParam] ICurve baseLine) + public Column(ICurve baseLine, string? units, Level? level = null, IReadOnlyList? displayValue = null) { this.baseLine = baseLine; + this.units = units; + this.level = level; + this.displayValue = ((IReadOnlyList?)displayValue) ?? new[] { (Base)baseLine }; } public ICurve baseLine { get; set; } public virtual Level? level { get; internal set; } - public string units { get; set; } + public string? units { get; set; } [DetachProperty] - public List displayValue { get; set; } + public IReadOnlyList displayValue { get; set; } + + #region Schema Info Constructors + + [SchemaInfo("Column", "Creates a Speckle column", "BIM", "Structure")] + [SchemaDeprecated, Obsolete("Use other constructor")] + public Column([SchemaMainParam] ICurve baseLine) + : this(baseLine, null) { } + #endregion } diff --git a/Objects/Objects/BuiltElements/Duct.cs b/Objects/Objects/BuiltElements/Duct.cs index f530be6faf..6ef56bbc53 100644 --- a/Objects/Objects/BuiltElements/Duct.cs +++ b/Objects/Objects/BuiltElements/Duct.cs @@ -7,10 +7,47 @@ namespace Objects.BuiltElements; -public class Duct : Base, IDisplayValue> +public class Duct : Base, IDisplayValue> { public Duct() { } + public Duct( + ICurve baseCurve, + double width, + double height, + double diameter, + double length, + string? units, + double velocity = 0, + IReadOnlyList? displayValue = null + ) + { + this.baseCurve = baseCurve; + this.width = width; + this.height = height; + this.diameter = diameter; + this.length = length; + this.units = units; + this.velocity = velocity; + this.displayValue = ((IReadOnlyList?)displayValue) ?? new[] { (Base)baseCurve }; + } + + [JsonIgnore, Obsolete("Replaced with baseCurve property")] + public Line? baseLine { get; set; } + + public ICurve baseCurve { get; set; } + public double width { get; set; } + public double height { get; set; } + public double diameter { get; set; } + public double length { get; set; } + public double velocity { get; set; } + + public string? units { get; set; } + + [DetachProperty] + public IReadOnlyList displayValue { get; set; } + + #region Schema Info Constructors /// /// SchemaBuilder constructor for a Speckle duct /// @@ -22,13 +59,8 @@ public Duct() { } /// Assign units when using this constructor due to , , and params [SchemaInfo("Duct", "Creates a Speckle duct", "BIM", "MEP"), SchemaDeprecated] public Duct([SchemaMainParam] Line baseLine, double width, double height, double diameter, double velocity = 0) - { - baseCurve = baseLine; - this.width = width; - this.height = height; - this.diameter = diameter; - this.velocity = velocity; - } + : this(baseLine, width, height, diameter, default, null, velocity) //TODO: what to do with length??? + { } /// /// SchemaBuilder constructor for a Speckle duct @@ -41,26 +73,6 @@ public Duct([SchemaMainParam] Line baseLine, double width, double height, double /// Assign units when using this constructor due to , , and params [SchemaInfo("Duct", "Creates a Speckle duct", "BIM", "MEP")] public Duct([SchemaMainParam] ICurve baseCurve, double width, double height, double diameter, double velocity = 0) - { - this.baseCurve = baseCurve; - this.width = width; - this.height = height; - this.diameter = diameter; - this.velocity = velocity; - } - - [JsonIgnore, Obsolete("Replaced with baseCurve property")] - public Line baseLine { get; set; } - - public ICurve baseCurve { get; set; } - public double width { get; set; } - public double height { get; set; } - public double diameter { get; set; } - public double length { get; set; } - public double velocity { get; set; } - - public string units { get; set; } - - [DetachProperty] - public List displayValue { get; set; } + : this(baseCurve, width, height, diameter, default, null, velocity) { } //TODO: what to do with length??? + #endregion } diff --git a/Objects/Objects/BuiltElements/Revit/RevitBeam.cs b/Objects/Objects/BuiltElements/Revit/RevitBeam.cs index b483b12a11..6d7859e3da 100644 --- a/Objects/Objects/BuiltElements/Revit/RevitBeam.cs +++ b/Objects/Objects/BuiltElements/Revit/RevitBeam.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Objects.Geometry; using Objects.Utils; using Speckle.Core.Kits; using Speckle.Core.Models; @@ -9,20 +10,20 @@ public class RevitBeam : Beam { public RevitBeam() { } - [SchemaInfo("RevitBeam", "Creates a Revit beam by curve and base level.", "Revit", "Structure")] public RevitBeam( string family, string type, - [SchemaMainParam] ICurve baseLine, - Level level, + ICurve baseLine, + Level? level, + string? units, + List? displayValue = null, List? parameters = null ) + : base(baseLine, level, units, displayValue) { this.family = family; this.type = type; - this.baseLine = baseLine; this.parameters = parameters?.ToBase(); - this.level = level; } public string family { get; set; } @@ -35,4 +36,18 @@ public RevitBeam( get => base.level; set => base.level = value; } + + #region Schema Info Constructors + + [SchemaInfo("RevitBeam", "Creates a Revit beam by curve and base level.", "Revit", "Structure")] + public RevitBeam( + string family, + string type, + [SchemaMainParam] ICurve baseLine, + Level level, + List? parameters = null + ) + : this(family, type, baseLine, level, null, parameters: parameters) { } + + #endregion } diff --git a/Objects/Objects/BuiltElements/Revit/RevitBrace.cs b/Objects/Objects/BuiltElements/Revit/RevitBrace.cs index 99499f8826..dc92fbf296 100644 --- a/Objects/Objects/BuiltElements/Revit/RevitBrace.cs +++ b/Objects/Objects/BuiltElements/Revit/RevitBrace.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Objects.Geometry; using Objects.Utils; using Speckle.Core.Kits; using Speckle.Core.Models; @@ -9,25 +10,42 @@ public class RevitBrace : Brace { public RevitBrace() { } - [SchemaInfo("RevitBrace", "Creates a Revit brace by curve and base level.", "Revit", "Structure")] public RevitBrace( string family, string type, - [SchemaMainParam] ICurve baseLine, - Level level, + ICurve baseLine, + Level? level, + string? units, + string? elementId, + IReadOnlyList? displayValue = null, List? parameters = null ) + : base(baseLine, units, displayValue) { this.family = family; this.type = type; - this.baseLine = baseLine; - this.parameters = parameters?.ToBase(); this.level = level; + this.elementId = elementId; + this.parameters = parameters?.ToBase(); } public string family { get; set; } public string type { get; set; } public Base? parameters { get; set; } - public string elementId { get; set; } - public Level level { get; set; } + public string? elementId { get; set; } + public Level? level { get; set; } + + #region Schema Info Constructor + + [SchemaInfo("RevitBrace", "Creates a Revit brace by curve and base level.", "Revit", "Structure")] + public RevitBrace( + string family, + string type, + [SchemaMainParam] ICurve baseLine, + Level level, + List? parameters = null + ) + : this(family, type, baseLine, level, null, null, parameters: parameters) { } + + #endregion } diff --git a/Objects/Objects/BuiltElements/Revit/RevitColumn.cs b/Objects/Objects/BuiltElements/Revit/RevitColumn.cs index 5a770db9ba..64e4db93b0 100644 --- a/Objects/Objects/BuiltElements/Revit/RevitColumn.cs +++ b/Objects/Objects/BuiltElements/Revit/RevitColumn.cs @@ -1,4 +1,6 @@ +using System; using System.Collections.Generic; +using Objects.Geometry; using Objects.Utils; using Speckle.Core.Kits; using Speckle.Core.Models; @@ -9,49 +11,82 @@ public class RevitColumn : Column { public RevitColumn() { } - /// - /// SchemaBuilder constructor for a Revit column - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Assign units when using this constructor due to and params - [SchemaInfo("RevitColumn Vertical", "Creates a vertical Revit Column by point and levels.", "Revit", "Architecture")] public RevitColumn( string family, string type, - [SchemaParamInfo("Only the lower point of this line will be used as base point."), SchemaMainParam] ICurve baseLine, - Level level, - Level topLevel, + ICurve baseLine, + Level? level, + Level? topLevel, + string? units, + string? elementId, double baseOffset = 0, double topOffset = 0, - bool structural = false, - [SchemaParamInfo("Rotation angle in radians")] double rotation = 0, + bool facingFlipped = false, + bool handFlipped = false, + bool isSlanted = false, + double rotation = 0, + IReadOnlyList? displayValue = null, List? parameters = null ) + : base(baseLine, units, level, displayValue) { this.family = family; this.type = type; - this.baseLine = baseLine; this.topLevel = topLevel; + this.elementId = elementId; this.baseOffset = baseOffset; this.topOffset = topOffset; + this.facingFlipped = facingFlipped; + this.handFlipped = handFlipped; + this.isSlanted = isSlanted; this.rotation = rotation; this.parameters = parameters?.ToBase(); - this.level = level; } - [ - SchemaDeprecated, - SchemaInfo("RevitColumn Slanted (old)", "Creates a slanted Revit Column by curve.", "Revit", "Structure") - ] + public Level? topLevel { get; set; } + public double baseOffset { get; set; } + public double topOffset { get; set; } + public bool facingFlipped { get; set; } + public bool handFlipped { get; set; } + public double rotation { get; set; } + public bool isSlanted { get; set; } + public string family { get; set; } + public string type { get; set; } + public Base? parameters { get; set; } + public string? elementId { get; set; } + + #region Schema Info Constructors + + [SchemaInfo("RevitColumn Vertical", "Creates a vertical Revit Column by point and levels.", "Revit", "Architecture")] + public RevitColumn( + string family, + string type, + [SchemaParamInfo("Only the lower point of this line will be used as base point."), SchemaMainParam] ICurve baseLine, + Level level, + Level topLevel, + double baseOffset = 0, + double topOffset = 0, + bool structural = false, + [SchemaParamInfo("Rotation angle in radians")] double rotation = 0, + List? parameters = null + ) + : this( + family, + type, + baseLine, + level, + topLevel, + null, + null, + baseOffset, + topOffset, + rotation: rotation, + parameters: parameters + ) { } + + [Obsolete("Use other constructors")] + [SchemaDeprecated] + [SchemaInfo("RevitColumn Slanted (old)", "Creates a slanted Revit Column by curve.", "Revit", "Structure")] [System.Diagnostics.CodeAnalysis.SuppressMessage( "Style", "IDE0060:Remove unused parameter", @@ -84,31 +119,7 @@ public RevitColumn( bool structural = false, List? parameters = null ) - { - this.family = family; - this.type = type; - this.baseLine = baseLine; - this.level = level; - this.topLevel = topLevel; - isSlanted = true; - this.parameters = parameters?.ToBase(); - } - - public new Level? level - { - get => base.level; - set => base.level = value; - } + : this(family, type, baseLine, level, topLevel, null, null, displayValue: null, parameters: parameters) { } - public Level? topLevel { get; set; } - public double baseOffset { get; set; } - public double topOffset { get; set; } - public bool facingFlipped { get; set; } - public bool handFlipped { get; set; } - public double rotation { get; set; } - public bool isSlanted { get; set; } - public string family { get; set; } - public string type { get; set; } - public Base? parameters { get; set; } - public string elementId { get; set; } + #endregion } diff --git a/Objects/Objects/BuiltElements/Revit/RevitDuct.cs b/Objects/Objects/BuiltElements/Revit/RevitDuct.cs index 8c75f4ba04..1501466456 100644 --- a/Objects/Objects/BuiltElements/Revit/RevitDuct.cs +++ b/Objects/Objects/BuiltElements/Revit/RevitDuct.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using Objects.BuiltElements.Revit.Interfaces; using Objects.Geometry; @@ -11,69 +12,52 @@ public class RevitDuct : Duct, IHasMEPConnectors { public RevitDuct() { } - /// - /// SchemaBuilder constructor for a Revit duct (deprecated) - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Assign units when using this constructor due to , , and params - [SchemaInfo("RevitDuct", "Creates a Revit duct", "Revit", "MEP"), SchemaDeprecated] public RevitDuct( string family, string type, - [SchemaMainParam] Line baseLine, + ICurve baseCurve, string systemName, string systemType, Level level, double width, double height, double diameter, + double length, + string? units, + string? elementId, double velocity = 0, + IReadOnlyList? displayValue = null, List? parameters = null ) + : base(baseCurve, width, height, diameter, length, units, velocity, displayValue) { - baseCurve = baseLine; this.family = family; this.type = type; - this.width = width; - this.height = height; - this.diameter = diameter; - this.velocity = velocity; this.systemName = systemName; this.systemType = systemType; - this.parameters = parameters?.ToBase(); this.level = level; + this.parameters = parameters?.ToBase(); + this.elementId = elementId; } - /// - /// SchemaBuilder constructor for a Revit duct - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Assign units when using this constructor due to , , and params - [SchemaInfo("RevitDuct", "Creates a Revit duct", "Revit", "MEP")] + public string family { get; set; } + public string type { get; set; } + public string systemName { get; set; } + public string systemType { get; set; } + public Level level { get; set; } + public Base? parameters { get; set; } + public string? elementId { get; set; } + public List Connectors { get; set; } = new(); + + #region Schema Info Constructors + + [SchemaInfo("RevitDuct (DEPRECATED)", "Creates a Revit duct", "Revit", "MEP")] + [SchemaDeprecated] + [Obsolete("Use other Constructor")] public RevitDuct( string family, string type, - [SchemaMainParam] ICurve baseCurve, + [SchemaMainParam] Line baseLine, string systemName, string systemType, Level level, @@ -84,7 +68,7 @@ public RevitDuct( List? parameters = null ) { - this.baseCurve = baseCurve; + baseCurve = baseLine; this.family = family; this.type = type; this.width = width; @@ -97,36 +81,44 @@ public RevitDuct( this.level = level; } - public string family { get; set; } - public string type { get; set; } - public string systemName { get; set; } - public string systemType { get; set; } - public Level level { get; set; } - public Base? parameters { get; set; } - public string elementId { get; set; } - public List Connectors { get; set; } = new(); + [SchemaInfo("RevitDuct", "Creates a Revit duct", "Revit", "MEP")] + public RevitDuct( + string family, + string type, + [SchemaMainParam] ICurve baseCurve, + string systemName, + string systemType, + Level level, + double width, + double height, + double diameter, + double velocity = 0, + List? parameters = null + ) + : this( + family, + type, + baseCurve, + systemName, + systemType, + level, + width, + height, + diameter, + default, //TODO: what to do with length? + null, + null, + velocity, + parameters: parameters + ) { } + + #endregion } public class RevitFlexDuct : RevitDuct { public RevitFlexDuct() { } - /// - /// SchemaBuilder constructor for a Revit flex duct - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Assign units when using this constructor due to , , and params - [SchemaInfo("RevitFlexDuct", "Creates a Revit flex duct", "Revit", "MEP")] public RevitFlexDuct( string family, string type, @@ -137,27 +129,76 @@ public RevitFlexDuct( double width, double height, double diameter, + double length, Vector startTangent, Vector endTangent, + string? units, + string? elementId, double velocity = 0, + IReadOnlyList? displayValue = null, List? parameters = null ) + : base( + family, + type, + baseCurve, + systemName, + systemType, + level, + width, + height, + diameter, + length, + units, + elementId, + velocity, + displayValue, + parameters + ) { - this.baseCurve = baseCurve; - this.family = family; - this.type = type; - this.width = width; - this.height = height; - this.diameter = diameter; this.startTangent = startTangent; this.endTangent = endTangent; - this.velocity = velocity; - this.systemName = systemName; - this.systemType = systemType; - this.parameters = parameters?.ToBase(); - this.level = level; } public Vector startTangent { get; set; } public Vector endTangent { get; set; } + + #region Schema Info Constructor + + [SchemaInfo("RevitFlexDuct", "Creates a Revit flex duct", "Revit", "MEP")] + public RevitFlexDuct( + string family, + string type, + [SchemaMainParam] ICurve baseCurve, + string systemName, + string systemType, + Level level, + double width, + double height, + double diameter, + Vector startTangent, + Vector endTangent, + double velocity = 0, + List? parameters = null + ) + : this( + family, + type, + baseCurve, + systemName, + systemType, + level, + width, + height, + diameter, + 0, + startTangent, + endTangent, + null, + null, + velocity, + parameters: parameters + ) { } + + #endregion } diff --git a/Objects/Objects/BuiltElements/Revit/RevitWall.cs b/Objects/Objects/BuiltElements/Revit/RevitWall.cs index ac9d4bfe29..a9d59dc5ad 100644 --- a/Objects/Objects/BuiltElements/Revit/RevitWall.cs +++ b/Objects/Objects/BuiltElements/Revit/RevitWall.cs @@ -11,69 +11,84 @@ public class RevitWall : Wall { public RevitWall() { } - /// - /// SchemaBuilder constructor for a Revit wall - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Assign units when using this constructor due to and params - [SchemaInfo( - "RevitWall by curve and levels", - "Creates a Revit wall with a top and base level.", - "Revit", - "Architecture" - )] public RevitWall( string family, string type, - [SchemaMainParam] ICurve baseLine, + ICurve baseLine, Level level, - Level topLevel, + Level? topLevel, + double height, + string? units, + string? elementId, double baseOffset = 0, double topOffset = 0, bool flipped = false, bool structural = false, - [SchemaParamInfo("Set in here any nested elements that this level might have.")] List? elements = null, + IReadOnlyList? displayValue = null, + List? elements = null, List? parameters = null ) + : base(height, units, baseLine, level, displayValue, elements) { this.family = family; this.type = type; - this.baseLine = baseLine; this.baseOffset = baseOffset; this.topOffset = topOffset; this.flipped = flipped; this.structural = structural; - this.level = level; + this.elementId = elementId; this.topLevel = topLevel; - this.elements = elements; this.parameters = parameters?.ToBase(); } - /// - /// SchemaBuilder constructor for a Revit wall - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Assign units when using this constructor due to , , and params + public string family { get; set; } + public string type { get; set; } + public double baseOffset { get; set; } + public double topOffset { get; set; } + public bool flipped { get; set; } + public bool structural { get; set; } + public Level? topLevel { get; set; } + public Base? parameters { get; set; } + public string? elementId { get; set; } + + #region Schema Info Constructors + + [SchemaInfo( + "RevitWall by curve and levels", + "Creates a Revit wall with a top and base level.", + "Revit", + "Architecture" + )] + public RevitWall( + string family, + string type, + [SchemaMainParam] ICurve baseLine, + Level level, + Level topLevel, + double baseOffset = 0, + double topOffset = 0, + bool flipped = false, + bool structural = false, + [SchemaParamInfo("Set in here any nested elements that this level might have.")] List? elements = null, + List? parameters = null + ) + : this( + family, + type, + baseLine, + level, + topLevel, + 0, + null, + null, + baseOffset, + topOffset, + flipped, + structural, + elements: elements, + parameters: parameters + ) { } + [SchemaInfo("RevitWall by curve and height", "Creates an unconnected Revit wall.", "Revit", "Architecture")] public RevitWall( string family, @@ -88,36 +103,23 @@ public RevitWall( [SchemaParamInfo("Set in here any nested elements that this wall might have.")] List? elements = null, List? parameters = null ) - { - this.family = family; - this.type = type; - this.baseLine = baseLine; - this.height = height; - this.baseOffset = baseOffset; - this.topOffset = topOffset; - this.flipped = flipped; - this.structural = structural; - this.level = level; - this.elements = elements; - this.parameters = parameters?.ToBase(); - } - - public string family { get; set; } - public string type { get; set; } - public double baseOffset { get; set; } - public double topOffset { get; set; } - public bool flipped { get; set; } - public bool structural { get; set; } - - public new Level? level - { - get => base.level; - set => base.level = value; - } - - public Level topLevel { get; set; } - public Base? parameters { get; set; } - public string elementId { get; set; } + : this( + family, + type, + baseLine, + level, + null, + height, + null, + null, + baseOffset, + topOffset, + flipped, + structural, + elements: elements, + parameters: parameters + ) { } + #endregion } public class RevitFaceWall : Wall diff --git a/Objects/Objects/BuiltElements/Wall.cs b/Objects/Objects/BuiltElements/Wall.cs index 9f6cb754d7..46b8684356 100644 --- a/Objects/Objects/BuiltElements/Wall.cs +++ b/Objects/Objects/BuiltElements/Wall.cs @@ -5,39 +5,48 @@ namespace Objects.BuiltElements; -public class Wall : Base, IDisplayValue> +public class Wall : Base, IDisplayValue> { public Wall() { } - /// - /// SchemaBuilder constructor for a Speckle wall - /// - /// - /// - /// - /// Assign units when using this constructor due to param - [SchemaInfo("Wall", "Creates a Speckle wall", "BIM", "Architecture")] public Wall( double height, - [SchemaMainParam] ICurve baseLine, - [SchemaParamInfo("Any nested elements that this wall might have")] List? elements = null + string? units, + ICurve baseLine, + Level? level = null, + IReadOnlyList? displayValue = null, + List? elements = null ) { this.height = height; + this.units = units; this.baseLine = baseLine; + this.level = level; + this.displayValue = ((IReadOnlyList?)displayValue) ?? new[] { (Base)baseLine }; this.elements = elements; } public double height { get; set; } - [DetachProperty] - public List? elements { get; set; } - + public string? units { get; set; } public ICurve baseLine { get; set; } public virtual Level? level { get; internal set; } - public string units { get; set; } + [DetachProperty] + public List? elements { get; set; } [DetachProperty] - public List displayValue { get; set; } + public IReadOnlyList displayValue { get; set; } + + #region SchemaInfo Ctors + + [SchemaInfo("Wall", "Creates a Speckle wall", "BIM", "Architecture")] + public Wall( + double height, + [SchemaMainParam] ICurve baseLine, + [SchemaParamInfo("Any nested elements that this wall might have")] List? elements = null + ) + : this(height, null, baseLine, null, null, elements) { } + + #endregion } diff --git a/Objects/Objects/Interfaces.cs b/Objects/Objects/Interfaces.cs index 4a4ff2d789..373e129d2e 100644 --- a/Objects/Objects/Interfaces.cs +++ b/Objects/Objects/Interfaces.cs @@ -28,7 +28,7 @@ public interface IHasArea /// /// The area of the object /// - double area { get; set; } + double area { get; } } /// @@ -39,7 +39,7 @@ public interface IHasVolume /// /// The volume of the object /// - double volume { get; set; } + double volume { get; } } /// @@ -50,12 +50,14 @@ public interface ICurve /// /// The length of the curve. /// - double length { get; set; } + double length { get; } /// /// The numerical domain driving the curve's internal parametrization. /// - Interval domain { get; set; } + Interval domain { get; } + + string units { get; } } /// @@ -101,13 +103,13 @@ public interface ITransformable /// Expected to be either a type or a of s, /// most likely or . /// -public interface IDisplayValue +public interface IDisplayValue { /// /// (s) will be used to display this /// if a native displayable object cannot be converted. /// - T displayValue { get; set; } + T displayValue { get; } } /// diff --git a/Objects/Objects/Objects.csproj b/Objects/Objects/Objects.csproj index 52f203fa66..62cb80c226 100644 --- a/Objects/Objects/Objects.csproj +++ b/Objects/Objects/Objects.csproj @@ -19,7 +19,7 @@ $(WarningsNotAsErrors); CA1008; CA1024; CA1034; CA1065; CA1708; CA1711; CA1716; CA1724; CA1725; - CA1819; + CA1819; CA2201; CA2225; CS0659; CS0661; CS0728; CS8618; IDE0041; IDE0060; IDE1006; diff --git a/Objects/Objects/Structural/Geometry/Element1D.cs b/Objects/Objects/Structural/Geometry/Element1D.cs index d1d74eab82..abce21d796 100644 --- a/Objects/Objects/Structural/Geometry/Element1D.cs +++ b/Objects/Objects/Structural/Geometry/Element1D.cs @@ -6,27 +6,74 @@ namespace Objects.Structural.Geometry; -public class Element1D : Base, IDisplayValue> +public class Element1D : Base, IDisplayValue> { public Element1D() { } - public Element1D(Line baseLine) + public Element1D( + Line baseLine, + Property1D property, + ElementType1D type, + string? name, + string? units, + Restraint? end1Releases = null, + Restraint? end2Releases = null, + Vector? end1Offset = null, + Vector? end2Offset = null, + Plane? localAxis = null, + Node? orientationNode = null, + double orientationAngle = 0, + IReadOnlyList? displayValue = null + ) { this.baseLine = baseLine; + this.property = property; + this.type = type; + this.name = name; + this.units = units; + this.end1Releases = end1Releases ?? new Restraint("FFFFFF"); + this.end2Releases = end2Releases ?? new Restraint("FFFFFF"); + this.end1Offset = end1Offset ?? new Vector(0, 0, 0); + this.end2Offset = end2Offset ?? new Vector(0, 0, 0); + this.localAxis = localAxis; + this.orientationNode = orientationNode; + this.orientationAngle = orientationAngle; + this.displayValue = ((IReadOnlyList?)displayValue) ?? new[] { (Base)baseLine }; } - /// - /// SchemaBuilder constructor for structural 1D element (based on local axis) - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// + public string? name { get; set; } //add unique id as base identifier, name can change too easily + public Line baseLine { get; set; } + + [DetachProperty] + public Property1D property { get; set; } + + public ElementType1D type { get; set; } + public Restraint end1Releases { get; set; } + public Restraint end2Releases { get; set; } + public Vector end1Offset { get; set; } + public Vector end2Offset { get; set; } + public Node? orientationNode { get; set; } + public double orientationAngle { get; set; } + public Plane? localAxis { get; set; } + + [DetachProperty] + public Base? parent { get; set; } //parent element + + [DetachProperty] + public Node? end1Node { get; set; } //startNode + + [DetachProperty] + public Node? end2Node { get; set; } //endNode + + [DetachProperty] + public List? topology { get; set; } + + public string? units { get; set; } + + [DetachProperty] + public IReadOnlyList displayValue { get; set; } + + #region Schema Info Constructors [SchemaInfo( "Element1D (from local axis)", "Creates a Speckle structural 1D element (from local axis)", @@ -46,31 +93,8 @@ public Element1D( [SchemaParamInfo("If null, defaults to no offsets")] Vector? end2Offset = null, Plane? localAxis = null ) - { - this.baseLine = baseLine; - this.property = property; - this.type = type; - this.name = name; - this.end1Releases = end1Releases ?? new Restraint("FFFFFF"); - this.end2Releases = end2Releases ?? new Restraint("FFFFFF"); - this.end1Offset = end1Offset ?? new Vector(0, 0, 0); - this.end2Offset = end2Offset ?? new Vector(0, 0, 0); - this.localAxis = localAxis; - } + : this(baseLine, property, type, name, null, end1Releases, end2Releases, end1Offset, end2Offset, localAxis) { } - /// - /// SchemaBuilder constructor for structural 1D element (based on orientation node and angle) - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// [SchemaInfo( "Element1D (from orientation node and angle)", "Creates a Speckle structural 1D element (from orientation node and angle)", @@ -91,48 +115,19 @@ public Element1D( Node? orientationNode = null, double orientationAngle = 0 ) - { - this.baseLine = baseLine; - this.property = property; - this.type = type; - this.name = name; - this.end1Releases = end1Releases ?? new Restraint("FFFFFF"); - this.end2Releases = end2Releases ?? new Restraint("FFFFFF"); - this.end1Offset = end1Offset ?? new Vector(0, 0, 0); - this.end2Offset = end2Offset ?? new Vector(0, 0, 0); - this.orientationNode = orientationNode; - this.orientationAngle = orientationAngle; - } - - public string? name { get; set; } //add unique id as base identifier, name can change too easily - public Line baseLine { get; set; } + : this( + baseLine, + property, + type, + name, + null, + end1Releases, + end2Releases, + end1Offset, + end2Offset, + orientationNode: orientationNode, + orientationAngle: orientationAngle + ) { } - [DetachProperty] - public Property1D property { get; set; } - - public ElementType1D type { get; set; } - public Restraint end1Releases { get; set; } - public Restraint end2Releases { get; set; } - public Vector end1Offset { get; set; } - public Vector end2Offset { get; set; } - public Node? orientationNode { get; set; } - public double orientationAngle { get; set; } - public Plane? localAxis { get; set; } - - [DetachProperty] - public Base parent { get; set; } //parent element - - [DetachProperty] - public Node end1Node { get; set; } //startNode - - [DetachProperty] - public Node end2Node { get; set; } //endNode - - [DetachProperty] - public List topology { get; set; } - - public string units { get; set; } - - [DetachProperty] - public List displayValue { get; set; } + #endregion } From 2e6e61f5f2e781b028f576ad477d7e93e6fa5742 Mon Sep 17 00:00:00 2001 From: Claire Kuang Date: Sat, 8 Jun 2024 08:20:50 -0700 Subject: [PATCH 08/52] CNX-9768: add additional properties to Civil3D pipes and structures (#3486) adds partdata to pressure pipes, and additional props to pipes and structures --- .../ConverterAutocadCivil.Civil.cs | 69 +++++++++++-------- 1 file changed, 39 insertions(+), 30 deletions(-) diff --git a/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.Civil.cs b/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.Civil.cs index fce119ee81..89f47ac800 100644 --- a/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.Civil.cs +++ b/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.Civil.cs @@ -12,7 +12,6 @@ using Civil = Autodesk.Civil; using Autodesk.AutoCAD.Geometry; using Acad = Autodesk.AutoCAD.Geometry; -using AcadDB = Autodesk.AutoCAD.DatabaseServices; using Objects.BuiltElements.Civil; using Alignment = Objects.BuiltElements.Alignment; @@ -1010,12 +1009,19 @@ public Structure StructureToSpeckle(CivilDB.Structure structure) }; // assign additional structure props - try { speckleStructure["grate"] = structure.Grate; } catch (Exception ex) when (!ex.IsFatal()) { } - try { speckleStructure["station"] = structure.Station; } catch (Exception ex) when (!ex.IsFatal()) { } - try { speckleStructure["network"] = structure.NetworkName; } catch (Exception ex) when (!ex.IsFatal()) { } AddNameAndDescriptionProperty(structure.Name, structure.Description, speckleStructure); speckleStructure["partData"] = PartDataRecordToSpeckle(structure.PartData); + try { speckleStructure["station"] = structure.Station; } catch (Exception ex) when (!ex.IsFatal()) { } + try { speckleStructure["network"] = structure.NetworkName; } catch (Exception ex) when (!ex.IsFatal()) { } + try { speckleStructure["rotation"] = structure.Rotation; } catch (Exception e) when (!e.IsFatal()) { } + try { speckleStructure["sumpDepth"] = structure.SumpDepth; } catch (Exception e) when (!e.IsFatal()) { } + try { speckleStructure["rimElevation"] = structure.RimElevation; } catch (Exception e) when (!e.IsFatal()) { } + try { speckleStructure["sumpElevation"] = structure.SumpElevation; } catch (Exception e) when (!e.IsFatal()) { } + try { speckleStructure["lengthOuter"] = structure.Length; } catch (Exception e) when (!e.IsFatal()) { } + try { speckleStructure["lengthInner"] = structure.InnerLength; } catch (Exception e) when (!e.IsFatal()) { } + try { speckleStructure["structureId"] = structure.Id.ToString(); } catch (Exception ex) when (!ex.IsFatal()) { } + return speckleStructure; } @@ -1036,22 +1042,30 @@ private List PartDataRecordToSpeckle(PartDataRecord partData) return fields; } +#if CIVIL2022_OR_GREATER + /// + /// Converts PressureNetworkPartData into a list of DataField + /// + private List PartDataRecordToSpeckle(PressureNetworkPartData partData) + { + List fields = new(); + + foreach (PressurePartProperty partField in partData) + { + CivilDataField field = new(partField.Name, partField.GetType().ToString(), partField.Value, null, null, partField.DisplayName); + fields.Add(field); + } + + return fields; + } +#endif + // pipes // TODO: add pressure fittings public Pipe PipeToSpeckle(CivilDB.Pipe pipe) { - // get the pipe curve - ICurve curve; - switch (pipe.SubEntityType) - { - case PipeSubEntityType.Straight: - var line = new Acad.LineSegment3d(pipe.StartPoint, pipe.EndPoint); - curve = LineToSpeckle(line); - break; - default: - curve = CurveToSpeckle(pipe.BaseCurve); - break; - } + ICurve curve = CurveToSpeckle(pipe.BaseCurve); + Pipe specklePipe = new() { @@ -1075,8 +1089,9 @@ public Pipe PipeToSpeckle(CivilDB.Pipe pipe) try { specklePipe["endOffset"] = pipe.EndOffset; } catch(Exception ex) when(!ex.IsFatal()) { } try { specklePipe["startStation"] = pipe.StartStation; } catch(Exception ex) when(!ex.IsFatal()) { } try { specklePipe["endStation"] = pipe.EndStation; } catch(Exception ex) when(!ex.IsFatal()) { } - try { specklePipe["startStructure"] = pipe.StartStructureId.ToString(); } catch(Exception ex) when(!ex.IsFatal()) { } - try { specklePipe["endStructure"] = pipe.EndStructureId.ToString(); } catch(Exception ex) when(!ex.IsFatal()) { } + try { specklePipe["startStructureId"] = pipe.StartStructureId.ToString(); } catch(Exception ex) when(!ex.IsFatal()) { } + try { specklePipe["endStructureId"] = pipe.EndStructureId.ToString(); } catch(Exception ex) when(!ex.IsFatal()) { } + try { specklePipe["pipeId"] = pipe.Id.ToString(); } catch (Exception ex) when (!ex.IsFatal()) { } return specklePipe; } @@ -1084,17 +1099,7 @@ public Pipe PipeToSpeckle(CivilDB.Pipe pipe) public Pipe PipeToSpeckle(PressurePipe pipe) { // get the pipe curve - ICurve curve; - switch (pipe.BaseCurve) - { - case AcadDB.Line: - var line = new LineSegment3d(pipe.StartPoint, pipe.EndPoint); - curve = LineToSpeckle(line); - break; - default: - curve = CurveToSpeckle(pipe.BaseCurve); - break; - } + ICurve curve = CurveToSpeckle(pipe.BaseCurve); Pipe specklePipe = new() { @@ -1107,8 +1112,11 @@ public Pipe PipeToSpeckle(PressurePipe pipe) // assign additional pipe props AddNameAndDescriptionProperty(pipe.Name, pipe.Description, specklePipe); - specklePipe["isPressurePipe"] = true; +#if CIVIL2022_OR_GREATER + specklePipe["partData"] = PartDataRecordToSpeckle(pipe.PartData); +#endif + specklePipe["isPressurePipe"] = true; try { specklePipe["partType"] = pipe.PartType.ToString(); } catch (Exception e) when (!e.IsFatal()) { } try { specklePipe["slope"] = pipe.Slope; } catch (Exception e) when (!e.IsFatal()) { } try { specklePipe["network"] = pipe.NetworkName; } catch (Exception e) when (!e.IsFatal()) { } @@ -1116,6 +1124,7 @@ public Pipe PipeToSpeckle(PressurePipe pipe) try { specklePipe["endOffset"] = pipe.EndOffset; } catch (Exception e) when (!e.IsFatal()) { } try { specklePipe["startStation"] = pipe.StartStation; } catch (Exception e) when (!e.IsFatal()) { } try { specklePipe["endStation"] = pipe.EndStation; } catch (Exception e) when (!e.IsFatal()) { } + try { specklePipe["pipeId"] = pipe.Id.ToString(); } catch (Exception ex) when (!ex.IsFatal()) { } return specklePipe; } From c4cd55edac6ffa3c3e313a0201e64decd9a7af38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B3zsef=20L=2E=20Kiss?= <50739844+jozseflkiss@users.noreply.github.com> Date: Wed, 12 Jun 2024 12:20:12 +0200 Subject: [PATCH 09/52] bug(Archicad): Properties not showing in viewer (#3472) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * export properties of GDL objects * build fix * build fix --------- Co-authored-by: József L. Kiss <> --- .../Commands/Command_GetElementBaseData.cs | 17 ++++++----------- .../Converters/DirectShapeConverter.cs | 15 ++++++++++++--- .../Converters/Converters/Utils.cs | 3 ++- .../BuiltElements/Archicad/DirectShape.cs | 3 +++ 4 files changed, 23 insertions(+), 15 deletions(-) diff --git a/ConnectorArchicad/ConnectorArchicad/Communication/Commands/Command_GetElementBaseData.cs b/ConnectorArchicad/ConnectorArchicad/Communication/Commands/Command_GetElementBaseData.cs index 56fe20729e..82d9499427 100644 --- a/ConnectorArchicad/ConnectorArchicad/Communication/Commands/Command_GetElementBaseData.cs +++ b/ConnectorArchicad/ConnectorArchicad/Communication/Commands/Command_GetElementBaseData.cs @@ -1,13 +1,12 @@ using System.Collections.Generic; using System.Threading.Tasks; -using Speckle.Core.Kits; -using Speckle.Newtonsoft.Json; -using Objects.BuiltElements.Archicad; using ConnectorArchicad.Communication.Commands; +using Objects.BuiltElements.Archicad; +using Speckle.Newtonsoft.Json; namespace Archicad.Communication.Commands; -sealed internal class GetElementBaseData : GetDataBase, ICommand> +sealed internal class GetElementBaseData : GetDataBase, ICommand { [JsonObject(MemberSerialization.OptIn)] private sealed class Result @@ -19,17 +18,13 @@ private sealed class Result public GetElementBaseData(IEnumerable applicationIds, bool sendProperties, bool sendListingParameters) : base(applicationIds, sendProperties, sendListingParameters) { } - public async Task> Execute() + public async Task Execute() { - Result result = await HttpCommandExecutor.Execute( + dynamic result = await HttpCommandExecutor.Execute( "GetElementBaseData", new Parameters(ApplicationIds, SendProperties, SendListingParameters) ); - foreach (var directShape in result.Datas) - { - directShape.units = Units.Meters; - } - return result.Datas; + return (Speckle.Newtonsoft.Json.Linq.JArray)result["elements"]; } } diff --git a/ConnectorArchicad/ConnectorArchicad/Converters/Converters/DirectShapeConverter.cs b/ConnectorArchicad/ConnectorArchicad/Converters/Converters/DirectShapeConverter.cs index c47a607798..fdb4711d10 100644 --- a/ConnectorArchicad/ConnectorArchicad/Converters/Converters/DirectShapeConverter.cs +++ b/ConnectorArchicad/ConnectorArchicad/Converters/Converters/DirectShapeConverter.cs @@ -7,6 +7,7 @@ using Archicad.Model; using Archicad.Operations; using Objects.Geometry; +using Speckle.Core.Kits; using Speckle.Core.Models; using Speckle.Core.Models.GraphTraversal; @@ -73,6 +74,7 @@ CancellationToken token IEnumerable result; result = await AsyncCommandProcessor.Execute(new Communication.Commands.CreateDirectShape(directShapes), token); + return result is null ? new List() : result.ToList(); } @@ -83,7 +85,8 @@ ConversionOptions conversionOptions ) { var elementModels = elements as ElementModelData[] ?? elements.ToArray(); - IEnumerable data = await AsyncCommandProcessor.Execute( + + Speckle.Newtonsoft.Json.Linq.JArray jArray = await AsyncCommandProcessor.Execute( new Communication.Commands.GetElementBaseData( elementModels.Select(e => e.applicationId), conversionOptions.SendProperties, @@ -93,7 +96,7 @@ ConversionOptions conversionOptions ); var directShapes = new List(); - if (data is null) + if (jArray is null) { return directShapes; } @@ -103,12 +106,18 @@ ConversionOptions conversionOptions context?.cumulativeTimer?.Begin(ConnectorArchicad.Properties.OperationNameTemplates.ConvertToSpeckle, Type.Name) ) { - foreach (Objects.BuiltElements.Archicad.DirectShape directShape in data) + foreach (Speckle.Newtonsoft.Json.Linq.JToken jToken in jArray) { + // convert between DTOs + Objects.BuiltElements.Archicad.DirectShape directShape = + Archicad.Converters.Utils.ConvertToSpeckleDTOs(jToken); + { + directShape.units = Units.Meters; directShape.displayValue = Operations.ModelConverter.MeshesToSpeckle( elementModels.First(e => e.applicationId == directShape.applicationId).model ); + directShapes.Add(directShape); } } diff --git a/ConnectorArchicad/ConnectorArchicad/Converters/Converters/Utils.cs b/ConnectorArchicad/ConnectorArchicad/Converters/Converters/Utils.cs index 792d1992e7..c2b992c49f 100644 --- a/ConnectorArchicad/ConnectorArchicad/Converters/Converters/Utils.cs +++ b/ConnectorArchicad/ConnectorArchicad/Converters/Converters/Utils.cs @@ -180,7 +180,8 @@ public static T ConvertToSpeckleDTOs(dynamic jObject) if (level != null) { - PropertyInfo propLevel = speckleObject.GetType().GetProperty("archicadLevel"); + PropertyInfo propLevel = + speckleObject.GetType().GetProperty("archicadLevel") ?? speckleObject.GetType().GetProperty("level"); propLevel.SetValue(speckleObject, level); } diff --git a/Objects/Objects/BuiltElements/Archicad/DirectShape.cs b/Objects/Objects/BuiltElements/Archicad/DirectShape.cs index 8b2f11caf0..8ae08ece91 100644 --- a/Objects/Objects/BuiltElements/Archicad/DirectShape.cs +++ b/Objects/Objects/BuiltElements/Archicad/DirectShape.cs @@ -16,7 +16,10 @@ public DirectShape(string applicationId, List displayValue) // Element base public string elementType { get; set; } + public List classifications { get; set; } + public Base? elementProperties { get; set; } + public Base? componentProperties { get; set; } public ArchicadLevel level { get; set; } From f37ad906940129f5e88d3d9775811556d1d9061d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B3zsef=20L=2E=20Kiss?= <50739844+jozseflkiss@users.noreply.github.com> Date: Wed, 12 Jun 2024 13:24:54 +0200 Subject: [PATCH 10/52] feat(Archicad): Add support for openings (#3470) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit add support for openings Co-authored-by: József L. Kiss <> --- .../AddOn/Sources/AddOn/AddOnMain.cpp | 4 + .../Sources/AddOn/Commands/CreateOpening.cpp | 373 ++++++++++++++++++ .../Sources/AddOn/Commands/CreateOpening.hpp | 33 ++ .../AddOn/Commands/CreateOpeningBase.cpp | 9 +- .../Sources/AddOn/Commands/GetDoorData.hpp | 4 +- .../Sources/AddOn/Commands/GetOpeningData.cpp | 156 ++++++++ .../Sources/AddOn/Commands/GetOpeningData.hpp | 24 ++ .../AddOn/Sources/AddOn/FieldNames.hpp | 68 ++++ .../AddOn/Sources/AddOn/ResourceIds.hpp | 2 + .../AddOn/Sources/AddOn/TypeNameTables.cpp | 60 +++ .../AddOn/Sources/AddOn/TypeNameTables.hpp | 8 + .../Commands/Command_CreateOpening.cs | 42 ++ .../Commands/Command_GetOpening.cs | 21 + .../Converters/ConverterArchicad.cs | 1 + .../Converters/Converters/OpeningConverter.cs | 135 +++++++ .../Converters/Converters/Utils.cs | 8 + .../Converters/ElementConverterManager.cs | 22 +- .../Converters/ElementTypeProvider.cs | 4 +- .../Converters/ModelConverter.cs | 208 ++++++++++ .../BuiltElements/Archicad/ArchicadOpening.cs | 83 ++++ 20 files changed, 1259 insertions(+), 6 deletions(-) create mode 100644 ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateOpening.cpp create mode 100644 ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateOpening.hpp create mode 100644 ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetOpeningData.cpp create mode 100644 ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetOpeningData.hpp create mode 100644 ConnectorArchicad/ConnectorArchicad/Communication/Commands/Command_CreateOpening.cs create mode 100644 ConnectorArchicad/ConnectorArchicad/Communication/Commands/Command_GetOpening.cs create mode 100644 ConnectorArchicad/ConnectorArchicad/Converters/Converters/OpeningConverter.cs create mode 100644 Objects/Objects/BuiltElements/Archicad/ArchicadOpening.cs diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/AddOnMain.cpp b/ConnectorArchicad/AddOn/Sources/AddOn/AddOnMain.cpp index 532f056af6..67e1d73399 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/AddOnMain.cpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/AddOnMain.cpp @@ -18,6 +18,7 @@ #include "Commands/GetElementBaseData.hpp" #include "Commands/GetGridElementData.hpp" #include "Commands/GetObjectData.hpp" +#include "Commands/GetOpeningData.hpp" #include "Commands/GetSlabData.hpp" #include "Commands/GetRoofData.hpp" #include "Commands/GetShellData.hpp" @@ -31,6 +32,7 @@ #include "Commands/CreateColumn.hpp" #include "Commands/CreateGridElement.hpp" #include "Commands/CreateObject.hpp" +#include "Commands/CreateOpening.hpp" #include "Commands/CreateRoof.hpp" #include "Commands/CreateSkylight.hpp" #include "Commands/CreateSlab.hpp" @@ -200,6 +202,7 @@ static GSErrCode RegisterAddOnCommands () CHECKERROR (ACAPI_AddOnAddOnCommunication_InstallAddOnCommandHandler (NewOwned ())); CHECKERROR (ACAPI_AddOnAddOnCommunication_InstallAddOnCommandHandler (NewOwned ())); CHECKERROR (ACAPI_AddOnAddOnCommunication_InstallAddOnCommandHandler (NewOwned ())); + CHECKERROR (ACAPI_AddOnAddOnCommunication_InstallAddOnCommandHandler (NewOwned ())); CHECKERROR (ACAPI_AddOnAddOnCommunication_InstallAddOnCommandHandler (NewOwned ())); CHECKERROR (ACAPI_AddOnAddOnCommunication_InstallAddOnCommandHandler (NewOwned ())); CHECKERROR (ACAPI_AddOnAddOnCommunication_InstallAddOnCommandHandler (NewOwned ())); @@ -213,6 +216,7 @@ static GSErrCode RegisterAddOnCommands () CHECKERROR (ACAPI_AddOnAddOnCommunication_InstallAddOnCommandHandler (NewOwned ())); CHECKERROR (ACAPI_AddOnAddOnCommunication_InstallAddOnCommandHandler (NewOwned ())); CHECKERROR (ACAPI_AddOnAddOnCommunication_InstallAddOnCommandHandler (NewOwned ())); + CHECKERROR (ACAPI_AddOnAddOnCommunication_InstallAddOnCommandHandler (NewOwned ())); CHECKERROR (ACAPI_AddOnAddOnCommunication_InstallAddOnCommandHandler (NewOwned ())); CHECKERROR (ACAPI_AddOnAddOnCommunication_InstallAddOnCommandHandler (NewOwned ())); CHECKERROR (ACAPI_AddOnAddOnCommunication_InstallAddOnCommandHandler (NewOwned ())); diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateOpening.cpp b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateOpening.cpp new file mode 100644 index 0000000000..7db770d5e1 --- /dev/null +++ b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateOpening.cpp @@ -0,0 +1,373 @@ +#include "CreateOpening.hpp" +#include "ResourceIds.hpp" +#include "ObjectState.hpp" +#include "Utility.hpp" +#include "Objects/Point.hpp" +#include "Objects/Vector.hpp" +#include "RealNumber.h" +#include "DGModule.hpp" +#include "LibpartImportManager.hpp" +#include "APIHelper.hpp" +#include "FieldNames.hpp" +#include "OnExit.hpp" +#include "ExchangeManager.hpp" +#include "TypeNameTables.hpp" +#include "Database.hpp" +#include "BM.hpp" + + +using namespace FieldNames; + + +namespace AddOnCommands +{ + + +GS::String CreateOpening::GetFieldName () const +{ + return FieldNames::Openings; +} + + +GS::UniString CreateOpening::GetUndoableCommandName () const +{ + return "CreateSpeckleOpening"; +} + + +GS::ErrCode CreateOpening::GetElementFromObjectState (const GS::ObjectState& os, + API_Element& element, + API_Element& elementMask, + API_ElementMemo& memo, + GS::UInt64& /*memoMask*/, + API_SubElement** /*marker*/, + AttributeManager& /*attributeManager*/, + LibpartImportManager& /*libpartImportManager*/, + GS::Array& log) const +{ + GS::ErrCode err = NoError; + + Utility::SetElementType (element.header, API_OpeningID); + + err = Utility::GetBaseElementData (element, &memo, nullptr, log); + if (err != NoError) + return err; + + err = GetElementBaseFromObjectState (os, element, elementMask); + if (err != NoError) + return err; + + if (!CheckEnvironment (os, element)) + return Error; + + if (os.Contains (Opening::FloorPlanDisplayMode)) { + GS::UniString floorPlanDisplayModeName; + os.Get (Opening::FloorPlanDisplayMode, floorPlanDisplayModeName); + + GS::Optional type = openingFloorPlanDisplayModeNames.FindValue (floorPlanDisplayModeName); + if (type.HasValue ()) { + element.opening.floorPlanParameters.floorPlanDisplayMode = type.Get (); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.floorPlanDisplayMode); + } + } + + if (os.Contains (Opening::ConnectionMode)) { + GS::UniString connectionModeName; + os.Get (Opening::ConnectionMode, connectionModeName); + + GS::Optional type = openingFloorPlanConnectionModeNames.FindValue (connectionModeName); + if (type.HasValue ()) { + element.opening.floorPlanParameters.connectionMode = type.Get (); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.connectionMode); + } + } + + if (os.Contains (Opening::CutSurfacesUseLineOfCutElements)) { + os.Get (Opening::CutSurfacesUseLineOfCutElements, element.opening.floorPlanParameters.cutSurfacesParameters.useLineOfCutElements); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.cutSurfacesParameters.useLineOfCutElements); + } + + if (os.Contains (Opening::CutSurfacesLinePenIndex)) { + os.Get (Opening::CutSurfacesLinePenIndex, element.opening.floorPlanParameters.cutSurfacesParameters.linePenIndex); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.cutSurfacesParameters.linePenIndex); + } + + GS::UniString attributeName; + if (os.Contains (Opening::CutSurfacesLineIndex)) { + + os.Get (Opening::CutSurfacesLineIndex, attributeName); + + if (!attributeName.IsEmpty ()) { + API_Attribute attrib; + BNZeroMemory (&attrib, sizeof (API_Attribute)); + attrib.header.typeID = API_LinetypeID; + CHCopyC (attributeName.ToCStr (), attrib.header.name); + + if (NoError == ACAPI_Attribute_Get (&attrib)) { + element.opening.floorPlanParameters.cutSurfacesParameters.lineIndex = attrib.header.index; + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.cutSurfacesParameters.lineIndex); + } + } + } + + if (os.Contains (Opening::OutlinesStyle)) { + GS::UniString outlinesStyleName; + os.Get (Opening::OutlinesStyle, outlinesStyleName); + + GS::Optional type = openingOutlinesStyleNames.FindValue (outlinesStyleName); + if (type.HasValue ()) { + element.opening.floorPlanParameters.outlinesParameters.outlinesStyle = type.Get (); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.outlinesParameters.outlinesStyle); + } + } + + if (os.Contains (Opening::OutlinesUseLineOfCutElements)) { + os.Get (Opening::OutlinesUseLineOfCutElements, element.opening.floorPlanParameters.outlinesParameters.useLineOfCutElements); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.outlinesParameters.useLineOfCutElements); + } + + if (os.Contains (Opening::OutlinesUncutLineIndex)) { + + os.Get (Opening::OutlinesUncutLineIndex, attributeName); + + if (!attributeName.IsEmpty ()) { + API_Attribute attrib; + BNZeroMemory (&attrib, sizeof (API_Attribute)); + attrib.header.typeID = API_LinetypeID; + CHCopyC (attributeName.ToCStr (), attrib.header.name); + + if (NoError == ACAPI_Attribute_Get (&attrib)) { + element.opening.floorPlanParameters.outlinesParameters.uncutLineIndex = attrib.header.index; + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.outlinesParameters.uncutLineIndex); + } + } + } + + if (os.Contains (Opening::OutlinesOverheadLineIndex)) { + + os.Get (Opening::OutlinesOverheadLineIndex, attributeName); + + if (!attributeName.IsEmpty ()) { + API_Attribute attrib; + BNZeroMemory (&attrib, sizeof (API_Attribute)); + attrib.header.typeID = API_LinetypeID; + CHCopyC (attributeName.ToCStr (), attrib.header.name); + + if (NoError == ACAPI_Attribute_Get (&attrib)) { + element.opening.floorPlanParameters.outlinesParameters.overheadLineIndex = attrib.header.index; + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.outlinesParameters.overheadLineIndex); + } + } + } + + if (os.Contains (Opening::OutlinesOverheadLinePenIndex)) { + os.Get (Opening::OutlinesOverheadLinePenIndex, element.opening.floorPlanParameters.outlinesParameters.overheadLinePenIndex); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.outlinesParameters.overheadLinePenIndex); + } + + if (os.Contains (Opening::UseCoverFills)) { + os.Get (Opening::UseCoverFills, element.opening.floorPlanParameters.coverFillsParameters.useCoverFills); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.coverFillsParameters.useCoverFills); + } + + if (os.Contains (Opening::UseFillsOfCutElements)) { + os.Get (Opening::UseFillsOfCutElements, element.opening.floorPlanParameters.coverFillsParameters.useFillsOfCutElements); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.coverFillsParameters.useFillsOfCutElements); + } + + if (os.Contains (Opening::CoverFillIndex)) { + + os.Get (Opening::CoverFillIndex, attributeName); + + if (!attributeName.IsEmpty ()) { + API_Attribute attrib; + BNZeroMemory (&attrib, sizeof (API_Attribute)); + attrib.header.typeID = API_FilltypeID; + CHCopyC (attributeName.ToCStr (), attrib.header.name); + + if (NoError == ACAPI_Attribute_Get (&attrib)) { + element.opening.floorPlanParameters.coverFillsParameters.coverFillIndex = attrib.header.index; + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.coverFillsParameters.coverFillIndex); + } + } + } + + if (os.Contains (Opening::CoverFillPenIndex)) { + os.Get (Opening::CoverFillPenIndex, element.opening.floorPlanParameters.coverFillsParameters.coverFillPenIndex); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.coverFillsParameters.coverFillPenIndex); + } + + if (os.Contains (Opening::CoverFillBackgroundPenIndex)) { + os.Get (Opening::CoverFillBackgroundPenIndex, element.opening.floorPlanParameters.coverFillsParameters.coverFillBackgroundPenIndex); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.coverFillsParameters.coverFillBackgroundPenIndex); + } + + if (os.Contains (Opening::CoverFillOrientation)) { + os.Get (Opening::CoverFillOrientation, element.opening.floorPlanParameters.coverFillsParameters.coverFillOrientation); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.coverFillsParameters.coverFillOrientation); + } + + if (os.Contains (Opening::CoverFillTransformationOrigoX)) { + os.Get (Opening::CoverFillTransformationOrigoX, element.opening.floorPlanParameters.coverFillTransformation.origo.x); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.coverFillTransformation.origo.x); + } + + if (os.Contains (Opening::CoverFillTransformationOrigoY)) { + os.Get (Opening::CoverFillTransformationOrigoY, element.opening.floorPlanParameters.coverFillTransformation.origo.y); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.coverFillTransformation.origo.y); + } + + if (os.Contains (Opening::CoverFillTransformationXAxisX)) { + os.Get (Opening::CoverFillTransformationXAxisX, element.opening.floorPlanParameters.coverFillTransformation.xAxis.x); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.coverFillTransformation.xAxis.x); + } + + if (os.Contains (Opening::CoverFillTransformationXAxisY)) { + os.Get (Opening::CoverFillTransformationXAxisY, element.opening.floorPlanParameters.coverFillTransformation.xAxis.y); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.coverFillTransformation.xAxis.y); + } + + if (os.Contains (Opening::CoverFillTransformationYAxisX)) { + os.Get (Opening::CoverFillTransformationYAxisX, element.opening.floorPlanParameters.coverFillTransformation.yAxis.x); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.coverFillTransformation.yAxis.x); + } + + if (os.Contains (Opening::CoverFillTransformationYAxisY)) { + os.Get (Opening::CoverFillTransformationYAxisY, element.opening.floorPlanParameters.coverFillTransformation.yAxis.y); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.coverFillTransformation.yAxis.y); + } + + if (os.Contains (Opening::ShowReferenceAxis)) { + os.Get (Opening::ShowReferenceAxis, element.opening.floorPlanParameters.referenceAxisParameters.showReferenceAxis); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.referenceAxisParameters.showReferenceAxis); + } + + if (os.Contains (Opening::ReferenceAxisPenIndex)) { + os.Get (Opening::ReferenceAxisPenIndex, element.opening.floorPlanParameters.referenceAxisParameters.referenceAxisPenIndex); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.referenceAxisParameters.referenceAxisPenIndex); + } + + if (os.Contains (Opening::ReferenceAxisLineTypeIndex)) { + + os.Get (Opening::ReferenceAxisLineTypeIndex, attributeName); + + if (!attributeName.IsEmpty ()) { + API_Attribute attrib; + BNZeroMemory (&attrib, sizeof (API_Attribute)); + attrib.header.typeID = API_LinetypeID; + CHCopyC (attributeName.ToCStr (), attrib.header.name); + + if (NoError == ACAPI_Attribute_Get (&attrib)) { + element.opening.floorPlanParameters.referenceAxisParameters.referenceAxisLineTypeIndex = attrib.header.index; + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.referenceAxisParameters.referenceAxisLineTypeIndex); + } + } + } + + if (os.Contains (Opening::ReferenceAxisOverhang)) { + os.Get (Opening::ReferenceAxisOverhang, element.opening.floorPlanParameters.referenceAxisParameters.referenceAxisOverhang); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, floorPlanParameters.referenceAxisParameters.referenceAxisOverhang); + } + + Objects::Point3D basePoint; + if (os.Contains (Opening::ExtrusionGeometryBasePoint)) { + os.Get (Opening::ExtrusionGeometryBasePoint, basePoint); + element.opening.extrusionGeometryData.frame.basePoint = basePoint.ToAPI_Coord3D (); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, extrusionGeometryData.frame.basePoint); + } + + Objects::Vector3D axisX; + if (os.Contains (Opening::ExtrusionGeometryXAxis)) { + os.Get (Opening::ExtrusionGeometryXAxis, axisX); + element.opening.extrusionGeometryData.frame.axisX = axisX.ToAPI_Vector3D (); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, extrusionGeometryData.frame.axisX); + } + + Objects::Vector3D axisY; + if (os.Contains (Opening::ExtrusionGeometryYAxis)) { + os.Get (Opening::ExtrusionGeometryYAxis, axisY); + element.opening.extrusionGeometryData.frame.axisY = axisY.ToAPI_Vector3D (); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, extrusionGeometryData.frame.axisY); + } + + Objects::Vector3D axisZ; + if (os.Contains (Opening::ExtrusionGeometryZAxis)) { + os.Get (Opening::ExtrusionGeometryZAxis, axisZ); + element.opening.extrusionGeometryData.frame.axisZ = axisZ.ToAPI_Vector3D (); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, extrusionGeometryData.frame.axisZ); + } + + if (os.Contains (Opening::BasePolygonType)) { + GS::UniString basePolygonTypeName; + os.Get (Opening::BasePolygonType, basePolygonTypeName); + + GS::Optional type = openingBasePolygonTypeNames.FindValue (basePolygonTypeName); + if (type.HasValue ()) { + element.opening.extrusionGeometryData.parameters.basePolygonType = type.Get (); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, extrusionGeometryData.parameters.basePolygonType); + } + } + + if (os.Contains (Opening::Width)) { + os.Get (Opening::Width, element.opening.extrusionGeometryData.parameters.width); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, extrusionGeometryData.parameters.width); + + element.opening.extrusionGeometryData.parameters.linkedStatus = API_OpeningNotLinked; + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, extrusionGeometryData.parameters.linkedStatus); + } + + if (os.Contains (Opening::Height)) { + os.Get (Opening::Height, element.opening.extrusionGeometryData.parameters.height); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, extrusionGeometryData.parameters.height); + + element.opening.extrusionGeometryData.parameters.linkedStatus = API_OpeningNotLinked; + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, extrusionGeometryData.parameters.linkedStatus); + } + + if (os.Contains (Opening::Constraint)) { + os.Get (Opening::Constraint, element.opening.extrusionGeometryData.parameters.constraint); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, extrusionGeometryData.parameters.constraint); + } + + if (os.Contains (Opening::AnchorIndex)) { + os.Get (Opening::AnchorIndex, element.opening.extrusionGeometryData.parameters.anchor); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, extrusionGeometryData.parameters.anchor); + } + + if (os.Contains (Opening::AnchorAltitude)) { + os.Get (Opening::AnchorAltitude, element.opening.extrusionGeometryData.parameters.anchorAltitude); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, extrusionGeometryData.parameters.anchorAltitude); + } + + if (os.Contains (Opening::LimitType)) { + GS::UniString limitTypeName; + os.Get (Opening::LimitType, limitTypeName); + + GS::Optional type = openingLimitTypeNames.FindValue (limitTypeName); + if (type.HasValue ()) { + element.opening.extrusionGeometryData.parameters.limitType = type.Get (); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, extrusionGeometryData.parameters.limitType); + } + } + + if (os.Contains (Opening::ExtrusionStartOffSet)) { + os.Get (Opening::ExtrusionStartOffSet, element.opening.extrusionGeometryData.parameters.extrusionStartOffset); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, extrusionGeometryData.parameters.extrusionStartOffset); + } + + if (os.Contains (Opening::FiniteBodyLength)) { + os.Get (Opening::FiniteBodyLength, element.opening.extrusionGeometryData.parameters.finiteBodyLength); + ACAPI_ELEMENT_MASK_SET (elementMask, API_OpeningType, extrusionGeometryData.parameters.finiteBodyLength); + } + + return err; +} + + +GS::String CreateOpening::GetName () const +{ + return CreateOpeningCommandName; +} + + +} \ No newline at end of file diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateOpening.hpp b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateOpening.hpp new file mode 100644 index 0000000000..8c9203b298 --- /dev/null +++ b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateOpening.hpp @@ -0,0 +1,33 @@ +#ifndef CREATE_OPENING_HPP +#define CREATE_OPENING_HPP + +#include "CreateOpeningBase.hpp" +#include "FieldNames.hpp" + + +namespace AddOnCommands { + + +class CreateOpening : public CreateOpeningBase { + GS::String GetFieldName () const override; + GS::UniString GetUndoableCommandName () const override; + + GSErrCode GetElementFromObjectState (const GS::ObjectState& os, + API_Element& element, + API_Element& elementMask, + API_ElementMemo& memo, + GS::UInt64& memoMask, + API_SubElement** marker, + AttributeManager& attributeManager, + LibpartImportManager& libpartImportManager, + GS::Array& log) const override; + +public: + virtual GS::String GetName () const override; +}; + + +} + + +#endif // !CREATE_OPENING_HPP \ No newline at end of file diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateOpeningBase.cpp b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateOpeningBase.cpp index d9582c709a..4c91bc8095 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateOpeningBase.cpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateOpeningBase.cpp @@ -29,7 +29,8 @@ bool CreateOpeningBase::CheckEnvironment (const GS::ObjectState& os, API_Element bool isParentWall = Utility::GetElementType (parentArchicadId) == API_WallID; bool isParentRoof = Utility::GetElementType (parentArchicadId) == API_RoofID; bool isParentShell = Utility::GetElementType (parentArchicadId) == API_ShellID; - if (!(parentExists && (isParentWall || isParentRoof || isParentShell))) { + bool isParentSlab = Utility::GetElementType (parentArchicadId) == API_SlabID; + if (!(parentExists && (isParentWall || isParentRoof || isParentShell || isParentSlab))) { return false; } @@ -45,6 +46,8 @@ bool CreateOpeningBase::CheckEnvironment (const GS::ObjectState& os, API_Element Utility::SetElementType (elem.header, API_RoofID); } else if (isParentShell) { Utility::SetElementType (elem.header, API_ShellID); + } else if (isParentSlab) { + Utility::SetElementType (elem.header, API_SlabID); } elem.header.guid = parentArchicadId; @@ -61,6 +64,10 @@ bool CreateOpeningBase::CheckEnvironment (const GS::ObjectState& os, API_Element element.window.owner = parentArchicadId; } else if (elementType == API_SkylightID) { element.skylight.owner = parentArchicadId; + } else if (elementType == API_OpeningID) { + element.opening.owner = parentArchicadId; + } else if (elementType == API_SlabID) { + element.opening.owner = parentArchicadId; } return true; diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetDoorData.hpp b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetDoorData.hpp index 317ad154f1..a5a69c739b 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetDoorData.hpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetDoorData.hpp @@ -1,5 +1,5 @@ -#ifndef GET_OPENING_DATA_HPP -#define GET_OPENING_DATA_HPP +#ifndef GET_DOOR_DATA_HPP +#define GET_DOOR_DATA_HPP #include "GetDataCommand.hpp" diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetOpeningData.cpp b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetOpeningData.cpp new file mode 100644 index 0000000000..54e4f1691f --- /dev/null +++ b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetOpeningData.cpp @@ -0,0 +1,156 @@ +#include "GetOpeningData.hpp" +#include "ResourceIds.hpp" +#include "ObjectState.hpp" +#include "Utility.hpp" +#include "Objects/Point.hpp" +#include "Objects/Vector.hpp" +#include "RealNumber.h" +#include "FieldNames.hpp" +#include "TypeNameTables.hpp" +using namespace FieldNames; + + +namespace AddOnCommands { + + +GS::String GetOpeningData::GetFieldName () const +{ + return Openings; +} + + +API_ElemTypeID GetOpeningData::GetElemTypeID () const +{ + return API_OpeningID; +} + + +GS::ErrCode GetOpeningData::SerializeElementType (const API_Element& element, + const API_ElementMemo& /*memo*/, + GS::ObjectState& os) const +{ + os.Add (ElementBase::ParentElementId, APIGuidToString (element.opening.owner)); + + API_Attribute attrib; + + // Opening Floor Parameters + os.Add (Opening::FloorPlanDisplayMode, openingFloorPlanDisplayModeNames.Get (element.opening.floorPlanParameters.floorPlanDisplayMode)); + os.Add (Opening::ConnectionMode, openingFloorPlanConnectionModeNames.Get (element.opening.floorPlanParameters.connectionMode)); + + // Opening Floor Plan Parameters - Cut Surfaces + os.Add (Opening::CutSurfacesUseLineOfCutElements, element.opening.floorPlanParameters.cutSurfacesParameters.useLineOfCutElements); + os.Add (Opening::CutSurfacesLinePenIndex, element.opening.floorPlanParameters.cutSurfacesParameters.linePenIndex); + BNZeroMemory (&attrib, sizeof (API_Attribute)); + attrib.header.typeID = API_LinetypeID; + attrib.header.index = element.opening.floorPlanParameters.cutSurfacesParameters.lineIndex; + + if (NoError == ACAPI_Attribute_Get (&attrib)) { + os.Add (Opening::CutSurfacesLineIndex, GS::UniString{attrib.header.name}); + } + + // Opening Floor Plan Parameters - Outlines Parameters + os.Add (Opening::OutlinesStyle, openingOutlinesStyleNames.Get (element.opening.floorPlanParameters.outlinesParameters.outlinesStyle)); + os.Add (Opening::OutlinesUseLineOfCutElements, element.opening.floorPlanParameters.outlinesParameters.useLineOfCutElements); + + BNZeroMemory (&attrib, sizeof (API_Attribute)); + attrib.header.typeID = API_LinetypeID; + attrib.header.index = element.opening.floorPlanParameters.outlinesParameters.uncutLineIndex; + + if (NoError == ACAPI_Attribute_Get (&attrib)) { + os.Add (Opening::OutlinesUncutLineIndex, GS::UniString{attrib.header.name}); + } + + BNZeroMemory (&attrib, sizeof (API_Attribute)); + attrib.header.typeID = API_LinetypeID; + attrib.header.index = element.opening.floorPlanParameters.outlinesParameters.overheadLineIndex; + + if (NoError == ACAPI_Attribute_Get (&attrib)) { + os.Add (Opening::OutlinesOverheadLineIndex, GS::UniString{attrib.header.name}); + } + + BNZeroMemory (&attrib, sizeof (API_Attribute)); + attrib.header.typeID = API_LinetypeID; + attrib.header.index = element.opening.floorPlanParameters.outlinesParameters.uncutLineIndex; + + if (NoError == ACAPI_Attribute_Get (&attrib)) { + os.Add (Opening::OutlinesUncutLineIndex, GS::UniString{attrib.header.name}); + } + os.Add (Opening::OutlinesOverheadLinePenIndex, element.opening.floorPlanParameters.outlinesParameters.overheadLinePenIndex); + + // Opening Floor Plan Parameters - Cover Fills + os.Add (Opening::UseCoverFills, element.opening.floorPlanParameters.coverFillsParameters.useCoverFills); + if (element.opening.floorPlanParameters.coverFillsParameters.useCoverFills) { + os.Add (Opening::UseFillsOfCutElements, element.opening.floorPlanParameters.coverFillsParameters.useFillsOfCutElements); + + BNZeroMemory (&attrib, sizeof (API_Attribute)); + attrib.header.typeID = API_FilltypeID; + attrib.header.index = element.opening.floorPlanParameters.coverFillsParameters.coverFillIndex; + + if (NoError == ACAPI_Attribute_Get (&attrib)) { + os.Add (Opening::CoverFillIndex, GS::UniString{attrib.header.name}); + } + + os.Add (Opening::CoverFillPenIndex, element.opening.floorPlanParameters.coverFillsParameters.coverFillPenIndex); + os.Add (Opening::CoverFillBackgroundPenIndex, element.opening.floorPlanParameters.coverFillsParameters.coverFillBackgroundPenIndex); + os.Add (Opening::CoverFillOrientation, element.opening.floorPlanParameters.coverFillsParameters.coverFillOrientation); + } + + // Opening Floor Plan Parameters - Cover Fill Transformation + os.Add (Opening::CoverFillTransformationOrigoX, element.opening.floorPlanParameters.coverFillTransformation.origo.x); + os.Add (Opening::CoverFillTransformationOrigoY, element.opening.floorPlanParameters.coverFillTransformation.origo.y); + os.Add (Opening::CoverFillTransformationXAxisX, element.opening.floorPlanParameters.coverFillTransformation.xAxis.x); + os.Add (Opening::CoverFillTransformationXAxisY, element.opening.floorPlanParameters.coverFillTransformation.xAxis.y); + os.Add (Opening::CoverFillTransformationYAxisX, element.opening.floorPlanParameters.coverFillTransformation.yAxis.x); + os.Add (Opening::CoverFillTransformationYAxisY, element.opening.floorPlanParameters.coverFillTransformation.yAxis.y); + + // Opening Floor Plan Parameters - Reference Axis + os.Add (Opening::ShowReferenceAxis, element.opening.floorPlanParameters.referenceAxisParameters.showReferenceAxis); + if (element.opening.floorPlanParameters.referenceAxisParameters.showReferenceAxis) { + os.Add (Opening::ReferenceAxisPenIndex, element.opening.floorPlanParameters.referenceAxisParameters.referenceAxisPenIndex); + + BNZeroMemory (&attrib, sizeof (API_Attribute)); + attrib.header.typeID = API_LinetypeID; + attrib.header.index = element.opening.floorPlanParameters.referenceAxisParameters.referenceAxisLineTypeIndex; + + if (NoError == ACAPI_Attribute_Get (&attrib)) { + os.Add (Opening::ReferenceAxisLineTypeIndex, GS::UniString{attrib.header.name}); + } + + os.Add (Opening::ReferenceAxisOverhang, element.opening.floorPlanParameters.referenceAxisParameters.referenceAxisOverhang); + } + + // Extrusion Geometry + os.Add (Opening::ExtrusionGeometryBasePoint, Objects::Point3D (element.opening.extrusionGeometryData.frame.basePoint)); + os.Add (Opening::ExtrusionGeometryXAxis, Objects::Vector3D(element.opening.extrusionGeometryData.frame.axisX)); + os.Add (Opening::ExtrusionGeometryYAxis, Objects::Vector3D (element.opening.extrusionGeometryData.frame.axisY)); + os.Add (Opening::ExtrusionGeometryZAxis, Objects::Vector3D (element.opening.extrusionGeometryData.frame.axisZ)); + + // Extrusion Geometry - Opening Extrusion Parameters + os.Add (Opening::BasePolygonType, openingBasePolygonTypeNames.Get (element.opening.extrusionGeometryData.parameters.basePolygonType)); + os.Add (Opening::Width, element.opening.extrusionGeometryData.parameters.width); + os.Add (Opening::Height, element.opening.extrusionGeometryData.parameters.height); + os.Add (Opening::Constraint, element.opening.extrusionGeometryData.parameters.constraint); + os.Add (Opening::Anchor, openingAnchorNames.Get (element.opening.extrusionGeometryData.parameters.anchor)); + os.Add (Opening::AnchorIndex, (element.opening.extrusionGeometryData.parameters.anchor)); + os.Add (Opening::AnchorAltitude, element.opening.extrusionGeometryData.parameters.anchorAltitude); + os.Add (Opening::LimitType, openingLimitTypeNames.Get (element.opening.extrusionGeometryData.parameters.limitType)); + os.Add (Opening::ExtrusionStartOffSet, element.opening.extrusionGeometryData.parameters.extrusionStartOffset); + os.Add (Opening::FiniteBodyLength, element.opening.extrusionGeometryData.parameters.finiteBodyLength); + os.Add (Opening::LinkedStatus, openingLinkedStatusNames.Get (element.opening.extrusionGeometryData.parameters.linkedStatus)); + + // Extrusion Geometry - Custom Base Polygon + if (element.opening.extrusionGeometryData.parameters.basePolygonType == API_OpeningBasePolygonCustom) { + // Reserved for future use + } + + return NoError; +} + + +GS::String GetOpeningData::GetName () const +{ + return GetOpeningDataCommandName; +} + + +} // namespace AddOnCommands diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetOpeningData.hpp b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetOpeningData.hpp new file mode 100644 index 0000000000..0a30edd766 --- /dev/null +++ b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetOpeningData.hpp @@ -0,0 +1,24 @@ +#ifndef GET_OPENING_DATA_HPP +#define GET_OPENING_DATA_HPP + +#include "GetDataCommand.hpp" + +namespace AddOnCommands { + + +class GetOpeningData : public GetDataCommand { + GS::String GetFieldName() const override; + API_ElemTypeID GetElemTypeID() const override; + GS::ErrCode SerializeElementType(const API_Element& elem, + const API_ElementMemo& memo, + GS::ObjectState& os) const override; + +public: + virtual GS::String GetName() const override; +}; + + +} + + +#endif // GET_OPENING_DATA_HPP \ No newline at end of file diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/FieldNames.hpp b/ConnectorArchicad/AddOn/Sources/AddOn/FieldNames.hpp index 64c80772da..3b05a70287 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/FieldNames.hpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/FieldNames.hpp @@ -83,6 +83,7 @@ static const char* Slabs = "slabs"; static const char* Walls = "walls"; static const char* Windows = "windows"; static const char* Zones = "zones"; +static const char* Openings = "openings"; static const char* Models = "models"; static const char* SubelementModels = "subelementModels"; @@ -474,6 +475,73 @@ static const char* pos = "pos"; static const char* transform = "transform"; } + +namespace Opening +{ +// Floor Plan Parameters +static const char* FloorPlanDisplayMode = "floorPlanDisplayMode"; +static const char* ConnectionMode = "connectionMode"; + +// Cut Surfaces Parameters +static const char* CutSurfacesUseLineOfCutElements = "cutsurfacesUseLineOfCutElements"; +static const char* CutSurfacesLinePenIndex = "cutsurfacesLinePenIndex"; +static const char* CutSurfacesLineIndex = "cutsurfacesLineIndex"; + +// Outlines Parameters +static const char* OutlinesStyle = "outlinesStyle"; +static const char* OutlinesUseLineOfCutElements = "outlinesUseLineOfCutElements"; // => Cut Surfaces Parameters-ben is megtallhat +static const char* OutlinesUncutLineIndex = "outlinesUncutLineIndex"; +static const char* OutlinesOverheadLineIndex = "outlinesOverheadLineIndex"; +static const char* OutlinesUncutLinePenIndex = "outlinesUncutLinePenIndex"; +static const char* OutlinesOverheadLinePenIndex = "outlinesOverheadLinePenIndex"; + +// Opening Cover Fills Parameters +static const char* UseCoverFills = "useCoverFills"; +static const char* UseFillsOfCutElements = "useFillsOfCutElements"; +static const char* CoverFillIndex = "coverFillIndex"; +static const char* CoverFillPenIndex = "coverFillPenIndex"; +static const char* CoverFillBackgroundPenIndex = "coverFillBackgroundPenIndex"; +static const char* CoverFillOrientation = "coverFillOrientation"; + +// Cover Fill Transformation Parameters +static const char* CoverFillTransformationOrigoX = "coverFillTransformationOrigoX"; +static const char* CoverFillTransformationOrigoY = "coverFillTransformationOrigoY"; +static const char* CoverFillTransformationOrigoZ = "coverFillTransformationOrigoZ"; +static const char* CoverFillTransformationXAxisX = "coverFillTransformationXAxisX"; +static const char* CoverFillTransformationXAxisY = "coverFillTransformationXAxisY"; +static const char* CoverFillTransformationXAxisZ = "coverFillTransformationXAxisZ"; +static const char* CoverFillTransformationYAxisX = "coverFillTransformationYAxisX"; +static const char* CoverFillTransformationYAxisY = "coverFillTransformationYAxisY"; +static const char* CoverFillTransformationYAxisZ = "coverFillTransformationYAxisZ"; + +// Reference Axis Parameters +static const char* ShowReferenceAxis = "showReferenceAxis"; +static const char* ReferenceAxisPenIndex = "referenceAxisPenIndex"; +static const char* ReferenceAxisLineTypeIndex = "referenceAxisLineTypeIndex"; +static const char* ReferenceAxisOverhang = "referenceAxisOverhang"; + +// Extrusion Geometry Parameters +static const char* ExtrusionGeometryBasePoint = "extrusionGeometryBasePoint"; +static const char* ExtrusionGeometryXAxis = "extrusionGeometryXAxis"; +static const char* ExtrusionGeometryYAxis = "extrusionGeometryYAxis"; +static const char* ExtrusionGeometryZAxis = "extrusionGeometryZAxis"; +static const char* BasePolygonType = "basePolygonType"; +static const char* Width = "width"; +static const char* Height = "height"; +static const char* Constraint = "constraint"; +static const char* Anchor = "anchor"; +static const char* AnchorIndex = "anchorIndex"; +static const char* AnchorAltitude = "anchorAltitude"; +static const char* LimitType = "limitType"; +static const char* ExtrusionStartOffSet = "extrusionStartOffSet"; +static const char* FiniteBodyLength = "finiteBodyLength"; +static const char* LinkedStatus = "linkedStatus"; +static const char* NCoords = "nCoords"; +static const char* NSubPolys = "nSubPolys"; +static const char* NArcs = "nArcs"; +} + + namespace GridElement { // Main diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/ResourceIds.hpp b/ConnectorArchicad/AddOn/Sources/AddOn/ResourceIds.hpp index 147706b24e..3b71424e11 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/ResourceIds.hpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/ResourceIds.hpp @@ -24,6 +24,7 @@ #define GetElementBaseDataCommandName "GetElementBaseData"; #define GetGridElementCommandName "GetGridElementData"; #define GetObjectDataCommandName "GetObjectData"; +#define GetOpeningDataCommandName "GetOpeningData"; #define GetSlabDataCommandName "GetSlabData"; #define GetRoomDataCommandName "GetRoomData"; #define GetRoofDataCommandName "GetRoofData"; @@ -38,6 +39,7 @@ #define CreateColumnCommandName "CreateColumn"; #define CreateGridElementCommandName "CreateGridElement"; #define CreateObjectCommandName "CreateObject"; +#define CreateOpeningCommandName "CreateOpening"; #define CreateSlabCommandName "CreateSlab"; #define CreateSkylightCommandName "CreateSkylight"; #define CreateRoofCommandName "CreateRoof"; diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/TypeNameTables.cpp b/ConnectorArchicad/AddOn/Sources/AddOn/TypeNameTables.cpp index 52c35275b6..610edbc1cc 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/TypeNameTables.cpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/TypeNameTables.cpp @@ -301,3 +301,63 @@ const GS::HashTable coreSymbolTypeNames { 3, "X"}, { 4, "CrossHair"} }; + + +const GS::HashTable openingFloorPlanDisplayModeNames +{ + { API_OpeningSymbolic, "Symbolic"}, + { API_OpeningSymbolicCut, "Symbolic Cut"}, + { API_OpeningSymbolicOverhead, "Symbolic Overhead"} +}; + + +const GS::HashTable openingFloorPlanConnectionModeNames +{ + { API_OpeningDisconnected, "Disconnected"}, + { API_OpeningConnected, "Connected"} +}; + + +const GS::HashTable openingOutlinesStyleNames +{ + { API_OpeningHideBorder, "Hide Border"}, + { API_OpeningShowUncutBorder, "Show Uncut Border"}, + { API_OpeningShowOverheadBorder, "Show Overhead Border"} +}; + + +const GS::HashTable openingBasePolygonTypeNames +{ + { API_OpeningBasePolygonRectangular, "Rectangular"}, + { API_OpeningBasePolygonCircular, "Circular"}, + { API_OpeningBasePolygonCustom, "Custom"} +}; + + +const GS::HashTable openingAnchorNames +{ + { APIAnc_LT, "Left Top"}, + { APIAnc_MT, "Middle Top"}, + { APIAnc_RT, "Right Top"}, + { APIAnc_LM, "Left Middle"}, + { APIAnc_MM, "Middle Middle"}, + { APIAnc_RM, "Right Middle"}, + { APIAnc_LB, "Left Bottom"}, + { APIAnc_MB, "Middle Bottom"}, + { APIAnc_RB, "Right Bottom"} +}; + + +const GS::HashTable openingLimitTypeNames +{ + { API_OpeningLimitInfinite, "Infinite"}, + { API_OpeningLimitFinite, "Finite"}, + { API_OpeningLimitHalfInfinite, "Half Infinite"} +}; + + +const GS::HashTable openingLinkedStatusNames +{ + { API_OpeningLinked, "Linked"}, + { API_OpeningNotLinked, "Not Linked"} +}; \ No newline at end of file diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/TypeNameTables.hpp b/ConnectorArchicad/AddOn/Sources/AddOn/TypeNameTables.hpp index b478c0ca60..12d8488997 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/TypeNameTables.hpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/TypeNameTables.hpp @@ -45,4 +45,12 @@ extern const GS::HashTable venTypeNames; extern const GS::HashTable coreSymbolTypeNames; +extern const GS::HashTable openingFloorPlanDisplayModeNames; +extern const GS::HashTable openingFloorPlanConnectionModeNames; +extern const GS::HashTable openingOutlinesStyleNames; +extern const GS::HashTable openingBasePolygonTypeNames; +extern const GS::HashTable openingAnchorNames; + +extern const GS::HashTable openingLimitTypeNames; +extern const GS::HashTable openingLinkedStatusNames; #endif diff --git a/ConnectorArchicad/ConnectorArchicad/Communication/Commands/Command_CreateOpening.cs b/ConnectorArchicad/ConnectorArchicad/Communication/Commands/Command_CreateOpening.cs new file mode 100644 index 0000000000..ab65f3dde1 --- /dev/null +++ b/ConnectorArchicad/ConnectorArchicad/Communication/Commands/Command_CreateOpening.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Speckle.Core.Models; +using Speckle.Newtonsoft.Json; +using Objects.BuiltElements.Archicad; + +namespace Archicad.Communication.Commands; + +sealed internal class CreateOpening : ICommand> +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class Parameters + { + [JsonProperty("openings")] + private IEnumerable Datas { get; } + + public Parameters(IEnumerable datas) + { + Datas = datas; + } + } + + [JsonObject(MemberSerialization.OptIn)] + private sealed class Result + { + [JsonProperty("applicationObjects")] + public IEnumerable ApplicationObjects { get; private set; } + } + + private IEnumerable Datas { get; } + + public CreateOpening(IEnumerable datas) + { + Datas = datas; + } + + public async Task> Execute() + { + var result = await HttpCommandExecutor.Execute("CreateOpening", new Parameters(Datas)); + return result == null ? null : result.ApplicationObjects; + } +} diff --git a/ConnectorArchicad/ConnectorArchicad/Communication/Commands/Command_GetOpening.cs b/ConnectorArchicad/ConnectorArchicad/Communication/Commands/Command_GetOpening.cs new file mode 100644 index 0000000000..5e64507ed7 --- /dev/null +++ b/ConnectorArchicad/ConnectorArchicad/Communication/Commands/Command_GetOpening.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using ConnectorArchicad.Communication.Commands; + +namespace Archicad.Communication.Commands; + +sealed internal class GetOpeningData : GetDataBase, ICommand +{ + public GetOpeningData(IEnumerable applicationIds, bool sendProperties, bool sendListingParameters) + : base(applicationIds, sendProperties, sendListingParameters) { } + + public async Task Execute() + { + dynamic result = await HttpCommandExecutor.Execute( + "GetOpeningData", + new Parameters(ApplicationIds, SendProperties, SendListingParameters) + ); + + return (Speckle.Newtonsoft.Json.Linq.JArray)result["openings"]; + } +} diff --git a/ConnectorArchicad/ConnectorArchicad/Converters/ConverterArchicad.cs b/ConnectorArchicad/ConnectorArchicad/Converters/ConverterArchicad.cs index 29506c6a58..c00821258c 100644 --- a/ConnectorArchicad/ConnectorArchicad/Converters/ConverterArchicad.cs +++ b/ConnectorArchicad/ConnectorArchicad/Converters/ConverterArchicad.cs @@ -68,6 +68,7 @@ public bool CanConvertToNativeImplemented(Base @object) Objects.BuiltElements.Roof _ => true, Objects.BuiltElements.Room _ => true, Objects.BuiltElements.Wall _ => true, + Objects.BuiltElements.Opening _ => true, // Archicad elements Objects.BuiltElements.Archicad.ArchicadDoor _ => true, diff --git a/ConnectorArchicad/ConnectorArchicad/Converters/Converters/OpeningConverter.cs b/ConnectorArchicad/ConnectorArchicad/Converters/Converters/OpeningConverter.cs new file mode 100644 index 0000000000..a7bf40d31f --- /dev/null +++ b/ConnectorArchicad/ConnectorArchicad/Converters/Converters/OpeningConverter.cs @@ -0,0 +1,135 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Archicad.Communication; +using Archicad.Model; +using Speckle.Core.Kits; +using Speckle.Core.Logging; +using Speckle.Core.Models; +using Speckle.Core.Models.GraphTraversal; + +namespace Archicad.Converters; + +public sealed class OpeningConverter : IConverter +{ + public Type Type => typeof(Objects.BuiltElements.Opening); + + public Task> ConvertToArchicad( + IEnumerable elements, + CancellationToken token + ) + { + var openings = new List(); + + var context = Archicad.Helpers.Timer.Context.Peek; + using ( + context?.cumulativeTimer?.Begin(ConnectorArchicad.Properties.OperationNameTemplates.ConvertToNative, Type.Name) + ) + { + foreach (var tc in elements) + { + token.ThrowIfCancellationRequested(); + + switch (tc.current) + { + case Objects.BuiltElements.Archicad.ArchicadOpening archicadOpening: + archicadOpening.parentApplicationId = tc.parent.current.id; + Archicad.Converters.Utils.ConvertToArchicadDTOs( + archicadOpening + ); + openings.Add(archicadOpening); + break; + case Objects.BuiltElements.Opening opening: + try + { + Objects.Geometry.Vector extrusionBasePoint, + extrusionXAxis, + extrusionYAxis, + extrusionZAxis; + double width, + height; + Operations.ModelConverter.GetExtrusionParametersFromOutline( + opening.outline, + out extrusionBasePoint, + out extrusionXAxis, + out extrusionYAxis, + out extrusionZAxis, + out width, + out height + ); + openings.Add( + new Objects.BuiltElements.Archicad.ArchicadOpening + { + id = opening.id, + applicationId = opening.applicationId, + parentApplicationId = tc.parent.current.id, + extrusionGeometryBasePoint = new Objects.Geometry.Point(extrusionBasePoint), + extrusionGeometryXAxis = extrusionXAxis, + extrusionGeometryYAxis = extrusionYAxis, + extrusionGeometryZAxis = extrusionZAxis, + width = width, + height = height, + anchorIndex = 4, + } + ); + } + catch (SpeckleException ex) + { + SpeckleLog.Logger.Error(ex.Message); + } + break; + } + } + + IEnumerable result; + result = AsyncCommandProcessor.Execute(new Communication.Commands.CreateOpening(openings), token).Result; + + return Task.FromResult(result is null ? new List() : result.ToList()); + } + } + + public async Task> ConvertToSpeckle( + IEnumerable elements, + CancellationToken token, + ConversionOptions conversionOptions + ) + { + var elementModels = elements as ElementModelData[] ?? elements.ToArray(); + + Speckle.Newtonsoft.Json.Linq.JArray jArray = await AsyncCommandProcessor.Execute( + new Communication.Commands.GetOpeningData( + elementModels.Select(e => e.applicationId), + conversionOptions.SendProperties, + conversionOptions.SendListingParameters + ), + token + ); + + List openings = new(); + if (jArray is null) + { + return openings; + } + + var context = Archicad.Helpers.Timer.Context.Peek; + using ( + context?.cumulativeTimer?.Begin(ConnectorArchicad.Properties.OperationNameTemplates.ConvertToNative, Type.Name) + ) + { + foreach (Speckle.Newtonsoft.Json.Linq.JToken jToken in jArray) + { + Objects.BuiltElements.Archicad.ArchicadOpening opening = + Archicad.Converters.Utils.ConvertToSpeckleDTOs(jToken); + { + opening.outline = Operations.ModelConverter.CreateOpeningOutline(opening); + opening.units = Units.Meters; + } + openings.Add(opening); + } + } + + return openings; + } +} diff --git a/ConnectorArchicad/ConnectorArchicad/Converters/Converters/Utils.cs b/ConnectorArchicad/ConnectorArchicad/Converters/Converters/Utils.cs index c2b992c49f..eb0e7e1477 100644 --- a/ConnectorArchicad/ConnectorArchicad/Converters/Converters/Utils.cs +++ b/ConnectorArchicad/ConnectorArchicad/Converters/Converters/Utils.cs @@ -68,6 +68,14 @@ public static MeshModel.Vertex PointToNative(Point point, string? units = null) }; } + public static Vector ScaleToNative(Vector vector, string? units = null) + { + units ??= vector.units; + var scale = Units.GetConversionFactor(units, Units.Meters); + + return new Vector(vector.x * scale, vector.y * scale, vector.z * scale); + } + public static Polycurve PolycurveToSpeckle(ElementShape.Polyline archiPolyline) { var poly = new Polycurve diff --git a/ConnectorArchicad/ConnectorArchicad/Converters/ElementConverterManager.cs b/ConnectorArchicad/ConnectorArchicad/Converters/ElementConverterManager.cs index 330860564c..287ba885c4 100644 --- a/ConnectorArchicad/ConnectorArchicad/Converters/ElementConverterManager.cs +++ b/ConnectorArchicad/ConnectorArchicad/Converters/ElementConverterManager.cs @@ -13,6 +13,7 @@ using Column = Objects.BuiltElements.Column; using Door = Objects.BuiltElements.Archicad.ArchicadDoor; using Fenestration = Objects.BuiltElements.Archicad.ArchicadFenestration; +using Opening = Objects.BuiltElements.Opening; using Floor = Objects.BuiltElements.Floor; using Roof = Objects.BuiltElements.Roof; using Wall = Objects.BuiltElements.Wall; @@ -20,6 +21,7 @@ using Skylight = Objects.BuiltElements.Archicad.ArchicadSkylight; using GridLine = Objects.BuiltElements.GridLine; using DesktopUI2.Models; +using Objects.BuiltElements.Archicad; namespace Archicad; @@ -95,13 +97,24 @@ private ElementConverterManager() allObjects.AddRange(objects); // subelements translated into "elements" property of the parent - if (typeof(Fenestration).IsAssignableFrom(elemenType)) + if (typeof(Fenestration).IsAssignableFrom(elemenType) || typeof(Opening).IsAssignableFrom(elemenType)) { Collection elementCollection = null; foreach (Base item in objects) { - Base parent = allObjects.Find(x => x.applicationId == ((Fenestration)item).parentApplicationId); + string parentApplicationId = null; + + if (item is Fenestration fenestration) + { + parentApplicationId = fenestration.parentApplicationId; + } + else if (item is ArchicadOpening opening) + { + parentApplicationId = opening.parentApplicationId; + } + + Base parent = allObjects.Find(x => x.applicationId == parentApplicationId); if (parent == null) { @@ -247,6 +260,11 @@ bool forReceive return Converters[typeof(Roof)]; } + if (elementType.IsSubclassOf(typeof(Opening))) + { + return Converters[typeof(Opening)]; + } + if (elementType.IsAssignableFrom(typeof(Objects.BuiltElements.Room))) { return Converters[typeof(Archicad.Room)]; diff --git a/ConnectorArchicad/ConnectorArchicad/Converters/ElementTypeProvider.cs b/ConnectorArchicad/ConnectorArchicad/Converters/ElementTypeProvider.cs index da11c33ced..a2cb24e846 100644 --- a/ConnectorArchicad/ConnectorArchicad/Converters/ElementTypeProvider.cs +++ b/ConnectorArchicad/ConnectorArchicad/Converters/ElementTypeProvider.cs @@ -10,6 +10,7 @@ using Wall = Objects.BuiltElements.Archicad.ArchicadWall; using Window = Objects.BuiltElements.Archicad.ArchicadWindow; using Skylight = Objects.BuiltElements.Archicad.ArchicadSkylight; +using Opening = Objects.BuiltElements.Archicad.ArchicadOpening; namespace Archicad; @@ -28,7 +29,8 @@ public static class ElementTypeProvider { "Door", typeof(Door) }, { "Window", typeof(Window) }, { "Skylight", typeof(Skylight) }, - { "GridElement", typeof(GridElement) } + { "GridElement", typeof(GridElement) }, + { "Opening", typeof(Opening) } }; public static Type GetTypeByName(string name) diff --git a/ConnectorArchicad/ConnectorArchicad/Converters/ModelConverter.cs b/ConnectorArchicad/ConnectorArchicad/Converters/ModelConverter.cs index f19785f14a..b8db6345f7 100644 --- a/ConnectorArchicad/ConnectorArchicad/Converters/ModelConverter.cs +++ b/ConnectorArchicad/ConnectorArchicad/Converters/ModelConverter.cs @@ -3,6 +3,8 @@ using System.Linq; using Archicad.Converters; using Archicad.Model; +using Objects.BuiltElements.Archicad; +using Objects; using Objects.Geometry; using Objects.Other; using Objects.Utils; @@ -393,4 +395,210 @@ MeshModel meshModel return angleCos > angleCosLimit; } + + public static ICurve CreateOpeningOutline(ArchicadOpening opening) + { + double halfWidth = opening.width / 2.0 ?? 0.0; + double halfHeight = opening.height / 2.0 ?? 0.0; + Vector basePoint = new(0, 0, 0); + Vector extrusionBasePoint = new(opening.extrusionGeometryBasePoint); + + // Speckle datastructure does not handle the translation component, which we will use manually later, so its left empty. + System.DoubleNumerics.Matrix4x4 rotMatrix = + new( + (float)opening.extrusionGeometryXAxis.x, + (float)opening.extrusionGeometryXAxis.y, + (float)opening.extrusionGeometryXAxis.z, + 0, + (float)opening.extrusionGeometryYAxis.x, + (float)opening.extrusionGeometryYAxis.y, + (float)opening.extrusionGeometryYAxis.z, + 0, + (float)opening.extrusionGeometryZAxis.x, + (float)opening.extrusionGeometryZAxis.y, + (float)opening.extrusionGeometryZAxis.z, + 0, + 0, + 0, + 0, + 1 + ); + + Objects.Other.Transform transform = new(System.DoubleNumerics.Matrix4x4.Transpose(rotMatrix)); + + AdjustBasePoint(ref basePoint, halfWidth, halfHeight, opening.anchorIndex ?? 0); + + return opening.basePolygonType == "Rectangular" + ? CreateRectangle(basePoint, transform, extrusionBasePoint, halfWidth, halfHeight) + : CreateEllipse(basePoint, transform, extrusionBasePoint, halfWidth, halfHeight, opening); + } + + private static readonly Action[] anchorActions = new Action[] + { + (Vector basePoint, double halfWidth, double halfHeight) => + { + basePoint.x = halfWidth; + basePoint.y = -halfHeight; + }, // APIAnc_LT + (Vector basePoint, double halfWidth, double halfHeight) => + { + basePoint.y = -halfHeight; + }, // APIAnc_MT + (Vector basePoint, double halfWidth, double halfHeight) => + { + basePoint.x = -halfWidth; + basePoint.y = -halfHeight; + }, // APIAnc_RT + (Vector basePoint, double halfWidth, double halfHeight) => + { + basePoint.x = halfWidth; + }, // APIAnc_LM + (Vector basePoint, double halfWidth, double halfHeight) => { }, // APIAnc_MM + (Vector basePoint, double halfWidth, double halfHeight) => + { + basePoint.x = -halfWidth; + }, // APIAnc_RM + (Vector basePoint, double halfWidth, double halfHeight) => + { + basePoint.x = halfWidth; + basePoint.y = halfHeight; + }, // APIAnc_LB + (Vector basePoint, double halfWidth, double halfHeight) => + { + basePoint.y = halfHeight; + }, // APIAnc_MB + (Vector basePoint, double halfWidth, double halfHeight) => + { + basePoint.x = -halfWidth; + basePoint.y = halfHeight; + } // APIAnc_RB + }; + + private static void AdjustBasePoint(ref Vector basePoint, double halfWidth, double halfHeight, int anchor) + { + if (anchor >= 0 && anchor < anchorActions.Length) + { + anchorActions[anchor](basePoint, halfWidth, halfHeight); + } + } + + private static Polyline CreateRectangle( + Vector basePoint, + Objects.Other.Transform transform, + Vector extrusionBasePoint, + double halfWidth, + double halfHeight + ) + { + var poly = new Objects.Geometry.Polyline + { + value = new List(), + closed = true, + units = Units.Meters + }; + + // Coordinates of the four corners of the rectangle + Vector[] points = + { + new(-halfWidth, -halfHeight, 0), + new(halfWidth, -halfHeight, 0), + new(halfWidth, halfHeight, 0), + new(-halfWidth, halfHeight, 0) + }; + + // Transform the points to the correct position + foreach (var point in points) + { + Vector transformedPoint = point + basePoint; + transformedPoint.TransformTo(transform, out transformedPoint); + transformedPoint += extrusionBasePoint; + poly.value.AddRange(transformedPoint.ToList()); + } + + // Close the polyline + poly.value.AddRange(poly.value.Take(3)); + + return poly; + } + + private static Ellipse CreateEllipse( + Vector basePoint, + Objects.Other.Transform transform, + Vector extrusionBasePoint, + double halfWidth, + double halfHeight, + ArchicadOpening opening + ) + { + Vector centerPoint = new(basePoint.x, basePoint.y, basePoint.z); + centerPoint.TransformTo(transform, out centerPoint); + centerPoint += extrusionBasePoint; + + Point center = new(centerPoint.x, centerPoint.y, centerPoint.z); + + Objects.Geometry.Plane plane = + new(center, opening.extrusionGeometryZAxis, opening.extrusionGeometryXAxis, opening.extrusionGeometryYAxis); + + return new Ellipse(plane, halfWidth, halfHeight, Units.Meters); + } + + public static void GetExtrusionParametersFromOutline( + ICurve outline, + out Vector extrusionBasePoint, + out Vector extrusionXAxis, + out Vector extrusionYAxis, + out Vector extrusionZAxis, + out double width, + out double height + ) + { + // Assign default values to out parameters + extrusionBasePoint = new Vector(); + extrusionXAxis = new Vector(); + extrusionYAxis = new Vector(); + extrusionZAxis = new Vector(); + width = 0; + height = 0; + + if (outline is not Polyline polyline) + { + extrusionBasePoint = null; + extrusionXAxis = null; + extrusionYAxis = null; + extrusionZAxis = null; + return; + } + + // Form the 4 points of the rectangle from the polyline + List points = Enumerable + .Range(0, polyline.value.Count / 3) + .Select( + i => new Vector(polyline.value[i * 3], polyline.value[i * 3 + 1], polyline.value[i * 3 + 2], polyline.units) + ) + .ToList(); + + Vector bottomLeft = Utils.ScaleToNative(points[0]); + Vector topLeft = Utils.ScaleToNative(points[1]); + Vector topRight = Utils.ScaleToNative(points[2]); + Vector bottomRight = Utils.ScaleToNative(points[3]); + + // We set the anchor point to Middle-Middle, so we can calculate the extrusion base point more easily like so. + extrusionBasePoint = (bottomLeft + bottomRight + topRight + topLeft) * 0.25; + + Vector verticalDiff = topRight - bottomRight; + height = verticalDiff.Length; + + extrusionYAxis = verticalDiff / height; + + Vector horizontalDiff = bottomRight - bottomLeft; + width = horizontalDiff.Length; + + // Calculate the extrusion X axis + extrusionXAxis = horizontalDiff / width; + + // The last extrusion axis will be the cross product of the other two + extrusionZAxis = Vector.CrossProduct(extrusionXAxis, extrusionYAxis); + + extrusionZAxis.Normalize(); + } } diff --git a/Objects/Objects/BuiltElements/Archicad/ArchicadOpening.cs b/Objects/Objects/BuiltElements/Archicad/ArchicadOpening.cs new file mode 100644 index 0000000000..3c501c95bf --- /dev/null +++ b/Objects/Objects/BuiltElements/Archicad/ArchicadOpening.cs @@ -0,0 +1,83 @@ +using System.Collections.Generic; +using Speckle.Core.Kits; +using Speckle.Core.Models; +using Objects.Geometry; + +namespace Objects.BuiltElements.Archicad; + +public class ArchicadOpening : Opening +{ + [SchemaInfo("ArchicadOpening", "Creates an Archicad opening.", "Archicad", "Structure")] + public ArchicadOpening() { } + + public string parentApplicationId { get; set; } + + // Element base + public string? elementType { get; set; } /*APINullabe*/ + + public List? classifications { get; set; } /*APINullabe*/ + public Base? elementProperties { get; set; } + public Base? componentProperties { get; set; } + + // Floor Plan Parameters + public string? floorPlanDisplayMode { get; set; } /*APINullabe*/ + public string? connectionMode { get; set; } /*APINullabe*/ + + // Cut Surfaces Parameters + public bool? cutsurfacesUseLineOfCutElements { get; set; } /*APINullabe*/ + public short? cutsurfacesLinePenIndex { get; set; } /*APINullabe*/ + public string? cutsurfacesLineIndex { get; set; } /*APINullabe*/ + + // Outlines Parameters + public string? outlinesStyle { get; set; } /*APINullabe*/ + public bool? outlinesUseLineOfCutElements { get; set; } /*APINullabe*/ + public string? outlinesUncutLineIndex { get; set; } /*APINullabe*/ + public string? outlinesOverheadLineIndex { get; set; } /*APINullabe*/ + public short? outlinesUncutLinePenIndex { get; set; } /*APINullabe*/ + public short? outlinesOverheadLinePenIndex { get; set; } /*APINullabe*/ + + // Opening Cover Fills Parameters + public bool? useCoverFills { get; set; } /*APINullabe*/ + public bool? useFillsOfCutElements { get; set; } /*APINullabe*/ + public string? coverFillIndex { get; set; } /*APINullabe*/ + public short? coverFillPenIndex { get; set; } /*APINullabe*/ + public short? coverFillBackgroundPenIndex { get; set; } /*APINullabe*/ + public string? coverFillOrientation { get; set; } /*APINullabe*/ // Kérdéses.. + + // Cover Fill Transformation Parameters + public double? coverFillTransformationOrigoX { get; set; } + public double? coverFillTransformationOrigoY { get; set; } + public double? coverFillTransformationOrigoZ { get; set; } + public double? coverFillTransformationXAxisX { get; set; } + public double? coverFillTransformationXAxisY { get; set; } + public double? coverFillTransformationXAxisZ { get; set; } + public double? coverFillTransformationYAxisX { get; set; } + public double? coverFillTransformationYAxisY { get; set; } + public double? coverFillTransformationYAxisZ { get; set; } + + // Reference Axis Parameters + public bool? showReferenceAxis { get; set; } /*APINullabe*/ + public short? referenceAxisPenIndex { get; set; } /*APINullabe*/ + public string? referenceAxisLineTypeIndex { get; set; } /*APINullabe*/ + public double? referenceAxisOverhang { get; set; } /*APINullabe*/ + + // Extrusion Geometry Parameters + // Plane Frame + public Point extrusionGeometryBasePoint { get; set; } + public Vector extrusionGeometryXAxis { get; set; } + public Vector extrusionGeometryYAxis { get; set; } + public Vector extrusionGeometryZAxis { get; set; } + + // Opening Extrustion Parameters + public string? basePolygonType { get; set; } /*APINullabe*/ + public double? width { get; set; } /*APINullabe*/ + public double? height { get; set; } /*APINullabe*/ + public string? constraint { get; set; } /*APINullabe*/ + public string? anchor { get; set; } /*APINullabe */ + public int? anchorIndex { get; set; } /*APINullabe*/ + public double? anchorAltitude { get; set; } /*APINullabe*/ + public string? limitType { get; set; } /*APINullabe*/ + public double? extrusionStartOffSet { get; set; } /*APINullabe*/ + public double? finiteBodyLength { get; set; } /*APINullabe*/ + public string? linkedStatus { get; set; } /*APINullabe*/ +} From aa132d24ead30e4b5648b54be90d1e5cf94e79a8 Mon Sep 17 00:00:00 2001 From: Claire Kuang Date: Wed, 12 Jun 2024 21:21:28 +0100 Subject: [PATCH 11/52] Update README.md (#3502) --- ConnectorAutocadCivil/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ConnectorAutocadCivil/README.md b/ConnectorAutocadCivil/README.md index 9b74423e0d..02e9f4749b 100644 --- a/ConnectorAutocadCivil/README.md +++ b/ConnectorAutocadCivil/README.md @@ -4,13 +4,13 @@ ## Introduction -This is the ⚠ALPHA⚠ version of the Speckle 2.0 AutoCAD Civil3D Connector. Currently, it only supports the very most basic conversions - please leave any comments, suggestions, and feature requests in our [Making Speckle](https://discourse.speckle.works/t/new-speckle-2-0-autocad-civil3d-suggestions/1155) forum discussion thread! +This is the ⚠Beta⚠ version of the Speckle 2.0 AutoCAD Civil3D Connector. Currently, it supports the basic objects for both Autocad and Civil3D (refer to our docs for a full list) - please leave any comments, suggestions, and feature requests in our [Making Speckle](https://discourse.speckle.works/t/new-speckle-2-0-autocad-civil3d-suggestions/1155) forum discussion thread! ## Documentation Comprehensive developer and user documentation can be found in our: -#### 📚 [Speckle Docs website](https://speckle.guide/dev/) +#### 📚 [Speckle Docs website](https://speckle.guide/user/autocadcivil.html) ## Developing & Debugging @@ -21,8 +21,8 @@ Comprehensive developer and user documentation can be found in our: #### Supported versions -- AutoCAD: 2021, 2022 -- Civil3D: 2021, 2022 +- AutoCAD: 2021, 2022, 2023, 2024, 2025 +- Civil3D: 2021, 2022, 2023, 2024, 2025 ### Getting Started From 14788666f8c377abdbcf54a1905968fe33eb546f Mon Sep 17 00:00:00 2001 From: Alan Rynne Date: Thu, 13 Jun 2024 17:01:39 +0200 Subject: [PATCH 12/52] Archicad: Update connector icon on mac (#3503) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * update connector icon for mac * use sln name * fix: Moved bundle and zipping steps after external PR check to ensure normal build CI works even with external contributors --------- Co-authored-by: József L. Kiss <> --- .circleci/scripts/config-template.yml | 51 +++++++++++------- .../AddOn/Sources/AddOn/AddOnMain.cpp | 43 +++++++-------- .../Sources/AddOnResources/RFIX/AddOnFix.grc | 8 ++- .../RFIX/Images/AddOnIconMac_18x18.svg | 18 +++++++ .../RFIX/Images/AddOnIconWin_18x18.svg | 17 ++++++ .../RFIX/Images/AddOnIcon_18x18.svg | 1 - .../ConnectorArchicad/Assets/icon-mac.icns | Bin 0 -> 37877 bytes .../ConnectorArchicad.csproj | 1 + .../ConnectorArchicad/Info.plist | 39 ++++++++++++++ 9 files changed, 133 insertions(+), 45 deletions(-) create mode 100644 ConnectorArchicad/AddOn/Sources/AddOnResources/RFIX/Images/AddOnIconMac_18x18.svg create mode 100644 ConnectorArchicad/AddOn/Sources/AddOnResources/RFIX/Images/AddOnIconWin_18x18.svg delete mode 100644 ConnectorArchicad/AddOn/Sources/AddOnResources/RFIX/Images/AddOnIcon_18x18.svg create mode 100644 ConnectorArchicad/ConnectorArchicad/Assets/icon-mac.icns create mode 100644 ConnectorArchicad/ConnectorArchicad/Info.plist diff --git a/.circleci/scripts/config-template.yml b/.circleci/scripts/config-template.yml index f64173dbb2..ff92a40566 100644 --- a/.circleci/scripts/config-template.yml +++ b/.circleci/scripts/config-template.yml @@ -341,6 +341,28 @@ jobs: # Each project will have individual jobs for each specific task it has to msbuild << parameters.slnname >>/<< parameters.slnname >>.sln /r /p:Configuration='<< parameters.build-config >>' /p:IsDesktopBuild=false /p:Version=$SEMVER /p:FileVersion=$VERSION environment: WORKFLOW_NUM: << pipeline.number >> + - unless: + condition: << parameters.build-with-mono >> + steps: + - run: + name: Publish x64 and arm64 + command: | + TAG=$(if [ "${CIRCLE_TAG}" ]; then echo $CIRCLE_TAG; else echo "2.0.999"; fi;) + SEMVER=$(echo "$TAG" | sed -e 's/\/[a-zA-Z-]*//') + VER=$(echo "$SEMVER" | sed -e 's/-.*//') + VERSION=$(echo $VER.$WORKFLOW_NUM) + $HOME/.dotnet/dotnet publish << parameters.slnname >>/<< parameters.projname >>/<< parameters.projname >>.csproj -c Release -r osx-arm64 --self-contained /p:IsDesktopBuild=false /p:Version=$SEMVER /p:FileVersion=$VERSION + $HOME/.dotnet/dotnet publish << parameters.slnname >>/<< parameters.projname >>/<< parameters.projname >>.csproj -c Release -r osx-x64 --self-contained /p:IsDesktopBuild=false /p:Version=$SEMVER /p:FileVersion=$VERSION + environment: + WORKFLOW_NUM: << pipeline.number >> + # Create installer + - run: + name: Exit if External PR + command: if [ "$CIRCLE_PR_REPONAME" ]; then circleci-agent step halt; fi + # Zip files for installer + - when: + condition: << parameters.build-with-mono >> + steps: # Compress build files - run: name: Zip Objects Kit files @@ -362,34 +384,25 @@ jobs: # Each project will have individual jobs for each specific task it has to condition: << parameters.build-with-mono >> steps: - run: - name: Publish x64 and arm64 + name: Create app bundles command: | - TAG=$(if [ "${CIRCLE_TAG}" ]; then echo $CIRCLE_TAG; else echo "2.0.999"; fi;) - SEMVER=$(echo "$TAG" | sed -e 's/\/[a-zA-Z-]*//') - VER=$(echo "$SEMVER" | sed -e 's/-.*//') - VERSION=$(echo $VER.$WORKFLOW_NUM) - $HOME/.dotnet/dotnet publish << parameters.slnname >>/<< parameters.projname >>/<< parameters.projname >>.csproj -c Release -r osx-arm64 --self-contained /p:IsDesktopBuild=false /p:Version=$SEMVER /p:FileVersion=$VERSION - $HOME/.dotnet/dotnet publish << parameters.slnname >>/<< parameters.projname >>/<< parameters.projname >>.csproj -c Release -r osx-x64 --self-contained /p:IsDesktopBuild=false /p:Version=$SEMVER /p:FileVersion=$VERSION - environment: - WORKFLOW_NUM: << pipeline.number >> + sh ./speckle-sharp-ci-tools/Mac/AppBundle/bundle.sh ./<< parameters.slnname >>/<< parameters.slnname >>/bin/Release/net6.0/osx-arm64/publish/ ./<< parameters.slnname >>/<< parameters.slnname >>/Info.plist ./<< parameters.slnname >>/<< parameters.slnname >>/Assets/icon-mac.icns ./<< parameters.slnname >>/<< parameters.slnname >>/bin/Release/net6.0/osx-arm64/<< parameters.slnname >> + sh ./speckle-sharp-ci-tools/Mac/AppBundle/bundle.sh ./<< parameters.slnname >>/<< parameters.slnname >>/bin/Release/net6.0/osx-x64/publish/ ./<< parameters.slnname >>/<< parameters.slnname >>/Info.plist ./<< parameters.slnname >>/<< parameters.slnname >>/Assets/icon-mac.icns ./<< parameters.slnname >>/<< parameters.slnname >>/bin/Release/net6.0/osx-x64/<< parameters.slnname >> - run: name: Zip Connector files command: | - cd "<< parameters.slnname >>/<< parameters.slnname >>/bin/Release/net6.0/osx-arm64/publish" - zip -r << parameters.slug >>-mac-arm64.zip "./" - cd "../../osx-x64/publish" - zip -r << parameters.slug >>-mac-x64.zip "./" + cd "<< parameters.slnname >>/<< parameters.slnname >>/bin/Release/net6.0/osx-arm64/" + zip -r << parameters.slug >>-mac-arm64.zip "./<< parameters.slnname >>.app" + cd "../osx-x64/" + zip -r << parameters.slug >>-mac-x64.zip "./<< parameters.slnname >>.app" # Copy installer files - run: name: Copy files to installer command: | mkdir -p speckle-sharp-ci-tools/Mac/<< parameters.installername >>/.installationFiles/ - cp << parameters.slnname >>/<< parameters.slnname >>/bin/Release/net6.0/osx-arm64/publish/<< parameters.slug >>-mac-arm64.zip speckle-sharp-ci-tools/Mac/<>/.installationFiles - cp << parameters.slnname >>/<< parameters.slnname >>/bin/Release/net6.0/osx-x64/publish/<< parameters.slug >>-mac-x64.zip speckle-sharp-ci-tools/Mac/<>/.installationFiles - # Create installer - - run: - name: Exit if External PR - command: if [ "$CIRCLE_PR_REPONAME" ]; then circleci-agent step halt; fi + cp << parameters.slnname >>/<< parameters.slnname >>/bin/Release/net6.0/osx-arm64/<< parameters.slug >>-mac-arm64.zip speckle-sharp-ci-tools/Mac/<>/.installationFiles + cp << parameters.slnname >>/<< parameters.slnname >>/bin/Release/net6.0/osx-x64/<< parameters.slug >>-mac-x64.zip speckle-sharp-ci-tools/Mac/<>/.installationFiles + - run: name: Build Mac installer command: ~/.dotnet/dotnet publish speckle-sharp-ci-tools/Mac/<>/<>.sln -r osx-x64 -c Release diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/AddOnMain.cpp b/ConnectorArchicad/AddOn/Sources/AddOn/AddOnMain.cpp index 67e1d73399..1fd3ac1daa 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/AddOnMain.cpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/AddOnMain.cpp @@ -92,46 +92,41 @@ class AvaloniaProcessManager { private: GS::UniString GetPlatformSpecificExecutablePath () { -#if defined (macintosh) - static const char* FileName = "ConnectorArchicad"; -#else - static const char* FileName = "ConnectorArchicad.exe"; -#endif - static const char* FolderNameCommon = "Common"; - static const char* FolderName = "ConnectorArchicad"; - IO::Location ownFileLoc; auto err = ACAPI_GetOwnLocation (&ownFileLoc); if (err != NoError) { return ""; } +#if defined (macintosh) + static const char* ProductionConnector = "../../../Common/ConnectorArchicad/ConnectorArchicad.app/Contents/MacOS/ConnectorArchicad"; +#else + static const char* ProductionConnector = "../../../Common/ConnectorArchicad/ConnectorArchicad.exe"; +#endif + IO::Location location (ownFileLoc); - location.DeleteLastLocalName (); - location.DeleteLastLocalName (); - location.DeleteLastLocalName (); - location.AppendToLocal (IO::Name (FolderNameCommon)); - location.AppendToLocal (IO::Name (FolderName)); - location.AppendToLocal (IO::Name (FileName)); + location.AppendToLocal (IO::RelativeLocation (ProductionConnector)); bool exist (false); err = IO::fileSystem.Contains (location, &exist); if (err != NoError || !exist) { location = ownFileLoc; - location.DeleteLastLocalName (); - location.DeleteLastLocalName (); - location.DeleteLastLocalName (); - location.DeleteLastLocalName (); - location.AppendToLocal (IO::Name (FolderName)); - location.AppendToLocal (IO::Name ("bin")); +#if defined (macintosh) #ifdef DEBUG - location.AppendToLocal (IO::Name ("Debug")); + static const char* DevelopmentConnector = "../../../../ConnectorArchicad/bin/Debug/net6.0/ConnectorArchicad"; #else - location.AppendToLocal (IO::Name ("Release")); + static const char* DevelopmentConnector = "../../../../ConnectorArchicad/bin/Release/net6.0/ConnectorArchicad"; #endif - location.AppendToLocal (IO::Name ("net6.0")); - location.AppendToLocal (IO::Name (FileName)); +#else +#ifdef DEBUG + static const char* DevelopmentConnector = "../../../../ConnectorArchicad/bin/Debug/net6.0/ConnectorArchicad.exe"; +#else + static const char* DevelopmentConnector = "../../../../ConnectorArchicad/bin/Release/net6.0/ConnectorArchicad.exe"; +#endif +#endif + + location.AppendToLocal (IO::RelativeLocation (DevelopmentConnector)); } GS::UniString executableStr; diff --git a/ConnectorArchicad/AddOn/Sources/AddOnResources/RFIX/AddOnFix.grc b/ConnectorArchicad/AddOn/Sources/AddOnResources/RFIX/AddOnFix.grc index 02d663f4d1..6ad0e355df 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOnResources/RFIX/AddOnFix.grc +++ b/ConnectorArchicad/AddOn/Sources/AddOnResources/RFIX/AddOnFix.grc @@ -6,9 +6,15 @@ AC_MDID_LOC /* Set AC_MDID_LOC value as your local id. */ } +#ifdef macintosh 'GICN' 10001 "AddOnIcon" { - "AddOnIcon" + "AddOnIconMac" } +#else +'GICN' 10001 "AddOnIcon" { + "AddOnIconWin" +} +#endif 'STR#' ID_FIX_ELEMENT_TYPE_STRINGS "Element Type Strings" { /* [ 1] */ "Wall" diff --git a/ConnectorArchicad/AddOn/Sources/AddOnResources/RFIX/Images/AddOnIconMac_18x18.svg b/ConnectorArchicad/AddOn/Sources/AddOnResources/RFIX/Images/AddOnIconMac_18x18.svg new file mode 100644 index 0000000000..0f1cfa4bb2 --- /dev/null +++ b/ConnectorArchicad/AddOn/Sources/AddOnResources/RFIX/Images/AddOnIconMac_18x18.svg @@ -0,0 +1,18 @@ + + AddOnIcon_18x18-svg + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ConnectorArchicad/AddOn/Sources/AddOnResources/RFIX/Images/AddOnIconWin_18x18.svg b/ConnectorArchicad/AddOn/Sources/AddOnResources/RFIX/Images/AddOnIconWin_18x18.svg new file mode 100644 index 0000000000..fbe59e2a11 --- /dev/null +++ b/ConnectorArchicad/AddOn/Sources/AddOnResources/RFIX/Images/AddOnIconWin_18x18.svg @@ -0,0 +1,17 @@ + + AddOnIconWin_18x18-svg + + + + + + + + + + + \ No newline at end of file diff --git a/ConnectorArchicad/AddOn/Sources/AddOnResources/RFIX/Images/AddOnIcon_18x18.svg b/ConnectorArchicad/AddOn/Sources/AddOnResources/RFIX/Images/AddOnIcon_18x18.svg deleted file mode 100644 index 4031449236..0000000000 --- a/ConnectorArchicad/AddOn/Sources/AddOnResources/RFIX/Images/AddOnIcon_18x18.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/ConnectorArchicad/ConnectorArchicad/Assets/icon-mac.icns b/ConnectorArchicad/ConnectorArchicad/Assets/icon-mac.icns new file mode 100644 index 0000000000000000000000000000000000000000..6ef6c0c3337a38f7c3a4ec06f4b9d4b40c311f07 GIT binary patch literal 37877 zcmZs?19UDyvncw-wr$(CZQHhO+sTgY?AW$#Cws@Xle_=(&VBE$^KPx~DNIeznx0zK zRZ~4yCiX4>K+2Pq2_x4(d<6giV67Di3E`k4p#RaR=B{3j){e}C|7gH}V}*a%@*j(C zWohaR0093Z|8-CZi2vvS5KJ6woq_&~MErLoHZe6b2Y~(;4+H=K{O8PnI`Ds82l5~J zzu`=N6n>Nd%>Uv3d$|9Q{{{bp2ZaCv`hVMxDS!k3B;sslTqG;w!sBY%$ zV&z~@$VAV@$VkuhqYQxkHxdv43=|Xq^rH(v5fqe^RQx~Nj~)OO=>IAO{=Z5={{PZM zz<;I-kX-Zc7AbpStw@S!WNwZV$cFETCD?k0l(&w6s)|0iiGu`%rQG5RNNk*Y)qoHz06rehY2DjJbKu z4_p^-s7PIBlJm&0cCuwmlwl#>0nA=l9g@5y%e1UZEobi4M(jFaivBP}!L;Paq7Ozd z(>IK+eZ2>oWU7YS^&hK_7)`x{1Slko1@d2_2udQkECy2-Kt_&7koZb87j=?8<#Mt` z0uKzzu)3;+&zy#kHGyqWyE3Pa8$-8hn3-tzqKASh3RSLpR5czt;;xz+r;8{vc<8Em zZlGC76SQvBP2Nk-^Frx#JtY@Kf~C)6?-wf>@ohp<-Q*_~9}I&)0nw6Jg6?GO!#7uP zpvZ>EV;)>F7O0dtYM>(ohS*|=d@U11Y7S21vA`4#dto(5@)m>msb{@Ha{Vdo_Ehud z-hmM~-^KVJ5#P}x176{i0Ds_*mJ5N@@UndjS%^c5qyRY`$L#BlP$fxkkVrTko*$)` zMyDEm0}kn-(8!V>pSHv7%ajTLT=25`NThTVipn<&CD|8$fRG7OZX-4n+_t;bKUjCoKz@>0KC;-f8YF;Bqx}1x9hjg1sO9j z*%u6AYyUH(6iFz-S(T+ccnUDE+KViC;Qb&O@2`+1c)`hah_^MAAEydwN#Q}Ud0ft9 z___E<)e%W}GI;fpAxRYFl$?{}_>eqr8e^Xyc<3+c>G8T2zzD3+K49&}|JIH|;+pK( zGk1A{IXD5q5mDe(1`|7o-Vp$>T=fm>Rw$emJ660fcJ+~{E+XDuq-yGM5txgfVlmG=Z;>Qx$+m! z;jw71CThmh@krY; ztM;0-e}V~fVYTteGR&#_izVX^n0b5_`0bU5Kh!O{*WwAFy`2APC`%W~2!uLTfIHJ4GUynHMjOLnkFhvkZe;igPx#H$oas?J3y5gf>L5N+mUuiV-gQ9T2{e3Sn_%v#|xgku@ z{-YY*)`jFp63EX*rZj!XcWVb4AvpFiywnGK-=kibB18wy@zx}miBcq}jAuWXnsF8p zwYi#51hY|hA{U~yPjRTn=Y~jKw~?MyGNNrP5X@;{k5tlXDf3Pg*Ct3=y05uq3du)3 z#-EE^q`9zqG1nsLuXi_}R3?;<7Rm%7N>RZK_jWoyGdKMz1L?C0JOV_`DPTmiyVde4 zI$;*x(pP}j;hsL`&s%5C|3b|h$fj=fU;rh}Cjx=zm34yeAvafs*Pjpii%0!)JZdOV zCiq=2VB;!nLM6Yi!jNqltLjQpiJO`WEqM*RB_Nr?JSU==v6Z);9qM8-S~)v)CJm1Q zlG)9q?^)YGh=YyXOZ(hR`ZFCJ*&YpArm8k(?d5l&QPoYL|lSl$9!JQH7i`ANp ziLg`_h1&RSm94DN24(|RK6Z+xwUNIr@aWgqib+eQ;yUmO61D5e>owOi2<8T}^25jo zLINi~wM+(g?z62GIQ8?KJhP2B$!0lYZkMFTP5+{bNbHZ$8w7h%aTe26NQk znRDPbXoJ*+uQAEYQE99Z8Nd!JlzA z{uKo82J;!jdbXk#xcM3*>iM*bUV^%Ph?#Uzi$RRF(jQ6lWNvd{8nq%D<5)Y?K6-@J zGUi0`qeZNvHm(m5vC%jK5H5))4yo5aXGfYII~$Yzzp{lGh2!H@M7sZ-NLL%P&} zgQ&k(dc_yJyZ(MJWh;+K;hTFLfrL*M7h{Pt6N<*u6rz^8UI4;)P!D$3yQ?(#GZ%<} zftS(HzTRJNXbxJqjfZ|;oDGOkS3^##Rnwl!+ltlzO!kT_3TyabTOLsvhZAJtUh_Z^ z3XXD4@XwQf_Sc$&;Yy~`s=<4;sG5bF{brUc@+gjgNQJg=6OA%=04qtdB~j+&Q<6|L zX4x`>JS8=ob99b|dKO3KC&RXe)6zZ0oM=s&meqTWN>_k%R);Ct-fh=u1bACmOma-< z$X^c!8swXUO-tfqtvxTVm+#WnR;Z**_PAEp(>%k3)Ftk~z#_x+t!RD&1f~-$ZH}&O zpx#$@lUu2~KWV&N^v31RROe3jJE1yHnmm-p1yYBnKy^3~kk%&3`!-1+S)UuoNwhuP zRSOb>DDljGa^J9&M9d^jmBfqg>s%wUJGB>>ffNN@g(;YDGc`swHO_OVoE&bGp{%nS zI{|dHqpb?z5rV4jm3MhPH35lEG|`<}pJANzu{8W~>+9(ejoxkUD^MoXkp?O@(~yqd z-T_(xz)%=9X* z>u)z5y2au>ezsi>tpy29&%h9P~|X+kT|V=cQD{;U18E z3Q$2k!)l_vD04#ANU5YaBd49K{-T(pfqfXOWWoZ;QrRY$Hd7m9V3x-x=7Y?I056W! z%Irm|=aPZbw0`Nk?+)puM^ayD%PXpU`}>mUI#G-?B~ecc{68kTDEcytemwnImK1x) z9mUViGweZ7g1(?^`&&+r=Qh_2?opsoI?rgz7R3S#{Cc6x2Rz`gIHtLS)GI1;S(RCA zv-|HaPkUI6obPn(tO~?q(bbQs&A?FTJytP3w;R$t%TcBmQbo4EWONY}#2xXYAdO3Q zpfNw7oI7416O61}6D3_$#JQkl)XrsZwNDUqhkhS{lkJ^W(3%9qkywXG@`|1PBGdRj zIoF?$${Yok53Qg!WP!CKKZvB?xIXT;Ux}%Pc(j<>^@wx1TTQPw5zEa@+NXHSw1D+d zZYi&e1JJ{mM>cS$vn%6M{(puVE9gg!=cgu$VU{sRa;V$f%p`E=wy5!5e>`Ab6m-cy zR7)dCM~;)m^TQK*E&YPaNzoV)^r7Mz^^M^2r2?F6O2^&=^tf^zErR^^F8pb$rLtSpE>C4uooQ{HED13x*gS!MIH;yc zt6S^K1acAat=kpq7bGA2Q4+)Z0`D-gA?D`&-+(=~WKmq9)54SK#K#tV+M33BvQT@r zRv4EzHW@zzT+WLgJu5Vu9kU9LA72~F(8{vZSiD#${Q6`bHcV(_CLB}H|806=?u5zc z$F)rdWDfG_5`a8y)H5-37>P!DNC2Zt;&dq|$m>_agX+TB8OC0(kpI@OY`YI2yhX+f6u zS9m334A$bBK&6D9t8fZ@a;^7Q-vqUn5&8Y*+zi4n-jBoFkX-pekzMVn{Rs>=@R-|YN7BrpS z=I(*vePVVfu!yYI8PP26)CLR-t56$=f!A|h=NA@$Yq0#cssp*t>|(5Kx*}Jr`Y;Ij zH+|kPWBIL`Cr%OOXqTu9kB{F-q~E?$=`8MdxsSR0$g1@>iZ<#o?i2Y>(Bq5b0tQ&S zmO-eVJmBcP4AmPQh;QrGI(G=B#-Iv>zY{pn?;+qf!OsIZ@-H*;Dh5RCl4JzL;Ig1g zHGh!qaK@JhXMdXA`f2J))6& zEJ?uh{oQpAohy%2*%L2Ka>P#Rc&lsmaEyw% zhpmFOq9t|n7={IBEL{Q~m>BfxweUP0|DL@-Zg7+W5sVu_X)^H2I3wBqgXv`vHtF3X zq@cVj+aY_+D0D*v$cb&fHiLCA6W5@yy!b#A|1z#$N^OtZ?KP%Q7laCf@3v&^u@xoaS8#?hm_7s_O}`A7NZ&Y zY+%Us{j~Z!wSa`{1l`C}Y%IfBOL9-P=CKt(pgTo?beMkn95<*`{7kpJs>}Pac*ls^}%jAX|!vEGE8c0+M?y zWL_?x#}!^xmoR0PJ6_~aIQrY9@Lr{%BXFXHgn%9MNmQ|!=1{n~>vG$!E?;K-jq;ZC zYx5xALV~YEA+%Zw<$!vT!#k+}Z<0sPf@;B(ncPaeNrjzoN@ILYu`Q2e!x9eDlnmwj zEV2ZP$O3iK?jhpiUFH!edPbq%%(&8N7IPDLLCsgS0LqW%xSRSnK z+9+XdaR487lKl}!@U`2-ChA+H6(98cS6xH=@zagVTrtudev6DJzOeN#t8i@TfZQe? zdMesXNDU-ASEIkPE>YN)4=a(SZCwVxwb^K+z9&!MI+rHMWUsGlgR_R{4{gBiJe<_WwP}mf^zBnSFHH-#H_9Eh z{8%y#dE=+blE7a#huumNTqOJXxRc6h9vc-pvA?KBvPd?gi^x2)PA97^zHgI2$*UoV zIV&9!pd`(o{y-M-#19Kj8k&9l@@lpMkc()8yfXY)A8PJ<^&Qi z!%CfgM_096^|1x^v`QiG%usGNK_<$lTQ>gI+&YjFGEXS=5Cire=O!jSiN)hu5+Mit zDo~8ARC(g*tCv-Q*vE*BXg@j}xa2Wi-4Qcl%+bqSu_N#v8xI%O7Sv+dK~iLm-P-o< z82ru^uHJMXySb1hpt_u&DFK>uM-1vHph!e*pjsr(|>&N*u2`&CfTQwE29Aub~^Q8S)z4-)?*Vb znuNCafcqWt4hM4e9^ z^WdLin4z{$_I~8YPQ$m^jDbmKnX&V|}u|fZ`Kj&6SYP!}frb+rW(kcG1plw<(xW?Jz@{ zo1+|V*@tGL(P2A@PiWfT8^}#6*Xmc<(@a1;={Ll396kI9uZnM8kW^loDNmoSC9A$e ze$=D;-MNLHR$9ENh$-QN>ZDP_EW0QAe`^UwrE{}FHDF7v3Vp8qH1CDnNqv3CV`9g- z@>g7T^?M^AslalydVa*}FOs9!Op~3eZF|h!m;&0Y6JhrtagyLv@dFQC+i{xE;40$O|`Vx$i?4}wU@EfF14L>hxbdqG3 zADP=Ryj69tRQr6Vz03-e|Iid$S9|Lx}Y{vK+VxR9KlEvIcLX`tVVVY6qBRN z3^clB!EP&uK7D5r9as_4;Vg_zXAX=PKpJkUc58BRd8p?iPKLWd>?Ly3TN>}X3?vNRqF@pF}>|c#xsq7P|Jp;m^ZB`86ZP)Qc{~$GwuTK zFXxT+*mcz&G_aALJgibQtLdyA2%*Z|kaGz77?r@s72!1iT@}d2OWFc8hNQ!?mV^T5 zsJ9cjIS&?YI%ibpF!lF(REACk&F!yc;rlGPTYHtQh35|LLbg}ik)j}77wNgq+MtA2 zeKXSEaihX=bP{q+%oDm^Sza-C%~|Ga*plkcd3yaUI0?=}0gFl0c+ew$maFGuGIu^U zs9I7(oB(?GU4-_DKiR5#+Zx`!Fs-P(L<{8K)SQ={y8! z(Z03430#lpdbRE-u9%rYCtc`l^BZ~!5s~eyUY-`1f-8WYf!SJ?PvXpfjk`Wj7c}nG z_$bryARFb)F3gmX(OFDBZVI;;Jx_-&ruLVS(w?=EHO?fP{Z?qh(Sji!DA9;TU(Ed| zzJ5SKk*aLg8mr5n)LrY+ zMi@AD$f#!_bX`NILJDc3L<)?^Ac8p{RzsJBv8bn(mN5$aQU+yRoqqW;%}WD@4{gU_8+SQ1II(#OMAMRIVly$+rsXIUDCRpa4`LfVyIwe>4$c%0 z-1wnwYT*Q^NZ0kxk-@nB4Fs5;DPSy3ZNiWeP+`JW@HRVOBJ=D{bVLT3w}aBMgb!iW z;|wnpc<#?bP!V4Gq#HY@(_S9JFgK)oa!{M}YQ?#i$dZH9ZgrKD|MB$L4hmd*IzbUr zL;WMZ#4{y&_pGI3fZoXbn+j9&7Y`pa2%MT$pO(#@L}l6&MhEQ@{%WeLx^QvT3tE0L zbS7&T1nPl~+k@}!9qvj|nUz8js2n1lmQqAchoL$a@RY!T;yy!skfe00Rz4AZxtFkK zm|x+4^!L;zO0taeM@3cU$r6ja+i#g$rVw)DEkR37Tw##u_Q@u&jw{l^-Y>wR|5Z#$ zJ@jlLby6$^x#hr&p?4eCr7zH@#>v&zg0sja+qp()G-2#iK zFr+Doc`$X$9C+*<>U{Qj!DvmuEkPTJe~sL^!ShHvDId&@$8Re@{4lNxKj6WAyx`}W;gD^$ZQ;#7O40u9?2 z_q2nd`@f4BOVQrln83I=9=T8++gwrFi?KBejeta%~9Wu{H|Fc-5%T9iT+sav6Nz=4n1K{gbovHnUE?!{u05E zDqkdJ80*2m1CMY?aDjF&ln;alqM)?IQ=09k6-?H^$6tk=WXfwewXnF;N=%w#4amF$oCo1g z13&T8`#VU&o}tr?TEf^r1GIEkuhJ zB`G>^`3#L3iZM%tgW6V6^L=DtQ)8rfO*#6;dup^o&dXzj|F|NJi+2eY*v=NFbtocv z)ppES<$_?*z-91>yw{`Z=B4-IE8D(*3l$5vCPdrCy08Q%4AMvh4~9!XcD8c)RKSwJ zsYn7vQU6I*geYMRWhGs7+!6H=^I9IY21UI+PHC|A6Cjqz6LCMYMQF)GCO~trww5c) z1Q~Bsa{KPo$W<_3MLTj{hEAKBmOI)kx`qB*w%7#vLNZXP!NPHE;x(tEYOtdWJrqPi zQ5!C-wtqcpPr#l)Qxh&XRL7pS!ZF2NI6`j{xbR4h*Ee>)@)&#*9^sc*mhgs`4QkId zAS<1>xu5?MR)(#tLeGe#&=D+Qci-ou~gmA=;8ujaKYr>Uz-hSrL2g#3YGjLoZ0e1{fqKZ%0p zRI5I44aH6*=kR;)`9xC$(so#LyXVW|TZspU<5zqB7|Zy{H)C8M&uDw*laIYX{UJmU zU#Gg0!z88h8xVgqI5%NzZSpoApdQz=j%nQ=tbB0-l_TGRjrRFPEmZl2om%8M!G^v? zVUW|oU9ATE+(b&i5y_ORMT~qJeOv6A--LBHdUek`@9N=L4S9C6u_D&D;WM^)E2!)B zFpCZdmo!j<|32*kuIYFF6N*=T8I0LsVIu`p5y6I}?Q@4J(>ejTCBum$Ykar2r2Oc( zf8=V2NI5IvlM|d08*Z9kLx*3wgCY(L0|Qkhv;}rYZXtz0l zf~m4J$1WQQl_9J29dz*9aW5-blD-U0oNiui=UaCRYoTSWkyV{5@aypm6CSr`!7fy= zM{`!1FW7p`53P^ZK{)tNKsH^2JdqsRz6SXY$ZgXf5Z^6`&h7;|vsOZ5B~T7I&zupWOj35?(7V z&YQNdpn$*WoE;S>)nLCvmzd;{%aDS&K}&t4OfW^&seM3u0zhv6J=+j4%AupM9}lqd z0&=#}q@&Ie49@Q<%7Lfxr)7|WciCy}D!(3{uJA6gC z)H2Y3@0@~$dY!pB=T}m=_1hLl8?Y>b2dI=KF!f`##)0+hqIXn)JTHKz+^{F+{bPzr zUS7IXwr2XTxMjTx&f`@=3&x{f@oZY5>3nRK= zaex*6-urKSuo6tTss@$H{q3dQ3U)1REY+^tiuIK2d(FxzVlqsd_bHW=D4JFbFzUds zl;>)5%O1Cx8ME&Vsj6V|DM`RRhvXUOFcrQ;jLJqY*(-r(^e+k;iAU3On-PrKx%Bk1 z8!7jQGIDK1JS$P4Uscc3*t!lE!!kz63tR6uu<@7%{hBzEIBh1w0%mUslHO}xf!%V4 zzzfkpUbq=dxLMM&7_{|=1iNdBmUvOkD~VRbvFN&Z;B{_{79>Enxi!}|H_S%em-OOI z3>pRqI^>tczRujV*nSY9SSOh+fqp*lhK0W*2wbA|UF2u@kvkO)XN_hg-61W0LA-;NaIXV|nKk%piqJ)_BhJ3a@OfNO(KVMSNVk)hE)ByE zQCKj+bxZo~u=yr$F1dP?B@+yEZ2Jj{ z1-)$PE2)F4IX!EU&mzZ{`~)5(%~e4*4*q>##x~sm)d5-z##>}vsj%4x9gcm0b2O{- zH)t4?eQ~3x6vBPZIJaY$Zq*y~*0i*RAN1I8K}1}Plq(jtsqvwY4;v+(sKF>Uyr49e z($)Zfe#A)^m!>NS%1=}>PBj1{_=h%&ju|0}r9N}J!O<)o8a(q(?!f}?m>Pt`SMA$$ znAOha!M>`R_?l;iyWv$WbAf`~GN7nTnctj8MoBb-aDb}4nDvu3LoKIuaj*Ysi4x>5opKKL(ytR2>Q*Btt(jE zBBM3~;dlw1@oCZ;Au(h|N3}UPi0Rm@?BSVmpJK{_$$1S>s5RV3?JWFL)H|a&>~Jz~ z$$TBs1H`!Tr~>9PcOs`Kck$N3?K{?J&vb?R-7vRI^MqCV4OZ5UTgcF6eyxALn(BVv z%lNiH)aLjLF5g0{4#NVWT}U@M3Dy`4UtjAVFX4{Yp%1&u-0g5EC06F;_6L&qWW;*>jduy9@D7R?rZgU~k}JPmh4cM_?l)x9U|*V~nNS=c9L#rIPT)#s z9rbH&M^RUin!N%0jRlz`PM}jPh@wJ-qCzw~K@V;ircKmc=t+&iI0GbBkL@3cybjse zr|9=^w514O>8Jf#xaE+mk;OtMhrG^*>%Q}~wHRsdWm($A`x|Y_D*v;-JLNd3R8Z0Y z$X{f>XpOQRX`n|g4p7q3J>3wr0#t)g>ao)2`>Q>u9^F!10rn;NN_IO0Wygnocl3g9 z>nI>05RT`I17a5tyDST93pTXX1i)2f+_{5lPKWl@62<}#F4>Sb)y+xz6$+M>^^k?J zgB2pV@ZeZxfH(K?c3XBFxbPvPx=(s_X?9|!BWu%cr{g=2-%qdw4x+)skR%*3UC>pn zwU0#nj|;T#I2P9~ob*K%g)@cSk4Af8L7VVJQj{UKN3h%zYG>YzZyapUIlr#S8&s?M zucUF;f6%3EaQ~e56XAdS-d+B>tvHCe5LHnxLnu5+PEly-otvW?c;NT4S~2Xq75C&~ z*}1RZcJof1Wf{DnTGW!yi~61HUEHy~+sqtg|veZVRBoVQE(IaCD%q%IvElOjcXfEcn;_`N$DjUd%EvIIWxIAg_ z`6tp34YGosbfLSV@a>Zq|Ni9kuq(n@O!a$ttIGc!SQ{4o8DM7{dNx9Poqk_d$4^Nlm~Qo!XX+;16lQ_oxc>V+|E^ z^bdVrxRlaw=uq>Nms72If-V8_4vb`dW2ffzt7pttk*WU%je&xHulRL1){*p=HMEsr z7p^Hl5N-(@wsgegNBbZs2Hj`N>&MEnob)wvJQ)6{J1BkWMMAgqh&t&&oUC;J#CG!L z7Vjv1Uk+Y+O~NI3WCOA%!-$K#(Qw2>=9Gj>j5f6P#BcHOj+r6N|K*`c?<8D~;Gsvw z1=_?N;Q)KXr{{v5z{}ycWeK5~`xjpqnd`xAVuTwvvTtvI1nC-*St*LfUnKM*X0U<| z-=sPz^(edGP}V=&1J0cv`+wcdMPoJTbQL!9N+zN}<9E-^If3J+ZmSW#?B~A7Rp$sz zDX%b@^VYSULaSZp#Tl(_&kF8~{A_Pe=t-yIc7(g3ms?*p^rVY!w{< z;E4RAwo1uM?|8kMMykKg(s_RO@9#s`Z$2(Mq?hZ5^&$C zV6A6i#=M|DCaCOgbi8aE5;WXUQl|X-+%<(|oYE7ndP@2|kvaZU)#9t~i77j-Ja4(z zZ<53_y;mz+{;cZ@*(3Q928V9G9X9FTfYr=^yQ7;N*ze-+2%~cl2{9x&9T=j(>c~lk{syKuadLFpU%rnqniy;S z5b)KTEjo9t`jY-hycw6Lf19HRu3gK5LPDB%FO31r8?ts|oG2gr<<-GJ!TH;UhPvwU zV2?2~raz+fFu|8q)B(6hbt|%QsP$?J9=7D#3u9j7sg&&^;Ipe%wQd=`+)fC2>d#b! zAQ{klxm`Mk9H%@HSUm$6Rd;)z3_fh0h*2JSgO7zz63E^-H-4lP3ly5x8k9F-=2?F*Or^Yttt*~U1Cff=%$o2thn zJLPKZ)ucdxnC?b5Lgx^IKQdXfWK&4l9#8~-5>#30%}q*)d9T~oFftoK7e>N2Tn^M= zg($Sxmh**_$GV0}rlmuzZsfb^?vpx2RMZR6jW=(DRF=lYh^uc{q1QuNy`^WAHK!ES zywu?YcMxKY>g^;eZy|0bN+b%JhAQiYF~EK(uU9Am*6(Y-S{OHT1pobmqAqlW3rDVT z@^I|X;&>-ww++(tcbfPeEQX7}*V_oRqqz2!k4e3RUSG8(jQL~_#Nl$U!K|HVU=le5 zLVc!owor(DOoqr@A!tL!SW<lS6tm=1DCljD0(nTwlE`l{xvT42Y*hgI z$DKcY*ip~^_i2%dCQ|IoT|k)~rGI7U*-5HAQ;MR;+dzS5_PBqvXJ+5;;0}%sNjj$~ zJ>?2piakQ-*hko-uw=zyUU*SGv^`&keA~?GQ_M6e*C&OR?bBe+86ghibkiR+o)enX zDy%aH@7+Y4!NRs98}G)Rx-5;G)54&ZB8x*LMbs8y14LnQB+eZ5=h{{;tlF*kZfZ=` z+1r@&5)kt_0(7Gj-zYF;eRz%aO)2kv{Lkm}^_yt2c*t{P2`;)5-QHUmJftqA&V5uq zCGxH>mO?2-jWD>CG2Rog^otgB5~+N3;3l8g(eWSDaH<&o@1@YwQQx%gh6%$}@$xF4 z_Bw>{__ibN8;CNWw6g7K63t8)>a~}th?L+a`8iR`)WS}m<10F@!@bJw&KjNjYotNM z8|rwvYrkw>k~j152RkANk7}S0uGv1*DnZaEYO5u=!jIzS#d+pOkG;iekN`x&MuKB% zP>YOq2vd+r7Zr3kIbZoOqL!O<3%_X>O=}|jV$F|EHvN>0hFLw{o!i3iVn{k}AKA-C z`xU0}Vmqvckd38vwX}pz$I?!>qK{L4plPa=jeX}|f>7F<3bj=7EQJsej0wHXmH~q6 zf82x_?+B=x?~TnHy_e!LC84mKXH&qpUh12|YJ!daX#Cx;r4aRGqZ-1VQU(mZXg@j# z(Lo3KdVBHDe97{74l|1pv7s9|!UL+DpFUuCe0_>SRMfE}|oh z^6l3trpv8|jO_yjTWeG;2zZ9M-TiP6AJ>VM1!%J8_qANqDN=mgDaV!!?s2(n!QInX zJ)Bs|Vg)gFy{0T~uRSUsAgLhMF3sX-u*Sj7z&TAQNu!o0O zFW_u>9SmadcEEmEvo5MXhwnGxQAWt*^&q`*#T`wYZw;Ny=^#RBrTNlC;JQ>mcRwwe z=deu~GVW-16y^lE-7KnHJn&;_cb^~w^n9ju-y;jU0oPTgh6gApcwb>n$Ty?A4PXBk zkuX2M+C3d28+zzt{)wIEh;H2&3$57pwqziJ{0%I`Fgaf?$l!wnEIwtKyb}I3aQ_X< zzue{94?Q_|g`e6tPzPlcXF@1*DBK5%PHmLZnP%b;>;v3t0Ao9Fjapd|1ktl!UsPWt zgUvrd+=9LbZD!TmVj%w7ZUGVc)tyW8g(#VM;mN-Q87=W&S%5moWsUbh2p6%xA?ImQ z+(8w;8-@WfV29p>QY6PS!FqcRVp-$&ArNbdGr77RP0imk0G!G5niPwRy-G^(ze0WQEbdktkm^_z zaRMGG?PeW=kw5CjpUvChD!#a{tO3Cdu2MHSNx%wF{}A-jn5u_Iu7K1eh$ST_^E1Xl z=)7x|1|zdz5A4(HSBCs#Me-YCz})B5rO4bYWIbkbHD2r_O8?Dn+;{{?N|+=CoJKR$ zXOo5B)2k2w!gwIYX5wNJB0BKDO*}1GKUTO3k{7BV?CXSooHs#b`4!eD)s^Xf z8Czj84C7AzSq8s%JbFL^R5x}0!D3;)zo7Mb=D9jGvu z=^A1i*jp5l#$`L&?ilZTb()~jFLcL*pC}2(%<`xzfrPflJPDbt*NV)JeTwFKDnXr! zAx&l=PBALfrbwBc&zuVPnL7ZYQGUy%&-H^fsy3SbwM=UiUX0n9aG--J@PpiHhqr*2 z{)>zhFpF6$yQ4b7ar@TUq(rEQ5}HSSw5N+&Px0faI>69{D@Y4UM9}w(G&{9Vg`$`C z6;&eVJB(Tmk$6xhn3h+NmZ6;(KS_ueT@DqCE;n?he;IA@wgj4_3-`>oNOaj^VX`w~ zQ-xB^TGVq!ZRC>b9%X5r@zM&Owgk+B>>kNQ*J|XN&+nMZWtGUF7tLT zdk-`lv}fYd$-vr2Qo>{jC-e`^eH{ODNn@G#i%D)8LEVHZRtf zAb-=?}HZa!fRh3!_$# z*us|=Z(f$?B`bTwqg$p}c{Q)@UQSi(aRE&6uE2NbajN?Qk5njf$m%a7%v2N>S6Vk0 z3Y}Y?k4GdZVoKIW?3sPP12RuHNb}7q=UI>8hE;;cUOzt1 z4-K7-AdX~W%O#VC>WCQdj2?7BCw>U2VPg!>?A zKs7HOVEAPQt5^y?SGtu)!qAeXLWdCfdhbOA;2(^y|MQ0|4oXP^;H^Oo{;J4*3Hj2&Zt1S3n^e zwP6JaG+|nf^c~m_Rkq<$Et9sWddEwG<8bosPSl()+wGU}FGjT#jwhlZepON3;B*7>DH#EJLna{Hp#a%wHFH=r$wWAK+81u`CUnPFWa22!j86 za$@I+ss&NOS4IK*{#)jODW8+S4(wa6*V9N$il&hdCIROGw^^;Hej^-$geVMvJeize zq7hEmkos>|kGYZ&YtbZvKtZz8shJV+E8p3P$MMLrKfXFjJt>M$8=yK#bNVnJF#Ig7 zWhgVly$fZxrsMSy73-Q#_I6lY`@FJ)PJWLe5yewQ!gfv?R6NzQ5cAr7Twcoyr5dDb zH2uxSYs*ev7h1{|D5xOq!05kw@xMsBr|?X^Ze94XZ5th{W81dVv28uEZQDu5w#|-h z+fH`B|Fz!reFtk_TXoIDs-t;OHGgA_yO_Gtn=$Eg@}?Xi+mlg@hS@>ZCDbk8Jcriu zkuw!W$>&4J!DI@VQXeV7iNaVFBRsfs|MS>_$2(2+ExIZqREeBtw`pN%*NThM6EuDy z2iw8x`G(J<-`1tRf^-mOx2j8AE%a!-WK(Y>g~gmc4aw3xa!GXeLvhVAeY__ldeA(w z?I$;4txI8GornO_^^xjxjiK3W;YmL2u=mz;_72;q9Ydzx zkO?GN6j^Z3{27bw`*Hr$nOvWz>dMQPt$n!Yf*ndTu|n|EZ`}mQlSwU|-Nsic-1>Qc zUS}vy%4Y|S`#3I0Tp2sA)>0Sx8qI%-N}97*!g(wp4Ys?Lk>+@uI^ z@9*tjN;RvWRDjR8Q-ojec^c?l`yI|m0D(r)Vo&;9V-F<%s+c7|McuSO{oiPvmscjj zfyY%fB9{3yEsd`ZVQZzf!63;+Ty@v8=6gU~t5AvGfd4w`Si3;{`=~<&QsBB=Ja|Q- z>lp7wuvnk`^9*KgRqt|o2%z#M#6AM4Qm@I-8PTr+;<5W$=g_}$d_vPWR6aB!CLv(bD@r$a05f@GvKU~Cv7z3Y zXz;n2gLmh2_R#}st*f*0krsG{6~zFAng};vloG)B^alMX>NM#w_=~jvkW~C`;m0)p z@l6wdaQp`Y?q#dfix!}NxO+Y0-4+i3ll!9d{YwJ?(|WNcq#;DJ`LMnrM0gM6<8-i) z1aNb0TXgfMB)jp1s{B%aiCAkM)OK{6Wtuxx=a~p=_N*lp#`EJv7?XJhZch}{XM}>w z1vcD;BniY8=-BUhVVengB~%_&N?{yi>&p!umYx3?;l8Pn6_CsG`be@}l_%-3Ed-B~ zz$+QT7qTo33-D>JA2+P4e=m)_W+PVTTUXRQ3lspkS}1Nf4+v4eu!HYRmL%O&eXq; zb5E?bV*DsyppVI(L_kaad1ynqZ&o7h3A8Zy9#m55O$i>N@(9t|M)fndBOE93S&-{u z1Az-BLB9GVP_~*dsl+aXV)5W(=M&y-EmTdNj+L60kG44y8|}1SxbhRm>`A?qP?lPw zR1qN-Iz5T$SGE6udq$3AViq;BRAzV8t%8%+U?pBoVDKk3Zm1(waZ1a)jUv~k_Xh3? zjX(uHYN>5R{=n}OJpF?zIOx+%O*%T43mxc{q42xp3wX&pgKfuOzID8@FwPgU8D3yu z9eaglu3+}=F`U!jJiJus&|4yzjLxDTnm>&L&@;pV?}7w4iv|KRZxw|ZoQh>cr=MZTg0^v`?zMBA8c zir!Gxt5IFj&EJ6nCzv!(za2HIt=^FM#klLa)!!GJ?PNN!px`^utu?Y|wW=RKVC5go zf$Z@3!~w33l{s(!$w!SuiKX2@TEwhrs=;KGrIWm$8B>Uxakg`g44?7BMBEOo?d&B< zBVv|ON<^99R#0A(=f@-uW@qOt2R;fSy^){10k2+g=R4L5nut-v16nVvrP`fDOPH-c zFy@=aBuVKCni;w8K5H5)p&EJkR{4kS>rYT4h>LIeQxH@cjZa<%vIGYZiOn z<)$s>VFX!%dT~K(<(A)iw60(0SGp&fkZIHzxAbdj39x7t33F9+c800L@Ap9yD;iGR z>!r-f4A;00)KyoEhF4y@oT|K*p4LIsw(QAMd6ttsNmqyHpV@cQcfVy*8y_NUlcBM`C(Uq?$IsUS^mmRRAT^L(1KSw#f48F!@jcHPVdl;y4pM+tz-cSS&l-XPc` zed&Cxeyq<#qxI8m(OjPBWZUzxE4Xj!!-Y~skGZnbXG^E|fXE_CU^nG)knG#@Lz^QI zAzW1?b2a8EX9&n<(qvw}>y8P3-gKsN(n*UpH*fJ9^LP@^f_r?UIEMxf2f%cdHb$QE zk2*j>Q5s3AXiEI36yD|QGFdB=IBJaKr1LKorI&rh@;6A3_*!T>J*+1v?)VtaLQ%cB zh?_yNZg>+9uZEy()UEbZ88ATBGAVon>R3m71fqq50)dDA1|-VT#_u`q-dZs;uPWcr zw+;;*cjotg?Il|)o8J2PO-$$D?7yoY9=q0RUcaEq`tqDK3w?X~6VJdn-_$D$O^Q(#~2Z;!tNFpmaYY~e@! zs-O0thSQFXZ9@Yvt(=hG#PA{YWNBQ_*Rs%S>(i1<)i<&6W5rfQ3{^szQEQQiw)tZ> zI1b`^Eap-96vr|%ADCAwROey>%8AiMu!d0KIw8M7YfoO8wGB$w+^z|9k=(+Zq^#$9 z4K&>UA~urytEv%9!*&YLDW-I_Vk0L%=aURp_FeRk40RfHvv#DPt4~Ef{%k7e))c+f znJ&b95p9RXc7MJk98rD=W>jxP{}~ig&ftf+M_urTS|387k6|96Q&EBlkzAPHl*U)4;a3L6F|ABMS{1II7mLRdKJ zR-2UcOh=eh0Zz|z-w8p}D3^ot#*O~?oqupu#tUkTdE1j-xSKHSd;K- zZuj!LWm6k{5A+?p5_DG#!=5Ckk?=^lh%suhzrn$vw2dWoM*nom`wGdEqe9wmEAGm! zcCrw`zxS7M$eMPo&2>J~4+h!KR;%CUv|@7%DFYnz-@?Dqbnh3zmc*ITCr{pvgE)hF zKa09>Z)`EBRWqeUWSYS4w{%O)-oP3XJ)V?&PYJU4+7|+a^;UKoUEUsGa;hE1pT2|w zQ0NBI)olz*zt&i1MDtQ8-v55fz2TEirhf3Sm!w+E$S=VYpkpHK36mH!kR3D!K~TR} zsC`Kpl{~+u>-ez?sx1Ca&+`%EdpgyK`=(4)#mqJGWT~kDsc<@5HhfOZXI|^@P?ZJI zFVK?uMd}6te?#g3h7zmFOm6bCT@Bbd#_xlU2sX=TzmRA<&kQ&b4!$y_4nJ}aH%r=W2Gi%V`PAIr&z-`VGHey)<^^*4;39CbfVO{$zwV_b)G zXfx_Fz&5D-|Io1pulbM;?G)sg1*5{On!?+A{B&-wHR@O82H9EC`qRR(d@xDmZr`WWQC#K9%>)6R(Yu`P1T8970+UovtC|Ls}9^ zzguswTL>h4gQNCX;XqpEIiGMj;}`7)HqBD(s0gl=A%CsGKF!UN&5FOJv7fOAXtBs)9Gn*h@(0)^0PCb1(8Xgy!2wr8dIo zTl&WyD4DhKlrBL&-&#Q1Y3uNZfYI4@+hrcTcBM+2*bs+->TbjDs6khf_S5KUYfH20 zwW;C22Jw>mDUY-y-hX`7K)!V$;ItAV%#;PLC`G8;!3_SjT+H2nEv&L>@qWlS`T@B% zq7S<@9&g59+q1w>%4nxdIZDzZCqtCXc(1q)1)im5`8P5T_h}A(a(Y?wuK2jBeR4T7 z=AIm;hy>tH6@_%^_8Bi|d@LXz|B}0ba7)EEX?%vC&l7$0Qd<=f6_7j_X zzX@3b8R474anCiQzX8{bH(yaHL5p7-_IVIptfIra$DIfJ)bvFvF+0HSBOMB%O4;>V zv??UTZ*uM+u9y!Km*n!6x`r&`(y*gup#dnu<}4+k!rY=9DRJPDaY~-De;~|dC>XUj z!i%&-HlUat%7-$XeWiv+V;XURC=zz*?<@60{r35EES0|r)(WAB<<@x09hNcUf!}N1 z$q++0s}ka{Ha+0y*IVJ5wM(?DFn-2aeam2_oN<)Fes}_c+JTP(`|IFlh;qDsBF|PX zhDy1t4(`{0(Vv5~VGF&$=~AILUytbtV5~9xbD8r*(ht0a)Fu;BF;{@pG9bBY^ps_~ z?9K%m2UycwjFFzVvx%vPi1M-rwWNv7I|;> zdwGRVn1YvUF=&n~qLv0vW`7X>ofS*VK!hhALYY@Fcb5EJ3tnQiZI zFM8A=sb+-tDXBl+f_KN7H7DU6IL}oaM2uKVVnFXxfW--qOo^;wzs)|f3s;?a7VPYvU>C}T?Bd*JD^dK>yb9KLqhR66;tcf%W z@RJP-z|whl<-cr$N*ZeamZ;gW^dYv4OAqLUWM)BaCmjqrPtN7)G{IWmg~pz5fUr9@ zS9DrELJ)qT44vaFW$U!nkGQibY*W3VwvC>7;`7f&w4f2s_c1Zo!*QhS(w zh74wC;V-W>YGlL53HQ_)*V(!(oR-w{Z2fp{G+K;e* zY&!a!F4%2dr!8f!$vF6y-8nhiPr~(w|H!~74o8?PSqPNbmU)(IZwd&8`YagG)qSi$1t!07 zH=h5=!!>HjT!qOskqM4fzT$Rrwi+q9-EpT^B6882G<5Rw)&T8srQW@e3Z04-S$W}D z1;XQ=#(Av!z+Xed8gFeP-*{{t(51I!pMJKN2x_X^ZmG8J)zJOhiS>E^k+uzS zy_zO1l?c{z2qCSQ`Y;iB=?<=0$=1c%EzHOSfFvu}_HV*rJU_(Ufa7fpKw9ytD^*ukxZ#>e=%YW+ux`-E_743liW@(? z1s_1QJ{0N?sI(zoEpE)I=wrL4WK2;O3x6>-z}a@eIN&rj$dHe!+(mS$YYKA9$vxv) zbQpQ3tDY}-0-StYvM|S`{NvH6UGtX0R+3Hs0Ld9Wg&Uf!}S`g=i;Y-?=Q-v_n^KAMG`^JB-W)TM7 zNVBQ=<4YTAS6#=A z*a{dCULZXe-tKs7d3NeJRB{+thEZFZ^oq)xLQS2Ek%+d%$OiXDA&xGKfMoSgJc}c( z-xFTXWk$KT{N~9rkUTinQD@9(x?A0!s|ip)Kg88ud-QX__3@#(z77 zz8-5gptg?a)iEHkP0z5HvbgG`LI)<~lz=N!AZd;#?|Mwla#jR&yYPEy@m4fpA~9L? zr*hUh=qTCS9Du@QCI{(+GF3dZ_v2UkbYDnH?~42=HiQ*I4EK z;Qzh2uOeId27JB3#U0Z+=8%(FtmEMHK#f5ld00>rcwh2%;B;m(+yx~Y-z zd5w#5OQr%puH3iC);GLPczvh(iaEu9Z^V6=)xY<1`e()iv*qUO+n9gHGP$vZ=6ZMJ z!A7U()$oalpbxWrPQAjC1n=!<<4^ApkNfn`M!H8VeYN1PO|b3)`KJPXwV-cp`L93R zXL-MF$_a^U)`kDx%YMo7{qx()+})dETP?wNlOwcWoRBzn-5bz6kjrs?md8+z_>|+j zMictfDW6qCNZhk7AiPWcR)u@kRCs_nljZZMrt^9=e@y4P_Si3`wbH^DgQoa~LL5q|r4)QKXsV`}%Dq`203;p#iE-)?Ud!2LA!}gJxZ({Q4jey!jdoZp{Ig8my zoLaQXg=!(N%Ju?0{*b;s-o0#-1fG1s1($+@)JC0%nt@`3GMVGmJ*|s2sxIeqUHRjX zE8CdFkH-H_dqV%n>&x9lxwFV*;z?U%Ip+okAhDwOjEz7d9HvDbf(h%wX(=AR!u-qc zJ#=*t8C|6>;(9`Jjr>OF_6RZrCGe;~(&Jl;m0y_{fu0AV=tpj?$J~!7M7EKBj-<}p zAuRZ|MUmNc_FjA#D=HvOJn`hJ0;`)CR2-sQk`W0)6VLu>FbsV`<;*77Uk|YI`}{#; z;*mFxmPs3-vR`oo^q8dl4_z447#9N=H~hwa3?jr6;f>ShRqO^QA7`0u&FZ#hRis(O2XnZbMJlMTpC8~8fwmdh8 z81M-}7k8V?6b`y0n%#P8U1qw&W$~GKEz>Vs5>p#R7&efmRqs_s#jlyaeXCB=?fr2D zZOc?RCbz=6h~c-TIM7nFMz+bO`?l3LTth!9ml z7?vXjYoL@9;&wQ|sb_4+WTX}!($6WKpIG5nXA+vg$~3jB4bRd;U0in1LuJ?A*N48D z`2Y2h9K#`UTI!9Yn10}AMXS3{jgWbVKVc)t7w^wGph9^i_z&j9 zpT^ZCD)dD^ls~mh_gru^xKeNxcGBa*eQO00~3saZHEeP-U zrCb|Y4q1sC5jJ73l7M<+1j=henb!QHfK@`hJr5JIiqtkGf6M}^o?CfLK*sx z)9^6?tR8*dv$42ub%GRM6^pah)&}Y{UxYi;b5^h``oUw2!JS6=>FuT`A#@G1k(=LI zeS>S1Gk;@CcfXoP-B_$kRXuw<^*Q^4+>k}UaHN)FwJ$<)K!5a0ug)}Ti3P#B1c%W1MEEO1NiU4ttsCS|jMr=J>}fo1B}XYHLnr|c_e-~*LtFS9}Tdgddl z$#2&Q18|S>dgeK{O?B3}KdtvL3wH5~M$Gvx=7gsI)}Ik394{|)w(%ZsnRduG7+WbQ zcKn4Mi^^GD(vycMl$GyvWqO{58c`|JLW5-_%Grj$8Mp7<%L%_moBo8<4Y*w?Uxt6O zVMG;ErRk=(#2+RFMr6UjufXD_$lBWcmg^T(^HKVhL2EgAQbYroi@cjx;m7zmV`(bJ z!k%FG-C{ofBlBLj6NP&wpdH9+^TjLs6BNp8F0%X&2ZggF;cMj78OVi3n1c+BVi{dNOtr05yYu|9 zr_)Ig#l!l$2U8%f7kj)AXW%4{$k1iddA+rMs~B4Czl^o zZ)J2fec*R53q-f`${*LK7Yxsj8bj`S9LEXQHHZ~lu@Lc*Vdiqtuzr~Qu$IvZ>-W2j z)|nRUdU!vKYkfIUc(qAtM!s0sxV^nv1w;S_^zetAw8Xuf*-f5>1V!m_Cm$p6)S?=~ zv0xsd|BSpqvPLeFU^Xllp1lu!w(mw~OT1+FTu@Z!ZLW6a$K^uLZ37lC4nu*)bi26- z0RrVn9Iq5wYR2Gy2lK6`f$MZ_*W~%I=;Y}H3S3*5Q4VO=&&f`$C$Khe@Kt?fk-ZSn zv@tsC;;C8VsnGyY1NGk+0OC!cNcmWhQ>m})H|(KCV7CeJDwh$W5pZxPU~(MnNjwZx zMOp<~=o9mPW7nkor8MzBcbx@ zGEM?^>21I~jf`NvJhRmCH5 z-^#NBz0F`pD24N*8zzKAH4`u1zSM7=C_rMHGd|G4Coy(5pb+%+Q0bZ zU>B%%67!chta{uHI_e2!;?9gG2I_wMfQRP5oT>>VnqLIog(kt4>`jWKDR0R~52k%Y znLz9-N3?2w_Tq zD)19U!aT2EPUN-47IJZHk*q1ZSG3xr3(*_>X+A98Egh{L@0dxy9)!67?R3^sLXc$e z{u3jvtE(Q(sD;Xhk*Qat*AN4Bl1oGLVqv&UI)$8$87K|S;@Hfwd*Zb4Y)5QK732ih zAz_E`c4B6d_l3l7=GtGDK|s!LJ}!O~?*{UD;|t88_$k9;tAbMdf>VF&Gt!}^hjFp9 zl*^^n(UbNiJDSpN1yb}x@2L4acR3c)!k|%95~E%IpdBo$DxtRE+o$c$p)(C@Ntae<*NzlL7)b~eu)##<$h~-n*%m>uo6n1EFURHog!7Wwa=XyG3Q z9SN1}2aqz5v;NE1zaCOSS=;7cCAgC!M$N5#BX8w+RSn7WW+>!#BAfr@d3(4M!^ zQ=nP%tyx|*?qN*3S??KkzCAM{gLWViu}maTNA1Tx$mGE08lMG%Ef&gRl=@OVLOClQ zveJT-$G)LAU%UxMF%2@W7aW4)0s2>7aoS8t%V6RL6aa9@T(^qc9UX>7>u+y&iHC6vW6o1ThRLF z5~=w;3Andh7{deMJHvf-abW%|Q%XolC^6?(C*1o3;z@>($gXJc_%(^rHXz(8SPqu^ z??=I7J14&ZcShhyV~T9lv>2epS!P}J$y106n4Kru2b`xGOI3qDHK6fFAjBd#($eX{ zZfKS-hHW>WY~M18kFw3)*R?$p4i)l>Ie0RHKUjd-Y&beWx6xs0cN*K2dK0mgkEh&4 z4zyr8@3kwecIOJ^eW+Unr2;;wfkWjSFf?Ws_ zkjG-5;*9%1?TAL z@$SC~B=|NL)Zd)y{^RTsU-;nqIhIe9F1uA1Q1;@t7jl)&BjPO?NzaTnF;e|U2QB^a z92gQD_UGTc3pQom%$K(ZZiXj2^#wh$IBMCR>w3gd!ew&OnM-OssSWZ@i*HZ<9sD(# z4hVIz1#+BKBVBzy@sC#gP=&6NY{a6{{zioz|e=3Wp`T?|~soNlevgiYPz z++7XY1xF`&sTF)>iVx*Gi6iy2cUT3azC?*7{b}}UNR#t$zt6m_2j&pasdJL^;~t5x z^Qs1KULyh;gk?^@(T+Ez&-c4>U7oIcghc(Iyqn@09!-NR$^$nsIDW}%DNo~{V8JV{ z#uq&JRKp@L#W{A;_2Hc@Ld**5!gHEfgtES;Cv#Ocp{b*T;)@^ob*sn7Z*hagMt5Yb zRIv(CHLq4ZbqV@0yhpDXDY{B6zC5P!(H5bf&HUIO@pt=_eZJSvAn|_Og_5|w(-0?} zi7n))M5LpEn(m8Wz~8pwp*fAS(3m*>;CtMs&?QCodr}@A!>Azr1t>kgsA@rX6I&!0 zp5;Sf-1MSiqo`;1K;mEi=#y8`)&dqI^WaSSCTG;a>F)+fGGpHgm`qw1_mfz{wklY6 zt38X@*j2?Rlx9M9!dZdLLR-aHb0lvLHpR+27g>%!c=C$YJ$pWq$3ahowv)ORypIFAZP zvN=+8k%mbX(Uz4>hyH{&6~Y@qB2<7iSj(G)2bHv_KO_19Y&9>YHePwD!ONZ1skMma z_NBuEWn^X+MnzW2v@&AFfZ38muGi|kW)h{jJv7=9#KRGyO^wa-Vq;ly_WTJw&QAaf zBD;N;nttk0_9|;TH&*#NPlzn#jCI(w^=KSMVW&Q|JJGyzZXeuXXk7EVfMDD_Z;s!h zg%9T03ulm#Z}UDJ@gSscv&CmlFVn@9O-5y}@J|X_A(^qkDZ;KY3(0cQr~FOL%#SitBpiHUD3lT(26TNm~gzwwvKw%P()QH=T8d~_dMdJblN_S zP$v*}wU|-o$QAtduWD;UKf_z`5x=)P{=eQe0$DdS4DLD5bkSC zKDFpHbpZH-iA2w{H8Qz1=fv?Ag29A|Ix&Qh2dGK5AIus;%g; zjoX*lNXJeeoRi}oDNHCPRxJJS1mY3&rgwDrgUdqSm(FA8_j-F_CVWAO`!tMG)%Okb zwaDdUKYgH$5UldK0qvnOgeWpp+}|ST-?5(gHu)f}CV>g2G|ql)d*$SJ z6bsRVA{O(y2FY3kE2bNzM&)59X2$KFA*MI_$>K)YSoyG8_VD939DkBxSP3*|jS2)Q(7EKkNK@=3YTm;BZi0+7l#c9=K+9kiI zNrTzY`6W^CESErT3d3t0zP&Ts^6jD89nXjL0C8oTp zym$9TC7Krv=L3C(-w8E*5;BMK8~H*1WbB)@t5lIsK6C#;HTpX1bVm7wcKDP%GBca@ z`iAOL+;gKGn_H;k{Z{&dp>IP2*?-}E+!M?>b5qJ6fcSyNAIy#z=l#~$?W1Ld80VVH zd4AT@_NaM(hW^=DdMWhz5zgU0{|haMPbl%MvG8~U`9Ah|frgN;Yv;w2 z-bX06KPGh4)JGW5ANxO1K%n`r1G@g=gz`WE=#%#my2Q5pTMM_{eL0=R0*E+e5xbQiWo$oRp@f|SY>o8SB*EFI8u0c&loFxq3dQABHdc*;_X;|o? z8jIH+>AiLf@z&80_6*VGw9Z;+Lf!-B_y_aH#<{lx!1$60?QwSp(=y(9ciR_S@Pufz z75InVT@4h2hD#YTb@GyW<=?oN7Tt6%I*`|u^az;>Xe43ZA@J^GbhZh#|g) z*jYKPH}nk3Ui{Z~&ws&iSNHsbrsQ>30Lra;2kSRedFE5?=qRw-i~<8i*C6cA)}rzj zZHjcNPv1@J7C(76gWMQsbN*wbB%&J6?zXjAa^9sdzb_?^D1^9&UcXw4)7{x0Z5T?x z8nsaoO+%tloo@r=kzj6lAsDf?yf%qcvbd&$y+N+Q-1;<{$iASv@&3Rqbx4$L5493< zVW-m6={x;gKl}Ddy#C7(vwvqdj3D%Bd|OaYg$LsHo~uQ1f>y)@EQ`cazgLb!4{4Pp zO*$R%VD*A|~QcVk8xHTi{LiHsfO9UZpDQ(|nO z;{ixqc&D7@&p*G#)HmBA7%Ca}&bVa1`ioO1Xmm{pLMEE46RG7eILX*Lx}?)%M$+33 z))#MiS5%Pe&xf{xn~@Go!T%+Ve;1~5xa0&V4P%O41pP;@a{T5^{j4-L#xMB%~h*ruS&0qkmT`%{}#65G}DkSU;bwA zXU}jEZ!F#8Rd%s3;d;|}j-2upmdlNSA-g`0%v7w^pgDKEfDolzObR&LE738IVI_)3 zZE^3ZdPbuoLYJ`r`#JGHRrjQBr-ABW#i<&R04NJQ%}X1+5o&kEBMH0?D8hH5@=9LW zuKZD+76Nyy7{=s8NJG@U(YjVpy=eq|bmrrOg@o`W;hL4c*KcyKcse_1GKzQfb(E>B z>XEc${jWo3PJ53nE9v9?%_RqLEvW6N;9@Zqfu~W3Nnw9f?#$9MfAxzSo|i3C8;xmY z`C3op_F>b)5}WbI* z_5qM8L~tKj;2gnFvlu*)NZfZ$ZdB;?j&qOvvjH^9BFD=!cgIMzn+F?YeZ#CoSsr6F zPs9k)mI<7iHW1=UsOy}K@B06pDsF)C5~*PqI7$W?6dUP32v7blU#ZgZ&Mkeh!6*v^ zGx?^s6W4}yfSN8qZUqh5xlDU-LyhZo*6&9j4wV%=9(hMt^6Y_2beY9@VI}O`+nW{9x+PqvgDHT%>Y&j^t^WwSW5yQiQg+GW=-t z?*ER+x~(?I8s}rrjFA>;p8FN+X$Bu8Z}zMliRlgm6Y+*_eqBxBs2EmUq!<+p*gk7-=qj7g~H3c=E4qgu4Tb zPBNQp?b=M}*k430-=bZpuNmc0cXZBdHw8yBi$SC6dD@=AT11R5!*XW@cRcf1*#nMv z+B1F@_mLn5l&bk-n%-oQThJ+V7!d*S^L8fS%Q=|xl_XQXu!q^d0fW~E-O6c(X_IaE_FlfQBY+)j7XO>Wg)Cc-lnqN?@Jh~52F$rmmXR+U+m&Ps54#oJ z?1Kf5sGk27L>>^=1g%kgMcxP19R6>Mq_zYX1ulzTWnV0Px1wT(9_BzrdhOcvrHm?` zOT(&^nfA-ubCvhYi+zn93TtD%?iZOYH2^3)pej0SlgL9VuMr8( zQhjozMx^(?UGBj`O!2T01iy8TdkLISneW=uLf_Lw1&9jX4B%2Ntg0STqkvj9i@n=I z30uw-^Qy+tUDKCqm#^!QPbAOVrguMJ}QdQ!jYh!Ls3Dyx{h1 zIRPmPH4vu9iBAyhX+eMTPZ8HT5l+>WeRoA9nwT_(?n=>~w}JWZth6f?vyBRrW`^ z))UUchn54XXYCFRI5sjO07aD%r1%$rkaem3o79p!_>QEPtXwQn2-%pZ#ov#>XXT5t5Bc-{3#n^QPOr}94Sop3z{QPx!c!eQ>iBF4dOc}{C;Y+B zO^P)L0YG-f|I)C;>32IKS<)VBgVHG0-9|^L04LoaJs-b0Wy%}3d*m6t_uAic!=%8o z!di#ZLR}w+CoWi`VG^PFfl>K~m1r+XUI_j$XJquZv&me_^{vE!MHIFjRxSvNQp3k+ z3}c0vz!H|I#Prd(?fC8TV%Rd)JL#<{1&I7D^IFx3KhhT?ySp<8G^a+N>J5yr0$IwD0Nzr-^Rt zE(=?T(@=VATq|*#?gspj93b;aAs9Ert9jJ*#WcotWiS9z!&&M?1eCvTi`EHlsGcLm zk%Uv0IpP4f(_vCDDCZim>L_oz*C? z`na9d{ImQEF2gfSL&1h2%n9yi)`t3zp@r+9IJp}&7%=PlXh1(DthM89xW&w zJ8kDPJ^b+0N7GPAzrC6oIOzD@Tzyq^7?v`g2p1$5f2bd=B7)%EZE#%Z8`r=q z#0|q@n}qOG1=wpIu=W-DLccF$6KrDaY{tCF5MWVj2^__UH&6!yTY*xU!vabcY_b+z zKizPFS4kd^FdiF-3E0c2yo|6c(g%f1p4XAHGWV(R!yJ!!v3RAB#3upWgNDGw^s&bnFfb42>rSc2Wa7NRaXCORBoxBrtywWzfSQsc4z z0nz}dXjvd0@B8|(eQyN9ly1&N8uXSvv6C8vzBCPjUq3+W z8*_ihHezqVBYMK9l<*Bk2^T>j!rfh_FE~XG zhPeWdjplW@u!!ZSU=hY6c>IfpN#A5JwSt8TJp38K;-t$(jAMC4wOMT^3yrRAu{hx_0KL?Oh5GE&vTFb^RFK5S3=HSLu_QHD>o*!txtga;7lohRCr8x&r|2>|5v}qW zl^yHR<*WhD$D%tM`dz}?^i*=*>Q-ffo!Obp#=l(Xunm%eVP-7xOH(m0-akjcl)b^i zYYFVnMly?H>~D?RAbDa^878grb{RB!6;#BPC)( z#r%ZC+fx5Jo?0Wr@j;{yWsoGC*b9zV`@(*Nvhe+ioDreXO^o_Hhig4gN(X-Dac|9|PHH%yx0;YyMM`jd%d z9v1hC27DzvEHHIw*v32Ae_}7~udJ#{{_voHXDf9-Gr-~h z|4jAgZxwisu)cW?TWY8EMlMqv4;4eidz?rR7+iKCYk&O?pVwGZ1ZKLm`IMm8emsMd z?0`6_LV_k1DAaT7Dp1P*hE`|c-#WjM%Y2Sdpvvnww;FWrOv=(4nk0%GVWh^~OmYbF zcHRx#u_$_L;QY+~uX$D1T9_~4lWw>wQsN!P3So5!6B8lfu3MLf;?#KpwuBlj3`#Ea zGzfT{g5xtV#aN87E^ZnBw#*my9S2t-Ebdei(%W~~#)0B}C0!ge&wYUS&#adQI{_%mz;J#6mKBG0Vv=BfZBdhG!?r^!uy=&4YedAHA=c@48Ig{D%}}o8Bgr#-#TmpZ74Vd!uO8 zI(IV_&+D%57baGOJpQ53s@DE(}ZsPHcBVS%44O9H<)M zeNyZ1@*L_GSP+_s<(OYyr%ZL6TVZhBWNvuOh?RX%Y*i#huLuP8;9w^cMayLvdC-^i z16G3CCL2jQvvVv3`wO=vtz9gjzv3jS-tL%%S`R+wnQz1=DoXK72vcKXh)zM+?gvR9MsQznMkP7&DV z2a}&En$Px8qg~qy{pm0wvTUIwwUjZ1ZTh)X)AApL8(IsF2P6ZlG5w#6%WN{Pi8UNK332Rwj%}%~j?<8;F1X=L&;u}!;!Fm<7^%X-n z6_?~3mM;+fa6~usPI|OTkiMU6qgD6Eck1Y}0=~-92i%ox|8?D=_g5ofa=?hWy1wGb z52lvD_33q9tVf%vM?pe8v!DH0^QEgFeC;HivE9pl83*grS0~^2o1V+=KNb~*Z5@7* z%K2=QjtjHH5bt+0iIKI*|bz9fNmKFnNDGFHB5bH8X;GW&Hr%g*65RW%2ya6DUP zTSTZGuQVXJ1aSx}N6m$+a}GiN|FHLs(ZXDQ4HiE4e}{W>4iow+Hw53Ytb0?ACIx;? z67yunXU@nqNu770T(#H+@SvtAgpbdrMnfZa2Xd}Ni1S6x;R6W zAmtyCoJxlxuR^mb*_gm9H0|WkqUu^|#w9a7fVW4#!)Dp@v#f75@_{E3wm$h?ij1hb zfV)cwijz4iV}Balm_*?Av$+$$aQ9}xcU}0Yld#F^v*!F=31Ay5xX$6tw%sD({UHEHtiqe#osaRA1Kt_ z5q`F-6Ou~2;(To~KK$K_HIc~Un4G2nzH9Q6ZB=2MF=sR#)vQ_ynz-?b4F$(i7nOF0zW`~M=%7b?jDjLUtWbhq#Vd)Bc z{toKA56|74m%nm$?Zr?IYp@r;{6@&h;?}k&4Ll;UEcT7}9WuRJ{|4MYvI_VSPD$Q)p+u;|Rm@iG*j>LMMxAA?Ma*VAs z=VwQojA^Qs;n2E7nwPm`N7-oxtGG>7@o;;77l2 zH>rr6wM0|>2_cN)bE*Fk@DC60RwX3=hdUEUJSPi^98^A>8aZ*zv0Y!)13MnDP5SEO zsm2faVlo!fx`Ic-z!Hga@m`OaS!eyj|Xq#iUP?Rml|%J zKSqZBKJdKG));}rf04Yyz`jXnA^&XMwU`kJM~m0;PH|E};%vDjGSIG;3bXK#=lWw0 zND6*|(48yHt=><6k>yV4Ts8pab6trN$l7kBL?z*P%&c};p>a#!-XlD)0X_>$0yV#b z!m&^Do4QSUh;{1Rm)Sk(RWwj$vrxWg5x@_wb;m! zM}gP~tw+>hyXmkR5orNAHM(auLniu6GVdb7RTQlc$X()FzSf6=EEoW)*a5>-KV;cg z>j2=?hFTU?cgw6zF9;x}7Y3i2z&DcAvCsEnixp&Ws!hlMZb3^P48?GqSV%Z6Uc+a- zut+OzRQ3$2ZLtrB@A-1p*-f`_WsJg!crK7f)%heC#;M&wYN51}RfkO~KeleK} zya?7X@aM6Coqq{7CibofSCk4LxcJ-)U)3kS%QjPhoJ<(b5C2m%S+~qZikgDGBEK7V z#V0`H2x&b!7&mX{Aw$D45Z9wrZ4rlnh7y{5kW!{|yO9uQ;L<`OVgiSLU2u;qvx3=G z4P4hV88w(V7SXe-qXQQlf-cCncIK)CqxZlZS_zSVk@!I>i1HR9ydSBJN-E2C$u_fV z2GR!QeRd>1_rLdn^aX|SuS*p7g70`X&J1ck2L6q3I{kZ4K;zT3x6TfL-Fa3V3_C{i z{}v@)a{Qkklnu@TJ)47hukuxt=zZkDC$oq}oqo%D!7GH(u97Sbgh1WYQecDO>6%`uqP;c0#C#m?LrT#+^A**X@H8?5T4I?Njd`!5+r~x zMfb6JaEF>POV_Q4sRJ&@r@~zUk?w<+-6qdH&s)(@nYX)K?e7S>^XLbgdySSQYTp?v zg{QPpsS2q~fM+;iX2mhDDd`hiqn0DXNxQ~=+BnqdER(cVoe-066zF=kVj6$%f?6$6 zo$XrKDhcHRUZrx#)K7v^+bo8f;sngWpgEnpxrR1w+WR?}RzcY#j@@Qgn9-~aA{$w9 z@0z(^m?)Npq2gI?6k`+cwT=pV`C54sawGiQU)PV&ACAbselr0Vtyy@;^sOI9aA}~5 znD(q&FwaFd{GX~H(bCo%xK5kA$3ALRJGI`-D9Xr*;YhnINDiD-xncxSgscz+l8$7= z`NPMxfOox1*ki%e($!dLe4RmrGc1Ujv~K~eI7D&1eUb=|T;`&^#?q;wWYBSoi2h{CXgBNTgGlaBwf|xg;vP=C&|ILGf~qn~!3`PAZ+d=^E^U`1SgX9}7PSFb zS`98^Kr*~ZG@1PT!$``@wVshygf39wf^ze%`{E?4WmY%vO4@Q~f^Zv(*x~bNi^`Ib zJoW*5&FD|tr;MNL?5>#?bGoRf6jRra_#glm7c>@$rJw@{S*)SJ>_tW&)N(b2gryRF zBtiqoTBQs=h*B-lK;b3@4GE3QoDSneH!-98^wb!D}B-ZoY9+24F;r^3! z&NYkvV4ZfsKV|FYn#)4qW^*5$Pj$hhn6vI`Y3%A=|6K7+I49!Tmv=gM@QCsTp)T`l zU4?W}H6I||LXJw4pz9p(ffHb03z|1vLGwZBn35$J+T%&SIG7eELd$)T_xIl11G5y? zy$-tdIR>Q(NHI~9{5f`rzLx~r9JKk)mX?nS{ty|q2Zns4!5qQee#c&G9Am$}IQy$k zuj5Hz(35u@xYmxrNloo{7irp4hXKQ`@zmK*hZ&6~90!1uaUu-~@X@K-fyT&@!#N&- zy9SL=WNl5XJB7sIYg7eK1Q=)4tF(!h;rJNU8(8AArbe7V=NWu5h2i5wCL3blN3R+6 z_DyLrno!pv%yX#dyWNXe*AsSXxO@9ah3lKtN@tfH3Rz|kjKB|BCwU1e!a|Zf&8q2O zk0rq$Wy=9O!O(ZJTgsiAl5|?~MbxdbN#WvH!U!aXh0PVM zV9xuY#NBR+{=02J*VF=&-S-EFV}0V;pB2^HH@hiRGCe{1rQ0u7RZbT>9jsdC86ncK zH-RoZL92HdbTt?p1Ibd!#WbaKS$5dYD|($&UCdF@Q^h=M7}x1qsBwF1N$IXDgpqBA zTx%>+e~B~{-LVezpqmS?$TTE%5Jv*fBwNDB$6N$f<)ED z*a(2gb8mzd21t{cwHvL$sSuDF39n3tsYuFD*%a0V#Dnx1axj-Fx}oxLrLN8-Tmv7O zk}7jZM^XZA0x|PIXXS0bRmgq#bmrj{%$VUlpVi326JZUE$RP$P71H^v=!XA)@>r@H zx__toHh4=cqjdngfE68}3Ph(VxJT}CMhXm9&=FVGT6;xoH~~|YbMumr5q^wSffYB~CD>*(p1nlj z7b6N&N`Wj=AMzl1Drj>eufpGkh?^6@>^v=#w$GNvNK66rFb71Y0EY2ZKe4b9hVM^%%6jgSR zk5NYJGgT%t&1hW$1^Q6|1WtR<1fNib?~MV*M?;}_QvgyhT;M39vUgI_J~wQ>@pyK$ z+4ZyhE-atfRVZ1TajU#i4nOI9q?6@*a8X5o|LLsUu|`G`nMU zyi>kRiV~PYS(BF2L{`NO{{bw_ZgEi2FldsckmzFwO8nYd;Z>dW-qdiZ>T}o_p;I^? zZQ-=3esgVH2U3qaB&(ZbvpZB+Di)d6BYQyBFvBMwlgVV0pP}{v5ia9zg;_Rwp3TzT zZI;AjTn}T~q<{j=qm)V+cQlWNwFN#QL9P?IE(@^CO_O*K)gSRbBWZ8-*mOdId5ge~*Fn0w-NMh@;s z)7De1hh!wOv4WM?q@rw^oVUj3dt*zNPyc`MgymBR6TIDS_3{JBi!m=j&U~X9vlx5lECF!Zw2mu+P`sLY8laH=$Zc>P%70iEWvQEEy zOhPhIL~q{vzoM^iP2#BYJ%8!17S+M%YAPO%1tibYexb^-kFA7jVB)*QDJu!0>;O+Ivdc zeeIyGIJ{KCDJZR>@8`{GGE)gC8UZ@z@AKG@Vl*o*SskMf+qzO+HInc6ZEMeq!6K33 zgTQzep>d94wTIOc&`>>^y}TQo@!F|VZr*trwx1vPG97jgE;f?ud`vx1W0sTBPyQ?v zSFC*8DGl^do!{lRIfgw4&HjvrcO@{c>SAK1G9giaZyMT~6^59U&<(5Vh#aZ9q{w4( zU9{Jqmn0an^2~r=67HWW{|ZU{ds--hKMaGAaud!<_v%(m!3C`|93}^I_X}+8cjtK| z=WlGsznwb|1wl^q4w_5IVK6E~ss($$%iN5gi*J?MT{*2mNrNsjC4-LtSIQph+U3pw z#P9HKXfHF8tbi%JFraeL%!i{PVpf{RYz$OYsR!>ExxN?_aECKT22{@Zxd21i)nR^f zpVL{!igB6#h2Dq|3}++qWGMny*q9wGEu3SEn;C5Iutp)9F?mI+_M#uOn;b27#UVKg zb$+&0PGyIMfVGQw4T6?du=uRb?m?;_UGMjAD*JXWC1iixA%lTJp(Lvz5gzI%7xGpL zUaz=w%mXD=HGS#dR&^`+eB?N*UA2pF23V?xCn?SW#asuZqsHw{24sxOjEUiRKl5fT zWI7TKcLjkG$*VhVB`it;+28YN*1IMThAetLm^G;vYl54OSeLrh+H%_-dV};6pF!o8 zi)kqRg2^D*U|UJ~*6>~@7P!*M_l?=VtX8PVgP|Gt<+3N}i2NKaaB)JoRQ-SyIYef) z9pc88@6%4=B;fv)*6rfoK-tQYr084Z%VB@hxgW1#I3yz{-EpFg z>DlXrFitoH72B6Ic8m)Jv(|?U36FgVB+(Po!kvGR{F?f|JY~Ux z05dBpGs8N8A^_ZXpaFr~*3^TT(ILL^%V78fq%TOH-W8H(gGQXiC7wsfN3}N6g!~<^ vU|&W|m8VdMBENJ5G&3I=SDwrcpJTqIyK@g+tAenable ConnectorArchicad Assets\icon.ico + true diff --git a/ConnectorArchicad/ConnectorArchicad/Info.plist b/ConnectorArchicad/ConnectorArchicad/Info.plist new file mode 100644 index 0000000000..e42154e574 --- /dev/null +++ b/ConnectorArchicad/ConnectorArchicad/Info.plist @@ -0,0 +1,39 @@ + + + + + CFBundleName + acconnector + CFBundleDisplayName + Archicad Connector + CFBundleIdentifier + systems.speckle.acconnector + CFBundleVersion + 2.0.0 + CFBundlePackageType + APPL + CFBundleSignature + spkl + CFBundleExecutable + ConnectorArchicad + CFBundleIconFile + icon-mac.icns + CFBundleShortVersionString + 2.0.0 + CFBundleURLTypes + + + CFBundleURLName + Speckle + CFBundleURLSchemes + + speckle + + + + NSPrincipalClass + NSApplication + NSHighResolutionCapable + + + \ No newline at end of file From f5868d3a8de11762ad2cb4421e604c6a673117ba Mon Sep 17 00:00:00 2001 From: KatKatKateryna <89912278+KatKatKateryna@users.noreply.github.com> Date: Fri, 14 Jun 2024 22:11:41 +0800 Subject: [PATCH 13/52] fetching branchName for FE2 URLs (#3487) * fetching branchName for FE2 URLs * Revert "fetching branchName for FE2 URLs" This reverts commit 331927b5e9fc9c7adf944490d6a23d40bcca1a6c. * move hack to async ResetApiClient --- .../Operations.VariableInputReceiveComponent.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Operations.VariableInputReceiveComponent.cs b/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Operations.VariableInputReceiveComponent.cs index ce1a78b883..0b2e02996c 100644 --- a/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Operations.VariableInputReceiveComponent.cs +++ b/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Operations.VariableInputReceiveComponent.cs @@ -547,6 +547,23 @@ public async Task ResetApiClient(StreamWrapper wrapper) try { account = wrapper?.GetAccount().Result; + + // hack for FE2 + // should we also fetch this for FE1-accounts that are trying to access FE2 stream? Probably not + if (account.serverInfo.frontend2 is true) + { + Client client = new(account); + var streamObj = client.StreamGet(StreamWrapper.StreamId, 100).Result; + foreach (Branch branch in streamObj.branches.items) + { + if (branch.id == StreamWrapper.BranchName) + { + StreamWrapper.BranchName = branch.name; + break; + } + } + client.Dispose(); + } } catch (SpeckleException e) { From d151cf579aab9ebdd2943dbd8d6dec120e8f4af4 Mon Sep 17 00:00:00 2001 From: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com> Date: Tue, 18 Jun 2024 12:56:33 +0100 Subject: [PATCH 14/52] Added support for IReadOnlyList deserialization (#3520) * Added Unit tests for current state * Fixed Tests * Fixed comment * Fixed other issues * removed a couple types that weren't needed --- .../SerializationUtilities/ValueConverter.cs | 25 +++++- .../SerializerBreakingChanges.cs | 1 - .../SerializerNonBreakingChanges.cs | 46 +++++++++++ .../ModelPropertySupportedTypes.cs | 81 +++++++++++++++++++ 4 files changed, 149 insertions(+), 4 deletions(-) create mode 100644 Objects/Tests/Objects.Tests.Unit/ModelPropertySupportedTypes.cs diff --git a/Core/Core/Serialisation/SerializationUtilities/ValueConverter.cs b/Core/Core/Serialisation/SerializationUtilities/ValueConverter.cs index f648f3274f..70246a1363 100644 --- a/Core/Core/Serialisation/SerializationUtilities/ValueConverter.cs +++ b/Core/Core/Serialisation/SerializationUtilities/ValueConverter.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Diagnostics.Contracts; using System.DoubleNumerics; using System.Drawing; using System.Globalization; @@ -157,16 +158,17 @@ public static bool ConvertValue(Type type, object? value, out object? convertedV #endregion } - // Handle List - if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>)) + // Handle List<>, IList<>, and IReadOnlyList<> + if (type.IsGenericType && IsGenericList(type)) { if (value is not List valueList) { return false; } + var targetType = typeof(List<>).MakeGenericType(type.GenericTypeArguments); Type listElementType = type.GenericTypeArguments[0]; - IList ret = Activator.CreateInstance(type, valueList.Count) as IList; + IList ret = Activator.CreateInstance(targetType, valueList.Count) as IList; foreach (object inputListElement in valueList) { if (!ConvertValue(listElementType, inputListElement, out object? convertedListElement)) @@ -311,4 +313,21 @@ public static bool ConvertValue(Type type, object? value, out object? convertedV return false; } + + /// + /// Tests that the given is assignable from a generic type def + /// + /// + /// + [Pure] + private static bool IsGenericList(Type type) + { + if (!type.IsGenericType) + { + return false; + } + + Type typeDef = type.GetGenericTypeDefinition(); + return typeDef == typeof(List<>) || typeDef == typeof(IList<>) || typeDef == typeof(IReadOnlyList<>); + } } diff --git a/Core/Tests/Speckle.Core.Tests.Unit/Serialisation/SerializerBreakingChanges.cs b/Core/Tests/Speckle.Core.Tests.Unit/Serialisation/SerializerBreakingChanges.cs index 163d7abd21..0fc9549fd2 100644 --- a/Core/Tests/Speckle.Core.Tests.Unit/Serialisation/SerializerBreakingChanges.cs +++ b/Core/Tests/Speckle.Core.Tests.Unit/Serialisation/SerializerBreakingChanges.cs @@ -1,5 +1,4 @@ using NUnit.Framework; -using Speckle.Core.Logging; using Speckle.Core.Serialisation; namespace Speckle.Core.Tests.Unit.Serialisation; diff --git a/Core/Tests/Speckle.Core.Tests.Unit/Serialisation/SerializerNonBreakingChanges.cs b/Core/Tests/Speckle.Core.Tests.Unit/Serialisation/SerializerNonBreakingChanges.cs index ef2c5e501e..30dbea5d06 100644 --- a/Core/Tests/Speckle.Core.Tests.Unit/Serialisation/SerializerNonBreakingChanges.cs +++ b/Core/Tests/Speckle.Core.Tests.Unit/Serialisation/SerializerNonBreakingChanges.cs @@ -87,6 +87,42 @@ public void ListToArray(double[] testCase) Assert.That(res.value, Is.EquivalentTo(testCase)); } + [Test, TestCaseSource(nameof(s_arrayTestCases))] + public void ListToIList(double[] testCase) + { + var from = new ListDoubleValueMock { value = testCase.ToList() }; + + var res = from.SerializeAsTAndDeserialize(); + Assert.That(res.value, Is.EquivalentTo(testCase)); + } + + [Test, TestCaseSource(nameof(s_arrayTestCases))] + public void ListToIReadOnlyList(double[] testCase) + { + var from = new ListDoubleValueMock { value = testCase.ToList() }; + + var res = from.SerializeAsTAndDeserialize(); + Assert.That(res.value, Is.EquivalentTo(testCase)); + } + + [Test, TestCaseSource(nameof(s_arrayTestCases))] + public void IListToList(double[] testCase) + { + var from = new IListDoubleValueMock { value = testCase.ToList() }; + + var res = from.SerializeAsTAndDeserialize(); + Assert.That(res.value, Is.EquivalentTo(testCase)); + } + + [Test, TestCaseSource(nameof(s_arrayTestCases))] + public void IReadOnlyListToList(double[] testCase) + { + var from = new IReadOnlyListDoubleValueMock { value = testCase.ToList() }; + + var res = from.SerializeAsTAndDeserialize(); + Assert.That(res.value, Is.EquivalentTo(testCase)); + } + [Test, TestCaseSource(nameof(MyEnums))] public void EnumToInt(MyEnum testCase) { @@ -171,6 +207,16 @@ public class ListDoubleValueMock : SerializerMock public List value { get; set; } } +public class IListDoubleValueMock : SerializerMock +{ + public IList value { get; set; } +} + +public class IReadOnlyListDoubleValueMock : SerializerMock +{ + public IReadOnlyList value { get; set; } +} + public class ArrayDoubleValueMock : SerializerMock { public double[] value { get; set; } diff --git a/Objects/Tests/Objects.Tests.Unit/ModelPropertySupportedTypes.cs b/Objects/Tests/Objects.Tests.Unit/ModelPropertySupportedTypes.cs new file mode 100644 index 0000000000..342e0cad23 --- /dev/null +++ b/Objects/Tests/Objects.Tests.Unit/ModelPropertySupportedTypes.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using System.DoubleNumerics; +using System.Drawing; +using System.Linq; +using NUnit.Framework; +using Speckle.Core.Models; +using Speckle.Core.Serialisation; +using Speckle.Newtonsoft.Json; + +namespace Objects.Tests.Unit; + +/// +/// Tests that all Base object models in the kit have properties that are an allowed type +/// This test is not exhaustive, there are plenty of generic arg combinations that will pass this test, +/// but still not work / are not defined behaviour. This test will just catch many types that definitely won't work +/// +public class ModelPropertySupportedTypes +{ + /// + /// Set of types that we support in Base objects + /// If it's not in the list, or is commented out, it's not supported by our serializer! + /// + /// + /// If you're tempted to add to this list, please ensure both our serializer and deserializer support properties of this type + /// Check the + /// Check the + /// (or is an interface where all concrete types are supported) + /// You should also consider adding a test in SerializerNonBreakingChanges + /// + private readonly HashSet _allowedTypes = + new() + { + typeof(Boolean), + typeof(Byte), + typeof(UInt32), + typeof(UInt64), + typeof(Int16), + typeof(Int32), + typeof(Int64), + //typeof(Half), + typeof(Single), + typeof(Double), + typeof(Char), + typeof(string), + typeof(DateTime), + typeof(Guid), + typeof(Color), + typeof(List<>), + typeof(Nullable<>), + typeof(IList<>), + typeof(IReadOnlyList<>), + typeof(Dictionary<,>), + //typeof(IDictionary<,>), + //typeof(IReadOnlyDictionary<,>), + typeof(ICurve), + typeof(Object), + typeof(Matrix4x4), + }; + + [Test] + [TestCaseSource(typeof(GenericTests), nameof(GenericTests.AvailableTypesInKit))] + public void TestObjects(Type t) + { + var members = DynamicBase.GetInstanceMembers(t).Where(p => !p.IsDefined(typeof(JsonIgnoreAttribute), true)); + + foreach (var prop in members) + { + if (prop.PropertyType.IsAssignableTo(typeof(Base))) + continue; + if (prop.PropertyType.IsEnum) + continue; + if (prop.PropertyType.IsSZArray) + continue; + + Type propType = prop.PropertyType; + Type typeDef = propType.IsGenericType ? propType.GetGenericTypeDefinition() : propType; + Assert.That(_allowedTypes, Does.Contain(typeDef), $"{typeDef} was not in allowedTypes"); + } + } +} From c9e27dea254646b70d7f173111192b7818b4746b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?O=C4=9Fuzhan=20Koral?= <45078678+oguzhankoral@users.noreply.github.com> Date: Mon, 1 Jul 2024 11:52:48 +0300 Subject: [PATCH 15/52] CNX-9775 sending revit elements via dynamo (#3495) * POC: nullable cache * Remove commented out lines * fix: Formatting --------- Co-authored-by: Alan Rynne --- .../AllRevitCategories.cs | 124 +++++++++++------- .../ConverterRevitShared/ConversionUtils.cs | 76 ++++++----- .../ConverterRevitShared/ConverterRevit.cs | 3 +- .../PartialClasses/ConvertWall.cs | 36 +++-- .../RevitElementTypeUtils.cs | 6 + 5 files changed, 154 insertions(+), 91 deletions(-) diff --git a/Objects/Converters/ConverterRevit/ConverterRevitShared/AllRevitCategories.cs b/Objects/Converters/ConverterRevit/ConverterRevitShared/AllRevitCategories.cs index cb51ab0a0f..14edc8539f 100644 --- a/Objects/Converters/ConverterRevit/ConverterRevitShared/AllRevitCategories.cs +++ b/Objects/Converters/ConverterRevit/ConverterRevitShared/AllRevitCategories.cs @@ -36,27 +36,45 @@ public IRevitCategoryInfo GetRevitCategoryInfo(Base @base) return elementType; } - var matchingType = revitDocumentAggregateCache - .GetOrInitializeWithDefaultFactory() - .GetAllObjects() - .Where(catInfo => catInfo.ElementTypeType == typeof(T) && catInfo.BuiltInCategories.Count == 0) - .FirstOrDefault(); + IRevitCategoryInfo matchingType; + + if (revitDocumentAggregateCache is null) + { + matchingType = elementType; + } + else + { + matchingType = revitDocumentAggregateCache + .GetOrInitializeWithDefaultFactory() + .GetAllObjects() + .Where(catInfo => catInfo.ElementTypeType == typeof(T) && catInfo.BuiltInCategories.Count == 0) + .FirstOrDefault(); + } if (matchingType != null) { return matchingType; } - var categoryInfo = revitDocumentAggregateCache - .GetOrInitializeWithDefaultFactory() - .GetOrAdd( - typeof(T).Name, - () => - { - return new RevitCategoryInfo(typeof(T).Name, null, typeof(T), new List()); - }, - out _ - ); + IRevitCategoryInfo categoryInfo; + + if (revitDocumentAggregateCache is null) + { + categoryInfo = new RevitCategoryInfo(typeof(T).Name, null, typeof(T), new List()); + } + else + { + categoryInfo = revitDocumentAggregateCache + .GetOrInitializeWithDefaultFactory() + .GetOrAdd( + typeof(T).Name, + () => + { + return new RevitCategoryInfo(typeof(T).Name, null, typeof(T), new List()); + }, + out _ + ); + } return categoryInfo; } @@ -128,30 +146,26 @@ public IRevitCategoryInfo GetRevitCategoryInfo(string categoryName) } categoryName = CategoryNameFormatted(categoryName); - var revitCategoryInfoCache = revitDocumentAggregateCache.GetOrInitializeWithDefaultFactory(); - categoryInfo = revitCategoryInfoCache.TryGet(categoryName); - if (categoryInfo != null) + IRevitObjectCache revitCategoryInfoCache; + if (revitDocumentAggregateCache is not null) { - return categoryInfo; - } + revitCategoryInfoCache = revitDocumentAggregateCache.GetOrInitializeWithDefaultFactory(); - foreach (var info in revitCategoryInfoCache.GetAllObjects()) - { - if (categoryName.IndexOf(info.CategoryName, StringComparison.OrdinalIgnoreCase) >= 0) + categoryInfo = revitCategoryInfoCache.TryGet(categoryName); + if (categoryInfo != null) { - return info; + return categoryInfo; } - foreach (var alias in info.CategoryAliases) + else { - if (categoryName.IndexOf(alias, StringComparison.OrdinalIgnoreCase) >= 0) - { - return info; - } + return SHC.Undefined; } } - - return SHC.Undefined; + else + { + return SHC.Undefined; + } } #endregion @@ -173,29 +187,43 @@ public IRevitCategoryInfo GetRevitCategoryInfo(string categoryName) // pre 2.16 we're passing the "category" string else { - var revitCat = revitDocumentAggregateCache - .GetOrInitializeWithDefaultFactory() - .TryGet(unformattedCatName); - - if (revitCat == null) + if (revitDocumentAggregateCache is null) { return null; } + else + { + var revitCat = revitDocumentAggregateCache + .GetOrInitializeWithDefaultFactory() + .TryGet(unformattedCatName); - bic = Categories.GetBuiltInCategory(revitCat); - formattedName = CategoryNameFormatted(unformattedCatName); + if (revitCat == null) + { + return null; + } + + bic = Categories.GetBuiltInCategory(revitCat); + formattedName = CategoryNameFormatted(unformattedCatName); + } } - return revitDocumentAggregateCache - .GetOrInitializeWithDefaultFactory() - .GetOrAdd( - formattedName, - () => - { - return new RevitCategoryInfo(formattedName, null, null, new List { bic }); - }, - out _ - ); + if (revitDocumentAggregateCache is null) + { + return new RevitCategoryInfo(formattedName, null, null, new List { bic }); + } + else + { + return revitDocumentAggregateCache + .GetOrInitializeWithDefaultFactory() + .GetOrAdd( + formattedName, + () => + { + return new RevitCategoryInfo(formattedName, null, null, new List { bic }); + }, + out _ + ); + } } private static string CategoryNameFormatted(string name) diff --git a/Objects/Converters/ConverterRevit/ConverterRevitShared/ConversionUtils.cs b/Objects/Converters/ConverterRevit/ConverterRevitShared/ConversionUtils.cs index 5aae514f7a..307cca0e29 100644 --- a/Objects/Converters/ConverterRevit/ConverterRevitShared/ConversionUtils.cs +++ b/Objects/Converters/ConverterRevit/ConverterRevitShared/ConversionUtils.cs @@ -390,39 +390,46 @@ private Parameter ParameterToSpeckle( #else ForgeTypeId unitTypeId = null; #endif + ParameterToSpeckleData paramData; + + // Local function to create ParameterToSpeckleData + ParameterToSpeckleData CreateParamData() + { + var definition = rp.Definition; + var newParamData = new ParameterToSpeckleData() + { + Definition = definition, + InternalName = paramInternalName, + IsReadOnly = rp.IsReadOnly, + IsShared = rp.IsShared, + IsTypeParameter = isTypeParameter, + Name = definition.Name, + UnitType = definition.GetUnityTypeString(), + }; + if (rp.StorageType == StorageType.Double) + { + unitTypeId = rp.GetUnitTypeId(); + newParamData.UnitsSymbol = GetSymbolUnit(rp, definition, unitTypeId); + newParamData.ApplicationUnits = + unitsOverride != null ? UnitsToNative(unitsOverride).ToUniqueString() : unitTypeId.ToUniqueString(); + } + return newParamData; + } // The parameter definitions are cached using the ParameterToSpeckleData struct // This is done because in the case of type and instance parameter there is lots of redundant data that needs to be extracted from the Revit DB // Caching noticeably speeds up the send process // TODO : could add some generic getOrAdd overloads to avoid creating closures - var paramData = revitDocumentAggregateCache - .GetOrInitializeEmptyCacheOfType(out _) - .GetOrAdd( - paramInternalName, - () => - { - var definition = rp.Definition; - var newParamData = new ParameterToSpeckleData() - { - Definition = definition, - InternalName = paramInternalName, - IsReadOnly = rp.IsReadOnly, - IsShared = rp.IsShared, - IsTypeParameter = isTypeParameter, - Name = definition.Name, - UnitType = definition.GetUnityTypeString(), - }; - if (rp.StorageType == StorageType.Double) - { - unitTypeId = rp.GetUnitTypeId(); - newParamData.UnitsSymbol = GetSymbolUnit(rp, definition, unitTypeId); - newParamData.ApplicationUnits = - unitsOverride != null ? UnitsToNative(unitsOverride).ToUniqueString() : unitTypeId.ToUniqueString(); - } - return newParamData; - }, - out _ - ); + if (revitDocumentAggregateCache is null) + { + paramData = CreateParamData(); + } + else + { + paramData = revitDocumentAggregateCache + .GetOrInitializeEmptyCacheOfType(out _) + .GetOrAdd(paramInternalName, CreateParamData, out _); + } return paramData.GetParameterObjectWithValue(rp.GetValue(paramData.Definition, unitTypeId)); } @@ -450,9 +457,16 @@ ForgeTypeId unitTypeId return null; } - return revitDocumentAggregateCache - .GetOrInitializeEmptyCacheOfType(out _) - .GetOrAdd(unitTypeId.ToUniqueString(), () => unitTypeId.GetSymbol(), out _); + if (revitDocumentAggregateCache is null) + { + return unitTypeId.GetSymbol(); + } + else + { + return revitDocumentAggregateCache + .GetOrInitializeEmptyCacheOfType(out _) + .GetOrAdd(unitTypeId.ToUniqueString(), () => unitTypeId.GetSymbol(), out _); + } } /// diff --git a/Objects/Converters/ConverterRevit/ConverterRevitShared/ConverterRevit.cs b/Objects/Converters/ConverterRevit/ConverterRevitShared/ConverterRevit.cs index 337f32a2f4..4c9a38d3f4 100644 --- a/Objects/Converters/ConverterRevit/ConverterRevitShared/ConverterRevit.cs +++ b/Objects/Converters/ConverterRevit/ConverterRevitShared/ConverterRevit.cs @@ -1,3 +1,4 @@ +#nullable enable using System; using System.Collections.Generic; using System.Linq; @@ -109,7 +110,7 @@ public ConverterRevit() Report.Log($"Using converter: {Name} v{ver}"); } - private IRevitDocumentAggregateCache revitDocumentAggregateCache; + private IRevitDocumentAggregateCache? revitDocumentAggregateCache; private IConvertedObjectsCache receivedObjectsCache; private TransactionManager transactionManager; diff --git a/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertWall.cs b/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertWall.cs index ef493e7fa9..73033b0442 100644 --- a/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertWall.cs +++ b/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertWall.cs @@ -264,19 +264,33 @@ private IEnumerable GetSubsetOfElementsInView(BuiltInCategory categor return children; } - var allSubelementsInView = revitDocumentAggregateCache - .GetOrInitializeEmptyCacheOfType>(out _) - .GetOrAdd( - category.ToString(), - () => - { - using var filter = new ElementCategoryFilter(category); - using var collector = new FilteredElementCollector(Doc, ViewSpecificOptions.View.Id); + var allSubelementsInView = new HashSet(); + + if (revitDocumentAggregateCache is null) + { + var filter = new ElementCategoryFilter(category); + var collector = new FilteredElementCollector(Doc, ViewSpecificOptions.View.Id); - return new HashSet(collector.WhereElementIsNotElementType().WherePasses(filter).ToElementIds()); - }, - out _ + allSubelementsInView = new HashSet( + collector.WhereElementIsNotElementType().WherePasses(filter).ToElementIds() ); + } + else + { + allSubelementsInView = revitDocumentAggregateCache + .GetOrInitializeEmptyCacheOfType>(out _) + .GetOrAdd( + category.ToString(), + () => + { + using var filter = new ElementCategoryFilter(category); + using var collector = new FilteredElementCollector(Doc, ViewSpecificOptions.View.Id); + + return new HashSet(collector.WhereElementIsNotElementType().WherePasses(filter).ToElementIds()); + }, + out _ + ); + } return children.Where(allSubelementsInView.Contains); } diff --git a/Objects/Converters/ConverterRevit/ConverterRevitShared/RevitElementTypeUtils.cs b/Objects/Converters/ConverterRevit/ConverterRevitShared/RevitElementTypeUtils.cs index f6ce277023..43678de1c8 100644 --- a/Objects/Converters/ConverterRevit/ConverterRevitShared/RevitElementTypeUtils.cs +++ b/Objects/Converters/ConverterRevit/ConverterRevitShared/RevitElementTypeUtils.cs @@ -140,6 +140,12 @@ public void SetElementFamily(Base @base, string family) appObj.Update(logItem: $"Could not find valid incoming type for element of type \"{element.speckle_type}\""); } var typeInfo = AllCategories.GetRevitCategoryInfo(element); + + if (revitDocumentAggregateCache is null) + { + return default; + } + var types = revitDocumentAggregateCache .GetOrInitializeWithDefaultFactory>() .GetOrAddGroupOfTypes(typeInfo); From 2c6087cd8f74e941fb92c1ae11aa64e99839f471 Mon Sep 17 00:00:00 2001 From: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com> Date: Mon, 8 Jul 2024 16:44:05 +0100 Subject: [PATCH 16/52] feat(Core): [CNX-7561] - GraphQL Client functions for new FE2 API (#3155) * First pass for resource implementation * parking for now * API tests * Deprecated legacy API functions * subscriptions * Deprecation of old * Tests * Removed generic args from graphql exception types * Fixed tests * More tests * More tests passing * Project Invites now done! * Version tests * Fix warnings * fixed integration tests * polish * removed unused using statements * xml doc * removed commented out properties * removed todo comments * more pr comments resolved * server limits * int enum as int * Fixed accidental negation of execution success * subscriptions * csharpier * subscription queries * using directive --- .../Speckle.Automate.Sdk/AutomationContext.cs | 1 + .../Speckle.Automate.Sdk.csproj | 2 +- Core/Core/Api/Exceptions.cs | 97 +++-- .../Client.ObsoleteOperations.cs | 250 ------------- .../Client.UserOperations.cs | 99 ----- Core/Core/Api/GraphQL/Client.cs | 243 ++++++------ .../Enums/FileUploadConversionStatus.cs | 10 + .../ProjectCommentsUpdatedMessageType.cs | 8 + .../ProjectFileImportUpdatedMessageType.cs | 7 + .../Enums/ProjectModelsUpdatedMessageType.cs | 8 + .../ProjectPendingModelsUpdatedMessageType.cs | 7 + .../Enums/ProjectUpdatedMessageType.cs | 7 + .../ProjectVersionsUpdatedMessageType.cs | 8 + .../Api/GraphQL/Enums/ProjectVisibility.cs | 8 + Core/Core/Api/GraphQL/Enums/ResourceType.cs | 9 + .../Enums/UserProjectsUpdatedMessageType.cs | 7 + .../GraphQL/GraphQLHttpClientExtensions.cs | 4 +- .../Core/Api/GraphQL/ISpeckleGraphQLClient.cs | 20 + Core/Core/Api/GraphQL/Inputs/CommentInputs.cs | 17 + Core/Core/Api/GraphQL/Inputs/ModelInputs.cs | 11 + Core/Core/Api/GraphQL/Inputs/ProjectInputs.cs | 39 ++ .../Api/GraphQL/Inputs/SubscriptionInputs.cs | 7 + Core/Core/Api/GraphQL/Inputs/VersionInputs.cs | 9 + .../Client.ActivityOperations.cs | 3 +- .../Client.BranchOperations.cs | 20 + .../Client.CommentOperations.cs | 6 + .../Client.CommitOperations.cs | 14 + .../Client.ObjectOperations.cs | 1 + .../Client.ServerOperations.cs | 1 + .../Client.StreamOperations.cs | 42 ++- .../Client.UserOperations.cs | 54 +++ .../Client.Subscriptions.Branch.cs | 0 .../Client.Subscriptions.Commit.cs | 0 .../Client.Subscriptions.Stream.cs | 0 .../LegacyGraphQLModels.cs} | 276 +++----------- Core/Core/Api/GraphQL/Legacy/Manager.cs | 98 +++++ .../{ => Legacy}/SubscriptionModels.cs | 12 + Core/Core/Api/GraphQL/Models/Collections.cs | 19 + Core/Core/Api/GraphQL/Models/Comment.cs | 25 ++ Core/Core/Api/GraphQL/Models/FileUpload.cs | 30 ++ Core/Core/Api/GraphQL/Models/Model.cs | 23 ++ .../Core/Api/GraphQL/Models/ModelsTreeItem.cs | 17 + .../Models/PendingStreamCollaborator.cs | 25 ++ Core/Core/Api/GraphQL/Models/Project.cs | 30 ++ .../Api/GraphQL/Models/ProjectCollaborator.cs | 9 + .../Api/GraphQL/Models/ResourceIdentifier.cs | 10 + .../Models/Responses/MutationResponses.cs | 42 +++ .../Api/GraphQL/Models/Responses/Responses.cs | 30 ++ Core/Core/Api/GraphQL/Models/ServerInfo.cs | 42 +++ .../GraphQL/Models/SubscriptionMessages.cs | 84 +++++ Core/Core/Api/GraphQL/Models/UserInfo.cs | 55 +++ Core/Core/Api/GraphQL/Models/Version.cs | 18 + .../Api/GraphQL/Models/ViewerResourceGroup.cs | 11 + .../Api/GraphQL/Models/ViewerResourceItem.cs | 10 + .../GraphQL/Resources/ActiveUserResource.cs | 165 +++++++++ .../Api/GraphQL/Resources/CommentResource.cs | 279 ++++++++++++++ .../Api/GraphQL/Resources/ModelResource.cs | 309 ++++++++++++++++ .../GraphQL/Resources/OtherUserResource.cs | 108 ++++++ .../Resources/ProjectInviteResource.cs | 260 +++++++++++++ .../Api/GraphQL/Resources/ProjectResource.cs | 349 ++++++++++++++++++ .../GraphQL/Resources/SubscriptionResource.cs | 216 +++++++++++ .../Api/GraphQL/Resources/VersionResource.cs | 252 +++++++++++++ .../Api/GraphQL/Resources/graphql.config.yml | 2 + Core/Core/Api/GraphQL/StreamRoles.cs | 12 + Core/Core/Api/Helpers.cs | 2 + Core/Core/Api/ServerLimits.cs | 3 + Core/Core/Credentials/Account.cs | 2 +- Core/Core/Credentials/AccountManager.cs | 2 + Core/Core/Credentials/Responses.cs | 43 +-- Core/Core/Credentials/StreamWrapper.cs | 1 + Core/Core/Logging/SpeckleException.cs | 5 +- Core/Core/SharpResources.cs | 6 + .../GraphQL/Legacy/LegacyAPITests.cs} | 7 +- .../GraphQL/Legacy}/Subscriptions/Branches.cs | 2 +- .../GraphQL/Legacy}/Subscriptions/Commits.cs | 2 +- .../GraphQL/Legacy}/Subscriptions/Streams.cs | 2 +- .../Resources/ActiveUserResourceTests.cs | 51 +++ .../GraphQL/Resources/CommentResourceTests.cs | 97 +++++ .../ModelResourceExceptionalTests.cs | 88 +++++ .../GraphQL/Resources/ModelResourceTests.cs | 96 +++++ .../Resources/OtherUserResourceTests.cs | 49 +++ .../ProjectInviteResourceExceptionalTests.cs | 32 ++ .../Resources/ProjectInviteResourceTests.cs | 107 ++++++ .../ProjectResourceExceptionalTests.cs | 113 ++++++ .../GraphQL/Resources/ProjectResourceTests.cs | 72 ++++ .../Resources/SubscriptionResourceTests.cs | 120 ++++++ .../GraphQL/Resources/VersionResourceTests.cs | 117 ++++++ .../Credentials/UserServerInfoTests.cs | 2 +- .../Fixtures.cs | 54 ++- .../GraphQLCLient.cs | 2 +- .../Api/GraphQLClient.cs | 15 +- .../AccountServerMigrationTests.cs | 13 +- .../Credentials/Accounts.cs | 2 +- .../Transports/TransportTests.cs | 1 - DesktopUI2/DesktopUI2/DummyBindings.cs | 7 +- DesktopUI2/DesktopUI2/Utils.cs | 1 + .../DesktopUI2/ViewModels/AccountViewModel.cs | 1 + .../ViewModels/NotificationViewModel.cs | 2 +- 98 files changed, 4129 insertions(+), 827 deletions(-) delete mode 100644 Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.ObsoleteOperations.cs delete mode 100644 Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.UserOperations.cs create mode 100644 Core/Core/Api/GraphQL/Enums/FileUploadConversionStatus.cs create mode 100644 Core/Core/Api/GraphQL/Enums/ProjectCommentsUpdatedMessageType.cs create mode 100644 Core/Core/Api/GraphQL/Enums/ProjectFileImportUpdatedMessageType.cs create mode 100644 Core/Core/Api/GraphQL/Enums/ProjectModelsUpdatedMessageType.cs create mode 100644 Core/Core/Api/GraphQL/Enums/ProjectPendingModelsUpdatedMessageType.cs create mode 100644 Core/Core/Api/GraphQL/Enums/ProjectUpdatedMessageType.cs create mode 100644 Core/Core/Api/GraphQL/Enums/ProjectVersionsUpdatedMessageType.cs create mode 100644 Core/Core/Api/GraphQL/Enums/ProjectVisibility.cs create mode 100644 Core/Core/Api/GraphQL/Enums/ResourceType.cs create mode 100644 Core/Core/Api/GraphQL/Enums/UserProjectsUpdatedMessageType.cs create mode 100644 Core/Core/Api/GraphQL/ISpeckleGraphQLClient.cs create mode 100644 Core/Core/Api/GraphQL/Inputs/CommentInputs.cs create mode 100644 Core/Core/Api/GraphQL/Inputs/ModelInputs.cs create mode 100644 Core/Core/Api/GraphQL/Inputs/ProjectInputs.cs create mode 100644 Core/Core/Api/GraphQL/Inputs/SubscriptionInputs.cs create mode 100644 Core/Core/Api/GraphQL/Inputs/VersionInputs.cs rename Core/Core/Api/GraphQL/{ => Legacy}/Client.GraphqlCleintOperations/Client.ActivityOperations.cs (96%) rename Core/Core/Api/GraphQL/{ => Legacy}/Client.GraphqlCleintOperations/Client.BranchOperations.cs (86%) rename Core/Core/Api/GraphQL/{ => Legacy}/Client.GraphqlCleintOperations/Client.CommentOperations.cs (89%) rename Core/Core/Api/GraphQL/{ => Legacy}/Client.GraphqlCleintOperations/Client.CommitOperations.cs (85%) rename Core/Core/Api/GraphQL/{ => Legacy}/Client.GraphqlCleintOperations/Client.ObjectOperations.cs (99%) rename Core/Core/Api/GraphQL/{ => Legacy}/Client.GraphqlCleintOperations/Client.ServerOperations.cs (96%) rename Core/Core/Api/GraphQL/{ => Legacy}/Client.GraphqlCleintOperations/Client.StreamOperations.cs (87%) create mode 100644 Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.UserOperations.cs rename Core/Core/Api/GraphQL/{ => Legacy}/Client.Subscriptions/Client.Subscriptions.Branch.cs (100%) rename Core/Core/Api/GraphQL/{ => Legacy}/Client.Subscriptions/Client.Subscriptions.Commit.cs (100%) rename Core/Core/Api/GraphQL/{ => Legacy}/Client.Subscriptions/Client.Subscriptions.Stream.cs (100%) rename Core/Core/Api/GraphQL/{Models.cs => Legacy/LegacyGraphQLModels.cs} (59%) create mode 100644 Core/Core/Api/GraphQL/Legacy/Manager.cs rename Core/Core/Api/GraphQL/{ => Legacy}/SubscriptionModels.cs (74%) create mode 100644 Core/Core/Api/GraphQL/Models/Collections.cs create mode 100644 Core/Core/Api/GraphQL/Models/Comment.cs create mode 100644 Core/Core/Api/GraphQL/Models/FileUpload.cs create mode 100644 Core/Core/Api/GraphQL/Models/Model.cs create mode 100644 Core/Core/Api/GraphQL/Models/ModelsTreeItem.cs create mode 100644 Core/Core/Api/GraphQL/Models/PendingStreamCollaborator.cs create mode 100644 Core/Core/Api/GraphQL/Models/Project.cs create mode 100644 Core/Core/Api/GraphQL/Models/ProjectCollaborator.cs create mode 100644 Core/Core/Api/GraphQL/Models/ResourceIdentifier.cs create mode 100644 Core/Core/Api/GraphQL/Models/Responses/MutationResponses.cs create mode 100644 Core/Core/Api/GraphQL/Models/Responses/Responses.cs create mode 100644 Core/Core/Api/GraphQL/Models/ServerInfo.cs create mode 100644 Core/Core/Api/GraphQL/Models/SubscriptionMessages.cs create mode 100644 Core/Core/Api/GraphQL/Models/UserInfo.cs create mode 100644 Core/Core/Api/GraphQL/Models/Version.cs create mode 100644 Core/Core/Api/GraphQL/Models/ViewerResourceGroup.cs create mode 100644 Core/Core/Api/GraphQL/Models/ViewerResourceItem.cs create mode 100644 Core/Core/Api/GraphQL/Resources/ActiveUserResource.cs create mode 100644 Core/Core/Api/GraphQL/Resources/CommentResource.cs create mode 100644 Core/Core/Api/GraphQL/Resources/ModelResource.cs create mode 100644 Core/Core/Api/GraphQL/Resources/OtherUserResource.cs create mode 100644 Core/Core/Api/GraphQL/Resources/ProjectInviteResource.cs create mode 100644 Core/Core/Api/GraphQL/Resources/ProjectResource.cs create mode 100644 Core/Core/Api/GraphQL/Resources/SubscriptionResource.cs create mode 100644 Core/Core/Api/GraphQL/Resources/VersionResource.cs create mode 100644 Core/Core/Api/GraphQL/Resources/graphql.config.yml create mode 100644 Core/Core/Api/GraphQL/StreamRoles.cs create mode 100644 Core/Core/SharpResources.cs rename Core/Tests/Speckle.Core.Tests.Integration/{Api.cs => Api/GraphQL/Legacy/LegacyAPITests.cs} (98%) rename Core/Tests/Speckle.Core.Tests.Integration/{ => Api/GraphQL/Legacy}/Subscriptions/Branches.cs (97%) rename Core/Tests/Speckle.Core.Tests.Integration/{ => Api/GraphQL/Legacy}/Subscriptions/Commits.cs (98%) rename Core/Tests/Speckle.Core.Tests.Integration/{ => Api/GraphQL/Legacy}/Subscriptions/Streams.cs (97%) create mode 100644 Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ActiveUserResourceTests.cs create mode 100644 Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/CommentResourceTests.cs create mode 100644 Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ModelResourceExceptionalTests.cs create mode 100644 Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ModelResourceTests.cs create mode 100644 Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/OtherUserResourceTests.cs create mode 100644 Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectInviteResourceExceptionalTests.cs create mode 100644 Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectInviteResourceTests.cs create mode 100644 Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectResourceExceptionalTests.cs create mode 100644 Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectResourceTests.cs create mode 100644 Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/SubscriptionResourceTests.cs create mode 100644 Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/VersionResourceTests.cs diff --git a/Automate/Speckle.Automate.Sdk/AutomationContext.cs b/Automate/Speckle.Automate.Sdk/AutomationContext.cs index 5844df540a..bacf101565 100644 --- a/Automate/Speckle.Automate.Sdk/AutomationContext.cs +++ b/Automate/Speckle.Automate.Sdk/AutomationContext.cs @@ -3,6 +3,7 @@ using Speckle.Automate.Sdk.Schema; using Speckle.Automate.Sdk.Schema.Triggers; using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Models; using Speckle.Core.Credentials; using Speckle.Core.Logging; using Speckle.Core.Models; diff --git a/Automate/Speckle.Automate.Sdk/Speckle.Automate.Sdk.csproj b/Automate/Speckle.Automate.Sdk/Speckle.Automate.Sdk.csproj index bbdb0d9a65..3b9b37c95e 100644 --- a/Automate/Speckle.Automate.Sdk/Speckle.Automate.Sdk.csproj +++ b/Automate/Speckle.Automate.Sdk/Speckle.Automate.Sdk.csproj @@ -13,7 +13,7 @@ true - $(WarningsNotAsErrors);CS8618; + $(WarningsNotAsErrors);CS8618;CS0618; diff --git a/Core/Core/Api/Exceptions.cs b/Core/Core/Api/Exceptions.cs index 7d10bd9974..f100fd22c3 100644 --- a/Core/Core/Api/Exceptions.cs +++ b/Core/Core/Api/Exceptions.cs @@ -9,50 +9,55 @@ namespace Speckle.Core.Api; /// /// Base class for GraphQL API exceptions /// -public class SpeckleGraphQLException : SpeckleException +public class SpeckleGraphQLException : SpeckleGraphQLException { - private readonly GraphQLRequest _request; - public GraphQLResponse? Response { get; } - - public SpeckleGraphQLException(string message, GraphQLRequest request, GraphQLResponse? response) - : base(message) - { - _request = request; - Response = response; - } + public new GraphQLResponse? Response => (GraphQLResponse?)base.Response; - public SpeckleGraphQLException(string message, Exception inner, GraphQLRequest request, GraphQLResponse? response) - : this(message, inner) - { - _request = request; - Response = response; - } + public SpeckleGraphQLException( + string message, + GraphQLRequest request, + GraphQLResponse? response, + Exception? innerException = null + ) + : base(message, request, response, innerException) { } public SpeckleGraphQLException() { } - public SpeckleGraphQLException(string message) + public SpeckleGraphQLException(string? message) : base(message) { } - public SpeckleGraphQLException(string message, Exception innerException) + public SpeckleGraphQLException(string? message, Exception? innerException) : base(message, innerException) { } +} + +public class SpeckleGraphQLException : SpeckleException +{ + private readonly GraphQLRequest _request; + public IGraphQLResponse? Response { get; } public IEnumerable ErrorMessages => Response?.Errors != null ? Response.Errors.Select(e => e.Message) : Enumerable.Empty(); public IDictionary? Extensions => Response?.Extensions; -} -public class SpeckleGraphQLException : SpeckleGraphQLException -{ - public SpeckleGraphQLException(string message, GraphQLRequest request, GraphQLResponse? response) - : base(message, request, response) { } + public SpeckleGraphQLException( + string? message, + GraphQLRequest request, + IGraphQLResponse? response, + Exception? innerException = null + ) + : base(message, innerException) + { + _request = request; + Response = response; + } public SpeckleGraphQLException() { } - public SpeckleGraphQLException(string message) + public SpeckleGraphQLException(string? message) : base(message) { } - public SpeckleGraphQLException(string message, Exception innerException) + public SpeckleGraphQLException(string? message, Exception? innerException) : base(message, innerException) { } } @@ -61,44 +66,56 @@ public SpeckleGraphQLException(string message, Exception innerException) /// https://www.apollographql.com/docs/apollo-server/v2/data/errors/#unauthenticated /// https://www.apollographql.com/docs/apollo-server/v2/data/errors/#forbidden /// -public class SpeckleGraphQLForbiddenException : SpeckleGraphQLException +public class SpeckleGraphQLForbiddenException : SpeckleGraphQLException { - public SpeckleGraphQLForbiddenException(GraphQLRequest request, GraphQLResponse response) - : base("Your request was forbidden", request, response) { } + public SpeckleGraphQLForbiddenException( + GraphQLRequest request, + IGraphQLResponse response, + Exception? innerException = null + ) + : base("Your request was forbidden", request, response, innerException) { } public SpeckleGraphQLForbiddenException() { } - public SpeckleGraphQLForbiddenException(string message) + public SpeckleGraphQLForbiddenException(string? message) : base(message) { } - public SpeckleGraphQLForbiddenException(string message, Exception innerException) + public SpeckleGraphQLForbiddenException(string? message, Exception? innerException) : base(message, innerException) { } } -public class SpeckleGraphQLInternalErrorException : SpeckleGraphQLException +public class SpeckleGraphQLInternalErrorException : SpeckleGraphQLException { - public SpeckleGraphQLInternalErrorException(GraphQLRequest request, GraphQLResponse response) - : base("Your request failed on the server side", request, response) { } + public SpeckleGraphQLInternalErrorException( + GraphQLRequest request, + IGraphQLResponse response, + Exception? innerException = null + ) + : base("Your request failed on the server side", request, response, innerException) { } public SpeckleGraphQLInternalErrorException() { } - public SpeckleGraphQLInternalErrorException(string message) + public SpeckleGraphQLInternalErrorException(string? message) : base(message) { } - public SpeckleGraphQLInternalErrorException(string message, Exception innerException) + public SpeckleGraphQLInternalErrorException(string? message, Exception? innerException) : base(message, innerException) { } } -public class SpeckleGraphQLStreamNotFoundException : SpeckleGraphQLException +public class SpeckleGraphQLStreamNotFoundException : SpeckleGraphQLException { - public SpeckleGraphQLStreamNotFoundException(GraphQLRequest request, GraphQLResponse response) - : base("Stream not found", request, response) { } + public SpeckleGraphQLStreamNotFoundException( + GraphQLRequest request, + IGraphQLResponse response, + Exception? innerException = null + ) + : base("Stream not found", request, response, innerException) { } public SpeckleGraphQLStreamNotFoundException() { } - public SpeckleGraphQLStreamNotFoundException(string message) + public SpeckleGraphQLStreamNotFoundException(string? message) : base(message) { } - public SpeckleGraphQLStreamNotFoundException(string message, Exception innerException) + public SpeckleGraphQLStreamNotFoundException(string? message, Exception? innerException) : base(message, innerException) { } } diff --git a/Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.ObsoleteOperations.cs b/Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.ObsoleteOperations.cs deleted file mode 100644 index 15d0054f0e..0000000000 --- a/Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.ObsoleteOperations.cs +++ /dev/null @@ -1,250 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Threading; -using System.Threading.Tasks; -using GraphQL; - -namespace Speckle.Core.Api; - -[SuppressMessage("Design", "CA1068:CancellationToken parameters must come last")] -public partial class Client -{ - #region Stream Grant Permission - - /// - /// Grants permissions to a user on a given stream. - /// - /// - /// - [Obsolete("Please use the `StreamUpdatePermission` method", true)] - public Task StreamGrantPermission(StreamPermissionInput permissionInput) - { - return StreamGrantPermission(CancellationToken.None, permissionInput); - } - - /// - /// Grants permissions to a user on a given stream. - /// - /// - /// - /// - [Obsolete("Please use the `StreamUpdatePermission` method", true)] - public async Task StreamGrantPermission( - CancellationToken cancellationToken, - StreamPermissionInput permissionInput - ) - { - var request = new GraphQLRequest - { - Query = - @" - mutation streamGrantPermission($permissionParams: StreamGrantPermissionInput!) { - streamGrantPermission(permissionParams:$permissionParams) - }", - Variables = new { permissionParams = permissionInput } - }; - - var res = await ExecuteGraphQLRequest>(request, cancellationToken).ConfigureAwait(false); - return (bool)res["streamGrantPermission"]; - } - - #endregion - - #region Cancellation token as last param - - [Obsolete("Use overload with cancellation token parameter last")] - public Task> StreamGetActivity( - CancellationToken cancellationToken, - string id, - DateTime? after = null, - DateTime? before = null, - DateTime? cursor = null, - string actionType = "", - int limit = 25 - ) - { - return StreamGetActivity(id, after, before, cursor, actionType, limit, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task> StreamGetBranches( - CancellationToken cancellationToken, - string streamId, - int branchesLimit = 10, - int commitsLimit = 10 - ) - { - return StreamGetBranches(streamId, branchesLimit, commitsLimit, CancellationToken.None); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task BranchCreate(CancellationToken cancellationToken, BranchCreateInput branchInput) - { - return BranchCreate(branchInput, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task BranchGet( - CancellationToken cancellationToken, - string streamId, - string branchName, - int commitsLimit = 10 - ) - { - return BranchGet(streamId, branchName, commitsLimit, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task BranchUpdate(CancellationToken cancellationToken, BranchUpdateInput branchInput) - { - return BranchUpdate(branchInput, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task BranchDelete(CancellationToken cancellationToken, BranchDeleteInput branchInput) - { - return BranchDelete(branchInput, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task StreamGetComments( - CancellationToken cancellationToken, - string streamId, - int limit = 25, - string? cursor = null - ) - { - return StreamGetComments(streamId, limit, cursor, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task StreamGetCommentScreenshot(CancellationToken cancellationToken, string id, string streamId) - { - return StreamGetCommentScreenshot(id, streamId, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task CommitGet(CancellationToken cancellationToken, string streamId, string commitId) - { - return CommitGet(streamId, commitId, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task> StreamGetCommits(CancellationToken cancellationToken, string streamId, int limit = 10) - { - return StreamGetCommits(streamId, limit, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task CommitCreate(CancellationToken cancellationToken, CommitCreateInput commitInput) - { - return CommitCreate(commitInput, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task CommitUpdate(CancellationToken cancellationToken, CommitUpdateInput commitInput) - { - return CommitUpdate(commitInput, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task CommitDelete(CancellationToken cancellationToken, CommitDeleteInput commitInput) - { - return CommitDelete(commitInput, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task CommitReceived(CancellationToken cancellationToken, CommitReceivedInput commitReceivedInput) - { - return CommitReceived(commitReceivedInput, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task ObjectGet(CancellationToken cancellationToken, string streamId, string objectId) - { - return ObjectGet(streamId, objectId, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task ObjectCountGet(CancellationToken cancellationToken, string streamId, string objectId) - { - return ObjectCountGet(streamId, objectId, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task StreamGet(CancellationToken cancellationToken, string id, int branchesLimit = 10) - { - return StreamGet(id, branchesLimit, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task> StreamsGet(CancellationToken cancellationToken, int limit = 10) - { - return StreamsGet(limit, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task> FavoriteStreamsGet(CancellationToken cancellationToken, int limit = 10) - { - return FavoriteStreamsGet(limit, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task> StreamSearch(CancellationToken cancellationToken, string query, int limit = 10) - { - return StreamSearch(query, limit, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task StreamCreate(CancellationToken cancellationToken, StreamCreateInput streamInput) - { - return StreamCreate(streamInput, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task StreamUpdate(CancellationToken cancellationToken, StreamUpdateInput streamInput) - { - return StreamUpdate(streamInput, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task StreamDelete(CancellationToken cancellationToken, string id) - { - return StreamDelete(id, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task StreamRevokePermission( - CancellationToken cancellationToken, - StreamRevokePermissionInput permissionInput - ) - { - return StreamRevokePermission(permissionInput, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task StreamGetPendingCollaborators(CancellationToken cancellationToken, string id) - { - return StreamGetPendingCollaborators(id, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task StreamInviteCreate(CancellationToken cancellationToken, StreamInviteCreateInput inviteCreateInput) - { - return StreamInviteCreate(inviteCreateInput, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task OtherUserGet(CancellationToken cancellationToken, string id) - { - return OtherUserGet(id, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task> UserSearch(CancellationToken cancellationToken, string query, int limit = 10) - { - return UserSearch(query, limit, cancellationToken); - } - #endregion -} diff --git a/Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.UserOperations.cs b/Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.UserOperations.cs deleted file mode 100644 index a79764e551..0000000000 --- a/Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.UserOperations.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using GraphQL; - -namespace Speckle.Core.Api; - -public partial class Client -{ - /// - /// Gets the currently active user profile. - /// - /// - /// - public async Task ActiveUserGet(CancellationToken cancellationToken = default) - { - var request = new GraphQLRequest - { - Query = - @"query User { - activeUser { - id, - email, - name, - bio, - company, - avatar, - verified, - profiles, - role, - } - }" - }; - return (await ExecuteGraphQLRequest(request, cancellationToken).ConfigureAwait(false)).activeUser; - } - - /// - /// Get another user's profile by its user id. - /// - /// Id of the user you are looking for - /// - /// - public async Task OtherUserGet(string id, CancellationToken cancellationToken = default) - { - var request = new GraphQLRequest - { - Query = - @"query LimitedUser($id: String!) { - otherUser(id: $id){ - id, - name, - bio, - company, - avatar, - verified, - role, - } - }", - Variables = new { id } - }; - return (await ExecuteGraphQLRequest(request, cancellationToken).ConfigureAwait(false)).otherUser; - } - - /// - /// Searches for a user on the server. - /// - /// String to search for. Must be at least 3 characters - /// Max number of users to return - /// - public async Task> UserSearch( - string query, - int limit = 10, - CancellationToken cancellationToken = default - ) - { - var request = new GraphQLRequest - { - Query = - @"query UserSearch($query: String!, $limit: Int!) { - userSearch(query: $query, limit: $limit) { - cursor, - items { - id - name - bio - company - avatar - verified - role - } - } - }", - Variables = new { query, limit } - }; - return (await ExecuteGraphQLRequest(request, cancellationToken).ConfigureAwait(false)) - .userSearch - .items; - } -} diff --git a/Core/Core/Api/GraphQL/Client.cs b/Core/Core/Api/GraphQL/Client.cs index bacfa63bc0..9a645006f4 100644 --- a/Core/Core/Api/GraphQL/Client.cs +++ b/Core/Core/Api/GraphQL/Client.cs @@ -15,6 +15,9 @@ using Serilog.Context; using Serilog.Core; using Serilog.Core.Enrichers; +using Serilog.Events; +using Speckle.Core.Api.GraphQL; +using Speckle.Core.Api.GraphQL.Resources; using Speckle.Core.Api.GraphQL.Serializer; using Speckle.Core.Credentials; using Speckle.Core.Helpers; @@ -23,71 +26,55 @@ namespace Speckle.Core.Api; -public sealed partial class Client : IDisposable +public sealed partial class Client : ISpeckleGraphQLClient, IDisposable { - [Obsolete] - internal Client() { } + public ProjectResource Project { get; } + public ModelResource Model { get; } + public VersionResource Version { get; } + public ActiveUserResource ActiveUser { get; } + public OtherUserResource OtherUser { get; } + public ProjectInviteResource ProjectInvite { get; } + public CommentResource Comment { get; } + public SubscriptionResource Subscription { get; } - public Client(Account account) - { - Account = account ?? throw new SpeckleException("Provided account is null."); - - HttpClient = Http.GetHttpProxyClient(null, TimeSpan.FromSeconds(30)); - Http.AddAuthHeader(HttpClient, account.token); + public string ServerUrl => Account.serverInfo.url; - HttpClient.DefaultRequestHeaders.Add("apollographql-client-name", Setup.HostApplication); - HttpClient.DefaultRequestHeaders.Add( - "apollographql-client-version", - Assembly.GetExecutingAssembly().GetName().Version.ToString() - ); + public string ApiToken => Account.token; - GQLClient = new GraphQLHttpClient( - new GraphQLHttpClientOptions - { - EndPoint = new Uri(new Uri(account.serverInfo.url), "/graphql"), - UseWebSocketForQueriesAndMutations = false, - WebSocketProtocol = "graphql-ws", - ConfigureWebSocketConnectionInitPayload = _ => - { - return Http.CanAddAuth(account.token, out string? authValue) ? new { Authorization = authValue } : null; - }, - }, - new NewtonsoftJsonSerializer(), - HttpClient - ); + public System.Version? ServerVersion { get; private set; } - GQLClient.WebSocketReceiveErrors.Subscribe(e => - { - if (e is WebSocketException we) - { - Console.WriteLine( - $"WebSocketException: {we.Message} (WebSocketError {we.WebSocketErrorCode}, ErrorCode {we.ErrorCode}, NativeErrorCode {we.NativeErrorCode}" - ); - } - else - { - Console.WriteLine($"Exception in websocket receive stream: {e}"); - } - }); - } + [JsonIgnore] + public Account Account { get; } - public string ServerUrl => Account.serverInfo.url; + private HttpClient HttpClient { get; } - public string ApiToken => Account.token; + public GraphQLHttpClient GQLClient { get; } - public System.Version? ServerVersion { get; set; } + /// + /// was null + public Client(Account account) + { + Account = account ?? throw new ArgumentException("Provided account is null."); - [JsonIgnore] - public Account Account { get; set; } + Project = new(this); + Model = new(this); + Version = new(this); + ActiveUser = new(this); + OtherUser = new(this); + ProjectInvite = new(this); + Comment = new(this); + Subscription = new(this); - private HttpClient HttpClient { get; set; } + HttpClient = CreateHttpClient(account); - public GraphQLHttpClient GQLClient { get; set; } + GQLClient = CreateGraphQLClient(account, HttpClient); + } public void Dispose() { try { + Subscription.Dispose(); UserStreamAddedSubscription?.Dispose(); UserStreamRemovedSubscription?.Dispose(); StreamUpdatedSubscription?.Dispose(); @@ -116,42 +103,34 @@ internal async Task ExecuteWithResiliencePolicies(Func> func) var delay = Backoff.DecorrelatedJitterBackoffV2(TimeSpan.FromSeconds(1), 5); var graphqlRetry = Policy - .Handle>() + .Handle() .WaitAndRetryAsync( delay, - (ex, timeout, context) => + (ex, timeout, _) => { - var graphqlEx = (SpeckleGraphQLException)ex; - SpeckleLog.Logger - .ForContext("graphqlExtensions", graphqlEx.Extensions) - .ForContext("graphqlErrorMessages", graphqlEx.ErrorMessages) - .Warning( - ex, - "The previous attempt at executing function to get {resultType} failed with {exceptionMessage}. Retrying after {timeout}.", - typeof(T).Name, - ex.Message, - timeout - ); + SpeckleLog.Logger.Debug( + ex, + "The previous attempt at executing function to get {resultType} failed with {exceptionMessage}. Retrying after {timeout}", + typeof(T).Name, + ex.Message, + timeout + ); } ); return await graphqlRetry.ExecuteAsync(func).ConfigureAwait(false); } - /// "FORBIDDEN" on "UNAUTHORIZED" response from server - /// All other request errors - /// The requested a cancel + /// public async Task ExecuteGraphQLRequest(GraphQLRequest request, CancellationToken cancellationToken = default) { using IDisposable context0 = LogContext.Push(CreateEnrichers(request)); + var timer = Stopwatch.StartNew(); - SpeckleLog.Logger.Debug("Starting execution of graphql request to get {resultType}", typeof(T).Name); - var timer = new Stopwatch(); - var success = false; - timer.Start(); + Exception? exception = null; try { - var result = await ExecuteWithResiliencePolicies(async () => + return await ExecuteWithResiliencePolicies(async () => { GraphQLResponse result = await GQLClient .SendMutationAsync(request, cancellationToken) @@ -160,58 +139,28 @@ public async Task ExecuteGraphQLRequest(GraphQLRequest request, Cancellati return result.Data; }) .ConfigureAwait(false); - success = true; - return result; } - // cancellations are bubbling up with no logging - catch (OperationCanceledException) + catch (Exception ex) { + exception = ex; throw; } - // we catch forbidden to rethrow, making sure its not logged. - catch (SpeckleGraphQLForbiddenException) - { - throw; - } - // anything else related to graphql gets logged - catch (SpeckleGraphQLException gqlException) - { - SpeckleLog.Logger - .ForContext("graphqlResponse", gqlException.Response) - .ForContext("graphqlExtensions", gqlException.Extensions) - .ForContext("graphqlErrorMessages", gqlException.ErrorMessages.ToList()) - .Warning( - gqlException, - "Execution of the graphql request to get {resultType} failed with {graphqlExceptionType} {exceptionMessage}.", - typeof(T).Name, - gqlException.GetType().Name, - gqlException.Message - ); - throw; - } - // we log and wrap anything that is not a graphql exception. - // this makes sure, that any graphql operation only throws SpeckleGraphQLExceptions - catch (Exception ex) when (!ex.IsFatal()) - { - SpeckleLog.Logger.Warning( - ex, - "Execution of the graphql request to get {resultType} failed without a graphql response. Cause {exceptionMessage}", - typeof(T).Name, - ex.Message - ); - throw new SpeckleGraphQLException("The graphql request failed without a graphql response", ex, request, null); - } finally { - // this is a performance metric log operation - // this makes sure that both success and failed operations report - // the same performance log - timer.Stop(); - var status = success ? "succeeded" : "failed"; - SpeckleLog.Logger.Information( - "Execution of graphql request to get {resultType} {resultStatus} after {elapsed} seconds", + LogEventLevel logLevel = exception switch + { + null => LogEventLevel.Information, + OperationCanceledException + => cancellationToken.IsCancellationRequested ? LogEventLevel.Debug : LogEventLevel.Error, + SpeckleException => LogEventLevel.Warning, + _ => LogEventLevel.Error, + }; + SpeckleLog.Logger.Write( + logLevel, + exception, + "Execution of the graphql request to get {resultType} completed with success:{status} after {elapsed} seconds", typeof(T).Name, - status, + exception is null, timer.Elapsed.TotalSeconds ); } @@ -236,7 +185,7 @@ internal void MaybeThrowFromGraphQLErrors(GraphQLRequest request, GraphQLResp ) ) { - throw new SpeckleGraphQLForbiddenException(request, response); + throw new SpeckleGraphQLForbiddenException(request, response); } if ( @@ -246,7 +195,7 @@ internal void MaybeThrowFromGraphQLErrors(GraphQLRequest request, GraphQLResp ) ) { - throw new SpeckleGraphQLStreamNotFoundException(request, response); + throw new SpeckleGraphQLStreamNotFoundException(request, response); } if ( @@ -257,7 +206,7 @@ internal void MaybeThrowFromGraphQLErrors(GraphQLRequest request, GraphQLResp ) ) { - throw new SpeckleGraphQLInternalErrorException(request, response); + throw new SpeckleGraphQLInternalErrorException(request, response); } throw new SpeckleGraphQLException("Request failed with errors", request, response); @@ -299,6 +248,10 @@ private ILogEventEnricher[] CreateEnrichers(GraphQLRequest request) }; } + IDisposable ISpeckleGraphQLClient.SubscribeTo(GraphQLRequest request, Action callback) => + SubscribeTo(request, callback); + + /// internal IDisposable SubscribeTo(GraphQLRequest request, Action callback) { using (LogContext.Push(CreateEnrichers(request))) @@ -325,7 +278,7 @@ internal IDisposable SubscribeTo(GraphQLRequest request, Action ca } } // we catch forbidden to rethrow, making sure its not logged. - catch (SpeckleGraphQLForbiddenException) + catch (SpeckleGraphQLForbiddenException) { throw; } @@ -356,7 +309,7 @@ internal IDisposable SubscribeTo(GraphQLRequest request, Action ca // so far we've swallowed these errors SpeckleLog.Logger.Error( ex, - "Subscription request for {resultType} failed with {exceptionMessage}", + "Subscription for {resultType} terminated unexpectedly with {exceptionMessage}", typeof(T).Name, ex.Message ); @@ -375,11 +328,57 @@ internal IDisposable SubscribeTo(GraphQLRequest request, Action ca ); throw new SpeckleGraphQLException( "The graphql request failed without a graphql response", - ex, request, - null + null, + ex ); } } } + + private static GraphQLHttpClient CreateGraphQLClient(Account account, HttpClient httpClient) + { + var gQLClient = new GraphQLHttpClient( + new GraphQLHttpClientOptions + { + EndPoint = new Uri(new Uri(account.serverInfo.url), "/graphql"), + UseWebSocketForQueriesAndMutations = false, + WebSocketProtocol = "graphql-ws", + ConfigureWebSocketConnectionInitPayload = _ => + { + return Http.CanAddAuth(account.token, out string? authValue) ? new { Authorization = authValue } : null; + }, + }, + new NewtonsoftJsonSerializer(), + httpClient + ); + + gQLClient.WebSocketReceiveErrors.Subscribe(e => + { + if (e is WebSocketException we) + { + Console.WriteLine( + $"WebSocketException: {we.Message} (WebSocketError {we.WebSocketErrorCode}, ErrorCode {we.ErrorCode}, NativeErrorCode {we.NativeErrorCode}" + ); + } + else + { + Console.WriteLine($"Exception in websocket receive stream: {e}"); + } + }); + return gQLClient; + } + + private static HttpClient CreateHttpClient(Account account) + { + var httpClient = Http.GetHttpProxyClient(null, TimeSpan.FromSeconds(30)); + Http.AddAuthHeader(httpClient, account.token); + + httpClient.DefaultRequestHeaders.Add("apollographql-client-name", Setup.HostApplication); + httpClient.DefaultRequestHeaders.Add( + "apollographql-client-version", + Assembly.GetExecutingAssembly().GetName().Version.ToString() + ); + return httpClient; + } } diff --git a/Core/Core/Api/GraphQL/Enums/FileUploadConversionStatus.cs b/Core/Core/Api/GraphQL/Enums/FileUploadConversionStatus.cs new file mode 100644 index 0000000000..4ab9e751bc --- /dev/null +++ b/Core/Core/Api/GraphQL/Enums/FileUploadConversionStatus.cs @@ -0,0 +1,10 @@ +namespace Speckle.Core.Api.GraphQL.Enums; + +//This enum isn't explicitly defined in the schema, instead its usages are int typed (But represent an enum) +public enum FileUploadConversionStatus +{ + Queued = 0, + Processing = 1, + Success = 2, + Error = 3, +} diff --git a/Core/Core/Api/GraphQL/Enums/ProjectCommentsUpdatedMessageType.cs b/Core/Core/Api/GraphQL/Enums/ProjectCommentsUpdatedMessageType.cs new file mode 100644 index 0000000000..2f11301dec --- /dev/null +++ b/Core/Core/Api/GraphQL/Enums/ProjectCommentsUpdatedMessageType.cs @@ -0,0 +1,8 @@ +namespace Speckle.Core.Api.GraphQL.Enums; + +public enum ProjectCommentsUpdatedMessageType +{ + ARCHIVED, + CREATED, + UPDATED, +} diff --git a/Core/Core/Api/GraphQL/Enums/ProjectFileImportUpdatedMessageType.cs b/Core/Core/Api/GraphQL/Enums/ProjectFileImportUpdatedMessageType.cs new file mode 100644 index 0000000000..7cb7d933b9 --- /dev/null +++ b/Core/Core/Api/GraphQL/Enums/ProjectFileImportUpdatedMessageType.cs @@ -0,0 +1,7 @@ +namespace Speckle.Core.Api.GraphQL.Enums; + +public enum ProjectFileImportUpdatedMessageType +{ + CREATED, + UPDATED, +} diff --git a/Core/Core/Api/GraphQL/Enums/ProjectModelsUpdatedMessageType.cs b/Core/Core/Api/GraphQL/Enums/ProjectModelsUpdatedMessageType.cs new file mode 100644 index 0000000000..1416691fa0 --- /dev/null +++ b/Core/Core/Api/GraphQL/Enums/ProjectModelsUpdatedMessageType.cs @@ -0,0 +1,8 @@ +namespace Speckle.Core.Api.GraphQL.Enums; + +public enum ProjectModelsUpdatedMessageType +{ + CREATED, + DELETED, + UPDATED, +} diff --git a/Core/Core/Api/GraphQL/Enums/ProjectPendingModelsUpdatedMessageType.cs b/Core/Core/Api/GraphQL/Enums/ProjectPendingModelsUpdatedMessageType.cs new file mode 100644 index 0000000000..42ac2bebf1 --- /dev/null +++ b/Core/Core/Api/GraphQL/Enums/ProjectPendingModelsUpdatedMessageType.cs @@ -0,0 +1,7 @@ +namespace Speckle.Core.Api.GraphQL.Enums; + +public enum ProjectPendingModelsUpdatedMessageType +{ + CREATED, + UPDATED, +} diff --git a/Core/Core/Api/GraphQL/Enums/ProjectUpdatedMessageType.cs b/Core/Core/Api/GraphQL/Enums/ProjectUpdatedMessageType.cs new file mode 100644 index 0000000000..3eccdd5a04 --- /dev/null +++ b/Core/Core/Api/GraphQL/Enums/ProjectUpdatedMessageType.cs @@ -0,0 +1,7 @@ +namespace Speckle.Core.Api.GraphQL.Enums; + +public enum ProjectUpdatedMessageType +{ + DELETED, + UPDATED, +} diff --git a/Core/Core/Api/GraphQL/Enums/ProjectVersionsUpdatedMessageType.cs b/Core/Core/Api/GraphQL/Enums/ProjectVersionsUpdatedMessageType.cs new file mode 100644 index 0000000000..14e1f7008e --- /dev/null +++ b/Core/Core/Api/GraphQL/Enums/ProjectVersionsUpdatedMessageType.cs @@ -0,0 +1,8 @@ +namespace Speckle.Core.Api.GraphQL.Enums; + +public enum ProjectVersionsUpdatedMessageType +{ + CREATED, + DELETED, + UPDATED, +} diff --git a/Core/Core/Api/GraphQL/Enums/ProjectVisibility.cs b/Core/Core/Api/GraphQL/Enums/ProjectVisibility.cs new file mode 100644 index 0000000000..9a62fff999 --- /dev/null +++ b/Core/Core/Api/GraphQL/Enums/ProjectVisibility.cs @@ -0,0 +1,8 @@ +namespace Speckle.Core.Api.GraphQL.Enums; + +public enum ProjectVisibility +{ + Private, + Public, + Unlisted +} diff --git a/Core/Core/Api/GraphQL/Enums/ResourceType.cs b/Core/Core/Api/GraphQL/Enums/ResourceType.cs new file mode 100644 index 0000000000..2fa31c46f1 --- /dev/null +++ b/Core/Core/Api/GraphQL/Enums/ResourceType.cs @@ -0,0 +1,9 @@ +namespace Speckle.Core.Api.GraphQL.Enums; + +public enum ResourceType +{ + commit, + stream, + @object, + comment +} diff --git a/Core/Core/Api/GraphQL/Enums/UserProjectsUpdatedMessageType.cs b/Core/Core/Api/GraphQL/Enums/UserProjectsUpdatedMessageType.cs new file mode 100644 index 0000000000..5225929afe --- /dev/null +++ b/Core/Core/Api/GraphQL/Enums/UserProjectsUpdatedMessageType.cs @@ -0,0 +1,7 @@ +namespace Speckle.Core.Api.GraphQL.Enums; + +public enum UserProjectsUpdatedMessageType +{ + ADDED, + REMOVED, +} diff --git a/Core/Core/Api/GraphQL/GraphQLHttpClientExtensions.cs b/Core/Core/Api/GraphQL/GraphQLHttpClientExtensions.cs index 64b7a76645..0e7244e98b 100644 --- a/Core/Core/Api/GraphQL/GraphQLHttpClientExtensions.cs +++ b/Core/Core/Api/GraphQL/GraphQLHttpClientExtensions.cs @@ -3,6 +3,8 @@ using System.Threading; using GraphQL.Client.Http; using System.Linq; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Models.Responses; namespace Speckle.Core.Api.GraphQL; @@ -13,7 +15,7 @@ public static class GraphQLHttpClientExtensions /// /// [Optional] defaults to an empty cancellation token /// object excluding any strings (eg "2.7.2-alpha.6995" becomes "2.7.2.6995") - /// + /// public static async Task GetServerVersion( this GraphQLHttpClient client, CancellationToken cancellationToken = default diff --git a/Core/Core/Api/GraphQL/ISpeckleGraphQLClient.cs b/Core/Core/Api/GraphQL/ISpeckleGraphQLClient.cs new file mode 100644 index 0000000000..5b6a372095 --- /dev/null +++ b/Core/Core/Api/GraphQL/ISpeckleGraphQLClient.cs @@ -0,0 +1,20 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using GraphQL; + +namespace Speckle.Core.Api.GraphQL; + +internal interface ISpeckleGraphQLClient +{ + /// "FORBIDDEN" on "UNAUTHORIZED" response from server + /// All other request errors + /// The requested a cancel + /// This already been disposed + internal Task ExecuteGraphQLRequest(GraphQLRequest request, CancellationToken cancellationToken); + + /// "FORBIDDEN" on "UNAUTHORIZED" response from server + /// All other request errors + /// This already been disposed + internal IDisposable SubscribeTo(GraphQLRequest request, Action callback); +} diff --git a/Core/Core/Api/GraphQL/Inputs/CommentInputs.cs b/Core/Core/Api/GraphQL/Inputs/CommentInputs.cs new file mode 100644 index 0000000000..df810adeb6 --- /dev/null +++ b/Core/Core/Api/GraphQL/Inputs/CommentInputs.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; + +namespace Speckle.Core.Api.GraphQL.Inputs; + +public sealed record CreateCommentInput( + CommentContentInput content, + string projectId, + string resourceIdString, + string? screenshot, + object? viewerState +); + +public sealed record EditCommentInput(CommentContentInput content, string commentId); + +public sealed record CreateCommentReplyInput(CommentContentInput content, string threadId); + +public sealed record CommentContentInput(IReadOnlyCollection? blobIds, object? doc); diff --git a/Core/Core/Api/GraphQL/Inputs/ModelInputs.cs b/Core/Core/Api/GraphQL/Inputs/ModelInputs.cs new file mode 100644 index 0000000000..817df64cab --- /dev/null +++ b/Core/Core/Api/GraphQL/Inputs/ModelInputs.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace Speckle.Core.Api.GraphQL.Inputs; + +public sealed record CreateModelInput(string name, string? description, string projectId); + +public sealed record DeleteModelInput(string id, string projectId); + +public sealed record UpdateModelInput(string id, string? name, string? description, string projectId); + +public sealed record ModelVersionsFilter(IReadOnlyList priorityIds, bool? priorityIdsOnly); diff --git a/Core/Core/Api/GraphQL/Inputs/ProjectInputs.cs b/Core/Core/Api/GraphQL/Inputs/ProjectInputs.cs new file mode 100644 index 0000000000..568d0093aa --- /dev/null +++ b/Core/Core/Api/GraphQL/Inputs/ProjectInputs.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using Speckle.Core.Api.GraphQL.Enums; + +namespace Speckle.Core.Api.GraphQL.Inputs; + +public sealed record ProjectCommentsFilter(bool? includeArchived, bool? loadedVersionsOnly, string? resourceIdString); + +public sealed record ProjectCreateInput(string? name, string? description, ProjectVisibility? visibility); + +public sealed record ProjectInviteCreateInput(string? email, string? role, string? serverRole, string? userId); + +public sealed record ProjectInviteUseInput(bool accept, string projectId, string token); + +public sealed record ProjectModelsFilter( + IReadOnlyList? contributors, + IReadOnlyList? excludeIds, + IReadOnlyList? ids, + bool? onlyWithVersions, + string? search, + IReadOnlyList sourceApps +); + +public sealed record ProjectModelsTreeFilter( + IReadOnlyList? contributors, + string? search, + IReadOnlyList? sourceApps +); + +public sealed record ProjectUpdateInput( + string id, + string? name = null, + string? description = null, + bool? allowPublicComments = null, + ProjectVisibility? visibility = null +); + +public sealed record ProjectUpdateRoleInput(string userId, string projectId, string? role); + +public sealed record UserProjectsFilter(string search, IReadOnlyList? onlyWithRoles = null); diff --git a/Core/Core/Api/GraphQL/Inputs/SubscriptionInputs.cs b/Core/Core/Api/GraphQL/Inputs/SubscriptionInputs.cs new file mode 100644 index 0000000000..86688a2864 --- /dev/null +++ b/Core/Core/Api/GraphQL/Inputs/SubscriptionInputs.cs @@ -0,0 +1,7 @@ +namespace Speckle.Core.Api.GraphQL.Inputs; + +public sealed record ViewerUpdateTrackingTarget( + string projectId, + string resourceIdString, + bool? loadedVersionsOnly = null +); diff --git a/Core/Core/Api/GraphQL/Inputs/VersionInputs.cs b/Core/Core/Api/GraphQL/Inputs/VersionInputs.cs new file mode 100644 index 0000000000..5bbee6e791 --- /dev/null +++ b/Core/Core/Api/GraphQL/Inputs/VersionInputs.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace Speckle.Core.Api.GraphQL.Inputs; + +public sealed record UpdateVersionInput(string versionId, string? message); + +public sealed record MoveVersionsInput(string targetModelName, IReadOnlyList versionIds); + +public sealed record DeleteVersionsInput(IReadOnlyList versionIds); diff --git a/Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.ActivityOperations.cs b/Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.ActivityOperations.cs similarity index 96% rename from Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.ActivityOperations.cs rename to Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.ActivityOperations.cs index 34250ecb8a..df43029474 100644 --- a/Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.ActivityOperations.cs +++ b/Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.ActivityOperations.cs @@ -8,6 +8,7 @@ namespace Speckle.Core.Api; public partial class Client { + //TODO: API gap /// /// Gets the activity of a stream /// @@ -25,7 +26,7 @@ public async Task> StreamGetActivity( DateTime? before = null, DateTime? cursor = null, string actionType = "", - int limit = 25, + int limit = ServerLimits.DEFAULT_PAGINATION_REQUEST, CancellationToken cancellationToken = default ) { diff --git a/Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.BranchOperations.cs b/Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.BranchOperations.cs similarity index 86% rename from Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.BranchOperations.cs rename to Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.BranchOperations.cs index 1c3a67b181..456098e093 100644 --- a/Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.BranchOperations.cs +++ b/Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.BranchOperations.cs @@ -1,7 +1,9 @@ +using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using GraphQL; +using Speckle.Core.Api.GraphQL.Resources; namespace Speckle.Core.Api; @@ -14,6 +16,9 @@ public partial class Client /// Id of the stream to get the branches from /// Max number of commits to retrieve /// + /// + /// + [Obsolete($"Use client.{nameof(Model)}.{nameof(ModelResource.GetModels)}")] public async Task> StreamGetBranchesWithLimitRetry(string streamId, int commitsLimit = 10) { List branches; @@ -38,6 +43,9 @@ public async Task> StreamGetBranchesWithLimitRetry(string streamId, /// Max number of commits to retrieve /// /// + /// + /// + [Obsolete($"Use client.{nameof(Model)}.{nameof(ModelResource.GetModels)}")] public async Task> StreamGetBranches( string streamId, int branchesLimit = 10, @@ -86,6 +94,8 @@ public async Task> StreamGetBranches( /// /// /// The branch id. + /// + [Obsolete($"Use client.{nameof(Model)}.{nameof(ModelResource.Create)}")] public async Task BranchCreate(BranchCreateInput branchInput, CancellationToken cancellationToken = default) { var request = new GraphQLRequest @@ -105,6 +115,10 @@ public async Task BranchCreate(BranchCreateInput branchInput, Cancellati /// Name of the branch to get /// /// The requested branch + /// Updated to Model.GetWithVersions + /// + /// + [Obsolete($"Use client.{nameof(Model)}.{nameof(ModelResource.Get)}")] public async Task BranchGet( string streamId, string branchName, @@ -154,6 +168,8 @@ public async Task BranchGet( /// Id of the project to get the model from /// Id of the model /// + /// + [Obsolete($"Use client.{nameof(Model)}.{nameof(ModelResource.Get)}")] public async Task ModelGet(string projectId, string modelId, CancellationToken cancellationToken = default) { var request = new GraphQLRequest @@ -190,6 +206,8 @@ public async Task ModelGet(string projectId, string modelId, Cancellatio /// /// /// The stream's id. + /// + [Obsolete($"Use client.{nameof(Model)}.{nameof(ModelResource.Update)}")] public async Task BranchUpdate(BranchUpdateInput branchInput, CancellationToken cancellationToken = default) { var request = new GraphQLRequest @@ -208,6 +226,8 @@ public async Task BranchUpdate(BranchUpdateInput branchInput, Cancellation /// /// /// + /// + [Obsolete($"Use client.{nameof(Model)}.{nameof(ModelResource.Delete)}")] public async Task BranchDelete(BranchDeleteInput branchInput, CancellationToken cancellationToken = default) { var request = new GraphQLRequest diff --git a/Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.CommentOperations.cs b/Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.CommentOperations.cs similarity index 89% rename from Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.CommentOperations.cs rename to Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.CommentOperations.cs index 20feaaf3be..aebce10af3 100644 --- a/Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.CommentOperations.cs +++ b/Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.CommentOperations.cs @@ -1,6 +1,8 @@ +using System; using System.Threading; using System.Threading.Tasks; using GraphQL; +using Speckle.Core.Api.GraphQL.Resources; namespace Speckle.Core.Api; @@ -14,6 +16,8 @@ public partial class Client /// Time to filter the comments with /// /// + /// + [Obsolete($"Use client.{nameof(CommentResource)}.{nameof(CommentResource.GetProjectComments)}")] public async Task StreamGetComments( string streamId, int limit = 25, @@ -78,6 +82,8 @@ public async Task StreamGetComments( /// Id of the stream to get the comment from /// /// + /// + [Obsolete($"Use client.{nameof(CommentResource)}.{nameof(CommentResource.GetProjectComments)}")] public async Task StreamGetCommentScreenshot( string id, string streamId, diff --git a/Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.CommitOperations.cs b/Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.CommitOperations.cs similarity index 85% rename from Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.CommitOperations.cs rename to Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.CommitOperations.cs index d76bc43037..d267b7623d 100644 --- a/Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.CommitOperations.cs +++ b/Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.CommitOperations.cs @@ -1,7 +1,9 @@ +using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using GraphQL; +using Speckle.Core.Api.GraphQL.Resources; namespace Speckle.Core.Api; @@ -14,6 +16,8 @@ public partial class Client /// Id of the commit to get /// /// + /// + [Obsolete($"Use client.{nameof(Version)}.{nameof(VersionResource.Get)}")] public async Task CommitGet(string streamId, string commitId, CancellationToken cancellationToken = default) { var request = new GraphQLRequest @@ -48,6 +52,8 @@ public async Task CommitGet(string streamId, string commitId, Cancellati /// Max number of commits to get /// /// The requested commits + /// + [Obsolete($"Use client.{nameof(Version)}.{nameof(VersionResource.GetVersions)}")] public async Task> StreamGetCommits( string streamId, int limit = 10, @@ -88,6 +94,8 @@ public async Task> StreamGetCommits( /// /// /// The commit id. + /// + [Obsolete($"Use client.{nameof(VersionResource)}.{nameof(VersionResource.Create)}")] public async Task CommitCreate(CommitCreateInput commitInput, CancellationToken cancellationToken = default) { var request = new GraphQLRequest @@ -106,6 +114,8 @@ public async Task CommitCreate(CommitCreateInput commitInput, Cancellati /// /// /// The stream's id. + /// + [Obsolete($"Use client.{nameof(VersionResource)}.{nameof(VersionResource.Update)}")] public async Task CommitUpdate(CommitUpdateInput commitInput, CancellationToken cancellationToken = default) { var request = new GraphQLRequest @@ -124,6 +134,8 @@ public async Task CommitUpdate(CommitUpdateInput commitInput, Cancellation /// /// /// + /// + [Obsolete($"Use client.{nameof(VersionResource)}.{nameof(VersionResource.Delete)}")] public async Task CommitDelete(CommitDeleteInput commitInput, CancellationToken cancellationToken = default) { var request = new GraphQLRequest @@ -143,6 +155,8 @@ public async Task CommitDelete(CommitDeleteInput commitInput, Cancellation /// /// /// + /// + [Obsolete($"Use client.{nameof(VersionResource)}.{nameof(VersionResource.Received)}")] public async Task CommitReceived( CommitReceivedInput commitReceivedInput, CancellationToken cancellationToken = default diff --git a/Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.ObjectOperations.cs b/Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.ObjectOperations.cs similarity index 99% rename from Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.ObjectOperations.cs rename to Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.ObjectOperations.cs index 79e9f8b90f..0f842bc5e6 100644 --- a/Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.ObjectOperations.cs +++ b/Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.ObjectOperations.cs @@ -6,6 +6,7 @@ namespace Speckle.Core.Api; public partial class Client { + //TODO: API Gap /// /// Gets data about the requested Speckle object from a stream. /// diff --git a/Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.ServerOperations.cs b/Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.ServerOperations.cs similarity index 96% rename from Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.ServerOperations.cs rename to Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.ServerOperations.cs index 2244822d73..1ceb07fab1 100644 --- a/Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.ServerOperations.cs +++ b/Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.ServerOperations.cs @@ -3,6 +3,7 @@ using System.Threading; using System.Threading.Tasks; using GraphQL; +using Speckle.Core.Api.GraphQL.Models.Responses; using Speckle.Core.Logging; namespace Speckle.Core.Api; diff --git a/Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.StreamOperations.cs b/Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.StreamOperations.cs similarity index 87% rename from Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.StreamOperations.cs rename to Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.StreamOperations.cs index f90a6849f3..dd7be354a3 100644 --- a/Core/Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.StreamOperations.cs +++ b/Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.StreamOperations.cs @@ -4,6 +4,9 @@ using System.Threading; using System.Threading.Tasks; using GraphQL; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Models.Responses; +using Speckle.Core.Api.GraphQL.Resources; using Speckle.Core.Logging; namespace Speckle.Core.Api; @@ -34,11 +37,11 @@ public async Task IsStreamAccessible(string id, CancellationToken cancella return stream.id == id; } - catch (SpeckleGraphQLForbiddenException) + catch (SpeckleGraphQLForbiddenException) { return false; } - catch (SpeckleGraphQLStreamNotFoundException) + catch (SpeckleGraphQLStreamNotFoundException) { return false; } @@ -52,6 +55,9 @@ public async Task IsStreamAccessible(string id, CancellationToken cancella /// Max number of branches to retrieve /// /// + /// + /// + [Obsolete($"Use client.{nameof(Project)}.{nameof(ProjectResource.GetWithModels)}")] public async Task StreamGet(string id, int branchesLimit = 10, CancellationToken cancellationToken = default) { var request = new GraphQLRequest @@ -100,6 +106,8 @@ public async Task StreamGet(string id, int branchesLimit = 10, Cancellat /// Max number of streams to return /// /// + /// + [Obsolete($"Use client.{nameof(ActiveUser)}.{nameof(ActiveUserResource.GetProjects)}")] public async Task> StreamsGet(int limit = 10, CancellationToken cancellationToken = default) { var request = new GraphQLRequest @@ -142,7 +150,7 @@ public async Task> StreamsGet(int limit = 10, CancellationToken can }}" }; - var res = await ExecuteGraphQLRequest(request, cancellationToken).ConfigureAwait(false); + var res = await ExecuteGraphQLRequest(request, cancellationToken).ConfigureAwait(false); if (res?.activeUser == null) { @@ -154,6 +162,7 @@ public async Task> StreamsGet(int limit = 10, CancellationToken can return res.activeUser.streams.items; } + //TODO: API GAP /// /// Gets all favorite streams for the current user /// @@ -201,7 +210,7 @@ public async Task> FavoriteStreamsGet(int limit = 10, CancellationT }} }}" }; - return (await ExecuteGraphQLRequest(request, cancellationToken).ConfigureAwait(false)) + return (await ExecuteGraphQLRequest(request, cancellationToken).ConfigureAwait(false)) .activeUser .favoriteStreams .items; @@ -214,6 +223,8 @@ public async Task> FavoriteStreamsGet(int limit = 10, CancellationT /// Max number of streams to return /// /// + /// + [Obsolete($"Use client.{nameof(ActiveUser)}.{nameof(ActiveUserResource.GetProjects)}")] public async Task> StreamSearch( string query, int limit = 10, @@ -258,6 +269,8 @@ public async Task> StreamSearch( /// /// /// The stream's id. + /// + [Obsolete($"Use client.{nameof(Project)}.{nameof(ProjectResource.Create)}")] public async Task StreamCreate(StreamCreateInput streamInput, CancellationToken cancellationToken = default) { var request = new GraphQLRequest @@ -275,6 +288,8 @@ public async Task StreamCreate(StreamCreateInput streamInput, Cancellati /// Note: the id field needs to be a valid stream id. /// /// The stream's id. + /// + [Obsolete($"Use client.{nameof(Project)}.{nameof(ProjectResource.Update)}")] public async Task StreamUpdate(StreamUpdateInput streamInput, CancellationToken cancellationToken = default) { var request = new GraphQLRequest @@ -294,6 +309,8 @@ public async Task StreamUpdate(StreamUpdateInput streamInput, Cancellation /// Id of the stream to be deleted /// /// + /// + [Obsolete($"Use client.{nameof(Project)}.{nameof(ProjectResource.Delete)}")] public async Task StreamDelete(string id, CancellationToken cancellationToken = default) { var request = new GraphQLRequest @@ -336,6 +353,8 @@ public async Task StreamRevokePermission( /// /// /// + /// + [Obsolete($"Use client.{nameof(Project)}.{nameof(ProjectResource.UpdateRole)}")] public async Task StreamUpdatePermission( StreamPermissionInput updatePermissionInput, CancellationToken cancellationToken = default @@ -362,6 +381,8 @@ mutation streamUpdatePermission($permissionParams: StreamUpdatePermissionInput!) /// /// /// + /// + [Obsolete($"Use client.{nameof(Project)}.{nameof(ProjectResource.GetWithTeam)}")] public async Task StreamGetPendingCollaborators( string streamId, CancellationToken cancellationToken = default @@ -396,6 +417,8 @@ public async Task StreamGetPendingCollaborators( /// /// /// + /// + [Obsolete($"Use client.{nameof(ProjectInvite)}.{nameof(ProjectInviteResource.Create)}")] public async Task StreamInviteCreate( StreamInviteCreateInput inviteCreateInput, CancellationToken cancellationToken = default @@ -427,6 +450,8 @@ mutation streamInviteCreate($input: StreamInviteCreateInput!) { /// Id of the invite to cancel /// /// + /// + [Obsolete($"Use client.{nameof(ProjectInvite)}.{nameof(ProjectInviteResource.Cancel)}")] public async Task StreamInviteCancel( string streamId, string inviteId, @@ -456,6 +481,8 @@ mutation streamInviteCancel( $streamId: String!, $inviteId: String! ) { /// /// /// + /// + [Obsolete($"Use client.{nameof(ProjectInvite)}.{nameof(ProjectInviteResource.Use)}")] public async Task StreamInviteUse( string streamId, string token, @@ -482,6 +509,13 @@ mutation streamInviteUse( $accept: Boolean!, $streamId: String!, $token: String! return (bool)res["streamInviteUse"]; } + /// + /// + /// + /// + /// + /// + [Obsolete($"Use client.{nameof(ActiveUser)}.{nameof(ActiveUserResource.ProjectInvites)}")] public async Task> GetAllPendingInvites(CancellationToken cancellationToken = default) { var request = new GraphQLRequest diff --git a/Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.UserOperations.cs b/Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.UserOperations.cs new file mode 100644 index 0000000000..66569e3b94 --- /dev/null +++ b/Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.UserOperations.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Resources; + +namespace Speckle.Core.Api; + +public partial class Client +{ + /// + /// Gets the currently active user profile. + /// + /// + /// + /// + [Obsolete($"Use client.{nameof(ActiveUser)}.{nameof(ActiveUserResource.Get)}")] + public async Task ActiveUserGet(CancellationToken cancellationToken = default) + { + return await ActiveUser.Get(cancellationToken).ConfigureAwait(false); + } + + /// + /// Get another user's profile by its user id. + /// + /// Id of the user you are looking for + /// + /// + /// + [Obsolete($"Use client.{nameof(OtherUser)}.{nameof(OtherUserResource.Get)}")] + public async Task OtherUserGet(string id, CancellationToken cancellationToken = default) + { + return await OtherUser.Get(id, cancellationToken).ConfigureAwait(false); + } + + /// + /// Searches for a user on the server. + /// + /// String to search for. Must be at least 3 characters + /// Max number of users to return + /// + /// + [Obsolete($"Use client.{nameof(OtherUser)}.{nameof(OtherUserResource.UserSearch)}")] + public async Task> UserSearch( + string query, + int limit = 10, + CancellationToken cancellationToken = default + ) + { + var res = await OtherUser.UserSearch(query, limit, cancellationToken: cancellationToken).ConfigureAwait(false); + return res.items; + } +} diff --git a/Core/Core/Api/GraphQL/Client.Subscriptions/Client.Subscriptions.Branch.cs b/Core/Core/Api/GraphQL/Legacy/Client.Subscriptions/Client.Subscriptions.Branch.cs similarity index 100% rename from Core/Core/Api/GraphQL/Client.Subscriptions/Client.Subscriptions.Branch.cs rename to Core/Core/Api/GraphQL/Legacy/Client.Subscriptions/Client.Subscriptions.Branch.cs diff --git a/Core/Core/Api/GraphQL/Client.Subscriptions/Client.Subscriptions.Commit.cs b/Core/Core/Api/GraphQL/Legacy/Client.Subscriptions/Client.Subscriptions.Commit.cs similarity index 100% rename from Core/Core/Api/GraphQL/Client.Subscriptions/Client.Subscriptions.Commit.cs rename to Core/Core/Api/GraphQL/Legacy/Client.Subscriptions/Client.Subscriptions.Commit.cs diff --git a/Core/Core/Api/GraphQL/Client.Subscriptions/Client.Subscriptions.Stream.cs b/Core/Core/Api/GraphQL/Legacy/Client.Subscriptions/Client.Subscriptions.Stream.cs similarity index 100% rename from Core/Core/Api/GraphQL/Client.Subscriptions/Client.Subscriptions.Stream.cs rename to Core/Core/Api/GraphQL/Legacy/Client.Subscriptions/Client.Subscriptions.Stream.cs diff --git a/Core/Core/Api/GraphQL/Models.cs b/Core/Core/Api/GraphQL/Legacy/LegacyGraphQLModels.cs similarity index 59% rename from Core/Core/Api/GraphQL/Models.cs rename to Core/Core/Api/GraphQL/Legacy/LegacyGraphQLModels.cs index e1edf79595..5dcff259f6 100644 --- a/Core/Core/Api/GraphQL/Models.cs +++ b/Core/Core/Api/GraphQL/Legacy/LegacyGraphQLModels.cs @@ -1,12 +1,20 @@ #nullable disable using System; using System.Collections.Generic; -using System.Text.Json.Serialization; +using Speckle.Core.Api.GraphQL.Enums; +using Speckle.Core.Api.GraphQL.Models; namespace Speckle.Core.Api; #region inputs +internal static class DeprecationMessages +{ + public const string FE2_DEPRECATION_MESSAGE = + $"Stream/Branch/Commit API is now deprecated, Use the new Project/Model/Version API functions in {nameof(Client)}"; +} + +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class StreamCreateInput { public string name { get; set; } @@ -14,6 +22,7 @@ public class StreamCreateInput public bool isPublic { get; set; } = true; } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class StreamUpdateInput { public string id { get; set; } @@ -22,6 +31,7 @@ public class StreamUpdateInput public bool isPublic { get; set; } = true; } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class StreamPermissionInput { public string streamId { get; set; } @@ -29,12 +39,14 @@ public class StreamPermissionInput public string role { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class StreamRevokePermissionInput { public string streamId { get; set; } public string userId { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class StreamInviteCreateInput { public string streamId { get; set; } @@ -44,6 +56,7 @@ public class StreamInviteCreateInput public string role { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class BranchCreateInput { public string streamId { get; set; } @@ -51,6 +64,7 @@ public class BranchCreateInput public string description { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class BranchUpdateInput { public string streamId { get; set; } @@ -59,6 +73,7 @@ public class BranchUpdateInput public string description { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class BranchDeleteInput { public string streamId { get; set; } @@ -79,6 +94,7 @@ public class CommitCreateInput public List previousCommitIds { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class CommitUpdateInput { public string streamId { get; set; } @@ -86,12 +102,14 @@ public class CommitUpdateInput public string message { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class CommitDeleteInput { public string streamId { get; set; } public string id { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class CommitReceivedInput { public string streamId { get; set; } @@ -102,6 +120,7 @@ public class CommitReceivedInput #endregion +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class Stream { public string id { get; set; } @@ -146,6 +165,7 @@ public override string ToString() } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class Collaborator { public string id { get; set; } @@ -159,24 +179,13 @@ public override string ToString() } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class StreamInvitesResponse { public List streamInvites { get; set; } } -public class PendingStreamCollaborator -{ - public string id { get; set; } - public string inviteId { get; set; } - public string streamId { get; set; } - public string streamName { get; set; } - public string title { get; set; } - public string role { get; set; } - public User invitedBy { get; set; } - public User user { get; set; } - public string token { get; set; } -} - +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class Branches { public int totalCount { get; set; } @@ -184,6 +193,7 @@ public class Branches public List items { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class Commits { public int totalCount { get; set; } @@ -191,6 +201,7 @@ public class Commits public List items { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class Commit { public string id { get; set; } @@ -246,6 +257,7 @@ public class InfoCommit public string branchName { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class SpeckleObject { public string id { get; set; } @@ -255,6 +267,7 @@ public class SpeckleObject public DateTime createdAt { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class Branch { public string id { get; set; } @@ -268,6 +281,7 @@ public override string ToString() } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class Streams { public int totalCount { get; set; } @@ -275,51 +289,14 @@ public class Streams public List items { get; set; } } -public class UserBase -{ - public string id { get; set; } - public string name { get; set; } - public string bio { get; set; } - public string company { get; set; } - public string avatar { get; set; } - public bool verified { get; set; } - public string role { get; set; } - public Streams streams { get; set; } -} - -public class LimitedUser : UserBase -{ - public override string ToString() - { - return $"Other user profile: ({name} | {id})"; - } -} - -public class User : UserBase -{ - public string email { get; set; } - public Streams favoriteStreams { get; set; } - - public override string ToString() - { - return $"User ({email} | {name} | {id})"; - } -} - +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class Resource { public string resourceId { get; set; } public ResourceType resourceType { get; set; } } -public enum ResourceType -{ - commit, - stream, - @object, - comment -} - +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class Location { public double x { get; set; } @@ -327,103 +304,33 @@ public class Location public double z { get; set; } } -public class UserData -{ - public User user { get; set; } -} - -/// -/// GraphQL DTO model for active user data -/// -public class ActiveUserData -{ - /// - /// User profile of the active user. - /// - public User activeUser { get; set; } -} - -/// -/// GraphQL DTO model for limited user data. Mostly referring to other user's profile. -/// -public class LimitedUserData -{ - /// - /// The limited user profile of another (non active user) - /// - public LimitedUser otherUser { get; set; } -} - +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class UserSearchData { public UserSearch userSearch { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class UserSearch { public string cursor { get; set; } public List items { get; set; } } -public class ServerInfoResponse -{ - // TODO: server and user models are duplicated here and in Core.Credentials.Responses - // a bit weird and unnecessary - shouldn't both Credentials and Api share the same models since they're - // all server models that should be consistent? am creating a new obj here as to not reference Credentials in - // this file but it should prob be refactored in the futrue - public ServerInfo serverInfo { get; set; } -} - -// TODO: prob remove and bring one level up and shared w Core.Credentials -public class ServerInfo -{ - public string name { get; set; } - public string company { get; set; } - public string version { get; set; } - public string adminContact { get; set; } - public string description { get; set; } - - /// - /// This field is not returned from the GQL API, - /// it should populated on construction from the response headers. - /// see - /// - public bool frontend2 { get; set; } - - /// - /// This field is not returned from the GQL API, - /// it should populated on construction. - /// see - /// - public string url { get; set; } - - public ServerMigration migration { get; set; } -} - -public class ServerMigration -{ - /// - /// New URI where this server is now deployed - /// - public Uri movedTo { get; set; } - - /// - /// Previous URI where this server used to be deployed - /// - public Uri movedFrom { get; set; } -} - +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class StreamData { public Stream stream { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class StreamsData { public Streams streams { get; set; } } #region comments +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class Comments { public int totalCount { get; set; } @@ -431,16 +338,18 @@ public class Comments public List items { get; set; } } -public class CommentData +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] +public sealed class CommentData { - public Comments comments { get; set; } - public List camPos { get; set; } - public object filters { get; set; } - public Location location { get; set; } - public object selection { get; set; } - public object sectionBox { get; set; } + public Comments comments { get; init; } + public List camPos { get; init; } + public object filters { get; init; } + public Location location { get; init; } + public object selection { get; init; } + public object sectionBox { get; init; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class CommentItem { public string id { get; set; } @@ -457,6 +366,7 @@ public class CommentItem public List resources { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class ContentContent { public string Type { get; set; } @@ -465,116 +375,28 @@ public class ContentContent public string Text { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class CommentsData { public Comments comments { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class CommentItemData { public CommentItem comment { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class CommentActivityMessage { public string type { get; set; } public CommentItem comment { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class CommentActivityResponse { public CommentActivityMessage commentActivity { get; set; } } #endregion - -#region manager api - -public class Connector -{ - public List Versions { get; set; } = new(); -} - -public class Version -{ - public Version(string number, string url, Os os = Os.Win, Architecture architecture = Architecture.Any) - { - Number = number; - Url = url; - Date = DateTime.Now; - Prerelease = Number.Contains("-"); - Os = os; - Architecture = architecture; - } - - public string Number { get; set; } - public string Url { get; set; } - public Os Os { get; set; } - public Architecture Architecture { get; set; } = Architecture.Any; - public DateTime Date { get; set; } - - [JsonIgnore] - public string DateTimeAgo => Helpers.TimeAgo(Date); - - public bool Prerelease { get; set; } -} - -/// -/// OS -/// NOTE: do not edit order and only append new items as they are serialized to ints -/// -public enum Os -{ - Win, //0 - OSX, //1 - Linux, //2 - Any //3 -} - -/// -/// Architecture -/// NOTE: do not edit order and only append new items as they are serialized to ints -/// -public enum Architecture -{ - Any, //0 - Arm, //1 - Intel //2 -} - -//GHOST API -public class Meta -{ - public Pagination pagination { get; set; } -} - -public class Pagination -{ - public int page { get; set; } - public string limit { get; set; } - public int pages { get; set; } - public int total { get; set; } - public object next { get; set; } - public object prev { get; set; } -} - -public class Tags -{ - public List tags { get; set; } - public Meta meta { get; set; } -} - -public class Tag -{ - public string id { get; set; } - public string name { get; set; } - public string slug { get; set; } - public string description { get; set; } - public string feature_image { get; set; } - public string visibility { get; set; } - public string codeinjection_head { get; set; } - public object codeinjection_foot { get; set; } - public object canonical_url { get; set; } - public string accent_color { get; set; } - public string url { get; set; } -} -#endregion diff --git a/Core/Core/Api/GraphQL/Legacy/Manager.cs b/Core/Core/Api/GraphQL/Legacy/Manager.cs new file mode 100644 index 0000000000..94c1585efb --- /dev/null +++ b/Core/Core/Api/GraphQL/Legacy/Manager.cs @@ -0,0 +1,98 @@ +#nullable disable +using System; +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Speckle.Core.Api.GraphQL; + +#region manager api + +public class Connector +{ + public List Versions { get; set; } = new(); +} + +public class ConnectorVersion +{ + public ConnectorVersion(string number, string url, Os os = Os.Win, Architecture architecture = Architecture.Any) + { + Number = number; + Url = url; + Date = DateTime.Now; + Prerelease = Number.Contains("-"); + Os = os; + Architecture = architecture; + } + + public string Number { get; set; } + public string Url { get; set; } + public Os Os { get; set; } + public Architecture Architecture { get; set; } = Architecture.Any; + public DateTime Date { get; set; } + + [JsonIgnore] + public string DateTimeAgo => Helpers.TimeAgo(Date); + + public bool Prerelease { get; set; } +} + +/// +/// OS +/// NOTE: do not edit order and only append new items as they are serialized to ints +/// +public enum Os +{ + Win, //0 + OSX, //1 + Linux, //2 + Any //3 +} + +/// +/// Architecture +/// NOTE: do not edit order and only append new items as they are serialized to ints +/// +public enum Architecture +{ + Any, //0 + Arm, //1 + Intel //2 +} + +//GHOST API +public class Meta +{ + public Pagination pagination { get; set; } +} + +public class Pagination +{ + public int page { get; set; } + public string limit { get; set; } + public int pages { get; set; } + public int total { get; set; } + public object next { get; set; } + public object prev { get; set; } +} + +public class Tags +{ + public List tags { get; set; } + public Meta meta { get; set; } +} + +public class Tag +{ + public string id { get; set; } + public string name { get; set; } + public string slug { get; set; } + public string description { get; set; } + public string feature_image { get; set; } + public string visibility { get; set; } + public string codeinjection_head { get; set; } + public object codeinjection_foot { get; set; } + public object canonical_url { get; set; } + public string accent_color { get; set; } + public string url { get; set; } +} +#endregion diff --git a/Core/Core/Api/GraphQL/SubscriptionModels.cs b/Core/Core/Api/GraphQL/Legacy/SubscriptionModels.cs similarity index 74% rename from Core/Core/Api/GraphQL/SubscriptionModels.cs rename to Core/Core/Api/GraphQL/Legacy/SubscriptionModels.cs index f1b253b610..f330899f23 100644 --- a/Core/Core/Api/GraphQL/SubscriptionModels.cs +++ b/Core/Core/Api/GraphQL/Legacy/SubscriptionModels.cs @@ -5,6 +5,7 @@ namespace Speckle.Core.Api.SubscriptionModels; #region streams +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class StreamInfo { public string id { get; set; } @@ -13,16 +14,19 @@ public class StreamInfo public string sharedBy { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class UserStreamAddedResult { public StreamInfo userStreamAdded { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class StreamUpdatedResult { public StreamInfo streamUpdated { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class UserStreamRemovedResult { public StreamInfo userStreamRemoved { get; set; } @@ -31,6 +35,7 @@ public class UserStreamRemovedResult #region branches +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class BranchInfo { public string id { get; set; } @@ -40,16 +45,19 @@ public class BranchInfo public string authorId { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class BranchCreatedResult { public BranchInfo branchCreated { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class BranchUpdatedResult { public BranchInfo branchUpdated { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class BranchDeletedResult { public BranchInfo branchDeleted { get; set; } @@ -58,6 +66,7 @@ public class BranchDeletedResult #region commits +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class CommitInfo { public string id { get; set; } @@ -74,16 +83,19 @@ public class CommitInfo public IList previousCommitIds { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class CommitCreatedResult { public CommitInfo commitCreated { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class CommitUpdatedResult { public CommitInfo commitUpdated { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class CommitDeletedResult { public CommitInfo commitDeleted { get; set; } diff --git a/Core/Core/Api/GraphQL/Models/Collections.cs b/Core/Core/Api/GraphQL/Models/Collections.cs new file mode 100644 index 0000000000..3e4738aad0 --- /dev/null +++ b/Core/Core/Api/GraphQL/Models/Collections.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; + +namespace Speckle.Core.Api.GraphQL.Models; + +public class ResourceCollection +{ + public int totalCount { get; init; } + + public List items { get; init; } + + public string? cursor { get; init; } +} + +public sealed class CommentReplyAuthorCollection : ResourceCollection { } + +public sealed class ProjectCommentCollection : ResourceCollection +{ + public int totalArchivedCount { get; init; } +} diff --git a/Core/Core/Api/GraphQL/Models/Comment.cs b/Core/Core/Api/GraphQL/Models/Comment.cs new file mode 100644 index 0000000000..75da443dd3 --- /dev/null +++ b/Core/Core/Api/GraphQL/Models/Comment.cs @@ -0,0 +1,25 @@ +#nullable disable + +using System; +using System.Collections.Generic; + +namespace Speckle.Core.Api.GraphQL.Models; + +public sealed class Comment +{ + public bool archived { get; init; } + public LimitedUser author { get; init; } + public string authorId { get; init; } + public DateTime createdAt { get; init; } + public bool hasParent { get; init; } + public string id { get; init; } + public Comment parent { get; init; } + public string rawText { get; init; } + public ResourceCollection replies { get; init; } + public CommentReplyAuthorCollection replyAuthors { get; init; } + public List resources { get; init; } + public string screenshot { get; init; } + public DateTime updatedAt { get; init; } + public DateTime? viewedAt { get; init; } + public List viewerResources { get; init; } +} diff --git a/Core/Core/Api/GraphQL/Models/FileUpload.cs b/Core/Core/Api/GraphQL/Models/FileUpload.cs new file mode 100644 index 0000000000..8327e24817 --- /dev/null +++ b/Core/Core/Api/GraphQL/Models/FileUpload.cs @@ -0,0 +1,30 @@ +#nullable disable + +using System; +using Speckle.Core.Api.GraphQL.Enums; + +namespace Speckle.Core.Api.GraphQL.Models; + +public sealed class FileUpload +{ + public string convertedCommitId { get; init; } + public DateTime convertedLastUpdate { get; init; } + public FileUploadConversionStatus convertedStatus { get; init; } + public string convertedVersionId { get; init; } + public string fileName { get; init; } + public int fileSize { get; init; } + public string fileType { get; init; } + public string id { get; init; } + public Model model { get; init; } + public string modelName { get; init; } + public string projectId { get; init; } + public bool uploadComplete { get; init; } + public DateTime uploadDate { get; init; } + public string userId { get; init; } + + [Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] + public string branchName { get; init; } + + [Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] + public string streamId { get; init; } +} diff --git a/Core/Core/Api/GraphQL/Models/Model.cs b/Core/Core/Api/GraphQL/Models/Model.cs new file mode 100644 index 0000000000..3c779960fa --- /dev/null +++ b/Core/Core/Api/GraphQL/Models/Model.cs @@ -0,0 +1,23 @@ +#nullable disable +using System; + +using System.Collections.Generic; + +namespace Speckle.Core.Api.GraphQL.Models; + +public sealed class Model +{ + public LimitedUser author { get; init; } + public List childrenTree { get; init; } + public ResourceCollection commentThreads { get; init; } + public DateTime createdAt { get; init; } + public string description { get; init; } + public string displayName { get; init; } + public string id { get; init; } + public string name { get; init; } + public List pendingImportedVersions { get; init; } + public Uri previewUrl { get; init; } + public DateTime updatedAt { get; init; } + public ResourceCollection versions { get; init; } + public Version version { get; init; } +} diff --git a/Core/Core/Api/GraphQL/Models/ModelsTreeItem.cs b/Core/Core/Api/GraphQL/Models/ModelsTreeItem.cs new file mode 100644 index 0000000000..f0d6e49966 --- /dev/null +++ b/Core/Core/Api/GraphQL/Models/ModelsTreeItem.cs @@ -0,0 +1,17 @@ +#nullable disable + +using System; +using System.Collections.Generic; + +namespace Speckle.Core.Api.GraphQL.Models; + +public sealed class ModelsTreeItem +{ + public List children { get; init; } + public string fullName { get; init; } + public bool hasChildren { get; init; } + public string id { get; init; } + public Model model { get; init; } + public string name { get; init; } + public DateTime updatedAt { get; init; } +} diff --git a/Core/Core/Api/GraphQL/Models/PendingStreamCollaborator.cs b/Core/Core/Api/GraphQL/Models/PendingStreamCollaborator.cs new file mode 100644 index 0000000000..805c0231ba --- /dev/null +++ b/Core/Core/Api/GraphQL/Models/PendingStreamCollaborator.cs @@ -0,0 +1,25 @@ +#nullable disable +using System; + +namespace Speckle.Core.Api.GraphQL.Models; + +public sealed class PendingStreamCollaborator +{ + public string id { get; init; } + public string inviteId { get; init; } + + public string projectId { get; init; } + + public string projectName { get; init; } + public string title { get; init; } + public string role { get; init; } + public LimitedUser invitedBy { get; init; } + public LimitedUser user { get; init; } + public string token { get; init; } + + [Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] + public string streamId { get; init; } + + [Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] + public string streamName { get; init; } +} diff --git a/Core/Core/Api/GraphQL/Models/Project.cs b/Core/Core/Api/GraphQL/Models/Project.cs new file mode 100644 index 0000000000..537ceb4d75 --- /dev/null +++ b/Core/Core/Api/GraphQL/Models/Project.cs @@ -0,0 +1,30 @@ +#nullable disable +using System; +using System.Collections.Generic; +using Speckle.Core.Api.GraphQL.Enums; + +namespace Speckle.Core.Api.GraphQL.Models; + +public sealed class Project +{ + public bool AllowPublicComments { get; init; } + public ProjectCommentCollection commentThreads { get; init; } + public DateTime createdAt { get; init; } + public string description { get; init; } + public string id { get; init; } + public List invitedTeam { get; init; } + public ResourceCollection models { get; init; } + public string name { get; init; } + public List pendingImportedModels { get; init; } + public string role { get; init; } + public List sourceApps { get; init; } + public List team { get; init; } + public DateTime updatedAt { get; init; } + public ProjectVisibility visibility { get; init; } + + public List viewerResources { get; init; } + public ResourceCollection versions { get; init; } + public Model model { get; init; } + public List modelChildrenTree { get; init; } + public ResourceCollection modelsTree { get; init; } +} diff --git a/Core/Core/Api/GraphQL/Models/ProjectCollaborator.cs b/Core/Core/Api/GraphQL/Models/ProjectCollaborator.cs new file mode 100644 index 0000000000..ea0628316a --- /dev/null +++ b/Core/Core/Api/GraphQL/Models/ProjectCollaborator.cs @@ -0,0 +1,9 @@ +#nullable disable + +namespace Speckle.Core.Api.GraphQL.Models; + +public sealed class ProjectCollaborator +{ + public string role { get; init; } + public LimitedUser user { get; init; } +} diff --git a/Core/Core/Api/GraphQL/Models/ResourceIdentifier.cs b/Core/Core/Api/GraphQL/Models/ResourceIdentifier.cs new file mode 100644 index 0000000000..670111caf9 --- /dev/null +++ b/Core/Core/Api/GraphQL/Models/ResourceIdentifier.cs @@ -0,0 +1,10 @@ +#nullable disable +using Speckle.Core.Api.GraphQL.Enums; + +namespace Speckle.Core.Api.GraphQL.Models; + +public sealed class ResourceIdentifier +{ + public string resourceId { get; init; } + public ResourceType resourceType { get; init; } +} diff --git a/Core/Core/Api/GraphQL/Models/Responses/MutationResponses.cs b/Core/Core/Api/GraphQL/Models/Responses/MutationResponses.cs new file mode 100644 index 0000000000..b456d05ea0 --- /dev/null +++ b/Core/Core/Api/GraphQL/Models/Responses/MutationResponses.cs @@ -0,0 +1,42 @@ +namespace Speckle.Core.Api.GraphQL.Models.Responses; + +#nullable disable +internal sealed class ProjectMutation +{ + public Project create { get; init; } + public Project update { get; init; } + public bool delete { get; init; } + public ProjectInviteMutation invites { get; init; } + + public Project updateRole { get; init; } +} + +internal sealed class ProjectInviteMutation +{ + public Project create { get; init; } + public bool use { get; init; } + public Project cancel { get; init; } +} + +internal sealed class ModelMutation +{ + public Model create { get; init; } + public Model update { get; init; } + public bool delete { get; init; } +} + +internal sealed class VersionMutation +{ + public bool delete { get; init; } + public Model moveToModel { get; init; } + public Version update { get; init; } +} + +internal sealed class CommentMutation +{ + public bool archive { get; init; } + public Comment create { get; init; } + public Comment edit { get; init; } + public bool markViewed { get; init; } + public Comment reply { get; init; } +} diff --git a/Core/Core/Api/GraphQL/Models/Responses/Responses.cs b/Core/Core/Api/GraphQL/Models/Responses/Responses.cs new file mode 100644 index 0000000000..3a16327bed --- /dev/null +++ b/Core/Core/Api/GraphQL/Models/Responses/Responses.cs @@ -0,0 +1,30 @@ +using Speckle.Newtonsoft.Json; + +namespace Speckle.Core.Api.GraphQL.Models.Responses; + +// This file holds simple records that represent the root GraphQL response data +// For this reason, we're keeping them internal, allowing us to be flexible without the concern for breaking. +// It also exposes fewer similarly named types to dependent assemblies + +internal record ProjectResponse([property: JsonRequired] Project project); + +internal record ActiveUserResponse(UserInfo? activeUser); + +internal record LimitedUserResponse(LimitedUser? otherUser); + +internal record ServerInfoResponse([property: JsonRequired] ServerInfo serverInfo); + +internal record ProjectMutationResponse([property: JsonRequired] ProjectMutation projectMutations); + +internal record ModelMutationResponse([property: JsonRequired] ModelMutation modelMutations); + +internal record VersionMutationResponse([property: JsonRequired] VersionMutation versionMutations); + +internal record ProjectInviteResponse(PendingStreamCollaborator? projectInvite); + +internal record UserSearchResponse([property: JsonRequired] ResourceCollection userSearch); + +//All of the above records could be replaced by either RequiredResponse or OptionalResponse, if we use an alias (see https://www.baeldung.com/graphql-field-name) +internal record RequiredResponse([property: JsonRequired] T data); + +internal record OptionalResponse(T? data); diff --git a/Core/Core/Api/GraphQL/Models/ServerInfo.cs b/Core/Core/Api/GraphQL/Models/ServerInfo.cs new file mode 100644 index 0000000000..11f053ebe8 --- /dev/null +++ b/Core/Core/Api/GraphQL/Models/ServerInfo.cs @@ -0,0 +1,42 @@ +#nullable disable +using System; + +namespace Speckle.Core.Api.GraphQL.Models; + +public sealed class ServerInfo +{ + public string name { get; init; } + public string company { get; init; } + public string version { get; init; } + public string adminContact { get; init; } + public string description { get; init; } + + /// + /// This field is not returned from the GQL API, + /// it should be populated after construction from the response headers. + /// see + /// + public bool frontend2 { get; set; } + + /// + /// This field is not returned from the GQL API, + /// it should be populated after construction. + /// see + /// + public string url { get; set; } + + public ServerMigration migration { get; init; } +} + +public sealed class ServerMigration +{ + /// + /// New URI where this server is now deployed + /// + public Uri movedTo { get; set; } + + /// + /// Previous URI where this server used to be deployed + /// + public Uri movedFrom { get; set; } +} diff --git a/Core/Core/Api/GraphQL/Models/SubscriptionMessages.cs b/Core/Core/Api/GraphQL/Models/SubscriptionMessages.cs new file mode 100644 index 0000000000..0ce6dc5607 --- /dev/null +++ b/Core/Core/Api/GraphQL/Models/SubscriptionMessages.cs @@ -0,0 +1,84 @@ +using System; +using Speckle.Core.Api.GraphQL.Enums; +using Speckle.Newtonsoft.Json; + +namespace Speckle.Core.Api.GraphQL.Models; + +public sealed class UserProjectsUpdatedMessage : EventArgs +{ + [JsonRequired] + public string id { get; init; } + + [JsonRequired] + public UserProjectsUpdatedMessageType type { get; init; } + + public Project? project { get; init; } +} + +public sealed class ProjectCommentsUpdatedMessage : EventArgs +{ + [JsonRequired] + public string id { get; init; } + + [JsonRequired] + public ProjectCommentsUpdatedMessageType type { get; init; } + + public Comment? comment { get; init; } +} + +public sealed class ProjectFileImportUpdatedMessage : EventArgs +{ + [JsonRequired] + public string id { get; init; } + + [JsonRequired] + public ProjectFileImportUpdatedMessageType type { get; init; } + + public FileUpload? upload { get; init; } +} + +public sealed class ProjectModelsUpdatedMessage : EventArgs +{ + [JsonRequired] + public string id { get; init; } + + [JsonRequired] + public ProjectModelsUpdatedMessageType type { get; init; } + + public Model? model { get; init; } +} + +public sealed class ProjectPendingModelsUpdatedMessage : EventArgs +{ + [JsonRequired] + public string id { get; init; } + + [JsonRequired] + public ProjectPendingModelsUpdatedMessageType type { get; init; } + + public FileUpload? model { get; init; } +} + +public sealed class ProjectUpdatedMessage : EventArgs +{ + [JsonRequired] + public string id { get; init; } + + [JsonRequired] + public ProjectUpdatedMessageType type { get; init; } + + public Project? project { get; init; } +} + +public sealed class ProjectVersionsUpdatedMessage : EventArgs +{ + [JsonRequired] + public string id { get; init; } + + [JsonRequired] + public ProjectVersionsUpdatedMessageType type { get; init; } + + public string? modelId { get; init; } + + public Version? version { get; init; } +} diff --git a/Core/Core/Api/GraphQL/Models/UserInfo.cs b/Core/Core/Api/GraphQL/Models/UserInfo.cs new file mode 100644 index 0000000000..b9d8926c6d --- /dev/null +++ b/Core/Core/Api/GraphQL/Models/UserInfo.cs @@ -0,0 +1,55 @@ +#nullable disable +using System; +using System.Collections.Generic; + +namespace Speckle.Core.Api.GraphQL.Models; + +public abstract class UserBase +{ + public ResourceCollection activity { get; init; } + public string avatar { get; init; } + public string bio { get; init; } + public string company { get; set; } + public string id { get; init; } + public string name { get; init; } + public string role { get; init; } + + public ResourceCollection timeline { get; init; } + public int totalOwnedStreamsFavorites { get; init; } + public bool? verified { get; init; } + + [Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] + public ResourceCollection commits { get; init; } + + [Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] + public ResourceCollection streams { get; init; } +} + +public sealed class LimitedUser : UserBase +{ + public override string ToString() + { + return $"Other user profile: ({name} | {id})"; + } +} + +/// +/// Named "User" in GraphQL Schema +/// +public sealed class UserInfo : UserBase +{ + public DateTime? createdAt { get; init; } + public string email { get; init; } + public bool? hasPendingVerification { get; init; } + public bool? isOnboardingFinished { get; init; } + public List projectInvites { get; init; } + public ResourceCollection projects { get; init; } + + [Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] + public ResourceCollection favoriteStreams { get; init; } + + public override string ToString() + { + return $"User ({email} | {name} | {id})"; + } +} diff --git a/Core/Core/Api/GraphQL/Models/Version.cs b/Core/Core/Api/GraphQL/Models/Version.cs new file mode 100644 index 0000000000..1aa46b0dae --- /dev/null +++ b/Core/Core/Api/GraphQL/Models/Version.cs @@ -0,0 +1,18 @@ +#nullable disable + +using System; + +namespace Speckle.Core.Api.GraphQL.Models; + +public sealed class Version +{ + public LimitedUser authorUser { get; init; } + public ResourceCollection commentThreads { get; init; } + public DateTime createdAt { get; init; } + public string id { get; init; } + public string message { get; init; } + public Model model { get; init; } + public Uri previewUrl { get; init; } + public string referencedObject { get; init; } + public string sourceApplication { get; init; } +} diff --git a/Core/Core/Api/GraphQL/Models/ViewerResourceGroup.cs b/Core/Core/Api/GraphQL/Models/ViewerResourceGroup.cs new file mode 100644 index 0000000000..3ef11ed67f --- /dev/null +++ b/Core/Core/Api/GraphQL/Models/ViewerResourceGroup.cs @@ -0,0 +1,11 @@ +#nullable disable + +using System.Collections.Generic; + +namespace Speckle.Core.Api.GraphQL.Models; + +public class ViewerResourceGroup +{ + public string identifier { get; init; } + public List items { get; init; } +} diff --git a/Core/Core/Api/GraphQL/Models/ViewerResourceItem.cs b/Core/Core/Api/GraphQL/Models/ViewerResourceItem.cs new file mode 100644 index 0000000000..12fc34e29f --- /dev/null +++ b/Core/Core/Api/GraphQL/Models/ViewerResourceItem.cs @@ -0,0 +1,10 @@ +#nullable disable + +namespace Speckle.Core.Api.GraphQL.Models; + +public class ViewerResourceItem +{ + public string modelId { get; init; } + public string objectId { get; init; } + public string versionId { get; init; } +} diff --git a/Core/Core/Api/GraphQL/Resources/ActiveUserResource.cs b/Core/Core/Api/GraphQL/Resources/ActiveUserResource.cs new file mode 100644 index 0000000000..f3ae780447 --- /dev/null +++ b/Core/Core/Api/GraphQL/Resources/ActiveUserResource.cs @@ -0,0 +1,165 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using GraphQL; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Models.Responses; + +namespace Speckle.Core.Api.GraphQL.Resources; + +public sealed class ActiveUserResource +{ + private readonly ISpeckleGraphQLClient _client; + + internal ActiveUserResource(ISpeckleGraphQLClient client) + { + _client = client; + } + + /// + /// Gets the currently active user profile. + /// + /// + /// + /// the requested user, or null if the user does not exist (i.e. was initialised with an unauthenticated account) + /// + public async Task Get(CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + query User { + activeUser { + id, + email, + name, + bio, + company, + avatar, + verified, + profiles, + role, + } + } + """; + var request = new GraphQLRequest { Query = QUERY }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + return response.activeUser; + } + + /// Max number of projects to fetch + /// Optional cursor for pagination + /// Optional filter + /// + /// + /// + public async Task> GetProjects( + int limit = ServerLimits.DEFAULT_PAGINATION_REQUEST, + string? cursor = null, + UserProjectsFilter? filter = null, + CancellationToken cancellationToken = default + ) + { + //language=graphql + const string QUERY = """ + query User($limit : Int!, $cursor: String, $filter: UserProjectsFilter) { + activeUser { + projects(limit: $limit, cursor: $cursor, filter: $filter) { + totalCount + items { + id + name + description + visibility + allowPublicComments + role + createdAt + updatedAt + sourceApps + } + } + } + } + """; + var request = new GraphQLRequest + { + Query = QUERY, + Variables = new + { + limit, + cursor, + filter + } + }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + if (response.activeUser is null) + { + throw new SpeckleGraphQLException("GraphQL response indicated that the ActiveUser could not be found"); + } + + return response.activeUser.projects; + } + + /// + /// + /// + public async Task> ProjectInvites(CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + query ProjectInvites { + activeUser { + projectInvites { + id + inviteId + invitedBy { + avatar + bio + company + id + name + role + verified + } + projectId + projectName + role + streamId + streamName + title + token + user { + id, + name, + bio, + company, + verified, + role, + } + } + } + } + """; + + var request = new GraphQLRequest { Query = QUERY }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + if (response.activeUser is null) + { + throw new SpeckleGraphQLException("GraphQL response indicated that the ActiveUser could not be found"); + } + + return response.activeUser.projectInvites; + } +} diff --git a/Core/Core/Api/GraphQL/Resources/CommentResource.cs b/Core/Core/Api/GraphQL/Resources/CommentResource.cs new file mode 100644 index 0000000000..bd23ae8e5c --- /dev/null +++ b/Core/Core/Api/GraphQL/Resources/CommentResource.cs @@ -0,0 +1,279 @@ +using System.Threading; +using System.Threading.Tasks; +using GraphQL; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Models.Responses; + +namespace Speckle.Core.Api.GraphQL.Resources; + +public sealed class CommentResource +{ + private readonly ISpeckleGraphQLClient _client; + + internal CommentResource(ISpeckleGraphQLClient client) + { + _client = client; + } + + /// + /// Max number of comments to fetch + /// Optional cursor for pagination + /// Optional filter + /// Max number of comment replies to fetch + /// Optional cursor for pagination + /// + /// + /// + public async Task> GetProjectComments( + string projectId, + int limit = ServerLimits.DEFAULT_PAGINATION_REQUEST, + string? cursor = null, + ProjectCommentsFilter? filter = null, + int repliesLimit = ServerLimits.DEFAULT_PAGINATION_REQUEST, + string? repliesCursor = null, + CancellationToken cancellationToken = default + ) + { + //language=graphql + const string QUERY = """ + query CommentThreads($projectId: String!, $cursor: String, $limit: Int!, $filter: ProjectCommentsFilter, $repliesLimit: Int, $repliesCursor: String) { + project(id: $projectId) { + commentThreads(cursor: $cursor, limit: $limit, filter: $filter) { + cursor + totalArchivedCount + totalCount + items { + archived + authorId + createdAt + hasParent + id + rawText + replies(limit: $repliesLimit, cursor: $repliesCursor) { + cursor + items { + archived + authorId + createdAt + hasParent + id + rawText + updatedAt + viewedAt + } + totalCount + } + resources { + resourceId + resourceType + } + screenshot + updatedAt + viewedAt + viewerResources { + modelId + objectId + versionId + } + data + } + } + } + } + """; + + GraphQLRequest request = + new() + { + Query = QUERY, + Variables = new + { + projectId, + cursor, + limit, + filter, + repliesLimit, + repliesCursor, + } + }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + return response.project.commentThreads; + } + + /// + /// This function only exists here to be able to integration tests the queries. + /// The process of creating a comment is more complex and javascript specific than we can expose to our SDKs at this time. + /// + /// + /// + /// + /// + internal async Task Create(CreateCommentInput input, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation Mutation($input: CreateCommentInput!) { + data:commentMutations { + create(input: $input) { + archived + authorId + createdAt + hasParent + id + rawText + resources { + resourceId + resourceType + } + screenshot + updatedAt + viewedAt + viewerResources { + modelId + objectId + versionId + } + data + } + } + } + """; + GraphQLRequest request = new(QUERY, variables: new { input }); + var res = await _client + .ExecuteGraphQLRequest>(request, cancellationToken) + .ConfigureAwait(false); + return res.data.create; + } + + /// + /// + /// + /// + /// + internal async Task Edit(EditCommentInput input, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation Mutation($input: EditCommentInput!) { + data:commentMutations { + edit(input: $input) { + archived + authorId + createdAt + hasParent + id + rawText + resources { + resourceId + resourceType + } + screenshot + updatedAt + viewedAt + viewerResources { + modelId + objectId + versionId + } + data + } + } + } + """; + GraphQLRequest request = new(QUERY, variables: new { input }); + var res = await _client + .ExecuteGraphQLRequest>(request, cancellationToken) + .ConfigureAwait(false); + return res.data.edit; + } + + /// + /// + /// + /// + /// + public async Task Archive(string commentId, bool archive = true, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation Mutation($commentId: String!, $archive: Boolean!) { + data:commentMutations { + archive(commentId: $commentId, archived: $archive) + } + } + """; + GraphQLRequest request = new(QUERY, variables: new { commentId, archive }); + var res = await _client + .ExecuteGraphQLRequest>(request, cancellationToken) + .ConfigureAwait(false); + return res.data.archive; + } + + /// + /// + /// + /// + public async Task MarkViewed(string commentId, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation Mutation($commentId: String!) { + data:commentMutations { + markViewed(commentId: $commentId) + } + } + """; + GraphQLRequest request = new(QUERY, variables: new { commentId }); + var res = await _client + .ExecuteGraphQLRequest>(request, cancellationToken) + .ConfigureAwait(false); + return res.data.markViewed; + } + + /// + /// + /// + /// + /// + internal async Task Reply(CreateCommentReplyInput input, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation Mutation($input: CreateCommentReplyInput!) { + data:commentMutations { + reply(input: $input) { + archived + authorId + createdAt + hasParent + id + rawText + resources { + resourceId + resourceType + } + screenshot + updatedAt + viewedAt + viewerResources { + modelId + objectId + versionId + } + data + } + } + } + """; + GraphQLRequest request = new(QUERY, variables: new { input }); + var res = await _client + .ExecuteGraphQLRequest>(request, cancellationToken) + .ConfigureAwait(false); + return res.data.reply; + } +} diff --git a/Core/Core/Api/GraphQL/Resources/ModelResource.cs b/Core/Core/Api/GraphQL/Resources/ModelResource.cs new file mode 100644 index 0000000000..95b3514a2b --- /dev/null +++ b/Core/Core/Api/GraphQL/Resources/ModelResource.cs @@ -0,0 +1,309 @@ +using System.Threading; +using System.Threading.Tasks; +using GraphQL; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Models.Responses; + +namespace Speckle.Core.Api.GraphQL.Resources; + +public sealed class ModelResource +{ + private readonly ISpeckleGraphQLClient _client; + + internal ModelResource(ISpeckleGraphQLClient client) + { + _client = client; + } + + /// + /// + /// + /// + /// + /// + public async Task Get(string modelId, string projectId, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + query ModelGet($modelId: String!, $projectId: String!) { + project(id: $projectId) { + model(id: $modelId) { + id + name + previewUrl + updatedAt + description + displayName + createdAt + author { + avatar + bio + company + id + name + role + totalOwnedStreamsFavorites + verified + } + } + } + } + """; + var request = new GraphQLRequest { Query = QUERY, Variables = new { modelId, projectId } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + return response.project.model; + } + + /// + /// + /// Max number of versions to fetch + /// Optional cursor for pagination + /// Optional versions filter + /// + /// + /// + /// + public async Task GetWithVersions( + string modelId, + string projectId, + int versionsLimit = ServerLimits.DEFAULT_PAGINATION_REQUEST, + string? versionsCursor = null, + ModelVersionsFilter? versionsFilter = null, + CancellationToken cancellationToken = default + ) + { + //language=graphql + const string QUERY = """ + query ModelGetWithVersions($modelId: String!, $projectId: String!, $versionsLimit: Int!, $versionsCursor: String, $versionsFilter: ModelVersionsFilter) { + project(id: $projectId) { + model(id: $modelId) { + id + name + previewUrl + updatedAt + versions(limit: $versionsLimit, cursor: $versionsCursor, filter: $versionsFilter) { + items { + id + referencedObject + message + sourceApplication + createdAt + previewUrl + authorUser { + totalOwnedStreamsFavorites + id + name + bio + company + verified + role + } + } + totalCount + cursor + } + description + displayName + createdAt + author { + avatar + bio + company + id + name + role + totalOwnedStreamsFavorites + verified + } + } + } + } + """; + + var request = new GraphQLRequest + { + Query = QUERY, + Variables = new + { + projectId, + modelId, + versionsLimit, + versionsCursor, + versionsFilter, + } + }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + return response.project.model; + } + + /// + /// Max number of models to fetch + /// Optional cursor for pagination + /// Optional models filter + /// + /// + /// + public async Task> GetModels( + string projectId, + int modelsLimit = ServerLimits.DEFAULT_PAGINATION_REQUEST, + string? modelsCursor = null, + ProjectModelsFilter? modelsFilter = null, + CancellationToken cancellationToken = default + ) + { + //language=graphql + const string QUERY = """ + query ProjectGetWithModels($projectId: String!, $modelsLimit: Int!, $modelsCursor: String, $modelsFilter: ProjectModelsFilter) { + project(id: $projectId) { + models(limit: $modelsLimit, cursor: $modelsCursor, filter: $modelsFilter) { + items { + id + name + previewUrl + updatedAt + displayName + description + createdAt + } + totalCount + cursor + } + } + } + """; + GraphQLRequest request = + new() + { + Query = QUERY, + Variables = new + { + projectId, + modelsLimit, + modelsCursor, + modelsFilter + } + }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + return response.project.models; + } + + /// + /// + /// + /// + public async Task Create(CreateModelInput input, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation ModelCreate($input: CreateModelInput!) { + modelMutations { + create(input: $input) { + id + displayName + name + description + createdAt + updatedAt + previewUrl + author { + avatar + bio + company + id + name + role + totalOwnedStreamsFavorites + verified + } + } + } + } + """; + + GraphQLRequest request = new() { Query = QUERY, Variables = new { input } }; + + var res = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + return res.modelMutations.create; + } + + /// + /// + /// + /// + public async Task Delete(DeleteModelInput input, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation ModelDelete($input: DeleteModelInput!) { + modelMutations { + delete(input: $input) + } + } + """; + + GraphQLRequest request = new() { Query = QUERY, Variables = new { input } }; + + var res = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + return res.modelMutations.delete; + } + + /// + /// + /// + /// + public async Task Update(UpdateModelInput input, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation ModelUpdate($input: UpdateModelInput!) { + modelMutations { + update(input: $input) { + id + name + displayName + description + createdAt + updatedAt + previewUrl + author { + avatar + bio + company + id + name + role + totalOwnedStreamsFavorites + verified + } + } + } + } + """; + + GraphQLRequest request = new() { Query = QUERY, Variables = new { input } }; + + var res = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + return res.modelMutations.update; + } +} diff --git a/Core/Core/Api/GraphQL/Resources/OtherUserResource.cs b/Core/Core/Api/GraphQL/Resources/OtherUserResource.cs new file mode 100644 index 0000000000..5f51ced885 --- /dev/null +++ b/Core/Core/Api/GraphQL/Resources/OtherUserResource.cs @@ -0,0 +1,108 @@ +using System.Threading; +using System.Threading.Tasks; +using GraphQL; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Models.Responses; + +namespace Speckle.Core.Api.GraphQL.Resources; + +public sealed class OtherUserResource +{ + private readonly ISpeckleGraphQLClient _client; + + internal OtherUserResource(ISpeckleGraphQLClient client) + { + _client = client; + } + + /// + /// + /// + /// + /// + /// the requested user, or null if the user does not exist + /// + public async Task Get(string id, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + query LimitedUser($id: String!) { + otherUser(id: $id){ + id, + name, + bio, + company, + avatar, + verified, + role, + } + } + """; + + var request = new GraphQLRequest { Query = QUERY, Variables = new { id } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + return response.otherUser; + } + + /// + /// Searches for a user on the server. + /// + /// String to search for. Must be at least 3 characters + /// Max number of users to fetch + /// Optional cursor for pagination + /// + /// + /// + /// + /// + public async Task> UserSearch( + string query, + int limit = ServerLimits.DEFAULT_PAGINATION_REQUEST, + string? cursor = null, + bool archived = false, + bool emailOnly = false, + CancellationToken cancellationToken = default + ) + { + //language=graphql + const string QUERY = """ + query UserSearch($query: String!, $limit: Int!, $cursor: String, $archived: Boolean, $emailOnly: Boolean) { + userSearch(query: $query, limit: $limit, cursor: $cursor, archived: $archived, emailOnly: $emailOnly) { + cursor, + items { + id + name + bio + company + avatar + verified + role + } + } + } + """; + + var request = new GraphQLRequest + { + Query = QUERY, + Variables = new + { + query, + limit, + cursor, + archived, + emailOnly + } + }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + return response.userSearch; + } +} diff --git a/Core/Core/Api/GraphQL/Resources/ProjectInviteResource.cs b/Core/Core/Api/GraphQL/Resources/ProjectInviteResource.cs new file mode 100644 index 0000000000..65f1cb1e30 --- /dev/null +++ b/Core/Core/Api/GraphQL/Resources/ProjectInviteResource.cs @@ -0,0 +1,260 @@ +using System.Threading; +using System.Threading.Tasks; +using GraphQL; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Models.Responses; + +namespace Speckle.Core.Api.GraphQL.Resources; + +public sealed class ProjectInviteResource +{ + private readonly ISpeckleGraphQLClient _client; + + internal ProjectInviteResource(ISpeckleGraphQLClient client) + { + _client = client; + } + + /// + /// + /// + /// + /// + public async Task Create( + string projectId, + ProjectInviteCreateInput input, + CancellationToken cancellationToken = default + ) + { + //language=graphql + const string QUERY = """ + mutation ProjectInviteCreate($projectId: ID!, $input: ProjectInviteCreateInput!) { + projectMutations { + invites { + create(projectId: $projectId, input: $input) { + id + name + description + visibility + allowPublicComments + role + createdAt + updatedAt + team { + role + user { + totalOwnedStreamsFavorites + id + name + bio + company + avatar + verified + role + } + } + invitedTeam { + id + inviteId + projectId + projectName + streamName + title + role + streamId + token + user { + totalOwnedStreamsFavorites + id + name + bio + company + avatar + verified + role + } + invitedBy { + totalOwnedStreamsFavorites + id + name + bio + company + avatar + verified + role + } + } + } + } + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { projectId, input } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + return response.projectMutations.invites.create; + } + + /// + /// + /// + /// + public async Task Use(ProjectInviteUseInput input, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation ProjectInviteUse($input: ProjectInviteUseInput!) { + projectMutations { + invites { + use(input: $input) + } + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { input } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + return response.projectMutations.invites.use; + } + + /// + /// + /// + /// The invite, or null if no invite exists + /// + public async Task Get( + string projectId, + string? token, + CancellationToken cancellationToken = default + ) + { + //language=graphql + const string QUERY = """ + query ProjectInvite($projectId: String!, $token: String) { + projectInvite(projectId: $projectId, token: $token) { + id + inviteId + invitedBy { + avatar + bio + company + id + name + role + totalOwnedStreamsFavorites + verified + } + projectId + projectName + role + streamId + streamName + title + token + user { + avatar + bio + company + id + name + role + totalOwnedStreamsFavorites + verified + } + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { projectId, token } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + return response.projectInvite; + } + + /// + /// + /// + /// + /// + public async Task Cancel(string projectId, string inviteId, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation ProjectInviteCancel($projectId: ID!, $inviteId: String!) { + projectMutations { + invites { + cancel(projectId: $projectId, inviteId: $inviteId) { + id + name + description + visibility + allowPublicComments + role + createdAt + updatedAt + team { + role + user { + totalOwnedStreamsFavorites + id + name + bio + company + avatar + verified + role + } + } + invitedTeam { + id + inviteId + projectId + projectName + streamName + title + role + streamId + token + user { + totalOwnedStreamsFavorites + id + name + bio + company + avatar + verified + role + } + invitedBy { + totalOwnedStreamsFavorites + id + name + bio + company + avatar + verified + role + } + } + } + } + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { projectId, inviteId } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + return response.projectMutations.invites.cancel; + } +} diff --git a/Core/Core/Api/GraphQL/Resources/ProjectResource.cs b/Core/Core/Api/GraphQL/Resources/ProjectResource.cs new file mode 100644 index 0000000000..f21fb723ae --- /dev/null +++ b/Core/Core/Api/GraphQL/Resources/ProjectResource.cs @@ -0,0 +1,349 @@ +using System.Threading; +using System.Threading.Tasks; +using GraphQL; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Models.Responses; + +namespace Speckle.Core.Api.GraphQL.Resources; + +public sealed class ProjectResource +{ + private readonly ISpeckleGraphQLClient _client; + + internal ProjectResource(ISpeckleGraphQLClient client) + { + _client = client; + } + + /// + /// + /// + /// + /// + /// + public async Task Get(string projectId, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + query Project($projectId: String!) { + project(id: $projectId) { + id + name + description + visibility + allowPublicComments + role + createdAt + updatedAt + sourceApps + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { projectId } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + return response.project; + } + + /// + /// Max number of models to fetch + /// Optional cursor for pagination + /// Optional models filter + /// + /// + /// + /// + /// + public async Task GetWithModels( + string projectId, + int modelsLimit = ServerLimits.DEFAULT_PAGINATION_REQUEST, + string? modelsCursor = null, + ProjectModelsFilter? modelsFilter = null, + CancellationToken cancellationToken = default + ) + { + //language=graphql + const string QUERY = """ + query ProjectGetWithModels($projectId: String!, $modelsLimit: Int!, $modelsCursor: String, $modelsFilter: ProjectModelsFilter) { + project(id: $projectId) { + id + name + description + visibility + allowPublicComments + role + createdAt + updatedAt + sourceApps + models(limit: $modelsLimit, cursor: $modelsCursor, filter: $modelsFilter) { + items { + id + name + previewUrl + updatedAt + displayName + description + createdAt + } + cursor + totalCount + } + } + } + """; + GraphQLRequest request = + new() + { + Query = QUERY, + Variables = new + { + projectId, + modelsLimit, + modelsCursor, + modelsFilter + } + }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + return response.project; + } + + /// + /// + /// + /// + /// + /// + public async Task GetWithTeam(string projectId, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + query ProjectGetWithTeam($projectId: String!) { + project(id: $projectId) { + id + name + description + visibility + allowPublicComments + role + createdAt + updatedAt + team { + role + user { + totalOwnedStreamsFavorites + id + name + bio + company + avatar + verified + role + } + } + invitedTeam { + id + inviteId + projectId + projectName + streamId + streamName + title + role + token + user { + totalOwnedStreamsFavorites + id + name + bio + company + avatar + verified + role + } + invitedBy { + totalOwnedStreamsFavorites + id + name + bio + company + avatar + verified + role + } + } + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { projectId } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + return response.project; + } + + /// + /// + /// + /// + public async Task Create(ProjectCreateInput input, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation ProjectCreate($input: ProjectCreateInput) { + projectMutations { + create(input: $input) { + id + name + description + visibility + allowPublicComments + role + createdAt + updatedAt + sourceApps + } + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { input } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + return response.projectMutations.create; + } + + /// + /// + /// + /// + public async Task Update(ProjectUpdateInput input, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation ProjectUpdate($input: ProjectUpdateInput!) { + projectMutations{ + update(update: $input) { + id + name + description + visibility + allowPublicComments + role + createdAt + updatedAt + } + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { input } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + return response.projectMutations.update; + } + + /// The id of the Project to delete + /// + /// + /// + public async Task Delete(string projectId, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation ProjectDelete($projectId: String!) { + projectMutations { + delete(id: $projectId) + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { projectId } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + return response.projectMutations.delete; + } + + /// + /// + /// + public async Task UpdateRole(ProjectUpdateRoleInput input, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation ProjectUpdateRole($input: ProjectUpdateRoleInput!) { + projectMutations { + updateRole(input: $input) { + id + name + description + visibility + allowPublicComments + role + createdAt + updatedAt + team { + role + user { + totalOwnedStreamsFavorites + id + name + bio + company + avatar + verified + role + } + } + invitedTeam { + id + inviteId + projectId + projectName + streamId + streamName + title + role + token + user { + totalOwnedStreamsFavorites + id + name + bio + company + avatar + verified + role + } + invitedBy { + totalOwnedStreamsFavorites + id + name + bio + company + avatar + verified + role + } + } + } + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { input } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + return response.projectMutations.updateRole; + } +} diff --git a/Core/Core/Api/GraphQL/Resources/SubscriptionResource.cs b/Core/Core/Api/GraphQL/Resources/SubscriptionResource.cs new file mode 100644 index 0000000000..d476ae71e0 --- /dev/null +++ b/Core/Core/Api/GraphQL/Resources/SubscriptionResource.cs @@ -0,0 +1,216 @@ +using System; +using System.Collections.Generic; +using GraphQL; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Models.Responses; + +namespace Speckle.Core.Api.GraphQL.Resources; + +public sealed class Subscription : IDisposable + where TEventArgs : EventArgs +{ + internal Subscription(ISpeckleGraphQLClient client, GraphQLRequest request) + { + _subscription = client.SubscribeTo>(request, (o, t) => Listeners?.Invoke(o, t.data)); + } + + public event EventHandler? Listeners; + + private readonly IDisposable _subscription; + + public void Dispose() + { + _subscription.Dispose(); + } +} + +public sealed class SubscriptionResource : IDisposable +{ + private readonly ISpeckleGraphQLClient _client; + private readonly List _subscriptions; + + internal SubscriptionResource(ISpeckleGraphQLClient client) + { + _client = client; + _subscriptions = new(); + } + + /// Track newly added or deleted projects owned by the active user + /// + /// You should add event listeners to the returned object.
+ /// You can add multiple listeners to a , and this should be preferred over creating many subscriptions.
+ /// You should ensure proper disposal of the when you're done (see )
+ /// Disposing of the or will also dispose any s it created. + ///
+ /// + public Subscription CreateUserProjectsUpdatedSubscription() + { + //language=graphql + const string QUERY = """ + subscription UserProjectsUpdated { + data:userProjectsUpdated { + id + project { + id + } + type + } + } + """; + GraphQLRequest request = new() { Query = QUERY }; + + Subscription subscription = new(_client, request); + _subscriptions.Add(subscription); + return subscription; + } + + /// Subscribe to updates to resource comments/threads. Optionally specify resource ID string to only receive updates regarding comments for those resources + /// + /// + public Subscription CreateProjectCommentsUpdatedSubscription( + ViewerUpdateTrackingTarget target + ) + { + //language=graphql + const string QUERY = """ + subscription Subscription($target: ViewerUpdateTrackingTarget!) { + data:projectCommentsUpdated(target: $target) { + comment { + id + } + id + type + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { target } }; + + Subscription subscription = new(_client, request); + _subscriptions.Add(subscription); + return subscription; + } + + /// Subscribe to changes to a project's models. Optionally specify to track + /// + /// + public Subscription CreateProjectModelsUpdatedSubscription( + string id, + IReadOnlyList? modelIds = null + ) + { + //language=graphql + const string QUERY = """ + subscription ProjectModelsUpdated($id: String!, $modelIds: [String!]) { + data:projectModelsUpdated(id: $id, modelIds: $modelIds) { + id + model { + id + name + previewUrl + updatedAt + description + displayName + createdAt + author { + avatar + bio + company + id + name + role + totalOwnedStreamsFavorites + verified + } + } + type + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { id, modelIds } }; + + Subscription subscription = new(_client, request); + _subscriptions.Add(subscription); + return subscription; + } + + /// Track updates to a specific project + /// + /// + public Subscription CreateProjectUpdatedSubscription(string id) + { + //language=graphql + const string QUERY = """ + subscription ProjectUpdated($id: String!) { + data:projectUpdated(id: $id) { + id + project { + id + name + description + visibility + allowPublicComments + role + createdAt + updatedAt + sourceApps + } + type + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { id } }; + + Subscription subscription = new(_client, request); + _subscriptions.Add(subscription); + return subscription; + } + + /// Subscribe to changes to a project's versions. + /// + /// + public Subscription CreateProjectVersionsUpdatedSubscription(string id) + { + //language=graphql + const string QUERY = """ + subscription ProjectVersionsUpdated($id: String!) { + data:projectVersionsUpdated(id: $id) { + id + modelId + type + version { + id + referencedObject + message + sourceApplication + createdAt + previewUrl + authorUser { + totalOwnedStreamsFavorites + id + name + bio + company + verified + role + avatar + } + } + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { id } }; + + Subscription subscription = new(_client, request); + _subscriptions.Add(subscription); + return subscription; + } + + public void Dispose() + { + foreach (var subscription in _subscriptions) + { + subscription.Dispose(); + } + } +} diff --git a/Core/Core/Api/GraphQL/Resources/VersionResource.cs b/Core/Core/Api/GraphQL/Resources/VersionResource.cs new file mode 100644 index 0000000000..e21ac09e0c --- /dev/null +++ b/Core/Core/Api/GraphQL/Resources/VersionResource.cs @@ -0,0 +1,252 @@ +using System.Threading; +using System.Threading.Tasks; +using GraphQL; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Models.Responses; +using Version = Speckle.Core.Api.GraphQL.Models.Version; + +namespace Speckle.Core.Api.GraphQL.Resources; + +public sealed class VersionResource +{ + private readonly ISpeckleGraphQLClient _client; + + internal VersionResource(ISpeckleGraphQLClient client) + { + _client = client; + } + + /// + /// + /// + /// + /// + /// + public async Task Get( + string versionId, + string modelId, + string projectId, + CancellationToken cancellationToken = default + ) + { + //language=graphql + const string QUERY = """ + query VersionGet($projectId: String!, $modelId: String!, $versionId: String!) { + project(id: $projectId) { + model(id: $modelId) { + version(id: $versionId) { + id + referencedObject + message + sourceApplication + createdAt + previewUrl + authorUser { + totalOwnedStreamsFavorites + id + name + bio + company + verified + role + avatar + } + } + } + } + } + """; + GraphQLRequest request = + new() + { + Query = QUERY, + Variables = new + { + projectId, + modelId, + versionId + } + }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + return response.project.model.version; + } + + /// + /// + /// Max number of versions to fetch + /// Optional cursor for pagination + /// Optional filter + /// + /// + public async Task> GetVersions( + string modelId, + string projectId, + int limit = ServerLimits.DEFAULT_PAGINATION_REQUEST, + string? cursor = null, + ModelVersionsFilter? filter = null, + CancellationToken cancellationToken = default + ) + { + //language=graphql + const string QUERY = """ + query VersionGetVersions($projectId: String!, $modelId: String!, $limit: Int!, $cursor: String, $filter: ModelVersionsFilter) { + project(id: $projectId) { + model(id: $modelId) { + versions(limit: $limit, cursor: $cursor, filter: $filter) { + items { + id + referencedObject + message + sourceApplication + createdAt + previewUrl + authorUser { + totalOwnedStreamsFavorites + id + name + bio + company + verified + role + avatar + } + } + cursor + totalCount + } + } + } + } + """; + + GraphQLRequest request = + new() + { + Query = QUERY, + Variables = new + { + projectId, + modelId, + limit, + cursor, + filter, + } + }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + return response.project.model.versions; + } + + /// + /// + /// + public async Task Create(CommitCreateInput input, CancellationToken cancellationToken = default) + { + //TODO: Implement on server + return await ((Client)_client).CommitCreate(input, cancellationToken).ConfigureAwait(false); + } + + /// + /// + /// + public async Task Update(UpdateVersionInput input, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation VersionUpdate($input: UpdateVersionInput!) { + versionMutations { + update(input: $input) { + id + referencedObject + message + sourceApplication + createdAt + previewUrl + authorUser { + totalOwnedStreamsFavorites + id + name + bio + company + verified + role + avatar + } + } + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { input, } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + return response.versionMutations.update; + } + + //TODO: Would we rather return the full model here? with or with out versions? + /// + /// + /// + /// + public async Task MoveToModel(MoveVersionsInput input, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation VersionMoveToModel($input: MoveVersionsInput!) { + versionMutations { + moveToModel(input: $input) { + id + } + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { input, } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + return response.versionMutations.moveToModel.id; + } + + /// + /// + /// + public async Task Delete(DeleteVersionsInput input, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation VersionDelete($input: DeleteVersionsInput!) { + versionMutations { + delete(input: $input) + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { input } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + return response.versionMutations.delete; + } + + /// + /// + /// + /// + public async Task Received( + CommitReceivedInput commitReceivedInput, + CancellationToken cancellationToken = default + ) + { + //TODO: Implement on server + return await ((Client)_client).CommitReceived(commitReceivedInput, cancellationToken).ConfigureAwait(false); + } +} diff --git a/Core/Core/Api/GraphQL/Resources/graphql.config.yml b/Core/Core/Api/GraphQL/Resources/graphql.config.yml new file mode 100644 index 0000000000..64c50ab285 --- /dev/null +++ b/Core/Core/Api/GraphQL/Resources/graphql.config.yml @@ -0,0 +1,2 @@ +schema: https://app.speckle.systems/graphql +documents: '**/*.graphql' diff --git a/Core/Core/Api/GraphQL/StreamRoles.cs b/Core/Core/Api/GraphQL/StreamRoles.cs new file mode 100644 index 0000000000..963fd2dd11 --- /dev/null +++ b/Core/Core/Api/GraphQL/StreamRoles.cs @@ -0,0 +1,12 @@ +namespace Speckle.Core.Api.GraphQL; + +/// +/// These are the default roles used by the server +/// +public static class StreamRoles +{ + public const string STREAM_OWNER = "stream:owner"; + public const string STREAM_CONTRIBUTOR = "stream:contributor"; + public const string STREAM_REVIEWER = "stream:reviewer"; + public const string? REVOKE = null; +} diff --git a/Core/Core/Api/Helpers.cs b/Core/Core/Api/Helpers.cs index dc3e2ef1c4..1450074229 100644 --- a/Core/Core/Api/Helpers.cs +++ b/Core/Core/Api/Helpers.cs @@ -10,6 +10,8 @@ using System.Runtime.InteropServices; using System.Text.Json; using System.Threading.Tasks; +using Speckle.Core.Api.GraphQL; +using Speckle.Core.Api.GraphQL.Models; using Speckle.Core.Credentials; using Speckle.Core.Helpers; using Speckle.Core.Kits; diff --git a/Core/Core/Api/ServerLimits.cs b/Core/Core/Api/ServerLimits.cs index 7f78def96a..262d1a63fc 100644 --- a/Core/Core/Api/ServerLimits.cs +++ b/Core/Core/Api/ServerLimits.cs @@ -11,4 +11,7 @@ public static class ServerLimits { public const int BRANCH_GET_LIMIT = 500; public const int OLD_BRANCH_GET_LIMIT = 100; + + /// the default `limit` argument value for paginated requests + public const int DEFAULT_PAGINATION_REQUEST = 25; } diff --git a/Core/Core/Credentials/Account.cs b/Core/Core/Credentials/Account.cs index 04d33e7693..184797d1ab 100644 --- a/Core/Core/Credentials/Account.cs +++ b/Core/Core/Credentials/Account.cs @@ -1,7 +1,7 @@ #nullable disable using System; using System.Threading.Tasks; -using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Models; using Speckle.Core.Helpers; namespace Speckle.Core.Credentials; diff --git a/Core/Core/Credentials/AccountManager.cs b/Core/Core/Credentials/AccountManager.cs index 0f9138825f..38708e6cd9 100644 --- a/Core/Core/Credentials/AccountManager.cs +++ b/Core/Core/Credentials/AccountManager.cs @@ -14,7 +14,9 @@ using GraphQL; using GraphQL.Client.Http; using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Models; using Speckle.Core.Api.GraphQL; +using Speckle.Core.Api.GraphQL.Models.Responses; using Speckle.Core.Api.GraphQL.Serializer; using Speckle.Core.Helpers; using Speckle.Core.Logging; diff --git a/Core/Core/Credentials/Responses.cs b/Core/Core/Credentials/Responses.cs index 810d3cae6a..94f7410a85 100644 --- a/Core/Core/Credentials/Responses.cs +++ b/Core/Core/Credentials/Responses.cs @@ -1,57 +1,16 @@ #nullable disable -using System; -using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Models; namespace Speckle.Core.Credentials; -[Obsolete("Use activeUser query and ActiveUserServerInfoResponse instead", true)] -public class UserServerInfoResponse -{ - public UserInfo user { get; set; } - public ServerInfo serverInfo { get; set; } -} - public class ActiveUserServerInfoResponse { public UserInfo activeUser { get; set; } public ServerInfo serverInfo { get; set; } } -[Obsolete("Use activeUser query and ActiveUserResponse instead", true)] -public class UserInfoResponse -{ - public UserInfo user { get; set; } -} - -public class ActiveUserResponse -{ - public UserInfo activeUser { get; set; } -} - -public class UserInfo -{ - public string id { get; set; } - public string name { get; set; } - public string email { get; set; } - public string company { get; set; } - public string avatar { get; set; } - - public Streams streams { get; set; } - public Commits commits { get; set; } -} - public class TokenExchangeResponse { public string token { get; set; } public string refreshToken { get; set; } } - -public class Streams -{ - public int totalCount { get; set; } -} - -public class Commits -{ - public int totalCount { get; set; } -} diff --git a/Core/Core/Credentials/StreamWrapper.cs b/Core/Core/Credentials/StreamWrapper.cs index 7c9d49caa2..5c578c46f3 100644 --- a/Core/Core/Credentials/StreamWrapper.cs +++ b/Core/Core/Credentials/StreamWrapper.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using System.Web; using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Models; using Speckle.Core.Helpers; using Speckle.Core.Logging; diff --git a/Core/Core/Logging/SpeckleException.cs b/Core/Core/Logging/SpeckleException.cs index 397b8b16ac..332d59501f 100644 --- a/Core/Core/Logging/SpeckleException.cs +++ b/Core/Core/Logging/SpeckleException.cs @@ -3,6 +3,7 @@ using System.Linq; using GraphQL; using Sentry; +using Speckle.Core.Api; namespace Speckle.Core.Logging; @@ -21,7 +22,7 @@ public SpeckleException(string? message, Exception? inner = null) public SpeckleException(string? message, Exception? inner, bool log = true, SentryLevel level = SentryLevel.Info) : base(message, inner) { } - [Obsolete("Use any other constructor")] + [Obsolete($"Use {nameof(SpeckleGraphQLException)} instead", true)] public SpeckleException(string? message, GraphQLError[] errors, bool log = true, SentryLevel level = SentryLevel.Info) : base(message) { @@ -32,7 +33,7 @@ public SpeckleException(string? message, GraphQLError[] errors, bool log = true, public SpeckleException(string message, bool log, SentryLevel level = SentryLevel.Info) : base(message) { } - [Obsolete("Use any other constructor", true)] + [Obsolete($"Use {nameof(SpeckleGraphQLException)} instead", true)] public List> GraphQLErrors { get; set; } #endregion } diff --git a/Core/Core/SharpResources.cs b/Core/Core/SharpResources.cs new file mode 100644 index 0000000000..3a7998cfa2 --- /dev/null +++ b/Core/Core/SharpResources.cs @@ -0,0 +1,6 @@ +#if !NET5_0_OR_GREATER +namespace System.Runtime.CompilerServices; + +internal static class IsExternalInit { } + +#endif diff --git a/Core/Tests/Speckle.Core.Tests.Integration/Api.cs b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Legacy/LegacyAPITests.cs similarity index 98% rename from Core/Tests/Speckle.Core.Tests.Integration/Api.cs rename to Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Legacy/LegacyAPITests.cs index bc3b574fcd..46b87d799d 100644 --- a/Core/Tests/Speckle.Core.Tests.Integration/Api.cs +++ b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Legacy/LegacyAPITests.cs @@ -1,12 +1,13 @@ using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL; using Speckle.Core.Credentials; using Speckle.Core.Models; using Speckle.Core.Tests.Unit.Kits; using Speckle.Core.Transports; -namespace Speckle.Core.Tests.Integration; +namespace Speckle.Core.Tests.Integration.Api.GraphQL.Legacy; -public class Api : IDisposable +public class LegacyAPITests : IDisposable { private string _branchId = ""; private string _branchName = ""; @@ -177,7 +178,7 @@ public async Task StreamUpdatePermission() var res = await _myClient.StreamUpdatePermission( new StreamPermissionInput { - role = "stream:reviewer", + role = StreamRoles.STREAM_REVIEWER, streamId = _streamId, userId = _secondUserAccount.userInfo.id } diff --git a/Core/Tests/Speckle.Core.Tests.Integration/Subscriptions/Branches.cs b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Legacy/Subscriptions/Branches.cs similarity index 97% rename from Core/Tests/Speckle.Core.Tests.Integration/Subscriptions/Branches.cs rename to Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Legacy/Subscriptions/Branches.cs index dce806731c..2833a279c1 100644 --- a/Core/Tests/Speckle.Core.Tests.Integration/Subscriptions/Branches.cs +++ b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Legacy/Subscriptions/Branches.cs @@ -2,7 +2,7 @@ using Speckle.Core.Api.SubscriptionModels; using Speckle.Core.Credentials; -namespace Speckle.Core.Tests.Integration.Subscriptions; +namespace Speckle.Core.Tests.Integration.Api.GraphQL.Legacy.Subscriptions; public class Branches : IDisposable { diff --git a/Core/Tests/Speckle.Core.Tests.Integration/Subscriptions/Commits.cs b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Legacy/Subscriptions/Commits.cs similarity index 98% rename from Core/Tests/Speckle.Core.Tests.Integration/Subscriptions/Commits.cs rename to Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Legacy/Subscriptions/Commits.cs index ed10de7b4c..7e25fad641 100644 --- a/Core/Tests/Speckle.Core.Tests.Integration/Subscriptions/Commits.cs +++ b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Legacy/Subscriptions/Commits.cs @@ -5,7 +5,7 @@ using Speckle.Core.Tests.Unit.Kits; using Speckle.Core.Transports; -namespace Speckle.Core.Tests.Integration.Subscriptions; +namespace Speckle.Core.Tests.Integration.Api.GraphQL.Legacy.Subscriptions; public class Commits : IDisposable { diff --git a/Core/Tests/Speckle.Core.Tests.Integration/Subscriptions/Streams.cs b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Legacy/Subscriptions/Streams.cs similarity index 97% rename from Core/Tests/Speckle.Core.Tests.Integration/Subscriptions/Streams.cs rename to Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Legacy/Subscriptions/Streams.cs index 5acd4d71c9..457c2d6b41 100644 --- a/Core/Tests/Speckle.Core.Tests.Integration/Subscriptions/Streams.cs +++ b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Legacy/Subscriptions/Streams.cs @@ -2,7 +2,7 @@ using Speckle.Core.Api.SubscriptionModels; using Speckle.Core.Credentials; -namespace Speckle.Core.Tests.Integration.Subscriptions; +namespace Speckle.Core.Tests.Integration.Api.GraphQL.Legacy.Subscriptions; public class Streams : IDisposable { diff --git a/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ActiveUserResourceTests.cs b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ActiveUserResourceTests.cs new file mode 100644 index 0000000000..1b2e810fa2 --- /dev/null +++ b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ActiveUserResourceTests.cs @@ -0,0 +1,51 @@ +using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Resources; + +namespace Speckle.Core.Tests.Integration.API.GraphQL.Resources; + +[TestOf(typeof(ActiveUserResource))] +public class ActiveUserResourceTests +{ + private Client _testUser; + private ActiveUserResource Sut => _testUser.ActiveUser; + + [OneTimeSetUp] + public async Task Setup() + { + _testUser = await Fixtures.SeedUserWithClient(); + } + + [Test] + public async Task ActiveUserGet() + { + var res = await Sut.Get(); + Assert.That(res.id, Is.EqualTo(_testUser.Account.userInfo.id)); + } + + [Test] + public async Task ActiveUserGet_NonAuthed() + { + var result = await Fixtures.Unauthed.ActiveUser.Get(); + Assert.That(result, Is.EqualTo(null)); + } + + [Test] + public async Task ActiveUserGetProjects() + { + var p1 = await _testUser.Project.Create(new("Project 1", null, null)); + var p2 = await _testUser.Project.Create(new("Project 2", null, null)); + + var res = await Sut.GetProjects(); + + Assert.That(res.items, Has.Exactly(1).Items.With.Property(nameof(Project.id)).EqualTo(p1.id)); + Assert.That(res.items, Has.Exactly(1).Items.With.Property(nameof(Project.id)).EqualTo(p2.id)); + Assert.That(res.items, Has.Count.EqualTo(2)); + } + + [Test] + public void ActiveUserGetProjects_NoAuth() + { + Assert.ThrowsAsync(async () => await Fixtures.Unauthed.ActiveUser.GetProjects()); + } +} diff --git a/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/CommentResourceTests.cs b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/CommentResourceTests.cs new file mode 100644 index 0000000000..cdf25948ea --- /dev/null +++ b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/CommentResourceTests.cs @@ -0,0 +1,97 @@ +using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Resources; + +namespace Speckle.Core.Tests.Integration.API.GraphQL.Resources; + +[TestOf(typeof(CommentResource))] +public class CommentResourceTests +{ + private Client _testUser; + private CommentResource Sut => _testUser.Comment; + private Project _project; + private Model _model; + private string _versionId; + private Comment _comment; + + [SetUp] + public async Task Setup() + { + _testUser = await Fixtures.SeedUserWithClient(); + _project = await _testUser.Project.Create(new("Test project", "", null)); + _model = await _testUser.Model.Create(new("Test Model 1", "", _project.id)); + _versionId = await Fixtures.CreateVersion(_testUser, _project.id, _model.name); + _comment = await CreateComment(); + } + + [Test] + public async Task GetProjectComments() + { + var comments = await Sut.GetProjectComments(_project.id); + Assert.That(comments.items.Count, Is.EqualTo(1)); + Assert.That(comments.totalCount, Is.EqualTo(1)); + + Comment comment = comments.items[0]; + Assert.That(comment, Is.Not.Null); + Assert.That(comment, Has.Property(nameof(Comment.authorId)).EqualTo(_testUser.Account.userInfo.id)); + + Assert.That(comment, Has.Property(nameof(Comment.id)).EqualTo(_comment.id)); + Assert.That(comment, Has.Property(nameof(Comment.authorId)).EqualTo(_comment.authorId)); + Assert.That(comment, Has.Property(nameof(Comment.archived)).EqualTo(_comment.archived)); + Assert.That(comment, Has.Property(nameof(Comment.archived)).EqualTo(false)); + Assert.That(comment, Has.Property(nameof(Comment.createdAt)).EqualTo(_comment.createdAt)); + } + + [Test] + public async Task MarkViewed() + { + var viewed = await Sut.MarkViewed(_comment.id); + Assert.That(viewed, Is.True); + viewed = await Sut.MarkViewed(_comment.id); + Assert.That(viewed, Is.True); + } + + [Test] + public async Task Archive() + { + var archived = await Sut.Archive(_comment.id); + Assert.That(archived, Is.True); + + archived = await Sut.Archive(_comment.id); + Assert.That(archived, Is.True); + } + + [Test] + public async Task Edit() + { + var blobs = await Fixtures.SendBlobData(_testUser.Account, _project.id); + var blobIds = blobs.Select(b => b.id).ToList(); + EditCommentInput input = new(new(blobIds, null), _comment.id); + + var editedComment = await Sut.Edit(input); + + Assert.That(editedComment, Is.Not.Null); + Assert.That(editedComment, Has.Property(nameof(Comment.id)).EqualTo(_comment.id)); + Assert.That(editedComment, Has.Property(nameof(Comment.authorId)).EqualTo(_comment.authorId)); + Assert.That(editedComment, Has.Property(nameof(Comment.createdAt)).EqualTo(_comment.createdAt)); + Assert.That(editedComment, Has.Property(nameof(Comment.updatedAt)).GreaterThanOrEqualTo(_comment.updatedAt)); + } + + [Test] + public async Task Reply() + { + var blobs = await Fixtures.SendBlobData(_testUser.Account, _project.id); + var blobIds = blobs.Select(b => b.id).ToList(); + CreateCommentReplyInput input = new(new(blobIds, null), _comment.id); + + var editedComment = await Sut.Reply(input); + + Assert.That(editedComment, Is.Not.Null); + } + + private async Task CreateComment() + { + return await Fixtures.CreateComment(_testUser, _project.id, _model.id, _versionId); + } +} diff --git a/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ModelResourceExceptionalTests.cs b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ModelResourceExceptionalTests.cs new file mode 100644 index 0000000000..8fe43ba10c --- /dev/null +++ b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ModelResourceExceptionalTests.cs @@ -0,0 +1,88 @@ +using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Enums; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Resources; + +namespace Speckle.Core.Tests.Integration.API.GraphQL.Resources; + +[TestOf(typeof(ModelResource))] +public class ModelResourceExceptionalTests +{ + private Client _testUser; + private ModelResource Sut => _testUser.Model; + private Project _project; + private Model _model; + + [OneTimeSetUp] + public async Task Setup() + { + _testUser = await Fixtures.SeedUserWithClient(); + _project = await _testUser.Project.Create(new("Test project", "", ProjectVisibility.Private)); + _model = await _testUser.Model.Create(new("Test Model", "", _project.id)); + } + + [TestCase(null)] + [TestCase("")] + [TestCase(" ")] + public void ModelCreate_Throws_InvalidInput(string name) + { + CreateModelInput input = new(name, null, _project.id); + Assert.CatchAsync(async () => await Sut.Create(input)); + } + + [Test] + public void ModelGet_Throws_NoAuth() + { + Assert.CatchAsync(async () => await Fixtures.Unauthed.Model.Get(_model.id, _project.id)); + } + + [Test] + public void ModelGet_Throws_NonExistentModel() + { + Assert.CatchAsync(async () => await Sut.Get("non existent model", _project.id)); + } + + [Test] + public void ModelGet_Throws_NonExistentProject() + { + Assert.ThrowsAsync( + async () => await Sut.Get(_model.id, "non existent project") + ); + } + + [Test] + public void ModelUpdate_Throws_NonExistentModel() + { + UpdateModelInput input = new("non-existent model", "MY new name", "MY new desc", _project.id); + + Assert.CatchAsync(async () => await Sut.Update(input)); + } + + [Test] + public void ModelUpdate_Throws_NonExistentProject() + { + UpdateModelInput input = new(_model.id, "MY new name", "MY new desc", "non-existent project"); + + Assert.ThrowsAsync(async () => await Sut.Update(input)); + } + + [Test] + public void ModelUpdate_Throws_NonAuthProject() + { + UpdateModelInput input = new(_model.id, "MY new name", "MY new desc", _project.id); + + Assert.CatchAsync(async () => await Fixtures.Unauthed.Model.Update(input)); + } + + [Test] + public async Task ModelDelete_Throws_NoAuth() + { + Model toDelete = await Sut.Create(new("Delete me", null, _project.id)); + DeleteModelInput input = new(toDelete.id, _project.id); + bool response = await Sut.Delete(input); + Assert.That(response, Is.True); + + Assert.CatchAsync(async () => _ = await Sut.Delete(input)); + } +} diff --git a/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ModelResourceTests.cs b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ModelResourceTests.cs new file mode 100644 index 0000000000..e2995acf29 --- /dev/null +++ b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ModelResourceTests.cs @@ -0,0 +1,96 @@ +using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Resources; + +namespace Speckle.Core.Tests.Integration.API.GraphQL.Resources; + +[TestOf(typeof(ModelResource))] +public class ModelResourceTests +{ + private Client _testUser; + private ModelResource Sut => _testUser.Model; + private Project _project; + private Model _model; + + [SetUp] + public async Task Setup() + { + _testUser = await Fixtures.SeedUserWithClient(); + _project = await _testUser.Project.Create(new("Test project", "", null)); + _model = await _testUser.Model.Create(new("Test Model", "", _project.id)); + } + + [TestCase("My Model", "My model description")] + [TestCase("my/nested/model", null)] + public async Task ModelCreate(string name, string description) + { + CreateModelInput input = new(name, description, _project.id); + Model result = await Sut.Create(input); + + Assert.That(result, Is.Not.Null); + Assert.That(result, Has.Property(nameof(result.id)).Not.Null); + Assert.That(result, Has.Property(nameof(result.name)).EqualTo(input.name).IgnoreCase); + Assert.That(result, Has.Property(nameof(result.description)).EqualTo(input.description)); + } + + [Test] + public async Task ModelGet() + { + Model result = await Sut.Get(_model.id, _project.id); + + Assert.That(result.id, Is.EqualTo(_model.id)); + Assert.That(result.name, Is.EqualTo(_model.name)); + Assert.That(result.description, Is.EqualTo(_model.description)); + Assert.That(result.createdAt, Is.EqualTo(_model.createdAt)); + Assert.That(result.updatedAt, Is.EqualTo(_model.updatedAt)); + } + + [Test] + public async Task GetModels() + { + var result = await Sut.GetModels(_project.id); + + Assert.That(result.items, Has.Count.EqualTo(1)); + Assert.That(result.totalCount, Is.EqualTo(1)); + Assert.That(result.items[0], Has.Property(nameof(Model.id)).EqualTo(_model.id)); + } + + [Test] + public async Task Project_GetModels() + { + var result = await _testUser.Project.GetWithModels(_project.id); + + Assert.That(result, Has.Property(nameof(Project.id)).EqualTo(_project.id)); + Assert.That(result.models.items, Has.Count.EqualTo(1)); + Assert.That(result.models.totalCount, Is.EqualTo(1)); + Assert.That(result.models.items[0], Has.Property(nameof(Model.id)).EqualTo(_model.id)); + } + + [Test] + public async Task ModelUpdate() + { + const string NEW_NAME = "MY new name"; + const string NEW_DESCRIPTION = "MY new desc"; + + UpdateModelInput input = new(_model.id, NEW_NAME, NEW_DESCRIPTION, _project.id); + Model updatedModel = await Sut.Update(input); + + Assert.That(updatedModel.id, Is.EqualTo(_model.id)); + Assert.That(updatedModel.name, Is.EqualTo(NEW_NAME).IgnoreCase); + Assert.That(updatedModel.description, Is.EqualTo(NEW_DESCRIPTION)); + Assert.That(updatedModel.updatedAt, Is.GreaterThanOrEqualTo(_model.updatedAt)); + } + + [Test] + public async Task ModelDelete() + { + DeleteModelInput input = new(_model.id, _project.id); + + bool response = await Sut.Delete(input); + Assert.That(response, Is.True); + + Assert.CatchAsync(async () => _ = await Sut.Get(_model.id, _project.id)); + Assert.CatchAsync(async () => _ = await Sut.Delete(input)); + } +} diff --git a/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/OtherUserResourceTests.cs b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/OtherUserResourceTests.cs new file mode 100644 index 0000000000..9959131e88 --- /dev/null +++ b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/OtherUserResourceTests.cs @@ -0,0 +1,49 @@ +using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Resources; +using Speckle.Core.Credentials; + +namespace Speckle.Core.Tests.Integration.API.GraphQL.Resources; + +[TestOf(typeof(OtherUserResource))] +public class OtherUserResourceTests +{ + private Client _testUser; + private Account _testData; + private OtherUserResource Sut => _testUser.OtherUser; + + [OneTimeSetUp] + public async Task Setup() + { + _testUser = await Fixtures.SeedUserWithClient(); + _testData = await Fixtures.SeedUser(); + } + + [Test] + public async Task OtherUserGet() + { + var res = await Sut.Get(_testData.userInfo.id); + Assert.That(res.name, Is.EqualTo(_testData.userInfo.name)); + } + + [Test] + public async Task OtherUserGet_NonExistentUser() + { + var result = await Sut.Get("AnIdThatDoesntExist"); + Assert.That(result, Is.Null); + } + + [Test] + public async Task UserSearch() + { + var res = await Sut.UserSearch(_testData.userInfo.email, 25); + Assert.That(res.items, Has.Count.EqualTo(1)); + Assert.That(res.items[0].id, Is.EqualTo(_testData.userInfo.id)); + } + + [Test] + public async Task UserSearch_NonExistentUser() + { + var res = await Sut.UserSearch("idontexist@example.com", 25); + Assert.That(res.items, Has.Count.EqualTo(0)); + } +} diff --git a/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectInviteResourceExceptionalTests.cs b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectInviteResourceExceptionalTests.cs new file mode 100644 index 0000000000..b2cee38c2a --- /dev/null +++ b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectInviteResourceExceptionalTests.cs @@ -0,0 +1,32 @@ +using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Resources; + +namespace Speckle.Core.Tests.Integration.API.GraphQL.Resources; + +[TestOf(typeof(ProjectInviteResource))] +public class ProjectInviteResourceExceptionalTests +{ + private Client _testUser; + private Project _project; + private ProjectInviteResource Sut => _testUser.ProjectInvite; + + [OneTimeSetUp] + public async Task Setup() + { + _testUser = await Fixtures.SeedUserWithClient(); + _project = await _testUser.Project.Create(new("test", null, null)); + } + + [TestCase(null, null, null, null)] + [TestCase(null, "something", "something", null)] + public void ProjectInviteCreate_InvalidInput(string email, string role, string serverRole, string userId) + { + Assert.CatchAsync(async () => + { + var input = new ProjectInviteCreateInput(email, role, serverRole, userId); + await Sut.Create(_project.id, input); + }); + } +} diff --git a/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectInviteResourceTests.cs b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectInviteResourceTests.cs new file mode 100644 index 0000000000..f6268db573 --- /dev/null +++ b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectInviteResourceTests.cs @@ -0,0 +1,107 @@ +using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Resources; + +namespace Speckle.Core.Tests.Integration.API.GraphQL.Resources; + +[TestOf(typeof(ProjectInviteResource))] +public class ProjectInviteResourceTests +{ + private Client _inviter, + _invitee; + private Project _project; + private PendingStreamCollaborator _createdInvite; + + [SetUp] + public async Task Setup() + { + _inviter = await Fixtures.SeedUserWithClient(); + _invitee = await Fixtures.SeedUserWithClient(); + _project = await _inviter.Project.Create(new("test", null, null)); + _createdInvite = await SeedInvite(); + } + + private async Task SeedInvite() + { + ProjectInviteCreateInput input = new(_invitee.Account.userInfo.email, null, null, null); + var res = await _inviter.ProjectInvite.Create(_project.id, input); + var invites = await _invitee.ActiveUser.ProjectInvites(); + return invites.First(i => i.projectId == res.id); + } + + [Test] + public async Task ProjectInviteCreate_ByEmail() + { + ProjectInviteCreateInput input = new(_invitee.Account.userInfo.email, null, null, null); + var res = await _inviter.ProjectInvite.Create(_project.id, input); + + var invites = await _invitee.ActiveUser.ProjectInvites(); + var invite = invites.First(i => i.projectId == res.id); + + Assert.That(res, Has.Property(nameof(_project.id)).EqualTo(_project.id)); + Assert.That(res.invitedTeam, Has.Count.EqualTo(1)); + Assert.That(invite.user.id, Is.EqualTo(_invitee.Account.userInfo.id)); + Assert.That(invite.token, Is.Not.Null); + } + + [Test] + public async Task ProjectInviteCreate_ByUserId() + { + ProjectInviteCreateInput input = new(null, null, null, _invitee.Account.userInfo.id); + var res = await _inviter.ProjectInvite.Create(_project.id, input); + + Assert.That(res, Has.Property(nameof(_project.id)).EqualTo(_project.id)); + Assert.That(res.invitedTeam, Has.Count.EqualTo(1)); + Assert.That(res.invitedTeam[0].user.id, Is.EqualTo(_invitee.Account.userInfo.id)); + } + + [Test] + public async Task ProjectInviteGet() + { + var collaborator = await _invitee.ProjectInvite.Get(_project.id, _createdInvite.token); + + Assert.That( + collaborator, + Has.Property(nameof(PendingStreamCollaborator.inviteId)).EqualTo(_createdInvite.inviteId) + ); + Assert.That(collaborator.user.id, Is.EqualTo(_createdInvite.user.id)); + } + + [Test] + public async Task ProjectInviteUse_MemberAdded() + { + ProjectInviteUseInput input = new(true, _createdInvite.projectId, _createdInvite.token); + var res = await _invitee.ProjectInvite.Use(input); + Assert.That(res, Is.True); + + var project = await _inviter.Project.GetWithTeam(_project.id); + var teamMembers = project.team.Select(c => c.user.id); + var expectedTeamMembers = new[] { _inviter.Account.userInfo.id, _invitee.Account.userInfo.id }; + Assert.That(teamMembers, Is.EquivalentTo(expectedTeamMembers)); + } + + [Test] + public async Task ProjectInviteCancel_MemberNotAdded() + { + var res = await _inviter.ProjectInvite.Cancel(_createdInvite.projectId, _createdInvite.inviteId); + + Assert.That(res.invitedTeam, Is.Empty); + } + + [Test] + [TestCase(StreamRoles.STREAM_OWNER)] + [TestCase(StreamRoles.STREAM_REVIEWER)] + [TestCase(StreamRoles.STREAM_CONTRIBUTOR)] + [TestCase(StreamRoles.REVOKE)] + public async Task ProjectUpdateRole(string newRole) + { + await ProjectInviteUse_MemberAdded(); + ProjectUpdateRoleInput input = new(_invitee.Account.userInfo.id, _project.id, newRole); + _ = await _inviter.Project.UpdateRole(input); + + Project finalProject = await _invitee.Project.Get(_project.id); + Assert.That(finalProject.role, Is.EqualTo(newRole)); + } +} diff --git a/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectResourceExceptionalTests.cs b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectResourceExceptionalTests.cs new file mode 100644 index 0000000000..47222b761f --- /dev/null +++ b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectResourceExceptionalTests.cs @@ -0,0 +1,113 @@ +using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL; +using Speckle.Core.Api.GraphQL.Enums; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Resources; + +namespace Speckle.Core.Tests.Integration.API.GraphQL.Resources; + +[TestOf(typeof(ProjectResource))] +public class ProjectResourceExceptionalTests +{ + private Client _testUser, + _secondUser, + _unauthedUser; + private Project _testProject; + private ProjectResource Sut => _testUser.Project; + + [OneTimeSetUp] + public async Task Setup() + { + _testUser = await Fixtures.SeedUserWithClient(); + _secondUser = await Fixtures.SeedUserWithClient(); + _unauthedUser = Fixtures.Unauthed; + _testProject = await _testUser.Project.Create(new("test project123", "desc", null)); + } + + //We want to check the following cases + // 1. User lacks permissions (without auth) + // 2. Target (Project or user) doesn't exist) + // 3. Cancellation + // 4. Server doesn't exist (is down) + //There's got to be a smarter way to parametrise these... + + [Test] + public void ProjectCreate_WithoutAuth() + { + ProjectCreateInput input = + new("The best project", "The best description for the best project", ProjectVisibility.Private); + + Assert.ThrowsAsync(async () => await _unauthedUser.Project.Create(input)); + } + + [Test] + public async Task ProjectGet_WithoutAuth() + { + ProjectCreateInput input = new("Private Stream", "A very private stream", ProjectVisibility.Private); + + Project privateStream = await Sut.Create(input); + + Assert.ThrowsAsync(async () => await _unauthedUser.Project.Get(privateStream.id)); + } + + [Test] + public void ProjectGet_NonExistentProject() + { + Assert.ThrowsAsync(async () => await Sut.Get("NonExistentProject")); + } + + [Test] + public void ProjectUpdate_NonExistentProject() + { + Assert.ThrowsAsync( + async () => _ = await Sut.Update(new("NonExistentProject", "My new name")) + ); + } + + [Test] + public void ProjectUpdate_NoAuth() + { + Assert.ThrowsAsync( + async () => _ = await _unauthedUser.Project.Update(new(_testProject.id, "My new name")) + ); + } + + [Test] + [TestCase(StreamRoles.STREAM_OWNER)] + [TestCase(StreamRoles.STREAM_CONTRIBUTOR)] + [TestCase(StreamRoles.STREAM_REVIEWER)] + [TestCase(StreamRoles.REVOKE)] + public void ProjectUpdateRole_NonExistentProject(string newRole) + { + ProjectUpdateRoleInput input = new(_secondUser.Account.id, "NonExistentProject", newRole); + + Assert.ThrowsAsync(async () => await Sut.UpdateRole(input)); + } + + [Test] + [TestCase(StreamRoles.STREAM_OWNER)] + [TestCase(StreamRoles.STREAM_CONTRIBUTOR)] + [TestCase(StreamRoles.STREAM_REVIEWER)] + [TestCase(StreamRoles.REVOKE)] + public void ProjectUpdateRole_NonAuth(string newRole) + { + ProjectUpdateRoleInput input = new(_secondUser.Account.id, "NonExistentProject", newRole); + Assert.ThrowsAsync(async () => await _unauthedUser.Project.UpdateRole(input)); + } + + [Test] + public async Task ProjectDelete_NonExistentProject() + { + bool response = await Sut.Delete(_testProject.id); + Assert.That(response, Is.True); + + Assert.ThrowsAsync(async () => _ = await Sut.Get(_testProject.id)); //TODO: Exception types + } + + [Test] + public void ProjectInvites_NoAuth() + { + Assert.ThrowsAsync(async () => await Fixtures.Unauthed.ActiveUser.ProjectInvites()); + } +} diff --git a/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectResourceTests.cs b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectResourceTests.cs new file mode 100644 index 0000000000..66dcba0a1e --- /dev/null +++ b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectResourceTests.cs @@ -0,0 +1,72 @@ +using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Enums; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Resources; + +namespace Speckle.Core.Tests.Integration.API.GraphQL.Resources; + +[TestOf(typeof(ProjectResource))] +public class ProjectResourceTests +{ + private Client _testUser; + private Project _testProject; + private ProjectResource Sut => _testUser.Project; + + [OneTimeSetUp] + public async Task Setup() + { + _testUser = await Fixtures.SeedUserWithClient(); + _testProject = await _testUser.Project.Create(new("test project123", "desc", null)); + } + + [TestCase("Very private project", "My secret project", ProjectVisibility.Private)] + [TestCase("Very public project", null, ProjectVisibility.Public)] + public async Task ProjectCreate(string name, string desc, ProjectVisibility visibility) + { + ProjectCreateInput input = new(name, desc, visibility); + Project result = await Sut.Create(input); + Assert.That(result, Is.Not.Null); + Assert.That(result, Has.Property(nameof(Project.id)).Not.Null); + Assert.That(result, Has.Property(nameof(Project.name)).EqualTo(input.name)); + Assert.That(result, Has.Property(nameof(Project.description)).EqualTo(input.description ?? string.Empty)); + Assert.That(result, Has.Property(nameof(Project.visibility)).EqualTo(input.visibility)); + } + + [Test] + public async Task ProjectGet() + { + Project result = await Sut.Get(_testProject.id); + + Assert.That(result.id, Is.EqualTo(_testProject.id)); + Assert.That(result.name, Is.EqualTo(_testProject.name)); + Assert.That(result.description, Is.EqualTo(_testProject.description)); + Assert.That(result.visibility, Is.EqualTo(_testProject.visibility)); + Assert.That(result.createdAt, Is.EqualTo(_testProject.createdAt)); + } + + [Test] + public async Task ProjectUpdate() + { + const string NEW_NAME = "MY new name"; + const string NEW_DESCRIPTION = "MY new desc"; + const ProjectVisibility NEW_VISIBILITY = ProjectVisibility.Public; + + Project newProject = await Sut.Update(new(_testProject.id, NEW_NAME, NEW_DESCRIPTION, null, NEW_VISIBILITY)); + + Assert.That(newProject.id, Is.EqualTo(_testProject.id)); + Assert.That(newProject.name, Is.EqualTo(NEW_NAME)); + Assert.That(newProject.description, Is.EqualTo(NEW_DESCRIPTION)); + Assert.That(newProject.visibility, Is.EqualTo(NEW_VISIBILITY)); + } + + [Test] + public async Task ProjectDelete() + { + Project toDelete = await Sut.Create(new("Delete me", null, null)); + bool response = await Sut.Delete(toDelete.id); + Assert.That(response, Is.True); + + Assert.ThrowsAsync(async () => _ = await Sut.Get(toDelete.id)); + } +} diff --git a/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/SubscriptionResourceTests.cs b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/SubscriptionResourceTests.cs new file mode 100644 index 0000000000..6e1e845c84 --- /dev/null +++ b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/SubscriptionResourceTests.cs @@ -0,0 +1,120 @@ +using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Resources; + +namespace Speckle.Core.Tests.Integration.API.GraphQL.Resources; + +[TestOf(typeof(SubscriptionResource))] +public class SubscriptionResourceTests +{ + private const int WAIT_PERIOD = 300; + private Client _testUser; + private Project _testProject; + private Model _testModel; + private string _testVersion; + + private SubscriptionResource Sut => _testUser.Subscription; + + [OneTimeSetUp] + public async Task Setup() + { + _testUser = await Fixtures.SeedUserWithClient(); + _testProject = await _testUser.Project.Create(new("test project123", "desc", null)); + _testModel = await _testUser.Model.Create(new("test model", "desc", _testProject.id)); + _testVersion = await Fixtures.CreateVersion(_testUser, _testProject.id, _testModel.name); + } + + [Test] + public async Task UserProjectsUpdated_SubscriptionIsCalled() + { + UserProjectsUpdatedMessage subscriptionMessage = null; + + using var sub = Sut.CreateUserProjectsUpdatedSubscription(); + sub.Listeners += (_, message) => subscriptionMessage = message; + + await Task.Delay(WAIT_PERIOD); // Give time to subscription to be setup + + var created = await _testUser.Project.Create(new(null, null, null)); + + await Task.Delay(WAIT_PERIOD); // Give time for subscription to be triggered + + Assert.That(subscriptionMessage, Is.Not.Null); + Assert.That(subscriptionMessage.id, Is.EqualTo(created.id)); + } + + [Test] + public async Task ProjectModelsUpdated_SubscriptionIsCalled() + { + ProjectModelsUpdatedMessage subscriptionMessage = null; + + using var sub = Sut.CreateProjectModelsUpdatedSubscription(_testProject.id); + sub.Listeners += (_, message) => subscriptionMessage = message; + + await Task.Delay(WAIT_PERIOD); // Give time to subscription to be setup + + CreateModelInput input = new("my model", "myDescription", _testProject.id); + var created = await _testUser.Model.Create(input); + + await Task.Delay(WAIT_PERIOD); // Give time for subscription to be triggered + + Assert.That(subscriptionMessage, Is.Not.Null); + Assert.That(subscriptionMessage.id, Is.EqualTo(created.id)); + } + + [Test] + public async Task ProjectUpdated_SubscriptionIsCalled() + { + ProjectUpdatedMessage subscriptionMessage = null; + + using var sub = Sut.CreateProjectUpdatedSubscription(_testProject.id); + sub.Listeners += (_, message) => subscriptionMessage = message; + + await Task.Delay(WAIT_PERIOD); // Give time to subscription to be setup + + var input = new ProjectUpdateInput(_testProject.id, "This is my new name"); + var created = await _testUser.Project.Update(input); + + await Task.Delay(WAIT_PERIOD); // Give time for subscription to be triggered + + Assert.That(subscriptionMessage, Is.Not.Null); + Assert.That(subscriptionMessage.id, Is.EqualTo(created.id)); + } + + [Test] + public async Task ProjectVersionsUpdated_SubscriptionIsCalled() + { + ProjectVersionsUpdatedMessage subscriptionMessage = null; + + using var sub = Sut.CreateProjectVersionsUpdatedSubscription(_testProject.id); + sub.Listeners += (_, message) => subscriptionMessage = message; + + await Task.Delay(WAIT_PERIOD); // Give time to subscription to be setup + + var created = await Fixtures.CreateVersion(_testUser, _testProject.id, _testModel.name); + + await Task.Delay(WAIT_PERIOD); // Give time for subscription to be triggered + + Assert.That(subscriptionMessage, Is.Not.Null); + Assert.That(subscriptionMessage.id, Is.EqualTo(created)); + } + + [Test] + public async Task ProjectCommentsUpdated_SubscriptionIsCalled() + { + string resourceIdString = $"{_testProject.id},{_testModel.id},{_testVersion}"; + ProjectCommentsUpdatedMessage subscriptionMessage = null; + + using var sub = Sut.CreateProjectCommentsUpdatedSubscription(new(_testProject.id, resourceIdString)); + sub.Listeners += (_, message) => subscriptionMessage = message; + + await Task.Delay(WAIT_PERIOD); // Give time to subscription to be setup + + var created = await Fixtures.CreateComment(_testUser, _testProject.id, _testModel.id, _testVersion); + + await Task.Delay(WAIT_PERIOD); // Give time for subscription to be triggered + + Assert.That(subscriptionMessage, Is.Not.Null); + Assert.That(subscriptionMessage.id, Is.EqualTo(created.id)); + } +} diff --git a/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/VersionResourceTests.cs b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/VersionResourceTests.cs new file mode 100644 index 0000000000..d54c966e0b --- /dev/null +++ b/Core/Tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/VersionResourceTests.cs @@ -0,0 +1,117 @@ +using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Resources; +using Version = Speckle.Core.Api.GraphQL.Models.Version; + +namespace Speckle.Core.Tests.Integration.API.GraphQL.Resources; + +[TestOf(typeof(VersionResource))] +public class VersionResourceTests +{ + private Client _testUser; + private VersionResource Sut => _testUser.Version; + private Project _project; + private Model _model1; + private Model _model2; + private Version _version; + + [SetUp] + public async Task Setup() + { + _testUser = await Fixtures.SeedUserWithClient(); + _project = await _testUser.Project.Create(new("Test project", "", null)); + _model1 = await _testUser.Model.Create(new("Test Model 1", "", _project.id)); + _model2 = await _testUser.Model.Create(new("Test Model 2", "", _project.id)); + + string versionId = await Fixtures.CreateVersion(_testUser, _project.id, "Test Model 1"); + + _version = await Sut.Get(versionId, _model1.id, _project.id); + } + + [Test] + public async Task VersionGet() + { + Version result = await Sut.Get(_version.id, _model1.id, _project.id); + + Assert.That(result, Has.Property(nameof(Version.id)).EqualTo(_version.id)); + Assert.That(result, Has.Property(nameof(Version.message)).EqualTo(_version.message)); + } + + [Test] + public async Task VersionsGet() + { + ResourceCollection result = await Sut.GetVersions(_model1.id, _project.id); + + Assert.That(result.items, Has.Count.EqualTo(1)); + Assert.That(result.totalCount, Is.EqualTo(1)); + Assert.That(result.items[0], Has.Property(nameof(Version.id)).EqualTo(_version.id)); + } + + [Test] + public async Task VersionReceived() + { + CommitReceivedInput input = + new() + { + commitId = _version.id, + message = "we receieved it", + sourceApplication = "Integration test", + streamId = _project.id + }; + var result = await Sut.Received(input); + + Assert.That(result, Is.True); + } + + [Test] + public async Task ModelGetWithVersions() + { + Model result = await _testUser.Model.GetWithVersions(_model1.id, _project.id); + + Assert.That(result, Has.Property(nameof(Model.id)).EqualTo(_model1.id)); + Assert.That(result.versions.items, Has.Count.EqualTo(1)); + Assert.That(result.versions.totalCount, Is.EqualTo(1)); + Assert.That(result.versions.items[0], Has.Property(nameof(Version.id)).EqualTo(_version.id)); + } + + [Test] + public async Task VersionUpdate() + { + const string NEW_MESSAGE = "MY new version message"; + + UpdateVersionInput input = new(_version.id, NEW_MESSAGE); + Version updatedVersion = await Sut.Update(input); + + Assert.That(updatedVersion, Has.Property(nameof(Version.id)).EqualTo(_version.id)); + Assert.That(updatedVersion, Has.Property(nameof(Version.message)).EqualTo(NEW_MESSAGE)); + Assert.That(updatedVersion, Has.Property(nameof(Version.previewUrl)).EqualTo(_version.previewUrl)); + } + + [Test] + public async Task VersionMoveToModel() + { + MoveVersionsInput input = new(_model2.name, new[] { _version.id }); + string id = await Sut.MoveToModel(input); + Assert.That(id, Is.EqualTo(_model2.id)); + Version movedVersion = await Sut.Get(_version.id, _model2.id, _project.id); + + Assert.That(movedVersion, Has.Property(nameof(Version.id)).EqualTo(_version.id)); + Assert.That(movedVersion, Has.Property(nameof(Version.message)).EqualTo(_version.message)); + Assert.That(movedVersion, Has.Property(nameof(Version.previewUrl)).EqualTo(_version.previewUrl)); + + Assert.CatchAsync(async () => await Sut.Get(id, _model1.id, _project.id)); + } + + [Test] + public async Task VersionDelete() + { + DeleteVersionsInput input = new(new[] { _version.id }); + + bool response = await Sut.Delete(input); + Assert.That(response, Is.True); + + Assert.CatchAsync(async () => _ = await Sut.Get(_version.id, _model1.id, _project.id)); + Assert.CatchAsync(async () => _ = await Sut.Delete(input)); + } +} diff --git a/Core/Tests/Speckle.Core.Tests.Integration/Credentials/UserServerInfoTests.cs b/Core/Tests/Speckle.Core.Tests.Integration/Credentials/UserServerInfoTests.cs index 0929c90926..2df5d5b3f5 100644 --- a/Core/Tests/Speckle.Core.Tests.Integration/Credentials/UserServerInfoTests.cs +++ b/Core/Tests/Speckle.Core.Tests.Integration/Credentials/UserServerInfoTests.cs @@ -1,5 +1,5 @@ using GraphQL.Client.Http; -using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Models; using Speckle.Core.Credentials; namespace Speckle.Core.Tests.Integration.Credentials; diff --git a/Core/Tests/Speckle.Core.Tests.Integration/Fixtures.cs b/Core/Tests/Speckle.Core.Tests.Integration/Fixtures.cs index 0e046f9bbf..268e297943 100644 --- a/Core/Tests/Speckle.Core.Tests.Integration/Fixtures.cs +++ b/Core/Tests/Speckle.Core.Tests.Integration/Fixtures.cs @@ -4,9 +4,12 @@ using System.Web; using Newtonsoft.Json; using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; using Speckle.Core.Credentials; using Speckle.Core.Logging; using Speckle.Core.Models; +using Speckle.Core.Transports; namespace Speckle.Core.Tests.Integration; @@ -23,7 +26,29 @@ public void BeforeAll() public static class Fixtures { - private static readonly ServerInfo s_server = new() { url = "http://localhost:3000", name = "Docker Server" }; + public static readonly ServerInfo Server = new() { url = "http://localhost:3000", name = "Docker Server" }; + + public static Client Unauthed => new Client(new Account { serverInfo = Server, userInfo = new UserInfo() }); + + public static async Task SeedUserWithClient() + { + return new Client(await SeedUser()); + } + + public static async Task CreateVersion(Client client, string projectId, string branchName) + { + using ServerTransport remote = new(client.Account, projectId); + var objectId = await Operations.Send(new() { applicationId = "ASDF" }, remote, false); + CommitCreateInput input = + new() + { + branchName = branchName, + message = "test version", + objectId = objectId, + streamId = projectId + }; + return await client.Version.Create(input); + } public static async Task SeedUser() { @@ -31,7 +56,7 @@ public static async Task SeedUser() Dictionary user = new() { - ["email"] = $"{seed.Substring(0, 7)}@acme.com", + ["email"] = $"{seed.Substring(0, 7)}@example.com", ["password"] = "12ABC3456789DEF0GHO", ["name"] = $"{seed.Substring(0, 5)} Name" }; @@ -40,7 +65,7 @@ public static async Task SeedUser() new HttpClientHandler { AllowAutoRedirect = false, CheckCertificateRevocationList = true } ); - httpClient.BaseAddress = new Uri(s_server.url); + httpClient.BaseAddress = new Uri(Server.url); string redirectUrl; try @@ -54,7 +79,7 @@ public static async Task SeedUser() } catch (Exception e) { - throw new Exception($"Cannot seed user on the server {s_server.url}", e); + throw new Exception($"Cannot seed user on the server {Server.url}", e); } Uri uri = new(redirectUrl); @@ -87,12 +112,12 @@ await tokenResponse.Content.ReadAsStringAsync() email = user["email"], name = user["name"] }, - serverInfo = s_server + serverInfo = Server }; using var client = new Client(acc); var user1 = await client.ActiveUserGet(); - acc.userInfo.id = user1.id; + acc.userInfo = user1; return acc; } @@ -132,6 +157,23 @@ private static Blob GenerateBlob(string content) File.WriteAllText(filePath, content); return new Blob(filePath); } + + internal static async Task CreateComment(Client client, string projectId, string modelId, string versionId) + { + var blobs = await SendBlobData(client.Account, projectId); + var blobIds = blobs.Select(b => b.id).ToList(); + CreateCommentInput input = new(new(blobIds, null), projectId, $"{projectId},{modelId},{versionId}", null, null); + return await client.Comment.Create(input); + } + + internal static async Task SendBlobData(Account account, string projectId) + { + using ServerTransport remote = new(account, projectId); + var blobs = Fixtures.GenerateThreeBlobs(); + Base myObject = new() { ["blobs"] = blobs }; + await Operations.Send(myObject, remote, false); + return blobs; + } } public class UserIdResponse diff --git a/Core/Tests/Speckle.Core.Tests.Integration/GraphQLCLient.cs b/Core/Tests/Speckle.Core.Tests.Integration/GraphQLCLient.cs index afdb06caed..985aae175b 100644 --- a/Core/Tests/Speckle.Core.Tests.Integration/GraphQLCLient.cs +++ b/Core/Tests/Speckle.Core.Tests.Integration/GraphQLCLient.cs @@ -19,7 +19,7 @@ public async Task Setup() [Test] public void ThrowsForbiddenException() { - Assert.ThrowsAsync>>( + Assert.ThrowsAsync( async () => await _client.ExecuteGraphQLRequest>( new GraphQLRequest diff --git a/Core/Tests/Speckle.Core.Tests.Unit/Api/GraphQLClient.cs b/Core/Tests/Speckle.Core.Tests.Unit/Api/GraphQLClient.cs index 0733e39a85..9baadcac8b 100644 --- a/Core/Tests/Speckle.Core.Tests.Unit/Api/GraphQLClient.cs +++ b/Core/Tests/Speckle.Core.Tests.Unit/Api/GraphQLClient.cs @@ -2,6 +2,7 @@ using GraphQL; using NUnit.Framework; using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Models; using Speckle.Core.Credentials; namespace Speckle.Core.Tests.Unit.Api; @@ -30,16 +31,10 @@ public void Dispose() private static IEnumerable ErrorCases() { + yield return new TestCaseData(typeof(SpeckleGraphQLForbiddenException), new Map { { "code", "FORBIDDEN" } }); + yield return new TestCaseData(typeof(SpeckleGraphQLForbiddenException), new Map { { "code", "UNAUTHENTICATED" } }); yield return new TestCaseData( - typeof(SpeckleGraphQLForbiddenException), - new Map { { "code", "FORBIDDEN" } } - ); - yield return new TestCaseData( - typeof(SpeckleGraphQLForbiddenException), - new Map { { "code", "UNAUTHENTICATED" } } - ); - yield return new TestCaseData( - typeof(SpeckleGraphQLInternalErrorException), + typeof(SpeckleGraphQLInternalErrorException), new Map { { "code", "INTERNAL_SERVER_ERROR" } } ); yield return new TestCaseData(typeof(SpeckleGraphQLException), new Map { { "foo", "bar" } }); @@ -109,7 +104,7 @@ public async Task TestExecuteWithResiliencePoliciesRetry() counter++; if (counter < maxRetryCount) { - throw new SpeckleGraphQLInternalErrorException(new GraphQLRequest(), new GraphQLResponse()); + throw new SpeckleGraphQLInternalErrorException(new GraphQLRequest(), new GraphQLResponse()); } return Task.FromResult(expectedResult); diff --git a/Core/Tests/Speckle.Core.Tests.Unit/Credentials/AccountServerMigrationTests.cs b/Core/Tests/Speckle.Core.Tests.Unit/Credentials/AccountServerMigrationTests.cs index 35c21adf7c..78c4f09330 100644 --- a/Core/Tests/Speckle.Core.Tests.Unit/Credentials/AccountServerMigrationTests.cs +++ b/Core/Tests/Speckle.Core.Tests.Unit/Credentials/AccountServerMigrationTests.cs @@ -1,5 +1,5 @@ using NUnit.Framework; -using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Models; using Speckle.Core.Credentials; namespace Speckle.Core.Tests.Unit.Credentials; @@ -14,12 +14,10 @@ public static IEnumerable MigrationTestCase() const string NEW_URL = "https://new.example.com"; const string OTHER_URL = "https://other.example.com"; Account oldAccount = CreateTestAccount(OLD_URL, null, new(NEW_URL)); - Account newAccount = CreateTestAccount(NEW_URL, new(OLD_URL), null); + string accountId = oldAccount.userInfo.id; // new account user must match old account user id + Account newAccount = CreateTestAccount(NEW_URL, new(OLD_URL), null, accountId); Account otherAccount = CreateTestAccount(OTHER_URL, null, null); - // new account user must match old account user id - newAccount.userInfo.id = oldAccount.userInfo.id; - List givenAccounts = new() { oldAccount, newAccount, otherAccount }; yield return new TestCaseData(givenAccounts, NEW_URL, new[] { newAccount }) @@ -59,8 +57,9 @@ public void TearDown() _accountsToCleanUp.Clear(); } - private static Account CreateTestAccount(string url, Uri movedFrom, Uri movedTo) + private static Account CreateTestAccount(string url, Uri movedFrom, Uri movedTo, string id = null) { + id ??= Guid.NewGuid().ToString(); return new Account { token = "myToken", @@ -72,7 +71,7 @@ private static Account CreateTestAccount(string url, Uri movedFrom, Uri movedTo) }, userInfo = new UserInfo { - id = Guid.NewGuid().ToString(), + id = id, email = "user@example.com", name = "user" } diff --git a/Core/Tests/Speckle.Core.Tests.Unit/Credentials/Accounts.cs b/Core/Tests/Speckle.Core.Tests.Unit/Credentials/Accounts.cs index ba0b1aec09..a9221ac4d7 100644 --- a/Core/Tests/Speckle.Core.Tests.Unit/Credentials/Accounts.cs +++ b/Core/Tests/Speckle.Core.Tests.Unit/Credentials/Accounts.cs @@ -1,5 +1,5 @@ using NUnit.Framework; -using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Models; using Speckle.Core.Credentials; namespace Speckle.Core.Tests.Unit.Credentials; diff --git a/Core/Tests/Speckle.Core.Tests.Unit/Transports/TransportTests.cs b/Core/Tests/Speckle.Core.Tests.Unit/Transports/TransportTests.cs index b264a6fe9d..067c5298c3 100644 --- a/Core/Tests/Speckle.Core.Tests.Unit/Transports/TransportTests.cs +++ b/Core/Tests/Speckle.Core.Tests.Unit/Transports/TransportTests.cs @@ -1,6 +1,5 @@ #nullable enable using NUnit.Framework; -using Speckle.Core.Api; using Speckle.Core.Transports; using Speckle.Newtonsoft.Json; diff --git a/DesktopUI2/DesktopUI2/DummyBindings.cs b/DesktopUI2/DesktopUI2/DummyBindings.cs index d818645212..5b75c85c76 100644 --- a/DesktopUI2/DesktopUI2/DummyBindings.cs +++ b/DesktopUI2/DesktopUI2/DummyBindings.cs @@ -9,6 +9,7 @@ using DesktopUI2.Models.Settings; using DesktopUI2.ViewModels; using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL; using Speckle.Core.Credentials; using Speckle.Core.Kits; using Speckle.Core.Models; @@ -224,21 +225,21 @@ public override List GetStreamsInFile() { id = "123", name = "Matteo Cominetti", - role = "stream:contributor", + role = StreamRoles.STREAM_CONTRIBUTOR, avatar = "https://avatars0.githubusercontent.com/u/2679513?s=88&v=4" }, new() { id = "321", name = "Izzy Lyseggen", - role = "stream:owner", + role = StreamRoles.STREAM_OWNER, avatar = "https://avatars2.githubusercontent.com/u/7717434?s=88&u=08db51f5799f6b21580485d915054b3582d519e6&v=4" }, new() { id = "456", name = "Dimitrie Stefanescu", - role = "stream:contributor", + role = StreamRoles.STREAM_CONTRIBUTOR, avatar = "https://avatars3.githubusercontent.com/u/7696515?s=88&u=fa253b5228d512e1ce79357c63925b7258e69f4c&v=4" } }; diff --git a/DesktopUI2/DesktopUI2/Utils.cs b/DesktopUI2/DesktopUI2/Utils.cs index 10057b4e38..d87a0f990c 100644 --- a/DesktopUI2/DesktopUI2/Utils.cs +++ b/DesktopUI2/DesktopUI2/Utils.cs @@ -19,6 +19,7 @@ using Material.Dialog.Interfaces; using SkiaSharp; using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Models; using Speckle.Core.Credentials; using Speckle.Core.Logging; diff --git a/DesktopUI2/DesktopUI2/ViewModels/AccountViewModel.cs b/DesktopUI2/DesktopUI2/ViewModels/AccountViewModel.cs index d1e5154642..f9faee7727 100644 --- a/DesktopUI2/DesktopUI2/ViewModels/AccountViewModel.cs +++ b/DesktopUI2/DesktopUI2/ViewModels/AccountViewModel.cs @@ -6,6 +6,7 @@ using Avalonia.Media.Imaging; using ReactiveUI; using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Models; using Speckle.Core.Credentials; using Speckle.Core.Helpers; using Speckle.Core.Logging; diff --git a/DesktopUI2/DesktopUI2/ViewModels/NotificationViewModel.cs b/DesktopUI2/DesktopUI2/ViewModels/NotificationViewModel.cs index 816e36c755..eb9cf10829 100644 --- a/DesktopUI2/DesktopUI2/ViewModels/NotificationViewModel.cs +++ b/DesktopUI2/DesktopUI2/ViewModels/NotificationViewModel.cs @@ -3,7 +3,7 @@ using Avalonia.Media; using Material.Icons; using ReactiveUI; -using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Models; namespace DesktopUI2.ViewModels; From 84bdb4f3fb7d6b2c2c6897115383c1952c223974 Mon Sep 17 00:00:00 2001 From: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com> Date: Mon, 8 Jul 2024 19:49:22 +0100 Subject: [PATCH 17/52] Reverted consolidation of UserInfo and UserBase (#3563) * Reverted consolidation of userinfo and user base * removed redundant comment * some small polish * correct schema nullability --- .../Client.UserOperations.cs | 2 +- .../Api/GraphQL/Models/Responses/Responses.cs | 2 +- .../GraphQL/Models/{UserInfo.cs => User.cs} | 5 +-- .../GraphQL/Resources/ActiveUserResource.cs | 2 +- Core/Core/Credentials/AccountManager.cs | 27 +++++++----- Core/Core/Credentials/Responses.cs | 42 +++++++++++++++---- .../Fixtures.cs | 3 +- 7 files changed, 57 insertions(+), 26 deletions(-) rename Core/Core/Api/GraphQL/Models/{UserInfo.cs => User.cs} (93%) diff --git a/Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.UserOperations.cs b/Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.UserOperations.cs index 66569e3b94..b1cbd1e762 100644 --- a/Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.UserOperations.cs +++ b/Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.UserOperations.cs @@ -16,7 +16,7 @@ public partial class Client /// /// [Obsolete($"Use client.{nameof(ActiveUser)}.{nameof(ActiveUserResource.Get)}")] - public async Task ActiveUserGet(CancellationToken cancellationToken = default) + public async Task ActiveUserGet(CancellationToken cancellationToken = default) { return await ActiveUser.Get(cancellationToken).ConfigureAwait(false); } diff --git a/Core/Core/Api/GraphQL/Models/Responses/Responses.cs b/Core/Core/Api/GraphQL/Models/Responses/Responses.cs index 3a16327bed..e731f3118d 100644 --- a/Core/Core/Api/GraphQL/Models/Responses/Responses.cs +++ b/Core/Core/Api/GraphQL/Models/Responses/Responses.cs @@ -8,7 +8,7 @@ namespace Speckle.Core.Api.GraphQL.Models.Responses; internal record ProjectResponse([property: JsonRequired] Project project); -internal record ActiveUserResponse(UserInfo? activeUser); +internal record ActiveUserResponse(User? activeUser); internal record LimitedUserResponse(LimitedUser? otherUser); diff --git a/Core/Core/Api/GraphQL/Models/UserInfo.cs b/Core/Core/Api/GraphQL/Models/User.cs similarity index 93% rename from Core/Core/Api/GraphQL/Models/UserInfo.cs rename to Core/Core/Api/GraphQL/Models/User.cs index b9d8926c6d..f3d6b26863 100644 --- a/Core/Core/Api/GraphQL/Models/UserInfo.cs +++ b/Core/Core/Api/GraphQL/Models/User.cs @@ -33,10 +33,7 @@ public override string ToString() } } -/// -/// Named "User" in GraphQL Schema -/// -public sealed class UserInfo : UserBase +public sealed class User : UserBase { public DateTime? createdAt { get; init; } public string email { get; init; } diff --git a/Core/Core/Api/GraphQL/Resources/ActiveUserResource.cs b/Core/Core/Api/GraphQL/Resources/ActiveUserResource.cs index f3ae780447..400385cc93 100644 --- a/Core/Core/Api/GraphQL/Resources/ActiveUserResource.cs +++ b/Core/Core/Api/GraphQL/Resources/ActiveUserResource.cs @@ -24,7 +24,7 @@ internal ActiveUserResource(ISpeckleGraphQLClient client) /// /// the requested user, or null if the user does not exist (i.e. was initialised with an unauthenticated account) /// - public async Task Get(CancellationToken cancellationToken = default) + public async Task Get(CancellationToken cancellationToken = default) { //language=graphql const string QUERY = """ diff --git a/Core/Core/Credentials/AccountManager.cs b/Core/Core/Credentials/AccountManager.cs index 38708e6cd9..bfd619f5c8 100644 --- a/Core/Core/Credentials/AccountManager.cs +++ b/Core/Core/Credentials/AccountManager.cs @@ -116,22 +116,29 @@ public static async Task GetUserInfo( new NewtonsoftJsonSerializer(), httpClient ); - //language=graphql - var request = new GraphQLRequest { Query = " query { activeUser { name email id company } }" }; - - var response = await gqlClient.SendQueryAsync(request, cancellationToken).ConfigureAwait(false); + const string QUERY = """ + query { + data:activeUser { + name + email + id + company + } + } + """; + var request = new GraphQLRequest { Query = QUERY }; + + var response = await gqlClient + .SendQueryAsync>(request, cancellationToken) + .ConfigureAwait(false); if (response.Errors != null) { - throw new SpeckleGraphQLException( - $"GraphQL request {nameof(GetUserInfo)} failed", - request, - response - ); + throw new SpeckleGraphQLException($"GraphQL request {nameof(GetUserInfo)} failed", request, response); } - return response.Data.activeUser; + return response.Data.data; } /// diff --git a/Core/Core/Credentials/Responses.cs b/Core/Core/Credentials/Responses.cs index 94f7410a85..212173f6bd 100644 --- a/Core/Core/Credentials/Responses.cs +++ b/Core/Core/Credentials/Responses.cs @@ -1,16 +1,44 @@ -#nullable disable +using System; +using Speckle.Core.Api; using Speckle.Core.Api.GraphQL.Models; namespace Speckle.Core.Credentials; -public class ActiveUserServerInfoResponse +internal sealed class ActiveUserServerInfoResponse { - public UserInfo activeUser { get; set; } - public ServerInfo serverInfo { get; set; } + public UserInfo activeUser { get; init; } + public ServerInfo serverInfo { get; init; } } -public class TokenExchangeResponse +internal sealed class TokenExchangeResponse { - public string token { get; set; } - public string refreshToken { get; set; } + public string token { get; init; } + public string refreshToken { get; init; } +} + +public sealed class UserInfo +{ + public string id { get; init; } + public string name { get; init; } + public string email { get; init; } + public string? company { get; init; } + public string? avatar { get; init; } + + [Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] + public Streams streams { get; init; } + + [Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] + public Commits commits { get; init; } +} + +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] +public class Streams +{ + public int totalCount { get; set; } +} + +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] +public class Commits +{ + public int totalCount { get; set; } } diff --git a/Core/Tests/Speckle.Core.Tests.Integration/Fixtures.cs b/Core/Tests/Speckle.Core.Tests.Integration/Fixtures.cs index 268e297943..7150dc38b8 100644 --- a/Core/Tests/Speckle.Core.Tests.Integration/Fixtures.cs +++ b/Core/Tests/Speckle.Core.Tests.Integration/Fixtures.cs @@ -114,9 +114,8 @@ await tokenResponse.Content.ReadAsStringAsync() }, serverInfo = Server }; - using var client = new Client(acc); - var user1 = await client.ActiveUserGet(); + var user1 = await AccountManager.GetUserInfo(acc.token, acc.serverInfo.url); acc.userInfo = user1; return acc; } From 9821069f866f90966469b7557afd11eb28bcf770 Mon Sep 17 00:00:00 2001 From: Alan Rynne Date: Wed, 10 Jul 2024 14:26:28 +0200 Subject: [PATCH 18/52] CNX-9438 Grasshopper in Rhino 8 - New Types Support for Text, User Attributes, and Hatches (#3569) * feat: Adds direct support for ModelObject conversions Additionally, fixes null reference exception on non-supported types. * fix: Incorrectly placed bracket --- .../ToSpeckleTaskCapableComponent.cs | 5 +-- .../Extras/Utilities.cs | 44 +++++++++++++++++-- 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/ConnectorGrasshopper/ConnectorGrasshopperShared/Conversion/ToSpeckleTaskCapableComponent.cs b/ConnectorGrasshopper/ConnectorGrasshopperShared/Conversion/ToSpeckleTaskCapableComponent.cs index bb5999362f..9b988a71b3 100644 --- a/ConnectorGrasshopper/ConnectorGrasshopperShared/Conversion/ToSpeckleTaskCapableComponent.cs +++ b/ConnectorGrasshopper/ConnectorGrasshopperShared/Conversion/ToSpeckleTaskCapableComponent.cs @@ -127,10 +127,7 @@ private IGH_Goo DoWork(object item, IGH_DataAccess DA) if (converted == null) { - AddRuntimeMessage( - GH_RuntimeMessageLevel.Warning, - $"Cannot convert item {DA.ParameterTargetPath(0)}[{DA.ParameterTargetIndex(0)}] to Speckle." - ); + AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Cannot convert {item} to Speckle."); return new GH_SpeckleBase(); } diff --git a/ConnectorGrasshopper/ConnectorGrasshopperShared/Extras/Utilities.cs b/ConnectorGrasshopper/ConnectorGrasshopperShared/Extras/Utilities.cs index 022dcfb1c8..e7f71c8af1 100644 --- a/ConnectorGrasshopper/ConnectorGrasshopperShared/Extras/Utilities.cs +++ b/ConnectorGrasshopper/ConnectorGrasshopperShared/Extras/Utilities.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Drawing; using System.Linq; +using System.Reflection; using System.Text.RegularExpressions; using System.Threading; using Grasshopper; @@ -16,6 +17,10 @@ using Speckle.Core.Logging; using Speckle.Core.Models; +#if RHINO8_OR_GREATER +using Grasshopper.Rhinoceros.Model; +#endif + namespace ConnectorGrasshopper.Extras; public static class Utilities @@ -476,14 +481,24 @@ public static object TryConvertItemToSpeckle( { if (value is null) { - return value; + return null; } - string refId = GetRefId(value); + value = UnwrapRhino8Object(value); if (value is IGH_Goo) { - value = value.GetType().GetProperty("Value").GetValue(value); + var valuePropInfo = value + .GetType() + .GetField("m_value", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + if (valuePropInfo != null) + { + var tempValue = valuePropInfo.GetValue(value); + if (tempValue != null) + { + value = tempValue; + } + } } if (value.GetType().IsSimpleType()) @@ -496,6 +511,7 @@ public static object TryConvertItemToSpeckle( var result = converter.ConvertToSpeckle(value); if (result != null) { + string refId = GetRefId(value); result.applicationId = refId; } @@ -522,6 +538,28 @@ public static object TryConvertItemToSpeckle( return null; } + private static object UnwrapRhino8Object(object value) + { +#if RHINO8_OR_GREATER + // INFO: Fill in here as we enable conversion for other non-model object types. + switch (value) + { + case ModelObject modelObject: + { + var propInfo = value + .GetType() + .GetProperty("Geometry", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + + return propInfo?.GetValue(value) ?? value; + } + default: + return value; + } +#else + return value; +#endif + } + public static string GetRefId(object value) { dynamic r = value; From e4f7c63ad7058eb7d78bde6725bc716fe7fae8f7 Mon Sep 17 00:00:00 2001 From: Ralph Wessel <150629054+r-wessel@users.noreply.github.com> Date: Thu, 11 Jul 2024 11:57:46 +0100 Subject: [PATCH 19/52] [CNX-9823] add material quantity attribute to archicad elements (#3567) * CNX-9823 Add MaterialQuantity attribute to Archicad elements (interim commit for add-on only) * CNX-9823 Add MaterialQuantity attribute to Archicad elements - Updated ArchicadConnector to deserialise and submit material quantities - Removed superfluous Material property names - Ensure MaterialQuantity members initialised to default values * Added material quantity take-offs for mesh, morph, roof, shell and slab elements * Explicitly included map & vector containers Removed C++17 use to allow AC25/26 builds Changed documentation comment style in C# to MS XML * Restored AC25 compatibility --------- Co-authored-by: r-wessel <97453280+rwessel-cerulean@users.noreply.github.com> --- .../Sources/AddOn/Commands/CreateCommand.cpp | 3 - .../Sources/AddOn/Commands/CreateDoor.cpp | 1 - .../AddOn/Commands/CreateGridElement.cpp | 1 - .../Sources/AddOn/Commands/CreateObject.cpp | 3 - .../Sources/AddOn/Commands/CreateOpening.cpp | 3 +- .../Sources/AddOn/Commands/CreateSkylight.cpp | 1 - .../Sources/AddOn/Commands/CreateWindow.cpp | 1 - .../Sources/AddOn/Commands/GetDataCommand.cpp | 416 +++- .../AddOn/Sources/AddOn/FieldNames.hpp | 1738 +++++++++-------- .../Sources/AddOn/PropertyExportManager.cpp | 22 +- .../Sources/AddOn/PropertyExportManager.hpp | 1 + .../Converters/Converters/Utils.cs | 22 + 12 files changed, 1320 insertions(+), 892 deletions(-) diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateCommand.cpp b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateCommand.cpp index b159461dd1..95f74c0232 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateCommand.cpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateCommand.cpp @@ -4,7 +4,6 @@ #include "CreateCommand.hpp" #include "LibpartImportManager.hpp" #include "ClassificationImportManager.hpp" -#include "APIHelper.hpp" #include "FieldNames.hpp" #include "OnExit.hpp" #include "ExchangeManager.hpp" @@ -124,8 +123,6 @@ GS::ObjectState CreateCommand::Execute (const GS::ObjectState& parameters, GS::P parameters.Get (GetFieldName (), objectStates); ACAPI_CallUndoableCommand (GetUndoableCommandName (), [&] () -> GSErrCode { - LibraryHelper helper (false); - GS::Array applicationObjects; AttributeManager* attributeManager = AttributeManager::GetInstance (); diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateDoor.cpp b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateDoor.cpp index 381589b14a..baa597270d 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateDoor.cpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateDoor.cpp @@ -6,7 +6,6 @@ #include "RealNumber.h" #include "DGModule.hpp" #include "LibpartImportManager.hpp" -#include "APIHelper.hpp" #include "FieldNames.hpp" #include "OnExit.hpp" #include "ExchangeManager.hpp" diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateGridElement.cpp b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateGridElement.cpp index 2b17b910a8..d18939ce1c 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateGridElement.cpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateGridElement.cpp @@ -1,7 +1,6 @@ #include "CreateGridElement.hpp" #include "ResourceIds.hpp" #include "ObjectState.hpp" -#include "APIHelper.hpp" #include "Utility.hpp" #include "Objects/Level.hpp" #include "Objects/Point.hpp" diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateObject.cpp b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateObject.cpp index 7381415197..d94909765d 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateObject.cpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateObject.cpp @@ -1,7 +1,6 @@ #include "CreateObject.hpp" #include "APIMigrationHelper.hpp" -#include "APIHelper.hpp" #include "LibpartImportManager.hpp" #include "ResourceIds.hpp" #include "Utility.hpp" @@ -131,8 +130,6 @@ GS::ObjectState CreateObject::Execute (const GS::ObjectState& parameters, GS::Pr parameters.Get (FieldNames::MeshModels, meshModels); ACAPI_CallUndoableCommand (GetUndoableCommandName (), [&] () -> GSErrCode { - LibraryHelper helper (false); - AttributeManager* attributeManager = AttributeManager::GetInstance (); LibpartImportManager* libpartImportManager = LibpartImportManager::GetInstance (); for (ModelInfo meshModel : meshModels) { diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateOpening.cpp b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateOpening.cpp index 7db770d5e1..dcee4ceea9 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateOpening.cpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateOpening.cpp @@ -7,7 +7,6 @@ #include "RealNumber.h" #include "DGModule.hpp" #include "LibpartImportManager.hpp" -#include "APIHelper.hpp" #include "FieldNames.hpp" #include "OnExit.hpp" #include "ExchangeManager.hpp" @@ -370,4 +369,4 @@ GS::String CreateOpening::GetName () const } -} \ No newline at end of file +} diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateSkylight.cpp b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateSkylight.cpp index dab3d245cb..fd56fb1efb 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateSkylight.cpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateSkylight.cpp @@ -7,7 +7,6 @@ #include "RealNumber.h" #include "DGModule.hpp" #include "LibpartImportManager.hpp" -#include "APIHelper.hpp" #include "FieldNames.hpp" #include "OnExit.hpp" #include "ExchangeManager.hpp" diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateWindow.cpp b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateWindow.cpp index aac1284db6..1cf6a03e98 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateWindow.cpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/CreateWindow.cpp @@ -7,7 +7,6 @@ #include "RealNumber.h" #include "DGModule.hpp" #include "LibpartImportManager.hpp" -#include "APIHelper.hpp" #include "FieldNames.hpp" #include "OnExit.hpp" #include "ExchangeManager.hpp" diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetDataCommand.cpp b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetDataCommand.cpp index 841b77126c..ed6d040642 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetDataCommand.cpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetDataCommand.cpp @@ -4,6 +4,382 @@ #include "Utility.hpp" #include "PropertyExportManager.hpp" +#include "BM.hpp" + +#include +#include + +namespace { + + /*! + Structure describing the surface area and volume quantities of a material in an element + */ + struct MaterialQuantity { + //The material index + API_AttributeIndex materialIndex{}; + //The net volume + double volume = 0.0; + //The net surface area + double surfaceArea = 0.0; + }; + + ///Array of elements + using ElementArray = std::vector; + ///Array of material quantities + using MaterialQuantArray = std::vector; + ///Array of quantities from a composite structure + using CompositeQuantityArray = GS::Array; + ///Function to retrieve the material index from an element + using MaterialGet = std::function; + ///Function to set the volume mask in a quantities calculation mask + using MaskSet = std::function; + ///Function to get the volume from a quantity calculation result + using QuantGet = std::function; + + + /*! + Structure facilitating the measurement of material quantities in an element + Each member is a function providing key information for quantity take-offs + */ + struct QuantityManager { + ///Gets the element building material + MaterialGet getMaterial; + ///Sets the volume take-off mask for the element + MaskSet setVolumeMask; + ///Gets the volume for the element (for the above material) + QuantGet getVolume; + ///Sets the surface area take-off mask for the element + MaskSet setAreaMask; + ///Gets the surface area for the element (for the above material) + QuantGet getArea; + }; + + /*! + Set a mask in a specified field (i.e. mark with a non-zero value to flag that this value should be calculated) + @param field A pointer to the target field + */ + void setMask(void* field) { + *(reinterpret_cast(field)) = 0xFF; + } + + /*! + Quantity management operation for handled Archicad element types (combines getter/setter for material, quants mask and quants values + NB: The API for elements is C structs with no abstraction or polymorphic behaviours. Rather than writing object wrappers for these structs, + the following collections of functions facilitate required getters/setters based on a type identifier + */ + std::map quantityManager = { + { API_BeamSegmentID, + { + [](const API_Element& element){ return element.beamSegment.assemblySegmentData.buildingMaterial; }, + [](API_ElementQuantityMask& mask){ setMask(&mask.beamSegment.volume); }, + [](const API_ElementQuantity& quant){ return quant.beamSegment.volume; }, + [](API_ElementQuantityMask& mask){ setMask(&mask.beamSegment.rightSurface); }, + [](const API_ElementQuantity& quant){ return quant.beamSegment.rightSurface; }, + } + }, + { API_ColumnSegmentID, + { + [](const API_Element& element){ return element.columnSegment.assemblySegmentData.buildingMaterial; }, + [](API_ElementQuantityMask& mask){ setMask(&mask.columnSegment.volume); }, + [](const API_ElementQuantity& quant){ return quant.columnSegment.volume; }, + [](API_ElementQuantityMask& mask){ setMask(&mask.columnSegment.coreNetSurface); }, + [](const API_ElementQuantity& quant){ return quant.columnSegment.coreNetSurface; }, + } + }, + { API_MeshID, + { + [](const API_Element& element){ return element.mesh.buildingMaterial; }, + [](API_ElementQuantityMask& mask){ setMask(&mask.mesh.volume); }, + [](const API_ElementQuantity& quant){ return quant.mesh.volume; }, + [](API_ElementQuantityMask& mask){ setMask(&mask.mesh.topSurface); }, + [](const API_ElementQuantity& quant){ return quant.mesh.topSurface; }, + } + }, + { API_MorphID, + { + [](const API_Element& element){ return element.morph.buildingMaterial; }, + [](API_ElementQuantityMask& mask){ setMask(&mask.morph.volume); }, + [](const API_ElementQuantity& quant){ return quant.morph.volume; }, + [](API_ElementQuantityMask& mask){ setMask(&mask.morph.surface); }, + [](const API_ElementQuantity& quant){ return quant.morph.surface; }, + } + }, + { API_RoofID, + { + [](const API_Element& element){ return element.roof.shellBase.buildingMaterial; }, + [](API_ElementQuantityMask& mask){ setMask(&mask.roof.volume); }, + [](const API_ElementQuantity& quant){ return quant.roof.volume; }, + [](API_ElementQuantityMask& mask){ setMask(&mask.roof.topSurface); }, + [](const API_ElementQuantity& quant){ return quant.roof.topSurface; }, + } + }, + { API_ShellID, + { + [](const API_Element& element){ return element.shell.shellBase.buildingMaterial; }, + [](API_ElementQuantityMask& mask){ setMask(&mask.shell.volume); }, + [](const API_ElementQuantity& quant){ return quant.shell.volume; }, + [](API_ElementQuantityMask& mask){ setMask(&mask.shell.referenceSurface); }, + [](const API_ElementQuantity& quant){ return quant.shell.referenceSurface; }, + } + }, + { API_SlabID, + { + [](const API_Element& element){ return element.slab.buildingMaterial; }, + [](API_ElementQuantityMask& mask){ setMask(&mask.slab.volume); }, + [](const API_ElementQuantity& quant){ return quant.slab.volume; }, + [](API_ElementQuantityMask& mask){ setMask(&mask.slab.topSurface); }, + [](const API_ElementQuantity& quant){ return quant.slab.topSurface; }, + } + }, + { API_WallID, + { + [](const API_Element& element){ return element.wall.buildingMaterial; }, + [](API_ElementQuantityMask& mask){ setMask(&mask.wall.volume); }, + [](const API_ElementQuantity& quant){ return quant.wall.volume; }, + [](API_ElementQuantityMask& mask){ setMask(&mask.wall.surface1); }, + [](const API_ElementQuantity& quant){ return quant.wall.surface1; }, + } + }, + }; + + + /*! + Collect the individual segments from a segmented assembly (e.g. beam/column) + @param segments A pointer to the assembly segments + @return An array containing the individual segments + */ + template + auto getSegments(const T* segments, Insert insert) { + ElementArray result; + if (segments == nullptr) + return result; + for (auto segmentIndex = BMGetPtrSize((GSConstPtr) segments) / sizeof(T); segmentIndex--; ) { + API_Element element{}; + insert(segments[segmentIndex], element); + result.push_back(element); + } + return result; + } //getBeamSegments + + + /*! + Get the type identifier for an element + @param element The target element + @return The element type ID, e.g. `API_WallID` + */ + API_ElemTypeID getTypeID(const API_Element& element) { +#ifdef ServerMainVers_2600 + return element.header.type.typeID; +#else + return element.header.typeID; +#endif + } + + + /*! + Determine if the specified element is an assembly, i.e. made of multiple parts (sub-elements) + @return True if the element is an assembly + */ + bool isAssembly(const API_Element& element) { + auto typeID = getTypeID(element); + return (typeID == API_BeamID) || (typeID == API_ColumnID); + } //isAssembly + + + /*! + Get the constituent parts of a specified element, e.g. the segments in a beam/column + @param element The source element + @param memo The source element memo + @return An array of element parts (empty if the element is not made of parts - the original element is never included) + */ + auto getElementParts(const API_Element& element, const API_ElementMemo& memo) { + switch (getTypeID(element)) { + case API_BeamID: + return getSegments(memo.beamSegments, [](const API_BeamSegmentType& segment, API_Element& element){ element.beamSegment = segment; }); + case API_ColumnID: + return getSegments(memo.columnSegments, [](const API_ColumnSegmentType& segment, API_Element& element){ element.columnSegment = segment; }); + default: + break; + } + return ElementArray{}; + } //getElementParts + + + /*! + Get the structure type for a specified element, e.g. how materials are arranged in the element body + @param element The target element + @return The element structural type + */ + auto getStructureType(const API_Element& element) { + switch (getTypeID(element)) { + case API_BeamSegmentID: + return element.beamSegment.assemblySegmentData.modelElemStructureType; + case API_ColumnSegmentID: + return element.columnSegment.assemblySegmentData.modelElemStructureType; + case API_RoofID: + return element.roof.shellBase.modelElemStructureType; + case API_ShellID: + return element.shell.shellBase.modelElemStructureType; + case API_SlabID: + return element.slab.modelElemStructureType; + case API_WallID: + return element.wall.modelElemStructureType; + default: + return API_BasicStructure; + } + } //getStructureType + + + /*! + Collect material quantities from the composite materials of a specified element + @param element The target element to export the properties from + @param elementQuantity Quantities extracted from the target element (out) + @param extendedQuantity Optional extended quantities calculated for some element types + @param quantityMask Mask to determine which quantities are required (minimise calculation time) + @return An error code (NoError = success) + */ + GS::ErrCode measureQuantities(const API_Element& element, API_ElementQuantity& elementQuantity, API_Quantities& extendedQuantity, + const API_QuantitiesMask& quantityMask) { + extendedQuantity.elements = &elementQuantity; + GS::Array elementPartQuantities; + API_QuantityPar quantityParameters{}; + quantityParameters.minOpeningSize = Eps; + MaterialQuantArray result; + return ACAPI_Element_GetQuantities(element.header.guid, &quantityParameters, &extendedQuantity, &quantityMask); + } //measureQuantities + + + /*! + Collect material quantities from the basic (single homogeneous) material of a specified element + @param element The target element to export the properties from + @return An array of material quantities collected from the element + */ + auto collectBasicQuantitites(const API_Element& element) { + MaterialQuantArray result; + //First determine that this element type has a suitable material definition and supports quantity take-offs for volume and area + auto manager = quantityManager.find(getTypeID(element)); + if (manager != quantityManager.end()) { + API_ElementQuantity elementQuantity{}; + API_Quantities extendedQuantity{}; + API_QuantitiesMask quantityMask{}; + //Set the appropriate masks for material volume/area quantity takeoffs + manager->second.setVolumeMask(quantityMask.elements); + manager->second.setAreaMask(quantityMask.elements); + measureQuantities(element, elementQuantity, extendedQuantity, quantityMask); + //Create a material quantity from the quantity takeoff + result.push_back({ + manager->second.getMaterial(element), + manager->second.getVolume(elementQuantity), + manager->second.getArea(elementQuantity) + }); + } + return result; + } //collectBasicQuantitites + + + /*! + Collect material quantities from the composite materials of a specified element + @param element The target element to export the properties from + @return An array of material quantities collected from the element composite structure + */ + auto collectCompositeQuantities(const API_Element& element) { + API_ElementQuantity elementQuantity{}; + API_Quantities extendedQuantity{}; + CompositeQuantityArray compositeQuantity{}; + extendedQuantity.composites = &compositeQuantity; + API_QuantitiesMask quantityMask{}; + //Set the appropriate masks for composite material volume/area quantity takeoffs + setMask(&quantityMask.composites.buildMatIndices); + setMask(&quantityMask.composites.volumes); + setMask(&quantityMask.composites.projectedArea); + measureQuantities(element, elementQuantity, extendedQuantity, quantityMask); + MaterialQuantArray result; + //Create material quantities from the quantity takeoff (one oer skin in the composite structure) + for (auto& skinQuant : compositeQuantity) + result.push_back({skinQuant.buildMatIndices, skinQuant.volumes, skinQuant.projectedArea}); + return result; + } //collectCompositeQuantities + + + /*! + Get the material quantities for a specified element + @param element The source element + @param memo The memo data attached to the element + @return An array of material quantities extracted from the element + */ + MaterialQuantArray getQuantity(const API_Element& element, const API_ElementMemo& memo) { + if (isAssembly(element)) { + MaterialQuantArray result; + //Get the constituent parts of the assembly and process each as an independent element + auto parts = getElementParts (element, memo); + if (!parts.empty()) { + API_ElementMemo partMemo{}; //NB: The memo is not used for the quantity take-offs in part elements, so an empty structure is fine + for (auto& part : parts) { + auto partMaterials = getQuantity(part, partMemo); + result.insert(result.end(), std::make_move_iterator(partMaterials.begin()), std::make_move_iterator(partMaterials.end())); + } + } + return result; + } + switch (getStructureType(element)) { + case API_BasicStructure: + return collectBasicQuantitites(element); + case API_CompositeStructure: + return collectCompositeQuantities(element); + case API_ProfileStructure: + break; + } + return MaterialQuantArray{}; + } //getQuantity + + + /*! + Serialise a specified material attribute for export + @param materialIndex The target material index + @param serialiser A serialiser for the exported data + @return NoError if the export serialisation completed without errors + */ + GS::ErrCode exportMaterial(API_AttributeIndex materialIndex, GS::ObjectState& serialiser) { + //Attempt to load the material attribute using the index + API_Attribute attribute{}; + attribute.header.index = materialIndex; + attribute.header.typeID = API_BuildingMaterialID; + auto error = ACAPI_Attribute_Get (&attribute); + if (error != NoError) + return error; + serialiser.Add(FieldNames::Material::Name, attribute.header.name); + return NoError; + } //exportMaterial + + + /*! + Serialise the material quantities of a specified element for export + @param element The target element + @param memo The memo data attached to the element + @param serialiser A serialiser for the exported data + @return NoError if the export serialisation completed without errors + */ + GS::ErrCode exportMaterialQuantities(const API_Element& element, const API_ElementMemo& memo, GS::ObjectState& serialiser) { + auto materialQuants = getQuantity(element, memo); + if (materialQuants.empty()) + return NoError; + const auto& serialMaterialQuants = serialiser.AddList (FieldNames::ElementBase::MaterialQuantities); + for (auto& quantity : materialQuants) { + GS::ObjectState serialMaterialQuant, serialMaterial; + auto error = exportMaterial (quantity.materialIndex, serialMaterial); + if (error != NoError) + return error; + serialMaterialQuant.Add(FieldNames::ElementBase::Quantity::Material, serialMaterial); + serialMaterialQuant.Add(FieldNames::ElementBase::Quantity::Volume, quantity.volume); + serialMaterialQuant.Add(FieldNames::ElementBase::Quantity::Area, quantity.surfaceArea); + serialMaterialQuant.Add(FieldNames::ElementBase::Quantity::Units, "m"); + serialMaterialQuants(serialMaterialQuant); + } + return NoError; + } //exportMaterialQuantities + +} + namespace AddOnCommands { @@ -164,19 +540,26 @@ GS::ErrCode SerializePropertyGroups (const GS::Array& de } +/*! + Export attached Archicad properties, listing properties (calculated) and material quantities from a specified element + @param element The target element to export the properties from + @param sendProperties True to export the Archicad properties attached to the element + @param sendListingParameters True to export calculated listing parameters from the element, e.g. top/bottom surface area etc + @param systemItemPairs Array pairing a classification system ID against a classification item ID (attached to the target element) + @param os A collector/serialiser for the exported data + @return NoError if the export was successful + */ GS::ErrCode GetDataCommand::ExportProperties (const API_Element& element, const bool& sendProperties, const bool& sendListingParameters, const GS::Array>& systemItemPairs, GS::ObjectState& os) const { - GS::ErrCode err = NoError; - if (!sendProperties && !sendListingParameters) return NoError; GS::Array elementDefinitions; GS::Array < GS::Pair>> componentsDefinitions; - err = PropertyExportManager::GetInstance ()->GetElementDefinitions (element, sendProperties, sendListingParameters, systemItemPairs, elementDefinitions, componentsDefinitions); + GS::ErrCode err = PropertyExportManager::GetInstance ()->GetElementDefinitions (element, sendProperties, sendListingParameters, systemItemPairs, elementDefinitions, componentsDefinitions); if (err != NoError) - return false; + return err; // element properties { @@ -275,23 +658,32 @@ GS::UInt64 GetDataCommand::GetMemoMask () const } -GS::ErrCode GetDataCommand::SerializeElementType(const API_Element& elem, const API_ElementMemo& /*memo*/, GS::ObjectState& os, const bool& sendProperties, const bool& sendListingParameters) const +/*! + Serialise the attributes, properties and quantities of a specified element + + elem: The target element + memo: Memo data attached to the element + os: A collector/serialiser for the exported data + sendProperties: True to export the Archicad properties attached to the element + sendListingParameters: True to export calculated listing parameters from the element, e.g. top/bottom surface area etc + + return: NoError if the serialisation was successful + */ +GS::ErrCode GetDataCommand::SerializeElementType(const API_Element& elem, const API_ElementMemo& memo, GS::ObjectState& os, const bool& sendProperties, const bool& sendListingParameters) const { - GS::ErrCode err = NoError; - os.Add(FieldNames::ElementBase::ApplicationId, APIGuidToString (elem.header.guid)); API_Attribute attribute; BNZeroMemory (&attribute, sizeof (API_Attribute)); attribute.header.typeID = API_LayerID; attribute.header.index = elem.header.layer; - if (NoError == ACAPI_Attribute_Get (&attribute)) { + if (ACAPI_Attribute_Get (&attribute) == NoError) { os.Add(FieldNames::ElementBase::Layer, GS::UniString{attribute.header.name}); } - - err = ExportClassificationsAndProperties (elem, os, sendProperties, sendListingParameters); - - return err; + auto err = exportMaterialQuantities (elem, memo, os); + if (err != NoError) + return err; + return ExportClassificationsAndProperties (elem, os, sendProperties, sendListingParameters); } diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/FieldNames.hpp b/ConnectorArchicad/AddOn/Sources/AddOn/FieldNames.hpp index 3b05a70287..81ac117969 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/FieldNames.hpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/FieldNames.hpp @@ -3,869 +3,881 @@ namespace FieldNames { - - -namespace ApplicationObject { -static const char* ApplicationObjects = "applicationObjects"; -static const char* Status = "Status"; -static const char* StateCreated = "Created"; -static const char* StateSkipped = "Skipped"; -static const char* StateUpdated = "Updated"; -static const char* StateFailed = "Failed"; -static const char* StateRemoved = "Removed"; -static const char* StateUnknown = "Unknown"; -static const char* OriginalId = "OriginalId"; -static const char* CreatedIds = "CreatedIds"; -static const char* Log = "Log"; -} - -namespace ElementBase -{ -static const char* Id = "id"; -static const char* ApplicationId = "applicationId"; -static const char* ApplicationIds = "applicationIds"; -static const char* ParentElementId = "parentApplicationId"; -static const char* ElementFilter = "elementFilter"; -static const char* FilterBy = "filterBy"; -static const char* ElementType = "elementType"; -static const char* ElementTypes = "elementTypes"; -static const char* Elements = "elements"; -static const char* SubElements = "subElements"; -static const char* Level = "level"; -static const char* Layer = "layer"; -static const char* Shape = "shape"; -static const char* Shape1 = "shape1"; -static const char* Shape2 = "shape2"; - -static const char* Classifications = "classifications"; -namespace Classification -{ -static const char* System = "system"; -static const char* Code = "code"; // id is reserved for Speckle id -static const char* Name = "name"; -} - -static const char* ElementProperties = "elementProperties"; -static const char* ComponentProperties = "componentProperties"; -static const char* SendListingParameters = "sendListingParameters"; -static const char* SendProperties = "sendProperties"; -namespace ComponentProperty -{ -static const char* Name = "name"; -static const char* PropertyGroups = "propertyGroups"; -} -namespace PropertyGroup -{ -static const char* Name = "name"; -static const char* PropertList = "propertyList"; -} -namespace Property -{ -static const char* Name = "name"; -static const char* Value = "value"; -static const char* Values = "values"; -static const char* Units = "units"; -} -} - -static const char* Elements = "elements"; -static const char* Beams = "beams"; -static const char* Columns = "columns"; -static const char* DirectShapes = "directShapes"; -static const char* Doors = "doors"; -static const char* GridElements = "gridElements"; -static const char* Objects = "objects"; -static const char* MeshModels = "meshModels"; -static const char* Roofs = "roofs"; -static const char* Shells = "shells"; -static const char* Skylights = "skylights"; -static const char* Slabs = "slabs"; -static const char* Walls = "walls"; -static const char* Windows = "windows"; -static const char* Zones = "zones"; -static const char* Openings = "openings"; - -static const char* Models = "models"; -static const char* SubelementModels = "subelementModels"; - -static const char* ShowOnStories = "showOnStories"; -static const char* VisibilityContData = "visibilityCont"; -static const char* VisibilityFillData = "visibilityFill"; -static const char* ShowOnHome = "showOnHome"; -static const char* ShowAllAbove = "showAllAbove"; -static const char* ShowAllBelow = "showAllBelow"; -static const char* ShowRelAbove = "showRelAbove"; -static const char* ShowRelBelow = "showRelBelow"; -static const char* HomeStoryOnlyValueName = "homeStoryOnly"; -static const char* HomeAndOneStoryUpValueName = "homeAndOneStoryUp"; -static const char* HomeAndOneStoryDownValueName = "homeAndOneStoryDown"; -static const char* HomeAndOneStoryUpAndDownValueName = "homeAndOneStoryUpAndDown"; -static const char* OneStoryUpValueName = "oneStoryUp"; -static const char* OneStoryDownValueName = "oneStoryDown"; -static const char* AllStoriesValueName = "allStories"; -static const char* AllRelevantStoriesValueName = "allRelevantStories"; -static const char* CustomStoriesValueName = "custom"; - -static const char* CoverFillTransformationType = "coverFillTransformationType"; -static const char* HatchOrientationType = "coverFillTransformationType"; -static const char* LinkToProjectOriginValueName = "linkToProjectOrigin"; -static const char* LinkToFillOriginValueName = "linkToFillOrigin"; -static const char* CustomDistortionValueName = "customDistortion"; -static const char* ThreeDDistortionValueName = "3DDistortion"; - - -namespace AssemblySegment { -static const char* SegmentName = "Segment #%d"; -static const char* SchemeName = "Scheme #%d"; -static const char* CutName = "Cut #%d"; - -static const char* HoleData = "Holes"; -static const char* SegmentData = "Segments"; -static const char* SchemeData = "Schemes"; -static const char* CutData = "Cuts"; -} - - -namespace PivotPolyEdge { -static const char* EdgeName = "Roof Pivot Poly Edge #%d"; -static const char* EdgeData = "roofPivotPolyEdges"; -} - - -namespace PivotPolyEdgeData { -static const char* NumLevelEdgeData = "nLevelEdgeData"; -} - - -namespace LevelEdge { -static const char* LevelEdgeName = "Roof Level #%d"; -static const char* LevelEdgeData = "roofLevels"; -} - - -namespace RoofSegmentData { -static const char* LevelAngle = "edgeLevelAngle"; -static const char* TopMaterial = "topMaterial"; -static const char* BottomMaterial = "bottomMaterial"; -static const char* CoverFillType = "coverFillType"; -static const char* EavesOverhang = "eavesOverhang"; -static const char* AngleType = "angleType"; -} - - -namespace Wall -{ -// Wall geometry -static const char* BaseOffset = "baseOffset"; -static const char* StartPoint = "startPoint"; -static const char* EndPoint = "endPoint"; -static const char* WallComplexity = "wallComplexity"; -static const char* Structure = "structure"; -static const char* GeometryMethod = "geometryMethod"; -static const char* BuildingMaterialName = "buildingMaterialName"; -static const char* CompositeName = "compositeName"; -static const char* ProfileName = "profileName"; -static const char* ArcAngle = "arcAngle"; -static const char* Thickness = "thickness"; -static const char* FirstThickness = "firstThickness"; -static const char* SecondThickness = "secondThickness"; -static const char* OutsideSlantAngle = "outsideSlantAngle"; -static const char* InsideSlantAngle = "insideSlantAngle"; -static const char* Height = "height"; -static const char* PolyCanChange = "polyWalllCornersCanChange"; -// Wall and stories relation -static const char* TopOffset = "topOffset"; -static const char* RelativeTopStoryIndex = "relativeTopStory"; -static const char* ReferenceLineLocation = "referenceLineLocation"; -static const char* ReferenceLineOffset = "referenceLineOffset"; -static const char* OffsetFromOutside = "offsetFromOutside"; -static const char* ReferenceLineStartIndex = "referenceLineStartIndex"; -static const char* ReferenceLineEndIndex = "referenceLineEndIndex"; -static const char* Flipped = "flipped"; -// Floor Plan and Section - Floor Plan Display -static const char* DisplayOptionName = "displayOptionName"; -static const char* ViewDepthLimitationName = "showProjectionName"; -// Floor Plan and Section - Cut Surfaces -static const char* CutLinePenIndex = "cutLinePen"; -static const char* CutLinetypeName = "cutLinetype"; -static const char* OverrideCutFillPenIndex = "overrideCutFillPen"; -static const char* OverrideCutFillBackgroundPenIndex = "overrideCutFillBackgroundPen"; -// Floor Plan and Section - Outlines -static const char* UncutLinePenIndex = "uncutLinePen"; -static const char* UncutLinetypeName = "uncutLinetype"; -static const char* OverheadLinePenIndex = "overheadLinePen"; -static const char* OverheadLinetypeName = "overheadLinetype"; -// Model - Override Surfaces -static const char* ReferenceMaterialName = "referenceMaterialName"; -static const char* ReferenceMaterialStartIndex = "referenceMaterialStartIndex"; -static const char* ReferenceMaterialEndIndex = "referenceMaterialEndIndex"; -static const char* OppositeMaterialName = "oppositeMaterialName"; -static const char* OppositeMaterialStartIndex = "oppositeMaterialStartIndex"; -static const char* OppositeMaterialEndIndex = "oppositeMaterialEndIndex"; -static const char* SideMaterialName = "sideMaterialName"; -static const char* MaterialsChained = "materialsChained"; -static const char* InheritEndSurface = "inheritEndSurface"; -static const char* AlignTexture = "alignTexture"; -static const char* Sequence = "sequence"; -// Model - Log Details (log height, start with half log, surface of horizontal edges, log shape) -static const char* LogHeight = "logHeight"; -static const char* StartWithHalfLog = "startWithHalfLog"; -static const char* SurfaceOfHorizontalEdges = "surfaceOfHorizontalEdges"; -static const char* LogShape = "logShape"; -// Model - Defines the relation of wall to zones (Zone Boundary, Reduce Zone Area Only, No Effect on Zones) -static const char* WallRelationToZoneName = "wallRelationToZoneName"; -// Does it have any embedded object? -static const char* HasDoor = "hasDoor"; -static const char* HasWindow = "hasWindow"; -} - - -namespace OpeningBase { -static const char* Door = "Door #%d"; -// -static const char* guid = "guid"; -// OpeningBase -static const char* width = "width"; -static const char* height = "height"; -static const char* subFloorThickness = "subFloorThickness"; -static const char* reflected = "reflected"; -static const char* oSide = "oSide"; -static const char* refSide = "refSide"; -static const char* VerticalLinkTypeName = "verticalLinkTypeName"; -static const char* VerticalLinkStoryIndex = "verticalLinkStoryIndex"; -static const char* WallCutUsing = "wallCutUsing"; - -static const char* PenIndex = "pen"; -static const char* LineTypeName = "lineTypeName"; -static const char* SectFillName = "sectFill"; -static const char* SectFillPenIndex = "sectFillPen"; -static const char* SectBackgroundPenIndex = "sectBackgroundPen"; -static const char* SectContPenIndex = "sectContPen"; -static const char* CutLineTypeName = "cutLineType"; -static const char* AboveViewLineTypeName = "aboveViewLineType"; -static const char* AboveViewLinePenIndex = "aboveViewLinePen"; -static const char* BelowViewLinePenIndex = "belowViewLinePen"; -static const char* BelowViewLineTypeName = "belowViewLineType"; -static const char* UseObjectPens = "useObjectPens"; -static const char* UseObjLinetypes = "useObjLinetypes"; -static const char* UseObjMaterials = "useObjMaterials"; -static const char* UseObjSectAttrs = "useObjSectAttrs"; -static const char* BuildingMaterialName = "buildingMaterial"; -static const char* LibraryPart = "libraryPart"; -static const char* DisplayOptionName = "displayOptionName"; - -static const char* revealDepthFromSide = "revealDepthFromSide"; -static const char* jambDepthHead = "jambDepthHead"; -static const char* jambDepth = "jambDepth"; -static const char* jambDepth2 = "jambDepth2"; -static const char* objLoc = "objLoc"; -static const char* lower = "lower"; -static const char* directionType = "directionType"; - -static const char* startPoint = "startPoint"; -static const char* dirVector = "dirVector"; -} - - -namespace Beam -{ -// Positioning -static const char* begC = "begC"; -static const char* endC = "endC"; -static const char* level = "level"; -static const char* isSlanted = "isSlanted"; -static const char* slantAngle = "slantAngle"; -static const char* beamShape = "beamShape"; -static const char* sequence = "sequence"; -static const char* curveAngle = "curveAngle"; -static const char* verticalCurveHeight = "verticalCurveHeight"; -static const char* isFlipped = "isFlipped"; -// End Cuts -static const char* nCuts = "nCuts"; -// Rreference Axis -static const char* anchorPoint = "anchorPoint"; -static const char* offset = "offset"; -static const char* profileAngle = "profileAngle"; -// Segment -static const char* segments = "segments"; -static const char* nSegments = "nSegments"; -static const char* nProfiles = "nProfiles"; - - -namespace BeamSegment -{ -// Segment override materials -static const char* LeftMaterial = "leftMaterial"; -static const char* TopMaterial = "topMaterial"; -static const char* RightMaterial = "rightMaterial"; -static const char* BottomMaterial = "bottomMaterial"; -static const char* EndsMaterial = "endsMaterial"; -// Segment - The overridden materials are chained -static const char* MaterialsChained = "materialChained"; -// Segment -static const char* segmentData = "assemblySegmentData"; -} - - -// Scheme -static const char* nSchemes = "nSchemes"; -// Hole -static const char* HoleName = "Hole #%d"; -static const char* holeType = "holeType"; -static const char* holeContourOn = "holeContourOn"; -static const char* holeId = "holeId"; -static const char* centerx = "centerx"; -static const char* centerz = "centerz"; -static const char* width = "width"; -static const char* height = "height"; -// Floor Plan and Section - Floor Plan Display -static const char* DisplayOptionName = "displayOptionName"; -static const char* UncutProjectionModeName = "uncutProjectionMode"; -static const char* OverheadProjectionModeName = "overheadProjectionMode"; -static const char* ViewDepthLimitationName = "showProjectionName"; -// Floor Plan and Section - Cut Surfaces -static const char* cutContourLinePen = "cutContourLinePen"; -static const char* CutContourLinetypeName = "cutContourLineType"; -static const char* OverrideCutFillPenIndex = "overrideCutFillPen"; -static const char* OverrideCutFillBackgroundPenIndex = "overrideCutFillBackgroundPen"; -// Floor Plan and Section - Outlines -static const char* ShowOutlineIndex = "showOutline"; -static const char* UncutLinePenIndex = "uncutLinePen"; -static const char* UncutLinetypeName = "uncutLinetype"; -static const char* OverheadLinePenIndex = "overheadLinePen"; -static const char* OverheadLinetypeName = "overheadLinetype"; -static const char* HiddenLinePenIndex = "hiddenLinePen"; -static const char* HiddenLinetypeName = "hiddenLinetype"; -// Floor Plan and Section - Symbol -static const char* ShowReferenceAxisIndex = "showReferenceAxis"; -static const char* refPen = "referencePen"; -static const char* refLtype = "referenceLinetype"; -// Floor Plan and Section - Cover Fills -static const char* useCoverFill = "useCoverFill"; -static const char* useCoverFillFromSurface = "useCoverFillFromSurface"; -static const char* coverFillForegroundPen = "coverFillForegroundPen"; -static const char* coverFillBackgroundPen = "coverFillBackgroundPen"; -static const char* coverFillType = "coverFillType"; -static const char* CoverFillTransformationOrigoX = "coverFillTransformationOrigoX"; -static const char* CoverFillTransformationOrigoY = "coverFillTransformationOrigoY"; -static const char* CoverFillTransformationXAxisX = "coverFillTransformationXAxisX"; -static const char* CoverFillTransformationXAxisY = "coverFillTransformationXAxisY"; -static const char* CoverFillTransformationYAxisX = "coverFillTransformationYAxisX"; -static const char* CoverFillTransformationYAxisY = "coverFillTransformationYAxisY"; -} - - -namespace Column -{ -// Positioning - geometry -static const char* origoPos = "origoPos"; -static const char* height = "height"; -// Positioning - story relation -static const char* bottomOffset = "bottomOffset"; -static const char* topOffset = "topOffset"; -static const char* relativeTopStory = "relativeTopStory"; -// Positioning - slanted column -static const char* isSlanted = "isSlanted"; -static const char* slantAngle = "slantAngle"; -static const char* slantDirectionAngle = "slantDirectionAngle"; -static const char* isFlipped = "isFlipped"; -// Positioning - wrapping -static const char* Wrapping = "wrapping"; -// Positioning - Defines the relation of column to zones (Zone Boundary, Reduce Zone Area Only, No Effect on Zones) -static const char* ColumnRelationToZoneName = "columnRelationToZoneName"; -// End Cuts -static const char* nCuts = "nCuts"; -// Reference Axis -static const char* coreAnchor = "coreAnchor"; -static const char* axisRotationAngle = "axisRotationAngle"; -// Segment -static const char* segments = "segments"; -static const char* nSegments = "nSegments"; -static const char* nProfiles = "nProfiles"; - - -namespace ColumnSegment -{ -// Segment - Veneer attributes -static const char* VenType = "veneerType"; -static const char* VenBuildingMaterial = "veneerBuildingMaterial"; -static const char* VenThick = "veneerThick"; -// Segment - The extrusion overridden material name -static const char* ExtrusionSurfaceMaterial = "extrusionSurfaceMaterial"; -// Segment - The ends overridden material name -static const char* EndsSurfaceMaterial = "endsSurfaceMaterial"; -// Segment - The overridden materials are chained -static const char* MaterialsChained = "materialChained"; -// Segment -static const char* segmentData = "assemblySegmentData"; -} - - -// Scheme -static const char* nSchemes = "nSchemes"; -// Floor Plan and Section - Floor Plan Display -static const char* DisplayOptionName = "displayOptionName"; -static const char* ViewDepthLimitationName = "showProjectionName"; -// Floor Plan and Section - Cut Surfaces -static const char* corePen = "corePen"; -static const char* CoreLinetypeName = "contLtype"; -static const char* VeneerPenIndex = "venLinePen"; -static const char* VeneerLinetypeName = "venLineType"; -static const char* OverrideCutFillPenIndex = "overrideCutFillPen"; -static const char* OverrideCutFillBackgroundPenIndex = "overrideCutFillBackgroundPen"; -// Floor Plan and Section - Outlines -static const char* UncutLinePenIndex = "uncutLinePen"; -static const char* UncutLinetypeName = "uncutLinetype"; -static const char* OverheadLinePenIndex = "overheadLinePen"; -static const char* OverheadLinetypeName = "overheadLinetype"; -static const char* HiddenLinePenIndex = "hiddenLinePen"; -static const char* HiddenLinetypeName = "hiddenLinetype"; -// Floor Plan and Section - Floor Plan Symbol -static const char* CoreSymbolTypeName = "coreSymbolTypeName"; -static const char* coreSymbolPar1 = "coreSymbolPar1"; -static const char* coreSymbolPar2 = "coreSymbolPar2"; -static const char* CoreSymbolPenIndex = "coreSymbolPen"; -// Floor Plan and Section - Cover Fills -static const char* useCoverFill = "useCoverFill"; -static const char* useCoverFillFromSurface = "useCoverFillFromSurface"; -static const char* coverFillForegroundPen = "coverFillForegroundPen"; -static const char* coverFillBackgroundPen = "coverFillBackgroundPen"; -static const char* coverFillType = "coverFillType"; -static const char* CoverFillTransformationOrigoX = "coverFillTransformationOrigoX"; -static const char* CoverFillTransformationOrigoY = "coverFillTransformationOrigoY"; -static const char* CoverFillTransformationXAxisX = "coverFillTransformationXAxisX"; -static const char* CoverFillTransformationXAxisY = "coverFillTransformationXAxisY"; -static const char* CoverFillTransformationYAxisX = "coverFillTransformationYAxisX"; -static const char* CoverFillTransformationYAxisY = "coverFillTransformationYAxisY"; -} - - -namespace AssemblySegmentData { -static const char* circleBased = "circleBased"; -static const char* modelElemStructureType = "modelElemStructureType"; -static const char* nominalHeight = "nominalHeight"; -static const char* nominalWidth = "nominalWidth"; -static const char* isWidthAndHeightLinked = "isWidthAndHeightLinked"; -static const char* isHomogeneous = "isHomogeneous"; -static const char* endWidth = "endWidth"; -static const char* endHeight = "endHeight"; -static const char* isEndWidthAndHeightLinked = "isEndWidthAndHeightLinked"; -static const char* buildingMaterial = "buildingMaterial"; -static const char* profileAttrName = "profileAttrName"; -} - - -namespace AssemblySegmentSchemeData { -static const char* lengthType = "lengthType"; -static const char* fixedLength = "fixedLength"; -static const char* lengthProportion = "lengthProportion"; -} - - -namespace AssemblySegmentCutData { -static const char* cutType = "cutType"; -static const char* customAngle = "customAngle"; -} - - -namespace Object -{ -// Main -static const char* pos = "pos"; -static const char* transform = "transform"; -} - - -namespace Opening -{ -// Floor Plan Parameters -static const char* FloorPlanDisplayMode = "floorPlanDisplayMode"; -static const char* ConnectionMode = "connectionMode"; - -// Cut Surfaces Parameters -static const char* CutSurfacesUseLineOfCutElements = "cutsurfacesUseLineOfCutElements"; -static const char* CutSurfacesLinePenIndex = "cutsurfacesLinePenIndex"; -static const char* CutSurfacesLineIndex = "cutsurfacesLineIndex"; - -// Outlines Parameters -static const char* OutlinesStyle = "outlinesStyle"; -static const char* OutlinesUseLineOfCutElements = "outlinesUseLineOfCutElements"; // => Cut Surfaces Parameters-ben is megtallhat -static const char* OutlinesUncutLineIndex = "outlinesUncutLineIndex"; -static const char* OutlinesOverheadLineIndex = "outlinesOverheadLineIndex"; -static const char* OutlinesUncutLinePenIndex = "outlinesUncutLinePenIndex"; -static const char* OutlinesOverheadLinePenIndex = "outlinesOverheadLinePenIndex"; - -// Opening Cover Fills Parameters -static const char* UseCoverFills = "useCoverFills"; -static const char* UseFillsOfCutElements = "useFillsOfCutElements"; -static const char* CoverFillIndex = "coverFillIndex"; -static const char* CoverFillPenIndex = "coverFillPenIndex"; -static const char* CoverFillBackgroundPenIndex = "coverFillBackgroundPenIndex"; -static const char* CoverFillOrientation = "coverFillOrientation"; - -// Cover Fill Transformation Parameters -static const char* CoverFillTransformationOrigoX = "coverFillTransformationOrigoX"; -static const char* CoverFillTransformationOrigoY = "coverFillTransformationOrigoY"; -static const char* CoverFillTransformationOrigoZ = "coverFillTransformationOrigoZ"; -static const char* CoverFillTransformationXAxisX = "coverFillTransformationXAxisX"; -static const char* CoverFillTransformationXAxisY = "coverFillTransformationXAxisY"; -static const char* CoverFillTransformationXAxisZ = "coverFillTransformationXAxisZ"; -static const char* CoverFillTransformationYAxisX = "coverFillTransformationYAxisX"; -static const char* CoverFillTransformationYAxisY = "coverFillTransformationYAxisY"; -static const char* CoverFillTransformationYAxisZ = "coverFillTransformationYAxisZ"; - -// Reference Axis Parameters -static const char* ShowReferenceAxis = "showReferenceAxis"; -static const char* ReferenceAxisPenIndex = "referenceAxisPenIndex"; -static const char* ReferenceAxisLineTypeIndex = "referenceAxisLineTypeIndex"; -static const char* ReferenceAxisOverhang = "referenceAxisOverhang"; - -// Extrusion Geometry Parameters -static const char* ExtrusionGeometryBasePoint = "extrusionGeometryBasePoint"; -static const char* ExtrusionGeometryXAxis = "extrusionGeometryXAxis"; -static const char* ExtrusionGeometryYAxis = "extrusionGeometryYAxis"; -static const char* ExtrusionGeometryZAxis = "extrusionGeometryZAxis"; -static const char* BasePolygonType = "basePolygonType"; -static const char* Width = "width"; -static const char* Height = "height"; -static const char* Constraint = "constraint"; -static const char* Anchor = "anchor"; -static const char* AnchorIndex = "anchorIndex"; -static const char* AnchorAltitude = "anchorAltitude"; -static const char* LimitType = "limitType"; -static const char* ExtrusionStartOffSet = "extrusionStartOffSet"; -static const char* FiniteBodyLength = "finiteBodyLength"; -static const char* LinkedStatus = "linkedStatus"; -static const char* NCoords = "nCoords"; -static const char* NSubPolys = "nSubPolys"; -static const char* NArcs = "nArcs"; -} - - -namespace GridElement -{ -// Main -static const char* begin = "begin"; -static const char* end = "end"; -static const char* angle = "angle"; -static const char* markerText = "markerText"; -static const char* isArc = "isArc"; -static const char* arcAngle = "arcAngle"; -} - - -namespace Slab -{ -// Geometry and positioning -static const char* Thickness = "thickness"; -static const char* Structure = "structure"; -static const char* CompositeName = "compositeName"; -static const char* BuildingMaterialName = "buildingMaterialName"; -static const char* ReferencePlaneLocation = "referencePlaneLocation"; -// Edge trims -static const char* EdgeAngleType = "edgeAngleType"; -static const char* EdgeAngle = "edgeAngle"; -// Floor Plan and Section - Cut Surfaces -static const char* sectContPen = "sectContPen"; -static const char* sectContLtype = "sectContLtype"; -static const char* cutFillPen = "cutFillPen"; -static const char* cutFillBackgroundPen = "cutFillBackgroundPen"; -// Floor Plan and Section - Outlines -static const char* contourPen = "contourPen"; -static const char* contourLineType = "contourLineType"; -static const char* hiddenContourLinePen = "hiddenContourLinePen"; -static const char* hiddenContourLineType = "hiddenContourLineType"; -// Floor Plan and Section - Cover Fills -static const char* useFloorFill = "useFloorFill"; -static const char* floorFillPen = "floorFillPen"; -static const char* floorFillBGPen = "floorFillBGPen"; -static const char* floorFillName = "floorFillName"; -static const char* use3DHatching = "use3DHatching"; -static const char* hatchOrientation = "hatchOrientation"; -static const char* hatchOrientationOrigoX = "hatchOrientationOrigoX"; -static const char* hatchOrientationOrigoY = "hatchOrientationOrigoY"; -static const char* hatchOrientationXAxisX = "hatchOrientationXAxisX"; -static const char* hatchOrientationXAxisY = "hatchOrientationXAxisY"; -static const char* hatchOrientationYAxisX = "hatchOrientationYAxisX"; -static const char* hatchOrientationYAxisY = "hatchOrientationYAxisY"; -// Model -static const char* topMat = "topMat"; -static const char* sideMat = "sideMat"; -static const char* botMat = "botMat"; -static const char* materialsChained = "materialsChained"; -} - - -namespace Roof -{ -// Geometry and positioning -static const char* RoofClassName = "roofClassName"; -static const char* PlaneRoofAngle = "planeRoofAngle"; -static const char* BaseLine = "baseLine"; -static const char* BegC = "begC"; -static const char* EndC = "endC"; -static const char* PosSign = "posSign"; -static const char* PivotPolygon = "pivotPolygon"; - -// Level -static const char* levels = "levels"; -static const char* LevelNum = "levelNum"; - -namespace RoofLevel { -static const char* LevelName = "Level #%d"; -static const char* levelData = "levels"; -} - -namespace LevelData { -static const char* LevelHeight = "levelHeight"; -static const char* LevelAngle = "levelAngle"; -} - -static const char* Thickness = "thickness"; -static const char* Structure = "structure"; -static const char* CompositeName = "compositeName"; -static const char* BuildingMaterialName = "buildingMaterialName"; -// Edge trims -static const char* EdgeAngleType = "edgeAngleType"; -static const char* EdgeAngle = "edgeAngle"; -// Floor Plan and Section - Floor Plan Display -static const char* DisplayOptionName = "displayOptionName"; -static const char* ViewDepthLimitationName = "showProjectionName"; -// Floor Plan and Section - Cut Surfaces -static const char* SectContPen = "sectContPen"; -static const char* SectContLtype = "sectContLtype"; -static const char* CutFillPen = "cutFillPen"; -static const char* CutFillBackgroundPen = "cutFillBackgroundPen"; -// Floor Plan and Section - Outlines -static const char* ContourPen = "contourPen"; -static const char* ContourLineType = "contourLineType"; -static const char* OverheadLinePen = "overheadLinePen"; -static const char* OverheadLinetype = "overheadLinetype"; -// Floor Plan and Section - Cover Fills -static const char* UseFloorFill = "useFloorFill"; -static const char* FloorFillPen = "floorFillPen"; -static const char* FloorFillBGPen = "floorFillBGPen"; -static const char* FloorFillName = "floorFillName"; -static const char* Use3DHatching = "use3DHatching"; -static const char* UseFillLocBaseLine = "useFillLocBaseLine"; -static const char* UseSlantedFill = "useSlantedFill"; -static const char* HatchOrientation = "hatchOrientation"; -static const char* HatchOrientationOrigoX = "hatchOrientationOrigoX"; -static const char* HatchOrientationOrigoY = "hatchOrientationOrigoY"; -static const char* HatchOrientationXAxisX = "hatchOrientationXAxisX"; -static const char* HatchOrientationXAxisY = "hatchOrientationXAxisY"; -static const char* HatchOrientationYAxisX = "hatchOrientationYAxisX"; -static const char* HatchOrientationYAxisY = "hatchOrientationYAxisY"; -// Model -static const char* TopMat = "topMat"; -static const char* SideMat = "sideMat"; -static const char* BotMat = "botMat"; -static const char* MaterialsChained = "materialsChained"; -static const char* TrimmingBodyName = "trimmingBodyName"; -} - - -namespace Shell -{ -// Geometry and positioning -static const char* ShellClassName = "shellClassName"; - -static const char* BasePlane = "basePlane"; - -static const char* Flipped = "flipped"; -static const char* HasContour = "hasContour"; -static const char* NumHoles = "numHoles"; -static const char* ShellContourName = "shellContour #%d"; -static const char* ShellContourData = "shellContours"; -static const char* ShellContourPlane = "shellContourPlane"; -static const char* ShellContourPoly = "shellContourPoly"; -static const char* ShellContourHeight = "shellContourHeight"; -static const char* ShellContourID = "shellContourID"; -static const char* ShellContourSideTypeName = "sideTypeName"; -static const char* ShellContourSideAngle = "sideAngle"; -static const char* ShellContourEdgeTypeName = "edgeTypeName"; -static const char* ShellContourEdgeSideMaterial = "edgeSideMaterial"; -static const char* ShellContourEdgeName = "shellContourEdge #%d"; -static const char* ShellContourEdgeData = "shellContourEdges"; -static const char* DefaultEdgeType = "defaultEdgeType"; - -static const char* SlantAngle = "slantAngle"; -static const char* RevolutionAngle = "revolutionAngle"; -static const char* DistortionAngle = "distortionAngle"; -static const char* SegmentedSurfaces = "segmentedSurfaces"; -static const char* ShapePlaneTilt = "shapePlaneTilt"; -static const char* BegPlaneTilt = "begPlaneTilt"; -static const char* EndPlaneTilt = "endPlaneTilt"; -static const char* AxisBase = "axisBase"; -static const char* Plane1 = "plane1"; -static const char* Plane2 = "plane2"; -static const char* BegC = "begC"; -static const char* BegAngle = "begAngle"; -static const char* ExtrusionVector = "extrusionVector"; -static const char* ShapeDirection = "shapeDirection"; -static const char* DistortionVector = "distortionVector"; -static const char* MorphingRuleName = "morphingRuleName"; - -static const char* BegShapeEdge = "begShapeEdge"; -static const char* BegShapeEdgeTrimSideType = "begShapeEdgeTrimSideType"; -static const char* BegShapeEdgeTrimSideAngle = "begShapeEdgeTrimSideAngle"; -static const char* BegShapeEdgeSideMaterial = "begShapeEdgeSideMaterial"; -static const char* BegShapeEdgeType = "begShapeEdgeType"; -static const char* EndShapeEdge = "endShapeEdge"; -static const char* EndShapeEdgeTrimSideType = "endShapeEdgeTrimSideType"; -static const char* EndShapeEdgeTrimSideAngle = "endShapeEdgeTrimSideAngle"; -static const char* EndShapeEdgeSideMaterial = "endShapeEdgeSideMaterial"; -static const char* EndShapeEdgeType = "endShapeEdgeType"; - -static const char* ExtrudedEdge1 = "extrudedEdge1"; -static const char* ExtrudedEdgeTrimSideType1 = "extrudedEdgeTrimSideType1"; -static const char* ExtrudedEdgeTrimSideAngle1 = "extrudedEdgeTrimSideAngle1"; -static const char* ExtrudedEdgeSideMaterial1 = "extrudedEdgeSideMaterial1"; -static const char* ExtrudedEdgeType1 = "extrudedEdgeType1"; -static const char* ExtrudedEdge2 = "extrudedEdge2"; -static const char* ExtrudedEdgeTrimSideType2 = "extrudedEdgeTrimSideType2"; -static const char* ExtrudedEdgeTrimSideAngle2 = "extrudedEdgeTrimSideAngle2"; -static const char* ExtrudedEdgeSideMaterial2 = "extrudedEdgeSideMaterial2"; -static const char* ExtrudedEdgeType2 = "extrudedEdgeType2"; - -static const char* RevolvedEdge1 = "revolvedEdge1"; -static const char* RevolvedEdgeTrimSideType1 = "revolvedEdgeTrimSideType1"; -static const char* RevolvedEdgeTrimSideAngle1 = "revolvedEdgeTrimSideAngle1"; -static const char* RevolvedEdgeSideMaterial1 = "revolvedEdgeSideMaterial1"; -static const char* RevolvedEdgeType1 = "revolvedEdgeType1"; -static const char* RevolvedEdge2 = "revolvedEdge2"; -static const char* RevolvedEdgeTrimSideType2 = "revolvedEdgeTrimSideType2"; -static const char* RevolvedEdgeTrimSideAngle2 = "revolvedEdgeTrimSideAngle2"; -static const char* RevolvedEdgeSideMaterial2 = "revolvedEdgeSideMaterial2"; -static const char* RevolvedEdgeType2 = "revolvedEdgeType2"; - -static const char* RuledEdge1 = "ruledEdge1"; -static const char* RuledEdgeTrimSideType1 = "ruledEdgeTrimSideType1"; -static const char* RuledEdgeTrimSideAngle1 = "ruledEdgeTrimSideAngle1"; -static const char* RuledEdgeSideMaterial1 = "ruledEdgeSideMaterial1"; -static const char* RuledEdgeType1 = "ruledEdgeType1"; -static const char* RuledEdge2 = "ruledEdge2"; -static const char* RuledEdgeTrimSideType2 = "ruledEdgeTrimSideType2"; -static const char* RuledEdgeTrimSideAngle2 = "ruledEdgeTrimSideAngle2"; -static const char* RuledEdgeSideMaterial2 = "ruledEdgeSideMaterial2"; -static const char* RuledEdgeType2 = "ruledEdgeType2"; - -static const char* Thickness = "thickness"; -static const char* Structure = "structure"; -static const char* CompositeName = "compositeName"; -static const char* BuildingMaterialName = "buildingMaterialName"; -// Edge trims -static const char* EdgeAngleType = "edgeAngleType"; -static const char* EdgeAngle = "edgeAngle"; -// Floor Plan and Section - Floor Plan Display -static const char* DisplayOptionName = "displayOptionName"; -static const char* ViewDepthLimitationName = "showProjectionName"; -// Floor Plan and Section - Cut Surfaces -static const char* SectContPen = "sectContPen"; -static const char* SectContLtype = "sectContLtype"; -static const char* CutFillPen = "cutFillPen"; -static const char* CutFillBackgroundPen = "cutFillBackgroundPen"; -// Floor Plan and Section - Outlines -static const char* ContourPen = "contourPen"; -static const char* ContourLineType = "contourLineType"; -static const char* OverheadLinePen = "overheadLinePen"; -static const char* OverheadLinetype = "overheadLinetype"; -// Floor Plan and Section - Cover Fills -static const char* UseFloorFill = "useFloorFill"; -static const char* FloorFillPen = "floorFillPen"; -static const char* FloorFillBGPen = "floorFillBGPen"; -static const char* FloorFillName = "floorFillName"; -static const char* Use3DHatching = "use3DHatching"; -static const char* UseFillLocBaseLine = "useFillLocBaseLine"; -static const char* UseSlantedFill = "useSlantedFill"; -static const char* HatchOrientation = "hatchOrientation"; -static const char* HatchOrientationOrigoX = "hatchOrientationOrigoX"; -static const char* HatchOrientationOrigoY = "hatchOrientationOrigoY"; -static const char* HatchOrientationXAxisX = "hatchOrientationXAxisX"; -static const char* HatchOrientationXAxisY = "hatchOrientationXAxisY"; -static const char* HatchOrientationYAxisX = "hatchOrientationYAxisX"; -static const char* HatchOrientationYAxisY = "hatchOrientationYAxisY"; -// Model -static const char* TopMat = "topMat"; -static const char* SideMat = "sideMat"; -static const char* BotMat = "botMat"; -static const char* MaterialsChained = "materialsChained"; -static const char* TrimmingBodyName = "trimmingBodyName"; -} - - -namespace Skylight -{ -// Geometry and positioning -static const char* VertexID = "vertexID"; -static const char* SkylightFixMode = "skylightFixMode"; -static const char* SkylightAnchor = "skylightAnchor"; -static const char* AnchorPosition = "anchorPosition"; -static const char* AnchorLevel = "anchorLevel"; -static const char* AzimuthAngle = "azimuthAngle"; -static const char* ElevationAngle = "elevationAngle"; -} - - -namespace Room -{ -static const char* Name = "name"; -static const char* Number = "number"; -static const char* BasePoint = "basePoint"; -static const char* Height = "height"; -static const char* Area = "area"; -static const char* Volume = "volume"; -} - - -namespace Model -{ -static const char* Vertices = "vertices"; -static const char* VertexX = "x"; -static const char* VertexY = "y"; -static const char* VertexZ = "z"; -static const char* PointId1 = "v1"; -static const char* PointId2 = "v2"; -static const char* PolygonId1 = "p1"; -static const char* PolygonId2 = "p2"; -static const char* EdgeStatus = "s"; -static const char* HiddenEdgeValueName = "HiddenEdge"; -static const char* SmoothEdgeValueName = "SmoothEdge"; -static const char* VisibleEdgeValueName = "VisibleEdge"; -static const char* Polygons = "polygons"; -static const char* Materials = "materials"; -static const char* PointIds = "pointIds"; -static const char* MaterialName = "name"; -static const char* Transparency = "transparency"; -static const char* AmbientColor = "ambientColor"; -static const char* EmissionColor = "emissionColor"; -static const char* Material = "material"; -static const char* Model = "model"; -static const char* ModelIds = "modelIds"; -static const char* Ids = "ids"; -static const char* Edges = "edges"; -} - - -namespace Level -{ -static const char* TypeName = "level"; -static const char* Index = "index"; -static const char* Name = "name"; -static const char* Elevation = "elevation"; -static const char* Units = "units"; -} - - -namespace Point -{ -static const char* X = "x"; -static const char* Y = "y"; -static const char* Z = "z"; -static const char* Units = "units"; -} - - + + + namespace ApplicationObject { + static const char* ApplicationObjects = "applicationObjects"; + static const char* Status = "Status"; + static const char* StateCreated = "Created"; + static const char* StateSkipped = "Skipped"; + static const char* StateUpdated = "Updated"; + static const char* StateFailed = "Failed"; + static const char* StateRemoved = "Removed"; + static const char* StateUnknown = "Unknown"; + static const char* OriginalId = "OriginalId"; + static const char* CreatedIds = "CreatedIds"; + static const char* Log = "Log"; + } + + namespace ElementBase + { + static const char* Id = "id"; + static const char* ApplicationId = "applicationId"; + static const char* ApplicationIds = "applicationIds"; + static const char* ParentElementId = "parentApplicationId"; + static const char* ElementFilter = "elementFilter"; + static const char* FilterBy = "filterBy"; + static const char* ElementType = "elementType"; + static const char* ElementTypes = "elementTypes"; + static const char* Elements = "elements"; + static const char* SubElements = "subElements"; + static const char* Level = "level"; + static const char* Layer = "layer"; + static const char* Shape = "shape"; + static const char* Shape1 = "shape1"; + static const char* Shape2 = "shape2"; + + static const char* Classifications = "classifications"; + namespace Classification + { + static const char* System = "system"; + static const char* Code = "code"; // id is reserved for Speckle id + static const char* Name = "name"; + } + + static const char* MaterialQuantities = "materialQuantities"; + static const char* ElementProperties = "elementProperties"; + static const char* ComponentProperties = "componentProperties"; + static const char* SendListingParameters = "sendListingParameters"; + static const char* SendProperties = "sendProperties"; + namespace Quantity + { + static const char* Material = "material"; + static const char* Volume = "volume"; + static const char* Area = "area"; + static const char* Units = "units"; + } + namespace ComponentProperty + { + static const char* Name = "name"; + static const char* PropertyGroups = "propertyGroups"; + } + namespace PropertyGroup + { + static const char* Name = "name"; + static const char* PropertList = "propertyList"; + } + namespace Property + { + static const char* Name = "name"; + static const char* Value = "value"; + static const char* Values = "values"; + static const char* Units = "units"; + } + } + + static const char* Elements = "elements"; + static const char* Beams = "beams"; + static const char* Columns = "columns"; + static const char* DirectShapes = "directShapes"; + static const char* Doors = "doors"; + static const char* GridElements = "gridElements"; + static const char* Objects = "objects"; + static const char* MeshModels = "meshModels"; + static const char* Roofs = "roofs"; + static const char* Shells = "shells"; + static const char* Skylights = "skylights"; + static const char* Slabs = "slabs"; + static const char* Walls = "walls"; + static const char* Windows = "windows"; + static const char* Zones = "zones"; + static const char* Openings = "openings"; + + static const char* Models = "models"; + static const char* SubelementModels = "subelementModels"; + + static const char* ShowOnStories = "showOnStories"; + static const char* VisibilityContData = "visibilityCont"; + static const char* VisibilityFillData = "visibilityFill"; + static const char* ShowOnHome = "showOnHome"; + static const char* ShowAllAbove = "showAllAbove"; + static const char* ShowAllBelow = "showAllBelow"; + static const char* ShowRelAbove = "showRelAbove"; + static const char* ShowRelBelow = "showRelBelow"; + static const char* HomeStoryOnlyValueName = "homeStoryOnly"; + static const char* HomeAndOneStoryUpValueName = "homeAndOneStoryUp"; + static const char* HomeAndOneStoryDownValueName = "homeAndOneStoryDown"; + static const char* HomeAndOneStoryUpAndDownValueName = "homeAndOneStoryUpAndDown"; + static const char* OneStoryUpValueName = "oneStoryUp"; + static const char* OneStoryDownValueName = "oneStoryDown"; + static const char* AllStoriesValueName = "allStories"; + static const char* AllRelevantStoriesValueName = "allRelevantStories"; + static const char* CustomStoriesValueName = "custom"; + + static const char* CoverFillTransformationType = "coverFillTransformationType"; + static const char* HatchOrientationType = "coverFillTransformationType"; + static const char* LinkToProjectOriginValueName = "linkToProjectOrigin"; + static const char* LinkToFillOriginValueName = "linkToFillOrigin"; + static const char* CustomDistortionValueName = "customDistortion"; + static const char* ThreeDDistortionValueName = "3DDistortion"; + + + namespace AssemblySegment { + static const char* SegmentName = "Segment #%d"; + static const char* SchemeName = "Scheme #%d"; + static const char* CutName = "Cut #%d"; + + static const char* HoleData = "Holes"; + static const char* SegmentData = "Segments"; + static const char* SchemeData = "Schemes"; + static const char* CutData = "Cuts"; + } + + + namespace PivotPolyEdge { + static const char* EdgeName = "Roof Pivot Poly Edge #%d"; + static const char* EdgeData = "roofPivotPolyEdges"; + } + + + namespace PivotPolyEdgeData { + static const char* NumLevelEdgeData = "nLevelEdgeData"; + } + + + namespace LevelEdge { + static const char* LevelEdgeName = "Roof Level #%d"; + static const char* LevelEdgeData = "roofLevels"; + } + + + namespace RoofSegmentData { + static const char* LevelAngle = "edgeLevelAngle"; + static const char* TopMaterial = "topMaterial"; + static const char* BottomMaterial = "bottomMaterial"; + static const char* CoverFillType = "coverFillType"; + static const char* EavesOverhang = "eavesOverhang"; + static const char* AngleType = "angleType"; + } + + + namespace Wall + { + // Wall geometry + static const char* BaseOffset = "baseOffset"; + static const char* StartPoint = "startPoint"; + static const char* EndPoint = "endPoint"; + static const char* WallComplexity = "wallComplexity"; + static const char* Structure = "structure"; + static const char* GeometryMethod = "geometryMethod"; + static const char* BuildingMaterialName = "buildingMaterialName"; + static const char* CompositeName = "compositeName"; + static const char* ProfileName = "profileName"; + static const char* ArcAngle = "arcAngle"; + static const char* Thickness = "thickness"; + static const char* FirstThickness = "firstThickness"; + static const char* SecondThickness = "secondThickness"; + static const char* OutsideSlantAngle = "outsideSlantAngle"; + static const char* InsideSlantAngle = "insideSlantAngle"; + static const char* Height = "height"; + static const char* PolyCanChange = "polyWalllCornersCanChange"; + // Wall and stories relation + static const char* TopOffset = "topOffset"; + static const char* RelativeTopStoryIndex = "relativeTopStory"; + static const char* ReferenceLineLocation = "referenceLineLocation"; + static const char* ReferenceLineOffset = "referenceLineOffset"; + static const char* OffsetFromOutside = "offsetFromOutside"; + static const char* ReferenceLineStartIndex = "referenceLineStartIndex"; + static const char* ReferenceLineEndIndex = "referenceLineEndIndex"; + static const char* Flipped = "flipped"; + // Floor Plan and Section - Floor Plan Display + static const char* DisplayOptionName = "displayOptionName"; + static const char* ViewDepthLimitationName = "showProjectionName"; + // Floor Plan and Section - Cut Surfaces + static const char* CutLinePenIndex = "cutLinePen"; + static const char* CutLinetypeName = "cutLinetype"; + static const char* OverrideCutFillPenIndex = "overrideCutFillPen"; + static const char* OverrideCutFillBackgroundPenIndex = "overrideCutFillBackgroundPen"; + // Floor Plan and Section - Outlines + static const char* UncutLinePenIndex = "uncutLinePen"; + static const char* UncutLinetypeName = "uncutLinetype"; + static const char* OverheadLinePenIndex = "overheadLinePen"; + static const char* OverheadLinetypeName = "overheadLinetype"; + // Model - Override Surfaces + static const char* ReferenceMaterialName = "referenceMaterialName"; + static const char* ReferenceMaterialStartIndex = "referenceMaterialStartIndex"; + static const char* ReferenceMaterialEndIndex = "referenceMaterialEndIndex"; + static const char* OppositeMaterialName = "oppositeMaterialName"; + static const char* OppositeMaterialStartIndex = "oppositeMaterialStartIndex"; + static const char* OppositeMaterialEndIndex = "oppositeMaterialEndIndex"; + static const char* SideMaterialName = "sideMaterialName"; + static const char* MaterialsChained = "materialsChained"; + static const char* InheritEndSurface = "inheritEndSurface"; + static const char* AlignTexture = "alignTexture"; + static const char* Sequence = "sequence"; + // Model - Log Details (log height, start with half log, surface of horizontal edges, log shape) + static const char* LogHeight = "logHeight"; + static const char* StartWithHalfLog = "startWithHalfLog"; + static const char* SurfaceOfHorizontalEdges = "surfaceOfHorizontalEdges"; + static const char* LogShape = "logShape"; + // Model - Defines the relation of wall to zones (Zone Boundary, Reduce Zone Area Only, No Effect on Zones) + static const char* WallRelationToZoneName = "wallRelationToZoneName"; + // Does it have any embedded object? + static const char* HasDoor = "hasDoor"; + static const char* HasWindow = "hasWindow"; + } + + + namespace OpeningBase { + static const char* Door = "Door #%d"; + // + static const char* guid = "guid"; + // OpeningBase + static const char* width = "width"; + static const char* height = "height"; + static const char* subFloorThickness = "subFloorThickness"; + static const char* reflected = "reflected"; + static const char* oSide = "oSide"; + static const char* refSide = "refSide"; + static const char* VerticalLinkTypeName = "verticalLinkTypeName"; + static const char* VerticalLinkStoryIndex = "verticalLinkStoryIndex"; + static const char* WallCutUsing = "wallCutUsing"; + + static const char* PenIndex = "pen"; + static const char* LineTypeName = "lineTypeName"; + static const char* SectFillName = "sectFill"; + static const char* SectFillPenIndex = "sectFillPen"; + static const char* SectBackgroundPenIndex = "sectBackgroundPen"; + static const char* SectContPenIndex = "sectContPen"; + static const char* CutLineTypeName = "cutLineType"; + static const char* AboveViewLineTypeName = "aboveViewLineType"; + static const char* AboveViewLinePenIndex = "aboveViewLinePen"; + static const char* BelowViewLinePenIndex = "belowViewLinePen"; + static const char* BelowViewLineTypeName = "belowViewLineType"; + static const char* UseObjectPens = "useObjectPens"; + static const char* UseObjLinetypes = "useObjLinetypes"; + static const char* UseObjMaterials = "useObjMaterials"; + static const char* UseObjSectAttrs = "useObjSectAttrs"; + static const char* BuildingMaterialName = "buildingMaterial"; + static const char* LibraryPart = "libraryPart"; + static const char* DisplayOptionName = "displayOptionName"; + + static const char* revealDepthFromSide = "revealDepthFromSide"; + static const char* jambDepthHead = "jambDepthHead"; + static const char* jambDepth = "jambDepth"; + static const char* jambDepth2 = "jambDepth2"; + static const char* objLoc = "objLoc"; + static const char* lower = "lower"; + static const char* directionType = "directionType"; + + static const char* startPoint = "startPoint"; + static const char* dirVector = "dirVector"; + } + + + namespace Beam + { + // Positioning + static const char* begC = "begC"; + static const char* endC = "endC"; + static const char* level = "level"; + static const char* isSlanted = "isSlanted"; + static const char* slantAngle = "slantAngle"; + static const char* beamShape = "beamShape"; + static const char* sequence = "sequence"; + static const char* curveAngle = "curveAngle"; + static const char* verticalCurveHeight = "verticalCurveHeight"; + static const char* isFlipped = "isFlipped"; + // End Cuts + static const char* nCuts = "nCuts"; + // Rreference Axis + static const char* anchorPoint = "anchorPoint"; + static const char* offset = "offset"; + static const char* profileAngle = "profileAngle"; + // Segment + static const char* segments = "segments"; + static const char* nSegments = "nSegments"; + static const char* nProfiles = "nProfiles"; + + + namespace BeamSegment + { + // Segment override materials + static const char* LeftMaterial = "leftMaterial"; + static const char* TopMaterial = "topMaterial"; + static const char* RightMaterial = "rightMaterial"; + static const char* BottomMaterial = "bottomMaterial"; + static const char* EndsMaterial = "endsMaterial"; + // Segment - The overridden materials are chained + static const char* MaterialsChained = "materialChained"; + // Segment + static const char* segmentData = "assemblySegmentData"; + } + + + // Scheme + static const char* nSchemes = "nSchemes"; + // Hole + static const char* HoleName = "Hole #%d"; + static const char* holeType = "holeType"; + static const char* holeContourOn = "holeContourOn"; + static const char* holeId = "holeId"; + static const char* centerx = "centerx"; + static const char* centerz = "centerz"; + static const char* width = "width"; + static const char* height = "height"; + // Floor Plan and Section - Floor Plan Display + static const char* DisplayOptionName = "displayOptionName"; + static const char* UncutProjectionModeName = "uncutProjectionMode"; + static const char* OverheadProjectionModeName = "overheadProjectionMode"; + static const char* ViewDepthLimitationName = "showProjectionName"; + // Floor Plan and Section - Cut Surfaces + static const char* cutContourLinePen = "cutContourLinePen"; + static const char* CutContourLinetypeName = "cutContourLineType"; + static const char* OverrideCutFillPenIndex = "overrideCutFillPen"; + static const char* OverrideCutFillBackgroundPenIndex = "overrideCutFillBackgroundPen"; + // Floor Plan and Section - Outlines + static const char* ShowOutlineIndex = "showOutline"; + static const char* UncutLinePenIndex = "uncutLinePen"; + static const char* UncutLinetypeName = "uncutLinetype"; + static const char* OverheadLinePenIndex = "overheadLinePen"; + static const char* OverheadLinetypeName = "overheadLinetype"; + static const char* HiddenLinePenIndex = "hiddenLinePen"; + static const char* HiddenLinetypeName = "hiddenLinetype"; + // Floor Plan and Section - Symbol + static const char* ShowReferenceAxisIndex = "showReferenceAxis"; + static const char* refPen = "referencePen"; + static const char* refLtype = "referenceLinetype"; + // Floor Plan and Section - Cover Fills + static const char* useCoverFill = "useCoverFill"; + static const char* useCoverFillFromSurface = "useCoverFillFromSurface"; + static const char* coverFillForegroundPen = "coverFillForegroundPen"; + static const char* coverFillBackgroundPen = "coverFillBackgroundPen"; + static const char* coverFillType = "coverFillType"; + static const char* CoverFillTransformationOrigoX = "coverFillTransformationOrigoX"; + static const char* CoverFillTransformationOrigoY = "coverFillTransformationOrigoY"; + static const char* CoverFillTransformationXAxisX = "coverFillTransformationXAxisX"; + static const char* CoverFillTransformationXAxisY = "coverFillTransformationXAxisY"; + static const char* CoverFillTransformationYAxisX = "coverFillTransformationYAxisX"; + static const char* CoverFillTransformationYAxisY = "coverFillTransformationYAxisY"; + } + + + namespace Column + { + // Positioning - geometry + static const char* origoPos = "origoPos"; + static const char* height = "height"; + // Positioning - story relation + static const char* bottomOffset = "bottomOffset"; + static const char* topOffset = "topOffset"; + static const char* relativeTopStory = "relativeTopStory"; + // Positioning - slanted column + static const char* isSlanted = "isSlanted"; + static const char* slantAngle = "slantAngle"; + static const char* slantDirectionAngle = "slantDirectionAngle"; + static const char* isFlipped = "isFlipped"; + // Positioning - wrapping + static const char* Wrapping = "wrapping"; + // Positioning - Defines the relation of column to zones (Zone Boundary, Reduce Zone Area Only, No Effect on Zones) + static const char* ColumnRelationToZoneName = "columnRelationToZoneName"; + // End Cuts + static const char* nCuts = "nCuts"; + // Reference Axis + static const char* coreAnchor = "coreAnchor"; + static const char* axisRotationAngle = "axisRotationAngle"; + // Segment + static const char* segments = "segments"; + static const char* nSegments = "nSegments"; + static const char* nProfiles = "nProfiles"; + + + namespace ColumnSegment + { + // Segment - Veneer attributes + static const char* VenType = "veneerType"; + static const char* VenBuildingMaterial = "veneerBuildingMaterial"; + static const char* VenThick = "veneerThick"; + // Segment - The extrusion overridden material name + static const char* ExtrusionSurfaceMaterial = "extrusionSurfaceMaterial"; + // Segment - The ends overridden material name + static const char* EndsSurfaceMaterial = "endsSurfaceMaterial"; + // Segment - The overridden materials are chained + static const char* MaterialsChained = "materialChained"; + // Segment + static const char* segmentData = "assemblySegmentData"; + } + + + // Scheme + static const char* nSchemes = "nSchemes"; + // Floor Plan and Section - Floor Plan Display + static const char* DisplayOptionName = "displayOptionName"; + static const char* ViewDepthLimitationName = "showProjectionName"; + // Floor Plan and Section - Cut Surfaces + static const char* corePen = "corePen"; + static const char* CoreLinetypeName = "contLtype"; + static const char* VeneerPenIndex = "venLinePen"; + static const char* VeneerLinetypeName = "venLineType"; + static const char* OverrideCutFillPenIndex = "overrideCutFillPen"; + static const char* OverrideCutFillBackgroundPenIndex = "overrideCutFillBackgroundPen"; + // Floor Plan and Section - Outlines + static const char* UncutLinePenIndex = "uncutLinePen"; + static const char* UncutLinetypeName = "uncutLinetype"; + static const char* OverheadLinePenIndex = "overheadLinePen"; + static const char* OverheadLinetypeName = "overheadLinetype"; + static const char* HiddenLinePenIndex = "hiddenLinePen"; + static const char* HiddenLinetypeName = "hiddenLinetype"; + // Floor Plan and Section - Floor Plan Symbol + static const char* CoreSymbolTypeName = "coreSymbolTypeName"; + static const char* coreSymbolPar1 = "coreSymbolPar1"; + static const char* coreSymbolPar2 = "coreSymbolPar2"; + static const char* CoreSymbolPenIndex = "coreSymbolPen"; + // Floor Plan and Section - Cover Fills + static const char* useCoverFill = "useCoverFill"; + static const char* useCoverFillFromSurface = "useCoverFillFromSurface"; + static const char* coverFillForegroundPen = "coverFillForegroundPen"; + static const char* coverFillBackgroundPen = "coverFillBackgroundPen"; + static const char* coverFillType = "coverFillType"; + static const char* CoverFillTransformationOrigoX = "coverFillTransformationOrigoX"; + static const char* CoverFillTransformationOrigoY = "coverFillTransformationOrigoY"; + static const char* CoverFillTransformationXAxisX = "coverFillTransformationXAxisX"; + static const char* CoverFillTransformationXAxisY = "coverFillTransformationXAxisY"; + static const char* CoverFillTransformationYAxisX = "coverFillTransformationYAxisX"; + static const char* CoverFillTransformationYAxisY = "coverFillTransformationYAxisY"; + } + + + namespace AssemblySegmentData { + static const char* circleBased = "circleBased"; + static const char* modelElemStructureType = "modelElemStructureType"; + static const char* nominalHeight = "nominalHeight"; + static const char* nominalWidth = "nominalWidth"; + static const char* isWidthAndHeightLinked = "isWidthAndHeightLinked"; + static const char* isHomogeneous = "isHomogeneous"; + static const char* endWidth = "endWidth"; + static const char* endHeight = "endHeight"; + static const char* isEndWidthAndHeightLinked = "isEndWidthAndHeightLinked"; + static const char* buildingMaterial = "buildingMaterial"; + static const char* profileAttrName = "profileAttrName"; + } + + + namespace AssemblySegmentSchemeData { + static const char* lengthType = "lengthType"; + static const char* fixedLength = "fixedLength"; + static const char* lengthProportion = "lengthProportion"; + } + + + namespace AssemblySegmentCutData { + static const char* cutType = "cutType"; + static const char* customAngle = "customAngle"; + } + + + namespace Object + { + // Main + static const char* pos = "pos"; + static const char* transform = "transform"; + } + + + namespace Opening + { + // Floor Plan Parameters + static const char* FloorPlanDisplayMode = "floorPlanDisplayMode"; + static const char* ConnectionMode = "connectionMode"; + + // Cut Surfaces Parameters + static const char* CutSurfacesUseLineOfCutElements = "cutsurfacesUseLineOfCutElements"; + static const char* CutSurfacesLinePenIndex = "cutsurfacesLinePenIndex"; + static const char* CutSurfacesLineIndex = "cutsurfacesLineIndex"; + + // Outlines Parameters + static const char* OutlinesStyle = "outlinesStyle"; + static const char* OutlinesUseLineOfCutElements = "outlinesUseLineOfCutElements"; // => Cut Surfaces Parameters-ben is megtallhat + static const char* OutlinesUncutLineIndex = "outlinesUncutLineIndex"; + static const char* OutlinesOverheadLineIndex = "outlinesOverheadLineIndex"; + static const char* OutlinesUncutLinePenIndex = "outlinesUncutLinePenIndex"; + static const char* OutlinesOverheadLinePenIndex = "outlinesOverheadLinePenIndex"; + + // Opening Cover Fills Parameters + static const char* UseCoverFills = "useCoverFills"; + static const char* UseFillsOfCutElements = "useFillsOfCutElements"; + static const char* CoverFillIndex = "coverFillIndex"; + static const char* CoverFillPenIndex = "coverFillPenIndex"; + static const char* CoverFillBackgroundPenIndex = "coverFillBackgroundPenIndex"; + static const char* CoverFillOrientation = "coverFillOrientation"; + + // Cover Fill Transformation Parameters + static const char* CoverFillTransformationOrigoX = "coverFillTransformationOrigoX"; + static const char* CoverFillTransformationOrigoY = "coverFillTransformationOrigoY"; + static const char* CoverFillTransformationOrigoZ = "coverFillTransformationOrigoZ"; + static const char* CoverFillTransformationXAxisX = "coverFillTransformationXAxisX"; + static const char* CoverFillTransformationXAxisY = "coverFillTransformationXAxisY"; + static const char* CoverFillTransformationXAxisZ = "coverFillTransformationXAxisZ"; + static const char* CoverFillTransformationYAxisX = "coverFillTransformationYAxisX"; + static const char* CoverFillTransformationYAxisY = "coverFillTransformationYAxisY"; + static const char* CoverFillTransformationYAxisZ = "coverFillTransformationYAxisZ"; + + // Reference Axis Parameters + static const char* ShowReferenceAxis = "showReferenceAxis"; + static const char* ReferenceAxisPenIndex = "referenceAxisPenIndex"; + static const char* ReferenceAxisLineTypeIndex = "referenceAxisLineTypeIndex"; + static const char* ReferenceAxisOverhang = "referenceAxisOverhang"; + + // Extrusion Geometry Parameters + static const char* ExtrusionGeometryBasePoint = "extrusionGeometryBasePoint"; + static const char* ExtrusionGeometryXAxis = "extrusionGeometryXAxis"; + static const char* ExtrusionGeometryYAxis = "extrusionGeometryYAxis"; + static const char* ExtrusionGeometryZAxis = "extrusionGeometryZAxis"; + static const char* BasePolygonType = "basePolygonType"; + static const char* Width = "width"; + static const char* Height = "height"; + static const char* Constraint = "constraint"; + static const char* Anchor = "anchor"; + static const char* AnchorIndex = "anchorIndex"; + static const char* AnchorAltitude = "anchorAltitude"; + static const char* LimitType = "limitType"; + static const char* ExtrusionStartOffSet = "extrusionStartOffSet"; + static const char* FiniteBodyLength = "finiteBodyLength"; + static const char* LinkedStatus = "linkedStatus"; + static const char* NCoords = "nCoords"; + static const char* NSubPolys = "nSubPolys"; + static const char* NArcs = "nArcs"; + } + + + namespace GridElement + { + // Main + static const char* begin = "begin"; + static const char* end = "end"; + static const char* angle = "angle"; + static const char* markerText = "markerText"; + static const char* isArc = "isArc"; + static const char* arcAngle = "arcAngle"; + } + + + namespace Slab + { + // Geometry and positioning + static const char* Thickness = "thickness"; + static const char* Structure = "structure"; + static const char* CompositeName = "compositeName"; + static const char* BuildingMaterialName = "buildingMaterialName"; + static const char* ReferencePlaneLocation = "referencePlaneLocation"; + // Edge trims + static const char* EdgeAngleType = "edgeAngleType"; + static const char* EdgeAngle = "edgeAngle"; + // Floor Plan and Section - Cut Surfaces + static const char* sectContPen = "sectContPen"; + static const char* sectContLtype = "sectContLtype"; + static const char* cutFillPen = "cutFillPen"; + static const char* cutFillBackgroundPen = "cutFillBackgroundPen"; + // Floor Plan and Section - Outlines + static const char* contourPen = "contourPen"; + static const char* contourLineType = "contourLineType"; + static const char* hiddenContourLinePen = "hiddenContourLinePen"; + static const char* hiddenContourLineType = "hiddenContourLineType"; + // Floor Plan and Section - Cover Fills + static const char* useFloorFill = "useFloorFill"; + static const char* floorFillPen = "floorFillPen"; + static const char* floorFillBGPen = "floorFillBGPen"; + static const char* floorFillName = "floorFillName"; + static const char* use3DHatching = "use3DHatching"; + static const char* hatchOrientation = "hatchOrientation"; + static const char* hatchOrientationOrigoX = "hatchOrientationOrigoX"; + static const char* hatchOrientationOrigoY = "hatchOrientationOrigoY"; + static const char* hatchOrientationXAxisX = "hatchOrientationXAxisX"; + static const char* hatchOrientationXAxisY = "hatchOrientationXAxisY"; + static const char* hatchOrientationYAxisX = "hatchOrientationYAxisX"; + static const char* hatchOrientationYAxisY = "hatchOrientationYAxisY"; + // Model + static const char* topMat = "topMat"; + static const char* sideMat = "sideMat"; + static const char* botMat = "botMat"; + static const char* materialsChained = "materialsChained"; + } + + + namespace Roof + { + // Geometry and positioning + static const char* RoofClassName = "roofClassName"; + static const char* PlaneRoofAngle = "planeRoofAngle"; + static const char* BaseLine = "baseLine"; + static const char* BegC = "begC"; + static const char* EndC = "endC"; + static const char* PosSign = "posSign"; + static const char* PivotPolygon = "pivotPolygon"; + + // Level + static const char* levels = "levels"; + static const char* LevelNum = "levelNum"; + + namespace RoofLevel { + static const char* LevelName = "Level #%d"; + static const char* levelData = "levels"; + } + + namespace LevelData { + static const char* LevelHeight = "levelHeight"; + static const char* LevelAngle = "levelAngle"; + } + + static const char* Thickness = "thickness"; + static const char* Structure = "structure"; + static const char* CompositeName = "compositeName"; + static const char* BuildingMaterialName = "buildingMaterialName"; + // Edge trims + static const char* EdgeAngleType = "edgeAngleType"; + static const char* EdgeAngle = "edgeAngle"; + // Floor Plan and Section - Floor Plan Display + static const char* DisplayOptionName = "displayOptionName"; + static const char* ViewDepthLimitationName = "showProjectionName"; + // Floor Plan and Section - Cut Surfaces + static const char* SectContPen = "sectContPen"; + static const char* SectContLtype = "sectContLtype"; + static const char* CutFillPen = "cutFillPen"; + static const char* CutFillBackgroundPen = "cutFillBackgroundPen"; + // Floor Plan and Section - Outlines + static const char* ContourPen = "contourPen"; + static const char* ContourLineType = "contourLineType"; + static const char* OverheadLinePen = "overheadLinePen"; + static const char* OverheadLinetype = "overheadLinetype"; + // Floor Plan and Section - Cover Fills + static const char* UseFloorFill = "useFloorFill"; + static const char* FloorFillPen = "floorFillPen"; + static const char* FloorFillBGPen = "floorFillBGPen"; + static const char* FloorFillName = "floorFillName"; + static const char* Use3DHatching = "use3DHatching"; + static const char* UseFillLocBaseLine = "useFillLocBaseLine"; + static const char* UseSlantedFill = "useSlantedFill"; + static const char* HatchOrientation = "hatchOrientation"; + static const char* HatchOrientationOrigoX = "hatchOrientationOrigoX"; + static const char* HatchOrientationOrigoY = "hatchOrientationOrigoY"; + static const char* HatchOrientationXAxisX = "hatchOrientationXAxisX"; + static const char* HatchOrientationXAxisY = "hatchOrientationXAxisY"; + static const char* HatchOrientationYAxisX = "hatchOrientationYAxisX"; + static const char* HatchOrientationYAxisY = "hatchOrientationYAxisY"; + // Model + static const char* TopMat = "topMat"; + static const char* SideMat = "sideMat"; + static const char* BotMat = "botMat"; + static const char* MaterialsChained = "materialsChained"; + static const char* TrimmingBodyName = "trimmingBodyName"; + } + + + namespace Shell + { + // Geometry and positioning + static const char* ShellClassName = "shellClassName"; + + static const char* BasePlane = "basePlane"; + + static const char* Flipped = "flipped"; + static const char* HasContour = "hasContour"; + static const char* NumHoles = "numHoles"; + static const char* ShellContourName = "shellContour #%d"; + static const char* ShellContourData = "shellContours"; + static const char* ShellContourPlane = "shellContourPlane"; + static const char* ShellContourPoly = "shellContourPoly"; + static const char* ShellContourHeight = "shellContourHeight"; + static const char* ShellContourID = "shellContourID"; + static const char* ShellContourSideTypeName = "sideTypeName"; + static const char* ShellContourSideAngle = "sideAngle"; + static const char* ShellContourEdgeTypeName = "edgeTypeName"; + static const char* ShellContourEdgeSideMaterial = "edgeSideMaterial"; + static const char* ShellContourEdgeName = "shellContourEdge #%d"; + static const char* ShellContourEdgeData = "shellContourEdges"; + static const char* DefaultEdgeType = "defaultEdgeType"; + + static const char* SlantAngle = "slantAngle"; + static const char* RevolutionAngle = "revolutionAngle"; + static const char* DistortionAngle = "distortionAngle"; + static const char* SegmentedSurfaces = "segmentedSurfaces"; + static const char* ShapePlaneTilt = "shapePlaneTilt"; + static const char* BegPlaneTilt = "begPlaneTilt"; + static const char* EndPlaneTilt = "endPlaneTilt"; + static const char* AxisBase = "axisBase"; + static const char* Plane1 = "plane1"; + static const char* Plane2 = "plane2"; + static const char* BegC = "begC"; + static const char* BegAngle = "begAngle"; + static const char* ExtrusionVector = "extrusionVector"; + static const char* ShapeDirection = "shapeDirection"; + static const char* DistortionVector = "distortionVector"; + static const char* MorphingRuleName = "morphingRuleName"; + + static const char* BegShapeEdge = "begShapeEdge"; + static const char* BegShapeEdgeTrimSideType = "begShapeEdgeTrimSideType"; + static const char* BegShapeEdgeTrimSideAngle = "begShapeEdgeTrimSideAngle"; + static const char* BegShapeEdgeSideMaterial = "begShapeEdgeSideMaterial"; + static const char* BegShapeEdgeType = "begShapeEdgeType"; + static const char* EndShapeEdge = "endShapeEdge"; + static const char* EndShapeEdgeTrimSideType = "endShapeEdgeTrimSideType"; + static const char* EndShapeEdgeTrimSideAngle = "endShapeEdgeTrimSideAngle"; + static const char* EndShapeEdgeSideMaterial = "endShapeEdgeSideMaterial"; + static const char* EndShapeEdgeType = "endShapeEdgeType"; + + static const char* ExtrudedEdge1 = "extrudedEdge1"; + static const char* ExtrudedEdgeTrimSideType1 = "extrudedEdgeTrimSideType1"; + static const char* ExtrudedEdgeTrimSideAngle1 = "extrudedEdgeTrimSideAngle1"; + static const char* ExtrudedEdgeSideMaterial1 = "extrudedEdgeSideMaterial1"; + static const char* ExtrudedEdgeType1 = "extrudedEdgeType1"; + static const char* ExtrudedEdge2 = "extrudedEdge2"; + static const char* ExtrudedEdgeTrimSideType2 = "extrudedEdgeTrimSideType2"; + static const char* ExtrudedEdgeTrimSideAngle2 = "extrudedEdgeTrimSideAngle2"; + static const char* ExtrudedEdgeSideMaterial2 = "extrudedEdgeSideMaterial2"; + static const char* ExtrudedEdgeType2 = "extrudedEdgeType2"; + + static const char* RevolvedEdge1 = "revolvedEdge1"; + static const char* RevolvedEdgeTrimSideType1 = "revolvedEdgeTrimSideType1"; + static const char* RevolvedEdgeTrimSideAngle1 = "revolvedEdgeTrimSideAngle1"; + static const char* RevolvedEdgeSideMaterial1 = "revolvedEdgeSideMaterial1"; + static const char* RevolvedEdgeType1 = "revolvedEdgeType1"; + static const char* RevolvedEdge2 = "revolvedEdge2"; + static const char* RevolvedEdgeTrimSideType2 = "revolvedEdgeTrimSideType2"; + static const char* RevolvedEdgeTrimSideAngle2 = "revolvedEdgeTrimSideAngle2"; + static const char* RevolvedEdgeSideMaterial2 = "revolvedEdgeSideMaterial2"; + static const char* RevolvedEdgeType2 = "revolvedEdgeType2"; + + static const char* RuledEdge1 = "ruledEdge1"; + static const char* RuledEdgeTrimSideType1 = "ruledEdgeTrimSideType1"; + static const char* RuledEdgeTrimSideAngle1 = "ruledEdgeTrimSideAngle1"; + static const char* RuledEdgeSideMaterial1 = "ruledEdgeSideMaterial1"; + static const char* RuledEdgeType1 = "ruledEdgeType1"; + static const char* RuledEdge2 = "ruledEdge2"; + static const char* RuledEdgeTrimSideType2 = "ruledEdgeTrimSideType2"; + static const char* RuledEdgeTrimSideAngle2 = "ruledEdgeTrimSideAngle2"; + static const char* RuledEdgeSideMaterial2 = "ruledEdgeSideMaterial2"; + static const char* RuledEdgeType2 = "ruledEdgeType2"; + + static const char* Thickness = "thickness"; + static const char* Structure = "structure"; + static const char* CompositeName = "compositeName"; + static const char* BuildingMaterialName = "buildingMaterialName"; + // Edge trims + static const char* EdgeAngleType = "edgeAngleType"; + static const char* EdgeAngle = "edgeAngle"; + // Floor Plan and Section - Floor Plan Display + static const char* DisplayOptionName = "displayOptionName"; + static const char* ViewDepthLimitationName = "showProjectionName"; + // Floor Plan and Section - Cut Surfaces + static const char* SectContPen = "sectContPen"; + static const char* SectContLtype = "sectContLtype"; + static const char* CutFillPen = "cutFillPen"; + static const char* CutFillBackgroundPen = "cutFillBackgroundPen"; + // Floor Plan and Section - Outlines + static const char* ContourPen = "contourPen"; + static const char* ContourLineType = "contourLineType"; + static const char* OverheadLinePen = "overheadLinePen"; + static const char* OverheadLinetype = "overheadLinetype"; + // Floor Plan and Section - Cover Fills + static const char* UseFloorFill = "useFloorFill"; + static const char* FloorFillPen = "floorFillPen"; + static const char* FloorFillBGPen = "floorFillBGPen"; + static const char* FloorFillName = "floorFillName"; + static const char* Use3DHatching = "use3DHatching"; + static const char* UseFillLocBaseLine = "useFillLocBaseLine"; + static const char* UseSlantedFill = "useSlantedFill"; + static const char* HatchOrientation = "hatchOrientation"; + static const char* HatchOrientationOrigoX = "hatchOrientationOrigoX"; + static const char* HatchOrientationOrigoY = "hatchOrientationOrigoY"; + static const char* HatchOrientationXAxisX = "hatchOrientationXAxisX"; + static const char* HatchOrientationXAxisY = "hatchOrientationXAxisY"; + static const char* HatchOrientationYAxisX = "hatchOrientationYAxisX"; + static const char* HatchOrientationYAxisY = "hatchOrientationYAxisY"; + // Model + static const char* TopMat = "topMat"; + static const char* SideMat = "sideMat"; + static const char* BotMat = "botMat"; + static const char* MaterialsChained = "materialsChained"; + static const char* TrimmingBodyName = "trimmingBodyName"; + } + + + namespace Skylight + { + // Geometry and positioning + static const char* VertexID = "vertexID"; + static const char* SkylightFixMode = "skylightFixMode"; + static const char* SkylightAnchor = "skylightAnchor"; + static const char* AnchorPosition = "anchorPosition"; + static const char* AnchorLevel = "anchorLevel"; + static const char* AzimuthAngle = "azimuthAngle"; + static const char* ElevationAngle = "elevationAngle"; + } + + + namespace Room + { + static const char* Name = "name"; + static const char* Number = "number"; + static const char* BasePoint = "basePoint"; + static const char* Height = "height"; + static const char* Area = "area"; + static const char* Volume = "volume"; + } + + + namespace Model + { + static const char* Vertices = "vertices"; + static const char* VertexX = "x"; + static const char* VertexY = "y"; + static const char* VertexZ = "z"; + static const char* PointId1 = "v1"; + static const char* PointId2 = "v2"; + static const char* PolygonId1 = "p1"; + static const char* PolygonId2 = "p2"; + static const char* EdgeStatus = "s"; + static const char* HiddenEdgeValueName = "HiddenEdge"; + static const char* SmoothEdgeValueName = "SmoothEdge"; + static const char* VisibleEdgeValueName = "VisibleEdge"; + static const char* Polygons = "polygons"; + static const char* Materials = "materials"; + static const char* PointIds = "pointIds"; + static const char* MaterialName = "name"; + static const char* Transparency = "transparency"; + static const char* AmbientColor = "ambientColor"; + static const char* EmissionColor = "emissionColor"; + static const char* Material = "material"; + static const char* Model = "model"; + static const char* ModelIds = "modelIds"; + static const char* Ids = "ids"; + static const char* Edges = "edges"; + } + + + namespace Level + { + static const char* TypeName = "level"; + static const char* Index = "index"; + static const char* Name = "name"; + static const char* Elevation = "elevation"; + static const char* Units = "units"; + } + + + namespace Point + { + static const char* X = "x"; + static const char* Y = "y"; + static const char* Z = "z"; + static const char* Units = "units"; + } + + namespace Material + { + static const char* Name = "name"; + } + } diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/PropertyExportManager.cpp b/ConnectorArchicad/AddOn/Sources/AddOn/PropertyExportManager.cpp index 5849c1fc82..68a320db04 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/PropertyExportManager.cpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/PropertyExportManager.cpp @@ -2,7 +2,6 @@ #include "Utility.hpp" #include "MD5Channel.hpp" - PropertyExportManager* PropertyExportManager::instance = nullptr; PropertyExportManager* PropertyExportManager::GetInstance () @@ -286,6 +285,16 @@ GS::UInt64 GenerateFingerPrint (const API_ElemType& elementType, const bool& sen } +/*! + Get property definitions for a specified element + @param element The target element to retrieve the property definitions for + @param sendProperties True to export the Archicad properties attached to the element + @param sendListingParameters True to export calculated listing parameters from the element, e.g. top/bottom surface area etc + @param systemItemPairs Array pairing a classification system ID against a classification item ID (attached to the target element) + @param elementDefinitions The element property definitions (retrieved in this function) + @param componentsDefinitions The component property definitions (paired with the component ID, retrieved in this function) + @return NoError if the definitions were retrieved without errors + */ GSErrCode PropertyExportManager::GetElementDefinitions (const API_Element& element, const bool& sendProperties, const bool& sendListingParameters, const GS::Array>& systemItemPairs, GS::Array& elementDefinitions, GS::Array>>& componentsDefinitions) { GSErrCode err = NoError; @@ -296,13 +305,15 @@ GSErrCode PropertyExportManager::GetElementDefinitions (const API_Element& eleme // element-level properties { + //Create a hash value for target element and prefs GS::UInt64 fingerPrint = GenerateFingerPrint (elementType, sendProperties, sendListingParameters, systemItemPairs); + //If we've already encountered this combo, use the property definitions we already found (will always be the same - saves a lot of time) if (cache.ContainsKey (fingerPrint)) { elementDefinitions = cache.Get (fingerPrint).first; elementUserDefinedDefinitions = cache.Get (fingerPrint).second; - } - else { + } else { if (sendProperties) { + //Collect user-defined property definitions for the target element when the user requests them err = ACAPI_Element_GetPropertyDefinitions (element.header.guid, API_PropertyDefinitionFilter_UserDefined, elementUserDefinedDefinitions); if (err != NoError) return err; @@ -310,10 +321,11 @@ GSErrCode PropertyExportManager::GetElementDefinitions (const API_Element& eleme GS::Array elementUserLevelBuiltInDefinitions; if (sendListingParameters) { + //Collect built-in property definitions for the target element when the user requests them err = ACAPI_Element_GetPropertyDefinitions (element.header.guid, API_PropertyDefinitionFilter_UserLevelBuiltIn, elementUserLevelBuiltInDefinitions); if (err != NoError) return err; - + //The list of definitions can include many things we don't want - filter it to definitions we're really interested in err = FilterDefinitionsByDefinitionIds (elementUserLevelBuiltInDefinitions, propertyGroupFilter.elementPropertiesFilter); if (err != NoError) return err; @@ -321,7 +333,7 @@ GSErrCode PropertyExportManager::GetElementDefinitions (const API_Element& eleme elementDefinitions = elementUserDefinedDefinitions; elementDefinitions.Append (elementUserLevelBuiltInDefinitions); - + //Add the definitions to the cache to save looking them up again for the same target specs cache.Add (fingerPrint, GS::Pair, GS::Array> (elementDefinitions, elementUserDefinedDefinitions)); } } diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/PropertyExportManager.hpp b/ConnectorArchicad/AddOn/Sources/AddOn/PropertyExportManager.hpp index 9710da6832..474df8d9bf 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/PropertyExportManager.hpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/PropertyExportManager.hpp @@ -19,6 +19,7 @@ class PropertyExportManager { PropertyGroupFilter propertyGroupFilter; GS::HashSet complexElementsToSkipFromComponentListing; + ///Cache of property definitions keyed by a hash of the target specifications (e.g. element type) - saves looking these up repeatedly GS::HashTable, GS::Array>> cache; protected: diff --git a/ConnectorArchicad/ConnectorArchicad/Converters/Converters/Utils.cs b/ConnectorArchicad/ConnectorArchicad/Converters/Converters/Utils.cs index eb0e7e1477..4bb483f880 100644 --- a/ConnectorArchicad/ConnectorArchicad/Converters/Converters/Utils.cs +++ b/ConnectorArchicad/ConnectorArchicad/Converters/Converters/Utils.cs @@ -5,6 +5,7 @@ using Objects; using Objects.BuiltElements.Archicad; using Objects.Geometry; +using Objects.Other; using Speckle.Core.Kits; using Speckle.Core.Logging; @@ -12,6 +13,8 @@ namespace Archicad.Converters; public static class Utils { + private const string MATERIAL_QUANTITIES_TAG = "materialQuantities"; + public static Point VertexToPoint(MeshModel.Vertex vertex) { return new Point @@ -159,7 +162,13 @@ public static ElementShape PolycurvesToElementShape(ICurve outline, List return shape; } + /// + /// Convert incoming JSON (from Archicad) to an equivalent (specified) object type + /// + /// The incoming JSON (handled as a dynamic object) + /// An object of the specified type (T) public static T ConvertToSpeckleDTOs(dynamic jObject) + where T : Speckle.Core.Models.Base { Objects.BuiltElements.Archicad.ArchicadLevel level = null; if (jObject.level != null) @@ -184,6 +193,14 @@ public static T ConvertToSpeckleDTOs(dynamic jObject) jObject.Remove("componentProperties"); } + //Seek optional material quantities attached to the element (volume/area associated with a building material) + List materialQuantities = null; + if (jObject.materialQuantities != null) + { + materialQuantities = jObject.materialQuantities.ToObject>(); + jObject.Remove(MATERIAL_QUANTITIES_TAG); + } + T speckleObject = jObject.ToObject(); if (level != null) @@ -205,6 +222,11 @@ public static T ConvertToSpeckleDTOs(dynamic jObject) propComponentProperties.SetValue(speckleObject, ComponentProperties.ToBase(componentProperties)); } + if (materialQuantities != null) + { + speckleObject[MATERIAL_QUANTITIES_TAG] = materialQuantities; + } + return speckleObject; } From 97021f0173f2ced42fbb58be7af452bae1dfc8f6 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Thu, 11 Jul 2024 15:41:39 +0100 Subject: [PATCH 20/52] Disable signed builds on non-main/dev branches (#3566) * don't sign builds unless they're tag AND in main or dev * make steps have different names * fix yaml? * only sign if there's a tag and it doesn't have a dash * rework matches to not be yaml arrays --- .circleci/scripts/config-template.yml | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/.circleci/scripts/config-template.yml b/.circleci/scripts/config-template.yml index ff92a40566..a3d7a5572f 100644 --- a/.circleci/scripts/config-template.yml +++ b/.circleci/scripts/config-template.yml @@ -203,17 +203,29 @@ jobs: # Each project will have individual jobs for each specific task it has to name: Exit if External PR shell: bash.exe command: if [ "$CIRCLE_PR_REPONAME" ]; then circleci-agent step halt; fi - - unless: # Build installers unsigned on non-tagged builds - condition: << pipeline.git.tag >> + - unless: # Build installers unsigned on non-tagged builds or pre-release tags + condition: + and: + - << pipeline.git.tag >> + - not: + matches: + pattern: "[-]+" + value: << pipeline.git.tag >> steps: - run: - name: Build Installer + name: Build Installer Unsigned command: speckle-sharp-ci-tools\InnoSetup\ISCC.exe speckle-sharp-ci-tools\%SLUG%.iss /Sbyparam=$p shell: cmd.exe #does not work in powershell environment: SLUG: << parameters.slug >> - - when: # Setup certificates and build installers signed for tagged builds - condition: << pipeline.git.tag >> + - when: # Setup certificates and build installers signed for tagged builds that aren't prerelease + condition: + and: + - << pipeline.git.tag >> + - not: + matches: + pattern: "[-]+" + value: << pipeline.git.tag >> steps: - run: name: "Digicert Signing Manager Setup" @@ -232,7 +244,7 @@ jobs: # Each project will have individual jobs for each specific task it has to command: | & $env:SSM\smksp_cert_sync.exe - run: - name: Build Installer + name: Build Installer Signed command: speckle-sharp-ci-tools\InnoSetup\ISCC.exe speckle-sharp-ci-tools\%SLUG%.iss /Sbyparam=$p /DSIGN_INSTALLER /DCODE_SIGNING_CERT_FINGERPRINT=%SM_CODE_SIGNING_CERT_SHA1_HASH% shell: cmd.exe #does not work in powershell environment: From 5381b0a9ab6b826a2f69b012b1f1538a45d0c74c Mon Sep 17 00:00:00 2001 From: connorivy <43247197+connorivy@users.noreply.github.com> Date: Mon, 15 Jul 2024 16:44:53 -0500 Subject: [PATCH 21/52] use constructors to generate element1ds (#3571) Co-authored-by: Connor Ivy --- .../PartialClasses/Geometry/ConvertFrame.cs | 67 +++++++------------ .../PartialClasses/Geometry/ConvertLinks.cs | 21 +++--- .../Structural/CSI/Geometry/CSIElement1D.cs | 35 +++++----- 3 files changed, 52 insertions(+), 71 deletions(-) diff --git a/Objects/Converters/ConverterCSI/ConverterCSIShared/PartialClasses/Geometry/ConvertFrame.cs b/Objects/Converters/ConverterCSI/ConverterCSIShared/PartialClasses/Geometry/ConvertFrame.cs index 437e01d621..1d46e480aa 100644 --- a/Objects/Converters/ConverterCSI/ConverterCSIShared/PartialClasses/Geometry/ConvertFrame.cs +++ b/Objects/Converters/ConverterCSI/ConverterCSIShared/PartialClasses/Geometry/ConvertFrame.cs @@ -8,6 +8,7 @@ using System.Linq; using CSiAPIv1; using Speckle.Core.Kits; +using Speckle.Core.Logging; namespace Objects.Converter.CSI; @@ -167,18 +168,13 @@ public CSIElement1D FrameToSpeckle(string name) { string units = ModelUnits(); - var speckleStructFrame = new CSIElement1D(); - - speckleStructFrame.name = name; string pointI, pointJ; pointI = pointJ = null; _ = Model.FrameObj.GetPoints(name, ref pointI, ref pointJ); var pointINode = PointToSpeckle(pointI); var pointJNode = PointToSpeckle(pointJ); - speckleStructFrame.end1Node = pointINode; - speckleStructFrame.end2Node = pointJNode; - var speckleLine = new Line(); + Line speckleLine; if (units != null) { speckleLine = new Line(pointINode.basePoint, pointJNode.basePoint, units); @@ -187,38 +183,22 @@ public CSIElement1D FrameToSpeckle(string name) { speckleLine = new Line(pointINode.basePoint, pointJNode.basePoint); } - speckleStructFrame.baseLine = speckleLine; + + string property, + SAuto; + property = SAuto = null; + Model.FrameObj.GetSection(name, ref property, ref SAuto); + var speckleProperty = Property1DToSpeckle(property); + eFrameDesignOrientation frameDesignOrientation = eFrameDesignOrientation.Null; Model.FrameObj.GetDesignOrientation(name, ref frameDesignOrientation); - switch (frameDesignOrientation) + var elementType = FrameDesignOrientationToElement1dType(frameDesignOrientation); + var speckleStructFrame = new CSIElement1D(speckleLine, speckleProperty, elementType) { - case eFrameDesignOrientation.Column: - { - speckleStructFrame.type = ElementType1D.Column; - break; - } - case eFrameDesignOrientation.Beam: - { - speckleStructFrame.type = ElementType1D.Beam; - break; - } - case eFrameDesignOrientation.Brace: - { - speckleStructFrame.type = ElementType1D.Brace; - break; - } - case eFrameDesignOrientation.Null: - { - //speckleStructFrame.memberType = MemberType.Generic1D; - speckleStructFrame.type = ElementType1D.Null; - break; - } - case eFrameDesignOrientation.Other: - { - speckleStructFrame.type = ElementType1D.Other; - break; - } - } + name = name, + end1Node = pointINode, + end2Node = pointJNode + }; bool[] iRelease, jRelease; @@ -238,12 +218,6 @@ public CSIElement1D FrameToSpeckle(string name) Model.FrameObj.GetLocalAxes(name, ref localAxis, ref advanced); speckleStructFrame.orientationAngle = localAxis; - string property, - SAuto; - property = SAuto = null; - Model.FrameObj.GetSection(name, ref property, ref SAuto); - speckleStructFrame.property = Property1DToSpeckle(property); - double offSetEnd1 = 0; double offSetEnd2 = 0; double RZ = 0; @@ -331,6 +305,17 @@ public CSIElement1D FrameToSpeckle(string name) return speckleStructFrame; } + private static ElementType1D FrameDesignOrientationToElement1dType(eFrameDesignOrientation frameDesignOrientation) => + frameDesignOrientation switch + { + eFrameDesignOrientation.Column => ElementType1D.Column, + eFrameDesignOrientation.Beam => ElementType1D.Beam, + eFrameDesignOrientation.Brace => ElementType1D.Brace, + eFrameDesignOrientation.Null => ElementType1D.Null, + eFrameDesignOrientation.Other => ElementType1D.Other, + _ => throw new SpeckleException($"Unrecognized eFrameDesignOrientation value, {frameDesignOrientation}"), + }; + public void SetFrameElementProperties(Element1D element1D, string newFrame, IList? log) { bool[] end1Release = null; diff --git a/Objects/Converters/ConverterCSI/ConverterCSIShared/PartialClasses/Geometry/ConvertLinks.cs b/Objects/Converters/ConverterCSI/ConverterCSIShared/PartialClasses/Geometry/ConvertLinks.cs index 205111dfa6..e1f6626b2e 100644 --- a/Objects/Converters/ConverterCSI/ConverterCSIShared/PartialClasses/Geometry/ConvertLinks.cs +++ b/Objects/Converters/ConverterCSI/ConverterCSIShared/PartialClasses/Geometry/ConvertLinks.cs @@ -6,6 +6,7 @@ using Objects.Structural.CSI.Properties; using System.Linq; using Speckle.Core.Kits; +using Objects.Structural.Properties; namespace Objects.Converter.CSI; @@ -46,18 +47,12 @@ public CSIElement1D LinkToSpeckle(string name) { string units = ModelUnits(); - var speckleStructLink = new CSIElement1D(); - - speckleStructLink.type = ElementType1D.Link; - speckleStructLink.name = name; string pointI, pointJ; pointI = pointJ = null; _ = Model.LinkObj.GetPoints(name, ref pointI, ref pointJ); var pointINode = PointToSpeckle(pointI); var pointJNode = PointToSpeckle(pointJ); - speckleStructLink.end1Node = pointINode; - speckleStructLink.end2Node = pointJNode; var speckleLine = new Line(); if (units != null) { @@ -67,17 +62,21 @@ public CSIElement1D LinkToSpeckle(string name) { speckleLine = new Line(pointINode.basePoint, pointJNode.basePoint); } - speckleStructLink.baseLine = speckleLine; + + string linkProp = null; + Model.LinkObj.GetProperty(name, ref linkProp); + Property1D property = LinkPropertyToSpeckle(linkProp); + + var speckleStructLink = new CSIElement1D(speckleLine, property, ElementType1D.Link); double localAxis = 0; bool advanced = false; Model.LinkObj.GetLocalAxes(name, ref localAxis, ref advanced); speckleStructLink.orientationAngle = localAxis; - speckleStructLink.type = ElementType1D.Link; - string linkProp = null; - Model.LinkObj.GetProperty(name, ref linkProp); - speckleStructLink.property = LinkPropertyToSpeckle(linkProp); + speckleStructLink.name = name; + speckleStructLink.end1Node = pointINode; + speckleStructLink.end2Node = pointJNode; var GUID = ""; Model.LinkObj.GetGUID(name, ref GUID); diff --git a/Objects/Objects/Structural/CSI/Geometry/CSIElement1D.cs b/Objects/Objects/Structural/CSI/Geometry/CSIElement1D.cs index 44d682b451..c3d4e3ad7d 100644 --- a/Objects/Objects/Structural/CSI/Geometry/CSIElement1D.cs +++ b/Objects/Objects/Structural/CSI/Geometry/CSIElement1D.cs @@ -10,6 +10,9 @@ namespace Objects.Structural.CSI.Geometry; public class CSIElement1D : Element1D { + public CSIElement1D(Line baseLine, Property1D property, ElementType1D type) + : this(baseLine, property, type, null, null, null, null, null, null, null) { } + /// /// SchemaBuilder constructor for structural 1D element (based on local axis) /// @@ -40,16 +43,8 @@ public CSIElement1D( double[]? Modifier = null, DesignProcedure DesignProcedure = DesignProcedure.NoDesign ) + : base(baseLine, property, type, name, end1Releases, end2Releases, end1Offset, end2Offset, localAxis) { - this.baseLine = baseLine; - this.property = property; - this.type = type; - this.name = name; - this.end1Releases = end1Releases ?? new Restraint("FFFFFF"); - this.end2Releases = end2Releases ?? new Restraint("FFFFFF"); - this.end1Offset = end1Offset ?? new Vector(0, 0, 0); - this.end2Offset = end2Offset ?? new Vector(0, 0, 0); - this.localAxis = localAxis; this.CSILinearSpring = CSILinearSpring; this.DesignProcedure = DesignProcedure; Modifiers = Modifier; @@ -92,17 +87,19 @@ public CSIElement1D( double[]? Modifier = null, DesignProcedure DesignProcedure = DesignProcedure.NoDesign ) + : base( + baseLine, + property, + type, + name, + end1Releases, + end2Releases, + end1Offset, + end2Offset, + orientationNode, + orientationAngle + ) { - this.baseLine = baseLine; - this.property = property; - this.type = type; - this.name = name; - this.end1Releases = end1Releases ?? new Restraint("FFFFFF"); - this.end2Releases = end2Releases ?? new Restraint("FFFFFF"); - this.end1Offset = end1Offset ?? new Vector(0, 0, 0); - this.end2Offset = end2Offset ?? new Vector(0, 0, 0); - this.orientationNode = orientationNode; - this.orientationAngle = orientationAngle; this.CSILinearSpring = CSILinearSpring; this.DesignProcedure = DesignProcedure; Modifiers = Modifier; From bef9821893aed211cd79d2f6b0b9886dcaba7d4d Mon Sep 17 00:00:00 2001 From: Iain Sproat <68657+iainsproat@users.noreply.github.com> Date: Tue, 16 Jul 2024 10:38:37 +0100 Subject: [PATCH 22/52] fix(core/http): log message should not duplicate path separator (#3573) - the Uri.PathAndQuery will return the leading path separator so the formatting context should not add it. - https://learn.microsoft.com/en-us/dotnet/api/system.uri?view=net-8.0#examples --- Core/Core/Helpers/Http.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/Core/Helpers/Http.cs b/Core/Core/Helpers/Http.cs index 8cb9076820..92f431aad5 100644 --- a/Core/Core/Helpers/Http.cs +++ b/Core/Core/Helpers/Http.cs @@ -225,7 +225,7 @@ CancellationToken cancellationToken SpeckleLog.Logger .ForContext("ExceptionType", policyResult.FinalException?.GetType()) .Information( - "Execution of http request to {httpScheme}://{hostUrl}/{relativeUrl} {resultStatus} with {httpStatusCode} after {elapsed} seconds and {retryCount} retries", + "Execution of http request to {httpScheme}://{hostUrl}{relativeUrl} {resultStatus} with {httpStatusCode} after {elapsed} seconds and {retryCount} retries", request.RequestUri.Scheme, request.RequestUri.Host, request.RequestUri.PathAndQuery, From 9855d2ea8d72c6e87938520bf3b13ba519f0990f Mon Sep 17 00:00:00 2001 From: Jonathon Broughton <760691+jsdbroughton@users.noreply.github.com> Date: Thu, 18 Jul 2024 09:16:59 +0100 Subject: [PATCH 23/52] Jsdb/navisworks/optiimisations (#3565) * improvements * Getters for Conversion Settings * These dynamic proprties needn't be set if they dont exist * remove this fake applicationId - it doesn't add anything useful * Use the Settings getters we already have rather than reinvent the wheel. * extract the magic separator char * More robust Progress looper and then implement more widely * HashSets for better distinct element handling * Multithreading traversals * Extracted Property Management * Move the CommitObject definition * Added stream setting to omit properties for geometry only * housekeeping * Extract the Conversion handoff and refactor methods out of Send * Developer mode stub class --- .../ConnectorNavisworksBindings.Conversion.cs | 390 +++++++++++++++ .../ConnectorNavisworksBindings.Events.cs | 6 +- .../ConnectorNavisworksBindings.File.cs | 16 +- .../ConnectorNavisworksBindings.Filters.cs | 16 +- .../ConnectorNavisworksBindings.Selections.cs | 34 +- .../ConnectorNavisworksBindings.Send.cs | 443 ++++-------------- .../ConnectorNavisworksBindings.Settings.cs | 9 +- .../Bindings/ConnectorNavisworksBindings.cs | 92 +--- .../ConnectorNavisworks.Shared.projitems | 2 + .../NavisworksOptions/Manager.cs | 30 +- .../NavisworksOptions/Properties.cs | 10 + .../ConnectorNavisworks/Other/Developer.cs | 3 + .../ConnectorNavisworks/Other/Elements.cs | 416 ++++++++-------- .../ConnectorNavisworks/Other/Invokers.cs | 1 - .../Other/SelectionBuilder.cs | 319 +++++++++---- .../ConverterNavisworks.Properties.cs | 74 ++- .../ConverterNavisworks.Settings.cs | 26 + 17 files changed, 1092 insertions(+), 795 deletions(-) create mode 100644 ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Conversion.cs create mode 100644 ConnectorNavisworks/ConnectorNavisworks/Other/Developer.cs diff --git a/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Conversion.cs b/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Conversion.cs new file mode 100644 index 0000000000..2823be9e47 --- /dev/null +++ b/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Conversion.cs @@ -0,0 +1,390 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Autodesk.Navisworks.Api; +using DesktopUI2.Models; +using DesktopUI2.Models.Settings; +using Speckle.ConnectorNavisworks.Other; +using Speckle.Core.Kits; +using Speckle.Core.Logging; +using Speckle.Core.Models; + +namespace Speckle.ConnectorNavisworks.Bindings; + +public partial class ConnectorBindingsNavisworks +{ + private ConversionInvoker _conversionInvoker; + + /// + /// Configures the converter settings. + /// + private void ConfigureConverter() + { + _navisworksConverter.SetConverterSettings(new Dictionary { { "_Mode", "objects" } }); + _conversionInvoker = new ConversionInvoker(_navisworksConverter); + } + + /// + /// Initializes the conversion of model items to elements. + /// + /// The list of model items to convert. + /// The dictionary to store the converted elements. + private void InitializeConversion( + IReadOnlyList modelItemsToConvert, + Dictionary> conversions + ) + { + var totalObjects = modelItemsToConvert.Count; + var objectIncrement = 1 / Math.Max((double)totalObjects, 1); + var objectInterval = Math.Max(totalObjects / 100, 1); + + for (int index = 0; index < modelItemsToConvert.Count; index++) + { + HandleConversionCancellation(); + + ModelItem modelItem = modelItemsToConvert[index]; + var element = new Element(modelItem); + + conversions.Add(element, new Tuple(Constants.ConversionState.ToConvert, null)); + + if (index % objectInterval == 0 || index == modelItemsToConvert.Count - 1) + { + _progressBar.Update((index + 1) * objectIncrement); + } + } + } + + /// + /// Sets up the converter with the appropriate settings and context document. + /// + /// The stream state containing the settings. + private void SetupConverter(StreamState state) + { + _defaultKit = KitManager.GetDefaultKit(); + _navisworksConverter = _defaultKit.LoadConverter(SpeckleNavisworksUtilities.VersionedAppName); + + CurrentSettings = state.Settings; + var settings = state.Settings.ToDictionary(setting => setting.Slug, setting => setting.Selection); + + _navisworksConverter.SetContextDocument(s_activeDoc); + _navisworksConverter.SetConverterSettings(settings); + _navisworksConverter.Report.ReportObjects.Clear(); + } + + /// + /// Prepares the elements from the provided model items for conversion. + /// + /// The list of model items to convert. + /// A dictionary of elements that are ready for conversion. + private Dictionary> PrepareElementsForConversion( + IReadOnlyList modelItemsToConvert + ) + { + _progressBar.BeginSubOperation(0.1, "Who's who? Let's check the ID cards..."); + var conversions = new Dictionary>(); + + InitializeConversion(modelItemsToConvert, conversions); + _progressBar.EndSubOperation(); + + return conversions; + } + + /// + /// Handles the cancellation of the conversion process. + /// + private void HandleConversionCancellation() + { + if (_progressBar.IsCanceled) + { + _progressViewModel.CancellationTokenSource.Cancel(); + } + _progressViewModel.CancellationToken.ThrowIfCancellationRequested(); + } + + /// + /// Handles conversion of elements and views. + /// + /// The current stream state. + /// Dictionary of elements to be converted. + /// The objects to commit. + /// Count of successfully converted objects. + private int ElementAndViewsConversion( + StreamState state, + IDictionary> conversions, + Collection commitObject + ) + { + _progressBar.BeginSubOperation(0.3, $"Spinning the alchemy wheel, transmuting {conversions.Count} objects..."); + ConfigureConverter(); + + var converted = ConvertObjects(conversions); + var convertedCount = ValidateConversion(converted); + + _progressBar.EndSubOperation(); + + BuildHierarchy(converted, state, commitObject); + ValidateHierarchy(commitObject); + + // Update report with conversion results + _progressViewModel.Report.Merge(_navisworksConverter.Report); + + ConvertViewsProcess(state, commitObject); + + // _progressBar.EndSubOperation(); + + return convertedCount; + } + + /// + /// Validates the converted objects and handles any conversion errors. + /// + /// The dictionary of converted objects. + /// The count of successfully converted objects. + /// Thrown when zero objects are converted successfully. + private int ValidateConversion(IDictionary> converted) + { + var convertedCount = converted.Count(x => x.Value.Item1 == Constants.ConversionState.Converted); + + if (convertedCount != 0) + { + return convertedCount; + } + + _settingsHandler.RestoreAutoSave(); + throw new SpeckleException("Zero objects converted successfully. Send stopped."); + } + + /// + /// Builds the nested object hierarchy for the converted elements. + /// + /// The dictionary of converted objects. + /// The current stream state. + /// The objects to commit. + private void BuildHierarchy( + Dictionary> converted, + StreamState state, + Collection commitObject + ) => + // _progressBar.StartNewSubOperation(0.2, "Building a family tree, data-style..."); + // commitObject.elements = Element.BuildNestedObjectHierarchy(converted, state).ToList(); + commitObject.elements = Element.BuildNestedObjectHierarchyInParallel(converted, state, _progressBar).ToList(); + + /// + /// Validates the hierarchy and ensures it contains elements. + /// + /// The objects to commit. + /// Thrown when the hierarchy contains no elements. + private void ValidateHierarchy(Collection commitObject) + { + if (commitObject.elements.Count != 0) + { + return; + } + + _settingsHandler.RestoreAutoSave(); + throw new SpeckleException( + "All Geometry objects in the selection are hidden or cannot be converted. Send stopped." + ); + } + + /// + /// Converts the views and updates the progress bar. + /// + /// The current stream state. + /// The objects to commit. + private void ConvertViewsProcess(StreamState state, Collection commitObject) + { + _progressBar.StartNewSubOperation(0.1, "Sending Views."); + ConvertViews(state, commitObject); + } + + /// + /// Converts a set of Navisworks elements into Speckle objects and logs their conversion status. + /// + /// A dictionary mapping elements to their conversion states and corresponding Base objects. + /// The number of successfully converted elements. + private Dictionary> ConvertObjects( + IDictionary> allConversions + ) + { + Dictionary> conversions = allConversions + .Where(c => c.Key != null && c.Value.Item1 == Constants.ConversionState.ToConvert) + .ToDictionary(c => c.Key, c => c.Value); + + int convertedCount = 0; + var conversionIncrement = conversions.Count != 0 ? 1.0 / conversions.Count : 0.0; + var conversionInterval = Math.Max(conversions.Count / 100, 1); + + for (var i = 0; i < conversions.Count; i++) + { + if (_progressBar.IsCanceled) + { + _progressViewModel.CancellationTokenSource.Cancel(); + } + + _progressViewModel.CancellationToken.ThrowIfCancellationRequested(); + + var nextToConvert = conversions.ElementAt(i); + var element = nextToConvert.Key; + + // Get the descriptor of the element + var descriptor = element.Descriptor(); + + if (_navisworksConverter.Report.ReportObjects.TryGetValue(element.IndexPath, out var applicationObject)) + { + _progressViewModel.Report.Log(applicationObject); + conversions[element] = new Tuple( + Constants.ConversionState.Converted, + nextToConvert.Value.Item2 + ); + continue; + } + + var reportObject = new ApplicationObject(element.IndexPath, descriptor) { applicationId = element.IndexPath }; + + if (!_navisworksConverter.CanConvertToSpeckle(element.ModelItem)) + { + reportObject.Update( + status: ApplicationObject.State.Skipped, + logItem: "Sending this object type is not supported in Navisworks" + ); + _progressViewModel.Report.Log(reportObject); + conversions[element] = new Tuple( + Constants.ConversionState.Skipped, + nextToConvert.Value.Item2 + ); + continue; + } + + _navisworksConverter.Report.Log(reportObject); + + // Convert the model item to Speckle + var converted = _conversionInvoker.Convert(element.ModelItem); + + if (converted == null) + { + reportObject.Update(status: ApplicationObject.State.Failed, logItem: "Conversion returned null"); + _progressViewModel.Report.Log(reportObject); + conversions[element] = new Tuple( + Constants.ConversionState.Failed, + nextToConvert.Value.Item2 + ); + continue; + } + + converted.applicationId = element.IndexPath; + conversions[element] = new Tuple(Constants.ConversionState.Converted, converted); + convertedCount++; + _conversionProgressDict["Conversion"] = convertedCount; + _progressViewModel.Update(_conversionProgressDict); + reportObject.Update(status: ApplicationObject.State.Created, logItem: $"Sent as {converted.speckle_type}"); + _progressViewModel.Report.Log(reportObject); + + if (i % conversionInterval != 0 && i != conversions.Count) + { + continue; + } + + double progress = (i + 1) * conversionIncrement; + _progressBar.Update(progress); + } + + return conversions; + } + + /// + /// Retrieves a list of ModelItems from the provided stream state. This method checks the selection filter, + /// populates hierarchy, omits hidden items, and verifies the visibility of the objects for conversion. + /// + /// The current stream state. + /// A list of model items to convert. + /// Thrown when no objects are selected or visible for conversion. + private List GetModelItemsForConversion(StreamState state) + { + var modelItemsToConvert = new List(); + + var selectionBuilder = new SelectionHandler(state, _progressViewModel) { ProgressBar = _progressBar }; + + selectionBuilder.GetFromFilter(); + + selectionBuilder.ValidateStartNodes(); + + // Check if any items have been selected + if (selectionBuilder.Count == 0) + { + throw new InvalidOperationException( + "Zero objects selected; send stopped. Please select some objects, or check that your filter can actually select something." + ); + } + + try + { + selectionBuilder.PopulateHierarchyAndOmitHidden(); + } + catch + { + throw new InvalidOperationException("An error occurred retrieving objects from your saved selection source."); + } + + modelItemsToConvert.AddRange(selectionBuilder.ModelItems); + + if (modelItemsToConvert.Count == 0) + { + throw new InvalidOperationException( + "Zero objects visible for conversion; send stopped. Please select some objects, or check that your filter can actually select something." + ); + } + + return modelItemsToConvert; + } + + /// + /// Converts Navisworks views into Base objects and adds them to a given commit object. + /// This includes selected views from a provided filter and, optionally, the active and home views. + /// + /// The current stream state, containing filter data. + /// The object to which converted views should be added. + private void ConvertViews(StreamState state, DynamicBase commitObject) + { + var views = new List(); + + _navisworksConverter.SetConverterSettings(new Dictionary { { "_Mode", "views" } }); + + if (state.Filter?.Slug == "views") + { + var selectionBuilder = new SelectionHandler(state, _progressViewModel) { ProgressBar = _progressBar }; + + var selectedViews = state.Filter.Selection + .Distinct() + .Select(selectionBuilder.ResolveSavedViewpoint) + .Select(_conversionInvoker.Convert) + .Where(c => c != null) + .ToList(); + + views.AddRange(selectedViews); + } + // Only send current view if we aren't sending other views. + else if (CurrentSettings.Find(x => x.Slug == "current-view") is CheckBoxSetting { IsChecked: true }) + { + var currentView = _conversionInvoker.Convert(s_activeDoc.CurrentViewpoint.ToViewpoint()); + var homeView = _conversionInvoker.Convert(s_activeDoc.HomeView); + + if (currentView != null) + { + currentView["name"] = "Active View"; + views.Add(currentView); + } + + if (homeView != null) + { + homeView["name"] = "Home View"; + views.Add(homeView); + } + } + + if (views.Count != 0) + { + commitObject["views"] = views; + } + } +} diff --git a/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Events.cs b/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Events.cs index 5e08d4d426..95fcdea20a 100644 --- a/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Events.cs +++ b/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Events.cs @@ -22,9 +22,9 @@ public partial class ConnectorBindingsNavisworks public void RegisterAppEvents() { // Register event handlers for document, selection sets, and model collection changes - s_doc.FileNameChanged += DocumentChangedEvent; - s_doc.SelectionSets.Changed += SetsChangedEvent; - s_doc.Models.CollectionChanged += ModelsChangedEvent; + s_activeDoc.FileNameChanged += DocumentChangedEvent; + s_activeDoc.SelectionSets.Changed += SetsChangedEvent; + s_activeDoc.Models.CollectionChanged += ModelsChangedEvent; } /// diff --git a/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.File.cs b/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.File.cs index 53edb2ffad..4393c0d21c 100644 --- a/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.File.cs +++ b/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.File.cs @@ -12,7 +12,7 @@ public partial class ConnectorBindingsNavisworks /// /// The list of stream states to write. public override void WriteStreamsToFile(List streams) => - SpeckleStreamManager.WriteStreamStateList(s_doc, streams); + SpeckleStreamManager.WriteStreamStateList(s_activeDoc, streams); /// /// Retrieves the list of stream states from the file. @@ -21,9 +21,9 @@ public override void WriteStreamsToFile(List streams) => public override List GetStreamsInFile() { var streams = new List(); - if (s_doc != null) + if (s_activeDoc != null) { - streams = SpeckleStreamManager.ReadState(s_doc); + streams = SpeckleStreamManager.ReadState(s_activeDoc); } return streams; @@ -35,19 +35,19 @@ public override List GetStreamsInFile() /// The name of the current file. public override string GetFileName() { - IsFileAndModelsPresent(); + IsFileAndAreModelsPresent(); - return s_doc?.CurrentFileName ?? string.Empty; + return s_activeDoc?.CurrentFileName ?? string.Empty; } - private static void IsFileAndModelsPresent() + private static void IsFileAndAreModelsPresent() { - if (s_doc == null) + if (s_activeDoc == null) { throw new FileNotFoundException("No active document found. Cannot Send."); } - if (s_doc.Models.Count == 0) + if (s_activeDoc.Models.Count == 0) { throw new FileNotFoundException("No models are appended. Nothing to Send."); } diff --git a/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Filters.cs b/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Filters.cs index 24ad7f49c3..a11f862a72 100644 --- a/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Filters.cs +++ b/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Filters.cs @@ -23,14 +23,14 @@ public override List GetSelectionFilters() var manualFilter = new ManualSelectionFilter(); - if (s_doc == null) + if (s_activeDoc == null) { return filters; } filters.Add(manualFilter); - var selectSetsRootItem = s_doc.SelectionSets.RootItem; + var selectSetsRootItem = s_activeDoc.SelectionSets.RootItem; var savedSelectionSets = selectSetsRootItem?.Children.Select(GetSets).ToList() ?? new List(); @@ -47,7 +47,7 @@ public override List GetSelectionFilters() filters.Add(selectionSetsFilter); } - var savedViewsRootItem = s_doc.SavedViewpoints.RootItem; + var savedViewsRootItem = s_activeDoc.SavedViewpoints.RootItem; var savedViews = savedViewsRootItem?.Children.Select(GetViews).Select(RemoveNullNodes).Where(x => x != null).ToList() @@ -61,14 +61,14 @@ public override List GetSelectionFilters() Name = "Saved Viewpoints", Icon = "FileTree", Description = - "Select a saved viewpoint and send its visible items in the commit. Only views with Hide/Require attribute checked are listed.", + "Select saved viewpoints and send their visible items in the commit. Only views with Hide/Require attribute checked are listed.", Values = savedViews, - SelectionMode = "Toggle" + SelectionMode = "Multiple" }; filters.Add(savedViewsFilter); } - DocumentClash clashPlugin = s_doc.GetClash(); + DocumentClash clashPlugin = s_activeDoc.GetClash(); var clashTests = clashPlugin?.TestsData; @@ -102,7 +102,7 @@ private static TreeNode GetSets(SavedItem savedItem) DisplayName = savedItem.DisplayName, Guid = savedItem.Guid, IndexWith = nameof(TreeNode.Guid), - Indices = s_doc.SelectionSets.CreateIndexPath(savedItem).ToArray() + Indices = s_activeDoc.SelectionSets.CreateIndexPath(savedItem).ToArray() }; if (!savedItem.IsGroup) @@ -121,7 +121,7 @@ private static TreeNode GetSets(SavedItem savedItem) private static TreeNode GetViews(SavedItem savedItem) { - var reference = s_doc.SavedViewpoints.CreateReference(savedItem); + var reference = s_activeDoc.SavedViewpoints.CreateReference(savedItem); var treeNode = new TreeNode { diff --git a/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Selections.cs b/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Selections.cs index 0a4b5b58b0..67a4c4de62 100644 --- a/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Selections.cs +++ b/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Selections.cs @@ -1,53 +1,35 @@ using System.Collections.Generic; using System.Linq; using System.Windows.Forms; -using Autodesk.Navisworks.Api; using Speckle.ConnectorNavisworks.Other; -using Application = Autodesk.Navisworks.Api.Application; using Cursor = System.Windows.Forms.Cursor; namespace Speckle.ConnectorNavisworks.Bindings; public partial class ConnectorBindingsNavisworks { + /// /// /// Parses list all selected Elements and their descendants that match criteria: /// 1. Is Selected /// 2. Is Visible in the current view /// - /// List of unique pseudoIds + /// List of unique IndexPaths as strings public override List GetSelectedObjects() { Cursor.Current = Cursors.WaitCursor; - // Current document, models and selected elements. - s_doc = Application.ActiveDocument; - - IsFileAndModelsPresent(); - - var appSelectedItems = s_doc.CurrentSelection.SelectedItems; + IsFileAndAreModelsPresent(); // Storing as a Set for consistency with the converter's handling of fragments and paths. - var selectedObjects = new HashSet(); - - var visible = appSelectedItems.Where(IsElementVisible); - var visibleItems = visible.ToList(); - var ids = visibleItems.Select(i => new Element().GetElement(i).PseudoId).Where(x => x != null); - - selectedObjects.UnionWith(ids); + var selectedObjects = new HashSet( + s_activeDoc.CurrentSelection.SelectedItems + .Where(Element.IsElementVisible) + .Select(Element.ResolveModelItemToIndexPath) + ); Cursor.Current = Cursors.Default; return selectedObjects.ToList(); } - - /// - /// Checks is the Element is hidden or if any of its ancestors is hidden - /// - /// - /// - private static bool IsElementVisible(ModelItem element) => - // Hidden status is stored at the earliest node in the hierarchy - // All of the the tree path nodes need to not be Hidden - element.AncestorsAndSelf.All(x => x.IsHidden != true); } diff --git a/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Send.cs b/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Send.cs index 11f23fd75d..4ca3c86a6e 100644 --- a/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Send.cs +++ b/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Send.cs @@ -15,7 +15,6 @@ using Speckle.Core.Logging; using Speckle.Core.Models; using Speckle.Core.Transports; -using static Speckle.ConnectorNavisworks.Other.SpeckleNavisworksUtilities; using Application = Autodesk.Navisworks.Api.Application; using Cursor = System.Windows.Forms.Cursor; @@ -23,7 +22,6 @@ namespace Speckle.ConnectorNavisworks.Bindings; public partial class ConnectorBindingsNavisworks { - private ConversionInvoker _conversionInvoker; private ConcurrentDictionary _conversionProgressDict; private int _convertedCount; private ProgressInvoker _progressBar; @@ -31,23 +29,11 @@ public partial class ConnectorBindingsNavisworks public override bool CanPreviewSend => false; - /// - /// Gets a new instance of a commit object with initial properties. - /// - private static Collection CommitObject => - new() - { - ["units"] = GetUnits(s_doc), - collectionType = "Navisworks Model", - name = s_doc.Title, - applicationId = "Root" - }; - // Stub - Preview send is not supported public override async void PreviewSend(StreamState state, ProgressViewModel progress) => await Task.Delay(TimeSpan.FromMilliseconds(500)).ConfigureAwait(false); - // TODO! + /// /// /// Sends the stream to Speckle. /// @@ -68,7 +54,7 @@ public override async Task SendStream(StreamState state, ProgressViewMod SetupProgressViewModel(); SetupConverter(state); - InitializeManagerOptionsForSend(state); + _settingsHandler.InitializeManagerOptionsForSend(state); try { @@ -115,11 +101,6 @@ public override async Task SendStream(StreamState state, ProgressViewMod var objectId = await SendConvertedObjectsToSpeckle(state, commitObject).ConfigureAwait(false); - if (_progressViewModel.Report.OperationErrors.Count != 0) - { - ConnectorHelpers.DefaultSendErrorHandler("", _progressViewModel.Report.OperationErrors.Last()); - } - _progressViewModel.CancellationToken.ThrowIfCancellationRequested(); commitId = await CreateCommit(state, objectId).ConfigureAwait(false); @@ -150,141 +131,10 @@ public override async Task SendStream(StreamState state, ProgressViewMod return commitId; } - /// - /// The SettingsManager can be seeded with the options for the conversion. - /// - /// - /// - private void InitializeManagerOptionsForSend(StreamState state) - { - var internalPropertySettings = state.Settings.Find(x => x.Slug == "internal-properties"); - var internalPropertyNames = state.Settings.Find(x => x.Slug == "internal-property-names"); - - if (internalPropertySettings != null && ((CheckBoxSetting)internalPropertySettings).IsChecked) - { - _settingsHandler.ShowInternalProperties(); - } - - if (internalPropertyNames != null && ((CheckBoxSetting)internalPropertyNames).IsChecked) - { - _settingsHandler.UseInternalPropertyNames(); - } - } - - /// - /// Validates the required properties before sending a stream. - /// - /// The stream state. - private void ValidateBeforeSending(StreamState state) - { - if (_progressViewModel == null) - { - throw new ArgumentException("No ProgressViewModel provided."); - } - - if (s_doc.ActiveSheet == null) - { - throw new InvalidOperationException("Your Document is empty. Nothing to Send."); - } - - if (state.Filter == null) - { - throw new InvalidOperationException("No filter provided. Nothing to Send."); - } - - if (state.Filter.Slug == "all" || state.CommitMessage == "Sent everything") - { - throw new InvalidOperationException("Everything Mode is not yet implemented. Send stopped."); - } - } - - /// - /// Configures the progress view model and sets up cancellation behavior. - /// - private void SetupProgressViewModel() - { - _progressViewModel.Report = new ProgressReport(); - - _progressViewModel.CancellationToken.Register(() => - { - try - { - _progressBar.Cancel(); - Application.EndProgress(); - } - catch (InvalidOperationException) - { - // ignored - } - - _settingsHandler.RestoreAutoSave(); - Cursor.Current = Cursors.Default; - }); - } - - /// - /// Prepares the elements from the provided model items for conversion. - /// - /// The list of model items to convert. - /// A dictionary of elements ready for conversion. - private Dictionary> PrepareElementsForConversion( - IReadOnlyList modelItemsToConvert - ) - { - _progressBar.BeginSubOperation(0.1, "Who's who? Let's check the ID cards..."); - var conversions = new Dictionary>(); - - var totalObjects = modelItemsToConvert.Count; - var objectIncrement = 1 / Math.Max((double)totalObjects, 1); - var objectInterval = Math.Max(totalObjects / 100, 1); - - for (int index = 0; index < modelItemsToConvert.Count; index++) - { - if (_progressBar.IsCanceled) - { - _progressViewModel.CancellationTokenSource.Cancel(); - } - - _progressViewModel.CancellationToken.ThrowIfCancellationRequested(); - - ModelItem modelItem = modelItemsToConvert[index]; - var element = new Element(); - element.GetElement(modelItem); - conversions.Add(element, new Tuple(Constants.ConversionState.ToConvert, null)); - - if (index % objectInterval == 0 || index == modelItemsToConvert.Count - 1) - { - _progressBar.Update((index + 1) * objectIncrement); - } - } - - _progressBar.EndSubOperation(); - - return conversions; - } - - /// - /// Sets up the converter with the appropriate settings and context document. - /// - /// The stream state containing the settings. - private void SetupConverter(StreamState state) - { - _defaultKit = KitManager.GetDefaultKit(); - _navisworksConverter = _defaultKit.LoadConverter(VersionedAppName); - - CurrentSettings = state.Settings; - var settings = state.Settings.ToDictionary(setting => setting.Slug, setting => setting.Selection); - - _navisworksConverter.SetContextDocument(s_doc); - _navisworksConverter.SetConverterSettings(settings); - _navisworksConverter.Report.ReportObjects.Clear(); - } - /// /// Prepares model items to be converted for a given stream state. /// /// The stream state. - /// Out parameter to return the total number of objects to convert. /// A list of ModelItem objects that are ready to be converted. private List PrepareModelItemsToConvert(StreamState state) { @@ -297,57 +147,10 @@ private List PrepareModelItemsToConvert(StreamState state) return modelItemsToConvert.Where(e => e != null).ToList(); } - /// - /// Handles conversion of elements and views. - /// - /// The current stream state. - /// Dictionary of elements to be converted. - /// The objects to commit. - /// Count of successfully converted objects. - private int ElementAndViewsConversion( - StreamState state, - IDictionary> conversions, - Collection commitObject - ) - { - _progressBar.BeginSubOperation(0.35, "Spinning the alchemy wheel, transmuting data..."); - _navisworksConverter.SetConverterSettings(new Dictionary { { "_Mode", "objects" } }); - _conversionInvoker = new ConversionInvoker(_navisworksConverter); - var converted = ConvertObjects(conversions); - var convertedCount = converted.Count(x => x.Value.Item1 == Constants.ConversionState.Converted); - - if (convertedCount == 0) - { - _settingsHandler.RestoreAutoSave(); - throw new SpeckleException("Zero objects converted successfully. Send stopped."); - } - - _progressBar.StartNewSubOperation(0.2, "Building a family tree, data-style..."); - - commitObject.elements = Element.BuildNestedObjectHierarchy(converted, state).ToList(); - - if (commitObject.elements.Count == 0) - { - _settingsHandler.RestoreAutoSave(); - throw new SpeckleException( - "All Geometry objects in the selection are hidden or cannot be converted. Send stopped." - ); - } - - _progressViewModel.Report.Merge(_navisworksConverter.Report); - - _progressBar.StartNewSubOperation(0.1, "Sending Views."); - ConvertViews(state, commitObject); - _progressBar.EndSubOperation(); - - return convertedCount; - } - /// /// Handles updates to the progress of the operation. /// /// A dictionary containing progress details. - /// The total number of converted items. private void HandleProgress(ConcurrentDictionary progressDict) { // If the "RemoteTransport" key exists in the dictionary and has a positive value @@ -375,12 +178,12 @@ private void HandleError(string _, Exception ex) => /// /// The current state of the stream. /// The collection of objects to send. - /// The total number of converted items. - /// The ID of the sent object. + /// The ID of the object being sent. private async Task SendConvertedObjectsToSpeckle(StreamState state, Base commitObject) { + _progressBar.EndSubOperation(); _progressBar.BeginSubOperation( - 0.3, + 0.95, $"Pack your bags, data! That's {_convertedCount} objects going on a trip to the Speckle universe..." ); @@ -413,11 +216,10 @@ private async Task SendConvertedObjectsToSpeckle(StreamState state, Base /// /// The StreamState object, contains stream details and client. /// The id of the object to commit. - /// The count of converted elements. /// The id of the created commit. private async Task CreateCommit(StreamState state, string objectId) { - _progressBar.BeginSubOperation(0.1, "Sealing the deal... Your data's new life begins in Speckle!"); + _progressBar.BeginSubOperation(1, "Sealing the deal... Your data's new life begins in Speckle!"); // Define a new commit input with stream details, object ID, and commit message var commit = new CommitCreateInput @@ -429,207 +231,126 @@ private async Task CreateCommit(StreamState state, string objectId) sourceApplication = HostAppNameVersion }; - string commitId = - // This block enables forcing a failed send to test the caching feature - // #if DEBUG - // if (!isRetrying) - // throw new SpeckleException("Debug mode: commit not created."); - // #endif - // Use the helper function to create the commit and retrieve the commit ID - await ConnectorHelpers + string commitId = await ConnectorHelpers .CreateCommit(state.Client, commit, _progressViewModel.CancellationToken) .ConfigureAwait(false); return commitId; } - /// - /// Retrieves a list of ModelItems from the provided stream state. This method checks the selection filter, - /// populates hierarchy, omits hidden items, and verifies the visibility of the objects for conversion. - /// - /// The current stream state. - /// A list of model items to convert. - /// Thrown when no objects are selected or visible for conversion. - private List GetModelItemsForConversion(StreamState state) + public async Task RetryLastConversionSend() { - var modelItemsToConvert = new List(); - - var selectionBuilder = new SelectionHandler(state, _progressViewModel) { ProgressBar = _progressBar }; - - selectionBuilder.GetFromFilter(); - - selectionBuilder.ValidateStartNodes(); - - // Check if any items have been selected - if (selectionBuilder.Count == 0) + if (s_activeDoc == null) { - throw new InvalidOperationException( - "Zero objects selected; send stopped. Please select some objects, or check that your filter can actually select something." - ); + return; } - try + if (CachedConvertedElements == null || s_cachedCommit == null) { - selectionBuilder.PopulateHierarchyAndOmitHidden(); + throw new SpeckleException("Cant retry last conversion: no cached conversion or commit found."); } - catch + + if (s_cachedCommit is Collection commitObject) { - throw new InvalidOperationException("An error occurred retrieving objects from your saved selection source."); - } + // _isRetrying = true; - modelItemsToConvert.AddRange(selectionBuilder.ModelItems); + var applicationProgress = Application.BeginProgress("Retrying that send to Speckle."); + _progressBar = new ProgressInvoker(applicationProgress); + _progressViewModel = new ProgressViewModel(); - if (modelItemsToConvert.Count == 0) - { - throw new InvalidOperationException( - "Zero objects visible for conversion; send stopped. Please select some objects, or check that your filter can actually select something." - ); - } + commitObject.elements = CachedConvertedElements; - return modelItemsToConvert; - } + var state = s_cachedState; - /// - /// Converts Navisworks views into Base objects and adds them to a given commit object. - /// This includes selected views from a provided filter and, optionally, the active and home views. - /// - /// The current stream state, containing filter data. - /// The object to which converted views should be added. - private void ConvertViews(StreamState state, DynamicBase commitObject) - { - var views = new List(); + _progressBar.BeginSubOperation(0.7, "Retrying cached conversion."); + _progressBar.EndSubOperation(); - _navisworksConverter.SetConverterSettings(new Dictionary { { "_Mode", "views" } }); + var objectId = await SendConvertedObjectsToSpeckle(state, commitObject).ConfigureAwait(false); - if (state.Filter?.Slug == "views") - { - var selectionBuilder = new SelectionHandler(state, _progressViewModel) { ProgressBar = _progressBar }; + if (_progressViewModel.Report.OperationErrors.Count != 0) + { + ConnectorHelpers.DefaultSendErrorHandler("", _progressViewModel.Report.OperationErrors.Last()); + } - var selectedViews = state.Filter.Selection - .Distinct() - .Select(selectionBuilder.ResolveSavedViewpoint) - .Select(_conversionInvoker.Convert) - .Where(c => c != null) - .ToList(); + _progressViewModel.CancellationToken.ThrowIfCancellationRequested(); - views.AddRange(selectedViews); - } - // Only send current view if we aren't sending other views. - else if (CurrentSettings.Find(x => x.Slug == "current-view") is CheckBoxSetting { IsChecked: true }) - { - var currentView = _conversionInvoker.Convert(s_doc.CurrentViewpoint.ToViewpoint()); - var homeView = _conversionInvoker.Convert(s_doc.HomeView); + state.Settings.Add(new CheckBoxSetting { Slug = "retrying", IsChecked = true }); - if (currentView != null) + string commitId; + try + { + commitId = await CreateCommit(state, objectId).ConfigureAwait(false); + } + finally { - currentView["name"] = "Active View"; - views.Add(currentView); + _progressBar.EndSubOperation(); + Application.EndProgress(); + Cursor.Current = Cursors.Default; } - if (homeView != null) + state.Settings.RemoveAll(x => x.Slug == "retrying"); + + if (string.IsNullOrEmpty(commitId)) { - homeView["name"] = "Home View"; - views.Add(homeView); + return; } } - if (views.Count != 0) - { - commitObject["views"] = views; - } + // nullify the cached conversion and commit on success. + s_cachedCommit = null; + + CachedConvertedElements = null; + // _isRetrying = false; } /// - /// Converts a set of Navisworks elements into Speckle objects and logs their conversion status. + /// Validates the required properties before sending a stream. /// - /// A dictionary mapping elements to their conversion states and corresponding Base objects. - /// The number of successfully converted elements. - private Dictionary> ConvertObjects( - IDictionary> allConversions - ) + /// The stream state. + void ValidateBeforeSending(StreamState state) { - Dictionary> conversions = allConversions - .Where(c => c.Key != null && c.Value.Item1 == Constants.ConversionState.ToConvert) - .ToDictionary(c => c.Key, c => c.Value); - - int convertedCount = 0; - var conversionIncrement = conversions.Count != 0 ? 1.0 / conversions.Count : 0.0; - var conversionInterval = Math.Max(conversions.Count / 100, 1); - - for (var i = 0; i < conversions.Count; i++) + if (_progressViewModel == null) { - if (_progressBar.IsCanceled) - { - _progressViewModel.CancellationTokenSource.Cancel(); - } - - _progressViewModel.CancellationToken.ThrowIfCancellationRequested(); - - var nextToConvert = conversions.ElementAt(i); - var element = nextToConvert.Key; - - // Get the descriptor of the element - var descriptor = element.Descriptor(); - - if (_navisworksConverter.Report.ReportObjects.TryGetValue(element.PseudoId, out var applicationObject)) - { - _progressViewModel.Report.Log(applicationObject); - conversions[element] = new Tuple( - Constants.ConversionState.Converted, - nextToConvert.Value.Item2 - ); - continue; - } + throw new ArgumentException("No ProgressViewModel provided."); + } - var reportObject = new ApplicationObject(element.PseudoId, descriptor) { applicationId = element.PseudoId }; + if (s_activeDoc.ActiveSheet == null) + { + throw new InvalidOperationException("Your Document is empty. Nothing to Send."); + } - if (!_navisworksConverter.CanConvertToSpeckle(element.ModelItem)) - { - reportObject.Update( - status: ApplicationObject.State.Skipped, - logItem: "Sending this object type is not supported in Navisworks" - ); - _progressViewModel.Report.Log(reportObject); - conversions[element] = new Tuple( - Constants.ConversionState.Skipped, - nextToConvert.Value.Item2 - ); - continue; - } + if (state.Filter == null) + { + throw new InvalidOperationException("No filter provided. Nothing to Send."); + } - _navisworksConverter.Report.Log(reportObject); + if (state.Filter.Slug == "all" || state.CommitMessage == "Sent everything") + { + throw new InvalidOperationException("Everything Mode is not yet implemented. Send stopped."); + } + } - // Convert the model item to Speckle - var converted = _conversionInvoker.Convert(element.ModelItem); + /// + /// Configures the progress view model and sets up cancellation behavior. + /// + private void SetupProgressViewModel() + { + _progressViewModel.Report = new ProgressReport(); - if (converted == null) + _progressViewModel.CancellationToken.Register(() => + { + try { - reportObject.Update(status: ApplicationObject.State.Failed, logItem: "Conversion returned null"); - _progressViewModel.Report.Log(reportObject); - conversions[element] = new Tuple( - Constants.ConversionState.Failed, - nextToConvert.Value.Item2 - ); - continue; + _progressBar.Cancel(); + Application.EndProgress(); } - - converted.applicationId = element.PseudoId; - conversions[element] = new Tuple(Constants.ConversionState.Converted, converted); - convertedCount++; - _conversionProgressDict["Conversion"] = convertedCount; - _progressViewModel.Update(_conversionProgressDict); - reportObject.Update(status: ApplicationObject.State.Created, logItem: $"Sent as {converted.speckle_type}"); - _progressViewModel.Report.Log(reportObject); - - if (i % conversionInterval != 0 && i != conversions.Count) + catch (InvalidOperationException) { - continue; + // ignored } - double progress = (i + 1) * conversionIncrement; - _progressBar.Update(progress); - } - - return conversions; + _settingsHandler.RestoreAutoSave(); + Cursor.Current = Cursors.Default; + }); } } diff --git a/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Settings.cs b/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Settings.cs index 009066868f..177a17ea8d 100644 --- a/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Settings.cs +++ b/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.Settings.cs @@ -80,6 +80,13 @@ public override List GetSettings() "Include the internal properties that reflect option types. Can be useful for downstream data analysis." }, new CheckBoxSetting + { + Slug = "exclude-properties", + Name = "Exclude Properties", + IsChecked = false, + Description = "Excludes Properties from the commit. Can reduce payloads for large models." + }, + new CheckBoxSetting { Slug = "internal-property-names", Name = "Internal Property Names", @@ -91,7 +98,7 @@ public override List GetSettings() { Slug = "coalesce-data", Name = "Coalesce Data from First Object to Geometry", - IsChecked = false, + IsChecked = true, Description = "All properties from the Geometry up the tree to the next First Object will be coalesced into the Geometry Node." } diff --git a/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.cs b/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.cs index 93dbe93c60..c73f1219a8 100644 --- a/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.cs +++ b/ConnectorNavisworks/ConnectorNavisworks/Bindings/ConnectorNavisworksBindings.cs @@ -1,21 +1,13 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using System.Windows.Forms; using Autodesk.Navisworks.Api; using DesktopUI2; using DesktopUI2.Models; -using DesktopUI2.Models.Settings; -using DesktopUI2.ViewModels; using Speckle.ConnectorNavisworks.NavisworksOptions; -using Speckle.ConnectorNavisworks.Other; using Speckle.Core.Kits; -using Speckle.Core.Logging; using Speckle.Core.Models; using static Speckle.ConnectorNavisworks.Other.SpeckleNavisworksUtilities; -using Application = Autodesk.Navisworks.Api.Application; -using Cursor = System.Windows.Forms.Cursor; using MenuItem = DesktopUI2.Models.MenuItem; namespace Speckle.ConnectorNavisworks.Bindings; @@ -23,7 +15,7 @@ namespace Speckle.ConnectorNavisworks.Bindings; public partial class ConnectorBindingsNavisworks : ConnectorBindings { // Much of the interaction in Navisworks is through the ActiveDocument API - private static Document s_doc; + private static Document s_activeDoc; internal static Control Control; private static object s_cachedCommit; @@ -37,10 +29,21 @@ public partial class ConnectorBindingsNavisworks : ConnectorBindings private readonly NavisworksOptionsManager _settingsHandler; + /// + /// Gets a new instance of a commit object with initial properties. + /// + private static Collection CommitObject => + new() + { + ["units"] = GetUnits(s_activeDoc), + collectionType = "Navisworks Model", + name = s_activeDoc.Title + }; + public ConnectorBindingsNavisworks(Document navisworksActiveDocument) { - s_doc = navisworksActiveDocument; - s_doc.SelectionSets.ToSavedItemCollection(); + s_activeDoc = navisworksActiveDocument; + s_activeDoc.SelectionSets.ToSavedItemCollection(); // Sets the Main Thread Control to Invoke commands on. Control = new Control(); @@ -84,7 +87,7 @@ public override string GetDocumentId() { // TODO! // An unsaved document has no path or filename - var fileName = s_doc.CurrentFileName; + var fileName = s_activeDoc.CurrentFileName; var hash = Core.Models.Utilities.HashString(fileName, Core.Models.Utilities.HashingFunctions.MD5); return hash; } @@ -93,69 +96,4 @@ public override List GetObjectsInView() // this returns all visible doc // TODO! => throw new NotImplementedException(); - - public async Task RetryLastConversionSend() - { - if (s_doc == null) - { - return; - } - - if (CachedConvertedElements == null || s_cachedCommit == null) - { - throw new SpeckleException("Cant retry last conversion: no cached conversion or commit found."); - } - - if (s_cachedCommit is Collection commitObject) - { - // _isRetrying = true; - - var applicationProgress = Application.BeginProgress("Retrying that send to Speckle."); - _progressBar = new ProgressInvoker(applicationProgress); - _progressViewModel = new ProgressViewModel(); - - commitObject.elements = CachedConvertedElements; - - var state = s_cachedState; - - _progressBar.BeginSubOperation(0.7, "Retrying cached conversion."); - _progressBar.EndSubOperation(); - - var objectId = await SendConvertedObjectsToSpeckle(state, commitObject).ConfigureAwait(false); - - if (_progressViewModel.Report.OperationErrors.Count != 0) - { - ConnectorHelpers.DefaultSendErrorHandler("", _progressViewModel.Report.OperationErrors.Last()); - } - - _progressViewModel.CancellationToken.ThrowIfCancellationRequested(); - - state.Settings.Add(new CheckBoxSetting { Slug = "retrying", IsChecked = true }); - - string commitId; - try - { - commitId = await CreateCommit(state, objectId).ConfigureAwait(false); - } - finally - { - _progressBar.EndSubOperation(); - Application.EndProgress(); - Cursor.Current = Cursors.Default; - } - - state.Settings.RemoveAll(x => x.Slug == "retrying"); - - if (string.IsNullOrEmpty(commitId)) - { - return; - } - } - - // nullify the cached conversion and commit on success. - s_cachedCommit = null; - - CachedConvertedElements = null; - // _isRetrying = false; - } } diff --git a/ConnectorNavisworks/ConnectorNavisworks/ConnectorNavisworks.Shared.projitems b/ConnectorNavisworks/ConnectorNavisworks/ConnectorNavisworks.Shared.projitems index 63fcb74627..fbabbd2c39 100644 --- a/ConnectorNavisworks/ConnectorNavisworks/ConnectorNavisworks.Shared.projitems +++ b/ConnectorNavisworks/ConnectorNavisworks/ConnectorNavisworks.Shared.projitems @@ -9,6 +9,7 @@ Speckle.ConnectorNavisworks + @@ -23,6 +24,7 @@ + diff --git a/ConnectorNavisworks/ConnectorNavisworks/NavisworksOptions/Manager.cs b/ConnectorNavisworks/ConnectorNavisworks/NavisworksOptions/Manager.cs index e58f088a69..84b34c6ef8 100644 --- a/ConnectorNavisworks/ConnectorNavisworks/NavisworksOptions/Manager.cs +++ b/ConnectorNavisworks/ConnectorNavisworks/NavisworksOptions/Manager.cs @@ -1,6 +1,34 @@ +using DesktopUI2.Models; +using DesktopUI2.Models.Settings; + namespace Speckle.ConnectorNavisworks.NavisworksOptions; /// /// Manages the Setting and Getting of Internal Navisworks options. /// -public partial class NavisworksOptionsManager { } +public partial class NavisworksOptionsManager +{ + internal void InitializeManagerOptionsForSend(StreamState state) + { + var internalPropertySettings = state.Settings.Find(x => x.Slug == "internal-properties"); + var internalPropertyNames = state.Settings.Find(x => x.Slug == "internal-property-names"); + + if (internalPropertySettings != null && ((CheckBoxSetting)internalPropertySettings).IsChecked) + { + ShowInternalProperties(); + } + else + { + HideInternalProperties(); + } + + if (internalPropertyNames != null && ((CheckBoxSetting)internalPropertyNames).IsChecked) + { + UseInternalPropertyNames(); + } + else + { + MaskInternalPropertyNames(); + } + } +} diff --git a/ConnectorNavisworks/ConnectorNavisworks/NavisworksOptions/Properties.cs b/ConnectorNavisworks/ConnectorNavisworks/NavisworksOptions/Properties.cs index 81992fa473..d5abf842c0 100644 --- a/ConnectorNavisworks/ConnectorNavisworks/NavisworksOptions/Properties.cs +++ b/ConnectorNavisworks/ConnectorNavisworks/NavisworksOptions/Properties.cs @@ -78,11 +78,21 @@ private void UpdateInternalPropertyNameSetting(bool enable) => /// public void ShowInternalProperties() => UpdateInternalPropertySetting(true); + /// + /// Hides internal properties. + /// + public void HideInternalProperties() => UpdateInternalPropertySetting(false); + /// /// Uses internal property names. /// public void UseInternalPropertyNames() => UpdateInternalPropertyNameSetting(true); + /// + /// Masks internal property names. + /// + public void MaskInternalPropertyNames() => UpdateInternalPropertyNameSetting(false); + /// /// Restores the internal properties display to its original state after the send process. /// diff --git a/ConnectorNavisworks/ConnectorNavisworks/Other/Developer.cs b/ConnectorNavisworks/ConnectorNavisworks/Other/Developer.cs new file mode 100644 index 0000000000..5c4b1230d0 --- /dev/null +++ b/ConnectorNavisworks/ConnectorNavisworks/Other/Developer.cs @@ -0,0 +1,3 @@ +namespace Speckle.ConnectorNavisworks.Other; + +public class Developer { } diff --git a/ConnectorNavisworks/ConnectorNavisworks/Other/Elements.cs b/ConnectorNavisworks/ConnectorNavisworks/Other/Elements.cs index 52068a8612..8559813ed5 100644 --- a/ConnectorNavisworks/ConnectorNavisworks/Other/Elements.cs +++ b/ConnectorNavisworks/ConnectorNavisworks/Other/Elements.cs @@ -1,10 +1,10 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Dynamic; using System.Linq; using Autodesk.Navisworks.Api; -using Autodesk.Navisworks.Api.ComApi; -using Autodesk.Navisworks.Api.Interop.ComApi; +using Autodesk.Navisworks.Api.DocumentParts; using DesktopUI2.Models; using DesktopUI2.Models.Settings; using Speckle.Core.Models; @@ -15,148 +15,48 @@ public class Element { private ModelItem _modelItem; - /// - /// Initializes a new instance of the Element class with a specified pseudoId and optionally a ModelItem. - /// - /// The pseudoId used to create the Element instance. - /// Optional ModelItem used to create the Element instance. Default is null. - private Element(string pseudoId, ModelItem modelItem = null) - { - PseudoId = pseudoId; - _modelItem = modelItem; - } - - private Element(string pseudoId) - { - PseudoId = pseudoId; - } - - public Element() { } + private string _indexPath; - public ModelItem ModelItem => Resolve(); - - public string PseudoId { get; private set; } - - private static readonly int[] s_lowerBounds = new[] { 1 }; - private static readonly string[] s_separator = new[] { "." }; - - /// - /// Creates a new Element instance using a given pseudoId. - /// - /// The pseudoId used to create the Element instance. - /// A new Element instance with its pseudoId set. - public static Element GetElement(string pseudoId) => new(pseudoId); - - /// - /// Creates a new Element instance using a given ModelItem. - /// - /// The ModelItem used to create the Element instance. - /// A new Element instance with its PseudoId and _modelItem field set. - public Element GetElement(ModelItem modelItem) => new(GetPseudoId(modelItem), modelItem); + const char SEPARATOR = '/'; - /// - /// Gets the PseudoId for the given ModelItem. - /// - /// The ModelItem for which to get the PseudoId. - /// The PseudoId of the given ModelItem. If the PseudoId is not set, it is calculated and returned. - private string GetPseudoId(ModelItem modelItem) + public string IndexPath { - if (PseudoId != null) + get { - return PseudoId; - } + if (_indexPath == null && _modelItem != null) + { + _indexPath = ResolveModelItemToIndexPath(_modelItem); + } - var arrayData = ((Array)ComApiBridge.ToInwOaPath(modelItem).ArrayData).ToArray(); - PseudoId = - arrayData.Length == 0 - ? Constants.ROOT_NODE_PSEUDO_ID - : string.Join("-", arrayData.Select(x => x.ToString().PadLeft(4, '0'))); - return PseudoId; + return _indexPath; + } } - /// - /// Resolves a ModelItem from a PseudoId. - /// - /// A ModelItem that corresponds to the PseudoId, or null if the PseudoId could not be resolved. - private ModelItem Resolve() + public ModelItem ModelItem { - if (_modelItem != null) + get { - return _modelItem; - } - - if (PseudoId == Constants.ROOT_NODE_PSEUDO_ID) - { - return Application.ActiveDocument.Models.RootItems.First; - } - - if (PseudoId != null) - { - int[] pathArray; - - try + if (_modelItem == null && _indexPath != null) { - pathArray = ParsePseudoIdToPathArray(PseudoId); - } - catch (ArgumentException) - { - return null; + _modelItem = ResolveIndexPath(_indexPath); } - var oneBasedArray = ConvertTo1BasedArray(pathArray); - var protoPath = CreateProtoPath(oneBasedArray); - - _modelItem = ComApiBridge.ToModelItem(protoPath); + return _modelItem; } - - return _modelItem; } - /// - /// Parses a PseudoId into a path array. - /// - /// The PseudoId to parse. - /// An array of integers representing the path. - /// Thrown when the PseudoId is malformed. - private int[] ParsePseudoIdToPathArray(string pseudoId) => - pseudoId - .Split('-') - .Select(x => - { - if (int.TryParse(x, out var value)) - { - return value; - } - - throw new ArgumentException("malformed path pseudoId"); - }) - .ToArray(); - - /// - /// Converts a zero-based integer array into a one-based array. - /// - /// The zero-based integer array to convert. - /// A one-based array with the same elements as the input array. - private Array ConvertTo1BasedArray(int[] pathArray) + public Element(string indexPath) { - var oneBasedArray = Array.CreateInstance(typeof(int), new[] { pathArray.Length }, s_lowerBounds); - Array.Copy(pathArray, 0, oneBasedArray, 1, pathArray.Length); - return oneBasedArray; + _indexPath = indexPath; } - /// - /// Creates a protoPath from a one-based array. - /// - /// The one-based array to use for creating the protoPath. - /// A protoPath that corresponds to the input array. - private InwOaPath CreateProtoPath(Array oneBasedArray) + public Element(ModelItem modelItem) { - var oState = ComApiBridge.State; - var protoPath = (InwOaPath)oState.ObjectFactory(nwEObjectType.eObjectType_nwOaPath); - protoPath.ArrayData = oneBasedArray; - return protoPath; + _modelItem = modelItem; } + private static readonly string[] s_separator = { SEPARATOR.ToString() }; + /// /// Generates a descriptor for the given model item. /// @@ -185,92 +85,148 @@ private static string ElementDescriptor(ModelItem modelItem) /// /// /// - /// The input dictionary to be converted into a hierarchical structure. /// An IEnumerable of root nodes representing the hierarchical structure. - public static IEnumerable BuildNestedObjectHierarchy( + public static IEnumerable BuildNestedObjectHierarchyInParallel( Dictionary> converted, - StreamState streamState + StreamState streamState, + ProgressInvoker progressBar ) { - var convertedDictionary = converted.ToDictionary(x => x.Key.PseudoId, x => (x.Value.Item2, x.Key)); + var convertedDictionary = converted.ToDictionary(x => x.Key.IndexPath, x => (x.Value.Item2, x.Key)); - // This dictionary is for looking up parents quickly - Dictionary lookupDictionary = new(); + ConcurrentDictionary lookupDictionary = new(); + ConcurrentDictionary potentialRootNodes = new(); - // This dictionary will hold potential root nodes until we confirm they are roots - Dictionary potentialRootNodes = new(); + int totalCount = convertedDictionary.Count; + const int DEFAULT_UPDATE_INTERVAL = 1000; - // First pass: Create lookup dictionary and identify potential root nodes - foreach (var pair in convertedDictionary) + List rootNodes = new(); // Initialize rootNodes here + + try { - var element = pair.Value.Key; - var pseudoId = element.PseudoId; - var baseNode = pair.Value.Item1; - var modelItem = element.ModelItem; - var type = baseNode?.GetType().Name; + // First pass: Populate lookup dictionary and identify potential root nodes + ExecuteWithProgress( + totalCount, + progressBar, + "Identifying roots", + i => + { + var pair = convertedDictionary.ElementAt(i); + var element = pair.Value.Key; + var indexPath = element.IndexPath; + var baseNode = pair.Value.Item1; + var modelItem = element.ModelItem; + var type = baseNode?.GetType().Name; + + if (baseNode == null) + { + return; + } - if (baseNode == null) - { - continue; - } + if ( + streamState.Settings.Find(x => x.Slug == "coalesce-data") is CheckBoxSetting { IsChecked: true } + && type == "GeometryNode" + ) + { + AddPropertyStackToGeometryNode(converted, modelItem, baseNode); + } - // Geometry Nodes can add all the properties to the FirstObject classification - this will help with the selection logic - if ( - streamState.Settings.Find(x => x.Slug == "coalesce-data") is CheckBoxSetting { IsChecked: true } - && type == "GeometryNode" - ) - { - AddPropertyStackToGeometryNode(converted, modelItem, baseNode); - } + string[] parts = indexPath.Split(SEPARATOR); + string parentKey = string.Join(SEPARATOR.ToString(), parts.Take(parts.Length - 1)); - string[] parts = pseudoId.Split('-'); - string parentKey = string.Join("-", parts.Take(parts.Length - 1)); + lookupDictionary.TryAdd(indexPath, baseNode); - lookupDictionary.Add(pseudoId, baseNode); + if (!lookupDictionary.ContainsKey(parentKey)) + { + potentialRootNodes.TryAdd(indexPath, baseNode); + } + } + ); + + // Second pass: Attach child nodes to parents and confirm root nodes + ExecuteWithProgress( + lookupDictionary.Count, + progressBar, + "Reuniting children with parents", + i => + { + var pair = lookupDictionary.ElementAt(i); + string key = pair.Key; + Base value = pair.Value; - if (!lookupDictionary.ContainsKey(parentKey)) - { - potentialRootNodes.Add(pseudoId, baseNode); - } - } + string[] parts = key.Split(SEPARATOR); + string parentKey = string.Join(SEPARATOR.ToString(), parts.Take(parts.Length - 1)); - // Second pass: Attach child nodes to their parents, and confirm root nodes - foreach (var pair in lookupDictionary) - { - string key = pair.Key; - Base value = pair.Value; + if (!lookupDictionary.TryGetValue(parentKey, out Base parentValue) || parentValue is not Collection parent) + { + return; + } - string[] parts = key.Split('-'); - string parentKey = string.Join("-", parts.Take(parts.Length - 1)); + parent.elements.Add(value); - if (!lookupDictionary.TryGetValue(parentKey, out Base value1)) - { - continue; - } + potentialRootNodes.TryRemove(key, out _); + } + ); - if (value1 is Collection parent) - { - parent.elements ??= new List(); - if (value != null) + rootNodes = potentialRootNodes.Values.ToList(); + + // Prune empty collections + ExecuteWithProgress( + rootNodes.Count, + progressBar, + "Recycling empties", + i => { - parent.elements.Add(value); + var rootNode = rootNodes[i]; + if (rootNode != null) + { + PruneEmptyCollections(rootNode); + } } - } + ); - // This node has a parent, so it's not a root node - potentialRootNodes.Remove(key); + rootNodes.RemoveAll(node => node is Collection { elements: null }); + } + catch (OperationCanceledException) + { + // Handle cancellation if needed + } + catch (Exception ex) + { + throw new InvalidOperationException("An error occurred during the operation.", ex); } - List rootNodes = potentialRootNodes.Values.ToList(); + return rootNodes; + } + + private static void ExecuteWithProgress( + int totalCount, + ProgressInvoker progressBar, + string operationName, + Action action + ) + { + int progressCounter = 0; + const int DEFAULT_UPDATE_INTERVAL = 1000; + + progressBar.BeginSubOperation(0.2, operationName); + progressBar.Update(0); - foreach (var rootNode in rootNodes.Where(rootNode => rootNode != null)) + for (int i = 0; i < totalCount; i++) { - PruneEmptyCollections(rootNode); - } + action(i); + + progressCounter++; + if (progressCounter % DEFAULT_UPDATE_INTERVAL != 0 && progressCounter != totalCount) + { + continue; + } - rootNodes.RemoveAll(node => node is Collection { elements: null }); + double progressValue = Math.Min((double)progressCounter / totalCount, 1.0); + progressBar.Update(progressValue); + } - return rootNodes; + progressBar.EndSubOperation(); } /// @@ -285,27 +241,41 @@ private static void AddPropertyStackToGeometryNode( DynamicBase baseNode ) { - var firstObjectAncestor = modelItem.FindFirstObjectAncestor(); - var ancestors = modelItem.Ancestors; + if (modelItem == null || baseNode == null || converted == null) + { + throw new ArgumentNullException("modelItem, baseNode, and converted cannot be null."); + } + + var firstObjectAncestor = + modelItem.FindFirstObjectAncestor() ?? throw new InvalidOperationException("firstObjectAncestor is null."); + var ancestors = modelItem.Ancestors ?? throw new InvalidOperationException("ancestors is null."); var trimmedAncestors = ancestors.TakeWhile(ancestor => ancestor != firstObjectAncestor).Append(firstObjectAncestor); - var propertyStack = trimmedAncestors + var filtered = trimmedAncestors .Select(item => converted.FirstOrDefault(keyValuePair => Equals(keyValuePair.Key.ModelItem, item))) + .Where(kVp => kVp.Key != null) // Filter out null keys .Select(kVp => kVp.Value.Item2["properties"] as Base) + .Where(propertySet => propertySet != null); // Filter out null property sets + + var categoryProperties = filtered.SelectMany( + propertySet => propertySet.GetMembers().Where(member => member.Value is Base), + (_, propertyCategory) => + new { Category = propertyCategory.Key, Properties = ((Base)propertyCategory.Value).GetMembers() } + ); + + var properties = categoryProperties .SelectMany( - propertySet => propertySet?.GetMembers().Where(member => member.Value is Base), - (_, propertyCategory) => - new { Category = propertyCategory.Key, Properties = ((Base)propertyCategory.Value).GetMembers() } - ) - .SelectMany( - categoryProperties => categoryProperties.Properties, - (categoryProperties, property) => - new { ConcatenatedKey = $"{categoryProperties.Category}--{property.Key}", property.Value } + cp => cp.Properties, + (cp, property) => new { ConcatenatedKey = $"{cp.Category}--{property.Key}", property.Value } ) - .Where(property => property.Value != null && !string.IsNullOrEmpty(property.Value.ToString())) + .Where(property => property.Value != null && !string.IsNullOrEmpty(property.Value.ToString())); + + var groupedProperties = properties .GroupBy(property => property.ConcatenatedKey) .Where(group => group.Select(item => item.Value).Distinct().Count() == 1) - .ToDictionary(group => group.Key, group => group.First().Value) + .ToDictionary(group => group.Key, group => group.First().Value); + + var formattedProperties = groupedProperties .Select( kVp => new @@ -315,11 +285,17 @@ DynamicBase baseNode kVp.Value } ) - .Where(item => item.Category != "Internal") + .Where(item => item.Category != "Internal"); + + var propertyStack = formattedProperties .GroupBy(item => item.Category) .ToDictionary(group => group.Key, group => group.ToDictionary(item => item.Property, item => item.Value)); - var propertiesBase = (Base)baseNode["properties"]; + if (baseNode["properties"] is not Base propertiesBase) + { + propertiesBase = new Base(); + baseNode["properties"] = propertiesBase; + } var baseProperties = propertiesBase.GetMembers().Where(item => item.Value is Base).ToList(); @@ -371,8 +347,6 @@ DynamicBase baseNode propertiesBase[stackProperty.Key] = newPropertyCategory; } } - - // baseNode["property-stack"] = propertyStack; } /// @@ -386,7 +360,7 @@ private static void PruneEmptyCollections(IDynamicMetaObjectProvider node) return; } - if (collection.elements == null) + if (collection.elements.Count == 0) { return; } @@ -395,10 +369,7 @@ private static void PruneEmptyCollections(IDynamicMetaObjectProvider node) { PruneEmptyCollections(collection.elements[i]); - if ( - collection.elements[i] is Collection childCollection - && (childCollection.elements == null || childCollection.elements.Count == 0) - ) + if (collection.elements[i] is Collection { elements.Count: 0 }) { collection.elements.RemoveAt(i); } @@ -406,7 +377,40 @@ collection.elements[i] is Collection childCollection if (collection.elements.Count == 0) { - collection.elements = null; + collection.elements = null!; } } + + public static ModelItem ResolveIndexPath(string indexPath) + { + var indexPathParts = indexPath.Split(SEPARATOR); + + var modelIndex = int.Parse(indexPathParts[0]); + var pathId = string.Join(SEPARATOR.ToString(), indexPathParts.Skip(1)); + + // assign the first part of indexPathParts to modelIndex and parse it to int, the second part to pathId string + ModelItemPathId modelItemPathId = new() { ModelIndex = modelIndex, PathId = pathId }; + + var modelItem = Application.ActiveDocument.Models.ResolvePathId(modelItemPathId); + return modelItem; + } + + public static string ResolveModelItemToIndexPath(ModelItem modelItem) + { + var modelItemPathId = Application.ActiveDocument.Models.CreatePathId(modelItem); + + return modelItemPathId.PathId == "a" + ? $"{modelItemPathId.ModelIndex}" + : $"{modelItemPathId.ModelIndex}{SEPARATOR}{modelItemPathId.PathId}"; + } + + /// + /// Checks is the Element is hidden or if any of its ancestors is hidden + /// + /// + /// + internal static bool IsElementVisible(ModelItem element) => + // Hidden status is stored at the earliest node in the hierarchy + // All the tree path nodes need to not be Hidden + element.AncestorsAndSelf.All(x => x.IsHidden != true); } diff --git a/ConnectorNavisworks/ConnectorNavisworks/Other/Invokers.cs b/ConnectorNavisworks/ConnectorNavisworks/Other/Invokers.cs index 88f72466ea..aeff0ca483 100644 --- a/ConnectorNavisworks/ConnectorNavisworks/Other/Invokers.cs +++ b/ConnectorNavisworks/ConnectorNavisworks/Other/Invokers.cs @@ -66,7 +66,6 @@ public sealed class ProgressInvoker : Invoker /// Provides an instance of the Progress class which starts reporting of progress /// of an operation, typically displaying a progress bar or dialog to the end user. /// - /// The title of the progress operation. Defaults to an empty string. public ProgressInvoker(Progress applicationProgress) { _progressBar = applicationProgress; diff --git a/ConnectorNavisworks/ConnectorNavisworks/Other/SelectionBuilder.cs b/ConnectorNavisworks/ConnectorNavisworks/Other/SelectionBuilder.cs index b1dcf4dfb6..80fcc3b514 100644 --- a/ConnectorNavisworks/ConnectorNavisworks/Other/SelectionBuilder.cs +++ b/ConnectorNavisworks/ConnectorNavisworks/Other/SelectionBuilder.cs @@ -1,6 +1,9 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using Autodesk.Navisworks.Api; using Autodesk.Navisworks.Gui; using DesktopUI2.Models; @@ -73,25 +76,24 @@ private HashSet GetObjectsFromSelection() // Selections are modelItem pseudo-ids. var selection = _filter.Selection; - var count = selection.Count; - var progressIncrement = 1.0 / count != 0 ? count : 1.0; - // Begin the progress sub-operation for getting objects from selection - ProgressBar.BeginSubOperation(0.05, "Rolling up the sleeves... Time to handpick your favorite data items!"); - - // Iterate over the selection and retrieve the corresponding model items - for (var i = 0; i < count; i++) - { - _progressViewModel.CancellationToken.ThrowIfCancellationRequested(); - ProgressBar.Update(i * progressIncrement); + ProgressLooper( + "Rolling up the sleeves... Time to handpick your favourite data items!", + (index) => + { + if (index >= selection.Count) + { + return false; + } - var pseudoId = selection[i]; - var element = Element.GetElement(pseudoId); - _uniqueModelItems.Add(element.ModelItem); - } + _progressViewModel.CancellationToken.ThrowIfCancellationRequested(); + _uniqueModelItems.Add(Element.ResolveIndexPath(selection[index])); - // End the progress sub-operation - ProgressBar.EndSubOperation(); + return true; + }, + 0.05, // Fraction of remaining time + totalCount: selection.Count // Pass the total count if known, else pass null + ); return _uniqueModelItems; } @@ -99,23 +101,18 @@ private HashSet GetObjectsFromSelection() /// /// Retrieves the model items from the saved viewpoint. /// - private IEnumerable GetObjectsFromSavedViewpoint() + private HashSet GetObjectsFromSavedViewpoint() { _uniqueModelItems.Clear(); - // Begin the progress sub-operation for getting objects from selection - ProgressBar.BeginSubOperation(0.05, "Checking the Canvas... Looking Closely!"); - // Get the selection from the filter var selection = _filter.Selection.FirstOrDefault(); if (string.IsNullOrEmpty(selection)) { - return Enumerable.Empty(); + return new HashSet(); } // Resolve the saved viewpoint based on the selection - // Makes the view active on the main thread. - var success = false; new Invoker().Invoke(() => @@ -132,26 +129,34 @@ private IEnumerable GetObjectsFromSavedViewpoint() if (!success) { - return Enumerable.Empty(); + return new HashSet(); } var models = Application.ActiveDocument.Models; Application.ActiveDocument.CurrentSelection.Clear(); - for (var i = 0; i < models.Count; i++) - { - var model = models.ElementAt(i); - var rootItem = model.RootItem; - if (!rootItem.IsHidden) + // Use ProgressLooper to handle the looping and progress updates + ProgressLooper( + "Checking the Canvas... Looking Closely!", + (index) => { - _uniqueModelItems.Add(rootItem); - } + if (index >= models.Count) + { + return false; + } - ProgressBar.Update(i + 1 / (double)models.Count); - } + var model = models[index]; + var rootItem = model.RootItem; + if (!rootItem.IsHidden) + { + _uniqueModelItems.Add(rootItem); + } - // End the progress sub-operation - ProgressBar.EndSubOperation(); + return true; + }, + 0.05, // Fraction of remaining time + models.Count // Pass the total count if known, else pass null + ); return _uniqueModelItems; } @@ -204,7 +209,6 @@ public SavedViewpoint ResolveSavedViewpoint(string savedViewReference) /// /// Resolves the SavedViewpoint based on the provided viewpoint match and saved view reference. /// - /// The dynamic object representing the viewpoint match. /// The saved view reference to resolve. /// The resolved SavedViewpoint. private SavedViewpoint ResolveSavedViewpointMatch(string savedViewReference) @@ -290,11 +294,14 @@ private HashSet GetObjectsFromSavedSets() { if (item.HasExplicitModelItems) { + // This is for saved selections. If the models are as were when selection was made, then the static results are all valid. _uniqueModelItems.AddRange(item.ExplicitModelItems); } - else if (item.HasSearch) + else if (item.HasSearch) // This is for saved searches. The results are dynamic and need to be resolved. { - _uniqueModelItems.AddRange(item.Search.FindAll(Application.ActiveDocument, false)); + // This is for saved searches. The results are dynamic and need to be resolved. + var foundModelItems = item.Search.FindAll(Application.ActiveDocument, false); + _uniqueModelItems.AddRange(foundModelItems); } } @@ -331,7 +338,7 @@ public void PopulateHierarchyAndOmitHidden() || _uniqueModelItems.Contains(firstObjectAncestor) ) { - return Enumerable.Empty(); + return new HashSet(); } var trimmedAncestors = targetFirstObjectChild.Ancestors @@ -350,29 +357,56 @@ public void PopulateHierarchyAndOmitHidden() var allAncestors = startNodes.SelectMany(e => e.Ancestors).Distinct().ToList(); ProgressLooper( - allAncestors.Count, "Brb, time traveling to find your data's great-grandparents...", i => { - _uniqueModelItems.Add(allAncestors.ElementAt(i)); + _uniqueModelItems.Add(allAncestors[i]); return true; }, - 0.05 + 0.05, + allAncestors.Count ); } _visited = new HashSet(); _descendantProgress = 0; - var allDescendants = startNodes.SelectMany(e => e.Descendants).Distinct().Count(); - ProgressBar.BeginSubOperation(0.1, "Validating descendants..."); + HashSet distinctDescendants = DistinctDescendants(startNodes); + var allDescendants = distinctDescendants.Count; + + ProgressLooper( + "Validating descendants...", + i => + { + _progressViewModel.CancellationToken.ThrowIfCancellationRequested(); + + TraverseDescendants( + startNodes[i], + allDescendants, + new Progress(value => + { + ProgressBar.Update(value); + }) + ); + + return true; + }, + 0.1, + startNodes.Count + ); + } + + private static HashSet DistinctDescendants(List startNodes) + { + var distinctDescendants = new HashSet(); foreach (var node in startNodes) { - TraverseDescendants(node, allDescendants); + var nodeDescendants = node.Descendants.ToList(); + distinctDescendants.UnionWith(nodeDescendants); } - ProgressBar.EndSubOperation(); + return distinctDescendants; } /// @@ -380,11 +414,13 @@ public void PopulateHierarchyAndOmitHidden() /// /// The starting node for traversal. /// The total number of descendants. - private void TraverseDescendants(ModelItem startNode, int totalDescendants) + private void TraverseDescendants(ModelItem startNode, int totalDescendants, IProgress progress) { - var descendantInterval = Math.Max(totalDescendants / 100.0, 1); + var descendantInterval = Math.Max(totalDescendants / 100.0, 1); // Update progress every 1% var validDescendants = new HashSet(); - int lastPercentile = 0; + + int updateCounter = 0; // Counter to track when to update the UI + int lastUpdate = 0; // Track the last update to avoid frequent updates Stack stack = new(); stack.Push(startNode); @@ -400,6 +436,7 @@ private void TraverseDescendants(ModelItem startNode, int totalDescendants) ModelItem currentNode = stack.Pop(); + // ReSharper disable once CanSimplifySetAddingWithSingleCall if (_visited.Contains(currentNode)) { continue; @@ -407,84 +444,196 @@ private void TraverseDescendants(ModelItem startNode, int totalDescendants) _visited.Add(currentNode); - if (currentNode.IsHidden) + bool isVisible = IsVisibleCached(currentNode); + if (!isVisible) { + // If node is hidden, skip processing it and all its descendants var descendantsCount = currentNode.Descendants.Count(); - _descendantProgress += descendantsCount + 1; - } - else - { - validDescendants.Add(currentNode); - _descendantProgress++; + Interlocked.Add(ref _descendantProgress, descendantsCount + 1); + continue; } + validDescendants.Add(currentNode); // currentNode is visible, process it + Interlocked.Increment(ref _descendantProgress); + if (currentNode.Children.Any()) { - foreach (var child in currentNode.Children.Where(e => !e.IsHidden)) + // Add visible children to the stack + + var childrenToProcess = new ConcurrentBag(); + + Parallel.ForEach( + currentNode.Children, + child => + { + if (_visited.Contains(child)) + { + return; + } + + if (IsVisibleCached(child)) + { + childrenToProcess.Add(child); + } + else + { + // If child is hidden, skip processing it and all its descendants + int descendantsCount = child.Descendants.Count(); + Interlocked.Add(ref _descendantProgress, descendantsCount + 1); + } + } + ); + + foreach (ModelItem child in childrenToProcess) { stack.Push(child); } } - _uniqueModelItems.AddRange(validDescendants); + lock (_uniqueModelItems) + { + _uniqueModelItems.UnionWith(validDescendants); + } + validDescendants.Clear(); + + updateCounter++; - int currentPercentile = (int)(_descendantProgress / descendantInterval); - if (currentPercentile <= lastPercentile) + if (!(updateCounter >= descendantInterval) || lastUpdate >= _descendantProgress) { continue; } - double progress = _descendantProgress / (double)totalDescendants; - ProgressBar.Update(progress); - lastPercentile = currentPercentile; + double progressValue = _descendantProgress / (double)totalDescendants; + progress.Report(progressValue); + lastUpdate = _descendantProgress; + updateCounter = 0; } } + // Cache to store visibility status of ModelItems + private readonly Dictionary _visibilityCache = new(); + + /// + /// Checks if a ModelItem is visible, with caching to avoid redundant calculations. + /// + /// The ModelItem to check visibility for. + /// True if the item is visible, false otherwise. + private bool IsVisibleCached(ModelItem item) + { + // Check if the result is already in the cache + if (_visibilityCache.TryGetValue(item, out bool isVisible)) + { + return isVisible; + } + // Calculate visibility if not in cache + isVisible = CalculateVisibility(item); + _visibilityCache[item] = isVisible; + return isVisible; + } + + /// + /// Placeholder for if the default visibility determination logic need augmenting. + /// + /// The ModelItem to check. + /// True if visible, false otherwise. + private static bool CalculateVisibility(ModelItem item) => !item.IsHidden; + /// /// Executes a given function while updating a progress bar. /// - /// The total number of iterations. /// The name of the operation. /// The function to execute on each iteration. /// The fraction of remaining time for the operation (optional). - private void ProgressLooper( - int totalCount, + /// The total number of iterations, if known. + public void ProgressLooper( string operationName, Func fn, - double fractionOfRemainingTime = 0 + double fractionOfRemainingTime = 0, + int? totalCount = null ) { - var increment = 1.0 / totalCount != 0 ? 1.0 / totalCount : 1.0; - var updateInterval = Math.Max(totalCount / 100, 1); + const int DEFAULT_UPDATE_INTERVAL = 1000; + const double DEFAULT_PROGRESS_INCREMENT = 0.01; + ProgressBar.BeginSubOperation(fractionOfRemainingTime, operationName); ProgressBar.Update(0); - for (int i = 0; i < totalCount; i++) + try { - if (ProgressBar.IsCanceled) + int i = 0; + double progress = 0; + double increment; + int updateInterval; + + if (totalCount.HasValue) { - _progressViewModel.CancellationTokenSource.Cancel(); + increment = 1.0 / totalCount.Value; + updateInterval = Math.Max(totalCount.Value / 100, 1); } - - _progressViewModel.CancellationToken.ThrowIfCancellationRequested(); - - bool shouldContinue = fn(i); - - if (!shouldContinue) + else { - break; + increment = DEFAULT_PROGRESS_INCREMENT; + updateInterval = DEFAULT_UPDATE_INTERVAL; } - if (i % updateInterval != 0 && i != totalCount) + while (true) { - continue; + if (ProgressBar.IsCanceled) + { + _progressViewModel.CancellationTokenSource.Cancel(); + break; + } + + _progressViewModel.CancellationToken.ThrowIfCancellationRequested(); + + if (!fn(i)) + { + break; + } + + var test = fn(i); + + i++; + + if (totalCount.HasValue) + { + progress = Math.Min((double)i / totalCount.Value, 1.0); + if (i % updateInterval == 0 || i == totalCount.Value) + { + ProgressBar.Update(progress); + } + + if (i >= totalCount.Value) + { + break; + } + } + else + { + if (i % updateInterval != 0) + { + continue; + } + + progress = Math.Min(progress + increment, 1.0); + ProgressBar.Update(progress); + } } - double progress = (i + 1) * increment; - ProgressBar.Update(progress); + ProgressBar.Update(1.0); + } + catch (OperationCanceledException) + { + // Handle cancellation if needed + } + catch (Exception ex) + { + throw new InvalidOperationException("An error occurred during the operation.", ex); + } + finally + { + ProgressBar.EndSubOperation(); } - - ProgressBar.EndSubOperation(); } /// diff --git a/Objects/Converters/ConverterNavisworks/ConverterNavisworks/ConverterNavisworks.Properties.cs b/Objects/Converters/ConverterNavisworks/ConverterNavisworks/ConverterNavisworks.Properties.cs index 34164afc93..09e1cf4b1d 100644 --- a/Objects/Converters/ConverterNavisworks/ConverterNavisworks/ConverterNavisworks.Properties.cs +++ b/Objects/Converters/ConverterNavisworks/ConverterNavisworks/ConverterNavisworks.Properties.cs @@ -78,8 +78,6 @@ Base propertyCategoryBase var useInternalNames = UseInternalPropertyNames; UpdatePropertyCategoryBase(propertyCategoryBase, useInternalNames ? internalName : propertyName, propertyValue); - - propertyCategoryBase.applicationId = propertyCategory.CombinedName.ToString(); } private static string GetSanitizedPropertyName(string displayName) @@ -195,40 +193,57 @@ private static void UpdatePropertyCategoryBase(Base propertyCategoryBase, string } } + /// + /// Adds properties of a given ModelItem to a Base object. + /// + /// The ModelItem whose properties are to be added. + /// The Base object to which the properties are to be added. private static void AddItemProperties(ModelItem element, Base @base) { @base["class"] = element.ClassName; - bool properties = ShouldIncludeProperties(); - + if (ExcludeProperties) + { + return; + } // Cascade through the Property Sets - @base["properties"] = properties ? GetPropertiesBase(element) : new Base(); + @base["properties"] = GetPropertiesBase(element); // If the node is a Model if (element.HasModel) { - ((Base)@base["properties"])["Model"] = GetModelProperties(element.Model); + (((Base)@base["properties"])!)["Model"] = GetModelProperties(element.Model); } // Internal Properties - AddInternalProperties(element, (Base)@base["properties"]); + if (IncludeInternalProperties) + { + AddInternalProperties(element, (Base)@base["properties"]); + } } - private static bool ShouldIncludeProperties() => - !bool.TryParse(Settings.FirstOrDefault(x => x.Key == "include-properties").Value, out bool result) || result; - + /// + /// Adds internal properties of a given ModelItem to a Base object. + /// + /// The ModelItem whose properties are to be added. + /// The Base object to which the properties are to be added. private static void AddInternalProperties(ModelItem element, Base propertiesBase) { Base internals = (Base)propertiesBase["Internal"] ?? new Base(); - internals["ClassDisplayName"] = element.ClassDisplayName ?? internals["ClassDisplayName"]; - internals["ClassName"] = element.ClassName ?? internals["ClassName"]; - internals["DisplayName"] = element.DisplayName ?? internals["DisplayName"]; - internals["InstanceGuid"] = - element.InstanceGuid.ToByteArray().Select(x => (int)x).Sum() > 0 ? element.InstanceGuid : null; - internals["Source"] = element.Model?.SourceFileName ?? internals["Source"]; - internals["Source Guid"] = element.Model?.SourceGuid ?? internals["Source Guid"]; - internals["NodeType"] = element.IsCollection + AddPropertyIfNotNullOrEmpty(internals, "ClassDisplayName", element.ClassDisplayName); + AddPropertyIfNotNullOrEmpty(internals, "ClassName", element.ClassName); + AddPropertyIfNotNullOrEmpty(internals, "DisplayName", element.DisplayName); + + if (element.InstanceGuid.ToByteArray().Select(x => (int)x).Sum() > 0) + { + internals["InstanceGuid"] = element.InstanceGuid; + } + + AddPropertyIfNotNullOrEmpty(internals, "Source", element.Model?.SourceFileName); + AddPropertyIfNotNullOrEmpty(internals, "Source Guid", element.Model?.SourceGuid); + + string nodeType = element.IsCollection ? "Collection" : element.IsComposite ? "Composite Object" @@ -238,9 +253,32 @@ private static void AddInternalProperties(ModelItem element, Base propertiesBase ? "Layer" : null; + AddPropertyIfNotNullOrEmpty(internals, "NodeType", nodeType); + propertiesBase["Internal"] = internals; } + /// + /// Adds a property to a Base object if the value is not null or empty. + /// + /// The Base object to which the property is to be added. + /// The name of the property to add. + /// The value of the property. + private static void AddPropertyIfNotNullOrEmpty(Base baseObject, string propertyName, object value) + { + if (value is string stringValue) + { + if (!string.IsNullOrEmpty(stringValue)) + { + baseObject[propertyName] = value; + } + } + else if (value != null) + { + baseObject[propertyName] = value; + } + } + private static Base GetModelProperties(Model elementModel) { Base model = diff --git a/Objects/Converters/ConverterNavisworks/ConverterNavisworks/ConverterNavisworks.Settings.cs b/Objects/Converters/ConverterNavisworks/ConverterNavisworks/ConverterNavisworks.Settings.cs index 780381fe2f..931a4f1f3b 100644 --- a/Objects/Converters/ConverterNavisworks/ConverterNavisworks/ConverterNavisworks.Settings.cs +++ b/Objects/Converters/ConverterNavisworks/ConverterNavisworks/ConverterNavisworks.Settings.cs @@ -70,6 +70,32 @@ private static Units CoordinateUnits } } + private static bool ExcludeProperties + { + get + { + if (!Settings.TryGetValue("exclude-properties", out string shouldExcludeProperties)) + { + return false; + } + + return shouldExcludeProperties == "True"; + } + } + + private static bool IncludeInternalProperties + { + get + { + if (!Settings.TryGetValue("internal-properties", out string shouldIncludeInternalProperties)) + { + return false; + } + + return shouldIncludeInternalProperties == "True"; + } + } + private static bool UseInternalPropertyNames { get From c22a3fad54f500b8044a692374de2818ba2427d9 Mon Sep 17 00:00:00 2001 From: Victor Wanderley Barbosa <61347148+vwnd@users.noreply.github.com> Date: Thu, 18 Jul 2024 10:22:08 +0200 Subject: [PATCH 24/52] fix: if headless use original material (#3511) * fix: if headless use original material * refactor: no need to pass doc --- .../ConverterRhinoGhShared/ConverterRhinoGh.Other.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/Converters/ConverterRhinoGh/ConverterRhinoGhShared/ConverterRhinoGh.Other.cs b/Objects/Converters/ConverterRhinoGh/ConverterRhinoGhShared/ConverterRhinoGh.Other.cs index 934809c384..bac2179410 100644 --- a/Objects/Converters/ConverterRhinoGh/ConverterRhinoGhShared/ConverterRhinoGh.Other.cs +++ b/Objects/Converters/ConverterRhinoGh/ConverterRhinoGhShared/ConverterRhinoGh.Other.cs @@ -218,7 +218,7 @@ public Other.RenderMaterial RenderMaterialToSpeckle(RH.Material material) } #else RH.Material matToUse = material; - if (!material.IsPhysicallyBased) + if (!material.IsPhysicallyBased && !Doc.IsHeadless) { matToUse = new RH.Material(); matToUse.CopyFrom(material); From 05cf31f18c440f91480c179110e27ee065945636 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Steinhagen?= Date: Thu, 18 Jul 2024 10:33:09 +0200 Subject: [PATCH 25/52] CNX-9877-units-fix (#3577) * Preprocessor directives for SAP and ETABS * Formatting and commenting --- .../ConverterCSIShared/ConverterCSIUtils.cs | 31 +++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/Objects/Converters/ConverterCSI/ConverterCSIShared/ConverterCSIUtils.cs b/Objects/Converters/ConverterCSI/ConverterCSIShared/ConverterCSIUtils.cs index 61dd48d6f6..49ef57d0ea 100644 --- a/Objects/Converters/ConverterCSI/ConverterCSIShared/ConverterCSIUtils.cs +++ b/Objects/Converters/ConverterCSI/ConverterCSIShared/ConverterCSIUtils.cs @@ -27,9 +27,25 @@ public string ModelUnits() return _modelUnits; } +#if ETABS || SAFE + + _modelUnits = GetModelUnitsFromETABS(); + +#else + + _modelUnits = GetModelUnitsFromSAP(); + +#endif + + return _modelUnits; + } + + public string GetModelUnitsFromETABS() + { eForce forceUnits = eForce.NotApplicable; eLength lengthUnits = eLength.NotApplicable; eTemperature temperatureUnits = eTemperature.NotApplicable; + // GetPresentUnits_2() works for ETABS and SAFE _ = Model.GetPresentUnits_2(ref forceUnits, ref lengthUnits, ref temperatureUnits); if (lengthUnits == eLength.NotApplicable) @@ -37,8 +53,19 @@ public string ModelUnits() throw new SpeckleException("Unable to retreive valid length units from the ETABS document"); } - _modelUnits = lengthUnits.ToString(); - return _modelUnits; + return lengthUnits.ToString(); + } + + public string GetModelUnitsFromSAP() + { + // GetPresentUnits() works for SAP 2000 and CSIBridge + var units = Model.GetPresentUnits(); + if (units != 0) + { + string[] unitsCat = units.ToString().Split('_'); + return unitsCat[1]; + } + throw new SpeckleException("Unable to retreive valid length units from the SAP2000 document"); } public double ScaleToNative(double value, string units) From b1ad169e2ceee59c6aa3b97f26383db0678390d4 Mon Sep 17 00:00:00 2001 From: Iain Sproat <68657+iainsproat@users.noreply.github.com> Date: Thu, 18 Jul 2024 10:58:21 +0100 Subject: [PATCH 26/52] feat(logging): adds correlation ID to http logs to assist debugging (#3579) Co-authored-by: JR-Morgan <45512892+JR-Morgan@users.noreply.github.com> --- Core/Core/Helpers/Http.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Core/Core/Helpers/Http.cs b/Core/Core/Helpers/Http.cs index 92f431aad5..e3562bc05b 100644 --- a/Core/Core/Helpers/Http.cs +++ b/Core/Core/Helpers/Http.cs @@ -225,14 +225,15 @@ CancellationToken cancellationToken SpeckleLog.Logger .ForContext("ExceptionType", policyResult.FinalException?.GetType()) .Information( - "Execution of http request to {httpScheme}://{hostUrl}{relativeUrl} {resultStatus} with {httpStatusCode} after {elapsed} seconds and {retryCount} retries", + "Execution of http request to {httpScheme}://{hostUrl}{relativeUrl} {resultStatus} with {httpStatusCode} after {elapsed} seconds and {retryCount} retries. Request correlation ID: {correlationId}", request.RequestUri.Scheme, request.RequestUri.Host, request.RequestUri.PathAndQuery, status, policyResult.Result?.StatusCode, timer.Elapsed.TotalSeconds, - retryCount ?? 0 + retryCount ?? 0, + context.CorrelationId.ToString() ); if (policyResult.Outcome == OutcomeType.Successful) { From 5c5489e7c81efce21d61d604e37d91c5c24f6ddb Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Thu, 18 Jul 2024 15:55:41 +0100 Subject: [PATCH 27/52] Cnx 9776 rhino 8 issue with non concurrent collections (#3582) * Use concurrent dictionary with GetOrAdd * fmt --- Core/Core/Models/DynamicBase.cs | 63 +++++++++++++++------------------ 1 file changed, 29 insertions(+), 34 deletions(-) diff --git a/Core/Core/Models/DynamicBase.cs b/Core/Core/Models/DynamicBase.cs index 9c024df836..8ef2314c20 100644 --- a/Core/Core/Models/DynamicBase.cs +++ b/Core/Core/Models/DynamicBase.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Dynamic; @@ -22,7 +23,7 @@ public class DynamicBase : DynamicObject, IDynamicMetaObjectProvider public const DynamicBaseMemberType DEFAULT_INCLUDE_MEMBERS = DynamicBaseMemberType.Instance | DynamicBaseMemberType.Dynamic; - private static readonly Dictionary> s_propInfoCache = new(); + private static readonly ConcurrentDictionary> s_propInfoCache = new(); /// /// The actual property bag, where dynamically added props are stored. @@ -47,8 +48,7 @@ public object? this[string key] return value; } - PopulatePropInfoCache(GetType()); - var prop = s_propInfoCache[GetType()].FirstOrDefault(p => p.Name == key); + var prop = GetPopulatePropInfoFromCache(GetType()).FirstOrDefault(p => p.Name == key); if (prop == null) { @@ -70,8 +70,7 @@ public object? this[string key] return; } - PopulatePropInfoCache(GetType()); - var prop = s_propInfoCache[GetType()].FirstOrDefault(p => p.Name == key); + var prop = GetPopulatePropInfoFromCache(GetType()).FirstOrDefault(p => p.Name == key); if (prop == null) { @@ -157,15 +156,14 @@ public bool IsPropNameValid(string name, out string reason) return true; } - private static void PopulatePropInfoCache(Type type) - { - if (!s_propInfoCache.ContainsKey(type)) - { - s_propInfoCache[type] = type.GetProperties(BindingFlags.Instance | BindingFlags.Public) - .Where(p => !p.IsDefined(typeof(IgnoreTheItemAttribute), true)) - .ToList(); - } - } + private static List GetPopulatePropInfoFromCache(Type type) => + s_propInfoCache.GetOrAdd( + type, + t => + t.GetProperties(BindingFlags.Instance | BindingFlags.Public) + .Where(p => !p.IsDefined(typeof(IgnoreTheItemAttribute), true)) + .ToList() + ); /// /// Gets all of the property names on this class, dynamic or not. @@ -173,8 +171,7 @@ private static void PopulatePropInfoCache(Type type) [Obsolete("Use `GetMembers(DynamicBaseMemberType.All).Keys` instead")] public override IEnumerable GetDynamicMemberNames() { - PopulatePropInfoCache(GetType()); - var pinfos = s_propInfoCache[GetType()]; + var pinfos = GetPopulatePropInfoFromCache(GetType()); var names = new List(_properties.Count + pinfos.Count); foreach (var pinfo in pinfos) @@ -202,8 +199,7 @@ public IEnumerable GetInstanceMembersNames() public static IEnumerable GetInstanceMembersNames(Type t) { - PopulatePropInfoCache(t); - var pinfos = s_propInfoCache[t]; + var pinfos = GetPopulatePropInfoFromCache(t); var names = new List(pinfos.Count); foreach (var pinfo in pinfos) @@ -225,8 +221,7 @@ public IEnumerable GetInstanceMembers() public static IEnumerable GetInstanceMembers(Type t) { - PopulatePropInfoCache(t); - var pinfos = s_propInfoCache[t]; + var pinfos = GetPopulatePropInfoFromCache(t); var names = new List(pinfos.Count); @@ -269,20 +264,20 @@ public IEnumerable GetMemberNames() if (includeMembers.HasFlag(DynamicBaseMemberType.Instance)) { - PopulatePropInfoCache(GetType()); - var pinfos = s_propInfoCache[GetType()].Where(x => - { - var hasIgnored = x.IsDefined(typeof(SchemaIgnore), true); - var hasObsolete = x.IsDefined(typeof(ObsoleteAttribute), true); - - // If obsolete is false and prop has obsolete attr - // OR - // If schemaIgnored is true and prop has schemaIgnore attr - return !( - !includeMembers.HasFlag(DynamicBaseMemberType.SchemaIgnored) && hasIgnored - || !includeMembers.HasFlag(DynamicBaseMemberType.Obsolete) && hasObsolete - ); - }); + var pinfos = GetPopulatePropInfoFromCache(GetType()) + .Where(x => + { + var hasIgnored = x.IsDefined(typeof(SchemaIgnore), true); + var hasObsolete = x.IsDefined(typeof(ObsoleteAttribute), true); + + // If obsolete is false and prop has obsolete attr + // OR + // If schemaIgnored is true and prop has schemaIgnore attr + return !( + !includeMembers.HasFlag(DynamicBaseMemberType.SchemaIgnored) && hasIgnored + || !includeMembers.HasFlag(DynamicBaseMemberType.Obsolete) && hasObsolete + ); + }); foreach (var pi in pinfos) { if (!dic.ContainsKey(pi.Name)) //todo This is a TEMP FIX FOR #1969, and should be reverted after a proper fix is made! From 8dad212038e5c96f818ebdf22b905f86892c2892 Mon Sep 17 00:00:00 2001 From: Euan O'Regan Date: Fri, 19 Jul 2024 07:49:43 +1000 Subject: [PATCH 28/52] Fix: resolved issue sending pipes from Revit (#3560) fix(Revit Connector): Added null check for pipes not connected to a MEPSystem in revit --- .../ConverterRevitShared/PartialClasses/ConvertPipe.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertPipe.cs b/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertPipe.cs index ac2d2ce760..a1d24fcb61 100644 --- a/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertPipe.cs +++ b/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertPipe.cs @@ -185,7 +185,7 @@ public BuiltElements.Pipe PipeToSpeckle(DB.Plumbing.Pipe revitPipe) baseCurve = baseLine, family = revitPipe.PipeType.FamilyName, type = revitPipe.PipeType.Name, - systemName = revitPipe.MEPSystem.Name, + systemName = revitPipe.MEPSystem?.Name ?? "", systemType = GetParamValue(revitPipe, BuiltInParameter.RBS_SYSTEM_CLASSIFICATION_PARAM), diameter = GetParamValue(revitPipe, BuiltInParameter.RBS_PIPE_DIAMETER_PARAM), length = GetParamValue(revitPipe, BuiltInParameter.CURVE_ELEM_LENGTH), @@ -242,7 +242,7 @@ public BuiltElements.Pipe PipeToSpeckle(DB.Plumbing.FlexPipe revitPipe) baseCurve = polyline, family = revitPipe.FlexPipeType.FamilyName, type = revitPipe.FlexPipeType.Name, - systemName = revitPipe.MEPSystem.Name, + systemName = revitPipe.MEPSystem?.Name ?? "", systemType = GetParamValue(revitPipe, BuiltInParameter.RBS_SYSTEM_CLASSIFICATION_PARAM), diameter = GetParamValue(revitPipe, BuiltInParameter.RBS_PIPE_DIAMETER_PARAM), length = GetParamValue(revitPipe, BuiltInParameter.CURVE_ELEM_LENGTH), From 96a6f294936109c2883e1239911de4e919bd3d45 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Mon, 22 Jul 2024 09:55:06 +0100 Subject: [PATCH 29/52] Cnx 9894 update circle ci windows images to newer ones (#3581) Updates images and orbs for circleci --- .circleci/config.yml | 5 ++--- .circleci/scripts/config-template.yml | 16 ++++++---------- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f623d0046f..029f1f98bb 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,9 +6,8 @@ setup: true # the path-filtering orb is required to continue a pipeline based on # the path of an updated fileset orbs: - path-filtering: circleci/path-filtering@0.1.3 - continuation: circleci/continuation@0.3.1 - python: circleci/python@2.0.3 + continuation: circleci/continuation@1.0.0 + python: circleci/python@2.1.1 jobs: setup: diff --git a/.circleci/scripts/config-template.yml b/.circleci/scripts/config-template.yml index a3d7a5572f..6e981eb308 100644 --- a/.circleci/scripts/config-template.yml +++ b/.circleci/scripts/config-template.yml @@ -2,12 +2,8 @@ version: 2.1 orbs: win: circleci/windows@5.0.0 - aws-s3: circleci/aws-s3@2.0.0 - codecov: codecov/codecov@3.2.2 - wait-for: cobli/wait-for@0.0.2 discord: antonioned/discord@0.1.0 - docker: circleci/docker@2.2.0 - macos: circleci/macos@2.4.1 + macos: circleci/macos@2.5.2 # The main workflows for our monorepo pipeline. workflows: @@ -74,7 +70,7 @@ commands: jobs: # Each project will have individual jobs for each specific task it has to execute (build, release...) build-sdk: docker: - - image: cimg/base:2021.01 + - image: cimg/base:2024.07 steps: - cached-checkout - run: @@ -121,7 +117,7 @@ jobs: # Each project will have individual jobs for each specific task it has to test-core: machine: - image: ubuntu-2204:2023.02.1 + image: ubuntu-2204:2024.05.1 resource_class: large steps: - cached-checkout @@ -536,7 +532,7 @@ jobs: # Each project will have individual jobs for each specific task it has to get-ci-tools: # Clones our ci tools and persists them to the workspace docker: - - image: cimg/base:2021.01 + - image: cimg/base:2024.07 steps: - add_ssh_keys: fingerprints: @@ -666,7 +662,7 @@ jobs: # Each project will have individual jobs for each specific task it has to deploy-connectors: # Uploads all installers found to S3 docker: - - image: cimg/base:2021.01 + - image: cimg/base:2024.07 steps: - run: name: Proceed to deploy @@ -674,7 +670,7 @@ jobs: # Each project will have individual jobs for each specific task it has to notify-deploy: docker: - - image: cimg/base:2021.01 + - image: cimg/base:2024.07 steps: - discord/status: mentions: "1067457311980933140" From 8c32ad40d19ae1e6d4aaa23d0e356f1c2d5e2a45 Mon Sep 17 00:00:00 2001 From: Claire Kuang Date: Mon, 22 Jul 2024 12:01:00 +0100 Subject: [PATCH 30/52] chore(civil3d): changes property sets and parts data to be a `Base` (#3584) * updates partsdata and property sets to send as base * do not send property sets if count is 0 --- .../UI/ConnectorBindingsAutocadCivil.Send.cs | 3 +-- .../ConnectorAutocadCivil/Utils.cs | 25 +++++++++++-------- .../ConverterAutocadCivil.Civil.cs | 9 ++++--- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Send.cs b/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Send.cs index 2c96f1eacd..0b329dd4d3 100644 --- a/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Send.cs +++ b/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Send.cs @@ -246,8 +246,7 @@ ref int convertedCount #if CIVIL // add property sets if this is Civil3D - var propertySets = obj.GetPropertySets(tr); - if (propertySets.Count > 0) + if (obj.TryGetPropertySets(tr, out Base propertySets)) { converted["propertySets"] = propertySets; } diff --git a/ConnectorAutocadCivil/ConnectorAutocadCivil/Utils.cs b/ConnectorAutocadCivil/ConnectorAutocadCivil/Utils.cs index eed231d48f..3abca748b9 100644 --- a/ConnectorAutocadCivil/ConnectorAutocadCivil/Utils.cs +++ b/ConnectorAutocadCivil/ConnectorAutocadCivil/Utils.cs @@ -460,34 +460,36 @@ public static void SetPropertySets(this Entity entity, Document doc, List /// /// - public static List> GetPropertySets(this DBObject obj, Transaction tr) + public static bool TryGetPropertySets(this DBObject obj, Transaction tr, out Base propertySets) { - var sets = new List>(); - ObjectIdCollection propertySets = null; + propertySets = new(); + + ObjectIdCollection propertySetIds = null; try { - propertySets = PropertyDataServices.GetPropertySets(obj); + propertySetIds = PropertyDataServices.GetPropertySets(obj); } catch (Autodesk.AutoCAD.Runtime.Exception e) { // This may throw if property sets do not exist on the object. // afaik, trycatch is necessary because there is no way to preemptively check if the set already exists. + return false; } - if (propertySets is null) + if (propertySetIds is null || propertySetIds.Count == 0) { - return sets; + return false; } - foreach (ObjectId id in propertySets) + foreach (ObjectId id in propertySetIds) { - var setDictionary = new Dictionary(); + Dictionary setDictionary = new() ; PropertySet propertySet = (PropertySet)tr.GetObject(id, OpenMode.ForRead); PropertySetDefinition setDef = (PropertySetDefinition)tr.GetObject(propertySet.PropertySetDefinition, OpenMode.ForRead); PropertyDefinitionCollection propDef = setDef.Definitions; - var propDefs = new Dictionary(); + Dictionary propDefs = new(); foreach (PropertyDefinition def in propDef) { propDefs.Add(def.Id, def); @@ -507,10 +509,11 @@ public static List> GetPropertySets(this DBObject obj if (setDictionary.Count > 0) { - sets.Add(CleanDictionary(setDictionary)); + propertySets[propertySet.Name] = CleanDictionary(setDictionary); } } - return sets; + + return true; } // Handles object types from property set dictionaries diff --git a/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.Civil.cs b/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.Civil.cs index 89f47ac800..3459ea1499 100644 --- a/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.Civil.cs +++ b/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.Civil.cs @@ -702,7 +702,7 @@ private Featureline FeaturelineToSpeckle(CivilDB.CorridorFeatureLine featureline var point = featureline.FeatureLinePoints[i]; baseCurvePoints.Add(point.XYZ); if (!point.IsBreak) { polylinePoints.Add(point.XYZ); } - if (polylinePoints.Count > 0 && (i == featureline.FeatureLinePoints.Count - 1 || point.IsBreak )) + if (polylinePoints.Count > 1 && (i == featureline.FeatureLinePoints.Count - 1 || point.IsBreak )) { var polyline = PolylineToSpeckle(new Polyline3d(Poly3dType.SimplePoly, polylinePoints, false)); polylines.Add(polyline); @@ -1029,17 +1029,18 @@ public Structure StructureToSpeckle(CivilDB.Structure structure) /// /// Converts PartData into a list of DataField /// - private List PartDataRecordToSpeckle(PartDataRecord partData) + private Base PartDataRecordToSpeckle(PartDataRecord partData) { + Base partDataBase = new(); List fields = new(); foreach (PartDataField partField in partData.GetAllDataFields()) { CivilDataField field = new(partField.Name, partField.DataType.ToString(), partField.Value, partField.Units.ToString(),partField.Context.ToString(), null); - fields.Add(field); + partDataBase[partField.Name] = field; } - return fields; + return partDataBase; } #if CIVIL2022_OR_GREATER From 885a05b12b0056a49c350926ce6989ca3ec06130 Mon Sep 17 00:00:00 2001 From: Iain Sproat <68657+iainsproat@users.noreply.github.com> Date: Tue, 23 Jul 2024 13:03:48 +0100 Subject: [PATCH 31/52] chore(send operation): print correlation ID in error & success messages (#3583) --- Core/Core/Api/Operations/Operations.Send.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Core/Core/Api/Operations/Operations.Send.cs b/Core/Core/Api/Operations/Operations.Send.cs index 882a831c24..da01128a02 100644 --- a/Core/Core/Api/Operations/Operations.Send.cs +++ b/Core/Core/Api/Operations/Operations.Send.cs @@ -83,9 +83,11 @@ public static async Task Send( var transportContext = transports.ToDictionary(t => t.TransportName, t => t.TransportContext); + var correlationId = Guid.NewGuid().ToString(); + // make sure all logs in the operation have the proper context using (LogContext.PushProperty("transportContext", transportContext)) - using (LogContext.PushProperty("correlationId", Guid.NewGuid().ToString())) + using (LogContext.PushProperty("correlationId", correlationId)) { var sendTimer = Stopwatch.StartNew(); SpeckleLog.Logger.Information("Starting send operation"); @@ -110,8 +112,9 @@ public static async Task Send( { SpeckleLog.Logger.Information( ex, - "Send operation failed after {elapsed} seconds", - sendTimer.Elapsed.TotalSeconds + "Send operation failed after {elapsed} seconds. Correlation ID: {correlationId}", + sendTimer.Elapsed.TotalSeconds, + correlationId ); if (ex is OperationCanceledException or SpeckleException) { @@ -134,10 +137,11 @@ public static async Task Send( .ForContext("note", "the elapsed summary doesn't need to add up to the total elapsed... Threading magic...") .ForContext("serializerElapsed", serializerV2.Elapsed) .Information( - "Finished sending {objectCount} objects after {elapsed}, result {objectId}", + "Finished sending {objectCount} objects after {elapsed}, result: {objectId}. Correlation ID: {correlationId}", transports.Max(t => t.SavedObjectCount), sendTimer.Elapsed.TotalSeconds, - hash + hash, + correlationId ); return hash; } From fe0c92347d7ad4f777ed761577f0cacb26ed5cdb Mon Sep 17 00:00:00 2001 From: jhdempsey86 <67244422+jhdempsey86@users.noreply.github.com> Date: Wed, 24 Jul 2024 09:00:18 +0100 Subject: [PATCH 32/52] Fix: AppliedAssembly.AdjustedElevation error, and Single Point Featureline error (#3578) * Code now defaults AppliedAssembly.AdjustedElevation to double.MinValue if accessing that property throws an error. Adjusted FeatureLineToSpeckle so it only works on featurelines with more than 2 points, otherwise accessing he spline throws an error * Switched back to double.NaN * AdjustedElevation is now a double? type * Update ConverterAutocadCivil.Civil.cs --------- Co-authored-by: Claire Kuang --- .../ConverterAutocadCivil.Civil.cs | 13 ++++++++++++- .../BuiltElements/Civil/CivilAppliedAssembly.cs | 4 ++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.Civil.cs b/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.Civil.cs index 3459ea1499..4f2b16454f 100644 --- a/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.Civil.cs +++ b/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.Civil.cs @@ -1169,7 +1169,18 @@ private CivilAppliedAssembly AppliedAssemblyToSpeckle(AppliedAssembly appliedAss speckleSubassemblies.Add(speckleSubassembly); } - CivilAppliedAssembly speckleAppliedAssembly = new(speckleSubassemblies, appliedAssembly.AdjustedElevation, ModelUnits); + double? adjustedElevation = null; + try + { + adjustedElevation = appliedAssembly.AdjustedElevation; + } + catch (ArgumentException e) when (!e.IsFatal()) + { + // Do nothing. Leave the value as null. + } + + CivilAppliedAssembly speckleAppliedAssembly = new(speckleSubassemblies, adjustedElevation, ModelUnits); + return speckleAppliedAssembly; } diff --git a/Objects/Objects/BuiltElements/Civil/CivilAppliedAssembly.cs b/Objects/Objects/BuiltElements/Civil/CivilAppliedAssembly.cs index f490c643de..be99041904 100644 --- a/Objects/Objects/BuiltElements/Civil/CivilAppliedAssembly.cs +++ b/Objects/Objects/BuiltElements/Civil/CivilAppliedAssembly.cs @@ -9,7 +9,7 @@ public CivilAppliedAssembly() { } public CivilAppliedAssembly( List appliedSubassemblies, - double adjustedElevation, + double? adjustedElevation, string units ) { @@ -20,7 +20,7 @@ string units public List appliedSubassemblies { get; set; } - public double adjustedElevation { get; set; } + public double? adjustedElevation { get; set; } public string units { get; set; } } From 536941bee49a962df8495f4f250548d3b6a04792 Mon Sep 17 00:00:00 2001 From: Ralph Wessel <150629054+r-wessel@users.noreply.github.com> Date: Wed, 24 Jul 2024 17:31:46 +0100 Subject: [PATCH 33/52] Fix: Corrects serialised data from Archicad (macOS) (#3587) GS::ObjectState::Add interprets C string as char array rather than string in Xcode - explicit string conversion is applied to prevent this --- .../AddOn/Sources/AddOn/Commands/GetDataCommand.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetDataCommand.cpp b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetDataCommand.cpp index ed6d040642..41242b887f 100644 --- a/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetDataCommand.cpp +++ b/ConnectorArchicad/AddOn/Sources/AddOn/Commands/GetDataCommand.cpp @@ -344,10 +344,10 @@ namespace { API_Attribute attribute{}; attribute.header.index = materialIndex; attribute.header.typeID = API_BuildingMaterialID; - auto error = ACAPI_Attribute_Get (&attribute); + auto error = ACAPI_Attribute_Get(&attribute); if (error != NoError) return error; - serialiser.Add(FieldNames::Material::Name, attribute.header.name); + serialiser.Add(FieldNames::Material::Name, GS::UniString{attribute.header.name}); return NoError; } //exportMaterial @@ -366,13 +366,13 @@ namespace { const auto& serialMaterialQuants = serialiser.AddList (FieldNames::ElementBase::MaterialQuantities); for (auto& quantity : materialQuants) { GS::ObjectState serialMaterialQuant, serialMaterial; - auto error = exportMaterial (quantity.materialIndex, serialMaterial); + auto error = exportMaterial(quantity.materialIndex, serialMaterial); if (error != NoError) return error; serialMaterialQuant.Add(FieldNames::ElementBase::Quantity::Material, serialMaterial); serialMaterialQuant.Add(FieldNames::ElementBase::Quantity::Volume, quantity.volume); serialMaterialQuant.Add(FieldNames::ElementBase::Quantity::Area, quantity.surfaceArea); - serialMaterialQuant.Add(FieldNames::ElementBase::Quantity::Units, "m"); + serialMaterialQuant.Add(FieldNames::ElementBase::Quantity::Units, GS::UniString{"m"}); serialMaterialQuants(serialMaterialQuant); } return NoError; @@ -733,7 +733,7 @@ GS::ObjectState GetDataCommand::Execute (const GS::ObjectState& parameters, err = SerializeElementType (element, memo, os); if (err != NoError) continue; - + listAdder (os); } From af53b70514411a211b7b0848a939017911301063 Mon Sep 17 00:00:00 2001 From: connorivy <43247197+connorivy@users.noreply.github.com> Date: Wed, 24 Jul 2024 12:15:07 -0500 Subject: [PATCH 34/52] Support shared parameter in schedules (#3585) * add check for shared parameter - to Speckle * update FieldType metadata logic * formatting * correctly assign isReadOnly parameter --------- Co-authored-by: Eduardo Di Loreto Co-authored-by: Connor Ivy --- .../PartialClasses/ConvertView.Schedule.cs | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertView.Schedule.cs b/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertView.Schedule.cs index 2dc43fefce..bf6de53479 100644 --- a/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertView.Schedule.cs +++ b/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertView.Schedule.cs @@ -244,7 +244,7 @@ Element firstElement var columnMetadata = new Base(); columnMetadata["BuiltInParameterInteger"] = info.field.ParameterId.IntegerValue; - columnMetadata["FieldType"] = info.field.FieldType.ToString(); + string fieldType = info.field.FieldType.ToString(); Parameter param; if (info.field.FieldType == ScheduleFieldType.ElementType) @@ -257,10 +257,26 @@ Element firstElement } else if (info.field.FieldType == ScheduleFieldType.Instance) { + // All shared parameters also use this type, regardless of whether they are instance or type parameters. + // ref: https://www.revitapidocs.com/2024/9888db7d-00d0-4fd7-a1a9-cdd1fb5fce16.htm if (firstElement != null) { param = firstElement.get_Parameter(builtInParameter); - columnMetadata["IsReadOnly"] = param?.IsReadOnly; + + // if the parameter is shared, we need to check the type parameterer too + if (firstType != null) + { + Parameter typeParam = firstType.get_Parameter(builtInParameter); + + // If the parameter is readonly in the element but not in the type, is a type parameter + if (typeParam != null && !typeParam.IsReadOnly && param != null && param.IsReadOnly) + { + columnMetadata["IsReadOnly"] = false; + fieldType = ScheduleFieldType.ElementType.ToString(); + } + } + + columnMetadata["IsReadOnly"] ??= param?.IsReadOnly ?? true; } } else @@ -273,6 +289,8 @@ Element firstElement info.field.FieldType.ToString() ); } + + columnMetadata["FieldType"] = fieldType; speckleTable.DefineColumn(columnMetadata); } From ffc35bfed0d374730d51ecaa29dbd5a4ec6c7f75 Mon Sep 17 00:00:00 2001 From: Claire Kuang Date: Thu, 25 Jul 2024 18:16:05 +0100 Subject: [PATCH 35/52] fix(civil3d): added units to civil classes missing them (#3591) added units to civil classes missing them --- .../ConverterAutocadCivil.Civil.cs | 43 ++++++++++++++----- Objects/Objects/BuiltElements/Baseline.cs | 2 + .../Civil/CivilAppliedSubassembly.cs | 2 + .../Civil/CivilBaselineRegion.cs | 2 + .../Civil/CivilCalculatedLink.cs | 2 + .../Civil/CivilCalculatedPoint.cs | 2 + 6 files changed, 43 insertions(+), 10 deletions(-) diff --git a/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.Civil.cs b/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.Civil.cs index 4f2b16454f..d4fc168cf2 100644 --- a/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.Civil.cs +++ b/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.Civil.cs @@ -148,7 +148,8 @@ public CivilAlignment AlignmentToSpeckle(CivilDB.Alignment alignment) stationEquationDirections = directions, offset = alignment.IsOffsetAlignment ? alignment.OffsetAlignmentInfo.NominalOffset : 0, site = alignment.SiteName ?? "", - style = alignment.StyleName ?? "" + style = alignment.StyleName ?? "", + units = ModelUnits }; AddNameAndDescriptionProperty(alignment.Name, alignment.Description, speckleAlignment); @@ -542,7 +543,8 @@ public CivilProfile ProfileToSpeckle(CivilDB.Profile profile) offset = profile.Offset, style = profile.StyleName ?? "", startStation = profile.StartingStation, - endStation = profile.EndingStation + endStation = profile.EndingStation, + units = ModelUnits }; AddNameAndDescriptionProperty(profile.Name, profile.Description, speckleProfile); @@ -615,8 +617,6 @@ public CivilProfile ProfileToSpeckle(CivilDB.Profile profile) speckleProfile.displayValue = PolylineToSpeckle(pvis, profile.Closed); } - speckleProfile.units = ModelUnits; - return speckleProfile; } private Line ProfileLineToSpeckle(ProfileTangent tangent) @@ -1136,6 +1136,7 @@ public Pipe PipeToSpeckle(PressurePipe pipe) private CivilDataField AppliedSubassemblyParamToSpeckle(IAppliedSubassemblyParam param) { CivilDataField baseParam = new(param.KeyName, param.ValueType.Name, param.ValueAsObject, null, null, param.DisplayName); + return baseParam; } @@ -1155,7 +1156,11 @@ private CivilAppliedSubassembly AppliedSubassemblyToSpeckle(AppliedSubassembly a Point soePoint = PointToSpeckle(appliedSubassembly.OriginStationOffsetElevationToBaseline); List speckleParameters = appliedSubassembly.Parameters.Select(p => AppliedSubassemblyParamToSpeckle(p)).ToList(); - CivilAppliedSubassembly speckleAppliedSubassembly = new(appliedSubassembly.SubassemblyId.ToString(), subassembly.Name, speckleShapes, soePoint, speckleParameters); + CivilAppliedSubassembly speckleAppliedSubassembly = new(appliedSubassembly.SubassemblyId.ToString(), subassembly.Name, speckleShapes, soePoint, speckleParameters) + { + units = ModelUnits + }; + return speckleAppliedSubassembly; } @@ -1201,7 +1206,11 @@ private CivilBaselineRegion BaselineRegionToSpeckle(BaselineRegion region) } // create the speckle region - CivilBaselineRegion speckleRegion = new(region.Name, region.StartStation, region.EndStation, assembly.Id.ToString(), assembly.Name, speckleAppliedAssemblies); + CivilBaselineRegion speckleRegion = new(region.Name, region.StartStation, region.EndStation, assembly.Id.ToString(), assembly.Name, speckleAppliedAssemblies) + { + units = ModelUnits + }; + return speckleRegion; } @@ -1229,7 +1238,11 @@ private CivilCalculatedLink CalculatedLinkToSpeckle(CalculatedLink link) specklePoints.Add(specklePoint); } - CivilCalculatedLink speckleLink = new(codes, specklePoints); + CivilCalculatedLink speckleLink = new(codes, specklePoints) + { + units = ModelUnits + }; + return speckleLink; } @@ -1240,7 +1253,11 @@ private CivilCalculatedPoint CalculatedPointToSpeckle(CalculatedPoint point) Vector normalBaseline = VectorToSpeckle(point.NormalToBaseline); Vector normalSubAssembly = VectorToSpeckle(point.NormalToSubassembly); Point soePoint = PointToSpeckle(point.StationOffsetElevationToBaseline); - CivilCalculatedPoint speckleCalculatedPoint = new(specklePoint, codes, normalBaseline, normalSubAssembly, soePoint); + CivilCalculatedPoint speckleCalculatedPoint = new(specklePoint, codes, normalBaseline, normalSubAssembly, soePoint) + { + units = ModelUnits + }; + return speckleCalculatedPoint; } @@ -1269,7 +1286,10 @@ private CivilBaseline BaselineToSpeckle(CivilDB.Baseline baseline) var profile = Trans.GetObject(baseline.ProfileId, OpenMode.ForRead) as CivilDB.Profile; CivilProfile speckleProfile = ProfileToSpeckle(profile); - speckleBaseline = new(baseline.Name, speckleRegions, baseline.SortedStations().ToList(), baseline.StartStation, baseline.EndStation, speckleAlignment, speckleProfile); + speckleBaseline = new(baseline.Name, speckleRegions, baseline.SortedStations().ToList(), baseline.StartStation, baseline.EndStation, speckleAlignment, speckleProfile) + { + units = ModelUnits + }; } else { @@ -1277,7 +1297,10 @@ private CivilBaseline BaselineToSpeckle(CivilDB.Baseline baseline) var featureline = Trans.GetObject(baseline.FeatureLineId, OpenMode.ForRead) as CivilDB.FeatureLine; Featureline speckleFeatureline = FeaturelineToSpeckle(featureline); - speckleBaseline = new(baseline.Name, speckleRegions, baseline.SortedStations().ToList(), baseline.StartStation, baseline.EndStation, speckleFeatureline); + speckleBaseline = new(baseline.Name, speckleRegions, baseline.SortedStations().ToList(), baseline.StartStation, baseline.EndStation, speckleFeatureline) + { + units = ModelUnits + }; } return speckleBaseline; diff --git a/Objects/Objects/BuiltElements/Baseline.cs b/Objects/Objects/BuiltElements/Baseline.cs index a78fc9b069..11b78ab09f 100644 --- a/Objects/Objects/BuiltElements/Baseline.cs +++ b/Objects/Objects/BuiltElements/Baseline.cs @@ -32,6 +32,8 @@ protected Baseline(string name, bool isFeaturelineBased) public Featureline? featureline { get; internal set; } public bool isFeaturelineBased { get; set; } + + public string units { get; set; } } /// diff --git a/Objects/Objects/BuiltElements/Civil/CivilAppliedSubassembly.cs b/Objects/Objects/BuiltElements/Civil/CivilAppliedSubassembly.cs index f4beb7a3d0..4619484ff9 100644 --- a/Objects/Objects/BuiltElements/Civil/CivilAppliedSubassembly.cs +++ b/Objects/Objects/BuiltElements/Civil/CivilAppliedSubassembly.cs @@ -34,4 +34,6 @@ List parameters [DetachProperty] public List parameters { get; set; } + + public string units { get; set; } } diff --git a/Objects/Objects/BuiltElements/Civil/CivilBaselineRegion.cs b/Objects/Objects/BuiltElements/Civil/CivilBaselineRegion.cs index 5aabffd3f7..b4d10b7c20 100644 --- a/Objects/Objects/BuiltElements/Civil/CivilBaselineRegion.cs +++ b/Objects/Objects/BuiltElements/Civil/CivilBaselineRegion.cs @@ -42,4 +42,6 @@ List appliedAssemblies [DetachProperty] public List appliedAssemblies { get; set; } + + public string units { get; set; } } diff --git a/Objects/Objects/BuiltElements/Civil/CivilCalculatedLink.cs b/Objects/Objects/BuiltElements/Civil/CivilCalculatedLink.cs index aa0bc94979..165f7ee268 100644 --- a/Objects/Objects/BuiltElements/Civil/CivilCalculatedLink.cs +++ b/Objects/Objects/BuiltElements/Civil/CivilCalculatedLink.cs @@ -17,4 +17,6 @@ public CivilCalculatedLink(List codes, List points [DetachProperty] public List points { get; set; } + + public string units { get; set; } } diff --git a/Objects/Objects/BuiltElements/Civil/CivilCalculatedPoint.cs b/Objects/Objects/BuiltElements/Civil/CivilCalculatedPoint.cs index c6ed147b4f..4393a9a594 100644 --- a/Objects/Objects/BuiltElements/Civil/CivilCalculatedPoint.cs +++ b/Objects/Objects/BuiltElements/Civil/CivilCalculatedPoint.cs @@ -32,4 +32,6 @@ Point stationOffsetElevationToBaseline public Vector normalToSubassembly { get; set; } public Point stationOffsetElevationToBaseline { get; set; } + + public string units { get; set; } } From 9d66940a716a3c52d998e8e542d6a7387097d596 Mon Sep 17 00:00:00 2001 From: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com> Date: Thu, 25 Jul 2024 22:06:26 +0100 Subject: [PATCH 36/52] Retry Fixes (#3592) * retry fixes * fix --- Core/Core/Api/GraphQL/Client.cs | 9 --- Core/Core/Helpers/Http.cs | 18 ++--- Core/Core/Transports/ServerUtils/ServerAPI.cs | 69 +++---------------- 3 files changed, 17 insertions(+), 79 deletions(-) diff --git a/Core/Core/Api/GraphQL/Client.cs b/Core/Core/Api/GraphQL/Client.cs index 9a645006f4..70384a6d12 100644 --- a/Core/Core/Api/GraphQL/Client.cs +++ b/Core/Core/Api/GraphQL/Client.cs @@ -92,15 +92,6 @@ public void Dispose() internal async Task ExecuteWithResiliencePolicies(Func> func) { - // TODO: handle these in the HttpClient factory with a custom RequestHandler class - // 408 Request Timeout - // 425 Too Early - // 429 Too Many Requests - // 500 Internal Server Error - // 502 Bad Gateway - // 503 Service Unavailable - // 504 Gateway Timeout - var delay = Backoff.DecorrelatedJitterBackoffV2(TimeSpan.FromSeconds(1), 5); var graphqlRetry = Policy .Handle() diff --git a/Core/Core/Helpers/Http.cs b/Core/Core/Helpers/Http.cs index e3562bc05b..5fe4ef0edb 100644 --- a/Core/Core/Helpers/Http.cs +++ b/Core/Core/Helpers/Http.cs @@ -30,16 +30,10 @@ public static AsyncRetryPolicy HttpAsyncPolicy(IEnumerable< .HandleTransientHttpError() .WaitAndRetryAsync( delay ?? DefaultDelay(), - (ex, timeSpan, retryAttempt, context) => { - //context.Remove("retryCount"); - //context.Add("retryCount", retryAttempt); - //Log.Information( - // ex.Exception, - // "The http request failed with {exceptionType} exception retrying after {cooldown} milliseconds. This is retry attempt {retryAttempt}", - // ex.GetType().Name, - // timeSpan.TotalSeconds * 1000, - // retryAttempt - //); + (ex, timeSpan, retryAttempt, context) => + { + context.Remove("retryCount"); + context.Add("retryCount", retryAttempt); } ); } @@ -209,11 +203,13 @@ CancellationToken cancellationToken var timer = new Stopwatch(); timer.Start(); context.Add("retryCount", 0); + + request.Headers.Add("x-request-id", context.CorrelationId.ToString()); + var policyResult = await Http.HttpAsyncPolicy(_delay) .ExecuteAndCaptureAsync( ctx => { - request.Headers.Add("x-request-id", ctx.CorrelationId.ToString()); return base.SendAsync(request, cancellationToken); }, context diff --git a/Core/Core/Transports/ServerUtils/ServerAPI.cs b/Core/Core/Transports/ServerUtils/ServerAPI.cs index d6a32a5412..09912368cf 100644 --- a/Core/Core/Transports/ServerUtils/ServerAPI.cs +++ b/Core/Core/Transports/ServerUtils/ServerAPI.cs @@ -27,8 +27,6 @@ public sealed class ServerApi : IDisposable, IServerApi private const int MAX_REQUEST_SIZE = 100_000_000; - private const int RETRY_COUNT = 3; - private static readonly HashSet s_retryCodes = new() { 408, 502, 503, 504 }; private static readonly char[] s_separator = { '\t' }; private static readonly string[] s_filenameSeparator = { "filename=" }; @@ -77,13 +75,9 @@ public async Task DownloadSingleObject(string streamId, string objectId) Method = HttpMethod.Get }; - HttpResponseMessage rootHttpResponse; - do - { - rootHttpResponse = await _client - .SendAsync(rootHttpMessage, HttpCompletionOption.ResponseContentRead, CancellationToken) - .ConfigureAwait(false); - } while (ShouldRetry(rootHttpResponse)); + HttpResponseMessage rootHttpResponse = await _client + .SendAsync(rootHttpMessage, HttpCompletionOption.ResponseContentRead, CancellationToken) + .ConfigureAwait(false); rootHttpResponse.EnsureSuccessStatusCode(); @@ -256,11 +250,7 @@ public async Task UploadBlobs(string streamId, IReadOnlyList<(string, string)> o try { - HttpResponseMessage response; - do - { - response = await _client.SendAsync(message, CancellationToken).ConfigureAwait(false); - } while (ShouldRetry(response)); //TODO: can we get rid of this now we have polly? + HttpResponseMessage response = await _client.SendAsync(message, CancellationToken).ConfigureAwait(false); response.EnsureSuccessStatusCode(); @@ -335,13 +325,9 @@ CbObjectDownloaded onObjectCallback childrenHttpMessage.Content = new StringContent(serializedPayload, Encoding.UTF8, "application/json"); childrenHttpMessage.Headers.Add("Accept", "text/plain"); - HttpResponseMessage childrenHttpResponse; - do - { - childrenHttpResponse = await _client - .SendAsync(childrenHttpMessage, HttpCompletionOption.ResponseHeadersRead, CancellationToken) - .ConfigureAwait(false); - } while (ShouldRetry(childrenHttpResponse)); + HttpResponseMessage childrenHttpResponse = await _client + .SendAsync(childrenHttpMessage, HttpCompletionOption.ResponseHeadersRead, CancellationToken) + .ConfigureAwait(false); childrenHttpResponse.EnsureSuccessStatusCode(); @@ -370,12 +356,8 @@ private async Task> HasObjectsImpl(string streamId, IRe string serializedPayload = JsonConvert.SerializeObject(payload); var uri = new Uri($"/api/diff/{streamId}", UriKind.Relative); - HttpResponseMessage response; using StringContent stringContent = new(serializedPayload, Encoding.UTF8, "application/json"); - do - { - response = await _client.PostAsync(uri, stringContent, CancellationToken).ConfigureAwait(false); - } while (ShouldRetry(response)); + HttpResponseMessage response = await _client.PostAsync(uri, stringContent, CancellationToken).ConfigureAwait(false); response.EnsureSuccessStatusCode(); @@ -434,11 +416,7 @@ private async Task UploadObjectsImpl(string streamId, List> HasBlobs(string streamId, IReadOnlyList using StringContent stringContent = new(payload, Encoding.UTF8, "application/json"); - //TODO: can we get rid of this now we have polly? - HttpResponseMessage response; - do - { - response = await _client.PostAsync(uri, stringContent, CancellationToken).ConfigureAwait(false); - } while (ShouldRetry(response)); + HttpResponseMessage response = await _client.PostAsync(uri, stringContent, CancellationToken).ConfigureAwait(false); response.EnsureSuccessStatusCode(); @@ -473,28 +446,6 @@ public async Task> HasBlobs(string streamId, IReadOnlyList return parsed; } - //TODO: can we get rid of this now we have polly? - private bool ShouldRetry(HttpResponseMessage? serverResponse) - { - if (serverResponse == null) - { - return true; - } - - if (!s_retryCodes.Contains((int)serverResponse.StatusCode)) - { - return false; - } - - if (RetriedCount >= RETRY_COUNT) - { - return false; - } - - RetriedCount += 1; - return true; - } - private sealed class BlobUploadResult { public List uploadResults { get; set; } From 82d98205abeb065438b43768c510031eac4eec55 Mon Sep 17 00:00:00 2001 From: Claire Kuang Date: Fri, 26 Jul 2024 14:28:33 +0100 Subject: [PATCH 37/52] bug(civil3d) : fixes pipe base curve conversion bc civil3d api sucks (#3593) Update ConverterAutocadCivil.Civil.cs --- .../ConverterAutocadCivil.Civil.cs | 39 +++++++++++++++++-- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.Civil.cs b/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.Civil.cs index d4fc168cf2..60110843bd 100644 --- a/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.Civil.cs +++ b/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/ConverterAutocadCivil.Civil.cs @@ -1065,8 +1065,23 @@ private List PartDataRecordToSpeckle(PressureNetworkPartData par // TODO: add pressure fittings public Pipe PipeToSpeckle(CivilDB.Pipe pipe) { - ICurve curve = CurveToSpeckle(pipe.BaseCurve); - + // get the pipe curve + // rant: if this is a straight or curved pipe, the BaseCurve prop is fake news && will return a DB.line with start and endpoints set to [0,0,0] & [0,0,1] + // do not use CurveToSpeckle(basecurve) 😡 + ICurve curve; + switch (pipe.SubEntityType) + { + case PipeSubEntityType.Straight: + var line = new Acad.LineSegment3d(pipe.StartPoint, pipe.EndPoint); + curve = LineToSpeckle(line); + break; + case PipeSubEntityType.Curved: + curve = ArcToSpeckle(pipe.Curve2d); + break; + default: + curve = CurveToSpeckle(pipe.BaseCurve); // basecurve is fake news, but we're still sending the other types with props for now + break; + } Pipe specklePipe = new() { @@ -1100,7 +1115,25 @@ public Pipe PipeToSpeckle(CivilDB.Pipe pipe) public Pipe PipeToSpeckle(PressurePipe pipe) { // get the pipe curve - ICurve curve = CurveToSpeckle(pipe.BaseCurve); + // rant: if this is a straight or curved pipe, the BaseCurve prop is fake news && will return a DB.line with start and endpoints set to [0,0,0] & [0,0,1] + // do not use CurveToSpeckle(basecurve) 😡 + ICurve curve; + switch (pipe.BaseCurve) + { + case Autodesk.AutoCAD.DatabaseServices.Line: + var line = new LineSegment3d(pipe.StartPoint, pipe.EndPoint); + curve = LineToSpeckle(line); + break; +#if CIVIL2024_OR_GREATER + case Autodesk.AutoCAD.DatabaseServices.Arc: + var arc = pipe.CurveGeometry.GetArc2d(); + curve = ArcToSpeckle(arc); + break; +#endif + default: + curve = CurveToSpeckle(pipe.BaseCurve); + break; + } Pipe specklePipe = new() { From eeb71611e300dcf1d5cd07e43246ef9fbe8b2db4 Mon Sep 17 00:00:00 2001 From: KatKatKateryna <89912278+KatKatKateryna@users.noreply.github.com> Date: Fri, 26 Jul 2024 17:50:34 +0100 Subject: [PATCH 38/52] QGIS attributes in Revit (#3595) duplicating attribute receive functionality from GisPolygon to GisFeature --- .../ConverterRevitShared/ConverterRevit.cs | 6 ++++ .../PartialClasses/ConvertPolygonElement.cs | 28 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/Objects/Converters/ConverterRevit/ConverterRevitShared/ConverterRevit.cs b/Objects/Converters/ConverterRevit/ConverterRevitShared/ConverterRevit.cs index 4c9a38d3f4..1f7e2ae368 100644 --- a/Objects/Converters/ConverterRevit/ConverterRevitShared/ConverterRevit.cs +++ b/Objects/Converters/ConverterRevit/ConverterRevitShared/ConverterRevit.cs @@ -734,6 +734,10 @@ public object ConvertToNativeObject(Base @object) case PolygonElement o: return PolygonElementToNative(o); + case GisFeature o: + return GisFeatureToNative(o); + + #if (REVIT2024) case RevitToposolid o: return ToposolidToNative(o); @@ -910,6 +914,8 @@ public bool CanConvertToNative(Base @object) Organization.DataTable _ => true, // GIS PolygonElement _ => true, + GisFeature feat when (feat.GetMembers(DynamicBaseMemberType.All).TryGetValue("displayValue", out var value) && + value is List valueList && valueList.Count > 0) => true, _ => false, }; if (objRes) diff --git a/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertPolygonElement.cs b/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertPolygonElement.cs index 890662766e..472f2f24dc 100644 --- a/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertPolygonElement.cs +++ b/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertPolygonElement.cs @@ -11,6 +11,34 @@ namespace Objects.Converter.Revit; public partial class ConverterRevit { + public ApplicationObject GisFeatureToNative(GisFeature gisFeature) + { + // targeting Polygon features with List of Meshes as dispayValue + var speckleDirectShape = new Objects.BuiltElements.Revit.DirectShape() + { + applicationId = gisFeature.applicationId ??= Guid.NewGuid().ToString(), + baseGeometries = new List(), + parameters = new Base(), + name = "", + category = RevitCategory.GenericModel + }; + + var traversal = new GraphTraversal(DefaultTraversal.DefaultRule); + var meshes = traversal.Traverse(gisFeature).Select(tc => tc.current).Where(b => b is Mesh); + + speckleDirectShape.baseGeometries.AddRange(meshes); + + foreach (var kvp in gisFeature.attributes.GetMembers()) + { + speckleDirectShape.parameters[kvp.Key] = new Objects.BuiltElements.Revit.Parameter() + { + name = kvp.Key, + value = kvp.Value + }; + } + return DirectShapeToNative(speckleDirectShape, ToNativeMeshSettingEnum.Default); + + } public ApplicationObject PolygonElementToNative(PolygonElement polygonElement) { var speckleDirectShape = new Objects.BuiltElements.Revit.DirectShape() From 39d9687e87c5fa43942d8d944689e0f263aa28a7 Mon Sep 17 00:00:00 2001 From: KatKatKateryna <89912278+KatKatKateryna@users.noreply.github.com> Date: Fri, 26 Jul 2024 18:24:12 +0100 Subject: [PATCH 39/52] formatting (#3596) --- .../ConverterRevitShared/ConverterRevit.cs | 10 +++++++--- .../PartialClasses/ConvertPolygonElement.cs | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Objects/Converters/ConverterRevit/ConverterRevitShared/ConverterRevit.cs b/Objects/Converters/ConverterRevit/ConverterRevitShared/ConverterRevit.cs index 1f7e2ae368..36fc3ba036 100644 --- a/Objects/Converters/ConverterRevit/ConverterRevitShared/ConverterRevit.cs +++ b/Objects/Converters/ConverterRevit/ConverterRevitShared/ConverterRevit.cs @@ -737,7 +737,6 @@ public object ConvertToNativeObject(Base @object) case GisFeature o: return GisFeatureToNative(o); - #if (REVIT2024) case RevitToposolid o: return ToposolidToNative(o); @@ -914,8 +913,13 @@ public bool CanConvertToNative(Base @object) Organization.DataTable _ => true, // GIS PolygonElement _ => true, - GisFeature feat when (feat.GetMembers(DynamicBaseMemberType.All).TryGetValue("displayValue", out var value) && - value is List valueList && valueList.Count > 0) => true, + GisFeature feat + when ( + feat.GetMembers(DynamicBaseMemberType.All).TryGetValue("displayValue", out var value) + && value is List valueList + && valueList.Count > 0 + ) + => true, _ => false, }; if (objRes) diff --git a/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertPolygonElement.cs b/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertPolygonElement.cs index 472f2f24dc..25f0dbcf0a 100644 --- a/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertPolygonElement.cs +++ b/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertPolygonElement.cs @@ -37,8 +37,8 @@ public ApplicationObject GisFeatureToNative(GisFeature gisFeature) }; } return DirectShapeToNative(speckleDirectShape, ToNativeMeshSettingEnum.Default); - } + public ApplicationObject PolygonElementToNative(PolygonElement polygonElement) { var speckleDirectShape = new Objects.BuiltElements.Revit.DirectShape() From ef996a37f6ee9a3768ce3cbfc83954b79dfd6eb4 Mon Sep 17 00:00:00 2001 From: Claire Kuang Date: Mon, 29 Jul 2024 15:42:12 +0100 Subject: [PATCH 40/52] fix(civil3d): prop sets & changes name to definition name (#3598) fixes receiving prop sets & changes name to definition name --- .../UI/ConnectorBindingsAutocadCivil.Receive.cs | 9 ++++++--- ConnectorAutocadCivil/ConnectorAutocadCivil/Utils.cs | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Receive.cs b/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Receive.cs index dddcd877d2..e17f04d0c8 100644 --- a/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Receive.cs +++ b/ConnectorAutocadCivil/ConnectorAutocadCivil/UI/ConnectorBindingsAutocadCivil.Receive.cs @@ -545,12 +545,15 @@ and not ApplicationObject.State.Updated // add property sets if this is Civil3D #if CIVIL - if (obj["propertySets"] is IReadOnlyList list) + if (obj["propertySets"] is Base propertySetsBase) { List> propertySets = new(); - foreach (var listObj in list) + foreach (object baseObj in propertySetsBase.GetMembers(DynamicBaseMemberType.Dynamic).Values) { - propertySets.Add(listObj as Dictionary); + if (baseObj is Dictionary propertySet) + { + propertySets.Add(propertySet); + } } try diff --git a/ConnectorAutocadCivil/ConnectorAutocadCivil/Utils.cs b/ConnectorAutocadCivil/ConnectorAutocadCivil/Utils.cs index 3abca748b9..461dec506e 100644 --- a/ConnectorAutocadCivil/ConnectorAutocadCivil/Utils.cs +++ b/ConnectorAutocadCivil/ConnectorAutocadCivil/Utils.cs @@ -509,7 +509,7 @@ public static bool TryGetPropertySets(this DBObject obj, Transaction tr, out Bas if (setDictionary.Count > 0) { - propertySets[propertySet.Name] = CleanDictionary(setDictionary); + propertySets[propertySet.PropertySetDefinitionName] = CleanDictionary(setDictionary); } } From 87198aff44bf1d794412da98721ff89b82bdc8a3 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Mon, 29 Jul 2024 15:59:56 +0100 Subject: [PATCH 41/52] Remove sentry (#3590) --- Core/Core/Core.csproj | 5 +-- Core/Core/Logging/SpeckleException.cs | 7 ++-- Core/Core/Logging/SpeckleLog.cs | 41 ------------------- DesktopUI2/DesktopUI2/ConnectorBindings.cs | 5 +-- .../Properties/ASBaseProperties.cs | 10 ----- 5 files changed, 7 insertions(+), 61 deletions(-) diff --git a/Core/Core/Core.csproj b/Core/Core/Core.csproj index 33510411b4..fac2359928 100644 --- a/Core/Core/Core.csproj +++ b/Core/Core/Core.csproj @@ -44,11 +44,10 @@ + - - @@ -57,8 +56,8 @@ - + diff --git a/Core/Core/Logging/SpeckleException.cs b/Core/Core/Logging/SpeckleException.cs index 332d59501f..ac59ed9cd2 100644 --- a/Core/Core/Logging/SpeckleException.cs +++ b/Core/Core/Logging/SpeckleException.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using GraphQL; -using Sentry; using Speckle.Core.Api; namespace Speckle.Core.Logging; @@ -19,18 +18,18 @@ public SpeckleException(string? message, Exception? inner = null) #region obsolete [Obsolete("Use any other constructor", true)] - public SpeckleException(string? message, Exception? inner, bool log = true, SentryLevel level = SentryLevel.Info) + public SpeckleException(string? message, Exception? inner, bool log = true) : base(message, inner) { } [Obsolete($"Use {nameof(SpeckleGraphQLException)} instead", true)] - public SpeckleException(string? message, GraphQLError[] errors, bool log = true, SentryLevel level = SentryLevel.Info) + public SpeckleException(string? message, GraphQLError[] errors, bool log = true) : base(message) { GraphQLErrors = errors.Select(error => new KeyValuePair("error", error.Message)).ToList(); } [Obsolete("Use any other constructor", true)] - public SpeckleException(string message, bool log, SentryLevel level = SentryLevel.Info) + public SpeckleException(string message, bool log) : base(message) { } [Obsolete($"Use {nameof(SpeckleGraphQLException)} instead", true)] diff --git a/Core/Core/Logging/SpeckleLog.cs b/Core/Core/Logging/SpeckleLog.cs index fba2ac094f..f020267fbf 100644 --- a/Core/Core/Logging/SpeckleLog.cs +++ b/Core/Core/Logging/SpeckleLog.cs @@ -3,7 +3,6 @@ using System.IO; using System.Reflection; using System.Runtime.InteropServices; -using Sentry; using Serilog; using Serilog.Core; using Serilog.Events; @@ -138,17 +137,6 @@ public static void Initialize( var id = GetUserIdFromDefaultAccount(); s_logger = s_logger.ForContext("id", id).ForContext("isMachineId", s_isMachineIdUsed); - // Configure scope after logger created. - SentrySdk.ConfigureScope(scope => - { - scope.User = new User { Id = id }; - }); - - SentrySdk.ConfigureScope(scope => - { - scope.SetTag("hostApplication", hostApplicationName); - }); - Logger .ForContext("userApplicationDataPath", SpecklePathProvider.UserApplicationDataPath()) .ForContext("installApplicationDataPath", SpecklePathProvider.InstallApplicationDataPath) @@ -222,35 +210,6 @@ SpeckleLogConfiguration logConfiguration ); } - if (logConfiguration.LogToSentry) - { - const string ENV = -#if DEBUG - "dev"; -#else - "production"; -#endif - - serilogLogConfiguration = serilogLogConfiguration.WriteTo.Sentry(o => - { - o.Dsn = logConfiguration.SentryDns; - o.Debug = false; - o.Environment = ENV; - o.Release = "SpeckleCore@" + Assembly.GetExecutingAssembly().GetName().Version; - o.AttachStacktrace = true; - o.StackTraceMode = StackTraceMode.Enhanced; - // Set traces_sample_rate to 1.0 to capture 100% of transactions for performance monitoring. - // We recommend adjusting this value in production. - o.TracesSampleRate = 1.0; - // Enable Global Mode if running in a client app - o.IsGlobalModeEnabled = true; - // Debug and higher are stored as breadcrumbs (default is Information) - o.MinimumBreadcrumbLevel = LogEventLevel.Debug; - // Warning and higher is sent as event (default is Error) - o.MinimumEventLevel = LogEventLevel.Error; - }); - } - var logger = serilogLogConfiguration.CreateLogger(); if (logConfiguration.LogToFile && !canLogToFile) diff --git a/DesktopUI2/DesktopUI2/ConnectorBindings.cs b/DesktopUI2/DesktopUI2/ConnectorBindings.cs index a86e22d24c..b0c7f3335b 100644 --- a/DesktopUI2/DesktopUI2/ConnectorBindings.cs +++ b/DesktopUI2/DesktopUI2/ConnectorBindings.cs @@ -5,7 +5,6 @@ using DesktopUI2.Models.Filters; using DesktopUI2.Models.Settings; using DesktopUI2.ViewModels; -using Sentry.Reflection; using Speckle.Core.Kits; using Speckle.Core.Models; @@ -16,9 +15,9 @@ namespace DesktopUI2; public abstract class ConnectorBindings { - public string ConnectorVersion => Assembly.GetAssembly(GetType()).GetNameAndVersion().Version; + public string ConnectorVersion => Assembly.GetAssembly(GetType()).GetName().Version.ToString(); - public string ConnectorName => Assembly.GetAssembly(GetType()).GetNameAndVersion().Name; + public string ConnectorName => Assembly.GetAssembly(GetType()).GetName().Name; //public List SavedStreamStates = new List(); diff --git a/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/AdvanceSteel/Properties/ASBaseProperties.cs b/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/AdvanceSteel/Properties/ASBaseProperties.cs index c4d51c0744..c1c00b44f8 100644 --- a/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/AdvanceSteel/Properties/ASBaseProperties.cs +++ b/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/AdvanceSteel/Properties/ASBaseProperties.cs @@ -1,16 +1,6 @@ #if ADVANCESTEEL using System; using System.Collections.Generic; -using System.Reflection; -using System.Text; -using Autodesk.AdvanceSteel.DocumentManagement; -using Autodesk.AdvanceSteel.DotNetRoots.Units; -using Autodesk.AutoCAD.DatabaseServices; -using Autodesk.AutoCAD.Internal.DatabaseServices; -using Objects.Structural.Analysis; -using Sentry; -using Speckle.Core.Kits; -using Speckle.Newtonsoft.Json.Linq; using static Autodesk.AdvanceSteel.DotNetRoots.Units.Unit; namespace Objects.Converter.AutocadCivil; From e5a38f31ab19769493256804d5e7d86bcb44e36d Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Mon, 29 Jul 2024 16:00:29 +0100 Subject: [PATCH 42/52] Cnx 9910 v2 needs better open uri handling (#3588) * Use new Open functions to get Process.Start for all platforms correct * fmt * Build fixes * remove extra usings * remove even more usings * even even more usings * Get rid of the catch just in case --- .../CreateStreamViewCustomization.cs | 3 +- .../ReceiveNode/ReceiveViewCustomization.cs | 3 +- .../SendNode/SendViewCustomization.cs | 3 +- .../ConnectorDynamo/ViewNode/View.cs | 4 +-- .../SpeckleWatchHandler.cs | 3 +- .../Accounts/Accounts.ListAccounts.cs | 4 +-- .../ConnectorGrasshopperShared/Loader.cs | 15 +++++---- .../Deprecated/Operations.ReceiveComponent.cs | 5 ++- .../Deprecated/Operations.SendComponent.cs | 3 +- .../Operations.SendComponentSync.cs | 4 +-- .../Operations.VariableInputSendComponent.cs | 3 +- .../Ops/Operations.SyncSendComponent.cs | 4 +-- ...perations.VariableInputReceiveComponent.cs | 4 +-- .../Operations.VariableInputSendComponent.cs | 2 +- .../ConnectorNavisworks/Entry/Ribbon.xaml.cs | 4 +-- ConnectorRevit/ConnectorRevit/Entry/App.cs | 4 +-- .../ConnectorRevit/Entry/HelpCommand.cs | 10 +++--- .../Operations/Operations.Send.Obsolete.cs | 2 +- Core/Core/Credentials/AccountManager.cs | 3 +- Core/Core/Helpers/Open.cs | 31 +++++++++++++++++++ Core/Core/Logging/SpeckleLog.cs | 2 +- Core/Core/Transports/Server.cs | 2 +- DesktopUI2/DesktopUI2/DummyBindings.cs | 5 ++- DesktopUI2/DesktopUI2/Utils.cs | 5 +-- .../DesktopUI2/ViewModels/CommentViewModel.cs | 4 +-- .../DesktopUI2/ViewModels/DialogViewModel.cs | 1 - .../DesktopUI2/ViewModels/HomeViewModel.cs | 5 ++- .../MappingTool/MappingsViewModel.cs | 11 ++----- .../ViewModels/NotificationViewModel.cs | 4 +-- .../ViewModels/OneClickViewModel.cs | 4 +-- .../ViewModels/ProgressViewModel.cs | 10 +----- .../DesktopUI2/ViewModels/StreamViewModel.cs | 10 ++---- .../ConverterAutocadCivil.Beams.cs | 1 - 33 files changed, 94 insertions(+), 84 deletions(-) create mode 100644 Core/Core/Helpers/Open.cs diff --git a/ConnectorDynamo/ConnectorDynamo/CreateStreamNode/CreateStreamViewCustomization.cs b/ConnectorDynamo/ConnectorDynamo/CreateStreamNode/CreateStreamViewCustomization.cs index bc03bd4d7b..004466b5e3 100644 --- a/ConnectorDynamo/ConnectorDynamo/CreateStreamNode/CreateStreamViewCustomization.cs +++ b/ConnectorDynamo/ConnectorDynamo/CreateStreamNode/CreateStreamViewCustomization.cs @@ -7,6 +7,7 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Threading; +using Speckle.Core.Helpers; namespace Speckle.ConnectorDynamo.CreateStreamNode; @@ -73,7 +74,7 @@ private void UpdateContextMenu() }; viewStreamMenuItem.Click += (a, e) => { - System.Diagnostics.Process.Start($"{createNode.Stream.ServerUrl}/streams/{createNode.Stream.StreamId}"); + Open.Url($"{createNode.Stream.ServerUrl}/streams/{createNode.Stream.StreamId}"); }; _nodeView.grid.ContextMenu.Items.Add(viewStreamMenuItem); } diff --git a/ConnectorDynamo/ConnectorDynamo/ReceiveNode/ReceiveViewCustomization.cs b/ConnectorDynamo/ConnectorDynamo/ReceiveNode/ReceiveViewCustomization.cs index 89acd2cc77..88301a5d2e 100644 --- a/ConnectorDynamo/ConnectorDynamo/ReceiveNode/ReceiveViewCustomization.cs +++ b/ConnectorDynamo/ConnectorDynamo/ReceiveNode/ReceiveViewCustomization.cs @@ -6,6 +6,7 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Threading; +using Speckle.Core.Helpers; namespace Speckle.ConnectorDynamo.ReceiveNode; @@ -99,7 +100,7 @@ private void UpdateContextMenu() }; viewStreamMenuItem.Click += (a, e) => { - System.Diagnostics.Process.Start($"{receiveNode.Stream.ServerUrl}/streams/{receiveNode.Stream.StreamId}"); + Open.Url($"{receiveNode.Stream.ServerUrl}/streams/{receiveNode.Stream.StreamId}"); }; _nodeView.grid.ContextMenu.Items.Add(viewStreamMenuItem); } diff --git a/ConnectorDynamo/ConnectorDynamo/SendNode/SendViewCustomization.cs b/ConnectorDynamo/ConnectorDynamo/SendNode/SendViewCustomization.cs index 8c8e888296..fbc6f138e7 100644 --- a/ConnectorDynamo/ConnectorDynamo/SendNode/SendViewCustomization.cs +++ b/ConnectorDynamo/ConnectorDynamo/SendNode/SendViewCustomization.cs @@ -7,6 +7,7 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Threading; +using Speckle.Core.Helpers; namespace Speckle.ConnectorDynamo.SendNode; @@ -83,7 +84,7 @@ private void UpdateContextMenu() var viewStream = new MenuItem { Header = $"View stream {stream.StreamId} @ {stream.ServerUrl} online ↗" }; viewStream.Click += (a, e) => { - System.Diagnostics.Process.Start($"{stream.ServerUrl}/streams/{stream.StreamId}"); + Open.Url($"{stream.ServerUrl}/streams/{stream.StreamId}"); }; customMenuItems.Add(viewStream); _nodeView.grid.ContextMenu.Items.Add(viewStream); diff --git a/ConnectorDynamo/ConnectorDynamo/ViewNode/View.cs b/ConnectorDynamo/ConnectorDynamo/ViewNode/View.cs index 4ad7db39ec..d3a699cfa6 100644 --- a/ConnectorDynamo/ConnectorDynamo/ViewNode/View.cs +++ b/ConnectorDynamo/ConnectorDynamo/ViewNode/View.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.ComponentModel; -using System.Diagnostics; using System.Linq; using Dynamo.Engine; using Dynamo.Graph.Nodes; @@ -9,6 +8,7 @@ using Newtonsoft.Json; using ProtoCore.AST.AssociativeAST; using Speckle.ConnectorDynamo.Functions; +using Speckle.Core.Helpers; namespace Speckle.ConnectorDynamo.ViewNode; @@ -103,7 +103,7 @@ internal void ViewStream() } AnalyticsUtils.TrackNodeRun("Stream View"); - Process.Start(Url); + Open.Url(Url); } //Cache input value each time it comes available diff --git a/ConnectorDynamo/ConnectorDynamoExtension/SpeckleWatchHandler.cs b/ConnectorDynamo/ConnectorDynamoExtension/SpeckleWatchHandler.cs index 310458f7aa..c212bc035f 100644 --- a/ConnectorDynamo/ConnectorDynamoExtension/SpeckleWatchHandler.cs +++ b/ConnectorDynamo/ConnectorDynamoExtension/SpeckleWatchHandler.cs @@ -6,6 +6,7 @@ using ProtoCore.Mirror; using Speckle.Core.Credentials; +using Speckle.Core.Helpers; using Speckle.Core.Logging; namespace Speckle.ConnectorDynamo.Extension; @@ -34,7 +35,7 @@ WatchHandlerCallback callback node.Clicked += () => { - System.Diagnostics.Process.Start(account.serverInfo.url); + Open.Url(account.serverInfo.url); }; node.Link = account.serverInfo.url; diff --git a/ConnectorGrasshopper/ConnectorGrasshopperShared/Accounts/Accounts.ListAccounts.cs b/ConnectorGrasshopper/ConnectorGrasshopperShared/Accounts/Accounts.ListAccounts.cs index c83d8a55d7..3e87ecbfad 100755 --- a/ConnectorGrasshopper/ConnectorGrasshopperShared/Accounts/Accounts.ListAccounts.cs +++ b/ConnectorGrasshopper/ConnectorGrasshopperShared/Accounts/Accounts.ListAccounts.cs @@ -1,7 +1,6 @@ #nullable enable using System; using System.Collections.Generic; -using System.Diagnostics; using System.Drawing; using System.Linq; using System.Windows.Forms; @@ -13,6 +12,7 @@ using Grasshopper.Kernel.Special; using Grasshopper.Kernel.Types; using Speckle.Core.Credentials; +using Speckle.Core.Helpers; namespace ConnectorGrasshopper.Accounts; @@ -47,7 +47,7 @@ public override bool AppendMenuItems(ToolStripDropDown menu) "Launch the Speckle Manager to add new accounts, or remove old ones.", (s, e) => { - Process.Start("speckle://goto=accounts"); + Open.Url("speckle://goto=accounts"); } ); return true; diff --git a/ConnectorGrasshopper/ConnectorGrasshopperShared/Loader.cs b/ConnectorGrasshopper/ConnectorGrasshopperShared/Loader.cs index 7a2f00ab71..01663e210f 100644 --- a/ConnectorGrasshopper/ConnectorGrasshopperShared/Loader.cs +++ b/ConnectorGrasshopper/ConnectorGrasshopperShared/Loader.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; using System.Threading.Tasks; @@ -213,17 +212,17 @@ private void AddSpeckleMenu(MenuStrip mainMenu) speckleMenu.DropDown.Items.Add( "Community Forum", Resources.forum16, - (o, args) => Process.Start("https://speckle.community/tag/grasshopper") + (o, args) => Open.Url("https://speckle.community/tag/grasshopper") ); speckleMenu.DropDown.Items.Add( "Tutorials", Resources.tutorials16, - (o, args) => Process.Start("https://speckle.systems/tag/grasshopper/") + (o, args) => Open.Url("https://speckle.systems/tag/grasshopper/") ); speckleMenu.DropDown.Items.Add( "Docs", Resources.docs16, - (o, args) => Process.Start("https://speckle.guide/user/grasshopper.html") + (o, args) => Open.Url("https://speckle.guide/user/grasshopper.html") ); speckleMenu.DropDown.Items.Add(new ToolStripSeparator()); @@ -257,11 +256,11 @@ private void AddSpeckleMenu(MenuStrip mainMenu) if (File.Exists(path) || Directory.Exists(path)) { - Process.Start(path); + Open.File(path); } else { - Process.Start(new ProcessStartInfo("https://speckle.systems/download") { UseShellExecute = true }); + Open.Url("https://speckle.systems/download"); } } catch (Exception ex) when (!ex.IsFatal()) @@ -314,9 +313,9 @@ private void CreateHeadlessTemplateMenu() Directory.CreateDirectory(path); } #if MAC - Process.Start("file://" + path); + Open.File("file://" + path); #else - Process.Start("explorer.exe", "/select, " + path); + Open.File("explorer.exe", "/select, " + path); #endif } ); diff --git a/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Deprecated/Operations.ReceiveComponent.cs b/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Deprecated/Operations.ReceiveComponent.cs index 6e535cd975..e4dd583fac 100644 --- a/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Deprecated/Operations.ReceiveComponent.cs +++ b/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Deprecated/Operations.ReceiveComponent.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Drawing; using System.Linq; @@ -23,6 +22,7 @@ using Speckle.Core.Api; using Speckle.Core.Api.SubscriptionModels; using Speckle.Core.Credentials; +using Speckle.Core.Helpers; using Speckle.Core.Logging; using Speckle.Core.Models; using Speckle.Core.Models.Extensions; @@ -273,8 +273,7 @@ public override void AppendAdditionalMenuItems(ToolStripDropDown menu) Menu_AppendItem( menu, $"View commit {ReceivedCommitId} @ {StreamWrapper.ServerUrl} online ↗", - (s, e) => - Process.Start($"{StreamWrapper.ServerUrl}/streams/{StreamWrapper.StreamId}/commits/{ReceivedCommitId}") + (s, e) => Open.Url($"{StreamWrapper.ServerUrl}/streams/{StreamWrapper.StreamId}/commits/{ReceivedCommitId}") ); } } diff --git a/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Deprecated/Operations.SendComponent.cs b/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Deprecated/Operations.SendComponent.cs index c160861f57..e381ec57e9 100644 --- a/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Deprecated/Operations.SendComponent.cs +++ b/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Deprecated/Operations.SendComponent.cs @@ -21,6 +21,7 @@ using Rhino; using Speckle.Core.Api; using Speckle.Core.Credentials; +using Speckle.Core.Helpers; using Speckle.Core.Logging; using Speckle.Core.Models; using Speckle.Core.Models.Extensions; @@ -179,7 +180,7 @@ public override void AppendAdditionalMenuItems(ToolStripDropDown menu) Menu_AppendItem( menu, $"View commit {ow.CommitId} @ {ow.ServerUrl} online ↗", - (s, e) => Process.Start($"{ow.ServerUrl}/streams/{ow.StreamId}/commits/{ow.CommitId}") + (s, e) => Open.Url($"{ow.ServerUrl}/streams/{ow.StreamId}/commits/{ow.CommitId}") ); } } diff --git a/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Deprecated/Operations.SendComponentSync.cs b/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Deprecated/Operations.SendComponentSync.cs index 8ab545b007..9296efe98b 100644 --- a/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Deprecated/Operations.SendComponentSync.cs +++ b/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Deprecated/Operations.SendComponentSync.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Drawing; using System.Linq; @@ -14,6 +13,7 @@ using Grasshopper.Kernel.Types; using Speckle.Core.Api; using Speckle.Core.Credentials; +using Speckle.Core.Helpers; using Speckle.Core.Kits; using Speckle.Core.Models; using Speckle.Core.Models.Extensions; @@ -149,7 +149,7 @@ protected override void AppendAdditionalComponentMenuItems(ToolStripDropDown men Menu_AppendItem( menu, $"View commit {ow.CommitId} @ {ow.ServerUrl} online ↗", - (s, e) => Process.Start($"{ow.ServerUrl}/streams/{ow.StreamId}/commits/{ow.CommitId}") + (s, e) => Open.Url($"{ow.ServerUrl}/streams/{ow.StreamId}/commits/{ow.CommitId}") ); } } diff --git a/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Deprecated/Operations.VariableInputSendComponent.cs b/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Deprecated/Operations.VariableInputSendComponent.cs index 7dfba45788..9389c4d969 100644 --- a/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Deprecated/Operations.VariableInputSendComponent.cs +++ b/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Deprecated/Operations.VariableInputSendComponent.cs @@ -22,6 +22,7 @@ using Rhino; using Speckle.Core.Api; using Speckle.Core.Credentials; +using Speckle.Core.Helpers; using Speckle.Core.Logging; using Speckle.Core.Models; using Speckle.Core.Models.Extensions; @@ -217,7 +218,7 @@ public override void AppendAdditionalMenuItems(ToolStripDropDown menu) Menu_AppendItem( menu, $"View commit {ow.CommitId} @ {ow.ServerUrl} online ↗", - (s, e) => Process.Start($"{ow.ServerUrl}/streams/{ow.StreamId}/commits/{ow.CommitId}") + (s, e) => Open.Url($"{ow.ServerUrl}/streams/{ow.StreamId}/commits/{ow.CommitId}") ); } } diff --git a/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Operations.SyncSendComponent.cs b/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Operations.SyncSendComponent.cs index 7ccbcb324d..1e7031a33c 100644 --- a/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Operations.SyncSendComponent.cs +++ b/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Operations.SyncSendComponent.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Drawing; using System.Linq; using System.Threading.Tasks; @@ -13,6 +12,7 @@ using Grasshopper.Kernel.Types; using Speckle.Core.Api; using Speckle.Core.Credentials; +using Speckle.Core.Helpers; using Speckle.Core.Logging; using Speckle.Core.Models; using Speckle.Core.Models.Extensions; @@ -86,7 +86,7 @@ public override void AppendAdditionalMenuItems(ToolStripDropDown menu) Menu_AppendItem( menu, $"View commit {ow.CommitId} @ {ow.ServerUrl} online ↗", - (s, e) => Process.Start($"{ow.ServerUrl}/streams/{ow.StreamId}/commits/{ow.CommitId}") + (s, e) => Open.Url($"{ow.ServerUrl}/streams/{ow.StreamId}/commits/{ow.CommitId}") ); } } diff --git a/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Operations.VariableInputReceiveComponent.cs b/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Operations.VariableInputReceiveComponent.cs index 0b2e02996c..608e7f4616 100644 --- a/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Operations.VariableInputReceiveComponent.cs +++ b/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Operations.VariableInputReceiveComponent.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Diagnostics; using System.Drawing; using System.Linq; using System.Net.Http; @@ -342,8 +341,7 @@ public override void AppendAdditionalMenuItems(ToolStripDropDown menu) Menu_AppendItem( menu, $"View commit {ReceivedCommitId} @ {StreamWrapper.ServerUrl} online ↗", - (s, e) => - Process.Start($"{StreamWrapper.ServerUrl}/streams/{StreamWrapper.StreamId}/commits/{ReceivedCommitId}") + (s, e) => Open.Url($"{StreamWrapper.ServerUrl}/streams/{StreamWrapper.StreamId}/commits/{ReceivedCommitId}") ); } } diff --git a/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Operations.VariableInputSendComponent.cs b/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Operations.VariableInputSendComponent.cs index cc4d4339e6..b80b5e5980 100644 --- a/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Operations.VariableInputSendComponent.cs +++ b/ConnectorGrasshopper/ConnectorGrasshopperShared/Ops/Operations.VariableInputSendComponent.cs @@ -228,7 +228,7 @@ public override void AppendAdditionalMenuItems(ToolStripDropDown menu) Menu_AppendItem( menu, $"View commit {ow.CommitId} @ {ow.ServerUrl} online ↗", - (s, e) => Process.Start($"{ow.ServerUrl}/streams/{ow.StreamId}/commits/{ow.CommitId}") + (s, e) => Open.Url($"{ow.ServerUrl}/streams/{ow.StreamId}/commits/{ow.CommitId}") ); } } diff --git a/ConnectorNavisworks/ConnectorNavisworks/Entry/Ribbon.xaml.cs b/ConnectorNavisworks/ConnectorNavisworks/Entry/Ribbon.xaml.cs index 081412e1ba..d602f5e96e 100644 --- a/ConnectorNavisworks/ConnectorNavisworks/Entry/Ribbon.xaml.cs +++ b/ConnectorNavisworks/ConnectorNavisworks/Entry/Ribbon.xaml.cs @@ -1,10 +1,10 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Windows.Forms; using Autodesk.Navisworks.Api.Plugins; using Speckle.ConnectorNavisworks.Bindings; +using Speckle.Core.Helpers; using Speckle.Core.Logging; using NavisworksApp = Autodesk.Navisworks.Api.Application; @@ -262,7 +262,7 @@ public override int ExecuteCommand(string commandId, params string[] parameters) case Community.COMMAND: { - Process.Start("https://speckle.community/tag/navisworks"); + Open.Url("https://speckle.community/tag/navisworks"); break; } diff --git a/ConnectorRevit/ConnectorRevit/Entry/App.cs b/ConnectorRevit/ConnectorRevit/Entry/App.cs index a02e17c337..f5f37980c9 100644 --- a/ConnectorRevit/ConnectorRevit/Entry/App.cs +++ b/ConnectorRevit/ConnectorRevit/Entry/App.cs @@ -1,5 +1,4 @@ using System; -using System.Diagnostics; using System.IO; using System.Reflection; using System.Windows.Media; @@ -18,6 +17,7 @@ using Speckle.DllConflictManagement.EventEmitter; using Speckle.DllConflictManagement.ConflictManagementOptions; using Autodesk.Revit.DB.Events; +using Speckle.Core.Helpers; namespace Speckle.ConnectorRevit.Entry; @@ -378,7 +378,7 @@ ex is KitException if (TaskDialogResult.CommandLink1 == tResult) { - Process.Start("https://speckle.community/"); + Open.Url("https://speckle.community/"); } } diff --git a/ConnectorRevit/ConnectorRevit/Entry/HelpCommand.cs b/ConnectorRevit/ConnectorRevit/Entry/HelpCommand.cs index 6c1cdcf8c6..01c558422b 100644 --- a/ConnectorRevit/ConnectorRevit/Entry/HelpCommand.cs +++ b/ConnectorRevit/ConnectorRevit/Entry/HelpCommand.cs @@ -1,9 +1,9 @@ using System; -using System.Diagnostics; using System.IO; using Autodesk.Revit.Attributes; using Autodesk.Revit.DB; using Autodesk.Revit.UI; +using Speckle.Core.Helpers; namespace Speckle.ConnectorRevit.Entry; @@ -12,7 +12,7 @@ public class ForumCommand : IExternalCommand { public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) { - Process.Start("https://speckle.community/"); + Open.Url("https://speckle.community/"); return Result.Succeeded; } } @@ -22,7 +22,7 @@ public class DocsCommand : IExternalCommand { public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) { - Process.Start("https://speckle.guide/user/revit.html"); + Open.Url("https://speckle.guide/user/revit.html"); return Result.Succeeded; } } @@ -32,7 +32,7 @@ public class TutorialsCommand : IExternalCommand { public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) { - Process.Start("https://speckle.systems/tutorials/"); + Open.Url("https://speckle.systems/tutorials/"); return Result.Succeeded; } } @@ -50,7 +50,7 @@ public Result Execute(ExternalCommandData commandData, ref string message, Eleme ); if (File.Exists(path)) { - Process.Start(path); + Open.File(path); } else { diff --git a/Core/Core/Api/Operations/Operations.Send.Obsolete.cs b/Core/Core/Api/Operations/Operations.Send.Obsolete.cs index 2d07a601eb..15f11ebd10 100644 --- a/Core/Core/Api/Operations/Operations.Send.Obsolete.cs +++ b/Core/Core/Api/Operations/Operations.Send.Obsolete.cs @@ -125,7 +125,7 @@ public static async Task Send( transports ??= new List(); using var sqLiteTransport = new SQLiteTransport { TransportName = "LC" }; - if (transports.Count == 0 && useDefaultCache == false) + if (transports.Count == 0 && !useDefaultCache) { throw new ArgumentException( "You need to provide at least one transport: cannot send with an empty transport list and no default cache.", diff --git a/Core/Core/Credentials/AccountManager.cs b/Core/Core/Credentials/AccountManager.cs index bfd619f5c8..6a5bf478b2 100644 --- a/Core/Core/Credentials/AccountManager.cs +++ b/Core/Core/Credentials/AccountManager.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; using System.Net; @@ -561,7 +560,7 @@ private static async Task GetAccessCode(string server, string challenge, var accessCode = ""; - Process.Start(new ProcessStartInfo($"{server}/authn/verify/sca/{challenge}") { UseShellExecute = true }); + Open.Url($"{server}/authn/verify/sca/{challenge}"); var task = Task.Run(() => { diff --git a/Core/Core/Helpers/Open.cs b/Core/Core/Helpers/Open.cs new file mode 100644 index 0000000000..06ac1265b4 --- /dev/null +++ b/Core/Core/Helpers/Open.cs @@ -0,0 +1,31 @@ +using System.Diagnostics; +using System.IO; + +namespace Speckle.Core.Helpers; + +public static class Open +{ + public static void Url(string url) + { + var psi = new ProcessStartInfo { FileName = url, UseShellExecute = true }; + Process.Start(psi); + } + + public static void File(string path, string? arguments = null) + { + var psi = new ProcessStartInfo { FileName = path, UseShellExecute = true }; + FileAttributes attr = System.IO.File.GetAttributes(path); + if (attr.HasFlag(FileAttributes.Directory)) + { + Process.Start(psi); + } + else + { + if (!string.IsNullOrWhiteSpace(arguments)) + { + psi.Arguments = arguments; + } + Process.Start(psi); + } + } +} diff --git a/Core/Core/Logging/SpeckleLog.cs b/Core/Core/Logging/SpeckleLog.cs index f020267fbf..6285549e46 100644 --- a/Core/Core/Logging/SpeckleLog.cs +++ b/Core/Core/Logging/SpeckleLog.cs @@ -229,7 +229,7 @@ public static void OpenCurrentLogFolder() { try { - Process.Start(s_logFolderPath); + Open.File(s_logFolderPath); } catch (FileNotFoundException ex) { diff --git a/Core/Core/Transports/Server.cs b/Core/Core/Transports/Server.cs index e92d238343..2c2eabb991 100644 --- a/Core/Core/Transports/Server.cs +++ b/Core/Core/Transports/Server.cs @@ -198,7 +198,7 @@ private void WriteTimerElapsed(object sender, ElapsedEventArgs e) return; } - if (_totalElapsed > 300 && _isWriting == false && _queue.Count != 0) + if (_totalElapsed > 300 && !_isWriting && _queue.Count != 0) { _totalElapsed = 0; _writeTimer.Enabled = false; diff --git a/DesktopUI2/DesktopUI2/DummyBindings.cs b/DesktopUI2/DesktopUI2/DummyBindings.cs index 5b75c85c76..6a813d5330 100644 --- a/DesktopUI2/DesktopUI2/DummyBindings.cs +++ b/DesktopUI2/DesktopUI2/DummyBindings.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Diagnostics; using System.Globalization; using System.Threading.Tasks; using DesktopUI2.Models; @@ -11,6 +10,7 @@ using Speckle.Core.Api; using Speckle.Core.Api.GraphQL; using Speckle.Core.Credentials; +using Speckle.Core.Helpers; using Speckle.Core.Kits; using Speckle.Core.Models; using Commits = Speckle.Core.Api.Commits; @@ -56,8 +56,7 @@ public override List GetCustomStreamMenuItems() public void OpenLink(StreamState state) { - //to open urls in .net core must set UseShellExecute = true - Process.Start(new ProcessStartInfo(state.ServerUrl) { UseShellExecute = true }); + Open.Url(state.ServerUrl); } public override string GetDocumentId() diff --git a/DesktopUI2/DesktopUI2/Utils.cs b/DesktopUI2/DesktopUI2/Utils.cs index d87a0f990c..90a0cf8b1b 100644 --- a/DesktopUI2/DesktopUI2/Utils.cs +++ b/DesktopUI2/DesktopUI2/Utils.cs @@ -21,6 +21,7 @@ using Speckle.Core.Api; using Speckle.Core.Api.GraphQL.Models; using Speckle.Core.Credentials; +using Speckle.Core.Helpers; using Speckle.Core.Logging; namespace DesktopUI2; @@ -227,11 +228,11 @@ public static void LaunchManager() if (File.Exists(path) || Directory.Exists(path)) { - Process.Start(path); + Open.File(path); } else { - Process.Start(new ProcessStartInfo("https://speckle.systems/download") { UseShellExecute = true }); + Open.Url("https://speckle.systems/download"); } } catch (Exception ex) diff --git a/DesktopUI2/DesktopUI2/ViewModels/CommentViewModel.cs b/DesktopUI2/DesktopUI2/ViewModels/CommentViewModel.cs index 4b43e32a3e..359116a9d7 100644 --- a/DesktopUI2/DesktopUI2/ViewModels/CommentViewModel.cs +++ b/DesktopUI2/DesktopUI2/ViewModels/CommentViewModel.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; using System.Threading.Tasks; @@ -10,6 +9,7 @@ using DesktopUI2.Views; using ReactiveUI; using Speckle.Core.Api; +using Speckle.Core.Helpers; using Speckle.Core.Logging; using Splat; using Stream = System.IO.Stream; @@ -135,7 +135,7 @@ public void OpenComment() url = $"{_client.Account.serverInfo.url}/projects/{StreamId}/"; } - Process.Start(new ProcessStartInfo(url) { UseShellExecute = true }); + Open.Url(url); Analytics.TrackEvent(Analytics.Events.DUIAction, new Dictionary { { "name", "Comment View" } }); } } diff --git a/DesktopUI2/DesktopUI2/ViewModels/DialogViewModel.cs b/DesktopUI2/DesktopUI2/ViewModels/DialogViewModel.cs index 3621f691ee..711d412c38 100644 --- a/DesktopUI2/DesktopUI2/ViewModels/DialogViewModel.cs +++ b/DesktopUI2/DesktopUI2/ViewModels/DialogViewModel.cs @@ -1,4 +1,3 @@ -using DesktopUI2.Models; using ReactiveUI; namespace DesktopUI2.ViewModels; diff --git a/DesktopUI2/DesktopUI2/ViewModels/HomeViewModel.cs b/DesktopUI2/DesktopUI2/ViewModels/HomeViewModel.cs index 4e6169fb8f..d1385cae54 100644 --- a/DesktopUI2/DesktopUI2/ViewModels/HomeViewModel.cs +++ b/DesktopUI2/DesktopUI2/ViewModels/HomeViewModel.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Diagnostics; using System.Linq; using System.Reactive; using System.Threading; @@ -605,7 +604,7 @@ public async void RemoveAccountCommand(Account account) public void OpenProfileCommand(Account account) { - Process.Start(new ProcessStartInfo($"{account.serverInfo.url}/profile") { UseShellExecute = true }); + Open.Url($"{account.serverInfo.url}/profile"); Analytics.TrackEvent( account, Analytics.Events.DUIAction, @@ -633,7 +632,7 @@ public void ViewOnlineCommand(object parameter) url = $"{streamAcc.Account.serverInfo.url.TrimEnd('/')}/projects/{streamAcc.Stream.id}"; } - Process.Start(new ProcessStartInfo(url) { UseShellExecute = true }); + Open.Url(url); Analytics.TrackEvent( streamAcc.Account, Analytics.Events.DUIAction, diff --git a/DesktopUI2/DesktopUI2/ViewModels/MappingTool/MappingsViewModel.cs b/DesktopUI2/DesktopUI2/ViewModels/MappingTool/MappingsViewModel.cs index f0e309d50b..69a9c7e29c 100644 --- a/DesktopUI2/DesktopUI2/ViewModels/MappingTool/MappingsViewModel.cs +++ b/DesktopUI2/DesktopUI2/ViewModels/MappingTool/MappingsViewModel.cs @@ -1,7 +1,6 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Reactive; using System.Threading.Tasks; @@ -15,6 +14,7 @@ using Objects.BuiltElements.Revit; using ReactiveUI; using Speckle.Core.Api; +using Speckle.Core.Helpers; using Speckle.Core.Logging; using Speckle.Core.Models; using Speckle.Core.Transports; @@ -672,7 +672,7 @@ public void SelectAllMappingsCommand() public void OpenGuideCommand() { - Process.Start(new ProcessStartInfo("https://speckle.guide/user/mapping-tool.html") { UseShellExecute = true }); + Open.Url("https://speckle.guide/user/mapping-tool.html"); } public void OpenStreamSelectorCommand() @@ -682,11 +682,6 @@ public void OpenStreamSelectorCommand() public void FeedbackCommand() { - Process.Start( - new ProcessStartInfo("https://speckle.community/t/mapping-tool-for-cad-bim-workflows/4086") - { - UseShellExecute = true - } - ); + Open.Url("https://speckle.community/t/mapping-tool-for-cad-bim-workflows/4086"); } } diff --git a/DesktopUI2/DesktopUI2/ViewModels/NotificationViewModel.cs b/DesktopUI2/DesktopUI2/ViewModels/NotificationViewModel.cs index eb9cf10829..a3fba0479c 100644 --- a/DesktopUI2/DesktopUI2/ViewModels/NotificationViewModel.cs +++ b/DesktopUI2/DesktopUI2/ViewModels/NotificationViewModel.cs @@ -1,9 +1,9 @@ using System; -using System.Diagnostics; using Avalonia.Media; using Material.Icons; using ReactiveUI; using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Helpers; namespace DesktopUI2.ViewModels; @@ -19,7 +19,7 @@ public NotificationViewModel(PendingStreamCollaborator invite, string serverUrl) Message = $"{invite.invitedBy.name} is inviting you to collaborate on '{invite.streamName}'!"; Launch = () => { - Process.Start(new ProcessStartInfo($"{serverUrl}/streams/{invite.streamId}") { UseShellExecute = true }); + Open.Url($"{serverUrl}/streams/{invite.streamId}"); }; } diff --git a/DesktopUI2/DesktopUI2/ViewModels/OneClickViewModel.cs b/DesktopUI2/DesktopUI2/ViewModels/OneClickViewModel.cs index b1f2865823..5204e04330 100644 --- a/DesktopUI2/DesktopUI2/ViewModels/OneClickViewModel.cs +++ b/DesktopUI2/DesktopUI2/ViewModels/OneClickViewModel.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Threading.Tasks; using Avalonia; @@ -8,6 +7,7 @@ using ReactiveUI; using Speckle.Core.Api; using Speckle.Core.Credentials; +using Speckle.Core.Helpers; using Speckle.Core.Logging; using Speckle.Core.Models; using Splat; @@ -235,7 +235,7 @@ private void CloseSendModal() private void ViewOnlineCommand() { - Process.Start(new ProcessStartInfo(Url) { UseShellExecute = true }); + Open.Url(Url); } private void CopyCommand() diff --git a/DesktopUI2/DesktopUI2/ViewModels/ProgressViewModel.cs b/DesktopUI2/DesktopUI2/ViewModels/ProgressViewModel.cs index 61bd5386ed..45cf8e54e3 100644 --- a/DesktopUI2/DesktopUI2/ViewModels/ProgressViewModel.cs +++ b/DesktopUI2/DesktopUI2/ViewModels/ProgressViewModel.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Threading; using System.Web; @@ -130,14 +129,7 @@ public void GetHelpCommand() } var safeReport = HttpUtility.UrlEncode(report); - Process.Start( - new ProcessStartInfo( - $"https://speckle.community/new-topic?title=I%20need%20help%20with...&body={safeReport}&category=help" - ) - { - UseShellExecute = true - } - ); + Open.Url($"https://speckle.community/new-topic?title=I%20need%20help%20with...&body={safeReport}&category=help"); } public void CancelCommand() diff --git a/DesktopUI2/DesktopUI2/ViewModels/StreamViewModel.cs b/DesktopUI2/DesktopUI2/ViewModels/StreamViewModel.cs index 87fd03799d..e5b92759d1 100644 --- a/DesktopUI2/DesktopUI2/ViewModels/StreamViewModel.cs +++ b/DesktopUI2/DesktopUI2/ViewModels/StreamViewModel.cs @@ -1363,16 +1363,10 @@ public async void ViewOnlineSavedStreamCommand() //ensure click transition has finished await Task.Delay(100).ConfigureAwait(true); - OpenUrl(Url); + Open.Url(Url); Analytics.TrackEvent(Analytics.Events.DUIAction, new Dictionary { { "name", "Stream View" } }); } - private void OpenUrl(string url) - { - //to open urls in .net core must set UseShellExecute = true - Process.Start(new ProcessStartInfo(url) { UseShellExecute = true }); - } - public async void CopyStreamURLCommand() { //ensure click transition has finished @@ -1443,7 +1437,7 @@ public async void SendCommand() url = $"{StreamState.ServerUrl}/projects/{StreamState.StreamId}/models/{SelectedBranch.Branch.id}"; } - OpenUrl(url); + Open.Url(url); }, Type = NotificationType.Success, Expiration = TimeSpan.FromSeconds(10) diff --git a/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/AdvanceSteel/ConverterAutocadCivil.Beams.cs b/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/AdvanceSteel/ConverterAutocadCivil.Beams.cs index 1646396b3d..2bd293fa24 100644 --- a/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/AdvanceSteel/ConverterAutocadCivil.Beams.cs +++ b/Objects/Converters/ConverterAutocadCivil/ConverterAutocadCivilShared/AdvanceSteel/ConverterAutocadCivil.Beams.cs @@ -1,5 +1,4 @@ #if ADVANCESTEEL -using System; using System.Collections.Generic; using System.Linq; From eb5757b65c4f1f3fde8389783a22b9af1e5c153a Mon Sep 17 00:00:00 2001 From: Ralph Wessel <150629054+r-wessel@users.noreply.github.com> Date: Tue, 30 Jul 2024 09:59:10 +0100 Subject: [PATCH 43/52] Fix: Throw SpeckleException on unhandled elements (#3599) * Change handling of non-polyline openings to throw SpeckleException rather than nulling object refs (which throws an exception terminating the receive) * Formatted with csharpier --- .../ConnectorArchicad/Converters/ModelConverter.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/ConnectorArchicad/ConnectorArchicad/Converters/ModelConverter.cs b/ConnectorArchicad/ConnectorArchicad/Converters/ModelConverter.cs index b8db6345f7..34f6546e20 100644 --- a/ConnectorArchicad/ConnectorArchicad/Converters/ModelConverter.cs +++ b/ConnectorArchicad/ConnectorArchicad/Converters/ModelConverter.cs @@ -10,6 +10,7 @@ using Objects.Utils; using Speckle.Core.Kits; using static Archicad.Model.MeshModel; +using Speckle.Core.Logging; namespace Archicad.Operations; @@ -562,11 +563,9 @@ out double height if (outline is not Polyline polyline) { - extrusionBasePoint = null; - extrusionXAxis = null; - extrusionYAxis = null; - extrusionZAxis = null; - return; + throw new SpeckleException( + $"An opening of type {outline.GetType()} has been found - only Polyline openings are currently supported" + ); } // Form the 4 points of the rectangle from the polyline From 2211fd9f8e1a7045f86a5b90c923b86b7829e355 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Tue, 30 Jul 2024 11:47:29 +0100 Subject: [PATCH 44/52] Fix circle regex (#3600) need full match on tag --- .circleci/scripts/config-template.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/scripts/config-template.yml b/.circleci/scripts/config-template.yml index 6e981eb308..c6898aaca2 100644 --- a/.circleci/scripts/config-template.yml +++ b/.circleci/scripts/config-template.yml @@ -205,7 +205,7 @@ jobs: # Each project will have individual jobs for each specific task it has to - << pipeline.git.tag >> - not: matches: - pattern: "[-]+" + pattern: "^.*-.*$" value: << pipeline.git.tag >> steps: - run: @@ -220,7 +220,7 @@ jobs: # Each project will have individual jobs for each specific task it has to - << pipeline.git.tag >> - not: matches: - pattern: "[-]+" + pattern: "^.*-.*$" value: << pipeline.git.tag >> steps: - run: From 3f9577b60bff35b99e92a0d2c61ff8e9422a9b5e Mon Sep 17 00:00:00 2001 From: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com> Date: Tue, 30 Jul 2024 14:31:15 +0100 Subject: [PATCH 45/52] Changed dot net installer url (#3602) Changed Installer url --- .circleci/scripts/config-template.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/scripts/config-template.yml b/.circleci/scripts/config-template.yml index c6898aaca2..be97b2f9df 100644 --- a/.circleci/scripts/config-template.yml +++ b/.circleci/scripts/config-template.yml @@ -77,7 +77,7 @@ jobs: # Each project will have individual jobs for each specific task it has to name: Install dotnet command: | curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --channel sts - curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --version 8.0.204 + curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --version 8.0.206 $HOME/.dotnet/dotnet --version - run: name: Enforce formatting From 26c31139fbab0d5e7f88b999a2601c5ac2f14ec8 Mon Sep 17 00:00:00 2001 From: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com> Date: Tue, 30 Jul 2024 15:03:12 +0100 Subject: [PATCH 46/52] Jrm/ci test (#3603) * Changed Installer url * Persist to workspace --- .circleci/scripts/config-template.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.circleci/scripts/config-template.yml b/.circleci/scripts/config-template.yml index be97b2f9df..888b899db8 100644 --- a/.circleci/scripts/config-template.yml +++ b/.circleci/scripts/config-template.yml @@ -214,6 +214,10 @@ jobs: # Each project will have individual jobs for each specific task it has to shell: cmd.exe #does not work in powershell environment: SLUG: << parameters.slug >> + - persist_to_workspace: + root: ./ + paths: + - speckle-sharp-ci-tools/Installers - when: # Setup certificates and build installers signed for tagged builds that aren't prerelease condition: and: @@ -320,7 +324,7 @@ jobs: # Each project will have individual jobs for each specific task it has to name: Install dotnet command: | curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --channel sts - curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --version 8.0.204 + curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --version 8.0.2xx $HOME/.dotnet/dotnet --version $HOME/.dotnet/dotnet --list-runtimes From b341303c48b6856d9dbc89bbc99c925c63a9871b Mon Sep 17 00:00:00 2001 From: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com> Date: Tue, 30 Jul 2024 15:27:08 +0100 Subject: [PATCH 47/52] Jrm/ci test (#3604) * Changed Installer url * Persist to workspace * dotnet version 206 --- .circleci/scripts/config-template.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/scripts/config-template.yml b/.circleci/scripts/config-template.yml index 888b899db8..c62490edc3 100644 --- a/.circleci/scripts/config-template.yml +++ b/.circleci/scripts/config-template.yml @@ -125,7 +125,7 @@ jobs: # Each project will have individual jobs for each specific task it has to name: Install dotnet command: | curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --channel sts - curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --version 8.0.204 + curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --version 8.0.206 $HOME/.dotnet/dotnet --version - run: name: Startup the Speckle Server @@ -324,7 +324,7 @@ jobs: # Each project will have individual jobs for each specific task it has to name: Install dotnet command: | curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --channel sts - curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --version 8.0.2xx + curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --version 8.0.206 $HOME/.dotnet/dotnet --version $HOME/.dotnet/dotnet --list-runtimes From 44a1a37deedd0eb4195bf258c4032be1b2f6e9a4 Mon Sep 17 00:00:00 2001 From: KatKatKateryna <89912278+KatKatKateryna@users.noreply.github.com> Date: Tue, 30 Jul 2024 20:43:17 +0100 Subject: [PATCH 48/52] duplicate list instead of referencing (#3605) --- Objects/Objects/Other/Revit/RevitInstance.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Objects/Objects/Other/Revit/RevitInstance.cs b/Objects/Objects/Other/Revit/RevitInstance.cs index 98614d7511..093cfec906 100644 --- a/Objects/Objects/Other/Revit/RevitInstance.cs +++ b/Objects/Objects/Other/Revit/RevitInstance.cs @@ -19,7 +19,11 @@ public class RevitInstance : Instance protected override IEnumerable GetTransformableGeometry() { - var allChildren = typedDefinition.elements ?? new List(); + var allChildren = new List(); + if (typedDefinition.elements != null) + { + allChildren.AddRange(typedDefinition.elements); + } if (typedDefinition.displayValue.Count != 0) { allChildren.AddRange(typedDefinition.displayValue); From 4f7829cb6e356bb9fc834da5b0c6efeee4ae71e8 Mon Sep 17 00:00:00 2001 From: Patrick He Date: Wed, 31 Jul 2024 14:35:49 +0200 Subject: [PATCH 49/52] Add filter to skip the modifying when the zone of space is null (#3525) Co-authored-by: Jinxuan He Co-authored-by: Dimitrie Stefanescu Co-authored-by: Claire Kuang --- .../ConverterRevitShared/PartialClasses/ConvertZone.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertZone.cs b/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertZone.cs index a0a1e699ca..a12b1f32d3 100644 --- a/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertZone.cs +++ b/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertZone.cs @@ -146,7 +146,7 @@ private DB.Zone CreateRevitZoneIfNeeded(Space speckleSpace, DB.Zone revitZone, P { revitZone = ConvertZoneToRevit(speckleSpace.zone, out _); } - else if (revitZone != null && revitZone.Phase.Name != targetPhase.Name) + else if (revitZone != null && revitZone.Phase.Name != targetPhase.Name && speckleSpace.zone != null) { Doc.Delete(revitZone.Id); From 3faacabd661d14dbc9f35f7330951a586c4df6f9 Mon Sep 17 00:00:00 2001 From: Jonathon Broughton <760691+jsdbroughton@users.noreply.github.com> Date: Wed, 31 Jul 2024 14:47:24 +0100 Subject: [PATCH 50/52] CNX-9912 - Implement a display fallback for Revit Areas equivalent to the outline. (#3594) * Implements displayValue to be the Area outline as there is no method to retrieve or generate a displayMesh * Meshify version held in reserve * Adding TeklaContourPlate correction Inflicted by the same error as AdvanceSteel * fmt --------- Co-authored-by: Claire Kuang --- .../PartialClasses/ConvertArea.cs | 73 +++++++++++++++++-- .../PartialClasses/ConvertContourPlate.cs | 15 +++- .../BuiltElements/AdvanceSteel/AsteelPlate.cs | 3 +- .../BuiltElements/AdvanceSteel/AsteelSlab.cs | 3 +- Objects/Objects/BuiltElements/Area.cs | 4 +- .../TeklaStructures/TeklaContourPlate.cs | 1 + 6 files changed, 86 insertions(+), 13 deletions(-) diff --git a/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertArea.cs b/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertArea.cs index 7d3e199be0..8c43b38e20 100644 --- a/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertArea.cs +++ b/Objects/Converters/ConverterRevit/ConverterRevitShared/PartialClasses/ConvertArea.cs @@ -1,6 +1,13 @@ +using System; +using System.Collections.Generic; using System.Linq; using Autodesk.Revit.DB; +using Objects.Geometry; +using Objects.Utils; +using Speckle.Core.Models; using DB = Autodesk.Revit.DB; +using Line = Objects.Geometry.Line; +using Mesh = Objects.Geometry.Mesh; using Point = Objects.Geometry.Point; namespace Objects.Converter.Revit; @@ -44,13 +51,14 @@ public BuiltElements.Area AreaToSpeckle(DB.Area revitArea) { var profiles = GetProfiles(revitArea); - var speckleArea = new BuiltElements.Area(); - - speckleArea.name = revitArea.get_Parameter(BuiltInParameter.ROOM_NAME).AsString(); - speckleArea.number = revitArea.Number; - speckleArea.center = (Point)LocationToSpeckle(revitArea); - speckleArea.level = ConvertAndCacheLevel(revitArea, BuiltInParameter.ROOM_LEVEL_ID); - if (profiles.Any()) + var speckleArea = new BuiltElements.Area + { + name = revitArea.get_Parameter(BuiltInParameter.ROOM_NAME).AsString(), + number = revitArea.Number, + center = (Point)LocationToSpeckle(revitArea), + level = ConvertAndCacheLevel(revitArea, BuiltInParameter.ROOM_LEVEL_ID) + }; + if (profiles.Count != 0) { speckleArea.outline = profiles[0]; } @@ -63,8 +71,57 @@ public BuiltElements.Area AreaToSpeckle(DB.Area revitArea) GetAllRevitParamsAndIds(speckleArea, revitArea); - //no mesh seems to be retriavable, not even using the SpatialElementGeometryCalculator + //no mesh seems to be retrievable, not even using the SpatialElementGeometryCalculator //speckleArea.displayValue = GetElementDisplayValue(revitArea); + + speckleArea.displayValue ??= new List(); + + if (profiles.Count != 0 && profiles[0] is Polycurve polyCurve) + { + speckleArea.displayValue.Add(polyCurve); + } + + // If life were simple this triangulation world be sufficient - we know areas are 2d planar - but could have curves. + // speckleArea.displayValue.Add(PolycurveToMesh(speckleArea.outline as Polycurve)); + return speckleArea; } + + private static Mesh PolycurveToMesh(Polycurve polycurve) + { + var mesh = new Mesh { units = polycurve.units }; + + // Convert all segments to Lines (assuming they are all Lines) + var segments = polycurve.segments.OfType().ToList(); + + var points = new List(); + foreach (var segment in segments.Where(segment => !PointExists(points, segment.start))) + { + points.Add(segment.start); + } + if (!polycurve.closed && !PointExists(points, segments.Last().end)) + { + points.Add(segments.Last().end); + } + + mesh.vertices = points.SelectMany(p => new List { p.x, p.y, p.z }).ToList(); + + mesh.faces = new List { points.Count }; // First element is the number of vertices in the face + mesh.faces.AddRange(Enumerable.Range(0, points.Count)); + + mesh.TriangulateMesh(); + + return mesh; + } + + private static bool PointExists(List points, Point newPoint) + { + const double TOLERANCE = 1e-6; // Adjust this tolerance as needed + return points.Any( + p => + Math.Abs(p.x - newPoint.x) < TOLERANCE + && Math.Abs(p.y - newPoint.y) < TOLERANCE + && Math.Abs(p.z - newPoint.z) < TOLERANCE + ); + } } diff --git a/Objects/Converters/ConverterTeklaStructures/ConverterTeklaStructuresShared/PartialClasses/ConvertContourPlate.cs b/Objects/Converters/ConverterTeklaStructures/ConverterTeklaStructuresShared/PartialClasses/ConvertContourPlate.cs index deaed09493..3ead851cd0 100644 --- a/Objects/Converters/ConverterTeklaStructures/ConverterTeklaStructuresShared/PartialClasses/ConvertContourPlate.cs +++ b/Objects/Converters/ConverterTeklaStructures/ConverterTeklaStructuresShared/PartialClasses/ConvertContourPlate.cs @@ -3,6 +3,7 @@ using BE = Objects.BuiltElements; using Objects.BuiltElements.TeklaStructures; using System.Linq; +using Speckle.Core.Models; using Tekla.Structures.Model; namespace Objects.Converter.TeklaStructures; @@ -12,6 +13,7 @@ public partial class ConverterTeklaStructures public void ContourPlateToNative(BE.Area area) { if (!(area.outline is Polyline)) { } + var ContourPlate = new ContourPlate(); if (area is TeklaContourPlate) { @@ -25,8 +27,10 @@ public void ContourPlateToNative(BE.Area area) { ContourPlate.AddContourPoint(ToTeklaContourPoint(cp)); } + SetPartProperties(ContourPlate, contour); } + ContourPlate.Insert(); //Model.CommitChanges(); } @@ -63,7 +67,15 @@ public TeklaContourPlate ContourPlateToSpeckle(Tekla.Structures.Model.ContourPla GetAllUserProperties(specklePlate, plate); var solid = plate.GetSolid(); - specklePlate.displayValue = new List { GetMeshFromSolid(solid) }; + + var displayMesh = GetMeshFromSolid(solid); + if (displayMesh != null) + { + var displayValue = new List(); + displayValue.Add(displayMesh); + specklePlate.displayValue = displayValue; + } + var rebars = plate.GetReinforcements(); if (rebars != null) { @@ -75,6 +87,7 @@ public TeklaContourPlate ContourPlateToSpeckle(Tekla.Structures.Model.ContourPla } } } + return specklePlate; } diff --git a/Objects/Objects/BuiltElements/AdvanceSteel/AsteelPlate.cs b/Objects/Objects/BuiltElements/AdvanceSteel/AsteelPlate.cs index a8335933ce..3fadf25571 100644 --- a/Objects/Objects/BuiltElements/AdvanceSteel/AsteelPlate.cs +++ b/Objects/Objects/BuiltElements/AdvanceSteel/AsteelPlate.cs @@ -6,7 +6,8 @@ namespace Objects.BuiltElements.AdvanceSteel; -public class AsteelPlate : Area, IDisplayValue>, IHasArea, IHasVolume, IAsteelObject +// TODO: This class really shouldn't inherit from Area, but we need to fix the inheritance chain in the future. +public class AsteelPlate : Area, IDisplayValue>, IHasArea, IHasVolume, IAsteelObject { [DetachProperty] public StructuralMaterial? material { get; set; } diff --git a/Objects/Objects/BuiltElements/AdvanceSteel/AsteelSlab.cs b/Objects/Objects/BuiltElements/AdvanceSteel/AsteelSlab.cs index bed8a7c882..85c0010215 100644 --- a/Objects/Objects/BuiltElements/AdvanceSteel/AsteelSlab.cs +++ b/Objects/Objects/BuiltElements/AdvanceSteel/AsteelSlab.cs @@ -6,7 +6,8 @@ namespace Objects.BuiltElements.AdvanceSteel; -public class AsteelSlab : Area, IDisplayValue>, IHasArea, IHasVolume, IAsteelObject +// TODO: This class really shouldn't inherit from Area, but we need to fix the inheritance chain in the future. +public class AsteelSlab : Area, IDisplayValue>, IHasArea, IHasVolume, IAsteelObject { [DetachProperty] public StructuralMaterial? material { get; set; } diff --git a/Objects/Objects/BuiltElements/Area.cs b/Objects/Objects/BuiltElements/Area.cs index 9305f8733f..119d8987c1 100644 --- a/Objects/Objects/BuiltElements/Area.cs +++ b/Objects/Objects/BuiltElements/Area.cs @@ -5,7 +5,7 @@ namespace Objects.BuiltElements; -public class Area : Base, IHasArea, IHasVolume, IDisplayValue> +public class Area : Base, IHasArea, IHasVolume, IDisplayValue> { public Area() { } @@ -31,7 +31,7 @@ public Area(string name, string number, Level level, [SchemaMainParam] Point cen public string units { get; set; } [DetachProperty] - public List displayValue { get; set; } + public List displayValue { get; set; } public double area { get; set; } public double volume { get; set; } diff --git a/Objects/Objects/BuiltElements/TeklaStructures/TeklaContourPlate.cs b/Objects/Objects/BuiltElements/TeklaStructures/TeklaContourPlate.cs index 7fcdcd22dc..c5944452a9 100644 --- a/Objects/Objects/BuiltElements/TeklaStructures/TeklaContourPlate.cs +++ b/Objects/Objects/BuiltElements/TeklaStructures/TeklaContourPlate.cs @@ -7,6 +7,7 @@ namespace Objects.BuiltElements.TeklaStructures; +// TODO: This class really shouldn't inherit from Area, but we need to fix the inheritance chain in the future. public class TeklaContourPlate : Area { [SchemaInfo("ContourPlate", "Creates a TeklaStructures contour plate.", "Tekla", "Structure")] From fdddfd7b3b73fe7b3f9fcc19edcab7ab6aa18fdf Mon Sep 17 00:00:00 2001 From: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com> Date: Wed, 31 Jul 2024 14:53:44 +0100 Subject: [PATCH 51/52] Change regex to Sign rc builds (#3607) Regex on RC builds --- .circleci/scripts/config-template.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/scripts/config-template.yml b/.circleci/scripts/config-template.yml index c62490edc3..cf4a02f4d4 100644 --- a/.circleci/scripts/config-template.yml +++ b/.circleci/scripts/config-template.yml @@ -199,13 +199,13 @@ jobs: # Each project will have individual jobs for each specific task it has to name: Exit if External PR shell: bash.exe command: if [ "$CIRCLE_PR_REPONAME" ]; then circleci-agent step halt; fi - - unless: # Build installers unsigned on non-tagged builds or pre-release tags + - unless: # Build Unsigned Installers for untagged, or wip/alpha/feature/etc. tags (non-stable, non-rc) condition: and: - << pipeline.git.tag >> - not: matches: - pattern: "^.*-.*$" + pattern: "^.*-(?!rc).*$" value: << pipeline.git.tag >> steps: - run: @@ -224,7 +224,7 @@ jobs: # Each project will have individual jobs for each specific task it has to - << pipeline.git.tag >> - not: matches: - pattern: "^.*-.*$" + pattern: "^.*-(?!rc).*$" value: << pipeline.git.tag >> steps: - run: From b65c3fa420798d59828d1ea2ff3a0a4661d2b28d Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Wed, 31 Jul 2024 15:35:05 +0100 Subject: [PATCH 52/52] add null check around assembly & location (#3606) * add null check around assembly & location * Add PathIsValid check * fmt --- .../DllConflictManager.cs | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/ConnectorCore/DllConflictManagement/DllConflictManager.cs b/ConnectorCore/DllConflictManagement/DllConflictManager.cs index 26f34f13c2..3921b8ee36 100644 --- a/ConnectorCore/DllConflictManagement/DllConflictManager.cs +++ b/ConnectorCore/DllConflictManagement/DllConflictManager.cs @@ -101,11 +101,29 @@ HashSet visitedAssemblies } } - private bool ShouldSkipCheckingConflictBecauseOfAssemblyLocation(Assembly loadedAssembly) + private static bool PathIsValid(string path) { + if (path.Any(x => Path.GetInvalidPathChars().Contains(x))) + { + return false; + } + return true; + } + + private bool ShouldSkipCheckingConflictBecauseOfAssemblyLocation(Assembly? loadedAssembly) + { + if (string.IsNullOrWhiteSpace(loadedAssembly?.Location)) + { + return false; + } + string location = loadedAssembly!.Location; + if (!PathIsValid(location)) + { + return false; + } foreach (var exactPath in _exactAssemblyPathsToIgnore) { - if (Path.GetDirectoryName(loadedAssembly.Location) == exactPath) + if (Path.GetDirectoryName(location) == exactPath) { return true; } @@ -113,7 +131,7 @@ private bool ShouldSkipCheckingConflictBecauseOfAssemblyLocation(Assembly loaded foreach (var pathFragment in _assemblyPathFragmentsToIgnore) { - if (loadedAssembly.Location.Contains(pathFragment)) + if (location.Contains(pathFragment)) { return true; }