diff --git a/README.md b/README.md index f6b0bd7..f7e5a3e 100644 --- a/README.md +++ b/README.md @@ -1 +1,4 @@ -## modbus \ No newline at end of file +## Работа с роботом + +### Тестовое окружение +* `poetry run python main.py --mode test` \ No newline at end of file diff --git a/app.log b/app.log new file mode 100644 index 0000000..a2b58ea --- /dev/null +++ b/app.log @@ -0,0 +1,26 @@ +2024-10-25 09:35:09,819 - logger - INFO - test +2024-10-25 09:35:13,350 - logger - INFO - test +2024-10-25 10:32:36,506 - logger - INFO - not_connected +2024-10-25 10:39:48,747 - logger - INFO - not_connected +2024-10-25 10:42:35,337 - logger - INFO - not_connected +2024-10-25 10:42:36,287 - logger - INFO - not_connected +2024-10-25 10:42:37,541 - logger - INFO - connected +2024-10-25 10:42:38,252 - logger - INFO - connected +2024-10-25 10:42:39,252 - logger - INFO - connected +2024-10-25 10:42:40,253 - logger - INFO - connected +2024-10-25 10:42:41,253 - logger - INFO - connected +2024-10-25 10:42:42,253 - logger - INFO - connected +2024-10-25 10:42:43,252 - logger - INFO - connected +2024-10-25 10:42:44,253 - logger - INFO - connected +2024-10-25 10:42:45,252 - logger - INFO - connected +2024-10-25 10:43:37,355 - logger - INFO - Value changed: not_connected +2024-10-25 10:43:40,252 - logger - INFO - Value changed: connected +2024-10-25 10:43:43,595 - logger - INFO - Value changed: not_connected +2024-10-25 10:53:29,935 - logger - INFO - Value changed: not_connected +2024-10-25 10:53:41,824 - logger - INFO - Value changed: not_connected +2024-10-25 10:53:45,860 - logger - INFO - Value changed: connected +2024-10-25 10:53:50,752 - logger - INFO - Value changed: not_connected +2024-10-25 10:53:52,751 - logger - INFO - Value changed: connected +2024-10-25 10:53:54,993 - logger - INFO - Value changed: not_connected +2024-10-25 10:53:57,014 - logger - INFO - Value changed: connected +2024-10-25 10:54:02,382 - logger - INFO - Value changed: not_connected diff --git a/gui/init.py b/gui/init.py index e256929..4181ae3 100644 --- a/gui/init.py +++ b/gui/init.py @@ -5,6 +5,9 @@ from PyQt5.QtGui import QColor, QPalette from gui.robot import ChangeRobot from gui.status import Status from gui.imitator import Imitator +from gui.visualize import Visualize +from gui.observable import Observable +from logger import logger class RightPanel(QWidget): @@ -12,7 +15,6 @@ class RightPanel(QWidget): super().__init__() layout = QVBoxLayout() for panel in panels: - # panel.setFixedHeight(200) layout.addWidget(panel) self.setLayout(layout) self.initUI() @@ -25,8 +27,11 @@ class RightPanel(QWidget): class MainPanel(QWidget): - def __init__(self): + def __init__(self, panels): super().__init__() + layout = QVBoxLayout() + for panel in panels: + layout.addWidget(panel) self.initUI() def initUI(self): @@ -40,20 +45,33 @@ class MainContentComponent(QWidget): def __init__(self, **kwargs): super().__init__() + self.observable = Observable(kwargs.get("get_status")) + self.observable.value_changed.connect(self.handle_value_change) + robotArgs = kwargs.get("robotPanel", {}) - robotPanel = ChangeRobot(**robotArgs) + self.robotPanel = ChangeRobot(**robotArgs) + statusArgs = kwargs.get("statusPanel", {}) - statusPanel = Status(**statusArgs) + self.statusPanel = Status(**statusArgs) + imitatorArgs = kwargs.get("imitatorPanel", {}) - imitatorPanel = Imitator(**imitatorArgs) + self.imitatorPanel = Imitator(**imitatorArgs) + self.rightPanel = RightPanel( panels=[ - statusPanel, - robotPanel, - imitatorPanel + self.statusPanel, + self.robotPanel, + self.imitatorPanel, + ] + ) + + visArgs = kwargs.get("visPanel", {}) + self.visPanel = Visualize(**visArgs) + self.mainPanel = MainPanel( + panels=[ + self.visPanel, ] ) - self.mainPanel = MainPanel() layout = QHBoxLayout() layout.addWidget(self.mainPanel) @@ -74,6 +92,14 @@ class MainContentComponent(QWidget): self.rightPanel.setFixedWidth(rightPanelWidth) super().resizeEvent(event) + def handle_value_change(self, new_value): + logger.info(f"Value changed: {new_value}") + + if new_value == "not_connected": + self.imitatorPanel.setDisabled(True) + if new_value == "connected": + self.imitatorPanel.setDisabled(False) + if __name__ == "__main__": app = QApplication(sys.argv) diff --git a/gui/observable.py b/gui/observable.py new file mode 100644 index 0000000..0e634ef --- /dev/null +++ b/gui/observable.py @@ -0,0 +1,22 @@ +from PyQt5.QtCore import pyqtSignal, QObject, QTimer +from logger import logger + +class Observable(QObject): + value_changed = pyqtSignal(object) # Сигнал с переданным значением + + def __init__(self, func, interval=1000): + super().__init__() + self.get_dynamic_value = func + self._value = None + + # Таймер для периодической проверки значения + self.timer = QTimer(self) + self.timer.timeout.connect(self.check_function) # Вызываем some_function на каждом таймере + self.timer.start(interval) # Интервал в миллисекундах + + def check_function(self): + # Пример функции, которая обновляет значение + new_value = self.get_dynamic_value() + if new_value != self._value: + self._value = new_value + self.value_changed.emit(new_value) \ No newline at end of file diff --git a/gui/visualize.py b/gui/visualize.py new file mode 100644 index 0000000..09b5767 --- /dev/null +++ b/gui/visualize.py @@ -0,0 +1,33 @@ + +from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QSizePolicy +from PyQt5.QtGui import QImage, QPixmap +from PyQt5.QtCore import QTimer + +class Visualize(QWidget): + def __init__(self, get_pybullet_image): + super().__init__() + self.get_pybullet_image = get_pybullet_image + + # Настройка компоновки + self.layout = QVBoxLayout(self) + self.layout.setContentsMargins(0, 0, 0, 0) # Убираем отступы по краям + self.label = QLabel(self) + self.label.setScaledContents(True) # Масштабирование изображения по размеру QLabel + self.label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) # QLabel занимает всю доступную область + self.layout.addWidget(self.label) + + # Таймер для обновления изображения + self.timer = QTimer() + self.timer.timeout.connect(self.update_image) + self.timer.start(100) # Обновление каждые 100 мс + + def update_image(self): + (rgb, width, height) = self.get_pybullet_image() + + # Преобразование RGB-данных в QImage + image = QImage(rgb, width, height, QImage.Format_RGB888) + pixmap = QPixmap.fromImage(image) + + # Обновление изображения на QLabel + self.label.setPixmap(pixmap) + \ No newline at end of file diff --git a/logger.py b/logger.py new file mode 100644 index 0000000..cd1c168 --- /dev/null +++ b/logger.py @@ -0,0 +1,14 @@ +import sys +import logging + +# Настройка логгера +logging.basicConfig( + level=logging.DEBUG, # Уровень логирования + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", + handlers=[ + logging.FileHandler("app.log"), # Запись в файл + logging.StreamHandler(sys.stdout), # Вывод в консоль + ], +) + +logger = logging.getLogger("logger") diff --git a/main.py b/main.py index bc908b2..62016a7 100644 --- a/main.py +++ b/main.py @@ -1,17 +1,20 @@ import json import sys import time +import argparse from PyQt5.QtWidgets import QApplication from robot.client_socket import SocketRobotArm from gui.init import MainContentComponent +from logger import logger class MyApp: with open("./robots.json", "r") as file: robots = json.load(file) - def __init__(self): + def __init__(self, mode): + self.mode = mode self.startRobot() self.startGui() @@ -27,6 +30,7 @@ class MyApp: def startGui(self): app = QApplication(sys.argv) mainWindow = MainContentComponent( + get_status=self.get_status, robotPanel={ "robots": self.robots, "updateRobot": self.updateRobot, @@ -41,6 +45,9 @@ class MyApp: "command_count": self.robot_app.get_command_count, "updateData": self.robot_app.upd_model, }, + visPanel={ + "get_pybullet_image": self.robot_app.get_pybullet_image, + }, ) mainWindow.setWindowTitle("ROBOT GUI") mainWindow.show() @@ -53,7 +60,8 @@ class MyApp: def updateRobot(self, robot): if robot in self.robots: selected_robot = robot - selected_robot = {"name": "test", "host": "127.0.0.1", "slave_id": 11} + if self.mode == "test": + selected_robot = {"name": "test", "host": "127.0.0.1", "slave_id": 11} if self.robot_app.status == "connected": self.robot_app.close() @@ -65,4 +73,14 @@ class MyApp: if __name__ == "__main__": - MyApp() + parser = argparse.ArgumentParser(description="MyApp Command Line Interface") + parser.add_argument( + "--mode", + type=str, + choices=["test", "prod"], + default="prod", + help="Mode of operation", + ) + # MyApp() + args = parser.parse_args() + app = MyApp(args.mode) diff --git a/robot/client_socket.py b/robot/client_socket.py index f8f836f..5b6bebb 100644 --- a/robot/client_socket.py +++ b/robot/client_socket.py @@ -61,6 +61,23 @@ class SocketRobotArm: useFixedBase=True, ) + def get_pybullet_image(self): + width, height, rgb, _, _ = p.getCameraImage( + width=320, + height=240, + viewMatrix=p.computeViewMatrix( + cameraEyePosition=[1, 1, 1], + cameraTargetPosition=[0, 0, 0], + cameraUpVector=[0, 0, 1], + ), + projectionMatrix=p.computeProjectionMatrixFOV( + fov=60.0, aspect=1.0, nearVal=0.1, farVal=10.0 + ), + renderer=p.ER_BULLET_HARDWARE_OPENGL, + physicsClientId=self.physics_client, + ) + return (rgb, width, height) + def close(self): self.socket.close() self.socket = None