Esempio n. 1
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
Esempio n. 2
0
	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)
Esempio n. 3
0
	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 = websockets.call(resumeinfo, detect, port)
		while c(): c.args = (yield websockets.WAIT)
Esempio n. 4
0
		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('admin', 'export_settings', (), {}, get_settings)
Esempio n. 5
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
Esempio n. 6
0
	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 = websockets.call(resumeinfo, cls.detect, port)
			while c(): c.args = (yield websockets.WAIT)
Esempio n. 7
0
		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]
Esempio n. 8
0
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)
Esempio n. 9
0
		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
Esempio n. 10
0
		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
Esempio n. 11
0
	def _call (self, name, a, ka): # {{{
		resumeinfo = [(yield), None]
		#log('other: %s %s %s' % (name, repr(a), repr(ka)))
		if not self.printer or self.printer not in ports or not ports[self.printer]:
			self.printer = self.find_printer()
			if not self.printer:
				log('No printer found')
				yield ('error', 'No printer found')
		def reply(success, ret):
			if success:
				resumeinfo[0](ret)
			else:
				log('printer errors')
				resumeinfo[0](None)
				#Connection._disable('admin', self.printer)
		ports[self.printer].call(name, (self.socket.data['role'],) + tuple(a), ka, reply)
		yield (yield websockets.WAIT)
Esempio n. 12
0
	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 websockets.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:
			websockets.call(None, self.detect, port)()
		if d:
			yield ('firmware upload for %s: ' % board + d)
		else:
			yield ('firmware for %s successfully uploaded' % board)
Esempio n. 13
0
	def _broadcast(cls, target, name, *args): # {{{
		if target is not None:
			#log('broadcasting to target %d' % target)
			if target not in Connection.connections:
				log('ignoring targeted broadcast of %s to missing connection %d' % (repr((name, args)), target))
				return
			target = Connection.connections[target].socket
			if target.monitor:
				#log('%s %s' % (name, repr(args)))
				getattr(target, name).event(*args)
			else:
				log("not broadcasting to target, because it isn't set to monitor")
		elif httpd:
			#log('broadcasting to all: %s' % repr((name, args)))
			for c in httpd.websockets:
				if c.monitor and c.initialized:
					#log('broadcasting to one')
					getattr(c, name).event(*args)
Esempio n. 14
0
	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 x[0] == self.uuid]:
				# This for loop always runs 0 or 1 times, never more.
				log('killing duplicate orphan')
				del orphans[x]
			orphans[self.run_id] = self
			Connection._broadcast(None, 'del_printer', port)
			if autodetect:
				websockets.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
Esempio n. 15
0
def check(packet):
	num = (len(packet) + 3) // 4
	l = len(packet) - num
	for t in range(num):
		s = packet[l + t]
		if s & 7 != t:
			log('bad index %x %x %x' % (s, l, t))
			return False
		for bit in range(5):
			check = 0
			for p in range(3):
				check ^= (packet[3 * t + p] if 3 * t + p < l + t else 0) & mask[bit][p]
			check ^= s & mask[bit][3]
			check ^= check >> 4
			check ^= check >> 2
			check ^= check >> 1
			if check & 1 != 0:
				log('bad checksum')
				return False
	log('good')
	return True
Esempio n. 16
0
if config['local'] != '':
	websockets.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'):
		websockets.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():
			websockets.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])

# Set default printer. {{{
if ' ' in config['printer']:
	default_printer = config['printer'].rsplit(' ', 1)
else:
	default_printer = (config['printer'], None)
# }}}

httpd = Server(config['port'], Connection, disconnect_cb = Connection.disconnect, httpdirs = fhs.read_data('html', dir = True, multiple = True), address = config['address'], log = config['log'], tls = tls)

log('running')
websockets.fgloop()
Esempio n. 17
0
		def process_done(fd, cond):
			data = p.stdout.read()
			if data:
				return True
			log('Flashing done; return: %s' % repr(p.wait()))
			return False
Esempio n. 18
0
		def reply(success, ret):
			if success:
				resumeinfo[0](ret)
			else:
				log('printer errors')
				resumeinfo[0](None)
Esempio n. 19
0
				def close_port(success, data):
					log('reconnect complete; closing server port')
					printer.close()
Esempio n. 20
0
		def boot_printer_input(fd, cond):
			id[2] = True
			ids = [protocol.single[code] for code in ('ID', 'STARTUP')]
			# CMD:1 ID:8 Checksum:3
			while len(id[0]) < 12:
				data = printer.read(12 - len(id[0]))
				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(map(ord, id[0])):
					log('skip non-id: %s (%s)' % (''.join('%02x' % ord(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(start)
							if p < f:
								f = p
							log('Keeping some')
							break
					else:
						id[0] = ''
					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.)
			GLib.source_remove(timeout_handle)
			# 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' % (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)
				return False
			run_id = nextid()
			log('accepting unknown printer on port %s (id %s)' % (port, ''.join('%02x' % ord(x) for x in run_id)))
			log('orphans: %s' % repr(orphans.keys()))
			process = subprocess.Popen((config['driver'], '--cdriver', config['cdriver'], '--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
Esempio n. 21
0
def detect(port): # {{{
	if port == '-' or port.startswith('!'):
		run_id = nextid()
		process = subprocess.Popen((config['driver'], '--cdriver', config['local'] or config['cdriver'], '--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, None, run_id)
		return False
	if not os.path.exists(port):
		log("not detecting on %s, because file doesn't exist." % port)
		return False
	log('detecting on %s' % port)
	if config['predetect']:
		subprocess.call(config['predetect'].replace('#PORT#', port), shell = True)
	try:
		printer = serial.Serial(port, baudrate = 115200, timeout = 0)
	except serial.SerialException:
		log('failed to open serial port.')
		traceback.print_exc();
		return False
	# We need to get the printer id first.  If the printer is booting, this can take a while.
	id = [None, None, None, None]	# data, timeouts, had data
	# Wait to make sure the command is interpreted as a new packet.
	def part2():
		id[0] = ''
		id[1] = 0
		id[2] = False
		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 boot_printer_input(fd, cond):
			id[2] = True
			ids = [protocol.single[code] for code in ('ID', 'STARTUP')]
			# CMD:1 ID:8 Checksum:3
			while len(id[0]) < 12:
				data = printer.read(12 - len(id[0]))
				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(map(ord, id[0])):
					log('skip non-id: %s (%s)' % (''.join('%02x' % ord(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(start)
							if p < f:
								f = p
							log('Keeping some')
							break
					else:
						id[0] = ''
					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.)
			GLib.source_remove(timeout_handle)
			# 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' % (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)
				return False
			run_id = nextid()
			log('accepting unknown printer on port %s (id %s)' % (port, ''.join('%02x' % ord(x) for x in run_id)))
			log('orphans: %s' % repr(orphans.keys()))
			process = subprocess.Popen((config['driver'], '--cdriver', config['cdriver'], '--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
		printer.write(protocol.single['ID'])
		timeout_handle = GLib.timeout_add(500, timeout)
		watcher = GLib.io_add_watch(printer.fileno(), GLib.IO_IN, boot_printer_input)
	# Wait at least a second before sending anything, otherwise the bootloader thinks we might be trying to reprogram it.
	# This is only a problem for RAMPS; don't wait for ports that cannot be RAMPS.
	if 'ACM' in port:
		GLib.timeout_add(1500, part2)
	else:
		part2()