public override string ReadLine()
        {
            var baseLine = base.ReadLine();

            if (baseLine == null)
            {
                return(null);
            }

            if (baseLine.EndsWith("; NO_PROCESSING"))
            {
                return(baseLine);
            }

            // if we are not printing or the line has no content don't process it
            if (baseLine.Length == 0 ||
                baseLine.Trim().Length == 0)
            {
                return(baseLine);
            }

            var lines = ProcessGCodeLine(baseLine, printer);

            for (int i = lines.Count - 1; i >= 1; i--)
            {
                queueStream.Add(lines[i], true);
            }

            var lineToSend = lines[0];

            return(lineToSend);
        }
Example #2
0
        public override string ReadLine()
        {
            var baseLine = base.ReadLine();

            if (baseLine == null)
            {
                return(null);
            }

            if (baseLine.EndsWith("; NO_PROCESSING"))
            {
                return(baseLine);
            }

            // if the line has no content don't process it
            if (baseLine.Length == 0 ||
                baseLine.Trim().Length == 0)
            {
                return(baseLine);
            }

            var lines = ProcessWriteRegEx(baseLine, printer);

            for (int i = lines.Count - 1; i >= 1; i--)
            {
                queueStream.Add(lines[i], true);
            }

            var lineToSend = lines[0];

            if (lineToSend != null &&
                LineIsMovement(lineToSend))
            {
                currentMove = GetPosition(lineToSend, currentMove);
            }

            // is it a position set?
            if (lineToSend.StartsWith("G92"))
            {
                GCodeFile.GetFirstNumberAfter("X", lineToSend, ref this.currentMove.position.X);
                GCodeFile.GetFirstNumberAfter("Y", lineToSend, ref this.currentMove.position.Y);
                GCodeFile.GetFirstNumberAfter("Z", lineToSend, ref this.currentMove.position.Z);
                GCodeFile.GetFirstNumberAfter("E", lineToSend, ref this.currentMove.extrusion);

                // tell the stream pipeline what the actual printer position is
                this.SetPrinterPosition(this.currentMove);
            }

            return(lineToSend);
        }
        public override string ReadLine()
        {
            // Send any commands that are queue before moving on to the internal stream.
            string nextCommand = queuedCommands.ReadLine();

            if (nextCommand != null)
            {
                return(nextCommand);
            }

            switch (RecoveryState)
            {
            // heat the extrude to remove it from the part
            case RecoveryState.RemoveHeating:
                // TODO: make sure we heat up all the extruders that we need to (all that are used)
                queuedCommands.Add("G21; set units to millimeters");
                queuedCommands.Add("M107; fan off");
                queuedCommands.Add("T0; set the active extruder to 0");
                queuedCommands.Add("G90; use absolute coordinates");
                queuedCommands.Add("G92 E0; reset the expected extruder position");
                queuedCommands.Add("M82; use absolute distance for extrusion");

                bool   hasHeatedBed = printer.Settings.GetValue <bool>(SettingsKey.has_heated_bed);
                double bedTemp      = printer.Settings.GetValue <double>(SettingsKey.bed_temperature);
                if (hasHeatedBed && bedTemp > 0)
                {
                    // start heating the bed
                    queuedCommands.Add($"M140 S{bedTemp}");
                }

                // heat up the extruder
                queuedCommands.Add("M109 S{0}".FormatWith(printer.Settings.Helpers.ExtruderTemperature(0)));

                if (hasHeatedBed && bedTemp > 0)
                {
                    // finish heating the bed
                    queuedCommands.Add($"M190 S{bedTemp}");
                }

                RecoveryState = RecoveryState.Raising;
                return("");

            // remove it from the part
            case RecoveryState.Raising:
                // We don't know where the printer is for sure (it make have been turned off). Disable leveling until we know where it is.
                PrintLevelingStream.AllowLeveling = false;
                queuedCommands.Add("M114 ; get current position");
                queuedCommands.Add("G91 ; move relative");
                queuedCommands.Add("G1 Z10 F{0}".FormatWith(printer.Settings.ZSpeed()));
                queuedCommands.Add("G90 ; move absolute");
                RecoveryState = RecoveryState.Homing;
                return("");

            // if top homing, home the extruder
            case RecoveryState.Homing:
                if (printer.Settings.GetValue <bool>(SettingsKey.z_homes_to_max))
                {
                    queuedCommands.Add("G28");
                }
                else
                {
                    // home x
                    queuedCommands.Add("G28 X0");
                    // home y
                    queuedCommands.Add("G28 Y0");
                    // move to the place we can home z from
                    Vector2 recoveryPositionXy = printer.Settings.GetValue <Vector2>(SettingsKey.recover_position_before_z_home);
                    queuedCommands.Add("G1 X{0:0.###}Y{1:0.###}F{2}".FormatWith(recoveryPositionXy.X, recoveryPositionXy.Y, printer.Settings.XSpeed()));
                    // home z
                    queuedCommands.Add("G28 Z0");
                }
                // We now know where the printer is re-enable print leveling
                PrintLevelingStream.AllowLeveling = true;
                RecoveryState = RecoveryState.FindingRecoveryLayer;
                return("");

            // This is to recover printing if an out a filament occurs.
            // Help the user move the extruder down to just touching the part
            case RecoveryState.FindingRecoveryLayer:
                if (false)                         // help the user get the head to the right position
                {
                    // move to above the completed print
                    // move over a know good part of the model at the current top layer (extrude vertex from gcode)
                    // let the user move down until they like the height
                    // calculate that position and continue
                }
                else                         // we are resuming because of disconnect or reset, skip this
                {
                    RecoveryState = RecoveryState.SkippingGCode;
                    goto case RecoveryState.SkippingGCode;
                }

            case RecoveryState.SkippingGCode:
                // run through the gcode that the device expected looking for things like temp
                // and skip everything else until we get to the point we left off last time
                int commandCount = 0;
                boundsOfSkippedLayers = RectangleDouble.ZeroIntersection;
                while (internalStream.GCodeFile.PercentComplete(internalStream.LineIndex) < percentDone)
                {
                    string line = internalStream.ReadLine();
                    if (line == null)
                    {
                        break;
                    }
                    commandCount++;

                    // make sure we don't parse comments
                    if (line.Contains(";"))
                    {
                        line = line.Split(';')[0];
                    }
                    lastDestination = GetPosition(line, lastDestination);

                    if (commandCount > 100)
                    {
                        boundsOfSkippedLayers.ExpandToInclude(lastDestination.position.Xy);
                    }

                    // check if the line is something we want to send to the printer (like a temp)
                    if (line.StartsWith("M109") ||                          // heat and wait extruder
                        line.StartsWith("M104") ||                                 // heat extruder
                        line.StartsWith("M190") ||                                 // heat and wait bed
                        line.StartsWith("M140") ||                                 // heat bed
                        line.StartsWith("T") ||                                 // switch extruder
                        line.StartsWith("M106") ||                                 // fan on
                        line.StartsWith("M107") ||                                 // fan off
                        line.StartsWith("G92"))                                    // set position
                    {
                        return(line);
                    }
                }

                RecoveryState = RecoveryState.PrimingAndMovingToStart;

                // make sure we always- pick up the last movement
                boundsOfSkippedLayers.ExpandToInclude(lastDestination.position.Xy);
                return("");

            case RecoveryState.PrimingAndMovingToStart:
            {
                if (printer.Settings.GetValue("z_homes_to_max") == "0")                                 // we are homed to the bed
                {
                    // move to the height we can recover printing from
                    Vector2 recoverPositionXy = printer.Settings.GetValue <Vector2>(SettingsKey.recover_position_before_z_home);
                    queuedCommands.Add(CreateMovementLine(new PrinterMove(new VectorMath.Vector3(recoverPositionXy.X, recoverPositionXy.Y, lastDestination.position.Z), 0, printer.Settings.ZSpeed())));
                }

                double extruderWidth = printer.Settings.GetValue <double>(SettingsKey.nozzle_diameter);
                // move to a position outside the printed bounds
                queuedCommands.Add(CreateMovementLine(new PrinterMove(
                                                          new Vector3(boundsOfSkippedLayers.Left - extruderWidth * 2, boundsOfSkippedLayers.Bottom + boundsOfSkippedLayers.Height / 2, lastDestination.position.Z),
                                                          0, printer.Settings.XSpeed())));

                // let's prime the extruder
                queuedCommands.Add("G1 E10 F{0}".FormatWith(printer.Settings.EFeedRate(0))); // extrude 10
                queuedCommands.Add("G1 E9");                                                 // and retract a bit

                // move to the actual print position
                queuedCommands.Add(CreateMovementLine(new PrinterMove(lastDestination.position, 0, printer.Settings.XSpeed())));

                /// reset the printer to know where the filament should be
                queuedCommands.Add("G92 E{0}".FormatWith(lastDestination.extrusion));
                RecoveryState = RecoveryState.PrintingSlow;
            }
                return("");

            case RecoveryState.PrintingSlow:
            {
                string lineToSend = internalStream.ReadLine();
                if (lineToSend == null)
                {
                    return(null);
                }

                if (!GCodeFile.IsLayerChange(lineToSend))
                {
                    // have not seen the end of this layer so keep printing slow
                    if (LineIsMovement(lineToSend))
                    {
                        PrinterMove currentMove = GetPosition(lineToSend, lastDestination);
                        PrinterMove moveToSend  = currentMove;

                        moveToSend.feedRate = recoverFeedRate;

                        lineToSend      = CreateMovementLine(moveToSend, lastDestination);
                        lastDestination = currentMove;
                        return(lineToSend);
                    }

                    return(lineToSend);
                }
            }

                // we only fall through to here after seeing the next "; Layer:"
                RecoveryState = RecoveryState.PrintingToEnd;
                return("");

            case RecoveryState.PrintingToEnd:
                return(internalStream.ReadLine());
            }

            return(null);
        }
        private void QueueAfterGCode()
        {
            string afterGcodeToQueue = "";

            switch (RequestedTool)
            {
            case 0:
                afterGcodeToQueue = printer.Settings.GetValue(SettingsKey.toolchange_gcode).Replace("\\n", "\n");
                break;

            case 1:
                afterGcodeToQueue = printer.Settings.GetValue(SettingsKey.toolchange_gcode_1).Replace("\\n", "\n");
                break;

            case 2:
                afterGcodeToQueue = printer.Settings.GetValue(SettingsKey.toolchange_gcode_2).Replace("\\n", "\n");
                break;

            case 3:
                afterGcodeToQueue = printer.Settings.GetValue(SettingsKey.toolchange_gcode_3).Replace("\\n", "\n");
                break;
            }

            PrinterMove newToolMove     = GetPosition(postSwitchLine, PrinterMove.Unknown);
            var         newToolPosition = newToolMove.position;
            var         lineNoComment   = postSwitchLine.Split(';')[0];

            // if there is no extrusion we can move directly to the desired position after the extruder switch.
            // Otherwise we need to go to the last position to start the extrusion.
            if (!lineNoComment.Contains("E"))
            {
                newToolPosition.X = newToolPosition.X == double.PositiveInfinity ? preSwitchPosition.X : newToolPosition.X;
                newToolPosition.Y = newToolPosition.Y == double.PositiveInfinity ? preSwitchPosition.Y : newToolPosition.Y;
            }

            // no matter what happens with the x and y we want to set our z if we have one before
            newToolPosition.Z = newToolPosition.Z == double.PositiveInfinity ? preSwitchPosition.Z : newToolPosition.Z;

            // put together the output we want to send
            var gcode = new StringBuilder();

            // If the printer is heating, make sure we are at temp before switching extruders
            var nextToolTargetTemp      = targetTemps[RequestedTool];
            var currentPrinterTargeTemp = printer.Connection.GetTargetHotendTemperature(RequestedTool);

            if (currentPrinterTargeTemp > 0 &&
                printer.Connection.GetActualHotendTemperature(RequestedTool) < nextToolTargetTemp - 3)
            {
                // ensure our next tool is at temp (the one we are switching to)
                gcode.AppendLine($"M109 T{RequestedTool} S{nextToolTargetTemp}");
            }

            if (afterGcodeToQueue.Trim().Length > 0)
            {
                gcode.Append(printer.Settings.ReplaceMacroValues(afterGcodeToQueue));
            }

            // move to selected tool to the last tool position at the travel speed
            if (newToolPosition.X != double.PositiveInfinity &&
                newToolPosition.Y != double.PositiveInfinity)
            {
                gcode.AppendLine($"\n G1 X{newToolPosition.X}Y{newToolPosition.Y}F{printer.Settings.XSpeed()}");
            }

            // move to the z position
            if (newToolPosition.Z != double.PositiveInfinity)
            {
                gcode.AppendLine($"G1 Z{newToolPosition.Z}F{printer.Settings.ZSpeed()}");
            }

            // set the feedrate back to what was before we added any code
            if (preSwitchFeedRate != double.PositiveInfinity)
            {
                gcode.AppendLine($"G1 F{preSwitchFeedRate}");
            }

            // and queue the travel
            gcode.AppendLine(postSwitchLine);

            queuedCommandsStream.Add(gcode.ToString());
        }
        public override string ReadLine()
        {
            string nextCommand = queuedCommands.ReadLine();

            if (nextCommand != null)
            {
                return(nextCommand);
            }

            switch (resumeState)
            {
            // heat the extrude to remove it from the part
            case ResumeState.RemoveHeating:
                // TODO: make sure we heat up all the extruders that we need to (all that are used)
                queuedCommands.Add("G21; set units to millimeters");
                queuedCommands.Add("M107; fan off");
                queuedCommands.Add("T0; set the active extruder to 0");
                queuedCommands.Add("G90; use absolute coordinates");
                queuedCommands.Add("G92 E0; reset the expected extruder position");
                queuedCommands.Add("M82; use absolute distance for extrusion");
                queuedCommands.Add("M109 S{0}".FormatWith(ActiveSliceSettings.Instance.ExtruderTemperature(0)));

                resumeState = ResumeState.Raising;
                return("");

            // remove it from the part
            case ResumeState.Raising:
                queuedCommands.Add("M114 ; get current position");
                queuedCommands.Add("G91 ; move relative");
                queuedCommands.Add("G1 Z10 F{0}".FormatWith(MovementControls.ZSpeed));
                queuedCommands.Add("G90 ; move absolute");
                resumeState = ResumeState.Homing;
                return("");

            // if top homing, home the extruder
            case ResumeState.Homing:
                if (ActiveSliceSettings.Instance.ActiveValue("z_homes_to_max") == "1")
                {
                    queuedCommands.Add("G28");
                }
                else
                {
                    // home x
                    queuedCommands.Add("G28 X0");
                    // home y
                    queuedCommands.Add("G28 Y0");
                    // move to the place we can home z from
                    Vector2 resumePositionXy = ActiveSliceSettings.Instance.ActiveVector2("resume_position_before_z_home");
                    queuedCommands.Add("G1 X{0:0.000}Y{1:0.000}F{2}".FormatWith(resumePositionXy.x, resumePositionXy.y, MovementControls.XSpeed));
                    // home z
                    queuedCommands.Add("G28 Z0");
                }
                resumeState = ResumeState.FindingResumeLayer;
                return("");

            // This is to resume printing if an out a filament occurs.
            // Help the user move the extruder down to just touching the part
            case ResumeState.FindingResumeLayer:
                if (false)                         // help the user get the head to the right position
                {
                    // move to above the completed print
                    // move over a know good part of the model at the current top layer (extrude vertex from gcode)
                    // let the user move down until they like the height
                    // calculate that position and continue
                }
                else                         // we are resuming because of disconnect or reset, skip this
                {
                    resumeState = ResumeState.SkippingGCode;
                    goto case ResumeState.SkippingGCode;
                }

            case ResumeState.SkippingGCode:
                // run through the gcode that the device expected looking for things like temp
                // and skip everything else until we get to the point we left off last time
                while (internalStream.FileStreaming.PercentComplete(internalStream.LineIndex) < percentDone)
                {
                    string line = internalStream.ReadLine();

                    lastDestination = GetPosition(line, lastDestination);

                    // check if the line is something we want to send to the printer (like a temp)
                    if (line.StartsWith("M109") ||
                        line.StartsWith("M104") ||
                        line.StartsWith("T") ||
                        line.StartsWith("M106") ||
                        line.StartsWith("M107"))
                    {
                        return(line);
                    }
                }

                resumeState = ResumeState.PrimingAndMovingToStart;
                return("");

            case ResumeState.PrimingAndMovingToStart:
            {
                // let's prime the extruder, move to a good position over the part, then start printing
                queuedCommands.Add("G1 E5");
                queuedCommands.Add("G1 E4");
                if (ActiveSliceSettings.Instance.ActiveValue("z_homes_to_max") == "0")                                 // we are homed to the bed
                {
                    // move to the height we can resume printing from
                    Vector2 resumePositionXy = ActiveSliceSettings.Instance.ActiveVector2("resume_position_before_z_home");
                    queuedCommands.Add(CreateMovementLine(new PrinterMove(new VectorMath.Vector3(resumePositionXy.x, resumePositionXy.y, lastDestination.position.z + 5), 0, MovementControls.ZSpeed)));
                    // move just above the actual print position
                    queuedCommands.Add(CreateMovementLine(new PrinterMove(lastDestination.position + new VectorMath.Vector3(0, 0, 5), 0, MovementControls.XSpeed)));
                    // move down to part
                    queuedCommands.Add(CreateMovementLine(new PrinterMove(lastDestination.position, 0, MovementControls.ZSpeed)));
                }
                else
                {
                    // move to the actual print position
                    queuedCommands.Add(CreateMovementLine(new PrinterMove(lastDestination.position, 0, MovementControls.ZSpeed)));
                }
                // extrude back to our filament start
                queuedCommands.Add("G1 E5");
                /// reset the printer to know where the filament should be
                queuedCommands.Add("G92 E{0}".FormatWith(lastDestination.extrusion));
                resumeState = ResumeState.PrintingSlow;
            }
                return("");

            case ResumeState.PrintingSlow:
            {
                string lineToSend = internalStream.ReadLine();
                if (lineToSend != null &&
                    lineToSend.StartsWith("; LAYER:"))
                {
                    if (lineToSend != null &&
                        LineIsMovement(lineToSend))
                    {
                        PrinterMove currentMove = GetPosition(lineToSend, lastDestination);
                        PrinterMove moveToSend  = currentMove;

                        double feedRate;

                        string firstLayerSpeed = ActiveSliceSettings.Instance.ActiveValue("resume_first_layer_speed");
                        if (!double.TryParse(firstLayerSpeed, out feedRate))
                        {
                            feedRate = 10;
                        }
                        feedRate *= 60;

                        moveToSend.feedRate = feedRate;

                        lineToSend      = CreateMovementLine(moveToSend, lastDestination);
                        lastDestination = currentMove;
                        return(lineToSend);
                    }

                    return(lineToSend);
                }
            }

                resumeState = ResumeState.PrintingToEnd;
                return("");

            case ResumeState.PrintingToEnd:
                return(internalStream.ReadLine());
            }

            return(null);
        }
        public override string ReadLine()
        {
            string lineToSend = base.ReadLine();

            if (lineToSend == null)
            {
                return(null);
            }

            if (lineToSend.EndsWith("; NO_PROCESSING"))
            {
                return(lineToSend);
            }

            // check if any of the heaters we will be switching to need to start heating
            ManageReHeating(lineToSend);

            if (lineToSend == compleatedBeforeGCodeString)
            {
                activeTool = requestedTool;
                SendState  = SendStates.Normal;
                QueueAfterGCode();
            }

            // track the tool state
            if (lineToSend.StartsWith("T"))
            {
                int changeCommandTool = -1;
                if (GCodeFile.GetFirstNumberAfter("T", lineToSend, ref changeCommandTool) &&
                    changeCommandTool != activeTool)
                {
                    requestedTool = changeCommandTool;
                    if (SendState == SendStates.Normal)
                    {
                        SendState = SendStates.WaitingForMove;
                        // don't queue the tool change until after the before gcode has been sent
                        return($"; waiting for move on T{requestedTool}");
                    }
                }
            }
            // check if there is a temperature change request
            else if (lineToSend.StartsWith("M104") || lineToSend.StartsWith("M109"))
            {
                double toolBeingSet = -1;
                // if there is a tool specification
                if (GCodeFile.GetFirstNumberAfter("T", lineToSend, ref toolBeingSet))
                {
                    if (toolBeingSet != activeTool)
                    {
                        // For smoothie, switch back to the extrude we were using before the temp change (smoothie switches to the specified extruder, marlin repetier do not)
                        queuedCommandsStream.Add("T{0} ; NO_PROCESSING".FormatWith(activeTool));
                    }
                }
            }

            if (QueueBeforeIfNeedToSwitchExtruders(lineToSend))
            {
                return("");
            }

            if (LineIsMovement(lineToSend))
            {
                lastDestination = GetPosition(lineToSend, lastDestination);
            }

            return(lineToSend);
        }