diff --git a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/Entry/Plugin.cs b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/Entry/Plugin.cs index 5712f6181d..35d346b9a2 100644 --- a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/Entry/Plugin.cs +++ b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/Entry/Plugin.cs @@ -144,7 +144,8 @@ private void RhinoDoc_EndOpenDocument(object sender, DocumentOpenEventArgs e) try { SpeckleCommandMac.CreateOrFocusSpeckle(); - } catch (Exception ex) + } + catch (Exception ex) when (!ex.IsFatal()) { SpeckleLog.Logger.Fatal(ex, "Failed to create or focus Speckle window"); RhinoApp.CommandLineOut.WriteLine($"Speckle error - {ex.ToFormattedString()}"); @@ -189,7 +190,7 @@ protected override LoadReturnCode OnLoad(ref string errorMessage) hostAppVersion ); } - catch (Exception e) + catch (Exception e) when (!e.IsFatal()) { RhinoApp.CommandLineOut.WriteLine("Failed to init speckle logger: " + e.ToFormattedString()); return LoadReturnCode.ErrorShowDialog; @@ -214,7 +215,8 @@ protected override LoadReturnCode OnLoad(ref string errorMessage) { Init(); } - catch (Exception ex) + // need investigation in seq of specific excpetions thrown (FileNotFound, TypeInitialization) + catch (Exception ex) when (!ex.IsFatal()) { SpeckleLog.Logger.Fatal(ex, "Failed to load Speckle Plugin with {exceptionMessage}", ex.Message); errorMessage = $"Failed to load Speckle Plugin with {ex.ToFormattedString()}"; @@ -267,17 +269,30 @@ private void EnsureVersionSettings() using (LogContext.PushProperty("path", path)) { - SpeckleLog.Logger.Debug("Deleting and Updating RUI settings file"); - if (File.Exists(path)) { + SpeckleLog.Logger.Information("Deleting and Updating RUI settings file"); try { File.Delete(path); } - catch (Exception ex) + catch (IOException ioEx) + { + SpeckleLog.Logger.Error( + ioEx, + "Failed to delete Speckle toolbar .rui file with {exceptionMessage}", + ioEx.Message + ); + RhinoApp.CommandLineOut.WriteLine($"Failed to delete Speckle toolbar {path} with {ioEx.ToFormattedString()}"); + } + catch (UnauthorizedAccessException uaEx) { - SpeckleLog.Logger.Warning(ex, "Failed to delete rui file {exceptionMessage}", ex.Message); + SpeckleLog.Logger.Error( + uaEx, + "Failed to delete Speckle toolbar .rui file with {exceptionMessage}", + uaEx.Message + ); + RhinoApp.CommandLineOut.WriteLine($"Failed to delete Speckle toolbar {path} with {uaEx.ToFormattedString()}"); } } } @@ -320,7 +335,7 @@ private void RhinoApp_Idle(object sender, EventArgs e) MappingBindings.UpdateExistingSchemaElements(MappingBindings.GetExistingSchemaElements()); } } - catch (Exception ex) { } + catch (Exception ex) when (!ex.IsFatal()) { } } private void RhinoDoc_DeselectObjects(object sender, RhinoObjectSelectionEventArgs e) diff --git a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/Entry/SpeckleCommandMac.cs b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/Entry/SpeckleCommandMac.cs index a4f30892a1..a5351e526b 100644 --- a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/Entry/SpeckleCommandMac.cs +++ b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/Entry/SpeckleCommandMac.cs @@ -32,7 +32,7 @@ protected override Result RunCommand(RhinoDoc doc, RunMode mode) CreateOrFocusSpeckle(); return Result.Success; } - catch (Exception e) + catch (Exception e) when (!e.IsFatal()) { SpeckleLog.Logger.Fatal(e, "Failed to create or focus Speckle window"); RhinoApp.CommandLineOut.WriteLine($"Speckle Error - { e.ToFormattedString() }"); diff --git a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/Entry/SpeckleCommandWin.cs b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/Entry/SpeckleCommandWin.cs index 05cc5b9ac0..866b39c1c5 100644 --- a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/Entry/SpeckleCommandWin.cs +++ b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/Entry/SpeckleCommandWin.cs @@ -3,6 +3,7 @@ using Rhino; using Rhino.Commands; using Rhino.UI; +using Speckle.Core.Logging; using Speckle.Core.Models.Extensions; namespace SpeckleRhino; @@ -25,8 +26,10 @@ protected override Result RunCommand(RhinoDoc doc, RunMode mode) Panels.OpenPanel(typeof(DuiPanel).GUID); return Result.Success; } - catch (Exception e) + catch (Exception e) when (!e.IsFatal()) { + // needs more investigation. logging to seq for now. + SpeckleLog.Logger.Error(e, "Failed to open Speckle Rhino Connector DuiPanel with {exceptionMessage}", e.Message); RhinoApp.CommandLineOut.WriteLine($"Speckle Error - {e.ToFormattedString()}"); return Result.Failure; } diff --git a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/Entry/SpeckleMappingsCommandMac.cs b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/Entry/SpeckleMappingsCommandMac.cs index b119681630..9b46cb1364 100644 --- a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/Entry/SpeckleMappingsCommandMac.cs +++ b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/Entry/SpeckleMappingsCommandMac.cs @@ -41,7 +41,7 @@ protected override Result RunCommand(RhinoDoc doc, RunMode mode) MainWindow.Activate(); return Result.Success; } - catch (Exception e) + catch (Exception e) when (!e.IsFatal()) { SpeckleLog.Logger.Fatal(e, "Failed to create or focus Speckle mappings window"); RhinoApp.CommandLineOut.WriteLine($"Speckle Error - {e.ToFormattedString()}"); diff --git a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/Entry/SpeckleMappingsCommandWin.cs b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/Entry/SpeckleMappingsCommandWin.cs index 4c997cc0a1..5a772999d5 100644 --- a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/Entry/SpeckleMappingsCommandWin.cs +++ b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/Entry/SpeckleMappingsCommandWin.cs @@ -4,6 +4,7 @@ using Rhino; using Rhino.Commands; using Rhino.UI; +using Speckle.Core.Logging; using Speckle.Core.Models.Extensions; namespace SpeckleRhino; @@ -26,8 +27,10 @@ protected override Result RunCommand(RhinoDoc doc, RunMode mode) Panels.OpenPanel(typeof(MappingsPanel).GUID); return Result.Success; } - catch (Exception e) + catch (Exception e) when (!e.IsFatal()) { + // needs more investigation. logging to seq for now. + SpeckleLog.Logger.Error(e, "Failed to open Speckle Rhino Mapper DuiPanel with {exceptionMessage}", e.Message); RhinoApp.CommandLineOut.WriteLine($"Speckle Error - {e.ToFormattedString()}"); return Result.Failure; } diff --git a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Previews.cs b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Previews.cs index 67eb7e402f..3c339dbf71 100644 --- a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Previews.cs +++ b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Previews.cs @@ -8,6 +8,7 @@ using Rhino.DocObjects; using Speckle.Core.Api; using Speckle.Core.Kits; +using Speckle.Core.Logging; using Speckle.Core.Models; namespace SpeckleRhino; @@ -195,16 +196,15 @@ public override async Task PreviewReceive(StreamState state, Progre } // create display conduit - try + PreviewConduit = new PreviewConduit(Preview); + if (PreviewConduit.Preview.Count == 0) { - PreviewConduit = new PreviewConduit(Preview); - } - catch (Exception e) - { - progress.Report.OperationErrors.Add(new Exception($"Could not create preview: {e.Message}")); + SpeckleLog.Logger.Information("No previewable geometry was found."); + progress.Report.OperationErrors.Add(new Exception($"No previewable objects found.")); ResetDocument(); return null; } + PreviewConduit.Enabled = true; Doc.Views.ActiveView.ActiveViewport.ZoomBoundingBox(PreviewConduit.bbox); Doc.Views.Redraw(); diff --git a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Receive.cs b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Receive.cs index 29bbf0b3a7..ab9b5b46a2 100644 --- a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Receive.cs +++ b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Receive.cs @@ -314,26 +314,29 @@ public override async Task ReceiveStream(StreamState state, Progres // gets objects by id directly or by application id user string private List GetObjectsByApplicationId(string applicationId) { + List match = new(); + if (string.IsNullOrEmpty(applicationId)) { - return new List(); + return match; } // first try to find the object by app id user string - var match = Doc.Objects.FindByUserString(ApplicationIdKey, applicationId, true).ToList(); + if (Doc.Objects.FindByUserString(ApplicationIdKey, applicationId, true) is RhinoObject[] foundObjects) + { + match = foundObjects.ToList(); + } // if nothing is found, look for the geom obj by its guid directly - if (!match.Any()) + if (match.Count == 0) { - try + if (Utils.GetGuidFromString(applicationId, out Guid id)) { - RhinoObject obj = Doc.Objects.FindId(new Guid(applicationId)); - if (obj != null) + if (Doc.Objects.FindId(id) is RhinoObject obj) { match.Add(obj); } } - catch { } } return match; @@ -542,9 +545,10 @@ private void BakeObject( Base render = obj["renderMaterial"] as Base ?? obj["@renderMaterial"] as Base; if (display != null) { - if (converter.ConvertToNative(display) is ObjectAttributes displayAttribute) + var convertedDisplay = converter.ConvertToNative(display) as ObjectAttributes; + if (convertedDisplay is not null) { - attributes = displayAttribute; + attributes = convertedDisplay; } } else if (render != null) @@ -607,8 +611,8 @@ private void BakeObject( // handle render material if (render != null) { - var convertedMaterial = converter.ConvertToNative(render) as RenderMaterial; //Maybe wrap in try catch in case no conversion exists? - if (convertedMaterial != null) + var convertedMaterial = converter.ConvertToNative(render) as RenderMaterial; + if (convertedMaterial is not null) { RhinoObject rhinoObject = Doc.Objects.FindId(id); rhinoObject.RenderMaterial = convertedMaterial; @@ -672,11 +676,7 @@ private void SetUserInfo(Base obj, ObjectAttributes attributes, ApplicationObjec // set application id var appId = parent != null ? parent.applicationId : obj.applicationId; - try - { - attributes.SetUserString(ApplicationIdKey, appId); - } - catch { } + attributes.SetUserString(ApplicationIdKey, appId); // set user dictionaries if (obj[UserDictionary] is Base userDictionary) diff --git a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Selection.cs b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Selection.cs index ddab48b1f5..f770a25efc 100644 --- a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Selection.cs +++ b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/ConnectorBindingsRhino.Selection.cs @@ -76,37 +76,23 @@ public override List GetSelectionFilters() public override void SelectClientObjects(List objs, bool deselect = false) { - var isPreview = PreviewConduit != null && PreviewConduit.Enabled ? true : false; + var isPreview = PreviewConduit != null && PreviewConduit.Enabled; foreach (var id in objs) { - RhinoObject obj = null; - try + if (Utils.GetGuidFromString(id, out Guid guid)) { - obj = Doc.Objects.FindId(new Guid(id)); // this is a rhinoobj - } - catch - { - continue; // this was a named view! - } - - if (obj != null) - { - if (deselect) + if (Doc.Objects.FindId(guid) is RhinoObject obj) { - obj.Select(false, true, false, true, true, true); + obj.Select(!deselect, true, true, true, true, true); } - else + else if (isPreview) { - obj.Select(true, true, true, true, true, true); + PreviewConduit.Enabled = false; + PreviewConduit.SelectPreviewObject(id, deselect); + PreviewConduit.Enabled = true; } } - else if (isPreview) - { - PreviewConduit.Enabled = false; - PreviewConduit.SelectPreviewObject(id, deselect); - PreviewConduit.Enabled = true; - } } Doc.Views.ActiveView.ActiveViewport.ZoomExtentsSelected(); diff --git a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/DuiPanel.xaml.cs b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/DuiPanel.xaml.cs index 1e5e51a768..af5a81ee18 100644 --- a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/DuiPanel.xaml.cs +++ b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/DuiPanel.xaml.cs @@ -3,6 +3,7 @@ using System.Windows.Controls; using DesktopUI2.ViewModels; using DesktopUI2.Views; +using Speckle.Core.Logging; namespace SpeckleRhino; @@ -24,6 +25,6 @@ public DuiPanel() DataContext = viewModel; AvaloniaHost.Content = new MainUserControl(); } - catch (Exception ex) { } + catch (Exception ex) when (!ex.IsFatal()) { } } } diff --git a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/MappingBindingsRhino.cs b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/MappingBindingsRhino.cs index e4eb11c24c..d43812e920 100644 --- a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/MappingBindingsRhino.cs +++ b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/MappingBindingsRhino.cs @@ -28,14 +28,18 @@ public override MappingSelectionInfo GetSelectionInfo() { try { - var selection = RhinoDoc.ActiveDoc.Objects.GetSelectedObjects(false, false).ToList(); - var result = new List(); + List selection = RhinoDoc.ActiveDoc.Objects.GetSelectedObjects(false, false)?.ToList(); + if (selection is null) + { + return new MappingSelectionInfo(new List(), 0); + } + var result = new List(); foreach (var obj in selection) { - var schemas = GetObjectSchemas(obj); + List schemas = GetObjectSchemas(obj); - if (!result.Any()) + if (result.Count == 0) { result = schemas; } @@ -44,11 +48,15 @@ public override MappingSelectionInfo GetSelectionInfo() //intersect lists //TODO: if some elements already have a schema and values are different //we should default to an empty schema, instead of potentially restoring the one with values - result = result.Where(x => schemas.Any(y => y.Name == x.Name)).ToList(); + List intersect = result.Where(x => schemas.Any(y => y.Name == x.Name))?.ToList(); + if (intersect is not null) + { + result = intersect; + } } //incompatible selection - if (!result.Any()) + if (result.Count == 0) { return new MappingSelectionInfo(new List(), selection.Count); } @@ -56,10 +64,11 @@ public override MappingSelectionInfo GetSelectionInfo() return new MappingSelectionInfo(result, selection.Count); } - catch (Exception ex) + catch (Exception ex) when (!ex.IsFatal()) { + // check seq to see if this has ever been thrown SpeckleLog.Logger.Error(ex, "Could not get selection info: {exceptionMessage}", ex.Message); - return new MappingSelectionInfo(new List(), 0); + throw; } } @@ -74,8 +83,7 @@ private List GetObjectSchemas(RhinoObject obj) try { - var existingSchema = GetExistingObjectSchema(obj); - if (existingSchema != null) + if (GetExistingObjectSchema(obj) is Schema existingSchema) { result.Add(existingSchema); } @@ -158,7 +166,7 @@ private List GetObjectSchemas(RhinoObject obj) } } } - catch (Exception ex) + catch (Exception ex) when (!ex.IsFatal()) { SpeckleLog.Logger.Error(ex, "Could not get object schemas: {exceptionMessage}", ex.Message); } @@ -278,8 +286,13 @@ private Schema GetExistingObjectSchema(RhinoObject obj) return JsonConvert.DeserializeObject(viewModel, settings); } - catch + catch (Exception ex) when (!ex.IsFatal()) { + SpeckleLog.Logger.Error( + ex, + "Could not deserialize Speckle mapping view key into a schema: {exceptionMessage}", + ex.Message + ); return null; } } @@ -298,20 +311,16 @@ public override void SetMappings(string schema, string viewModel) public override void ClearMappings(List ids) { - foreach (var id in ids) + foreach (string id in ids) { - try + if (Utils.GetGuidFromString(id, out Guid guid)) { - var obj = RhinoDoc.ActiveDoc.Objects.FindId(new Guid(id)); - if (obj == null) + if (RhinoDoc.ActiveDoc.Objects.FindId(guid) is RhinoObject obj) { - continue; + obj.Attributes.DeleteUserString(SpeckleMappingKey); + obj.Attributes.DeleteUserString(SpeckleMappingViewKey); } - - obj.Attributes.DeleteUserString(SpeckleMappingKey); - obj.Attributes.DeleteUserString(SpeckleMappingViewKey); } - catch { } } SpeckleRhinoConnectorPlugin.Instance.ExistingSchemaLogExpired = true; @@ -339,28 +348,29 @@ public override List GetExistingSchemaElements() public override void HighlightElements(List ids) { - try + if (ids is not null && Display is not null) { Display.ObjectIds = ids; - RhinoDoc.ActiveDoc?.Views.Redraw(); - } - catch (Exception ex) - { - //fail silently + RhinoDoc.ActiveDoc?.Views?.Redraw(); } } public override void SelectElements(List ids) { - try + if (ids is not null) { - RhinoDoc.ActiveDoc.Objects.UnselectAll(); - RhinoDoc.ActiveDoc.Objects.Select(ids.Select(x => Guid.Parse(x))); - RhinoDoc.ActiveDoc?.Views.Redraw(); - } - catch (Exception ex) - { - //fail silently + RhinoDoc.ActiveDoc?.Objects?.UnselectAll(); + List guids = new(); + foreach (string id in ids) + { + if (Utils.GetGuidFromString(id, out Guid guid)) + { + guids.Add(guid); + } + } + + RhinoDoc.ActiveDoc?.Objects?.Select(guids); + RhinoDoc.ActiveDoc?.Views?.Redraw(); } } } diff --git a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/MappingsPanel.xaml.cs b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/MappingsPanel.xaml.cs index 89cdbe23ef..a1a6acaafd 100644 --- a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/MappingsPanel.xaml.cs +++ b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/UI/MappingsPanel.xaml.cs @@ -3,6 +3,7 @@ using System.Windows.Controls; using DesktopUI2.ViewModels.MappingTool; using DesktopUI2.Views; +using Speckle.Core.Logging; namespace SpeckleRhino; @@ -24,6 +25,6 @@ public MappingsPanel() DataContext = viewModel; AvaloniaHost.Content = new MappingsControl(); } - catch (Exception ex) { } + catch (Exception ex) when (!ex.IsFatal()) { } } } diff --git a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/Utils.cs b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/Utils.cs index c3b4f8b8e9..c450775a74 100644 --- a/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/Utils.cs +++ b/ConnectorRhino/ConnectorRhino/ConnectorRhinoShared/Utils.cs @@ -1,14 +1,13 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Drawing; using System.Linq; -using System.Text.RegularExpressions; using Rhino; using Rhino.Display; using Rhino.DocObjects; using Rhino.Geometry; using Speckle.Core.Kits; +using Speckle.Core.Logging; using Speckle.Core.Models; using Point = Rhino.Geometry.Point; @@ -27,18 +26,49 @@ public static class Utils public static string AppName = "Rhino"; #endif - public static string invalidRhinoChars = @"{}()"; + public static string invalidRhinoChars = @"{}()[]"; /// - /// Removes invalid characters for Rhino layer and block names + /// Creates a valid name for Rhino layers, blocks, and named views. /// - /// - /// - public static string RemoveInvalidRhinoChars(string str) + /// Layer, block, or named view name + /// The original name if valid, or "@name" if not. + /// From trial and error, names cannot begin with invalidRhinoChars. This has been encountered in grasshopper branch syntax. + public static string MakeValidName(string str) { - // using this to handle grasshopper branch syntax - string cleanStr = str.Replace("{", "").Replace("}", ""); - return cleanStr; + if (string.IsNullOrEmpty(str)) + { + return str; + } + else + { + return invalidRhinoChars.Contains(str[0]) ? $"@{str}" : str; + } + } + + /// + /// Attemps to retrieve a Guid from a string + /// + /// + /// Guid on success, null on failure + public static bool GetGuidFromString(string s, out Guid id) + { + id = Guid.Empty; + if (string.IsNullOrEmpty(s)) + { + return false; + } + + try + { + id = Guid.Parse(s); + } + catch (FormatException) + { + return false; + } + + return true; } /// @@ -53,28 +83,24 @@ public static bool FindObjectBySelectedId(RhinoDoc doc, string id, out object ob { descriptor = string.Empty; obj = null; - try - { - Guid guid = new(id); // try to get guid from object id - RhinoObject geom = doc.Objects.FindId(guid); - if (geom != null) + if (GetGuidFromString(id, out Guid guid)) + { + if (doc.Objects.FindId(guid) is RhinoObject geom) { descriptor = Formatting.ObjectDescriptor(geom); obj = geom; } else { - var layer = doc.Layers.FindId(guid); - if (layer != null) + if (doc.Layers.FindId(guid) is Layer layer) { descriptor = "Layer"; obj = layer; } else { - var standardView = doc.Views.Find(guid)?.ActiveViewport; - if (standardView != null) + if (doc.Views.Find(guid)?.ActiveViewport is RhinoViewport standardView) { descriptor = "Standard View"; obj = new ViewInfo(standardView); @@ -82,7 +108,7 @@ public static bool FindObjectBySelectedId(RhinoDoc doc, string id, out object ob } } } - catch // this was a named view name + else // this was probably a named view (saved by name, not guid) { var viewIndex = doc.NamedViews.FindByName(id); if (viewIndex != -1) @@ -92,48 +118,62 @@ public static bool FindObjectBySelectedId(RhinoDoc doc, string id, out object ob } } - return obj == null ? false : true; + return obj != null; } #region extension methods + /// - /// Finds a layer from its full path + /// Creates a layer from its name and parent /// /// - /// Full path of layer - /// Create the layer if it doesn't already exist - /// Null on failure - /// Note: The created layer path may be different from the input path, due to removal of invalid chars - public static Layer GetLayer(this RhinoDoc doc, string path, bool MakeIfNull = false) + /// + /// The new layer + /// Layer name is invalid. + /// Layer parent could not be set, or a layer with the same name already exists. + public static Layer MakeLayer(this RhinoDoc doc, string name, Layer parentLayer = null) { - Layer MakeLayer(string name, Layer parentLayer = null) + if (!Layer.IsValidName(name)) + { + throw new ArgumentException("Layer name is invalid."); + } + + using Layer newLayer = new() { Color = Color.AliceBlue, Name = name }; + if (parentLayer != null) { try { - Layer newLayer = new() { Color = Color.AliceBlue, Name = name }; - if (parentLayer != null) - { - newLayer.ParentLayerId = parentLayer.Id; - } - - int newIndex = doc.Layers.Add(newLayer); - if (newIndex < 0) - { - return null; - } - - return doc.Layers.FindIndex(newIndex); + newLayer.ParentLayerId = parentLayer.Id; } - catch (Exception e) + catch (Exception e) when (!e.IsFatal()) { - return null; + throw new InvalidOperationException("Could not set layer parent id.", e); } } - var cleanPath = RemoveInvalidRhinoChars(path); + int newIndex = doc.Layers.Add(newLayer); + if (newIndex is -1) + { + throw new InvalidOperationException("A layer with the same name already exists."); + } + + return newLayer; + } + + /// + /// Finds a layer from its full path + /// + /// + /// Full path of layer + /// Create the layer if it doesn't already exist + /// The layer on success. On failure, returns null. + /// Note: The created layer path may be different from the input path, due to removal of invalid chars + public static Layer GetLayer(this RhinoDoc doc, string path, bool makeIfNull = false) + { + Layer layer; + var cleanPath = MakeValidName(path); int index = doc.Layers.FindByFullPath(cleanPath, RhinoMath.UnsetIntIndex); - Layer layer = doc.Layers.FindIndex(index); - if (layer == null && MakeIfNull) + if (index is RhinoMath.UnsetIntIndex && makeIfNull) { var layerNames = cleanPath.Split(new[] { Layer.PathSeparator }, StringSplitOptions.RemoveEmptyEntries); @@ -146,18 +186,48 @@ Layer MakeLayer(string name, Layer parentLayer = null) currentLayer = doc.GetLayer(currentLayerPath); if (currentLayer == null) { - currentLayer = MakeLayer(layerNames[i], parent); - } - - if (currentLayer == null) - { - break; + try + { + currentLayer = doc.MakeLayer(layerNames[i], parent); + } + catch (ArgumentException argEx) + { + SpeckleLog.Logger.Error( + argEx, + "Failed to create layer {layerPath} with {exceptionMessage}", + currentLayerPath, + argEx.Message + ); + RhinoApp.CommandLineOut.WriteLine( + $"Failed to create layer {currentLayerPath} while creating {cleanPath}: {argEx.Message}" + ); + break; + } + catch (InvalidOperationException ioEx) + { + SpeckleLog.Logger.Error( + ioEx, + "Failed to create layer {layerPath} with {exceptionMessage}", + currentLayerPath, + ioEx.Message + ); + RhinoApp.CommandLineOut.WriteLine( + $"Failed to create layer {currentLayerPath} while creating {cleanPath}: {ioEx.Message}" + ); + break; + } } parent = currentLayer; } + layer = currentLayer; } + else + { + layer = doc.Layers.FindIndex(index); + } + return layer; } @@ -188,6 +258,7 @@ public static List StandardViews(this RhinoDoc doc) } #region Preview + public class PreviewConduit : DisplayConduit { public BoundingBox bbox; @@ -206,9 +277,15 @@ public PreviewConduit(List preview) foreach (var previewObj in preview) { var converted = new List(); - var toBeConverted = previewObj.Convertible + List toBeConverted = previewObj.Convertible ? previewObj.Converted - : previewObj.Fallback.SelectMany(o => o.Converted).ToList(); + : previewObj.Fallback?.SelectMany(o => o.Converted)?.ToList(); + + if (toBeConverted is null) + { + continue; + } + foreach (var obj in toBeConverted) { switch (obj) @@ -227,14 +304,14 @@ public PreviewConduit(List preview) converted.Add(obj); } - if (!Preview.ContainsKey(previewObj.OriginalId)) + if (!Preview.ContainsKey(previewObj.OriginalId) && converted.Count > 0) { Preview.Add(previewObj.OriginalId, converted); } } } - private Dictionary> Preview { get; set; } = new(); + public Dictionary> Preview { get; set; } = new(); public void SelectPreviewObject(string id, bool unselect = false) { @@ -361,63 +438,4 @@ public static string ObjectDescriptor(RhinoObject obj) var simpleType = obj.ObjectType.ToString(); return obj.HasName ? $"{simpleType}" : $"{simpleType} {obj.Name}"; } - - public static string TimeAgo(string timestamp) - { - //TODO: this implementation is almost the same as Speckle.Core.Api.Helpers - TimeSpan timeAgo; - try - { - timeAgo = DateTime.Now.Subtract(DateTime.Parse(timestamp)); - } - catch (FormatException e) - { - Debug.WriteLine("Could not parse the string to a DateTime"); - return ""; - } - - if (timeAgo.TotalSeconds < 60) - { - return "less than a minute ago"; - } - - if (timeAgo.TotalMinutes < 60) - { - return $"about {timeAgo.Minutes} minute{PluralS(timeAgo.Minutes)} ago"; - } - - if (timeAgo.TotalHours < 24) - { - return $"about {timeAgo.Hours} hour{PluralS(timeAgo.Hours)} ago"; - } - - if (timeAgo.TotalDays < 7) - { - return $"about {timeAgo.Days} day{PluralS(timeAgo.Days)} ago"; - } - - if (timeAgo.TotalDays < 30) - { - return $"about {timeAgo.Days / 7} week{PluralS(timeAgo.Days / 7)} ago"; - } - - if (timeAgo.TotalDays < 365) - { - return $"about {timeAgo.Days / 30} month{PluralS(timeAgo.Days / 30)} ago"; - } - - return $"over {timeAgo.Days / 356} year{PluralS(timeAgo.Days / 356)} ago"; - } - - public static string PluralS(int num) - { - return num != 1 ? "s" : ""; - } - - public static string CommitInfo(string stream, string branch, string commitId) - { - string formatted = $"{stream}[ {branch} @ {commitId} ]"; - string clean = Regex.Replace(formatted, @"[^\u0000-\u007F]+", string.Empty).Trim(); // remove emojis and trim :( - return clean; - } } diff --git a/Objects/Converters/ConverterRhinoGh/ConverterRhinoGhShared/ConverterRhinoGh.Utils.cs b/Objects/Converters/ConverterRhinoGh/ConverterRhinoGhShared/ConverterRhinoGh.Utils.cs index d9ef3296c9..616c6f9175 100644 --- a/Objects/Converters/ConverterRhinoGh/ConverterRhinoGhShared/ConverterRhinoGh.Utils.cs +++ b/Objects/Converters/ConverterRhinoGh/ConverterRhinoGhShared/ConverterRhinoGh.Utils.cs @@ -310,7 +310,7 @@ public static Layer MakeLayer(RhinoDoc doc, string name, Layer parentLayer = nul throw new ArgumentException("Layer name is invalid."); } - using Layer newLayer = new() { Color = Color.AliceBlue, Name = name }; + Layer newLayer = new() { Color = Color.AliceBlue, Name = name }; if (parentLayer != null) { try diff --git a/Objects/Converters/ConverterRhinoGh/ConverterRhinoGhShared/ConverterRhinoGh.cs b/Objects/Converters/ConverterRhinoGh/ConverterRhinoGhShared/ConverterRhinoGh.cs index 6d5ff49cd2..2da0f8fe4c 100644 --- a/Objects/Converters/ConverterRhinoGh/ConverterRhinoGhShared/ConverterRhinoGh.cs +++ b/Objects/Converters/ConverterRhinoGh/ConverterRhinoGhShared/ConverterRhinoGh.cs @@ -15,6 +15,7 @@ using Rhino.DocObjects; using Speckle.Core.Kits; +using Speckle.Core.Logging; using Speckle.Core.Models; using Plane = Objects.Geometry.Plane; using RH = Rhino.Geometry; @@ -333,16 +334,9 @@ public Base ConvertToSpeckle(object @object) @base = LayerToSpeckle(o); break; default: - if (reportObj != null) - { - reportObj.Update( - status: ApplicationObject.State.Skipped, - logItem: $"{@object.GetType()} type not supported" - ); - Report.UpdateReportObject(reportObj); - } - - return @base; + throw new ConversionNotSupportedException( + $"Rhino object of type {@object.GetType()} is not supported for conversion." + ); } if (@base is null) @@ -370,7 +364,20 @@ public Base ConvertToSpeckle(object @object) @base["@SpeckleSchema"] = schema; } } - catch (Exception ex) + catch (ConversionNotSupportedException e) + { + SpeckleLog.Logger.Information(e, "{exceptionMessage}"); + reportObj?.Update(status: ApplicationObject.State.Skipped, logItem: e.Message); + } + catch (SpeckleException e) + { + SpeckleLog.Logger.Warning(e, "{exceptionMessage}"); + reportObj?.Update( + status: ApplicationObject.State.Failed, + logItem: $"{@object.GetType()} unhandled conversion error: {e.Message}\n{e.StackTrace}" + ); + } + catch (Exception ex) when (!ex.IsFatal()) { reportObj?.Update( status: ApplicationObject.State.Failed, @@ -559,48 +566,48 @@ public object ConvertToNative(Base @object) break; default: - if (reportObj != null) - { - reportObj.Update( - status: ApplicationObject.State.Skipped, - logItem: $"{@object.GetType()} type not supported" - ); - Report.UpdateReportObject(reportObj); - } - - break; + throw new ConversionNotSupportedException( + $"Speckle object of type {@object.GetType()} is not supported for conversion." + ); } } - catch (Exception ex) + catch (ConversionNotSupportedException e) + { + SpeckleLog.Logger.Information(e, "{exceptionMessage}"); + reportObj?.Update(status: ApplicationObject.State.Skipped, logItem: e.Message); + } + catch (SpeckleException e) + { + SpeckleLog.Logger.Warning(e, "{exceptionMessage}"); + reportObj?.Update( + status: ApplicationObject.State.Failed, + logItem: $"{@object.GetType()} unhandled conversion error: {e.Message}\n{e.StackTrace}" + ); + } + catch (Exception e) when (!e.IsFatal()) { - reportObj.Update( + reportObj?.Update( status: ApplicationObject.State.Failed, - logItem: $"{@object.GetType()} unhandled converion error: {ex.Message}\n{ex.StackTrace}" + logItem: $"{@object.GetType()} unhandled conversion error: {e.Message}\n{e.StackTrace}" ); } switch (rhinoObj) { case ApplicationObject o: // some to native methods return an application object (if object is baked to doc during conv) - rhinoObj = o.Converted.Any() ? o.Converted : null; - if (reportObj != null) - { - reportObj.Update( - status: o.Status, - createdIds: o.CreatedIds, - converted: o.Converted, - container: o.Container, - log: o.Log - ); - } + rhinoObj = o.Converted.Count == 0 ? null : o.Converted; + reportObj?.Update( + status: o.Status, + createdIds: o.CreatedIds, + converted: o.Converted, + container: o.Container, + log: o.Log + ); break; default: - if (reportObj != null) - { - reportObj.Update(log: notes); - } + reportObj?.Update(log: notes); break; }