def _onGetPrintJobFinished(self, reply): status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) if not self._printers: return # Ignore the data for now, we don't have info about a printer yet. printer = self._printers[0] if status_code == 200: try: result = json.loads(bytes(reply.readAll()).decode("utf-8")) except json.decoder.JSONDecodeError: Logger.log("w", "Received an invalid print job state message: Not valid JSON.") return if printer.activePrintJob is None: print_job = PrintJobOutputModel(output_controller=self._output_controller) printer.updateActivePrintJob(print_job) else: print_job = printer.activePrintJob print_job.updateState(result["state"]) print_job.updateTimeElapsed(result["time_elapsed"]) print_job.updateTimeTotal(result["time_total"]) print_job.updateName(result["name"]) elif status_code == 404: # No job found, so delete the active print job (if any!) printer.updateActivePrintJob(None) else: Logger.log("w", "Got status code {status_code} while trying to get printer data".format(status_code=status_code))
def _sendNextGcodeLine(self): if self._gcode_position >= len(self._gcode): self._printers[0].updateActivePrintJob(None) self._is_printing = False return line = self._gcode[self._gcode_position] if ";" in line: line = line[:line.find(";")] line = line.strip() # Don't send empty lines. But we do have to send something, so send M105 instead. # Don't send the M0 or M1 to the machine, as M0 and M1 are handled as an LCD menu pause. if line == "" or line == "M0" or line == "M1": line = "M105" checksum = functools.reduce(lambda x, y: x ^ y, map(ord, "N%d%s" % (self._gcode_position, line))) self._sendCommand("N%d%s*%d" % (self._gcode_position, line, checksum)) progress = (self._gcode_position / len(self._gcode)) elapsed_time = int(time() - self._print_start_time) print_job = self._printers[0].activePrintJob if print_job is None: controller = GenericOutputController(self) controller.setCanUpdateFirmware(True) print_job = PrintJobOutputModel(output_controller=controller, name=CuraApplication.getInstance().getPrintInformation().jobName) print_job.updateState("printing") self._printers[0].updateActivePrintJob(print_job) print_job.updateTimeElapsed(elapsed_time) estimated_time = self._print_estimated_time if progress > .1: estimated_time = self._print_estimated_time * (1 - progress) + elapsed_time print_job.updateTimeTotal(estimated_time) self._gcode_position += 1
def _updatePrintJob(self, print_job: PrintJobOutputModel, data: Dict) -> None: print_job.updateTimeTotal(data["time_total"]) print_job.updateTimeElapsed(data["time_elapsed"]) print_job.updateState(data["status"]) print_job.updateOwner(data["owner"])
def _createPrintJobModel(self, data: Dict[str, Any]) -> PrintJobOutputModel: print_job = PrintJobOutputModel(output_controller=ClusterUM3PrinterOutputController(self), key=data["uuid"], name= data["name"]) print_job.stateChanged.connect(self._printJobStateChanged) self._print_jobs.append(print_job) return print_job
def _onRequestFinished(self, reply): if reply.error() == QNetworkReply.TimeoutError: Logger.log("w", "Received a timeout on a request to the instance") self._connection_state_before_timeout = self._connection_state self.setConnectionState(ConnectionState.error) return if self._connection_state_before_timeout and reply.error( ) == QNetworkReply.NoError: # There was a timeout, but we got a correct answer again. if self._last_response_time: Logger.log( "d", "We got a response from the instance after %s of silence", time() - self._last_response_time) self.setConnectionState(self._connection_state_before_timeout) self._connection_state_before_timeout = None if reply.error() == QNetworkReply.NoError: self._last_response_time = time() http_status_code = reply.attribute( QNetworkRequest.HttpStatusCodeAttribute) if not http_status_code: # Received no or empty reply return if reply.operation() == QNetworkAccessManager.GetOperation: if self._api_prefix + "printer" in reply.url().toString( ): # Status update from /printer. if not self._printers: self._createPrinterList() # An OctoPrint instance has a single printer. printer = self._printers[0] if http_status_code == 200: if not self.acceptsCommands: self._setAcceptsCommands(True) self.setConnectionText( i18n_catalog.i18nc( "@info:status", "Connected to OctoPrint on {0}").format( self._key)) if self._connection_state == ConnectionState.connecting: self.setConnectionState(ConnectionState.connected) try: json_data = json.loads( bytes(reply.readAll()).decode("utf-8")) except json.decoder.JSONDecodeError: Logger.log( "w", "Received invalid JSON from octoprint instance.") json_data = {} if "temperature" in json_data: if not self._number_of_extruders_set: self._number_of_extruders = 0 while "tool%d" % self._number_of_extruders in json_data[ "temperature"]: self._number_of_extruders += 1 if self._number_of_extruders > 1: # Recreate list of printers to match the new _number_of_extruders self._createPrinterList() printer = self._printers[0] if self._number_of_extruders > 0: self._number_of_extruders_set = True # Check for hotend temperatures for index in range(0, self._number_of_extruders): extruder = printer.extruders[index] if ("tool%d" % index) in json_data["temperature"]: hotend_temperatures = json_data["temperature"][ "tool%d" % index] extruder.updateTargetHotendTemperature( hotend_temperatures["target"]) extruder.updateHotendTemperature( hotend_temperatures["actual"]) else: extruder.updateTargetHotendTemperature(0) extruder.updateHotendTemperature(0) if "bed" in json_data["temperature"]: bed_temperatures = json_data["temperature"]["bed"] actual_temperature = bed_temperatures[ "actual"] if bed_temperatures[ "actual"] is not None else -1 printer.updateBedTemperature(actual_temperature) target_temperature = bed_temperatures[ "target"] if bed_temperatures[ "target"] is not None else -1 printer.updateTargetBedTemperature( target_temperature) else: printer.updateBedTemperature(-1) printer.updateTargetBedTemperature(0) printer_state = "offline" if "state" in json_data: if json_data["state"]["flags"]["error"]: printer_state = "error" elif json_data["state"]["flags"]["paused"]: printer_state = "paused" elif json_data["state"]["flags"]["printing"]: printer_state = "printing" elif json_data["state"]["flags"]["ready"]: printer_state = "idle" printer.updateState(printer_state) elif http_status_code == 401: printer.updateState("offline") if printer.activePrintJob: printer.activePrintJob.updateState("offline") self.setConnectionText( i18n_catalog.i18nc( "@info:status", "OctoPrint on {0} does not allow access to print"). format(self._key)) pass elif http_status_code == 409: if self._connection_state == ConnectionState.connecting: self.setConnectionState(ConnectionState.connected) printer.updateState("offline") if printer.activePrintJob: printer.activePrintJob.updateState("offline") self.setConnectionText( i18n_catalog.i18nc( "@info:status", "The printer connected to OctoPrint on {0} is not operational" ).format(self._key)) else: printer.updateState("offline") if printer.activePrintJob: printer.activePrintJob.updateState("offline") Logger.log("w", "Received an unexpected returncode: %d", http_status_code) elif self._api_prefix + "job" in reply.url().toString( ): # Status update from /job: if not self._printers: return # Ignore the data for now, we don't have info about a printer yet. printer = self._printers[0] if http_status_code == 200: try: json_data = json.loads( bytes(reply.readAll()).decode("utf-8")) except json.decoder.JSONDecodeError: Logger.log( "w", "Received invalid JSON from octoprint instance.") json_data = {} if printer.activePrintJob is None: print_job = PrintJobOutputModel( output_controller=self._output_controller) printer.updateActivePrintJob(print_job) else: print_job = printer.activePrintJob print_job_state = "offline" if "state" in json_data: if json_data["state"] == "Error": print_job_state = "error" elif json_data["state"] == "Paused": print_job_state = "paused" elif json_data["state"] == "Printing": print_job_state = "printing" elif json_data["state"] == "Operational": print_job_state = "ready" printer.updateState("idle") print_job.updateState(print_job_state) print_time = json_data["progress"]["printTime"] if print_time: print_job.updateTimeElapsed(print_time) if json_data["progress"][ "completion"]: # not 0 or None or "" print_job.updateTimeTotal( print_time / (json_data["progress"]["completion"] / 100)) else: print_job.updateTimeTotal(0) else: print_job.updateTimeElapsed(0) print_job.updateTimeTotal(0) print_job.updateName(json_data["job"]["file"]["name"]) else: pass # TODO: Handle errors elif self._api_prefix + "settings" in reply.url().toString( ): # OctoPrint settings dump from /settings: if http_status_code == 200: try: json_data = json.loads( bytes(reply.readAll()).decode("utf-8")) except json.decoder.JSONDecodeError: Logger.log( "w", "Received invalid JSON from octoprint instance.") json_data = {} if "feature" in json_data and "sdSupport" in json_data[ "feature"]: self._sd_supported = json_data["feature"]["sdSupport"] if "webcam" in json_data and "streamUrl" in json_data[ "webcam"]: self._camera_shares_proxy = False stream_url = json_data["webcam"]["streamUrl"] if not stream_url: #empty string or None self._camera_url = "" elif stream_url[:4].lower() == "http": # absolute uri self._camera_url = stream_url elif stream_url[:2] == "//": # protocol-relative self._camera_url = "%s:%s" % (self._protocol, stream_url) elif stream_url[: 1] == ":": # domain-relative (on another port) self._camera_url = "%s://%s%s" % ( self._protocol, self._address, stream_url) elif stream_url[: 1] == "/": # domain-relative (on same port) self._camera_url = "%s://%s:%d%s" % ( self._protocol, self._address, self._port, stream_url) self._camera_shares_proxy = True else: Logger.log("w", "Unusable stream url received: %s", stream_url) self._camera_url = "" Logger.log("d", "Set OctoPrint camera url to %s", self._camera_url) if self._camera_url != "" and len(self._printers) > 0: self._printers[0].setCamera( NetworkCamera(self._camera_url)) if "rotate90" in json_data["webcam"]: self._camera_rotation = -90 if json_data["webcam"][ "rotate90"] else 0 if json_data["webcam"]["flipH"] and json_data[ "webcam"]["flipV"]: self._camera_mirror = False self._camera_rotation += 180 elif json_data["webcam"]["flipH"]: self._camera_mirror = True elif json_data["webcam"]["flipV"]: self._camera_mirror = True self._camera_rotation += 180 else: self._camera_mirror = False self.cameraOrientationChanged.emit() elif reply.operation() == QNetworkAccessManager.PostOperation: if self._api_prefix + "files" in reply.url().toString( ): # Result from /files command: if http_status_code == 201: Logger.log( "d", "Resource created on OctoPrint instance: %s", reply.header( QNetworkRequest.LocationHeader).toString()) else: pass # TODO: Handle errors reply.uploadProgress.disconnect(self._onUploadProgress) self._progress_message.hide() if self._forced_queue or not self._auto_print: location = reply.header(QNetworkRequest.LocationHeader) if location: file_name = QUrl( reply.header(QNetworkRequest.LocationHeader). toString()).fileName() message = Message( i18n_catalog.i18nc( "@info:status", "Saved to OctoPrint as {0}").format(file_name)) else: message = Message( i18n_catalog.i18nc("@info:status", "Saved to OctoPrint")) message.addAction( "open_browser", i18n_catalog.i18nc("@action:button", "OctoPrint..."), "globe", i18n_catalog.i18nc("@info:tooltip", "Open the OctoPrint web interface")) message.actionTriggered.connect( self._onMessageActionTriggered) message.show() elif self._api_prefix + "job" in reply.url().toString( ): # Result from /job command (eg start/pause): if http_status_code == 204: Logger.log("d", "Octoprint job command accepted") else: pass # TODO: Handle errors elif self._api_prefix + "printer/command" in reply.url().toString( ): # Result from /printer/command (gcode statements): if http_status_code == 204: Logger.log("d", "Octoprint gcode command(s) accepted") else: pass # TODO: Handle errors else: Logger.log("d", "OctoPrintOutputDevice got an unhandled operation %s", reply.operation())
}, { "attribute": "name", "value": "Turtles" }, { "attribute": "bedTemperature", "value": 200 }, { "attribute": "targetBedTemperature", "value": 9001 }, { "attribute": "activePrintJob", "value": PrintJobOutputModel(MagicMock()) }, { "attribute": "state", "value": "BEEPBOOP" }, ] @pytest.mark.parametrize("data", test_validate_data_get_set) def test_getAndSet(data): model = PrinterOutputModel(MagicMock()) # Convert the first letter into a capital attribute = list(data["attribute"]) attribute[0] = attribute[0].capitalize()