diff --git a/plugins/SliceInfoPlugin/SliceInfo.py b/plugins/SliceInfoPlugin/SliceInfo.py index af8a8b9853..090d703325 100755 --- a/plugins/SliceInfoPlugin/SliceInfo.py +++ b/plugins/SliceInfoPlugin/SliceInfo.py @@ -5,14 +5,13 @@ import json import os import platform import time -from typing import cast, Optional, Set +from typing import cast, Optional, Set, TYPE_CHECKING from PyQt5.QtCore import pyqtSlot, QObject +from PyQt5.QtNetwork import QNetworkRequest from UM.Extension import Extension -from UM.Application import Application from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator -from UM.Message import Message from UM.i18n import i18nCatalog from UM.Logger import Logger from UM.PluginRegistry import PluginRegistry @@ -20,7 +19,8 @@ from UM.Qt.Duration import DurationFormat from cura import ApplicationMetadata -from .SliceInfoJob import SliceInfoJob +if TYPE_CHECKING: + from PyQt5.QtNetwork import QNetworkReply catalog = i18nCatalog("cura") @@ -36,7 +36,8 @@ class SliceInfo(QObject, Extension): QObject.__init__(self, parent) Extension.__init__(self) - self._application = Application.getInstance() + from cura.CuraApplication import CuraApplication + self._application = CuraApplication.getInstance() self._application.getOutputDeviceManager().writeStarted.connect(self._onWriteStarted) self._application.getPreferences().addPreference("info/send_slice_info", True) @@ -56,7 +57,7 @@ class SliceInfo(QObject, Extension): ## Perform action based on user input. # Note that clicking "Disable" won't actually disable the data sending, but rather take the user to preferences where they can disable it. def messageActionTriggered(self, message_id, action_id): - Application.getInstance().getPreferences().setValue("info/asked_send_slice_info", True) + self._application.getPreferences().setValue("info/asked_send_slice_info", True) if action_id == "MoreInfo": self.showMoreInfoDialog() self.send_slice_info_message.hide() @@ -69,7 +70,7 @@ class SliceInfo(QObject, Extension): def _createDialog(self, qml_name): Logger.log("d", "Creating dialog [%s]", qml_name) file_path = os.path.join(PluginRegistry.getInstance().getPluginPath(self.getPluginId()), qml_name) - dialog = Application.getInstance().createQmlComponent(file_path, {"manager": self}) + dialog = self._application.createQmlComponent(file_path, {"manager": self}) return dialog @pyqtSlot(result = str) @@ -87,12 +88,10 @@ class SliceInfo(QObject, Extension): @pyqtSlot(bool) def setSendSliceInfo(self, enabled: bool): - Application.getInstance().getPreferences().setValue("info/send_slice_info", enabled) + self._application.getPreferences().setValue("info/send_slice_info", enabled) def _getUserModifiedSettingKeys(self) -> list: - from cura.CuraApplication import CuraApplication - application = cast(CuraApplication, Application.getInstance()) - machine_manager = application.getMachineManager() + machine_manager = self._application.getMachineManager() global_stack = machine_manager.activeMachine user_modified_setting_keys = set() # type: Set[str] @@ -106,30 +105,28 @@ class SliceInfo(QObject, Extension): def _onWriteStarted(self, output_device): try: - if not Application.getInstance().getPreferences().getValue("info/send_slice_info"): + if not self._application.getPreferences().getValue("info/send_slice_info"): Logger.log("d", "'info/send_slice_info' is turned off.") return # Do nothing, user does not want to send data - from cura.CuraApplication import CuraApplication - application = cast(CuraApplication, Application.getInstance()) - machine_manager = application.getMachineManager() - print_information = application.getPrintInformation() + machine_manager = self._application.getMachineManager() + print_information = self._application.getPrintInformation() global_stack = machine_manager.activeMachine data = dict() # The data that we're going to submit. data["time_stamp"] = time.time() data["schema_version"] = 0 - data["cura_version"] = application.getVersion() + data["cura_version"] = self._application.getVersion() data["cura_build_type"] = ApplicationMetadata.CuraBuildType - active_mode = Application.getInstance().getPreferences().getValue("cura/active_mode") + active_mode = self._application.getPreferences().getValue("cura/active_mode") if active_mode == 0: data["active_mode"] = "recommended" else: data["active_mode"] = "custom" - data["camera_view"] = application.getPreferences().getValue("general/camera_perspective_mode") + data["camera_view"] = self._application.getPreferences().getValue("general/camera_perspective_mode") if data["camera_view"] == "orthographic": data["camera_view"] = "orthogonal" #The database still only recognises the old name "orthogonal". @@ -142,7 +139,7 @@ class SliceInfo(QObject, Extension): machine_settings_changed_by_user = True data["machine_settings_changed_by_user"] = machine_settings_changed_by_user - data["language"] = Application.getInstance().getPreferences().getValue("general/language") + data["language"] = self._application.getPreferences().getValue("general/language") data["os"] = {"type": platform.system(), "version": platform.version()} data["active_machine"] = {"definition_id": global_stack.definition.getId(), @@ -184,7 +181,7 @@ class SliceInfo(QObject, Extension): data["models"] = [] # Listing all files placed on the build plate - for node in DepthFirstIterator(application.getController().getScene().getRoot()): + for node in DepthFirstIterator(self._application.getController().getScene().getRoot()): if node.callDecoration("isSliceable"): model = dict() model["hash"] = node.getMeshData().getHash() @@ -263,10 +260,23 @@ class SliceInfo(QObject, Extension): # Convert data to bytes binary_data = json.dumps(data).encode("utf-8") - # Sending slice info non-blocking - reportJob = SliceInfoJob(self.info_url, binary_data) - reportJob.start() + # Send slice info non-blocking + network_manager = self._application.getHttpNetworkRequestManager() + network_manager.post(self.info_url, data = binary_data, + callback = self._onRequestFinished, error_callback = self._onRequestError) except Exception: # We really can't afford to have a mistake here, as this would break the sending of g-code to a device # (Either saving or directly to a printer). The functionality of the slice data is not *that* important. Logger.logException("e", "Exception raised while sending slice info.") # But we should be notified about these problems of course. + + def _onRequestFinished(self, reply: "QNetworkReply") -> None: + status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) + if status_code == 200: + Logger.log("i", "SliceInfo sent successfully") + return + + data = reply.readAll().data().decode("utf-8") + Logger.log("e", "SliceInfo request failed, status code %s, data: %s", status_code, data) + + def _onRequestError(self, reply: "QNetworkReply", error: "QNetworkReply.NetworkError") -> None: + Logger.log("e", "Got error for SliceInfo request: %s", reply.errorString()) diff --git a/plugins/SliceInfoPlugin/SliceInfoJob.py b/plugins/SliceInfoPlugin/SliceInfoJob.py deleted file mode 100644 index 50d6b560f1..0000000000 --- a/plugins/SliceInfoPlugin/SliceInfoJob.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2017 Ultimaker B.V. -# Cura is released under the terms of the LGPLv3 or higher. -from UM.Job import Job -from UM.Logger import Logger -from UM.Platform import Platform - -import ssl -import urllib.request -import urllib.error - -import certifi - - -class SliceInfoJob(Job): - def __init__(self, url, data): - super().__init__() - self._url = url - self._data = data - - def run(self): - if not self._url or not self._data: - Logger.log("e", "URL or DATA for sending slice info was not set!") - return - - # CURA-6698 Create an SSL context and use certifi CA certificates for verification. - context = ssl.SSLContext(protocol = ssl.PROTOCOL_TLSv1_2) - context.load_verify_locations(cafile = certifi.where()) - - # Submit data - kwoptions = {"data": self._data, - "timeout": 5, - "context": context} - - Logger.log("i", "Sending anonymous slice info to [%s]...", self._url) - - try: - f = urllib.request.urlopen(self._url, **kwoptions) - Logger.log("i", "Sent anonymous slice info.") - f.close() - except urllib.error.HTTPError: - Logger.logException("e", "An HTTP error occurred while trying to send slice information") - except Exception: # We don't want any exception to cause problems - Logger.logException("e", "An exception occurred while trying to send slice information")