Exemplo n.º 1
0
    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
Exemplo n.º 2
0
    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
Exemplo n.º 3
0
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()
Exemplo n.º 4
0
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()