####################################
# Variables for the eSpooler macros
#
# Configure these for your setup.
#
[gcode_macro _MMU_ESPOOLER_VARS]
# Prefix of name of the `output_pin` for the eSpooler.
# The `output_pin` name must follow the pattern {prefix}_rwd_{gate}
# and {prefix}_en_{gate}. By default we prefix it with an underscore
# to prevent them from being shown in UI applications (like mainsail).
#
variable_pin_prefix: '_mmu_dc_espooler'

# Default max number of seconds for eSpooler to run
# Note: Each time any eSpooler **starts** it will restart the timeout
#
variable_default_timeout: 60

# The step speed where you want to max out the eSpooler to run at full speed.
#
variable_max_step_speed: 200

# Minimum distance of a move required to activate the eSpooler
# The lowest valid value for this is 50 because eSpooler macros
# will not even be considered if the distance is less than 50.
#
variable_min_distance: 200

# Adjusts the speed conversion ratio
# For the following examples, let's assume max_step_speed = 50.
# And remember actual eSpooler speed values are between 0.0 (off) and 1.0 (full speed) (inclusive)
#
# The formula looks like this:
# ({step_speed} / {max_step_speed}) ^ {step_speed_exponent}
#
# With step_speed_exponent of 1 would have a linear ratio:
# If I am running with a step speed of 50mm/s, the eSpooler would run at full speed (1.0)
# Calculated via (50/50)^1
# If I am running with a step speed of 25mm/s, the eSpooler would run at half speed (0.5)
# Calculated via (25/50)^1
#
# With step_speed_exponent of 0.2 would have a linear ratio:
# If I am running with a step speed of 50mm/s, the eSpooler would run at full speed (1.0)
# Calculated via (50/50)^0.2
# If I am running with a step speed of 25mm/s, the eSpooler would run at half speed (0.87)
# Calculated via (25/50)^0.2
#
variable_step_speed_exponent: 0.5

# Internal variable for tracking the gates with eSpoolers
variable_espooler_gates: ''
gcode: # Leave empty

###########################################################################
# Include DC motor pin definitions
#
# This should be after the eSpooler vars macro to ensure the users
# custom vars override our default ones.
#
[include dc_espooler_hw.cfg]

###########################################################################
# Macro to actuate the correct DC motor for the gate being unloaded
#
# Easiest integration is to set this in mmu_parameters.cfg:
#
#  eSpooler_start_macro: MMU_ESPOOLER_START
#
[gcode_macro MMU_ESPOOLER_START]
gcode:
    _MMU_ESPOOLER_CTL {rawparams}

    # Param hints UI
    {% set dummy = None if True else "
    {% set d = params.GATE|default(current_gate)|int %}
    {% set d = params.SCALE|default(cfg_scale)|float %}
    {% set d = params.TIMEOUT|default(default_timeout)|float %}
    " %} # End param hints for UI


###########################################################################
# Macro to stop the DC eSpooler motor
#
# Easiest integration is to set this in mmu_macro_vars.cfg:
#
#  eSpooler_stop_macro: MMU_ESPOOLER_STOP
#
[gcode_macro MMU_ESPOOLER_STOP]
gcode:
    _MMU_ESPOOLER_CTL {rawparams} SPEED={0}
    
    # Param hints UI
    {% set dummy = None if True else "
    {% set d = params.GATE|default(current_gate)|int %}
    " %} # End param hints for UI

###########################################################################
# Macro to control the DC eSpooler motor
#
# Used by both the start and stop eSpooler macros
#
[gcode_macro _MMU_ESPOOLER_CTL]
gcode:
    {% set vars = printer["gcode_macro _MMU_ESPOOLER_VARS"] %}
    {% set current_gate = printer['mmu'].gate %}
    {% set gate = params.GATE|default(current_gate)|int %}
    {% set step_speed = params.STEP_SPEED|default(-1)|float %}
    {% set expected_distance = params.MAX_DISTANCE|default(-1)|float %}
    {% set homing_move = params.HOMING_MOVE|default(0)|int %}
    {% set speed = params.SPEED|default(-1)|float %}
    {% set pin_prefix = vars.pin_prefix %}
    {% set pin = ('%s_rwd_%d' % (pin_prefix, gate)) if printer['output_pin %s_rwd_%d' % (pin_prefix, gate)] else None %}
    {% set en_pin = ('%s_en_%d' % (pin_prefix, gate)) if printer['output_pin %s_en_%d' % (pin_prefix, gate)] else None %}
    {% set pin_cfg = ('output_pin %s' % pin) if pin else None %}
    {% set pwm = (printer.configfile.settings[pin_cfg].scale) if pin_cfg else False %}
    {% set default_timeout = vars.default_timeout %}
    {% set timeout = params.TIMEOUT|default(default_timeout)|float %}

    # Convert speed
    {% if speed < 0 and step_speed >= 0 %}
        # determine speed from step speed
        {% if not pwm %}
            # TODO: something special for long slow (tbd) moves? 
            #       delayed start so it runs after the stepper has run for a little bit?
            #       or just don't run on long slow moves?
            {% set speed = 1 %}
        {% elif step_speed > vars.max_step_speed %}
            {% set speed = 1 %}
        {% else %}
            {% set speed = (step_speed / vars.max_step_speed) ** vars.step_speed_exponent %}
        {% endif %}
    {% elif speed < 0 %}
        {% set speed = 1 %}
    {% endif %}

    {% if gate < 0 %}
        RESPOND TYPE=error MSG="No active gate. Cannot start espooler."
    {% elif not pin %}
        RESPOND TYPE=error MSG="{pin_cfg} does not exist. Cannot start espooler."
    {% elif expected_distance >= 0 and expected_distance < vars.min_distance %}
        MMU_LOG DEBUG=1 MSG="Travel distance ({expected_distance}) is shorter than the configured min distance ({vars.min_distance}). Ignoring espooler activation."
    {% else %}
        MMU_LOG DEBUG=1 MSG="Setting espooler {pin} to {speed}"
        {% set cfg_scale = printer.configfile.settings[pin_cfg].scale|default(1)|float %}
        {% set scale = params.SCALE|default(cfg_scale)|float %}
        {% if en_pin and speed > 0 %}
            SET_PIN PIN="{en_pin}" value="{1}"
        {% endif %}

        SET_PIN PIN="{pin}" value="{speed * cfg_scale * scale}"

        {% if timeout > 0 and speed > 0 and printer[pin_cfg].value == 0 %}
            UPDATE_DELAYED_GCODE ID=mmu_espooler_timeout DURATION={timeout}
        {% elif speed == 0 %}
            # Cancel delayed gcode...if all are turned off
            {% set values = [] %}
            {% for igate in (vars.espooler_gates) %}
                {% set value = printer['output_pin %s_rwd_%d' % (pin_prefix, igate)].value %}
                {% set value = 0 if (igate == gate or value == 0) else value %}
                {% set d = values.append(value) %}
            {% endfor %}
            {% if values|sum == 0 %}
                UPDATE_DELAYED_GCODE ID=mmu_espooler_timeout DURATION=0
            {% endif %}
        {% endif %}

        {% if en_pin and speed == 0 %}
            SET_PIN PIN="{en_pin}" value="{0}"
        {% endif %}
    {% endif %}

###########################################################################
# Delayed gcode to run on startup to identify all the eSpoolers which
# will be used after espooler timeout to ensure all eSpoolers have
# stopped.
#
[delayed_gcode mmu_espooler_startup]
initial_duration: 1.
gcode:
    {% set vars = printer["gcode_macro _MMU_ESPOOLER_VARS"] %}
    {% set pin_prefix = vars.pin_prefix %}
    {% set pin_cfg_prefix = 'output_pin %s_rwd_' % pin_prefix %}
    {% set espooler_gates = [] %}
    {% for key in printer %}
        {% if key.startswith(pin_cfg_prefix) %}
            {% set gate = key | replace(pin_cfg_prefix, '') | int %}
            {% set d = espooler_gates.append(gate | string) %}
        {% endif %}
    {% endfor %}
    SET_GCODE_VARIABLE MACRO=_MMU_ESPOOLER_VARS VARIABLE=espooler_gates VALUE={espooler_gates|join(',')}

###########################################################################
# Delayed gcode to stop all eSpooler after timeout. This is used as a 
# failsafe (unless explicitly disabled by setting a timeout of 0) to ensure
# the eSpoolers do not run indefinitely.
#
[delayed_gcode mmu_espooler_timeout]
gcode:
    {% set vars = printer["gcode_macro _MMU_ESPOOLER_VARS"] %}
    {% for gate in (vars.espooler_gates) %}
        MMU_ESPOOLER_STOP GATE={gate}
    {% endfor %}