Exemple #1
0
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)
Exemple #2
0
	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
Exemple #3
0
	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
Exemple #4
0
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)
Exemple #5
0
    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
Exemple #6
0
	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)
Exemple #7
0
	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
Exemple #8
0
    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
Exemple #9
0
    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)
Exemple #10
0
    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
Exemple #11
0
	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
Exemple #12
0
	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