mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-06 22:47:29 -06:00
Move backend plugin logic to Cura from Uranium
I was running into abstraction issues when it was defined in Uranium. Instead of trying to fight those, it's just easier to move it to Cura CURA-10717
This commit is contained in:
parent
26bf00ccc0
commit
f3bc7bf28a
3 changed files with 98 additions and 1 deletions
74
cura/BackendPlugin.py
Normal file
74
cura/BackendPlugin.py
Normal file
|
@ -0,0 +1,74 @@
|
|||
# Copyright (c) 2023 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
import subprocess
|
||||
from typing import Optional, List
|
||||
|
||||
from UM.Logger import Logger
|
||||
from UM.PluginObject import PluginObject
|
||||
|
||||
|
||||
class BackendPlugin(PluginObject):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self.__port: int = 0
|
||||
self._plugin_address: str = "127.0.0.1"
|
||||
self._plugin_command: Optional[List[str]] = None
|
||||
self._process = None
|
||||
self._is_running = False
|
||||
|
||||
def isRunning(self):
|
||||
return self._is_running
|
||||
|
||||
def setPort(self, port: int) -> None:
|
||||
self.__port = port
|
||||
|
||||
def getPort(self) -> int:
|
||||
return self.__port
|
||||
|
||||
def _validatePluginCommand(self) -> list[str]:
|
||||
"""
|
||||
Validate the plugin command and add the port parameter if it is missing.
|
||||
|
||||
:return: A list of strings containing the validated plugin command.
|
||||
"""
|
||||
if not self._plugin_command or "--port" in self._plugin_command:
|
||||
return self._plugin_command or []
|
||||
|
||||
return self._plugin_command + ["--port", str(self.__port)]
|
||||
|
||||
def start(self) -> bool:
|
||||
"""
|
||||
Starts the backend_plugin process.
|
||||
|
||||
:return: True if the plugin process started successfully, False otherwise.
|
||||
"""
|
||||
try:
|
||||
# STDIN needs to be None because we provide no input, but communicate via a local socket instead.
|
||||
# The NUL device sometimes doesn't exist on some computers.
|
||||
self._process = subprocess.Popen(self._validatePluginCommand(), stdin = None)
|
||||
self._is_running = True
|
||||
return True
|
||||
except PermissionError:
|
||||
Logger.log("e", f"Couldn't start backend_plugin [{self._plugin_id}]: No permission to execute process.")
|
||||
except FileNotFoundError:
|
||||
Logger.logException("e", f"Unable to find backend_plugin executable [{self._plugin_id}]")
|
||||
except BlockingIOError:
|
||||
Logger.logException("e", f"Couldn't start backend_plugin [{self._plugin_id}]: Resource is temporarily unavailable")
|
||||
except OSError as e:
|
||||
Logger.logException("e", f"Couldn't start backend_plugin [{self._plugin_id}]: Operating system is blocking it (antivirus?)")
|
||||
return False
|
||||
|
||||
def stop(self) -> bool:
|
||||
if not self._process:
|
||||
self._is_running = False
|
||||
return True # Nothing to stop
|
||||
|
||||
try:
|
||||
self._process.terminate()
|
||||
return_code = self._process.wait()
|
||||
self._is_running = False
|
||||
Logger.log("d", f"Backend_plugin [{self._plugin_id}] was killed. Received return code {return_code}")
|
||||
return True
|
||||
except PermissionError:
|
||||
Logger.log("e", "Unable to kill running engine. Access is denied.")
|
||||
return False
|
|
@ -205,6 +205,8 @@ class CuraApplication(QtApplication):
|
|||
self._cura_scene_controller = None
|
||||
self._machine_error_checker = None
|
||||
|
||||
self._backend_plugins: List[BackendPlugin] = []
|
||||
|
||||
self._machine_settings_manager = MachineSettingsManager(self, parent = self)
|
||||
self._material_management_model = None
|
||||
self._quality_management_model = None
|
||||
|
@ -792,6 +794,7 @@ class CuraApplication(QtApplication):
|
|||
|
||||
self._plugin_registry.addType("profile_reader", self._addProfileReader)
|
||||
self._plugin_registry.addType("profile_writer", self._addProfileWriter)
|
||||
self._plugin_registry.addType("backend_plugin", self._addBackendPlugin)
|
||||
|
||||
if Platform.isLinux():
|
||||
lib_suffixes = {"", "64", "32", "x32"} # A few common ones on different distributions.
|
||||
|
@ -1730,6 +1733,12 @@ class CuraApplication(QtApplication):
|
|||
def _addProfileWriter(self, profile_writer):
|
||||
pass
|
||||
|
||||
def _addBackendPlugin(self, backend_plugin: "BackendPlugin") -> None:
|
||||
self._backend_plugins.append(backend_plugin)
|
||||
|
||||
def getBackendPlugins(self) -> List["BackendPlugin"]:
|
||||
return self._backend_plugins
|
||||
|
||||
@pyqtSlot("QSize")
|
||||
def setMinimumWindowSize(self, size):
|
||||
main_window = self.getMainWindow()
|
||||
|
|
|
@ -70,7 +70,7 @@ class CuraEngineBackend(QObject, Backend):
|
|||
os.path.join(CuraApplication.getInstallPrefix(), "bin"),
|
||||
os.path.dirname(os.path.abspath(sys.executable)),
|
||||
]
|
||||
|
||||
self._last_backend_plugin_port = self._port + 1000
|
||||
for path in search_path:
|
||||
engine_path = os.path.join(path, executable_name)
|
||||
if os.path.isfile(engine_path):
|
||||
|
@ -176,6 +176,20 @@ class CuraEngineBackend(QObject, Backend):
|
|||
|
||||
application.initializationFinished.connect(self.initialize)
|
||||
|
||||
def startPlugins(self) -> None:
|
||||
"""
|
||||
Ensure that all backend plugins are started
|
||||
:return:
|
||||
"""
|
||||
backend_plugins = CuraApplication.getInstance().getBackendPlugins()
|
||||
for backend_plugin in backend_plugins:
|
||||
if backend_plugin.isRunning():
|
||||
continue
|
||||
# Set the port to prevent plugins from using the same one.
|
||||
backend_plugin.setPort(self._last_backend_plugin_port)
|
||||
self.__last_backend_plugin_port += 1
|
||||
backend_plugin.start()
|
||||
|
||||
def _resetLastSliceTimeStats(self) -> None:
|
||||
self._time_start_process = None
|
||||
self._time_send_message = None
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue