def upload(self, port, board): # {{{ wake = (yield) assert self.socket.data['role'] in ('benjamin', 'admin') assert port in ports if ports[port]: disable(ports[port], 'disabled for upload') def cancel(): # Waking the generator kills the process. wake('Aborted') ports[port] = cancel command = self._get_command(board, port) data = [''] log('Flashing firmware: ' + ' '.join(command)) broadcast(None, 'port_state', port, 3) process = subprocess.Popen(command, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.STDOUT, close_fds = True) def output(): d = '' try: d = process.stdout.read().decode('utf-8') except: data[0] += '\nError writing %s firmware: ' % board + traceback.format_exc() log(repr(data[0])) wake(data[0]) return False if d != '': #broadcast(None, 'message', port, '\n'.join(data[0].split('\n')[-4:])) data[0] += d return True wake(data[0]) return False def error(): data[0] += '\nError writing %s firmware: ' % board log(repr(data[0])) wake(data[0]) return False fl = fcntl.fcntl(process.stdout.fileno(), fcntl.F_GETFL) fcntl.fcntl(process.stdout.fileno(), fcntl.F_SETFL, fl | os.O_NONBLOCK) websocketd.add_read(process.stdout, output, error) broadcast(None, 'uploading', port, 'uploading firmware for %s' % board) #broadcast(None, 'message', port, '') d = (yield) try: process.kill() # In case it wasn't dead yet. except: pass try: process.communicate() # Clean up. except: pass broadcast(None, 'uploading', port, None) #broadcast(None, 'message', port, '') broadcast(None, 'port_state', port, 0) ports[port] = None if autodetect: websocketd.call(None, detect, port) if d: return 'firmware upload for %s: ' % board + d else: return 'firmware for %s successfully uploaded' % board
def upload(self, port, board): # {{{ assert self.socket.data['role'] in ('benjamin', 'admin') assert ports[port] is None wake = (yield) command = self._get_command(board, port) data = [''] log('Flashing firmware: ' + ' '.join(command)) process = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True) def output(fd, cond): d = '' try: d = process.stdout.read().decode('utf-8') except: data[ 0] += '\nError writing %s firmware: ' % board + traceback.format_exc( ) log(repr(data[0])) wake(data[0]) return False if d != '': self._broadcast(None, 'message', port, '\n'.join(data[0].split('\n')[-4:])) data[0] += d return True wake(data[0]) return False fl = fcntl.fcntl(process.stdout.fileno(), fcntl.F_GETFL) fcntl.fcntl(process.stdout.fileno(), fcntl.F_SETFL, fl | os.O_NONBLOCK) GLib.io_add_watch(process.stdout, GLib.IO_IN | GLib.IO_PRI | GLib.IO_HUP, output) self._broadcast(None, 'blocked', port, 'uploading firmware for %s' % board) self._broadcast(None, 'message', port, '') d = (yield) try: process.kill() # In case it wasn't dead yet. except OSError: pass process.communicate() # Clean up. self._broadcast(None, 'blocked', port, None) self._broadcast(None, 'message', port, '') if autodetect: websocketd.call(None, self.detect, port) if d: return 'firmware upload for %s: ' % board + d else: return 'firmware for %s successfully uploaded' % board
def add_port(port): # {{{ if port in ports: log('already existing port %s cannot be added' % port) return if not re.match(config['whitelist'], port) or re.match(config['blacklist'], port) or re.match(config['add-blacklist'], port): #log('skipping blacklisted or non-whitelisted port %s' % port) return ports[port] = None broadcast(None, 'new_port', port, upload_options(port)) broadcast(None, 'port_state', port, 0) if autodetect: websocketd.call(None, detect, port)
def detect_all(cls): # {{{ resumeinfo = [(yield), None] for p in ports: if ports[p] is not None: continue c = websocketd.call(resumeinfo, cls.detect, p) while c(): c.args = (yield websocketd.WAIT)
def printer_input(self, fd, cond): # {{{ line = self.process.stdout.readline() if line == b'': log('%s died.' % self.name) self.process.communicate() # Clean up the zombie. for t in range(3): for w in self.waiters[t]: self.waiters[t][w](False, 'Printer died') Connection._disable('admin', self.port) return False data = json.loads(line.decode('utf-8')) #log('printer input:' + repr(data)) if data[1] == 'broadcast': Connection._broadcast(data[2], data[3], self.port, *(data[4:])) elif data[1] == 'disconnect': # Don't remember a printer that hasn't sent its name yet. port = self.port ports[self.port] = None # If there already is an orphan with the same uuid, kill the old orphan. for o in [x for x in orphans if orphans[x].uuid == self.uuid]: # This for loop always runs 0 or 1 times, never more. log('killing duplicate orphan') orphans[o].call('die', ( 'admin', 'replaced by connection with same uuid', ), {}, lambda success, ret: None) del orphans[o] orphans[self.run_id] = self Connection._broadcast(None, 'del_printer', port) if autodetect: websocketd.call(None, Connection.detect, self.port) elif data[1] == 'error': if data[0] is None: # Error on command without id. log('error on command without id: %s' % repr(data)) else: self.waiters[0].pop(data[0])(False, data[2]) elif data[1] == 'return': self.waiters[0].pop(data[0])(True, data[2]) elif data[1] == 'movecb': self.waiters[1].pop(data[0])(True, data[2]) elif data[1] == 'tempcb': self.waiters[2].pop(data[0])(True, data[2]) else: raise AssertionError('invalid reply from printer process: %s' % repr(data)) return True
def upload(self, port, board): # {{{ assert self.socket.data['role'] in ('benjamin', 'admin') assert ports[port] is None resumeinfo = [(yield), None] sudo, brd, protocol, baudrate, mcu = self._get_info(board) self.disable(port) data = [''] filename = fhs.read_data(os.path.join('firmware', brd + '.hex'), opened = False) command = sudo + (config['avrdude'], '-q', '-q', '-c', protocol) + baudrate + ('-p', mcu, '-P', port, '-U', 'flash:w:' + filename + ':i') log('Flashing firmware: ' + ' '.join(command)) process = subprocess.Popen(command, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.STDOUT, close_fds = True) def output(fd, cond): d = '' try: d = process.stdout.read() except: data[0] += '\nError writing %s firmware: ' % board + traceback.format_exc() log(repr(data[0])) resumeinfo[0](data[0]) return False if d != '': self._broadcast(None, 'message', port, '\n'.join(data[0].split('\n')[-4:])) data[0] += d return True resumeinfo[0](data[0]) return False fl = fcntl.fcntl(process.stdout.fileno(), fcntl.F_GETFL) fcntl.fcntl(process.stdout.fileno(), fcntl.F_SETFL, fl | os.O_NONBLOCK) GLib.io_add_watch(process.stdout, GLib.IO_IN | GLib.IO_PRI | GLib.IO_HUP, output) self._broadcast(None, 'blocked', port, 'uploading firmware for %s' % board) self._broadcast(None, 'message', port, '') d = (yield websocketd.WAIT) try: process.kill() # In case it wasn't dead yet. except OSError: pass process.communicate() # Clean up. self._broadcast(None, 'blocked', port, None) self._broadcast(None, 'message', port, '') if autodetect: websocketd.call(None, self.detect, port)() if d: yield ('firmware upload for %s: ' % board + d) else: yield ('firmware for %s successfully uploaded' % board)
def detect(cls, port): # {{{ resumeinfo = [(yield), None] log('detecting printer on %s' % port) if port not in ports or ports[port] != None: log('port is not in detectable state') return ports[port] = False c = websocketd.call(resumeinfo, detect, port) while c(): c.args = (yield websocketd.WAIT)
def printer_input(self, fd, cond): # {{{ line = self.process.stdout.readline() if line == '': log('%s died.' % self.name) self.process.communicate() # Clean up the zombie. for t in range(3): for w in self.waiters[t]: self.waiters[t][w](False, 'Printer died') Connection._disable('admin', self.port) return False data = json.loads(line) #log('printer input:' + repr(data)) if data[1] == 'broadcast': Connection._broadcast(data[2], data[3], self.port, *(data[4:])) elif data[1] == 'disconnect': # Don't remember a printer that hasn't sent its name yet. port = self.port ports[self.port] = None # If there already is an orphan with the same uuid, kill the old orphan. for o in [x for x in orphans if orphans[x].uuid == self.uuid]: # This for loop always runs 0 or 1 times, never more. log('killing duplicate orphan') orphans[o].call('die', ('admin', 'replaced by connection with same uuid',), {}, lambda success, ret: None) del orphans[x] orphans[self.run_id] = self Connection._broadcast(None, 'del_printer', port) if autodetect: websocketd.call(None, Connection.detect, self.port)() elif data[1] == 'error': if data[0] is None: # Error on command without id. log('error on command without id: %s' % repr(data)) else: self.waiters[0].pop(data[0])(False, data[2]) elif data[1] == 'return': self.waiters[0].pop(data[0])(True, data[2]) elif data[1] == 'movecb': self.waiters[1].pop(data[0])(True, data[2]) elif data[1] == 'tempcb': self.waiters[2].pop(data[0])(True, data[2]) else: raise AssertionError('invalid reply from printer process: %s' % repr(data)) return True
def upload(self, port, board): # {{{ assert self.socket.data['role'] in ('benjamin', 'admin') assert ports[port] is None wake = (yield) command = self._get_command(board, port) data = [''] log('Flashing firmware: ' + ' '.join(command)) process = subprocess.Popen(command, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.STDOUT, close_fds = True) def output(fd, cond): d = '' try: d = process.stdout.read().decode('utf-8') except: data[0] += '\nError writing %s firmware: ' % board + traceback.format_exc() log(repr(data[0])) wake(data[0]) return False if d != '': self._broadcast(None, 'message', port, '\n'.join(data[0].split('\n')[-4:])) data[0] += d return True wake(data[0]) return False fl = fcntl.fcntl(process.stdout.fileno(), fcntl.F_GETFL) fcntl.fcntl(process.stdout.fileno(), fcntl.F_SETFL, fl | os.O_NONBLOCK) GLib.io_add_watch(process.stdout, GLib.IO_IN | GLib.IO_PRI | GLib.IO_HUP, output) self._broadcast(None, 'blocked', port, 'uploading firmware for %s' % board) self._broadcast(None, 'message', port, '') d = (yield) try: process.kill() # In case it wasn't dead yet. except OSError: pass process.communicate() # Clean up. self._broadcast(None, 'blocked', port, None) self._broadcast(None, 'message', port, '') if autodetect: websocketd.call(None, self.detect, port) if d: return 'firmware upload for %s: ' % board + d else: return 'firmware for %s successfully uploaded' % board
def machine_input(self): # {{{ while self.process is not None: data = self.process.stdout.read() if data is None: #log('%s: no data now' % self.name) # No more data. return True if data == b'': # Connection closed. self.die('because there was an error') return False self.buffer += data #log('machine input:' + repr(data)) while b'\n'[0] in self.buffer: pos = self.buffer.index(b'\n'[0]) line = self.buffer[:pos] self.buffer = self.buffer[pos + 1:] data = json.loads(line.decode('utf-8')) #log('machine command input:' + repr(data)) if data[1] == 'broadcast': broadcast(data[2], data[3], self.uuid, *(data[4:])) elif data[1] == 'disconnect': port = self.port ports[self.port] = None broadcast(None, 'port_state', port, 0) if autodetect: websocketd.call(None, detect, port) elif data[1] == 'error': if data[0] is None: # Error on command without id. log('error on command without id: %s' % repr(data)) else: self.waiters[0].pop(data[0])(False, data[2]) elif data[1] == 'return': self.waiters[0].pop(data[0])(True, data[2]) elif data[1] == 'movecb': self.waiters[1].pop(data[0])(True, data[2]) elif data[1] == 'tempcb': self.waiters[2].pop(data[0])(True, data[2]) else: raise AssertionError('invalid reply from machine process: %s' % repr(data))
def add_port(cls, port): # {{{ resumeinfo = [(yield), None] if port in ports: log('already existing port %s cannot be added' % port) return if re.match(config['blacklist'], port) or re.match(config['add-blacklist'], port): #log('skipping blacklisted port %s' % port) return ports[port] = None cls._broadcast(None, 'new_port', port); if autodetect: c = websocketd.call(resumeinfo, cls.detect, port) while c(): c.args = (yield websocketd.WAIT)
def process_done(fd, cond): data = p.stdout.read() if data: log('Data from completion callback: %s' % repr(data)) return True log('Callback for print completion done; return: %s' % repr(p.wait())) return False GLib.io_add_watch(p.stdout.fileno(), GLib.IO_IN, process_done) # }}} if config['local'] != '': websocketd.call(None, Connection.add_port, '-') # Detect serial ports. {{{ # Assume a GNU/Linux system; if you have something else, you need to come up with a way to iterate over all your serial ports and implement it here. Patches welcome, especially if they are platform-independent. try: # Try Linux sysfs. for tty in os.listdir('/sys/class/tty'): websocketd.call(None, Connection.add_port, '/dev/' + tty) except: # Try more generic approach. Don't use this by default, because it doesn't detect all ports on GNU/Linux. try: import serial.tools.list_ports for tty in serial.tools.list_ports.comports(): websocketd.call(None, Connection.add_port, tty[0]) except: traceback.print_exc()
if config['done']: cmd = config['done'] cmd = cmd.replace('[[STATE]]', 'completed' if completed else 'aborted').replace('[[REASON]]', reason) log('running %s' % cmd) p = subprocess.Popen(cmd, stdout = subprocess.PIPE, shell = True, close_fds = True) def process_done(fd, cond): data = p.stdout.read() if data: return True log('Flashing done; return: %s' % repr(p.wait())) return False GLib.io_add_watch(p.stdout.fileno(), GLib.IO_IN, process_done) # }}} if config['local'] != '': websocketd.call(None, Connection.add_port, '-')() # Assume a GNU/Linux system; if you have something else, you need to come up with a way to iterate over all your serial ports and implement it here. Patches welcome, especially if they are platform-independent. try: # Try Linux sysfs. for tty in os.listdir('/sys/class/tty'): websocketd.call(None, Connection.add_port, '/dev/' + tty)() except: # Try more generic approach. Don't use this by default, because it doesn't detect all ports on GNU/Linux. try: import serial.tools.list_ports for tty in serial.tools.list_ports.comports(): websocketd.call(None, Connection.add_port, tty[0])() except: traceback.print_exc() log('Not probing serial ports, because an error occurred: %s' % sys.exc_info()[1])