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 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 job_done(port, completed, reason): # {{{ broadcast(None, 'running', 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 job completion done; return: %s' % repr(p.wait())) return False def process_error(): log('Job completion process returned error.') return False websocketd.add_read(p.stdout, process_done, process_error)
def __init__(self, port, process, detectport, run_id, send=True): # {{{ '''Create a new Machine object. This can be called for several reasons: - At startup, every saved machine is started. In this case, port is None. - When a new machine with an unknown uuid is detected on a port. In this case, port, detectport and run_id are set. ''' if port is not None: self.detecting = True broadcast(None, 'port_state', port, 1) self.name = None self.uuid = None self.port = port self.run_id = run_id self.process = process self.waiters = ({}, {}, {}) self.next_mid = 0 self.buffer = b'' fl = fcntl.fcntl(process.stdout.fileno(), fcntl.F_GETFL) fcntl.fcntl(process.stdout.fileno(), fcntl.F_SETFL, fl | os.O_NONBLOCK) self.input_handle = websocketd.add_read(process.stdout, self.machine_input, self.machine_error) def get_vars(success, vars, cb=None): if not success: log('failed to get vars') if detectport is not None: log('Closing server port anyway') detectport.close() return # The child has opened the port now; close our handle. if detectport is not None: log('Driver started; closing server port') detectport.close() 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() if send: self.call('get_globals', ('admin', ), {}, get_vars) else: def finish(cb): self.call('get_globals', ('admin', ), {}, lambda success, vars: get_vars(success, vars, cb)) del self.finish self.finish = finish
def __init__(self, port, process, detectport, run_id): # {{{ self.detecting = True broadcast(None, 'port_state', port, 1) self.name = None self.uuid = None self.port = port self.run_id = run_id self.process = process self.waiters = ({}, {}, {}) self.next_mid = 0 self.buffer = b'' fl = fcntl.fcntl(process.stdout.fileno(), fcntl.F_GETFL) fcntl.fcntl(process.stdout.fileno(), fcntl.F_SETFL, fl | os.O_NONBLOCK) self.input_handle = websocketd.add_read(process.stdout, self.printer_input, self.printer_error) old = [] def get_settings(success, settings): if not success: log('failed to get settings') return self.call('import_settings', ['admin', settings], {}, lambda success, ret: None) websocketd.remove_read(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() try: orphans[self.run_id].process.communicate() except: pass except OSError: pass del orphans[self.run_id] self.detecting = False self.call('send_printer', ['admin', None], {}, lambda success, data: broadcast(None, 'port_state', port, 2)) 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)) self.call('get_globals', ('admin',), {}, get_vars)
def __init__(self, port, process, run_id, send = True): # {{{ '''Create a new Machine object. This can be called for several reasons: - At startup, every saved machine is started. In this case, port is None. - When a new machine with an unknown uuid is detected on a port. In this case, port and run_id are set. ''' if port is not None: self.detecting = True broadcast(None, 'port_state', port, 1) self.name = None self.uuid = None self.port = port self.run_id = run_id self.process = process self.waiters = ({}, {}, {}) self.next_mid = 0 self.buffer = b'' fl = fcntl.fcntl(process.stdout.fileno(), fcntl.F_GETFL) fcntl.fcntl(process.stdout.fileno(), fcntl.F_SETFL, fl | os.O_NONBLOCK) self.input_handle = websocketd.add_read(process.stdout, self.machine_input, self.machine_error) 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() if send: self.call('get_globals', ('admin',), {}, get_vars) else: def finish(cb): self.call('get_globals', ('admin',), {}, lambda success, vars: get_vars(success, vars, cb)) del self.finish self.finish = finish
def part2(): id[0] = b'' id[1] = 0 id[2] = False def timeout(): id[1] += 1 if id[1] >= 30: # Timeout. Give up. websocketd.remove_read(watcher) printer.close() log('Timeout waiting for printer on port %s; giving up.' % port) ports[port] = None broadcast(None, 'port_state', port, 0) return if not id[2]: printer.write(protocol.single['ID']) else: id[2] = False timeout_handle[0] = websocketd.add_timeout(time.time() + .5, timeout) 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 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 printer.write(protocol.single['ID']) timeout_handle = [websocketd.add_timeout(time.time() + .5, timeout)] watcher = websocketd.add_read(printer, boot_printer_input, boot_printer_error) def cancel(): websocketd.remove_timeout(timeout_handle[0]) websocketd.remove_read(watcher) printer.close() ports[port] = None ports[port] = cancel
def __init__(self, port, process, detectport, run_id): # {{{ self.detecting = True broadcast(None, 'port_state', port, 1) self.name = None self.uuid = None self.port = port self.run_id = run_id self.process = process self.waiters = ({}, {}, {}) self.next_mid = 0 self.buffer = b'' fl = fcntl.fcntl(process.stdout.fileno(), fcntl.F_GETFL) fcntl.fcntl(process.stdout.fileno(), fcntl.F_SETFL, fl | os.O_NONBLOCK) self.input_handle = websocketd.add_read(process.stdout, self.printer_input, self.printer_error) old = [] def get_settings(success, settings): if not success: log('failed to get settings') return self.call('import_settings', ['admin', settings], {}, lambda success, ret: None) websocketd.remove_read(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() except: pass try: orphans[self.run_id].process.communicate() except: pass del orphans[self.run_id] self.detecting = False self.call( 'send_printer', ['admin', None], {}, lambda success, data: broadcast(None, 'port_state', port, 2)) 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)) self.call('get_globals', ('admin', ), {}, get_vars)
def part2(): id[0] = b'' id[1] = 0 id[2] = 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 boot_machine_input(): id[2] = True ids = [protocol.single[code][0] for code in ('ID', 'STARTUP')] # CMD:1 ID:8 + 16 Checksum:9 Total: 34 while len(id[0]) < 34: try: data = machine.read(34 - len(id[0])) except OSError: continue except IOError: continue id[0] += data #log('incomplete id: ' + id[0]) if len(id[0]) < 34: if len(id[0]) > 0 and id[0][0] == protocol.single[ 'CONTROLLER'][0]: # This is a controller. Spawn the process, then cancel this detection. websocketd.remove_timeout(timeout_handle[0]) machine.close() ports[port] = None broadcast(None, 'port_state', port, 0) log('Starting controller driver on ' + port) env = os.environ.copy() env['PORT'] = port subprocess.Popen(config['controller'], env=env, shell=True) return False 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, )), 1) 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 machine was running and tried to send an id. Check the id. uuid = id[0][9:9 + 16] if (uuid[7] & 0xf0) != 0x40 or (uuid[9] & 0xc0) != 0x80: # Broken uuid; create a new one and set it. log('broken uuid: ' + repr(uuid)) uuid = None else: uuid = ''.join('%02x' % x for x in uuid[:16]) uuid = uuid[:8] + '-' + uuid[8:12] + '-' + uuid[ 12:16] + '-' + uuid[16:20] + '-' + uuid[20:32] id[0] = id[0][1:9] running_machine = [ p for p in machines if machines[p].run_id == id[0] ] assert len(running_machine) < 2 if len(running_machine) > 0: p = running_machine[0] assert p.uuid == uuid if p.port is not None: disable( p.uuid, 'disabled machine which was detected on different port' ) log('rediscovered machine %s on %s' % (''.join('%02x' % x for x in id[0]), port)) ports[port] = p.uuid p.port = port def close_port(success, data): log('reconnect complete; closing server port') machine.close() p.call( 'reconnect', ['admin', port], {}, lambda success, ret: (ports[port].call( 'send_machine', ['admin', None], {}, close_port) if success else close_port())) broadcast(None, 'port_state', port, 2) return False run_id = nextid() # Find uuid or create new Machine object. if uuid in machines: log('accepting known machine on port %s (uuid %s)' % (port, uuid)) machines[uuid].port = port ports[port] = uuid log('connecting %s to port %s' % (uuid, port)) machines[uuid].call('connect', ['admin', port, [chr(x) for x in run_id]], {}, lambda success, ret: None) else: log('accepting unknown machine on port %s' % port) # Close detect port so it doesn't interfere. machine.close() #log('machines: %s' % repr(tuple(machines.keys()))) process = subprocess.Popen( (fhs.read_data('driver.py', opened=False), '--uuid', uuid if uuid is not None else '', '--allow-system', config['allow-system']) + (('--system', ) if fhs.is_system else ()) + (('--arc', 'False') if not config['arc'] else ()), stdin=subprocess.PIPE, stdout=subprocess.PIPE, close_fds=True) new_machine = Machine(port, process, run_id, send=uuid is not None) 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) if uuid is None: def prefinish(success, uuid): assert success new_machine.uuid = uuid new_machine.finish(finish) new_machine.call('reset_uuid', ['admin'], {}, prefinish) else: finish() return False 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 machine.write(protocol.single['ID']) timeout_handle = [websocketd.add_timeout(time.time() + .5, timeout)] watcher = websocketd.add_read(machine, boot_machine_input, boot_machine_error) def cancel(): websocketd.remove_timeout(timeout_handle[0]) websocketd.remove_read(watcher) machine.close() ports[port] = None ports[port] = cancel
def part2(): id[0] = b'' id[1] = 0 id[2] = 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 boot_machine_input(): id[2] = True ids = [protocol.single[code][0] for code in ('ID', 'STARTUP')] # CMD:1 ID:8 + 16 Checksum:9 Total: 34 while len(id[0]) < 34: try: data = machine.read(34 - len(id[0])) except OSError: continue except IOError: continue id[0] += data #log('incomplete id: ' + id[0]) if len(id[0]) < 34: if len(id[0]) > 0 and id[0][0] == protocol.single['CONTROLLER'][0]: # This is a controller. Spawn the process, then cancel this detection. websocketd.remove_timeout(timeout_handle[0]) machine.close() ports[port] = None broadcast(None, 'port_state', port, 0) log('Starting controller driver on ' + port) env = os.environ.copy() env['PORT'] = port subprocess.Popen(config['controller'], env = env, shell = True) return False 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,)), 1) 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 machine was running and tried to send an id. Check the id. uuid = id[0][9:9 + 16] if (uuid[7] & 0xf0) != 0x40 or (uuid[9] & 0xc0) != 0x80: # Broken uuid; create a new one and set it. log('broken uuid: ' + repr(uuid)) uuid = None else: uuid = ''.join('%02x' % x for x in uuid[:16]) uuid = uuid[:8] + '-' + uuid[8:12] + '-' + uuid[12:16] + '-' + uuid[16:20] + '-' + uuid[20:32] id[0] = id[0][1:9] running_machine = [p for p in machines if machines[p].run_id == id[0]] assert len(running_machine) < 2 if len(running_machine) > 0: p = running_machine[0] assert p.uuid == uuid if p.port is not None: disable(p.uuid, 'disabled machine which was detected on different port') log('rediscovered machine %s on %s' % (''.join('%02x' % x for x in id[0]), port)) ports[port] = p.uuid p.port = port def close_port(success, data): log('reconnect complete; closing server port') machine.close() p.call('reconnect', ['admin', port], {}, lambda success, ret: (ports[port].call('send_machine', ['admin', None], {}, close_port) if success else close_port())) broadcast(None, 'port_state', port, 2) return False run_id = nextid() # Find uuid or create new Machine object. if uuid in machines: log('accepting known machine on port %s (uuid %s)' % (port, uuid)) machines[uuid].port = port ports[port] = uuid log('connecting %s to port %s' % (uuid, port)) machines[uuid].call('connect', ['admin', port, [chr(x) for x in run_id]], {}, lambda success, ret: None) else: log('accepting unknown machine on port %s' % port) # Close detect port so it doesn't interfere. machine.close() #log('machines: %s' % repr(tuple(machines.keys()))) process = subprocess.Popen((fhs.read_data('driver.py', opened = False), '--uuid', uuid if uuid is not None else '', '--allow-system', config['allow-system']) + (('--system',) if fhs.is_system else ()) + (('--arc', 'False') if not config['arc'] else ()), stdin = subprocess.PIPE, stdout = subprocess.PIPE, close_fds = True) new_machine = Machine(port, process, run_id, send = False) 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) if uuid is None: def prefinish(success, uuid): assert success new_machine.uuid = uuid new_machine.finish(finish) new_machine.call('reset_uuid', ['admin'], {}, prefinish) else: new_machine.finish(finish) return False 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 machine.write(protocol.single['ID']) timeout_handle = [websocketd.add_timeout(time.time() + .5, timeout)] watcher = websocketd.add_read(machine, boot_machine_input, boot_machine_error) def cancel(): websocketd.remove_timeout(timeout_handle[0]) websocketd.remove_read(watcher) machine.close() ports[port] = None ports[port] = cancel
def part2(): id[0] = b'' id[1] = 0 id[2] = False def timeout(): id[1] += 1 if id[1] >= 30: # Timeout. Give up. websocketd.remove_read(watcher) printer.close() log('Timeout waiting for printer on port %s; giving up.' % port) ports[port] = None broadcast(None, 'port_state', port, 0) return if not id[2]: printer.write(protocol.single['ID']) else: id[2] = False timeout_handle[0] = websocketd.add_timeout(time.time() + .5, timeout) 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 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 printer.write(protocol.single['ID']) timeout_handle = [websocketd.add_timeout(time.time() + .5, timeout)] watcher = websocketd.add_read(printer, boot_printer_input, boot_printer_error) def cancel(): websocketd.remove_timeout(timeout_handle[0]) websocketd.remove_read(watcher) printer.close() ports[port] = None ports[port] = cancel