diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 4460018..0000000 Binary files a/.DS_Store and /dev/null differ diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..37b324a --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,11 @@ +# Specify base image +FROM ros:humble +ARG DEBIAN_FRONTEND=noninteractive + +# Install system dependencies +RUN apt-get -y update +RUN apt-get -y install python3-pip + +# Install python dependencies +COPY requirements.txt requirements.txt +RUN pip3 install -r requirements.txt diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..1cae126 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,27 @@ +{ + "name": "X17 Dev Container", + "build": { + "dockerfile": "Dockerfile", + "context": ".." + }, + "customizations": { + "vscode": { + "extensions": [ + "ms-iot.vscode-ros", // ROS extension + "ms-python.python", // Python extension (if needed) + "ms-azuretools.vscode-docker" // Docker extension + ], + "settings": { + "terminal.integrated.shell.linux": "/bin/bash" + } + } + }, + "mounts": [ + "source=${localWorkspaceFolder}/,target=/workspace,type=bind" + ], + "runArgs": [ + "--network", "host" // Use host network + ], + "remoteUser": "root", + "postCreateCommand": "echo 'source /opt/ros/humble/setup.bash' >> /root/.bashrc" +} diff --git a/.github/workflows/black-linter.yaml b/.github/workflows/black-linter.yaml deleted file mode 100644 index 400a4d6..0000000 --- a/.github/workflows/black-linter.yaml +++ /dev/null @@ -1,27 +0,0 @@ -name: Black Linter - -on: - pull_request: - branches: - - master # Replace with your default branch - push: - -jobs: - lint: - name: Run Black - runs-on: ubuntu-latest - - steps: - - name: Check out code - uses: actions/checkout@v2 - - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: 3.8 # You can specify the version you need - - - name: Install dependencies - run: pip install "black<23" - - - name: Run Black - run: black --diff --check $(git ls-files '*.py') # Replace "." with the path to your Python files or directories \ No newline at end of file diff --git a/.gitignore b/.gitignore index 7de0f21..96d02b7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,9 @@ __pycache__/ ui/__pycache__ log/ -.env \ No newline at end of file +.env +build +install +.vscode/ +.idea/ +.DS_Store \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json deleted file mode 100644 index 4ede37a..0000000 --- a/.vscode/tasks.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "tasks": [ - { - "type": "cppbuild", - "label": "C/C++: clang build active file", - "command": "/usr/bin/clang", - "args": [ - "-fcolor-diagnostics", - "-fansi-escape-codes", - "-g", - "${file}", - "-o", - "${fileDirname}/${fileBasenameNoExtension}" - ], - "options": { - "cwd": "${fileDirname}" - }, - "problemMatcher": [ - "$gcc" - ], - "group": { - "kind": "build", - "isDefault": true - }, - "detail": "Task generated by Debugger." - } - ], - "version": "2.0.0" -} \ No newline at end of file diff --git a/CHANGES.md b/CHANGES.md new file mode 100644 index 0000000..522e693 --- /dev/null +++ b/CHANGES.md @@ -0,0 +1,46 @@ +# X17-Surface PHOENIX BRANCH + +This is the phoenix branch for X17-Surface. It is a rewrite of the previous X17-Surface codebase, with a focus on readability and maintainability. I will try to remove as much unnecessary complexity as possible while still maintaining the core functionality. + +> [!WARNING] +> This branch is still a work in progress and is not yet fully functional. Do not merge into main until sufficient testing has been completed. + +### Changes Made + +#### 1. New .devcontainer setup +This will allow for easier development in a consistent environment. +Requires the following components to be installed on your machine: +- Docker Desktop +- Visual Studio Code +- Visual Studio Code Dev Containers extension + +When all of the above are installed, you can open the project in Visual Studio Code and it will prompt you to reopen in a container. If you are not prompted, you can use the command palette (Ctrl+Shift+P) and select "Dev-Containers: Rebuild and Reopen in Container". + +#### 2. xml -> yaml conversion +The configuration files have been converted from XML to YAML format for better readability and ease of use. I plan on doing the same for the CORE repo as well. + +#### 3. New script directory +A new directory has been created for scripts, making it easier to manage and organize scripts related to the project. (Running, building, etc.) + +#### 4. New launch configuration +There is a new launch configuration. It is intended to create more parity between the surface and core functionality. This can be found in the `ui` directory. Now to launch the surface, you can use the following command (Does not work yet, just a template): + +``` +ros2 launch ui surface_launch.yaml +``` +> Note: Make sure to build and source your workspace before running the launch command. + +After the launch command, the interface should be accessable at `http://127.0.0.1:5000`. I have run into some issues when trying to access the interface via `localhost` (which is where vscode will try to send you if you click on the link), so it is currently recommended to use the IP address directly. I have only had this issue on MacOS so far. + +### Known working platforms +> Format: Device (Architecture) - OS Version (Date tested) +- M3 MacBook Pro (ARM) - MacOS Sequoia 15.0 (9/19/2024) +- Adam's Desktop (x86) - WSL2 on Windows 11 (9/20/2024) + +### Known issues +- You cannot access any hardware yet (gamepad, etc.) + +### Updates + +9/19/2024: Initial commit. Implemented new .devcontainer setup, converted XML to YAML, created new script directory, and added new launch configuration. +The only nodes that are launched with the above launch command are the 'ui' node (Flask application) and the gamepad. I have not tested with the actual hardware yet so who knows if it will work. \ No newline at end of file diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index a4fcf17..0000000 --- a/Dockerfile +++ /dev/null @@ -1,23 +0,0 @@ -FROM ros:humble - -ARG DEBIAN_FRONTEND=noninteractive -RUN apt-get -y update -RUN apt-get -y install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev \ - libgstreamer-plugins-bad1.0-dev gstreamer1.0-plugins-base \ - gstreamer1.0-plugins-good gstreamer1.0-plugins-bad \ - gstreamer1.0-plugins-ugly gstreamer1.0-libav \ - gstreamer1.0-tools gstreamer1.0-x gstreamer1.0-alsa \ - gstreamer1.0-gl gstreamer1.0-gtk3 gstreamer1.0-qt5 \ - gstreamer1.0-pulseaudio xorg python3-pip ssh \ - libxkbcommon-x11-0 libxcb* qtbase5-dev - -COPY requirements.txt requirements.txt - -RUN pip3 install -r requirements.txt - -COPY . /X16-Surface/ -WORKDIR /X16-Surface/ -RUN bash /X16-Surface/build.sh - -WORKDIR / -CMD bash /X16-Surface/run.sh diff --git a/build.sh b/build.sh deleted file mode 100755 index 3a2d391..0000000 --- a/build.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/bash - -source /opt/ros/humble/setup.bash -colcon build --build-base /build --install-base /install diff --git a/compose.yaml b/compose.yaml deleted file mode 100644 index fa36f74..0000000 --- a/compose.yaml +++ /dev/null @@ -1,11 +0,0 @@ -services: - surface: - build: . - network_mode: "host" - privileged: true - tty: true - volumes: - - .:/X16-Surface - - /tmp/.X11-unix:/tmp/.X11-unix - environment: - DISPLAY: diff --git a/requirements.txt b/requirements.txt index 1f0c149..0894bc3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,6 +4,6 @@ numpy==1.21.5 opencv_python==4.7.0.68 paramiko==3.4.0 pygame==2.5.2 -PyQt5==5.15.10 python-dotenv==1.0.1 setuptools==59.6.0 +flask==3.0.3 diff --git a/ros/gamepad/src/sender.py b/ros/gamepad/src/sender.py index e3d72cd..e7f278a 100644 --- a/ros/gamepad/src/sender.py +++ b/ros/gamepad/src/sender.py @@ -168,7 +168,7 @@ def process_event(event): # If the gamepad is disconnected, try to reconnect it elif event.type == pygame.JOYDEVICEREMOVED: if not reconnect_gamepad(): - print("\nNo gamepad found, exiting") + node.get_logger().info("\nNo gamepad found, exiting") pygame.quit() rclpy.shutdown() sys.exit(0) @@ -203,7 +203,7 @@ def reconnect_gamepad(): i = GAMEPAD_TIMEOUT while i >= 0 and not reconnected: try: - print('Gamepad disconnected, reconnect within {:2} seconds'.format(i), end='\r') + node.get_logger().info('Gamepad disconnected, reconnect within {:2} seconds'.format(i)) pygame.init() pygame.joystick.init() # make sure there is only one joystick @@ -220,30 +220,32 @@ def reconnect_gamepad(): i -= 1 if reconnected: - print('\nGamepad reconnected') + node.get_logger().info('\nGamepad reconnected') joystick = pygame.joystick.Joystick(0) return reconnected if __name__ == '__main__': - global pub, pub_tools, data_thread, gamepad_thread + global pub, pub_tools, data_thread, gamepad_thread, node + # Initialize the ros node + rclpy.init() + node = rclpy.create_node('gp_pub') + try: init_pygame() except: - print('No gamepad found, please connect a gamepad') + node.get_logger().info('No gamepad found, please connect a gamepad') if not reconnect_gamepad(): - print("\nNo gamepad found, exiting") + node.get_logger().info("\nNo gamepad found, exiting") pygame.quit() sys.exit(0) - # Initialize the ros node - rclpy.init() - node = rclpy.create_node('gp_pub') + # Create the publishers - pub = node.create_publisher(RovVelocityCommand, 'rov_velocity', 10) + pub = node.create_publisher(RovVelocityCommand, '/rov_velocity', 10) pub_tools = node.create_publisher(ToolsCommandMsg, 'tools', 10) # Create the timers diff --git a/run.sh b/run.sh deleted file mode 100755 index e15b9d0..0000000 --- a/run.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/bash - -source /install/setup.bash -export ROS_DOMAIN_ID=69 -ros2 run ui runner.py diff --git a/scripts/build.sh b/scripts/build.sh new file mode 100755 index 0000000..fba29a8 --- /dev/null +++ b/scripts/build.sh @@ -0,0 +1,6 @@ +#!/usr/bin/bash + +source /opt/ros/humble/setup.bash +colcon build +. install/setup.bash +export ROS_DOMAIN_ID=69 \ No newline at end of file diff --git a/scripts/clean.sh b/scripts/clean.sh new file mode 100755 index 0000000..bc0da1d --- /dev/null +++ b/scripts/clean.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +# Define directories to be removed +directories=("log" "install" "build") + +# Loop through each directory and remove it if it exists +for dir in "${directories[@]}"; do + if [ -d "$dir" ]; then + echo "Removing directory: $dir" + rm -rf "$dir" + else + echo "Directory $dir does not exist." + fi +done + +source /opt/ros/humble/setup.bash + +echo "Cleanup complete." \ No newline at end of file diff --git a/scripts/run.sh b/scripts/run.sh new file mode 100755 index 0000000..63b39e4 --- /dev/null +++ b/scripts/run.sh @@ -0,0 +1,5 @@ +#!/usr/bin/bash + +. install/setup.bash +export ROS_DOMAIN_ID=69 +ros2 launch ui surface_launch.yaml diff --git a/ui/.DS_Store b/ui/.DS_Store deleted file mode 100644 index 91a6edd..0000000 Binary files a/ui/.DS_Store and /dev/null differ diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt index 4f03c27..ebdf5ec 100644 --- a/ui/CMakeLists.txt +++ b/ui/CMakeLists.txt @@ -16,20 +16,18 @@ find_package(rclpy REQUIRED) ament_python_install_package(${PROJECT_NAME}) +install(DIRECTORY + launch + DESTINATION share/${PROJECT_NAME}/ +) + install(PROGRAMS - src/runner.py - src/streams.py - src/ssh.py - src/main.py - src/interface.py - src/gamepad.py - src/subscribers/ThrustersSurface.py - src/subscribers/DepthSurface.py - src/subscribers/GamepadListener.py - src/subscribers/GamepadSender.py - src/subscribers/config.py - src/subscribers/TempListener.py - src/subscribers/LeakListener.py + src/app.py + DESTINATION lib/${PROJECT_NAME} +) + +install(DIRECTORY + src/templates DESTINATION lib/${PROJECT_NAME} ) diff --git a/ui/__pycache__/camera_stream.cpython-311.pyc b/ui/__pycache__/camera_stream.cpython-311.pyc deleted file mode 100644 index a622b44..0000000 Binary files a/ui/__pycache__/camera_stream.cpython-311.pyc and /dev/null differ diff --git a/ui/__pycache__/controls.cpython-311.pyc b/ui/__pycache__/controls.cpython-311.pyc deleted file mode 100644 index a4da18b..0000000 Binary files a/ui/__pycache__/controls.cpython-311.pyc and /dev/null differ diff --git a/ui/__pycache__/readouts.cpython-311.pyc b/ui/__pycache__/readouts.cpython-311.pyc deleted file mode 100644 index 8d20437..0000000 Binary files a/ui/__pycache__/readouts.cpython-311.pyc and /dev/null differ diff --git a/ui/launch/surface_launch.yaml b/ui/launch/surface_launch.yaml new file mode 100644 index 0000000..cbf0853 --- /dev/null +++ b/ui/launch/surface_launch.yaml @@ -0,0 +1,11 @@ +launch: + +- node: + pkg: ui + exec: app.py + namespace: rov + +- node: + pkg: gamepad + exec: sender.py + namespace: rov \ No newline at end of file diff --git a/ui/src/.DS_Store b/ui/src/.DS_Store deleted file mode 100644 index 207c359..0000000 Binary files a/ui/src/.DS_Store and /dev/null differ diff --git a/ui/src/app.py b/ui/src/app.py new file mode 100644 index 0000000..894ca43 --- /dev/null +++ b/ui/src/app.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 +import os +from flask import Flask, render_template, request, redirect, url_for +# Import rlcpy for logging +import rclpy + +app = Flask(__name__) + +@app.route('/') +def index(): + return render_template('index.html') + +if __name__ == "__main__": + # Initialize the ros node + rclpy.init() + node = rclpy.create_node('flask_server') + node.get_logger().info("Flask server started") + + # Run the flask app + app.run(host='0.0.0.0', port=5000) \ No newline at end of file diff --git a/ui/src/gamepad.py b/ui/src/gamepad.py deleted file mode 100644 index 401f784..0000000 --- a/ui/src/gamepad.py +++ /dev/null @@ -1,32 +0,0 @@ -import subprocess - -gamepad_connect_cmd = "ros2 run gamepad sender.py" - - -class gamepad: - def __init__(self, connection): - self.gamepad_process = None - self.connection = False - self.ssh_connection = connection - - def start(self): - if self.ssh_connection is None: - print("ERROR: gamepad unable to connect") - return - - print("Connecting to the gamepad...") - self.gamepad_process = subprocess.Popen( - gamepad_connect_cmd, - shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - ) - if self.gamepad_process is not None: - print(f"Process {self.gamepad_process.pid} started") - else: - print("Failed to connect to the gamepad") - - def stop(self): - if self.gamepad_process is not None: - self.gamepad_process.kill() - print(f"Process {self.gamepad_process.pid} killed") diff --git a/ui/src/interface.py b/ui/src/interface.py deleted file mode 100644 index da7700d..0000000 --- a/ui/src/interface.py +++ /dev/null @@ -1,597 +0,0 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'ui/src/interface.ui' -# -# Created by: PyQt5 UI code generator 5.15.9 -# -# WARNING: Any manual changes made to this file will be lost when pyuic5 is -# run again. Do not edit this file unless you know what you are doing. - - -from PyQt5 import QtCore, QtGui, QtWidgets - - -class Ui_MainWindow(object): - def setupUi(self, MainWindow): - MainWindow.setObjectName("MainWindow") - MainWindow.resize(1440, 847) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) - sizePolicy.setHorizontalStretch(1) - sizePolicy.setVerticalStretch(1) - sizePolicy.setHeightForWidth(MainWindow.sizePolicy().hasHeightForWidth()) - MainWindow.setSizePolicy(sizePolicy) - self.centralwidget = QtWidgets.QWidget(MainWindow) - self.centralwidget.setObjectName("centralwidget") - self.mainframe = QtWidgets.QFrame(self.centralwidget) - self.mainframe.setGeometry(QtCore.QRect(10, 10, 1421, 831)) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) - sizePolicy.setHorizontalStretch(1) - sizePolicy.setVerticalStretch(1) - sizePolicy.setHeightForWidth(self.mainframe.sizePolicy().hasHeightForWidth()) - self.mainframe.setSizePolicy(sizePolicy) - self.mainframe.setFrameShape(QtWidgets.QFrame.StyledPanel) - self.mainframe.setFrameShadow(QtWidgets.QFrame.Raised) - self.mainframe.setObjectName("mainframe") - self.thrusterwidget = QtWidgets.QFrame(self.mainframe) - self.thrusterwidget.setEnabled(True) - self.thrusterwidget.setGeometry(QtCore.QRect(10, 10, 811, 231)) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) - sizePolicy.setHorizontalStretch(1) - sizePolicy.setVerticalStretch(1) - sizePolicy.setHeightForWidth(self.thrusterwidget.sizePolicy().hasHeightForWidth()) - self.thrusterwidget.setSizePolicy(sizePolicy) - self.thrusterwidget.setFrameShape(QtWidgets.QFrame.StyledPanel) - self.thrusterwidget.setFrameShadow(QtWidgets.QFrame.Raised) - self.thrusterwidget.setObjectName("thrusterwidget") - self.gridLayout_5 = QtWidgets.QGridLayout(self.thrusterwidget) - self.gridLayout_5.setObjectName("gridLayout_5") - self.label = QtWidgets.QLabel(self.thrusterwidget) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth()) - self.label.setSizePolicy(sizePolicy) - self.label.setObjectName("label") - self.gridLayout_5.addWidget(self.label, 0, 0, 1, 2, QtCore.Qt.AlignHCenter) - self.widget = QtWidgets.QWidget(self.thrusterwidget) - self.widget.setObjectName("widget") - self.gridLayout = QtWidgets.QGridLayout(self.widget) - self.gridLayout.setObjectName("gridLayout") - self.tfllabel = QtWidgets.QLabel(self.widget) - self.tfllabel.setMaximumSize(QtCore.QSize(41, 16777215)) - self.tfllabel.setObjectName("tfllabel") - self.gridLayout.addWidget(self.tfllabel, 2, 0, 1, 1) - self.label_39 = QtWidgets.QLabel(self.widget) - self.label_39.setObjectName("label_39") - self.gridLayout.addWidget(self.label_39, 7, 1, 1, 1) - self.label_37 = QtWidgets.QLabel(self.widget) - self.label_37.setObjectName("label_37") - self.gridLayout.addWidget(self.label_37, 5, 1, 1, 1) - self.tbllabel = QtWidgets.QLabel(self.widget) - self.tbllabel.setObjectName("tbllabel") - self.gridLayout.addWidget(self.tbllabel, 6, 0, 1, 1) - self.tfrlabel = QtWidgets.QLabel(self.widget) - self.tfrlabel.setObjectName("tfrlabel") - self.gridLayout.addWidget(self.tfrlabel, 4, 0, 1, 1) - self.label_13 = QtWidgets.QLabel(self.widget) - self.label_13.setObjectName("label_13") - self.gridLayout.addWidget(self.label_13, 3, 1, 1, 1) - self.label_10 = QtWidgets.QLabel(self.widget) - self.label_10.setObjectName("label_10") - self.gridLayout.addWidget(self.label_10, 0, 1, 1, 1) - self.tbrlabel = QtWidgets.QLabel(self.widget) - self.tbrlabel.setObjectName("tbrlabel") - self.gridLayout.addWidget(self.tbrlabel, 8, 0, 1, 1) - self.label_2 = QtWidgets.QLabel(self.widget) - self.label_2.setObjectName("label_2") - self.gridLayout.addWidget(self.label_2, 0, 2, 1, 1, QtCore.Qt.AlignHCenter) - self.label_11 = QtWidgets.QLabel(self.widget) - self.label_11.setObjectName("label_11") - self.gridLayout.addWidget(self.label_11, 0, 3, 1, 1, QtCore.Qt.AlignRight) - self.label_12 = QtWidgets.QLabel(self.widget) - self.label_12.setObjectName("label_12") - self.gridLayout.addWidget(self.label_12, 3, 3, 1, 1, QtCore.Qt.AlignRight) - self.label_36 = QtWidgets.QLabel(self.widget) - self.label_36.setObjectName("label_36") - self.gridLayout.addWidget(self.label_36, 5, 3, 1, 1, QtCore.Qt.AlignRight) - self.label_38 = QtWidgets.QLabel(self.widget) - self.label_38.setObjectName("label_38") - self.gridLayout.addWidget(self.label_38, 7, 3, 1, 1, QtCore.Qt.AlignRight) - self.tfloutput = QtWidgets.QProgressBar(self.widget) - self.tfloutput.setProperty("value", 24) - self.tfloutput.setObjectName("tfloutput") - self.gridLayout.addWidget(self.tfloutput, 2, 1, 1, 3) - self.tfroutput = QtWidgets.QProgressBar(self.widget) - self.tfroutput.setProperty("value", 24) - self.tfroutput.setObjectName("tfroutput") - self.gridLayout.addWidget(self.tfroutput, 4, 1, 1, 3) - self.tbloutput = QtWidgets.QProgressBar(self.widget) - self.tbloutput.setProperty("value", 24) - self.tbloutput.setObjectName("tbloutput") - self.gridLayout.addWidget(self.tbloutput, 6, 1, 1, 3) - self.tbroutput = QtWidgets.QProgressBar(self.widget) - self.tbroutput.setProperty("value", 24) - self.tbroutput.setObjectName("tbroutput") - self.gridLayout.addWidget(self.tbroutput, 8, 1, 1, 3) - self.label_3 = QtWidgets.QLabel(self.widget) - self.label_3.setObjectName("label_3") - self.gridLayout.addWidget(self.label_3, 3, 2, 1, 1, QtCore.Qt.AlignHCenter) - self.label_4 = QtWidgets.QLabel(self.widget) - self.label_4.setObjectName("label_4") - self.gridLayout.addWidget(self.label_4, 5, 2, 1, 1, QtCore.Qt.AlignHCenter) - self.label_5 = QtWidgets.QLabel(self.widget) - self.label_5.setObjectName("label_5") - self.gridLayout.addWidget(self.label_5, 7, 2, 1, 1, QtCore.Qt.AlignHCenter) - self.gridLayout_5.addWidget(self.widget, 1, 0, 1, 1) - self.widget_2 = QtWidgets.QWidget(self.thrusterwidget) - self.widget_2.setObjectName("widget_2") - self.gridLayout_3 = QtWidgets.QGridLayout(self.widget_2) - self.gridLayout_3.setObjectName("gridLayout_3") - self.tfllabel_2 = QtWidgets.QLabel(self.widget_2) - self.tfllabel_2.setMaximumSize(QtCore.QSize(41, 16777215)) - self.tfllabel_2.setObjectName("tfllabel_2") - self.gridLayout_3.addWidget(self.tfllabel_2, 2, 0, 1, 1) - self.label_40 = QtWidgets.QLabel(self.widget_2) - self.label_40.setObjectName("label_40") - self.gridLayout_3.addWidget(self.label_40, 7, 1, 1, 1) - self.label_41 = QtWidgets.QLabel(self.widget_2) - self.label_41.setObjectName("label_41") - self.gridLayout_3.addWidget(self.label_41, 5, 1, 1, 1) - self.tbllabel_2 = QtWidgets.QLabel(self.widget_2) - self.tbllabel_2.setObjectName("tbllabel_2") - self.gridLayout_3.addWidget(self.tbllabel_2, 6, 0, 1, 1) - self.tfrlabel_2 = QtWidgets.QLabel(self.widget_2) - self.tfrlabel_2.setObjectName("tfrlabel_2") - self.gridLayout_3.addWidget(self.tfrlabel_2, 4, 0, 1, 1) - self.label_14 = QtWidgets.QLabel(self.widget_2) - self.label_14.setObjectName("label_14") - self.gridLayout_3.addWidget(self.label_14, 3, 1, 1, 1) - self.label_15 = QtWidgets.QLabel(self.widget_2) - self.label_15.setObjectName("label_15") - self.gridLayout_3.addWidget(self.label_15, 0, 1, 1, 1) - self.tbrlabel_2 = QtWidgets.QLabel(self.widget_2) - self.tbrlabel_2.setObjectName("tbrlabel_2") - self.gridLayout_3.addWidget(self.tbrlabel_2, 8, 0, 1, 1) - self.label_6 = QtWidgets.QLabel(self.widget_2) - self.label_6.setObjectName("label_6") - self.gridLayout_3.addWidget(self.label_6, 0, 2, 1, 1, QtCore.Qt.AlignHCenter) - self.label_16 = QtWidgets.QLabel(self.widget_2) - self.label_16.setObjectName("label_16") - self.gridLayout_3.addWidget(self.label_16, 0, 3, 1, 1, QtCore.Qt.AlignRight) - self.label_17 = QtWidgets.QLabel(self.widget_2) - self.label_17.setObjectName("label_17") - self.gridLayout_3.addWidget(self.label_17, 3, 3, 1, 1, QtCore.Qt.AlignRight) - self.label_42 = QtWidgets.QLabel(self.widget_2) - self.label_42.setObjectName("label_42") - self.gridLayout_3.addWidget(self.label_42, 5, 3, 1, 1, QtCore.Qt.AlignRight) - self.label_43 = QtWidgets.QLabel(self.widget_2) - self.label_43.setObjectName("label_43") - self.gridLayout_3.addWidget(self.label_43, 7, 3, 1, 1, QtCore.Qt.AlignRight) - self.tfloutput_2 = QtWidgets.QProgressBar(self.widget_2) - self.tfloutput_2.setProperty("value", 24) - self.tfloutput_2.setObjectName("tfloutput_2") - self.gridLayout_3.addWidget(self.tfloutput_2, 2, 1, 1, 3) - self.tfroutput_2 = QtWidgets.QProgressBar(self.widget_2) - self.tfroutput_2.setProperty("value", 24) - self.tfroutput_2.setObjectName("tfroutput_2") - self.gridLayout_3.addWidget(self.tfroutput_2, 4, 1, 1, 3) - self.tbloutput_2 = QtWidgets.QProgressBar(self.widget_2) - self.tbloutput_2.setProperty("value", 24) - self.tbloutput_2.setObjectName("tbloutput_2") - self.gridLayout_3.addWidget(self.tbloutput_2, 6, 1, 1, 3) - self.tbroutput_2 = QtWidgets.QProgressBar(self.widget_2) - self.tbroutput_2.setProperty("value", 24) - self.tbroutput_2.setObjectName("tbroutput_2") - self.gridLayout_3.addWidget(self.tbroutput_2, 8, 1, 1, 3) - self.label_7 = QtWidgets.QLabel(self.widget_2) - self.label_7.setObjectName("label_7") - self.gridLayout_3.addWidget(self.label_7, 3, 2, 1, 1, QtCore.Qt.AlignHCenter) - self.label_8 = QtWidgets.QLabel(self.widget_2) - self.label_8.setObjectName("label_8") - self.gridLayout_3.addWidget(self.label_8, 5, 2, 1, 1, QtCore.Qt.AlignHCenter) - self.label_9 = QtWidgets.QLabel(self.widget_2) - self.label_9.setObjectName("label_9") - self.gridLayout_3.addWidget(self.label_9, 7, 2, 1, 1, QtCore.Qt.AlignHCenter) - self.gridLayout_5.addWidget(self.widget_2, 1, 1, 1, 1) - self.velocitywidget = QtWidgets.QFrame(self.mainframe) - self.velocitywidget.setGeometry(QtCore.QRect(830, 10, 161, 231)) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) - sizePolicy.setHorizontalStretch(1) - sizePolicy.setVerticalStretch(1) - sizePolicy.setHeightForWidth(self.velocitywidget.sizePolicy().hasHeightForWidth()) - self.velocitywidget.setSizePolicy(sizePolicy) - self.velocitywidget.setFrameShape(QtWidgets.QFrame.StyledPanel) - self.velocitywidget.setFrameShadow(QtWidgets.QFrame.Raised) - self.velocitywidget.setObjectName("velocitywidget") - self.gridLayout_6 = QtWidgets.QGridLayout(self.velocitywidget) - self.gridLayout_6.setObjectName("gridLayout_6") - self.xlabel = QtWidgets.QLabel(self.velocitywidget) - self.xlabel.setObjectName("xlabel") - self.gridLayout_6.addWidget(self.xlabel, 1, 0, 1, 1) - self.xoutput = QtWidgets.QLCDNumber(self.velocitywidget) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.xoutput.sizePolicy().hasHeightForWidth()) - self.xoutput.setSizePolicy(sizePolicy) - self.xoutput.setObjectName("xoutput") - self.gridLayout_6.addWidget(self.xoutput, 1, 2, 1, 1) - self.ylabel = QtWidgets.QLabel(self.velocitywidget) - self.ylabel.setObjectName("ylabel") - self.gridLayout_6.addWidget(self.ylabel, 2, 0, 1, 1) - self.youtput = QtWidgets.QLCDNumber(self.velocitywidget) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.youtput.sizePolicy().hasHeightForWidth()) - self.youtput.setSizePolicy(sizePolicy) - self.youtput.setObjectName("youtput") - self.gridLayout_6.addWidget(self.youtput, 2, 2, 1, 1) - self.xlabel_2 = QtWidgets.QLabel(self.velocitywidget) - self.xlabel_2.setMaximumSize(QtCore.QSize(16, 16777215)) - self.xlabel_2.setObjectName("xlabel_2") - self.gridLayout_6.addWidget(self.xlabel_2, 3, 0, 1, 2) - self.zoutput = QtWidgets.QLCDNumber(self.velocitywidget) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.zoutput.sizePolicy().hasHeightForWidth()) - self.zoutput.setSizePolicy(sizePolicy) - self.zoutput.setObjectName("zoutput") - self.gridLayout_6.addWidget(self.zoutput, 3, 2, 1, 1) - self.velocitylabel = QtWidgets.QLabel(self.velocitywidget) - self.velocitylabel.setObjectName("velocitylabel") - self.gridLayout_6.addWidget(self.velocitylabel, 0, 0, 1, 3, QtCore.Qt.AlignHCenter) - self.depthwidget = QtWidgets.QFrame(self.mainframe) - self.depthwidget.setGeometry(QtCore.QRect(180, 330, 221, 71)) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) - sizePolicy.setHorizontalStretch(1) - sizePolicy.setVerticalStretch(1) - sizePolicy.setHeightForWidth(self.depthwidget.sizePolicy().hasHeightForWidth()) - self.depthwidget.setSizePolicy(sizePolicy) - self.depthwidget.setFrameShape(QtWidgets.QFrame.StyledPanel) - self.depthwidget.setFrameShadow(QtWidgets.QFrame.Raised) - self.depthwidget.setObjectName("depthwidget") - self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.depthwidget) - self.horizontalLayout_4.setObjectName("horizontalLayout_4") - self.depthlabel = QtWidgets.QLabel(self.depthwidget) - self.depthlabel.setObjectName("depthlabel") - self.horizontalLayout_4.addWidget(self.depthlabel, 0, QtCore.Qt.AlignHCenter) - self.depthoutput = QtWidgets.QLCDNumber(self.depthwidget) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.depthoutput.sizePolicy().hasHeightForWidth()) - self.depthoutput.setSizePolicy(sizePolicy) - self.depthoutput.setObjectName("depthoutput") - self.horizontalLayout_4.addWidget(self.depthoutput) - self.finemodewidget = QtWidgets.QFrame(self.mainframe) - self.finemodewidget.setGeometry(QtCore.QRect(180, 410, 221, 71)) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) - sizePolicy.setHorizontalStretch(1) - sizePolicy.setVerticalStretch(1) - sizePolicy.setHeightForWidth(self.finemodewidget.sizePolicy().hasHeightForWidth()) - self.finemodewidget.setSizePolicy(sizePolicy) - self.finemodewidget.setFrameShape(QtWidgets.QFrame.StyledPanel) - self.finemodewidget.setFrameShadow(QtWidgets.QFrame.Raised) - self.finemodewidget.setObjectName("finemodewidget") - self.horizontalLayout_5 = QtWidgets.QHBoxLayout(self.finemodewidget) - self.horizontalLayout_5.setObjectName("horizontalLayout_5") - self.finemodelabel = QtWidgets.QLabel(self.finemodewidget) - self.finemodelabel.setObjectName("finemodelabel") - self.horizontalLayout_5.addWidget(self.finemodelabel, 0, QtCore.Qt.AlignHCenter) - self.finemodeoutput = QtWidgets.QLCDNumber(self.finemodewidget) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.finemodeoutput.sizePolicy().hasHeightForWidth()) - self.finemodeoutput.setSizePolicy(sizePolicy) - self.finemodeoutput.setObjectName("finemodeoutput") - self.horizontalLayout_5.addWidget(self.finemodeoutput) - self.pnumaticswidget = QtWidgets.QFrame(self.mainframe) - self.pnumaticswidget.setGeometry(QtCore.QRect(640, 250, 291, 231)) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) - sizePolicy.setHorizontalStretch(1) - sizePolicy.setVerticalStretch(1) - sizePolicy.setHeightForWidth(self.pnumaticswidget.sizePolicy().hasHeightForWidth()) - self.pnumaticswidget.setSizePolicy(sizePolicy) - self.pnumaticswidget.setFrameShape(QtWidgets.QFrame.StyledPanel) - self.pnumaticswidget.setFrameShadow(QtWidgets.QFrame.Raised) - self.pnumaticswidget.setObjectName("pnumaticswidget") - self.gridLayout_2 = QtWidgets.QGridLayout(self.pnumaticswidget) - self.gridLayout_2.setObjectName("gridLayout_2") - self.pnumaticlabel = QtWidgets.QLabel(self.pnumaticswidget) - self.pnumaticlabel.setObjectName("pnumaticlabel") - self.gridLayout_2.addWidget(self.pnumaticlabel, 0, 1, 1, 1) - self.line1label = QtWidgets.QLabel(self.pnumaticswidget) - self.line1label.setObjectName("line1label") - self.gridLayout_2.addWidget(self.line1label, 1, 0, 1, 1, QtCore.Qt.AlignHCenter) - self.line2label = QtWidgets.QLabel(self.pnumaticswidget) - self.line2label.setObjectName("line2label") - self.gridLayout_2.addWidget(self.line2label, 1, 1, 1, 1, QtCore.Qt.AlignHCenter) - self.line3label = QtWidgets.QLabel(self.pnumaticswidget) - self.line3label.setObjectName("line3label") - self.gridLayout_2.addWidget(self.line3label, 1, 2, 1, 1, QtCore.Qt.AlignHCenter) - self.line1output = QtWidgets.QLCDNumber(self.pnumaticswidget) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.line1output.sizePolicy().hasHeightForWidth()) - self.line1output.setSizePolicy(sizePolicy) - self.line1output.setObjectName("line1output") - self.gridLayout_2.addWidget(self.line1output, 2, 0, 1, 1) - self.line2output = QtWidgets.QLCDNumber(self.pnumaticswidget) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.line2output.sizePolicy().hasHeightForWidth()) - self.line2output.setSizePolicy(sizePolicy) - self.line2output.setObjectName("line2output") - self.gridLayout_2.addWidget(self.line2output, 2, 1, 1, 1) - self.line3output = QtWidgets.QLCDNumber(self.pnumaticswidget) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.line3output.sizePolicy().hasHeightForWidth()) - self.line3output.setSizePolicy(sizePolicy) - self.line3output.setObjectName("line3output") - self.gridLayout_2.addWidget(self.line3output, 2, 2, 1, 1) - self.rotationwidget = QtWidgets.QFrame(self.mainframe) - self.rotationwidget.setGeometry(QtCore.QRect(1000, 10, 161, 231)) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) - sizePolicy.setHorizontalStretch(1) - sizePolicy.setVerticalStretch(1) - sizePolicy.setHeightForWidth(self.rotationwidget.sizePolicy().hasHeightForWidth()) - self.rotationwidget.setSizePolicy(sizePolicy) - self.rotationwidget.setFrameShape(QtWidgets.QFrame.StyledPanel) - self.rotationwidget.setFrameShadow(QtWidgets.QFrame.Raised) - self.rotationwidget.setObjectName("rotationwidget") - self.gridLayout_7 = QtWidgets.QGridLayout(self.rotationwidget) - self.gridLayout_7.setObjectName("gridLayout_7") - self.rotxlabel = QtWidgets.QLabel(self.rotationwidget) - self.rotxlabel.setObjectName("rotxlabel") - self.gridLayout_7.addWidget(self.rotxlabel, 1, 0, 1, 1) - self.rotxoutput = QtWidgets.QLCDNumber(self.rotationwidget) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.rotxoutput.sizePolicy().hasHeightForWidth()) - self.rotxoutput.setSizePolicy(sizePolicy) - self.rotxoutput.setObjectName("rotxoutput") - self.gridLayout_7.addWidget(self.rotxoutput, 1, 2, 1, 1) - self.rotylabel = QtWidgets.QLabel(self.rotationwidget) - self.rotylabel.setObjectName("rotylabel") - self.gridLayout_7.addWidget(self.rotylabel, 2, 0, 1, 1) - self.rotyoutput = QtWidgets.QLCDNumber(self.rotationwidget) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.rotyoutput.sizePolicy().hasHeightForWidth()) - self.rotyoutput.setSizePolicy(sizePolicy) - self.rotyoutput.setObjectName("rotyoutput") - self.gridLayout_7.addWidget(self.rotyoutput, 2, 2, 1, 1) - self.rotzlabel = QtWidgets.QLabel(self.rotationwidget) - self.rotzlabel.setMaximumSize(QtCore.QSize(16, 16777215)) - self.rotzlabel.setObjectName("rotzlabel") - self.gridLayout_7.addWidget(self.rotzlabel, 3, 0, 1, 2) - self.rotzoutput = QtWidgets.QLCDNumber(self.rotationwidget) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.rotzoutput.sizePolicy().hasHeightForWidth()) - self.rotzoutput.setSizePolicy(sizePolicy) - self.rotzoutput.setObjectName("rotzoutput") - self.gridLayout_7.addWidget(self.rotzoutput, 3, 2, 1, 1) - self.rotationlabel = QtWidgets.QLabel(self.rotationwidget) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.rotationlabel.sizePolicy().hasHeightForWidth()) - self.rotationlabel.setSizePolicy(sizePolicy) - self.rotationlabel.setObjectName("rotationlabel") - self.gridLayout_7.addWidget(self.rotationlabel, 0, 0, 1, 3) - self.leakwidget = QtWidgets.QFrame(self.mainframe) - self.leakwidget.setGeometry(QtCore.QRect(180, 250, 221, 71)) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) - sizePolicy.setHorizontalStretch(1) - sizePolicy.setVerticalStretch(1) - sizePolicy.setHeightForWidth(self.leakwidget.sizePolicy().hasHeightForWidth()) - self.leakwidget.setSizePolicy(sizePolicy) - self.leakwidget.setFrameShape(QtWidgets.QFrame.StyledPanel) - self.leakwidget.setFrameShadow(QtWidgets.QFrame.Raised) - self.leakwidget.setObjectName("leakwidget") - self.horizontalLayout_6 = QtWidgets.QHBoxLayout(self.leakwidget) - self.horizontalLayout_6.setObjectName("horizontalLayout_6") - self.leaklabel = QtWidgets.QLabel(self.leakwidget) - self.leaklabel.setObjectName("leaklabel") - self.horizontalLayout_6.addWidget(self.leaklabel, 0, QtCore.Qt.AlignHCenter) - self.leakoutput = QtWidgets.QLCDNumber(self.leakwidget) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.leakoutput.sizePolicy().hasHeightForWidth()) - self.leakoutput.setSizePolicy(sizePolicy) - self.leakoutput.setObjectName("leakoutput") - self.horizontalLayout_6.addWidget(self.leakoutput) - self.tempwidget = QtWidgets.QFrame(self.mainframe) - self.tempwidget.setGeometry(QtCore.QRect(410, 250, 221, 71)) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) - sizePolicy.setHorizontalStretch(1) - sizePolicy.setVerticalStretch(1) - sizePolicy.setHeightForWidth(self.tempwidget.sizePolicy().hasHeightForWidth()) - self.tempwidget.setSizePolicy(sizePolicy) - self.tempwidget.setFrameShape(QtWidgets.QFrame.StyledPanel) - self.tempwidget.setFrameShadow(QtWidgets.QFrame.Raised) - self.tempwidget.setObjectName("tempwidget") - self.horizontalLayout_7 = QtWidgets.QHBoxLayout(self.tempwidget) - self.horizontalLayout_7.setObjectName("horizontalLayout_7") - self.templabel = QtWidgets.QLabel(self.tempwidget) - self.templabel.setObjectName("templabel") - self.horizontalLayout_7.addWidget(self.templabel, 0, QtCore.Qt.AlignHCenter) - self.tempoutput = QtWidgets.QLCDNumber(self.tempwidget) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.tempoutput.sizePolicy().hasHeightForWidth()) - self.tempoutput.setSizePolicy(sizePolicy) - self.tempoutput.setObjectName("tempoutput") - self.horizontalLayout_7.addWidget(self.tempoutput) - self.offsetwidget = QtWidgets.QFrame(self.mainframe) - self.offsetwidget.setGeometry(QtCore.QRect(410, 330, 221, 151)) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) - sizePolicy.setHorizontalStretch(1) - sizePolicy.setVerticalStretch(1) - sizePolicy.setHeightForWidth(self.offsetwidget.sizePolicy().hasHeightForWidth()) - self.offsetwidget.setSizePolicy(sizePolicy) - self.offsetwidget.setFrameShape(QtWidgets.QFrame.StyledPanel) - self.offsetwidget.setFrameShadow(QtWidgets.QFrame.Raised) - self.offsetwidget.setObjectName("offsetwidget") - self.gridLayout_4 = QtWidgets.QGridLayout(self.offsetwidget) - self.gridLayout_4.setObjectName("gridLayout_4") - self.label_18 = QtWidgets.QLabel(self.offsetwidget) - self.label_18.setObjectName("label_18") - self.gridLayout_4.addWidget(self.label_18, 1, 0, 1, 1) - self.offsetinput = QtWidgets.QDoubleSpinBox(self.offsetwidget) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.offsetinput.sizePolicy().hasHeightForWidth()) - self.offsetinput.setSizePolicy(sizePolicy) - self.offsetinput.setObjectName("offsetinput") - self.gridLayout_4.addWidget(self.offsetinput, 1, 1, 1, 1) - self.label_19 = QtWidgets.QLabel(self.offsetwidget) - self.label_19.setObjectName("label_19") - self.gridLayout_4.addWidget(self.label_19, 2, 0, 1, 1) - self.offsetinput_2 = QtWidgets.QDoubleSpinBox(self.offsetwidget) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.offsetinput_2.sizePolicy().hasHeightForWidth()) - self.offsetinput_2.setSizePolicy(sizePolicy) - self.offsetinput_2.setObjectName("offsetinput_2") - self.gridLayout_4.addWidget(self.offsetinput_2, 2, 1, 1, 1) - self.offsetlabel = QtWidgets.QLabel(self.offsetwidget) - self.offsetlabel.setObjectName("offsetlabel") - self.gridLayout_4.addWidget(self.offsetlabel, 0, 0, 1, 2, QtCore.Qt.AlignHCenter) - self.pushButton = QtWidgets.QPushButton(self.mainframe) - self.pushButton.setGeometry(QtCore.QRect(1170, 10, 241, 81)) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) - sizePolicy.setHorizontalStretch(1) - sizePolicy.setVerticalStretch(1) - sizePolicy.setHeightForWidth(self.pushButton.sizePolicy().hasHeightForWidth()) - self.pushButton.setSizePolicy(sizePolicy) - self.pushButton.setObjectName("pushButton") - self.velocitywidget_2 = QtWidgets.QFrame(self.mainframe) - self.velocitywidget_2.setGeometry(QtCore.QRect(10, 250, 161, 231)) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) - sizePolicy.setHorizontalStretch(1) - sizePolicy.setVerticalStretch(1) - sizePolicy.setHeightForWidth(self.velocitywidget_2.sizePolicy().hasHeightForWidth()) - self.velocitywidget_2.setSizePolicy(sizePolicy) - self.velocitywidget_2.setFrameShape(QtWidgets.QFrame.StyledPanel) - self.velocitywidget_2.setFrameShadow(QtWidgets.QFrame.Raised) - self.velocitywidget_2.setObjectName("velocitywidget_2") - self.gridLayout_8 = QtWidgets.QGridLayout(self.velocitywidget_2) - self.gridLayout_8.setObjectName("gridLayout_8") - self.xlabel_3 = QtWidgets.QLabel(self.velocitywidget_2) - self.xlabel_3.setObjectName("xlabel_3") - self.gridLayout_8.addWidget(self.xlabel_3, 1, 0, 1, 1) - self.xoutput_2 = QtWidgets.QLCDNumber(self.velocitywidget_2) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.xoutput_2.sizePolicy().hasHeightForWidth()) - self.xoutput_2.setSizePolicy(sizePolicy) - self.xoutput_2.setObjectName("xoutput_2") - self.gridLayout_8.addWidget(self.xoutput_2, 1, 2, 1, 1) - self.ylabel_2 = QtWidgets.QLabel(self.velocitywidget_2) - self.ylabel_2.setObjectName("ylabel_2") - self.gridLayout_8.addWidget(self.ylabel_2, 2, 0, 1, 1) - self.youtput_2 = QtWidgets.QLCDNumber(self.velocitywidget_2) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.youtput_2.sizePolicy().hasHeightForWidth()) - self.youtput_2.setSizePolicy(sizePolicy) - self.youtput_2.setObjectName("youtput_2") - self.gridLayout_8.addWidget(self.youtput_2, 2, 2, 1, 1) - self.xlabel_4 = QtWidgets.QLabel(self.velocitywidget_2) - self.xlabel_4.setMaximumSize(QtCore.QSize(16, 16777215)) - self.xlabel_4.setObjectName("xlabel_4") - self.gridLayout_8.addWidget(self.xlabel_4, 3, 0, 1, 2) - self.zoutput_2 = QtWidgets.QLCDNumber(self.velocitywidget_2) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.zoutput_2.sizePolicy().hasHeightForWidth()) - self.zoutput_2.setSizePolicy(sizePolicy) - self.zoutput_2.setObjectName("zoutput_2") - self.gridLayout_8.addWidget(self.zoutput_2, 3, 2, 1, 1) - self.velocitylabel_2 = QtWidgets.QLabel(self.velocitywidget_2) - self.velocitylabel_2.setObjectName("velocitylabel_2") - self.gridLayout_8.addWidget(self.velocitylabel_2, 0, 0, 1, 3, QtCore.Qt.AlignHCenter) - MainWindow.setCentralWidget(self.centralwidget) - - self.retranslateUi(MainWindow) - QtCore.QMetaObject.connectSlotsByName(MainWindow) - - def retranslateUi(self, MainWindow): - _translate = QtCore.QCoreApplication.translate - MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow")) - self.label.setText(_translate("MainWindow", "THRUSTERS")) - self.tfllabel.setText(_translate("MainWindow", "TFL")) - self.label_39.setText(_translate("MainWindow", "-127")) - self.label_37.setText(_translate("MainWindow", "-127")) - self.tbllabel.setText(_translate("MainWindow", "TBL")) - self.tfrlabel.setText(_translate("MainWindow", "TFR")) - self.label_13.setText(_translate("MainWindow", "-127")) - self.label_10.setText(_translate("MainWindow", "-127")) - self.tbrlabel.setText(_translate("MainWindow", "TBR")) - self.label_2.setText(_translate("MainWindow", "0")) - self.label_11.setText(_translate("MainWindow", "127")) - self.label_12.setText(_translate("MainWindow", "127")) - self.label_36.setText(_translate("MainWindow", "127")) - self.label_38.setText(_translate("MainWindow", "127")) - self.label_3.setText(_translate("MainWindow", "0")) - self.label_4.setText(_translate("MainWindow", "0")) - self.label_5.setText(_translate("MainWindow", "0")) - self.tfllabel_2.setText(_translate("MainWindow", "BFL")) - self.label_40.setText(_translate("MainWindow", "-127")) - self.label_41.setText(_translate("MainWindow", "-127")) - self.tbllabel_2.setText(_translate("MainWindow", "BBL")) - self.tfrlabel_2.setText(_translate("MainWindow", "BFR")) - self.label_14.setText(_translate("MainWindow", "-127")) - self.label_15.setText(_translate("MainWindow", "-127")) - self.tbrlabel_2.setText(_translate("MainWindow", "BBR")) - self.label_6.setText(_translate("MainWindow", "0")) - self.label_16.setText(_translate("MainWindow", "127")) - self.label_17.setText(_translate("MainWindow", "127")) - self.label_42.setText(_translate("MainWindow", "127")) - self.label_43.setText(_translate("MainWindow", "127")) - self.label_7.setText(_translate("MainWindow", "0")) - self.label_8.setText(_translate("MainWindow", "0")) - self.label_9.setText(_translate("MainWindow", "0")) - self.xlabel.setText(_translate("MainWindow", "X")) - self.ylabel.setText(_translate("MainWindow", "Y")) - self.xlabel_2.setText(_translate("MainWindow", "Z")) - self.velocitylabel.setText(_translate("MainWindow", "VELOCITY OUTPUT")) - self.depthlabel.setText(_translate("MainWindow", "DEPTH")) - self.finemodelabel.setText(_translate("MainWindow", "FINE MODE")) - self.pnumaticlabel.setText(_translate("MainWindow", "PNUMATICS")) - self.line1label.setText(_translate("MainWindow", "LINE 1")) - self.line2label.setText(_translate("MainWindow", "LINE 2")) - self.line3label.setText(_translate("MainWindow", "LINE 3")) - self.rotxlabel.setText(_translate("MainWindow", "X")) - self.rotylabel.setText(_translate("MainWindow", "Y")) - self.rotzlabel.setText(_translate("MainWindow", "Z")) - self.rotationlabel.setText(_translate("MainWindow", "ROTATION OUTPUT")) - self.leaklabel.setText(_translate("MainWindow", "LEAK SENSOR")) - self.templabel.setText(_translate("MainWindow", "TEMPERATURE")) - self.label_18.setText(_translate("MainWindow", "ADD º")) - self.label_19.setText(_translate("MainWindow", "SUBTRACT º")) - self.offsetlabel.setText(_translate("MainWindow", "TEMP OFFSET")) - self.pushButton.setText(_translate("MainWindow", "Start Camera Streams")) - self.xlabel_3.setText(_translate("MainWindow", "Xº")) - self.ylabel_2.setText(_translate("MainWindow", "Yº")) - self.xlabel_4.setText(_translate("MainWindow", "Zº")) - self.velocitylabel_2.setText(_translate("MainWindow", "ROV ANGLE ")) diff --git a/ui/src/interface.ui b/ui/src/interface.ui deleted file mode 100644 index 42f7bca..0000000 --- a/ui/src/interface.ui +++ /dev/null @@ -1,993 +0,0 @@ - - - MainWindow - - - - 0 - 0 - 1440 - 847 - - - - - 1 - 1 - - - - MainWindow - - - - - - 10 - 10 - 1421 - 831 - - - - - 1 - 1 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - true - - - - 10 - 10 - 811 - 231 - - - - - 1 - 1 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - 0 - 0 - - - - THRUSTERS - - - - - - - - - - - 41 - 16777215 - - - - TFL - - - - - - - -127 - - - - - - - -127 - - - - - - - TBL - - - - - - - TFR - - - - - - - -127 - - - - - - - -127 - - - - - - - TBR - - - - - - - 0 - - - - - - - 127 - - - - - - - 127 - - - - - - - 127 - - - - - - - 127 - - - - - - - 24 - - - - - - - 24 - - - - - - - 24 - - - - - - - 24 - - - - - - - 0 - - - - - - - 0 - - - - - - - 0 - - - - - - - - - - - - - - 41 - 16777215 - - - - BFL - - - - - - - -127 - - - - - - - -127 - - - - - - - BBL - - - - - - - BFR - - - - - - - -127 - - - - - - - -127 - - - - - - - BBR - - - - - - - 0 - - - - - - - 127 - - - - - - - 127 - - - - - - - 127 - - - - - - - 127 - - - - - - - 24 - - - - - - - 24 - - - - - - - 24 - - - - - - - 24 - - - - - - - 0 - - - - - - - 0 - - - - - - - 0 - - - - - - - - - - - - 830 - 10 - 161 - 231 - - - - - 1 - 1 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - X - - - - - - - - 0 - 0 - - - - - - - - Y - - - - - - - - 0 - 0 - - - - - - - - - 16 - 16777215 - - - - Z - - - - - - - - 0 - 0 - - - - - - - - VELOCITY OUTPUT - - - - - - - - - 180 - 330 - 221 - 71 - - - - - 1 - 1 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - DEPTH - - - - - - - - 0 - 0 - - - - - - - - - - 180 - 410 - 221 - 71 - - - - - 1 - 1 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - FINE MODE - - - - - - - - 0 - 0 - - - - - - - - - - 640 - 250 - 291 - 231 - - - - - 1 - 1 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - PNUMATICS - - - - - - - LINE 1 - - - - - - - LINE 2 - - - - - - - LINE 3 - - - - - - - - 0 - 0 - - - - - - - - - 0 - 0 - - - - - - - - - 0 - 0 - - - - - - - - - - 1000 - 10 - 161 - 231 - - - - - 1 - 1 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - X - - - - - - - - 0 - 0 - - - - - - - - Y - - - - - - - - 0 - 0 - - - - - - - - - 16 - 16777215 - - - - Z - - - - - - - - 0 - 0 - - - - - - - - - 0 - 0 - - - - ROTATION OUTPUT - - - - - - - - - 180 - 250 - 221 - 71 - - - - - 1 - 1 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - LEAK SENSOR - - - - - - - - 0 - 0 - - - - - - - - - - 410 - 250 - 221 - 71 - - - - - 1 - 1 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - TEMPERATURE - - - - - - - - 0 - 0 - - - - - - - - - - 410 - 330 - 221 - 151 - - - - - 1 - 1 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - ADD º - - - - - - - - 0 - 0 - - - - - - - - SUBTRACT º - - - - - - - - 0 - 0 - - - - - - - - TEMP OFFSET - - - - - - - - - 1170 - 10 - 241 - 81 - - - - - 1 - 1 - - - - Start Camera Streams - - - - - - 10 - 250 - 161 - 231 - - - - - 1 - 1 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - - - - - - - - 0 - 0 - - - - - - - - - - - - - - - - 0 - 0 - - - - - - - - - 16 - 16777215 - - - - - - - - - - - - 0 - 0 - - - - - - - - ROV ANGLE - - - - - - - - - - - diff --git a/ui/src/main.py b/ui/src/main.py deleted file mode 100644 index 74126a3..0000000 --- a/ui/src/main.py +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env python3 - -from PyQt5.QtCore import * -from PyQt5.QtWidgets import * -from interface import Ui_MainWindow -from ssh import ssh -from streams import streams -from gamepad import gamepad - -class MainWindow(QMainWindow): - def __init__(self, ssh_comm): - super().__init__() - self.ui = Ui_MainWindow() - self.ui.setupUi(self) - self.ssh = ssh_comm - # setting general window properties - self.setWindowTitle("ROX X16") - screen_geometry = QDesktopWidget().screenGeometry() - screen_height = screen_geometry.height() - screen_width = screen_geometry.width() - self.setGeometry(0, screen_height // 2, screen_width, screen_height // 2) - self.setMaximumSize(screen_width, screen_height // 2) - self.setMinimumSize(screen_width, screen_height // 2) - - def closeEvent(self, event): - confirmation = QMessageBox.question( - self, - "Exit", - "Are you sure you want to exit?", - QMessageBox.Yes | QMessageBox.No, - ) - if confirmation == QMessageBox.Yes: - #streams.stop() - # gamepad.stop() - #ssh_comm = ssh() - self.ssh.close() - print("Closing application") - event.accept() - else: - event.ignore() \ No newline at end of file diff --git a/ui/src/runner.py b/ui/src/runner.py deleted file mode 100644 index ba0b496..0000000 --- a/ui/src/runner.py +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/env python3 - -# TODO: Refine error handling across all files - -import sys -import rclpy -import threading -from PyQt5.QtWidgets import QApplication -from main import MainWindow -from ThrustersSurface import ThrustersSurfaceNode -from DepthSurface import DepthSurfaceNode -from GamepadListener import GamepadSurfaceNode -from TempListener import TempListenerNode -from LeakListener import LeakListenerNode -import multiprocessing - -from interface import Ui_MainWindow -from ssh import ssh -from streams import streams -from GamepadSender import GamepadNode - - -def run_multiple_nodes(nodes): - while True: - for node in nodes: - rclpy.spin_once(node, timeout_sec=0.01) - - -def main(): - rclpy.init() - - print("Starting SSH processes...") - ssh_comm = ssh() - connection = ssh_comm.connect() - try: - print("Starting camera stream processes...") - streams_comm = streams(connection) - streams_comm.start() - - print("Connecting gamepad...") - # TODO: this - gamepad = GamepadNode() - - app = QApplication(sys.argv) - window = MainWindow(ssh_comm) - - print("Connecting fronted ros nodes...") - thrusters = ThrustersSurfaceNode(window=window) - depth = DepthSurfaceNode(window=window) - surfacegp = GamepadSurfaceNode(window=window) - temp = TempListenerNode(window=window) - leak = LeakListenerNode(window=window) - nodelist = [thrusters, depth, surfacegp, gamepad, temp, leak] - node_thread = threading.Thread( - target=run_multiple_nodes, args=(nodelist,)) - node_thread.daemon = True - node_thread.start() - - print("Starting application...") - window.show() - while(app.exec_()): - pass - streams_comm.stop() - sys.exit(1) - except Exception as e: - ssh_comm.close() - print(f"ERROR: {e}") - - -if __name__ == "__main__": - main() diff --git a/ui/src/ssh.py b/ui/src/ssh.py deleted file mode 100644 index e8ccce3..0000000 --- a/ui/src/ssh.py +++ /dev/null @@ -1,134 +0,0 @@ -import paramiko -import netifaces -import time -import os -from dotenv import load_dotenv - -load_dotenv(dotenv_path=f"{os.getcwd()}/src/X16-Surface/.env") - - -class ssh: - def __init__(self): - self.ssh_host = os.getenv("HOST_IP") - print(f"HOST IP IS {self.ssh_host}") - self.ssh_username = os.getenv("HOST_USERNAME") - self.ssh_password = os.getenv("HOST_PASSWORD") - self.device_name1 = os.getenv("DEVICE_NAME1") - self.device_name2 = os.getenv("DEVICE_NAME2") - self.device_name3 = os.getenv("DEVICE_NAME3") - self.device_name4 = os.getenv("DEVICE_NAME4") - self.ssh_client = None - self.pid_list = list() - self.connection = None - - def connect(self): - try: - # getting the local ip address - ip = self.get_ip() - print(f"Local IP address: {ip}") - #ros_id = 69 - # commands to launch on the pi - ros2_source_cmd = "source ~/.bashrc >> ~/ros2_ws/startup_logs/sourcebash.txt && export ROS_DOMAIN_ID=69 && source ros2_ws/install/setup.bash >> ~/ros2_ws/startup_logs/source.txt && echo $ROS_DOMAIN_ID >> ~/ros2_ws/startup_logs/domain_id_tmux" - #ros2_launch_cmd = "ros2 launch rov_launch run_rov_launch.xml >> ~/ros2_ws/startup_logs/launch.txt" - stream1_launch_cmd = f"gst-launch-1.0 -v v4l2src device={self.device_name1} ! video/x-h264, width=1920,height=1080! h264parse ! queue ! rtph264pay config-interval=10 pt=96 ! udpsink host={ip} port=5600 sync=false buffer-size=1048576 & echo $! > pid.txt" - stream2_launch_cmd = f"gst-launch-1.0 -v v4l2src device={self.device_name2} ! video/x-h264, width=1920,height=1080! h264parse ! queue ! rtph264pay config-interval=10 pt=96 ! udpsink host={ip} port=5601 sync=false buffer-size=1048576 & echo $! > pid.txt" - stream3_launch_cmd = f"gst-launch-1.0 -v v4l2src device={self.device_name3} ! video/x-h264, width=1920,height=1080! h264parse ! queue ! rtph264pay config-interval=10 pt=96 ! udpsink host={ip} port=5602 sync=false buffer-size=1048576 & echo $! > pid.txt" - stream4_launch_cmd = f"gst-launch-1.0 -v v4l2src device={self.device_name4} ! video/x-h264, width=1920,height=1080! h264parse ! queue ! rtph264pay config-interval=10 pt=96 ! udpsink host={ip} port=5603 sync=false buffer-size=1048576 & echo $! > pid.txt" - - # print(stream1_launch_cmd) - - # establishing the ssh connection - print(f"Establishing SSH connection to {self.ssh_host}...") - self.ssh_client = paramiko.SSHClient() - self.ssh_client.set_missing_host_key_policy( - paramiko.AutoAddPolicy()) - self.ssh_client.connect( - self.ssh_host, - username=self.ssh_username, - password=self.ssh_password, - timeout=5, - ) - if self.ssh_client is not None: - print("SSH connection established") - self.connection = True - else: - print("ERROR: SSH connection failed") - return - - # launching the ros2 nodes on the pi - # self.launch_ros2_nodes(ros2_source_cmd, ros2_launch_cmd) - - # launching the camera streams on the pi - self.launch_stream(1, stream1_launch_cmd) - self.launch_stream(2, stream2_launch_cmd) - self.launch_stream(3, stream3_launch_cmd) - self.launch_stream(4, stream4_launch_cmd) - - return self.connection - - except Exception as e: - print(f"ERROR: {e}") - return - - def close(self): - # killing each process - # if self.pid_list is not None: - # for pid in self.pid_list: - # self.ssh_client.exec_command("kill " + pid) - kill_gst_command = "ps aux | grep 'gst-launch-1.0' | awk '{print $2}' | xargs -r kill -9" - kill_ros_and_tmux_command = "ps aux | grep ros2 | awk '{print $2}' | xargs kill -9 && tmux kill-session -t ros2_session" - combined_kill_command = f"{kill_ros_and_tmux_command} && {kill_gst_command}" - if self.ssh_client is not None: - self.ssh_client.exec_command(combined_kill_command) - - # "ps aux | grep ros2 | awk '{print $2}' | xargs kill -9 && tmux kill-session -t ros2_session") - - # closing the ssh connection - if self.ssh_client is not None: - self.ssh_client.close() - print("SSH connection closed") - - def get_ip(self): - try: - interfaces = netifaces.interfaces() - for interface in interfaces: - addrs = netifaces.ifaddresses(interface) - if netifaces.AF_INET in addrs: - for addr_info in addrs[netifaces.AF_INET]: - ip_address = addr_info["addr"] - if ip_address.startswith("192.168.1."): - return ip_address - except Exception as e: - print(f"ERROR: {e}") - - def launch_ros2_nodes(self, ros2_source_cmd, ros2_launch_cmd): - try: - print("Launching ROS2 nodes...") - - # Concatenate the commands and run them in a single exec_command call - full_command = f"tmux new-session -d -s ros2_session 'bash -c \"{ros2_source_cmd} && {ros2_launch_cmd}\"'" - self.ssh_client.exec_command(full_command) - - time.sleep(1) - print("ROS2 nodes launched") - except Exception as e: - print(f"ERROR: {e}") - - def launch_stream(self, num, cmd): - try: - print(f"Launching camera stream {num}...") - self.ssh_client.exec_command(cmd) - time.sleep(1) - __, stdout, __ = self.ssh_client.exec_command("cat pid.txt") - self.pid = stdout.read().decode("utf-8").strip() - self.pid_list.append(self.pid) - print(f"Process {self.pid} started") - except Exception as e: - print(f"ERROR: {e}") - - -# launch command for camera stream 1 -# gst-launch-1.0 -v v4l2src device=/dev/video0 ! image/jpeg, width=1920, height=1080, framerate=30/1 ! jpegparse ! rtpjpegpay ! udpsink host=10.0.0.103 port=5600 sync=false buffer-size=1048576 -# gst-launch-1.0 -v udpsrc port=5600 ! application/x-rtp, payload=26 ! rtpjpegdepay ! jpegdec ! autovideosink - -# launch command for camera stream 2 diff --git a/ui/src/streams.py b/ui/src/streams.py deleted file mode 100644 index dc7fc9b..0000000 --- a/ui/src/streams.py +++ /dev/null @@ -1,76 +0,0 @@ -import subprocess - -stream1_receive_cmd = "gst-launch-1.0 udpsrc port=5600 ! application/x-rtp ! rtpjitterbuffer ! rtph264depay ! avdec_h264 ! videoconvert ! autovideosink sync=false" -stream2_receive_cmd = "gst-launch-1.0 udpsrc port=5601 ! application/x-rtp ! rtpjitterbuffer ! rtph264depay ! avdec_h264 ! videoconvert ! autovideosink sync=false" -stream3_receive_cmd = "gst-launch-1.0 udpsrc port=5602 ! application/x-rtp ! rtpjitterbuffer ! rtph264depay ! avdec_h264 ! videoconvert ! autovideosink sync=false" -stream4_receive_cmd = "gst-launch-1.0 udpsrc port=5603 ! application/x-rtp ! rtpjitterbuffer ! rtph264depay ! avdec_h264 ! videoconvert ! autovideosink sync=false" - - - -class streams: - def __init__(self, connection): - self.stream1_process = None - self.stream2_process = None - self.ssh_connection = connection - - def start(self): - if self.ssh_connection is None: - print("ERROR: camera streams unable to start") - return - - print("Receiving camera stream 1...") - self.stream1_process = subprocess.Popen( - stream1_receive_cmd, - shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - ) - if self.stream1_process is not None: - print(f"Process {self.stream1_process.pid} started") - else: - print("Failed to receive camera stream 1") - - print("Receiving camera stream 2...") - self.stream2_process = subprocess.Popen( - stream2_receive_cmd, - shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - ) - if self.stream2_process is not None: - print(f"Process {self.stream2_process.pid} started") - else: - print("Failed to receive camera stream 2") - - print("Receiving camera stream 3...") - self.stream3_process = subprocess.Popen( - stream3_receive_cmd, - shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - ) - if self.stream3_process is not None: - print(f"Process {self.stream3_process.pid} started") - else: - print("Failed to receive camera stream 3") - - print("Receiving camera stream 4...") - self.stream4_process = subprocess.Popen( - stream4_receive_cmd, - shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - ) - if self.stream4_process is not None: - print(f"Process {self.stream4_process.pid} started") - else: - print("Failed to receive camera stream 4") - - - def stop(self): - if self.stream1_process is not None: - self.stream1_process.kill() - print(f"Process {self.stream1_process.pid} killed") - if self.stream2_process is not None: - self.stream2_process.kill() - print(f"Process {self.stream2_process.pid} killed") diff --git a/ui/src/subscribers/DepthSurface.py b/ui/src/subscribers/DepthSurface.py deleted file mode 100644 index d2f4c10..0000000 --- a/ui/src/subscribers/DepthSurface.py +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env python3 - -import json -import rclpy -from rclpy.node import Node -from std_msgs.msg import Float64 - -class DepthSurfaceNode(Node): - def __init__(self, window): - super().__init__("surface_depth") - self.subscription = self.create_subscription( - Float64, "/depth", self.callback, 10 - ) - - # Initialize the thrust array - self.window = window - print("initialized") - - def callback(self, data): - depth = json.dumps(data.data) - self.window.ui.depthoutput.display(data.data) - - print(depth, flush=True, end=" ") - - -def main(args=None): - rclpy.init(args=args) - node = DepthSurfaceNode() - rclpy.spin(node) - rclpy.shutdown() - - -if __name__ == "__main__": - main() diff --git a/ui/src/subscribers/GamepadListener.py b/ui/src/subscribers/GamepadListener.py deleted file mode 100644 index ae9a233..0000000 --- a/ui/src/subscribers/GamepadListener.py +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env python3 - -import json -import rclpy -from rclpy.node import Node -from shared_msgs.msg import RovVelocityCommand -from std_msgs.msg import Float64 - - -class GamepadSurfaceNode(Node): - def __init__(self, window): - super().__init__("surface_gamepad") - self.subscription = self.create_subscription( - RovVelocityCommand, "/rov_velocity", self.callback, 10 - ) - - # Initialize the thrust array - self.thrust = [0, 0, 0, 0, 0, 0, 0, 0] - self.window = window - print("initialized") - - def callback(self, data): - #print('callback') - fine = json.dumps(data.is_fine) - pool_centric = json.dumps(data.is_pool_centric) - pitch_lock = json.dumps(data.pitch_lock) - depth_lock = json.dumps(data.pitch_lock) - - twist = data.twist - linear = twist.linear - angular = twist.angular - self.window.ui.xoutput.display(linear.x) - self.window.ui.youtput.display(linear.y) - self.window.ui.zoutput.display(linear.z) - self.window.ui.rotxoutput.display(linear.x) - self.window.ui.rotyoutput.display(linear.y) - self.window.ui.rotzoutput.display(angular.z) - self.window.ui.finemodeoutput.display(fine) - -def main(args=None): - rclpy.init(args=args) - node = GamepadSurfaceNode() - rclpy.spin(node) - rclpy.shutdown() - - -if __name__ == "__main__": - main() diff --git a/ui/src/subscribers/GamepadSender.py b/ui/src/subscribers/GamepadSender.py deleted file mode 100644 index 6daec1e..0000000 --- a/ui/src/subscribers/GamepadSender.py +++ /dev/null @@ -1,207 +0,0 @@ - -import pygame -import sys -import time - -# ROS -import rclpy -from rclpy.node import Node -from std_msgs.msg import String, Bool, Empty -from shared_msgs.msg import RovVelocityCommand, ToolsCommandMsg -from geometry_msgs.msg import Twist - -from config import * - - -class GamepadNode(Node): - def __init__(self): - try: - super().__init__('gp_pub') - self.pub = self.create_publisher( - RovVelocityCommand, 'rov_velocity', 10) - self.pub_tools = self.create_publisher( - ToolsCommandMsg, 'tools', 10) - - self.tools = [0, 0, 0, 0, 0] - - self.SCALE_TRANSLATIONAL_X = 1.0 - self.SCALE_TRANSLATIONAL_Y = 1.0 - self.SCALE_TRANSLATIONAL_Z = 1.0 - - self.SCALE_ROTATIONAL_X = 1.0 - self.SCALE_ROTATIONAL_Y = 1.0 - self.SCALE_ROTATIONAL_Z = 1.0 - - self.TRIM_X = 0.0 - self.TRIM_Y = 0.0 - self.TRIM_Z = 0.0 - - self.REVERSE = 1 - self.LOCKOUT = True - self.is_fine = 0 - self.is_pool_centric = False - self.depth_lock = False - self.pitch_lock = False - self.GAMEPAD_TIMEOUT = 20 - - self.gamepad_state = gamepad_state - - self.init_pygame() - - self.timer_data = self.create_timer(0.1, self.pub_data) - self.timer_gamepad = self.create_timer(0.001, self.update_gamepad) - - print('ready') - - except Exception as e: - print(f'Error initializing gamepad: {e}') - self.handle_gamepad_init_error() - - def handle_gamepad_init_error(self): - print('No gamepad found, please connect a gamepad') - if not self.reconnect_gamepad(): - print("\nNo gamepad found, exiting") - pygame.quit() - sys.exit(0) - - def init_pygame(self): - pygame.init() - pygame.joystick.init() - assert pygame.joystick.get_count() == 1 - self.joystick = pygame.joystick.Joystick(0) - - def reconnect_gamepad(self): - reconnected = False - i = self.GAMEPAD_TIMEOUT - while i >= 0 and not reconnected: - try: - print('Gamepad disconnected, reconnect within {:2} seconds'.format( - i), end='\r') - pygame.init() - pygame.joystick.init() - if pygame.joystick.get_count() == 1: - self.joystick = pygame.joystick.Joystick(0) - reconnected = True - else: - pygame.quit() - assert False - except: - pygame.time.wait(1000) - i -= 1 - - if reconnected: - print('\nGamepad reconnected') - self.joystick = pygame.joystick.Joystick(0) - - return reconnected - - def correct_raw(self, raw, abbv): - sign = (raw >= 0) * 2 - 1 - raw = abs(raw) - - if abbv == 'LT' or abbv == 'RT': - dead_zone = TRIGGER_DEAD_ZONE - value_range = TRIGGER_RANGE - else: - dead_zone = STICK_DEAD_ZONE - value_range = STICK_RANGE - - if raw < dead_zone: - return 0.0 - - raw -= dead_zone - raw *= value_range / (value_range - dead_zone) - raw = 1.0 if raw > value_range else raw / value_range - corrected = round(raw, 3) - corrected *= sign - return corrected - - def process_event(self, event): - if event.type == pygame.JOYBUTTONDOWN: - self.gamepad_state[JOY_BUTTON[event.button]] = 1 - if event.button == JOY_BUTTON_KEY['A']: - self.tools[0] = not self.tools[0] - elif event.button == JOY_BUTTON_KEY['B']: - self.tools[1] = not self.tools[1] - elif event.button == JOY_BUTTON_KEY['X']: - self.tools[2] = not self.tools[2] - elif event.button == JOY_BUTTON_KEY['Y'] and self.LOCKOUT: - self.tools[3] = not self.tools[3] - elif event.button == JOY_BUTTON_KEY['MENU']: - self.is_pool_centric = not self.is_pool_centric - - elif event.type == pygame.JOYBUTTONUP: - self.gamepad_state[JOY_BUTTON[event.button]] = 0 - - elif event.type == pygame.JOYHATMOTION: - if event.value[1] == 1: - if self.is_fine < 3: - self.is_fine +=1 - elif event.value[1] == -1: - if self.is_fine > 0: - self.is_fine -=1 - else: - pass - if event.value[0] == -1: - self.pitch_lock = not self.pitch_lock - elif event.value[0] == 1: - self.depth_lock = not self.depth_lock - self.is_pool_centric = True - else: - pass - - elif event.type == pygame.JOYAXISMOTION: - self.gamepad_state[JOY_AXIS[event.axis]] = self.correct_raw( - event.value, JOY_AXIS[event.axis]) - - elif event.type == pygame.JOYDEVICEREMOVED: - if not self.reconnect_gamepad(): - print("\nNo gamepad found, exiting") - pygame.quit() - rclpy.shutdown() - sys.exit(0) - - def pub_data(self): - self.pub.publish(self.getMessage()) - self.pub_tools.publish(self.getTools()) - - def update_gamepad(self): - for event in pygame.event.get(): - self.process_event(event) - - def getMessage(self): - t = Twist() - t.linear.x = - \ - (self.gamepad_state['LSY'] * - self.SCALE_TRANSLATIONAL_X + self.TRIM_X) * self.REVERSE - t.linear.y = - \ - (self.gamepad_state['LSX'] * - self.SCALE_TRANSLATIONAL_Y + self.TRIM_Y) * self.REVERSE - t.linear.z = ((self.gamepad_state['RT'] - self.gamepad_state['LT'] - ) / 2.0) * self.SCALE_TRANSLATIONAL_Z + self.TRIM_Z - - if self.gamepad_state['LB'] == 1: - x = 1 * self.SCALE_ROTATIONAL_X - elif self.gamepad_state['RB'] == 1: - x = -1 * self.SCALE_ROTATIONAL_X - else: - x = 0.0 - - t.angular.x = -x - t.angular.y = (-self.gamepad_state['RSY'] - * self.SCALE_ROTATIONAL_Y) * self.REVERSE - t.angular.z = -self.gamepad_state['RSX'] * self.SCALE_ROTATIONAL_Z - - new_msg = RovVelocityCommand() - new_msg.twist = t - new_msg.is_fine = self.is_fine - new_msg.is_pool_centric = self.is_pool_centric - new_msg.depth_lock = self.depth_lock - new_msg.pitch_lock = self.pitch_lock - - return new_msg - - def getTools(self): - tm = ToolsCommandMsg() - tm.tools = [i for i in self.tools] - return tm diff --git a/ui/src/subscribers/LeakListener.py b/ui/src/subscribers/LeakListener.py deleted file mode 100644 index 98d6f36..0000000 --- a/ui/src/subscribers/LeakListener.py +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env python3 - -import rclpy -from rclpy.node import Node -from std_msgs.msg import Bool - -class LeakListenerNode(Node): - def __init__(self, window): - super().__init__("leak_listener") - self.subscription = self.create_subscription( - Bool, "leak_sensor", self.callback, 10 - ) - - self.window = window - - def callback(self, data): - self.window.ui.leakoutput.display(data.data) - - - -def main(args=None): - rclpy.init(args=args) - node = LeakListenerNode() - rclpy.spin(node) - rclpy.shutdown() - - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/ui/src/subscribers/TempListener.py b/ui/src/subscribers/TempListener.py deleted file mode 100644 index 5237a87..0000000 --- a/ui/src/subscribers/TempListener.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env python3 - -import rclpy -from rclpy.node import Node -from shared_msgs.msg import TempMsg - - -class TempListenerNode(Node): - def __init__(self, window): - super().__init__("temp_listener") - self.subscription = self.create_subscription( - TempMsg, "water_temp", self.callback, 10 - ) - - self.window = window - - def callback(self, data): - add = self.window.ui.offsetinput.value() - sub = self.window.ui.offsetinput_2.value() - self.window.ui.tempoutput.display(data.temperature + add - sub) - print(data.temperature + add - sub) - - -def main(args=None): - rclpy.init(args=args) - node = TempListenerNode() - rclpy.spin(node) - rclpy.shutdown() - - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/ui/src/subscribers/ThrustersSurface.py b/ui/src/subscribers/ThrustersSurface.py deleted file mode 100644 index 4721500..0000000 --- a/ui/src/subscribers/ThrustersSurface.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env python3 - -import json -import rclpy -from rclpy.node import Node -from shared_msgs.msg import FinalThrustMsg - - -class ThrustersSurfaceNode(Node): - def __init__(self, window): - super().__init__("thrusters_surface") - self.subscription = self.create_subscription( - FinalThrustMsg, "/final_thrust", self.thrust_callback, 10 - ) - - # Initialize the thrust array - self.thrust = [0, 0, 0, 0, 0, 0, 0, 0] - self.window = window - print("initialized") - - def thrust_callback(self, comm): - self.window.ui.tfloutput_2.setProperty("value", int(comm.thrusters[4])/255 * 100) - self.window.ui.tfroutput_2.setProperty("value", int(comm.thrusters[7])/255 * 100) - self.window.ui.tbloutput_2.setProperty("value", int(comm.thrusters[5])/255 * 100) - self.window.ui.tbroutput_2.setProperty("value", int(comm.thrusters[6])/255 * 100) - self.window.ui.tfloutput.setProperty("value", int(comm.thrusters[0])/255 * 100) - self.window.ui.tfroutput.setProperty("value", int(comm.thrusters[3])/255 * 100) - self.window.ui.tbloutput.setProperty("value", int(comm.thrusters[1])/255 * 100) - self.window.ui.tbroutput.setProperty("value", int(comm.thrusters[2])/255 * 100) - self.thrust[0] = int(comm.thrusters[0]) - self.thrust[1] = int(comm.thrusters[1]) - self.thrust[2] = int(comm.thrusters[2]) - self.thrust[3] = int(comm.thrusters[3]) - self.thrust[4] = int(comm.thrusters[4]) - self.thrust[5] = int(comm.thrusters[5]) - self.thrust[6] = int(comm.thrusters[6]) - self.thrust[7] = int(comm.thrusters[7]) - - # Print the thrust array as JSON - self.get_logger().info(json.dumps(self.thrust)) - print("running") - - -def main(args=None): - rclpy.init(args=args) - node = ThrustersSurfaceNode() - rclpy.spin(node) - rclpy.shutdown() - - -if __name__ == "__main__": - main() diff --git a/ui/src/subscribers/config.py b/ui/src/subscribers/config.py deleted file mode 100644 index f307690..0000000 --- a/ui/src/subscribers/config.py +++ /dev/null @@ -1,37 +0,0 @@ -MIN_ABS_DIFFERENCE = 0 -# New ranges for the triggers and sticks (Probably need to adjust these values) -TRIGGER_DEAD_ZONE = 0.09 -TRIGGER_RANGE = 1.0 -STICK_DEAD_ZONE = 0.09 -STICK_RANGE = 1.0 - -# Mapping of JoyAxisMotion events to gamepad_state keys -# The number is the axis given by pygame -JOY_AXIS = {1: 'LSY', 0: 'LSX', 4: 'RSY', 3: 'RSX', 2: 'LT', 5: 'RT'} - -# Mapping of JoyButton events to gamepad_state keys -# The number is the button given by pygame -JOY_BUTTON = {3: 'Y', 1: 'B', 0: 'A', 2: 'X', 9: 'LSZ', 10: 'RSZ', 4: 'LB', 5: 'RB', 8: 'XBOX', 6: 'START', 7: 'MENU'} -JOY_BUTTON_KEY = {'Y': 3, 'B': 1, 'A': 0, 'X': 2, 'LSZ': 9, 'RSZ': 10, 'LB': 4, 'RB': 5, 'XBOX': 8, 'START': 6, 'MENU': 7} - -gamepad_state = { - "LSX": 0.0, - "LSY": 0.0, - "RSX": 0.0, - "RSY": 0.0, - "LT": -1.0, - "RT": -1.0, - "DPADX": 0, - "DPADY": 0, - "Y": 0, - "B": 0, - "A": 0, - "X": 0, - "LSZ": 0, - "RSZ": 0, - "LB": 0, - "RB": 0, - "XBOX": 0, - "START": 0, - "MENU": 0, -} diff --git a/ui/src/templates/index.html b/ui/src/templates/index.html new file mode 100644 index 0000000..ea7c6b4 --- /dev/null +++ b/ui/src/templates/index.html @@ -0,0 +1,14 @@ + + + + WebRTC Video Stream(s) + + +
+ + + + +
+ + \ No newline at end of file diff --git a/ui/testing/working_opencv.py b/ui/testing/working_opencv.py deleted file mode 100644 index 1ab1e31..0000000 --- a/ui/testing/working_opencv.py +++ /dev/null @@ -1,65 +0,0 @@ -#!/usr/bin/env python -import cv2 -import gi -import numpy as np -import command as command - -gi.require_version("Gst", "1.0") -from gi.repository import Gst - - -class Video: - def __init__(self): - Gst.init(None) - self.latest_frame = self._new_frame = None - self.command = "udpsrc port=5600 ! application/x-rtp ! rtpjitterbuffer ! rtph264depay ! avdec_h264 ! decodebin ! videoconvert ! video/x-raw,format=(string)BGR ! videoconvert ! appsink emit-signals=true sync=false max-buffers=2 drop=true" - self.video_pipe = None - self.video_sink = None - self.run(self.command) - - def start_gst(self, command): - self.video_pipe = Gst.parse_launch(command) - self.video_pipe.set_state(Gst.State.PLAYING) - self.video_sink = self.video_pipe.get_by_name("appsink0") - - @staticmethod - def gst_to_opencv(sample): - buf = sample.get_buffer() - caps_structure = sample.get_caps().get_structure(0) - array = np.ndarray( - (caps_structure.get_value("height"), caps_structure.get_value("width"), 3), - buffer=buf.extract_dup(0, buf.get_size()), - dtype=np.uint8, - ) - return array - - def frame(self): - if self.frame_available: - self.latest_frame = self._new_frame - self._new_frame = None - return self.latest_frame - - def frame_available(self): - return self._new_frame is not None - - def run(self, command): - self.start_gst(command) - self.video_sink.connect("new-sample", self.callback) - - def callback(self, sink): - sample = sink.emit("pull-sample") - self._new_frame = self.gst_to_opencv(sample) - return Gst.FlowReturn.OK - - -if __name__ == "__main__": - video = Video() - while not video.frame_available(): - cv2.waitKey(30) - while True: - if video.frame_available(): - frame = video.frame() - cv2.imshow("frame", frame) - if cv2.waitKey(1) & 0xFF == ord("q"): - break - cv2.destroyAllWindows()