Skip to content

Commit

Permalink
debugging of job state machine
Browse files Browse the repository at this point in the history
  • Loading branch information
moggieuk committed Sep 10, 2023
1 parent 3b50f72 commit 4d884e9
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 47 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ Happy Hare has a built in help system to aid remembering the command set. It can
MMU_SET_GATE_MAP : Define the type and color of filaments on each gate
MMU_STATUS : Complete dump of current MMU state and important configuration
MMU_SYNC_GEAR_MOTOR : Sync the MMU gear motor to the extruder stepper
MMU_UNLOCK : Wakeup the MMU prior to resume to restore temperatures and timeouts
```
> MMU_HELP MACROS=1 TESTING=1
Expand Down
4 changes: 3 additions & 1 deletion doc/command_ref.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ Firstly you can get a quick reminder of commands using the `MMU_HELP` command fr
MMU_SET_GATE_MAP : Define the type and color of filaments on each gate
MMU_STATUS : Complete dump of current MMU state and important configuration
MMU_SYNC_GEAR_MOTOR : Sync the MMU gear motor to the extruder motor
MMU_UNLOCK : Wakeup the MMU prior to resume to restore temperatures and timeouts
```
Expand All @@ -46,9 +47,10 @@ Firstly you can get a quick reminder of commands using the `MMU_HELP` command fr
| `MMU_LOAD` | Loads filament in currently selected tool/gate to extruder. Optionally performs just the extruder load part of the sequence - designed for bypass loading or non MMU use | `EXTRUDER_ONLY=[0\|1]` To force just the extruder loading (automatic if bypass selected) |
| `MMU_EJECT` | Eject filament and park it in the MMU gate or does the extruder unloading part of the unload sequence if in bypass | `EXTRUDER_ONLY=[0\|1]` To force just the extruder unloading (automatic if bypass selected) |
| `MMU_PRELOAD` | Helper for filament loading. Feed filament into gate, MMU will catch it and correctly position at the specified gate | `GATE=[0..n]` The specific gate to preload. If omitted the currently selected gate can be loaded |
| `MMU_PAUSE` | Pause the current print and lock the MMU operations | `FORCE_IN_PRINT=[0\|1]` This option forces the handling of pause as if it occurred in print and is useful for testing. Calls `PAUSE` by default or your `pause_macro` if set |
| `MMU_PAUSE` | Pause the current print and lock the MMU operations. (`MMU_UNLOCK + RESUME` or just `RESUME` to continue print) | `FORCE_IN_PRINT=[0\|1]` This option forces the handling of pause as if it occurred in print and is useful for testing. Calls `PAUSE` by default or your `pause_macro` if set |
| `MMU_RECOVER` | Recover filament position and optionally reset MMU state. Useful to call prior to RESUME if you intervene/manipulate filament by hand | `TOOL=[0..n]\|-2` Optionally force set the currently selected tool (-2 = bypass). Use caution! <br>`GATE=[0..n]` Optionally force set the currently selected gate if TTG mapping is being leveraged otherwise it will get the gate associated with current tool. Use caution! <br>`LOADED=[0\|1]` Optionally specify if the filamanet is fully loaded or fully unloaded. Use caution! If not specified, MMU will try to discover filament position <br>`STRICT=[0\|1]` If automatically detecting impose stricter testing for filament position (temporarily sets 'strict_filament_recovery' parameter) |
| `MMU_ENCODER` | Displays the current value of the MMU encoder or explicitly enable or disable the encoder. Note that the encoder state is set automatically so this will only be sticky until next tool change | `ENABLE=[0\|1]` Enable/Disable <br>`VALUE=..` Set the current distance |
| `MMU_UNLOCK` | Wakeup the MMU prior to RESUME to restore temperatures and timeouts | None |
| `MMU_HELP` | Generate reminder list of command set | `TESTING=[0\|1]` Also list the testing commands <br>`MACROS=[0\|1]` Also list the callback backros |
<br>

Expand Down
84 changes: 38 additions & 46 deletions extras/mmu.py
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,7 @@ def __init__(self, config):
self.gcode.register_command('MMU_LOAD', self.cmd_MMU_LOAD, desc=self.cmd_MMU_LOAD_help)
self.gcode.register_command('MMU_EJECT', self.cmd_MMU_EJECT, desc = self.cmd_MMU_EJECT_help)
self.gcode.register_command('MMU_PAUSE', self.cmd_MMU_PAUSE, desc = self.cmd_MMU_PAUSE_help)
self.gcode.register_command('MMU_UNLOCK', self.cmd_MMU_UNLOCK, desc = self.cmd_MMU_UNLOCK_help)
self.gcode.register_command('MMU_RECOVER', self.cmd_MMU_RECOVER, desc = self.cmd_MMU_RECOVER_help)

# User Setup and Testing
Expand Down Expand Up @@ -466,10 +467,6 @@ def __init__(self, config):
self.gcode.register_command('_MMU_STEP_MOVE', self.cmd_MMU_STEP_MOVE, desc = self.cmd_MMU_STEP_MOVE_help)
self.gcode.register_command('_MMU_STEP_SET_FILAMENT', self.cmd_MMU_STEP_SET_FILAMENT, desc = self.cmd_MMU_STEP_SET_FILAMENT_help)

# gcode_macro = self.printer.load_object(config, 'gcode_macro') # PAUL
# self.print_start_gcode = gcode_macro.load_template(config, 'print_start_gcode', '_MMU_PRINT_START') # PAUL
# self.print_end_gcode = gcode_macro.load_template(config, 'print_end_gcode', '_MMU_PRINT_END') # PAUL

# We setup MMU hardware during configuration since some hardware like endstop requires
# configuration during the MCU config phase, which happens before klipper connection
# This assumes that the hardware configuartion appears before the `[mmu]` section
Expand All @@ -494,6 +491,7 @@ def _setup_logging(self):
self.mmu_logger.addHandler(queue_handler)

def _setup_mmu_hardware(self, config):
self._log_debug("MMU Hardware Initialization -------------------------------")
self.mmu_hardware = self.printer.lookup_object('mmu_hardware', None)

# Selector h/w setup ------
Expand Down Expand Up @@ -800,14 +798,14 @@ def handle_ready(self):
prev_pause = self.gcode.register_command('PAUSE', None)
if prev_pause is not None:
self.gcode.register_command('__PAUSE', prev_pause)
self.gcode.register_command('PAUSE', self.cmd_MMU_PAUSE, desc = self.cmd_MMU_PAUSE_help)
self.gcode.register_command('PAUSE', self.cmd_PAUSE, desc = self.cmd_PAUSE_help)
else:
self._log_error('No existing PAUSE macro found!')

prev_resume = self.gcode.register_command('RESUME', None)
if prev_resume is not None:
self.gcode.register_command('__RESUME', prev_resume)
self.gcode.register_command('RESUME', self.cmd_RESUME, desc = self.cmd_RESUME_help)
self.gcode.register_command('RESUME', self.cmd_MMU_RESUME, desc = self.cmd_MMU_RESUME_help)
else:
self._log_error('No existing RESUME macro found!')

Expand Down Expand Up @@ -854,7 +852,7 @@ def _wrap_gcode_command(self, command, exception=False):
if exception:
raise MmuError("Error running %s: %s" % (macro, str(e)))
else:
self._log_error("Error running %s: %s" % (macro, str(e)))
self._log_debug("Error running %s: %s" % (macro, str(e)))

####################################
# LOGGING AND STATISTICS FUNCTIONS #
Expand Down Expand Up @@ -1926,7 +1924,7 @@ def _handle_idle_timeout_event(self, eventtime, event_type):
new_ps.pop('filament_used')
old_state = prev_ps['state']
new_state = ps['state']
self._log_debug("PAUL: Current Job State: %s, print_stats: %s" % (self.print_job_state.upper(), new_ps)) # PAUL temp
#self._log_trace("Current Job State: %s, print_stats: %s" % (self.print_job_state.upper(), new_ps))

if new_state is not old_state:
if new_state == "printing":
Expand All @@ -1936,33 +1934,33 @@ def _handle_idle_timeout_event(self, eventtime, event_type):
pass
else:
# This is a 'started' state
self._log_error("PAUL: DETECTED JOB START, new_state=%s, current state=%s" % (new_state, self.print_job_state))
self.reactor.register_callback(self._print_start_event_handler)
elif new_state == "complete":
self._log_error("PAUL: DETECTED JOB COMPLETE, new_state=%s, current state=%s" % (new_state, self.print_job_state))
self.reactor.register_callback(self._print_complete_event_handler)
elif new_state == "error":
self._log_error("PAUL: DETECTED JOB ERROR, new_state=%s, current state=%s" % (new_state, self.print_job_state))
self.reactor.register_callback(self._print_error_event_handler)
self._log_trace("Automaticaly detected JOB START, new_state=%s, current state=%s" % (new_state, self.print_job_state))
self._exec_gcode("_MMU_PRINT_START")
# self.reactor.register_callback(self._print_start_event_handler)
elif new_state in ("complete", "error"):
self._log_trace("Automatically detected JOB %s, new_state=%s, current state=%s" % (new_state.upper(), new_state, self.print_job_state))
self._exec_gcode("_MMU_PRINT_END STATE=%s" % new_state)
# elif new_state == "complete":
# self._log_trace("Automatically detected JOB COMPLETE, new_state=%s, current state=%s" % (new_state, self.print_job_state))
# self.reactor.register_callback(self._print_complete_event_handler)
# elif new_state == "error":
# self._log_trace("Automatically detected JOB ERROR, new_state=%s, current state=%s" % (new_state, self.print_job_state))
# self.reactor.register_callback(self._print_error_event_handler)

self.last_print_stats = new_ps

if event_type == "idle" and self._is_in_endstate():
self._set_print_job_state("standby")

def _print_start_event_handler(self, eventtime):
self._exec_gcode("_MMU_PRINT_START")
# PAUL self._exec_gcode(self.print_start_gcode)

def _print_complete_event_handler(self, eventtime):
self._exec_gcode("_MMU_PRINT_END STATE=complete")

def _print_error_event_handler(self, eventtime):
self._exec_gcode("_MMU_PRINT_END STATE=error")
# def _print_start_event_handler(self, eventtime):
# self._exec_gcode("_MMU_PRINT_START")
#
# def _print_complete_event_handler(self, eventtime):
# self._exec_gcode("_MMU_PRINT_END STATE=complete")
#
# def _print_error_event_handler(self, eventtime):
# self._exec_gcode("_MMU_PRINT_END STATE=error")

# PAUL def _exec_gcode(self, template):
# PAUL try:
# PAUL self.gcode.run_script(template.render())
def _exec_gcode(self, command):
try:
self.gcode.run_script_from_command(command)
Expand Down Expand Up @@ -1992,14 +1990,15 @@ def _on_print_start(self):
self._enable_encoder_sensor(True) # Enable runout/clog detection
self._reset_encoder_counts() # Encoder 0000

# Ricky to perform during event processing(?)
self._sync_gear_to_extruder(self.sync_to_extruder, servo=True, in_print=True)
msg = "MMU initialized ready for print"
if self.filament_pos == self.FILAMENT_POS_LOADED:
# Ensure gear/extruder are synchronized if necessary
self._sync_gear_to_extruder(self.sync_to_extruder, servo=True, in_print=True)
msg += " (initial tool T%s loaded)" % self.tool_selected
else:
msg += " (no filament loaded)"
self._log_info(msg)
self.toolhead.wait_moves()
self._set_print_job_state("printing")

def _mmu_pause(self, reason, force_in_print=False):
Expand Down Expand Up @@ -2060,15 +2059,6 @@ def _mmu_resume(self):
self._set_print_job_state(self.resume_to_state)
self.resume_to_state = "standby"

# PAUL
# def _pause(self):
# # PAUL? self.resume_to_state = "printing" if self._is_in_print() else "standby"
#
# def _resume(self):
# self._set_print_job_state(self.resume_to_state)
# self.resume_to_state = "standby"
# # Put back anythig done in _pause()

# If this is called automatically it will occur after the user's print ends.
# Therefore don't do anything that requires operating kinematics
def _on_print_end(self, state="complete"):
Expand All @@ -2080,9 +2070,11 @@ def _on_print_end(self, state="complete"):
self.reactor.update_timer(self.heater_off_handler, self.reactor.NEVER) # Don't automatically turn off extruder heaters
self._disable_encoder_sensor() # Disable runout/clog detection

# Ricky to perform during event processing(?)
if self.printer.lookup_object("idle_timeout").idle_timeout != self.default_idle_timeout:
self.gcode.run_script_from_command("SET_IDLE_TIMEOUT TIMEOUT=%d" % self.default_idle_timeout) # Restore original idle_timeout
self._sync_gear_to_extruder(False, servo=True, in_print=False)
self.toolhead.wait_moves()
self._set_print_job_state(state)

def _save_toolhead_position_and_lift(self, remember=None, z_hop_height=None):
Expand Down Expand Up @@ -4035,25 +4027,25 @@ def cmd_MMU_PRINT_END(self, gcmd):
def cmd_MMU_PAUSE(self, gcmd):
if self._check_is_disabled(): return
if self._check_in_bypass(): return
force_in_prrnt = bool(gcmd.get_int('FORCE_IN_PRINT', 0, minval=0, maxval=1))
force_in_print = bool(gcmd.get_int('FORCE_IN_PRINT', 0, minval=0, maxval=1))
self._mmu_pause("Pause macro was directly called", force_in_print)

cmd_MMU_UNLOCK_help = "Wakeup the MMU prior to resume to restore temperatures and timeouts"
def cmd_MMU_UNLOCK(self, gcmd):
if self._check_is_disabled(): return
if self._is_mmu_paused_locked():
if self._is_mmu_pause_locked():
self._mmu_unlock()

# Not a user facing command - used in automatic wrapper
cmd_RESUME_help = "Wrapper around default RESUME macro"
def cmd_RESUME(self, gcmd):
cmd_MMU_RESUME_help = "Wrapper around default RESUME macro"
def cmd_MMU_RESUME(self, gcmd):
if not self.is_enabled:
self._wrap_gcode_command("__RESUME") # User defined or Klipper default behavior
return

self._log_trace("MMU RESUME wrapper called")
if not self._is_paused() and not self._is_mmu_paused():
self._log_always("Print is not paused")
self._log_always("Print is not paused. Resume ignored.")
return

if self._is_mmu_pause_locked():
Expand All @@ -4078,15 +4070,15 @@ def cmd_RESUME(self, gcmd):
def cmd_PAUSE(self, gcmd): # PAUL WIP
if self.is_enabled:
self._log_trace("MMU PAUSE wrapper called")
# TODO what is the meaning for mmu if called during a print?
# TODO what is the semantic meaning for mmu if called during a print?
self._wrap_gcode_command("__PAUSE") # User defined or Klipper default behavior

# Not a user facing command - used in automatic wrapper
cmd_CLEAR_PAUSE_help = "Wrapper around default CLEAR_PAUSE macro"
def cmd_CLEAR_PAUSE(self, gcmd):
if self.is_enabled:
self._log_trace("MMU CLEAR_PAUSE wrapper called")
# TODO what is the meaning for mmu if called during a print?
# TODO what is the semantic meaning for mmu if called during a print?
self._wrap_gcode_command("__CLEAR_PAUSE") # User defined or Klipper default behavior

# PAUL old MMU_RESUME
Expand Down

0 comments on commit 4d884e9

Please sign in to comment.