toolhead: Synchronize print_time to main mcu's time
Instead of starting the print_time at zero, start it at the estimated time of the main mcu's clock. This simplifies the code and it ensures that print_time never goes backwards. Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
parent
b9586bad18
commit
5dfe4e1eb9
|
@ -39,6 +39,7 @@ class PrinterFan:
|
||||||
class PrinterHeaterFan:
|
class PrinterHeaterFan:
|
||||||
def __init__(self, printer, config):
|
def __init__(self, printer, config):
|
||||||
self.fan = PrinterFan(printer, config)
|
self.fan = PrinterFan(printer, config)
|
||||||
|
self.mcu = printer.objects['mcu']
|
||||||
heater = config.get("heater", "extruder0")
|
heater = config.get("heater", "extruder0")
|
||||||
self.heater = extruder.get_printer_heater(printer, heater)
|
self.heater = extruder.get_printer_heater(printer, heater)
|
||||||
self.heater_temp = config.getfloat("heater_temp", 50.0)
|
self.heater_temp = config.getfloat("heater_temp", 50.0)
|
||||||
|
@ -51,9 +52,8 @@ class PrinterHeaterFan:
|
||||||
power = 0.
|
power = 0.
|
||||||
if target_temp or current_temp > self.heater_temp:
|
if target_temp or current_temp > self.heater_temp:
|
||||||
power = 1.
|
power = 1.
|
||||||
mcu_time = self.fan.mcu_fan.get_mcu().system_to_mcu_time(
|
print_time = self.mcu.estimated_print_time(eventtime) + FAN_MIN_TIME
|
||||||
eventtime + FAN_MIN_TIME)
|
self.fan.set_speed(print_time, power)
|
||||||
self.fan.set_pwm(mcu_time, power)
|
|
||||||
return eventtime + 1.
|
return eventtime + 1.
|
||||||
|
|
||||||
def add_printer_objects(printer, config):
|
def add_printer_objects(printer, config):
|
||||||
|
|
|
@ -443,15 +443,13 @@ class MCU:
|
||||||
self._config_crc = None
|
self._config_crc = None
|
||||||
self._pin_map = config.get('pin_map', None)
|
self._pin_map = config.get('pin_map', None)
|
||||||
self._custom = config.get('custom', '')
|
self._custom = config.get('custom', '')
|
||||||
|
self._mcu_freq = 0.
|
||||||
# Move command queuing
|
# Move command queuing
|
||||||
ffi_main, self._ffi_lib = chelper.get_ffi()
|
ffi_main, self._ffi_lib = chelper.get_ffi()
|
||||||
self._max_stepper_error = config.getfloat(
|
self._max_stepper_error = config.getfloat(
|
||||||
'max_stepper_error', 0.000025, minval=0.)
|
'max_stepper_error', 0.000025, minval=0.)
|
||||||
self._stepqueues = []
|
self._stepqueues = []
|
||||||
self._steppersync = None
|
self._steppersync = None
|
||||||
# Print time to clock epoch calculations
|
|
||||||
self._print_start_time = 0.
|
|
||||||
self._mcu_freq = 0.
|
|
||||||
# Stats
|
# Stats
|
||||||
self._stats_sumsq_base = 0.
|
self._stats_sumsq_base = 0.
|
||||||
self._mcu_tick_avg = 0.
|
self._mcu_tick_avg = 0.
|
||||||
|
@ -471,9 +469,6 @@ class MCU:
|
||||||
self.is_shutdown = True
|
self.is_shutdown = True
|
||||||
self._shutdown_msg = msg = params['#msg']
|
self._shutdown_msg = msg = params['#msg']
|
||||||
logging.info("%s: %s" % (params['#name'], self._shutdown_msg))
|
logging.info("%s: %s" % (params['#name'], self._shutdown_msg))
|
||||||
pst = self._print_start_time
|
|
||||||
logging.info("Clock last synchronized at %.6f (%d)" % (
|
|
||||||
pst, int(pst * self._mcu_freq)))
|
|
||||||
self.serial.dump_debug()
|
self.serial.dump_debug()
|
||||||
prefix = "MCU shutdown: "
|
prefix = "MCU shutdown: "
|
||||||
if params['#name'] == 'is_shutdown':
|
if params['#name'] == 'is_shutdown':
|
||||||
|
@ -521,12 +516,9 @@ class MCU:
|
||||||
self.serial.connect_file(outfile, dict_data)
|
self.serial.connect_file(outfile, dict_data)
|
||||||
# Handle pacing
|
# Handle pacing
|
||||||
if not pace:
|
if not pace:
|
||||||
def dummy_set_print_start_time(eventtime):
|
def dummy_estimated_print_time(eventtime):
|
||||||
pass
|
return 0.
|
||||||
def dummy_get_print_buffer_time(eventtime, last_move_end):
|
self.estimated_print_time = dummy_estimated_print_time
|
||||||
return 1.250
|
|
||||||
self.set_print_start_time = dummy_set_print_start_time
|
|
||||||
self.get_print_buffer_time = dummy_get_print_buffer_time
|
|
||||||
def timeout_handler(self, eventtime):
|
def timeout_handler(self, eventtime):
|
||||||
last_clock, last_clock_time = self.serial.get_last_clock()
|
last_clock, last_clock_time = self.serial.get_last_clock()
|
||||||
timeout = last_clock_time + self.COMM_TIMEOUT
|
timeout = last_clock_time + self.COMM_TIMEOUT
|
||||||
|
@ -698,22 +690,14 @@ class MCU:
|
||||||
def create_command(self, msg):
|
def create_command(self, msg):
|
||||||
return self.serial.msgparser.create_command(msg)
|
return self.serial.msgparser.create_command(msg)
|
||||||
# Clock syncing
|
# Clock syncing
|
||||||
def set_print_start_time(self, eventtime):
|
|
||||||
clock = self.serial.get_clock(eventtime)
|
|
||||||
logging.debug("Synchronizing mcu clock at %.6f to %d" % (
|
|
||||||
eventtime, clock))
|
|
||||||
est_mcu_time = clock / self._mcu_freq
|
|
||||||
self._print_start_time = est_mcu_time
|
|
||||||
def get_print_buffer_time(self, eventtime, print_time):
|
|
||||||
if self.is_shutdown:
|
|
||||||
return 0.
|
|
||||||
mcu_time = print_time + self._print_start_time
|
|
||||||
est_mcu_time = self.serial.get_clock(eventtime) / self._mcu_freq
|
|
||||||
return mcu_time - est_mcu_time
|
|
||||||
def print_to_mcu_time(self, print_time):
|
def print_to_mcu_time(self, print_time):
|
||||||
return print_time + self._print_start_time
|
return print_time
|
||||||
def system_to_mcu_time(self, eventtime):
|
def print_time_to_clock(self, print_time):
|
||||||
return self.serial.get_clock(eventtime) / self._mcu_freq
|
return int(print_time * self._mcu_freq)
|
||||||
|
def clock_to_print_time(self, clock):
|
||||||
|
return clock / self._mcu_freq
|
||||||
|
def estimated_print_time(self, eventtime):
|
||||||
|
return self.clock_to_print_time(self.serial.get_clock(eventtime))
|
||||||
def get_mcu_freq(self):
|
def get_mcu_freq(self):
|
||||||
return self._mcu_freq
|
return self._mcu_freq
|
||||||
def get_last_clock(self):
|
def get_last_clock(self):
|
||||||
|
@ -726,8 +710,7 @@ class MCU:
|
||||||
def flush_moves(self, print_time):
|
def flush_moves(self, print_time):
|
||||||
if self._steppersync is None:
|
if self._steppersync is None:
|
||||||
return
|
return
|
||||||
mcu_time = print_time + self._print_start_time
|
clock = self.print_time_to_clock(print_time)
|
||||||
clock = int(mcu_time * self._mcu_freq)
|
|
||||||
ret = self._ffi_lib.steppersync_flush(self._steppersync, clock)
|
ret = self._ffi_lib.steppersync_flush(self._steppersync, clock)
|
||||||
if ret:
|
if ret:
|
||||||
raise error("Internal error in stepcompress")
|
raise error("Internal error in stepcompress")
|
||||||
|
|
|
@ -204,18 +204,17 @@ class ToolHead:
|
||||||
self.move_flush_time = config.getfloat(
|
self.move_flush_time = config.getfloat(
|
||||||
'move_flush_time', 0.050, above=0.)
|
'move_flush_time', 0.050, above=0.)
|
||||||
self.print_time = 0.
|
self.print_time = 0.
|
||||||
self.last_print_end_time = self.reactor.monotonic()
|
|
||||||
self.need_check_stall = -1.
|
self.need_check_stall = -1.
|
||||||
self.print_stall = 0
|
self.print_stall = 0
|
||||||
self.synch_print_time = True
|
self.sync_print_time = True
|
||||||
self.forced_synch = False
|
self.last_flush_from_idle = False
|
||||||
self.flush_timer = self.reactor.register_timer(self._flush_handler)
|
self.flush_timer = self.reactor.register_timer(self._flush_handler)
|
||||||
self.move_queue.set_flush_time(self.buffer_time_high)
|
self.move_queue.set_flush_time(self.buffer_time_high)
|
||||||
# Motor off tracking
|
# Motor off tracking
|
||||||
self.motor_off_time = config.getfloat(
|
self.need_motor_off = False
|
||||||
'motor_off_time', 600.000, minval=0.)
|
self.motor_off_time = config.getfloat('motor_off_time', 600., above=0.)
|
||||||
self.motor_off_timer = self.reactor.register_timer(
|
self.motor_off_timer = self.reactor.register_timer(
|
||||||
self._motor_off_handler)
|
self._motor_off_handler, self.reactor.NOW)
|
||||||
# Create kinematics class
|
# Create kinematics class
|
||||||
self.extruder = extruder.DummyExtruder()
|
self.extruder = extruder.DummyExtruder()
|
||||||
self.move_queue.set_extruder(self.extruder)
|
self.move_queue.set_extruder(self.extruder)
|
||||||
|
@ -230,97 +229,80 @@ class ToolHead:
|
||||||
flush_to_time = self.print_time - self.move_flush_time
|
flush_to_time = self.print_time - self.move_flush_time
|
||||||
self.mcu.flush_moves(flush_to_time)
|
self.mcu.flush_moves(flush_to_time)
|
||||||
def get_next_move_time(self):
|
def get_next_move_time(self):
|
||||||
if self.synch_print_time:
|
if not self.sync_print_time:
|
||||||
curtime = self.reactor.monotonic()
|
return self.print_time
|
||||||
if self.print_time:
|
self.sync_print_time = False
|
||||||
buffer_time = self.mcu.get_print_buffer_time(
|
est_print_time = self.mcu.estimated_print_time(self.reactor.monotonic())
|
||||||
curtime, self.print_time)
|
if self.last_flush_from_idle and self.print_time > est_print_time:
|
||||||
self.print_time += max(self.buffer_time_start - buffer_time, 0.)
|
self.print_stall += 1
|
||||||
if self.forced_synch:
|
self.last_flush_from_idle = False
|
||||||
self.print_stall += 1
|
self.need_motor_off = True
|
||||||
self.forced_synch = False
|
self.print_time = max(
|
||||||
else:
|
self.print_time, est_print_time + self.buffer_time_start)
|
||||||
self.mcu.set_print_start_time(curtime)
|
self.reactor.update_timer(self.flush_timer, self.reactor.NOW)
|
||||||
self.print_time = self.buffer_time_start
|
|
||||||
self._reset_motor_off()
|
|
||||||
self.reactor.update_timer(self.flush_timer, self.reactor.NOW)
|
|
||||||
self.synch_print_time = False
|
|
||||||
return self.print_time
|
return self.print_time
|
||||||
def _flush_lookahead(self, must_synch=False):
|
def _flush_lookahead(self, must_sync=False):
|
||||||
synch_print_time = self.synch_print_time
|
sync_print_time = self.sync_print_time
|
||||||
self.move_queue.flush()
|
self.move_queue.flush()
|
||||||
if synch_print_time or must_synch:
|
self.last_flush_from_idle = False
|
||||||
self.synch_print_time = True
|
if sync_print_time or must_sync:
|
||||||
|
self.sync_print_time = True
|
||||||
self.move_queue.set_flush_time(self.buffer_time_high)
|
self.move_queue.set_flush_time(self.buffer_time_high)
|
||||||
self.mcu.flush_moves(self.print_time)
|
self.mcu.flush_moves(self.print_time)
|
||||||
|
self.need_check_stall = -1.
|
||||||
|
self.reactor.update_timer(self.flush_timer, self.reactor.NEVER)
|
||||||
def get_last_move_time(self):
|
def get_last_move_time(self):
|
||||||
self._flush_lookahead()
|
self._flush_lookahead()
|
||||||
return self.get_next_move_time()
|
return self.get_next_move_time()
|
||||||
def reset_print_time(self):
|
def reset_print_time(self):
|
||||||
self._flush_lookahead(must_synch=True)
|
self._flush_lookahead(must_sync=True)
|
||||||
self.print_time = 0.
|
self.print_time = self.mcu.estimated_print_time(self.reactor.monotonic())
|
||||||
self.last_print_end_time = self.reactor.monotonic()
|
|
||||||
self.need_check_stall = -1.
|
|
||||||
self.forced_synch = False
|
|
||||||
self._reset_motor_off()
|
|
||||||
def _check_stall(self):
|
def _check_stall(self):
|
||||||
eventtime = self.reactor.monotonic()
|
eventtime = self.reactor.monotonic()
|
||||||
if not self.print_time:
|
if self.sync_print_time:
|
||||||
# Building initial queue - make sure to flush on idle input
|
# Building initial queue - make sure to flush on idle input
|
||||||
self.reactor.update_timer(self.flush_timer, eventtime + 0.100)
|
self.reactor.update_timer(self.flush_timer, eventtime + 0.100)
|
||||||
return
|
return
|
||||||
# Check if there are lots of queued moves and stall if so
|
# Check if there are lots of queued moves and stall if so
|
||||||
while 1:
|
while 1:
|
||||||
buffer_time = self.mcu.get_print_buffer_time(
|
est_print_time = self.mcu.estimated_print_time(eventtime)
|
||||||
eventtime, self.print_time)
|
buffer_time = self.print_time - est_print_time
|
||||||
stall_time = buffer_time - self.buffer_time_high
|
stall_time = buffer_time - self.buffer_time_high
|
||||||
if stall_time <= 0.:
|
if stall_time <= 0.:
|
||||||
break
|
break
|
||||||
eventtime = self.reactor.pause(eventtime + stall_time)
|
if self.mcu.is_fileoutput():
|
||||||
if not self.print_time:
|
self.need_check_stall = self.reactor.NEVER
|
||||||
return
|
return
|
||||||
self.need_check_stall = self.print_time - stall_time + 0.100
|
eventtime = self.reactor.pause(eventtime + min(1., stall_time))
|
||||||
|
self.need_check_stall = est_print_time + self.buffer_time_high + 0.100
|
||||||
def _flush_handler(self, eventtime):
|
def _flush_handler(self, eventtime):
|
||||||
try:
|
try:
|
||||||
if not self.print_time:
|
|
||||||
# Input idled before filling lookahead queue - flush it
|
|
||||||
self._flush_lookahead()
|
|
||||||
if not self.print_time:
|
|
||||||
return self.reactor.NEVER
|
|
||||||
print_time = self.print_time
|
print_time = self.print_time
|
||||||
buffer_time = self.mcu.get_print_buffer_time(eventtime, print_time)
|
buffer_time = print_time - self.mcu.estimated_print_time(eventtime)
|
||||||
if buffer_time > self.buffer_time_low:
|
if buffer_time > self.buffer_time_low:
|
||||||
# Running normally - reschedule check
|
# Running normally - reschedule check
|
||||||
return eventtime + buffer_time - self.buffer_time_low
|
return eventtime + buffer_time - self.buffer_time_low
|
||||||
# Under ran low buffer mark - flush lookahead queue
|
# Under ran low buffer mark - flush lookahead queue
|
||||||
self._flush_lookahead(must_synch=True)
|
self._flush_lookahead(must_sync=True)
|
||||||
if print_time != self.print_time:
|
if print_time != self.print_time:
|
||||||
# Flushed something - retry
|
self.last_flush_from_idle = True
|
||||||
self.forced_synch = True
|
|
||||||
return self.reactor.NOW
|
|
||||||
if buffer_time > 0.:
|
|
||||||
# Wait for buffer to fully empty
|
|
||||||
return eventtime + buffer_time
|
|
||||||
self.reset_print_time()
|
|
||||||
except:
|
except:
|
||||||
logging.exception("Exception in flush_handler")
|
logging.exception("Exception in flush_handler")
|
||||||
self.force_shutdown()
|
self.force_shutdown()
|
||||||
return self.reactor.NEVER
|
return self.reactor.NEVER
|
||||||
# Motor off timer
|
# Motor off timer
|
||||||
def _reset_motor_off(self):
|
|
||||||
if not self.print_time:
|
|
||||||
waketime = self.reactor.monotonic() + self.motor_off_time
|
|
||||||
else:
|
|
||||||
waketime = self.reactor.NEVER
|
|
||||||
self.reactor.update_timer(self.motor_off_timer, waketime)
|
|
||||||
def _motor_off_handler(self, eventtime):
|
def _motor_off_handler(self, eventtime):
|
||||||
|
if not self.need_motor_off or not self.sync_print_time:
|
||||||
|
return eventtime + self.motor_off_time
|
||||||
|
elapsed_time = self.mcu.estimated_print_time(eventtime) - self.print_time
|
||||||
|
if elapsed_time < self.motor_off_time:
|
||||||
|
return eventtime + self.motor_off_time - elapsed_time
|
||||||
try:
|
try:
|
||||||
self.motor_off()
|
self.motor_off()
|
||||||
self.reset_print_time()
|
|
||||||
except:
|
except:
|
||||||
logging.exception("Exception in motor_off_handler")
|
logging.exception("Exception in motor_off_handler")
|
||||||
self.force_shutdown()
|
self.force_shutdown()
|
||||||
return self.reactor.NEVER
|
return eventtime + self.motor_off_time
|
||||||
# Movement commands
|
# Movement commands
|
||||||
def get_position(self):
|
def get_position(self):
|
||||||
return list(self.commanded_pos)
|
return list(self.commanded_pos)
|
||||||
|
@ -353,13 +335,15 @@ class ToolHead:
|
||||||
self.kin.motor_off(last_move_time)
|
self.kin.motor_off(last_move_time)
|
||||||
self.extruder.motor_off(last_move_time)
|
self.extruder.motor_off(last_move_time)
|
||||||
self.dwell(STALL_TIME)
|
self.dwell(STALL_TIME)
|
||||||
|
self.need_motor_off = False
|
||||||
logging.debug('; Max time of %f' % (last_move_time,))
|
logging.debug('; Max time of %f' % (last_move_time,))
|
||||||
def wait_moves(self):
|
def wait_moves(self):
|
||||||
self._flush_lookahead()
|
self._flush_lookahead()
|
||||||
if self.mcu.is_fileoutput():
|
if self.mcu.is_fileoutput():
|
||||||
return
|
return
|
||||||
eventtime = self.reactor.monotonic()
|
eventtime = self.reactor.monotonic()
|
||||||
while self.print_time:
|
while (not self.sync_print_time
|
||||||
|
or self.print_time >= self.mcu.estimated_print_time(eventtime)):
|
||||||
eventtime = self.reactor.pause(eventtime + 0.100)
|
eventtime = self.reactor.pause(eventtime + 0.100)
|
||||||
def query_endstops(self):
|
def query_endstops(self):
|
||||||
last_move_time = self.get_last_move_time()
|
last_move_time = self.get_last_move_time()
|
||||||
|
@ -378,12 +362,9 @@ class ToolHead:
|
||||||
def stats(self, eventtime):
|
def stats(self, eventtime):
|
||||||
buffer_time = 0.
|
buffer_time = 0.
|
||||||
print_time = self.print_time
|
print_time = self.print_time
|
||||||
if print_time:
|
est_print_time = self.mcu.estimated_print_time(eventtime)
|
||||||
is_active = True
|
is_active = not self.sync_print_time or print_time + 60. > est_print_time
|
||||||
buffer_time = max(0., self.mcu.get_print_buffer_time(
|
buffer_time = max(0., print_time - est_print_time)
|
||||||
eventtime, print_time))
|
|
||||||
else:
|
|
||||||
is_active = eventtime < self.last_print_end_time + 60.
|
|
||||||
return is_active, "print_time=%.3f buffer_time=%.3f print_stall=%d" % (
|
return is_active, "print_time=%.3f buffer_time=%.3f print_stall=%d" % (
|
||||||
print_time, buffer_time, self.print_stall)
|
print_time, buffer_time, self.print_stall)
|
||||||
def force_shutdown(self):
|
def force_shutdown(self):
|
||||||
|
|
Loading…
Reference in New Issue