def __init__(self, serial, server): self.__serial = serial self.__signals = server self.proto = SprinterProtocol(serial, self) # 'idle', 'job_done', 'printing', 'paused, 'error' self.printer_state = 'idle' # 'active', 'hot', 'cool', 'idle' self.monitor_state = 'active' self.monitor_timer = Timeout(self.monitor_event_loop) self.report_timer = Timeout(self.report_status) self.temp_timer = Timeout(self.query_temp) self.proto.request_temps() self.__cue_monitor() self.query_temp() # used by pdq request print self.src_path = None self.cache_path = None self.cache_file = None self.job_length = 0 self.job_counter = 0 self.job_dry = False
def __init__(self, serial, server): self.__serial = serial self.__signals = server self.proto = SprinterProtocol(serial, self) # 'idle', 'job_done', 'printing', 'paused, 'error' self.printer_state = "idle" # 'active', 'hot', 'cool', 'idle' self.monitor_state = "active" self.monitor_timer = Timeout(self.monitor_event_loop) self.report_timer = Timeout(self.report_status) self.temp_timer = Timeout(self.query_temp) self.proto.request_temps() self.__cue_monitor() self.query_temp() # used by pdq request print self.src_path = None self.cache_path = None self.cache_file = None self.job_length = 0 self.job_counter = 0 self.job_dry = False
class SprinterMonitor(object): """Implements a gobject-based threadless eventloop, which hooks into SprinterProtocol to facilitate highlevel communication with the printer, as well as pushing the printer's state to the host. It may be safely assumed that when this class is instanced, the printer is in a live state, and everything else is connected already.""" def __init__(self, serial, server): self.__serial = serial self.__signals = server self.proto = SprinterProtocol(serial, self) # 'idle', 'job_done', 'printing', 'paused, 'error' self.printer_state = "idle" # 'active', 'hot', 'cool', 'idle' self.monitor_state = "active" self.monitor_timer = Timeout(self.monitor_event_loop) self.report_timer = Timeout(self.report_status) self.temp_timer = Timeout(self.query_temp) self.proto.request_temps() self.__cue_monitor() self.query_temp() # used by pdq request print self.src_path = None self.cache_path = None self.cache_file = None self.job_length = 0 self.job_counter = 0 self.job_dry = False def get_max_temp_state(self): """The max temp returned is the highest of the set of all temperature states and temperature targets. If this number is less than or equal to 30, then "max_temp" will be set to None, indicating that the system is in fully cooled state and may be ignored. """ readings = [self.proto.temps["b"]] + self.proto.temps["t"] targets = [self.proto.targets["b"]] + self.proto.targets["t"] targets = [i for i in targets if i is not None] union = readings + targets union.sort() max_temp = union.pop() # FIXME rather than an arbitrary definition of room # temperature or whatever '30' is supposed to represent, None # should be returned only after the change in temperature over # the last three minutes was less than a degree or something # like that AND its low enough that it won't likely start your # house on fire or cause burns to anyone who touches the hot # bits. return max_temp if max_temp > 30 else None def on_state_changed(self): """Called by the wrapped SprinterProtocol when its state has changed. Currently, this is called when either a tool or the bed's target temperature is set, or when any tool or the bed's temperature is reported.""" self.report_timer.set(1) def report_status(self): """Generate a report of the temperature and tool states, and push that info to the host via dbus signal.""" status = {"thermistors": {"tools": [], "bed": (0, None)}, "printer_state": self.printer_state} status["thermistors"]["bed"] = (self.proto.temps["b"], self.proto.targets["b"]) for state in zip(self.proto.temps["t"], self.proto.targets["t"]): status["thermistors"]["tools"].append(state) self.__signals.on_report(json.dumps(status)) def __change_monitor_state(self, new_state): """Changes the state of the monitor, and possibly initiates a timeout.""" assert new_state in ["active", "hot", "cool", "idle"] if new_state != self.monitor_state: print "Monitor state is now", new_state old_state = self.monitor_state self.monitor_state = new_state if new_state in ["active", "hot", "cool"]: self.temp_timer.set(1) def request(self, soup): """Takes a block of text, cleans it, and then adds it into the command queue. Inferres where in the queue it should go from context.""" if self.printer_state == "idle": self.proto.request(soup) elif self.printer_state in ("paused", "error"): self.proto.request(soup, interrupt=True) self.__change_monitor_state("active") self.__cue_monitor() def print_file(self, file_path): """Streams gcode from a file object to the printer.""" self.src_path = file_path self.printer_state = "printing" self.__change_monitor_state("active") def print_prep(self): self.cache_path = mkstemp()[1] self.cache_file = open(self.cache_path, "w") src_file = open(self.src_path, "r") self.job_length = 0 self.job_counter = 0 for soup in src_file: cleaned = clean_gcode(soup) self.job_length += len(cleaned) for gcode in cleaned: self.cache_file.write(gcode + "\n") self.cache_file.close() self.cache_file = open(self.cache_path, "r") self.job_dry = False def print_step(self): if not self.job_dry and self.proto.get_appetite() < 50: print "PRINT STEP", self.job_counter, self.job_length percent = str(min((100.0 / self.job_length) * self.job_counter, 100.0)) self.__signals.on_pdq_print_progress(percent) chunk = [] for i in range(100): line = self.cache_file.readline() if line != "": chunk.append(line.strip()) else: break if len(chunk) == 0: # the job's almost done, I guess self.job_dry = True else: self.job_counter += len(chunk) self.proto.request("\n".join(chunk)) if self.job_dry and self.proto.buffer_status() == "idle": self.print_finish() def print_finish(self): self.job_counter = self.job_length self.__signals.on_pdq_print_complete() self.printer_state = "idle" self.cache_file.close() def query_temp(self): """Periodically is called to query for temperature changes.""" self.proto.request_temps() delay = None if self.monitor_state == "active": if self.printer_state == "printing": delay = 5 else: delay = 2 elif self.monitor_state == "hot": delay = 2 elif self.monitor_state == "cool": delay = 5 if delay is not None: self.temp_timer.set(delay) def __cue_monitor(self): """Schedules a monitor timeout, if one is not already scheduled. The delay is determined by the printer's state.""" seconds = 0.5 if self.monitor_state == "active": seconds = 0.1 elif self.monitor_state == "cool": seconds = 5 self.monitor_timer.set(seconds) def monitor_event_loop(self): """Called periodically depending on the monitor status.""" self.proto.execute_requests() if self.printer_state == "printing": if not self.cache_file: self.print_prep() self.print_step() self.__cue_monitor() elif self.proto.buffer_status() == "active": self.__change_monitor_state("active") self.__cue_monitor() else: if self.monitor_state == "active": self.__change_monitor_state("hot") self.__cue_monitor() else: max_temp = self.get_max_temp_state() if not max_temp: self.__change_monitor_state("idle") else: if max_temp < 50: self.__change_monitor_state("cool") self.__cue_monitor()
class SprinterMonitor(object): """Implements a gobject-based threadless eventloop, which hooks into SprinterProtocol to facilitate highlevel communication with the printer, as well as pushing the printer's state to the host. It may be safely assumed that when this class is instanced, the printer is in a live state, and everything else is connected already.""" def __init__(self, serial, server): self.__serial = serial self.__signals = server self.proto = SprinterProtocol(serial, self) # 'idle', 'job_done', 'printing', 'paused, 'error' self.printer_state = 'idle' # 'active', 'hot', 'cool', 'idle' self.monitor_state = 'active' self.monitor_timer = Timeout(self.monitor_event_loop) self.report_timer = Timeout(self.report_status) self.temp_timer = Timeout(self.query_temp) self.proto.request_temps() self.__cue_monitor() self.query_temp() # used by pdq request print self.src_path = None self.cache_path = None self.cache_file = None self.job_length = 0 self.job_counter = 0 self.job_dry = False def get_max_temp_state(self): """The max temp returned is the highest of the set of all temperature states and temperature targets. If this number is less than or equal to 30, then "max_temp" will be set to None, indicating that the system is in fully cooled state and may be ignored. """ readings = [self.proto.temps["b"]] + self.proto.temps["t"] targets = [self.proto.targets["b"]] + self.proto.targets["t"] targets = [i for i in targets if i is not None] union = readings + targets union.sort() max_temp = union.pop() # FIXME rather than an arbitrary definition of room # temperature or whatever '30' is supposed to represent, None # should be returned only after the change in temperature over # the last three minutes was less than a degree or something # like that AND its low enough that it won't likely start your # house on fire or cause burns to anyone who touches the hot # bits. return max_temp if max_temp > 30 else None def on_state_changed(self): """Called by the wrapped SprinterProtocol when its state has changed. Currently, this is called when either a tool or the bed's target temperature is set, or when any tool or the bed's temperature is reported.""" self.report_timer.set(1) def report_status(self): """Generate a report of the temperature and tool states, and push that info to the host via dbus signal.""" status = { "thermistors" : { "tools": [], "bed" : (0, None), }, "printer_state" : self.printer_state, } status["thermistors"]["bed"] = ( self.proto.temps["b"], self.proto.targets["b"]) for state in zip(self.proto.temps["t"], self.proto.targets["t"]): status["thermistors"]["tools"].append(state) self.__signals.on_report(json.dumps(status)) def __change_monitor_state(self, new_state): """Changes the state of the monitor, and possibly initiates a timeout.""" assert new_state in ['active', 'hot', 'cool', 'idle'] if new_state != self.monitor_state: print "Monitor state is now", new_state old_state = self.monitor_state self.monitor_state = new_state if new_state in ['active', 'hot', 'cool']: self.temp_timer.set(1) def request(self, soup): """Takes a block of text, cleans it, and then adds it into the command queue. Inferres where in the queue it should go from context.""" if self.printer_state == "idle": self.proto.request(soup) elif self.printer_state in ("paused", "error"): self.proto.request(soup, interrupt=True) self.__change_monitor_state("active") self.__cue_monitor() def print_file(self, file_path): """Streams gcode from a file object to the printer.""" self.src_path = file_path self.printer_state = "printing" self.__change_monitor_state("active") def print_prep(self): self.cache_path = mkstemp()[1] self.cache_file = open(self.cache_path, "w") src_file = open(self.src_path, "r") self.job_length = 0 self.job_counter = 0 for soup in src_file: cleaned = clean_gcode(soup) self.job_length += len(cleaned) for gcode in cleaned: self.cache_file.write(gcode + "\n") self.cache_file.close() self.cache_file = open(self.cache_path, "r") self.job_dry = False def print_step(self): if not self.job_dry and self.proto.get_appetite() < 50: print "PRINT STEP", self.job_counter, self.job_length percent = str(min((100.0/self.job_length)*self.job_counter, 100.0)) self.__signals.on_pdq_print_progress(percent) chunk = [] for i in range(100): line = self.cache_file.readline() if line != '': chunk.append(line.strip()) else: break if len(chunk) == 0: # the job's almost done, I guess self.job_dry = True else: self.job_counter += len(chunk) self.proto.request("\n".join(chunk)) if self.job_dry and self.proto.buffer_status() == "idle": self.print_finish() def print_finish(self): self.job_counter = self.job_length self.__signals.on_pdq_print_complete() self.printer_state = 'idle' self.cache_file.close() def query_temp(self): """Periodically is called to query for temperature changes.""" self.proto.request_temps() delay = None if self.monitor_state == "active": if self.printer_state == "printing": delay = 5 else: delay = 2 elif self.monitor_state == "hot": delay = 2 elif self.monitor_state == "cool": delay = 5 if delay is not None: self.temp_timer.set(delay) def __cue_monitor(self): """Schedules a monitor timeout, if one is not already scheduled. The delay is determined by the printer's state.""" seconds = .5 if self.monitor_state == "active": seconds = .1 elif self.monitor_state == "cool": seconds = 5 self.monitor_timer.set(seconds) def monitor_event_loop(self): """Called periodically depending on the monitor status.""" self.proto.execute_requests() if self.printer_state == "printing": if not self.cache_file: self.print_prep() self.print_step() self.__cue_monitor() elif self.proto.buffer_status() == "active": self.__change_monitor_state("active") self.__cue_monitor() else: if self.monitor_state == "active": self.__change_monitor_state("hot") self.__cue_monitor() else: max_temp = self.get_max_temp_state() if not max_temp: self.__change_monitor_state("idle") else: if max_temp < 50: self.__change_monitor_state("cool") self.__cue_monitor()