Пример #1
0
    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."
            )
Пример #2
0
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
Пример #3
0
    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
Пример #4
0
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
Пример #5
0
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
Пример #6
0
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
Пример #7
0
    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
Пример #8
0
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
Пример #9
0
	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.")
Пример #10
0
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)
Пример #11
0
	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:")
Пример #12
0
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)
Пример #13
0
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:")
Пример #14
0
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:")
Пример #15
0
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)
Пример #16
0
	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:")
Пример #17
0
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)