def process_done(): data = p.stdout.read() if data: log('Data from completion callback: %s' % repr(data)) return True log('Callback for job completion done; return: %s' % repr(p.wait())) return False
def debug(priority, message): if priority <= config['loglimit']: log(message, depth=1) if logfile: logfile.write('{}: ({}) {}\n'.format( time.strftime('%c', time.gmtime()), priority, message)) logfile.flush()
def _call(self, name, a, ka): # {{{ wake = (yield) #log('other: %s %s %s' % (name, repr(a), repr(ka))) if 'machine' in ka: machine = ka.pop('machine') else: machine = None if machine not in machines: if len(machines) == 1: machine = tuple(machines.keys())[0] else: options = [m for m in machines if machines[m].port is not None] if len(options) == 1: machine = options[0] else: log('No active machine') return ('error', 'No active machine') if name.endswith('_POST'): log('refusing to call function only meant for POST') return ('error', 'Invalid function name') def reply(success, ret): if success: wake(ret) else: log('machine errors') wake(('error', ret)) #disable(machine, 'machine replied with error to wake up') machines[machine].call(name, (self.socket.data['role'],) + tuple(a), ka, reply) return (yield)
def boot_printer_error(): log('error during printer detection on port %s.' % port) websocketd.remove_timeout(timeout_handle[0]) printer.close() ports[port] = None broadcast(None, 'port_state', port, 0) return False
def print_done(port, completed, reason): # {{{ broadcast(None, 'printing', port.port, False) 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(): 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 def process_error(): log('Print completion process returned error.') return False websocketd.add_read(p.stdout, process_done, process_error)
def boot_machine_error(): log('error during machine detection on port %s.' % port) websocketd.remove_timeout(timeout_handle[0]) machine.close() ports[port] = None broadcast(None, 'port_state', port, 0) return False
def disable(role, port): # {{{ if port not in ports: log('not disabling nonexistent port %s' % port) return p = ports[port] if p is None: log('not disabling inactive port %s' % port) return if not isinstance(p, Port): # Detecting or similar; cancel operation. p() return if p.detecting: p.die() return # Forget the printer. First tell the printer to die p.die() ports[port] = None if p: def done(success, ret): websocketd.remove_read(p.input_handle) try: p.process.kill() except OSError: pass try: p.process.communicate() except: pass p.process = None p.call('die', (role, 'disabled by user',), {}, done) if p not in (None, False): broadcast(None, 'del_printer', port) broadcast(None, 'port_state', port, 0)
def post(self, connection): # Add to queue (POST). if 'file' not in connection.post[1] or 'port' not in connection.post[0] or 'action' not in connection.post[0]: self.reply(connection, 400) return False port = connection.post[0]['port'][0] action = connection.post[0]['action'][0] if port not in ports or not ports[port]: log('port not found: %s' % port) self.reply(connection, 404) return False post = connection.post[1].pop('file') def cb(success, ret): self.reply(connection, 200 if success else 400, '' if ret is None else ret.encode('utf8'), 'text/plain;charset=utf8') os.unlink(post[0]); connection.socket.close() if action == 'queue_add': ports[port].call('queue_add_file', [connection.data['role'], post[0], post[1]], {}, cb) elif action == 'audio_add': ports[port].call('audio_add_file', [connection.data['role'], post[0], post[1]], {}, cb) elif action == 'import': ports[port].call('import_file', [connection.data['role'], post[0], post[1]], {}, cb) else: os.unlink(post[0]); self.reply(connection, 400) return False return True
def _call(self, name, a, ka): # {{{ wake = (yield) #log('other: %s %s %s' % (name, repr(a), repr(ka))) if 'machine' in ka: machine = ka.pop('machine') else: machine = None if machine not in machines: if len(machines) == 1: machine = tuple(machines.keys())[0] else: options = [m for m in machines if machines[m].port is not None] if len(options) == 1: machine = options[0] else: log('No active machine') return ('error', 'No active machine') if name.endswith('_POST'): log('refusing to call function only meant for POST') return ('error', 'Invalid function name') def reply(success, ret): if success: wake(ret) else: log('machine errors') wake(('error', ret)) #disable(machine, 'machine replied with error to wake up') machines[machine].call(name, (self.socket.data['role'], ) + tuple(a), ka, reply) return (yield)
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 finish(): log('finish detect %s' % repr(uuid)) ports[port] = uuid machines[uuid] = new_machine log('connecting new machine %s to port %s' % (uuid, port)) new_machine.call('connect', ['admin', port, [chr(x) for x in run_id]], {}, lambda success, ret: None)
def remove_port(port): # {{{ log('removing port %s' % port) if port not in ports: return if ports[port]: ports[port].make_orphan() del ports[port] broadcast(None, 'del_port', port)
def remove_port(port): # {{{ log('removing port %s' % port) if port not in ports: return if ports[port]: disable(ports[port], 'port is removed') del ports[port] broadcast(None, 'del_port', port)
def finish(success, uuid): assert success ports[port] = uuid printers[uuid] = new_printer log('connecting new printer %s to port %s' % (uuid, port)) new_printer.call('connect', ['admin', port, [chr(x) for x in run_id]], {}, lambda success, ret: None)
def remove_port(port): # {{{ log('removing port %s' % port) if port not in ports: return if ports[port]: machines[ports[port]].disconnect() del ports[port] broadcast(None, 'del_port', port)
def remove_port(cls, port): # {{{ log('removing port %s' % port) if port not in ports: return if ports[port]: # Close serial port, in case it still exists. cls._disable('admin', port) del ports[port] cls._broadcast(None, 'del_port', port)
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 die(self, reason = 'at request'): # {{{ log('{} died {}.'.format(self.name, reason)) self.process.kill() try: self.process.communicate() except: pass for t in range(3): for w in self.waiters[t]: self.waiters[t][w](False, 'Printer {} died {}'.format(self.name, reason))
def make_orphan(self): # {{{ if ports[self.port]: self.call('disconnect', ['admin'], {}, lambda cd, arg: 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 broadcast(None, 'del_printer', self.port)
def post(self, connection): # Add to queue (POST). if 'file' not in connection.post[1] or 'machine' not in connection.post[ 0] or 'action' not in connection.post[0]: log('invalid post: {}'.format(connection.post)) self.reply(connection, 400) return False machine = connection.post[0]['machine'][0] action = connection.post[0]['action'][0] if machine not in machines or not isinstance(machines[machine], Machine): log('machine not found: %s' % machine) self.reply(connection, 404) return False # Count files, so we know when the connection should be closed. # Use a list to make it accessible from the callback. num = [len(connection.post[1]['file'])] for post in connection.post[1].pop('file'): def cb(success, ret, filename): self.reply(connection, 200 if success else 400, b'' if ret is None else ret.encode('utf-8'), 'text/plain;charset=utf-8') os.unlink(filename) num[0] -= 1 if num[0] == 0: connection.socket.close() def cbwrap(filename): '''This function makes sure that filename gets its own scope and is not changed by the for loop.''' return lambda success, ret: cb(success, ret, filename) if action == 'queue_add': machines[machine].call( 'queue_add_POST', [connection.data['role'], post[0], post[1]], {}, cbwrap(post[0])) elif action == 'probe_add': machines[machine].call( 'probe_add_POST', [connection.data['role'], post[0], post[1]], {}, cbwrap(post[0])) elif action == 'audio_add': machines[machine].call( 'audio_add_POST', [connection.data['role'], post[0], post[1]], {}, cbwrap(post[0])) elif action == 'import': machines[machine].call( 'import_POST', [connection.data['role'], post[0], post[1]], {}, cbwrap(post[0])) else: cb(false, 'invalid POST action', post[0]) 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 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 add_port(port): # {{{ 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 broadcast(None, 'new_port', port) broadcast(None, 'port_state', port, 0) if autodetect: detect(port, 'admin')
def add_port(cls, port): # {{{ wake = (yield) 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: return (yield from cls._generator_call(wake, cls.detect, port))
def get_vars(success, vars): if not success: log('failed to get vars') return # The child has opened the port now; close our handle. if detectport is not None: log('Driver started; closing server port') detectport.close() self.uuid = vars['uuid'] # Copy settings from orphan with the same run_id, then kill the orphan. if self.run_id in orphans and orphans[self.run_id].uuid == self.uuid: orphans[self.run_id].call('export_settings', ('admin',), {}, get_settings)
def printer_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. log('%s died.' % self.name) try: self.process.communicate() # Clean up the zombie. except: pass for t in range(3): for w in self.waiters[t]: self.waiters[t][w](False, 'Printer died') disable('admin', self.port) return False self.buffer += data #log('printer 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('printer command input:' + repr(data)) if data[1] == 'broadcast': 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 broadcast(None, 'port_state', port, 0) self.make_orphan() if autodetect: detect(self.port, 'admin') 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))
def get_vars(success, vars, cb = None): if not success: log('failed to get vars') return if self.uuid is None: log('new uuid:' + repr(vars['uuid'])) self.uuid = vars['uuid'] else: assert self.uuid == vars['uuid'] self.detecting = False self.call('send_machine', ['admin', None], {}, lambda success, data: broadcast(None, 'port_state', port, 2)) if cb is not None: cb()
def add_port(cls, port): # {{{ wake = (yield) 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: return (yield from cls._generator_call(wake, cls.detect, port))
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 get_settings(success, settings): if not success: log('failed to get settings') return self.call('import_settings', ['admin', settings], {}, lambda success, ret: None) GLib.source_remove(orphans[self.run_id].input_handle) orphans[self.run_id].call('die', ('admin', 'replaced by new connection',), {}, lambda success, ret: None) try: orphans[self.run_id].process.kill() orphans[self.run_id].process.communicate() except OSError: pass del orphans[self.run_id]
def disable(uuid, reason): # {{{ if uuid not in machines: log('not disabling nonexistent machine %s' % uuid) return p = machines[uuid] if p.port not in ports: log("not disabling machine which isn't enabled") return p.call('disconnect', ('admin', reason), {}, lambda success, ret: None) port = p.port ports[port] = None p.port = None broadcast(None, 'port_state', port, 0)
def call(self, name, args, kargs, cb): # {{{ data = json.dumps([self.next_mid, name, args, kargs]) + '\n' #log('calling %s on %d' % (repr(data), self.process.stdin.fileno())) try: self.process.stdin.write(data) except: log('killing printer handle because of IOError') #traceback.print_exc() cb(False, None) Connection._disable('admin', self.port) return self.waiters[0][self.next_mid] = cb self.next_mid += 1
def load(name, group): # {{{ # Set default user data; can be replaced (or not returned) below. ret = default_user(group, name) if not os.path.exists(os.path.join(config['data'], 'users', group.lower())): debug(0, 'user.load called for nonexistent group {}:{}'.format(name, group)) return None if not os.path.exists(os.path.join(config['data'], 'users', group.lower(), name.lower())): if os.path.exists(os.path.join(config['data'], 'users', group.lower(), 'Open')): # Create new user. save(ret) else: debug(0, 'user.load called for nonexistent user {}:{}'.format(name, group)) return None if (name.lower(), group.lower()) in users: ret = users[(name.lower(), group.lower())] for ln in open(os.path.join(config['data'], 'users', group.lower(), name.lower()), errors = 'replace'): if ln.strip() == '': continue try: key, value = ln.strip().split('=', 1) except ValueError: log('Failed to parse line from user config for %s:%s: %s' % (name, group, ln.strip())) continue if key in unsaved: # This key should not have been in the file. continue if key in ('nosave', 'sandbox'): ret[key] = value == 'True' continue if key.startswith('answer:'): try: a, c, s, q = key.split(':', 3) except: log('Failed to parse answer key {}; ignoring'.format(key)) continue if (c, s) not in ret['answers']: ret['answers'][(c, s)] = {} try: ret['answers'][(c, s)][q] = [json.loads(a) for a in value.split(';')] assert all('raw' in x for x in ret['answers'][(c, s)][q]) assert all('style' in x for x in ret['answers'][(c, s)][q]) except: ret['answers'][(c, s)][q] = [{'raw': a, 'style': []} for a in value.split(';')] continue ret[key] = value.rstrip('\n') # Make sure name and group match file location. if ret['filename'] != name.lower(): ret['filename'] = name.lower() if ret['group'].casefold() != group.casefold(): ret['group'] = group return ret
def printer_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. log('%s died.' % self.name) try: self.process.communicate() # Clean up the zombie. except: pass for t in range(3): for w in self.waiters[t]: self.waiters[t][w](False, 'Printer died') disable('admin', self.port) return False self.buffer += data #log('printer 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('printer command input:' + repr(data)) if data[1] == 'broadcast': 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 broadcast(None, 'port_state', port, 0) self.make_orphan() if autodetect: detect(self.port, 'admin') 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))
def timeout(): id[1] += 1 if id[1] >= 30: # Timeout. Give up. GLib.source_remove(watcher) printer.close() log('Timeout waiting for printer on port %s; giving up.' % port) ports[port] = None return False if not id[2]: printer.write(protocol.single['ID']) else: id[2] = False return True
def print_done(port, completed, reason): # {{{ Connection._broadcast(None, 'printing', port.port, False) 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)
def read(self, line): # {{{ if line.strip() == '': return try: action, dev = line.split(None, 1) except: log('invalid command on admin socket: %s' % line) return if action == 'add': add_port(dev.strip()) elif action == 'remove': remove_port(dev.strip()) else: log('invalid action on admin socket: %s' % line)
def call(self, name, args, kargs, cb): # {{{ data = json.dumps([self.next_mid, name, args, kargs]) + '\n' #log('calling %s on %d' % (repr(data), self.process.stdin.fileno())) try: self.process.stdin.write(data.encode('utf-8')) self.process.stdin.flush() except: log('killing printer handle because of IOError') #traceback.print_exc() cb(False, None) Connection._disable('admin', self.port) return self.waiters[0][self.next_mid] = cb self.next_mid += 1
def make_orphan(self): # {{{ if ports[self.port]: self.call('disconnect', ['admin'], {}, lambda cd, arg: 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 broadcast(None, 'del_printer', self.port)
def get_vars(success, vars): if not success: log('failed to get vars') return # The child has opened the port now; close our handle. if detectport is not None: log('Driver started; closing server port') detectport.close() self.uuid = vars['uuid'] # Copy settings from orphan with the same run_id, then kill the orphan. if self.run_id in orphans and orphans[ self.run_id].uuid == self.uuid: orphans[self.run_id].call('export_settings', ('admin', ), {}, get_settings)
def die(self, reason='at request'): # {{{ log('{} died {}.'.format(self.name, reason)) try: self.process.kill() except: pass try: self.process.communicate() except: pass for t in range(3): for w in self.waiters[t]: self.waiters[t][w](False, 'Printer {} died {}'.format( self.name, reason))
def boot_printer_input(): id[2] = True ids = [protocol.single[code][0] for code in ('ID', 'STARTUP')] # CMD:1 ID:8 Checksum:3 while len(id[0]) < 12: try: data = printer.read(12 - len(id[0])) except OSError: continue except IOError: continue id[0] += data #log('incomplete id: ' + id[0]) if len(id[0]) < 12: return True if id[0][0] not in ids or not protocol.check(id[0]): log('skip non-id: %s (%s)' % (''.join('%02x' % x for x in id[0]), repr(id[0]))) f = len(id[0]) for start in ids: if start in id[0][1:]: p = id[0].index(bytes((start,))) if p < f: f = p log('Keeping some') if f == 0: f = 1 id[0] = id[0][f:] return True # We have something to handle; cancel the timeout, but keep the serial port open to avoid a reset. (I don't think this even works, but it doesn't hurt.) websocketd.remove_timeout(timeout_handle[0]) # This printer was running and tried to send an id. Check the id. id[0] = id[0][1:9] if id[0] in orphans: log('accepting orphan %s on %s' % (''.join('%02x' % x for x in id[0]), port)) ports[port] = orphans.pop(id[0]) ports[port].port = port def close_port(success, data): log('reconnect complete; closing server port') printer.close() log('reconnecting %s' % port) ports[port].call('reconnect', ['admin', port], {}, lambda success, ret: ports[port].call('send_printer', ['admin', None], {}, close_port) if success else close_port) broadcast(None, 'port_state', port, 2) return False run_id = nextid() log('accepting unknown printer on port %s (id %s)' % (port, ''.join('%02x' % x for x in run_id))) #log('orphans: %s' % repr(tuple(orphans.keys()))) process = subprocess.Popen((fhs.read_data('driver.py', opened = False), '--cdriver', fhs.read_data('franklin-cdriver', opened = False), '--port', port, '--run-id', run_id, '--allow-system', config['allow-system']) + (('--system',) if fhs.is_system else ()), stdin = subprocess.PIPE, stdout = subprocess.PIPE, close_fds = True) ports[port] = Port(port, process, printer, run_id) return False
def read_boards(): # {{{ boards = {} for d in fhs.read_data('hardware', packagename='arduino', dir=True, multiple=True): for board in os.listdir(d): boards_txt = os.path.join(d, board, 'boards' + os.extsep + 'txt') if not os.path.exists(boards_txt): continue with open(boards_txt) as b: for line in b: if line.startswith('#') or line.strip() == '': continue parse = re.match('([^.=]+)\.([^=]+)=(.*)$', line.strip()) if parse is None: log('Warning: invalid line in %s: %s' % (boards_txt, line.strip())) continue tag, option, value = parse.groups() if tag not in boards: boards[tag] = {} if option in boards[tag]: if boards[tag][option] != value: log('%s: duplicate tag %s.%s with different value (%s != %s); using %s' % (boards_txt, tag, option, value, boards[tag][option], boards[tag][option])) continue boards[tag][option] = value for tag in tuple(boards.keys()): if 'name' not in boards[tag]: boards[tag]['name'] = tag if any(x not in boards[tag] for x in ('upload.protocol', 'upload.speed', 'build.mcu', 'upload.maximum_size')): #log('skipping %s because hardware information is incomplete (%s)' % (boards[tag]['name'], repr(boards[tag]))) del boards[tag] continue if int(boards[tag]['upload.maximum_size']) < 30000: # Not enough memory; don't complain about skipping this board. del boards[tag] continue if fhs.read_data(os.path.join( 'firmware', boards[tag]['build.mcu'] + os.extsep + 'hex'), opened=False) is None: #log('skipping %s because firmware for %s is not installed' % (boards[tag]['name'], boards[tag]['build.mcu'])) del boards[tag] continue return boards
def close(self): if self.game.Public.name not in instances: # Already closed. return log("stopped instance '%s'" % self.game.Public.name) del instances[self.game.Public.name] for c in connections: if connections[c].instance != self: continue leave({'connection': connections[c]}) self.game.Public._die() for p in self.game.players: p.Private._die() if self is not title_game: title_game.game.Public.games.remove(self.game.Public.name)
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 timeout(): id[1] += 1 if id[1] >= 30: # Timeout. Give up. websocketd.remove_read(watcher) machine.close() log('Timeout waiting for machine on port %s; giving up.' % port) ports[port] = None broadcast(None, 'port_state', port, 0) return if not id[2]: machine.write(protocol.single['ID']) else: id[2] = False timeout_handle[0] = websocketd.add_timeout(time.time() + .5, timeout)
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
def get_vars(success, vars): if not success: log('failed to get vars') return # The child has opened the port now; close our handle. if detectport is not None: log('Driver started; closing server port') detectport.close() self.uuid = vars['uuid'] # Copy settings from orphan with the same run_id, then kill the orphan. if self.run_id in orphans and orphans[self.run_id].uuid == self.uuid: orphans[self.run_id].call('export_settings', ('admin',), {}, get_settings) else: self.detecting = False self.call('send_printer', ['admin', None], {}, lambda success, data: broadcast(None, 'port_state', port, 2))