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:
Jaime van Kessel 2023-07-11 11:24:28 +02:00
parent 26bf00ccc0
commit f3bc7bf28a
No known key found for this signature in database
GPG key ID: C85F7A3AF1BAA7C4
3 changed files with 98 additions and 1 deletions

74
cura/BackendPlugin.py Normal file
View 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

View file

@ -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()

View file

@ -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