def on_port_created(self, port, *args, **kwargs): # if we're already connected ignore it if self._printer.is_closed_or_error(): connection_options = get_connection_options() self._logger.info("on_port_created connection_options %s" % (repr(connection_options))) # is the new device in the port list? yes, tell the view model self._logger.info("Checking if %s is in %s" % (port, repr(connection_options["ports"]))) if port in connection_options["ports"]: self._plugin_manager.send_plugin_message( self._plugin_name, port) # if autoconnect and the new port matches, try to connect if self._settings.global_get_boolean(["serial", "autoconnect"]): autoconnect_delay = self._settings.get_int( ["autoconnect_delay"]) self._logger.info( "autoconnect_delay {0}".format(autoconnect_delay)) Timer(autoconnect_delay, self.do_auto_connect, [port]).start() else: self._logger.info( "Not autoconnecting because autoconnect is turned off." ) else: self._logger.warning( "Won't autoconnect because %s isn't in %s" % (port, repr(connection_options["ports"]))) else: self._logger.warning( "Not auto connecting because printer is not closed nor in error state." )
def _get_options(): connection_options = get_connection_options() profile_options = printerProfileManager.get_all() default_profile = printerProfileManager.get_default() options = dict( ports=connection_options["ports"], baudrates=connection_options["baudrates"], printerProfiles=[ dict(id=printer_profile["id"], name=printer_profile["name"] if "name" in printer_profile else printer_profile["id"]) for printer_profile in profile_options.values() if "id" in printer_profile ], projectProfiles=[ "OPTOMA", "acer_IR", "viewsonic_IR", "acer", "viewsonic", "user_define" ], portPreference=connection_options["portPreference"], portPreference1=connection_options["portPreference1"], baudratePreference=connection_options["baudratePreference"], printerProfilePreference=default_profile["id"] if "id" in default_profile else None, projector=connection_options["projector"], ) return options
def update(self, dt): connections = get_connection_options() self.ids.ports.values = connections['ports'] self.ids.baudrates.values = map(str, connections['baudrates']) profiles = [] for i in conf.plugin._printer_profile_manager.get_all(): profiles.append( conf.plugin._printer_profile_manager.get_all()[i]['name']) self.ids.profiles.values = profiles if self.first: if connections['portPreference'] != None: self.ids.ports.text = connections['portPreference'] else: self.ids.ports.text = "None" if connections['baudratePreference'] != None: self.ids.baudrates.text = str( connections['baudratePreference']) else: self.ids.baudrates.text = "None" self.ids.profiles.text = conf.plugin._printer_profile_manager.get_all( )['_default']['name'] self.first = False self.connection = conf.plugin._printer.get_current_connection() if self.connection != self.oldConnection: if self.connection[0] != 'Closed': self.ids.ports.disabled = True self.ids.baudrates.disabled = True self.ids.profiles.disabled = True self.ids.connect.text = "Disconnect" self.ids.connect.on_press = conf.plugin._printer.disconnect self.ids.ports.text = self.connection[1] self.ids.baudrates.text = str(self.connection[2]) self.ids.profiles.text = self.connection[3]['name'] else: self.ids.ports.disabled = False self.ids.baudrates.disabled = False self.ids.profiles.disabled = False self.ids.connect.text = "Connect" self.ids.connect.on_press = lambda: conf.plugin._printer.connect( self.ids.ports.text, self.ids.baudrates.text, self.ids. profiles.text) self.oldConnection = self.connection
def _get_options(): connection_options = get_connection_options() profile_options = printerProfileManager.get_all() default_profile = printerProfileManager.get_default() options = dict( ports=connection_options["ports"], baudrates=connection_options["baudrates"], printerProfiles=[dict(id=printer_profile["id"], name=printer_profile["name"] if "name" in printer_profile else printer_profile["id"]) for printer_profile in profile_options.values() if "id" in printer_profile], portPreference=connection_options["portPreference"], baudratePreference=connection_options["baudratePreference"], printerProfilePreference=default_profile["id"] if "id" in default_profile else None ) return options
def connectionCommand(): valid_commands = {"connect": [], "disconnect": []} command, data, response = get_json_command_from_request( request, valid_commands) if response is not None: return response if command == "connect": connection_options = get_connection_options() port = None port1 = None projector = None baudrate = None printerProfile = None if "port" in data.keys(): port = data["port"] if port not in connection_options["ports"]: return make_response("Invalid port: %s" % port, 400) if "baudrate" in data.keys(): baudrate = data["baudrate"] if baudrate not in connection_options["baudrates"]: return make_response("Invalid baudrate: %d" % baudrate, 400) if "printerProfile" in data.keys(): printerProfile = data["printerProfile"] if not printerProfileManager.exists(printerProfile): return make_response( "Invalid printer profile: %s" % printerProfile, 400) if "save" in data.keys() and data["save"]: port1 = data["port1"] projector = data["projector"] settings().set(["serial", "port"], port) settings().set(["serial", "port1"], port1) settings().set(["serial", "projector"], projector) settings().setInt(["serial", "baudrate"], baudrate) printerProfileManager.set_default(printerProfile) if "autoconnect" in data.keys(): settings().setBoolean(["serial", "autoconnect"], data["autoconnect"]) settings().save() printer.connect(port=port, baudrate=baudrate, profile=printerProfile) elif command == "disconnect": printer.disconnect() return NO_CONTENT
def update(self, dt): connections = get_connection_options() self.ids.ports.values = connections['ports'] self.ids.baudrates.values = map(str, connections['baudrates']) profiles = [] for i in conf.plugin._printer_profile_manager.get_all(): profiles.append(conf.plugin._printer_profile_manager.get_all()[i]['name']) self.ids.profiles.values = profiles if self.first: if connections['portPreference'] != None: self.ids.ports.text = connections['portPreference'] else: self.ids.ports.text = "None" if connections['baudratePreference'] != None: self.ids.baudrates.text = str(connections['baudratePreference']) else: self.ids.baudrates.text = "None" self.ids.profiles.text = conf.plugin._printer_profile_manager.get_all()['_default']['name'] self.first = False self.connection = conf.plugin._printer.get_current_connection() if self.connection != self.oldConnection: if self.connection[0] != 'Closed': self.ids.ports.disabled = True self.ids.baudrates.disabled = True self.ids.profiles.disabled = True self.ids.connect.text = "Disconnect" self.ids.connect.on_press = conf.plugin._printer.disconnect self.ids.ports.text = self.connection[1] self.ids.baudrates.text = str(self.connection[2]) self.ids.profiles.text = self.connection[3]['name'] else: self.ids.ports.disabled = False self.ids.baudrates.disabled = False self.ids.profiles.disabled = False self.ids.connect.text = "Connect" self.ids.connect.on_press = lambda: conf.plugin._printer.connect(self.ids.ports.text, self.ids.baudrates.text, self.ids.profiles.text) self.oldConnection = self.connection
def connectionCommand(): valid_commands = { "connect": [], "disconnect": [], "fake_ack": [] } command, data, response = get_json_command_from_request(request, valid_commands) if response is not None: return response if command == "connect": connection_options = get_connection_options() port = None baudrate = None printerProfile = None if "port" in data.keys(): port = data["port"] if port not in connection_options["ports"] and port != "AUTO": return make_response("Invalid port: %s" % port, 400) if "baudrate" in data.keys(): baudrate = data["baudrate"] if baudrate not in connection_options["baudrates"] and baudrate != 0: return make_response("Invalid baudrate: %d" % baudrate, 400) if "printerProfile" in data.keys(): printerProfile = data["printerProfile"] if not printerProfileManager.exists(printerProfile): return make_response("Invalid printer profile: %s" % printerProfile, 400) if "save" in data.keys() and data["save"]: settings().set(["serial", "port"], port) settings().setInt(["serial", "baudrate"], baudrate) printerProfileManager.set_default(printerProfile) if "autoconnect" in data.keys(): settings().setBoolean(["serial", "autoconnect"], data["autoconnect"]) settings().save() printer.connect(port=port, baudrate=baudrate, profile=printerProfile) elif command == "disconnect": printer.disconnect() elif command == "fake_ack": printer.fake_ack() return NO_CONTENT
def on_port_created(self, port, *args, **kwargs): # if we're already connected ignore it if self._printer.is_closed_or_error(): connection_options = get_connection_options() self._logger.info("on_port_created connection_options %s" % (repr(connection_options))) # is the new device in the port list? yes, tell the view model self._logger.info("Checking if %s is in %s" % (port, repr(connection_options["ports"]))) if port in connection_options["ports"]: self._plugin_manager.send_plugin_message(self._plugin_name, port) # if autoconnect and the new port matches, try to connect if self._settings.global_get_boolean(["serial", "autoconnect"]): self._logger.info("autoconnect_delay %d", self._settings.get(["autoconnect_delay"])) Timer(self._settings.get(["autoconnect_delay"]), self.do_auto_connect, [port]).start() else: self._logger.info("Not autoconnecting because autoconnect is turned off.") else: self._logger.warning("Won't autoconnect because %s isn't in %s" % (port, repr(connection_options["ports"]))) else: self._logger.warning("Not auto connecting because printer is not closed nor in error state.")
def getSettings(): logger = logging.getLogger(__name__) s = settings() connectionOptions = get_connection_options() data = { "api": { "enabled": s.getBoolean(["api", "enabled"]), "key": s.get(["api", "key"]) if admin_permission.can() else "n/a", "allowCrossOrigin": s.get(["api", "allowCrossOrigin"]) }, "appearance": { "name": s.get(["appearance", "name"]), "color": s.get(["appearance", "color"]), "colorTransparent": s.getBoolean(["appearance", "colorTransparent"]), "defaultLanguage": s.get(["appearance", "defaultLanguage"]) }, "printer": { "defaultExtrusionLength": s.getInt(["printerParameters", "defaultExtrusionLength"]) }, "webcam": { "streamUrl": s.get(["webcam", "stream"]), "snapshotUrl": s.get(["webcam", "snapshot"]), "ffmpegPath": s.get(["webcam", "ffmpeg"]), "bitrate": s.get(["webcam", "bitrate"]), "ffmpegThreads": s.get(["webcam", "ffmpegThreads"]), "watermark": s.getBoolean(["webcam", "watermark"]), "flipH": s.getBoolean(["webcam", "flipH"]), "flipV": s.getBoolean(["webcam", "flipV"]), "rotate90": s.getBoolean(["webcam", "rotate90"]) }, "feature": { "gcodeViewer": s.getBoolean(["gcodeViewer", "enabled"]), "temperatureGraph": s.getBoolean(["feature", "temperatureGraph"]), "waitForStart": s.getBoolean(["feature", "waitForStartOnConnect"]), "alwaysSendChecksum": s.getBoolean(["feature", "alwaysSendChecksum"]), "sdSupport": s.getBoolean(["feature", "sdSupport"]), "sdRelativePath": s.getBoolean(["feature", "sdRelativePath"]), "sdAlwaysAvailable": s.getBoolean(["feature", "sdAlwaysAvailable"]), "swallowOkAfterResend": s.getBoolean(["feature", "swallowOkAfterResend"]), "repetierTargetTemp": s.getBoolean(["feature", "repetierTargetTemp"]), "externalHeatupDetection": s.getBoolean(["feature", "externalHeatupDetection"]), "keyboardControl": s.getBoolean(["feature", "keyboardControl"]), "pollWatched": s.getBoolean(["feature", "pollWatched"]), "ignoreIdenticalResends": s.getBoolean(["feature", "ignoreIdenticalResends"]) }, "serial": { "port": connectionOptions["portPreference"], "baudrate": connectionOptions["baudratePreference"], "portOptions": connectionOptions["ports"], "baudrateOptions": connectionOptions["baudrates"], "autoconnect": s.getBoolean(["serial", "autoconnect"]), "timeoutConnection": s.getFloat(["serial", "timeout", "connection"]), "timeoutDetection": s.getFloat(["serial", "timeout", "detection"]), "timeoutCommunication": s.getFloat(["serial", "timeout", "communication"]), "timeoutTemperature": s.getFloat(["serial", "timeout", "temperature"]), "timeoutSdStatus": s.getFloat(["serial", "timeout", "sdStatus"]), "log": s.getBoolean(["serial", "log"]), "additionalPorts": s.get(["serial", "additionalPorts"]), "longRunningCommands": s.get(["serial", "longRunningCommands"]), "checksumRequiringCommands": s.get(["serial", "checksumRequiringCommands"]), "helloCommand": s.get(["serial", "helloCommand"]), "ignoreErrorsFromFirmware": s.getBoolean(["serial", "ignoreErrorsFromFirmware"]), "disconnectOnErrors": s.getBoolean(["serial", "disconnectOnErrors"]), "triggerOkForM29": s.getBoolean(["serial", "triggerOkForM29"]), "supportResendsWithoutOk": s.getBoolean(["serial", "supportResendsWithoutOk"]), "maxTimeoutsIdle": s.getInt(["serial", "maxCommunicationTimeouts", "idle"]), "maxTimeoutsPrinting": s.getInt(["serial", "maxCommunicationTimeouts", "printing"]), "maxTimeoutsLong": s.getInt(["serial", "maxCommunicationTimeouts", "long"]) }, "folder": { "uploads": s.getBaseFolder("uploads"), "timelapse": s.getBaseFolder("timelapse"), "timelapseTmp": s.getBaseFolder("timelapse_tmp"), "logs": s.getBaseFolder("logs"), "watched": s.getBaseFolder("watched") }, "temperature": { "profiles": s.get(["temperature", "profiles"]), "cutoff": s.getInt(["temperature", "cutoff"]) }, "system": { "actions": s.get(["system", "actions"]), "events": s.get(["system", "events"]) }, "terminalFilters": s.get(["terminalFilters"]), "scripts": { "gcode": { "afterPrinterConnected": None, "beforePrintStarted": None, "afterPrintCancelled": None, "afterPrintDone": None, "beforePrintPaused": None, "afterPrintResumed": None, "snippets": dict() } }, "server": { "commands": { "systemShutdownCommand": s.get(["server", "commands", "systemShutdownCommand"]), "systemRestartCommand": s.get(["server", "commands", "systemRestartCommand"]), "serverRestartCommand": s.get(["server", "commands", "serverRestartCommand"]) }, "diskspace": { "warning": s.getInt(["server", "diskspace", "warning"]), "critical": s.getInt(["server", "diskspace", "critical"]) } } } gcode_scripts = s.listScripts("gcode") if gcode_scripts: data["scripts"] = dict(gcode=dict()) for name in gcode_scripts: data["scripts"]["gcode"][name] = s.loadScript("gcode", name, source=True) def process_plugin_result(name, result): if result: try: jsonify(test=result) except: logger.exception("Error while jsonifying settings from plugin {}, please contact the plugin author about this".format(name)) if not "plugins" in data: data["plugins"] = dict() if "__enabled" in result: del result["__enabled"] data["plugins"][name] = result for plugin in octoprint.plugin.plugin_manager().get_implementations(octoprint.plugin.SettingsPlugin): try: result = plugin.on_settings_load() process_plugin_result(plugin._identifier, result) except TypeError: logger.warn("Could not load settings for plugin {name} ({version}) since it called super(...)".format(name=plugin._plugin_name, version=plugin._plugin_version)) logger.warn("in a way which has issues due to OctoPrint's dynamic reloading after plugin operations.") logger.warn("Please contact the plugin's author and ask to update the plugin to use a direct call like") logger.warn("octoprint.plugin.SettingsPlugin.on_settings_load(self) instead.") except: logger.exception("Could not load settings for plugin {name} ({version})".format(version=plugin._plugin_version, name=plugin._plugin_name)) return jsonify(data)
def run(self): if not self._allowRoot: self._check_for_root() global app global babel global printer global printerProfileManager global fileManager global slicingManager global analysisQueue global userManager global eventManager global loginManager global pluginManager global appSessionManager global pluginLifecycleManager global preemptiveCache global debug from tornado.ioloop import IOLoop from tornado.web import Application, RequestHandler import sys debug = self._debug # first initialize the settings singleton and make sure it uses given configfile and basedir if available s = settings(init=True, basedir=self._basedir, configfile=self._configfile) # then monkey patch a bunch of stuff util.tornado.fix_ioloop_scheduling() util.flask.enable_additional_translations(additional_folders=[s.getBaseFolder("translations")]) # setup app self._setup_app(app) # setup i18n self._setup_i18n(app) # then initialize logging self._setup_logging(self._debug, self._logConf) self._logger = logging.getLogger(__name__) def exception_logger(exc_type, exc_value, exc_tb): self._logger.error("Uncaught exception", exc_info=(exc_type, exc_value, exc_tb)) sys.excepthook = exception_logger self._logger.info("Starting OctoPrint %s" % DISPLAY_VERSION) # start the intermediary server self._start_intermediary_server(s) # then initialize the plugin manager pluginManager = octoprint.plugin.plugin_manager(init=True) printerProfileManager = PrinterProfileManager() eventManager = events.eventManager() analysisQueue = octoprint.filemanager.analysis.AnalysisQueue() slicingManager = octoprint.slicing.SlicingManager(s.getBaseFolder("slicingProfiles"), printerProfileManager) storage_managers = dict() storage_managers[octoprint.filemanager.FileDestinations.LOCAL] = octoprint.filemanager.storage.LocalFileStorage(s.getBaseFolder("uploads")) fileManager = octoprint.filemanager.FileManager(analysisQueue, slicingManager, printerProfileManager, initial_storage_managers=storage_managers) printer = Printer(fileManager, analysisQueue, printerProfileManager) appSessionManager = util.flask.AppSessionManager() pluginLifecycleManager = LifecycleManager(pluginManager) preemptiveCache = PreemptiveCache(os.path.join(s.getBaseFolder("data"), "preemptive_cache_config.yaml")) # ... and initialize all plugins def octoprint_plugin_inject_factory(name, implementation): """Factory for injections for all OctoPrintPlugins""" if not isinstance(implementation, octoprint.plugin.OctoPrintPlugin): # we only care about OctoPrintPlugins return None return dict( plugin_manager=pluginManager, printer_profile_manager=printerProfileManager, event_bus=eventManager, analysis_queue=analysisQueue, slicing_manager=slicingManager, file_manager=fileManager, printer=printer, app_session_manager=appSessionManager, plugin_lifecycle_manager=pluginLifecycleManager, data_folder=os.path.join(settings().getBaseFolder("data"), name), preemptive_cache=preemptiveCache ) def settings_plugin_inject_factory(name, implementation): """Factory for additional injections depending on plugin type""" if not isinstance(implementation, octoprint.plugin.SettingsPlugin): # we only care about SettingsPlugins return None # SettingsPlugin instnances get a PluginSettings instance injected default_settings = implementation.get_settings_defaults() get_preprocessors, set_preprocessors = implementation.get_settings_preprocessors() plugin_settings = octoprint.plugin.plugin_settings(name, defaults=default_settings, get_preprocessors=get_preprocessors, set_preprocessors=set_preprocessors) return dict(settings=plugin_settings) def settings_plugin_config_migration_and_cleanup(name, implementation): """Take care of migrating and cleaning up any old settings""" if not isinstance(implementation, octoprint.plugin.SettingsPlugin): return settings_version = implementation.get_settings_version() settings_migrator = implementation.on_settings_migrate if settings_version is not None and settings_migrator is not None: stored_version = implementation._settings.get_int([octoprint.plugin.SettingsPlugin.config_version_key]) if stored_version is None or stored_version < settings_version: settings_migrator(settings_version, stored_version) implementation._settings.set_int([octoprint.plugin.SettingsPlugin.config_version_key], settings_version) implementation.on_settings_cleanup() implementation._settings.save() implementation.on_settings_initialized() pluginManager.implementation_inject_factories=[octoprint_plugin_inject_factory, settings_plugin_inject_factory] pluginManager.initialize_implementations() settingsPlugins = pluginManager.get_implementations(octoprint.plugin.SettingsPlugin) for implementation in settingsPlugins: try: settings_plugin_config_migration_and_cleanup(implementation._identifier, implementation) except: self._logger.exception("Error while trying to migrate settings for plugin {}, ignoring it".format(implementation._identifier)) pluginManager.implementation_post_inits=[settings_plugin_config_migration_and_cleanup] pluginManager.log_all_plugins() # initialize file manager and register it for changes in the registered plugins fileManager.initialize() pluginLifecycleManager.add_callback(["enabled", "disabled"], lambda name, plugin: fileManager.reload_plugins()) # initialize slicing manager and register it for changes in the registered plugins slicingManager.initialize() pluginLifecycleManager.add_callback(["enabled", "disabled"], lambda name, plugin: slicingManager.reload_slicers()) # setup jinja2 self._setup_jinja2() # make sure plugin lifecycle events relevant for jinja2 are taken care of def template_enabled(name, plugin): if plugin.implementation is None or not isinstance(plugin.implementation, octoprint.plugin.TemplatePlugin): return self._register_additional_template_plugin(plugin.implementation) def template_disabled(name, plugin): if plugin.implementation is None or not isinstance(plugin.implementation, octoprint.plugin.TemplatePlugin): return self._unregister_additional_template_plugin(plugin.implementation) pluginLifecycleManager.add_callback("enabled", template_enabled) pluginLifecycleManager.add_callback("disabled", template_disabled) # setup assets self._setup_assets() # configure timelapse octoprint.timelapse.configure_timelapse() # setup command triggers events.CommandTrigger(printer) if self._debug: events.DebugEventListener() # setup access control userManagerName = s.get(["accessControl", "userManager"]) try: clazz = octoprint.util.get_class(userManagerName) userManager = clazz() except AttributeError as e: self._logger.exception("Could not instantiate user manager {}, falling back to FilebasedUserManager!".format(userManagerName)) userManager = octoprint.users.FilebasedUserManager() finally: userManager.enabled = s.getBoolean(["accessControl", "enabled"]) loginManager = LoginManager() loginManager.session_protection = "strong" loginManager.user_callback = load_user if not userManager.enabled: loginManager.anonymous_user = users.DummyUser principals.identity_loaders.appendleft(users.dummy_identity_loader) loginManager.init_app(app) # register API blueprint self._setup_blueprints() ## Tornado initialization starts here if self._host is None: self._host = s.get(["server", "host"]) if self._port is None: self._port = s.getInt(["server", "port"]) ioloop = IOLoop() ioloop.install() self._router = SockJSRouter(self._create_socket_connection, "/sockjs") upload_suffixes = dict(name=s.get(["server", "uploads", "nameSuffix"]), path=s.get(["server", "uploads", "pathSuffix"])) def mime_type_guesser(path): from octoprint.filemanager import get_mime_type return get_mime_type(path) download_handler_kwargs = dict( as_attachment=True, allow_client_caching=False ) additional_mime_types=dict(mime_type_guesser=mime_type_guesser) admin_validator = dict(access_validation=util.tornado.access_validation_factory(app, loginManager, util.flask.admin_validator)) no_hidden_files_validator = dict(path_validation=util.tornado.path_validation_factory(lambda path: not octoprint.util.is_hidden_path(path), status_code=404)) def joined_dict(*dicts): if not len(dicts): return dict() joined = dict() for d in dicts: joined.update(d) return joined server_routes = self._router.urls + [ # various downloads (r"/downloads/timelapse/([^/]*\.mp[g4])", util.tornado.LargeResponseHandler, joined_dict(dict(path=s.getBaseFolder("timelapse")), download_handler_kwargs, no_hidden_files_validator)), (r"/downloads/files/local/(.*)", util.tornado.LargeResponseHandler, joined_dict(dict(path=s.getBaseFolder("uploads")), download_handler_kwargs, no_hidden_files_validator, additional_mime_types)), (r"/downloads/logs/([^/]*)", util.tornado.LargeResponseHandler, joined_dict(dict(path=s.getBaseFolder("logs")), download_handler_kwargs, admin_validator)), # camera snapshot (r"/downloads/camera/current", util.tornado.UrlProxyHandler, dict(url=s.get(["webcam", "snapshot"]), as_attachment=True, access_validation=util.tornado.access_validation_factory(app, loginManager, util.flask.user_validator))), # generated webassets (r"/static/webassets/(.*)", util.tornado.LargeResponseHandler, dict(path=os.path.join(s.getBaseFolder("generated"), "webassets"))), # online indicators - text file with "online" as content and a transparent gif (r"/online.txt", util.tornado.StaticDataHandler, dict(data="online\n")), (r"/online.gif", util.tornado.StaticDataHandler, dict(data=bytes(base64.b64decode("R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")), content_type="image/gif")) ] # fetch additional routes from plugins for name, hook in pluginManager.get_hooks("octoprint.server.http.routes").items(): try: result = hook(list(server_routes)) except: self._logger.exception("There was an error while retrieving additional server routes from plugin hook {name}".format(**locals())) else: if isinstance(result, (list, tuple)): for entry in result: if not isinstance(entry, tuple) or not len(entry) == 3: continue if not isinstance(entry[0], basestring): continue if not isinstance(entry[2], dict): continue route, handler, kwargs = entry route = r"/plugin/{name}/{route}".format(name=name, route=route if not route.startswith("/") else route[1:]) self._logger.debug("Adding additional route {route} handled by handler {handler} and with additional arguments {kwargs!r}".format(**locals())) server_routes.append((route, handler, kwargs)) server_routes.append((r".*", util.tornado.UploadStorageFallbackHandler, dict(fallback=util.tornado.WsgiInputContainer(app.wsgi_app), file_prefix="octoprint-file-upload-", file_suffix=".tmp", suffixes=upload_suffixes))) self._tornado_app = Application(server_routes) max_body_sizes = [ ("POST", r"/api/files/([^/]*)", s.getInt(["server", "uploads", "maxSize"])), ("POST", r"/api/languages", 5 * 1024 * 1024) ] # allow plugins to extend allowed maximum body sizes for name, hook in pluginManager.get_hooks("octoprint.server.http.bodysize").items(): try: result = hook(list(max_body_sizes)) except: self._logger.exception("There was an error while retrieving additional upload sizes from plugin hook {name}".format(**locals())) else: if isinstance(result, (list, tuple)): for entry in result: if not isinstance(entry, tuple) or not len(entry) == 3: continue if not entry[0] in util.tornado.UploadStorageFallbackHandler.BODY_METHODS: continue if not isinstance(entry[2], int): continue method, route, size = entry route = r"/plugin/{name}/{route}".format(name=name, route=route if not route.startswith("/") else route[1:]) self._logger.debug("Adding maximum body size of {size}B for {method} requests to {route})".format(**locals())) max_body_sizes.append((method, route, size)) self._stop_intermediary_server() # initialize and bind the server self._server = util.tornado.CustomHTTPServer(self._tornado_app, max_body_sizes=max_body_sizes, default_max_body_size=s.getInt(["server", "maxSize"])) self._server.listen(self._port, address=self._host) eventManager.fire(events.Events.STARTUP) # auto connect if s.getBoolean(["serial", "autoconnect"]): (port, baudrate) = s.get(["serial", "port"]), s.getInt(["serial", "baudrate"]) printer_profile = printerProfileManager.get_default() connectionOptions = get_connection_options() if port in connectionOptions["ports"]: printer.connect(port=port, baudrate=baudrate, profile=printer_profile["id"] if "id" in printer_profile else "_default") # start up watchdogs if s.getBoolean(["feature", "pollWatched"]): # use less performant polling observer if explicitely configured observer = PollingObserver() else: # use os default observer = Observer() observer.schedule(util.watchdog.GcodeWatchdogHandler(fileManager, printer), s.getBaseFolder("watched")) observer.start() # run our startup plugins octoprint.plugin.call_plugin(octoprint.plugin.StartupPlugin, "on_startup", args=(self._host, self._port)) def call_on_startup(name, plugin): implementation = plugin.get_implementation(octoprint.plugin.StartupPlugin) if implementation is None: return implementation.on_startup(self._host, self._port) pluginLifecycleManager.add_callback("enabled", call_on_startup) # prepare our after startup function def on_after_startup(): self._logger.info("Listening on http://%s:%d" % (self._host, self._port)) # now this is somewhat ugly, but the issue is the following: startup plugins might want to do things for # which they need the server to be already alive (e.g. for being able to resolve urls, such as favicons # or service xmls or the like). While they are working though the ioloop would block. Therefore we'll # create a single use thread in which to perform our after-startup-tasks, start that and hand back # control to the ioloop def work(): octoprint.plugin.call_plugin(octoprint.plugin.StartupPlugin, "on_after_startup") def call_on_after_startup(name, plugin): implementation = plugin.get_implementation(octoprint.plugin.StartupPlugin) if implementation is None: return implementation.on_after_startup() pluginLifecycleManager.add_callback("enabled", call_on_after_startup) # when we are through with that we also run our preemptive cache if settings().getBoolean(["devel", "cache", "preemptive"]): self._execute_preemptive_flask_caching(preemptiveCache) import threading threading.Thread(target=work).start() ioloop.add_callback(on_after_startup) # prepare our shutdown function def on_shutdown(): # will be called on clean system exit and shutdown the watchdog observer and call the on_shutdown methods # on all registered ShutdownPlugins self._logger.info("Shutting down...") observer.stop() observer.join() octoprint.plugin.call_plugin(octoprint.plugin.ShutdownPlugin, "on_shutdown") if self._octoprint_daemon is not None: self._logger.info("Cleaning up daemon pidfile") self._octoprint_daemon.terminated() self._logger.info("Goodbye!") atexit.register(on_shutdown) def sigterm_handler(*args, **kwargs): # will stop tornado on SIGTERM, making the program exit cleanly def shutdown_tornado(): ioloop.stop() ioloop.add_callback_from_signal(shutdown_tornado) signal.signal(signal.SIGTERM, sigterm_handler) try: # this is the main loop - as long as tornado is running, OctoPrint is running ioloop.start() except (KeyboardInterrupt, SystemExit): pass except: self._logger.fatal("Now that is embarrassing... Something really really went wrong here. Please report this including the stacktrace below in OctoPrint's bugtracker. Thanks!") self._logger.exception("Stacktrace follows:")
def getSettings(): s = settings() connectionOptions = get_connection_options() data = { "api": { "enabled": s.getBoolean(["api", "enabled"]), "key": s.get(["api", "key"]) if admin_permission.can() else "n/a", "allowCrossOrigin": s.get(["api", "allowCrossOrigin"]) }, "appearance": { "name": s.get(["appearance", "name"]), "color": s.get(["appearance", "color"]), "colorTransparent": s.getBoolean(["appearance", "colorTransparent"]), "defaultLanguage": s.get(["appearance", "defaultLanguage"]) }, "printer": { "defaultExtrusionLength": s.getInt(["printerParameters", "defaultExtrusionLength"]) }, "webcam": { "streamUrl": s.get(["webcam", "stream"]), "snapshotUrl": s.get(["webcam", "snapshot"]), "ffmpegPath": s.get(["webcam", "ffmpeg"]), "bitrate": s.get(["webcam", "bitrate"]), "ffmpegThreads": s.get(["webcam", "ffmpegThreads"]), "watermark": s.getBoolean(["webcam", "watermark"]), "flipH": s.getBoolean(["webcam", "flipH"]), "flipV": s.getBoolean(["webcam", "flipV"]) }, "feature": { "gcodeViewer": s.getBoolean(["gcodeViewer", "enabled"]), "temperatureGraph": s.getBoolean(["feature", "temperatureGraph"]), "waitForStart": s.getBoolean(["feature", "waitForStartOnConnect"]), "alwaysSendChecksum": s.getBoolean(["feature", "alwaysSendChecksum"]), "sdSupport": s.getBoolean(["feature", "sdSupport"]), "sdAlwaysAvailable": s.getBoolean(["feature", "sdAlwaysAvailable"]), "swallowOkAfterResend": s.getBoolean(["feature", "swallowOkAfterResend"]), "repetierTargetTemp": s.getBoolean(["feature", "repetierTargetTemp"]), "keyboardControl": s.getBoolean(["feature", "keyboardControl"]) }, "serial": { "port": connectionOptions["portPreference"], "baudrate": connectionOptions["baudratePreference"], "portOptions": connectionOptions["ports"], "baudrateOptions": connectionOptions["baudrates"], "autoconnect": s.getBoolean(["serial", "autoconnect"]), "timeoutConnection": s.getFloat(["serial", "timeout", "connection"]), "timeoutDetection": s.getFloat(["serial", "timeout", "detection"]), "timeoutCommunication": s.getFloat(["serial", "timeout", "communication"]), "timeoutTemperature": s.getFloat(["serial", "timeout", "temperature"]), "timeoutSdStatus": s.getFloat(["serial", "timeout", "sdStatus"]), "log": s.getBoolean(["serial", "log"]), "additionalPorts": s.get(["serial", "additionalPorts"]) }, "folder": { "uploads": s.getBaseFolder("uploads"), "timelapse": s.getBaseFolder("timelapse"), "timelapseTmp": s.getBaseFolder("timelapse_tmp"), "logs": s.getBaseFolder("logs"), "watched": s.getBaseFolder("watched") }, "temperature": { "profiles": s.get(["temperature", "profiles"]) }, "system": { "actions": s.get(["system", "actions"]), "events": s.get(["system", "events"]) }, "terminalFilters": s.get(["terminalFilters"]), "scripts": { "gcode": { "afterPrinterConnected": None, "beforePrintStarted": None, "afterPrintCancelled": None, "afterPrintDone": None, "beforePrintPaused": None, "afterPrintResumed": None, "snippets": dict() } } } gcode_scripts = s.listScripts("gcode") if gcode_scripts: data["scripts"] = dict(gcode=dict()) for name in gcode_scripts: data["scripts"]["gcode"][name] = s.loadScript("gcode", name, source=True) def process_plugin_result(name, plugin, result): if result: if not "plugins" in data: data["plugins"] = dict() if "__enabled" in result: del result["__enabled"] data["plugins"][name] = result octoprint.plugin.call_plugin(octoprint.plugin.SettingsPlugin, "on_settings_load", callback=process_plugin_result) return jsonify(data)
class Server(): def __init__(self, configfile=None, basedir=None, host="0.0.0.0", port=5000, debug=False, allowRoot=False, logConf=None): self._configfile = configfile self._basedir = basedir self._host = host self._port = port self._debug = debug self._allowRoot = allowRoot self._logConf = logConf self._server = None def run(self): if not self._allowRoot: self._checkForRoot() global printer global printerProfileManager global fileManager global slicingManager global analysisQueue global userManager global eventManager global loginManager global pluginManager global appSessionManager global debug from tornado.ioloop import IOLoop from tornado.web import Application import sys debug = self._debug # first initialize the settings singleton and make sure it uses given configfile and basedir if available settings(init=True, basedir=self._basedir, configfile=self._configfile) # then initialize logging self._initLogging(self._debug, self._logConf) logger = logging.getLogger(__name__) def exception_logger(exc_type, exc_value, exc_tb): logger.error("Uncaught exception", exc_info=(exc_type, exc_value, exc_tb)) sys.excepthook = exception_logger logger.info("Starting OctoPrint %s" % DISPLAY_VERSION) # then initialize the plugin manager pluginManager = octoprint.plugin.plugin_manager(init=True) printerProfileManager = PrinterProfileManager() eventManager = events.eventManager() analysisQueue = octoprint.filemanager.analysis.AnalysisQueue() slicingManager = octoprint.slicing.SlicingManager( settings().getBaseFolder("slicingProfiles"), printerProfileManager) storage_managers = dict() storage_managers[ octoprint.filemanager.FileDestinations. LOCAL] = octoprint.filemanager.storage.LocalFileStorage( settings().getBaseFolder("uploads")) fileManager = octoprint.filemanager.FileManager( analysisQueue, slicingManager, printerProfileManager, initial_storage_managers=storage_managers) printer = Printer(fileManager, analysisQueue, printerProfileManager) appSessionManager = util.flask.AppSessionManager() def octoprint_plugin_inject_factory(name, implementation): if not isinstance(implementation, octoprint.plugin.OctoPrintPlugin): return None return dict(plugin_manager=pluginManager, printer_profile_manager=printerProfileManager, event_bus=eventManager, analysis_queue=analysisQueue, slicing_manager=slicingManager, file_manager=fileManager, printer=printer, app_session_manager=appSessionManager) def settings_plugin_inject_factory(name, implementation): if not isinstance(implementation, octoprint.plugin.SettingsPlugin): return None default_settings = implementation.get_settings_defaults() get_preprocessors, set_preprocessors = implementation.get_settings_preprocessors( ) plugin_settings = octoprint.plugin.plugin_settings( name, defaults=default_settings, get_preprocessors=get_preprocessors, set_preprocessors=set_preprocessors) return dict(settings=plugin_settings) pluginManager.initialize_implementations(additional_inject_factories=[ octoprint_plugin_inject_factory, settings_plugin_inject_factory ]) pluginManager.log_all_plugins() slicingManager.initialize() # configure additional template folders for jinja2 template_plugins = pluginManager.get_implementations( octoprint.plugin.TemplatePlugin) additional_template_folders = [] for plugin in template_plugins: folder = plugin.get_template_folder() if folder is not None: additional_template_folders.append( plugin.get_template_folder()) import jinja2 jinja_loader = jinja2.ChoiceLoader([ app.jinja_loader, jinja2.FileSystemLoader(additional_template_folders) ]) app.jinja_loader = jinja_loader del jinja2 app.jinja_env.add_extension("jinja2.ext.do") # configure timelapse octoprint.timelapse.configureTimelapse() # setup command triggers events.CommandTrigger(printer) if self._debug: events.DebugEventListener() if settings().getBoolean(["accessControl", "enabled"]): userManagerName = settings().get(["accessControl", "userManager"]) try: clazz = octoprint.util.get_class(userManagerName) userManager = clazz() except AttributeError, e: logger.exception( "Could not instantiate user manager %s, will run with accessControl disabled!" % userManagerName) app.wsgi_app = util.ReverseProxied( app.wsgi_app, settings().get(["server", "reverseProxy", "prefixHeader"]), settings().get(["server", "reverseProxy", "schemeHeader"]), settings().get(["server", "reverseProxy", "hostHeader"]), settings().get(["server", "reverseProxy", "prefixFallback"]), settings().get(["server", "reverseProxy", "schemeFallback"]), settings().get(["server", "reverseProxy", "hostFallback"])) secret_key = settings().get(["server", "secretKey"]) if not secret_key: import string from random import choice chars = string.ascii_lowercase + string.ascii_uppercase + string.digits secret_key = "".join(choice(chars) for _ in xrange(32)) settings().set(["server", "secretKey"], secret_key) settings().save() app.secret_key = secret_key loginManager = LoginManager() loginManager.session_protection = "strong" loginManager.user_callback = load_user if userManager is None: loginManager.anonymous_user = users.DummyUser principals.identity_loaders.appendleft(users.dummy_identity_loader) loginManager.init_app(app) if self._host is None: self._host = settings().get(["server", "host"]) if self._port is None: self._port = settings().getInt(["server", "port"]) app.debug = self._debug from octoprint.server.api import api from octoprint.server.apps import apps # register API blueprint app.register_blueprint(api, url_prefix="/api") app.register_blueprint(apps, url_prefix="/apps") # also register any blueprints defined in BlueprintPlugins blueprint_plugins = octoprint.plugin.plugin_manager( ).get_implementations(octoprint.plugin.BlueprintPlugin) for plugin in blueprint_plugins: name = plugin._identifier blueprint = plugin.get_blueprint() if blueprint is None: continue if plugin.is_blueprint_protected(): from octoprint.server.util import apiKeyRequestHandler, corsResponseHandler blueprint.before_request(apiKeyRequestHandler) blueprint.after_request(corsResponseHandler) url_prefix = "/plugin/{name}".format(name=name) app.register_blueprint(blueprint, url_prefix=url_prefix) logger.debug( "Registered API of plugin {name} under URL prefix {url_prefix}" .format(name=name, url_prefix=url_prefix)) ## Tornado initialization starts here ioloop = IOLoop() ioloop.install() self._router = SockJSRouter(self._createSocketConnection, "/sockjs") upload_suffixes = dict( name=settings().get(["server", "uploads", "nameSuffix"]), path=settings().get(["server", "uploads", "pathSuffix"])) self._tornado_app = Application(self._router.urls + [ (r"/downloads/timelapse/([^/]*\.mpg)", util.tornado.LargeResponseHandler, dict(path=settings().getBaseFolder("timelapse"), as_attachment=True)), #(r"/downloads/files/local/([^/]*\.(gco|gcode|g|stl))", util.tornado.LargeResponseHandler, dict(path=settings().getBaseFolder("uploads"), as_attachment=True)), (r"/downloads/files/local/([^/]*)", util.tornado.LargeResponseHandler, dict(path=settings().getBaseFolder("uploads"), as_attachment=True)), (r"/downloads/logs/([^/]*)", util.tornado.LargeResponseHandler, dict(path=settings().getBaseFolder("logs"), as_attachment=True, access_validation=util.tornado.access_validation_factory( app, loginManager, util.flask.admin_validator))), (r"/downloads/camera/current", util.tornado.UrlForwardHandler, dict(url=settings().get(["webcam", "snapshot"]), as_attachment=True, access_validation=util.tornado.access_validation_factory( app, loginManager, util.flask.user_validator))), (r".*", util.tornado.UploadStorageFallbackHandler, dict(fallback=util.tornado.WsgiInputContainer(app.wsgi_app), file_prefix="octoprint-file-upload-", file_suffix=".tmp", suffixes=upload_suffixes)) ]) max_body_sizes = [("POST", r"/api/files/([^/]*)", settings().getInt(["server", "uploads", "maxSize"]))] self._server = util.tornado.CustomHTTPServer( self._tornado_app, max_body_sizes=max_body_sizes, default_max_body_size=settings().getInt(["server", "maxSize"])) self._server.listen(self._port, address=self._host) eventManager.fire(events.Events.STARTUP) if settings().getBoolean(["serial", "autoconnect"]): (port, baudrate) = settings().get( ["serial", "port"]), settings().getInt(["serial", "baudrate"]) printer_profile = printerProfileManager.get_default() connectionOptions = get_connection_options() if port in connectionOptions["ports"]: printer.connect(port=port, baudrate=baudrate, profile=printer_profile["id"] if "id" in printer_profile else "_default") # start up watchdogs observer = Observer() observer.schedule( util.watchdog.GcodeWatchdogHandler(fileManager, printer), settings().getBaseFolder("watched")) observer.start() # run our startup plugins octoprint.plugin.call_plugin(octoprint.plugin.StartupPlugin, "on_startup", args=(self._host, self._port)) # prepare our after startup function def on_after_startup(): logger.info("Listening on http://%s:%d" % (self._host, self._port)) # now this is somewhat ugly, but the issue is the following: startup plugins might want to do things for # which they need the server to be already alive (e.g. for being able to resolve urls, such as favicons # or service xmls or the like). While they are working though the ioloop would block. Therefore we'll # create a single use thread in which to perform our after-startup-tasks, start that and hand back # control to the ioloop def work(): octoprint.plugin.call_plugin(octoprint.plugin.StartupPlugin, "on_after_startup") import threading threading.Thread(target=work).start() ioloop.add_callback(on_after_startup) # prepare our shutdown function def on_shutdown(): logger.info("Goodbye!") settings().setFloat(["serial", "zpos"], printer._currentZ) settings().save(True) observer.stop() observer.join() octoprint.plugin.call_plugin(octoprint.plugin.ShutdownPlugin, "on_shutdown") atexit.register(on_shutdown) try: ioloop.start() except KeyboardInterrupt: pass except: logger.fatal( "Now that is embarrassing... Something really really went wrong here. Please report this including the stacktrace below in OctoPrint's bugtracker. Thanks!" ) logger.exception("Stacktrace follows:")
class Server(): def __init__(self, configfile=None, basedir=None, host="0.0.0.0", port=5000, debug=False, allowRoot=False, logConf=None): self._configfile = configfile self._basedir = basedir self._host = host self._port = port self._debug = debug self._allowRoot = allowRoot self._logConf = logConf self._server = None self._logger = None self._lifecycle_callbacks = defaultdict(list) self._template_searchpaths = [] def run(self): if not self._allowRoot: self._check_for_root() global app global babel global printer global printerProfileManager global fileManager global slicingManager global analysisQueue global userManager global eventManager global loginManager global pluginManager global appSessionManager global pluginLifecycleManager global debug from tornado.ioloop import IOLoop from tornado.web import Application, RequestHandler import sys debug = self._debug # first initialize the settings singleton and make sure it uses given configfile and basedir if available s = settings(init=True, basedir=self._basedir, configfile=self._configfile) # then monkey patch a bunch of stuff util.tornado.fix_ioloop_scheduling() util.flask.enable_additional_translations(additional_folders=[s.getBaseFolder("translations")]) # setup app self._setup_app() # setup i18n self._setup_i18n(app) # then initialize logging self._setup_logging(self._debug, self._logConf) self._logger = logging.getLogger(__name__) def exception_logger(exc_type, exc_value, exc_tb): self._logger.error("Uncaught exception", exc_info=(exc_type, exc_value, exc_tb)) sys.excepthook = exception_logger self._logger.info("Starting OctoPrint %s" % DISPLAY_VERSION) # then initialize the plugin manager pluginManager = octoprint.plugin.plugin_manager(init=True) printerProfileManager = PrinterProfileManager() eventManager = events.eventManager() analysisQueue = octoprint.filemanager.analysis.AnalysisQueue() slicingManager = octoprint.slicing.SlicingManager(s.getBaseFolder("slicingProfiles"), printerProfileManager) storage_managers = dict() storage_managers[octoprint.filemanager.FileDestinations.LOCAL] = octoprint.filemanager.storage.LocalFileStorage(s.getBaseFolder("uploads")) fileManager = octoprint.filemanager.FileManager(analysisQueue, slicingManager, printerProfileManager, initial_storage_managers=storage_managers) printer = Printer(fileManager, analysisQueue, printerProfileManager) appSessionManager = util.flask.AppSessionManager() pluginLifecycleManager = LifecycleManager(pluginManager) def octoprint_plugin_inject_factory(name, implementation): if not isinstance(implementation, octoprint.plugin.OctoPrintPlugin): return None return dict( plugin_manager=pluginManager, printer_profile_manager=printerProfileManager, event_bus=eventManager, analysis_queue=analysisQueue, slicing_manager=slicingManager, file_manager=fileManager, printer=printer, app_session_manager=appSessionManager, plugin_lifecycle_manager=pluginLifecycleManager, data_folder=os.path.join(settings().getBaseFolder("data"), name) ) def settings_plugin_inject_factory(name, implementation): if not isinstance(implementation, octoprint.plugin.SettingsPlugin): return None default_settings = implementation.get_settings_defaults() get_preprocessors, set_preprocessors = implementation.get_settings_preprocessors() plugin_settings = octoprint.plugin.plugin_settings(name, defaults=default_settings, get_preprocessors=get_preprocessors, set_preprocessors=set_preprocessors) return dict(settings=plugin_settings) def settings_plugin_config_migration(name, implementation): if not isinstance(implementation, octoprint.plugin.SettingsPlugin): return settings_version = implementation.get_settings_version() settings_migrator = implementation.on_settings_migrate if settings_version is not None and settings_migrator is not None: stored_version = implementation._settings.get_int(["_config_version"]) if stored_version is None or stored_version < settings_version: settings_migrator(settings_version, stored_version) implementation._settings.set_int(["_config_version"], settings_version) implementation._settings.save() implementation.on_settings_initialized() pluginManager.implementation_inject_factories=[octoprint_plugin_inject_factory, settings_plugin_inject_factory] pluginManager.initialize_implementations() settingsPlugins = pluginManager.get_implementations(octoprint.plugin.SettingsPlugin) for implementation in settingsPlugins: try: settings_plugin_config_migration(implementation._identifier, implementation) except: self._logger.exception("Error while trying to migrate settings for plugin {}, ignoring it".format(implementation._identifier)) pluginManager.implementation_post_inits=[settings_plugin_config_migration] pluginManager.log_all_plugins() # initialize file manager and register it for changes in the registered plugins fileManager.initialize() pluginLifecycleManager.add_callback(["enabled", "disabled"], lambda name, plugin: fileManager.reload_plugins()) # initialize slicing manager and register it for changes in the registered plugins slicingManager.initialize() pluginLifecycleManager.add_callback(["enabled", "disabled"], lambda name, plugin: slicingManager.reload_slicers()) # setup jinja2 self._setup_jinja2() def template_enabled(name, plugin): if plugin.implementation is None or not isinstance(plugin.implementation, octoprint.plugin.TemplatePlugin): return self._register_additional_template_plugin(plugin.implementation) def template_disabled(name, plugin): if plugin.implementation is None or not isinstance(plugin.implementation, octoprint.plugin.TemplatePlugin): return self._unregister_additional_template_plugin(plugin.implementation) pluginLifecycleManager.add_callback("enabled", template_enabled) pluginLifecycleManager.add_callback("disabled", template_disabled) # setup assets self._setup_assets() # configure timelapse octoprint.timelapse.configureTimelapse() # setup command triggers events.CommandTrigger(printer) if self._debug: events.DebugEventListener() # setup access control if s.getBoolean(["accessControl", "enabled"]): userManagerName = s.get(["accessControl", "userManager"]) try: clazz = octoprint.util.get_class(userManagerName) userManager = clazz() except AttributeError, e: self._logger.exception("Could not instantiate user manager %s, will run with accessControl disabled!" % userManagerName) app.wsgi_app = util.ReverseProxied( app.wsgi_app, s.get(["server", "reverseProxy", "prefixHeader"]), s.get(["server", "reverseProxy", "schemeHeader"]), s.get(["server", "reverseProxy", "hostHeader"]), s.get(["server", "reverseProxy", "prefixFallback"]), s.get(["server", "reverseProxy", "schemeFallback"]), s.get(["server", "reverseProxy", "hostFallback"]) ) secret_key = s.get(["server", "secretKey"]) if not secret_key: import string from random import choice chars = string.ascii_lowercase + string.ascii_uppercase + string.digits secret_key = "".join(choice(chars) for _ in xrange(32)) s.set(["server", "secretKey"], secret_key) s.save() app.secret_key = secret_key loginManager = LoginManager() loginManager.session_protection = "strong" loginManager.user_callback = load_user if userManager is None: loginManager.anonymous_user = users.DummyUser principals.identity_loaders.appendleft(users.dummy_identity_loader) loginManager.init_app(app) if self._host is None: self._host = s.get(["server", "host"]) if self._port is None: self._port = s.getInt(["server", "port"]) app.debug = self._debug # register API blueprint self._setup_blueprints() ## Tornado initialization starts here ioloop = IOLoop() ioloop.install() self._router = SockJSRouter(self._create_socket_connection, "/sockjs") upload_suffixes = dict(name=s.get(["server", "uploads", "nameSuffix"]), path=s.get(["server", "uploads", "pathSuffix"])) server_routes = self._router.urls + [ # various downloads (r"/downloads/timelapse/([^/]*\.mpg)", util.tornado.LargeResponseHandler, dict(path=s.getBaseFolder("timelapse"), as_attachment=True)), (r"/downloads/files/local/(.*)", util.tornado.LargeResponseHandler, dict(path=s.getBaseFolder("uploads"), as_attachment=True, path_validation=util.tornado.path_validation_factory(lambda path: not os.path.basename(path).startswith("."), status_code=404))), (r"/downloads/logs/([^/]*)", util.tornado.LargeResponseHandler, dict(path=s.getBaseFolder("logs"), as_attachment=True, access_validation=util.tornado.access_validation_factory(app, loginManager, util.flask.admin_validator))), # camera snapshot (r"/downloads/camera/current", util.tornado.UrlForwardHandler, dict(url=s.get(["webcam", "snapshot"]), as_attachment=True, access_validation=util.tornado.access_validation_factory(app, loginManager, util.flask.user_validator))), # generated webassets (r"/static/webassets/(.*)", util.tornado.LargeResponseHandler, dict(path=os.path.join(s.getBaseFolder("generated"), "webassets"))) ] for name, hook in pluginManager.get_hooks("octoprint.server.http.routes").items(): try: result = hook(list(server_routes)) except: self._logger.exception("There was an error while retrieving additional server routes from plugin hook {name}".format(**locals())) else: if isinstance(result, (list, tuple)): for entry in result: if not isinstance(entry, tuple) or not len(entry) == 3: continue if not isinstance(entry[0], basestring): continue if not isinstance(entry[2], dict): continue route, handler, kwargs = entry route = r"/plugin/{name}/{route}".format(name=name, route=route if not route.startswith("/") else route[1:]) self._logger.debug("Adding additional route {route} handled by handler {handler} and with additional arguments {kwargs!r}".format(**locals())) server_routes.append((route, handler, kwargs)) server_routes.append((r".*", util.tornado.UploadStorageFallbackHandler, dict(fallback=util.tornado.WsgiInputContainer(app.wsgi_app), file_prefix="octoprint-file-upload-", file_suffix=".tmp", suffixes=upload_suffixes))) self._tornado_app = Application(server_routes) max_body_sizes = [ ("POST", r"/api/files/([^/]*)", s.getInt(["server", "uploads", "maxSize"])), ("POST", r"/api/languages", 5 * 1024 * 1024) ] # allow plugins to extend allowed maximum body sizes for name, hook in pluginManager.get_hooks("octoprint.server.http.bodysize").items(): try: result = hook(list(max_body_sizes)) except: self._logger.exception("There was an error while retrieving additional upload sizes from plugin hook {name}".format(**locals())) else: if isinstance(result, (list, tuple)): for entry in result: if not isinstance(entry, tuple) or not len(entry) == 3: continue if not entry[0] in util.tornado.UploadStorageFallbackHandler.BODY_METHODS: continue if not isinstance(entry[2], int): continue method, route, size = entry route = r"/plugin/{name}/{route}".format(name=name, route=route if not route.startswith("/") else route[1:]) self._logger.debug("Adding maximum body size of {size}B for {method} requests to {route})".format(**locals())) max_body_sizes.append((method, route, size)) self._server = util.tornado.CustomHTTPServer(self._tornado_app, max_body_sizes=max_body_sizes, default_max_body_size=s.getInt(["server", "maxSize"])) self._server.listen(self._port, address=self._host) eventManager.fire(events.Events.STARTUP) if s.getBoolean(["serial", "autoconnect"]): (port, baudrate) = s.get(["serial", "port"]), s.getInt(["serial", "baudrate"]) printer_profile = printerProfileManager.get_default() connectionOptions = get_connection_options() if port in connectionOptions["ports"]: printer.connect(port=port, baudrate=baudrate, profile=printer_profile["id"] if "id" in printer_profile else "_default") # start up watchdogs if s.getBoolean(["feature", "pollWatched"]): # use less performant polling observer if explicitely configured observer = PollingObserver() else: # use os default observer = Observer() observer.schedule(util.watchdog.GcodeWatchdogHandler(fileManager, printer), s.getBaseFolder("watched")) observer.start() # run our startup plugins octoprint.plugin.call_plugin(octoprint.plugin.StartupPlugin, "on_startup", args=(self._host, self._port)) def call_on_startup(name, plugin): implementation = plugin.get_implementation(octoprint.plugin.StartupPlugin) if implementation is None: return implementation.on_startup(self._host, self._port) pluginLifecycleManager.add_callback("enabled", call_on_startup) # prepare our after startup function def on_after_startup(): self._logger.info("Listening on http://%s:%d" % (self._host, self._port)) # now this is somewhat ugly, but the issue is the following: startup plugins might want to do things for # which they need the server to be already alive (e.g. for being able to resolve urls, such as favicons # or service xmls or the like). While they are working though the ioloop would block. Therefore we'll # create a single use thread in which to perform our after-startup-tasks, start that and hand back # control to the ioloop def work(): octoprint.plugin.call_plugin(octoprint.plugin.StartupPlugin, "on_after_startup") def call_on_after_startup(name, plugin): implementation = plugin.get_implementation(octoprint.plugin.StartupPlugin) if implementation is None: return implementation.on_after_startup() pluginLifecycleManager.add_callback("enabled", call_on_after_startup) import threading threading.Thread(target=work).start() ioloop.add_callback(on_after_startup) # prepare our shutdown function def on_shutdown(): # will be called on clean system exit and shutdown the watchdog observer and call the on_shutdown methods # on all registered ShutdownPlugins self._logger.info("Shutting down...") observer.stop() observer.join() octoprint.plugin.call_plugin(octoprint.plugin.ShutdownPlugin, "on_shutdown") self._logger.info("Goodbye!") atexit.register(on_shutdown) def sigterm_handler(*args, **kwargs): # will stop tornado on SIGTERM, making the program exit cleanly def shutdown_tornado(): ioloop.stop() ioloop.add_callback_from_signal(shutdown_tornado) signal.signal(signal.SIGTERM, sigterm_handler) try: # this is the main loop - as long as tornado is running, OctoPrint is running ioloop.start() except (KeyboardInterrupt, SystemExit): pass except: self._logger.fatal("Now that is embarrassing... Something really really went wrong here. Please report this including the stacktrace below in OctoPrint's bugtracker. Thanks!") self._logger.exception("Stacktrace follows:")
def getSettings(): logger = logging.getLogger(__name__) s = settings() connectionOptions = get_connection_options() data = { "api": { "enabled": s.getBoolean(["api", "enabled"]), "key": s.get(["api", "key"]) if admin_permission.can() else "n/a", "allowCrossOrigin": s.get(["api", "allowCrossOrigin"]) }, "appearance": { "name": s.get(["appearance", "name"]), "color": s.get(["appearance", "color"]), "colorTransparent": s.getBoolean(["appearance", "colorTransparent"]), "defaultLanguage": s.get(["appearance", "defaultLanguage"]) }, "printer": { "defaultExtrusionLength": s.getInt(["printerParameters", "defaultExtrusionLength"]) }, "webcam": { "streamUrl": s.get(["webcam", "stream"]), "snapshotUrl": s.get(["webcam", "snapshot"]), "ffmpegPath": s.get(["webcam", "ffmpeg"]), "bitrate": s.get(["webcam", "bitrate"]), "ffmpegThreads": s.get(["webcam", "ffmpegThreads"]), "watermark": s.getBoolean(["webcam", "watermark"]), "flipH": s.getBoolean(["webcam", "flipH"]), "flipV": s.getBoolean(["webcam", "flipV"]), "rotate90": s.getBoolean(["webcam", "rotate90"]) }, "feature": { "gcodeViewer": s.getBoolean(["gcodeViewer", "enabled"]), "temperatureGraph": s.getBoolean(["feature", "temperatureGraph"]), "waitForStart": s.getBoolean(["feature", "waitForStartOnConnect"]), "alwaysSendChecksum": s.getBoolean(["feature", "alwaysSendChecksum"]), "sdSupport": s.getBoolean(["feature", "sdSupport"]), "sdAlwaysAvailable": s.getBoolean(["feature", "sdAlwaysAvailable"]), "swallowOkAfterResend": s.getBoolean(["feature", "swallowOkAfterResend"]), "repetierTargetTemp": s.getBoolean(["feature", "repetierTargetTemp"]), "grbl": s.getBoolean(["feature", "grbl"]), "zaxis": s.getBoolean(["feature", "zaxis"]), "externalHeatupDetection": s.getBoolean(["feature", "externalHeatupDetection"]), "keyboardControl": s.getBoolean(["feature", "keyboardControl"]) }, "serial": { "port": connectionOptions["portPreference"], "baudrate": connectionOptions["baudratePreference"], "portOptions": connectionOptions["ports"], "baudrateOptions": connectionOptions["baudrates"], "autoconnect": s.getBoolean(["serial", "autoconnect"]), "timeoutConnection": s.getFloat(["serial", "timeout", "connection"]), "timeoutDetection": s.getFloat(["serial", "timeout", "detection"]), "timeoutCommunication": s.getFloat(["serial", "timeout", "communication"]), "timeoutTemperature": s.getFloat(["serial", "timeout", "temperature"]), "timeoutSdStatus": s.getFloat(["serial", "timeout", "sdStatus"]), "log": s.getBoolean(["serial", "log"]), "additionalPorts": s.get(["serial", "additionalPorts"]), "longRunningCommands": s.get(["serial", "longRunningCommands"]) }, "folder": { "uploads": s.getBaseFolder("uploads"), "timelapse": s.getBaseFolder("timelapse"), "timelapseTmp": s.getBaseFolder("timelapse_tmp"), "logs": s.getBaseFolder("logs"), "watched": s.getBaseFolder("watched") }, "temperature": { "profiles": s.get(["temperature", "profiles"]), "cutoff": s.getInt(["temperature", "cutoff"]) }, "system": { "actions": s.get(["system", "actions"]), "events": s.get(["system", "events"]) }, "terminalFilters": s.get(["terminalFilters"]), "scripts": { "gcode": { "afterPrinterConnected": None, "beforePrintStarted": None, "afterPrintCancelled": None, "afterPrintDone": None, "beforePrintPaused": None, "afterPrintResumed": None, "snippets": dict() } } } gcode_scripts = s.listScripts("gcode") if gcode_scripts: data["scripts"] = dict(gcode=dict()) for name in gcode_scripts: data["scripts"]["gcode"][name] = s.loadScript("gcode", name, source=True) def process_plugin_result(name, result): if result: try: jsonify(test=result) except: logger.exception( "Error while jsonifying settings from plugin {}, please contact the plugin author about this" .format(name)) if not "plugins" in data: data["plugins"] = dict() if "__enabled" in result: del result["__enabled"] data["plugins"][name] = result for plugin in octoprint.plugin.plugin_manager().get_implementations( octoprint.plugin.SettingsPlugin): try: result = plugin.on_settings_load() process_plugin_result(plugin._identifier, result) except TypeError: logger.warn( "Could not load settings for plugin {name} ({version}) since it called super(...)" .format(name=plugin._plugin_name, version=plugin._plugin_version)) logger.warn( "in a way which has issues due to OctoPrint's dynamic reloading after plugin operations." ) logger.warn( "Please contact the plugin's author and ask to update the plugin to use a direct call like" ) logger.warn( "octoprint.plugin.SettingsPlugin.on_settings_load(self) instead." ) except: logger.exception( "Could not load settings for plugin {name} ({version})".format( version=plugin._plugin_version, name=plugin._plugin_name)) #print("settings.py getSettings: data", data["plugins"]) return jsonify(data)
def run(self): if not self._allowRoot: self._check_for_root() global app global babel global printer global printerProfileManager global fileManager global slicingManager global analysisQueue global userManager global eventManager global loginManager global pluginManager global appSessionManager global pluginLifecycleManager global preemptiveCache global debug from tornado.ioloop import IOLoop from tornado.web import Application, RequestHandler import sys debug = self._debug # first initialize the settings singleton and make sure it uses given configfile and basedir if available s = settings(init=True, basedir=self._basedir, configfile=self._configfile) # then monkey patch a bunch of stuff util.tornado.fix_ioloop_scheduling() util.flask.enable_additional_translations(additional_folders=[s.getBaseFolder("translations")]) # setup app self._setup_app(app) # setup i18n self._setup_i18n(app) # then initialize logging self._setup_logging(self._debug, self._logConf) self._logger = logging.getLogger(__name__) def exception_logger(exc_type, exc_value, exc_tb): self._logger.error("Uncaught exception", exc_info=(exc_type, exc_value, exc_tb)) sys.excepthook = exception_logger self._logger.info("Starting BEEweb %s" % DISPLAY_VERSION) # start the intermediary server self._start_intermediary_server(s) # then initialize the plugin manager pluginManager = octoprint.plugin.plugin_manager(init=True) printerProfileManager = PrinterProfileManager() eventManager = events.eventManager() analysisQueue = octoprint.filemanager.analysis.AnalysisQueue() slicingManager = octoprint.slicing.SlicingManager(s.getBaseFolder("slicingProfiles"), printerProfileManager) storage_managers = dict() storage_managers[octoprint.filemanager.FileDestinations.LOCAL] = octoprint.filemanager.storage.LocalFileStorage(s.getBaseFolder("uploads")) fileManager = octoprint.filemanager.FileManager(analysisQueue, slicingManager, printerProfileManager, initial_storage_managers=storage_managers) printer = BeePrinter(fileManager, analysisQueue, printerProfileManager) appSessionManager = util.flask.AppSessionManager() pluginLifecycleManager = LifecycleManager(pluginManager) preemptiveCache = PreemptiveCache(os.path.join(s.getBaseFolder("data"), "preemptive_cache_config.yaml")) # ... and initialize all plugins def octoprint_plugin_inject_factory(name, implementation): """Factory for injections for all OctoPrintPlugins""" if not isinstance(implementation, octoprint.plugin.OctoPrintPlugin): # we only care about OctoPrintPlugins return None return dict( plugin_manager=pluginManager, printer_profile_manager=printerProfileManager, event_bus=eventManager, analysis_queue=analysisQueue, slicing_manager=slicingManager, file_manager=fileManager, printer=printer, app_session_manager=appSessionManager, plugin_lifecycle_manager=pluginLifecycleManager, data_folder=os.path.join(settings().getBaseFolder("data"), name), preemptive_cache=preemptiveCache ) def settings_plugin_inject_factory(name, implementation): """Factory for additional injections depending on plugin type""" if not isinstance(implementation, octoprint.plugin.SettingsPlugin): # we only care about SettingsPlugins return None # SettingsPlugin instnances get a PluginSettings instance injected default_settings = implementation.get_settings_defaults() get_preprocessors, set_preprocessors = implementation.get_settings_preprocessors() plugin_settings = octoprint.plugin.plugin_settings(name, defaults=default_settings, get_preprocessors=get_preprocessors, set_preprocessors=set_preprocessors) return dict(settings=plugin_settings) def settings_plugin_config_migration_and_cleanup(name, implementation): """Take care of migrating and cleaning up any old settings""" if not isinstance(implementation, octoprint.plugin.SettingsPlugin): return settings_version = implementation.get_settings_version() settings_migrator = implementation.on_settings_migrate if settings_version is not None and settings_migrator is not None: stored_version = implementation._settings.get_int([octoprint.plugin.SettingsPlugin.config_version_key]) if stored_version is None or stored_version < settings_version: settings_migrator(settings_version, stored_version) implementation._settings.set_int([octoprint.plugin.SettingsPlugin.config_version_key], settings_version) implementation.on_settings_cleanup() implementation._settings.save() implementation.on_settings_initialized() pluginManager.implementation_inject_factories=[octoprint_plugin_inject_factory, settings_plugin_inject_factory] pluginManager.initialize_implementations() settingsPlugins = pluginManager.get_implementations(octoprint.plugin.SettingsPlugin) for implementation in settingsPlugins: try: settings_plugin_config_migration_and_cleanup(implementation._identifier, implementation) except: self._logger.exception("Error while trying to migrate settings for plugin {}, ignoring it".format(implementation._identifier)) pluginManager.implementation_post_inits=[settings_plugin_config_migration_and_cleanup] pluginManager.log_all_plugins() # initialize file manager and register it for changes in the registered plugins fileManager.initialize() pluginLifecycleManager.add_callback(["enabled", "disabled"], lambda name, plugin: fileManager.reload_plugins()) # initialize slicing manager and register it for changes in the registered plugins slicingManager.initialize() pluginLifecycleManager.add_callback(["enabled", "disabled"], lambda name, plugin: slicingManager.reload_slicers()) # setup jinja2 self._setup_jinja2() # make sure plugin lifecycle events relevant for jinja2 are taken care of def template_enabled(name, plugin): if plugin.implementation is None or not isinstance(plugin.implementation, octoprint.plugin.TemplatePlugin): return self._register_additional_template_plugin(plugin.implementation) def template_disabled(name, plugin): if plugin.implementation is None or not isinstance(plugin.implementation, octoprint.plugin.TemplatePlugin): return self._unregister_additional_template_plugin(plugin.implementation) pluginLifecycleManager.add_callback("enabled", template_enabled) pluginLifecycleManager.add_callback("disabled", template_disabled) # setup assets self._setup_assets() # configure timelapse octoprint.timelapse.configure_timelapse() # setup command triggers events.CommandTrigger(printer) if self._debug: events.DebugEventListener() # setup access control userManagerName = s.get(["accessControl", "userManager"]) try: clazz = octoprint.util.get_class(userManagerName) userManager = clazz() except AttributeError as e: self._logger.exception("Could not instantiate user manager {}, falling back to FilebasedUserManager!".format(userManagerName)) userManager = octoprint.users.FilebasedUserManager() finally: userManager.enabled = s.getBoolean(["accessControl", "enabled"]) loginManager = LoginManager() loginManager.session_protection = "strong" loginManager.user_callback = load_user if not userManager.enabled: loginManager.anonymous_user = users.DummyUser principals.identity_loaders.appendleft(users.dummy_identity_loader) loginManager.init_app(app) # register API blueprint self._setup_blueprints() ## Tornado initialization starts here if self._host is None: self._host = s.get(["server", "host"]) if self._port is None: self._port = s.getInt(["server", "port"]) ioloop = IOLoop() ioloop.install() self._router = SockJSRouter(self._create_socket_connection, "/sockjs") upload_suffixes = dict(name=s.get(["server", "uploads", "nameSuffix"]), path=s.get(["server", "uploads", "pathSuffix"])) def mime_type_guesser(path): from octoprint.filemanager import get_mime_type return get_mime_type(path) download_handler_kwargs = dict( as_attachment=True, allow_client_caching=False ) additional_mime_types=dict(mime_type_guesser=mime_type_guesser) admin_validator = dict(access_validation=util.tornado.access_validation_factory(app, loginManager, util.flask.admin_validator)) no_hidden_files_validator = dict(path_validation=util.tornado.path_validation_factory(lambda path: not octoprint.util.is_hidden_path(path), status_code=404)) def joined_dict(*dicts): if not len(dicts): return dict() joined = dict() for d in dicts: joined.update(d) return joined server_routes = self._router.urls + [ # various downloads (r"/downloads/timelapse/([^/]*\.mp[g4])", util.tornado.LargeResponseHandler, joined_dict(dict(path=s.getBaseFolder("timelapse")), download_handler_kwargs, no_hidden_files_validator)), (r"/downloads/files/local/(.*)", util.tornado.LargeResponseHandler, joined_dict(dict(path=s.getBaseFolder("uploads")), download_handler_kwargs, no_hidden_files_validator, additional_mime_types)), (r"/downloads/logs/([^/]*)", util.tornado.LargeResponseHandler, joined_dict(dict(path=s.getBaseFolder("logs")), download_handler_kwargs, admin_validator)), # camera snapshot (r"/downloads/camera/current", util.tornado.UrlProxyHandler, dict(url=s.get(["webcam", "snapshot"]), as_attachment=True, access_validation=util.tornado.access_validation_factory(app, loginManager, util.flask.user_validator))), # generated webassets (r"/static/webassets/(.*)", util.tornado.LargeResponseHandler, dict(path=os.path.join(s.getBaseFolder("generated"), "webassets"))), # online indicators - text file with "online" as content and a transparent gif (r"/online.txt", util.tornado.StaticDataHandler, dict(data="online\n")), (r"/online.gif", util.tornado.StaticDataHandler, dict(data=bytes(base64.b64decode("R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")), content_type="image/gif")), (r"/stl/([^/]*\.stl)", util.tornado.LargeResponseHandler, dict(path=os.path.join(s.getBaseFolder("stls")))), (r"/firmware/([^/]*\.BIN)", util.tornado.LargeResponseHandler, dict(path=os.path.join(s.getBaseFolder("firmware")))) ] # fetch additional routes from plugins for name, hook in pluginManager.get_hooks("octoprint.server.http.routes").items(): try: result = hook(list(server_routes)) except: self._logger.exception("There was an error while retrieving additional server routes from plugin hook {name}".format(**locals())) else: if isinstance(result, (list, tuple)): for entry in result: if not isinstance(entry, tuple) or not len(entry) == 3: continue if not isinstance(entry[0], basestring): continue if not isinstance(entry[2], dict): continue route, handler, kwargs = entry route = r"/plugin/{name}/{route}".format(name=name, route=route if not route.startswith("/") else route[1:]) self._logger.debug("Adding additional route {route} handled by handler {handler} and with additional arguments {kwargs!r}".format(**locals())) server_routes.append((route, handler, kwargs)) server_routes.append((r".*", util.tornado.UploadStorageFallbackHandler, dict(fallback=util.tornado.WsgiInputContainer(app.wsgi_app), file_prefix="octoprint-file-upload-", file_suffix=".tmp", suffixes=upload_suffixes))) self._tornado_app = Application(server_routes) max_body_sizes = [ ("POST", r"/api/files/([^/]*)", s.getInt(["server", "uploads", "maxSize"])), ("POST", r"/api/languages", 5 * 1024 * 1024) ] # allow plugins to extend allowed maximum body sizes for name, hook in pluginManager.get_hooks("octoprint.server.http.bodysize").items(): try: result = hook(list(max_body_sizes)) except: self._logger.exception("There was an error while retrieving additional upload sizes from plugin hook {name}".format(**locals())) else: if isinstance(result, (list, tuple)): for entry in result: if not isinstance(entry, tuple) or not len(entry) == 3: continue if not entry[0] in util.tornado.UploadStorageFallbackHandler.BODY_METHODS: continue if not isinstance(entry[2], int): continue method, route, size = entry route = r"/plugin/{name}/{route}".format(name=name, route=route if not route.startswith("/") else route[1:]) self._logger.debug("Adding maximum body size of {size}B for {method} requests to {route})".format(**locals())) max_body_sizes.append((method, route, size)) self._stop_intermediary_server() # initialize and bind the server self._server = util.tornado.CustomHTTPServer(self._tornado_app, max_body_sizes=max_body_sizes, default_max_body_size=s.getInt(["server", "maxSize"])) self._server.listen(self._port, address=self._host) eventManager.fire(events.Events.STARTUP) # auto connect #if s.getBoolean(["serial", "autoconnect"]): # (port, baudrate) = s.get(["serial", "port"]), s.getInt(["serial", "baudrate"]) # printer_profile = printerProfileManager.get_default() # connectionOptions = get_connection_options() # if port in connectionOptions["ports"]: # printer.connect(port=port, baudrate=baudrate, profile=printer_profile["id"] if "id" in printer_profile else "_default") if s.getBoolean(["usb", "autoconnect"]): printer_profile = printerProfileManager.get_default() connectionOptions = get_connection_options() printer.connect() # start up watchdogs if s.getBoolean(["feature", "pollWatched"]): # use less performant polling observer if explicitely configured observer = PollingObserver() else: # use os default observer = Observer() observer.schedule(util.watchdog.GcodeWatchdogHandler(fileManager, printer), s.getBaseFolder("watched")) observer.start() # run our startup plugins octoprint.plugin.call_plugin(octoprint.plugin.StartupPlugin, "on_startup", args=(self._host, self._port)) def call_on_startup(name, plugin): implementation = plugin.get_implementation(octoprint.plugin.StartupPlugin) if implementation is None: return implementation.on_startup(self._host, self._port) pluginLifecycleManager.add_callback("enabled", call_on_startup) # prepare our after startup function def on_after_startup(): self._logger.info("Listening on http://%s:%d" % (self._host, self._port)) # now this is somewhat ugly, but the issue is the following: startup plugins might want to do things for # which they need the server to be already alive (e.g. for being able to resolve urls, such as favicons # or service xmls or the like). While they are working though the ioloop would block. Therefore we'll # create a single use thread in which to perform our after-startup-tasks, start that and hand back # control to the ioloop def work(): octoprint.plugin.call_plugin(octoprint.plugin.StartupPlugin, "on_after_startup") def call_on_after_startup(name, plugin): implementation = plugin.get_implementation(octoprint.plugin.StartupPlugin) if implementation is None: return implementation.on_after_startup() pluginLifecycleManager.add_callback("enabled", call_on_after_startup) # when we are through with that we also run our preemptive cache if settings().getBoolean(["devel", "cache", "preemptive"]): self._execute_preemptive_flask_caching(preemptiveCache) import threading threading.Thread(target=work).start() from octoprint.server.util.connection_util import detect_bvc_printer_connection if printer.is_closed_or_error(): bvc_conn_thread = threading.Thread(target=detect_bvc_printer_connection, args=(printer.connect, )) bvc_conn_thread.daemon = True bvc_conn_thread.start() ioloop.add_callback(on_after_startup) # prepare our shutdown function def on_shutdown(): # will be called on clean system exit and shutdown the watchdog observer and call the on_shutdown methods # on all registered ShutdownPlugins self._logger.info("Shutting down...") observer.stop() observer.join() octoprint.plugin.call_plugin(octoprint.plugin.ShutdownPlugin, "on_shutdown") if self._octoprint_daemon is not None: self._logger.info("Cleaning up daemon pidfile") self._octoprint_daemon.terminated() self._logger.info("Goodbye!") atexit.register(on_shutdown) def sigterm_handler(*args, **kwargs): # will stop tornado on SIGTERM, making the program exit cleanly def shutdown_tornado(): ioloop.stop() ioloop.add_callback_from_signal(shutdown_tornado) signal.signal(signal.SIGTERM, sigterm_handler) try: # this is the main loop - as long as tornado is running, OctoPrint is running ioloop.start() except (KeyboardInterrupt, SystemExit): pass except: self._logger.fatal("Now that is embarrassing... Something really really went wrong here. Please report this including the stacktrace below in OctoPrint's bugtracker. Thanks!") self._logger.exception("Stacktrace follows:")
def getSettings(): s = settings() connectionOptions = get_connection_options() data = { "api": { "enabled": s.getBoolean(["api", "enabled"]), "key": s.get(["api", "key"]) if admin_permission.can() else "n/a", "allowCrossOrigin": s.get(["api", "allowCrossOrigin"]) }, "appearance": { "name": s.get(["appearance", "name"]), "color": s.get(["appearance", "color"]), "colorTransparent": s.getBoolean(["appearance", "colorTransparent"]), "defaultLanguage": s.get(["appearance", "defaultLanguage"]) }, "printer": { "defaultExtrusionLength": s.getInt(["printerParameters", "defaultExtrusionLength"]) }, "webcam": { "streamUrl": s.get(["webcam", "stream"]), "snapshotUrl": s.get(["webcam", "snapshot"]), "ffmpegPath": s.get(["webcam", "ffmpeg"]), "bitrate": s.get(["webcam", "bitrate"]), "ffmpegThreads": s.get(["webcam", "ffmpegThreads"]), "watermark": s.getBoolean(["webcam", "watermark"]), "flipH": s.getBoolean(["webcam", "flipH"]), "flipV": s.getBoolean(["webcam", "flipV"]), "rotate90": s.getBoolean(["webcam", "rotate90"]) }, "feature": { "gcodeViewer": s.getBoolean(["gcodeViewer", "enabled"]), "temperatureGraph": s.getBoolean(["feature", "temperatureGraph"]), "waitForStart": s.getBoolean(["feature", "waitForStartOnConnect"]), "alwaysSendChecksum": s.getBoolean(["feature", "alwaysSendChecksum"]), "sdSupport": s.getBoolean(["feature", "sdSupport"]), "sdAlwaysAvailable": s.getBoolean(["feature", "sdAlwaysAvailable"]), "swallowOkAfterResend": s.getBoolean(["feature", "swallowOkAfterResend"]), "repetierTargetTemp": s.getBoolean(["feature", "repetierTargetTemp"]), "externalHeatupDetection": s.getBoolean(["feature", "externalHeatupDetection"]), "keyboardControl": s.getBoolean(["feature", "keyboardControl"]) }, "serial": { "port": connectionOptions["portPreference"], "baudrate": connectionOptions["baudratePreference"], "portOptions": connectionOptions["ports"], "baudrateOptions": connectionOptions["baudrates"], "autoconnect": s.getBoolean(["serial", "autoconnect"]), "timeoutConnection": s.getFloat(["serial", "timeout", "connection"]), "timeoutDetection": s.getFloat(["serial", "timeout", "detection"]), "timeoutCommunication": s.getFloat(["serial", "timeout", "communication"]), "timeoutTemperature": s.getFloat(["serial", "timeout", "temperature"]), "timeoutSdStatus": s.getFloat(["serial", "timeout", "sdStatus"]), "log": s.getBoolean(["serial", "log"]), "additionalPorts": s.get(["serial", "additionalPorts"]), "longRunningCommands": s.get(["serial", "longRunningCommands"]) }, "folder": { "uploads": s.getBaseFolder("uploads"), "timelapse": s.getBaseFolder("timelapse"), "timelapseTmp": s.getBaseFolder("timelapse_tmp"), "logs": s.getBaseFolder("logs"), "watched": s.getBaseFolder("watched") }, "temperature": { "profiles": s.get(["temperature", "profiles"]), "cutoff": s.getInt(["temperature", "cutoff"]) }, "system": { "actions": s.get(["system", "actions"]), "events": s.get(["system", "events"]) }, "terminalFilters": s.get(["terminalFilters"]), "scripts": { "gcode": { "afterPrinterConnected": None, "beforePrintStarted": None, "afterPrintCancelled": None, "afterPrintDone": None, "beforePrintPaused": None, "afterPrintResumed": None, "snippets": dict() } } } gcode_scripts = s.listScripts("gcode") if gcode_scripts: data["scripts"] = dict(gcode=dict()) for name in gcode_scripts: data["scripts"]["gcode"][name] = s.loadScript("gcode", name, source=True) def process_plugin_result(name, plugin, result): if result: if not "plugins" in data: data["plugins"] = dict() if "__enabled" in result: del result["__enabled"] data["plugins"][name] = result octoprint.plugin.call_plugin(octoprint.plugin.SettingsPlugin, "on_settings_load", callback=process_plugin_result) return jsonify(data)