| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  | #!/usr/bin/env python | 
					
						
							|  |  |  | # Main code for host side printer firmware | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # Copyright (C) 2016  Kevin O'Connor <kevin@koconnor.net> | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # This file may be distributed under the terms of the GNU GPLv3 license. | 
					
						
							|  |  |  | import sys, optparse, ConfigParser, logging, time, threading | 
					
						
							|  |  |  | import gcode, cartesian, util, mcu, fan, heater, reactor | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ConfigWrapper: | 
					
						
							|  |  |  |     def __init__(self, printer, section): | 
					
						
							|  |  |  |         self.printer = printer | 
					
						
							|  |  |  |         self.section = section | 
					
						
							|  |  |  |     def get(self, option, default=None): | 
					
						
							|  |  |  |         if not self.printer.fileconfig.has_option(self.section, option): | 
					
						
							|  |  |  |             return default | 
					
						
							|  |  |  |         return self.printer.fileconfig.get(self.section, option) | 
					
						
							|  |  |  |     def getint(self, option, default=None): | 
					
						
							|  |  |  |         if not self.printer.fileconfig.has_option(self.section, option): | 
					
						
							|  |  |  |             return default | 
					
						
							|  |  |  |         return self.printer.fileconfig.getint(self.section, option) | 
					
						
							|  |  |  |     def getfloat(self, option, default=None): | 
					
						
							|  |  |  |         if not self.printer.fileconfig.has_option(self.section, option): | 
					
						
							|  |  |  |             return default | 
					
						
							|  |  |  |         return self.printer.fileconfig.getfloat(self.section, option) | 
					
						
							|  |  |  |     def getboolean(self, option, default=None): | 
					
						
							|  |  |  |         if not self.printer.fileconfig.has_option(self.section, option): | 
					
						
							|  |  |  |             return default | 
					
						
							|  |  |  |         return self.printer.fileconfig.getboolean(self.section, option) | 
					
						
							|  |  |  |     def getsection(self, section): | 
					
						
							|  |  |  |         return ConfigWrapper(self.printer, section) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class Printer: | 
					
						
							|  |  |  |     def __init__(self, conffile, debuginput=None): | 
					
						
							|  |  |  |         self.fileconfig = ConfigParser.RawConfigParser() | 
					
						
							|  |  |  |         self.fileconfig.read(conffile) | 
					
						
							|  |  |  |         self.reactor = reactor.Reactor() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self._pconfig = ConfigWrapper(self, 'printer') | 
					
						
							|  |  |  |         ptty = self._pconfig.get('pseudo_tty', '/tmp/printer') | 
					
						
							|  |  |  |         if debuginput is None: | 
					
						
							|  |  |  |             pseudo_tty = util.create_pty(ptty) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             pseudo_tty = debuginput.fileno() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.gcode = gcode.GCodeParser( | 
					
						
							|  |  |  |             self, pseudo_tty, inputfile=debuginput is not None) | 
					
						
							|  |  |  |         self.mcu = None | 
					
						
							|  |  |  |         self.stat_timer = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.objects = {} | 
					
						
							|  |  |  |         if self.fileconfig.has_section('fan'): | 
					
						
							|  |  |  |             self.objects['fan'] = fan.PrinterFan( | 
					
						
							|  |  |  |                 self, ConfigWrapper(self, 'fan')) | 
					
						
							|  |  |  |         if self.fileconfig.has_section('heater_nozzle'): | 
					
						
							|  |  |  |             self.objects['heater_nozzle'] = heater.PrinterHeater( | 
					
						
							|  |  |  |                 self, ConfigWrapper(self, 'heater_nozzle')) | 
					
						
							|  |  |  |         if self.fileconfig.has_section('heater_bed'): | 
					
						
							|  |  |  |             self.objects['heater_bed'] = heater.PrinterHeater( | 
					
						
							|  |  |  |                 self, ConfigWrapper(self, 'heater_bed')) | 
					
						
							| 
									
										
										
										
											2016-07-07 14:23:48 -04:00
										 |  |  |         self.objects['toolhead'] = cartesian.ToolHead( | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |             self, self._pconfig) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def stats(self, eventtime): | 
					
						
							|  |  |  |         out = [] | 
					
						
							|  |  |  |         out.append(self.gcode.stats(eventtime)) | 
					
						
							| 
									
										
										
										
											2016-07-07 14:23:48 -04:00
										 |  |  |         out.append(self.objects['toolhead'].stats(eventtime)) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |         out.append(self.mcu.stats(eventtime)) | 
					
						
							|  |  |  |         logging.info("Stats %.0f: %s" % (eventtime, ' '.join(out))) | 
					
						
							|  |  |  |         return eventtime + 1. | 
					
						
							|  |  |  |     def build_config(self): | 
					
						
							|  |  |  |         for oname in sorted(self.objects.keys()): | 
					
						
							|  |  |  |             self.objects[oname].build_config() | 
					
						
							|  |  |  |         self.gcode.build_config() | 
					
						
							|  |  |  |         self.mcu.build_config() | 
					
						
							|  |  |  |     def connect(self): | 
					
						
							|  |  |  |         self.mcu = mcu.MCU(self, ConfigWrapper(self, 'mcu')) | 
					
						
							|  |  |  |         self.mcu.connect() | 
					
						
							|  |  |  |         self.build_config() | 
					
						
							|  |  |  |         self.stats_timer = self.reactor.register_timer( | 
					
						
							|  |  |  |             self.stats, self.reactor.NOW) | 
					
						
							|  |  |  |     def connect_debug(self, debugoutput): | 
					
						
							|  |  |  |         self.mcu = mcu.DummyMCU(debugoutput) | 
					
						
							|  |  |  |         self.mcu.connect() | 
					
						
							|  |  |  |         self.build_config() | 
					
						
							|  |  |  |     def connect_file(self, output, dictionary): | 
					
						
							|  |  |  |         self.mcu = mcu.MCU(self, ConfigWrapper(self, 'mcu')) | 
					
						
							|  |  |  |         self.mcu.connect_file(output, dictionary) | 
					
						
							|  |  |  |         self.build_config() | 
					
						
							|  |  |  |     def run(self): | 
					
						
							|  |  |  |         self.gcode.run() | 
					
						
							|  |  |  |         # If gcode exits, then exit the MCU | 
					
						
							|  |  |  |         self.stats(time.time()) | 
					
						
							|  |  |  |         self.mcu.disconnect() | 
					
						
							|  |  |  |         self.stats(time.time()) | 
					
						
							|  |  |  |     def shutdown(self): | 
					
						
							|  |  |  |         self.gcode.shutdown() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ###################################################################### | 
					
						
							|  |  |  | # Startup | 
					
						
							|  |  |  | ###################################################################### | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def read_dictionary(filename): | 
					
						
							|  |  |  |     dfile = open(filename, 'rb') | 
					
						
							|  |  |  |     dictionary = dfile.read() | 
					
						
							|  |  |  |     dfile.close() | 
					
						
							|  |  |  |     return dictionary | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def store_dictionary(filename, printer): | 
					
						
							|  |  |  |     f = open(filename, 'wb') | 
					
						
							|  |  |  |     f.write(printer.mcu.serial.msgparser.raw_identify_data) | 
					
						
							|  |  |  |     f.close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def main(): | 
					
						
							|  |  |  |     usage = "%prog [options] <config file>" | 
					
						
							|  |  |  |     opts = optparse.OptionParser(usage) | 
					
						
							|  |  |  |     opts.add_option("-o", "--debugoutput", dest="outputfile", | 
					
						
							|  |  |  |                     help="write output to file instead of to serial port") | 
					
						
							|  |  |  |     opts.add_option("-i", "--debuginput", dest="inputfile", | 
					
						
							|  |  |  |                     help="read commands from file instead of from tty port") | 
					
						
							|  |  |  |     opts.add_option("-l", "--logfile", dest="logfile", | 
					
						
							|  |  |  |                     help="write log to file instead of stderr") | 
					
						
							|  |  |  |     opts.add_option("-v", action="store_true", dest="verbose", | 
					
						
							|  |  |  |                     help="enable debug messages") | 
					
						
							|  |  |  |     opts.add_option("-d", dest="read_dictionary", | 
					
						
							|  |  |  |                     help="file to read for mcu protocol dictionary") | 
					
						
							|  |  |  |     opts.add_option("-D", dest="write_dictionary", | 
					
						
							|  |  |  |                     help="file to write mcu protocol dictionary") | 
					
						
							|  |  |  |     options, args = opts.parse_args() | 
					
						
							|  |  |  |     if len(args) != 1: | 
					
						
							|  |  |  |         opts.error("Incorrect number of arguments") | 
					
						
							|  |  |  |     conffile = args[0] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     debuginput = debugoutput = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     debuglevel = logging.INFO | 
					
						
							|  |  |  |     if options.verbose: | 
					
						
							|  |  |  |         debuglevel = logging.DEBUG | 
					
						
							|  |  |  |     if options.inputfile: | 
					
						
							|  |  |  |         debuginput = open(options.inputfile, 'rb') | 
					
						
							|  |  |  |     if options.outputfile: | 
					
						
							|  |  |  |         debugoutput = open(options.outputfile, 'wb') | 
					
						
							|  |  |  |     if options.logfile: | 
					
						
							|  |  |  |         logoutput = open(options.logfile, 'wb') | 
					
						
							|  |  |  |         logging.basicConfig(stream=logoutput, level=debuglevel) | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         logging.basicConfig(level=debuglevel) | 
					
						
							|  |  |  |     logging.info("Starting Klippy...") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Start firmware | 
					
						
							|  |  |  |     printer = Printer(conffile, debuginput=debuginput) | 
					
						
							|  |  |  |     if debugoutput: | 
					
						
							|  |  |  |         proto_dict = read_dictionary(options.read_dictionary) | 
					
						
							|  |  |  |         printer.connect_file(debugoutput, proto_dict) | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         printer.connect() | 
					
						
							|  |  |  |     if options.write_dictionary: | 
					
						
							|  |  |  |         store_dictionary(options.write_dictionary, printer) | 
					
						
							|  |  |  |     printer.run() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if __name__ == '__main__': | 
					
						
							|  |  |  |     main() |