def __init__(self, octolapse_settings, octoprint_printer_profile, g90_influences_extruder): self.Settings = octolapse_settings self.Printer = Printer(self.Settings.current_printer()) self.Snapshot = Snapshot(self.Settings.current_snapshot()) self.OctoprintPrinterProfile = octoprint_printer_profile self.Origin = { "X": self.Printer.origin_x, "Y": self.Printer.origin_y, "Z": self.Printer.origin_z } self.BoundingBox = utility.get_bounding_box(self.Printer, octoprint_printer_profile) self.PrinterTolerance = self.Printer.printer_position_confirmation_tolerance self.Positions = deque(maxlen=5) self.SavedPosition = None self.HasRestrictedPosition = len( self.Snapshot.position_restrictions) > 0 self.reset() self.Extruder = Extruder(octolapse_settings) if self.Printer.g90_influences_extruder in ['true', 'false']: self.G90InfluencesExtruder = True if self.Printer.g90_influences_extruder == 'true' else False else: self.G90InfluencesExtruder = g90_influences_extruder if self.Printer.z_hop is None: self.Printer.z_hop = 0 self.Commands = command.Commands() self.LocationDetectionCommands = [] self.create_location_detection_commands()
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 setUp(self): self.Settings = OctolapseSettings(NamedTemporaryFile().name) self.OctoprintPrinterProfile = self.create_octoprint_printer_profile() printer = get_printer_profile() self.Settings.printers.update({printer["guid"]: Printer(printer=printer)}) self.Settings.profiles.current_printer_profile_guid = printer["guid"] self.Extruder = Extruder(self.Settings) self.Position = Position(self.Settings, self.OctoprintPrinterProfile, False)
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()
class Position(object): def __init__(self, octolapse_settings, octoprint_printer_profile, g90_influences_extruder): self.Settings = octolapse_settings self.Printer = Printer(self.Settings.current_printer()) self.Snapshot = Snapshot(self.Settings.current_snapshot()) self.OctoprintPrinterProfile = octoprint_printer_profile self.Origin = { "X": self.Printer.origin_x, "Y": self.Printer.origin_y, "Z": self.Printer.origin_z } self.BoundingBox = utility.get_bounding_box(self.Printer, octoprint_printer_profile) self.PrinterTolerance = self.Printer.printer_position_confirmation_tolerance self.Positions = deque(maxlen=5) self.SavedPosition = None self.HasRestrictedPosition = len( self.Snapshot.position_restrictions) > 0 self.reset() self.Extruder = Extruder(octolapse_settings) if self.Printer.g90_influences_extruder in ['true', 'false']: self.G90InfluencesExtruder = True if self.Printer.g90_influences_extruder == 'true' else False else: self.G90InfluencesExtruder = g90_influences_extruder if self.Printer.z_hop is None: self.Printer.z_hop = 0 self.Commands = command.Commands() self.LocationDetectionCommands = [] self.create_location_detection_commands() def create_location_detection_commands(self): if self.Printer.auto_position_detection_commands is not None: trimmed_commands = self.Printer.auto_position_detection_commands.strip( ) if len(trimmed_commands) > 0: self.LocationDetectionCommands = [ x.strip().upper() for x in self.Printer.auto_position_detection_commands.split(',') ] if "G28" not in self.LocationDetectionCommands: self.LocationDetectionCommands.append("G28") if "G29" not in self.LocationDetectionCommands: self.LocationDetectionCommands.append("G29") def reset(self): # todo: This reset function doesn't seem to reset everything. self.Positions.clear() self.SavedPosition = None def update_position(self, x=None, y=None, z=None, e=None, f=None, force=False, calculate_changes=False): num_positions = len(self.Positions) if num_positions == 0: return pos = self.Positions[0] pos.update_position(self.BoundingBox, x, y, z, e, f, force) if calculate_changes and num_positions > 1: previous_pos = self.Positions[1] pos.HasPositionChanged = not pos.is_position_equal( previous_pos, self.PrinterTolerance) pos.HasStateChanged = not pos.is_state_equal( previous_pos, self.PrinterTolerance) def to_dict(self): if len(self.Positions) > 0: previous_pos = self.Positions[0] return previous_pos.to_dict() return None def to_position_dict(self): if len(self.Positions) > 0: previous_pos = self.Positions[0] return previous_pos.to_position_dict() return None def to_state_dict(self): if len(self.Positions) > 0: previous_pos = self.Positions[0] return previous_pos.to_state_dict() return None def z_delta(self, pos, index=0): previous_pos = self.get_position(index) if previous_pos is not None: # calculate ZDelta if pos.Height is not None: if previous_pos.Height is None: return pos.Height else: return pos.Height - previous_pos.Height return 0 def distance_to_zlift(self, index=0): pos = self.get_position(index) assert (isinstance(pos, Pos)) if pos is None: return None # get the lift amount, but don't restrict it so we can log properly amount_to_lift = pos.distance_to_zlift(self.Printer.z_hop, False) if amount_to_lift < 0: # the current lift is negative self.Settings.current_debug_profile().log_warning( "position.py - A 'distance_to_zlift' was requested, " "but the current lift is already above the z_hop height.") return 0 elif amount_to_lift > self.Printer.z_hop: # For some reason we're lower than we expected self.Settings.current_debug_profile().log_warning( "position.py - A 'distance_to_zlift' was requested, " "but was found to be more than the z_hop height.") return self.Printer.z_hop else: # we are in-between 0 and z_hop, calculate lift return amount_to_lift def has_state_changed(self, index=0): pos = self.get_position(index) if pos is None: return None return pos.HasStateChanged def is_in_position(self, index=0): pos = self.get_position(index) if pos is None: return None return pos.IsInPosition def in_path_position(self, index=0): pos = self.get_position(index) if pos is None: return None return pos.InPathPosition def has_position_changed(self, index=0): pos = self.get_position(index) if pos is None: return None return pos.HasPositionChanged def has_position_error(self, index=0): pos = self.get_position(index) if pos is None: return None return pos.HasPositionError def position_error(self, index=0): pos = self.get_position(index) if pos is None: return None return pos.PositionError def x(self, index=0): pos = self.get_position(index) if pos is None: return None return pos.X def x_offset(self, index=0): pos = self.get_position(index) if pos is None: return None return pos.XOffset def y(self, index=0): pos = self.get_position(index) if pos is None: return None return pos.Y def y_offset(self, index=0): pos = self.get_position(index) if pos is None: return None return pos.YOffset def z(self, index=0): pos = self.get_position(index) if pos is None: return None return pos.Z def z_offset(self, index=0): pos = self.get_position(index) if pos is None: return None return pos.ZOffset def e(self, index=0): pos = self.get_position(index) if pos is None: return None return pos.E def e_offset(self, index=0): pos = self.get_position(index) if pos is None: return None return pos.EOffset def f(self, index=0): pos = self.get_position(index) if pos is None: return None return pos.F def is_zhop(self, index=0): pos = self.get_position(index) if pos is None: return None return pos.IsZHop def is_layer_change(self, index=0): pos = self.get_position(index) if pos is None: return None return pos.IsLayerChange def layer(self, index=0): pos = self.get_position(index) if pos is None: return None return pos.Layer def height(self, index=0): pos = self.get_position(index) if pos is None: return None return pos.Height def is_relative(self, index=0): pos = self.get_position(index) if pos is None: return None return pos.IsRelative def is_extruder_relative(self, index=0): pos = self.get_position(index) if pos is None: return None return pos.IsExtruderRelative def is_metric(self, index=0): pos = self.get_position(index) if pos is None: return None return pos.IsMetric def has_received_home_command(self, index=0): pos = self.get_position(index) if pos is None: return False return pos.HasReceivedHomeCommand and self.has_homed_axes(index) def command_requires_location_detection(self, cmd): if self.Printer.auto_detect_position: gcode = command.get_gcode_from_string(cmd) if gcode in self.LocationDetectionCommands: return True return False def requires_location_detection(self, index=0): pos = self.get_position(index) if pos is None: return False if self.command_requires_location_detection(pos.GCode): return True return False def undo_update(self): pos = self.get_position(0) if pos is not None: self.Positions.popleft() self.Extruder.undo_update() def get_position(self, index=0): if len(self.Positions) > index: return self.Positions[index] return None def update(self, gcode): cmd = self.Commands.get_command(gcode) # a new position pos = None previous_pos = None num_positions = len(self.Positions) if num_positions > 0: pos = Pos(self.Printer, self.OctoprintPrinterProfile, self.Positions[0]) previous_pos = Pos(self.Printer, self.OctoprintPrinterProfile, self.Positions[0]) if pos is None: pos = Pos(self.Printer, self.OctoprintPrinterProfile) if previous_pos is None: previous_pos = Pos(self.Printer, self.OctoprintPrinterProfile) # reset the current position state (copied from the previous position, # or a # new position) pos.reset_state() # set the pos gcode cmd pos.GCode = gcode # apply the cmd to the position tracker # TODO: this should NOT be an else/if structure anymore.. Simplify if cmd is not None: if cmd.Command in self.Commands.CommandsRequireMetric and not pos.IsMetric: pos.HasPositionError = True pos.PositionError = "Units are not metric. Unable to continue print." elif cmd.Command in ["G0", "G1"]: # Movement if cmd.parse(): self.Settings.current_debug_profile( ).log_position_command_received("Received {0}".format( cmd.Name)) x = cmd.Parameters["X"].Value y = cmd.Parameters["Y"].Value z = cmd.Parameters["Z"].Value e = cmd.Parameters["E"].Value f = cmd.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.IsRelative is not None: if pos.HasPositionError and not pos.IsRelative: pos.HasPositionError = False pos.PositionError = "" pos.update_position(self.BoundingBox, x, y, z, e=None, f=f) else: self.Settings.current_debug_profile( ).log_position_command_received( "Position - Unable to update the X/Y/Z axis position, the axis mode (" "relative/absolute) has not been explicitly set via G90/G91. " ) if e is not None: if pos.IsExtruderRelative is not None: if pos.HasPositionError and not pos.IsExtruderRelative: pos.HasPositionError = False pos.PositionError = "" pos.update_position(self.BoundingBox, x=None, y=None, z=None, e=e, f=None) else: self.Settings.current_debug_profile().log_error( "Position - Unable to update the extruder position, the extruder mode (" "relative/absolute) 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 previous_pos 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", previous_pos.X, previous_pos.Y, previous_pos.Z, previous_pos.E, pos.X, pos.Y, pos.Z, pos.E) self.Settings.current_debug_profile().log_position_change( message) else: error_message = "Unable to parse the gcode command: {0}".format( gcode) pos.HasPositionError = True # Todo: We need to end the timelapse if we get here #pos.X = None #pos.Y = None #pos.Z = None #pos.E = None# #pos.F = None pos.PositionError = "Unable to parse the gcode command" self.Settings.current_debug_profile().log_error( "Position - {0}".format(error_message)) elif cmd.Command == "G20": # change units to inches if pos.IsMetric is None or pos.IsMetric: self.Settings.current_debug_profile( ).log_position_command_received( "Received G20 - Switching units to inches.") pos.IsMetric = False else: self.Settings.current_debug_profile( ).log_position_command_received( "Received G20 - Already in inches.") elif cmd.Command == "G21": # change units to millimeters if pos.IsMetric is None or not pos.IsMetric: self.Settings.current_debug_profile( ).log_position_command_received( "Received G21 - Switching units to millimeters.") pos.IsMetric = True else: self.Settings.current_debug_profile( ).log_position_command_received( "Received G21 - Already in millimeters.") elif cmd.Command == "G28": # Home if cmd.parse(): pos.HasReceivedHomeCommand = True x = cmd.Parameters["X"].Value y = cmd.Parameters["Y"].Value z = cmd.Parameters["Z"].Value # ignore the W parameter, it's used in Prusa firmware to indicate a home without mesh bed leveling # w = cmd.Parameters["W"].Value x_homed = False y_homed = False z_homed = False if x is not None: x_homed = True if y is not None: y_homed = True if z is not None: z_homed = True # if there are no x,y or z parameters, we're homing all axes if x is None and y is None and z is None: x_homed = True y_homed = True z_homed = True home_strings = [] if x_homed: pos.XHomed = True pos.X = self.Origin[ "X"] if not self.Printer.auto_detect_position else None if pos.X is None: home_strings.append("Homing X to Unknown Origin.") else: home_strings.append("Homing X to {0}.".format( get_formatted_coordinate(pos.X))) if y_homed: pos.YHomed = True pos.Y = self.Origin[ "Y"] if not self.Printer.auto_detect_position else None if pos.Y is None: home_strings.append("Homing Y to Unknown Origin.") else: home_strings.append("Homing Y to {0}.".format( get_formatted_coordinate(pos.Y))) if z_homed: pos.ZHomed = True pos.Z = self.Origin[ "Z"] if not self.Printer.auto_detect_position else None if pos.Z is None: home_strings.append("Homing Z to Unknown Origin.") else: home_strings.append("Homing Z to {0}.".format( get_formatted_coordinate(pos.Z))) self.Settings.current_debug_profile( ).log_position_command_received("Received G28 - ".format( " ".join(home_strings))) pos.HasPositionError = False pos.PositionError = None # we must do this in case we have more than one home command previous_pos = Pos(self.Printer, self.OctoprintPrinterProfile, pos) else: self.Settings.current_debug_profile().log_error( "Position - Unable to parse the Gcode:{0}".format( gcode)) elif cmd.Command == "G90": # change x,y,z to absolute if pos.IsRelative is None or pos.IsRelative: self.Settings.current_debug_profile( ).log_position_command_received( "Received G90 - Switching to absolute x,y,z coordinates." ) pos.IsRelative = False else: self.Settings.current_debug_profile( ).log_position_command_received( "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 is None or pos.IsExtruderRelative: self.Settings.current_debug_profile( ).log_position_command_received( "Received G90 - Switching to absolute extruder coordinates" ) pos.IsExtruderRelative = False else: self.Settings.current_debug_profile( ).log_position_command_received( "Received G90 - Already using absolute extruder coordinates" ) elif cmd.Command == "G91": # change x,y,z to relative if pos.IsRelative is None or not pos.IsRelative: self.Settings.current_debug_profile( ).log_position_command_received( "Received G91 - Switching to relative x,y,z coordinates" ) pos.IsRelative = True else: self.Settings.current_debug_profile( ).log_position_command_received( "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 pos.IsExtruderRelative is None or not pos.IsExtruderRelative: self.Settings.current_debug_profile( ).log_position_command_received( "Received G91 - Switching to relative extruder coordinates" ) pos.IsExtruderRelative = True else: self.Settings.current_debug_profile( ).log_position_command_received( "Received G91 - Already using relative extruder coordinates" ) elif cmd.Command == "M83": # Extruder - Set Relative if pos.IsExtruderRelative is None or not pos.IsExtruderRelative: self.Settings.current_debug_profile( ).log_position_command_received( "Received M83 - Switching Extruder to Relative Coordinates" ) pos.IsExtruderRelative = True elif cmd.Command == "M82": # Extruder - Set Absolute if pos.IsExtruderRelative is None or pos.IsExtruderRelative: self.Settings.current_debug_profile( ).log_position_command_received( "Received M82 - Switching Extruder to Absolute Coordinates" ) pos.IsExtruderRelative = False elif cmd.Command == "G92": # Set Position (offset) if cmd.parse(): x = cmd.Parameters["X"].Value y = cmd.Parameters["Y"].Value z = cmd.Parameters["Z"].Value e = cmd.Parameters["E"].Value 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.get_float(x, 0) if y is not None and pos.Y is not None and pos.YHomed: pos.YOffset = pos.Y - utility.get_float(y, 0) if z is not None and pos.Z is not None and pos.ZHomed: pos.ZOffset = pos.Z - utility.get_float(z, 0) if e is not None and pos.E is not None: pos.EOffset = pos.E - utility.get_float(e, 0) self.Settings.current_debug_profile( ).log_position_command_received( "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.current_debug_profile().log_error( "Position - Unable to parse the Gcode:{0}".format( gcode)) ######################################## # Update the extruder monitor. self.Extruder.update(self.e_relative_pos(pos)) ######################################## # If we have a homed axis, detect changes. ######################################## # for now we're going to ignore extruder changes and just run calculations every time. # has_extruder_changed = self.Extruder.HasChanged() pos.HasPositionChanged = not pos.is_position_equal(previous_pos, 0) pos.HasStateChanged = not pos.is_state_equal(previous_pos, self.PrinterTolerance) if pos.has_homed_position() and previous_pos.has_homed_position(): # if (hasExtruderChanged or pos.HasPositionChanged): if pos.HasPositionChanged: if self.HasRestrictedPosition: _is_in_position, _intersections = self.calculate_path_intersections( self.Snapshot.position_restrictions, pos.X, pos.Y, previous_pos.X, previous_pos.Y) if _is_in_position: pos.IsInPosition = _is_in_position else: pos.IsInPosition = False pos.InPathPosition = _intersections else: pos.IsInPosition = True # calculate LastExtrusionHeight and Height if self.Extruder.is_extruding(): pos.LastExtrusionHeight = pos.Z # see if we have primed yet if self.Printer.priming_height > 0: if not pos.IsPrimed and pos.LastExtrusionHeight < self.Printer.priming_height: pos.IsPrimed = True else: # if we have no priming height set, just set IsPrimed = true. pos.IsPrimed = True # make sure we are primed before calculating height/layers if pos.IsPrimed: if pos.Height is None or utility.round_to( pos.Z, self.PrinterTolerance) > previous_pos.Height: pos.Height = utility.round_to(pos.Z, self.PrinterTolerance) self.Settings.current_debug_profile( ).log_position_height_change( "Position - Reached New Height:{0}.".format( pos.Height)) # calculate layer change if (utility.round_to(self.z_delta(pos), self.PrinterTolerance) > 0 or pos.Layer == 0): pos.IsLayerChange = True pos.Layer += 1 self.Settings.current_debug_profile( ).log_position_layer_change( "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 = pos.distance_to_zlift(self.Printer.z_hop) # todo: replace rounding with a call to is close or greater than utility function lift = utility.round_to(lift, self.PrinterTolerance) is_lifted = lift >= self.Printer.z_hop and ( not self.Extruder.is_extruding() or self.Extruder.is_extruding_start()) if is_lifted or self.Printer.z_hop == 0: pos.IsZHop = True if pos.IsZHop and self.Printer.z_hop > 0: self.Settings.current_debug_profile().log_position_zhop( "Position - Zhop:{0}".format(self.Printer.z_hop)) self.Positions.appendleft(pos) def has_homed_position(self, index=0): if len(self.Positions) <= index: return None pos = self.Positions[index] return pos.has_homed_position() def has_homed_axes(self, index=0): if len(self.Positions) <= index: return None pos = self.Positions[index] return pos.has_homed_axes() def x_relative(self, index=0, x=None): if x: if len(self.Positions) <= index: return None pos = self.Positions[index] return x - pos.X + pos.XOffset else: if len(self.Positions) <= index + 1: return None pos = self.Positions[index] previous_pos = self.Positions[index + 1] return pos.X - previous_pos.X def y_relative(self, index=0, y=None): if y: if len(self.Positions) <= index: return None pos = self.Positions[index] return y - pos.Y + pos.YOffset else: if len(self.Positions) <= index + 1: return None pos = self.Positions[index] previous_pos = self.Positions[index + 1] return pos.Y - previous_pos.Y def z_relative(self, index=0, z=None): if z: if len(self.Positions) <= index: return None pos = self.Positions[index] return z - pos.Z + pos.ZOffset else: if len(self.Positions) <= index + 1: return None pos = self.Positions[index] previous_pos = self.Positions[index + 1] return pos.Z - previous_pos.Z def e_relative(self, index=0, e=None): if e: if len(self.Positions) <= index: return None pos = self.Positions[index] return e - pos.E + pos.EOffset else: if len(self.Positions) <= index + 1: return None pos = self.Positions[index] previous_pos = self.Positions[index + 1] return pos.E - previous_pos.E def e_relative_pos(self, pos): if len(self.Positions) < 1: return None previous_pos = self.Positions[0] return pos.E - previous_pos.E @staticmethod def is_at_position(x, y, z, pos, tolerance, apply_offsets): if apply_offsets: x = x + pos.XOffset y = y + pos.YOffset if z is not None: z = z + pos.ZOffset if ((pos.X is None or utility.is_close(pos.X, x, abs_tol=tolerance)) and (pos.Y is None or utility.is_close(pos.Y, y, abs_tol=tolerance)) and (z is None or pos.Z is None or utility.is_close(pos.Z, z, abs_tol=tolerance))): return True return False def is_at_previous_position(self, x, y, z=None): if len(self.Positions) < 2: return False return self.is_at_position( x, y, z, self.Positions[1], self.Printer.printer_position_confirmation_tolerance, True) def is_at_current_position(self, x, y, z=None): if len(self.Positions) < 1: return False return self.is_at_position( x, y, z, self.Positions[0], self.Printer.printer_position_confirmation_tolerance, True) def get_position_string(self, index=0): if len(self.Positions) < 1: return get_formatted_coordinates(None, None, None, None) current_position = self.Positions[index] return get_formatted_coordinates(current_position.X, current_position.Y, current_position.Z, current_position.E) def calculate_path_intersections(self, restrictions, x, y, previous_x, previous_y): if self.calculate_is_in_position( restrictions, x, y, self.Printer.printer_position_confirmation_tolerance): return True, None if previous_x is None or previous_y is None: return False, False return False, self.calculate_in_position_intersection( restrictions, x, y, previous_x, previous_y, self.Printer.printer_position_confirmation_tolerance) @staticmethod def calculate_in_position_intersection(restrictions, x, y, previous_x, previous_y, tolerance): intersections = [] for restriction in restrictions: cur_intersections = restriction.get_intersections( x, y, previous_x, previous_y) if cur_intersections: for cur_intersection in cur_intersections: intersections.append(cur_intersection) if len(intersections) == 0: return False for intersection in intersections: if Position.calculate_is_in_position(restrictions, intersection[0], intersection[1], tolerance): # calculate the distance from x/y previous to the intersection distance_to_intersection = math.sqrt( math.pow(previous_x - intersection[0], 2) + math.pow(previous_y - intersection[1], 2)) # calculate the length of the lin x,y to previous_x, previous_y total_distance = math.sqrt( math.pow(previous_x - x, 2) + math.pow(previous_y - y, 2)) if total_distance > 0: path_ratio_1 = distance_to_intersection / total_distance path_ratio_2 = 1.0 - path_ratio_1 else: path_ratio_1 = 0 path_ratio_2 = 0 return { 'intersection': intersection, 'path_ratio_1': path_ratio_1, 'path_ratio_2': path_ratio_2 } return False @staticmethod def calculate_is_in_position(restrictions, x, y, tolerance): # we need to know if there is at least one required position has_required_position = False # isInPosition will be used to determine if we return # true where we have at least one required type in_position = False # loop through each restriction for restriction in restrictions: if restriction.Type == "required": # we have at least on required position, so at least one point must be in # position for us to return true has_required_position = True if restriction.is_in_position(x, y, tolerance): if restriction.Type == "forbidden": # if we're in a forbidden position, return false now return False else: # we're in position in at least one required position restriction in_position = True if has_required_position: # if at least one position restriction is required return in_position # if we're here then we only have forbidden restrictions, but the point # was not within the restricted area(s) return True
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)
def setUp(self): self.Settings = OctolapseSettings("c:\\temp\\octolapse.log") self.Extruder = Extruder(self.Settings)
def setUp(self): self.Settings = OctolapseSettings(NamedTemporaryFile().name) self.Extruder = Extruder(self.Settings) # set the retraction distance self.Extruder.Printerretraction_length = 4
class TestExtruder(unittest.TestCase): def setUp(self): self.Settings = OctolapseSettings(NamedTemporaryFile().name) self.Extruder = Extruder(self.Settings) # set the retraction distance self.Extruder.Printerretraction_length = 4 @staticmethod def create_octoprint_printer_profile(): return { "volume": { "custom_box": False, "width": 250, "depth": 200, "height": 200 } } def test_ResetInitialState(self): """Test the initial extruder state, change all values, reset and check again""" # Check the initial state self.assertEquals(len(self.Extruder.StateHistory), 1) # add some states state1 = ExtruderState() self.Extruder.add_state(state1) state2 = ExtruderState() self.Extruder.add_state(state2) state3 = ExtruderState() self.Extruder.add_state(state3) # check the length of StateHistory self.assertEquals(len(self.Extruder.StateHistory), 4) # reset the state and check again self.Extruder.reset() self.assertEquals(len(self.Extruder.StateHistory), 0) def test_ExtruderState_InitialValues(self): # create a new state state = ExtruderState() # verify the initial values self.assertEquals(state.E, 0) self.assertEquals(state.extrusion_length, 0.0) self.assertEquals(state.extruder_length_total, 0.0) self.assertEquals(state.retraction_length, 0.0) self.assertEquals(state.deretraction_length, 0.0) self.assertFalse(state.is_extruding_start) self.assertFalse(state.is_extruding) self.assertFalse(state.is_primed) self.assertFalse(state.is_retracting_start) self.assertFalse(state.is_retracting) self.assertFalse(state.is_partially_retracted) self.assertFalse(state.is_retracted) self.assertFalse(state.is_deretracting_start) self.assertFalse(state.is_deretracting) self.assertFalse(state.is_deretracted) self.assertFalse(state.has_changed) def test_ExtruderStateCopy(self): # create a new state state = ExtruderState() # change all the default values state.E = 1 state.extrusion_length = 100 state.extruder_length_total = 200 state.retraction_length = 300 state.deretraction_length = 400 state.is_extruding_start = True state.is_extruding = True state.is_primed = True state.is_retracting_start = True state.is_retracting = True state.is_partially_retracted = True state.is_retracted = True state.is_deretracting_start = True state.is_deretracting = True state.is_deretracted = True state.has_changed = True # copy to a new state new_state = ExtruderState(state) # verify the state was copied correctly self.assertEquals(new_state.E, 1) self.assertEquals(new_state.extrusion_length, 100) self.assertEquals(new_state.extruder_length_total, 200) self.assertEquals(new_state.retraction_length, 300) self.assertEquals(new_state.deretraction_length, 400) self.assertTrue(new_state.is_extruding_start) self.assertTrue(new_state.is_extruding) self.assertTrue(new_state.is_primed) self.assertTrue(new_state.is_retracting_start) self.assertTrue(new_state.is_retracting) self.assertTrue(new_state.is_partially_retracted) self.assertTrue(new_state.is_retracted) self.assertTrue(new_state.is_deretracting_start) self.assertTrue(new_state.is_deretracting) self.assertTrue(new_state.is_deretracted) self.assertTrue(new_state.has_changed) def test_has_changed(self): """Test the has_changed flag""" # test the initial state self.assertFalse(self.Extruder.has_changed()) # test updating with no movement - Change to primed self.Extruder.update(0) self.assertTrue(self.Extruder.has_changed()) # test updating with no movement self.Extruder.update(0) self.assertFalse(self.Extruder.has_changed()) # test updating with movement self.Extruder.update(1) self.assertTrue(self.Extruder.has_changed()) # test updating with no movement self.Extruder.update(0) self.assertTrue(self.Extruder.has_changed()) # test updating with slight movement self.Extruder.update(.0001) self.assertTrue(self.Extruder.has_changed()) # test updating with slight movement self.Extruder.update(.0001) self.assertTrue(self.Extruder.has_changed()) # test updating with slight movement self.Extruder.update(.01) self.assertFalse(self.Extruder.has_changed()) # test updating with slight movement self.Extruder.update(.01) self.assertFalse(self.Extruder.has_changed()) # test updating with no movement self.Extruder.update(0) self.assertTrue(self.Extruder.has_changed()) # test updating with no movement, is_primed changed from 0 to 1 self.Extruder.update(0) self.assertFalse(self.Extruder.has_changed()) # test updating with no movement self.Extruder.update(0) self.assertFalse(self.Extruder.has_changed()) # test updating with slight negative movement self.Extruder.update(-0.01) self.assertTrue(self.Extruder.has_changed()) def test_ExtruderStates_Initial(self): """Test the All Extruder States""" # test the initial state self.assertFalse(self.Extruder.is_extruding_start()) self.assertFalse(self.Extruder.is_extruding()) self.assertFalse(self.Extruder.is_primed()) self.assertFalse(self.Extruder.is_retracting_start()) self.assertFalse(self.Extruder.is_retracting()) self.assertFalse(self.Extruder.is_partially_retracted()) self.assertFalse(self.Extruder.is_retracted()) self.assertFalse(self.Extruder.is_deretracting_start()) self.assertFalse(self.Extruder.is_deretracting()) self.assertFalse(self.Extruder.is_deretracted()) def test_ExtruderStates_FromExtruding(self): # 1, 0 - From extruding to primed self.Extruder.reset() self.Extruder.update(1) self.Extruder.update(0) self.assertFalse(self.Extruder.is_extruding_start()) self.assertFalse(self.Extruder.is_extruding()) self.assertTrue(self.Extruder.is_primed()) self.assertFalse(self.Extruder.is_retracting_start()) self.assertFalse(self.Extruder.is_retracting()) self.assertFalse(self.Extruder.is_partially_retracted()) self.assertFalse(self.Extruder.is_retracted()) self.assertFalse(self.Extruder.is_deretracting_start()) self.assertFalse(self.Extruder.is_deretracting()) self.assertFalse(self.Extruder.is_deretracted()) # 1, 1 - keep extruding self.Extruder.reset() self.Extruder.update(1) self.Extruder.update(1) self.assertFalse(self.Extruder.is_extruding_start()) self.assertTrue(self.Extruder.is_extruding()) self.assertFalse(self.Extruder.is_primed()) self.assertFalse(self.Extruder.is_retracting_start()) self.assertFalse(self.Extruder.is_retracting()) self.assertFalse(self.Extruder.is_partially_retracted()) self.assertFalse(self.Extruder.is_retracted()) self.assertFalse(self.Extruder.is_deretracting_start()) self.assertFalse(self.Extruder.is_deretracting()) self.assertFalse(self.Extruder.is_deretracted()) # 1, -1 - from extruding to partially retracted self.Extruder.reset() self.Extruder.update(1) self.Extruder.update(-1) self.assertFalse(self.Extruder.is_extruding_start()) self.assertFalse(self.Extruder.is_extruding()) self.assertFalse(self.Extruder.is_primed()) self.assertTrue(self.Extruder.is_retracting_start()) self.assertTrue(self.Extruder.is_retracting()) self.assertTrue(self.Extruder.is_partially_retracted()) self.assertFalse(self.Extruder.is_retracted()) self.assertFalse(self.Extruder.is_deretracting_start()) self.assertFalse(self.Extruder.is_deretracting()) self.assertFalse(self.Extruder.is_deretracted()) # 1, -4 - from extruding to fully retracted self.Extruder.reset() self.Extruder.update(1) self.Extruder.update(-4) self.assertFalse(self.Extruder.is_extruding_start()) self.assertFalse(self.Extruder.is_extruding()) self.assertFalse(self.Extruder.is_primed()) self.assertTrue(self.Extruder.is_retracting_start()) self.assertTrue(self.Extruder.is_retracting()) self.assertFalse(self.Extruder.is_partially_retracted()) self.assertTrue(self.Extruder.is_retracted()) self.assertFalse(self.Extruder.is_deretracting_start()) self.assertFalse(self.Extruder.is_deretracting()) self.assertFalse(self.Extruder.is_deretracted()) # 1, -5 - from extruding to beyond fully retracted self.Extruder.reset() self.Extruder.update(1) self.Extruder.update(-5) self.assertFalse(self.Extruder.is_extruding_start()) self.assertFalse(self.Extruder.is_extruding()) self.assertFalse(self.Extruder.is_primed()) self.assertTrue(self.Extruder.is_retracting_start()) self.assertTrue(self.Extruder.is_retracting()) self.assertFalse(self.Extruder.is_partially_retracted()) self.assertTrue(self.Extruder.is_retracted()) self.assertFalse(self.Extruder.is_deretracting_start()) self.assertFalse(self.Extruder.is_deretracting()) self.assertFalse(self.Extruder.is_deretracted()) def test_ExtruderStates_FromPrimed(self): # 0, 0 - remain primed self.Extruder.reset() self.Extruder.update(0) self.Extruder.update(0) self.assertFalse(self.Extruder.is_extruding_start()) self.assertFalse(self.Extruder.is_extruding()) self.assertTrue(self.Extruder.is_primed()) self.assertFalse(self.Extruder.is_retracting_start()) self.assertFalse(self.Extruder.is_retracting()) self.assertFalse(self.Extruder.is_partially_retracted()) self.assertFalse(self.Extruder.is_retracted()) self.assertFalse(self.Extruder.is_deretracting_start()) self.assertFalse(self.Extruder.is_deretracting()) self.assertFalse(self.Extruder.is_deretracted()) # 0, 1 - from primed to extruding self.Extruder.reset() self.Extruder.update(0) self.Extruder.update(1) self.assertTrue(self.Extruder.is_extruding_start()) self.assertTrue(self.Extruder.is_extruding()) self.assertFalse(self.Extruder.is_primed()) self.assertFalse(self.Extruder.is_retracting_start()) self.assertFalse(self.Extruder.is_retracting()) self.assertFalse(self.Extruder.is_partially_retracted()) self.assertFalse(self.Extruder.is_retracted()) self.assertFalse(self.Extruder.is_deretracting_start()) self.assertFalse(self.Extruder.is_deretracting()) self.assertFalse(self.Extruder.is_deretracted()) # 0, -1 - from primed to partially retracted self.Extruder.reset() self.Extruder.update(0) self.Extruder.update(-1) self.assertFalse(self.Extruder.is_extruding_start()) self.assertFalse(self.Extruder.is_extruding()) self.assertFalse(self.Extruder.is_primed()) self.assertTrue(self.Extruder.is_retracting_start()) self.assertTrue(self.Extruder.is_retracting()) self.assertTrue(self.Extruder.is_partially_retracted()) self.assertFalse(self.Extruder.is_retracted()) self.assertFalse(self.Extruder.is_deretracting_start()) self.assertFalse(self.Extruder.is_deretracting()) self.assertFalse(self.Extruder.is_deretracted()) # 0, -4 - from primed to fully retracted self.Extruder.reset() self.Extruder.update(0) self.Extruder.update(-4) self.assertFalse(self.Extruder.is_extruding_start()) self.assertFalse(self.Extruder.is_extruding()) self.assertFalse(self.Extruder.is_primed()) self.assertTrue(self.Extruder.is_retracting_start()) self.assertTrue(self.Extruder.is_retracting()) self.assertFalse(self.Extruder.is_partially_retracted()) self.assertTrue(self.Extruder.is_retracted()) self.assertFalse(self.Extruder.is_deretracting_start()) self.assertFalse(self.Extruder.is_deretracting()) self.assertFalse(self.Extruder.is_deretracted()) # 0, -5 - from primed to beyond fully retracted self.Extruder.reset() self.Extruder.update(0) self.Extruder.update(-5) self.assertFalse(self.Extruder.is_extruding_start()) self.assertFalse(self.Extruder.is_extruding()) self.assertFalse(self.Extruder.is_primed()) self.assertTrue(self.Extruder.is_retracting_start()) self.assertTrue(self.Extruder.is_retracting()) self.assertFalse(self.Extruder.is_partially_retracted()) self.assertTrue(self.Extruder.is_retracted()) self.assertFalse(self.Extruder.is_deretracting_start()) self.assertFalse(self.Extruder.is_deretracting()) self.assertFalse(self.Extruder.is_deretracted()) def test_ExtruderStates_FromPartiallyRetracted(self): # -2,3 - from partially retracted to extruding self.Extruder.reset() self.Extruder.update(-2) self.Extruder.update(3) self.assertTrue(self.Extruder.is_extruding_start()) self.assertTrue(self.Extruder.is_extruding()) self.assertFalse(self.Extruder.is_primed()) self.assertFalse(self.Extruder.is_retracting_start()) self.assertFalse(self.Extruder.is_retracting()) self.assertFalse(self.Extruder.is_partially_retracted()) self.assertFalse(self.Extruder.is_retracted()) self.assertTrue(self.Extruder.is_deretracting_start()) self.assertTrue(self.Extruder.is_deretracting()) self.assertTrue(self.Extruder.is_deretracted()) # -2,2 - deretract self.Extruder.reset() self.Extruder.update(-2) self.Extruder.update(2) self.assertFalse(self.Extruder.is_extruding_start()) self.assertFalse(self.Extruder.is_extruding()) self.assertTrue(self.Extruder.is_primed()) self.assertFalse(self.Extruder.is_retracting_start()) self.assertFalse(self.Extruder.is_retracting()) self.assertFalse(self.Extruder.is_partially_retracted()) self.assertFalse(self.Extruder.is_retracted()) self.assertTrue(self.Extruder.is_deretracting_start()) self.assertTrue(self.Extruder.is_deretracting()) self.assertTrue(self.Extruder.is_deretracted()) # -2,1 - deretract from partially retracted to partially retracted self.Extruder.reset() self.Extruder.update(-2) self.Extruder.update(1) self.assertFalse(self.Extruder.is_extruding_start()) self.assertFalse(self.Extruder.is_extruding()) self.assertFalse(self.Extruder.is_primed()) self.assertFalse(self.Extruder.is_retracting_start()) self.assertFalse(self.Extruder.is_retracting()) self.assertTrue(self.Extruder.is_partially_retracted()) self.assertFalse(self.Extruder.is_retracted()) self.assertTrue(self.Extruder.is_deretracting_start()) self.assertTrue(self.Extruder.is_deretracting()) self.assertFalse(self.Extruder.is_deretracted()) # -2, 0 - remain partially retracted self.Extruder.reset() self.Extruder.update(-2) self.Extruder.update(0) self.assertFalse(self.Extruder.is_extruding_start()) self.assertFalse(self.Extruder.is_extruding()) self.assertFalse(self.Extruder.is_primed()) self.assertFalse(self.Extruder.is_retracting_start()) self.assertFalse(self.Extruder.is_retracting()) self.assertTrue(self.Extruder.is_partially_retracted()) self.assertFalse(self.Extruder.is_retracted()) self.assertFalse(self.Extruder.is_deretracting_start()) self.assertFalse(self.Extruder.is_deretracting()) self.assertFalse(self.Extruder.is_deretracted()) # -2,-1 - retract from partially retracted to partially retracted self.Extruder.reset() self.Extruder.update(-2) self.Extruder.update(-1) self.assertFalse(self.Extruder.is_extruding_start()) self.assertFalse(self.Extruder.is_extruding()) self.assertFalse(self.Extruder.is_primed()) self.assertFalse(self.Extruder.is_retracting_start()) self.assertTrue(self.Extruder.is_retracting()) self.assertTrue(self.Extruder.is_partially_retracted()) self.assertFalse(self.Extruder.is_retracted()) self.assertFalse(self.Extruder.is_deretracting_start()) self.assertFalse(self.Extruder.is_deretracting()) self.assertFalse(self.Extruder.is_deretracted()) # -2,-2 - from partially retracted to fully retracted self.Extruder.reset() self.Extruder.update(-2) self.Extruder.update(-2) self.assertFalse(self.Extruder.is_extruding_start()) self.assertFalse(self.Extruder.is_extruding()) self.assertFalse(self.Extruder.is_primed()) self.assertFalse(self.Extruder.is_retracting_start()) self.assertTrue(self.Extruder.is_retracting()) self.assertFalse(self.Extruder.is_partially_retracted()) self.assertTrue(self.Extruder.is_retracted()) self.assertFalse(self.Extruder.is_deretracting_start()) self.assertFalse(self.Extruder.is_deretracting()) self.assertFalse(self.Extruder.is_deretracted()) # -2,-3 - from partially retracted to beyond partially retracted self.Extruder.reset() self.Extruder.update(-2) self.Extruder.update(-3) self.assertFalse(self.Extruder.is_extruding_start()) self.assertFalse(self.Extruder.is_extruding()) self.assertFalse(self.Extruder.is_primed()) self.assertFalse(self.Extruder.is_retracting_start()) self.assertTrue(self.Extruder.is_retracting()) self.assertFalse(self.Extruder.is_partially_retracted()) self.assertTrue(self.Extruder.is_retracted()) self.assertFalse(self.Extruder.is_deretracting_start()) self.assertFalse(self.Extruder.is_deretracting()) self.assertFalse(self.Extruder.is_deretracted()) def test_ExtruderStates_FromFullyRetracted(self): # -4,5 - Fully Retracted To Extruding self.Extruder.reset() self.Extruder.update(-4) self.Extruder.update(5) self.assertTrue(self.Extruder.is_extruding_start()) self.assertTrue(self.Extruder.is_extruding()) self.assertFalse(self.Extruder.is_primed()) self.assertFalse(self.Extruder.is_retracting_start()) self.assertFalse(self.Extruder.is_retracting()) self.assertFalse(self.Extruder.is_partially_retracted()) self.assertFalse(self.Extruder.is_retracted()) self.assertTrue(self.Extruder.is_deretracting_start()) self.assertTrue(self.Extruder.is_deretracting()) self.assertTrue(self.Extruder.is_deretracted()) # -4,4 - Fully Retracted to Primed/Deretracted self.Extruder.reset() self.Extruder.update(-4) self.Extruder.update(4) self.assertFalse(self.Extruder.is_extruding_start()) self.assertFalse(self.Extruder.is_extruding()) self.assertTrue(self.Extruder.is_primed()) self.assertFalse(self.Extruder.is_retracting_start()) self.assertFalse(self.Extruder.is_retracting()) self.assertFalse(self.Extruder.is_partially_retracted()) self.assertFalse(self.Extruder.is_retracted()) self.assertTrue(self.Extruder.is_deretracting_start()) self.assertTrue(self.Extruder.is_deretracting()) self.assertTrue(self.Extruder.is_deretracted()) # -4,1 - Fully Retracted to Partially Retracted self.Extruder.reset() self.Extruder.update(-4) self.Extruder.update(1) self.assertFalse(self.Extruder.is_extruding_start()) self.assertFalse(self.Extruder.is_extruding()) self.assertFalse(self.Extruder.is_primed()) self.assertFalse(self.Extruder.is_retracting_start()) self.assertFalse(self.Extruder.is_retracting()) self.assertTrue(self.Extruder.is_partially_retracted()) self.assertFalse(self.Extruder.is_retracted()) self.assertTrue(self.Extruder.is_deretracting_start()) self.assertTrue(self.Extruder.is_deretracting()) self.assertFalse(self.Extruder.is_deretracted()) # -4,0 - Remain Fully Retracted self.Extruder.reset() self.Extruder.update(-4) self.Extruder.update(0) self.assertFalse(self.Extruder.is_extruding_start()) self.assertFalse(self.Extruder.is_extruding()) self.assertFalse(self.Extruder.is_primed()) self.assertFalse(self.Extruder.is_retracting_start()) self.assertFalse(self.Extruder.is_retracting()) self.assertFalse(self.Extruder.is_partially_retracted()) self.assertTrue(self.Extruder.is_retracted()) self.assertFalse(self.Extruder.is_deretracting_start()) self.assertFalse(self.Extruder.is_deretracting()) self.assertFalse(self.Extruder.is_deretracted()) # -4,-1 - Fully Retracted, Continue Retracting self.Extruder.reset() self.Extruder.update(-4) self.Extruder.update(-1) self.assertFalse(self.Extruder.is_extruding_start()) self.assertFalse(self.Extruder.is_extruding()) self.assertFalse(self.Extruder.is_primed()) self.assertFalse(self.Extruder.is_retracting_start()) self.assertTrue(self.Extruder.is_retracting()) self.assertFalse(self.Extruder.is_partially_retracted()) self.assertTrue(self.Extruder.is_retracted()) self.assertFalse(self.Extruder.is_deretracting_start()) self.assertFalse(self.Extruder.is_deretracting()) self.assertFalse(self.Extruder.is_deretracted()) def test_ExtruderStates_FromBeyondFullyRetracted(self): # -5,6 - Beyond fully retracted to extruding self.Extruder.reset() self.Extruder.update(-5) self.Extruder.update(6) self.assertTrue(self.Extruder.is_extruding_start()) self.assertTrue(self.Extruder.is_extruding()) self.assertFalse(self.Extruder.is_primed()) self.assertFalse(self.Extruder.is_retracting_start()) self.assertFalse(self.Extruder.is_retracting()) self.assertFalse(self.Extruder.is_partially_retracted()) self.assertFalse(self.Extruder.is_retracted()) self.assertTrue(self.Extruder.is_deretracting_start()) self.assertTrue(self.Extruder.is_deretracting()) self.assertTrue(self.Extruder.is_deretracted()) # -5,5 - Beyond fully retracted to primed/deretracted self.Extruder.reset() self.Extruder.update(-5) self.Extruder.update(5) self.assertFalse(self.Extruder.is_extruding_start()) self.assertFalse(self.Extruder.is_extruding()) self.assertTrue(self.Extruder.is_primed()) self.assertFalse(self.Extruder.is_retracting_start()) self.assertFalse(self.Extruder.is_retracting()) self.assertFalse(self.Extruder.is_partially_retracted()) self.assertFalse(self.Extruder.is_retracted()) self.assertTrue(self.Extruder.is_deretracting_start()) self.assertTrue(self.Extruder.is_deretracting()) self.assertTrue(self.Extruder.is_deretracted()) # -5,4 - Beyond fully retracted to partially retracted self.Extruder.reset() self.Extruder.update(-5) self.Extruder.update(4) self.assertFalse(self.Extruder.is_extruding_start()) self.assertFalse(self.Extruder.is_extruding()) self.assertFalse(self.Extruder.is_primed()) self.assertFalse(self.Extruder.is_retracting_start()) self.assertFalse(self.Extruder.is_retracting()) self.assertTrue(self.Extruder.is_partially_retracted()) self.assertFalse(self.Extruder.is_retracted()) self.assertTrue(self.Extruder.is_deretracting_start()) self.assertTrue(self.Extruder.is_deretracting()) self.assertFalse(self.Extruder.is_deretracted()) # -5,1 - Beyond fully retracted to fully retracted self.Extruder.reset() self.Extruder.update(-5) self.Extruder.update(1) self.assertFalse(self.Extruder.is_extruding_start()) self.assertFalse(self.Extruder.is_extruding()) self.assertFalse(self.Extruder.is_primed()) self.assertFalse(self.Extruder.is_retracting_start()) self.assertFalse(self.Extruder.is_retracting()) self.assertFalse(self.Extruder.is_partially_retracted()) self.assertTrue(self.Extruder.is_retracted()) self.assertTrue(self.Extruder.is_deretracting_start()) self.assertTrue(self.Extruder.is_deretracting()) self.assertFalse(self.Extruder.is_deretracted()) # -5,0 - Remain beyond fully retracted self.Extruder.reset() self.Extruder.update(-5) self.Extruder.update(0) self.assertFalse(self.Extruder.is_extruding_start()) self.assertFalse(self.Extruder.is_extruding()) self.assertFalse(self.Extruder.is_primed()) self.assertFalse(self.Extruder.is_retracting_start()) self.assertFalse(self.Extruder.is_retracting()) self.assertFalse(self.Extruder.is_partially_retracted()) self.assertTrue(self.Extruder.is_retracted()) self.assertFalse(self.Extruder.is_deretracting_start()) self.assertFalse(self.Extruder.is_deretracting()) self.assertFalse(self.Extruder.is_deretracted()) # -5, -1 - Beyond fully retracted, continuing to retract self.Extruder.reset() self.Extruder.update(-5) self.Extruder.update(-1) self.assertFalse(self.Extruder.is_extruding_start()) self.assertFalse(self.Extruder.is_extruding()) self.assertFalse(self.Extruder.is_primed()) self.assertFalse(self.Extruder.is_retracting_start()) self.assertTrue(self.Extruder.is_retracting()) self.assertFalse(self.Extruder.is_partially_retracted()) self.assertTrue(self.Extruder.is_retracted()) self.assertFalse(self.Extruder.is_deretracting_start()) self.assertFalse(self.Extruder.is_deretracting()) self.assertFalse(self.Extruder.is_deretracted()) def test_ExtruderStateTriggered(self): self.assertTrue( self.Extruder._extruder_state_triggered(None, False) is None) self.assertTrue( self.Extruder._extruder_state_triggered(None, True) is None) self.assertTrue( self.Extruder._extruder_state_triggered(True, False) is None) self.assertTrue(self.Extruder._extruder_state_triggered(True, True)) self.assertTrue( self.Extruder._extruder_state_triggered(False, False) is None) self.assertFalse(self.Extruder._extruder_state_triggered(False, True)) def test_extruderTriggers_NoFilter(self): """Test the extruder triggers""" # test with no filters - should trigger self.Extruder.reset() state = ExtruderState() self.Extruder.add_state(state) state.is_primed = False # turn this off so we don't have to account for this default state triggers = ExtruderTriggers(None, None, None, None, None, None, None, None, None, None) self.assertTrue(self.Extruder.is_triggered(triggers)) def test_extruderTriggers_on_extruding_start(self): self.Extruder.reset() state = ExtruderState() self.Extruder.add_state(state) # test on_extruding_start - True Filter triggers = ExtruderTriggers(True, None, None, None, None, None, None, None, None, None) # test True with true filter state.is_extruding_start = True state.is_primed = False # turn this off so we don't have to account for this default state self.assertTrue(self.Extruder.is_triggered(triggers)) # test False with True filter state.is_extruding_start = False self.assertTrue(not self.Extruder.is_triggered(triggers)) # test on_extruding_start - False Filter triggers = ExtruderTriggers(False, None, None, None, None, None, None, None, None, None) # test True with False filter state.is_extruding_start = True self.assertTrue(not self.Extruder.is_triggered(triggers)) # test False with False filter state.is_extruding_start = False self.assertTrue(not self.Extruder.is_triggered(triggers)) def test_extruderTriggers_on_extruding(self): # test onExtruding self.Extruder.reset() state = ExtruderState() self.Extruder.add_state(state) triggers = ExtruderTriggers(None, True, None, None, None, None, None, None, None, None) # test True with true filter state.is_extruding = True state.is_primed = False # turn this off so we don't have to account for this default state self.assertTrue(self.Extruder.is_triggered(triggers)) # test False with True filter state.is_extruding = False self.assertFalse(self.Extruder.is_triggered(triggers)) # test on_extruding - False Filter triggers = ExtruderTriggers(None, False, None, None, None, None, None, None, None, None) # test True with False filter state.is_extruding = True self.assertFalse(self.Extruder.is_triggered(triggers)) # test False with False filter state.is_extruding = False self.assertFalse(self.Extruder.is_triggered(triggers)) def test_extruderTriggers_on_primed(self): self.Extruder.reset() state = ExtruderState() self.Extruder.add_state(state) triggers = ExtruderTriggers(None, None, True, None, None, None, None, None, None, None) # test True with true filter state.is_primed = True self.assertTrue(self.Extruder.is_triggered(triggers)) # test False with True filter state.is_primed = False self.assertTrue(not self.Extruder.is_triggered(triggers)) # test on_primed - False Filter triggers = ExtruderTriggers(None, None, False, None, None, None, None, None, None, None) # test True with False filter state.is_primed = True self.assertTrue(not self.Extruder.is_triggered(triggers)) # test False with False filter state.is_primed = False self.assertTrue(not self.Extruder.is_triggered(triggers)) def test_extruderTriggers_on_retracting_start(self): # test on_retracting_start self.Extruder.reset() state = ExtruderState() self.Extruder.add_state(state) triggers = ExtruderTriggers(None, None, None, True, None, None, None, None, None, None) # test True with true filter state.is_retracting_start = True state.is_primed = False # turn this off so we don't have to account for this default state self.assertTrue(self.Extruder.is_triggered(triggers)) # test False with True filter state.is_retracting_start = False self.assertTrue(not self.Extruder.is_triggered(triggers)) # test on_retracting_start - False Filter triggers = ExtruderTriggers(None, None, None, False, None, None, None, None, None, None) # test True with False filter state.is_retracting_start = True self.assertTrue(not self.Extruder.is_triggered(triggers)) # test False with False filter state.is_retracting_start = False self.assertTrue(not self.Extruder.is_triggered(triggers)) def test_extruderTriggers_on_retracting(self): # test on_retracting self.Extruder.reset() state = ExtruderState() self.Extruder.add_state(state) triggers = ExtruderTriggers(None, None, None, None, True, None, None, None, None, None) # test True with true filter state.is_retracting = True state.is_primed = False # turn this off so we don't have to account for this default state self.assertTrue(self.Extruder.is_triggered(triggers)) # test False with True filter state.is_retracting = False self.assertTrue(not self.Extruder.is_triggered(triggers)) # test on_retracting - False Filter triggers = ExtruderTriggers(None, None, None, None, False, None, None, None, None, None) # test True with False filter state.is_retracting = True self.assertTrue(not self.Extruder.is_triggered(triggers)) # test False with False filter state.is_retracting = False self.assertTrue(not self.Extruder.is_triggered(triggers)) def test_extruderTriggers_on_partially_retracted(self): # test on_partially_retracted self.Extruder.reset() state = ExtruderState() self.Extruder.add_state(state) triggers = ExtruderTriggers(None, None, None, None, None, True, None, None, None, None) # test True with true filter state.is_partially_retracted = True state.is_primed = False # turn this off so we don't have to account for this default state self.assertTrue(self.Extruder.is_triggered(triggers)) # test False with True filter state.is_partially_retracted = False self.assertTrue(not self.Extruder.is_triggered(triggers)) # test on_partially_retracted - False Filter triggers = ExtruderTriggers(None, None, None, None, None, False, None, None, None, None) # test True with False filter state.is_partially_retracted = True self.assertTrue(not self.Extruder.is_triggered(triggers)) # test False with False filter state.is_partially_retracted = False self.assertTrue(not self.Extruder.is_triggered(triggers)) def test_extruderTriggers_on_retracted(self): # test on_retracted self.Extruder.reset() state = ExtruderState() self.Extruder.add_state(state) triggers = ExtruderTriggers(None, None, None, None, None, None, True, None, None, None) # test True with true filter state.is_retracted = True state.is_primed = False # turn this off so we don't have to account for this default state self.assertTrue(self.Extruder.is_triggered(triggers)) # test False with True filter state.is_retracted = False self.assertTrue(not self.Extruder.is_triggered(triggers)) # test on_retracted - False Filter triggers = ExtruderTriggers(None, None, None, None, None, None, False, None, None, None) # test True with False filter state.is_retracted = True self.assertTrue(not self.Extruder.is_triggered(triggers)) # test False with False filter state.is_retracted = False self.assertTrue(not self.Extruder.is_triggered(triggers)) def test_extruderTriggers_on_deretracting_start(self): # test on_deretracting_start self.Extruder.reset() state = ExtruderState() self.Extruder.add_state(state) triggers = ExtruderTriggers(None, None, None, None, None, None, None, True, None, None) # test True with true filter state.is_deretracting_start = True state.is_primed = False # turn this off so we don't have to account for this default state self.assertTrue(self.Extruder.is_triggered(triggers)) # test False with True filter state.is_deretracting_start = False self.assertTrue(not self.Extruder.is_triggered(triggers)) # test on_deretracting_start - False Filter triggers = ExtruderTriggers(None, None, None, None, None, None, None, False, None, None) # test True with False filter state.is_deretracting_start = True self.assertTrue(not self.Extruder.is_triggered(triggers)) # test False with False filter state.is_deretracting_start = False self.assertTrue(not self.Extruder.is_triggered(triggers)) def test_extruderTriggers_on_deretracting(self): # test on_deretracting self.Extruder.reset() state = ExtruderState() self.Extruder.add_state(state) triggers = ExtruderTriggers(None, None, None, None, None, None, None, None, True, None) # test True with true filter state.is_deretracting = True state.is_primed = False # turn this off so we don't have to account for this default state self.assertTrue(self.Extruder.is_triggered(triggers)) # test False with True filter state.is_deretracting = False self.assertTrue(not self.Extruder.is_triggered(triggers)) # test on_deretracting - False Filter triggers = ExtruderTriggers(None, None, None, None, None, None, None, None, False, None) # test True with False filter state.is_deretracting = True self.assertTrue(not self.Extruder.is_triggered(triggers)) # test False with False filter state.is_deretracting = False self.assertTrue(not self.Extruder.is_triggered(triggers)) def test_extruderTriggers_on_deretracted(self): # test on_deretracted self.Extruder.reset() state = ExtruderState() self.Extruder.add_state(state) triggers = ExtruderTriggers(None, None, None, None, None, None, None, None, None, True) # test True with true filter state.is_deretracted = True state.is_primed = False # turn this off so we don't have to account for this default state self.assertTrue(self.Extruder.is_triggered(triggers)) # test False with True filter state.is_deretracted = False self.assertTrue(not self.Extruder.is_triggered(triggers)) # test on_deretracted - False Filter triggers = ExtruderTriggers(None, None, None, None, None, None, None, None, None, False) # test True with False filter state.is_deretracted = True self.assertTrue(not self.Extruder.is_triggered(triggers)) # test False with False filter state.is_deretracted = False self.assertTrue(not self.Extruder.is_triggered(triggers)) def test_extruderTriggers_Mixed(self): # Test mixed nones, trues and falses self.Extruder.reset() state = ExtruderState() self.Extruder.add_state(state) triggers = ExtruderTriggers(None, True, False, None, True, False, None, True, False, None) # Forbidden Due to is_primed state.is_extruding_start = True state.is_extruding = True state.is_primed = True state.is_retracting_start = True state.is_retracting = True state.is_partially_retracted = False state.is_retracted = True state.is_deretracting_start = True state.is_deretracting = False state.is_deretracted = True self.assertTrue(not self.Extruder.is_triggered(triggers)) # True - is extruding state.is_extruding_start = False state.is_extruding = True state.is_primed = False state.is_retracting_start = True state.is_retracting = False state.is_partially_retracted = False state.is_retracted = True state.is_deretracting_start = False state.is_deretracting = False state.is_deretracted = True self.assertTrue(self.Extruder.is_triggered(triggers)) # Test all false states and all Nones state.is_extruding_start = True state.is_extruding = True state.is_primed = True state.is_retracting_start = True state.is_retracting = True state.is_partially_retracted = True state.is_retracted = True state.is_deretracting_start = True state.is_deretracting = True state.is_deretracted = True triggers = ExtruderTriggers(None, None, None, None, None, None, None, None, None, None) self.assertTrue(self.Extruder.is_triggered(triggers)) triggers = ExtruderTriggers(False, True, True, True, True, True, True, True, True, True) self.assertFalse(self.Extruder.is_triggered(triggers)) triggers = ExtruderTriggers(True, False, True, True, True, True, True, True, True, True) self.assertFalse(self.Extruder.is_triggered(triggers)) triggers = ExtruderTriggers(True, True, False, True, True, True, True, True, True, True) self.assertFalse(self.Extruder.is_triggered(triggers)) triggers = ExtruderTriggers(True, True, True, False, True, True, True, True, True, True) self.assertFalse(self.Extruder.is_triggered(triggers)) triggers = ExtruderTriggers(True, True, True, True, False, True, True, True, True, True) self.assertFalse(self.Extruder.is_triggered(triggers)) triggers = ExtruderTriggers(True, True, True, True, True, False, True, True, True, True) self.assertFalse(self.Extruder.is_triggered(triggers)) triggers = ExtruderTriggers(True, True, True, True, True, True, False, True, True, True) self.assertFalse(self.Extruder.is_triggered(triggers)) triggers = ExtruderTriggers(True, True, True, True, True, True, True, False, True, True) self.assertFalse(self.Extruder.is_triggered(triggers)) triggers = ExtruderTriggers(True, True, True, True, True, True, True, True, False, True) self.assertFalse(self.Extruder.is_triggered(triggers)) triggers = ExtruderTriggers(True, True, True, True, True, True, True, True, True, False) self.assertFalse(self.Extruder.is_triggered(triggers))
def setUp(self): self.Settings = OctolapseSettings(NamedTemporaryFile().name) self.Extruder = Extruder(self.Settings)
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)