| 
									
										
										
										
											2017-06-27 20:23:30 -04:00
										 |  |  | #!/usr/bin/env python2 | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  | # Main code for host side printer firmware | 
					
						
							|  |  |  | # | 
					
						
							| 
									
										
										
										
											2024-06-15 12:13:35 -04:00
										 |  |  | # Copyright (C) 2016-2024  Kevin O'Connor <kevin@koconnor.net> | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  | # | 
					
						
							|  |  |  | # This file may be distributed under the terms of the GNU GPLv3 license. | 
					
						
							| 
									
										
										
										
											2020-10-30 13:02:44 -04:00
										 |  |  | import sys, os, gc, optparse, logging, time, collections, importlib | 
					
						
							| 
									
										
										
										
											2021-01-08 12:07:45 -05:00
										 |  |  | import util, reactor, queuelogger, msgproto | 
					
						
							| 
									
										
										
										
											2020-06-19 06:18:58 -04:00
										 |  |  | import gcode, configfile, pins, mcu, toolhead, webhooks | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-11 22:21:55 -05:00
										 |  |  | message_ready = "Printer is ready" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-30 14:30:45 -05:00
										 |  |  | message_startup = """
 | 
					
						
							| 
									
										
										
										
											2018-03-12 23:12:39 -04:00
										 |  |  | Printer is not ready | 
					
						
							| 
									
										
										
										
											2016-11-30 14:30:45 -05:00
										 |  |  | The klippy host software is attempting to connect.  Please | 
					
						
							|  |  |  | retry in a few moments. | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | message_restart = """
 | 
					
						
							| 
									
										
										
										
											2016-11-30 23:47:40 -05:00
										 |  |  | Once the underlying issue is corrected, use the "RESTART" | 
					
						
							|  |  |  | command to reload the config and restart the host software. | 
					
						
							| 
									
										
										
										
											2016-11-30 14:30:45 -05:00
										 |  |  | Printer is halted | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  | class Printer: | 
					
						
							| 
									
										
										
										
											2018-09-04 20:49:47 -04:00
										 |  |  |     config_error = configfile.error | 
					
						
							| 
									
										
										
										
											2021-01-08 12:07:45 -05:00
										 |  |  |     command_error = gcode.CommandError | 
					
						
							| 
									
										
										
										
											2020-09-16 12:15:19 -04:00
										 |  |  |     def __init__(self, main_reactor, bglogger, start_args): | 
					
						
							| 
									
										
										
										
											2017-05-01 13:44:06 -04:00
										 |  |  |         self.bglogger = bglogger | 
					
						
							| 
									
										
										
										
											2017-08-21 17:19:43 -04:00
										 |  |  |         self.start_args = start_args | 
					
						
							| 
									
										
										
										
											2020-09-16 12:15:19 -04:00
										 |  |  |         self.reactor = main_reactor | 
					
						
							| 
									
										
										
										
											2018-09-02 12:43:40 -04:00
										 |  |  |         self.reactor.register_callback(self._connect) | 
					
						
							| 
									
										
										
										
											2016-11-30 14:30:45 -05:00
										 |  |  |         self.state_message = message_startup | 
					
						
							| 
									
										
										
										
											2020-04-25 12:41:44 -04:00
										 |  |  |         self.in_shutdown_state = False | 
					
						
							| 
									
										
										
										
											2016-11-30 23:47:40 -05:00
										 |  |  |         self.run_result = None | 
					
						
							| 
									
										
										
										
											2018-10-07 12:22:10 -04:00
										 |  |  |         self.event_handlers = {} | 
					
						
							| 
									
										
										
										
											2020-06-19 06:18:58 -04:00
										 |  |  |         self.objects = collections.OrderedDict() | 
					
						
							| 
									
										
										
										
											2020-08-04 16:00:23 -04:00
										 |  |  |         # Init printer components that must be setup prior to config | 
					
						
							| 
									
										
										
										
											2020-08-11 21:21:41 -04:00
										 |  |  |         for m in [gcode, webhooks]: | 
					
						
							| 
									
										
										
										
											2020-08-04 16:00:23 -04:00
										 |  |  |             m.add_early_printer_objects(self) | 
					
						
							| 
									
										
										
										
											2017-08-21 17:19:43 -04:00
										 |  |  |     def get_start_args(self): | 
					
						
							|  |  |  |         return self.start_args | 
					
						
							| 
									
										
										
										
											2018-01-19 22:22:17 -05:00
										 |  |  |     def get_reactor(self): | 
					
						
							|  |  |  |         return self.reactor | 
					
						
							|  |  |  |     def get_state_message(self): | 
					
						
							| 
									
										
										
										
											2020-06-23 07:56:50 -04:00
										 |  |  |         if self.state_message == message_ready: | 
					
						
							|  |  |  |             category = "ready" | 
					
						
							|  |  |  |         elif self.state_message == message_startup: | 
					
						
							|  |  |  |             category = "startup" | 
					
						
							| 
									
										
										
										
											2020-08-16 14:08:38 -04:00
										 |  |  |         elif self.in_shutdown_state: | 
					
						
							|  |  |  |             category = "shutdown" | 
					
						
							| 
									
										
										
										
											2020-06-23 07:56:50 -04:00
										 |  |  |         else: | 
					
						
							|  |  |  |             category = "error" | 
					
						
							|  |  |  |         return self.state_message, category | 
					
						
							| 
									
										
										
										
											2020-04-25 12:41:44 -04:00
										 |  |  |     def is_shutdown(self): | 
					
						
							|  |  |  |         return self.in_shutdown_state | 
					
						
							| 
									
										
										
										
											2018-06-16 15:15:17 -04:00
										 |  |  |     def _set_state(self, msg): | 
					
						
							| 
									
										
										
										
											2019-01-11 12:36:09 -05:00
										 |  |  |         if self.state_message in (message_ready, message_startup): | 
					
						
							|  |  |  |             self.state_message = msg | 
					
						
							| 
									
										
										
										
											2018-06-16 15:15:17 -04:00
										 |  |  |         if (msg != message_ready | 
					
						
							|  |  |  |             and self.start_args.get('debuginput') is not None): | 
					
						
							|  |  |  |             self.request_exit('error_exit') | 
					
						
							| 
									
										
										
										
											2024-06-15 12:13:35 -04:00
										 |  |  |     def update_error_msg(self, oldmsg, newmsg): | 
					
						
							|  |  |  |         if (self.state_message != oldmsg | 
					
						
							|  |  |  |             or self.state_message in (message_ready, message_startup) | 
					
						
							|  |  |  |             or newmsg in (message_ready, message_startup)): | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         self.state_message = newmsg | 
					
						
							|  |  |  |         logging.error(newmsg) | 
					
						
							| 
									
										
										
										
											2018-01-19 22:22:17 -05:00
										 |  |  |     def add_object(self, name, obj): | 
					
						
							| 
									
										
										
										
											2021-03-05 13:59:20 -05:00
										 |  |  |         if name in self.objects: | 
					
						
							| 
									
										
										
										
											2018-01-19 22:22:17 -05:00
										 |  |  |             raise self.config_error( | 
					
						
							|  |  |  |                 "Printer object '%s' already created" % (name,)) | 
					
						
							|  |  |  |         self.objects[name] = obj | 
					
						
							| 
									
										
										
										
											2018-09-04 20:49:47 -04:00
										 |  |  |     def lookup_object(self, name, default=configfile.sentinel): | 
					
						
							| 
									
										
										
										
											2018-01-19 22:22:17 -05:00
										 |  |  |         if name in self.objects: | 
					
						
							|  |  |  |             return self.objects[name] | 
					
						
							| 
									
										
										
										
											2018-09-04 20:49:47 -04:00
										 |  |  |         if default is configfile.sentinel: | 
					
						
							| 
									
										
										
										
											2018-01-19 22:22:17 -05:00
										 |  |  |             raise self.config_error("Unknown config object '%s'" % (name,)) | 
					
						
							|  |  |  |         return default | 
					
						
							| 
									
										
										
										
											2018-09-02 12:51:37 -04:00
										 |  |  |     def lookup_objects(self, module=None): | 
					
						
							|  |  |  |         if module is None: | 
					
						
							|  |  |  |             return list(self.objects.items()) | 
					
						
							|  |  |  |         prefix = module + ' ' | 
					
						
							|  |  |  |         objs = [(n, self.objects[n]) | 
					
						
							|  |  |  |                 for n in self.objects if n.startswith(prefix)] | 
					
						
							|  |  |  |         if module in self.objects: | 
					
						
							|  |  |  |             return [(module, self.objects[module])] + objs | 
					
						
							| 
									
										
										
										
											2018-01-19 22:22:17 -05:00
										 |  |  |         return objs | 
					
						
							| 
									
										
										
										
											2020-05-05 14:10:30 -04:00
										 |  |  |     def load_object(self, config, section, default=configfile.sentinel): | 
					
						
							| 
									
										
										
										
											2018-01-19 21:25:58 -05:00
										 |  |  |         if section in self.objects: | 
					
						
							| 
									
										
										
										
											2018-06-14 12:00:55 -04:00
										 |  |  |             return self.objects[section] | 
					
						
							| 
									
										
										
										
											2018-02-03 12:53:11 -05:00
										 |  |  |         module_parts = section.split() | 
					
						
							|  |  |  |         module_name = module_parts[0] | 
					
						
							| 
									
										
										
										
											2018-01-19 21:25:58 -05:00
										 |  |  |         py_name = os.path.join(os.path.dirname(__file__), | 
					
						
							|  |  |  |                                'extras', module_name + '.py') | 
					
						
							| 
									
										
										
										
											2018-06-27 13:01:48 -04:00
										 |  |  |         py_dirname = os.path.join(os.path.dirname(__file__), | 
					
						
							|  |  |  |                                   'extras', module_name, '__init__.py') | 
					
						
							|  |  |  |         if not os.path.exists(py_name) and not os.path.exists(py_dirname): | 
					
						
							| 
									
										
										
										
											2020-05-05 14:10:30 -04:00
										 |  |  |             if default is not configfile.sentinel: | 
					
						
							|  |  |  |                 return default | 
					
						
							|  |  |  |             raise self.config_error("Unable to load module '%s'" % (section,)) | 
					
						
							| 
									
										
										
										
											2018-01-19 21:25:58 -05:00
										 |  |  |         mod = importlib.import_module('extras.' + module_name) | 
					
						
							| 
									
										
										
										
											2018-02-03 12:53:11 -05:00
										 |  |  |         init_func = 'load_config' | 
					
						
							|  |  |  |         if len(module_parts) > 1: | 
					
						
							|  |  |  |             init_func = 'load_config_prefix' | 
					
						
							|  |  |  |         init_func = getattr(mod, init_func, None) | 
					
						
							| 
									
										
										
										
											2020-05-05 14:10:30 -04:00
										 |  |  |         if init_func is None: | 
					
						
							|  |  |  |             if default is not configfile.sentinel: | 
					
						
							|  |  |  |                 return default | 
					
						
							|  |  |  |             raise self.config_error("Unable to load module '%s'" % (section,)) | 
					
						
							|  |  |  |         self.objects[section] = init_func(config.getsection(section)) | 
					
						
							|  |  |  |         return self.objects[section] | 
					
						
							| 
									
										
										
										
											2018-02-03 12:17:42 -05:00
										 |  |  |     def _read_config(self): | 
					
						
							| 
									
										
										
										
											2018-09-04 20:49:47 -04:00
										 |  |  |         self.objects['configfile'] = pconfig = configfile.PrinterConfig(self) | 
					
						
							|  |  |  |         config = pconfig.read_main_config() | 
					
						
							| 
									
										
										
										
											2017-05-01 13:44:06 -04:00
										 |  |  |         if self.bglogger is not None: | 
					
						
							| 
									
										
										
										
											2018-09-04 20:49:47 -04:00
										 |  |  |             pconfig.log_config(config) | 
					
						
							| 
									
										
										
										
											2017-04-29 13:57:02 -04:00
										 |  |  |         # Create printer components | 
					
						
							| 
									
										
										
										
											2020-04-25 13:27:41 -04:00
										 |  |  |         for m in [pins, mcu]: | 
					
						
							| 
									
										
										
										
											2018-07-12 22:26:32 -04:00
										 |  |  |             m.add_printer_objects(config) | 
					
						
							| 
									
										
										
										
											2018-09-04 20:49:47 -04:00
										 |  |  |         for section_config in config.get_prefix_sections(''): | 
					
						
							| 
									
										
										
										
											2020-05-05 14:10:30 -04:00
										 |  |  |             self.load_object(config, section_config.get_name(), None) | 
					
						
							| 
									
										
										
										
											2018-07-12 22:15:45 -04:00
										 |  |  |         for m in [toolhead]: | 
					
						
							| 
									
										
										
										
											2018-07-12 22:26:32 -04:00
										 |  |  |             m.add_printer_objects(config) | 
					
						
							| 
									
										
										
										
											2017-03-12 22:43:05 -04:00
										 |  |  |         # Validate that there are no undefined parameters in the config file | 
					
						
							| 
									
										
										
										
											2018-09-04 20:49:47 -04:00
										 |  |  |         pconfig.check_unused_options(config) | 
					
						
							| 
									
										
										
										
											2017-08-26 00:13:36 -04:00
										 |  |  |     def _connect(self, eventtime): | 
					
						
							| 
									
										
										
										
											2016-11-30 14:30:45 -05:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2018-02-03 12:17:42 -05:00
										 |  |  |             self._read_config() | 
					
						
							| 
									
										
										
										
											2019-11-04 23:00:00 -05:00
										 |  |  |             self.send_event("klippy:mcu_identify") | 
					
						
							| 
									
										
										
										
											2019-01-08 11:09:55 -05:00
										 |  |  |             for cb in self.event_handlers.get("klippy:connect", []): | 
					
						
							| 
									
										
										
										
											2018-01-19 22:49:27 -05:00
										 |  |  |                 if self.state_message is not message_startup: | 
					
						
							| 
									
										
										
										
											2018-10-18 12:49:26 -04:00
										 |  |  |                     return | 
					
						
							| 
									
										
										
										
											2019-01-08 11:09:55 -05:00
										 |  |  |                 cb() | 
					
						
							| 
									
										
										
										
											2017-09-06 14:51:47 -04:00
										 |  |  |         except (self.config_error, pins.error) as e: | 
					
						
							| 
									
										
										
										
											2016-11-30 14:57:18 -05:00
										 |  |  |             logging.exception("Config error") | 
					
						
							| 
									
										
										
										
											2021-06-10 21:30:09 -04:00
										 |  |  |             self._set_state("%s\n%s" % (str(e), message_restart)) | 
					
						
							| 
									
										
										
										
											2019-01-08 13:46:26 -05:00
										 |  |  |             return | 
					
						
							| 
									
										
										
										
											2017-06-09 23:32:49 -04:00
										 |  |  |         except msgproto.error as e: | 
					
						
							| 
									
										
										
										
											2024-06-15 12:27:36 -04:00
										 |  |  |             msg = "Protocol error" | 
					
						
							|  |  |  |             logging.exception(msg) | 
					
						
							|  |  |  |             self._set_state(msg) | 
					
						
							|  |  |  |             self.send_event("klippy:notify_mcu_error", msg, {"error": str(e)}) | 
					
						
							| 
									
										
										
										
											2020-03-22 13:20:07 -04:00
										 |  |  |             util.dump_mcu_build() | 
					
						
							| 
									
										
										
										
											2019-01-08 13:46:26 -05:00
										 |  |  |             return | 
					
						
							| 
									
										
										
										
											2017-06-09 23:32:49 -04:00
										 |  |  |         except mcu.error as e: | 
					
						
							| 
									
										
										
										
											2024-06-15 12:34:29 -04:00
										 |  |  |             msg = "MCU error during connect" | 
					
						
							|  |  |  |             logging.exception(msg) | 
					
						
							|  |  |  |             self._set_state(msg) | 
					
						
							|  |  |  |             self.send_event("klippy:notify_mcu_error", msg, {"error": str(e)}) | 
					
						
							| 
									
										
										
										
											2020-03-22 13:20:07 -04:00
										 |  |  |             util.dump_mcu_build() | 
					
						
							| 
									
										
										
										
											2019-01-08 13:46:26 -05:00
										 |  |  |             return | 
					
						
							| 
									
										
										
										
											2019-04-04 18:37:20 -04:00
										 |  |  |         except Exception as e: | 
					
						
							| 
									
										
										
										
											2016-11-30 14:30:45 -05:00
										 |  |  |             logging.exception("Unhandled exception during connect") | 
					
						
							| 
									
										
										
										
											2020-09-17 01:59:18 -04:00
										 |  |  |             self._set_state("Internal error during connect: %s\n%s" | 
					
						
							|  |  |  |                             % (str(e), message_restart,)) | 
					
						
							| 
									
										
										
										
											2019-01-08 13:46:26 -05:00
										 |  |  |             return | 
					
						
							| 
									
										
										
										
											2019-01-08 10:55:18 -05:00
										 |  |  |         try: | 
					
						
							|  |  |  |             self._set_state(message_ready) | 
					
						
							|  |  |  |             for cb in self.event_handlers.get("klippy:ready", []): | 
					
						
							|  |  |  |                 if self.state_message is not message_ready: | 
					
						
							|  |  |  |                     return | 
					
						
							|  |  |  |                 cb() | 
					
						
							| 
									
										
										
										
											2019-04-04 18:37:20 -04:00
										 |  |  |         except Exception as e: | 
					
						
							| 
									
										
										
										
											2019-01-08 10:55:18 -05:00
										 |  |  |             logging.exception("Unhandled exception during ready callback") | 
					
						
							| 
									
										
										
										
											2020-09-17 01:59:18 -04:00
										 |  |  |             self.invoke_shutdown("Internal error during ready callback: %s" | 
					
						
							|  |  |  |                                  % (str(e),)) | 
					
						
							| 
									
										
										
										
											2016-11-28 13:14:56 -05:00
										 |  |  |     def run(self): | 
					
						
							| 
									
										
										
										
											2017-02-06 13:31:34 -05:00
										 |  |  |         systime = time.time() | 
					
						
							|  |  |  |         monotime = self.reactor.monotonic() | 
					
						
							| 
									
										
										
										
											2017-09-27 11:43:14 -04:00
										 |  |  |         logging.info("Start printer at %s (%.1f %.1f)", | 
					
						
							|  |  |  |                      time.asctime(time.localtime(systime)), systime, monotime) | 
					
						
							| 
									
										
										
										
											2018-09-12 19:11:20 -04:00
										 |  |  |         # Enter main reactor loop | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             self.reactor.run() | 
					
						
							|  |  |  |         except: | 
					
						
							| 
									
										
										
										
											2020-09-06 12:45:27 -04:00
										 |  |  |             msg = "Unhandled exception during run" | 
					
						
							|  |  |  |             logging.exception(msg) | 
					
						
							|  |  |  |             # Exception from a reactor callback - try to shutdown | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 self.reactor.register_callback((lambda e: | 
					
						
							|  |  |  |                                                 self.invoke_shutdown(msg))) | 
					
						
							|  |  |  |                 self.reactor.run() | 
					
						
							|  |  |  |             except: | 
					
						
							|  |  |  |                 logging.exception("Repeat unhandled exception during run") | 
					
						
							|  |  |  |                 # Another exception - try to exit | 
					
						
							|  |  |  |                 self.run_result = "error_exit" | 
					
						
							| 
									
										
										
										
											2018-09-12 19:11:20 -04:00
										 |  |  |         # Check restart flags | 
					
						
							|  |  |  |         run_result = self.run_result | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             if run_result == 'firmware_restart': | 
					
						
							| 
									
										
										
										
											2022-07-25 19:01:17 -04:00
										 |  |  |                 self.send_event("klippy:firmware_restart") | 
					
						
							| 
									
										
										
										
											2019-01-08 10:55:18 -05:00
										 |  |  |             self.send_event("klippy:disconnect") | 
					
						
							| 
									
										
										
										
											2018-09-12 19:11:20 -04:00
										 |  |  |         except: | 
					
						
							|  |  |  |             logging.exception("Unhandled exception during post run") | 
					
						
							|  |  |  |         return run_result | 
					
						
							| 
									
										
										
										
											2020-05-05 13:57:07 -04:00
										 |  |  |     def set_rollover_info(self, name, info, log=True): | 
					
						
							|  |  |  |         if log: | 
					
						
							|  |  |  |             logging.info(info) | 
					
						
							|  |  |  |         if self.bglogger is not None: | 
					
						
							|  |  |  |             self.bglogger.set_rollover_info(name, info) | 
					
						
							| 
									
										
										
										
											2024-06-15 12:13:35 -04:00
										 |  |  |     def invoke_shutdown(self, msg, details={}): | 
					
						
							| 
									
										
										
										
											2020-04-25 12:41:44 -04:00
										 |  |  |         if self.in_shutdown_state: | 
					
						
							| 
									
										
										
										
											2017-10-12 15:15:14 -04:00
										 |  |  |             return | 
					
						
							| 
									
										
										
										
											2020-03-24 08:37:36 -04:00
										 |  |  |         logging.error("Transition to shutdown state: %s", msg) | 
					
						
							| 
									
										
										
										
											2020-04-25 12:41:44 -04:00
										 |  |  |         self.in_shutdown_state = True | 
					
						
							| 
									
										
										
										
											2024-06-15 12:13:35 -04:00
										 |  |  |         self._set_state(msg) | 
					
						
							| 
									
										
										
										
											2019-01-08 09:15:40 -05:00
										 |  |  |         for cb in self.event_handlers.get("klippy:shutdown", []): | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 cb() | 
					
						
							|  |  |  |             except: | 
					
						
							|  |  |  |                 logging.exception("Exception during shutdown handler") | 
					
						
							| 
									
										
										
										
											2020-09-17 01:59:18 -04:00
										 |  |  |         logging.info("Reactor garbage collection: %s", | 
					
						
							|  |  |  |                      self.reactor.get_gc_stats()) | 
					
						
							| 
									
										
										
										
											2024-06-15 12:13:35 -04:00
										 |  |  |         self.send_event("klippy:notify_mcu_shutdown", msg, details) | 
					
						
							|  |  |  |     def invoke_async_shutdown(self, msg, details): | 
					
						
							| 
									
										
										
										
											2018-09-02 12:43:40 -04:00
										 |  |  |         self.reactor.register_async_callback( | 
					
						
							| 
									
										
										
										
											2024-06-15 12:13:35 -04:00
										 |  |  |             (lambda e: self.invoke_shutdown(msg, details))) | 
					
						
							| 
									
										
										
										
											2018-10-07 12:22:10 -04:00
										 |  |  |     def register_event_handler(self, event, callback): | 
					
						
							|  |  |  |         self.event_handlers.setdefault(event, []).append(callback) | 
					
						
							|  |  |  |     def send_event(self, event, *params): | 
					
						
							|  |  |  |         return [cb(*params) for cb in self.event_handlers.get(event, [])] | 
					
						
							| 
									
										
										
										
											2018-06-16 15:15:17 -04:00
										 |  |  |     def request_exit(self, result): | 
					
						
							| 
									
										
										
										
											2019-06-27 18:32:25 -04:00
										 |  |  |         if self.run_result is None: | 
					
						
							|  |  |  |             self.run_result = result | 
					
						
							| 
									
										
										
										
											2016-11-30 23:47:40 -05:00
										 |  |  |         self.reactor.end() | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ###################################################################### | 
					
						
							|  |  |  | # Startup | 
					
						
							|  |  |  | ###################################################################### | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-01 20:57:26 -04:00
										 |  |  | def import_test(): | 
					
						
							|  |  |  |     # Import all optional modules (used as a build test) | 
					
						
							|  |  |  |     dname = os.path.dirname(__file__) | 
					
						
							|  |  |  |     for mname in ['extras', 'kinematics']: | 
					
						
							|  |  |  |         for fname in os.listdir(os.path.join(dname, mname)): | 
					
						
							|  |  |  |             if fname.endswith('.py') and fname != '__init__.py': | 
					
						
							|  |  |  |                 module_name = fname[:-3] | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 iname = os.path.join(dname, mname, fname, '__init__.py') | 
					
						
							|  |  |  |                 if not os.path.exists(iname): | 
					
						
							|  |  |  |                     continue | 
					
						
							|  |  |  |                 module_name = fname | 
					
						
							|  |  |  |             importlib.import_module(mname + '.' + module_name) | 
					
						
							|  |  |  |     sys.exit(0) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-14 11:46:35 -04:00
										 |  |  | def arg_dictionary(option, opt_str, value, parser): | 
					
						
							|  |  |  |     key, fname = "dictionary", value | 
					
						
							|  |  |  |     if '=' in value: | 
					
						
							|  |  |  |         mcu_name, fname = value.split('=', 1) | 
					
						
							|  |  |  |         key = "dictionary_" + mcu_name | 
					
						
							|  |  |  |     if parser.values.dictionary is None: | 
					
						
							|  |  |  |         parser.values.dictionary = {} | 
					
						
							|  |  |  |     parser.values.dictionary[key] = fname | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  | def main(): | 
					
						
							|  |  |  |     usage = "%prog [options] <config file>" | 
					
						
							|  |  |  |     opts = optparse.OptionParser(usage) | 
					
						
							| 
									
										
										
										
											2017-08-21 17:19:43 -04:00
										 |  |  |     opts.add_option("-i", "--debuginput", dest="debuginput", | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |                     help="read commands from file instead of from tty port") | 
					
						
							| 
									
										
										
										
											2019-02-27 13:18:43 -05:00
										 |  |  |     opts.add_option("-I", "--input-tty", dest="inputtty", | 
					
						
							|  |  |  |                     default='/tmp/printer', | 
					
						
							| 
									
										
										
										
											2016-11-20 20:40:31 -05:00
										 |  |  |                     help="input tty name (default is /tmp/printer)") | 
					
						
							| 
									
										
										
										
											2020-08-11 16:26:07 -04:00
										 |  |  |     opts.add_option("-a", "--api-server", dest="apiserver", | 
					
						
							|  |  |  |                     help="api server unix domain socket filename") | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04: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") | 
					
						
							| 
									
										
										
										
											2017-08-21 17:19:43 -04:00
										 |  |  |     opts.add_option("-o", "--debugoutput", dest="debugoutput", | 
					
						
							|  |  |  |                     help="write output to file instead of to serial port") | 
					
						
							| 
									
										
										
										
											2017-08-14 11:46:35 -04:00
										 |  |  |     opts.add_option("-d", "--dictionary", dest="dictionary", type="string", | 
					
						
							|  |  |  |                     action="callback", callback=arg_dictionary, | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |                     help="file to read for mcu protocol dictionary") | 
					
						
							| 
									
										
										
										
											2021-10-01 20:57:26 -04:00
										 |  |  |     opts.add_option("--import-test", action="store_true", | 
					
						
							|  |  |  |                     help="perform an import module test") | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     options, args = opts.parse_args() | 
					
						
							| 
									
										
										
										
											2021-10-01 20:57:26 -04:00
										 |  |  |     if options.import_test: | 
					
						
							|  |  |  |         import_test() | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     if len(args) != 1: | 
					
						
							|  |  |  |         opts.error("Incorrect number of arguments") | 
					
						
							| 
									
										
										
										
											2020-08-11 16:26:07 -04:00
										 |  |  |     start_args = {'config_file': args[0], 'apiserver': options.apiserver, | 
					
						
							|  |  |  |                   'start_reason': 'startup'} | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     debuglevel = logging.INFO | 
					
						
							|  |  |  |     if options.verbose: | 
					
						
							|  |  |  |         debuglevel = logging.DEBUG | 
					
						
							| 
									
										
										
										
											2017-08-21 17:19:43 -04:00
										 |  |  |     if options.debuginput: | 
					
						
							|  |  |  |         start_args['debuginput'] = options.debuginput | 
					
						
							|  |  |  |         debuginput = open(options.debuginput, 'rb') | 
					
						
							| 
									
										
										
										
											2020-08-04 15:47:25 -04:00
										 |  |  |         start_args['gcode_fd'] = debuginput.fileno() | 
					
						
							| 
									
										
										
										
											2016-11-20 20:40:31 -05:00
										 |  |  |     else: | 
					
						
							| 
									
										
										
										
											2020-08-04 15:47:25 -04:00
										 |  |  |         start_args['gcode_fd'] = util.create_pty(options.inputtty) | 
					
						
							| 
									
										
										
										
											2017-08-21 17:19:43 -04:00
										 |  |  |     if options.debugoutput: | 
					
						
							|  |  |  |         start_args['debugoutput'] = options.debugoutput | 
					
						
							| 
									
										
										
										
											2017-08-14 11:46:35 -04:00
										 |  |  |         start_args.update(options.dictionary) | 
					
						
							| 
									
										
										
										
											2020-08-04 15:47:25 -04:00
										 |  |  |     bglogger = None | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     if options.logfile: | 
					
						
							| 
									
										
										
										
											2020-06-19 06:18:58 -04:00
										 |  |  |         start_args['log_file'] = options.logfile | 
					
						
							| 
									
										
										
										
											2016-11-11 20:22:39 -05:00
										 |  |  |         bglogger = queuelogger.setup_bg_logging(options.logfile, debuglevel) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     else: | 
					
						
							| 
									
										
										
										
											2022-06-24 13:17:20 +02:00
										 |  |  |         logging.getLogger().setLevel(debuglevel) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     logging.info("Starting Klippy...") | 
					
						
							| 
									
										
										
										
											2023-05-03 06:05:37 -04:00
										 |  |  |     git_info = util.get_git_version() | 
					
						
							|  |  |  |     git_vers = git_info["version"] | 
					
						
							|  |  |  |     extra_files = [fname for code, fname in git_info["file_status"] | 
					
						
							| 
									
										
										
										
											2023-04-26 18:24:39 -04:00
										 |  |  |                    if (code in ('??', '!!') and fname.endswith('.py') | 
					
						
							|  |  |  |                        and (fname.startswith('klippy/kinematics/') | 
					
						
							|  |  |  |                             or fname.startswith('klippy/extras/')))] | 
					
						
							| 
									
										
										
										
											2023-05-03 06:05:37 -04:00
										 |  |  |     modified_files = [fname for code, fname in git_info["file_status"] | 
					
						
							|  |  |  |                       if code == 'M'] | 
					
						
							| 
									
										
										
										
											2023-04-26 18:24:39 -04:00
										 |  |  |     extra_git_desc = "" | 
					
						
							|  |  |  |     if extra_files: | 
					
						
							|  |  |  |         if not git_vers.endswith('-dirty'): | 
					
						
							|  |  |  |             git_vers = git_vers + '-dirty' | 
					
						
							|  |  |  |         if len(extra_files) > 10: | 
					
						
							|  |  |  |             extra_files[10:] = ["(+%d files)" % (len(extra_files) - 10,)] | 
					
						
							|  |  |  |         extra_git_desc += "\nUntracked files: %s" % (', '.join(extra_files),) | 
					
						
							|  |  |  |     if modified_files: | 
					
						
							|  |  |  |         if len(modified_files) > 10: | 
					
						
							|  |  |  |             modified_files[10:] = ["(+%d files)" % (len(modified_files) - 10,)] | 
					
						
							|  |  |  |         extra_git_desc += "\nModified files: %s" % (', '.join(modified_files),) | 
					
						
							| 
									
										
										
										
											2023-05-03 06:05:37 -04:00
										 |  |  |     extra_git_desc += "\nBranch: %s" % (git_info["branch"]) | 
					
						
							|  |  |  |     extra_git_desc += "\nRemote: %s" % (git_info["remote"]) | 
					
						
							|  |  |  |     extra_git_desc += "\nTracked URL: %s" % (git_info["url"]) | 
					
						
							| 
									
										
										
										
											2023-04-26 18:24:39 -04:00
										 |  |  |     start_args['software_version'] = git_vers | 
					
						
							| 
									
										
										
										
											2020-06-19 06:18:58 -04:00
										 |  |  |     start_args['cpu_info'] = util.get_cpu_info() | 
					
						
							| 
									
										
										
										
											2017-05-01 13:44:06 -04:00
										 |  |  |     if bglogger is not None: | 
					
						
							| 
									
										
										
										
											2018-04-03 12:13:06 -04:00
										 |  |  |         versions = "\n".join([ | 
					
						
							|  |  |  |             "Args: %s" % (sys.argv,), | 
					
						
							| 
									
										
										
										
											2023-04-26 18:24:39 -04:00
										 |  |  |             "Git version: %s%s" % (repr(start_args['software_version']), | 
					
						
							|  |  |  |                                    extra_git_desc), | 
					
						
							| 
									
										
										
										
											2020-06-19 06:18:58 -04:00
										 |  |  |             "CPU: %s" % (start_args['cpu_info'],), | 
					
						
							| 
									
										
										
										
											2018-04-03 12:13:06 -04:00
										 |  |  |             "Python: %s" % (repr(sys.version),)]) | 
					
						
							|  |  |  |         logging.info(versions) | 
					
						
							| 
									
										
										
										
											2019-09-01 18:09:20 -04:00
										 |  |  |     elif not options.debugoutput: | 
					
						
							|  |  |  |         logging.warning("No log file specified!" | 
					
						
							|  |  |  |                         " Severe timing issues may result!") | 
					
						
							| 
									
										
										
										
											2020-09-16 22:23:44 -04:00
										 |  |  |     gc.disable() | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-21 17:19:43 -04:00
										 |  |  |     # Start Printer() class | 
					
						
							| 
									
										
										
										
											2016-11-30 23:47:40 -05:00
										 |  |  |     while 1: | 
					
						
							| 
									
										
										
										
											2018-04-03 12:13:06 -04:00
										 |  |  |         if bglogger is not None: | 
					
						
							|  |  |  |             bglogger.clear_rollover_info() | 
					
						
							|  |  |  |             bglogger.set_rollover_info('versions', versions) | 
					
						
							| 
									
										
										
										
											2020-09-16 21:42:24 -04:00
										 |  |  |         gc.collect() | 
					
						
							| 
									
										
										
										
											2020-09-16 22:23:44 -04:00
										 |  |  |         main_reactor = reactor.Reactor(gc_checking=True) | 
					
						
							| 
									
										
										
										
											2020-09-16 12:15:19 -04:00
										 |  |  |         printer = Printer(main_reactor, bglogger, start_args) | 
					
						
							| 
									
										
										
										
											2016-11-30 23:47:40 -05:00
										 |  |  |         res = printer.run() | 
					
						
							| 
									
										
										
										
											2018-06-16 15:15:17 -04:00
										 |  |  |         if res in ['exit', 'error_exit']: | 
					
						
							| 
									
										
										
										
											2017-08-21 17:19:43 -04:00
										 |  |  |             break | 
					
						
							|  |  |  |         time.sleep(1.) | 
					
						
							| 
									
										
										
										
											2020-09-16 12:15:19 -04:00
										 |  |  |         main_reactor.finalize() | 
					
						
							| 
									
										
										
										
											2020-09-16 21:42:24 -04:00
										 |  |  |         main_reactor = printer = None | 
					
						
							| 
									
										
										
										
											2017-08-21 17:19:43 -04:00
										 |  |  |         logging.info("Restarting printer") | 
					
						
							|  |  |  |         start_args['start_reason'] = res | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-11 20:22:39 -05:00
										 |  |  |     if bglogger is not None: | 
					
						
							|  |  |  |         bglogger.stop() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-16 15:15:17 -04:00
										 |  |  |     if res == 'error_exit': | 
					
						
							|  |  |  |         sys.exit(-1) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  | if __name__ == '__main__': | 
					
						
							|  |  |  |     main() |