diff --git a/MatterControl.Printing/GCode/GCodeMemoryFile.cs b/MatterControl.Printing/GCode/GCodeMemoryFile.cs index 8c9137a63..7bf5132a9 100644 --- a/MatterControl.Printing/GCode/GCodeMemoryFile.cs +++ b/MatterControl.Printing/GCode/GCodeMemoryFile.cs @@ -942,6 +942,11 @@ namespace MatterControl.Printing return false; } + /// + /// Get the height of this layer (from the top of the previous layer to the top of this layer). + /// + /// + /// public override double GetLayerHeight(int layerIndex) { if (layerHeights.Count > 0) @@ -962,6 +967,11 @@ namespace MatterControl.Printing return .5; } + /// + /// Get the height of the top of this layer as measure from the bed + /// + /// + /// public override double GetLayerTop(int layerIndex) { double total = 0; @@ -972,6 +982,21 @@ namespace MatterControl.Printing return total; } + /// + /// Get the height of the bottom of this layer as measure from the bed + /// + /// + /// + public double GetLayerBottom(int layerIndex) + { + double total = 0; + for (int i = 0; i < layerIndex; i++) + { + total += GetLayerHeight(i); + } + return total; + } + public override int GetLayerIndex(int instructionIndex) { if (instructionIndex >= 0 diff --git a/MatterControlLib/PartPreviewWindow/PrinterTabPage.cs b/MatterControlLib/PartPreviewWindow/PrinterTabPage.cs index 273cefaa9..1fcfb3277 100644 --- a/MatterControlLib/PartPreviewWindow/PrinterTabPage.cs +++ b/MatterControlLib/PartPreviewWindow/PrinterTabPage.cs @@ -461,11 +461,10 @@ namespace MatterHackers.MatterControl.PartPreviewWindow { var bodyRow = new GuiWidget() { - HAnchor = HAnchor.Fit | HAnchor.Center, + HAnchor = HAnchor.Stretch, VAnchor = VAnchor.Top | VAnchor.Fit, //BackgroundColor = new Color(theme.Colors.PrimaryBackgroundColor, 128), MinimumSize = new Vector2(275, 140), - Selectable = false }; // Progress section @@ -477,9 +476,8 @@ namespace MatterHackers.MatterControl.PartPreviewWindow var progressContainer = new FlowLayoutWidget(FlowDirection.TopToBottom) { - Margin = new BorderDouble(50, 0), VAnchor = VAnchor.Center | VAnchor.Fit, - HAnchor = HAnchor.Center | HAnchor.Fit, + HAnchor = HAnchor.Stretch, }; expandingContainer.AddChild(progressContainer); @@ -491,12 +489,88 @@ namespace MatterHackers.MatterControl.PartPreviewWindow }; progressContainer.AddChild(progressDial); + var bottomRow = new GuiWidget() + { + HAnchor = HAnchor.Stretch, + VAnchor = VAnchor.Fit + }; + progressContainer.AddChild(bottomRow); + var timeContainer = new FlowLayoutWidget() { HAnchor = HAnchor.Center | HAnchor.Fit, Margin = 3 }; - progressContainer.AddChild(timeContainer); + bottomRow.AddChild(timeContainer); + + // we can only reslice on 64 bit, because in 64 bit we always have the gcode loaded + if (IntPtr.Size == 8) + { + var resliceButton = new TextButton("Re-Slice", theme) + { + HAnchor = HAnchor.Right, + VAnchor = VAnchor.Center, + Margin = new BorderDouble(0, 0, 7, 0) + }; + bool activelySlicing = false; + resliceButton.Click += (s, e) => + { + resliceButton.Enabled = false; + UiThread.RunOnIdle(async () => + { + if (!activelySlicing + && printer.Settings.IsValid() + && printer.Bed.EditContext.SourceItem != null) + { + activelySlicing = true; + if (bottomRow.Name == null) + { + bottomRow.Name = printer.Bed.EditContext.GCodeFilePath; + } + await ApplicationController.Instance.Tasks.Execute("Saving".Localize(), printer.Bed.SaveChanges); + // start up a new slice on a backgroud thread + await ApplicationController.Instance.SliceItemLoadOutput( + printer, + printer.Bed.Scene, + printer.Bed.EditContext.GCodeFilePath); + // Switch to the 3D layer view if on Model view + if (printer.ViewState.ViewMode == PartViewMode.Model) + { + printer.ViewState.ViewMode = PartViewMode.Layers3D; + } + // when it is done queue it to the change to gcode stream + var message2 = "Would you like to switch to the new G-Code? Before you switch, check that your are seeing the changes you expect.".Localize(); + var caption2 = "Switch to new G-Code?".Localize(); + StyledMessageBox.ShowMessageBox(async (clickedOk2) => + { + if (clickedOk2) + { + if (printer.Connection != null + && (printer.Connection.PrinterIsPrinting || printer.Connection.PrinterIsPaused)) + { + printer.Connection.SwitchToGCode(printer.Bed.EditContext.GCodeFilePath); + bottomRow.Name = printer.Bed.EditContext.GCodeFilePath; + } + } + else + { + await ApplicationController.Instance.SliceItemLoadOutput( + printer, + printer.Bed.Scene, + bottomRow.Name); + } + activelySlicing = false; + resliceButton.Enabled = true; + }, message2, caption2, StyledMessageBox.MessageType.YES_NO, "Switch".Localize(), "Cancel".Localize()); + } + else + { + resliceButton.Enabled = true; + } + }); + }; + bottomRow.AddChild(resliceButton); + } timeContainer.AddChild(new ImageWidget(AggContext.StaticData.LoadIcon("fa-clock_24.png", theme.InvertIcons)) { diff --git a/MatterControlLib/PrinterCommunication/Io/GCodeSwitcher.cs b/MatterControlLib/PrinterCommunication/Io/GCodeSwitcher.cs new file mode 100644 index 000000000..457b4fdf4 --- /dev/null +++ b/MatterControlLib/PrinterCommunication/Io/GCodeSwitcher.cs @@ -0,0 +1,141 @@ +/* +Copyright (c) 2015, Lars Brubaker +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of the FreeBSD Project. +*/ + +using MatterControl.Printing; +using MatterHackers.VectorMath; +using System.Threading; +using System.Threading.Tasks; + +namespace MatterHackers.MatterControl.PrinterCommunication.Io +{ + public class GCodeSwitcher : GCodeStream + { + private GCodeMemoryFile switchToGCode = null; + PrinterConnection printerConnection; + + public GCodeSwitcher(string gcodeFilename, PrinterConnection printerConnection, int startLine = 0) + { + this.printerConnection = printerConnection; + var fileStreaming = GCodeFile.Load(gcodeFilename, + new Vector4(), + new Vector4(), + new Vector4(), + Vector4.One, + CancellationToken.None); + + this.GCodeFile = fileStreaming; + LineIndex = startLine; + } + + public GCodeFile GCodeFile { get; private set; } + public int LineIndex { get; private set; } = -1; + + public override void Dispose() + { + } + + public override string ReadLine() + { + if (LineIndex < GCodeFile.LineCount) + { + if (LineIndex > 1 + && GCodeFile is GCodeMemoryFile currentMemoryFile) + { + if (switchToGCode != null) + { + var prevlayerIndex = currentMemoryFile.GetLayerIndex(LineIndex - 1); + var layerIndex = currentMemoryFile.GetLayerIndex(LineIndex); + // we only try to switch as we are changing layers + if (prevlayerIndex < layerIndex) + { + var currentBottom = currentMemoryFile.GetLayerBottom(layerIndex); + // see if there is a layer height that is compatible in the new gcode + for (int i = 0; i < switchToGCode.LayerCount; i++) + { + // find the first layer in the new code that is greater than or eaqual to our current height + var switchBottom = switchToGCode.GetLayerBottom(i); + if (switchBottom >= currentBottom) + { + // is the current gcode the same or bigger than the new gcode + if (currentBottom >= switchBottom) + { + // switch the first time we can + GCodeFile = switchToGCode; + LineIndex = switchToGCode.GetFirstLayerInstruction(i); + printerConnection.QueueLine($"G92 E{switchToGCode.Instruction(LineIndex).EPosition}", true); + switchToGCode = null; + } + else // only switch if we are within one layer height of the new gcode + { + if(currentBottom - switchBottom < switchToGCode.GetLayerHeight(layerIndex)) + { + GCodeFile = switchToGCode; + LineIndex = switchToGCode.GetFirstLayerInstruction(i); + printerConnection.QueueLine($"G92 E{switchToGCode.Instruction(LineIndex).EPosition}", true); + } + } + // we are done evaluating after the first found layer + break; + } + } + } + } + } + return GCodeFile.Instruction(LineIndex++).Line; + } + + return null; + } + + public override void SetPrinterPosition(PrinterMove position) + { + } + + public void SwitchTo(string gcodeFilename) + { + if (GCodeFile is GCodeMemoryFile) + { + Task.Run(() => + { + var switchToGCode = GCodeFile.Load(gcodeFilename, + new Vector4(), + new Vector4(), + new Vector4(), + Vector4.One, + CancellationToken.None); + + if (switchToGCode is GCodeMemoryFile memoryFile) + { + this.switchToGCode = memoryFile; + } + }); + } + } + } +} \ No newline at end of file diff --git a/MatterControlLib/PrinterCommunication/Io/PrintRecoveryStream.cs b/MatterControlLib/PrinterCommunication/Io/PrintRecoveryStream.cs index d1070623b..5b98b97d4 100644 --- a/MatterControlLib/PrinterCommunication/Io/PrintRecoveryStream.cs +++ b/MatterControlLib/PrinterCommunication/Io/PrintRecoveryStream.cs @@ -38,7 +38,7 @@ namespace MatterHackers.MatterControl.PrinterCommunication.Io public class PrintRecoveryStream : GCodeStream { - private GCodeFileStream internalStream; + private GCodeSwitcher internalStream; private double percentDone; double recoverFeedRate; PrinterMove lastDestination; @@ -48,7 +48,7 @@ namespace MatterHackers.MatterControl.PrinterCommunication.Io public RecoveryState RecoveryState { get; private set; } = RecoveryState.RemoveHeating; private PrinterConfig printer; - public PrintRecoveryStream(PrinterConfig printer, GCodeFileStream internalStream, double percentDone) + public PrintRecoveryStream(PrinterConfig printer, GCodeSwitcher internalStream, double percentDone) { this.printer = printer; this.internalStream = internalStream; diff --git a/MatterControlLib/PrinterCommunication/PrinterConnection.cs b/MatterControlLib/PrinterCommunication/PrinterConnection.cs index a3d90967b..63555a820 100644 --- a/MatterControlLib/PrinterCommunication/PrinterConnection.cs +++ b/MatterControlLib/PrinterCommunication/PrinterConnection.cs @@ -184,7 +184,7 @@ namespace MatterHackers.MatterControl.PrinterCommunication private PrinterMove lastReportedPosition; - private GCodeFileStream gCodeFileStream0 = null; + private GCodeSwitcher gCodeFileSwitcher0 = null; private SendProgressStream sendProgressStream1 = null; private PauseHandlingStream pauseHandlingStream2 = null; private QueuedCommandsStream queuedCommandStream3 = null; @@ -513,6 +513,11 @@ namespace MatterHackers.MatterControl.PrinterCommunication } } + public void SwitchToGCode(string gCodeFilePath) + { + gCodeFileSwitcher0.SwitchTo(gCodeFilePath); + } + public string ComPort => printer.Settings?.Helpers.ComPort(); public string DriverType => (this.ComPort == "Emulator") ? "Emulator" : printer.Settings?.GetValue("driver_type"); @@ -542,9 +547,9 @@ namespace MatterHackers.MatterControl.PrinterCommunication { get { - if (gCodeFileStream0 != null) + if (gCodeFileSwitcher0 != null) { - return gCodeFileStream0?.GCodeFile?.GetLayerIndex(gCodeFileStream0.LineIndex) ?? -1; + return gCodeFileSwitcher0?.GCodeFile?.GetLayerIndex(gCodeFileSwitcher0.LineIndex) ?? -1; } return -1; @@ -597,9 +602,9 @@ namespace MatterHackers.MatterControl.PrinterCommunication return 100.0; } else if (NumberOfLinesInCurrentPrint > 0 - && gCodeFileStream0?.GCodeFile != null) + && gCodeFileSwitcher0?.GCodeFile != null) { - return gCodeFileStream0.GCodeFile.PercentComplete(gCodeFileStream0.LineIndex); + return gCodeFileSwitcher0.GCodeFile.PercentComplete(gCodeFileSwitcher0.LineIndex); } else { @@ -714,12 +719,12 @@ namespace MatterHackers.MatterControl.PrinterCommunication { get { - if (gCodeFileStream0?.GCodeFile == null) + if (gCodeFileSwitcher0?.GCodeFile == null) { return 0; } - return gCodeFileStream0.GCodeFile.Ratio0to1IntoContainedLayer(gCodeFileStream0.LineIndex); + return gCodeFileSwitcher0.GCodeFile.Ratio0to1IntoContainedLayer(gCodeFileSwitcher0.LineIndex); } } @@ -754,22 +759,22 @@ namespace MatterHackers.MatterControl.PrinterCommunication } } - public int TotalLayersInPrint => gCodeFileStream0?.GCodeFile?.LayerCount ?? -1; + public int TotalLayersInPrint => gCodeFileSwitcher0?.GCodeFile?.LayerCount ?? -1; - private int NumberOfLinesInCurrentPrint => gCodeFileStream0?.GCodeFile?.LineCount ?? -1; + private int NumberOfLinesInCurrentPrint => gCodeFileSwitcher0?.GCodeFile?.LineCount ?? -1; public int TotalSecondsInPrint { get { - if (gCodeFileStream0?.GCodeFile?.LineCount > 0) + if (gCodeFileSwitcher0?.GCodeFile?.LineCount > 0) { if (this.FeedRateRatio != 0) { - return (int)(gCodeFileStream0.GCodeFile.TotalSecondsInPrint / this.FeedRateRatio); + return (int)(gCodeFileSwitcher0.GCodeFile.TotalSecondsInPrint / this.FeedRateRatio); } - return (int)(gCodeFileStream0.GCodeFile.TotalSecondsInPrint); + return (int)(gCodeFileSwitcher0.GCodeFile.TotalSecondsInPrint); } return 0; @@ -2031,7 +2036,7 @@ namespace MatterHackers.MatterControl.PrinterCommunication private void ClearQueuedGCode() { - gCodeFileStream0?.GCodeFile?.Clear(); + gCodeFileSwitcher0?.GCodeFile?.Clear(); } private void DonePrintingSdFile(object sender, FoundStringEventArgs e) @@ -2096,24 +2101,19 @@ namespace MatterHackers.MatterControl.PrinterCommunication GCodeStream firstStreamToRead = null; if (gcodeFilename != null) { - gCodeFileStream0 = new GCodeFileStream(GCodeFile.Load(gcodeFilename, - new Vector4(), - new Vector4(), - new Vector4(), - Vector4.One, - CancellationToken.None)); + gCodeFileSwitcher0 = new GCodeSwitcher(gcodeFilename, this); if (this.RecoveryIsEnabled && activePrintTask != null) // We are resuming a failed print (do lots of interesting stuff). { - sendProgressStream1 = new SendProgressStream(printer, new PrintRecoveryStream(printer, gCodeFileStream0, activePrintTask.PercentDone)); + sendProgressStream1 = new SendProgressStream(printer, new PrintRecoveryStream(printer, gCodeFileSwitcher0, activePrintTask.PercentDone)); // And increment the recovery count activePrintTask.RecoveryCount++; activePrintTask.Commit(); } else { - sendProgressStream1 = new SendProgressStream(printer, gCodeFileStream0); + sendProgressStream1 = new SendProgressStream(printer, gCodeFileSwitcher0); } pauseHandlingStream2 = new PauseHandlingStream(printer, sendProgressStream1); @@ -2121,7 +2121,7 @@ namespace MatterHackers.MatterControl.PrinterCommunication } else { - gCodeFileStream0 = null; + gCodeFileSwitcher0 = null; firstStreamToRead = new NotPrintingStream(); } @@ -2167,12 +2167,12 @@ namespace MatterHackers.MatterControl.PrinterCommunication double secondsSinceStartedPrint = timeSinceStartedPrint.Elapsed.TotalSeconds; if (timeSinceStartedPrint.Elapsed.TotalSeconds > 0 - && gCodeFileStream0 != null + && gCodeFileSwitcher0 != null && (secondsSinceUpdateHistory > secondsSinceStartedPrint || secondsSinceUpdateHistory + 1 < secondsSinceStartedPrint - || lineSinceUpdateHistory + 20 < gCodeFileStream0.LineIndex)) + || lineSinceUpdateHistory + 20 < gCodeFileSwitcher0.LineIndex)) { - double currentDone = gCodeFileStream0.GCodeFile.PercentComplete(gCodeFileStream0.LineIndex); + double currentDone = gCodeFileSwitcher0.GCodeFile.PercentComplete(gCodeFileSwitcher0.LineIndex); // Only update the amount done if it is greater than what is recorded. // We don't want to mess up the resume before we actually resume it. if (activePrintTask != null @@ -2190,7 +2190,7 @@ namespace MatterHackers.MatterControl.PrinterCommunication //timer.Restart(); } secondsSinceUpdateHistory = secondsSinceStartedPrint; - lineSinceUpdateHistory = gCodeFileStream0.LineIndex; + lineSinceUpdateHistory = gCodeFileSwitcher0.LineIndex; } Thread.Sleep(5);