Exemplo n.º 1
0
class Position(object):
    def __init__(self, octolapseSettings, octoprintPrinterProfile,
                 g90InfluencesExtruder):
        self.Settings = octolapseSettings
        self.Printer = self.Settings.CurrentPrinter()
        self.OctoprintPrinterProfile = octoprintPrinterProfile
        self.Origin = {
            "X": self.Printer.origin_x,
            "Y": self.Printer.origin_y,
            "Z": self.Printer.origin_z
        }

        self.BoundingBox = utility.GetBoundingBox(self.Printer,
                                                  octoprintPrinterProfile)
        self.PrinterTolerance = self.Printer.printer_position_confirmation_tolerance
        self.Positions = []
        self.Reset()

        self.Extruder = Extruder(octolapseSettings)
        self.G90InfluencesExtruder = g90InfluencesExtruder

        if (self.Printer.z_hop is None):
            self.Printer.z_hop = 0

        self.Commands = Commands()

    def Reset(self):
        # todo:  This reset function doesn't seem to reset everything.
        self.Positions = []

        self.SavedPosition = None

    def UpdatePosition(self,
                       x=None,
                       y=None,
                       z=None,
                       e=None,
                       f=None,
                       force=False,
                       calculateChanges=False):
        numPositions = len(self.Positions)
        if (numPositions == 0):
            return
        pos = self.Positions[0]
        pos.UpdatePosition(self.BoundingBox, x, y, z, e, f, force)
        if (calculateChanges and numPositions > 1):
            previousPos = self.Positions[1]
            pos.HasPositionChanged = not pos.IsPositionEqual(
                previousPos, self.PrinterTolerance)
            pos.HasStateChanged = not pos.IsStateEqual(previousPos,
                                                       self.PrinterTolerance)

    def SavePosition(self,
                     x=None,
                     y=None,
                     z=None,
                     e=None,
                     f=None,
                     force=False):
        if (len(self.Positions) == 0):
            return
        self.SavedPosition = Pos(self.Positions[0])

    def ToDict(self):
        positionDict = None
        if (len(self.Positions) > 0):
            previousPos = self.Positions[0]
            return previousPos.ToDict()
        return None

    def ToPositionDict(self):
        positionDict = None
        if (len(self.Positions) > 0):
            previousPos = self.Positions[0]
            return previousPos.ToPositionDict()
        return None

    def ToStateDict(self):
        positionDict = None
        if (len(self.Positions) > 0):
            previousPos = self.Positions[0]
            return previousPos.ToStateDict()
        return None

    def ZDelta(self, pos):
        if (len(self.Positions) > 0):
            previousPos = self.Positions[0]
            # calculate ZDelta
            if (pos.Height is not None):
                if (previousPos.Height is None):
                    return pos.Height
                else:
                    return pos.Height - previousPos.Height
        return 0

    def HasStateChanged(self, index=0):
        if (len(self.Positions) > index):
            return self.Positions[index].HasStateChanged
        return None

    def HasPositionChanged(self, index=0):
        if (len(self.Positions) > index):
            return self.Positions[index].HasPositionChanged
        return None

    def HasPositionError(self, index=0):
        if (len(self.Positions) > index):
            return self.Positions[index].HasPositionError
        return None

    def DistanceToZLift(self, index=0):
        if (len(self.Positions) > index):
            pos = self.Positions[index]
            currentLift = utility.round_to(pos.Z - pos.Height,
                                           self.PrinterTolerance)
            if (currentLift < self.Printer.z_hop):
                return self.Printer.z_hop - currentLift
            return 0
        return None

    def PositionError(self, index=0):
        if (len(self.Positions) > index):
            return self.Positions[index].PositionError
        return None

    def X(self, index=0):
        if (len(self.Positions) > index):
            return self.Positions[index].X
        return None

    def Y(self, index=0):
        if (len(self.Positions) > index):
            return self.Positions[index].Y
        return None

    def Z(self, index=0):
        if (len(self.Positions) > index):
            return self.Positions[index].Z
        return None

    def E(self, index=0):
        if (len(self.Positions) > index):
            return self.Positions[index].E
        return None

    def F(self, index=0):
        if (len(self.Positions) > index):
            return self.Positions[index].F
        return None

    def IsZHop(self, index=0):
        if (len(self.Positions) > index):
            return self.Positions[index].IsZHop
        return None

    def IsLayerChange(self, index=0):
        if (len(self.Positions) > index):
            return self.Positions[index].IsLayerChange
        return None

    def Layer(self, index=0):
        if (len(self.Positions) > index):
            return self.Positions[index].Layer
        return None

    def Height(self, index=0):
        if (len(self.Positions) > index):
            return self.Positions[index].Height
        return None

    def IsRelative(self, index=0):
        if (len(self.Positions) > index):
            return self.Positions[index].IsRelative
        return None

    def IsExtruderRelative(self, index=0):
        if (len(self.Positions) > index):
            return self.Positions[index].IsExtruderRelative
        return None

    def HasReceivedHomeCommand(self, index=0):
        if (len(self.Positions) > index):
            return self.Positions[
                index].HasReceivedHomeCommand and self.HasHomedAxis(index)
        return False

    def UndoUpdate(self):
        if (len(self.Positions) > 0):
            del self.Positions[0]
        self.Extruder.UndoUpdate()

    def Update(self, gcode):
        command = self.Commands.GetCommand(gcode)
        # a new position

        pos = None
        previousPos = None
        numPositions = len(self.Positions)
        if (numPositions > 0):
            pos = Pos(self.OctoprintPrinterProfile, self.Positions[0])
            if (numPositions > 1):
                previousPos = Pos(self.OctoprintPrinterProfile,
                                  self.Positions[1])
        if (pos is None):
            pos = Pos(self.OctoprintPrinterProfile)
        if (previousPos is None):
            previousPos = Pos(self.OctoprintPrinterProfile)

        # reset the current position state (copied from the previous position, or a new position)
        pos.ResetState()
        # set the pos gcode command
        pos.GCode = gcode

        # apply the command to the position tracker
        if (command is not None):
            # I'm currently too lazy to keep this DRY
            # TODO:  Make DRY
            if (command.Command in ["G0", "G1"]):
                #Movement
                if (command.Parse()):
                    self.Settings.CurrentDebugProfile(
                    ).LogPositionCommandReceived("Received {0}".format(
                        command.Name))
                    x = command.Parameters["X"].Value
                    y = command.Parameters["Y"].Value
                    z = command.Parameters["Z"].Value
                    e = command.Parameters["E"].Value
                    f = command.Parameters["F"].Value

                    if (x is not None or y is not None or z is not None
                            or f is not None):

                        if (pos.HasPositionError and not pos.IsRelative):
                            pos.HasPositionError = False
                            pos.PositionError = ""
                        pos.UpdatePosition(self.BoundingBox,
                                           x,
                                           y,
                                           z,
                                           e=None,
                                           f=f)

                    if (e is not None):
                        if (pos.IsExtruderRelative is not None):
                            if (pos.HasPositionError
                                    and not pos.IsExtruderRelative):
                                pos.HasPositionError = False
                                pos.PositionError = ""
                            pos.UpdatePosition(self.BoundingBox,
                                               x=None,
                                               y=None,
                                               z=None,
                                               e=e,
                                               f=None)
                        else:
                            self.Settings.CurrentDebugProfile().LogError(
                                "Position - Unable to update the extruder position, no extruder coordinate system has been selected (absolute/relative)."
                            )
                    message = "Position Change - {0} - {1} Move From(X:{2},Y:{3},Z:{4},E:{5}) - To(X:{6},Y:{7},Z:{8},E:{9})"
                    if (previousPos is None):
                        message = message.format(
                            gcode,
                            "Relative" if pos.IsRelative else "Absolute",
                            "None", "None", "None", "None", pos.X, pos.Y,
                            pos.Z, pos.E)
                    else:
                        message = message.format(
                            gcode,
                            "Relative" if pos.IsRelative else "Absolute",
                            previousPos.X, previousPos.Y, previousPos.Z,
                            previousPos.E, pos.X, pos.Y, pos.Z, pos.E)
                    self.Settings.CurrentDebugProfile().LogPositionChange(
                        message)

                else:
                    self.Settings.CurrentDebugProfile().LogError(
                        "Position - Unable to parse the gcode command: {0}".
                        format(gcode))
            elif (command.Command == "G28"):
                # Home
                if (command.Parse()):
                    pos.HasReceivedHomeCommand = True
                    x = command.Parameters["X"].Value
                    y = command.Parameters["Y"].Value
                    z = command.Parameters["Z"].Value
                    xHomed = False
                    yHomed = False
                    zHomed = False
                    if (x is not None):
                        xHomed = True
                    if (y is not None):
                        yHomed = True
                    if (z is not None):
                        zHomed = True
                    if (x is None and y is None and z is None):
                        xHomed = True
                        yHomed = True
                        zHomed = True

                    homeStrings = []
                    if (xHomed):
                        pos.XHomed = True
                        pos.X = self.Origin[
                            "X"] if not self.Printer.auto_detect_origin else None
                        if (pos.X is None):
                            homeStrings.append("Homing X to Unknown Origin.")
                        else:
                            homeStrings.append("Homing X to {0}.".format(
                                GetFormattedCoordinate(pos.X)))
                    zHomedString = ""
                    if (yHomed):
                        pos.YHomed = True
                        pos.Y = self.Origin[
                            "Y"] if not self.Printer.auto_detect_origin else None
                        if (pos.Y is None):
                            homeStrings.append("Homing Y to Unknown Origin.")
                        else:
                            homeStrings.append("Homing Y to {0}.".format(
                                GetFormattedCoordinate(pos.Y)))
                    xHomedString = ""
                    if (zHomed):
                        pos.ZHomed = True
                        pos.Z = self.Origin[
                            "Z"] if not self.Printer.auto_detect_origin else None
                        if (pos.Z is None):
                            homeStrings.append("Homing Z to Unknown Origin.")
                        else:
                            homeStrings.append("Homing Z to {0}.".format(
                                GetFormattedCoordinate(pos.Z)))

                    self.Settings.CurrentDebugProfile(
                    ).LogPositionCommandReceived("Received G28 - ".format(
                        " ".join(homeStrings)))
                    pos.HasPositionError = False
                    pos.PositionError = None
                else:
                    self.Settings.CurrentDebugProfile().LogError(
                        "Position - Unable to parse the Gcode:{0}".format(
                            gcode))
            elif (command.Command == "G90"):
                # change x,y,z to absolute
                if (pos.IsRelative):
                    self.Settings.CurrentDebugProfile(
                    ).LogPositionCommandReceived(
                        "Received G90 - Switching to absolute x,y,z coordinates."
                    )
                    pos.IsRelative = False
                else:
                    self.Settings.CurrentDebugProfile(
                    ).LogPositionCommandReceived(
                        "Received G90 - Already using absolute x,y,z coordinates."
                    )

                # for some firmwares we need to switch the extruder to absolute coordinates as well
                if (self.G90InfluencesExtruder):
                    if (pos.IsExtruderRelative):
                        self.Settings.CurrentDebugProfile(
                        ).LogPositionCommandReceived(
                            "Received G90 - Switching to absolute extruder coordinates"
                        )
                        pos.IsExtruderRelative = False
                    else:
                        self.Settings.CurrentDebugProfile(
                        ).LogPositionCommandReceived(
                            "Received G90 - Already using absolute extruder coordinates"
                        )
            elif (command.Command == "G91"):
                # change x,y,z to relative
                if (not pos.IsRelative):
                    self.Settings.CurrentDebugProfile(
                    ).LogPositionCommandReceived(
                        "Received G91 - Switching to relative x,y,z coordinates"
                    )
                    pos.IsRelative = True
                else:
                    self.Settings.CurrentDebugProfile(
                    ).LogPositionCommandReceived(
                        "Received G91 - Already using relative x,y,z coordinates"
                    )

                # for some firmwares we need to switch the extruder to absolute coordinates as well
                if (self.G90InfluencesExtruder):
                    if (not pos.IsExtruderRelative):
                        self.Settings.CurrentDebugProfile(
                        ).LogPositionCommandReceived(
                            "Received G91 - Switching to relative extruder coordinates"
                        )
                        pos.IsExtruderRelative = True
                    else:
                        self.Settings.CurrentDebugProfile(
                        ).LogPositionCommandReceived(
                            "Received G91 - Already using relative extruder coordinates"
                        )
            elif (command.Command == "M83"):
                # Extruder - Set Relative
                if (pos.IsExtruderRelative is None
                        or not pos.IsExtruderRelative):
                    self.Settings.CurrentDebugProfile(
                    ).LogPositionCommandReceived(
                        "Received M83 - Switching Extruder to Relative Coordinates"
                    )
                    pos.IsExtruderRelative = True
            elif (command.Command == "M82"):
                # Extruder - Set Absolute
                if (pos.IsExtruderRelative is None or pos.IsExtruderRelative):
                    self.Settings.CurrentDebugProfile(
                    ).LogPositionCommandReceived(
                        "Received M82 - Switching Extruder to Absolute Coordinates"
                    )
                    pos.IsExtruderRelative = False
            elif (command.Command == "G92"):
                # Set Position (offset)
                if (command.Parse()):
                    previousRelativeValue = pos.IsRelative
                    previousExtruderRelativeValue = pos.IsExtruderRelative
                    x = command.Parameters["X"].Value
                    y = command.Parameters["Y"].Value
                    z = command.Parameters["Z"].Value
                    e = command.Parameters["E"].Value
                    resetAll = False
                    if (x is None and y is None and z is None and e is None):
                        pos.XOffset = pos.X
                        pos.YOffset = pos.Y
                        pos.ZOffset = pos.Z
                        pos.EOffset = pos.E
                    # set the offsets if they are provided
                    if (x is not None and pos.X is not None and pos.XHomed):
                        pos.XOffset = pos.X - utility.getfloat(x, 0)
                    if (y is not None and pos.Y is not None and pos.YHomed):
                        pos.YOffset = pos.Y - utility.getfloat(y, 0)
                    if (z is not None and pos.Z is not None and pos.ZHomed):
                        pos.ZOffset = pos.Z - utility.getfloat(z, 0)
                    if (e is not None and pos.E is not None):
                        pos.EOffset = pos.E - utility.getfloat(e, 0)
                    self.Settings.CurrentDebugProfile(
                    ).LogPositionCommandReceived(
                        "Received G92 - Set Position.  Command:{0}, XOffset:{1}, YOffset:{2}, ZOffset:{3}, EOffset:{4}"
                        .format(gcode, pos.XOffset, pos.YOffset, pos.ZOffset,
                                pos.EOffset))
                else:
                    self.Settings.CurrentDebugProfile().LogError(
                        "Position - Unable to parse the Gcode:{0}".format(
                            gcode))

            ########################################
            # Update the extruder monitor.
            self.Extruder.Update(self.ERelative(pos))
            ########################################
            # If we have a homed axis, detect changes.
            ########################################
            hasExtruderChanged = self.Extruder.HasChanged()
            pos.HasPositionChanged = not pos.IsPositionEqual(
                previousPos, self.PrinterTolerance)
            pos.HasStateChanged = not pos.IsStateEqual(previousPos,
                                                       self.PrinterTolerance)

            if (self.HasHomedPosition()):

                if (hasExtruderChanged or pos.HasPositionChanged):

                    # calculate LastExtrusionHeight and Height
                    if (self.Extruder.IsExtruding()):
                        pos.LastExtrusionHeight = pos.Z
                        if (pos.Height is None or
                                utility.round_to(pos.Z, self.PrinterTolerance)
                                > previousPos.Height):
                            pos.Height = utility.round_to(
                                pos.Z, self.PrinterTolerance)
                            self.Settings.CurrentDebugProfile(
                            ).LogPositionHeightChange(
                                "Position - Reached New Height:{0}.".format(
                                    pos.Height))

                        # calculate layer change
                        if (utility.round_to(self.ZDelta(pos),
                                             self.PrinterTolerance) > 0
                                or pos.Layer == 0):
                            pos.IsLayerChange = True
                            pos.Layer += 1
                            self.Settings.CurrentDebugProfile(
                            ).LogPositionLayerChange(
                                "Position - Layer:{0}.".format(pos.Layer))
                        else:
                            pos.IsLayerChange = False

                    # Calculate ZHop based on last extrusion height
                    if (pos.LastExtrusionHeight is not None):
                        # calculate lift, taking into account floating point rounding
                        lift = utility.round_to(
                            pos.Z - pos.LastExtrusionHeight,
                            self.PrinterTolerance)
                        if (lift >= self.Printer.z_hop):
                            lift = self.Printer.z_hop
                        isLifted = self.Printer.z_hop > 0.0 and lift >= self.Printer.z_hop and (
                            not self.Extruder.IsExtruding()
                            or self.Extruder.IsExtrudingStart())

                        if (isLifted):
                            pos.IsZHop = True

                    if (pos.IsZHop):
                        self.Settings.CurrentDebugProfile().LogPositionZHop(
                            "Position - Zhop:{0}".format(self.Printer.z_hop))

        # Add the current position, remove positions if we have more than 5 from the end
        self.Positions.insert(0, pos)
        while (len(self.Positions) > 5):
            del self.Positions[5]

    def HasHomedPosition(self, index=0):
        if (len(self.Positions) <= index):
            return None
        pos = self.Positions[index]
        return (self.HasHomedAxis(index) and pos.X is not None
                and pos.Y is not None and pos.Z is not None)

    def HasHomedAxis(self, index=0):
        if (len(self.Positions) <= index):
            return None
        pos = self.Positions[index]
        return (pos.XHomed and pos.YHomed and pos.ZHomed)

    def XRelative(self):
        if (len(self.Positions) < 2):
            return None
        pos = self.Positions[0]
        prevoiusPos = self.Positions[1]
        return pos.X - previousPos.X

    def YRelative(self):
        if (len(self.Positions) < 2):
            return None
        pos = self.Positions[0]
        prevoiusPos = self.Positions[1]
        return pos.Y - previousPos.Y

    def ZRelative(self):
        if (len(self.Positions) < 2):
            return None
        pos = self.Positions[0]
        prevoiusPos = self.Positions[1]
        return pos.Z - previousPos.Z

    def ERelative(self, pos):
        if (len(self.Positions) < 1):
            return None
        previousPos = self.Positions[0]
        return pos.E - previousPos.E

    def IsAtPosition(self, x, y, z, pos, tolerance, applyOffset):
        if (applyOffset):
            x = x + pos.XOffset
            y = y + pos.YOffset
            if (z is not None):
                z = z + pos.ZOffset

        if ((pos.X is None or utility.isclose(pos.X, x, abs_tol=tolerance)) and
            (pos.Y is None or utility.isclose(pos.Y, y, abs_tol=tolerance))
                and (z is None or pos.Z is None
                     or utility.isclose(pos.Z, z, abs_tol=tolerance))):
            return True
        return False

    def IsAtPreviousPosition(self, x, y, z=None, applyOffset=True):
        if (len(self.Positions) < 2):
            return False
        return self.IsAtPosition(
            x, y, z, self.Positions[1],
            self.Printer.printer_position_confirmation_tolerance, True)

    def IsAtCurrentPosition(self, x, y, z=None, applyOffset=True):
        if (len(self.Positions) < 1):
            return False
        return self.IsAtPosition(
            x, y, z, self.Positions[0],
            self.Printer.printer_position_confirmation_tolerance, True)

    def IsAtSavedPosition(self, x, y, z=None, applyOffset=True):
        if (self.SavedPosition is None):
            return False
        return self.IsAtPosition(
            x, y, z, self.SavedPosition,
            self.Printer.printer_position_confirmation_tolerance, True)
Exemplo n.º 2
0
class Position(object):
    def __init__(self, octolapseSettings, octoprintPrinterProfile,
                 g90InfluencesExtruder):
        self.Settings = octolapseSettings
        self.Printer = self.Settings.CurrentPrinter()
        self.OctoprintPrinterProfile = octoprintPrinterProfile
        self.PrinterTolerance = self.Printer.printer_position_confirmation_tolerance
        self.Positions = []
        self.Reset()

        self.Extruder = Extruder(octolapseSettings)
        self.G90InfluencesExtruder = g90InfluencesExtruder

        if (self.Printer.z_hop is None):
            self.Printer.z_hop = 0

        self.Commands = Commands()

    def Reset(self):

        self.Positions = []
        self.HasPositionError = False
        self.PositionError = None
        self.IsLayerChange = False
        self.HasPositionChanged = False
        self.Layer = 0
        self.Height = 0
        self.SavedPosition = None

    def UpdatePosition(self,
                       x=None,
                       y=None,
                       z=None,
                       e=None,
                       f=None,
                       force=False):
        if (len(self.Positions) == 0):
            return
        pos = self.Positions[0]
        pos.UpdatePosition(x, y, z, e, f, force)

    def SavePosition(self,
                     x=None,
                     y=None,
                     z=None,
                     e=None,
                     f=None,
                     force=False):
        if (len(self.Positions) == 0):
            return
        self.SavedPosition = Pos(self.Positions[0])

    def ZDelta(self, pos):
        if (len(self.Positions) > 0):
            previousPos = self.Positions[0]
            # calculate ZDelta
            if (pos.Height is not None):
                if (previousPos.Height is None):
                    return pos.Height
                else:
                    return pos.Height - previousPos.Height
        return 0

    def X(self):
        if (len(self.Positions) > 0):
            return self.Positions[0].X
        return None

    def Y(self):
        if (len(self.Positions) > 0):
            return self.Positions[0].Y
        return None

    def Z(self):
        if (len(self.Positions) > 0):
            return self.Positions[0].Z
        return None

    def E(self):
        if (len(self.Positions) > 0):
            return self.Positions[0].E
        return None

    def F(self):
        if (len(self.Positions) > 0):
            return self.Positions[0].F
        return None

    def IsZHop(self):
        if (len(self.Positions) > 0):
            return self.Positions[0].IsZHop
        return None

    def IsRelative(self):
        if (len(self.Positions) > 0):
            return self.Positions[0].IsRelative
        return None

    def IsExtruderRelative(self):
        if (len(self.Positions) > 0):
            return self.Positions[0].IsExtruderRelative
        return None

    def UndoUpdate(self):
        if (len(self.Positions) > 0):
            del self.Positions[0]
        self.Extruder.UndoUpdate()

    def Update(self, gcode):
        # reset state variables
        self.IsLayerChange = False
        self.HasPositionChanged = False

        command = self.Commands.GetCommand(gcode)
        # a new position

        pos = None
        previousPos = None
        numPositions = len(self.Positions)
        if (numPositions > 0):
            pos = Pos(self.OctoprintPrinterProfile, self.Positions[0])
            if (numPositions > 1):
                previousPos = Pos(self.OctoprintPrinterProfile,
                                  self.Positions[1])
        if (pos is None):
            pos = Pos(self.OctoprintPrinterProfile)
        if (previousPos is None):
            previousPos = Pos(self.OctoprintPrinterProfile)

        # Movement detected, set the previous values
        # disect the gcode and use it to update our position

        if (pos.IsZHopStart):
            pos.IsZHop = True

        elif (pos.IsZHopCompleting):
            pos.IsZHop = False
            pos.IsZHopCompleting = False

        pos.IsZHopStart = False

        # apply the command to the position tracker
        if (command is not None):
            if (command.Command in ["G0", "G1"]):
                #Movement
                if (command.Parse()):
                    self.Settings.CurrentDebugProfile(
                    ).LogPositionCommandReceived("Received {0}".format(
                        command.Name))
                    x = command.Parameters["X"].Value
                    y = command.Parameters["Y"].Value
                    z = command.Parameters["Z"].Value
                    e = command.Parameters["E"].Value
                    f = command.Parameters["F"].Value

                    if (x is not None or y is not None or z is not None
                            or f is not None):

                        if (self.HasPositionError and not pos.IsRelative):
                            self.HasPositionError = False
                            self.PositionError = ""
                        pos.UpdatePosition(x, y, z, e=None, f=f)

                    if (e is not None):
                        if (pos.IsExtruderRelative is not None):
                            if (self.HasPositionError
                                    and not pos.IsExtruderRelative):
                                self.HasPositionError = False
                                self.PositionError = ""
                            pos.UpdatePosition(x=None,
                                               y=None,
                                               z=None,
                                               e=e,
                                               f=None)
                        else:
                            self.Settings.CurrentDebugProfile().LogError(
                                "Position - Unable to update the extruder position, no extruder coordinate system has been selected (absolute/relative)."
                            )
                    message = "Position Change - {0} - {1} Move From(X:{2},Y:{3},Z:{4},E:{5}) - To(X:{6},Y:{7},Z:{8},E:{9})"
                    if (previousPos is None):
                        message = message.format(
                            gcode,
                            "Relative" if pos.IsRelative else "Absolute",
                            "None", "None", "None", "None", pos.X, pos.Y,
                            pos.Z, pos.E)
                    else:
                        message = message.format(
                            gcode,
                            "Relative" if pos.IsRelative else "Absolute",
                            previousPos.X, previousPos.Y, previousPos.Z,
                            previousPos.E, pos.X, pos.Y, pos.Z, pos.E)
                    self.Settings.CurrentDebugProfile().LogPositionChange(
                        message)

                else:
                    self.Settings.CurrentDebugProfile().LogError(
                        "Position - Unable to parse the gcode command: {0}".
                        format(gcode))

                # If we've not yet homed the axis

                #########################################################
            elif (command.Command == "G28"):
                # test homing of only X,Y or Z

                if (command.Parse()):
                    x = command.Parameters["X"].Value
                    y = command.Parameters["Y"].Value
                    z = command.Parameters["Z"].Value
                    if (x is not None):
                        pos.XHomed = True
                    if (y is not None):
                        pos.YHomed = True
                    if (z is not None):
                        pos.ZHomed = True
                    if (x is None and y is None and z is None):
                        pos.XHomed = True
                        pos.YHomed = True
                        pos.ZHomed = True

                    self.Settings.CurrentDebugProfile(
                    ).LogPositionCommandReceived(
                        "Received G28 - Homing to {0}".format(
                            GetFormattedCoordinates(x, y, z, pos.E)))
                    self.HasPositionError = False
                    self.PositionError = None
                else:
                    self.Settings.CurrentDebugProfile().LogError(
                        "Position - Unable to parse the Gcode:{0}".format(
                            gcode))
            elif (command.Command == "G90"):
                # change x,y,z to absolute
                if (pos.IsRelative):
                    self.Settings.CurrentDebugProfile(
                    ).LogPositionCommandReceived(
                        "Received G90 - Switching to absolute x,y,z coordinates."
                    )
                    pos.IsRelative = False
                else:
                    self.Settings.CurrentDebugProfile(
                    ).LogPositionCommandReceived(
                        "Received G90 - Already using absolute x,y,z coordinates."
                    )

                # for some firmwares we need to switch the extruder to absolute coordinates as well
                if (self.G90InfluencesExtruder):
                    if (pos.IsExtruderRelative):
                        self.Settings.CurrentDebugProfile(
                        ).LogPositionCommandReceived(
                            "Received G90 - Switching to absolute extruder coordinates"
                        )
                        pos.IsExtruderRelative = False
                    else:
                        self.Settings.CurrentDebugProfile(
                        ).LogPositionCommandReceived(
                            "Received G90 - Already using absolute extruder coordinates"
                        )
            elif (command.Command == "G91"):
                # change x,y,z to relative
                if (not pos.IsRelative):
                    self.Settings.CurrentDebugProfile(
                    ).LogPositionCommandReceived(
                        "Received G91 - Switching to relative x,y,z coordinates"
                    )
                    pos.IsRelative = True
                else:
                    self.Settings.CurrentDebugProfile(
                    ).LogPositionCommandReceived(
                        "Received G91 - Already using relative x,y,z coordinates"
                    )

                # for some firmwares we need to switch the extruder to absolute coordinates as well
                if (self.G90InfluencesExtruder):
                    if (not pos.IsExtruderRelative):
                        self.Settings.CurrentDebugProfile(
                        ).LogPositionCommandReceived(
                            "Received G91 - Switching to relative extruder coordinates"
                        )
                        pos.IsExtruderRelative = True
                    else:
                        self.Settings.CurrentDebugProfile(
                        ).LogPositionCommandReceived(
                            "Received G91 - Already using relative extruder coordinates"
                        )
            elif (command.Command == "M83"):
                if (pos.IsExtruderRelative is None
                        or not pos.IsExtruderRelative):
                    self.Settings.CurrentDebugProfile(
                    ).LogPositionCommandReceived(
                        "Received M83 - Switching Extruder to Relative Coordinates"
                    )
                    pos.IsExtruderRelative = True
            elif (command.Command == "M82"):

                if (pos.IsExtruderRelative is None or pos.IsExtruderRelative):
                    self.Settings.CurrentDebugProfile(
                    ).LogPositionCommandReceived(
                        "Received M82 - Switching Extruder to Absolute Coordinates"
                    )
                    pos.IsExtruderRelative = False
            elif (command.Command == "G92"):
                if (command.Parse()):
                    previousRelativeValue = self.IsRelative
                    previousExtruderRelativeValue = self.IsExtruderRelative
                    x = command.Parameters["X"].Value
                    y = command.Parameters["Y"].Value
                    z = command.Parameters["Z"].Value
                    e = command.Parameters["E"].Value
                    resetAll = False
                    if (x is None and y is None and z is None and e is None):
                        pos.XOffset = pos.X
                        pos.YOffset = pos.Y
                        pos.ZOffset = pos.Z
                        pos.EOffset = pos.E
                    # set the offsets if they are provided
                    if (x is not None and pos.X is not None and pos.XHomed):
                        pos.XOffset = pos.X - utility.getfloat(x, 0)
                    if (y is not None and pos.Y is not None and pos.YHomed):
                        pos.YOffset = pos.Y - utility.getfloat(y, 0)
                    if (z is not None and pos.Z is not None and pos.ZHomed):
                        pos.ZOffset = pos.Z - utility.getfloat(z, 0)
                    if (e is not None and pos.E is not None):
                        pos.EOffset = pos.E - utility.getfloat(e, 0)
                    self.Settings.CurrentDebugProfile(
                    ).LogPositionCommandReceived(
                        "Received G92 - Set Position.  Command:{0}, XOffset:{1}, YOffset:{2}, ZOffset:{3}, EOffset:{4}"
                        .format(gcode, pos.XOffset, pos.YOffset, pos.ZOffset,
                                pos.EOffset))
                else:
                    self.Settings.CurrentDebugProfile().LogError(
                        "Position - Unable to parse the Gcode:{0}".format(
                            gcode))

            #########################################################
            # Update the extruder monitor if there was movement
            self.Extruder.Update(self.ERelative(pos))

            if (self.HasHomedAxis()):
                hasExtruderChanged = (utility.round_to(
                    self.ERelative(pos), self.PrinterTolerance) != 0)
                hasXYZChanged = (
                    utility.round_to(pos.X, self.PrinterTolerance) !=
                    utility.round_to(previousPos.X, self.PrinterTolerance)
                    or utility.round_to(pos.Y, self.PrinterTolerance) !=
                    utility.round_to(previousPos.Y, self.PrinterTolerance)
                    or utility.round_to(pos.Z, self.PrinterTolerance) !=
                    utility.round_to(previousPos.Z, self.PrinterTolerance))

                if (hasExtruderChanged or hasXYZChanged):
                    self.HasPositionChanged = True

                    # calculate LastExtrusionHeight and Height
                    if (self.Extruder.IsExtruding()
                            or self.Extruder.IsExtrudingStart()):
                        pos.LastExtrusionHeight = pos.Z
                        if (pos.Height is None or utility.round_to(
                                pos.Z, self.PrinterTolerance) > self.Height):
                            self.Height = utility.round_to(
                                pos.Z, self.PrinterTolerance)
                            pos.Height = self.Height
                            self.Settings.CurrentDebugProfile(
                            ).LogPositionHeightChange(
                                "Position - Reached New Height:{0}.".format(
                                    pos.Height))

                    # calculate layer change
                    if (utility.round_to(self.ZDelta(pos),
                                         self.PrinterTolerance) > 0
                            or self.Layer == 0):
                        self.IsLayerChange = True
                        self.Layer += 1
                        self.Settings.CurrentDebugProfile(
                        ).LogPositionLayerChange(
                            "Position - Layer:{0}.".format(self.Layer))
                    else:
                        self.IsLayerChange = False

                    # Calculate ZHop based on last extrusion height
                    if (pos.LastExtrusionHeight is not None):
                        # calculate lift, taking into account floating point rounding
                        lift = utility.round_to(
                            pos.Z - pos.LastExtrusionHeight,
                            self.PrinterTolerance)
                        if (lift >= self.Printer.z_hop):
                            lift = self.Printer.z_hop
                        isLifted = self.Printer.z_hop > 0.0 and lift >= self.Printer.z_hop and (
                            not self.Extruder.IsExtruding()
                            or self.Extruder.IsExtrudingStart())

                        if (isLifted):
                            if (not pos.IsZHop):
                                pos.IsZHopStart = True
                        else:
                            if (pos.IsZHop):
                                pos.IsZHopCompleting = True

                    if (pos.IsZHopStart):
                        self.Settings.CurrentDebugProfile().LogPositionZHop(
                            "Position - ZhopStart:{0}".format(
                                self.Printer.z_hop))
                    if (pos.IsZHop):
                        self.Settings.CurrentDebugProfile().LogPositionZHop(
                            "Position - Zhop:{0}".format(self.Printer.z_hop))
                    if (pos.IsZHopCompleting):
                        self.Settings.CurrentDebugProfile().LogPositionZHop(
                            "Position - IsZHopCompleting:{0}".format(
                                self.Printer.z_hop))

        # Add the current position, remove positions if we have more than 5 from the end
        self.Positions.insert(0, pos)
        while (len(self.Positions) > 5):
            del self.Positions[5]

    def HasHomedAxis(self):
        if (len(self.Positions) < 2):
            return False
        pos = self.Positions[0]
        previousPos = self.Positions[1]

        return (pos.XHomed and pos.YHomed and pos.ZHomed and pos.X is not None
                and pos.Y is not None and pos.Z is not None
                and previousPos.X is not None and previousPos.Y is not None
                and previousPos.Z is not None)

    def XRelative(self):
        if (len(self.Positions) < 2):
            return None
        pos = self.Positions[0]
        prevoiusPos = self.Positions[1]
        return pos.X - previousPos.X

    def YRelative(self):
        if (len(self.Positions) < 2):
            return None
        pos = self.Positions[0]
        prevoiusPos = self.Positions[1]
        return pos.Y - previousPos.Y

    def ZRelative(self):
        if (len(self.Positions) < 2):
            return None
        pos = self.Positions[0]
        prevoiusPos = self.Positions[1]
        return pos.Z - previousPos.Z

    def ERelative(self, pos):
        if (len(self.Positions) < 1):
            return None
        previousPos = self.Positions[0]
        return pos.E - previousPos.E

    def IsAtPosition(self, x, y, z, pos, tolerance, applyOffset):
        if (applyOffset):
            x = x + pos.XOffset
            y = y + pos.YOffset
            if (z is not None):
                z = z + pos.ZOffset

        if ((pos.X is None or utility.isclose(pos.X, x, abs_tol=tolerance)) and
            (pos.Y is None or utility.isclose(pos.Y, y, abs_tol=tolerance))
                and (z is None or pos.Z is None
                     or utility.isclose(pos.Z, z, abs_tol=tolerance))):
            return True
        return False

    def IsAtPreviousPosition(self, x, y, z=None, applyOffset=True):
        if (len(self.Positions) < 2):
            return False
        return self.IsAtPosition(
            x, y, z, self.Positions[1],
            self.Printer.printer_position_confirmation_tolerance, True)

    def IsAtCurrentPosition(self, x, y, z=None, applyOffset=True):
        if (len(self.Positions) < 1):
            return False
        return self.IsAtPosition(
            x, y, z, self.Positions[0],
            self.Printer.printer_position_confirmation_tolerance, True)

    def IsAtSavedPosition(self, x, y, z=None, applyOffset=True):
        if (self.SavedPosition is None):
            return False
        return self.IsAtPosition(
            x, y, z, self.SavedPosition,
            self.Printer.printer_position_confirmation_tolerance, True)