From 2bd677cb9c605163878aa87d12b16cc305a0d688 Mon Sep 17 00:00:00 2001 From: Lars Brubaker Date: Mon, 29 Oct 2018 16:38:31 -0700 Subject: [PATCH 1/2] Check that loaded gcode has a print time > 30 seconds or warn Removed gcodeDetails as a type issue: MatterHackers/MCCentral#4309 Printing file that makes less than 30 second gcode does not show error --- .../GCode/GCodeMemoryFile.cs | 17 +- .../ApplicationView/ApplicationController.cs | 17 ++ .../PartPreviewWindow/GCode3DWidget.cs | 6 +- .../GCodeDetails/GCodeDetails.cs | 201 +++++++++--------- .../GCodeDetails/GCodeDetailsView.cs | 39 ++-- .../GCodeDetails/GCodeLayerDetailsView.cs | 15 +- 6 files changed, 140 insertions(+), 155 deletions(-) diff --git a/MatterControl.Printing/GCode/GCodeMemoryFile.cs b/MatterControl.Printing/GCode/GCodeMemoryFile.cs index e7ab8fc54..a1054b522 100644 --- a/MatterControl.Printing/GCode/GCodeMemoryFile.cs +++ b/MatterControl.Printing/GCode/GCodeMemoryFile.cs @@ -117,17 +117,6 @@ namespace MatterControl.Printing GCodeCommandQueue.Insert(insertIndex, printerMachineInstruction); } - public static GCodeFile ParseGCodeString(string gcodeContents, - Vector4 maxAccelerationMmPerS2, - Vector4 maxVelocityMmPerS, - Vector4 velocitySameAsStopMmPerS, - Vector4 speedMultiplier, - CancellationToken cancellationToken) - { - return ParseFileContents(gcodeContents, - maxAccelerationMmPerS2, maxVelocityMmPerS, velocitySameAsStopMmPerS, speedMultiplier, cancellationToken, null); - } - public static GCodeMemoryFile Load(Stream fileStream, Vector4 maxAccelerationMmPerS2, Vector4 maxVelocityMmPerS, @@ -140,9 +129,11 @@ namespace MatterControl.Printing { using (var reader = new StreamReader(fileStream)) { - return ParseFileContents(reader.ReadToEnd(), + var gcodeMemoryFile = ParseFileContents(reader.ReadToEnd(), maxAccelerationMmPerS2, maxVelocityMmPerS, velocitySameAsStopMmPerS, speedMultiplier, cancellationToken, progressReporter); + + return gcodeMemoryFile; } } catch (Exception e) @@ -213,7 +204,7 @@ namespace MatterControl.Printing return crCount + 1; } - public static GCodeMemoryFile ParseFileContents(string gCodeString, + private static GCodeMemoryFile ParseFileContents(string gCodeString, Vector4 maxAccelerationMmPerS2, Vector4 maxVelocityMmPerS, Vector4 velocitySameAsStopMmPerS, diff --git a/MatterControlLib/ApplicationView/ApplicationController.cs b/MatterControlLib/ApplicationView/ApplicationController.cs index e873631e0..946215d2f 100644 --- a/MatterControlLib/ApplicationView/ApplicationController.cs +++ b/MatterControlLib/ApplicationView/ApplicationController.cs @@ -2441,6 +2441,23 @@ If you experience adhesion problems, please re-run leveling." }); }); + if (printer.Bed.LoadedGCode is GCodeMemoryFile gcodeMemoryFile) + { + // try to validate the gcode file and warn if it seems invalid. + // for now the definition of invalid is that it has a print time of < 30 seconds + var estimatedPrintSeconds = gcodeMemoryFile.EstimatedPrintSeconds(); + if (estimatedPrintSeconds < 30) + { + var message = "The time to print this G-Code is estimated to be {0} seconds.\n\nPlease check your part for errors if this is unexpected." + .Localize() + .FormatWith((int)estimatedPrintSeconds); + UiThread.RunOnIdle(() => + { + StyledMessageBox.ShowMessageBox(message, "Warning, very short print".Localize()); + }); + } + } + return Task.CompletedTask; }); diff --git a/MatterControlLib/PartPreviewWindow/GCode3DWidget.cs b/MatterControlLib/PartPreviewWindow/GCode3DWidget.cs index 89abbf841..7fb8b3595 100644 --- a/MatterControlLib/PartPreviewWindow/GCode3DWidget.cs +++ b/MatterControlLib/PartPreviewWindow/GCode3DWidget.cs @@ -136,12 +136,10 @@ namespace MatterHackers.MatterControl.PartPreviewWindow speedsWidget.Visible = renderSpeeds; // Single instance shared across widgets - var gcodeDetails = new GCodeDetails(printer, printer.Bed.LoadedGCode); - loadedGCodeSection.AddChild( new SectionWidget( "Details".Localize(), - new GCodeDetailsView(gcodeDetails, theme) + new GCodeDetailsView(printer.Bed.LoadedGCode, printer, theme) { HAnchor = HAnchor.Stretch, Margin = new BorderDouble(bottom: 3), @@ -156,7 +154,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow loadedGCodeSection.AddChild( new SectionWidget( "Layer".Localize(), - new GCodeLayerDetailsView(gcodeDetails, sceneContext, theme) + new GCodeLayerDetailsView(printer.Bed.LoadedGCode, sceneContext, theme) { HAnchor = HAnchor.Stretch, Margin = new BorderDouble(bottom: 3), diff --git a/MatterControlLib/PartPreviewWindow/GCodeDetails/GCodeDetails.cs b/MatterControlLib/PartPreviewWindow/GCodeDetails/GCodeDetails.cs index bfd025f78..c5b32ea2c 100644 --- a/MatterControlLib/PartPreviewWindow/GCodeDetails/GCodeDetails.cs +++ b/MatterControlLib/PartPreviewWindow/GCodeDetails/GCodeDetails.cs @@ -33,120 +33,34 @@ using MatterHackers.MatterControl.SlicerConfiguration; namespace MatterHackers.MatterControl.PartPreviewWindow { - public class GCodeDetails + public static class GCodeDetails { - private GCodeFile loadedGCode; - - private PrinterConfig printer; - public GCodeDetails(PrinterConfig printer, GCodeFile loadedGCode) + public static string LayerTime(this GCodeFile loadedGCode, int activeLayerIndex) { - this.printer = printer; - this.loadedGCode = loadedGCode; + return loadedGCode.InstructionTime(activeLayerIndex, activeLayerIndex + 1); } - public string SecondsToTime(double seconds) + public static string LayerTimeToHere(this GCodeFile loadedGCode, int activeLayerIndex) { - int secondsRemaining = (int)seconds; - int hoursRemaining = (int)(secondsRemaining / (60 * 60)); - int minutesRemaining = (int)(secondsRemaining / 60 - hoursRemaining * 60); - - secondsRemaining = secondsRemaining % 60; - - if (hoursRemaining > 0) - { - return $"{hoursRemaining} h, {minutesRemaining} min"; - } - else if(minutesRemaining > 10) - { - return $"{minutesRemaining} min"; - } - else - { - return $"{minutesRemaining} min {secondsRemaining} s"; - } + return loadedGCode.InstructionTime(0, activeLayerIndex + 1); } - public string EstimatedPrintTime + public static string LayerTimeFromeHere(this GCodeFile loadedGCode, int activeLayerIndex) { - get - { - if (loadedGCode == null || loadedGCode.LayerCount == 0) - { - return "---"; - } - - return SecondsToTime(loadedGCode.Instruction(0).secondsToEndFromHere); - } + return loadedGCode.InstructionTime(activeLayerIndex + 1, int.MaxValue); } - public double EstimatedPrintSeconds - { - get - { - if (loadedGCode == null || loadedGCode.LayerCount == 0) - { - return 0; - } - - return loadedGCode.Instruction(0).secondsToEndFromHere; - } - } - - public string FilamentUsed => string.Format("{0:0.0} mm", loadedGCode.GetFilamentUsedMm(printer.Settings.GetValue(SettingsKey.filament_diameter))); - - public string FilamentVolume => string.Format("{0:0.00} cm³", loadedGCode.GetFilamentCubicMm(printer.Settings.GetValue(SettingsKey.filament_diameter)) / 1000); - - public string EstimatedMass => this.TotalMass <= 0 ? "Unknown" : string.Format("{0:0.00} g", this.TotalMass); - - public string EstimatedCost => this.TotalCost <= 0 ? "Unknown" : string.Format("${0:0.00}", this.TotalCost); - - public double TotalMass - { - get - { - double filamentDiameter = printer.Settings.GetValue(SettingsKey.filament_diameter); - double filamentDensity = printer.Settings.GetValue(SettingsKey.filament_density); - - return loadedGCode.GetFilamentWeightGrams(filamentDiameter, filamentDensity); - } - } - - public double GetLayerHeight(int layerIndex) + public static string EstimatedPrintTime(this GCodeFile loadedGCode) { if (loadedGCode == null || loadedGCode.LayerCount == 0) { - return 0; + return "---"; } - return loadedGCode.GetLayerHeight(layerIndex); + return SecondsToTime(loadedGCode.Instruction(0).secondsToEndFromHere); } - internal object GetLayerTop(int layerIndex) - { - if (loadedGCode == null || loadedGCode.LayerCount == 0) - { - return 0; - } - - return loadedGCode.GetLayerTop(layerIndex); - } - - public string LayerTime(int activeLayerIndex) - { - return InstructionTime(activeLayerIndex, activeLayerIndex + 1); - } - - public string LayerTimeToHere(int activeLayerIndex) - { - return InstructionTime(0, activeLayerIndex + 1); - } - - public string LayerTimeFromeHere(int activeLayerIndex) - { - return InstructionTime(activeLayerIndex + 1, int.MaxValue); - } - - private string InstructionTime(int startLayer, int endLayer) + private static string InstructionTime(this GCodeFile loadedGCode, int startLayer, int endLayer) { if (loadedGCode == null || loadedGCode.LayerCount == 0) { @@ -160,7 +74,89 @@ namespace MatterHackers.MatterControl.PartPreviewWindow return SecondsToTime(secondsToEndFromStart - secondsToEndFromEnd); } - public string GetLayerFanSpeeds(int activeLayerIndex) + public static string SecondsToTime(double seconds) + { + int secondsRemaining = (int)seconds; + int hoursRemaining = (int)(secondsRemaining / (60 * 60)); + int minutesRemaining = (int)(secondsRemaining / 60 - hoursRemaining * 60); + + secondsRemaining = secondsRemaining % 60; + + if (hoursRemaining > 0) + { + return $"{hoursRemaining} h, {minutesRemaining} min"; + } + else if (minutesRemaining > 10) + { + return $"{minutesRemaining} min"; + } + else + { + return $"{minutesRemaining} min {secondsRemaining} s"; + } + } + + public static double EstimatedPrintSeconds(this GCodeFile loadedGCode) + { + if (loadedGCode == null || loadedGCode.LayerCount == 0) + { + return 0; + } + + return loadedGCode.Instruction(0).secondsToEndFromHere; + } + + public static string FilamentUsed(this GCodeFile loadedGCode, PrinterConfig printer) + { + return string.Format("{0:0.0} mm", loadedGCode.GetFilamentUsedMm(printer.Settings.GetValue(SettingsKey.filament_diameter))); + } + + public static string FilamentVolume(this GCodeFile loadedGCode, PrinterConfig printer) + { + return string.Format("{0:0.00} cm³", loadedGCode.GetFilamentCubicMm(printer.Settings.GetValue(SettingsKey.filament_diameter)) / 1000); + } + + public static string EstimatedMass(this GCodeFile loadedGCode, PrinterConfig printer) + { + var totalMass = TotalCost(loadedGCode, printer); + return totalMass <= 0 ? "Unknown" : string.Format("{0:0.00} g", totalMass); + } + + public static string EstimatedCost(this GCodeFile loadedGCode, PrinterConfig printer) + { + var totalMass = TotalCost(loadedGCode, printer); + return totalMass <= 0 ? "Unknown" : string.Format("${0:0.00}", totalMass); + } + + public static double TotalMass(this GCodeFile loadedGCode, PrinterConfig printer) + { + double filamentDiameter = printer.Settings.GetValue(SettingsKey.filament_diameter); + double filamentDensity = printer.Settings.GetValue(SettingsKey.filament_density); + + return loadedGCode.GetFilamentWeightGrams(filamentDiameter, filamentDensity); + } + + public static double GetLayerHeight(this GCodeFile loadedGCode, int layerIndex) + { + if (loadedGCode == null || loadedGCode.LayerCount == 0) + { + return 0; + } + + return loadedGCode.GetLayerHeight(layerIndex); + } + + internal static object GetLayerTop(this GCodeFile loadedGCode, int layerIndex) + { + if (loadedGCode == null || loadedGCode.LayerCount == 0) + { + return 0; + } + + return loadedGCode.GetLayerTop(layerIndex); + } + + public static string GetLayerFanSpeeds(this GCodeFile loadedGCode, int activeLayerIndex) { if (loadedGCode == null || loadedGCode.LayerCount == 0) { @@ -198,13 +194,10 @@ namespace MatterHackers.MatterControl.PartPreviewWindow return fanSpeeds; } - public double TotalCost + public static double TotalCost(this GCodeFile loadedGCode, PrinterConfig printer) { - get - { - double filamentCost = printer.Settings.GetValue(SettingsKey.filament_cost); - return this.TotalMass / 1000 * filamentCost; - } + double filamentCost = printer.Settings.GetValue(SettingsKey.filament_cost); + return loadedGCode.TotalMass(printer) / 1000 * filamentCost; } } } \ No newline at end of file diff --git a/MatterControlLib/PartPreviewWindow/GCodeDetails/GCodeDetailsView.cs b/MatterControlLib/PartPreviewWindow/GCodeDetails/GCodeDetailsView.cs index 477e1dfbf..cda91467b 100644 --- a/MatterControlLib/PartPreviewWindow/GCodeDetails/GCodeDetailsView.cs +++ b/MatterControlLib/PartPreviewWindow/GCodeDetails/GCodeDetailsView.cs @@ -29,6 +29,7 @@ either expressed or implied, of the FreeBSD Project. using System; using System.Linq; +using MatterControl.Printing; using MatterHackers.Agg; using MatterHackers.Agg.UI; using MatterHackers.Localizations; @@ -41,29 +42,27 @@ namespace MatterHackers.MatterControl.PartPreviewWindow { private EventHandler unregisterEvents; private ThemeConfig theme; - private GCodeDetails gcodeDetails; - public GCodeDetailsView(GCodeDetails gcodeDetails, ThemeConfig theme) + public GCodeDetailsView(GCodeFile gCodeMemoryFile, PrinterConfig printerConfig, ThemeConfig theme) : base(FlowDirection.TopToBottom) { - this.gcodeDetails = gcodeDetails; this.theme = theme; // put in the print time - AddSetting("Print Time".Localize(), gcodeDetails.EstimatedPrintTime); + AddSetting("Print Time".Localize(), gCodeMemoryFile.EstimatedPrintTime()); // show the filament used - AddSetting("Filament Length".Localize(), gcodeDetails.FilamentUsed); + AddSetting("Filament Length".Localize(), gCodeMemoryFile.FilamentUsed(printerConfig)); - AddSetting("Filament Volume".Localize(), gcodeDetails.FilamentVolume); + AddSetting("Filament Volume".Localize(), gCodeMemoryFile.FilamentVolume(printerConfig)); // Cost info is only displayed when available - conditionalCostPanel is invisible when cost <= 0 - TextWidget costTextWidget = AddSetting("Estimated Cost".Localize(), gcodeDetails.EstimatedCost); + TextWidget costTextWidget = AddSetting("Estimated Cost".Localize(), gCodeMemoryFile.EstimatedCost(printerConfig)); - TextWidget massTextWidget = AddSetting("Estimated Mass".Localize(), gcodeDetails.EstimatedMass); + TextWidget massTextWidget = AddSetting("Estimated Mass".Localize(), gCodeMemoryFile.EstimatedMass(printerConfig)); var conditionalCostContainer = costTextWidget.Parent; - conditionalCostContainer.Visible = gcodeDetails.TotalCost > 0; + conditionalCostContainer.Visible = gCodeMemoryFile.TotalCost(printerConfig) > 0; PrinterSettings.SettingChanged.RegisterEvent((s, e) => { @@ -73,32 +72,18 @@ namespace MatterHackers.MatterControl.PartPreviewWindow || stringEvent.Data == SettingsKey.filament_diameter || stringEvent.Data == SettingsKey.filament_density) { - massTextWidget.Text = gcodeDetails.EstimatedMass; - conditionalCostContainer.Visible = gcodeDetails.TotalCost > 0; + massTextWidget.Text = gCodeMemoryFile.EstimatedMass(printerConfig); + conditionalCostContainer.Visible = gCodeMemoryFile.TotalCost(printerConfig) > 0; - if (gcodeDetails.TotalCost > 0) + if (gCodeMemoryFile.TotalCost(printerConfig) > 0) { - costTextWidget.Text = gcodeDetails.EstimatedCost; + costTextWidget.Text = gCodeMemoryFile.EstimatedCost(printerConfig); } } } }, ref unregisterEvents); } - public override void OnLoad(EventArgs args) - { - // try to validate the gcode file and warn if it seems invalid. - // for now the definition of invalid is that it has a print time of < 30 seconds - if(gcodeDetails.EstimatedPrintSeconds < 30) - { - var message = "The time to print this G-Code is estimated to be {0} seconds.\n\nPlease check your part for errors if this is unexpected.".Localize(); - message = message.FormatWith((int)gcodeDetails.EstimatedPrintSeconds); - StyledMessageBox.ShowMessageBox(message, "Warning, very short print".Localize()); - } - - base.OnLoad(args); - } - TextWidget AddSetting(string title, string value) { var textWidget = new TextWidget(value, textColor: theme.Colors.PrimaryTextColor, pointSize: theme.DefaultFontSize) diff --git a/MatterControlLib/PartPreviewWindow/GCodeDetails/GCodeLayerDetailsView.cs b/MatterControlLib/PartPreviewWindow/GCodeDetails/GCodeLayerDetailsView.cs index d0c3e8c05..20dea445d 100644 --- a/MatterControlLib/PartPreviewWindow/GCodeDetails/GCodeLayerDetailsView.cs +++ b/MatterControlLib/PartPreviewWindow/GCodeDetails/GCodeLayerDetailsView.cs @@ -28,6 +28,7 @@ either expressed or implied, of the FreeBSD Project. */ using System; +using MatterControl.Printing; using MatterHackers.Agg.UI; using MatterHackers.Localizations; using MatterHackers.MatterControl.ConfigurationPage; @@ -38,7 +39,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow { private ThemeConfig theme; - public GCodeLayerDetailsView(GCodeDetails gcodeDetails, BedConfig sceneContext, ThemeConfig theme) + public GCodeLayerDetailsView(GCodeFile gCodeMemoryFile, BedConfig sceneContext, ThemeConfig theme) : base(FlowDirection.TopToBottom) { this.theme = theme; @@ -54,12 +55,12 @@ namespace MatterHackers.MatterControl.PartPreviewWindow void UpdateLayerDisplay(object sender, EventArgs e) { layerIndex.Text = $"{sceneContext.ActiveLayerIndex + 1}"; - layerTime.Text = gcodeDetails.LayerTime(sceneContext.ActiveLayerIndex); - layerTimeToHere.Text = gcodeDetails.LayerTimeToHere(sceneContext.ActiveLayerIndex); - layerTimeFromHere.Text = gcodeDetails.LayerTimeFromeHere(sceneContext.ActiveLayerIndex); - layerHeight.Text = $"{gcodeDetails.GetLayerHeight(sceneContext.ActiveLayerIndex):0.###}"; - layerWidth.Text = $"{gcodeDetails.GetLayerTop(sceneContext.ActiveLayerIndex):0.###}"; - var fanSpeed = gcodeDetails.GetLayerFanSpeeds(sceneContext.ActiveLayerIndex); + layerTime.Text = gCodeMemoryFile.LayerTime(sceneContext.ActiveLayerIndex); + layerTimeToHere.Text = gCodeMemoryFile.LayerTimeToHere(sceneContext.ActiveLayerIndex); + layerTimeFromHere.Text = gCodeMemoryFile.LayerTimeFromeHere(sceneContext.ActiveLayerIndex); + layerHeight.Text = $"{gCodeMemoryFile.GetLayerHeight(sceneContext.ActiveLayerIndex):0.###}"; + layerWidth.Text = $"{gCodeMemoryFile.GetLayerTop(sceneContext.ActiveLayerIndex):0.###}"; + var fanSpeed = gCodeMemoryFile.GetLayerFanSpeeds(sceneContext.ActiveLayerIndex); layerFanSpeeds.Text = string.IsNullOrWhiteSpace(fanSpeed) ? "Unchanged" : fanSpeed; } From d99af94609ddb0e706ad10fe9360b9590b8e1842 Mon Sep 17 00:00:00 2001 From: Lars Brubaker Date: Mon, 29 Oct 2018 16:56:29 -0700 Subject: [PATCH 2/2] offset past and copy objects --- .../ApplicationView/ApplicationController.cs | 2 +- .../PartPreviewWindow/View3D/SceneActions.cs | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/MatterControlLib/ApplicationView/ApplicationController.cs b/MatterControlLib/ApplicationView/ApplicationController.cs index 946215d2f..60158c853 100644 --- a/MatterControlLib/ApplicationView/ApplicationController.cs +++ b/MatterControlLib/ApplicationView/ApplicationController.cs @@ -570,7 +570,7 @@ namespace MatterHackers.MatterControl new SceneSelectionOperation() { TitleResolver = () => "Duplicate".Localize(), - Action = (scene) => scene.DuplicateItem(), + Action = (scene) => scene.DuplicateItem(5), IsEnabled = (scene) => scene.SelectedItem != null, Icon = AggContext.StaticData.LoadIcon("duplicate.png").SetPreMultiply(), }, diff --git a/MatterControlLib/PartPreviewWindow/View3D/SceneActions.cs b/MatterControlLib/PartPreviewWindow/View3D/SceneActions.cs index 1745061b0..fa316097e 100644 --- a/MatterControlLib/PartPreviewWindow/View3D/SceneActions.cs +++ b/MatterControlLib/PartPreviewWindow/View3D/SceneActions.cs @@ -48,6 +48,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow { public static class SceneActions { + private static int pastObjectXOffset = 5; public static List GetSelectedItems(this InteractiveScene scene) { var selectedItem = scene.SelectedItem; @@ -187,6 +188,8 @@ namespace MatterHackers.MatterControl.PartPreviewWindow { Clipboard.Instance.SetText("!--IObjectSelection--!"); ApplicationController.ClipboardItem = selectedItem.Clone(); + // put it back in right where we cut it from + pastObjectXOffset = 0; scene.DeleteSelection(); } @@ -199,6 +202,8 @@ namespace MatterHackers.MatterControl.PartPreviewWindow { Clipboard.Instance.SetText("!--IObjectSelection--!"); ApplicationController.ClipboardItem = selectedItem.Clone(); + // when we copy an object put it back in with a slight offset + pastObjectXOffset = 5; } } @@ -224,12 +229,14 @@ namespace MatterHackers.MatterControl.PartPreviewWindow { if (Clipboard.Instance.GetText() == "!--IObjectSelection--!") { - scene.DuplicateItem(ApplicationController.ClipboardItem); + scene.DuplicateItem(pastObjectXOffset, ApplicationController.ClipboardItem); + // each time we put in the object offset it a bit more + pastObjectXOffset += 5; } } } - public static async void DuplicateItem(this InteractiveScene scene, IObject3D sourceItem = null) + public static async void DuplicateItem(this InteractiveScene scene, double xOffset, IObject3D sourceItem = null) { if (sourceItem == null) { @@ -255,6 +262,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow foreach(var item in copyList) { var clonedItem = item.Clone(); + clonedItem.Translate(xOffset); // make the name unique var newName = agg_basics.GetNonCollidingName(item.Name, scene.DescendantsAndSelf().Select((d) => d.Name)); clonedItem.Name = newName; @@ -268,6 +276,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow { var clonedItem = sourceItem.Clone(); + clonedItem.Translate(xOffset); // make the name unique var newName = agg_basics.GetNonCollidingName(sourceItem.Name, scene.DescendantsAndSelf().Select((d) => d.Name)); clonedItem.Name = newName;