2016-05-25 17:37:40 +02: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
|
2016-11-12 02:22:39 +01:00
|
|
|
import gcode, toolhead, util, mcu, fan, heater, extruder, reactor, queuelogger
|
2016-05-25 17:37:40 +02:00
|
|
|
|
|
|
|
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:
|
2016-11-21 02:40:31 +01:00
|
|
|
def __init__(self, conffile, input_fd, is_fileinput=False):
|
2016-05-25 17:37:40 +02:00
|
|
|
self.fileconfig = ConfigParser.RawConfigParser()
|
|
|
|
self.fileconfig.read(conffile)
|
|
|
|
self.reactor = reactor.Reactor()
|
|
|
|
|
2016-11-21 02:40:31 +01:00
|
|
|
self.gcode = gcode.GCodeParser(self, input_fd, is_fileinput)
|
2016-11-28 19:14:56 +01:00
|
|
|
self.mcu = mcu.MCU(self, ConfigWrapper(self, 'mcu'))
|
|
|
|
self.stats_timer = self.reactor.register_timer(
|
|
|
|
self.stats, self.reactor.NOW)
|
|
|
|
self.connect_timer = self.reactor.register_timer(
|
|
|
|
self.connect, self.reactor.NOW)
|
2016-11-30 04:46:05 +01:00
|
|
|
self.debugoutput = self.dictionary = None
|
2016-05-25 17:37:40 +02:00
|
|
|
|
|
|
|
self.objects = {}
|
|
|
|
if self.fileconfig.has_section('fan'):
|
|
|
|
self.objects['fan'] = fan.PrinterFan(
|
|
|
|
self, ConfigWrapper(self, 'fan'))
|
2016-07-10 18:23:35 +02:00
|
|
|
if self.fileconfig.has_section('extruder'):
|
|
|
|
self.objects['extruder'] = extruder.PrinterExtruder(
|
|
|
|
self, ConfigWrapper(self, 'extruder'))
|
2016-05-25 17:37:40 +02:00
|
|
|
if self.fileconfig.has_section('heater_bed'):
|
|
|
|
self.objects['heater_bed'] = heater.PrinterHeater(
|
|
|
|
self, ConfigWrapper(self, 'heater_bed'))
|
2016-07-07 21:52:44 +02:00
|
|
|
self.objects['toolhead'] = toolhead.ToolHead(
|
2016-11-21 02:40:31 +01:00
|
|
|
self, ConfigWrapper(self, 'printer'))
|
2016-11-30 04:46:05 +01:00
|
|
|
def set_fileoutput(self, debugoutput, dictionary):
|
|
|
|
self.debugoutput = debugoutput
|
|
|
|
self.dictionary = dictionary
|
2016-05-25 17:37:40 +02:00
|
|
|
def stats(self, eventtime):
|
|
|
|
out = []
|
|
|
|
out.append(self.gcode.stats(eventtime))
|
2016-07-07 20:23:48 +02:00
|
|
|
out.append(self.objects['toolhead'].stats(eventtime))
|
2016-05-25 17:37:40 +02: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()
|
2016-11-28 19:14:56 +01:00
|
|
|
def connect(self, eventtime):
|
2016-11-30 04:46:05 +01:00
|
|
|
if self.debugoutput is not None:
|
|
|
|
self.reactor.update_timer(self.stats_timer, self.reactor.NEVER)
|
|
|
|
self.mcu.connect_file(self.debugoutput, self.dictionary)
|
2016-05-25 17:37:40 +02:00
|
|
|
self.mcu.connect()
|
|
|
|
self.build_config()
|
2016-11-28 19:14:56 +01:00
|
|
|
self.gcode.run()
|
|
|
|
self.reactor.unregister_timer(self.connect_timer)
|
|
|
|
return self.reactor.NEVER
|
|
|
|
def run(self):
|
|
|
|
self.reactor.run()
|
2016-05-25 17:37:40 +02:00
|
|
|
# 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 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")
|
2016-11-21 02:40:31 +01:00
|
|
|
opts.add_option("-I", "--input-tty", dest="inputtty", default='/tmp/printer',
|
|
|
|
help="input tty name (default is /tmp/printer)")
|
2016-05-25 17:37:40 +02:00
|
|
|
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")
|
|
|
|
options, args = opts.parse_args()
|
|
|
|
if len(args) != 1:
|
|
|
|
opts.error("Incorrect number of arguments")
|
|
|
|
conffile = args[0]
|
|
|
|
|
2016-11-21 02:40:31 +01:00
|
|
|
input_fd = debuginput = debugoutput = bglogger = None
|
2016-05-25 17:37:40 +02:00
|
|
|
|
|
|
|
debuglevel = logging.INFO
|
|
|
|
if options.verbose:
|
|
|
|
debuglevel = logging.DEBUG
|
|
|
|
if options.inputfile:
|
|
|
|
debuginput = open(options.inputfile, 'rb')
|
2016-11-21 02:40:31 +01:00
|
|
|
input_fd = debuginput.fileno()
|
|
|
|
else:
|
|
|
|
input_fd = util.create_pty(options.inputtty)
|
2016-05-25 17:37:40 +02:00
|
|
|
if options.outputfile:
|
|
|
|
debugoutput = open(options.outputfile, 'wb')
|
|
|
|
if options.logfile:
|
2016-11-12 02:22:39 +01:00
|
|
|
bglogger = queuelogger.setup_bg_logging(options.logfile, debuglevel)
|
2016-05-25 17:37:40 +02:00
|
|
|
else:
|
|
|
|
logging.basicConfig(level=debuglevel)
|
|
|
|
logging.info("Starting Klippy...")
|
|
|
|
|
|
|
|
# Start firmware
|
2016-11-21 02:40:31 +01:00
|
|
|
printer = Printer(conffile, input_fd, is_fileinput=debuginput is not None)
|
2016-05-25 17:37:40 +02:00
|
|
|
if debugoutput:
|
|
|
|
proto_dict = read_dictionary(options.read_dictionary)
|
2016-11-30 04:46:05 +01:00
|
|
|
printer.set_fileoutput(debugoutput, proto_dict)
|
2016-05-25 17:37:40 +02:00
|
|
|
printer.run()
|
|
|
|
|
2016-11-12 02:22:39 +01:00
|
|
|
if bglogger is not None:
|
|
|
|
bglogger.stop()
|
|
|
|
|
2016-05-25 17:37:40 +02:00
|
|
|
if __name__ == '__main__':
|
|
|
|
main()
|