Просмотр исходного кода

Merge branch 'CURA-4252_improve_zeroconf_service'

Diego Prado Gesto 7 лет назад
Родитель
Сommit
40f548ba99
1 измененных файлов с 56 добавлено и 7 удалено
  1. 56 7
      plugins/UM3NetworkPrinting/NetworkPrinterOutputDevicePlugin.py

+ 56 - 7
plugins/UM3NetworkPrinting/NetworkPrinterOutputDevicePlugin.py

@@ -1,19 +1,18 @@
 # Copyright (c) 2017 Ultimaker B.V.
 # Cura is released under the terms of the LGPLv3 or higher.
 
-import os
 import time
 import json
+from queue import Queue
+from threading import Event, Thread
 
-from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, pyqtSlot
+from PyQt5.QtCore import QObject, pyqtSlot
 from PyQt5.QtCore import QUrl
 from PyQt5.QtGui import QDesktopServices
-from PyQt5.QtNetwork import QNetworkRequest, QNetworkAccessManager, QNetworkReply
-from PyQt5.QtQml import QQmlComponent, QQmlContext
+from PyQt5.QtNetwork import QNetworkRequest, QNetworkAccessManager
 from UM.Application import Application
 from UM.Logger import Logger
 from UM.OutputDevice.OutputDevicePlugin import OutputDevicePlugin
-from UM.PluginRegistry import PluginRegistry
 from UM.Preferences import Preferences
 from UM.Signal import Signal, signalemitter
 from zeroconf import Zeroconf, ServiceBrowser, ServiceStateChange, ServiceInfo  # type: ignore
@@ -57,6 +56,16 @@ class NetworkPrinterOutputDevicePlugin(QObject, OutputDevicePlugin):
 
         self._network_requests_buffer = {}  # store api responses until data is complete
 
+        # The zeroconf service changed requests are handled in a separate thread, so we can re-schedule the requests
+        # which fail to get detailed service info.
+        # Any new or re-scheduled requests will be appended to the request queue, and the handling thread will pick
+        # them up and process them.
+        self._service_changed_request_queue = Queue()
+        self._service_changed_request_event = Event()
+        self._service_changed_request_thread = Thread(target = self._handleOnServiceChangedRequests,
+                                                      daemon = True)
+        self._service_changed_request_thread.start()
+
     addPrinterSignal = Signal()
     removePrinterSignal = Signal()
     printerListChanged = Signal()
@@ -76,7 +85,7 @@ class NetworkPrinterOutputDevicePlugin(QObject, OutputDevicePlugin):
         # After network switching, one must make a new instance of Zeroconf
         # On windows, the instance creation is very fast (unnoticable). Other platforms?
         self._zero_conf = Zeroconf()
-        self._browser = ServiceBrowser(self._zero_conf, u'_ultimaker._tcp.local.', [self._onServiceChanged])
+        self._browser = ServiceBrowser(self._zero_conf, u'_ultimaker._tcp.local.', [self._appendServiceChangedRequest])
 
         # Look for manual instances from preference
         for address in self._manual_instances:
@@ -265,7 +274,8 @@ class NetworkPrinterOutputDevicePlugin(QObject, OutputDevicePlugin):
         else:
             self.getOutputDeviceManager().removeOutputDevice(key)
 
-    ##  Handler for zeroConf detection
+    ##  Handler for zeroConf detection.
+    #   Return True or False indicating if the process succeeded.
     def _onServiceChanged(self, zeroconf, service_type, name, state_change):
         if state_change == ServiceStateChange.Added:
             Logger.log("d", "Bonjour service added: %s" % name)
@@ -295,11 +305,50 @@ class NetworkPrinterOutputDevicePlugin(QObject, OutputDevicePlugin):
                         Logger.log("w", "The type of the found device is '%s', not 'printer'! Ignoring.." % type_of_device )
             else:
                 Logger.log("w", "Could not get information about %s" % name)
+                return False
 
         elif state_change == ServiceStateChange.Removed:
             Logger.log("d", "Bonjour service removed: %s" % name)
             self.removePrinterSignal.emit(str(name))
 
+        return True
+
+    ##  Appends a service changed request so later the handling thread will pick it up and processes it.
+    def _appendServiceChangedRequest(self, zeroconf, service_type, name, state_change):
+        # append the request and set the event so the event handling thread can pick it up
+        item = (zeroconf, service_type, name, state_change)
+        self._service_changed_request_queue.put(item)
+        self._service_changed_request_event.set()
+
+    def _handleOnServiceChangedRequests(self):
+        while True:
+            # wait for the event to be set
+            self._service_changed_request_event.wait(timeout = 5.0)
+            # stop if the application is shutting down
+            if Application.getInstance().isShuttingDown():
+                return
+
+            self._service_changed_request_event.clear()
+
+            # handle all pending requests
+            reschedule_requests = []  # a list of requests that have failed so later they will get re-scheduled
+            while not self._service_changed_request_queue.empty():
+                request = self._service_changed_request_queue.get()
+                zeroconf, service_type, name, state_change = request
+                try:
+                    result = self._onServiceChanged(zeroconf, service_type, name, state_change)
+                    if not result:
+                        reschedule_requests.append(request)
+                except Exception:
+                    Logger.logException("e", "Failed to get service info for [%s] [%s], the request will be rescheduled",
+                                        service_type, name)
+                    reschedule_requests.append(request)
+
+            # re-schedule the failed requests if any
+            if reschedule_requests:
+                for request in reschedule_requests:
+                    self._service_changed_request_queue.put(request)
+
     @pyqtSlot()
     def openControlPanel(self):
         Logger.log("d", "Opening print jobs web UI...")