class MainApp(QCoreApplication):
    def __init__(self, argv):
        super().__init__(argv)
        self.server = QTcpServer(self)
        self.server.setMaxPendingConnections(1)
        self.server.newConnection.connect(self.onNewConnection)
        self.server.listen(QHostAddress.Any, 6666)
        self.client = QTcpSocket(self)
        self.cap = cv2.VideoCapture(0)

    @pyqtSlot()
    def onNewConnection(self):
        self.client = self.server.nextPendingConnection()
        self.client.disconnected.connect(self.onClientDisconnected)
        self.client.readyRead.connect(self.onClientReadyRead)
        print('connected')

    @pyqtSlot()
    def onClientDisconnected(self):
        print('disconnected')

    @pyqtSlot()
    def onClientReadyRead(self):
        while self.client.canReadLine():
            line = self.client.readLine()[:-1]

            if 'get' == line:
                ret, frame = self.cap.read()
                data = cv2.imencode('.jpg', frame)[1]
                self.client.writeData((str(len(data)) + '\n').encode())
                self.client.writeData(data)
class MKSOutputDevice(NetworkedPrinterOutputDevice):
    def __init__(self, instance_id: str, address: str, properties: dict,
                 **kwargs) -> None:
        super().__init__(device_id=instance_id,
                         address=address,
                         properties=properties,
                         **kwargs)
        self._address = address
        self._port = 8080
        self._key = instance_id
        self._properties = properties

        self._target_bed_temperature = 0
        self._num_extruders = 1
        self._hotend_temperatures = [0] * self._num_extruders
        self._target_hotend_temperatures = [0] * self._num_extruders

        self._monitor_view_qml_path = os.path.join(
            os.path.dirname(os.path.abspath(__file__)), "MonitorItem4x.qml")
        # self._monitor_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "MonitorItem.qml")

        self.setPriority(
            3
        )  # Make sure the output device gets selected above local file output and Octoprint XD
        self._active_machine = CuraApplication.getInstance().getMachineManager(
        ).activeMachine
        self.setName(instance_id)
        self.setShortDescription(
            i18n_catalog.i18nc("@action:button", "Print over TFT"))
        self.setDescription(
            i18n_catalog.i18nc("@properties:tooltip", "Print over TFT"))
        self.setIconName("print")
        self.setConnectionText(
            i18n_catalog.i18nc("@info:status",
                               "Connected to TFT on {0}").format(self._key))
        Application.getInstance().globalContainerStackChanged.connect(
            self._onGlobalContainerChanged)

        self._socket = None
        self._gl = None
        self._command_queue = Queue()
        self._isPrinting = False
        self._isPause = False
        self._isSending = False
        self._gcode = None
        self._isConnect = False
        self._printing_filename = ""
        self._printing_progress = 0
        self._printing_time = 0
        self._start_time = 0
        self._pause_time = 0
        self.last_update_time = 0
        self.angle = 10
        self._connection_state_before_timeout = None
        self._sdFileList = False
        self.sdFiles = []
        self._mdialog = None
        self._mfilename = None
        self._uploadpath = ''

        self._settings_reply = None
        self._printer_reply = None
        self._job_reply = None
        self._command_reply = None
        self._screenShot = None

        self._image_reply = None
        self._stream_buffer = b""
        self._stream_buffer_start_index = -1

        self._post_reply = None
        self._post_multi_part = None
        self._post_part = None
        self._last_file_name = None
        self._last_file_path = None

        self._progress_message = None
        self._error_message = None
        self._connection_message = None
        self.__additional_components_view = None

        self._update_timer = QTimer()
        self._update_timer.setInterval(
            2000)  # TODO; Add preference for update interval
        self._update_timer.setSingleShot(False)
        self._update_timer.timeout.connect(self._update)

        self._manager = QNetworkAccessManager()
        self._manager.finished.connect(self._onRequestFinished)

        self._preheat_timer = QTimer()
        self._preheat_timer.setSingleShot(True)
        self._preheat_timer.timeout.connect(self.cancelPreheatBed)
        self._exception_message = None
        self._output_controller = GenericOutputController(self)
        self._number_of_extruders = 1
        self._camera_url = ""
        # Application.getInstance().getOutputDeviceManager().outputDevicesChanged.connect(self._onOutputDevicesChanged)
        CuraApplication.getInstance().getCuraSceneController(
        ).activeBuildPlateChanged.connect(self.CreateMKSController)

    def _onOutputDevicesChanged(self):
        Logger.log("d", "MKS _onOutputDevicesChanged")

    def connect(self):
        if self._socket is not None:
            self._socket.close()
        self._socket = QTcpSocket()
        self._socket.connectToHost(self._address, self._port)
        global_container_stack = CuraApplication.getInstance(
        ).getGlobalContainerStack()
        self.setShortDescription(
            i18n_catalog.i18nc(
                "@action:button",
                "Print over " + global_container_stack.getName()))
        self.setDescription(
            i18n_catalog.i18nc(
                "@properties:tooltip",
                "Print over " + global_container_stack.getName()))
        Logger.log("d", "MKS socket connecting ")
        # self._socket.waitForConnected(2000)
        self.setConnectionState(
            cast(ConnectionState, UnifiedConnectionState.Connecting))
        self._setAcceptsCommands(True)
        self._socket.readyRead.connect(self.on_read)
        self._update_timer.start()

    def getProperties(self):
        return self._properties

    @pyqtSlot(str, result=str)
    def getProperty(self, key):
        key = key.encode("utf-8")
        if key in self._properties:
            return self._properties.get(key, b"").decode("utf-8")
        else:
            return ""

    @pyqtSlot(result=str)
    def getKey(self):
        return self._key

    @pyqtProperty(str, constant=True)
    def address(self):
        return self._properties.get(b"address", b"").decode("utf-8")

    @pyqtProperty(str, constant=True)
    def name(self):
        return self._properties.get(b"name", b"").decode("utf-8")

    @pyqtProperty(str, constant=True)
    def firmwareVersion(self):
        return self._properties.get(b"firmware_version", b"").decode("utf-8")

    @pyqtProperty(str, constant=True)
    def ipAddress(self):
        return self._address

    @pyqtSlot(float, float)
    def preheatBed(self, temperature, duration):
        self._setTargetBedTemperature(temperature)
        if duration > 0:
            self._preheat_timer.setInterval(duration * 1000)
            self._preheat_timer.start()
        else:
            self._preheat_timer.stop()

    @pyqtSlot()
    def cancelPreheatBed(self):
        self._setTargetBedTemperature(0)
        self._preheat_timer.stop()

    @pyqtSlot()
    def printtest(self):
        self.sendCommand("M104 S0\r\n M140 S0\r\n M106 S255")

    @pyqtSlot()
    def printer_state(self):
        if len(self._printers) <= 0:
            return "offline"
        return self.printers[0].state

    @pyqtSlot()
    def selectfile(self):
        if self._last_file_name:
            return True
        else:
            return False

    @pyqtSlot(str)
    def deleteSDFiles(self, filename):
        # filename = "几何图.gcode"
        self._sendCommand("M30 1:/" + filename)
        self.sdFiles.remove(filename)
        self._sendCommand("M20")

    @pyqtSlot(str)
    def printSDFiles(self, filename):
        self._sendCommand("M23 " + filename)
        self._sendCommand("M24")

    @pyqtSlot()
    def selectFileToUplload(self):
        preferences = Application.getInstance().getPreferences()
        preferences.addPreference("mkswifi/autoprint", "True")
        preferences.addPreference("mkswifi/savepath", "")
        filename, _ = QFileDialog.getOpenFileName(
            None, "choose file", preferences.getValue("mkswifi/savepath"),
            "Gcode(*.gcode;*.g;*.goc)")
        preferences.setValue("mkswifi/savepath", filename)
        self._uploadpath = filename
        if ".g" in filename.lower():
            # Logger.log("d", "selectfile:"+filename)
            if filename in self.sdFiles:
                if self._mdialog:
                    self._mdialog.close()
                self._mdialog = QDialog()
                self._mdialog.setWindowTitle("The " +
                                             filename[filename.rfind("/") +
                                                      1:] +
                                             " file already exists.")
                dialogvbox = QVBoxLayout()
                dialoghbox = QHBoxLayout()
                yesbtn = QPushButton("yes")
                nobtn = QPushButton("no")
                yesbtn.clicked.connect(lambda: self.renameupload(filename))
                nobtn.clicked.connect(self.closeMDialog)
                content = QLabel(
                    "The " + filename[filename.rfind("/") + 1:] +
                    " file already exists. Do you want to rename and upload it?"
                )
                self._mfilename = QLineEdit()
                self._mfilename.setText(filename[filename.rfind("/") + 1:])
                dialoghbox.addWidget(yesbtn)
                dialoghbox.addWidget(nobtn)
                dialogvbox.addWidget(content)
                dialogvbox.addWidget(self._mfilename)
                dialogvbox.addLayout(dialoghbox)
                self._mdialog.setLayout(dialogvbox)
                self._mdialog.exec_()
                return
            if len(filename[filename.rfind("/") + 1:]) >= 30:
                if self._mdialog:
                    self._mdialog.close()
                self._mdialog = QDialog()
                self._mdialog.setWindowTitle(
                    "File name is too long to upload, please rename it.")
                dialogvbox = QVBoxLayout()
                dialoghbox = QHBoxLayout()
                yesbtn = QPushButton("yes")
                nobtn = QPushButton("no")
                yesbtn.clicked.connect(lambda: self.renameupload(filename))
                nobtn.clicked.connect(self.closeMDialog)
                content = QLabel(
                    "File name is too long to upload, please rename it.")
                self._mfilename = QLineEdit()
                self._mfilename.setText(filename[filename.rfind("/") + 1:])
                dialoghbox.addWidget(yesbtn)
                dialoghbox.addWidget(nobtn)
                dialogvbox.addWidget(content)
                dialogvbox.addWidget(self._mfilename)
                dialogvbox.addLayout(dialoghbox)
                self._mdialog.setLayout(dialogvbox)
                self._mdialog.exec_()
                return
            if self.isBusy():
                if self._exception_message:
                    self._exception_message.hide()
                self._exception_message = Message(
                    i18n_catalog.i18nc(
                        "@info:status",
                        "File cannot be transferred during printing."))
                self._exception_message.show()
                return
            self.uploadfunc(filename)

    def closeMDialog(self):
        if self._mdialog:
            self._mdialog.close()

    def renameupload(self, filename):
        if self._mfilename and ".g" in self._mfilename.text().lower():
            filename = filename[:filename.
                                rfind("/")] + "/" + self._mfilename.text()
            if self._mfilename.text() in self.sdFiles:
                if self._mdialog:
                    self._mdialog.close()
                self._mdialog = QDialog()
                self._mdialog.setWindowTitle("The " +
                                             filename[filename.rfind("/") +
                                                      1:] +
                                             " file already exists.")
                dialogvbox = QVBoxLayout()
                dialoghbox = QHBoxLayout()
                yesbtn = QPushButton("yes")
                nobtn = QPushButton("no")
                yesbtn.clicked.connect(lambda: self.renameupload(filename))
                nobtn.clicked.connect(self.closeMDialog)
                content = QLabel(
                    "The " + filename[filename.rfind("/") + 1:] +
                    " file already exists. Do you want to rename and upload it?"
                )
                self._mfilename = QLineEdit()
                self._mfilename.setText(filename[filename.rfind("/") + 1:])
                dialoghbox.addWidget(yesbtn)
                dialoghbox.addWidget(nobtn)
                dialogvbox.addWidget(content)
                dialogvbox.addWidget(self._mfilename)
                dialogvbox.addLayout(dialoghbox)
                self._mdialog.setLayout(dialogvbox)
                self._mdialog.exec_()
                return
            if len(filename[filename.rfind("/") + 1:]) >= 30:
                if self._mdialog:
                    self._mdialog.close()
                self._mdialog = QDialog()
                self._mdialog.setWindowTitle(
                    "File name is too long to upload, please rename it.")
                dialogvbox = QVBoxLayout()
                dialoghbox = QHBoxLayout()
                yesbtn = QPushButton("yes")
                nobtn = QPushButton("no")
                yesbtn.clicked.connect(lambda: self.renameupload(filename))
                nobtn.clicked.connect(self.closeMDialog)
                content = QLabel(
                    "File name is too long to upload, please rename it.")
                self._mfilename = QLineEdit()
                self._mfilename.setText(filename[filename.rfind("/") + 1:])
                dialoghbox.addWidget(yesbtn)
                dialoghbox.addWidget(nobtn)
                dialogvbox.addWidget(content)
                dialogvbox.addWidget(self._mfilename)
                dialogvbox.addLayout(dialoghbox)
                self._mdialog.setLayout(dialogvbox)
                self._mdialog.exec_()
                return
            if self.isBusy():
                if self._exception_message:
                    self._exception_message.hide()
                self._exception_message = Message(
                    i18n_catalog.i18nc(
                        "@info:status",
                        "File cannot be transferred during printing."))
                self._exception_message.show()
                return
            self._mdialog.close()
            self.uploadfunc(filename)

    def uploadfunc(self, filename):
        preferences = Application.getInstance().getPreferences()
        preferences.addPreference("mkswifi/autoprint", "True")
        preferences.addPreference("mkswifi/savepath", "")
        self._update_timer.stop()
        self._isSending = True
        self._preheat_timer.stop()
        single_string_file_data = ""
        try:
            f = open(self._uploadpath, "r")
            single_string_file_data = f.read()
            file_name = filename[filename.rfind("/") + 1:]
            self._last_file_name = file_name
            self._progress_message = Message(
                i18n_catalog.i18nc("@info:status", "Sending data to printer"),
                0,
                False,
                -1,
                i18n_catalog.i18nc("@info:title", "Sending Data"),
                option_text=i18n_catalog.i18nc("@label", "Print jobs"),
                option_state=preferences.getValue("mkswifi/autoprint"))
            self._progress_message.addAction(
                "Cancel", i18n_catalog.i18nc("@action:button", "Cancel"), None,
                "")
            self._progress_message.actionTriggered.connect(
                self._cancelSendGcode)
            self._progress_message.optionToggled.connect(
                self._onOptionStateChanged)
            self._progress_message.show()
            self._post_multi_part = QHttpMultiPart(QHttpMultiPart.FormDataType)
            self._post_part = QHttpPart()
            self._post_part.setHeader(
                QNetworkRequest.ContentDispositionHeader,
                "form-data; name=\"file\"; filename=\"%s\"" % file_name)
            self._post_part.setBody(single_string_file_data.encode())
            self._post_multi_part.append(self._post_part)
            post_request = QNetworkRequest(
                QUrl("http://%s/upload?X-Filename=%s" %
                     (self._address, file_name)))
            post_request.setRawHeader(b'Content-Type',
                                      b'application/octet-stream')
            post_request.setRawHeader(b'Connection', b'keep-alive')
            self._post_reply = self._manager.post(post_request,
                                                  self._post_multi_part)
            self._post_reply.uploadProgress.connect(self._onUploadProgress)
            self._post_reply.sslErrors.connect(self._onUploadError)
            self._gcode = None
        except IOError as e:
            Logger.log(
                "e",
                "An exception occurred in network connection: %s" % str(e))
            self._progress_message.hide()
            self._error_message = Message(
                i18n_catalog.i18nc("@info:status",
                                   "Send file to printer failed."))
            self._error_message.show()
            self._update_timer.start()
        except Exception as e:
            self._update_timer.start()
            self._progress_message.hide()
            Logger.log(
                "e",
                "An exception occurred in network connection: %s" % str(e))

    @pyqtProperty("QVariantList")
    def getSDFiles(self):
        self._sendCommand("M20")
        return list(self.sdFiles)

    def _setTargetBedTemperature(self, temperature):
        if not self._updateTargetBedTemperature(temperature):
            return
        self._sendCommand(["M140 S%s" % temperature])

    @pyqtSlot(str)
    def sendCommand(self, cmd):
        self._sendCommand(cmd)

    def _sendCommand(self, cmd):
        # Logger.log("d", "_sendCommand %s" % str(cmd))
        if self._socket and self._socket.state() == 2 or self._socket.state(
        ) == 3:
            if isinstance(cmd, str):
                self._command_queue.put(cmd + "\r\n")
            elif isinstance(cmd, list):
                for eachCommand in cmd:
                    self._command_queue.put(eachCommand + "\r\n")

    def disconnect(self):
        # self._updateJobState("")
        self._isConnect = False
        self.setConnectionState(
            cast(ConnectionState, UnifiedConnectionState.Closed))
        if self._socket is not None:
            self._socket.readyRead.disconnect(self.on_read)
            self._socket.close()
        if self._progress_message:
            self._progress_message.hide()
        if self._error_message:
            self._error_message.hide()
        self._update_timer.stop()

    def isConnected(self):
        return self._isConnect

    def isBusy(self):
        return self._isPrinting or self._isPause

    def requestWrite(self,
                     node,
                     file_name=None,
                     filter_by_machine=False,
                     file_handler=None,
                     **kwargs):
        self.writeStarted.emit(self)
        self._update_timer.stop()
        self._isSending = True
        # imagebuff = self._gl.glReadPixels(0, 0, 800, 800, self._gl.GL_RGB,
        #                                   self._gl.GL_UNSIGNED_BYTE)
        active_build_plate = CuraApplication.getInstance(
        ).getMultiBuildPlateModel().activeBuildPlate
        scene = CuraApplication.getInstance().getController().getScene()
        gcode_dict = getattr(scene, "gcode_dict", None)
        if not gcode_dict:
            return
        self._gcode = gcode_dict.get(active_build_plate, None)
        # Logger.log("d", "mks ready for print")
        self.startPrint()

    def startPrint(self):
        global_container_stack = CuraApplication.getInstance(
        ).getGlobalContainerStack()
        if not global_container_stack:
            return
        if self._error_message:
            self._error_message.hide()
            self._error_message = None

        if self._progress_message:
            self._progress_message.hide()
            self._progress_message = None

        if self.isBusy():
            self._error_message = Message(
                i18n_catalog.i18nc("@info:status",
                                   "Sending data to printer"), 0, False, -1,
                i18n_catalog.i18nc("@info:title", "Sending Data"))
            self._error_message.show()
            return
        job_name = Application.getInstance().getPrintInformation(
        ).jobName.strip()
        if job_name is "":
            job_name = "untitled_print"
            job_name = "cura_file"
        filename = "%s.gcode" % job_name
        if filename in self.sdFiles:
            if self._mdialog:
                self._mdialog.close()
            self._mdialog = QDialog()
            self._mdialog.setWindowTitle("The " +
                                         filename[filename.rfind("/") + 1:] +
                                         " file already exists.")
            dialogvbox = QVBoxLayout()
            dialoghbox = QHBoxLayout()
            yesbtn = QPushButton("yes")
            nobtn = QPushButton("no")
            yesbtn.clicked.connect(self.recheckfilename)
            nobtn.clicked.connect(self.closeMDialog)
            content = QLabel(
                "The " + filename[filename.rfind("/") + 1:] +
                " file already exists. Do you want to rename and upload it?")
            self._mfilename = QLineEdit()
            self._mfilename.setText(filename[filename.rfind("/") + 1:])
            dialoghbox.addWidget(yesbtn)
            dialoghbox.addWidget(nobtn)
            dialogvbox.addWidget(content)
            dialogvbox.addWidget(self._mfilename)
            dialogvbox.addLayout(dialoghbox)
            self._mdialog.setLayout(dialogvbox)
            self._mdialog.exec_()
            return
        if len(filename[filename.rfind("/") + 1:]) >= 30:
            if self._mdialog:
                self._mdialog.close()
            self._mdialog = QDialog()
            self._mdialog.setWindowTitle(
                "File name is too long to upload, please rename it.")
            dialogvbox = QVBoxLayout()
            dialoghbox = QHBoxLayout()
            yesbtn = QPushButton("yes")
            nobtn = QPushButton("no")
            yesbtn.clicked.connect(self.recheckfilename)
            nobtn.clicked.connect(self.closeMDialog)
            content = QLabel(
                "File name is too long to upload, please rename it.")
            self._mfilename = QLineEdit()
            self._mfilename.setText(filename[filename.rfind("/") + 1:])
            dialoghbox.addWidget(yesbtn)
            dialoghbox.addWidget(nobtn)
            dialogvbox.addWidget(content)
            dialogvbox.addWidget(self._mfilename)
            dialogvbox.addLayout(dialoghbox)
            self._mdialog.setLayout(dialogvbox)
            self._mdialog.exec_()
            return
        self._startPrint(filename)

    def recheckfilename(self):
        if self._mfilename and ".g" in self._mfilename.text().lower():
            filename = self._mfilename.text()
            if filename in self.sdFiles:
                if self._mdialog:
                    self._mdialog.close()
                self._mdialog = QDialog()
                self._mdialog.setWindowTitle("The " +
                                             filename[filename.rfind("/") +
                                                      1:] +
                                             " file already exists.")
                dialogvbox = QVBoxLayout()
                dialoghbox = QHBoxLayout()
                yesbtn = QPushButton("yes")
                nobtn = QPushButton("no")
                yesbtn.clicked.connect(self.recheckfilename)
                nobtn.clicked.connect(self.closeMDialog)
                content = QLabel(
                    "The " + filename[filename.rfind("/") + 1:] +
                    " file already exists. Do you want to rename and upload it?"
                )
                self._mfilename = QLineEdit()
                self._mfilename.setText(filename[filename.rfind("/") + 1:])
                dialoghbox.addWidget(yesbtn)
                dialoghbox.addWidget(nobtn)
                dialogvbox.addWidget(content)
                dialogvbox.addWidget(self._mfilename)
                dialogvbox.addLayout(dialoghbox)
                self._mdialog.setLayout(dialogvbox)
                self._mdialog.exec_()
                return
            if len(filename[filename.rfind("/") + 1:]) >= 30:
                if self._mdialog:
                    self._mdialog.close()
                self._mdialog = QDialog()
                self._mdialog.setWindowTitle(
                    "File name is too long to upload, please rename it.")
                dialogvbox = QVBoxLayout()
                dialoghbox = QHBoxLayout()
                yesbtn = QPushButton("yes")
                nobtn = QPushButton("no")
                yesbtn.clicked.connect(self.recheckfilename)
                nobtn.clicked.connect(self.closeMDialog)
                content = QLabel(
                    "File name is too long to upload, please rename it.")
                self._mfilename = QLineEdit()
                self._mfilename.setText(filename[filename.rfind("/") + 1:])
                dialoghbox.addWidget(yesbtn)
                dialoghbox.addWidget(nobtn)
                dialogvbox.addWidget(content)
                dialogvbox.addWidget(self._mfilename)
                dialogvbox.addLayout(dialoghbox)
                self._mdialog.setLayout(dialogvbox)
                self._mdialog.exec_()
                return
            if self.isBusy():
                if self._exception_message:
                    self._exception_message.hide()
                self._exception_message = Message(
                    i18n_catalog.i18nc(
                        "@info:status",
                        "File cannot be transferred during printing."))
                self._exception_message.show()
                return
            self._mdialog.close()
            self._startPrint(filename)

    def _messageBoxCallback(self, button):
        def delayedCallback():
            if button == QMessageBox.Yes:
                self.startPrint()
            else:
                CuraApplication.getInstance().getController().setActiveStage(
                    "PrepareStage")

    def _startPrint(self, file_name="cura_file.gcode"):
        self._preheat_timer.stop()
        self._screenShot = utils.take_screenshot()
        try:
            preferences = Application.getInstance().getPreferences()
            preferences.addPreference("mkswifi/autoprint", "True")
            preferences.addPreference("mkswifi/savepath", "")
            # CuraApplication.getInstance().showPrintMonitor.emit(True)
            self._progress_message = Message(
                i18n_catalog.i18nc("@info:status", "Sending data to printer"),
                0,
                False,
                -1,
                i18n_catalog.i18nc("@info:title", "Sending Data"),
                option_text=i18n_catalog.i18nc("@label", "Print jobs"),
                option_state=preferences.getValue("mkswifi/autoprint"))
            self._progress_message.addAction(
                "Cancel", i18n_catalog.i18nc("@action:button", "Cancel"), None,
                "")
            self._progress_message.actionTriggered.connect(
                self._cancelSendGcode)
            self._progress_message.optionToggled.connect(
                self._onOptionStateChanged)
            self._progress_message.show()
            # job_name = Application.getInstance().getPrintInformation().jobName.strip()
            # if job_name is "":
            #     job_name = "untitled_print"
            # job_name = "cura_file"
            # file_name = "%s.gcode" % job_name
            self._last_file_name = file_name
            Logger.log(
                "d", "mks: " + file_name + Application.getInstance().
                getPrintInformation().jobName.strip())

            single_string_file_data = ""
            if self._screenShot:
                single_string_file_data += utils.add_screenshot(
                    self._screenShot, 50, 50, ";simage:")
                single_string_file_data += utils.add_screenshot(
                    self._screenShot, 200, 200, ";;gimage:")
                single_string_file_data += "\r"
            last_process_events = time.time()
            for line in self._gcode:
                single_string_file_data += line
                if time.time() > last_process_events + 0.05:
                    QCoreApplication.processEvents()
                    last_process_events = time.time()

            self._post_multi_part = QHttpMultiPart(QHttpMultiPart.FormDataType)
            self._post_part = QHttpPart()
            # self._post_part.setHeader(QNetworkRequest.ContentTypeHeader, b'application/octet-stream')
            self._post_part.setHeader(
                QNetworkRequest.ContentDispositionHeader,
                "form-data; name=\"file\"; filename=\"%s\"" % file_name)
            self._post_part.setBody(single_string_file_data.encode())
            self._post_multi_part.append(self._post_part)
            post_request = QNetworkRequest(
                QUrl("http://%s/upload?X-Filename=%s" %
                     (self._address, file_name)))
            post_request.setRawHeader(b'Content-Type',
                                      b'application/octet-stream')
            post_request.setRawHeader(b'Connection', b'keep-alive')
            self._post_reply = self._manager.post(post_request,
                                                  self._post_multi_part)
            self._post_reply.uploadProgress.connect(self._onUploadProgress)
            self._post_reply.sslErrors.connect(self._onUploadError)
            # Logger.log("d", "http://%s:80/upload?X-Filename=%s" % (self._address, file_name))
            self._gcode = None
        except IOError as e:
            Logger.log(
                "e",
                "An exception occurred in network connection: %s" % str(e))
            self._progress_message.hide()
            self._error_message = Message(
                i18n_catalog.i18nc("@info:status",
                                   "Send file to printer failed."))
            self._error_message.show()
            self._update_timer.start()
        except Exception as e:
            self._update_timer.start()
            self._progress_message.hide()
            Logger.log(
                "e",
                "An exception occurred in network connection: %s" % str(e))

    def _printFile(self):
        self._sendCommand("M23 " + self._last_file_name)
        self._sendCommand("M24")

    def _onUploadProgress(self, bytes_sent, bytes_total):
        if bytes_total > 0:
            new_progress = bytes_sent / bytes_total * 100
            # Treat upload progress as response. Uploading can take more than 10 seconds, so if we don't, we can get
            # timeout responses if this happens.
            self._last_response_time = time.time()
            if new_progress > self._progress_message.getProgress():
                self._progress_message.show(
                )  # Ensure that the message is visible.
                self._progress_message.setProgress(bytes_sent / bytes_total *
                                                   100)
        else:
            self._progress_message.setProgress(0)
            self._progress_message.hide()

    def _onUploadError(self, reply, sslerror):
        Logger.log("d", "Upload Error")

    def _setHeadPosition(self, x, y, z, speed):
        self._sendCommand("G0 X%s Y%s Z%s F%s" % (x, y, z, speed))

    def _setHeadX(self, x, speed):
        self._sendCommand("G0 X%s F%s" % (x, speed))

    def _setHeadY(self, y, speed):
        self._sendCommand("G0 Y%s F%s" % (y, speed))

    def _setHeadZ(self, z, speed):
        self._sendCommand("G0 Z%s F%s" % (z, speed))

    def _homeHead(self):
        self._sendCommand("G28 X Y")

    def _homeBed(self):
        self._sendCommand("G28 Z")

    def _moveHead(self, x, y, z, speed):
        self._sendCommand(
            ["G91", "G0 X%s Y%s Z%s F%s" % (x, y, z, speed), "G90"])

    def _update(self):
        if self._socket is not None and (self._socket.state() == 2
                                         or self._socket.state() == 3):
            _send_data = "M105\r\nM997\r\n"
            if self.isBusy():
                _send_data += "M994\r\nM992\r\nM27\r\n"
            while self._command_queue.qsize() > 0:
                _queue_data = self._command_queue.get()
                if "M23" in _queue_data:
                    self._socket.writeData(_queue_data.encode())
                    continue
                if "M24" in _queue_data:
                    self._socket.writeData(_queue_data.encode())
                    continue
                _send_data += _queue_data
            # Logger.log("d", "_send_data: \r\n%s" % _send_data)
            self._socket.writeData(_send_data.encode())
            self._socket.flush()
            # self._socket.waitForReadyRead()
        else:
            Logger.log("d", "MKS wifi reconnecting")
            self.disconnect()
            self.connect()

    def _setJobState(self, job_state):
        if job_state == "abort":
            command = "M26"
        elif job_state == "print":
            if self._isPause:
                command = "M25"
            else:
                command = "M24"
        elif job_state == "pause":
            command = "M25"
        if command:
            self._sendCommand(command)

    @pyqtSlot()
    def cancelPrint(self):
        self._sendCommand("M26")

    @pyqtSlot()
    def pausePrint(self):
        if self.printers[0].state == "paused":
            self._sendCommand("M24")
        else:
            self._sendCommand("M25")

    @pyqtSlot()
    def resumePrint(self):
        self._sendCommand("M25")

    def on_read(self):
        if not self._socket:
            self.disconnect()
            return
        try:
            if not self._isConnect:
                self._isConnect = True
            if self._connection_state != UnifiedConnectionState.Connected:
                self._sendCommand("M20")
                self.setConnectionState(
                    cast(ConnectionState, UnifiedConnectionState.Connected))
                self.setConnectionText(
                    i18n_catalog.i18nc("@info:status", "TFT Connect succeed"))
            # ss = str(self._socket.readLine().data(), encoding=sys.getfilesystemencoding())
            # while self._socket.canReadLine():
            # ss = str(self._socket.readLine().data(), encoding=sys.getfilesystemencoding())
            # ss_list = ss.split("\r\n")
            if not self._printers:
                self._createPrinterList()
            printer = self.printers[0]
            while self._socket.canReadLine():
                s = str(self._socket.readLine().data(),
                        encoding=sys.getfilesystemencoding())
                # Logger.log("d", "mks recv: "+s)
                s = s.replace("\r", "").replace("\n", "")
                # if time.time() - self.last_update_time > 10 or time.time() - self.last_update_time<-10:
                #     Logger.log("d", "mks time:"+str(self.last_update_time)+str(time.time()))
                #     self._sendCommand("M20")
                #     self.last_update_time = time.time()
                if "T" in s and "B" in s and "T0" in s:
                    t0_temp = s[s.find("T0:") + len("T0:"):s.find("T1:")]
                    t1_temp = s[s.find("T1:") + len("T1:"):s.find("@:")]
                    bed_temp = s[s.find("B:") + len("B:"):s.find("T0:")]
                    t0_nowtemp = float(t0_temp[0:t0_temp.find("/")])
                    t0_targettemp = float(t0_temp[t0_temp.find("/") +
                                                  1:len(t0_temp)])
                    t1_nowtemp = float(t1_temp[0:t1_temp.find("/")])
                    t1_targettemp = float(t1_temp[t1_temp.find("/") +
                                                  1:len(t1_temp)])
                    bed_nowtemp = float(bed_temp[0:bed_temp.find("/")])
                    bed_targettemp = float(bed_temp[bed_temp.find("/") +
                                                    1:len(bed_temp)])
                    # cura 3.4 new api
                    printer.updateBedTemperature(bed_nowtemp)
                    printer.updateTargetBedTemperature(bed_targettemp)
                    extruder = printer.extruders[0]
                    extruder.updateTargetHotendTemperature(t0_targettemp)
                    extruder.updateHotendTemperature(t0_nowtemp)
                    # self._number_of_extruders = 1
                    # extruder = printer.extruders[1]
                    # extruder.updateHotendTemperature(t1_nowtemp)
                    # extruder.updateTargetHotendTemperature(t1_targettemp)
                    # only on lower 3.4
                    # self._setBedTemperature(bed_nowtemp)
                    # self._updateTargetBedTemperature(bed_targettemp)
                    # if self._num_extruders > 1:
                    # self._setHotendTemperature(1, t1_nowtemp)
                    # self._updateTargetHotendTemperature(1, t1_targettemp)
                    # self._setHotendTemperature(0, t0_nowtemp)
                    # self._updateTargetHotendTemperature(0, t0_targettemp)
                    continue
                if printer.activePrintJob is None:
                    print_job = PrintJobOutputModel(
                        output_controller=self._output_controller)
                    printer.updateActivePrintJob(print_job)
                else:
                    print_job = printer.activePrintJob
                if s.startswith("M997"):
                    job_state = "offline"
                    if "IDLE" in s:
                        self._isPrinting = False
                        self._isPause = False
                        job_state = 'idle'
                    elif "PRINTING" in s:
                        self._isPrinting = True
                        self._isPause = False
                        job_state = 'printing'
                    elif "PAUSE" in s:
                        self._isPrinting = False
                        self._isPause = True
                        job_state = 'paused'
                    print_job.updateState(job_state)
                    printer.updateState(job_state)
                    # self._updateJobState(job_state)
                    continue
                # print_job.updateState('idle')
                # printer.updateState('idle')
                if s.startswith("M994"):
                    if self.isBusy() and s.rfind("/") != -1:
                        self._printing_filename = s[s.rfind("/") +
                                                    1:s.rfind(";")]
                    else:
                        self._printing_filename = ""
                    print_job.updateName(self._printing_filename)
                    # self.setJobName(self._printing_filename)
                    continue
                if s.startswith("M992"):
                    if self.isBusy():
                        tm = s[s.find("M992") + len("M992"):len(s)].replace(
                            " ", "")
                        mms = tm.split(":")
                        self._printing_time = int(mms[0]) * 3600 + int(
                            mms[1]) * 60 + int(mms[2])
                    else:
                        self._printing_time = 0
                    # Logger.log("d", self._printing_time)
                    print_job.updateTimeElapsed(self._printing_time)
                    # self.setTimeElapsed(self._printing_time)
                    # print_job.updateTimeTotal(self._printing_time)
                    # self.setTimeTotal(self._printing_time)
                    continue
                if s.startswith("M27"):
                    if self.isBusy():
                        self._printing_progress = float(
                            s[s.find("M27") + len("M27"):len(s)].replace(
                                " ", ""))
                        totaltime = self._printing_time / self._printing_progress * 100
                    else:
                        self._printing_progress = 0
                        totaltime = self._printing_time * 100
                    # Logger.log("d", self._printing_time)
                    # Logger.log("d", totaltime)
                    # self.setProgress(self._printing_progress)
                    print_job.updateTimeTotal(self._printing_time)
                    print_job.updateTimeElapsed(self._printing_time * 2 -
                                                totaltime)
                    continue
                if 'Begin file list' in s:
                    self._sdFileList = True
                    self.sdFiles = []
                    self.last_update_time = time.time()
                    continue
                if 'End file list' in s:
                    self._sdFileList = False
                    continue
                if self._sdFileList:
                    s = s.replace("\n", "").replace("\r", "")
                    if s.lower().endswith("gcode") or s.lower().endswith(
                            "gco") or s.lower.endswith("g"):
                        self.sdFiles.append(s)
                    continue
        except Exception as e:
            print(e)

    def _updateTargetBedTemperature(self, temperature):
        if self._target_bed_temperature == temperature:
            return False
        self._target_bed_temperature = temperature
        self.targetBedTemperatureChanged.emit()
        return True

    def _updateTargetHotendTemperature(self, index, temperature):
        if self._target_hotend_temperatures[index] == temperature:
            return False
        self._target_hotend_temperatures[index] = temperature
        self.targetHotendTemperaturesChanged.emit()
        return True

    def _createPrinterList(self):
        printer = PrinterOutputModel(
            output_controller=self._output_controller,
            number_of_extruders=self._number_of_extruders)
        printer.updateName(self.name)
        self._printers = [printer]
        self.printersChanged.emit()

    def _onRequestFinished(self, reply):
        http_status_code = reply.attribute(
            QNetworkRequest.HttpStatusCodeAttribute)
        self._isSending = True
        self._update_timer.start()
        self._sendCommand("M20")
        preferences = Application.getInstance().getPreferences()
        preferences.addPreference("mkswifi/autoprint", "True")
        # preferences.addPreference("mkswifi/savepath", "")
        # preferences.setValue("mkswifi/autoprint", str(self._progress_message.getOptionState()))
        if preferences.getValue("mkswifi/autoprint"):
            self._printFile()
        if not http_status_code:
            return

    def _onOptionStateChanged(self, optstate):
        preferences = Application.getInstance().getPreferences()
        preferences.setValue("mkswifi/autoprint", str(optstate))

    def _cancelSendGcode(self, message_id, action_id):
        self._update_timer.start()
        self._isSending = False
        self._progress_message.hide()
        self._post_reply.abort()

    def CreateMKSController(self):
        Logger.log("d", "Creating additional ui components for mkscontroller.")
        # self.__additional_components_view = CuraApplication.getInstance().createQmlComponent(self._monitor_view_qml_path, {"mkscontroller": self})
        self.__additional_components_view = Application.getInstance(
        ).createQmlComponent(self._monitor_view_qml_path, {"manager": self})
        # trlist = CuraApplication.getInstance()._additional_components
        # for comp in trlist:
        Logger.log("w", "create mkscontroller ")
        if not self.__additional_components_view:
            Logger.log("w", "Could not create ui components for tft35.")
            return

    def _onGlobalContainerChanged(self) -> None:
        self._global_container_stack = Application.getInstance(
        ).getGlobalContainerStack()
        definitions = self._global_container_stack.definition.findDefinitions(
            key="cooling")
        Logger.log("d", definitions[0].label)
class MainWindow(QMainWindow):

    WaitingAnimation = [' |', ' /', ' -', ' \\']
    WaitingAnimationFrameDurationMs = 100
    ConnectionTimerTimeoutMs = 5000
    DataExchangeTimerTimeoutMs = 250

    def __init__(self):
        super().__init__()
        self.targetAddr = '0.0.0.0'
        self.targetPort = 0
        QSettings.setPath(QSettings.IniFormat, QSettings.UserScope,
                          QDir.currentPath())
        self.settings = QSettings(QSettings.IniFormat, QSettings.UserScope,
                                  'config')
        self.readSettings()

        self.connectionSettings = ConnectionSettings(self.targetAddr,
                                                     self.targetPort, self)
        self.connectionSettings.settingsChanged.connect(
            self.onConnectionSettingsSettingsChanged)
        self.socket = QTcpSocket(self)
        self.socket.stateChanged.connect(self.onSocketStateChanged)
        self.socket.readyRead.connect(self.onSocketReadyRead)
        self.frameLength = 0
        self.lastSocketState = QAbstractSocket.UnconnectedState
        self.animationTimer = QTimer(self)
        self.animationTimer.timeout.connect(self.onAnimationTimerTimeout)
        self.animationCounter = 0
        self.connectionTimer = QTimer(self)
        self.connectionTimer.setSingleShot(True)
        self.connectionTimer.timeout.connect(self.onConnectionTimerTimeout)
        self.dataExchangeTimer = QTimer(self)
        self.dataExchangeTimer.timeout.connect(self.onDataExchangeTimerTimeout)

        self.initUI()
        self.onConnectionSettingsSettingsChanged(self.targetAddr,
                                                 self.targetPort)
        self.onSocketStateChanged()

    #     self.cap = cv2.VideoCapture(0)
    #     self.dispTimer = QTimer(self)
    #     self.dispTimer.timeout.connect(self.onDispTimerTimeout)
    #     self.dispTimer.start(100)

    # @pyqtSlot()
    # def onDispTimerTimeout(self):
    #     ret, frame = self.cap.read()
    #     image = self.cvToQtIm(frame)
    #     pixmap = QPixmap.fromImage(image)
    #     self.streamDisp.setPixmap(pixmap)

    def initUI(self):
        self.setGeometry(300, 300, 300, 300)
        self.setWindowTitle('Diag Tool')

        statusBar = QStatusBar(self)
        self.targetInfo = QLabel(self)
        self.connectionStatus = QLabel(self)
        statusBar.addWidget(self.targetInfo, 0)
        statusBar.addWidget(self.connectionStatus, 1)
        self.setStatusBar(statusBar)

        self.streamDisp = QLabel(self)
        self.streamDisp.setSizePolicy(QSizePolicy.MinimumExpanding,
                                      QSizePolicy.MinimumExpanding)

        layout = QGridLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(self.streamDisp, 0, 0, 0, 4)
        self.setCentralWidget(QWidget(self))
        self.centralWidget().setLayout(layout)

        self.menuTarget = self.menuBar().addMenu('Target')
        self.menuTargetConnect = QAction('Connect', self)
        self.menuTargetConnect.triggered.connect(self.onMenuTargetConnect)
        self.menuTargetSettings = QAction('Settings', self)
        self.menuTargetSettings.triggered.connect(self.onMenuTargetSettings)
        self.menuTarget.addAction(self.menuTargetConnect)
        self.menuTarget.addAction(self.menuTargetSettings)

    def closeEvent(self, event):
        self.writeSettings()

    def cvToQtIm(self, cvIm):
        cvIm = cv2.cvtColor(cvIm, cv2.COLOR_BGR2RGB)
        return QImage(cvIm.data, cvIm.shape[1], cvIm.shape[0],
                      QImage.Format_RGB888)

    def readSettings(self):
        value = self.settings.value('target/addr')
        if value: self.targetAddr = str(value)
        value = self.settings.value('target/port')
        if value: self.targetPort = int(value)

    def writeSettings(self):
        self.settings.setValue('target/addr', self.targetAddr)
        self.settings.setValue('target/port', self.targetPort)

    @pyqtSlot()
    def onMenuTargetConnect(self):
        if QAbstractSocket.UnconnectedState == self.socket.state():
            self.menuTargetConnect.setEnabled(False)
            self.connectionSettings.setUserInputEnabled(False)
            self.socket.connectToHost(QHostAddress(self.targetAddr),
                                      self.targetPort)
            self.connectionTimer.start(self.ConnectionTimerTimeoutMs)
        elif QAbstractSocket.ConnectedState == self.socket.state():
            self.menuTargetConnect.setEnabled(False)
            self.socket.disconnectFromHost()

    @pyqtSlot()
    def onMenuTargetSettings(self):
        self.connectionSettings.show()

    @pyqtSlot()
    def onSocketStateChanged(self):
        #print(self.socket.state())
        if QAbstractSocket.UnconnectedState == self.socket.state():
            self.dataExchangeTimer.timeout.emit()
            self.dataExchangeTimer.start(self.DataExchangeTimerTimeoutMs)
            self.animationTimer.stop()
            self.menuTargetConnect.setText('Connect')
            self.menuTargetConnect.setEnabled(True)
            self.connectionStatus.setText('Offline')
            self.connectionSettings.setUserInputEnabled(True)
            if QAbstractSocket.ConnectingState == self.lastSocketState:
                QMessageBox.warning(self, 'Warning', 'Unable to connect!')
        elif QAbstractSocket.ConnectedState == self.socket.state():
            self.connectionTimer.stop()
            self.animationTimer.stop()
            self.dataExchangeTimer.start()
            self.menuTargetConnect.setText('Disconnect')
            self.menuTargetConnect.setEnabled(True)
            self.connectionStatus.setText('Online')
        elif QAbstractSocket.ConnectingState == self.socket.state():
            self.connectionStatus.setText('Connecting')
            self.animationCounter = 0
            self.animationTimer.start(self.WaitingAnimationFrameDurationMs)
        elif QAbstractSocket.ClosingState == self.socket.state():
            self.connectionStatus.setText('Disconnecting')
            self.animationCounter = 0
            self.animationTimer.start(self.WaitingAnimationFrameDurationMs)
        self.lastSocketState = self.socket.state()

    @pyqtSlot()
    def onSocketReadyRead(self):
        if 0 == self.frameLength:
            if self.socket.canReadLine():
                data = self.socket.readLine()[:-1]
                self.frameLength = int(data)

        if self.frameLength != 0:
            if self.socket.bytesAvailable() >= self.frameLength:
                data = self.socket.read(self.frameLength)
                arr = np.frombuffer(data, dtype=np.uint8)
                cvIm = cv2.imdecode(arr, cv2.IMREAD_COLOR)
                self.frameLength = 0
                image = self.cvToQtIm(cvIm)
                pixmap = QPixmap.fromImage(image)
                self.streamDisp.setPixmap(pixmap)

    @pyqtSlot(str, int)
    def onConnectionSettingsSettingsChanged(self, addr, port):
        self.targetInfo.setText('Target (' + addr + ':' + str(port) + ')')
        self.targetAddr = addr
        self.targetPort = port

    @pyqtSlot()
    def onAnimationTimerTimeout(self):
        if QAbstractSocket.ConnectingState == self.socket.state():
            text = 'Connecting'
        else:
            text = 'Disconnecting'

        text += self.WaitingAnimation[self.animationCounter]
        self.animationCounter = (self.animationCounter + 1) % len(
            self.WaitingAnimation)
        self.connectionStatus.setText(text)

    @pyqtSlot()
    def onConnectionTimerTimeout(self):
        self.socket.abort()

    @pyqtSlot()
    def onDataExchangeTimerTimeout(self):
        if QAbstractSocket.ConnectedState == self.socket.state():
            self.socket.writeData('get\n'.encode())
class Client(QWidget):
    def __init__(self, parent=None):
        super(Client, self).__init__(parent)

        self.socket = QTcpSocket()
        self.socket.readyRead.connect(self.update_status)
        self.socket.connected.connect(self.set_is_connected)
        self.remote = ServerIO(self.socket)

        self.viewer = CanvasViewer()
        self.monitor = StatusMonitor()
        self.controller = Controller()
        # self.b_register = Button("Register")
        self.set_layout()

        self.register_dialog = RegisterDialog(self)
        self.register_dialog.show()
        self.register_dialog.b_register.clicked.connect(self.register)

        self.agent = Agent(self.viewer, self.monitor, self.controller)
        self.agent.ready_read_step.connect(self.read_new_step)

        self.setWindowTitle("The Standard Model Game")

    def set_layout(self):
        # self.b_next_turn.setEnabled(False)
        layout = QHBoxLayout()
        sublayout = QVBoxLayout()
        layout.addWidget(self.viewer.canvas)
        sublayout.addWidget(self.monitor.canvas)
        sublayout.addWidget(self.monitor.messagebox)
        sublayout.addWidget(self.controller.b_buy_node)
        sublayout.addWidget(self.controller.b_build_detector)
        sublayout.addWidget(self.controller.b_next_turn)
        layout.addLayout(sublayout)
        self.setLayout(layout)

    def read_new_step(self):
        while (self.agent.can_read_new_step()):
            step = self.agent.read_new_step()
            self.remote.send_message(step)

    def update_status(self):
        while (self.socket.canReadLine()):
            print("update status")
            self.remote.update_data()
            step = self.remote.get_step()
            if self.agent.is_leagal_step(step):
                self.agent.process(step)
                print("process step")
            else:
                print("Something is wrong!!!")
            # self.controller.checkout_my_turn()

    def register(self):
        host = self.register_dialog.get_host()
        port = self.register_dialog.get_port()
        self.socket.connectToHost(host, port)

    def init(self):
        # self.setGeometry(100, 100, 1000, 1000)
        pass

    def set_is_connected(self):
        username = self.register_dialog.get_username()
        avatar = self.register_dialog.get_avatar()
        qmessage = QByteArray()
        qmessage.append("%s@%d\n" % (username, avatar))
        self.socket.write(qmessage)
        self.register_dialog.setVisible(False)