// Split up the G-code file into different segments holding corresponding G-code lines plus feedrate // Each segment is created when // - the tool number OR // - the print region as indicated by S3D ("; feature..." or "; ...") // changes. // These segments are combined later on in the post-processing step public async Task PreProcess() { StreamReader reader = new StreamReader(input); string lineBuffer = await reader.ReadLineAsync(); if (lineBuffer == null) { throw new ProcessorException("File is empty"); } if (lineBuffer.Contains("G-Code generated by Simplify3D(R)")) { maxProgress.Report((int)input.Length); double feedrate = DefaultFeedrate; double firstLayerHeight = 0; bool rotaryPrinting = settings.RotaryPrinting != null; int lineNumber = 1, numExtrusions = 0; bool isInterfacingSet = true; GCodeLayer layer = new GCodeLayer(0, 0.0), lastLayer = null; GCodeSegment segment = new GCodeSegment("Initialization", -1, null); layer.Segments.Add(segment); HashSet <int> usedTools = new HashSet <int>(); do { bool writeLine = true; GCodeLine line = new GCodeLine(lineBuffer); if (lineBuffer.StartsWith(";", StringComparison.InvariantCulture)) { if (lineBuffer.StartsWith("; layer ", StringComparison.InvariantCulture)) { segment.LastPosition = lastPoint.Clone(); // Add past layer layers.Add(layer); lastLayer = layer; // Get the Z height. S3D provides it via the comment except before the end string lastParameter = lineBuffer.Split(' ').Last(); double zHeight = (lastParameter == "end") ? double.NaN : double.Parse(lastParameter, FrmMain.numberFormat); if (lineBuffer.StartsWith("; layer 1, Z =", StringComparison.InvariantCulture)) { firstLayerHeight = zHeight; } // Create a new one layer = new GCodeLayer(layer.Number + 1, zHeight); segment = new GCodeSegment(lineBuffer, segment.Tool, segment); layer.Segments.Add(segment); isInterfacingSet = layer.Number < 2; } else if ((layer.Number == 0 && lineNumber > 2 && !lineBuffer.Contains("layerHeight")) || lineBuffer.StartsWith("; tool", StringComparison.InvariantCulture) || lineBuffer.StartsWith("; process", StringComparison.InvariantCulture)) { // Keep first two comment lines but get rid of S3D process description and // remove "; tool" as well as "; process" lines because they are completely useless writeLine = false; // Try to get the tool change parameters if (lineBuffer.Contains("toolChangeRetractionDistance")) { double?value = line.GetFValue(',', true); if (value.HasValue) { toolChangeRetractionDistance = value.Value; } } if (lineBuffer.Contains("toolChangeRetractionSpeed")) { double?value = line.GetFValue(',', true); if (value.HasValue) { toolChangeRetractionSpeed = value.Value; } } } else if (layer.Number > 0) { // T-codes are generated just before a new segment starts string region = lineBuffer.Substring(lineBuffer.StartsWith("; feature", StringComparison.InvariantCulture) ? 9 : 1).Trim(); if (segment.Lines.Count == 0) { segment.Name = region; } else { segment = new GCodeSegment(region, segment.Tool, segment); layer.Segments.Add(segment); } } } else { int?gCode = line.GetIValue('G'); if (gCode.HasValue) { // G0 / G1 if (gCode == 0 || gCode == 1) { double?xParam = line.GetFValue('X'); double?yParam = line.GetFValue('Y'); double?zParam = line.GetFValue('Z'); if (xParam.HasValue) { lastPoint.X = xParam.Value; } if (yParam.HasValue) { if (rotaryPrinting) { yParam = HandleRescale(firstLayerHeight, line, yParam); } lastPoint.Y = yParam.Value; } if (zParam.HasValue) { lastPoint.Z = zParam.Value; } if (numExtrusions < 2) { if (line.GetFValue('E').HasValue) { numExtrusions++; writeLine = false; } } double?fParam = line.GetFValue('F'); if (fParam.HasValue) { feedrate = fParam.Value / 60.0; } if (!isInterfacingSet && segment.Tool != -1 && xParam.HasValue && yParam.HasValue) { segment.IsInterfacing = GetClosestSegment(lastLayer, xParam.Value, yParam.Value)?.Tool != segment.Tool; isInterfacingSet = true; } } // G10 else if (gCode == 10) { int? pParam = line.GetIValue('P'); double?sParam = line.GetFValue('S'); if (pParam.HasValue && pParam.Value > 0 && pParam.Value <= settings.Tools.Length && sParam.HasValue) { // G10 P... S... settings.Tools[pParam.Value - 1].ActiveTemperature = (decimal)sParam.Value; } } else if (gCode == 28) { lastPoint = homingPosition.Clone(); } } else { int?mCode = line.GetIValue('M'); if (mCode.HasValue) { // M104 if (mCode == 104) { double?sParam = line.GetFValue('S'); int? tParam = line.GetIValue('T'); if (sParam.HasValue && tParam.HasValue && tParam.Value > 0 && tParam.Value <= settings.Tools.Length) { ToolSettings toolSettings = settings.Tools[tParam.Value - 1]; if (toolSettings.Type == ToolType.Nozzle) { if (toolSettings.ActiveTemperature <= 0m) { toolSettings.ActiveTemperature = (decimal)sParam.Value; segment.AddLine($"G10 P{tParam} R{toolSettings.StandbyTemperature.ToString(FrmMain.numberFormat)} S{toolSettings.ActiveTemperature.ToString(FrmMain.numberFormat)}"); } else { segment.AddLine($"G10 P{tParam} S{sParam.Value.ToString(FrmMain.numberFormat)}"); } } writeLine = false; } } } else { // T-Code int?tCode = line.GetIValue('T'); if (tCode.HasValue) { if (tCode > 0 && tCode <= settings.Tools.Length) { usedTools.Add(tCode.Value); if (settings.Tools[tCode.Value - 1].Type == ToolType.Nozzle) { // Keep track of tools in use. Tool change sequences are inserted by the post-processor if (segment.Lines.Count <= 1) { segment.Tool = tCode.Value; } else { segment = new GCodeSegment(segment.Name, tCode.Value, segment); layer.Segments.Add(segment); } writeLine = false; } else { // Make sure we don't print with inproperly configured tools... throw new ProcessorException($"Tool {tCode} is not configured as a nozzle (see line {lineNumber})"); } } else if (segment.Lines.Count == 0) { segment.Tool = -1; } else { segment = new GCodeSegment(segment.Name, -1, segment); layer.Segments.Add(segment); } } } } } // Add this line unless it was handled before if (writeLine) { line.Feedrate = feedrate; segment.AddLine(line); } lineBuffer = await reader.ReadLineAsync(); lineNumber++; // Report progress to the UI progress.Report((int)input.Position); } while (lineBuffer != null); layers.Add(layer); RemoveHeatingOfUnusedTools(usedTools); if (rotaryPrinting) { RotaryPrintingFixes(); } } else if (lineBuffer.Contains("Diabase")) { throw new ProcessorException("File has been already processed"); } else { throw new ProcessorException("File was not generated by Simplify3D"); } }
private void InsertPreheatingSequences(ref int iteration) { // Check if at least one tool needs preheating or quit otherwise if (!settings.Tools.Any(tool => tool.PreheatTime > 0.0m)) { iteration += layers.Count * 2 - 1; progress.Report(iteration); return; } var preheatCounters = new Dictionary <int, double>(); // Tool number vs. Elapsed time var position = new Coordinate(); var previousPosition = position.Clone(); // First calculate duration of each line for (var layerNumber = 0; layerNumber < layers.Count - 1; layerNumber++) { var layer = layers[layerNumber]; foreach (var segment in layer.Segments) { foreach (var line in segment.Lines) { int?gCode = line.GetIValue('G'); // G0 / G1 if (gCode == 0 || gCode == 1) { var xParam = line.GetFValue('X'); var yParam = line.GetFValue('Y'); var zParam = line.GetFValue('Z'); var eParam = line.GetFValue('E'); if (xParam.HasValue) { position.X = xParam.Value; } if (yParam.HasValue) { position.Y = yParam.Value; } if (zParam.HasValue) { position.Z = zParam.Value; } line.Distance = Math.Sqrt( Math.Pow(position.X - previousPosition.X, 2) + Math.Pow(position.Y - previousPosition.Y, 2) + Math.Pow(position.Z - previousPosition.Z, 2) + (eParam.HasValue ? Math.Pow(eParam.Value, 2) : 0) ); if (line.Feedrate > 0.0) { var rule = GetRule(segment.Tool, layerNumber, segment); var feedrate = line.Feedrate * (rule == null ? 1 : (rule.SpeedFactor / 100.0)); // TODO: Take into account accelerations here? line.Duration = line.Distance / feedrate; } previousPosition.AssignFrom(position); } else if (gCode == 4) { var sParam = line.GetFValue('S'); if (sParam.HasValue) { line.Duration = sParam.Value; } else { var pParam = line.GetIValue('P'); if (pParam.HasValue) { line.Duration = pParam.Value / 1000.0; } } } else if (gCode == 28) { previousPosition.AssignFrom(homingPosition); position.AssignFrom(homingPosition); } else if (gCode == 32) { previousPosition.AssignFrom(afterProbingPosition); position.AssignFrom(afterProbingPosition); } } } progress.Report(iteration++); } // Now go through the file backwards and insert preheating commands for (var layerNumber = layers.Count - 1; layerNumber >= 1; layerNumber--) { var layer = layers[layerNumber]; // Start at last segment in layer for (var segmentNumber = layer.Segments.Count - 1; segmentNumber >= 0; segmentNumber--) { var segment = layer.Segments[segmentNumber]; // Start at last line in layer for (var lineNumber = segment.Lines.Count - 1; lineNumber >= 0; lineNumber--) { var line = segment.Lines[lineNumber]; // We found a tool change if (line.Content.EndsWith(ToolChangeMarker, StringComparison.InvariantCulture)) { // Take into account tool change times var tool = settings.Tools[segment.Tool - 1]; // See if we need to use preheating for this tool if (tool.PreheatTime > 0.0m) { // Reset possibly existing preheating time to just the tool change time // In case we were already waiting we will have to wait even longer. preheatCounters[segment.Tool] = (settings.Tools[segment.Tool - 1].AutoClean) ? ToolChangeDurationWithCleaning : ToolChangeDuration; } } // We only care for other commands if we are taking time if (preheatCounters.Count > 0) { var timeSpent = 0.0; if (line.Duration.HasValue) { timeSpent = line.Duration.Value; } int?gCode = line.GetIValue('G'); if (gCode == 10) { var pParam = line.GetIValue('P'); var rParam = line.GetIValue('R'); if (pParam.HasValue && rParam.HasValue && pParam > 0 && pParam <= settings.Tools.Length) { if (preheatCounters.ContainsKey(pParam.Value)) { // Remove this line again if we are still preheating segment.Lines.RemoveAt(lineNumber); } } } // Check if any of the tools we want to preheat has had enough time to do so yet foreach (var toolNumber in preheatCounters.Keys.ToList()) { var tool = settings.Tools[toolNumber - 1]; var totalTimeSpent = preheatCounters[toolNumber] + timeSpent; if (totalTimeSpent > (double)tool.PreheatTime) { // We've been doing enough stuff to generate a good G10 code segment.Lines.Insert(lineNumber, new GCodeLine($"G10 P{toolNumber} R{tool.ActiveTemperature.ToString(FrmMain.numberFormat)}")); preheatCounters.Remove(toolNumber); } else { // Need to do some more... preheatCounters[toolNumber] = totalTimeSpent; } } } } } progress.Report(iteration++); } // Override first generated G10 codes if we could not preheat in time if (preheatCounters.Count > 0 && layers.Count > 0 && layers[0].Segments.Count > 0) { foreach (GCodeLine line in layers[0].Segments[0].Lines) { int?gCode = line.GetIValue('G'); if (gCode == 10) { int?pParam = line.GetIValue('P'); if (pParam.HasValue && preheatCounters.ContainsKey(pParam.Value)) { ToolSettings tool = settings.Tools[pParam.Value - 1]; var activeTemp = tool.ActiveTemperature.ToString(FrmMain.numberFormat); line.Content = $"G10 P{pParam} R{activeTemp} S{activeTemp}"; preheatCounters.Remove(pParam.Value); } } } } }