Example #1
0
 def __init__(self, jog_response_file, gcs = None, config = None, logger = None):
     if not config:
         self.config = ConfigService()
     else:
         self.config = config
     
     if not gcs:
         self.gcs = GCodeServiceClient()
     else:
         self.gcs = gcs
     
     if logger:
         self.log = logger
     else:
         self.log = logging.getLogger('Jog')
         ch = logging.StreamHandler()
         ch.setLevel(logging.DEBUG)
         formatter = logging.Formatter("%(levelname)s : %(message)s")
         ch.setFormatter(formatter)
         self.log.addHandler(ch)
     
     self.jog_response_file = jog_response_file
     # Erase content of jog_response_file
     open(jog_response_file, 'w').close()
     
     self.backtrack = int(self.config.get('jog', 'backtrack', 20))
     self.response = {}
     self.tokens = []
     self.cq = queue.Queue()
     self.running = False
     
     self.send_thread = None
Example #2
0
def main():
    # Setup arguments
    parser = argparse.ArgumentParser()
    parser.add_argument("-L",
                        "--log",
                        help="Use logfile to store log messages.",
                        default='<stdout>')
    parser.add_argument("-p",
                        "--pidfile",
                        help="File to store process pid.",
                        default=os.path.join(RUN_PATH, 'xmlrpcserver.pid'))

    # Get arguments
    args = parser.parse_args()
    pidfile = args.pidfile

    with open(pidfile, 'w') as f:
        f.write(str(os.getpid()))

    time.sleep(2)

    gcs = GCodeServiceClient()
    config = ConfigService()

    rpc = create(gcs, config, args.log)
    rpc.start()
    rpc.loop()
Example #3
0
    def __init__(self,
                 arch='armhf',
                 mcu='atmega1280',
                 notify_update=None,
                 config=None,
                 gcs=None):
        self.config = config
        if not config:
            self.config = ConfigService()

        if not gcs:
            self.gcs = GCodeServiceClient()
        else:
            self.gcs = gcs

        self.arch = arch
        self.mcu = mcu
        self.remote = RemoteVersion(arch, mcu, config=config)

        self.tasks = []
        self.notify_update = notify_update

        self.reboot_required = False
        self.status = ''
        self.task = ''
Example #4
0
def main():
    from ws4py.client.threadedclient import WebSocketClient
    from fabtotum.fabui.notify       import NotifyService
    from fabtotum.utils.pyro.gcodeclient import GCodeServiceClient
    from fabtotum.fabui.config import ConfigService
    from fabtotum.os.paths     import RUN_PATH
    import logging
    import argparse
    import os
    
    
    # Setup arguments
    parser = argparse.ArgumentParser()
    parser.add_argument("-L", "--log", help="Use logfile to store log messages.",   default='/var/log/fabui/gpiomonitor.log')
    parser.add_argument("-p", "--pidfile", help="File to store process pid.",       default=os.path.join(RUN_PATH, 'gpiomonitor.pid') )

    # Get arguments
    args = parser.parse_args()
    pidfile = args.pidfile
    
    with open(pidfile, 'w') as f:
        f.write( str(os.getpid()) )
    
    config = ConfigService()

    # Load configuration
    NOTIFY_FILE         = config.get('general', 'notify_file')
    ##################################################################
    SOCKET_HOST         = config.get('socket', 'host')
    SOCKET_PORT         = config.get('socket', 'port')
    ##################################################################
    EVENT_PIN           = config.get('totumduino', 'event_pin')
    
    # Pyro GCodeService wrapper
    gcs = GCodeServiceClient()
    
    ws = WebSocketClient('ws://'+SOCKET_HOST +':'+SOCKET_PORT+'/')
    ws.connect();

    # Notification service
    ns = NotifyService(ws, NOTIFY_FILE, config)
    
    # Setup logger
    logger2 = logging.getLogger('GPIOMonitor')
    logger2.setLevel(logging.DEBUG)
    fh = logging.FileHandler(args.log, mode='w')

    #~ formatter = logging.Formatter("%(name)s - %(levelname)s : %(message)s")
    formatter = logging.Formatter("[%(asctime)s] %(levelname)s : %(message)s")
    fh.setFormatter(formatter)
    fh.setLevel(logging.DEBUG)
    logger2.addHandler(fh)
    
    gpioMonitor = GPIOMonitor(ns, gcs, logger2, EVENT_PIN)
    gpioMonitor.start()
    gpioMonitor.loop()
Example #5
0
    def __init__(self, stats_file, gcs=None, config=None, logger=None):

        if not gcs:
            self.gcs = GCodeServiceClient()
        else:
            self.gcs = gcs

        if not config:
            self.config = ConfigService()
        else:
            self.config = config

        if logger:
            self.log = logger
        else:
            self.log = logging.getLogger('StatsMonitor')
            ch = logging.StreamHandler()
            ch.setLevel(logging.DEBUG)
            formatter = logging.Formatter("%(levelname)s : %(message)s")
            ch.setFormatter(formatter)
            self.log.addHandler(ch)

        self.stats_file = stats_file
        self.running = False

        self.monitor_thread = None
        self.monitor_write_thread = None
        self.ev_update = Event()
        self.backtrack = int(self.config.get('monitor', 'backtrack', 20))
        self.update_period = float(self.config.get('monitor', 'period'))

        ## Monitor variables, fill arrays with zeros
        self.ext_temp = [0.0] * self.backtrack
        self.ext_temp_target = [0.0] * self.backtrack
        self.bed_temp = [0.0] * self.backtrack
        self.bed_temp_target = [0.0] * self.backtrack
        self.delta = [0] * self.backtrack
        self.last_update_time = time.time()

        # re
        #~ self.re_temp = re.compile('ok\sT:(?P<T>[0-9]+\.[0-9]+)\s\/(?P<TT>[0-9]+\.[0-9]+)\sB:(?P<B>[0-9]+\.[0-9]+)\s\/(?P<BT>[0-9]+\.[0-9]+)\s')

        self.config.register_callback(self.__reload_config)
def test_case():
    from fabtotum.utils.pyro.gcodeclient import GCodeServiceClient

    # Pyro GCodeService wrapper
    gcs = GCodeServiceClient()

    # Failure
    RETR = 1

    print ">> G0"
    reply = gcs.send('G0')
    if reply:
        print "<<", " ".join(reply)
        if reply[0] == 'ok':
            # Success
            print _("Totumduino link is working.")
            RETR = 0

    # Result
    exit(RETR)
 def __init__(self, log_trace, monitor_file = None, gcs = None, use_callback = True):
     
     self.config = ConfigService()
     
     self.monitor_file = monitor_file
     self.trace_file = log_trace
     
     self.monitor_lock = RLock()
     self.monitor_info = {
         "progress"              : 0.0,
         "paused"                : False,
         "print_started"         : False,
         "started"               : time.time(),
         "auto_shutdown"         : False,
         "completed"             : False,
         "completed_time"        : 0,
         "layer_count"           : 0,
         "current_layer"         : 0,
         "filename"              : "",
         "task_id"               : 0,
         "ext_temp"              : 0.0,
         "ext_temp_target"       : 0.0,
         "bed_temp"              : 0.0,
         "bed_temp_target"       : 0.0,
         "z_override"            : 0.0,
         "rpm"                   : 0.0,
         "laser"                 : 0.0,
         "fan"                   : 0.0,    
         "speed"                 : 100.0,
         "flow_rate"             : 100.0,
         "tip"                   : False,
         "message"               : '',
         "current_line_number"   : 0,
         "gcode_info"            : None
     }
     
     if not gcs:
         self.gcs = GCodeServiceClient()
     else:
         self.gcs = gcs
     
     if use_callback:
         self.gcs.register_callback(self.callback_handler)
     
     self.macro_error = 0
     self.macro_warning = 0
     self.macro_skipped = 0
     
     self.progress_monitor = None
     
     logging.basicConfig( filename=log_trace, level=logging.INFO, format='%(message)s')
Example #8
0
class Jog:
 
    def __init__(self, jog_response_file, gcs = None, config = None, logger = None):
        if not config:
            self.config = ConfigService()
        else:
            self.config = config
        
        if not gcs:
            self.gcs = GCodeServiceClient()
        else:
            self.gcs = gcs
        
        if logger:
            self.log = logger
        else:
            self.log = logging.getLogger('Jog')
            ch = logging.StreamHandler()
            ch.setLevel(logging.DEBUG)
            formatter = logging.Formatter("%(levelname)s : %(message)s")
            ch.setFormatter(formatter)
            self.log.addHandler(ch)
        
        self.jog_response_file = jog_response_file
        # Erase content of jog_response_file
        open(jog_response_file, 'w').close()
        
        self.backtrack = int(self.config.get('jog', 'backtrack', 20))
        self.response = {}
        self.tokens = []
        self.cq = queue.Queue()
        self.running = False
        
        self.send_thread = None
    
    def __add_token(self, token):
        to_remove = []
        
        if len(self.tokens) < self.backtrack:
            if token not in self.tokens:
                self.tokens.append(token)
        else:
            if token not in self.tokens:
                to_remove.append( self.tokens[0] )
                self.tokens = self.tokens[1:] + [token]
        
        return to_remove
    
    def __send_thread(self):
        self.log.debug("Jog thread: started")
        
        with open(self.jog_response_file, 'w') as f:
            f.write(json.dumps(self.response) )
        
        while self.running:
            
            cmd = self.cq.get()
            
            if cmd.id == Command.KILL:
                break
                
            elif cmd.id == Command.CLEAR:
                self.response = {}
                
            elif cmd.id == Command.GCODE:
                token = cmd.data[0]
                gcode = cmd.data[1]
                
                #~ reply = self.gcs.send(gcode, group = 'jog:{0}'.format(token) )
                #~ self.log.debug("jog.send [%s]", gcode)
                reply = self.gcs.send(gcode, group = 'jog' )
                #~ for ln in reply:
                    #~ self.log.debug("jog.reply [%s] : %s", gcode, ln)
                #print "jog:", reply
                
                # self.tokens
                
                to_remove = self.__add_token(token)
                for tok in to_remove:
                    if tok in self.response:
                        del self.response[tok]
                
                self.response[token] = {'code': gcode, 'reply': reply}
                
            print self.response
            with open(self.jog_response_file, 'w') as f:
                f.write(json.dumps(self.response) )
                
        self.log.debug("Jog thread: stopped")
        
    ### API ###
    
    def is_running(self):
        """ Returns `True` if the service is running """
        return self.running
    
    def start(self):
        """ Start the service. """
        self.running = True
        self.send_thread = Thread( name = "Jog-send", target = self.__send_thread )
        self.send_thread.start()
        
    def clear(self):
        """ Clear response file """
        self.cq.put( Command.clear() )
        
    def stop(self):
        """ Stop the service. """
        self.running = False
        # Used to wake up send_thread so it can detect the condition
        self.cq.put( Command.kill() )
        
    def loop(self):
        """ Loop until the service is stopped. """
        if self.send_thread:
            self.send_thread.join()
    
    def send(self, token, gcode):
        """
        Send a gcode command and write the reply to jog_response_file.
        
        :param token: ID used to pair gcode command and reply
        :param gcode: Gcode to send
        :type token: string
        :type gcode: string
        """
        self.cq.put( Command.gcode(token, gcode) )
Example #9
0
class StatsMonitor:

    MAX_DELTA_TIME = 60  # Maximum allowed delta time

    def __init__(self, stats_file, gcs=None, config=None, logger=None):

        if not gcs:
            self.gcs = GCodeServiceClient()
        else:
            self.gcs = gcs

        if not config:
            self.config = ConfigService()
        else:
            self.config = config

        if logger:
            self.log = logger
        else:
            self.log = logging.getLogger('StatsMonitor')
            ch = logging.StreamHandler()
            ch.setLevel(logging.DEBUG)
            formatter = logging.Formatter("%(levelname)s : %(message)s")
            ch.setFormatter(formatter)
            self.log.addHandler(ch)

        self.stats_file = stats_file
        self.running = False

        self.monitor_thread = None
        self.monitor_write_thread = None
        self.ev_update = Event()
        self.backtrack = int(self.config.get('monitor', 'backtrack', 20))
        self.update_period = float(self.config.get('monitor', 'period'))

        ## Monitor variables, fill arrays with zeros
        self.ext_temp = [0.0] * self.backtrack
        self.ext_temp_target = [0.0] * self.backtrack
        self.bed_temp = [0.0] * self.backtrack
        self.bed_temp_target = [0.0] * self.backtrack
        self.delta = [0] * self.backtrack
        self.last_update_time = time.time()

        # re
        #~ self.re_temp = re.compile('ok\sT:(?P<T>[0-9]+\.[0-9]+)\s\/(?P<TT>[0-9]+\.[0-9]+)\sB:(?P<B>[0-9]+\.[0-9]+)\s\/(?P<BT>[0-9]+\.[0-9]+)\s')

        self.config.register_callback(self.__reload_config)

    def __reload_config(self):

        old_backtrack = self.backtrack

        self.backtrack = int(self.config.get('monitor', 'backtrack'))
        self.update_period = float(self.config.get('monitor', 'period'))

        if old_backtrack != self.backtrack:
            self.ext_temp = [0.0] * self.backtrack
            self.ext_temp_target = [0.0] * self.backtrack
            self.bed_temp = [0.0] * self.backtrack
            self.bed_temp_target = [0.0] * self.backtrack
            self.delta = [0] * self.backtrack
            self.last_update_time = time.time()

    #~ def __parse_temperature(self, line):
    #~ match = self.re_temp.search(line)
    #~ if match:
    #~ return ( match.group('T'), match.group('TT'), match.group('B'), match.group('BT') )

    @staticmethod
    def __rotate_values(value_list, new_value=None):
        if new_value == None:
            new_value = value_list[-1]

        return value_list[1:] + [new_value]

    def __update_values(self,
                        ext_temp=None,
                        ext_temp_target=None,
                        bed_temp=None,
                        bed_temp_target=None):
        """
        Update all values and delta.
        """

        delta = time.time() - self.last_update_time
        if delta > StatsMonitor.MAX_DELTA_TIME:
            delta = StatsMonitor.MAX_DELTA_TIME

        self.ext_temp = self.__rotate_values(self.ext_temp, ext_temp)
        self.ext_temp_target = self.__rotate_values(self.ext_temp_target,
                                                    ext_temp_target)
        self.bed_temp = self.__rotate_values(self.bed_temp, bed_temp)
        self.bed_temp_target = self.__rotate_values(self.bed_temp_target,
                                                    bed_temp_target)
        self.delta = self.__rotate_values(self.delta, delta)

        self.last_update_time = time.time()

        # Trigger write operation
        self.ev_update.set()

    def __write_thread(self):
        """
        Thread to handle write_stats from a single location.
        """
        self.log.debug("StatsMonitor write thread: started [{0}]".format(
            self.update_period))
        while self.running:
            # Used both as a delay and event trigger
            if self.ev_update.wait(self.update_period):
                # There was an update event, so write the new data
                self.__write_stats()
                self.ev_update.clear()

                #self.__write_stats()

        self.log.debug("StatsMonitor write thread: stopped")

    def __monitor_thread(self):
        """
        Thread for periodic temperature reading.
        """
        self.log.debug("StatsMonitor thread: started")
        while self.running:

            # Get temperature
            # Timeout is to prevent waiting for too long when there is a long running
            # command like M109,M190,G29 or G28
            #~ reply = self.gcs.send('M105', group = 'monitor', timeout = 2)
            reply = self.gcs.send('M105', group='monitor', block=True)
            if reply != None:  # No timeout occured
                try:
                    #a, b, c, d = self.__parse_temperature(reply[0])
                    temps = parseM105(reply)
                    if temps:
                        a = temps['T']
                        b = temps['target']['T']
                        c = temps['B']
                        d = temps['target']['B']
                        self.__update_values(a, b, c, d)

                    # with this one there is too much callback threads on the queue
                    # self.gcs.push("temp_change:all", [a, b, c, d])
                except Exception as e:
                    self.log.debug("MONITOR: M105 error, %s", str(e))
            else:
                self.log.debug("MONITOR: M105 aborted")

            # Wait for the specified period of time before reading temp again
            time.sleep(self.update_period)

        self.log.debug("StatsMonitor thread: stopped")

    def __temp_change_callback(self, action, data):
        """
        Capture asynchronous temperature updates during M109 and M190.
        """
        if action == 'ext_bed':
            self.log.debug("Ext: %f, Bed: %f", float(data[0]), float(data[1]))
            self.__update_values(ext_temp=float(data[0]),
                                 bed_temp=float(data[1]))
        elif action == 'all':
            self.log.debug("Ext: %f/%f, Bed: %f/%f", float(data[0]),
                           float(data[1]), float(data[2]), float(data[3]))
            self.__update_values(float(data[0]), float(data[1]),
                                 float(data[2]), float(data[3]))
        elif action == 'bed':
            self.log.debug("Bed: %f", float(data[0]))
            self.__update_values(bed_temp=float(data[0]))
        elif action == 'ext':
            self.log.debug("Ext: %f", float(data[0]))
            self.__update_values(ext_temp=float(data[0]))

    def __gcode_action_callback(self, action, data):
        """
        Capture asynchronous events that modify temperature.
        """
        if action == 'heating':

            if data[0] == 'M109':
                self.__update_values(ext_temp_target=float(data[1]))
            elif data[0] == 'M190':
                self.__update_values(bed_temp_target=float(data[1]))
            elif data[0] == 'M104':
                self.__update_values(ext_temp_target=float(data[1]))
            elif data[0] == 'M140':
                self.__update_values(bed_temp_target=float(data[1]))

    def __callback_handler(self, action, data):
        """
        General callback handler.
        """

        if action.startswith('temp_change'):
            self.__temp_change_callback(action.split(':')[1], data)
        elif action.startswith('gcode_action'):
            self.__gcode_action_callback(action.split(':')[1], data)

    def __write_stats(self):
        """
        Write stats to the stats_file.
        """
        stats = {
            'ext_temp': self.ext_temp,
            'ext_temp_target': self.ext_temp_target,
            'bed_temp': self.bed_temp,
            'bed_temp_target': self.bed_temp_target,
            'delta': self.delta
        }

        with open(self.stats_file, 'w') as f:
            f.write(json.dumps(stats))

    ### API ###

    def set_backtrack_size(self, size):
        # TODO:
        if self.backtrack != size:
            # resize
            pass
        self.backtrack = size

    def start(self):
        self.running = True

        self.gcs.register_callback(self.__callback_handler)

        self.monitor_thread = Thread(name="Monitor",
                                     target=self.__monitor_thread)
        self.monitor_thread.start()

        self.monitor_write_thread = Thread(name="Monitor_write",
                                           target=self.__write_thread)
        self.monitor_write_thread.start()

    def loop(self):
        if self.monitor_thread:
            self.monitor_thread.join()

    def stop(self):
        self.running = False
        self.ev_update.set()
Example #10
0
def test_case():
    from fabtotum.utils.pyro.gcodeclient import GCodeServiceClient
    from fabtotum.fabui.config import ConfigService
    from selftests_common import getEndstopValues, getFrontPanelStatus

    # Pyro GCodeService wrapper
    gcs = GCodeServiceClient()
    config = ConfigService()

    try:
        safety_door = config.get('settings', 'safety.door')
    except KeyError:
        safety_door = 0

    try:
        switch = config.get('settings', 'switch')
    except KeyError:
        switch = 0

    try:
        bed_enabled = config.get('settings', 'hardware')['bed']['enable']
    except KeyError:
        bed_enabled = True

    ####################################################################

    RETR = 0

    # Read 24VDC
    reply = gcs.send("M751")
    if reply:
        #V_24V:24.562 V
        try:
            V = float(reply[0].split(':')[1].split()[0])
            if V > 19.200 and V < 28.800:
                print _(
                    "[V] PSU Voltage nominal 24V DC (+/-20% tolerance): {0}V"
                ).format(V)
            else:
                print _(
                    "CRITICAL: PSU Voltage anomaly. Expected 24V DC +/-20%, got : {0}V"
                ).format(V)
                RETR = 1
        except:
            print _("No response for M751")
            RETR = 1
    else:
        print _("No response for M751")
        RETR = 1

    # Read 5VDC
    reply = gcs.send("M752")
    if reply:
        #V_5V:4.979 V
        try:
            V = float(reply[0].split(':')[1].split()[0])
            if V > 4.0 and V < 6.0:
                print _(
                    "[V] 5V DC Power Supply is Nominal (+/-20% tolerance): {0}V"
                ).format(V)
            else:
                print _(
                    "CRITICAL: 5V DC Power Supply anomaly. Expected 5V DC +/-20%, got : {0}V"
                ).format(V)
                RETR = 1
        except:
            print _("No response for M752")
            RETR = 1
    else:
        print _("No response for M752")
        RETR = 1

    # Read Current
    reply = gcs.send("M753")
    if reply:
        #Isinked:0.000 A
        try:
            A = float(reply[0].split(':')[1].split()[0])
            if A < 0.5:
                print _("[A] Power consumption is Nominal : {0}A < 500mA"
                        ).format(A)
            else:
                print _(
                    "CRITICAL: Power consumption Anomaly : {0}A, expected < 500mA"
                ).format(A)
                RETR = 1
        except:
            print _("No response for M753")
            RETR = 1
    else:
        print _("No response for M753")
        RETR = 1

    # Result
    exit(RETR)
Example #11
0
def test_case():
    from fabtotum.utils.pyro.gcodeclient import GCodeServiceClient
    from fabtotum.fabui.config import ConfigService
    from selftests_common import getEndstopValues, getFrontPanelStatus

    # Pyro GCodeService wrapper
    gcs = GCodeServiceClient()
    config = ConfigService()

    try:
        safety_door = config.get('settings', 'safety.door')
    except KeyError:
        safety_door = 0

    try:
        switch = config.get('settings', 'switch')
    except KeyError:
        switch = 0

    try:
        bed_enabled = config.get('settings', 'hardware')['bed']['enable']
    except KeyError:
        bed_enabled = True

    ####################################################################

    # Success
    RETR = 0

    #~ print "Checking safety measures..."
    if safety_door == 1:
        if not getFrontPanelStatus(gcs):
            print _(
                "! Front panel door is opened, please close it or disable door safety."
            )
            gcs.send('M18')
            exit(1)

    endstops_start = getEndstopValues(gcs)

    if switch == 0:  # x_min endstop
        if not endstops_start['x_min'] or not endstops_start['y_max']:
            print _(
                "Move the head to front-left corner so that it triggeres the endstops"
            )
            #~ print "  Note: if you have done this and still get this error message, it could be the x_min is damaged"
            gcs.send('M18')
            exit(1)
    else:
        if not endstops_start['x_max'] or not endstops_start['y_max']:
            print _(
                "Move the head to front-right corner so that it triggeres the endstops"
            )
            gcs.send('M18')
            exit(1)

    gcs.send('G27')

    ## check X endstops
    # Home all axis without probe
    endstops_after_g27 = getEndstopValues(gcs)
    if switch == 0:
        if endstops_start['x_min'] and not endstops_after_g27['x_min']:
            print _("x_min endstop: PASSED")
        else:
            print _("x_min endstop: FAILED")
            gcs.send('M18')
            exit(1)
    else:
        if endstops_start['x_max'] and not endstops_after_g27['x_max']:
            print _("x_max endstop: PASSED")
        else:
            print _("x_max endstop: FAILED")
            gcs.send('M18')
            exit(1)

    if endstops_start['y_max'] and not endstops_after_g27['y_max']:
        print _("y_max endstop: PASSED")
    else:
        print _("x_max endstop: FAILED")
        gcs.send('M18')
        exit(1)

    ## check Z endstop
    # move the platform up
    gcs.send('G91')
    gcs.send('G0 Z-10.00 F1000.00')
    gcs.send('M400')
    endstops_tmp = getEndstopValues(gcs)

    if not endstops_tmp['z_max'] and endstops_after_g27['z_max']:
        print _("z_max endstop: PASSED")
    else:
        print _("z_max endstop: FAILED")
        gcs.send('M18')
        exit(1)

    # If x_min is used for homing then check x_max
    if switch == 0:
        reply = gcs.send('M734')
        endstop_warning = reply[0]
        # Disable endstop warning
        reply = gcs.send('M734 S0')
        # Move head to x_max position
        gcs.send('G90')
        gcs.send('G0 X234.00 F1000.00')
        gcs.send('M400')
        endstops_tmp = getEndstopValues(gcs)
        # Move away from the endstop
        gcs.send('G91')
        gcs.send('G0 X-10 F1000.00')
        gcs.send('M400')

        # Restore enable endstop warning
        reply = gcs.send('M734 S{0}'.format(endstop_warning))

        if not endstops_start['x_max'] and endstops_tmp['x_max']:
            print _("x_max endstop: PASSED")
        else:
            print _("x_max endstop: FAILED")
            gcs.send('M18')
            exit(1)

    ## Check y_min endstop
    reply = gcs.send('M734')
    endstop_warning = reply[0]
    # Disable endstop warning
    reply = gcs.send('M734 S0')
    # Move head to y_min position
    gcs.send('G90')
    gcs.send('G0 Y245.0 F1000.00')
    gcs.send('M400')
    endstops_tmp = getEndstopValues(gcs)
    # Move away from the endstop
    gcs.send('G91')
    gcs.send('G0 Y-10 F1000.00')
    gcs.send('M400')
    # Restore enable endstop warning
    reply = gcs.send('M734 S{0}'.format(endstop_warning))

    if not endstops_start['y_min'] and endstops_tmp['y_min']:
        print _("y_min endstop: PASSED")
    else:
        print _("y_min endstop: FAILED")
        gcs.send('M18')
        exit(1)

    # Result
    exit(RETR)
Example #12
0
class GCodePusher(object):
    """
    GCode pusher.
    """

    TASK_PREPARING = 'preparing'
    TASK_RUNNING = 'running'
    TASK_PAUSED = 'paused'
    TASK_COMPLETED = 'completed'
    TASK_COMPLETING = 'completing'
    TASK_ABORTING = 'aborting'
    TASK_ABORTED = 'aborted'
    TASK_TERMINATED = 'terminated'

    TYPE_PRINT = 'print'
    TYPE_MILL = 'mill'
    TYPE_SCAN = 'scan'
    TYPE_LASER = 'laser'

    UPDATE_PERIOD = 2  # seconds

    def __init__(self,
                 log_trace=None,
                 monitor_file=None,
                 gcs=None,
                 config=None,
                 use_callback=True,
                 use_stdout=False,
                 update_period=2,
                 lang='en_US.UTF-8',
                 send_email=False,
                 auto_shutdown=False):

        self.monitor_lock = RLock()

        self.UPDATE_PERIOD = update_period
        self.gcode_info = None
        self.lang = lang
        _ = setLanguage(self.lang)
        self.use_stdout = use_stdout
        # Task specific attributes
        self.task_stats = {
            'type': 'unknown',
            'controller': 'unknown',
            'id': 0,
            'pid': os.getpid(),
            'status': GCodePusher.TASK_PREPARING,
            'started_time': time.time(),
            'estimated_time': 0,
            'completed_time': 0,
            'duration': 0,
            'percent': 0.0,
            'auto_shutdown': auto_shutdown,
            'send_email': send_email,
            'message': ''
        }

        # Pusher/File specific attributes
        self.pusher_stats = {
            'file': {
                'full_path': '',
                'name': '',
            },
            'line_total': 0,
            'line_current': 0,
            'type': GCodeInfo.RAW,
            'first_move': False
        }

        # Pusher/File specific attributes
        self.override_stats = {
            'z_override': 0.0,
            'fan': 0,
            'rpm': 0,
            'laser': 0,
            'flow_rate': 0,
            'speed': 0
        }

        self.standardized_stats = {
            'task': self.task_stats,
            'gpusher': self.pusher_stats,
            'override': self.override_stats
        }

        if not config:
            self.config = ConfigService()
        else:
            self.config = config

        if not gcs:
            self.gcs = GCodeServiceClient()
        else:
            self.gcs = gcs

        if not monitor_file:
            monitor_file = self.config.get('general', 'task_monitor')

        if not log_trace:
            log_trace = self.config.get('general', 'trace')

        self.monitor_file = monitor_file
        self.trace_file = log_trace

        self.trace_logger = logging.getLogger('Trace')
        self.trace_logger.setLevel(logging.INFO)

        ch = logging.FileHandler(log_trace)
        formatter = logging.Formatter("%(message)s")
        ch.setFormatter(formatter)
        ch.setLevel(logging.INFO)
        self.trace_logger.addHandler(ch)

        self.temperatres_file = self.config.get('general', 'temperature')

        if use_callback:
            self.gcs.register_callback(self.callback_handler)

        self.gmacro = GMacroHandler(self.gcs,
                                    self.config,
                                    self.trace,
                                    self.resetTrace,
                                    lang=self.lang)

        self.progress_monitor = None
        self.db = Database(self.config)

    def __send_task_email(self):

        if not self.task_stats["send_email"]:
            return

        import shlex, subprocess
        cmd = 'sudo -u www-data php /usr/share/fabui/index.php Std sendTaskEmail/{0}'.format(
            self.task_stats['id'])
        try:
            output = subprocess.check_output(shlex.split(cmd))
            self.trace(_("Email sent"))
        except subprocess.CalledProcessError as e:
            self.trace(_("Email sending failed"))

    def __self_destruct(self):
        import signal
        print 'Initiating SIGKILL'
        pid = os.getpid()
        os.kill(pid, signal.SIGKILL)
        print 'SIGKILL Failed'

    def add_monitor_group(self, group, content={}):
        """
        Add a custom group to monitor file with a specific content.
        If not content is provided, the groups is initialized with an empty dict.
        
        :param group: Group name.
        :param content: Dictinary containg group attributes.
        :type group: string
        :type content: dict
        """
        if group not in self.standardized_stats:
            self.standardized_stats[group] = content

        return self.standardized_stats[group]

    def remove_monitor_group(self, group):
        """
        Remove a custom group from the monitor file.
        
        :param group: Group name.
        :type group: string
        """
        if group not in ['task', 'override', 'gpusher']:
            del self.standardized_stats[group]

    def update_monitor_file(self):
        """
        Write stats to monitor file
        """
        # Update duration
        self.task_stats['duration'] = str(
            time.time() - float(self.task_stats['started_time']))

        if self.monitor_file:
            with open(self.monitor_file, 'w+') as file:
                file.write(json.dumps(self.standardized_stats))

    def trace(self, log_msg):
        """ 
        Write to log message to trace file
        
        :param log_msg: Log message
        :type log_msg: string
        """
        if self.use_stdout:
            print log_msg
        else:
            self.trace_logger.info(log_msg)

    def resetTrace(self):
        """ Reset trace file """
        #with open(self.trace_file, 'w'):
        #   pass
        open(self.trace_file, 'w').close()

    def __shutdown_procedure(self):
        self.trace(_("Schutting down..."))

        # Wait for all commands to be finished
        reply = self.gcs.send('M400')

        # Tell totumduino Raspberry is going to sleep :'(
        reply = self.gcs.send('M729')

        # Stop the GCodeService connection
        self.gcs.stop()

        os.system('poweroff')

    def first_move_callback(self):
        self.trace(_("Task Started"))

    def __first_move_callback(self):
        """
        Triggered when first move command in file executed
        """
        self.pusher_stats['first_move'] = True
        self.first_move_callback()

    def gcode_comment_callback(self, data):
        """
        Triggered when a comment in gcode file is detected
        """
        pass

    def __gcode_comment_callback(self, data):

        if 'layer' in data:
            with self.monitor_lock:
                if 'print' in self.standardized_stats:
                    self.standardized_stats['print']['layer_current'] = data[
                        'layer']
                    self.update_monitor_file()

        self.gcode_comment_callback(data)

    def temp_change_callback(self, action, data):
        """
        Triggered when temperature change is detected
        """
        pass

    def __temp_change_callback(self, action, data):
        self.temp_change_callback(action, data)

    def gcode_action_callback(self, action, data):
        """
        Triggered when action hook for a gcode command is activated 
        """
        pass

    def __gcode_action_callback(self, action, data):

        monitor_write = False

        with self.monitor_lock:
            if action == 'heating':

                if data[0] == 'M109':
                    self.trace(
                        _("Wait for nozzle temperature to reach {0}&deg;C").
                        format(data[1]))
                elif data[0] == 'M190':
                    self.trace(
                        _("Wait for bed temperature to reach {0}&deg;C").
                        format(data[1]))
                elif data[0] == 'M104':
                    self.trace(
                        _("Nozzle temperature set to {0}&deg;C").format(
                            data[1]))
                elif data[0] == 'M140':
                    self.trace(
                        _("Bed temperature set to {0}&deg;C").format(data[1]))

            elif action == 'cooling':

                if data[0] == 'M106':

                    if (len(data) > 1):
                        s_value = data[1]
                    else:
                        s_value = FAN_MAX_VALUE

                    value = int((float(s_value) / FAN_MAX_VALUE) * 100)
                    self.trace(_("Fan value set to {0}%").format(value))
                    self.override_stats['fan'] = float(s_value)
                    monitor_write = True
                elif data[0] == 'M107':
                    self.trace(_("Fan off"))
                    self.override_stats['fan'] = 0.0
                    monitor_write = True

            elif action == 'z_override':
                self.override_stats['z_override'] = float(data[0])

            elif action == 'printing':

                if data[0] == 'M220':  # Speed factor
                    value = float(data[1])
                    self.trace(_("Speed factor set to {0}%").format(value))
                    self.override_stats['speed'] = float(data[1])
                    monitor_write = True
                elif data[0] == 'M221':  # Extruder flow
                    value = float(data[1])
                    self.trace(_("Extruder flow set to {0}%").format(value))
                    self.override_stats['flow_rate'] = float(data[1])
                    monitor_write = True

            elif action == 'milling':
                if data[0] == 'M0':
                    """ .. todo: do something with it """
                    pass
                elif data[0] == 'M1':
                    """ .. todo: do something with it """
                    pass
                elif data[0] == 'M3':
                    value = int(data[1])
                    self.trace(_("Milling motor RPM set to {0}").format(value))
                    self.override_stats['rpm'] = float(data[1])
                    monitor_write = True
                elif data[0] == 'M4':
                    value = int(data[1])
                    self.trace(_("Milling motor RPM set to {0}").format(value))
                    self.override_stats['rpm'] = float(data[1])
                    monitor_write = True
                elif data[0] == 'M6':
                    value = float(data[1])
                    """ .. todo: Check whether laser power should be scaled from 0-255 to 0.0-100.0 """
                    self.trace(_("Laser power set to {0}%").format(value))
                    self.override_stats['laser'] = float(data[1])
                    monitor_write = True

            elif action == 'pause':
                self.pause()

            elif action == 'message':
                self.task_stats['message'] = data
                self.trace(data)

            if monitor_write:
                self.update_monitor_file()

        self.gcode_action_callback(action, data)

    def file_done_callback(self):
        """ Triggered when gcode file execution is completed """
        if self.task_stats["auto_shutdown"]:
            self.__shutdown_procedure()
        else:
            self._stop()

    def __file_done_callback(self, data):
        """
        Internal file done callback preventing user from overloading it.
        """
        with self.monitor_lock:
            self.task_stats['percent'] = 100.0
            self.update_monitor_file()

        self.file_done_callback()

        if self.task_stats['status'] == self.TASK_COMPLETED:
            self.__send_task_email()

    def finish_task(self):
        self.gcs.finish()

    def set_task_status(self, status):
        """
        Set task status.
        
        :param status: Can be one of `GCodePusher.TASK_*` values
        """
        with self.monitor_lock:
            self.task_stats['status'] = status
            self.update_monitor_file()

        if (status == GCodePusher.TASK_COMPLETED
                or status == GCodePusher.TASK_ABORTED):
            self.task_stats['completed_time'] = time.time()
            self.__update_task_db()

        if (status == GCodePusher.TASK_COMPLETED
                or status == GCodePusher.TASK_COMPLETING
                or status == GCodePusher.TASK_ABORTING
                or status == GCodePusher.TASK_ABORTED
                or status == GCodePusher.TASK_RUNNING):
            self.__update_task_db()

    def is_aborted(self):
        return (self.task_stats['status'] == GCodePusher.TASK_ABORTED
                or self.task_stats['status'] == GCodePusher.TASK_ABORTING)

    def is_paused(self):
        return self.task_stats['status'] == GCodePusher.TASK_PAUSED

    def is_started(self):
        return self.task_stats['status'] == GCodePusher.TASK_RUNNING

    def is_completed(self):
        return (self.task_stats['status'] == GCodePusher.TASK_COMPLETED
                or self.task_stats['status'] == GCodePusher.TASK_COMPLETING)

    def state_change_callback(self, data):
        """
        Triggered when state is changed. (paused/resumed)
        """
        pass

    def __state_change_callback(self, data):
        """
        """

        with self.monitor_lock:
            if data == 'paused':
                #~ self.trace( _("Task has been paused") )
                self.task_stats['status'] = GCodePusher.TASK_PAUSED
                #self.monitor_info["paused"] = True
            elif data == 'resumed':
                self.task_stats['status'] = GCodePusher.TASK_RUNNING

            elif data == 'aborted':
                #~ self.trace( _("Task has been aborted") )
                self.task_stats['status'] = GCodePusher.TASK_ABORTING
                self.__update_task_db()

            elif data == 'terminated':
                self.trace(_("Task has been terminated"))
                self.task_stats['status'] = GCodePusher.TASK_TERMINATED
                self.update_monitor_file()
                self.__self_destruct()

            self.update_monitor_file()

        self.state_change_callback(data)

        with self.monitor_lock:
            if data == 'resuming':
                self.gcs.resumed()

    def progress_callback(self, percentage):
        """ 
        Triggered when progress percentage changes 
        
        :param percentage: Progress percentage 0.0 to 100.0
        :type percentage: float
        """
        pass

    def error_callback(self, error_no):
        """ 
        Triggered when an error occures.
        
        :param error_no: Error number
        :param error_msg: Error message
        :type error_no: int
        :type error_msg: string
        """
        pass

    def __error_callback(self, error_no):
        # TODO: process errors
        # TODO: complete ERROR_MESSAGE
        self.error_callback(error_no)

    def __config_change_callback(self, id, data):
        if id == 'shutdown':
            with self.monitor_lock:
                self.task_stats["auto_shutdown"] = (data == 'on')
                self.update_monitor_file()
        elif id == 'email':
            with self.monitor_lock:
                self.task_stats["send_email"] = (data == 'on')
                self.update_monitor_file()
        elif id == 'reload':
            self.config.reload()

    def callback_handler(self, action, data):

        if action == 'file_done':
            self.__file_done_callback(data)
        elif action == 'gcode_comment':
            self.__gcode_comment_callback(data)
        elif action.startswith('gcode_action'):
            self.__gcode_action_callback(action.split(':')[1], data)
        elif action == 'first_move':
            self.__first_move_callback()
        elif action.startswith('temp_change'):
            self.__temp_change_callback(action.split(':')[1], data)
        elif action == 'state_change':
            self.__state_change_callback(data)
        elif action.startswith('config:'):
            self.__config_change_callback(action.split(':')[1], data)
        elif action == 'error':
            self.__error_callback(data[0])
        elif action == 'self_descruct':
            print 'Self Descruct sequence activated...'
            self.__self_destruct()
        else:
            self.custom_action_callback(action, data)

    def custom_action_callback(self, action, data):
        """
        Handle user defined action
        """
        pass

    def get_progress(self):
        """ 
        Get progress of file push or override this function for custom progress calculation.
        """
        return self.gcs.get_progress()

    def __progress_monitor_thread(self):
        old_progress = -1
        monitor_write = False

        while self.gcs.still_running():

            progress = self.get_progress()

            if old_progress != progress:
                old_progress = progress
                dur = float(self.task_stats['duration'])

                first_move = self.pusher_stats['first_move']

                if progress == 0.0 or first_move == False:
                    self.task_stats['estimated_time'] = 0
                else:
                    self.task_stats['estimated_time'] = (
                        (dur / float(progress)) * 100.0)

                print self.task_stats['estimated_time'], progress

                self.progress_callback(progress)

            # Write progress even if it did not change because duration is update
            # during update_monitor_file()
            with self.monitor_lock:
                self.task_stats['percent'] = progress
                self.update_monitor_file()

            time.sleep(GCodePusher.UPDATE_PERIOD)

    def prepare_task(self,
                     task_id,
                     task_type='unknown',
                     task_controller='make',
                     gcode_file=None):

        self.task_stats['type'] = task_type
        self.task_stats['controller'] = task_controller
        self.task_stats['id'] = task_id
        self.task_stats['status'] = GCodePusher.TASK_PREPARING
        self.task_stats['started_time'] = time.time()
        self.task_stats['completed_time'] = 0
        self.task_stats['estimated_time'] = 0
        self.task_stats['duration'] = 0
        self.task_stats['percent'] = 0.0
        #~ self.task_stats['auto_shutdown']    = auto_shutdown # configured in __init __
        #~ self.task_stats['send_email']       = send_email    # configured in __init __
        self.task_stats['message'] = ''

        self.override_stats['z_override'] = 0.0
        self.override_stats['fan'] = 0
        self.override_stats['rpm'] = 0
        self.override_stats['laser'] = 0
        self.override_stats['flow_rate'] = 100.0
        self.override_stats['speed'] = 100.0

        self.resetTrace()

        if gcode_file:
            self.trace(_("Processing file"))
            self.trace(_("This may take a while, please wait"))
            gfile = GCodeFile(gcode_file)
            self.trace(_("File processed"))
            task_db = self.get_task(task_id)
            file = self.get_file(task_db['id_file'])
            self.pusher_stats['file']['full_path'] = gcode_file
            self.pusher_stats['file']['name'] = file['client_name']
            self.pusher_stats['line_total'] = gfile.info['line_count']
            self.pusher_stats['line_current'] = 0
            self.pusher_stats['type'] = gfile.info['type']
            self.pusher_stats['first_move'] = False

            if gfile.info['type'] == GCodeInfo.PRINT:
                engine = 'unknown'
                if 'slicer' in gfile.info:
                    engine = gfile.info['slicer']

                layer_total = 0
                if 'layer_count' in gfile.info:
                    layer_total = int(gfile.info['layer_count'])

                if 'print' not in self.standardized_stats:
                    self.print_stats = {
                        'layer_total': layer_total,
                        'layer_current': 0,
                        'engine': engine
                    }
                    self.add_monitor_group('print', self.print_stats)
                else:
                    self.standardized_stats['print']['engine'] = engine
                    self.standardized_stats['print']['layer_current'] = 0
                    self.standardized_stats['print'][
                        'layer_total'] = layer_total

            elif gfile.info['type'] == GCodeInfo.MILL or gfile.info[
                    'type'] == GCodeInfo.DRILL:
                self.mill_stats = {
                    # Place holder
                }
                self.add_monitor_group('mill', self.mill_stats)

            #~ elif gfile.info['type'] == GCodeInfo.LASER:
            #~ self.laser_stats = {
            #~ # Place holder
            #~ }
            #~ self.add_monitor_group('laser', self.laser_stats)

        if self.monitor_file:
            self.progress_monitor = Thread(
                target=self.__progress_monitor_thread)
            self.progress_monitor.start()

        with self.monitor_lock:
            self.update_monitor_file()

    def __update_task_db(self):
        """
        Converts task_stats to compatible format for sys_tasks table and writes
        the values to the database.
        """
        task_id = self.task_stats['id']

        if task_id == 0:
            return

        task_db = Task(self.db, task_id)

        if (self.task_stats['status'] == GCodePusher.TASK_PREPARING
                or self.task_stats['status'] == GCodePusher.TASK_RUNNING
                or self.task_stats['status'] == GCodePusher.TASK_PAUSED):

            task_db['status'] = GCodePusher.TASK_RUNNING

        elif (self.task_stats['status'] == GCodePusher.TASK_COMPLETED
              or self.task_stats['status'] == GCodePusher.TASK_ABORTING
              or self.task_stats['status'] == GCodePusher.TASK_ABORTED):
            task_db['status'] = self.task_stats['status']

            task_db['finish_date'] = timestamp2datetime(
                self.task_stats['completed_time'])

        task_db['type'] = self.task_stats['type']
        task_db['controller'] = self.task_stats['controller']

        tid = task_db.write()

        if task_id == TableItem.DEFAULT:
            self.task_stats['id'] = tid
            with self.monitor_lock:
                self.update_monitor_file()

    def loop(self):
        """
        Wait for all GCodePusher threads to finish.
        """
        self.gcs.loop()
        if self.progress_monitor:
            self.progress_monitor.join()
        time.sleep(0.5)

    def __stop_thread(self):
        self.gcs.stop()

    def stop(self):
        """
        Signal all GCodePusher threads to stop.
        """
        stop_thread = Thread(target=self.__stop_thread)
        stop_thread.start()

    def exec_macro(self, preset, args=None, atomic=True):
        """
        Execute macro command.
        """
        return self.gmacro.run(preset, args, atomic, reset_trace=False)

    def send(self,
             code,
             block=True,
             timeout=None,
             trace=None,
             group='gcode',
             expected_reply='ok'):
        """
        Send a single gcode command and display trace message.
        """
        if trace:
            self.trace(trace)

        #TODO: ConnectionClosedError
        return self.gcs.send(code,
                             block=block,
                             timeout=timeout,
                             group=group,
                             expected_reply=expected_reply)

    def send_file(self, filename):
        """
        Send a file to totumduino. File will be send line by line and it's progress 
        can be monitored using `get_progress` function.
        When the file has been completely sent `file_done_callback` will be called.
        """
        return self.gcs.send_file(filename)

    def get_temperature_history(self):
        """
        Return temperature history data.
        """
        try:
            json_f = open(self.temperatres_file, 'r')
            return json.load(json_f)
        except:
            return {}

    #### Object related API ####

    def get_task(self, task_id):
        t = Task(self.db, task_id)
        if t.exists():
            return t
        return None

    def get_file(self, file_id):
        f = File(self.db, file_id)
        if f.exists():
            return f
        return None

    def get_object(self, object_id):
        obj = Object(self.db, object_id)
        if obj.exists():
            return obj
        return None

    def add_object(self, name, desc, user_id, public=Object.PUBLIC):
        """
        Add object to database.
        """
        obj = Object(self.db,
                     user_id=user_id,
                     name=name,
                     desc=desc,
                     public=public)
        obj.write()

        return obj

    def delete_object(self, object_id):
        """
        Remove object from database and all the files associated to it.
        """
        to_delete = []

        obj = self.get_object(object_id)
        if obj:
            ofmap = ObjFile(self.db)

            files = ofmap.object_files(object_id)
            for fid in files:
                f = File(self.db, file_id=fid)
                aids = ofmap.file_associations(fid)
                # Check if file is only associated to one object
                # if so we can remove it from db and filesystem
                if len(aids) == 1:
                    to_delete.append(f['full_path'])
                    f.delete()

            # Remove all associations with object_id
            aids = ofmap.object_associations(object_id)
            ofmap.delete(aids)

            obj.delete()

            for f in to_delete:
                try:
                    os.remove(f)
                except Exception as e:
                    pass

    def pause(self):
        self.gcs.pause()

    def resume(self):
        self.gcs.resume()
class GCodePusher(object):
    """
    GCode pusher.
    """
    
    def __init__(self, log_trace, monitor_file = None, gcs = None, use_callback = True):
        
        self.config = ConfigService()
        
        self.monitor_file = monitor_file
        self.trace_file = log_trace
        
        self.monitor_lock = RLock()
        self.monitor_info = {
            "progress"              : 0.0,
            "paused"                : False,
            "print_started"         : False,
            "started"               : time.time(),
            "auto_shutdown"         : False,
            "completed"             : False,
            "completed_time"        : 0,
            "layer_count"           : 0,
            "current_layer"         : 0,
            "filename"              : "",
            "task_id"               : 0,
            "ext_temp"              : 0.0,
            "ext_temp_target"       : 0.0,
            "bed_temp"              : 0.0,
            "bed_temp_target"       : 0.0,
            "z_override"            : 0.0,
            "rpm"                   : 0.0,
            "laser"                 : 0.0,
            "fan"                   : 0.0,    
            "speed"                 : 100.0,
            "flow_rate"             : 100.0,
            "tip"                   : False,
            "message"               : '',
            "current_line_number"   : 0,
            "gcode_info"            : None
        }
        
        if not gcs:
            self.gcs = GCodeServiceClient()
        else:
            self.gcs = gcs
        
        if use_callback:
            self.gcs.register_callback(self.callback_handler)
        
        self.macro_error = 0
        self.macro_warning = 0
        self.macro_skipped = 0
        
        self.progress_monitor = None
        
        logging.basicConfig( filename=log_trace, level=logging.INFO, format='%(message)s')
    
    def writeMonitor(self):
        """
        Write stats to monitor file
        """
        _layers =   {
                    'total' : str(self.monitor_info['layer_count']), 
                    'actual': str(self.monitor_info['current_layer'])
                    }
 
        _stats  =   {
                    "percent"           : str(self.monitor_info['progress']),
                    "line_number"       : str(self.monitor_info['current_line_number']),
                    "extruder"          : str(self.monitor_info['ext_temp']),
                    "bed"               : str(self.monitor_info['bed_temp']),
                    "extruder_target"   : str(self.monitor_info['ext_temp_target']),
                    "bed_target"        : str(self.monitor_info['bed_temp_target'] ),
                    "z_override"        : str(self.monitor_info['z_override']),
                    "layers"            : str(self.monitor_info['layer_count']),
                    "rpm"               : str(self.monitor_info['rpm']),
                    "fan"               : str(self.monitor_info['fan']),
                    "speed"             : str(self.monitor_info['speed']),
                    "flow_rate"         : str(self.monitor_info['flow_rate'])
                    }
                                
        _tip    =   {
                    "show"              : str(self.monitor_info['tip']),
                    "message"           : str(self.monitor_info['message'])
                    }

        if self.monitor_info["gcode_info"]:
            filename = self.monitor_info["gcode_info"]["filename"]
            line_count = self.monitor_info["gcode_info"]["line_count"]
        else:
            filename =''
            line_count = 0

         
        _print  =   {
                    "name"              : str(filename),
                    "lines"             : str(line_count),
                    "print_started"     : str(self.monitor_info["print_started"]),
                    "started"           : str(self.monitor_info["started"]),
                    "paused"            : str(self.monitor_info["paused"]),
                    "completed"         : str(self.monitor_info["completed"]),
                    "completed_time"    : str(self.monitor_info["completed_time"]),
                    "shutdown"          : str(self.monitor_info["auto_shutdown"]),
                    "tip"               : _tip,
                    "stats"             : _stats
                    }

        engine = 'unknown'
        if self.monitor_info["gcode_info"]:
            if 'slicer' in self.monitor_info["gcode_info"]:
                engine = self.monitor_info["gcode_info"]["slicer"]
        
        stats   =   {
                    "type"      : "print", 
                    "print"     : _print,
                    "engine"    : str(engine),
                    "task_id"   : self.monitor_info["task_id"]
                    }
            
        if self.monitor_file:
            with open(self.monitor_file,'w+') as file:
                file.write(json.dumps(stats))
    
    def trace(self, log_msg):
        """ 
        Write to log message to trace file
        
        :param log_msg: Log message
        :type log_msg: string
        """
        logging.info(log_msg)
        
    def resetTrace(self):
        """ Reset trace file """
        with open(self.trace_file, 'w'):
            pass
    
    def __shutdown_procedure(self):
        self.trace( _("Schutting down...") )
        
        # Wait for all commands to be finished
        reply = self.gcs.send('M400')
        
        # Tell totumduino Raspberry is going to sleep :'(
        reply = self.gcs.send('M729')
        
        # Stop the GCodeService connection
        self.gcs.stop()
        
        # TODO: trigger system shutdown
        
    def first_move_callback(self):
        self.trace( _("Task Started") )
    
    def __first_move_callback(self):
        """
        Triggered when first move command in file executed
        """
        
        self.monitor_lock.acquire()
        self.monitor_info['print_started'] = True
        self.monitor_lock.release()
        
        self.first_move_callback()
    
    def gcode_comment_callback(self, data):
        """
        Triggered when a comment in gcode file is detected
        """
        pass
    
    def __gcode_comment_callback(self, data):

        self.monitor_lock.acquire()
        if 'layer' in data:
            self.monitor_info['current_layer'] = data['layer']
        self.monitor_lock.release()
        
        self.gcode_comment_callback(data)

    def temp_change_callback(self, action, data):
        """
        Triggered when temperature change is detected
        """
        pass
        
    def __temp_change_callback(self, action, data):

        self.monitor_lock.acquire()
        
        if action == 'all':
            #print "Ext: {0}, Bed: {1}".format(data[0], data[1])
            self.monitor_info['ext_temp'] = float(data[0])
            self.monitor_info['bed_temp'] = float(data[1])
        elif action == 'bed':
            #print "Bed: {0}".format(data[0])
            self.monitor_info['bed_temp'] = float(data[0])
        elif action == 'ext':
            #print "Ext: {0}".format(data[0])
            self.monitor_info['ext_temp'] = float(data[0])
            
        self.monitor_lock.release()
        
        self.writeMonitor()
        
        self.temp_change_callback(action, data)
    
    def gcode_action_callback(self, action, data):
        """
        Triggered when action hook for a gcode command is activated 
        """
        pass
                
    def __gcode_action_callback(self, action, data):

        monitor_write = False

        self.monitor_lock.acquire()
        
        if action == 'heating':

            if data[0] == 'M109':
                self.trace( _("Wait for nozzle temperature to reach {0}&deg;C").format(data[1]) )
                self.monitor_info['ext_temp_target'] = float(data[1])
                monitor_write = True
            elif data[0] == 'M190':
                self.trace( _("Wait for bed temperature to reach {0}&deg;C").format(data[1]) )
                self.monitor_info['bed_temp_target'] = float(data[1])
                monitor_write = True
            elif data[0] == 'M104':
                self.trace( _("Nozzle temperature set to {0}&deg;C").format(data[1]) )
                self.monitor_info['ext_temp_target'] = float(data[1])
                monitor_write = True
            elif data[0] == 'M140':
                self.trace( _("Bed temperature set to {0}&deg;C").format(data[1]) )
                self.monitor_info['bed_temp_target'] = float(data[1])
                monitor_write = True
            
        elif action == 'cooling':
            
            if data[0] == 'M106':
                value = int((float( data[1] ) / 255) * 100)
                self.trace( _("Fan value set to {0}%").format(value) )
                self.monitor_info['fan'] = float(data[1])
                monitor_write = True
            elif data[0] == 'M107':
                self.trace( _("Fan off") )
                self.monitor_info['fan'] = 0.0
                monitor_write = True
                
        elif action == 'printing':
            
            if data[0] == 'M220': # Speed factor
                value = float( data[1] )
                self.trace( _("Speed factor set to {0}%").format(value) )
                self.monitor_info['speed'] = float(data[1])
                monitor_write = True
            elif data[0] == 'M221': # Extruder flow
                value = float( data[1] )
                self.trace( _("Extruder flow set to {0}%").format(value) )
                self.monitor_info['flow_rate'] = float(data[1])
                monitor_write = True
                
        elif action == 'milling':
            if data[0] == 'M0':
                """ .. todo: do something with it """
                pass
            elif data[0] == 'M1':
                """ .. todo: do something with it """
                pass
            elif data[0] == 'M3':
                self.trace( _("Milling motor RPM set to {0}%").format(value) )
                self.monitor_info['rpm'] = float(data[1])
                monitor_write = True
            elif data[0] == 'M4':
                self.trace( _("Milling motor RPM set to {0}%").format(value) )
                self.monitor_info['rpm'] = float(data[1])
                monitor_write = True
            elif data[0] == 'M6':
                """ .. todo: Check whether laser power should be scaled from 0-255 to 0.0-100.0 """
                self.trace( _("Laser power set to {0}%").format(value) )
                self.monitor_info['laser'] = float(data[1])
                monitor_write = True
                
        elif action == 'message':
            print "MSG: {0}".format(data)
            
        self.monitor_lock.release()
        
        if monitor_write:
            self.writeMonitor()
        
        self.gcode_action_callback(action, data)

    def file_done_callback(self):
        """ Triggered when gcode file execution is completed """
        if self.monitor_info["auto_shutdown"]:
            self.__shutdown_procedure()
        else:
            self._stop()
            
    def __file_done_callback(self, data):
        """
        Internal file done callback preventing user from overloading it.
        """
        self.monitor_lock.acquire()
        
        self.monitor_info["completed_time"] = int(time.time())
        self.monitor_info["completed"] = True        
        self.monitor_info['progress'] = 100.0 #gcs.get_progress()
        self.writeMonitor()
        
        self.monitor_lock.release()
        
        self.file_done_callback()
    
    def state_change_callback(self, data):
        """
        Triggered when state is changed. (paused/resumed)
        """
        pass
        
    def __state_change_callback(self, data):
        self.monitor_lock.acquire()
        
        if data == 'paused':
            self.trace( _("Print is now paused") )
            self.monitor_info["paused"] = True
        elif data == 'resumed':
            self.monitor_info["paused"] = False
            
        self.monitor_lock.release()
        
        self.writeMonitor()
        
        self.state_change_callback(data)
    
    def progress_callback(self, percentage):
        """ 
        Triggered when progress percentage changes 
        
        :param percentage: Progress percentage 0.0 to 100.0
        :type percentage: float
        """
        pass
    
    def error_callback(self, error_no, error_msg):
        """ 
        Triggered when an error occures.
        
        :param error_no: Error number
        :param error_msg: Error message
        :type error_no: int
        :type error_msg: string
        """
        pass
    
    def __error_callback(self, error_no, error_msg):
        # TODO: process errors
        # TODO: complete ERROR_MESSAGE
        self.error_callback(error_no, error_msg)
    
    def callback_handler(self, action, data):
        if action == 'file_done':
            self.__file_done_callback(data)
        elif action == 'gcode_comment':
            self.__gcode_comment_callback(data)
        elif action.startswith('gcode_action'):
            self.__gcode_action_callback(action.split(':')[1], data)
        elif action == 'first_move':
            self.__first_move_callback()
        elif action.startswith('temp_change'):
            self.__temp_change_callback(action.split(':')[1], data)
        elif action == 'state_change':
            self.__state_change_callback()
        elif action == 'error':
            self.__error_callback(data[0], data[1])

    def progress_monitor_thread(self):
        old_progress = -1
        monitor_write = False
        
        while self.gcs.still_running():
            
            progress = self.gcs.get_progress()
            
            if self.monitor_info["gcode_info"]:
                if self.monitor_info["gcode_info"]["type"] == GCodeInfo.PRINT:
                    reply = self.gcs.send("M105")
                    self.monitor_lock.acquire()
                    try:
                        a, b, c, d = parse_temperature(reply[0])
                        self.monitor_info['ext_temp'] = a
                        self.monitor_info['ext_temp_target'] = b
                        self.monitor_info['bed_temp'] = c
                        self.monitor_info['bed_temp_target'] = d
                    except Exception:
                        pass
                    self.monitor_lock.release()
                    monitor_write = True
                
            if old_progress != progress:
                old_progress = progress
                self.monitor_lock.acquire()
                self.monitor_info['progress'] = progress
                self.monitor_lock.release()
                self.progress_callback(progress)
                monitor_write = True

            if monitor_write:
                self.writeMonitor()
                monitor_write = False

            time.sleep(2)
       
    def prepare(self, gcode_file, task_id,
                    ext_temp_target = 0.0,
                    bed_temp_target = 0.0,
                    rpm = 0):
        """
        
        
        :param gcode_file:
        :param task_id:
        :param ext_temp_target:
        :param bed_temp_target:
        :param rpm: ???
        """
        
        gfile = GCodeFile(gcode_file)
        
        self.monitor_info["progress"] = 0.0
        self.monitor_info["paused"] = False
        self.monitor_info["print_started"] = False
        self.monitor_info["started"] = time.time()
        self.monitor_info["auto_shutdown"] = False
        self.monitor_info["completed"] = False
        self.monitor_info["completed_time"] = 0
        self.monitor_info["layer_count" ] = 0
        self.monitor_info["current_layer"] = 0
        self.monitor_info["filename"] = gcode_file
        self.monitor_info["task_id"] = task_id
        #self.monitor_info["ext_temp"] = ext_temp
        self.monitor_info["ext_temp_target"] = ext_temp_target
        #self.monitor_info["bed_temp"] = bed_temp
        self.monitor_info["bed_temp_target"] = bed_temp_target
        self.monitor_info["z_override"] = 0.0
        self.monitor_info["rpm"] = 0
        self.monitor_info["fan"] = 0.0
        self.monitor_info["speed"] = 100.0
        self.monitor_info["flow_rate"] = 100.0
        self.monitor_info["tip"] = False
        self.monitor_info["message"] = ''
        self.monitor_info["current_line_number"] = 0
        self.monitor_info["gcode_info"] = gfile.info
        
        if self.monitor_file:
            print "Creating monitor thread"
            
            self.progress_monitor = Thread( target=self.progress_monitor_thread )
            self.progress_monitor.start() 
        else:
            print "Skipping monitor thread"
        
        if gfile.info['type'] == GCodeInfo.PRINT:
            # READ TEMPERATURES BEFORE PRINT STARTS (improve UI feedback response)
            reply = self.gcs.send("M105")
            if reply:
                ext_temp, ext_temp_target, bed_temp, bed_temp_target = parse_temperature(reply[0])

        self.monitor_info["ext_temp"] = ext_temp
        self.monitor_info["ext_temp_target"] = ext_temp_target
        self.monitor_info["bed_temp"] = bed_temp
        self.monitor_info["bed_temp_target"] = bed_temp_target
        self.monitor_info["z_override"] = 0.0
        self.monitor_info["rpm"] = rpm
        
        self.resetTrace()
    
    def loop(self):
        """
        Wait for all GCodePusher threads to finish.
        """
        self.gcs.loop()
        if self.progress_monitor:
            self.progress_monitor.join()
        time.sleep(0.5)
        
    def __stop_thread(self):
        self.gcs.stop()   
        
    def stop(self):
        """
        Signal all GCodePusher threads to stop.
        """
        stop_thread = Thread( target = self.__stop_thread )
        stop_thread.start()
            
    def reset_macro_status(self):
        """
        Reset macro status counters to zero.
        """
        self.macro_warning = 0
        self.macro_error = 0
        self.macro_skipped = 0
    
    def macro_start(self):
        pass
        # self.atomic_begin()
        
    def macro_end(self):
        pass
        # self.atomic_end()
    
    def macro(self, code, expected_reply, timeout, error_msg, delay_after, warning=False, verbose=True):
        """
        Send a command and check it's reply.
        
        :param code: gcode
        :param expected_reply: Expected reply
        :param error_msg: Error message to display
        :param timeout: Reply timeout in seconds
        :param delay_after: Time in seconds to wait after receiving the rely
        :param warning: Treat wrong reply as warning not as error
        :param verbose: Whether initial message should be displayed or not.
        :type code: string
        :type expected_reply: string
        :type timeout: float
        :type error_msg: string
        :type delay_after: float
        :type warning: bool
        :type verbose: bool
        """
        if self.macro_error == 0:
            if verbose:
                self.trace(error_msg)
            
            reply = self.gcs.send(code, timeout=timeout, group = 'macro')
            if expected_reply:
                # Check if the reply is as expected
                if reply[0] != expected_reply:
                    if warning:
                        self.trace(error_msg + _(": Warning!"))
                        self.macro_warning += 1
                    else:
                        self.trace(error_msg + _(": Failed ({0})".format(reply[0]) ))
                        self.macro_error += 1
        else:
            self.trace(error_msg + _(": Skipped"))
            self.macro_skipped += 1
                
        #time.sleep(delay_after) #wait the desired amount
        
    def send(self, code, block = True, timeout = None, trace = None):
        """
        Send a single gcode command and display trace message.
        """
        if trace:
            self.trace(trace)
        return self.gcs.send(code, expected_reply, block, timeout)
        
    def send_file(self, filename):
        """
        """
        return self.gcs.send_file(filename)
Example #14
0
def main():
    config = ConfigService()
    gcs = GCodeServiceClient()
    data = {}

    # Memory
    with open('/proc/meminfo', 'r') as f:
        meminfo = f.read().split()
        data['mem_total'] = int(meminfo[1])
        data['mem_free'] = int(meminfo[4])
        data['mem_used_percentage'] = int(
            (data['mem_free'] * 100.0) / data['mem_total'])

    # Board Temperature
    with open('/sys/class/thermal/thermal_zone0/temp', 'r') as f:
        tmp = float(f.read())
        data['temp'] = tmp / 1000.0

    # Uptime
    with open('/proc/uptime', 'r') as f:
        tmp = f.read().split()
        data['time_alive'] = round(float(tmp[0]))

    # BCM2709 RPi2/RPi3
    # BCM2708 RPi1
    # Raspberry Pi version
    #soc_id = shell_exec('</proc/cpuinfo grep Hardware | awk \'{print $3}\'')[0].strip()
    #name_id = ''
    #soc_name = {'BCM2708' : 'Raspberry Pi Model B', 'BCM2709' : 'Raspberry Pi 3 Model B' }
    #if soc_id in soc_name:
    #    data['rpi_version'] = soc_name[soc_id]
    #else:
    #    data['rpi_version'] = soc_id

    data['rpi_version'] = rpi_version()

    # Storage
    tmp = shell_exec('df -Ph')
    table_header = tmp[0].split()[:-1]
    table_rows = []
    visible_partitions = [
        '/tmp', '/mnt/bigtemp', '/mnt/userdata', '/mnt/live/mnt/changes',
        '/mnt/live/mnt/bundles', '/mnt/live/mnt/boot'
    ]
    for row in tmp[1:]:
        tmp2 = row.split()
        if tmp2[5] in visible_partitions:
            table_rows.append(" ".join(tmp2))

    data['table_header'] = table_header
    data['table_rows'] = table_rows

    # OS Info
    data['os_info'] = shell_exec('uname -a')[0].strip()

    # Fabtotum info
    #reply = gcs.send('M765')
    #fw = reply[0].split()[1]

    #reply = gcs.send('M763')
    #hw = reply[0]
    #data['fabtotum_info'] = {'fw':fw, 'hw':hw}

    # FABUI version
    ##db = Database(config)
    #fabui_version = SysConfig(db)
    #fabui_version.query_by('key', 'fabui_version')
    #data['fabui_version'] = fabui_version['text']

    data['unit_configs'] = config.settings

    network_json_info = open(config.get('general', 'network_info_file'))
    network_info = json.load(network_json_info)

    data['eth_bytes'] = get_rx_tx_bytes('eth0')

    if 'wlan0' in network_info['interfaces']:
        data['wlan_bytes'] = get_rx_tx_bytes('wlan0')

    print json.dumps(data)
Example #15
0
#!/bin/env python
# -*- coding: utf-8; -*-

#from gcodeclient import GCodeServicePyroClient
from fabtotum.utils.pyro.gcodeclient import GCodeServiceClient

def callback_function(action, data):
    print "Callback working:", action
    if action == 'file_done':
        gcs.stop()
    else:
        print action, data
        

gcs = GCodeServiceClient()

#print gcs.send('M732')
#print gcs.send('M734')
#print gcs.send('M730')

print gcs.send('M756 E102')




#~ reply = gcs.send('M119')
#~ print reply

#~ data = gcs.send('M503')
#data = "".join(data, "\n")
#z_probe_old = float(data.split("Z Probe Length: ")[1].split("\n")[0])
Example #16
0
    def __init__(self,
                 log_trace=None,
                 monitor_file=None,
                 gcs=None,
                 config=None,
                 use_callback=True,
                 use_stdout=False,
                 update_period=2,
                 lang='en_US.UTF-8',
                 send_email=False,
                 auto_shutdown=False):

        self.monitor_lock = RLock()

        self.UPDATE_PERIOD = update_period
        self.gcode_info = None
        self.lang = lang
        _ = setLanguage(self.lang)
        self.use_stdout = use_stdout
        # Task specific attributes
        self.task_stats = {
            'type': 'unknown',
            'controller': 'unknown',
            'id': 0,
            'pid': os.getpid(),
            'status': GCodePusher.TASK_PREPARING,
            'started_time': time.time(),
            'estimated_time': 0,
            'completed_time': 0,
            'duration': 0,
            'percent': 0.0,
            'auto_shutdown': auto_shutdown,
            'send_email': send_email,
            'message': ''
        }

        # Pusher/File specific attributes
        self.pusher_stats = {
            'file': {
                'full_path': '',
                'name': '',
            },
            'line_total': 0,
            'line_current': 0,
            'type': GCodeInfo.RAW,
            'first_move': False
        }

        # Pusher/File specific attributes
        self.override_stats = {
            'z_override': 0.0,
            'fan': 0,
            'rpm': 0,
            'laser': 0,
            'flow_rate': 0,
            'speed': 0
        }

        self.standardized_stats = {
            'task': self.task_stats,
            'gpusher': self.pusher_stats,
            'override': self.override_stats
        }

        if not config:
            self.config = ConfigService()
        else:
            self.config = config

        if not gcs:
            self.gcs = GCodeServiceClient()
        else:
            self.gcs = gcs

        if not monitor_file:
            monitor_file = self.config.get('general', 'task_monitor')

        if not log_trace:
            log_trace = self.config.get('general', 'trace')

        self.monitor_file = monitor_file
        self.trace_file = log_trace

        self.trace_logger = logging.getLogger('Trace')
        self.trace_logger.setLevel(logging.INFO)

        ch = logging.FileHandler(log_trace)
        formatter = logging.Formatter("%(message)s")
        ch.setFormatter(formatter)
        ch.setLevel(logging.INFO)
        self.trace_logger.addHandler(ch)

        self.temperatres_file = self.config.get('general', 'temperature')

        if use_callback:
            self.gcs.register_callback(self.callback_handler)

        self.gmacro = GMacroHandler(self.gcs,
                                    self.config,
                                    self.trace,
                                    self.resetTrace,
                                    lang=self.lang)

        self.progress_monitor = None
        self.db = Database(self.config)
Example #17
0
def test_case():
    from fabtotum.utils.pyro.gcodeclient import GCodeServiceClient
    from fabtotum.fabui.config import ConfigService
    from selftests_common import getEndstopValues, getFrontPanelStatus

    # Pyro GCodeService wrapper
    gcs = GCodeServiceClient()
    config = ConfigService()

    try:
        safety_door = config.get('settings', 'safety.door')
    except KeyError:
        safety_door = 0

    try:
        switch = config.get('settings', 'switch')
    except KeyError:
        switch = 0

    try:
        bed_enabled = config.get('settings', 'hardware')['bed']['enable']
    except KeyError:
        bed_enabled = True

    ####################################################################

    # Success
    RETR = 0

    #~ print "Checking safety measures..."
    if safety_door == 1:
        if not getFrontPanelStatus(gcs):
            print _(
                "! Front panel door is opened, please close it or disable door safety."
            )
            gcs.send('M18')
            exit(1)

    gcs.send('G27')

    for i in [5, 7, 8, 10, 15, 18]:
        speed = int(1000 * i)
        print 'Speed F = {0} mm/sec'.format(round(speed / 60.0, 2))
        gcs.send('G90')
        # Long streight movements
        gcs.send('G0 X3 Y3 F{0}'.format(speed))
        gcs.send('G0 X3 Y232 F{0}'.format(speed))
        gcs.send('G0 X212 Y232 F{0}'.format(speed))
        gcs.send('G0 X212 Y3 F{0}'.format(speed))
        gcs.send('G0 X3 Y3 F{0}'.format(speed))

        # Long diagonal movements
        gcs.send('G0 X212 Y212 F{0}'.format(speed))
        gcs.send('G0 X3 Y212 F{0}'.format(speed))
        gcs.send('G0 X212 Y3 F{0}'.format(speed))
        gcs.send('G0 X3 Y3 F{0}'.format(speed))

        # Short zig-zag (square) movements
        gcs.send('G0 X50 Y3 F{0}'.format(speed))
        gcs.send('G0 X50  Y20 F{0}'.format(speed))
        gcs.send('G0 X100 Y20 F{0}'.format(speed))
        gcs.send('G0 X100 Y40 F{0}'.format(speed))
        gcs.send('G0 X50  Y40 F{0}'.format(speed))
        gcs.send('G0 X50  Y60 F{0}'.format(speed))
        gcs.send('G0 X100 Y60 F{0}'.format(speed))
        gcs.send('G0 X100 Y80 F{0}'.format(speed))
        gcs.send('G0 X50  Y80 F{0}'.format(speed))
        gcs.send('G0 X50  Y100 F{0}'.format(speed))
        gcs.send('G0 X100 Y100 F{0}'.format(speed))
        gcs.send('G0 X100 Y120 F{0}'.format(speed))
        gcs.send('G0 X50  Y120 F{0}'.format(speed))
        gcs.send('G0 X50  Y140 F{0}'.format(speed))
        gcs.send('G0 X100 Y140 F{0}'.format(speed))
        gcs.send('G0 X100 Y160 F{0}'.format(speed))
        gcs.send('G0 X50  Y160 F{0}'.format(speed))
        gcs.send('G0 X50  Y180 F{0}'.format(speed))
        gcs.send('G0 X100 Y180 F{0}'.format(speed))
        gcs.send('G0 X100 Y200 F{0}'.format(speed))

        gcs.send('G0 X3 Y3 F{0}'.format(speed))

    # Result
    exit(RETR)