toolhead: Support flushing even while lookahead queue is idle

Track a "NeedPrime" queue state instead of the "Flushed" state, and
continue running the background flushing timer as long as there may be
data in any of the move queues.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
Kevin O'Connor 2023-11-28 11:27:25 -05:00
parent b7b13588c7
commit c491ea669f
1 changed files with 46 additions and 27 deletions

View File

@ -189,6 +189,8 @@ class MoveQueue:
BUFFER_TIME_LOW = 1.0 BUFFER_TIME_LOW = 1.0
BUFFER_TIME_HIGH = 2.0 BUFFER_TIME_HIGH = 2.0
BUFFER_TIME_START = 0.250 BUFFER_TIME_START = 0.250
BGFLUSH_LOW_TIME = 0.200
BGFLUSH_BATCH_TIME = 0.200
MIN_KIN_TIME = 0.100 MIN_KIN_TIME = 0.100
MOVE_BATCH_TIME = 0.500 MOVE_BATCH_TIME = 0.500
STEPCOMPRESS_FLUSH_TIME = 0.050 STEPCOMPRESS_FLUSH_TIME = 0.050
@ -230,11 +232,12 @@ class ToolHead:
self.need_check_pause = -1. self.need_check_pause = -1.
# Print time tracking # Print time tracking
self.print_time = 0. self.print_time = 0.
self.special_queuing_state = "Flushed" self.special_queuing_state = "NeedPrime"
self.priming_timer = None self.priming_timer = None
self.drip_completion = None self.drip_completion = None
# Flush tracking # Flush tracking
self.flush_timer = self.reactor.register_timer(self._flush_handler) self.flush_timer = self.reactor.register_timer(self._flush_handler)
self.do_kick_flush_timer = True
self.last_flush_time = self.need_flush_time = 0. self.last_flush_time = self.need_flush_time = 0.
# Kinematic step generation scan window time tracking # Kinematic step generation scan window time tracking
self.kin_flush_delay = SDS_CHECK_TIME self.kin_flush_delay = SDS_CHECK_TIME
@ -315,10 +318,9 @@ class ToolHead:
# Resync print_time if necessary # Resync print_time if necessary
if self.special_queuing_state: if self.special_queuing_state:
if self.special_queuing_state != "Drip": if self.special_queuing_state != "Drip":
# Transition from "Flushed"/"Priming" state to main state # Transition from "NeedPrime"/"Priming" state to main state
self.special_queuing_state = "" self.special_queuing_state = ""
self.need_check_pause = -1. self.need_check_pause = -1.
self.reactor.update_timer(self.flush_timer, self.reactor.NOW)
self._calc_print_time() self._calc_print_time()
# Queue moves into trapezoid motion queue (trapq) # Queue moves into trapezoid motion queue (trapq)
next_move_time = self.print_time next_move_time = self.print_time
@ -341,24 +343,22 @@ class ToolHead:
self._update_drip_move_time(next_move_time) self._update_drip_move_time(next_move_time)
self.note_kinematic_activity(next_move_time + self.kin_flush_delay) self.note_kinematic_activity(next_move_time + self.kin_flush_delay)
self._advance_move_time(next_move_time) self._advance_move_time(next_move_time)
def flush_step_generation(self): def _flush_lookahead(self):
# Transition from "Flushed"/"Priming"/main state to "Flushed" state # Transit from "NeedPrime"/"Priming"/"Drip"/main state to "NeedPrime"
self.move_queue.flush() self.move_queue.flush()
self.special_queuing_state = "Flushed" self.special_queuing_state = "NeedPrime"
self.need_check_pause = -1. self.need_check_pause = -1.
self.reactor.update_timer(self.flush_timer, self.reactor.NEVER)
self.move_queue.set_flush_time(BUFFER_TIME_HIGH) self.move_queue.set_flush_time(BUFFER_TIME_HIGH)
self.check_stall_time = 0. self.check_stall_time = 0.
# Flush all queues def flush_step_generation(self):
self._advance_flush_time(self.need_flush_time)
def _flush_lookahead(self):
if self.special_queuing_state:
return self.flush_step_generation()
self.move_queue.flush()
def get_last_move_time(self):
self._flush_lookahead() self._flush_lookahead()
self._advance_flush_time(self.need_flush_time)
def get_last_move_time(self):
if self.special_queuing_state: if self.special_queuing_state:
self._flush_lookahead()
self._calc_print_time() self._calc_print_time()
else:
self.move_queue.flush()
return self.print_time return self.print_time
def _check_pause(self): def _check_pause(self):
eventtime = self.reactor.monotonic() eventtime = self.reactor.monotonic()
@ -366,11 +366,11 @@ class ToolHead:
buffer_time = self.print_time - est_print_time buffer_time = self.print_time - est_print_time
if self.special_queuing_state: if self.special_queuing_state:
if self.check_stall_time: if self.check_stall_time:
# Was in "Flushed" state and got there from idle input # Was in "NeedPrime" state and got there from idle input
if est_print_time < self.check_stall_time: if est_print_time < self.check_stall_time:
self.print_stall += 1 self.print_stall += 1
self.check_stall_time = 0. self.check_stall_time = 0.
# Transition from "Flushed"/"Priming" state to "Priming" state # Transition from "NeedPrime"/"Priming" state to "Priming" state
self.special_queuing_state = "Priming" self.special_queuing_state = "Priming"
self.need_check_pause = -1. self.need_check_pause = -1.
if self.priming_timer is None: if self.priming_timer is None:
@ -397,7 +397,7 @@ class ToolHead:
self.priming_timer = None self.priming_timer = None
try: try:
if self.special_queuing_state == "Priming": if self.special_queuing_state == "Priming":
self.flush_step_generation() self._flush_lookahead()
self.check_stall_time = self.print_time self.check_stall_time = self.print_time
except: except:
logging.exception("Exception in priming_handler") logging.exception("Exception in priming_handler")
@ -405,15 +405,28 @@ class ToolHead:
return self.reactor.NEVER return self.reactor.NEVER
def _flush_handler(self, eventtime): def _flush_handler(self, eventtime):
try: try:
print_time = self.print_time est_print_time = self.mcu.estimated_print_time(eventtime)
buffer_time = print_time - self.mcu.estimated_print_time(eventtime) if not self.special_queuing_state:
if buffer_time > BUFFER_TIME_LOW: # In "main" state - flush lookahead if buffer runs low
# Running normally - reschedule check print_time = self.print_time
return eventtime + buffer_time - BUFFER_TIME_LOW buffer_time = print_time - est_print_time
# Under ran low buffer mark - flush lookahead queue if buffer_time > BUFFER_TIME_LOW:
self.flush_step_generation() # Running normally - reschedule check
if print_time != self.print_time: return eventtime + buffer_time - BUFFER_TIME_LOW
self.check_stall_time = self.print_time # Under ran low buffer mark - flush lookahead queue
self._flush_lookahead()
if print_time != self.print_time:
self.check_stall_time = self.print_time
# In "NeedPrime"/"Priming" state - flush queues if needed
while 1:
if self.last_flush_time >= self.need_flush_time:
self.do_kick_flush_timer = True
return self.reactor.NEVER
buffer_time = self.last_flush_time - est_print_time
if buffer_time > BGFLUSH_LOW_TIME:
return eventtime + buffer_time - BGFLUSH_LOW_TIME
ftime = est_print_time + BGFLUSH_LOW_TIME + BGFLUSH_BATCH_TIME
self._advance_flush_time(min(self.need_flush_time, ftime))
except: except:
logging.exception("Exception in flush_handler") logging.exception("Exception in flush_handler")
self.printer.invoke_shutdown("Exception in flush_handler") self.printer.invoke_shutdown("Exception in flush_handler")
@ -483,11 +496,12 @@ class ToolHead:
self._advance_move_time(npt) self._advance_move_time(npt)
def drip_move(self, newpos, speed, drip_completion): def drip_move(self, newpos, speed, drip_completion):
self.dwell(self.kin_flush_delay) self.dwell(self.kin_flush_delay)
# Transition from "Flushed"/"Priming"/main state to "Drip" state # Transition from "NeedPrime"/"Priming"/main state to "Drip" state
self.move_queue.flush() self.move_queue.flush()
self.special_queuing_state = "Drip" self.special_queuing_state = "Drip"
self.need_check_pause = self.reactor.NEVER self.need_check_pause = self.reactor.NEVER
self.reactor.update_timer(self.flush_timer, self.reactor.NEVER) self.reactor.update_timer(self.flush_timer, self.reactor.NEVER)
self.do_kick_flush_timer = False
self.move_queue.set_flush_time(BUFFER_TIME_HIGH) self.move_queue.set_flush_time(BUFFER_TIME_HIGH)
self.check_stall_time = 0. self.check_stall_time = 0.
self.drip_completion = drip_completion self.drip_completion = drip_completion
@ -495,6 +509,7 @@ class ToolHead:
try: try:
self.move(newpos, speed) self.move(newpos, speed)
except self.printer.command_error as e: except self.printer.command_error as e:
self.reactor.update_timer(self.flush_timer, self.reactor.NOW)
self.flush_step_generation() self.flush_step_generation()
raise raise
# Transmit move in "drip" mode # Transmit move in "drip" mode
@ -504,6 +519,7 @@ class ToolHead:
self.move_queue.reset() self.move_queue.reset()
self.trapq_finalize_moves(self.trapq, self.reactor.NEVER) self.trapq_finalize_moves(self.trapq, self.reactor.NEVER)
# Exit "Drip" state # Exit "Drip" state
self.reactor.update_timer(self.flush_timer, self.reactor.NOW)
self.flush_step_generation() self.flush_step_generation()
# Misc commands # Misc commands
def stats(self, eventtime): def stats(self, eventtime):
@ -560,6 +576,9 @@ class ToolHead:
last_move.timing_callbacks.append(callback) last_move.timing_callbacks.append(callback)
def note_kinematic_activity(self, kin_time): def note_kinematic_activity(self, kin_time):
self.need_flush_time = max(self.need_flush_time, kin_time) self.need_flush_time = max(self.need_flush_time, kin_time)
if self.do_kick_flush_timer:
self.do_kick_flush_timer = False
self.reactor.update_timer(self.flush_timer, self.reactor.NOW)
def get_max_velocity(self): def get_max_velocity(self):
return self.max_velocity, self.max_accel return self.max_velocity, self.max_accel
def _calc_junction_deviation(self): def _calc_junction_deviation(self):