| | |
| | | info_scroll.setMinimumWidth(320) |
| | | right_layout.addWidget(info_scroll, stretch=1) |
| | | |
| | | self.path_browse_btn.clicked.connect(self._browse_path_file) |
| | | self.path_load_btn.clicked.connect(self._load_path_from_ui) |
| | | self.origin_update_btn.clicked.connect(self._update_origin_from_ui) |
| | | self.port_refresh_btn.clicked.connect(lambda: self._refresh_serial_ports(initial=False)) |
| | | self.serial_toggle_btn.clicked.connect(self._toggle_serial_connection) |
| | | self.start_button.clicked.connect(self._start_control) |
| | | self.stop_button.clicked.connect(self._stop_control) |
| | | if self.controller is not None: |
| | | self.path_browse_btn.clicked.connect(self._browse_path_file) |
| | | self.path_load_btn.clicked.connect(self._load_path_from_ui) |
| | | self.origin_update_btn.clicked.connect(self._update_origin_from_ui) |
| | | self.port_refresh_btn.clicked.connect(lambda: self._refresh_serial_ports(initial=False)) |
| | | self.serial_toggle_btn.clicked.connect(self._toggle_serial_connection) |
| | | self.start_button.clicked.connect(self._start_control) |
| | | self.stop_button.clicked.connect(self._stop_control) |
| | | |
| | | def _apply_saved_values(self): |
| | | path_file = self.gui_state.get("path_file", "") |
| | |
| | | self.port_combo.blockSignals(False) |
| | | |
| | | def _toggle_serial_connection(self): |
| | | if self.controller.is_connected(): |
| | | if self.controller.is_running(): |
| | | QtWidgets.QMessageBox.warning(self, "串口", "请先停止运动控制,再关闭串口。") |
| | | return |
| | | self.controller.disconnect() |
| | | QtWidgets.QMessageBox.information(self, "串口", "串口已关闭。") |
| | | else: |
| | | port = self.port_combo.currentText().strip() |
| | | if not port: |
| | | QtWidgets.QMessageBox.warning(self, "串口", "没有可用串口,请先连接设备。") |
| | | return |
| | | try: |
| | | self.controller.set_port(port) |
| | | except RuntimeError as exc: |
| | | QtWidgets.QMessageBox.warning(self, "串口", str(exc)) |
| | | return |
| | | if not self.controller.connect(): |
| | | QtWidgets.QMessageBox.warning(self, "串口", "串口打开失败,请检查连接。") |
| | | return |
| | | self._save_state({"serial_port": port}) |
| | | QtWidgets.QMessageBox.information(self, "串口", f"串口 {port} 已打开。") |
| | | self._update_serial_ui() |
| | | self._update_control_buttons() |
| | | QtWidgets.QMessageBox.warning(self, "串口", "HITL 模式下该操作由仿真器控制") |
| | | |
| | | def _start_control(self): |
| | | if self._is_control_running(): |
| | | return |
| | | if not self.controller.mower_ctrl: |
| | | QtWidgets.QMessageBox.warning(self, "控制", "请先加载路径文件。") |
| | | return |
| | | if not self.controller.is_connected(): |
| | | QtWidgets.QMessageBox.warning(self, "控制", "请先打开串口。") |
| | | return |
| | | |
| | | self.start_button.setEnabled(False) |
| | | self.stop_button.setEnabled(True) |
| | | |
| | | def control_entry(): |
| | | try: |
| | | self.controller.run() |
| | | except Exception as exc: |
| | | print(f"[ERROR] 控制线程异常: {exc}") |
| | | QtCore.QMetaObject.invokeMethod( |
| | | self, |
| | | "_show_error_message", |
| | | QtCore.Qt.QueuedConnection, |
| | | QtCore.Q_ARG(str, f"控制线程异常: {exc}"), |
| | | ) |
| | | finally: |
| | | QtCore.QMetaObject.invokeMethod(self, "_on_control_finished", QtCore.Qt.QueuedConnection) |
| | | |
| | | self.control_thread = threading.Thread(target=control_entry, daemon=True) |
| | | self.control_thread.start() |
| | | self._update_control_buttons() |
| | | QtWidgets.QMessageBox.warning(self, "控制", "HITL 模式下请通过仿真器启动") |
| | | |
| | | def _stop_control(self): |
| | | if not self._is_control_running(): |
| | | return |
| | | self.controller.stop() |
| | | self.stop_button.setEnabled(False) |
| | | QtWidgets.QMessageBox.warning(self, "控制", "HITL 模式下请通过仿真器停止") |
| | | |
| | | def _is_control_running(self) -> bool: |
| | | return bool(self.control_thread and self.control_thread.is_alive()) |
| | | return False |
| | | |
| | | def _save_state(self, updates: Dict[str, str]): |
| | | self.gui_state.update(updates) |
| | |
| | | self.persist_state_cb(updates) |
| | | |
| | | def _update_serial_ui(self): |
| | | connected = self.controller.is_connected() |
| | | self.serial_toggle_btn.setText("关闭串口" if connected else "打开串口") |
| | | self.port_combo.setEnabled(not connected) |
| | | self.port_refresh_btn.setEnabled(not connected) |
| | | self.serial_toggle_btn.setEnabled(False) |
| | | self.port_combo.setEnabled(False) |
| | | self.port_refresh_btn.setEnabled(False) |
| | | |
| | | def _update_control_buttons(self): |
| | | can_start = bool(self.controller.mower_ctrl and self.controller.is_connected() and not self._is_control_running()) |
| | | self.start_button.setEnabled(can_start) |
| | | self.stop_button.setEnabled(self._is_control_running()) |
| | | self.start_button.setEnabled(False) |
| | | self.stop_button.setEnabled(False) |
| | | |
| | | @QtCore.pyqtSlot() |
| | | def _on_control_finished(self): |
| | | self.control_thread = None |
| | | self._update_control_buttons() |
| | | self.stop_button.setEnabled(False) |
| | | pass |
| | | |
| | | @QtCore.pyqtSlot(str) |
| | | def _show_error_message(self, message: str): |
| | | QtWidgets.QMessageBox.warning(self, "错误", message) |
| | | |
| | | def closeEvent(self, event): |
| | | self._stop_control() |
| | | if self.control_thread: |
| | | self.control_thread.join(timeout=3.0) |
| | | if self.controller.is_connected(): |
| | | self.controller.disconnect() |
| | | super().closeEvent(event) |
| | | else: |
| | | QtRealtimeDashboard = None |