BackendPlugin.py 3.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. # Copyright (c) 2023 Ultimaker B.V.
  2. # Cura is released under the terms of the LGPLv3 or higher.
  3. import subprocess
  4. from typing import Optional, List
  5. from UM.Logger import Logger
  6. from UM.Settings.AdditionalSettingDefinitionAppender import AdditionalSettingDefinitionsAppender
  7. class BackendPlugin(AdditionalSettingDefinitionsAppender):
  8. def __init__(self) -> None:
  9. super().__init__()
  10. self.__port: int = 0
  11. self._plugin_address: str = "127.0.0.1"
  12. self._plugin_command: Optional[List[str]] = None
  13. self._process = None
  14. self._is_running = False
  15. self._supported_slots: List[int] = []
  16. self.appender_type = "PLUGIN"
  17. def getSupportedSlots(self) -> List[int]:
  18. return self._supported_slots
  19. def isRunning(self):
  20. return self._is_running
  21. def setPort(self, port: int) -> None:
  22. self.__port = port
  23. def getPort(self) -> int:
  24. return self.__port
  25. def getAddress(self) -> str:
  26. return self._plugin_address
  27. def _validatePluginCommand(self) -> list[str]:
  28. """
  29. Validate the plugin command and add the port parameter if it is missing.
  30. :return: A list of strings containing the validated plugin command.
  31. """
  32. if not self._plugin_command or "--port" in self._plugin_command:
  33. return self._plugin_command or []
  34. return self._plugin_command + ["--port", str(self.__port)]
  35. def start(self) -> bool:
  36. """
  37. Starts the backend_plugin process.
  38. :return: True if the plugin process started successfully, False otherwise.
  39. """
  40. try:
  41. # STDIN needs to be None because we provide no input, but communicate via a local socket instead.
  42. # The NUL device sometimes doesn't exist on some computers.
  43. Logger.info(f"Starting backend_plugin [{self._plugin_id}] with command: {self._validatePluginCommand()}")
  44. self._process = subprocess.Popen(self._validatePluginCommand(), stdin = None)
  45. self._is_running = True
  46. return True
  47. except PermissionError:
  48. Logger.log("e", f"Couldn't start backend_plugin [{self._plugin_id}]: No permission to execute process.")
  49. except FileNotFoundError:
  50. Logger.logException("e", f"Unable to find backend_plugin executable [{self._plugin_id}]")
  51. except BlockingIOError:
  52. Logger.logException("e", f"Couldn't start backend_plugin [{self._plugin_id}]: Resource is temporarily unavailable")
  53. except OSError as e:
  54. Logger.logException("e", f"Couldn't start backend_plugin [{self._plugin_id}]: Operating system is blocking it (antivirus?)")
  55. return False
  56. def stop(self) -> bool:
  57. if not self._process:
  58. self._is_running = False
  59. return True # Nothing to stop
  60. try:
  61. self._process.terminate()
  62. return_code = self._process.wait()
  63. self._is_running = False
  64. Logger.log("d", f"Backend_plugin [{self._plugin_id}] was killed. Received return code {return_code}")
  65. return True
  66. except PermissionError:
  67. Logger.log("e", "Unable to kill running engine. Access is denied.")
  68. return False