## Client klipper macro definitions
##
## Copyright (C) 2022 Alex Zellner <alexander.zellner@googlemail.com>
##
## This file may be distributed under the terms of the GNU GPLv3 license

## add [include mainsail.cfg] to your printer.cfg

## Customization:
## copy the following macro to your printer.cfg (outside of mainsail.cfg)

#[gcode_macro _CLIENT_VARIABLE]
#variable_use_custom_pos  : False ; use custom park coordinates for x,y [True/False]
#variable_custom_park_x   : 0.0   ; custom x position; value must be within your defined min and max of X
#variable_custom_park_y   : 0.0   ; custom y position; value must be within your defined min and max of Y
#variable_custom_park_dz  : 2.0   ; custom dz value; the value in mm to lift the nozzle when move to park position
#variable_retract         : 1.0   ; the value to retract while PAUSE
#variable_cancel_retract  : 5.0   ; the value to retract while CANCEL_PRINT
#variable_speed_retract   : 35.0  ; retract speed in mm/s
#variable_unretract       : 1.0   ; the value to unretract while RESUME
#variable_speed_unretract : 35.0  ; unretract speed in mm/s
#variable_speed_hop       : 15.0  ; z move speed in mm/s
#variable_speed_move      : 100.0 ; move speed in mm/s
#variable_park_at_cancel  : False ; allow to move the toolhead to park while execute CANCEL_PRINT [True,False]
## !!! Caution [firmware_retraction] must be defined in the printer.cfg if you set use_fw_retract: True !!!
#variable_use_fw_retract  : False ; use fw_retraction instead of the manual version [True/False]
#gcode:

## After you uncomment it add your custom values

## You now can use your PAUSE macro direct in your M600 here a short example:

#[gcode_macro M600]
#description: Filament change
#gcode: PAUSE X=10 Y=10 Z_MIN=50

## That will park your head front left with a minimum height of 50mm above the bed. If your head
## is already above 50mm it will use only the z_hop specified with dz.

[virtual_sdcard]
path: ~/printer_data/gcodes
on_error_gcode: CANCEL_PRINT

[pause_resume]

[display_status]

[gcode_macro CANCEL_PRINT]
description: Cancel the actual running print
rename_existing: CANCEL_PRINT_BASE
gcode:
  ##### get user parameters or use default #####
  {% set macro_found = True if printer['gcode_macro _CLIENT_VARIABLE'] is defined else False %}
  {% set client = printer['gcode_macro _CLIENT_VARIABLE'] %}
  {% set allow_park = False if not macro_found
                 else False if client.park_at_cancel is not defined
                 else True  if client.park_at_cancel|lower == 'true'
                 else False %}
  {% set retract      = 5.0  if not macro_found else client.cancel_retract|default(5.0)|abs %}
  ##### end of definitions #####
  {% if not printer.pause_resume.is_paused and allow_park %} _TOOLHEAD_PARK_PAUSE_CANCEL {% endif %}
  _CLIENT_RETRACT LENGTH={retract}
  TURN_OFF_HEATERS
  M106 S0
  # clear pause_next_layer and pause_at_layer as preparation for next print
  SET_GCODE_VARIABLE MACRO=SET_PRINT_STATS_INFO VARIABLE=pause_next_layer VALUE="{{'enable': False, 'call':"PAUSE"}}"
  SET_GCODE_VARIABLE MACRO=SET_PRINT_STATS_INFO VARIABLE=pause_at_layer VALUE="{{'enable': False, 'layer': 0, 'call':"PAUSE"}}"
  CANCEL_PRINT_BASE

[gcode_macro PAUSE]
description: Pause the actual running print
rename_existing: PAUSE_BASE
gcode:
  PAUSE_BASE
  _TOOLHEAD_PARK_PAUSE_CANCEL {rawparams}

[gcode_macro RESUME]
description: Resume the actual running print
rename_existing: RESUME_BASE
gcode:
  ##### get user parameters or use default #####
  {% set macro_found = True if printer['gcode_macro _CLIENT_VARIABLE'] is defined else False %}
  {% set client = printer['gcode_macro _CLIENT_VARIABLE'] %}
  {% set velocity = printer.configfile.settings.pause_resume.recover_velocity %}
  {% set sp_move        = velocity if not macro_found else client.speed_move|default(velocity) %}
  ##### end of definitions #####
  _CLIENT_EXTRUDE
  RESUME_BASE VELOCITY={params.VELOCITY|default(sp_move)}
  
# Usage: SET_PAUSE_NEXT_LAYER [MACRO=<name>]
[gcode_macro SET_PAUSE_NEXT_LAYER]
description: Enable a pause if the next layer is reached
gcode: SET_GCODE_VARIABLE MACRO=SET_PRINT_STATS_INFO VARIABLE=pause_next_layer VALUE="{{'enable':True, 'call':params.MACRO|default("PAUSE")}}"

# Usage: SET_PAUSE_AT_LAYER [LAYER=<number>] [MACRO=<name>]
[gcode_macro SET_PAUSE_AT_LAYER]
description: Enable/disable a pause if a given layer number is reached
gcode:
  {% if params.LAYER is defined %}
    SET_GCODE_VARIABLE MACRO=SET_PRINT_STATS_INFO VARIABLE=pause_at_layer VALUE="{{'enable': True, 'layer':params.LAYER|int, 'call':params.MACRO|default("PAUSE")}}"
  {% else %}
    SET_GCODE_VARIABLE MACRO=SET_PRINT_STATS_INFO VARIABLE=pause_at_layer VALUE="{{'enable': False, 'layer':0, 'call':"PAUSE"}}"
  {% endif %}

# Usage: SET_PRINT_STATS_INFO [TOTAL_LAYER=<total_layer_count>] [CURRENT_LAYER= <current_layer>]
[gcode_macro SET_PRINT_STATS_INFO]
rename_existing: SET_PRINT_STATS_INFO_BASE
description: Overwrite, to get pause_next_layer and pause_at_layer feature 
variable_pause_next_layer: {'enable':False, 'call':"PAUSE"}
variable_pause_at_layer  : {'enable':False, 'layer':0, 'call':"PAUSE"}
gcode:
  {% if pause_next_layer.enable %}
    {action_respond_info("%s, forced by pause_next_layer" % pause_next_layer.call)}
    {pause_next_layer.call} ; execute the given gcode to pause, should be either M600 or PAUSE
    SET_GCODE_VARIABLE MACRO=SET_PRINT_STATS_INFO VARIABLE=pause_next_layer VALUE="{{'enable': False, 'call':"PAUSE"}}"
  {% elif pause_at_layer.enable and params.CURRENT_LAYER is defined and params.CURRENT_LAYER|int == pause_at_layer.layer %}
    {action_respond_info("%s, forced by pause_at_layer [%d]" % (pause_at_layer.call, pause_at_layer.layer))}
    {pause_at_layer.call} ; execute the given gcode to pause, should be either M600 or PAUSE
    SET_GCODE_VARIABLE MACRO=SET_PRINT_STATS_INFO VARIABLE=pause_at_layer VALUE="{{'enable': False, 'layer': 0, 'call':"PAUSE"}}"
  {% endif %}
  SET_PRINT_STATS_INFO_BASE {rawparams}
  
##### internal use #####
[gcode_macro _TOOLHEAD_PARK_PAUSE_CANCEL]
description: Helper: park toolhead used in PAUSE and CANCEL_PRINT
gcode:
  ##### get user parameters or use default #####
  {% set macro_found = True if printer['gcode_macro _CLIENT_VARIABLE'] is defined else False %}
  {% set client = printer['gcode_macro _CLIENT_VARIABLE'] %}
  {% set velocity = printer.configfile.settings.pause_resume.recover_velocity %}
  {% set use_custom     = False if not macro_found
                     else False if client.use_custom_pos is not defined
                     else True  if client.use_custom_pos|lower == 'true'
                     else False %}
  {% set custom_park_x  = 0.0 if not macro_found else client.custom_park_x|default(0.0) %}
  {% set custom_park_y  = 0.0 if not macro_found else client.custom_park_y|default(0.0) %}
  {% set park_dz        = 2.0 if not macro_found else client.custom_park_dz|default(2.0)|abs %}
  {% set sp_hop         = 900  if not macro_found else client.speed_hop|default(15) * 60 %}
  {% set sp_move        = velocity * 60 if not macro_found else client.speed_move|default(velocity) * 60 %}
  ##### get config and toolhead values #####
  {% set origion   = printer.gcode_move.homing_origin %}
  {% set act       = printer.gcode_move.gcode_position %}
  {% set max       = printer.toolhead.axis_maximum %}
  {% set cone      = printer.toolhead.cone_start_z|default(max.z) %} ; height as long the toolhead can reach max and min of an delta
  {% set round_bed = True if printer.configfile.settings.printer.kinematics is in ['delta','polar','rotary_delta','winch']
                else False %}
  ##### define park position #####
  {% set z_min = params.Z_MIN|default(0)|float %}
  {% set z_park = [[(act.z + park_dz), z_min]|max, (max.z - origion.z)]|min %}
  {% set x_park = params.X       if params.X is defined
             else custom_park_x  if use_custom
             else 0.0            if round_bed
             else (max.x - 5.0) %}
  {% set y_park = params.Y       if params.Y is defined
             else custom_park_y  if use_custom
             else (max.y - 5.0)  if round_bed and z_park < cone
             else 0.0            if round_bed
             else (max.y - 5.0) %}
  ##### end of definitions #####
  _CLIENT_RETRACT
  {% if "xyz" in printer.toolhead.homed_axes %}
    G90
    G1 Z{z_park} F{sp_hop}
    G1 X{x_park} Y{y_park} F{sp_move}
    {% if not printer.gcode_move.absolute_coordinates %} G91 {% endif %}
  {% else %}
    {action_respond_info("Printer not homed")}
  {% endif %}
  
[gcode_macro _CLIENT_EXTRUDE]
description: Extrudes, if the extruder is hot enough
gcode:
  {% set macro_found = True if printer['gcode_macro _CLIENT_VARIABLE'] is defined else False %}
  {% set client = printer['gcode_macro _CLIENT_VARIABLE'] %}
  {% set use_fw_retract = False if not macro_found
                     else False if client.use_fw_retract is not defined
                     else True  if client.use_fw_retract|lower == 'true' and printer.firmware_retraction is defined
                     else False %}

  {% set length = (params.LENGTH|float) if params.LENGTH is defined
             else 1.0 if not macro_found
             else client.unretract|default(1.0) %}

  {% set speed = params.SPEED if params.SPEED is defined
            else 35 if not macro_found
            else client.speed_unretract|default(35) %}

  {% set absolute_extrude = printer.gcode_move.absolute_extrude %}

  {% if printer.extruder.can_extrude %}
    {% if use_fw_retract %}
      {% if length < 0 %}
        G10
      {% else %}
        G11
      {% endif %}
    {% else %}
      M83
      G1 E{length} F{(speed|float|abs) * 60}
      {% if absolute_extrude %}
        M82
      {% endif %}
    {% endif %}
  {% else %}
    {action_respond_info("Extruder not hot enough")}
  {% endif %}

[gcode_macro _CLIENT_RETRACT]
description: Retracts, if the extruder is hot enough
gcode:
  {% set macro_found = True if printer['gcode_macro _CLIENT_VARIABLE'] is defined else False %}
  {% set client = printer['gcode_macro _CLIENT_VARIABLE'] %}

  {% set length = (params.LENGTH|float) if params.LENGTH is defined
             else 1.0 if not macro_found
             else client.retract|default(1.0) %}

  {% set speed = params.SPEED if params.SPEED is defined
            else 35 if not macro_found
            else client.speed_retract|default(35) %}

  _CLIENT_EXTRUDE LENGTH=-{length|float|abs} SPEED={speed|float|abs}
