From b52961d908022019d645cc16e74c39316ae408c0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 8 Jul 2024 18:11:07 +0000 Subject: [PATCH] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/float/setup.py | 3 +- src/float/test/test_clang_format.py | 14 ++- .../camera_streamer/launch/camera_launch.py | 48 ++++---- src/pi/camera_streamer/setup.py | 10 +- src/pi/camera_streamer/test/test_flake8.py | 8 +- src/pi/camera_streamer/test/test_mypy.py | 5 +- src/pi/camera_streamer/test/test_pep257.py | 2 +- .../flood_detection/flood_detector.py | 8 +- .../flood_detection/gpio_reader_dry_run.py | 4 +- .../launch/flood_detection_launch.py | 6 +- src/pi/flood_detection/setup.py | 16 +-- src/pi/flood_detection/test/test_flake8.py | 8 +- src/pi/flood_detection/test/test_mypy.py | 5 +- src/pi/flood_detection/test/test_pep257.py | 2 +- src/pi/manipulators/launch/manip_launch.py | 35 +++--- .../manipulators/lgpio_manipulator.py | 7 +- .../manipulators/lgpio_manipulator_dry_run.py | 4 +- .../manipulators/manipulator_dry_run.py | 4 +- .../manipulators/manipulator_node.py | 20 ++-- .../manipulators/valve_manipulator_dry_run.py | 5 +- .../manipulators/valve_manipulator_node.py | 3 +- src/pi/manipulators/setup.py | 12 +- src/pi/manipulators/test/test_flake8.py | 8 +- src/pi/manipulators/test/test_mypy.py | 5 +- src/pi/manipulators/test/test_pep257.py | 2 +- src/pi/pi_info/launch/pi_info_launch.py | 15 +-- src/pi/pi_info/pi_info/heartbeat_node.py | 6 +- src/pi/pi_info/pi_info/ip_publisher.py | 22 ++-- src/pi/pi_info/setup.py | 13 ++- src/pi/pi_info/test/test_flake8.py | 8 +- src/pi/pi_info/test/test_mypy.py | 5 +- src/pi/pi_info/test/test_pep257.py | 2 +- src/pi/pi_main/launch/pi_launch.py | 55 ++++----- src/pi/pi_main/pi_main/copy.py | 3 +- src/pi/pi_main/pi_main/run_on_boot.py | 5 +- src/pi/pi_main/setup.py | 20 ++-- src/pi/pi_main/test/test_flake8.py | 8 +- src/pi/pi_main/test/test_mypy.py | 5 +- src/pi/pi_main/test/test_pep257.py | 2 +- src/pi/pi_main/test/test_run_on_boot.py | 4 +- .../launch/mavros_launch.py | 21 ++-- src/pi/pixhawk_communication/setup.py | 10 +- .../pixhawk_communication/test/test_flake8.py | 8 +- .../pixhawk_communication/test/test_mypy.py | 5 +- .../pixhawk_communication/test/test_pep257.py | 2 +- .../temp_sensor/launch/temp_sensor_launch.py | 6 +- src/pi/temp_sensor/setup.py | 16 +-- src/pi/temp_sensor/temp_sensor/temp_sensor.py | 8 +- .../temp_sensor/temp_sensor_dry_run.py | 4 +- src/pi/temp_sensor/test/test_flake8.py | 8 +- src/pi/temp_sensor/test/test_mypy.py | 5 +- src/pi/temp_sensor/test/test_pep257.py | 2 +- .../flight_control/auto_docking_node.py | 11 +- .../flight_control/control_inverter_node.py | 11 +- .../flight_control/keyboard_control_node.py | 44 ++++--- .../flight_control/manual_control_node.py | 53 ++++----- .../flight_control/multiplexer.py | 47 ++++---- .../launch/flight_control_launch.py | 34 +++--- .../launch/keyboard_control_launch.py | 14 +-- src/surface/flight_control/setup.py | 11 +- .../flight_control/test/test_flake8.py | 8 +- .../test/test_keyboard_control.py | 4 +- .../test/test_manual_control.py | 3 +- src/surface/flight_control/test/test_mypy.py | 5 +- .../flight_control/test/test_pep257.py | 2 +- src/surface/gui/gui/app.py | 13 ++- .../gui/gui_nodes/auxiliary_nodes/timer.py | 20 ++-- .../gui/gui/gui_nodes/event_nodes/client.py | 37 +++--- .../gui/gui_nodes/event_nodes/publisher.py | 11 +- .../gui/gui/gui_nodes/event_nodes/server.py | 13 ++- .../gui/gui_nodes/event_nodes/subscriber.py | 15 ++- src/surface/gui/gui/operator_app.py | 16 +-- src/surface/gui/gui/pilot_app.py | 98 ++++++++-------- src/surface/gui/gui/styles/custom_styles.py | 13 +-- src/surface/gui/gui/widgets/arm.py | 16 +-- src/surface/gui/gui/widgets/circle.py | 23 ++-- src/surface/gui/gui/widgets/float_comm.py | 80 ++++++------- src/surface/gui/gui/widgets/flood_warning.py | 24 ++-- src/surface/gui/gui/widgets/heartbeat.py | 7 +- src/surface/gui/gui/widgets/ip_widget.py | 9 +- .../gui/gui/widgets/livestream_header.py | 22 ++-- src/surface/gui/gui/widgets/logger.py | 16 +-- src/surface/gui/gui/widgets/seagrass.py | 74 +++++++----- .../gui/gui/widgets/tabs/general_debug_tab.py | 11 +- src/surface/gui/gui/widgets/task_selector.py | 12 +- src/surface/gui/gui/widgets/temperature.py | 11 +- .../gui/gui/widgets/thruster_tester.py | 30 +++-- src/surface/gui/gui/widgets/timer.py | 34 +++--- src/surface/gui/gui/widgets/video_widget.py | 53 +++++---- src/surface/gui/launch/operator_launch.py | 35 +++--- src/surface/gui/launch/pilot_launch.py | 31 +++-- src/surface/gui/setup.py | 38 +++--- src/surface/gui/test/test_app.py | 2 +- src/surface/gui/test/test_flake8.py | 8 +- src/surface/gui/test/test_mypy.py | 5 +- src/surface/gui/test/test_pep257.py | 2 +- .../launch/controller_launch.py | 14 +-- src/surface/ps5_controller/setup.py | 10 +- .../ps5_controller/test/test_flake8.py | 8 +- src/surface/ps5_controller/test/test_mypy.py | 5 +- .../ps5_controller/test/test_pep257.py | 2 +- src/surface/rov_flir/launch/flir_launch.py | 39 ++++--- .../rov_flir/rov_flir/flir_watchdog.py | 52 ++++++--- src/surface/rov_flir/setup.py | 12 +- src/surface/rov_flir/test/test_flake8.py | 8 +- src/surface/rov_flir/test/test_mypy.py | 5 +- src/surface/rov_flir/test/test_pep257.py | 2 +- src/surface/rov_gazebo/launch/sim_launch.py | 92 ++++++++------- src/surface/rov_gazebo/setup.py | 33 +++--- src/surface/rov_gazebo/test/test_flake8.py | 8 +- src/surface/rov_gazebo/test/test_mypy.py | 5 +- src/surface/rov_gazebo/test/test_pep257.py | 2 +- .../surface_main/launch/competition_launch.py | 21 ++-- .../launch/debug_surface_all_nodes_launch.py | 21 ++-- .../launch/surface_all_nodes_launch.py | 29 +++-- .../launch/surface_operator_launch.py | 43 +++---- .../launch/surface_pilot_launch.py | 33 +++--- src/surface/surface_main/setup.py | 10 +- src/surface/surface_main/test/test_flake8.py | 8 +- src/surface/surface_main/test/test_mypy.py | 5 +- src/surface/surface_main/test/test_pep257.py | 2 +- .../launch/serial_reader_launch.py | 18 +-- src/surface/transceiver/setup.py | 14 ++- src/surface/transceiver/test/test_flake8.py | 8 +- src/surface/transceiver/test/test_mypy.py | 5 +- src/surface/transceiver/test/test_parser.py | 109 +++++++++++++++--- src/surface/transceiver/test/test_pep257.py | 2 +- .../transceiver/transceiver/serial_reader.py | 85 ++++++++------ .../launch/vehicle_manager_launch.py | 14 +-- src/surface/vehicle_manager/setup.py | 11 +- .../vehicle_manager/test/test_flake8.py | 8 +- src/surface/vehicle_manager/test/test_mypy.py | 5 +- .../vehicle_manager/test/test_pep257.py | 2 +- .../connection_manager_node.py | 58 +++++----- 134 files changed, 1257 insertions(+), 1089 deletions(-) diff --git a/src/float/setup.py b/src/float/setup.py index 352b2cd1..931aba27 100644 --- a/src/float/setup.py +++ b/src/float/setup.py @@ -7,8 +7,7 @@ version='1.2.0', packages=[PACKAGE_NAME], data_files=[ - ('share/ament_index/resource_index/packages', - ['resource/' + PACKAGE_NAME]), + ('share/ament_index/resource_index/packages', ['resource/' + PACKAGE_NAME]), ('share/' + PACKAGE_NAME, ['package.xml']), ], install_requires=['setuptools'], diff --git a/src/float/test/test_clang_format.py b/src/float/test/test_clang_format.py index e934506c..79c4c989 100644 --- a/src/float/test/test_clang_format.py +++ b/src/float/test/test_clang_format.py @@ -7,11 +7,10 @@ import pytest from ament_clang_format.main import main - -INO_EXTENSION = ".ino" -SKETCHES = ["float_transceiver", "surface_transceiver"] -SRC = "src" -INCLUDE = "include" +INO_EXTENSION = '.ino' +SKETCHES = ['float_transceiver', 'surface_transceiver'] +SRC = 'src' +INCLUDE = 'include' @pytest.mark.linter @@ -20,7 +19,10 @@ def test_clang_format() -> None: regular_cpp = [ *[os.path.join(os.getcwd(), folder) for folder in [SRC, INCLUDE]], - *[os.path.join(os.getcwd(), folder, folder + INO_EXTENSION) for folder in SKETCHES] + *[ + os.path.join(os.getcwd(), folder, folder + INO_EXTENSION) + for folder in SKETCHES + ], ] config_file = os.path.join(os.getcwd(), '.clang-format.yaml') diff --git a/src/pi/camera_streamer/launch/camera_launch.py b/src/pi/camera_streamer/launch/camera_launch.py index 2370ee7b..37635b0e 100644 --- a/src/pi/camera_streamer/launch/camera_launch.py +++ b/src/pi/camera_streamer/launch/camera_launch.py @@ -1,4 +1,5 @@ """camera_streamer launch file.""" + from launch.launch_description import LaunchDescription from launch_ros.actions import Node @@ -18,39 +19,38 @@ def generate_launch_description() -> LaunchDescription: # USB 3.0 front (fisheye) front_cam_node: Node = Node( - exec_name="front_cam", - package="v4l2_camera", - executable="v4l2_camera_node", - namespace="front_cam", + exec_name='front_cam', + package='v4l2_camera', + executable='v4l2_camera_node', + namespace='front_cam', parameters=[ - {"video_device": - "/dev/v4l/by-id/usb-3.0_USB_Camera_3.0_USB_Camera-video-index0"}, - {"image_size": [640, 480]}, - {"time_per_frame": [1, 30]} + { + 'video_device': '/dev/v4l/by-id/usb-3.0_USB_Camera_3.0_USB_Camera-video-index0' + }, + {'image_size': [640, 480]}, + {'time_per_frame': [1, 30]}, ], - remappings=[("/pi/front_cam/image_raw", "/tether/front_cam/image_raw")], + remappings=[('/pi/front_cam/image_raw', '/tether/front_cam/image_raw')], emulate_tty=True, - output='screen' + output='screen', ) # USB 3.0 bottom bottom_cam_node: Node = Node( - exec_name="bottom_cam", - package="v4l2_camera", - executable="v4l2_camera_node", - namespace="bottom_cam", + exec_name='bottom_cam', + package='v4l2_camera', + executable='v4l2_camera_node', + namespace='bottom_cam', parameters=[ - {"video_device": - "/dev/v4l/by-id/usb-3.0_USB_Camera_3.0_USB_Camera_2020042501-video-index0"}, - {"image_size": [640, 480]}, - {"time_per_frame": [1, 30]} + { + 'video_device': '/dev/v4l/by-id/usb-3.0_USB_Camera_3.0_USB_Camera_2020042501-video-index0' + }, + {'image_size': [640, 480]}, + {'time_per_frame': [1, 30]}, ], - remappings=[("/pi/bottom_cam/image_raw", "/tether/bottom_cam/image_raw")], + remappings=[('/pi/bottom_cam/image_raw', '/tether/bottom_cam/image_raw')], emulate_tty=True, - output='screen' + output='screen', ) - return LaunchDescription([ - front_cam_node, - bottom_cam_node - ]) + return LaunchDescription([front_cam_node, bottom_cam_node]) diff --git a/src/pi/camera_streamer/setup.py b/src/pi/camera_streamer/setup.py index f2c86b78..f3ca4796 100644 --- a/src/pi/camera_streamer/setup.py +++ b/src/pi/camera_streamer/setup.py @@ -1,4 +1,5 @@ """setup.py for camera_streamer module.""" + import os from glob import glob @@ -11,12 +12,13 @@ version='1.2.0', packages=[PACKAGE_NAME], data_files=[ - ('share/ament_index/resource_index/packages', - ['resource/' + PACKAGE_NAME]), + ('share/ament_index/resource_index/packages', ['resource/' + PACKAGE_NAME]), ('share/' + PACKAGE_NAME, ['package.xml']), # Include all launch files. - (os.path.join('share', PACKAGE_NAME, 'launch'), - glob('launch/*launch.[pxy][yma]*')) + ( + os.path.join('share', PACKAGE_NAME, 'launch'), + glob('launch/*launch.[pxy][yma]*'), + ), ], install_requires=['setuptools'], zip_safe=True, diff --git a/src/pi/camera_streamer/test/test_flake8.py b/src/pi/camera_streamer/test/test_flake8.py index 8ae474ef..6ffa8264 100644 --- a/src/pi/camera_streamer/test/test_flake8.py +++ b/src/pi/camera_streamer/test/test_flake8.py @@ -13,8 +13,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -from ament_flake8.main import main_with_errors import pytest +from ament_flake8.main import main_with_errors @pytest.mark.flake8 @@ -22,6 +22,6 @@ def test_flake8() -> None: """Tests flake8 on this module.""" error_code, errors = main_with_errors(argv=[]) - assert error_code == 0, \ - f'Found {len(errors)} code style errors / warnings:\n' + \ - '\n'.join(errors) + assert error_code == 0, ( + f'Found {len(errors)} code style errors / warnings:\n' + '\n'.join(errors) + ) diff --git a/src/pi/camera_streamer/test/test_mypy.py b/src/pi/camera_streamer/test/test_mypy.py index c2e823a6..f43986b1 100644 --- a/src/pi/camera_streamer/test/test_mypy.py +++ b/src/pi/camera_streamer/test/test_mypy.py @@ -1,6 +1,7 @@ """Test mypy on this module.""" import os + import pytest from ament_mypy.main import main @@ -9,6 +10,6 @@ @pytest.mark.linter def test_mypy() -> None: """Tests mypy on this module.""" - path = os.path.join(os.getcwd(), "..", "..", "..", "pyproject.toml") - error_code = main(argv=["--config", path]) + path = os.path.join(os.getcwd(), '..', '..', '..', 'pyproject.toml') + error_code = main(argv=['--config', path]) assert error_code == 0, 'Found code style errors / warnings' diff --git a/src/pi/camera_streamer/test/test_pep257.py b/src/pi/camera_streamer/test/test_pep257.py index 62bed98c..67305010 100644 --- a/src/pi/camera_streamer/test/test_pep257.py +++ b/src/pi/camera_streamer/test/test_pep257.py @@ -23,6 +23,6 @@ @pytest.mark.pep257 def test_pep257() -> None: """Tests pep257 on this module.""" - path = os.path.join(os.getcwd(), "..", "..", "..", "pyproject.toml") + path = os.path.join(os.getcwd(), '..', '..', '..', 'pyproject.toml') error_code = main(argv=['.', 'test', '--config', path]) assert error_code == 0, 'Found code style errors / warnings' diff --git a/src/pi/flood_detection/flood_detection/flood_detector.py b/src/pi/flood_detection/flood_detection/flood_detector.py index 1a67cc14..44757747 100644 --- a/src/pi/flood_detection/flood_detection/flood_detector.py +++ b/src/pi/flood_detection/flood_detection/flood_detector.py @@ -1,14 +1,14 @@ +import lgpio import rclpy from rclpy.node import Node + from rov_msgs.msg import Flooding -import lgpio # Pins used for GPIO DETECT_PIN = 17 class FloodDetector(Node): - def __init__(self) -> None: super().__init__('gpio_reader', parameter_overrides=[]) self.publisher = self.create_publisher(Flooding, 'flooding', 10) @@ -23,9 +23,9 @@ def timer_callback(self) -> None: # If any of the sensors detect water, send true to /tether/flooding msg = Flooding() - msg.flooding = (flood_reading == lgpio.HIGH) + msg.flooding = flood_reading == lgpio.HIGH if msg.flooding: - self.get_logger().error("The ebay is flooding") + self.get_logger().error('The ebay is flooding') self.publisher.publish(msg) diff --git a/src/pi/flood_detection/flood_detection/gpio_reader_dry_run.py b/src/pi/flood_detection/flood_detection/gpio_reader_dry_run.py index 798646ef..a0df09ed 100644 --- a/src/pi/flood_detection/flood_detection/gpio_reader_dry_run.py +++ b/src/pi/flood_detection/flood_detection/gpio_reader_dry_run.py @@ -20,10 +20,10 @@ def main() -> None: data = lgpio.gpio_read(gpio_chip, DETECT_PIN) if data != old_data or first_run: - print("Pin 17: %s" % data) + print('Pin 17: %s' % data) if data: - print("\nBad Flooding Thing") + print('\nBad Flooding Thing') first_run = False diff --git a/src/pi/flood_detection/launch/flood_detection_launch.py b/src/pi/flood_detection/launch/flood_detection_launch.py index d1a63b68..1993243d 100644 --- a/src/pi/flood_detection/launch/flood_detection_launch.py +++ b/src/pi/flood_detection/launch/flood_detection_launch.py @@ -17,9 +17,7 @@ def generate_launch_description() -> LaunchDescription: executable='flood_detector', emulate_tty=True, output='screen', - remappings=[('/pi/flooding', '/tether/flooding')] + remappings=[('/pi/flooding', '/tether/flooding')], ) - return LaunchDescription([ - flood_detection - ]) + return LaunchDescription([flood_detection]) diff --git a/src/pi/flood_detection/setup.py b/src/pi/flood_detection/setup.py index 252d3186..5add992f 100644 --- a/src/pi/flood_detection/setup.py +++ b/src/pi/flood_detection/setup.py @@ -1,6 +1,7 @@ -from setuptools import setup -from glob import glob import os +from glob import glob + +from setuptools import setup package_name = 'flood_detection' @@ -9,12 +10,13 @@ version='1.2.0', packages=[package_name], data_files=[ - ('share/ament_index/resource_index/packages', - ['resource/' + package_name]), + ('share/ament_index/resource_index/packages', ['resource/' + package_name]), ('share/' + package_name, ['package.xml']), # Include all launch files. - (os.path.join('share', package_name, 'launch'), - glob('launch/*launch.[pxy][yma]*')) + ( + os.path.join('share', package_name, 'launch'), + glob('launch/*launch.[pxy][yma]*'), + ), ], install_requires=['setuptools'], zip_safe=True, @@ -26,7 +28,7 @@ entry_points={ 'console_scripts': [ 'flood_detector = flood_detection.flood_detector:main', - 'dry_run = flood_detection.gpio_reader_dry_run:main' + 'dry_run = flood_detection.gpio_reader_dry_run:main', ], }, ) diff --git a/src/pi/flood_detection/test/test_flake8.py b/src/pi/flood_detection/test/test_flake8.py index eac16eef..4d880687 100644 --- a/src/pi/flood_detection/test/test_flake8.py +++ b/src/pi/flood_detection/test/test_flake8.py @@ -12,14 +12,14 @@ # See the License for the specific language governing permissions and # limitations under the License. -from ament_flake8.main import main_with_errors import pytest +from ament_flake8.main import main_with_errors @pytest.mark.flake8 @pytest.mark.linter def test_flake8() -> None: rc, errors = main_with_errors(argv=[]) - assert rc == 0, \ - 'Found %d code style errors / warnings:\n' % len(errors) + \ - '\n'.join(errors) + assert rc == 0, 'Found %d code style errors / warnings:\n' % len( + errors + ) + '\n'.join(errors) diff --git a/src/pi/flood_detection/test/test_mypy.py b/src/pi/flood_detection/test/test_mypy.py index c2e823a6..f43986b1 100644 --- a/src/pi/flood_detection/test/test_mypy.py +++ b/src/pi/flood_detection/test/test_mypy.py @@ -1,6 +1,7 @@ """Test mypy on this module.""" import os + import pytest from ament_mypy.main import main @@ -9,6 +10,6 @@ @pytest.mark.linter def test_mypy() -> None: """Tests mypy on this module.""" - path = os.path.join(os.getcwd(), "..", "..", "..", "pyproject.toml") - error_code = main(argv=["--config", path]) + path = os.path.join(os.getcwd(), '..', '..', '..', 'pyproject.toml') + error_code = main(argv=['--config', path]) assert error_code == 0, 'Found code style errors / warnings' diff --git a/src/pi/flood_detection/test/test_pep257.py b/src/pi/flood_detection/test/test_pep257.py index b6808e1d..91de534e 100644 --- a/src/pi/flood_detection/test/test_pep257.py +++ b/src/pi/flood_detection/test/test_pep257.py @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -from ament_pep257.main import main import pytest +from ament_pep257.main import main @pytest.mark.linter diff --git a/src/pi/manipulators/launch/manip_launch.py b/src/pi/manipulators/launch/manip_launch.py index c9320c09..1da245bd 100644 --- a/src/pi/manipulators/launch/manip_launch.py +++ b/src/pi/manipulators/launch/manip_launch.py @@ -3,38 +3,33 @@ def generate_launch_description() -> LaunchDescription: - manip_node = Node( - package="manipulators", - executable="manipulators", + package='manipulators', + executable='manipulators', parameters=[ - {"left": 2}, - {"right": 3}, + {'left': 2}, + {'right': 3}, # {"light": 2}, ], - remappings=[("/pi/manipulator_control", "/tether/manipulator_control")], + remappings=[('/pi/manipulator_control', '/tether/manipulator_control')], emulate_tty=True, - output="screen" + output='screen', ) lgpio_manip_node = Node( - package="manipulators", - executable="lgpio_manipulator", - remappings=[("/pi/manipulator_control", "/tether/manipulator_control")], + package='manipulators', + executable='lgpio_manipulator', + remappings=[('/pi/manipulator_control', '/tether/manipulator_control')], emulate_tty=True, - output="screen" + output='screen', ) valve_manip_node = Node( - package="manipulators", - executable="valve_manipulator", - remappings=[("/pi/valve_manipulator", "/tether/valve_manipulator")], + package='manipulators', + executable='valve_manipulator', + remappings=[('/pi/valve_manipulator', '/tether/valve_manipulator')], emulate_tty=True, - output="screen" + output='screen', ) - return LaunchDescription([ - manip_node, - lgpio_manip_node, - valve_manip_node - ]) + return LaunchDescription([manip_node, lgpio_manip_node, valve_manip_node]) diff --git a/src/pi/manipulators/manipulators/lgpio_manipulator.py b/src/pi/manipulators/manipulators/lgpio_manipulator.py index add765ec..3d4c5c78 100644 --- a/src/pi/manipulators/manipulators/lgpio_manipulator.py +++ b/src/pi/manipulators/manipulators/lgpio_manipulator.py @@ -11,7 +11,6 @@ class Manipulator(Node): - def __init__(self) -> None: super().__init__('lgpio_manipulator') @@ -19,7 +18,7 @@ def __init__(self) -> None: Manip, 'manipulator_control', self.manip_callback, - qos_profile_system_default + qos_profile_system_default, ) self.gpio_handle = lgpio.gpiochip_open(0) @@ -31,12 +30,12 @@ def manip_callback(self, message: Manip) -> None: manip_id = message.manip_id activated = message.activated - if manip_id == "left": + if manip_id == 'left': if activated: lgpio.gpio_write(self.gpio_handle, MANIP_PIN_ONE, lgpio.HIGH) else: lgpio.gpio_write(self.gpio_handle, MANIP_PIN_ONE, lgpio.LOW) - elif manip_id == "right": + elif manip_id == 'right': if activated: lgpio.gpio_write(self.gpio_handle, MANIP_PIN_TWO, lgpio.HIGH) else: diff --git a/src/pi/manipulators/manipulators/lgpio_manipulator_dry_run.py b/src/pi/manipulators/manipulators/lgpio_manipulator_dry_run.py index e614f5af..689f6943 100644 --- a/src/pi/manipulators/manipulators/lgpio_manipulator_dry_run.py +++ b/src/pi/manipulators/manipulators/lgpio_manipulator_dry_run.py @@ -15,7 +15,7 @@ def main() -> None: try: print('Starting loop') while True: - print("On") + print('On') lgpio.gpio_write(gpio_handle, MANIP_PIN_ONE, lgpio.HIGH) lgpio.gpio_write(gpio_handle, MANIP_PIN_TWO, lgpio.HIGH) @@ -30,5 +30,5 @@ def main() -> None: lgpio.gpio_write(gpio_handle, MANIP_PIN_TWO, lgpio.LOW) -if __name__ == "__main__": +if __name__ == '__main__': main() diff --git a/src/pi/manipulators/manipulators/manipulator_dry_run.py b/src/pi/manipulators/manipulators/manipulator_dry_run.py index e89b5082..e9d6f143 100644 --- a/src/pi/manipulators/manipulators/manipulator_dry_run.py +++ b/src/pi/manipulators/manipulators/manipulator_dry_run.py @@ -1,6 +1,6 @@ +import time from tca9555 import TCA9555 -import time ALL_BITS = (0, 1, 2, 3, 4, 5) @@ -31,5 +31,5 @@ def main() -> None: gpio.unset_bits(bits=ALL_BITS) -if __name__ == "__main__": +if __name__ == '__main__': main() diff --git a/src/pi/manipulators/manipulators/manipulator_node.py b/src/pi/manipulators/manipulators/manipulator_node.py index e424fb77..4ece657a 100644 --- a/src/pi/manipulators/manipulators/manipulator_node.py +++ b/src/pi/manipulators/manipulators/manipulator_node.py @@ -2,31 +2,31 @@ from rclpy.node import Node from rclpy.parameter import Parameter from rclpy.qos import qos_profile_system_default -from rov_msgs.msg import Manip from tca9555 import TCA9555 +from rov_msgs.msg import Manip + ALL_BITS = (0, 1, 2, 3, 4, 5) class Manipulator(Node): - def __init__(self) -> None: - super().__init__('manipulator', - parameter_overrides=[]) + super().__init__('manipulator', parameter_overrides=[]) self.subscription = self.create_subscription( Manip, 'manipulator_control', self.manip_callback, - qos_profile_system_default + qos_profile_system_default, ) self.declare_parameters( - namespace="", + namespace='', parameters=[ - ("left", Parameter.Type.INTEGER), - ("right", Parameter.Type.INTEGER) - ]) + ('left', Parameter.Type.INTEGER), + ('right', Parameter.Type.INTEGER), + ], + ) # Initialize with standard I2C-bus address of TCA9555 a.k.a 0x20 self.i2c = TCA9555() # can put in the address as a param in hexadecimal @@ -40,7 +40,7 @@ def manip_callback(self, message: Manip) -> None: manip_id = message.manip_id activated = message.activated - if manip_id != "valve": + if manip_id != 'valve': pin = self.get_parameter(manip_id).get_parameter_value().integer_value if activated: diff --git a/src/pi/manipulators/manipulators/valve_manipulator_dry_run.py b/src/pi/manipulators/manipulators/valve_manipulator_dry_run.py index 4284fca0..f21bf3ec 100644 --- a/src/pi/manipulators/manipulators/valve_manipulator_dry_run.py +++ b/src/pi/manipulators/manipulators/valve_manipulator_dry_run.py @@ -1,6 +1,7 @@ -import lgpio import time +import lgpio + # Configuration SERVO_PIN = 12 # pin used to drive PWM fan FREQ = 50 @@ -34,5 +35,5 @@ def main() -> None: lgpio.gpiochip_close(gpio_handle) -if __name__ == "__main__": +if __name__ == '__main__': main() diff --git a/src/pi/manipulators/manipulators/valve_manipulator_node.py b/src/pi/manipulators/manipulators/valve_manipulator_node.py index 66be7fe3..c53f7f34 100644 --- a/src/pi/manipulators/manipulators/valve_manipulator_node.py +++ b/src/pi/manipulators/manipulators/valve_manipulator_node.py @@ -2,6 +2,7 @@ import rclpy from rclpy.node import Node from rclpy.qos import qos_profile_system_default + from rov_msgs.msg import ValveManip # Configuration @@ -15,7 +16,7 @@ def __init__(self) -> None: ValveManip, 'valve_manipulator', self.manip_callback, - qos_profile_system_default + qos_profile_system_default, ) self.gpio_handle = lgpio.gpiochip_open(0) diff --git a/src/pi/manipulators/setup.py b/src/pi/manipulators/setup.py index afe23bd1..ce73db64 100644 --- a/src/pi/manipulators/setup.py +++ b/src/pi/manipulators/setup.py @@ -1,4 +1,5 @@ """setup.py for manipulators module.""" + import os from glob import glob @@ -11,12 +12,13 @@ version='1.2.0', packages=[PACKAGE_NAME], data_files=[ - ('share/ament_index/resource_index/packages', - ['resource/' + PACKAGE_NAME]), + ('share/ament_index/resource_index/packages', ['resource/' + PACKAGE_NAME]), ('share/' + PACKAGE_NAME, ['package.xml']), # Include all launch files. - (os.path.join('share', PACKAGE_NAME, 'launch'), - glob('launch/*launch.[pxy][yma]*')) + ( + os.path.join('share', PACKAGE_NAME, 'launch'), + glob('launch/*launch.[pxy][yma]*'), + ), ], install_requires=['setuptools'], zip_safe=True, @@ -32,7 +34,7 @@ 'valve_manipulator = manipulators.valve_manipulator_node:main', 'dry_run_valve_manipulator = manipulators.valve_manipulator_dry_run:main', 'lgpio_manipulator = manipulators.lgpio_manipulator:main', - 'dry_run_lgpio_manipulator = manipulators.lgpio_manipulator_dry_run:main' + 'dry_run_lgpio_manipulator = manipulators.lgpio_manipulator_dry_run:main', ], }, ) diff --git a/src/pi/manipulators/test/test_flake8.py b/src/pi/manipulators/test/test_flake8.py index 8ae474ef..6ffa8264 100644 --- a/src/pi/manipulators/test/test_flake8.py +++ b/src/pi/manipulators/test/test_flake8.py @@ -13,8 +13,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -from ament_flake8.main import main_with_errors import pytest +from ament_flake8.main import main_with_errors @pytest.mark.flake8 @@ -22,6 +22,6 @@ def test_flake8() -> None: """Tests flake8 on this module.""" error_code, errors = main_with_errors(argv=[]) - assert error_code == 0, \ - f'Found {len(errors)} code style errors / warnings:\n' + \ - '\n'.join(errors) + assert error_code == 0, ( + f'Found {len(errors)} code style errors / warnings:\n' + '\n'.join(errors) + ) diff --git a/src/pi/manipulators/test/test_mypy.py b/src/pi/manipulators/test/test_mypy.py index c2e823a6..f43986b1 100644 --- a/src/pi/manipulators/test/test_mypy.py +++ b/src/pi/manipulators/test/test_mypy.py @@ -1,6 +1,7 @@ """Test mypy on this module.""" import os + import pytest from ament_mypy.main import main @@ -9,6 +10,6 @@ @pytest.mark.linter def test_mypy() -> None: """Tests mypy on this module.""" - path = os.path.join(os.getcwd(), "..", "..", "..", "pyproject.toml") - error_code = main(argv=["--config", path]) + path = os.path.join(os.getcwd(), '..', '..', '..', 'pyproject.toml') + error_code = main(argv=['--config', path]) assert error_code == 0, 'Found code style errors / warnings' diff --git a/src/pi/manipulators/test/test_pep257.py b/src/pi/manipulators/test/test_pep257.py index 62bed98c..67305010 100644 --- a/src/pi/manipulators/test/test_pep257.py +++ b/src/pi/manipulators/test/test_pep257.py @@ -23,6 +23,6 @@ @pytest.mark.pep257 def test_pep257() -> None: """Tests pep257 on this module.""" - path = os.path.join(os.getcwd(), "..", "..", "..", "pyproject.toml") + path = os.path.join(os.getcwd(), '..', '..', '..', 'pyproject.toml') error_code = main(argv=['.', 'test', '--config', path]) assert error_code == 0, 'Found code style errors / warnings' diff --git a/src/pi/pi_info/launch/pi_info_launch.py b/src/pi/pi_info/launch/pi_info_launch.py index 92223c9a..3946d7fa 100644 --- a/src/pi/pi_info/launch/pi_info_launch.py +++ b/src/pi/pi_info/launch/pi_info_launch.py @@ -14,11 +14,11 @@ def generate_launch_description() -> LaunchDescription: """ # Launches the heartbeat_node heartbeat_node = Node( - package="pi_info", - executable="heartbeat_node", - remappings=[("/pi/pi_heartbeat", "/tether/pi_heartbeat")], + package='pi_info', + executable='heartbeat_node', + remappings=[('/pi/pi_heartbeat', '/tether/pi_heartbeat')], emulate_tty=True, - output='screen' + output='screen', ) # Launches ip_publisher node. @@ -27,10 +27,7 @@ def generate_launch_description() -> LaunchDescription: executable='ip_publisher', emulate_tty=True, output='screen', - remappings=[('/pi/ip_address', '/tether/ip_address')] + remappings=[('/pi/ip_address', '/tether/ip_address')], ) - return LaunchDescription([ - heartbeat_node, - ip_publisher_node - ]) + return LaunchDescription([heartbeat_node, ip_publisher_node]) diff --git a/src/pi/pi_info/pi_info/heartbeat_node.py b/src/pi/pi_info/pi_info/heartbeat_node.py index 64ae76fa..cc925b2e 100644 --- a/src/pi/pi_info/pi_info/heartbeat_node.py +++ b/src/pi/pi_info/pi_info/heartbeat_node.py @@ -10,10 +10,10 @@ class HeartbeatNode(Node): def __init__(self) -> None: - super().__init__("heartbeat_node", parameter_overrides=[]) + super().__init__('heartbeat_node', parameter_overrides=[]) self.publisher = self.create_publisher( - Heartbeat, "pi_heartbeat", qos_profile_system_default + Heartbeat, 'pi_heartbeat', qos_profile_system_default ) self.timer = self.create_timer(1 / PUBLISH_RATE, self.timer_callback) @@ -29,5 +29,5 @@ def main() -> None: rclpy.spin(heartbeat_node, executor=executor) -if __name__ == "__main__": +if __name__ == '__main__': main() diff --git a/src/pi/pi_info/pi_info/ip_publisher.py b/src/pi/pi_info/pi_info/ip_publisher.py index ecb9a64e..3facd797 100644 --- a/src/pi/pi_info/pi_info/ip_publisher.py +++ b/src/pi/pi_info/pi_info/ip_publisher.py @@ -10,12 +10,12 @@ class IPPublisher(Node): - def __init__(self) -> None: """Create IP Publisher node.""" super().__init__('ip_publisher') - self.publisher = self.create_publisher(IPAddress, 'ip_address', - qos_profile_system_default) + self.publisher = self.create_publisher( + IPAddress, 'ip_address', qos_profile_system_default + ) timer_period = 0.5 # seconds self.create_timer(timer_period, self.timer_callback) self.failed_ethernet = False @@ -30,7 +30,7 @@ def timer_callback(self) -> None: self.failed_ethernet = False except OSError: if not self.failed_ethernet: - self.get_logger().warn("No ethernet IP address found.") + self.get_logger().warn('No ethernet IP address found.') self.failed_ethernet = True try: @@ -38,7 +38,7 @@ def timer_callback(self) -> None: self.failed_wireless = False except OSError: if not self.failed_wireless: - self.get_logger().warn("No wireless IP address found.") + self.get_logger().warn('No wireless IP address found.') self.failed_wireless = True self.publisher.publish(msg) @@ -47,11 +47,13 @@ def timer_callback(self) -> None: # https://stackoverflow.com/questions/24196932/how-can-i-get-the-ip-address-from-a-nic-network-interface-controller-in-python def get_ip_address(ifname: str = 'eth0') -> str: s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - return socket.inet_ntoa(fcntl.ioctl( - s.fileno(), - 0x8915, # SIOCGIFADDR - struct.pack('256s', ifname[:15].encode()) - )[20:24]) + return socket.inet_ntoa( + fcntl.ioctl( + s.fileno(), + 0x8915, # SIOCGIFADDR + struct.pack('256s', ifname[:15].encode()), + )[20:24] + ) def main() -> None: diff --git a/src/pi/pi_info/setup.py b/src/pi/pi_info/setup.py index af9e8d4d..c7bcb8ff 100644 --- a/src/pi/pi_info/setup.py +++ b/src/pi/pi_info/setup.py @@ -10,12 +10,13 @@ version='1.0.0', packages=find_packages(exclude=['test']), data_files=[ - ('share/ament_index/resource_index/packages', - ['resource/' + PACKAGE_NAME]), + ('share/ament_index/resource_index/packages', ['resource/' + PACKAGE_NAME]), ('share/' + PACKAGE_NAME, ['package.xml']), # Include all launch files. - (os.path.join('share', PACKAGE_NAME, 'launch'), - glob('launch/*launch.[pxy][yma]*')) + ( + os.path.join('share', PACKAGE_NAME, 'launch'), + glob('launch/*launch.[pxy][yma]*'), + ), ], install_requires=['setuptools'], zip_safe=True, @@ -26,8 +27,8 @@ tests_require=['pytest'], entry_points={ 'console_scripts': [ - "heartbeat_node = pi_info.heartbeat_node:main", - 'ip_publisher = pi_info.ip_publisher:main' + 'heartbeat_node = pi_info.heartbeat_node:main', + 'ip_publisher = pi_info.ip_publisher:main', ], }, ) diff --git a/src/pi/pi_info/test/test_flake8.py b/src/pi/pi_info/test/test_flake8.py index 8ae474ef..6ffa8264 100644 --- a/src/pi/pi_info/test/test_flake8.py +++ b/src/pi/pi_info/test/test_flake8.py @@ -13,8 +13,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -from ament_flake8.main import main_with_errors import pytest +from ament_flake8.main import main_with_errors @pytest.mark.flake8 @@ -22,6 +22,6 @@ def test_flake8() -> None: """Tests flake8 on this module.""" error_code, errors = main_with_errors(argv=[]) - assert error_code == 0, \ - f'Found {len(errors)} code style errors / warnings:\n' + \ - '\n'.join(errors) + assert error_code == 0, ( + f'Found {len(errors)} code style errors / warnings:\n' + '\n'.join(errors) + ) diff --git a/src/pi/pi_info/test/test_mypy.py b/src/pi/pi_info/test/test_mypy.py index c2e823a6..f43986b1 100644 --- a/src/pi/pi_info/test/test_mypy.py +++ b/src/pi/pi_info/test/test_mypy.py @@ -1,6 +1,7 @@ """Test mypy on this module.""" import os + import pytest from ament_mypy.main import main @@ -9,6 +10,6 @@ @pytest.mark.linter def test_mypy() -> None: """Tests mypy on this module.""" - path = os.path.join(os.getcwd(), "..", "..", "..", "pyproject.toml") - error_code = main(argv=["--config", path]) + path = os.path.join(os.getcwd(), '..', '..', '..', 'pyproject.toml') + error_code = main(argv=['--config', path]) assert error_code == 0, 'Found code style errors / warnings' diff --git a/src/pi/pi_info/test/test_pep257.py b/src/pi/pi_info/test/test_pep257.py index 62bed98c..67305010 100644 --- a/src/pi/pi_info/test/test_pep257.py +++ b/src/pi/pi_info/test/test_pep257.py @@ -23,6 +23,6 @@ @pytest.mark.pep257 def test_pep257() -> None: """Tests pep257 on this module.""" - path = os.path.join(os.getcwd(), "..", "..", "..", "pyproject.toml") + path = os.path.join(os.getcwd(), '..', '..', '..', 'pyproject.toml') error_code = main(argv=['.', 'test', '--config', path]) assert error_code == 0, 'Found code style errors / warnings' diff --git a/src/pi/pi_main/launch/pi_launch.py b/src/pi/pi_main/launch/pi_launch.py index c0d92c95..241f0c25 100644 --- a/src/pi/pi_main/launch/pi_launch.py +++ b/src/pi/pi_main/launch/pi_launch.py @@ -1,9 +1,10 @@ """pi_launch launch file.""" + import os from ament_index_python.packages import get_package_share_directory -from launch.launch_description import LaunchDescription from launch.actions import GroupAction, IncludeLaunchDescription +from launch.launch_description import LaunchDescription from launch.launch_description_sources import PythonLaunchDescriptionSource from launch_ros.actions import PushRosNamespace @@ -23,11 +24,9 @@ def generate_launch_description() -> LaunchDescription: manip_path = get_package_share_directory('manipulators') manip_launch = IncludeLaunchDescription( - PythonLaunchDescriptionSource([ - os.path.join( - manip_path, 'launch', 'manip_launch.py' - ) - ]) + PythonLaunchDescriptionSource( + [os.path.join(manip_path, 'launch', 'manip_launch.py')] + ) ) # Commented out because no usb cams are planned @@ -46,44 +45,38 @@ def generate_launch_description() -> LaunchDescription: pixhawk_path = get_package_share_directory('pixhawk_communication') pixhawk_launch = IncludeLaunchDescription( - PythonLaunchDescriptionSource([ - os.path.join( - pixhawk_path, 'launch', 'mavros_launch.py' - ), - ]) + PythonLaunchDescriptionSource( + [ + os.path.join(pixhawk_path, 'launch', 'mavros_launch.py'), + ] + ) ) # Pi Info pi_info_path = get_package_share_directory('pi_info') pi_info_launch = IncludeLaunchDescription( - PythonLaunchDescriptionSource([ - os.path.join( - pi_info_path, 'launch', 'pi_info_launch.py' - ) - ]) + PythonLaunchDescriptionSource( + [os.path.join(pi_info_path, 'launch', 'pi_info_launch.py')] + ) ) # Flood detection flood_sensors_path = get_package_share_directory('flood_detection') flood_detection_launch = IncludeLaunchDescription( - PythonLaunchDescriptionSource([ - os.path.join( - flood_sensors_path, 'launch', 'flood_detection_launch.py' - ) - ]) + PythonLaunchDescriptionSource( + [os.path.join(flood_sensors_path, 'launch', 'flood_detection_launch.py')] + ) ) # Temperature sensor temp_sensor_path = get_package_share_directory('temp_sensor') temp_sensor_launch = IncludeLaunchDescription( - PythonLaunchDescriptionSource([ - os.path.join( - temp_sensor_path, 'launch', 'temp_sensor_launch.py' - ) - ]) + PythonLaunchDescriptionSource( + [os.path.join(temp_sensor_path, 'launch', 'temp_sensor_launch.py')] + ) ) namespace_launch = GroupAction( @@ -94,10 +87,12 @@ def generate_launch_description() -> LaunchDescription: # cam_launch, flood_detection_launch, temp_sensor_launch, - pi_info_launch + pi_info_launch, ] ) - return LaunchDescription([ - namespace_launch, - ]) + return LaunchDescription( + [ + namespace_launch, + ] + ) diff --git a/src/pi/pi_main/pi_main/copy.py b/src/pi/pi_main/pi_main/copy.py index 957347de..7bceb486 100644 --- a/src/pi/pi_main/pi_main/copy.py +++ b/src/pi/pi_main/pi_main/copy.py @@ -1,4 +1,5 @@ """Copies udev rules from separate process to ensure ideal protections of sudo.""" + import os import shutil import sys @@ -17,4 +18,4 @@ service_dst = os.path.join(service_dst_folder, 'pi_main.service') shutil.copy(service_src, service_dst) - print("Copying udev rules and services") + print('Copying udev rules and services') diff --git a/src/pi/pi_main/pi_main/run_on_boot.py b/src/pi/pi_main/pi_main/run_on_boot.py index d49bb712..e0669078 100644 --- a/src/pi/pi_main/pi_main/run_on_boot.py +++ b/src/pi/pi_main/pi_main/run_on_boot.py @@ -1,8 +1,9 @@ """When run sets up environment for the robot to run on boot.""" + import os -import sys -import subprocess import pathlib +import subprocess +import sys from ament_index_python.packages import get_package_share_directory diff --git a/src/pi/pi_main/setup.py b/src/pi/pi_main/setup.py index fde8a61f..971c9302 100644 --- a/src/pi/pi_main/setup.py +++ b/src/pi/pi_main/setup.py @@ -1,4 +1,5 @@ """setup.py for pi_main module.""" + import os from glob import glob @@ -12,16 +13,15 @@ version='1.2.0', packages=[PACKAGE_NAME], data_files=[ - ('share/ament_index/resource_index/packages', - ['resource/' + PACKAGE_NAME]), + ('share/ament_index/resource_index/packages', ['resource/' + PACKAGE_NAME]), ('share/' + PACKAGE_NAME, ['package.xml']), # Include all launch files. - (os.path.join('share', PACKAGE_NAME, 'launch'), - glob('launch/*launch.[pxy][yma]*')), - (os.path.join('share', PACKAGE_NAME, 'udev_rules'), - glob('udev_rules/*')), - (os.path.join('share', PACKAGE_NAME, 'services'), - glob('services/*')) + ( + os.path.join('share', PACKAGE_NAME, 'launch'), + glob('launch/*launch.[pxy][yma]*'), + ), + (os.path.join('share', PACKAGE_NAME, 'udev_rules'), glob('udev_rules/*')), + (os.path.join('share', PACKAGE_NAME, 'services'), glob('services/*')), ], install_requires=['setuptools'], zip_safe=True, @@ -31,8 +31,6 @@ license='Apache License 2.0', tests_require=['pytest'], entry_points={ - 'console_scripts': [ - 'install = pi_main.run_on_boot:main' - ], + 'console_scripts': ['install = pi_main.run_on_boot:main'], }, ) diff --git a/src/pi/pi_main/test/test_flake8.py b/src/pi/pi_main/test/test_flake8.py index 8ae474ef..6ffa8264 100644 --- a/src/pi/pi_main/test/test_flake8.py +++ b/src/pi/pi_main/test/test_flake8.py @@ -13,8 +13,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -from ament_flake8.main import main_with_errors import pytest +from ament_flake8.main import main_with_errors @pytest.mark.flake8 @@ -22,6 +22,6 @@ def test_flake8() -> None: """Tests flake8 on this module.""" error_code, errors = main_with_errors(argv=[]) - assert error_code == 0, \ - f'Found {len(errors)} code style errors / warnings:\n' + \ - '\n'.join(errors) + assert error_code == 0, ( + f'Found {len(errors)} code style errors / warnings:\n' + '\n'.join(errors) + ) diff --git a/src/pi/pi_main/test/test_mypy.py b/src/pi/pi_main/test/test_mypy.py index c2e823a6..f43986b1 100644 --- a/src/pi/pi_main/test/test_mypy.py +++ b/src/pi/pi_main/test/test_mypy.py @@ -1,6 +1,7 @@ """Test mypy on this module.""" import os + import pytest from ament_mypy.main import main @@ -9,6 +10,6 @@ @pytest.mark.linter def test_mypy() -> None: """Tests mypy on this module.""" - path = os.path.join(os.getcwd(), "..", "..", "..", "pyproject.toml") - error_code = main(argv=["--config", path]) + path = os.path.join(os.getcwd(), '..', '..', '..', 'pyproject.toml') + error_code = main(argv=['--config', path]) assert error_code == 0, 'Found code style errors / warnings' diff --git a/src/pi/pi_main/test/test_pep257.py b/src/pi/pi_main/test/test_pep257.py index 62bed98c..67305010 100644 --- a/src/pi/pi_main/test/test_pep257.py +++ b/src/pi/pi_main/test/test_pep257.py @@ -23,6 +23,6 @@ @pytest.mark.pep257 def test_pep257() -> None: """Tests pep257 on this module.""" - path = os.path.join(os.getcwd(), "..", "..", "..", "pyproject.toml") + path = os.path.join(os.getcwd(), '..', '..', '..', 'pyproject.toml') error_code = main(argv=['.', 'test', '--config', path]) assert error_code == 0, 'Found code style errors / warnings' diff --git a/src/pi/pi_main/test/test_run_on_boot.py b/src/pi/pi_main/test/test_run_on_boot.py index 9ce875ef..5ea62ac0 100644 --- a/src/pi/pi_main/test/test_run_on_boot.py +++ b/src/pi/pi_main/test/test_run_on_boot.py @@ -11,8 +11,8 @@ def test_install_on_boot() -> None: main() # Test for files being copied correctly - actual_rules_files = set(os.listdir(os.path.join("/etc", "udev", "rules.d"))) - expected_rules_files = set(["i2c.rules", "camera.rules", "pixhawk.rules"]) + actual_rules_files = set(os.listdir(os.path.join('/etc', 'udev', 'rules.d'))) + expected_rules_files = set(['i2c.rules', 'camera.rules', 'pixhawk.rules']) assert expected_rules_files.issubset(actual_rules_files) assert os.path.exists(os.path.join('/etc', 'systemd', 'system', 'pi_main.service')) diff --git a/src/pi/pixhawk_communication/launch/mavros_launch.py b/src/pi/pixhawk_communication/launch/mavros_launch.py index 9bbf903d..8d5d6e8e 100644 --- a/src/pi/pixhawk_communication/launch/mavros_launch.py +++ b/src/pi/pixhawk_communication/launch/mavros_launch.py @@ -1,4 +1,5 @@ """mavros_launch launch file.""" + from launch.launch_description import LaunchDescription from launch_ros.actions import Node @@ -14,28 +15,26 @@ def generate_launch_description() -> LaunchDescription: """ mavros_node = Node( - package="mavros", - executable="mavros_node", - output="screen", - namespace="mavros", + package='mavros', + executable='mavros_node', + output='screen', + namespace='mavros', parameters=[ # https://github.com/mavlink/mavros/issues/1632 # Set the system_id to 255 so mavros_node is treated as # Ground Control Station (GCS). To use RC control it needs to # receive a signal from a GCS. - {"system_id": 255}, + {'system_id': 255}, # plugin_allowlist allows which mavros nodes get launched. The default is all of them. - {"plugin_allowlist": ["sys_status", "rc_io", "command"]}, - {"fcu_url": "/dev/ttyPixhawk"} + {'plugin_allowlist': ['sys_status', 'rc_io', 'command']}, + {'fcu_url': '/dev/ttyPixhawk'}, ], remappings=[ ('/pi/mavros/state', '/tether/mavros/state'), ('/pi/mavros/rc/override', '/tether/mavros/rc/override'), ('/pi/mavros/cmd/arming', '/tether/mavros/cmd/arming'), ('/pi/mavros/cmd/command', '/tether/mavros/cmd/command'), - ] + ], ) - return LaunchDescription([ - mavros_node - ]) + return LaunchDescription([mavros_node]) diff --git a/src/pi/pixhawk_communication/setup.py b/src/pi/pixhawk_communication/setup.py index c8b5411e..261443eb 100644 --- a/src/pi/pixhawk_communication/setup.py +++ b/src/pi/pixhawk_communication/setup.py @@ -1,4 +1,5 @@ """setup.py for pixhawk_communication module.""" + import os from glob import glob @@ -11,12 +12,13 @@ version='1.2.0', packages=[PACKAGE_NAME], data_files=[ - ('share/ament_index/resource_index/packages', - ['resource/' + PACKAGE_NAME]), + ('share/ament_index/resource_index/packages', ['resource/' + PACKAGE_NAME]), ('share/' + PACKAGE_NAME, ['package.xml']), # Include all launch files. - (os.path.join('share', PACKAGE_NAME, 'launch'), - glob('launch/*launch.[pxy][yma]*')) + ( + os.path.join('share', PACKAGE_NAME, 'launch'), + glob('launch/*launch.[pxy][yma]*'), + ), ], install_requires=['setuptools'], zip_safe=True, diff --git a/src/pi/pixhawk_communication/test/test_flake8.py b/src/pi/pixhawk_communication/test/test_flake8.py index 8ae474ef..6ffa8264 100644 --- a/src/pi/pixhawk_communication/test/test_flake8.py +++ b/src/pi/pixhawk_communication/test/test_flake8.py @@ -13,8 +13,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -from ament_flake8.main import main_with_errors import pytest +from ament_flake8.main import main_with_errors @pytest.mark.flake8 @@ -22,6 +22,6 @@ def test_flake8() -> None: """Tests flake8 on this module.""" error_code, errors = main_with_errors(argv=[]) - assert error_code == 0, \ - f'Found {len(errors)} code style errors / warnings:\n' + \ - '\n'.join(errors) + assert error_code == 0, ( + f'Found {len(errors)} code style errors / warnings:\n' + '\n'.join(errors) + ) diff --git a/src/pi/pixhawk_communication/test/test_mypy.py b/src/pi/pixhawk_communication/test/test_mypy.py index c2e823a6..f43986b1 100644 --- a/src/pi/pixhawk_communication/test/test_mypy.py +++ b/src/pi/pixhawk_communication/test/test_mypy.py @@ -1,6 +1,7 @@ """Test mypy on this module.""" import os + import pytest from ament_mypy.main import main @@ -9,6 +10,6 @@ @pytest.mark.linter def test_mypy() -> None: """Tests mypy on this module.""" - path = os.path.join(os.getcwd(), "..", "..", "..", "pyproject.toml") - error_code = main(argv=["--config", path]) + path = os.path.join(os.getcwd(), '..', '..', '..', 'pyproject.toml') + error_code = main(argv=['--config', path]) assert error_code == 0, 'Found code style errors / warnings' diff --git a/src/pi/pixhawk_communication/test/test_pep257.py b/src/pi/pixhawk_communication/test/test_pep257.py index 62bed98c..67305010 100644 --- a/src/pi/pixhawk_communication/test/test_pep257.py +++ b/src/pi/pixhawk_communication/test/test_pep257.py @@ -23,6 +23,6 @@ @pytest.mark.pep257 def test_pep257() -> None: """Tests pep257 on this module.""" - path = os.path.join(os.getcwd(), "..", "..", "..", "pyproject.toml") + path = os.path.join(os.getcwd(), '..', '..', '..', 'pyproject.toml') error_code = main(argv=['.', 'test', '--config', path]) assert error_code == 0, 'Found code style errors / warnings' diff --git a/src/pi/temp_sensor/launch/temp_sensor_launch.py b/src/pi/temp_sensor/launch/temp_sensor_launch.py index 2cf40b50..aef11550 100644 --- a/src/pi/temp_sensor/launch/temp_sensor_launch.py +++ b/src/pi/temp_sensor/launch/temp_sensor_launch.py @@ -17,9 +17,7 @@ def generate_launch_description() -> LaunchDescription: executable='temp_sensor', emulate_tty=True, output='screen', - remappings=[('/pi/temperature', '/tether/temperature')] + remappings=[('/pi/temperature', '/tether/temperature')], ) - return LaunchDescription([ - temp_sensor - ]) + return LaunchDescription([temp_sensor]) diff --git a/src/pi/temp_sensor/setup.py b/src/pi/temp_sensor/setup.py index 9daf6180..b89b85b0 100644 --- a/src/pi/temp_sensor/setup.py +++ b/src/pi/temp_sensor/setup.py @@ -1,6 +1,7 @@ -from setuptools import setup -from glob import glob import os +from glob import glob + +from setuptools import setup package_name = 'temp_sensor' @@ -9,12 +10,13 @@ version='1.2.0', packages=[package_name], data_files=[ - ('share/ament_index/resource_index/packages', - ['resource/' + package_name]), + ('share/ament_index/resource_index/packages', ['resource/' + package_name]), ('share/' + package_name, ['package.xml']), # Include all launch files. - (os.path.join('share', package_name, 'launch'), - glob('launch/*launch.[pxy][yma]*')) + ( + os.path.join('share', package_name, 'launch'), + glob('launch/*launch.[pxy][yma]*'), + ), ], install_requires=['setuptools'], zip_safe=True, @@ -26,7 +28,7 @@ entry_points={ 'console_scripts': [ 'temp_sensor = temp_sensor.temp_sensor:main', - 'dry_run = temp_sensor.temp_sensor_dry_run:main' + 'dry_run = temp_sensor.temp_sensor_dry_run:main', ], }, ) diff --git a/src/pi/temp_sensor/temp_sensor/temp_sensor.py b/src/pi/temp_sensor/temp_sensor/temp_sensor.py index 37c412c3..db43dc24 100644 --- a/src/pi/temp_sensor/temp_sensor/temp_sensor.py +++ b/src/pi/temp_sensor/temp_sensor/temp_sensor.py @@ -1,7 +1,7 @@ import rclpy +import tsys01 from rclpy.node import Node from rclpy.qos import QoSPresetProfiles -import tsys01 from rov_msgs.msg import Temperature @@ -9,11 +9,11 @@ class TempSensor(Node): - def __init__(self) -> None: super().__init__('temp_sensor', parameter_overrides=[]) - self.publisher = self.create_publisher(Temperature, 'temperature', - QoSPresetProfiles.DEFAULT.value) + self.publisher = self.create_publisher( + Temperature, 'temperature', QoSPresetProfiles.DEFAULT.value + ) self.sensor = tsys01.TSYS01() self.sensor.init() diff --git a/src/pi/temp_sensor/temp_sensor/temp_sensor_dry_run.py b/src/pi/temp_sensor/temp_sensor/temp_sensor_dry_run.py index 598a05cf..e659ceaa 100644 --- a/src/pi/temp_sensor/temp_sensor/temp_sensor_dry_run.py +++ b/src/pi/temp_sensor/temp_sensor/temp_sensor_dry_run.py @@ -14,7 +14,7 @@ def main() -> None: print( sensor.temperature(), # Get temperature in default units (Centigrade) '\t', - sensor.temperature(tsys01.UNITS_Farenheit) + sensor.temperature(tsys01.UNITS_Farenheit), ) except OSError: print('Failed to read temperature, trying again') @@ -22,5 +22,5 @@ def main() -> None: sleep(1) -if __name__ == "__main__": +if __name__ == '__main__': main() diff --git a/src/pi/temp_sensor/test/test_flake8.py b/src/pi/temp_sensor/test/test_flake8.py index eac16eef..4d880687 100644 --- a/src/pi/temp_sensor/test/test_flake8.py +++ b/src/pi/temp_sensor/test/test_flake8.py @@ -12,14 +12,14 @@ # See the License for the specific language governing permissions and # limitations under the License. -from ament_flake8.main import main_with_errors import pytest +from ament_flake8.main import main_with_errors @pytest.mark.flake8 @pytest.mark.linter def test_flake8() -> None: rc, errors = main_with_errors(argv=[]) - assert rc == 0, \ - 'Found %d code style errors / warnings:\n' % len(errors) + \ - '\n'.join(errors) + assert rc == 0, 'Found %d code style errors / warnings:\n' % len( + errors + ) + '\n'.join(errors) diff --git a/src/pi/temp_sensor/test/test_mypy.py b/src/pi/temp_sensor/test/test_mypy.py index c2e823a6..f43986b1 100644 --- a/src/pi/temp_sensor/test/test_mypy.py +++ b/src/pi/temp_sensor/test/test_mypy.py @@ -1,6 +1,7 @@ """Test mypy on this module.""" import os + import pytest from ament_mypy.main import main @@ -9,6 +10,6 @@ @pytest.mark.linter def test_mypy() -> None: """Tests mypy on this module.""" - path = os.path.join(os.getcwd(), "..", "..", "..", "pyproject.toml") - error_code = main(argv=["--config", path]) + path = os.path.join(os.getcwd(), '..', '..', '..', 'pyproject.toml') + error_code = main(argv=['--config', path]) assert error_code == 0, 'Found code style errors / warnings' diff --git a/src/pi/temp_sensor/test/test_pep257.py b/src/pi/temp_sensor/test/test_pep257.py index b6808e1d..91de534e 100644 --- a/src/pi/temp_sensor/test/test_pep257.py +++ b/src/pi/temp_sensor/test/test_pep257.py @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -from ament_pep257.main import main import pytest +from ament_pep257.main import main @pytest.mark.linter diff --git a/src/surface/flight_control/flight_control/auto_docking_node.py b/src/surface/flight_control/flight_control/auto_docking_node.py index ce4d75f0..1d2978b2 100644 --- a/src/surface/flight_control/flight_control/auto_docking_node.py +++ b/src/surface/flight_control/flight_control/auto_docking_node.py @@ -8,16 +8,16 @@ class AutoDocker(Node): - def __init__(self) -> None: super().__init__('auto_docker') self.control_server = self.create_service( - AutonomousFlight, 'auto_control_toggle', self.task_control_callback) + AutonomousFlight, 'auto_control_toggle', self.task_control_callback + ) self.pixhawk_control = self.create_publisher( PixhawkInstruction, - "pixhawk_control", + 'pixhawk_control', QoSPresetProfiles.DEFAULT.value, ) @@ -25,8 +25,9 @@ def __init__(self) -> None: # TODO: Add cam frame subscriber here to act as control loop for auto docking - def task_control_callback(self, request: AutonomousFlight.Request, - response: AutonomousFlight.Response) -> AutonomousFlight.Response: + def task_control_callback( + self, request: AutonomousFlight.Request, response: AutonomousFlight.Response + ) -> AutonomousFlight.Response: self.current_state = request.state response.current_state = request.state return response diff --git a/src/surface/flight_control/flight_control/control_inverter_node.py b/src/surface/flight_control/flight_control/control_inverter_node.py index 5b27a55d..905f1240 100644 --- a/src/surface/flight_control/flight_control/control_inverter_node.py +++ b/src/surface/flight_control/flight_control/control_inverter_node.py @@ -18,8 +18,7 @@ def joystick_map(raw: float) -> float: class ControlInverterNode(Node): def __init__(self) -> None: - super().__init__('control_inverter_node', - parameter_overrides=[]) + super().__init__('control_inverter_node', parameter_overrides=[]) self.inverted = False @@ -27,20 +26,18 @@ def __init__(self) -> None: CameraControllerSwitch, 'camera_switch', self.invert_callback, - QoSPresetProfiles.DEFAULT.value + QoSPresetProfiles.DEFAULT.value, ) self.control_subscription = self.create_subscription( PixhawkInstruction, 'uninverted_pixhawk_control', self.control_callback, - QoSPresetProfiles.DEFAULT.value + QoSPresetProfiles.DEFAULT.value, ) self.pixhawk_control = self.create_publisher( - PixhawkInstruction, - 'pixhawk_control', - QoSPresetProfiles.DEFAULT.value + PixhawkInstruction, 'pixhawk_control', QoSPresetProfiles.DEFAULT.value ) def invert_callback(self, _: CameraControllerSwitch) -> None: diff --git a/src/surface/flight_control/flight_control/keyboard_control_node.py b/src/surface/flight_control/flight_control/keyboard_control_node.py index 3830efa8..0e888212 100644 --- a/src/surface/flight_control/flight_control/keyboard_control_node.py +++ b/src/surface/flight_control/flight_control/keyboard_control_node.py @@ -1,29 +1,29 @@ from typing import Optional import rclpy.utilities -from rclpy.publisher import Publisher from pynput.keyboard import Key, KeyCode, Listener from rclpy.node import Node +from rclpy.publisher import Publisher from rclpy.qos import qos_profile_system_default from rov_msgs.msg import PixhawkInstruction # key bindings -FORWARD = "w" -BACKWARD = "s" -LEFT = "a" -RIGHT = "d" -UP = "2" -DOWN = "x" - -ROLL_LEFT = "j" -ROLL_RIGHT = "l" -PITCH_UP = "i" -PITCH_DOWN = "k" -YAW_LEFT = "h" -YAW_RIGHT = ";" - -HELP = "p" +FORWARD = 'w' +BACKWARD = 's' +LEFT = 'a' +RIGHT = 'd' +UP = '2' +DOWN = 'x' + +ROLL_LEFT = 'j' +ROLL_RIGHT = 'l' +PITCH_UP = 'i' +PITCH_DOWN = 'k' +YAW_LEFT = 'h' +YAW_RIGHT = ';' + +HELP = 'p' HELP_MSG = """ Use keyboard to control ROV @@ -56,9 +56,7 @@ def __init__(self) -> None: super().__init__('keyboard_listener_node', parameter_overrides=[]) self.rc_pub: Publisher = self.create_publisher( - PixhawkInstruction, - 'uninverted_pixhawk_control', - qos_profile_system_default + PixhawkInstruction, 'uninverted_pixhawk_control', qos_profile_system_default ) self.get_logger().info(HELP_MSG) @@ -131,15 +129,13 @@ def pub_rov_control(self) -> None: forward=float(self.status[FORWARD] - self.status[BACKWARD]), lateral=float(self.status[LEFT] - self.status[RIGHT]), yaw=float(self.status[YAW_LEFT] - self.status[YAW_RIGHT]), - author=PixhawkInstruction.KEYBOARD_CONTROL + author=PixhawkInstruction.KEYBOARD_CONTROL, ) self.rc_pub.publish(instruction) def spin(self) -> None: - with Listener( - on_press=self.on_press, on_release=self.on_release - ) as listener: + with Listener(on_press=self.on_press, on_release=self.on_release) as listener: while rclpy.utilities.ok() and listener.running: rclpy.spin_once(self, timeout_sec=0.1) @@ -149,5 +145,5 @@ def main() -> None: KeyboardListenerNode().spin() -if __name__ == "__main__": +if __name__ == '__main__': main() diff --git a/src/surface/flight_control/flight_control/manual_control_node.py b/src/surface/flight_control/flight_control/manual_control_node.py index f06dba99..924807ee 100644 --- a/src/surface/flight_control/flight_control/manual_control_node.py +++ b/src/surface/flight_control/flight_control/manual_control_node.py @@ -2,11 +2,11 @@ from enum import IntEnum import rclpy +from mavros_msgs.srv import CommandBool from rclpy.executors import MultiThreadedExecutor from rclpy.node import Node from rclpy.qos import qos_profile_sensor_data, qos_profile_system_default from sensor_msgs.msg import Joy -from mavros_msgs.srv import CommandBool from rov_msgs.msg import (CameraControllerSwitch, Manip, PixhawkInstruction, ValveManip) @@ -44,7 +44,7 @@ ARM_MESSAGE = CommandBool.Request(value=True) DISARM_MESSAGE = CommandBool.Request(value=False) -CONTROLLER_MODE_PARAM = "controller_mode" +CONTROLLER_MODE_PARAM = 'controller_mode' class ControllerMode(IntEnum): @@ -56,33 +56,26 @@ class ManualControlNode(Node): def __init__(self) -> None: super().__init__('manual_control_node') - mode_param = self.declare_parameter(CONTROLLER_MODE_PARAM, value=ControllerMode.ARM) + mode_param = self.declare_parameter( + CONTROLLER_MODE_PARAM, value=ControllerMode.ARM + ) self.rc_pub = self.create_publisher( - PixhawkInstruction, - 'uninverted_pixhawk_control', - qos_profile_system_default + PixhawkInstruction, 'uninverted_pixhawk_control', qos_profile_system_default ) self.subscription = self.create_subscription( - Joy, - 'joy', - self.controller_callback, - qos_profile_sensor_data + Joy, 'joy', self.controller_callback, qos_profile_sensor_data ) # Manipulators self.manip_publisher = self.create_publisher( - Manip, - 'manipulator_control', - qos_profile_system_default + Manip, 'manipulator_control', qos_profile_system_default ) # Valve Manip self.valve_manip = self.create_publisher( - ValveManip, - "valve_manipulator", - qos_profile_system_default + ValveManip, 'valve_manipulator', qos_profile_system_default ) controller_mode = ControllerMode(mode_param.value) @@ -91,18 +84,16 @@ def __init__(self) -> None: # Control camera switching self.misc_controls_callback = self.toggle_cameras self.camera_toggle_publisher = self.create_publisher( - CameraControllerSwitch, - "camera_switch", - qos_profile_system_default + CameraControllerSwitch, 'camera_switch', qos_profile_system_default ) else: self.misc_controls_callback = self.set_arming # Control arming - self.arm_client = self.create_client(CommandBool, "mavros/cmd/arming") + self.arm_client = self.create_client(CommandBool, 'mavros/cmd/arming') self.manip_buttons: dict[int, ManipButton] = { - X_BUTTON: ManipButton("left"), - O_BUTTON: ManipButton("right") + X_BUTTON: ManipButton('left'), + O_BUTTON: ManipButton('right'), } self.seen_left_cam = False @@ -122,11 +113,12 @@ def joystick_to_pixhawk(self, msg: Joy) -> None: instruction = PixhawkInstruction( forward=float(axes[LJOYY]), # Left Joystick Y lateral=-float(axes[LJOYX]), # Left Joystick X - vertical=float(axes[L2PRESS_PERCENT] - axes[R2PRESS_PERCENT]) / 2, # L2/R2 triggers + vertical=float(axes[L2PRESS_PERCENT] - axes[R2PRESS_PERCENT]) + / 2, # L2/R2 triggers roll=float(buttons[L1] - buttons[R1]), # L1/R1 buttons pitch=float(axes[RJOYY]), # Right Joystick Y yaw=-float(axes[RJOYX]), # Right Joystick X - author=PixhawkInstruction.MANUAL_CONTROL + author=PixhawkInstruction.MANUAL_CONTROL, ) self.rc_pub.publish(instruction) @@ -141,8 +133,9 @@ def manip_callback(self, msg: Joy) -> None: new_manip_state = not manip_button.is_active manip_button.is_active = new_manip_state - manip_msg = Manip(manip_id=manip_button.claw, - activated=manip_button.is_active) + manip_msg = Manip( + manip_id=manip_button.claw, activated=manip_button.is_active + ) self.manip_publisher.publish(manip_msg) manip_button.last_button_state = just_pressed @@ -169,10 +162,14 @@ def toggle_cameras(self, msg: Joy) -> None: self.seen_left_cam = True elif buttons[MENU] == UNPRESSED and self.seen_right_cam: self.seen_right_cam = False - self.camera_toggle_publisher.publish(CameraControllerSwitch(toggle_right=True)) + self.camera_toggle_publisher.publish( + CameraControllerSwitch(toggle_right=True) + ) elif buttons[PAIRING_BUTTON] == UNPRESSED and self.seen_left_cam: self.seen_left_cam = False - self.camera_toggle_publisher.publish(CameraControllerSwitch(toggle_right=False)) + self.camera_toggle_publisher.publish( + CameraControllerSwitch(toggle_right=False) + ) def set_arming(self, msg: Joy) -> None: """Set the arming state using the menu and pairing buttons.""" diff --git a/src/surface/flight_control/flight_control/multiplexer.py b/src/surface/flight_control/flight_control/multiplexer.py index 4ec21109..b627d65e 100644 --- a/src/surface/flight_control/flight_control/multiplexer.py +++ b/src/surface/flight_control/flight_control/multiplexer.py @@ -1,10 +1,10 @@ +from typing import Callable + import rclpy +from mavros_msgs.msg import OverrideRCIn from rclpy.executors import MultiThreadedExecutor - from rclpy.node import Node from rclpy.qos import QoSPresetProfiles -from mavros_msgs.msg import OverrideRCIn -from typing import Callable from rov_msgs.msg import PixhawkInstruction from rov_msgs.srv import AutonomousFlight @@ -42,32 +42,29 @@ def joystick_map(raw: float) -> float: class MultiplexerNode(Node): def __init__(self) -> None: - super().__init__('multiplexer', - parameter_overrides=[]) + super().__init__('multiplexer', parameter_overrides=[]) self.state = AutonomousFlight.Request.STOP self.autonomous_toggle = self.create_service( - AutonomousFlight, - 'auto_control_toggle', - self.state_control + AutonomousFlight, 'auto_control_toggle', self.state_control ) self.control_subscription = self.create_subscription( PixhawkInstruction, 'pixhawk_control', self.control_callback, - QoSPresetProfiles.DEFAULT.value + QoSPresetProfiles.DEFAULT.value, ) self.rc_pub = self.create_publisher( - OverrideRCIn, - 'mavros/rc/override', - QoSPresetProfiles.DEFAULT.value + OverrideRCIn, 'mavros/rc/override', QoSPresetProfiles.DEFAULT.value ) @staticmethod - def apply(msg: PixhawkInstruction, function_to_apply: Callable[[float], float]) -> None: + def apply( + msg: PixhawkInstruction, function_to_apply: Callable[[float], float] + ) -> None: """Apply a function to each dimension of this PixhawkInstruction.""" msg.forward = function_to_apply(msg.forward) msg.vertical = function_to_apply(msg.vertical) @@ -93,24 +90,30 @@ def to_override_rc_in(msg: PixhawkInstruction) -> OverrideRCIn: return rc_msg - def state_control(self, req: AutonomousFlight.Request, - res: AutonomousFlight.Response) -> AutonomousFlight.Response: + def state_control( + self, req: AutonomousFlight.Request, res: AutonomousFlight.Response + ) -> AutonomousFlight.Response: self.state = req.state res.current_state = req.state return res def control_callback(self, msg: PixhawkInstruction) -> None: - - if msg.author == PixhawkInstruction.MANUAL_CONTROL and \ - self.state == AutonomousFlight.Request.STOP: + if ( + msg.author == PixhawkInstruction.MANUAL_CONTROL + and self.state == AutonomousFlight.Request.STOP + ): # Smooth out adjustments # TODO look into maybe doing inheritance on a PixhawkInstruction MultiplexerNode.apply(msg, joystick_map) - elif msg.author == PixhawkInstruction.KEYBOARD_CONTROL and \ - self.state == AutonomousFlight.Request.STOP: + elif ( + msg.author == PixhawkInstruction.KEYBOARD_CONTROL + and self.state == AutonomousFlight.Request.STOP + ): pass - elif msg.author == PixhawkInstruction.AUTONOMOUS_CONTROL and \ - self.state == AutonomousFlight.Request.START: + elif ( + msg.author == PixhawkInstruction.AUTONOMOUS_CONTROL + and self.state == AutonomousFlight.Request.START + ): pass else: return diff --git a/src/surface/flight_control/launch/flight_control_launch.py b/src/surface/flight_control/launch/flight_control_launch.py index 1bf8bbb1..3b636e2f 100644 --- a/src/surface/flight_control/launch/flight_control_launch.py +++ b/src/surface/flight_control/launch/flight_control_launch.py @@ -1,6 +1,6 @@ from launch.launch_description import LaunchDescription -from launch_ros.actions import Node from launch.substitutions.launch_configuration import LaunchConfiguration +from launch_ros.actions import Node def generate_launch_description() -> LaunchDescription: @@ -8,13 +8,15 @@ def generate_launch_description() -> LaunchDescription: package='flight_control', executable='manual_control_node', parameters=[ - {"controller_mode": LaunchConfiguration('controller_mode', default=0)} + {'controller_mode': LaunchConfiguration('controller_mode', default=0)} + ], + remappings=[ + ('/surface/manipulator_control', '/tether/manipulator_control'), + ('/surface/valve_manipulator', '/tether/valve_manipulator'), + ('/surface/mavros/cmd/arming', '/tether/mavros/cmd/arming'), ], - remappings=[('/surface/manipulator_control', '/tether/manipulator_control'), - ('/surface/valve_manipulator', '/tether/valve_manipulator'), - ('/surface/mavros/cmd/arming', '/tether/mavros/cmd/arming')], emulate_tty=True, - output='screen' + output='screen', ) auto_docking_node = Node( @@ -22,14 +24,14 @@ def generate_launch_description() -> LaunchDescription: executable='auto_docking_node', remappings=[('/surface/manipulator_control', '/tether/manipulator_control')], emulate_tty=True, - output='screen' + output='screen', ) control_inverter_node = Node( package='flight_control', executable='control_inverter_node', emulate_tty=True, - output='screen' + output='screen', ) multiplexer_node = Node( @@ -37,12 +39,14 @@ def generate_launch_description() -> LaunchDescription: executable='multiplexer_node', remappings=[('/surface/mavros/rc/override', '/tether/mavros/rc/override')], emulate_tty=True, - output='screen' + output='screen', ) - return LaunchDescription([ - manual_control_node, - auto_docking_node, - control_inverter_node, - multiplexer_node - ]) + return LaunchDescription( + [ + manual_control_node, + auto_docking_node, + control_inverter_node, + multiplexer_node, + ] + ) diff --git a/src/surface/flight_control/launch/keyboard_control_launch.py b/src/surface/flight_control/launch/keyboard_control_launch.py index 58a4ebbc..c1e6f7b2 100644 --- a/src/surface/flight_control/launch/keyboard_control_launch.py +++ b/src/surface/flight_control/launch/keyboard_control_launch.py @@ -8,14 +8,14 @@ def generate_launch_description() -> LaunchDescription: package='flight_control', executable='keyboard_control_node', emulate_tty=True, - output='screen' + output='screen', ) control_inverter_node = Node( package='flight_control', executable='control_inverter_node', emulate_tty=True, - output='screen' + output='screen', ) multiplexer_node = Node( @@ -23,18 +23,16 @@ def generate_launch_description() -> LaunchDescription: executable='multiplexer', remappings=[('/surface/mavros/rc/override', '/tether/mavros/rc/override')], emulate_tty=True, - output='screen' + output='screen', ) namespace_launch: GroupAction = GroupAction( actions=[ - PushRosNamespace("surface"), + PushRosNamespace('surface'), keyboard_control_node, control_inverter_node, - multiplexer_node + multiplexer_node, ] ) - return LaunchDescription([ - namespace_launch - ]) + return LaunchDescription([namespace_launch]) diff --git a/src/surface/flight_control/setup.py b/src/surface/flight_control/setup.py index 137031ca..affe9f04 100644 --- a/src/surface/flight_control/setup.py +++ b/src/surface/flight_control/setup.py @@ -10,12 +10,13 @@ version='1.2.0', packages=[PACKAGE_NAME], data_files=[ - ('share/ament_index/resource_index/packages', - ['resource/' + PACKAGE_NAME]), + ('share/ament_index/resource_index/packages', ['resource/' + PACKAGE_NAME]), ('share/' + PACKAGE_NAME, ['package.xml']), # Include all launch files. - (os.path.join('share', PACKAGE_NAME, 'launch'), - glob('launch/*launch.[pxy][yma]*')) + ( + os.path.join('share', PACKAGE_NAME, 'launch'), + glob('launch/*launch.[pxy][yma]*'), + ), ], install_requires=['setuptools'], zip_safe=True, @@ -30,7 +31,7 @@ 'keyboard_control_node = flight_control.keyboard_control_node:main', 'auto_docking_node = flight_control.auto_docking_node:main', 'control_inverter_node = flight_control.control_inverter_node:main', - 'multiplexer_node = flight_control.multiplexer:main' + 'multiplexer_node = flight_control.multiplexer:main', ], }, ) diff --git a/src/surface/flight_control/test/test_flake8.py b/src/surface/flight_control/test/test_flake8.py index 8ae474ef..6ffa8264 100644 --- a/src/surface/flight_control/test/test_flake8.py +++ b/src/surface/flight_control/test/test_flake8.py @@ -13,8 +13,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -from ament_flake8.main import main_with_errors import pytest +from ament_flake8.main import main_with_errors @pytest.mark.flake8 @@ -22,6 +22,6 @@ def test_flake8() -> None: """Tests flake8 on this module.""" error_code, errors = main_with_errors(argv=[]) - assert error_code == 0, \ - f'Found {len(errors)} code style errors / warnings:\n' + \ - '\n'.join(errors) + assert error_code == 0, ( + f'Found {len(errors)} code style errors / warnings:\n' + '\n'.join(errors) + ) diff --git a/src/surface/flight_control/test/test_keyboard_control.py b/src/surface/flight_control/test/test_keyboard_control.py index eb40872f..b286ea70 100644 --- a/src/surface/flight_control/test/test_keyboard_control.py +++ b/src/surface/flight_control/test/test_keyboard_control.py @@ -1,6 +1,6 @@ -from flight_control.keyboard_control_node import KeyboardListenerNode -import rclpy import pytest +import rclpy +from flight_control.keyboard_control_node import KeyboardListenerNode @pytest.fixture diff --git a/src/surface/flight_control/test/test_manual_control.py b/src/surface/flight_control/test/test_manual_control.py index 7dc8049c..5deef032 100644 --- a/src/surface/flight_control/test/test_manual_control.py +++ b/src/surface/flight_control/test/test_manual_control.py @@ -23,11 +23,10 @@ def test_joystick_profiles() -> None: forward=0, vertical=1, lateral=-1, - # Not nice possible values pitch=0.34, yaw=-0.6, - roll=0.92 + roll=0.92, ) msg = MultiplexerNode.to_override_rc_in(instruction) diff --git a/src/surface/flight_control/test/test_mypy.py b/src/surface/flight_control/test/test_mypy.py index c2e823a6..f43986b1 100644 --- a/src/surface/flight_control/test/test_mypy.py +++ b/src/surface/flight_control/test/test_mypy.py @@ -1,6 +1,7 @@ """Test mypy on this module.""" import os + import pytest from ament_mypy.main import main @@ -9,6 +10,6 @@ @pytest.mark.linter def test_mypy() -> None: """Tests mypy on this module.""" - path = os.path.join(os.getcwd(), "..", "..", "..", "pyproject.toml") - error_code = main(argv=["--config", path]) + path = os.path.join(os.getcwd(), '..', '..', '..', 'pyproject.toml') + error_code = main(argv=['--config', path]) assert error_code == 0, 'Found code style errors / warnings' diff --git a/src/surface/flight_control/test/test_pep257.py b/src/surface/flight_control/test/test_pep257.py index 62bed98c..67305010 100644 --- a/src/surface/flight_control/test/test_pep257.py +++ b/src/surface/flight_control/test/test_pep257.py @@ -23,6 +23,6 @@ @pytest.mark.pep257 def test_pep257() -> None: """Tests pep257 on this module.""" - path = os.path.join(os.getcwd(), "..", "..", "..", "pyproject.toml") + path = os.path.join(os.getcwd(), '..', '..', '..', 'pyproject.toml') error_code = main(argv=['.', 'test', '--config', path]) assert error_code == 0, 'Found code style errors / warnings' diff --git a/src/surface/gui/gui/app.py b/src/surface/gui/gui/app.py index 60716bb7..0619acdb 100644 --- a/src/surface/gui/gui/app.py +++ b/src/surface/gui/gui/app.py @@ -1,12 +1,12 @@ import atexit -import signal import os +import signal import qdarktheme import rclpy.utilities +from ament_index_python.packages import get_package_share_directory from PyQt6.QtWidgets import QApplication, QWidget from rclpy.node import Node -from ament_index_python.packages import get_package_share_directory class App(QWidget): @@ -31,11 +31,12 @@ def run_gui(self) -> None: # Apply theme theme_param = self.theme_param.get_parameter_value().string_value - theme_path = os.path.join(get_package_share_directory("gui"), - "styles", theme_param + ".qss") + theme_path = os.path.join( + get_package_share_directory('gui'), 'styles', theme_param + '.qss' + ) - base_theme = "dark" if theme_param == "dark" else "light" - custom_styles = "\n" + base_theme = 'dark' if theme_param == 'dark' else 'light' + custom_styles = '\n' if os.path.exists(theme_path): with open(theme_path, encoding='utf-8') as theme_file: custom_styles += theme_file.read() diff --git a/src/surface/gui/gui/gui_nodes/auxiliary_nodes/timer.py b/src/surface/gui/gui/gui_nodes/auxiliary_nodes/timer.py index 2d4c9330..a8af42bf 100644 --- a/src/surface/gui/gui/gui_nodes/auxiliary_nodes/timer.py +++ b/src/surface/gui/gui/gui_nodes/auxiliary_nodes/timer.py @@ -1,14 +1,15 @@ from typing import Optional + import rclpy -from rclpy.node import Node +from rclpy.duration import Duration from rclpy.executors import MultiThreadedExecutor +from rclpy.node import Node from rclpy.qos import qos_profile_system_default from rclpy.time import Time -from rclpy.duration import Duration + from rov_msgs.msg import MissionTimerTick from rov_msgs.srv import MissionTimerSet - PUBLISH_RATE = 10 # Hz DEFAULT_DURATION = Duration(seconds=15 * 60) @@ -23,14 +24,14 @@ class TimerNode(Node): def __init__(self) -> None: """Initialize the publisher, service client, and internal variables.""" - super().__init__("timer_node", parameter_overrides=[]) + super().__init__('timer_node', parameter_overrides=[]) self.publisher = self.create_publisher( - MissionTimerTick, "mission_timer", qos_profile_system_default + MissionTimerTick, 'mission_timer', qos_profile_system_default ) self.set_time_service = self.create_service( - MissionTimerSet, "set_mission_timer", callback=self.set_time_callback + MissionTimerSet, 'set_mission_timer', callback=self.set_time_callback ) self.publisher_timer = self.create_timer(1 / PUBLISH_RATE, self.timer_callback) @@ -71,8 +72,9 @@ def timer_callback(self) -> None: self.do_tick() self.publish_tick_message() - def set_time_callback(self, request: MissionTimerSet.Request, - response: MissionTimerSet.Response) -> MissionTimerSet.Response: + def set_time_callback( + self, request: MissionTimerSet.Request, response: MissionTimerSet.Response + ) -> MissionTimerSet.Response: """ Handle a request to start, stop, or reset the timer. @@ -111,5 +113,5 @@ def run_timer() -> None: rclpy.spin(timer_node, executor=executor) -if __name__ == "__main__": +if __name__ == '__main__': run_timer() diff --git a/src/surface/gui/gui/gui_nodes/event_nodes/client.py b/src/surface/gui/gui/gui_nodes/event_nodes/client.py index f81487df..f7af01ea 100644 --- a/src/surface/gui/gui/gui_nodes/event_nodes/client.py +++ b/src/surface/gui/gui/gui_nodes/event_nodes/client.py @@ -14,9 +14,14 @@ class GUIEventClient(Node): # The fix internally is already out on Rolling # Set to None for no timeout limits on service requests # else set to float number of seconds to limit request spinning - def __init__(self, srv_type: SrvType, topic: str, signal: pyqtBoundSignal, - timeout: float = 3.0, expected_namespace: str = '/surface/gui') -> None: - + def __init__( + self, + srv_type: SrvType, + topic: str, + signal: pyqtBoundSignal, + timeout: float = 3.0, + expected_namespace: str = '/surface/gui', + ) -> None: # Name this node with a sanitized version of the topic self.name = f'client_{re.sub(r"[^a-zA-Z0-9_]", "_", topic)}' super().__init__(self.name, parameter_overrides=[]) @@ -28,34 +33,40 @@ def __init__(self, srv_type: SrvType, topic: str, signal: pyqtBoundSignal, self.expected_namespace = expected_namespace self.cli = self.create_client(srv_type, topic) - Thread(target=self.__connect_to_service, daemon=True, - name=f'{self.name}_connect_to_service').start() + Thread( + target=self.__connect_to_service, + daemon=True, + name=f'{self.name}_connect_to_service', + ).start() def __connect_to_service(self) -> None: """Connect this client to a server in a separate thread.""" while not self.cli.wait_for_service(timeout_sec=self.timeout): self.get_logger().info( 'Service for GUI event client node on topic' - f' {self.expected_namespace}/{self.topic} unavailable, waiting again...') + f' {self.expected_namespace}/{self.topic} unavailable, waiting again...' + ) def send_request_async(self, request: SrvTypeRequest) -> None: """Send request to server in separate thread.""" - Thread(target=self.__send_request_with_signal, - kwargs={'request': request}, - daemon=True, name=f'{self.name}_send_request').start() + Thread( + target=self.__send_request_with_signal, + kwargs={'request': request}, + daemon=True, + name=f'{self.name}_send_request', + ).start() def __send_request_with_signal(self, request: SrvTypeRequest) -> None: """Send synchronous request to server and emit signal.""" future = self.cli.call_async(request) - rclpy.spin_until_future_complete( - self, future, timeout_sec=self.timeout) + rclpy.spin_until_future_complete(self, future, timeout_sec=self.timeout) try: result = future.result() if result is not None: self.signal.emit(result) else: - self.get_logger().error("Request received no response.") + self.get_logger().error('Request received no response.') except TypeError as exception: - self.get_logger().error("Request received incorrectly typed response.") + self.get_logger().error('Request received incorrectly typed response.') self.get_logger().error(str(exception)) diff --git a/src/surface/gui/gui/gui_nodes/event_nodes/publisher.py b/src/surface/gui/gui/gui_nodes/event_nodes/publisher.py index 93acfd3e..5457f33b 100644 --- a/src/surface/gui/gui/gui_nodes/event_nodes/publisher.py +++ b/src/surface/gui/gui/gui_nodes/event_nodes/publisher.py @@ -1,18 +1,21 @@ -from typing import Generic, TypeVar import re +from typing import Generic, TypeVar from rclpy.node import Node from rclpy.qos import QoSProfile, qos_profile_system_default - MsgT = TypeVar('MsgT') class GUIEventPublisher(Node, Generic[MsgT]): """Publisher for sending messages from the GUI.""" - def __init__(self, msg_type: type[MsgT], topic: str, - qos_profile: QoSProfile = qos_profile_system_default) -> None: + def __init__( + self, + msg_type: type[MsgT], + topic: str, + qos_profile: QoSProfile = qos_profile_system_default, + ) -> None: # Name this node with a sanitized version of the topic name = f'publisher_{re.sub(r"[^a-zA-Z0-9_]", "_", topic)}' super().__init__(name, parameter_overrides=[]) diff --git a/src/surface/gui/gui/gui_nodes/event_nodes/server.py b/src/surface/gui/gui/gui_nodes/event_nodes/server.py index e2199612..837f20b4 100644 --- a/src/surface/gui/gui/gui_nodes/event_nodes/server.py +++ b/src/surface/gui/gui/gui_nodes/event_nodes/server.py @@ -3,16 +3,20 @@ from threading import Thread from typing import Callable -from rclpy.service import SrvType, SrvTypeRequest, SrvTypeResponse from rclpy.executors import SingleThreadedExecutor from rclpy.node import Node +from rclpy.service import SrvType, SrvTypeRequest, SrvTypeResponse class GUIEventServer(Node): """Multithreaded server for processing server requests to update GUI.""" - def __init__(self, srv_type: SrvType, topic: str, - callback: Callable[[SrvTypeRequest, SrvTypeResponse], SrvTypeResponse]): + def __init__( + self, + srv_type: SrvType, + topic: str, + callback: Callable[[SrvTypeRequest, SrvTypeResponse], SrvTypeResponse], + ): """ Initialize this server with a CALLBACK for processing requests. @@ -26,6 +30,5 @@ def __init__(self, srv_type: SrvType, topic: str, custom_executor = SingleThreadedExecutor() custom_executor.add_node(self) - Thread(target=custom_executor.spin, daemon=True, - name=f'{name}_spin').start() + Thread(target=custom_executor.spin, daemon=True, name=f'{name}_spin').start() atexit.register(custom_executor.shutdown) diff --git a/src/surface/gui/gui/gui_nodes/event_nodes/subscriber.py b/src/surface/gui/gui/gui_nodes/event_nodes/subscriber.py index 2fa1ba7a..84fb3624 100644 --- a/src/surface/gui/gui/gui_nodes/event_nodes/subscriber.py +++ b/src/surface/gui/gui/gui_nodes/event_nodes/subscriber.py @@ -12,8 +12,13 @@ class GUIEventSubscriber(Node): """Multithreaded subscriber for receiving messages to the GUI.""" - def __init__(self, msg_type: MsgType, topic: str, signal: pyqtBoundSignal, - qos_profile: QoSProfile = qos_profile_system_default): + def __init__( + self, + msg_type: MsgType, + topic: str, + signal: pyqtBoundSignal, + qos_profile: QoSProfile = qos_profile_system_default, + ): # Name this node with a sanitized version of the topic name: str = f'subscriber_{re.sub(r"[^a-zA-Z0-9_]", "_", topic)}' super().__init__(name, parameter_overrides=[]) @@ -21,11 +26,11 @@ def __init__(self, msg_type: MsgType, topic: str, signal: pyqtBoundSignal, self.signal = signal self.subscription = self.create_subscription( - msg_type, topic, lambda data: signal.emit(data), qos_profile) + msg_type, topic, lambda data: signal.emit(data), qos_profile + ) # Wrap in silly lambda becuase PyQ6 and ROS won't play nice custom_executor = SingleThreadedExecutor() custom_executor.add_node(self) - Thread(target=custom_executor.spin, daemon=True, - name=f'{name}_spin').start() + Thread(target=custom_executor.spin, daemon=True, name=f'{name}_spin').start() atexit.register(custom_executor.shutdown) diff --git a/src/surface/gui/gui/operator_app.py b/src/surface/gui/gui/operator_app.py index f193f928..026fc180 100644 --- a/src/surface/gui/gui/operator_app.py +++ b/src/surface/gui/gui/operator_app.py @@ -1,14 +1,14 @@ from gui.app import App -from gui.widgets.logger import Logger -from gui.widgets.tabs.general_debug_tab import GeneralDebugTab from gui.widgets.float_comm import FloatComm -from gui.widgets.timer import InteractiveTimer -from gui.widgets.task_selector import TaskSelector from gui.widgets.flood_warning import FloodWarning -from gui.widgets.temperature import TemperatureSensor from gui.widgets.heartbeat import HeartbeatWidget from gui.widgets.ip_widget import IPWidget -from PyQt6.QtWidgets import QTabWidget, QWidget, QVBoxLayout, QHBoxLayout +from gui.widgets.logger import Logger +from gui.widgets.tabs.general_debug_tab import GeneralDebugTab +from gui.widgets.task_selector import TaskSelector +from gui.widgets.temperature import TemperatureSensor +from gui.widgets.timer import InteractiveTimer +from PyQt6.QtWidgets import QHBoxLayout, QTabWidget, QVBoxLayout, QWidget class OperatorApp(App): @@ -47,8 +47,8 @@ def __init__(self) -> None: self.setLayout(root_layout) tabs = QTabWidget() - tabs.addTab(main_tab, "Main") - tabs.addTab(GeneralDebugTab(), "General Debug") + tabs.addTab(main_tab, 'Main') + tabs.addTab(GeneralDebugTab(), 'General Debug') root_layout.addWidget(tabs) diff --git a/src/surface/gui/gui/pilot_app.py b/src/surface/gui/gui/pilot_app.py index 2152f090..6d306989 100644 --- a/src/surface/gui/gui/pilot_app.py +++ b/src/surface/gui/gui/pilot_app.py @@ -1,30 +1,33 @@ +import enum + from gui.app import App from gui.widgets.arm import Arm -from gui.widgets.timer import TimerDisplay from gui.widgets.flood_warning import FloodWarning -from gui.widgets.video_widget import (CameraDescription, CameraType, - VideoWidget) from gui.widgets.livestream_header import LivestreamHeader - +from gui.widgets.timer import TimerDisplay +from gui.widgets.video_widget import CameraDescription, CameraType, VideoWidget from PyQt6.QtCore import Qt -from PyQt6.QtWidgets import QHBoxLayout, QVBoxLayout from PyQt6.QtGui import QScreen - -import enum - +from PyQt6.QtWidgets import QHBoxLayout, QVBoxLayout FRONT_CAM_TOPIC = 'front_cam/image_raw' BOTTOM_CAM_TOPIC = 'bottom_cam/image_raw' class GuiType(enum.Enum): - PILOT = "pilot" - LIVESTREAM = "livestream" - DEBUG = "debug" + PILOT = 'pilot' + LIVESTREAM = 'livestream' + DEBUG = 'debug' -TWO_MONITOR_CONFIG: dict[GuiType, int | None] = {GuiType.PILOT: None, GuiType.LIVESTREAM: 1} -THREE_MONITOR_CONFIG: dict[GuiType, int | None] = {GuiType.PILOT: 2, GuiType.LIVESTREAM: 1} +TWO_MONITOR_CONFIG: dict[GuiType, int | None] = { + GuiType.PILOT: None, + GuiType.LIVESTREAM: 1, +} +THREE_MONITOR_CONFIG: dict[GuiType, int | None] = { + GuiType.PILOT: 2, + GuiType.LIVESTREAM: 1, +} class PilotApp(App): @@ -50,23 +53,19 @@ def __init__(self) -> None: self.setWindowTitle('Pilot GUI - CWRUbotix ROV 2024') front_cam_description = CameraDescription( - front_cam_type, - FRONT_CAM_TOPIC, - "Front Camera", - 1280, 720 + front_cam_type, FRONT_CAM_TOPIC, 'Front Camera', 1280, 720 ) bottom_cam_description = CameraDescription( - bottom_cam_type, - BOTTOM_CAM_TOPIC, - "Bottom Camera", - 1280, 720 + bottom_cam_type, BOTTOM_CAM_TOPIC, 'Bottom Camera', 1280, 720 ) - main_layout.addWidget(VideoWidget(front_cam_description), - alignment=Qt.AlignmentFlag.AlignHCenter) + main_layout.addWidget( + VideoWidget(front_cam_description), + alignment=Qt.AlignmentFlag.AlignHCenter, + ) main_layout.addWidget( VideoWidget(bottom_cam_description), - alignment=Qt.AlignmentFlag.AlignHCenter + alignment=Qt.AlignmentFlag.AlignHCenter, ) main_layout.addLayout(self.make_bottom_bar()) @@ -84,24 +83,22 @@ def __init__(self) -> None: self.setWindowTitle('Livestream GUI - CWRUbotix ROV 2024') front_cam_description = CameraDescription( - front_cam_type, - FRONT_CAM_TOPIC, - "Forward Camera", - 920, 690 + front_cam_type, FRONT_CAM_TOPIC, 'Forward Camera', 920, 690 ) bottom_cam_description = CameraDescription( - bottom_cam_type, - BOTTOM_CAM_TOPIC, - "Down Camera", - 920, 690 + bottom_cam_type, BOTTOM_CAM_TOPIC, 'Down Camera', 920, 690 ) video_layout = QHBoxLayout() - video_layout.addWidget(VideoWidget(front_cam_description), - alignment=Qt.AlignmentFlag.AlignHCenter) - video_layout.addWidget(VideoWidget(bottom_cam_description), - alignment=Qt.AlignmentFlag.AlignHCenter) + video_layout.addWidget( + VideoWidget(front_cam_description), + alignment=Qt.AlignmentFlag.AlignHCenter, + ) + video_layout.addWidget( + VideoWidget(bottom_cam_description), + alignment=Qt.AlignmentFlag.AlignHCenter, + ) video_layout.setSpacing(0) main_layout.addLayout(video_layout) @@ -111,25 +108,21 @@ def __init__(self) -> None: self.setWindowTitle('Debug GUI - CWRUbotix ROV 2024') front_cam_description = CameraDescription( - front_cam_type, - FRONT_CAM_TOPIC, - "Front Camera", - 721, 541 + front_cam_type, FRONT_CAM_TOPIC, 'Front Camera', 721, 541 ) bottom_cam_description = CameraDescription( - bottom_cam_type, - BOTTOM_CAM_TOPIC, - "Bottom Camera", - 721, 541 + bottom_cam_type, BOTTOM_CAM_TOPIC, 'Bottom Camera', 721, 541 ) video_layout = QHBoxLayout() - video_layout.addWidget(VideoWidget(front_cam_description), - alignment=Qt.AlignmentFlag.AlignHCenter) + video_layout.addWidget( + VideoWidget(front_cam_description), + alignment=Qt.AlignmentFlag.AlignHCenter, + ) video_layout.addWidget( VideoWidget(bottom_cam_description), - alignment=Qt.AlignmentFlag.AlignHCenter + alignment=Qt.AlignmentFlag.AlignHCenter, ) main_layout.addLayout(video_layout) @@ -151,12 +144,15 @@ def make_bottom_bar(self) -> QHBoxLayout: bottom_screen_layout.addWidget(timer) flood_widget = FloodWarning() - bottom_screen_layout.addWidget(flood_widget, alignment=Qt.AlignmentFlag.AlignHCenter | - Qt.AlignmentFlag.AlignBottom) + bottom_screen_layout.addWidget( + flood_widget, + alignment=Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignBottom, + ) arm = Arm() - bottom_screen_layout.addWidget(arm, alignment=Qt.AlignmentFlag.AlignRight | - Qt.AlignmentFlag.AlignBottom) + bottom_screen_layout.addWidget( + arm, alignment=Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignBottom + ) return bottom_screen_layout diff --git a/src/surface/gui/gui/styles/custom_styles.py b/src/surface/gui/gui/styles/custom_styles.py index f914a1fe..92fedf7f 100644 --- a/src/surface/gui/gui/styles/custom_styles.py +++ b/src/surface/gui/gui/styles/custom_styles.py @@ -1,23 +1,22 @@ -from PyQt6.QtWidgets import QWidget, QPushButton +from PyQt6.QtWidgets import QPushButton, QWidget class IndicatorMixin(QWidget): - - _PROPERTY_NAME = "widgetState" + _PROPERTY_NAME = 'widgetState' # A component is running, enabled, or armed - _ON = "on" + _ON = 'on' # A component is disabled, not running, or disarmed, but could be enabled through this widget - _OFF = "off" + _OFF = 'off' # A component is disabled, not expected to have any effect or perform its function because of # some external factor, either another widget or something external to the gui # For example, a the arm button when the pi is not connected - _INACTIVE = "inactive" + _INACTIVE = 'inactive' # Removes any state - _NO_STATE = "" + _NO_STATE = '' def set_on(self) -> None: self.setProperty(IndicatorMixin._PROPERTY_NAME, IndicatorMixin._ON) diff --git a/src/surface/gui/gui/widgets/arm.py b/src/surface/gui/gui/widgets/arm.py index 59509179..33ae91e4 100644 --- a/src/surface/gui/gui/widgets/arm.py +++ b/src/surface/gui/gui/widgets/arm.py @@ -21,7 +21,6 @@ class Arm(QWidget): vehicle_state_signal = pyqtSignal(VehicleState) def __init__(self) -> None: - super().__init__() layout = QHBoxLayout() @@ -30,8 +29,8 @@ def __init__(self) -> None: self.arm_button = ButtonIndicator() self.disarm_button = ButtonIndicator() - self.arm_button.setText("Arm") - self.disarm_button.setText("Disarm") + self.arm_button.setText('Arm') + self.disarm_button.setText('Disarm') self.arm_button.setMinimumWidth(self.BUTTON_WIDTH) self.disarm_button.setMinimumWidth(self.BUTTON_WIDTH) @@ -52,9 +51,12 @@ def __init__(self) -> None: self.command_response_signal.connect(self.arm_status) - self.arm_client = GUIEventClient(CommandBool, "mavros/cmd/arming", - self.command_response_signal, - expected_namespace='/tether') + self.arm_client = GUIEventClient( + CommandBool, + 'mavros/cmd/arming', + self.command_response_signal, + expected_namespace='/tether', + ) self.mavros_subscription = GUIEventSubscriber( VehicleState, @@ -73,7 +75,7 @@ def disarm_clicked(self) -> None: @pyqtSlot(CommandBool.Response) def arm_status(self, res: CommandBool.Response) -> None: if not res: - self.arm_client.get_logger().warn("Failed to arm or disarm.") + self.arm_client.get_logger().warn('Failed to arm or disarm.') @pyqtSlot(VehicleState) def vehicle_state_callback(self, msg: VehicleState) -> None: diff --git a/src/surface/gui/gui/widgets/circle.py b/src/surface/gui/gui/widgets/circle.py index 23e6d873..4d0ced50 100644 --- a/src/surface/gui/gui/widgets/circle.py +++ b/src/surface/gui/gui/widgets/circle.py @@ -1,18 +1,22 @@ from typing import Optional + from gui.styles.custom_styles import IndicatorMixin from PyQt6.QtCore import QSize, Qt from PyQt6.QtGui import QColor -from PyQt6.QtWidgets import QWidget, QLabel +from PyQt6.QtWidgets import QLabel, QWidget class Circle(QLabel): - def __init__(self, parent: Optional[QWidget] = None, - radius: int = 50, - color: Optional[QColor | Qt.GlobalColor] = None) -> None: + def __init__( + self, + parent: Optional[QWidget] = None, + radius: int = 50, + color: Optional[QColor | Qt.GlobalColor] = None, + ) -> None: super().__init__(parent) self.setFixedSize(QSize(2 * radius, 2 * radius)) stylesheet = self.styleSheet() - self.setStyleSheet(f"{stylesheet}border-radius: {radius}px;") + self.setStyleSheet(f'{stylesheet}border-radius: {radius}px;') if color: self.set_color(color) @@ -20,12 +24,13 @@ def __init__(self, parent: Optional[QWidget] = None, def set_color(self, color: QColor | Qt.GlobalColor) -> None: if isinstance(color, Qt.GlobalColor): color = QColor(color) - style = f"background-color: rgb({color.red()}, {color.green()}, {color.blue()});" - self.setStyleSheet(f"{self.styleSheet()}{style}") + style = ( + f'background-color: rgb({color.red()}, {color.green()}, {color.blue()});' + ) + self.setStyleSheet(f'{self.styleSheet()}{style}') class CircleIndicator(Circle, IndicatorMixin): - def __init__(self, parent: Optional[QWidget] = None, - radius: int = 50) -> None: + def __init__(self, parent: Optional[QWidget] = None, radius: int = 50) -> None: super().__init__(parent, radius) self.set_inactive() diff --git a/src/surface/gui/gui/widgets/float_comm.py b/src/surface/gui/gui/widgets/float_comm.py index fbf2cd55..eb3dbbef 100644 --- a/src/surface/gui/gui/widgets/float_comm.py +++ b/src/surface/gui/gui/widgets/float_comm.py @@ -25,20 +25,22 @@ def __init__(self) -> None: self.handle_data_signal.connect(self.handle_data) self.handle_serial_signal.connect(self.handle_serial) self.handle_data_single_signal.connect(self.handle_single) - GUIEventSubscriber(FloatData, "transceiver_data", self.handle_data_signal) - GUIEventSubscriber(FloatSerial, "float_serial", self.handle_serial_signal) - GUIEventSubscriber(FloatSingle, "transceiver_single", self.handle_data_single_signal) + GUIEventSubscriber(FloatData, 'transceiver_data', self.handle_data_signal) + GUIEventSubscriber(FloatSerial, 'float_serial', self.handle_serial_signal) + GUIEventSubscriber( + FloatSingle, 'transceiver_single', self.handle_data_single_signal + ) - command_pub = GUIEventPublisher(FloatCommand, "float_command") + command_pub = GUIEventPublisher(FloatCommand, 'float_command') info_layout = QVBoxLayout() single_layout = QHBoxLayout() self.team_number = QLabel('Waiting for Team #') - self.time = QLabel("Waiting for Time") - self.pressure = QLabel("Waiting for Pressure") - self.average_pressure = QLabel("Avg Pressure: 0/5") + self.time = QLabel('Waiting for Time') + self.pressure = QLabel('Waiting for Pressure') + self.average_pressure = QLabel('Avg Pressure: 0/5') single_layout.addWidget(self.team_number) single_layout.addWidget(self.time) @@ -47,8 +49,8 @@ def __init__(self) -> None: self.single_time = QLabel('Waiting for Time') self.single_pressure = QLabel('Waiting for Pressure') - self.profile_number = QLabel("Waiting for profile #") - self.profile_half = QLabel("Waiting for profile half") + self.profile_number = QLabel('Waiting for profile #') + self.profile_half = QLabel('Waiting for profile half') info_layout.addWidget(self.profile_number) info_layout.addWidget(self.profile_half) @@ -56,27 +58,27 @@ def __init__(self) -> None: info_and_buttons = QHBoxLayout() info_and_buttons.addLayout(info_layout) - submerge_button = QPushButton("Submerge") - pump_button = QPushButton("Pump") - suck_button = QPushButton("Suck") - return_button = QPushButton("Return") - stop_button = QPushButton("Stop") - - submerge_button.clicked.connect(lambda: command_pub.publish( - FloatCommand(command=FloatCommand.SUBMERGE) - )) - pump_button.clicked.connect(lambda: command_pub.publish( - FloatCommand(command=FloatCommand.PUMP) - )) - suck_button.clicked.connect(lambda: command_pub.publish( - FloatCommand(command=FloatCommand.SUCK) - )) - return_button.clicked.connect(lambda: command_pub.publish( - FloatCommand(command=FloatCommand.RETURN) - )) - stop_button.clicked.connect(lambda: command_pub.publish( - FloatCommand(command=FloatCommand.STOP) - )) + submerge_button = QPushButton('Submerge') + pump_button = QPushButton('Pump') + suck_button = QPushButton('Suck') + return_button = QPushButton('Return') + stop_button = QPushButton('Stop') + + submerge_button.clicked.connect( + lambda: command_pub.publish(FloatCommand(command=FloatCommand.SUBMERGE)) + ) + pump_button.clicked.connect( + lambda: command_pub.publish(FloatCommand(command=FloatCommand.PUMP)) + ) + suck_button.clicked.connect( + lambda: command_pub.publish(FloatCommand(command=FloatCommand.SUCK)) + ) + return_button.clicked.connect( + lambda: command_pub.publish(FloatCommand(command=FloatCommand.RETURN)) + ) + stop_button.clicked.connect( + lambda: command_pub.publish(FloatCommand(command=FloatCommand.STOP)) + ) info_and_buttons.addWidget(submerge_button) info_and_buttons.addWidget(pump_button) @@ -89,7 +91,7 @@ def __init__(self) -> None: self.console.setLineWrapMode(QTextEdit.LineWrapMode.NoWrap) font = self.console.font() - font.setFamily("Courier") + font.setFamily('Courier') font.setPointSize(11) self.console.setFont(font) @@ -122,9 +124,9 @@ def handle_data(self, msg: FloatData) -> None: msg : FloatData the data from the float """ - self.team_number.setText(f"Team #: {msg.team_number}") - self.profile_number.setText(f"Profile #: {msg.profile_number}") - self.profile_half.setText(f"Profile half: {msg.profile_half}") + self.team_number.setText(f'Team #: {msg.team_number}') + self.profile_number.setText(f'Profile #: {msg.profile_number}') + self.profile_half.setText(f'Profile half: {msg.profile_half}') time_data = list(msg.time_data) depth_data = list(msg.depth_data) @@ -168,14 +170,14 @@ def handle_serial(self, msg: FloatSerial) -> None: def handle_single(self, msg: FloatSingle) -> None: self.counter += 1 - self.team_number.setText(f"Team #: {msg.team_number}") - self.time.setText(f"Time: {msg.time_ms} (ms)") + self.team_number.setText(f'Team #: {msg.team_number}') + self.time.setText(f'Time: {msg.time_ms} (ms)') # Magic mbar -> Kpa pressure = round(msg.pressure / 10, 4) avg_pressure = round(msg.average_pressure / 10, 4) - self.pressure.setText(f"Pressure: {pressure} (kPa)") + self.pressure.setText(f'Pressure: {pressure} (kPa)') if msg.average_pressure != float(): - self.average_pressure.setText(f"Avg Pressure: {avg_pressure} (kPa)") + self.average_pressure.setText(f'Avg Pressure: {avg_pressure} (kPa)') else: - self.average_pressure.setText(f"Avg Pressure: {self.counter}/5") + self.average_pressure.setText(f'Avg Pressure: {self.counter}/5') diff --git a/src/surface/gui/gui/widgets/flood_warning.py b/src/surface/gui/gui/widgets/flood_warning.py index 3e48b5b0..de853df3 100644 --- a/src/surface/gui/gui/widgets/flood_warning.py +++ b/src/surface/gui/gui/widgets/flood_warning.py @@ -1,22 +1,20 @@ +import os + +from ament_index_python.packages import get_package_share_directory from gui.gui_nodes.event_nodes.subscriber import GUIEventSubscriber from gui.widgets.circle import CircleIndicator -from PyQt6.QtCore import pyqtSignal, pyqtSlot, QUrl +from PyQt6.QtCore import QUrl, pyqtSignal, pyqtSlot from PyQt6.QtGui import QFont -from PyQt6.QtWidgets import QHBoxLayout, QLabel, QVBoxLayout, QWidget from PyQt6.QtMultimedia import QSoundEffect +from PyQt6.QtWidgets import QHBoxLayout, QLabel, QVBoxLayout, QWidget from rov_msgs.msg import Flooding -from ament_index_python.packages import get_package_share_directory -import os - - # The 'Loop' enum has int values, not 'Loop', unbeknownst to mypy Q_SOUND_EFFECT_LOOP_FOREVER: int = QSoundEffect.Loop.Infinite.value # type: ignore class FloodWarning(QWidget): - signal = pyqtSignal(Flooding) def __init__(self) -> None: @@ -32,7 +30,7 @@ def __init__(self) -> None: header_layout = QHBoxLayout() label = QLabel('Flooding Status') - font = QFont("Arial", 14) + font = QFont('Arial', 14) label.setFont(font) header_layout.addWidget(label) self.indicator_circle = CircleIndicator(radius=10) @@ -46,7 +44,7 @@ def __init__(self) -> None: self.setLayout(flood_layout) alarm_sound_path = os.path.join( - get_package_share_directory("gui"), "sounds", "alarm.wav" + get_package_share_directory('gui'), 'sounds', 'alarm.wav' ) self.alarm_sound = QSoundEffect() self.alarm_sound.setSource(QUrl.fromLocalFile(alarm_sound_path)) @@ -56,7 +54,9 @@ def __init__(self) -> None: def refresh(self, msg: Flooding) -> None: if msg.flooding: self.indicator.setText('FLOODING') - self.subscription.get_logger().error("Robot is actively flooding, do something!") + self.subscription.get_logger().error( + 'Robot is actively flooding, do something!' + ) self.warning_msg_latch = True self.indicator_circle.set_off() @@ -67,6 +67,8 @@ def refresh(self, msg: Flooding) -> None: self.indicator.setText('No Water present') self.indicator_circle.set_on() if self.warning_msg_latch: - self.subscription.get_logger().warning("Robot flooding has reset itself.") + self.subscription.get_logger().warning( + 'Robot flooding has reset itself.' + ) self.warning_msg_latch = False self.alarm_sound.setLoopCount(0) diff --git a/src/surface/gui/gui/widgets/heartbeat.py b/src/surface/gui/gui/widgets/heartbeat.py index dbd3240d..b0b8c1be 100644 --- a/src/surface/gui/gui/widgets/heartbeat.py +++ b/src/surface/gui/gui/widgets/heartbeat.py @@ -8,20 +8,21 @@ class HeartbeatWidget(QWidget): - signal = pyqtSignal(VehicleState) def __init__(self) -> None: super().__init__() self.signal.connect(self.refresh) - self.subscription = GUIEventSubscriber(VehicleState, 'vehicle_state_event', self.signal) + self.subscription = GUIEventSubscriber( + VehicleState, 'vehicle_state_event', self.signal + ) # Create a latch variable self.warning_msg_latch: bool = False heartbeat_layout = QVBoxLayout() - font = QFont("Arial", 14) + font = QFont('Arial', 14) pi_status_layout = QHBoxLayout() self.pi_indicator = QLabel('No Pi Status') diff --git a/src/surface/gui/gui/widgets/ip_widget.py b/src/surface/gui/gui/widgets/ip_widget.py index eb83c16a..72452250 100644 --- a/src/surface/gui/gui/widgets/ip_widget.py +++ b/src/surface/gui/gui/widgets/ip_widget.py @@ -6,7 +6,6 @@ class IPWidget(QWidget): - signal = pyqtSignal(IPAddress) def __init__(self) -> None: @@ -17,7 +16,9 @@ def __init__(self) -> None: ip_layout = QVBoxLayout() wired_str = f'Last known Pi Wired IP: {IPAddress.ETHERNET_ADDRESS__DEFAULT}' - wireless_str = f'Last known Pi Wireless IP: {IPAddress.WIRELESS_ADDRESS__DEFAULT}' + wireless_str = ( + f'Last known Pi Wireless IP: {IPAddress.WIRELESS_ADDRESS__DEFAULT}' + ) self.ethernet_label = QLabel(wired_str) self.wireless_label = QLabel(wireless_str) @@ -28,4 +29,6 @@ def __init__(self) -> None: @pyqtSlot(IPAddress) def refresh(self, msg: IPAddress) -> None: self.ethernet_label.setText(f'Last known Pi Wired IP: {msg.ethernet_address}') - self.wireless_label.setText(f'Last known Pi Wireless IP: {msg.wireless_address}') + self.wireless_label.setText( + f'Last known Pi Wireless IP: {msg.wireless_address}' + ) diff --git a/src/surface/gui/gui/widgets/livestream_header.py b/src/surface/gui/gui/widgets/livestream_header.py index 021ae40c..e69bd335 100644 --- a/src/surface/gui/gui/widgets/livestream_header.py +++ b/src/surface/gui/gui/widgets/livestream_header.py @@ -1,9 +1,9 @@ -from PyQt6.QtCore import Qt -from PyQt6.QtWidgets import QHBoxLayout, QLabel, QVBoxLayout, QWidget -from PyQt6.QtGui import QPixmap +import os from ament_index_python.packages import get_package_share_directory -import os +from PyQt6.QtCore import Qt +from PyQt6.QtGui import QPixmap +from PyQt6.QtWidgets import QHBoxLayout, QLabel, QVBoxLayout, QWidget class LivestreamHeader(QWidget): @@ -14,27 +14,31 @@ def __init__(self) -> None: self.setLayout(root_layout) logo_path = os.path.join( - get_package_share_directory("gui"), "images", "CWRUbotix Logo.png" + get_package_share_directory('gui'), 'images', 'CWRUbotix Logo.png' ) logo_pixmap = QPixmap(logo_path) logo_label = QLabel() logo_label.setPixmap( - logo_pixmap.scaled(100, 100, Qt.AspectRatioMode.KeepAspectRatio, - Qt.TransformationMode.SmoothTransformation) + logo_pixmap.scaled( + 100, + 100, + Qt.AspectRatioMode.KeepAspectRatio, + Qt.TransformationMode.SmoothTransformation, + ) ) root_layout.addWidget(logo_label) root_layout.addSpacing(10) text_vbox = QVBoxLayout() - title_label = QLabel("Explorer Team 25 - CWRUbotix") + title_label = QLabel('Explorer Team 25 - CWRUbotix') title_label.setStyleSheet('QLabel { font-size: 35px; }') title_label.setAlignment(Qt.AlignmentFlag.AlignAbsolute) text_vbox.addWidget(title_label) - subtitle_label = QLabel("Case Western Reserve University - Cleveland, Ohio") + subtitle_label = QLabel('Case Western Reserve University - Cleveland, Ohio') subtitle_label.setStyleSheet('QLabel { font-size: 20px; }') subtitle_label.setAlignment(Qt.AlignmentFlag.AlignAbsolute) text_vbox.addWidget(subtitle_label) diff --git a/src/surface/gui/gui/widgets/logger.py b/src/surface/gui/gui/widgets/logger.py index 3a2cacbb..bb7f94df 100644 --- a/src/surface/gui/gui/widgets/logger.py +++ b/src/surface/gui/gui/widgets/logger.py @@ -7,12 +7,14 @@ from rclpy.impl.logging_severity import LoggingSeverity # Dictionary linking LoggingSeverity to a QColor -SEVERITY_LEVELS_DICT = {LoggingSeverity.UNSET: QColor(0, 0, 0), - LoggingSeverity.DEBUG: QColor(50, 50, 50), - LoggingSeverity.INFO: QColor(150, 150, 150), - LoggingSeverity.WARN: QColor(150, 150, 0), - LoggingSeverity.ERROR: QColor(255, 0, 0), - LoggingSeverity.FATAL: QColor(168, 0, 0)} +SEVERITY_LEVELS_DICT = { + LoggingSeverity.UNSET: QColor(0, 0, 0), + LoggingSeverity.DEBUG: QColor(50, 50, 50), + LoggingSeverity.INFO: QColor(150, 150, 150), + LoggingSeverity.WARN: QColor(150, 150, 0), + LoggingSeverity.ERROR: QColor(255, 0, 0), + LoggingSeverity.FATAL: QColor(168, 0, 0), +} class Logger(QWidget): @@ -43,7 +45,7 @@ def __init__(self) -> None: layout.addWidget(self.textbox) self.terminal_font = self.textbox.font() - self.terminal_font.setFamily("Courier") + self.terminal_font.setFamily('Courier') self.terminal_font.setPointSize(11) self.print_log_signal.connect(self.print_log) diff --git a/src/surface/gui/gui/widgets/seagrass.py b/src/surface/gui/gui/widgets/seagrass.py index d9b66278..d49640e3 100644 --- a/src/surface/gui/gui/widgets/seagrass.py +++ b/src/surface/gui/gui/widgets/seagrass.py @@ -1,13 +1,13 @@ from typing import Callable, Optional -from gui.widgets.video_widget import CameraDescription, PauseableVideoWidget, CameraType +from gui.widgets.video_widget import (CameraDescription, CameraType, + PauseableVideoWidget) from PyQt6.QtCore import Qt from PyQt6.QtWidgets import (QFrame, QGridLayout, QHBoxLayout, QLabel, QPushButton, QVBoxLayout, QWidget) class SeagrassWidget(QWidget): - BUTTON_WIDTH = 120 def __init__(self) -> None: @@ -16,32 +16,36 @@ def __init__(self) -> None: root_layout = QHBoxLayout(self) self.after_grid = SeagrassGrid(self.update_result_text) - self.before_grid = SeagrassGrid(self.update_result_text, self.after_grid.set_button) + self.before_grid = SeagrassGrid( + self.update_result_text, self.after_grid.set_button + ) # Before layout before_layout = QVBoxLayout() before_btns_layout = QHBoxLayout() - set_all_green = QPushButton("Set All Green") + set_all_green = QPushButton('Set All Green') set_all_green.setMaximumWidth(self.BUTTON_WIDTH) set_all_green.clicked.connect(lambda: self.before_grid.reset_grid(True)) - set_all_white = QPushButton("Set All White") + set_all_white = QPushButton('Set All White') set_all_white.setMaximumWidth(self.BUTTON_WIDTH) set_all_white.clicked.connect(lambda: self.before_grid.reset_grid(False)) before_btns_layout.addWidget(set_all_green) before_btns_layout.addWidget(set_all_white) - before_layout.addWidget(QLabel("Before"), alignment=Qt.AlignmentFlag.AlignCenter) + before_layout.addWidget( + QLabel('Before'), alignment=Qt.AlignmentFlag.AlignCenter + ) before_layout.addLayout(before_btns_layout) before_layout.addWidget(self.before_grid.frame) before_layout.addStretch() - bottom_cam_description = CameraDescription(CameraType.ETHERNET, - 'bottom_cam/image_raw', - 'Bottom Camera', 640, 370) + bottom_cam_description = CameraDescription( + CameraType.ETHERNET, 'bottom_cam/image_raw', 'Bottom Camera', 640, 370 + ) # Bottom cam self.bottom_cam = PauseableVideoWidget(bottom_cam_description) @@ -49,13 +53,13 @@ def __init__(self) -> None: after_layout = QVBoxLayout() after_bttn_layout = QHBoxLayout() - match_before = QPushButton("Match Before") + match_before = QPushButton('Match Before') match_before.setMaximumWidth(120) match_before.clicked.connect(self.before_grid.update_connected_grid) after_bttn_layout.addWidget(match_before) - after_layout.addWidget(QLabel("After"), alignment=Qt.AlignmentFlag.AlignCenter) + after_layout.addWidget(QLabel('After'), alignment=Qt.AlignmentFlag.AlignCenter) after_layout.addLayout(after_bttn_layout) after_layout.addWidget(self.after_grid.frame) @@ -67,8 +71,8 @@ def __init__(self) -> None: result_widget.setLayout(result_layout) - self.before_label = QLabel("Before: ") - self.after_label = QLabel("After: ") + self.before_label = QLabel('Before: ') + self.after_label = QLabel('After: ') self.diff_label = QLabel() result_layout.addWidget(self.before_label) @@ -92,21 +96,24 @@ def update_result_text(self) -> None: diff: int = abs(after_num - before_num) if after_num > before_num: - result = f"{diff} went from white to green" + result = f'{diff} went from white to green' elif after_num < before_num: - result = f"{diff} went from green to white" + result = f'{diff} went from green to white' else: - result = "There was no change" + result = 'There was no change' - self.before_label.setText(f"Before: {before_num} green") - self.after_label.setText(f"After: {after_num} green") + self.before_label.setText(f'Before: {before_num} green') + self.after_label.setText(f'After: {after_num} green') self.diff_label.setText(result) class SeagrassGrid(QWidget): - def __init__(self, update_result_text: Callable[[], None], - set_other_button: Optional[Callable[[int, bool], None]] = None) -> None: + def __init__( + self, + update_result_text: Callable[[], None], + set_other_button: Optional[Callable[[int, bool], None]] = None, + ) -> None: super().__init__() self.set_other_button: Optional[Callable[[int, bool], None]] = set_other_button @@ -121,7 +128,7 @@ def __init__(self, update_result_text: Callable[[], None], self.frame = QFrame() self.frame.setLayout(grid_layout) - self.frame.setStyleSheet("border: 1px solid gray") + self.frame.setStyleSheet('border: 1px solid gray') self.all_buttons: list[SeagrassButton] = [] N = 8 @@ -129,9 +136,9 @@ def __init__(self, update_result_text: Callable[[], None], for row in range(N): for col in range(N): - seagrass_button: SeagrassButton = SeagrassButton(button_id, 50, - update_result_text, - self.set_other_button) + seagrass_button: SeagrassButton = SeagrassButton( + button_id, 50, update_result_text, self.set_other_button + ) self.all_buttons.append(seagrass_button) grid_layout.addWidget(seagrass_button, row, col) @@ -153,7 +160,7 @@ def get_num_recovered(self) -> int: def update_connected_grid(self) -> None: if self.set_other_button is None: - raise ValueError("self.set_other_button has been called on after grid") + raise ValueError('self.set_other_button has been called on after grid') for button in self.all_buttons: self.set_other_button(button.button_id, button.recovered) @@ -164,15 +171,20 @@ def set_button(self, button_id: int, recovered: bool) -> None: class SeagrassButton(QPushButton): - def __init__(self, button_id: int, size: int, update_text: Callable[[], None], - set_other_button: Optional[Callable[[int, bool], None]] = None) -> None: + def __init__( + self, + button_id: int, + size: int, + update_text: Callable[[], None], + set_other_button: Optional[Callable[[int, bool], None]] = None, + ) -> None: super(SeagrassButton, self).__init__() self.button_id: int = button_id self.setFixedSize(size, size) self.recovered = True - self.setStyleSheet("border: 1px solid gray; background-color : green") + self.setStyleSheet('border: 1px solid gray; background-color : green') self.update_text = update_text self.set_other_button = set_other_button @@ -188,11 +200,11 @@ def set_color(self, recovered: bool) -> None: self.recovered = recovered if recovered: - color = "green" + color = 'green' else: - color = "white" + color = 'white' - self.setStyleSheet(f"border: 1px solid gray; background-color :{color}") + self.setStyleSheet(f'border: 1px solid gray; background-color :{color}') # Update other button if self.set_other_button is not None: diff --git a/src/surface/gui/gui/widgets/tabs/general_debug_tab.py b/src/surface/gui/gui/widgets/tabs/general_debug_tab.py index 80dd5e7e..e7d0f5fd 100644 --- a/src/surface/gui/gui/widgets/tabs/general_debug_tab.py +++ b/src/surface/gui/gui/widgets/tabs/general_debug_tab.py @@ -12,11 +12,14 @@ def __init__(self) -> None: super().__init__() top_bar = QHBoxLayout() - top_bar.addWidget(IPWidget(), alignment=Qt.AlignmentFlag.AlignTop | - Qt.AlignmentFlag.AlignLeft) + top_bar.addWidget( + IPWidget(), alignment=Qt.AlignmentFlag.AlignTop | Qt.AlignmentFlag.AlignLeft + ) - top_bar.addWidget(HeartbeatWidget(), alignment=Qt.AlignmentFlag.AlignTop | - Qt.AlignmentFlag.AlignLeft) + top_bar.addWidget( + HeartbeatWidget(), + alignment=Qt.AlignmentFlag.AlignTop | Qt.AlignmentFlag.AlignLeft, + ) right_bar = QVBoxLayout() right_bar.addWidget(ThrusterTester()) diff --git a/src/surface/gui/gui/widgets/task_selector.py b/src/surface/gui/gui/widgets/task_selector.py index 308ca680..5b110441 100644 --- a/src/surface/gui/gui/widgets/task_selector.py +++ b/src/surface/gui/gui/widgets/task_selector.py @@ -1,7 +1,8 @@ from gui.gui_nodes.event_nodes.client import GUIEventClient +from gui.styles.custom_styles import ButtonIndicator from PyQt6.QtCore import pyqtSignal, pyqtSlot from PyQt6.QtWidgets import QGridLayout, QPushButton, QWidget -from gui.styles.custom_styles import ButtonIndicator + from rov_msgs.srv import AutonomousFlight WIDTH = 200 @@ -15,14 +16,15 @@ class TaskSelector(QWidget): def __init__(self) -> None: super().__init__() - self.task_controller = GUIEventClient(AutonomousFlight, 'auto_control_toggle', - self.scheduler_response_signal) + self.task_controller = GUIEventClient( + AutonomousFlight, 'auto_control_toggle', self.scheduler_response_signal + ) layout = QGridLayout() self.setLayout(layout) # Create Start button - self.start_btn = ButtonIndicator("Start Auto") + self.start_btn = ButtonIndicator('Start Auto') self.start_btn.clicked.connect( lambda: self.task_controller.send_request_async( AutonomousFlight.Request(state=AutonomousFlight.Request.START) @@ -32,7 +34,7 @@ def __init__(self) -> None: self.start_btn.setFixedWidth(WIDTH) # Create Stop button - stop_btn = QPushButton("Stop Auto") + stop_btn = QPushButton('Stop Auto') stop_btn.clicked.connect( lambda: self.task_controller.send_request_async( AutonomousFlight.Request(state=AutonomousFlight.Request.STOP) diff --git a/src/surface/gui/gui/widgets/temperature.py b/src/surface/gui/gui/widgets/temperature.py index dcf6a80e..9cba418f 100644 --- a/src/surface/gui/gui/widgets/temperature.py +++ b/src/surface/gui/gui/widgets/temperature.py @@ -1,10 +1,11 @@ from collections import deque from gui.gui_nodes.event_nodes.subscriber import GUIEventSubscriber -from rov_msgs.msg import Temperature from PyQt6.QtCore import pyqtSignal, pyqtSlot -from PyQt6.QtWidgets import (QLabel, QLineEdit, QPushButton, - QVBoxLayout, QWidget) +from PyQt6.QtWidgets import (QLabel, QLineEdit, QPushButton, QVBoxLayout, + QWidget) + +from rov_msgs.msg import Temperature MIN_TEMP_C = 0 MAX_TEMP_C = 200 @@ -19,9 +20,7 @@ def __init__(self) -> None: self.temperature_reading_signal.connect(self.temperature_received) self.temp_subscriber: GUIEventSubscriber = GUIEventSubscriber( - Temperature, - "temperature", - self.temperature_reading_signal + Temperature, 'temperature', self.temperature_reading_signal ) self.temps: deque[float] = deque(maxlen=QUEUE_LEN) diff --git a/src/surface/gui/gui/widgets/thruster_tester.py b/src/surface/gui/gui/widgets/thruster_tester.py index 1e2aee72..521fae0d 100644 --- a/src/surface/gui/gui/widgets/thruster_tester.py +++ b/src/surface/gui/gui/widgets/thruster_tester.py @@ -22,16 +22,14 @@ def __init__(self) -> None: super().__init__() self.cmd_client: GUIEventClient = GUIEventClient( - CommandLong, - "mavros/cmd/command", - self.command_response_signal + CommandLong, 'mavros/cmd/command', self.command_response_signal ) self.command_response_signal.connect(self.command_response_handler) layout = QVBoxLayout() self.setLayout(layout) - heading = QLabel("Thruster Pin Configuration") + heading = QLabel('Thruster Pin Configuration') pin_numbers_grid = QGridLayout() self.pin_input_widgets = [] @@ -59,10 +57,10 @@ def __init__(self) -> None: pin_numbers_grid.setColumnStretch(6, 1) pin_assignment_button = QPushButton() - pin_assignment_button.setText("Send Pin Assignments") + pin_assignment_button.setText('Send Pin Assignments') test_button = QPushButton() - test_button.setText("Test Thrusters") + test_button.setText('Test Thrusters') test_button.clicked.connect(self.async_send_message) layout.addWidget(heading) @@ -70,7 +68,9 @@ def __init__(self) -> None: layout.addWidget(pin_assignment_button) layout.addWidget(test_button) - def test_motor_for_time(self, motor_index: int, throttle: float, duration: float) -> None: + def test_motor_for_time( + self, motor_index: int, throttle: float, duration: float + ) -> None: """ Run a motor for an (approximate) length of time (blocking). @@ -87,7 +87,9 @@ def test_motor_for_time(self, motor_index: int, throttle: float, duration: float """ throttle = max(min(throttle, 1.0), -1.0) # Clamp the throttle between -1 and 1 - throttle_percent = 50 * throttle + 50 # Rescale to be from 0 to 100, with 50 at the center + throttle_percent = ( + 50 * throttle + 50 + ) # Rescale to be from 0 to 100, with 50 at the center start_time = time.time() while time.time() - start_time < duration: @@ -99,18 +101,20 @@ def test_motor_for_time(self, motor_index: int, throttle: float, duration: float param3=float(throttle_percent), param4=0.0, # Time between tests param5=0.0, # Number of motors to test - param6=2.0 # MOTOR_TEST_ORDER_BOARD + param6=2.0, # MOTOR_TEST_ORDER_BOARD ) ) time.sleep(0.05) def async_send_message(self) -> None: - Thread(target=self.send_test_message, daemon=True, name="thruster_test_thread").start() + Thread( + target=self.send_test_message, daemon=True, name='thruster_test_thread' + ).start() def send_test_message(self) -> None: for motor_index in range(0, self.MOTOR_COUNT): - self.cmd_client.get_logger().info(f"Testing thruster {motor_index}") + self.cmd_client.get_logger().info(f'Testing thruster {motor_index}') self.test_motor_for_time(motor_index, self.TEST_THROTTLE, self.TEST_LENGTH) self.test_motor_for_time(motor_index, 0.0, 0.5) @@ -122,4 +126,6 @@ def send_pin_assignments(self) -> None: @pyqtSlot(CommandLong.Response) def command_response_handler(self, res: CommandLong.Response) -> None: - self.cmd_client.get_logger().debug(f"Test response: {res.success}, {res.result}") + self.cmd_client.get_logger().debug( + f'Test response: {res.success}, {res.result}' + ) diff --git a/src/surface/gui/gui/widgets/timer.py b/src/surface/gui/gui/widgets/timer.py index 8a8e7fc4..e94c7120 100644 --- a/src/surface/gui/gui/widgets/timer.py +++ b/src/surface/gui/gui/widgets/timer.py @@ -1,15 +1,16 @@ +from gui.gui_nodes.event_nodes.client import GUIEventClient +from gui.gui_nodes.event_nodes.subscriber import GUIEventSubscriber from PyQt6.QtCore import Qt, pyqtSignal, pyqtSlot from PyQt6.QtGui import QFont from PyQt6.QtWidgets import QGridLayout, QLabel, QPushButton, QWidget -from rov_msgs.msg import MissionTimerTick -from rov_msgs.srv import MissionTimerSet from rclpy.duration import Duration -from gui.gui_nodes.event_nodes.client import GUIEventClient -from gui.gui_nodes.event_nodes.subscriber import GUIEventSubscriber - +from rov_msgs.msg import MissionTimerTick +from rov_msgs.srv import MissionTimerSet -RESET_SECONDS = 15 * 60 # The number of seconds to set the timer to when reset is clicked +RESET_SECONDS = ( + 15 * 60 +) # The number of seconds to set the timer to when reset is clicked class TimerDisplay(QLabel): @@ -32,7 +33,7 @@ def __init__(self) -> None: self.setAlignment(Qt.AlignmentFlag.AlignCenter) self.setFont(QFont('Arial', 36)) - self.setText("--:--") + self.setText('--:--') def update_label(self, seconds_left: int) -> None: """ @@ -45,7 +46,7 @@ def update_label(self, seconds_left: int) -> None: """ minutes = seconds_left // 60 seconds = seconds_left % 60 - self.setText(f"{minutes:02d}:{seconds:02d}") + self.setText(f'{minutes:02d}:{seconds:02d}') @pyqtSlot(MissionTimerTick) def mission_timer_callback(self, msg: MissionTimerTick) -> None: @@ -79,9 +80,7 @@ def __init__(self) -> None: super().__init__() self.set_timer_client = GUIEventClient( - MissionTimerSet, - "set_mission_timer", - self.set_timer_response_signal + MissionTimerSet, 'set_mission_timer', self.set_timer_response_signal ) self.set_timer_response_signal.connect(self.set_time_response_callback) @@ -116,17 +115,14 @@ def mission_timer_callback(self, msg: MissionTimerTick) -> None: """ self.toggle_btn.setChecked(msg.is_running) if msg.is_running: - self.toggle_btn.setText("Pause") + self.toggle_btn.setText('Pause') else: - self.toggle_btn.setText("Start") + self.toggle_btn.setText('Start') def toggle_timer(self) -> None: """If the ROS timer is running, pause it. If it's paused, resume it.""" self.set_timer_client.send_request_async( - MissionTimerSet.Request( - set_running=True, - running=not self.timer.running - ) + MissionTimerSet.Request(set_running=True, running=not self.timer.running) ) def reset_timer(self) -> None: @@ -136,7 +132,7 @@ def reset_timer(self) -> None: set_time=True, time=Duration(seconds=RESET_SECONDS).to_msg(), set_running=True, - running=False + running=False, ) ) @@ -151,4 +147,4 @@ def set_time_response_callback(self, res: MissionTimerSet.Response) -> None: The ROS response sent by the timer node. """ if not res.success: - self.set_timer_client.get_logger().error("Failed to set timer") + self.set_timer_client.get_logger().error('Failed to set timer') diff --git a/src/surface/gui/gui/widgets/video_widget.py b/src/surface/gui/gui/widgets/video_widget.py index 190de68f..284a3dec 100644 --- a/src/surface/gui/gui/widgets/video_widget.py +++ b/src/surface/gui/gui/widgets/video_widget.py @@ -81,7 +81,9 @@ def __init__(self, camera_description: CameraDescription) -> None: self.setLayout(layout) self.video_frame_label = QLabel() - self.video_frame_label.setText(f'This topic had no frame: {camera_description.topic}') + self.video_frame_label.setText( + f'This topic had no frame: {camera_description.topic}' + ) layout.addWidget(self.video_frame_label) self.label = QLabel(camera_description.label) @@ -93,16 +95,16 @@ def __init__(self, camera_description: CameraDescription) -> None: self.handle_frame_signal.connect(self.handle_frame) self.camera_subscriber: GUIEventSubscriber = GUIEventSubscriber( - Image, camera_description.topic, self.handle_frame_signal) + Image, camera_description.topic, self.handle_frame_signal + ) @pyqtSlot(Image) def handle_frame(self, frame: Image) -> None: - cv_image = self.cv_bridge.imgmsg_to_cv2( - frame, desired_encoding='passthrough') + cv_image = self.cv_bridge.imgmsg_to_cv2(frame, desired_encoding='passthrough') - qt_image: QImage = self.convert_cv_qt(cv_image, - self.camera_description.width, - self.camera_description.height) + qt_image: QImage = self.convert_cv_qt( + cv_image, self.camera_description.width, self.camera_description.height + ) self.video_frame_label.setPixmap(QPixmap.fromImage(qt_image)) @@ -127,7 +129,7 @@ def convert_cv_qt(self, cv_img: MatLike, width: int = 0, height: int = 0) -> QIm img_format = QImage.Format.Format_Grayscale8 else: - raise ValueError("Somehow not color or grayscale image.") + raise ValueError('Somehow not color or grayscale image.') qt_image = QImage(cv_img.data, w, h, bytes_per_line, img_format) qt_image = qt_image.scaled(width, height, Qt.AspectRatioMode.KeepAspectRatio) @@ -142,9 +144,12 @@ class SwitchableVideoWidget(VideoWidget): controller_signal = pyqtSignal(CameraControllerSwitch) - def __init__(self, camera_descriptions: list[CameraDescription], - controller_button_topic: Optional[str] = None, - default_cam_num: int = 0): + def __init__( + self, + camera_descriptions: list[CameraDescription], + controller_button_topic: Optional[str] = None, + default_cam_num: int = 0, + ): self.camera_descriptions = camera_descriptions self.active_cam = default_cam_num @@ -152,7 +157,9 @@ def __init__(self, camera_descriptions: list[CameraDescription], super().__init__(camera_descriptions[self.active_cam]) - self.button: QPushButton = QPushButton(camera_descriptions[self.active_cam].label) + self.button: QPushButton = QPushButton( + camera_descriptions[self.active_cam].label + ) self.button.setMaximumWidth(self.BUTTON_WIDTH) self.button.clicked.connect(self.gui_camera_switch) @@ -160,15 +167,16 @@ def __init__(self, camera_descriptions: list[CameraDescription], if isinstance(layout, QVBoxLayout): layout.addWidget(self.button, alignment=Qt.AlignmentFlag.AlignCenter) else: - self.camera_subscriber.get_logger().error("Missing Layout") + self.camera_subscriber.get_logger().error('Missing Layout') if controller_button_topic is not None: self.controller_signal.connect(self.controller_camera_switch) - self.controller_publisher = GUIEventPublisher(CameraControllerSwitch, - controller_button_topic) - self.controller_subscriber = GUIEventSubscriber(CameraControllerSwitch, - controller_button_topic, - self.controller_signal) + self.controller_publisher = GUIEventPublisher( + CameraControllerSwitch, controller_button_topic + ) + self.controller_subscriber = GUIEventSubscriber( + CameraControllerSwitch, controller_button_topic, self.controller_signal + ) @pyqtSlot(CameraControllerSwitch) def controller_camera_switch(self, switch: CameraControllerSwitch) -> None: @@ -188,11 +196,14 @@ def camera_switch(self, toggle_right: bool) -> None: self.camera_subscriber.destroy_node() self.camera_subscriber = GUIEventSubscriber( - Image, self.camera_description.topic, self.handle_frame_signal) + Image, self.camera_description.topic, self.handle_frame_signal + ) self.button.setText(self.camera_description.label) # Updates text for info when no frame received. - self.video_frame_label.setText(f'This topic had no frame: {self.camera_description.topic}') + self.video_frame_label.setText( + f'This topic had no frame: {self.camera_description.topic}' + ) self.label.setText(self.camera_description.label) @@ -214,7 +225,7 @@ def __init__(self, camera_description: CameraDescription) -> None: if isinstance(layout, QVBoxLayout): layout.addWidget(self.button, alignment=Qt.AlignmentFlag.AlignCenter) else: - self.camera_subscriber.get_logger().error("Missing Layout") + self.camera_subscriber.get_logger().error('Missing Layout') self.is_paused = False diff --git a/src/surface/gui/launch/operator_launch.py b/src/surface/gui/launch/operator_launch.py index 1f07f4a3..a9d2872d 100644 --- a/src/surface/gui/launch/operator_launch.py +++ b/src/surface/gui/launch/operator_launch.py @@ -10,31 +10,26 @@ def generate_launch_description() -> LaunchDescription: package='gui', executable='run_operator', parameters=[{'theme': LaunchConfiguration('theme', default='dark')}], - remappings=[("/surface/gui/bottom_cam/image_raw", "/surface/bottom_cam/image_raw"), - ("/surface/gui/auto_control_toggle", "/surface/auto_control_toggle"), - ("/surface/gui/mavros/cmd/command", "/tether/mavros/cmd/command"), - ("/surface/gui/mavros/param/set", "/tether/mavros/param/set"), - ("/surface/gui/mavros/param/pull", "/tether/mavros/param/pull"), - ("/surface/gui/temperature", "/tether/temperature"), - ("/surface/gui/vehicle_state_event", "/surface/vehicle_state_event"), - ("/surface/gui/mavros/cmd/arming", "/tether/mavros/cmd/arming"), - ("/surface/gui/ip_address", "/tether/ip_address"), - ("/surface/gui/flooding", "/tether/flooding")], + remappings=[ + ('/surface/gui/bottom_cam/image_raw', '/surface/bottom_cam/image_raw'), + ('/surface/gui/auto_control_toggle', '/surface/auto_control_toggle'), + ('/surface/gui/mavros/cmd/command', '/tether/mavros/cmd/command'), + ('/surface/gui/mavros/param/set', '/tether/mavros/param/set'), + ('/surface/gui/mavros/param/pull', '/tether/mavros/param/pull'), + ('/surface/gui/temperature', '/tether/temperature'), + ('/surface/gui/vehicle_state_event', '/surface/vehicle_state_event'), + ('/surface/gui/mavros/cmd/arming', '/tether/mavros/cmd/arming'), + ('/surface/gui/ip_address', '/tether/ip_address'), + ('/surface/gui/flooding', '/tether/flooding'), + ], emulate_tty=True, - output='screen' + output='screen', ) - timer_node: Node = Node( - package='gui', - executable='run_timer' - ) + timer_node: Node = Node(package='gui', executable='run_timer') namespace_launch = GroupAction( - actions=[ - PushRosNamespace('gui'), - gui_node, - timer_node - ] + actions=[PushRosNamespace('gui'), gui_node, timer_node] ) return LaunchDescription([namespace_launch]) diff --git a/src/surface/gui/launch/pilot_launch.py b/src/surface/gui/launch/pilot_launch.py index d9d91078..92bc66e6 100644 --- a/src/surface/gui/launch/pilot_launch.py +++ b/src/surface/gui/launch/pilot_launch.py @@ -9,24 +9,23 @@ def generate_launch_description() -> LaunchDescription: pilot_node = Node( package='gui', executable='run_pilot', - parameters=[{'theme': LaunchConfiguration('theme', default='dark')}, - {'simulation': LaunchConfiguration('simulation', default='false')}, - {'gui': LaunchConfiguration('gui', default='pilot')}], - remappings=[("/surface/gui/mavros/cmd/arming", "/tether/mavros/cmd/arming"), - ("/surface/gui/camera_switch", "/surface/camera_switch"), - ("/surface/gui/bottom_cam/image_raw", "/surface/bottom_cam/image_raw"), - ("/surface/gui/front_cam/image_raw", "/surface/front_cam/image_raw"), - ("/surface/gui/depth_cam/image_raw", "/tether/depth_cam/image_raw"), - ("/surface/gui/vehicle_state_event", "/surface/vehicle_state_event")], + parameters=[ + {'theme': LaunchConfiguration('theme', default='dark')}, + {'simulation': LaunchConfiguration('simulation', default='false')}, + {'gui': LaunchConfiguration('gui', default='pilot')}, + ], + remappings=[ + ('/surface/gui/mavros/cmd/arming', '/tether/mavros/cmd/arming'), + ('/surface/gui/camera_switch', '/surface/camera_switch'), + ('/surface/gui/bottom_cam/image_raw', '/surface/bottom_cam/image_raw'), + ('/surface/gui/front_cam/image_raw', '/surface/front_cam/image_raw'), + ('/surface/gui/depth_cam/image_raw', '/tether/depth_cam/image_raw'), + ('/surface/gui/vehicle_state_event', '/surface/vehicle_state_event'), + ], emulate_tty=True, - output='screen' + output='screen', ) - namespace_launch = GroupAction( - actions=[ - PushRosNamespace('gui'), - pilot_node - ] - ) + namespace_launch = GroupAction(actions=[PushRosNamespace('gui'), pilot_node]) return LaunchDescription([namespace_launch]) diff --git a/src/surface/gui/setup.py b/src/surface/gui/setup.py index 8b2ffd8a..50cdf631 100644 --- a/src/surface/gui/setup.py +++ b/src/surface/gui/setup.py @@ -1,4 +1,5 @@ """setup.py for the gui module.""" + import os from glob import glob @@ -9,26 +10,27 @@ setup( name=PACKAGE_NAME, version='1.2.0', - packages=[PACKAGE_NAME, os.path.join(PACKAGE_NAME, 'widgets'), - os.path.join(PACKAGE_NAME, 'styles'), - os.path.join(PACKAGE_NAME, 'gui_nodes', 'auxiliary_nodes'), - os.path.join(PACKAGE_NAME, 'gui_nodes', 'event_nodes')], + packages=[ + PACKAGE_NAME, + os.path.join(PACKAGE_NAME, 'widgets'), + os.path.join(PACKAGE_NAME, 'styles'), + os.path.join(PACKAGE_NAME, 'gui_nodes', 'auxiliary_nodes'), + os.path.join(PACKAGE_NAME, 'gui_nodes', 'event_nodes'), + ], data_files=[ - ('share/ament_index/resource_index/packages', - ['resource/' + PACKAGE_NAME]), + ('share/ament_index/resource_index/packages', ['resource/' + PACKAGE_NAME]), ('share/' + PACKAGE_NAME, ['package.xml']), # Include all launch files. - (os.path.join('share', PACKAGE_NAME, 'launch'), - glob('launch/*launch.[pxy][yma]*')), + ( + os.path.join('share', PACKAGE_NAME, 'launch'), + glob('launch/*launch.[pxy][yma]*'), + ), # Include all style files. - (os.path.join('share', PACKAGE_NAME, 'styles'), - glob('gui/styles/*.qss')), + (os.path.join('share', PACKAGE_NAME, 'styles'), glob('gui/styles/*.qss')), # Include all images. - (os.path.join('share', PACKAGE_NAME, 'images'), - glob('gui/images/*')), + (os.path.join('share', PACKAGE_NAME, 'images'), glob('gui/images/*')), # Include all sounds. - (os.path.join('share', PACKAGE_NAME, 'sounds'), - glob('gui/sounds/*')), + (os.path.join('share', PACKAGE_NAME, 'sounds'), glob('gui/sounds/*')), ], install_requires=['setuptools'], zip_safe=True, @@ -38,8 +40,10 @@ license='Apache License 2.0', tests_require=['pytest', 'pytest-qt', 'pytest-xvfb'], entry_points={ - 'console_scripts': ['run_pilot = gui.pilot_app:run_gui_pilot', - 'run_operator = gui.operator_app:run_gui_operator', - 'run_timer = gui.gui_nodes.auxiliary_nodes.timer:run_timer'], + 'console_scripts': [ + 'run_pilot = gui.pilot_app:run_gui_pilot', + 'run_operator = gui.operator_app:run_gui_operator', + 'run_timer = gui.gui_nodes.auxiliary_nodes.timer:run_timer', + ], }, ) diff --git a/src/surface/gui/test/test_app.py b/src/surface/gui/test/test_app.py index 863ef972..9104e6a4 100644 --- a/src/surface/gui/test/test_app.py +++ b/src/surface/gui/test/test_app.py @@ -6,6 +6,6 @@ @pytest.fixture def test_app_instantiation(qtbot: QtBot) -> None: """Unit test for App instantiation.""" - app = App("test") + app = App('test') app.show() qtbot.addWiget(app) diff --git a/src/surface/gui/test/test_flake8.py b/src/surface/gui/test/test_flake8.py index 8ae474ef..6ffa8264 100644 --- a/src/surface/gui/test/test_flake8.py +++ b/src/surface/gui/test/test_flake8.py @@ -13,8 +13,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -from ament_flake8.main import main_with_errors import pytest +from ament_flake8.main import main_with_errors @pytest.mark.flake8 @@ -22,6 +22,6 @@ def test_flake8() -> None: """Tests flake8 on this module.""" error_code, errors = main_with_errors(argv=[]) - assert error_code == 0, \ - f'Found {len(errors)} code style errors / warnings:\n' + \ - '\n'.join(errors) + assert error_code == 0, ( + f'Found {len(errors)} code style errors / warnings:\n' + '\n'.join(errors) + ) diff --git a/src/surface/gui/test/test_mypy.py b/src/surface/gui/test/test_mypy.py index c2e823a6..f43986b1 100644 --- a/src/surface/gui/test/test_mypy.py +++ b/src/surface/gui/test/test_mypy.py @@ -1,6 +1,7 @@ """Test mypy on this module.""" import os + import pytest from ament_mypy.main import main @@ -9,6 +10,6 @@ @pytest.mark.linter def test_mypy() -> None: """Tests mypy on this module.""" - path = os.path.join(os.getcwd(), "..", "..", "..", "pyproject.toml") - error_code = main(argv=["--config", path]) + path = os.path.join(os.getcwd(), '..', '..', '..', 'pyproject.toml') + error_code = main(argv=['--config', path]) assert error_code == 0, 'Found code style errors / warnings' diff --git a/src/surface/gui/test/test_pep257.py b/src/surface/gui/test/test_pep257.py index 62bed98c..67305010 100644 --- a/src/surface/gui/test/test_pep257.py +++ b/src/surface/gui/test/test_pep257.py @@ -23,6 +23,6 @@ @pytest.mark.pep257 def test_pep257() -> None: """Tests pep257 on this module.""" - path = os.path.join(os.getcwd(), "..", "..", "..", "pyproject.toml") + path = os.path.join(os.getcwd(), '..', '..', '..', 'pyproject.toml') error_code = main(argv=['.', 'test', '--config', path]) assert error_code == 0, 'Found code style errors / warnings' diff --git a/src/surface/ps5_controller/launch/controller_launch.py b/src/surface/ps5_controller/launch/controller_launch.py index 66468239..1a9abd61 100644 --- a/src/surface/ps5_controller/launch/controller_launch.py +++ b/src/surface/ps5_controller/launch/controller_launch.py @@ -3,15 +3,13 @@ def generate_launch_description() -> LaunchDescription: - # launches node to capture joystick data controller_node = Node( - package='joy', - executable='joy_node', - emulate_tty=True, - output='screen' + package='joy', executable='joy_node', emulate_tty=True, output='screen' ) - return LaunchDescription([ - controller_node, - ]) + return LaunchDescription( + [ + controller_node, + ] + ) diff --git a/src/surface/ps5_controller/setup.py b/src/surface/ps5_controller/setup.py index b7b1c22a..9d0b59a8 100644 --- a/src/surface/ps5_controller/setup.py +++ b/src/surface/ps5_controller/setup.py @@ -1,4 +1,5 @@ """setup.py for the ps5_controller module.""" + import os from glob import glob @@ -11,12 +12,13 @@ version='1.2.0', packages=[PACKAGE_NAME], data_files=[ - ('share/ament_index/resource_index/packages', - ['resource/' + PACKAGE_NAME]), + ('share/ament_index/resource_index/packages', ['resource/' + PACKAGE_NAME]), ('share/' + PACKAGE_NAME, ['package.xml']), # Include all launch files. - (os.path.join('share', PACKAGE_NAME, 'launch'), - glob('launch/*launch.[pxy][yma]*')) + ( + os.path.join('share', PACKAGE_NAME, 'launch'), + glob('launch/*launch.[pxy][yma]*'), + ), ], install_requires=['setuptools'], zip_safe=True, diff --git a/src/surface/ps5_controller/test/test_flake8.py b/src/surface/ps5_controller/test/test_flake8.py index 8ae474ef..6ffa8264 100644 --- a/src/surface/ps5_controller/test/test_flake8.py +++ b/src/surface/ps5_controller/test/test_flake8.py @@ -13,8 +13,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -from ament_flake8.main import main_with_errors import pytest +from ament_flake8.main import main_with_errors @pytest.mark.flake8 @@ -22,6 +22,6 @@ def test_flake8() -> None: """Tests flake8 on this module.""" error_code, errors = main_with_errors(argv=[]) - assert error_code == 0, \ - f'Found {len(errors)} code style errors / warnings:\n' + \ - '\n'.join(errors) + assert error_code == 0, ( + f'Found {len(errors)} code style errors / warnings:\n' + '\n'.join(errors) + ) diff --git a/src/surface/ps5_controller/test/test_mypy.py b/src/surface/ps5_controller/test/test_mypy.py index c2e823a6..f43986b1 100644 --- a/src/surface/ps5_controller/test/test_mypy.py +++ b/src/surface/ps5_controller/test/test_mypy.py @@ -1,6 +1,7 @@ """Test mypy on this module.""" import os + import pytest from ament_mypy.main import main @@ -9,6 +10,6 @@ @pytest.mark.linter def test_mypy() -> None: """Tests mypy on this module.""" - path = os.path.join(os.getcwd(), "..", "..", "..", "pyproject.toml") - error_code = main(argv=["--config", path]) + path = os.path.join(os.getcwd(), '..', '..', '..', 'pyproject.toml') + error_code = main(argv=['--config', path]) assert error_code == 0, 'Found code style errors / warnings' diff --git a/src/surface/ps5_controller/test/test_pep257.py b/src/surface/ps5_controller/test/test_pep257.py index 62bed98c..67305010 100644 --- a/src/surface/ps5_controller/test/test_pep257.py +++ b/src/surface/ps5_controller/test/test_pep257.py @@ -23,6 +23,6 @@ @pytest.mark.pep257 def test_pep257() -> None: """Tests pep257 on this module.""" - path = os.path.join(os.getcwd(), "..", "..", "..", "pyproject.toml") + path = os.path.join(os.getcwd(), '..', '..', '..', 'pyproject.toml') error_code = main(argv=['.', 'test', '--config', path]) assert error_code == 0, 'Found code style errors / warnings' diff --git a/src/surface/rov_flir/launch/flir_launch.py b/src/surface/rov_flir/launch/flir_launch.py index bbd612aa..b69fbc0d 100644 --- a/src/surface/rov_flir/launch/flir_launch.py +++ b/src/surface/rov_flir/launch/flir_launch.py @@ -1,12 +1,12 @@ import os from ament_index_python.packages import get_package_share_directory +from launch.actions import GroupAction +from launch.conditions import IfCondition from launch.launch_description import LaunchDescription +from launch.substitutions.launch_configuration import LaunchConfiguration from launch_ros.actions import Node, PushRosNamespace from launch_ros.parameter_descriptions import Parameter -from launch.substitutions.launch_configuration import LaunchConfiguration -from launch.conditions import IfCondition -from launch.actions import GroupAction def generate_launch_description() -> LaunchDescription: @@ -14,8 +14,9 @@ def generate_launch_description() -> LaunchDescription: bottom_arg = LaunchConfiguration('launch_bottom', default=True) ns_arg = LaunchConfiguration('ns', default='') - parameter_file = os.path.join(get_package_share_directory('rov_flir'), 'config', - 'blackfly_s.yaml') + parameter_file = os.path.join( + get_package_share_directory('rov_flir'), 'config', 'blackfly_s.yaml' + ) parameters = { 'debug': False, @@ -51,7 +52,7 @@ def generate_launch_description() -> LaunchDescription: 'chunk_selector_gain': 'Gain', 'chunk_enable_gain': True, 'chunk_selector_timestamp': 'Timestamp', - 'chunk_enable_timestamp': True + 'chunk_enable_timestamp': True, } # launches node to run front flir camera @@ -62,10 +63,12 @@ def generate_launch_description() -> LaunchDescription: exec_name='front_cam', emulate_tty=True, output='screen', - parameters=[Parameter('serial_number', '23473566'), - Parameter('parameter_file', parameter_file), - parameters], - condition=IfCondition(front_arg) + parameters=[ + Parameter('serial_number', '23473566'), + Parameter('parameter_file', parameter_file), + parameters, + ], + condition=IfCondition(front_arg), ) # launches node to run bottom flir camera @@ -76,18 +79,16 @@ def generate_launch_description() -> LaunchDescription: exec_name='bottom_cam', emulate_tty=True, output='screen', - parameters=[Parameter('serial_number', '23473577'), - Parameter('parameter_file', parameter_file), - parameters], - condition=IfCondition(bottom_arg) + parameters=[ + Parameter('serial_number', '23473577'), + Parameter('parameter_file', parameter_file), + parameters, + ], + condition=IfCondition(bottom_arg), ) namespace_launch = GroupAction( - actions=[ - PushRosNamespace(ns_arg), - front_cam, - bottom_cam - ] + actions=[PushRosNamespace(ns_arg), front_cam, bottom_cam] ) return LaunchDescription([namespace_launch]) diff --git a/src/surface/rov_flir/rov_flir/flir_watchdog.py b/src/surface/rov_flir/rov_flir/flir_watchdog.py index da33babd..757f36d0 100644 --- a/src/surface/rov_flir/rov_flir/flir_watchdog.py +++ b/src/surface/rov_flir/rov_flir/flir_watchdog.py @@ -1,20 +1,20 @@ import atexit import os -import subprocess -from subprocess import Popen import re +import subprocess from signal import SIGINT +from subprocess import Popen import rclpy from rclpy.executors import MultiThreadedExecutor from rclpy.node import Node WATCHDOG_RATE = 10 -NAMESPACE = "surface" +NAMESPACE = 'surface' MIN_FPS = 1.0 -class Watchdog(): +class Watchdog: def __init__(self, name: str, node: Node, args: list[str]) -> None: self.name = name self.node = node @@ -24,28 +24,30 @@ def __init__(self, name: str, node: Node, args: list[str]) -> None: self.start_process() def start_process(self) -> None: - self.process = Popen(self.args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + self.process = Popen( + self.args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT + ) if self.process.stdout is None: - raise RuntimeError("Child process has not stdout") + raise RuntimeError('Child process has not stdout') os.set_blocking(self.process.stdout.fileno(), False) def read_stdout(self) -> bytes: if self.process.stdout is None: - raise RuntimeError("Child process has not stdout") + raise RuntimeError('Child process has not stdout') return self.process.stdout.readline() def poll(self) -> None: if self.process.poll() is not None: - self.node.get_logger().warning(f"{self.name} has crashed, restarting...") + self.node.get_logger().warning(f'{self.name} has crashed, restarting...') self.start_process() return line = self.read_stdout() while line: - match = re.search(r"rate \[Hz] in +([\d\.]+) out", line.decode().strip()) + match = re.search(r'rate \[Hz] in +([\d\.]+) out', line.decode().strip()) if match: try: rate = float(match.group(1)) @@ -54,7 +56,7 @@ def poll(self) -> None: if rate < MIN_FPS: # If we're receiving less than 1 fps, assume the camera has disconnected - self.node.get_logger().warning(f"{self.name} frozen, killing...") + self.node.get_logger().warning(f'{self.name} frozen, killing...') self.process.send_signal(SIGINT) return @@ -66,23 +68,35 @@ def kill(self) -> None: class FlirWatchdogNode(Node): def __init__(self) -> None: - super().__init__("flir_watchdog_node") + super().__init__('flir_watchdog_node') self.timer = self.create_timer(1 / WATCHDOG_RATE, self.timer_callback) - self.get_logger().info("Starting camera drivers") + self.get_logger().info('Starting camera drivers') self.front_watchdog = Watchdog( - name="Front cam", + name='Front cam', node=self, - args=["ros2", "launch", "rov_flir", "flir_launch.py", - "launch_bottom:=false", f"ns:={NAMESPACE}"] + args=[ + 'ros2', + 'launch', + 'rov_flir', + 'flir_launch.py', + 'launch_bottom:=false', + f'ns:={NAMESPACE}', + ], ) self.bottom_watchdog = Watchdog( - name="Bottom cam", + name='Bottom cam', node=self, - args=["ros2", "launch", "rov_flir", "flir_launch.py", - "launch_front:=false", f"ns:={NAMESPACE}"] + args=[ + 'ros2', + 'launch', + 'rov_flir', + 'flir_launch.py', + 'launch_front:=false', + f'ns:={NAMESPACE}', + ], ) atexit.register(self.front_watchdog.kill) @@ -100,5 +114,5 @@ def main() -> None: rclpy.spin(flir_watchdog_node, executor=executor) -if __name__ == "__main__": +if __name__ == '__main__': main() diff --git a/src/surface/rov_flir/setup.py b/src/surface/rov_flir/setup.py index 4009075c..2ffd530b 100644 --- a/src/surface/rov_flir/setup.py +++ b/src/surface/rov_flir/setup.py @@ -10,14 +10,14 @@ version='1.2.0', packages=[PACKAGE_NAME], data_files=[ - ('share/ament_index/resource_index/packages', - ['resource/' + PACKAGE_NAME]), + ('share/ament_index/resource_index/packages', ['resource/' + PACKAGE_NAME]), ('share/' + PACKAGE_NAME, ['package.xml']), # Include all launch files. - (os.path.join('share', PACKAGE_NAME, 'launch'), - glob('launch/*launch.[pxy][yma]*')), - (os.path.join('share', PACKAGE_NAME, 'config'), - glob('config/*')) + ( + os.path.join('share', PACKAGE_NAME, 'launch'), + glob('launch/*launch.[pxy][yma]*'), + ), + (os.path.join('share', PACKAGE_NAME, 'config'), glob('config/*')), ], install_requires=['setuptools'], zip_safe=True, diff --git a/src/surface/rov_flir/test/test_flake8.py b/src/surface/rov_flir/test/test_flake8.py index eac16eef..4d880687 100644 --- a/src/surface/rov_flir/test/test_flake8.py +++ b/src/surface/rov_flir/test/test_flake8.py @@ -12,14 +12,14 @@ # See the License for the specific language governing permissions and # limitations under the License. -from ament_flake8.main import main_with_errors import pytest +from ament_flake8.main import main_with_errors @pytest.mark.flake8 @pytest.mark.linter def test_flake8() -> None: rc, errors = main_with_errors(argv=[]) - assert rc == 0, \ - 'Found %d code style errors / warnings:\n' % len(errors) + \ - '\n'.join(errors) + assert rc == 0, 'Found %d code style errors / warnings:\n' % len( + errors + ) + '\n'.join(errors) diff --git a/src/surface/rov_flir/test/test_mypy.py b/src/surface/rov_flir/test/test_mypy.py index c2e823a6..f43986b1 100644 --- a/src/surface/rov_flir/test/test_mypy.py +++ b/src/surface/rov_flir/test/test_mypy.py @@ -1,6 +1,7 @@ """Test mypy on this module.""" import os + import pytest from ament_mypy.main import main @@ -9,6 +10,6 @@ @pytest.mark.linter def test_mypy() -> None: """Tests mypy on this module.""" - path = os.path.join(os.getcwd(), "..", "..", "..", "pyproject.toml") - error_code = main(argv=["--config", path]) + path = os.path.join(os.getcwd(), '..', '..', '..', 'pyproject.toml') + error_code = main(argv=['--config', path]) assert error_code == 0, 'Found code style errors / warnings' diff --git a/src/surface/rov_flir/test/test_pep257.py b/src/surface/rov_flir/test/test_pep257.py index 62bed98c..67305010 100644 --- a/src/surface/rov_flir/test/test_pep257.py +++ b/src/surface/rov_flir/test/test_pep257.py @@ -23,6 +23,6 @@ @pytest.mark.pep257 def test_pep257() -> None: """Tests pep257 on this module.""" - path = os.path.join(os.getcwd(), "..", "..", "..", "pyproject.toml") + path = os.path.join(os.getcwd(), '..', '..', '..', 'pyproject.toml') error_code = main(argv=['.', 'test', '--config', path]) assert error_code == 0, 'Found code style errors / warnings' diff --git a/src/surface/rov_gazebo/launch/sim_launch.py b/src/surface/rov_gazebo/launch/sim_launch.py index 9fbd152f..a67dcf0c 100644 --- a/src/surface/rov_gazebo/launch/sim_launch.py +++ b/src/surface/rov_gazebo/launch/sim_launch.py @@ -1,33 +1,42 @@ import os from ament_index_python.packages import get_package_share_directory +from launch.actions import (ExecuteProcess, GroupAction, + IncludeLaunchDescription) from launch.launch_description import LaunchDescription -from launch.actions import ExecuteProcess, GroupAction, IncludeLaunchDescription from launch.launch_description_sources import PythonLaunchDescriptionSource from launch_ros.actions import Node, PushRosNamespace -NAMESPACE = "simulation" +NAMESPACE = 'simulation' def generate_launch_description() -> LaunchDescription: - rov_gazebo_path: str = get_package_share_directory("rov_gazebo") - surface_main_path: str = get_package_share_directory("surface_main") + rov_gazebo_path: str = get_package_share_directory('rov_gazebo') + surface_main_path: str = get_package_share_directory('surface_main') - world_file = "rov24_coral.sdf" - world_path: str = os.path.join(rov_gazebo_path, "worlds", world_file) + world_file = 'rov24_coral.sdf' + world_path: str = os.path.join(rov_gazebo_path, 'worlds', world_file) - params_file = "sub.parm" - params_path: str = os.path.join(rov_gazebo_path, "config", params_file) + params_file = 'sub.parm' + params_path: str = os.path.join(rov_gazebo_path, 'config', params_file) # TODO gz_sim launch might be nice start_gazebo = ExecuteProcess( - cmd=['ign', 'gazebo', '-v', '3', '-r', world_path], - output='screen' + cmd=['ign', 'gazebo', '-v', '3', '-r', world_path], output='screen' ) start_ardusub = ExecuteProcess( - cmd=["ardusub", '-S', '-w', '-M', 'JSON', '--defaults', params_path, '-I0', ], - output='screen' + cmd=[ + 'ardusub', + '-S', + '-w', + '-M', + 'JSON', + '--defaults', + params_path, + '-I0', + ], + output='screen', ) # Translate messages MAV <-> ROS @@ -37,10 +46,10 @@ def generate_launch_description() -> LaunchDescription: output='screen', namespace='mavros', parameters=[ - {"system_id": 255}, - {"fcu_url": "tcp://localhost"}, - {"gcs_url": "udp://@localhost:14550"}, - {"plugin_allowlist": ["rc_io", "sys_status", "command"]} + {'system_id': 255}, + {'fcu_url': 'tcp://localhost'}, + {'gcs_url': 'udp://@localhost:14550'}, + {'plugin_allowlist': ['rc_io', 'sys_status', 'command']}, ], remappings=[ (f'/{NAMESPACE}/mavros/state', '/tether/mavros/state'), @@ -48,55 +57,54 @@ def generate_launch_description() -> LaunchDescription: (f'/{NAMESPACE}/mavros/cmd/arming', '/tether/mavros/cmd/arming'), (f'/{NAMESPACE}/mavros/cmd/command', '/tether/mavros/cmd/command'), ], - emulate_tty=True + emulate_tty=True, ) cam_bridge_node = Node( - package="ros_gz_image", - executable="image_bridge", - name="image_bridge", - arguments=["front_cam", "bottom_cam"], - output="screen", + package='ros_gz_image', + executable='image_bridge', + name='image_bridge', + arguments=['front_cam', 'bottom_cam'], + output='screen', remappings=[ - (f"/{NAMESPACE}/front_cam", "/surface/front_cam/image_raw"), - (f"/{NAMESPACE}/bottom_cam", "/surface/bottom_cam/image_raw"), + (f'/{NAMESPACE}/front_cam', '/surface/front_cam/image_raw'), + (f'/{NAMESPACE}/bottom_cam', '/surface/bottom_cam/image_raw'), ], ) keyboard_control_node = Node( - package="flight_control", - executable="keyboard_control_node", - namespace="surface", - output="screen", - name="keyboard_control_node", - emulate_tty=True + package='flight_control', + executable='keyboard_control_node', + namespace='surface', + output='screen', + name='keyboard_control_node', + emulate_tty=True, ) # Launches the pi heartbeat node heartbeat_node = Node( - package="pi_info", - executable="heartbeat_node", - remappings=[(f"/{NAMESPACE}/pi_heartbeat", "/tether/pi_heartbeat")], + package='pi_info', + executable='heartbeat_node', + remappings=[(f'/{NAMESPACE}/pi_heartbeat', '/tether/pi_heartbeat')], emulate_tty=True, - output="screen" + output='screen', ) # Launches the ip address node ip_node = Node( - package="pi_info", - executable="ip_publisher", - remappings=[(f"/{NAMESPACE}/ip_address", "/tether/ip_address")], + package='pi_info', + executable='ip_publisher', + remappings=[(f'/{NAMESPACE}/ip_address', '/tether/ip_address')], emulate_tty=True, - output="screen" + output='screen', ) # Launches Surface Nodes surface_launch = IncludeLaunchDescription( PythonLaunchDescriptionSource( - [os.path.join(surface_main_path, "launch", "surface_all_nodes_launch.py")] + [os.path.join(surface_main_path, 'launch', 'surface_all_nodes_launch.py')] ), - launch_arguments=[('simulation', 'true'), - ('gui', 'debug')] + launch_arguments=[('simulation', 'true'), ('gui', 'debug')], ) namespace_launch = GroupAction( @@ -105,7 +113,7 @@ def generate_launch_description() -> LaunchDescription: mav_ros_node, heartbeat_node, cam_bridge_node, - ip_node + ip_node, ] ) diff --git a/src/surface/rov_gazebo/setup.py b/src/surface/rov_gazebo/setup.py index 75e45060..64c2f003 100644 --- a/src/surface/rov_gazebo/setup.py +++ b/src/surface/rov_gazebo/setup.py @@ -1,32 +1,33 @@ """setup.py for the rov_gazebo module.""" + import os from glob import glob from setuptools import setup -PACKAGE_NAME = "rov_gazebo" +PACKAGE_NAME = 'rov_gazebo' setup( name=PACKAGE_NAME, - version="1.2.0", + version='1.2.0', packages=[PACKAGE_NAME], data_files=[ - ("share/ament_index/resource_index/packages", ["resource/" + PACKAGE_NAME]), - ("share/" + PACKAGE_NAME, ["package.xml"]), - (os.path.join("share", PACKAGE_NAME, "launch"), glob("launch/*.py")), - (os.path.join("share", PACKAGE_NAME, "description"), glob("description/*")), - (os.path.join("share", PACKAGE_NAME, "config"), glob("config/*")), - (os.path.join("share", PACKAGE_NAME, "worlds"), glob("worlds/*")), - (os.path.join("share", PACKAGE_NAME, "meshes"), glob("meshes/*")), + ('share/ament_index/resource_index/packages', ['resource/' + PACKAGE_NAME]), + ('share/' + PACKAGE_NAME, ['package.xml']), + (os.path.join('share', PACKAGE_NAME, 'launch'), glob('launch/*.py')), + (os.path.join('share', PACKAGE_NAME, 'description'), glob('description/*')), + (os.path.join('share', PACKAGE_NAME, 'config'), glob('config/*')), + (os.path.join('share', PACKAGE_NAME, 'worlds'), glob('worlds/*')), + (os.path.join('share', PACKAGE_NAME, 'meshes'), glob('meshes/*')), ], - install_requires=["setuptools"], + install_requires=['setuptools'], zip_safe=True, - maintainer="Seongmin Jung", - maintainer_email="sxj754@case.edu", - description="MATE ROV simulation", - license="Apache License 2.0", - tests_require=["pytest"], + maintainer='Seongmin Jung', + maintainer_email='sxj754@case.edu', + description='MATE ROV simulation', + license='Apache License 2.0', + tests_require=['pytest'], entry_points={ - "console_scripts": [], + 'console_scripts': [], }, ) diff --git a/src/surface/rov_gazebo/test/test_flake8.py b/src/surface/rov_gazebo/test/test_flake8.py index 8ae474ef..6ffa8264 100644 --- a/src/surface/rov_gazebo/test/test_flake8.py +++ b/src/surface/rov_gazebo/test/test_flake8.py @@ -13,8 +13,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -from ament_flake8.main import main_with_errors import pytest +from ament_flake8.main import main_with_errors @pytest.mark.flake8 @@ -22,6 +22,6 @@ def test_flake8() -> None: """Tests flake8 on this module.""" error_code, errors = main_with_errors(argv=[]) - assert error_code == 0, \ - f'Found {len(errors)} code style errors / warnings:\n' + \ - '\n'.join(errors) + assert error_code == 0, ( + f'Found {len(errors)} code style errors / warnings:\n' + '\n'.join(errors) + ) diff --git a/src/surface/rov_gazebo/test/test_mypy.py b/src/surface/rov_gazebo/test/test_mypy.py index c2e823a6..f43986b1 100644 --- a/src/surface/rov_gazebo/test/test_mypy.py +++ b/src/surface/rov_gazebo/test/test_mypy.py @@ -1,6 +1,7 @@ """Test mypy on this module.""" import os + import pytest from ament_mypy.main import main @@ -9,6 +10,6 @@ @pytest.mark.linter def test_mypy() -> None: """Tests mypy on this module.""" - path = os.path.join(os.getcwd(), "..", "..", "..", "pyproject.toml") - error_code = main(argv=["--config", path]) + path = os.path.join(os.getcwd(), '..', '..', '..', 'pyproject.toml') + error_code = main(argv=['--config', path]) assert error_code == 0, 'Found code style errors / warnings' diff --git a/src/surface/rov_gazebo/test/test_pep257.py b/src/surface/rov_gazebo/test/test_pep257.py index 62bed98c..67305010 100644 --- a/src/surface/rov_gazebo/test/test_pep257.py +++ b/src/surface/rov_gazebo/test/test_pep257.py @@ -23,6 +23,6 @@ @pytest.mark.pep257 def test_pep257() -> None: """Tests pep257 on this module.""" - path = os.path.join(os.getcwd(), "..", "..", "..", "pyproject.toml") + path = os.path.join(os.getcwd(), '..', '..', '..', 'pyproject.toml') error_code = main(argv=['.', 'test', '--config', path]) assert error_code == 0, 'Found code style errors / warnings' diff --git a/src/surface/surface_main/launch/competition_launch.py b/src/surface/surface_main/launch/competition_launch.py index 282765c5..5c2bd542 100644 --- a/src/surface/surface_main/launch/competition_launch.py +++ b/src/surface/surface_main/launch/competition_launch.py @@ -1,24 +1,23 @@ import os from ament_index_python.packages import get_package_share_directory -from launch.launch_description import LaunchDescription from launch.actions import IncludeLaunchDescription +from launch.launch_description import LaunchDescription from launch.launch_description_sources import PythonLaunchDescriptionSource def generate_launch_description() -> LaunchDescription: - surface_path = get_package_share_directory('surface_main') all_launch = IncludeLaunchDescription( - PythonLaunchDescriptionSource([ - os.path.join( - surface_path, 'launch', 'surface_all_nodes_launch.py' - ) - ]), - launch_arguments=[('gui', 'livestream')] + PythonLaunchDescriptionSource( + [os.path.join(surface_path, 'launch', 'surface_all_nodes_launch.py')] + ), + launch_arguments=[('gui', 'livestream')], ) - return LaunchDescription([ - all_launch, - ]) + return LaunchDescription( + [ + all_launch, + ] + ) diff --git a/src/surface/surface_main/launch/debug_surface_all_nodes_launch.py b/src/surface/surface_main/launch/debug_surface_all_nodes_launch.py index 9e2650cc..9c809248 100644 --- a/src/surface/surface_main/launch/debug_surface_all_nodes_launch.py +++ b/src/surface/surface_main/launch/debug_surface_all_nodes_launch.py @@ -1,24 +1,23 @@ import os from ament_index_python.packages import get_package_share_directory -from launch.launch_description import LaunchDescription from launch.actions import IncludeLaunchDescription +from launch.launch_description import LaunchDescription from launch.launch_description_sources import PythonLaunchDescriptionSource def generate_launch_description() -> LaunchDescription: - surface_path: str = get_package_share_directory('surface_main') all_launch = IncludeLaunchDescription( - PythonLaunchDescriptionSource([ - os.path.join( - surface_path, 'launch', 'surface_all_nodes_launch.py' - ) - ]), - launch_arguments=[('gui', 'debug')] + PythonLaunchDescriptionSource( + [os.path.join(surface_path, 'launch', 'surface_all_nodes_launch.py')] + ), + launch_arguments=[('gui', 'debug')], ) - return LaunchDescription([ - all_launch, - ]) + return LaunchDescription( + [ + all_launch, + ] + ) diff --git a/src/surface/surface_main/launch/surface_all_nodes_launch.py b/src/surface/surface_main/launch/surface_all_nodes_launch.py index d2b5514e..ec5b4685 100644 --- a/src/surface/surface_main/launch/surface_all_nodes_launch.py +++ b/src/surface/surface_main/launch/surface_all_nodes_launch.py @@ -1,32 +1,29 @@ import os from ament_index_python.packages import get_package_share_directory -from launch.launch_description import LaunchDescription from launch.actions import IncludeLaunchDescription +from launch.launch_description import LaunchDescription from launch.launch_description_sources import PythonLaunchDescriptionSource def generate_launch_description() -> LaunchDescription: - surface_path: str = get_package_share_directory('surface_main') pilot_launch = IncludeLaunchDescription( - PythonLaunchDescriptionSource([ - os.path.join( - surface_path, 'launch', 'surface_pilot_launch.py' - ) - ]), + PythonLaunchDescriptionSource( + [os.path.join(surface_path, 'launch', 'surface_pilot_launch.py')] + ), ) operator_launch = IncludeLaunchDescription( - PythonLaunchDescriptionSource([ - os.path.join( - surface_path, 'launch', 'surface_operator_launch.py' - ) - ]), + PythonLaunchDescriptionSource( + [os.path.join(surface_path, 'launch', 'surface_operator_launch.py')] + ), ) - return LaunchDescription([ - pilot_launch, - operator_launch, - ]) + return LaunchDescription( + [ + pilot_launch, + operator_launch, + ] + ) diff --git a/src/surface/surface_main/launch/surface_operator_launch.py b/src/surface/surface_main/launch/surface_operator_launch.py index 9bcaef29..be08cda8 100644 --- a/src/surface/surface_main/launch/surface_operator_launch.py +++ b/src/surface/surface_main/launch/surface_operator_launch.py @@ -1,14 +1,13 @@ import os from ament_index_python.packages import get_package_share_directory -from launch.launch_description import LaunchDescription from launch.actions import GroupAction, IncludeLaunchDescription +from launch.launch_description import LaunchDescription from launch.launch_description_sources import PythonLaunchDescriptionSource from launch_ros.actions import PushRosNamespace def generate_launch_description() -> LaunchDescription: - gui_path: str = get_package_share_directory('gui') flight_control_path: str = get_package_share_directory('flight_control') vehicle_manager_path: str = get_package_share_directory('vehicle_manager') @@ -16,50 +15,40 @@ def generate_launch_description() -> LaunchDescription: # Launches Gui gui_launch = IncludeLaunchDescription( - PythonLaunchDescriptionSource([ - os.path.join( - gui_path, 'launch', 'operator_launch.py' - ) - ]), + PythonLaunchDescriptionSource( + [os.path.join(gui_path, 'launch', 'operator_launch.py')] + ), ) # Launches flight_control (auto docking, manual control, etc.) flight_control_launch = IncludeLaunchDescription( - PythonLaunchDescriptionSource([ - os.path.join( - flight_control_path, 'launch', 'flight_control_launch.py' - ) - ]), + PythonLaunchDescriptionSource( + [os.path.join(flight_control_path, 'launch', 'flight_control_launch.py')] + ), ) # Launches Vehicle Manager vehicle_manager_launch = IncludeLaunchDescription( - PythonLaunchDescriptionSource([ - os.path.join( - vehicle_manager_path, 'launch', 'vehicle_manager_launch.py' - ) - ]), + PythonLaunchDescriptionSource( + [os.path.join(vehicle_manager_path, 'launch', 'vehicle_manager_launch.py')] + ), ) # Launches Transceiver transceiver_launch = IncludeLaunchDescription( - PythonLaunchDescriptionSource([ - os.path.join( - transceiver_path, 'launch', 'serial_reader_launch.py' - ) - ]), + PythonLaunchDescriptionSource( + [os.path.join(transceiver_path, 'launch', 'serial_reader_launch.py')] + ), ) namespace_launch = GroupAction( actions=[ - PushRosNamespace("surface"), + PushRosNamespace('surface'), gui_launch, flight_control_launch, vehicle_manager_launch, - transceiver_launch + transceiver_launch, ] ) - return LaunchDescription([ - namespace_launch - ]) + return LaunchDescription([namespace_launch]) diff --git a/src/surface/surface_main/launch/surface_pilot_launch.py b/src/surface/surface_main/launch/surface_pilot_launch.py index c7f03f5a..579bd5ca 100644 --- a/src/surface/surface_main/launch/surface_pilot_launch.py +++ b/src/surface/surface_main/launch/surface_pilot_launch.py @@ -1,16 +1,15 @@ import os from ament_index_python.packages import get_package_share_directory -from launch.launch_description import LaunchDescription from launch.actions import GroupAction, IncludeLaunchDescription -from launch.launch_description_sources import PythonLaunchDescriptionSource -from launch_ros.actions import PushRosNamespace, Node from launch.conditions import UnlessCondition +from launch.launch_description import LaunchDescription +from launch.launch_description_sources import PythonLaunchDescriptionSource from launch.substitutions import LaunchConfiguration +from launch_ros.actions import Node, PushRosNamespace def generate_launch_description() -> LaunchDescription: - gui_path: str = get_package_share_directory('gui') controller_path: str = get_package_share_directory('ps5_controller') # flir_path: str = get_package_share_directory('rov_flir') @@ -19,20 +18,16 @@ def generate_launch_description() -> LaunchDescription: # Launches Gui gui_launch = IncludeLaunchDescription( - PythonLaunchDescriptionSource([ - os.path.join( - gui_path, 'launch', 'pilot_launch.py' - ) - ]), + PythonLaunchDescriptionSource( + [os.path.join(gui_path, 'launch', 'pilot_launch.py')] + ), ) # Launches Controller controller_launch = IncludeLaunchDescription( - PythonLaunchDescriptionSource([ - os.path.join( - controller_path, 'launch', 'controller_launch.py' - ) - ]), + PythonLaunchDescriptionSource( + [os.path.join(controller_path, 'launch', 'controller_launch.py')] + ), ) # Launches flir @@ -51,18 +46,16 @@ def generate_launch_description() -> LaunchDescription: name='flir_watchdog', emulate_tty=True, output='screen', - condition=UnlessCondition(simulation_configuration) + condition=UnlessCondition(simulation_configuration), ) namespace_launch = GroupAction( actions=[ - PushRosNamespace("surface"), + PushRosNamespace('surface'), gui_launch, controller_launch, - flir_watchdog + flir_watchdog, ] ) - return LaunchDescription([ - namespace_launch - ]) + return LaunchDescription([namespace_launch]) diff --git a/src/surface/surface_main/setup.py b/src/surface/surface_main/setup.py index 5816ae2f..054b9b74 100644 --- a/src/surface/surface_main/setup.py +++ b/src/surface/surface_main/setup.py @@ -1,4 +1,5 @@ """setup.py for the surface_main module.""" + import os from glob import glob @@ -11,12 +12,13 @@ version='1.2.0', packages=[PACKAGE_NAME], data_files=[ - ('share/ament_index/resource_index/packages', - ['resource/' + PACKAGE_NAME]), + ('share/ament_index/resource_index/packages', ['resource/' + PACKAGE_NAME]), ('share/' + PACKAGE_NAME, ['package.xml']), # Include all launch files. - (os.path.join('share', PACKAGE_NAME, 'launch'), - glob('launch/*launch.[pxy][yma]*')) + ( + os.path.join('share', PACKAGE_NAME, 'launch'), + glob('launch/*launch.[pxy][yma]*'), + ), ], install_requires=['setuptools'], zip_safe=True, diff --git a/src/surface/surface_main/test/test_flake8.py b/src/surface/surface_main/test/test_flake8.py index 8ae474ef..6ffa8264 100644 --- a/src/surface/surface_main/test/test_flake8.py +++ b/src/surface/surface_main/test/test_flake8.py @@ -13,8 +13,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -from ament_flake8.main import main_with_errors import pytest +from ament_flake8.main import main_with_errors @pytest.mark.flake8 @@ -22,6 +22,6 @@ def test_flake8() -> None: """Tests flake8 on this module.""" error_code, errors = main_with_errors(argv=[]) - assert error_code == 0, \ - f'Found {len(errors)} code style errors / warnings:\n' + \ - '\n'.join(errors) + assert error_code == 0, ( + f'Found {len(errors)} code style errors / warnings:\n' + '\n'.join(errors) + ) diff --git a/src/surface/surface_main/test/test_mypy.py b/src/surface/surface_main/test/test_mypy.py index c2e823a6..f43986b1 100644 --- a/src/surface/surface_main/test/test_mypy.py +++ b/src/surface/surface_main/test/test_mypy.py @@ -1,6 +1,7 @@ """Test mypy on this module.""" import os + import pytest from ament_mypy.main import main @@ -9,6 +10,6 @@ @pytest.mark.linter def test_mypy() -> None: """Tests mypy on this module.""" - path = os.path.join(os.getcwd(), "..", "..", "..", "pyproject.toml") - error_code = main(argv=["--config", path]) + path = os.path.join(os.getcwd(), '..', '..', '..', 'pyproject.toml') + error_code = main(argv=['--config', path]) assert error_code == 0, 'Found code style errors / warnings' diff --git a/src/surface/surface_main/test/test_pep257.py b/src/surface/surface_main/test/test_pep257.py index 62bed98c..67305010 100644 --- a/src/surface/surface_main/test/test_pep257.py +++ b/src/surface/surface_main/test/test_pep257.py @@ -23,6 +23,6 @@ @pytest.mark.pep257 def test_pep257() -> None: """Tests pep257 on this module.""" - path = os.path.join(os.getcwd(), "..", "..", "..", "pyproject.toml") + path = os.path.join(os.getcwd(), '..', '..', '..', 'pyproject.toml') error_code = main(argv=['.', 'test', '--config', path]) assert error_code == 0, 'Found code style errors / warnings' diff --git a/src/surface/transceiver/launch/serial_reader_launch.py b/src/surface/transceiver/launch/serial_reader_launch.py index c4168c84..cecbe8ab 100644 --- a/src/surface/transceiver/launch/serial_reader_launch.py +++ b/src/surface/transceiver/launch/serial_reader_launch.py @@ -1,5 +1,5 @@ -from launch_ros.actions import Node from launch.launch_description import LaunchDescription +from launch_ros.actions import Node def generate_launch_description() -> LaunchDescription: @@ -17,13 +17,13 @@ def generate_launch_description() -> LaunchDescription: package='transceiver', executable='serial', emulate_tty=True, - output="screen", - remappings=[("/surface/transceiver_data", "/surface/gui/transceiver_data"), - ("/surface/float_command", "/surface/gui/float_command"), - ("/surface/float_serial", "/surface/gui/float_serial"), - ("/surface/transceiver_single", "/surface/gui/transceiver_single")] + output='screen', + remappings=[ + ('/surface/transceiver_data', '/surface/gui/transceiver_data'), + ('/surface/float_command', '/surface/gui/float_command'), + ('/surface/float_serial', '/surface/gui/float_serial'), + ('/surface/transceiver_single', '/surface/gui/transceiver_single'), + ], ) - return LaunchDescription([ - reader_node - ]) + return LaunchDescription([reader_node]) diff --git a/src/surface/transceiver/setup.py b/src/surface/transceiver/setup.py index 540b2ff6..3ea4b8d5 100644 --- a/src/surface/transceiver/setup.py +++ b/src/surface/transceiver/setup.py @@ -1,6 +1,7 @@ -from setuptools import setup -from glob import glob import os +from glob import glob + +from setuptools import setup package_name = 'transceiver' @@ -9,12 +10,13 @@ version='1.2.0', packages=[package_name], data_files=[ - ('share/ament_index/resource_index/packages', - ['resource/' + package_name]), + ('share/ament_index/resource_index/packages', ['resource/' + package_name]), ('share/' + package_name, ['package.xml']), # Include all launch files. - (os.path.join('share', package_name, 'launch'), - glob('launch/*launch.[pxy][yma]*')) + ( + os.path.join('share', package_name, 'launch'), + glob('launch/*launch.[pxy][yma]*'), + ), ], install_requires=['setuptools'], zip_safe=True, diff --git a/src/surface/transceiver/test/test_flake8.py b/src/surface/transceiver/test/test_flake8.py index eac16eef..4d880687 100644 --- a/src/surface/transceiver/test/test_flake8.py +++ b/src/surface/transceiver/test/test_flake8.py @@ -12,14 +12,14 @@ # See the License for the specific language governing permissions and # limitations under the License. -from ament_flake8.main import main_with_errors import pytest +from ament_flake8.main import main_with_errors @pytest.mark.flake8 @pytest.mark.linter def test_flake8() -> None: rc, errors = main_with_errors(argv=[]) - assert rc == 0, \ - 'Found %d code style errors / warnings:\n' % len(errors) + \ - '\n'.join(errors) + assert rc == 0, 'Found %d code style errors / warnings:\n' % len( + errors + ) + '\n'.join(errors) diff --git a/src/surface/transceiver/test/test_mypy.py b/src/surface/transceiver/test/test_mypy.py index c2e823a6..f43986b1 100644 --- a/src/surface/transceiver/test/test_mypy.py +++ b/src/surface/transceiver/test/test_mypy.py @@ -1,6 +1,7 @@ """Test mypy on this module.""" import os + import pytest from ament_mypy.main import main @@ -9,6 +10,6 @@ @pytest.mark.linter def test_mypy() -> None: """Tests mypy on this module.""" - path = os.path.join(os.getcwd(), "..", "..", "..", "pyproject.toml") - error_code = main(argv=["--config", path]) + path = os.path.join(os.getcwd(), '..', '..', '..', 'pyproject.toml') + error_code = main(argv=['--config', path]) assert error_code == 0, 'Found code style errors / warnings' diff --git a/src/surface/transceiver/test/test_parser.py b/src/surface/transceiver/test/test_parser.py index 27572a89..3361904e 100644 --- a/src/surface/transceiver/test/test_parser.py +++ b/src/surface/transceiver/test/test_parser.py @@ -1,6 +1,7 @@ +from collections import deque from queue import Queue from typing import Generator, TypeVar -from collections import deque + import pytest from transceiver.serial_reader import SerialReaderPacketHandler @@ -8,21 +9,26 @@ T = TypeVar('T') -PACKET = "ROS:11,1,1:313901,988.53;314843,988.57;315785,988.99;316727,991.56;317669,996.21;318611,1002.36;319553,1010.36;320495,1021.11;321437,1034.42;322379,1050.23;323321,1051.86;324263,1053.20;325206,1053.32;326146,1053.46;327088,1053.52;328030,1053.58;328972,1053.61;329914,1053.64;330856,1053.61;331798,1053.60;332740,1053.65;333682,1053.58;334624,1053.53;335566,1053.52;336508,1053.39;337453,1053.41;338395,1053.46;339337,1053.37;340279,1053.42;341221,1053.49;342163,1053.54" # noqa: E501 +PACKET = 'ROS:11,1,1:313901,988.53;314843,988.57;315785,988.99;316727,991.56;317669,996.21;318611,1002.36;319553,1010.36;320495,1021.11;321437,1034.42;322379,1050.23;323321,1051.86;324263,1053.20;325206,1053.32;326146,1053.46;327088,1053.52;328030,1053.58;328972,1053.61;329914,1053.64;330856,1053.61;331798,1053.60;332740,1053.65;333682,1053.58;334624,1053.53;335566,1053.52;336508,1053.39;337453,1053.41;338395,1053.46;339337,1053.37;340279,1053.42;341221,1053.49;342163,1053.54' # noqa: E501 -NOT_THREE_SECTIONS = "ROS:11,1,1313901,988.53;314843,988.57;315785,988.99;316727,991.56;317669,996.21;318611,1002.36;319553,1010.36;320495,1021.11;321437,1034.42;322379,1050.23;323321,1051.86;324263,1053.20;325206,1053.32;326146,1053.46;327088,1053.52;328030,1053.58;328972,1053.61;329914,1053.64;330856,1053.61;331798,1053.60;332740,1053.65;333682,1053.58;334624,1053.53;335566,1053.52;336508,1053.39;337453,1053.41;338395,1053.46;339337,1053.37;340279,1053.42;341221,1053.49;342163,1053.54" # noqa: E501 +NOT_THREE_SECTIONS = 'ROS:11,1,1313901,988.53;314843,988.57;315785,988.99;316727,991.56;317669,996.21;318611,1002.36;319553,1010.36;320495,1021.11;321437,1034.42;322379,1050.23;323321,1051.86;324263,1053.20;325206,1053.32;326146,1053.46;327088,1053.52;328030,1053.58;328972,1053.61;329914,1053.64;330856,1053.61;331798,1053.60;332740,1053.65;333682,1053.58;334624,1053.53;335566,1053.52;336508,1053.39;337453,1053.41;338395,1053.46;339337,1053.37;340279,1053.42;341221,1053.49;342163,1053.54' # noqa: E501 -HEADER_TWO_ELEMENTS = "ROS:11,1:313901,988.53;314843,988.57;315785,988.99;316727,991.56;317669,996.21;318611,1002.36;319553,1010.36;320495,1021.11;321437,1034.42;322379,1050.23;323321,1051.86;324263,1053.20;325206,1053.32;326146,1053.46;327088,1053.52;328030,1053.58;328972,1053.61;329914,1053.64;330856,1053.61;331798,1053.60;332740,1053.65;333682,1053.58;334624,1053.53;335566,1053.52;336508,1053.39;337453,1053.41;338395,1053.46;339337,1053.37;340279,1053.42;341221,1053.49;342163,1053.54" # noqa: E501 +HEADER_TWO_ELEMENTS = 'ROS:11,1:313901,988.53;314843,988.57;315785,988.99;316727,991.56;317669,996.21;318611,1002.36;319553,1010.36;320495,1021.11;321437,1034.42;322379,1050.23;323321,1051.86;324263,1053.20;325206,1053.32;326146,1053.46;327088,1053.52;328030,1053.58;328972,1053.61;329914,1053.64;330856,1053.61;331798,1053.60;332740,1053.65;333682,1053.58;334624,1053.53;335566,1053.52;336508,1053.39;337453,1053.41;338395,1053.46;339337,1053.37;340279,1053.42;341221,1053.49;342163,1053.54' # noqa: E501 -ROS_SINGLE_ONE = "ROS:SINGLE:25:5552,992.4500" -ROS_SINGLE_TWO = "ROS:SINGLE:25:11071,994.4299" -ROS_SINGLE_THREE = "ROS:SINGLE:25:16592,992.9600" -ROS_SINGLE_FOUR = "ROS:SINGLE:25:22112,993.3699" -ROS_SINGLE_FIVE = "ROS:SINGLE:25:27631,993.2600" +ROS_SINGLE_ONE = 'ROS:SINGLE:25:5552,992.4500' +ROS_SINGLE_TWO = 'ROS:SINGLE:25:11071,994.4299' +ROS_SINGLE_THREE = 'ROS:SINGLE:25:16592,992.9600' +ROS_SINGLE_FOUR = 'ROS:SINGLE:25:22112,993.3699' +ROS_SINGLE_FIVE = 'ROS:SINGLE:25:27631,993.2600' -ROS_SINGLE_MSGS = (ROS_SINGLE_ONE, ROS_SINGLE_TWO, ROS_SINGLE_THREE, ROS_SINGLE_FOUR, - ROS_SINGLE_FIVE) +ROS_SINGLE_MSGS = ( + ROS_SINGLE_ONE, + ROS_SINGLE_TWO, + ROS_SINGLE_THREE, + ROS_SINGLE_FOUR, + ROS_SINGLE_FIVE, +) TEST_VALUES = (992.4500, 994.4299, 992.9600, 993.3699, 993.26) EXPECTED_VALUES = (992.4500, 993.43995, 993.27996666666, 993.30245, 993.29396) @@ -39,22 +45,91 @@ def test_message_parser(packet_handler: SerialReaderPacketHandler) -> None: team_number=11, profile_number=1, profile_half=1, - time_data=[5.231683254241943, 5.247383117675781, 5.263083457946777, 5.278783321380615, 5.294483184814453, 5.310183525085449, 5.325883388519287, 5.341583251953125, 5.357283115386963, 5.372983455657959, 5.388683319091797, 5.404383182525635, 5.420100212097168, 5.435766696929932, 5.4514665603637695, 5.467166900634766, 5.4828667640686035, 5.498566627502441, 5.514266490936279, 5.529966831207275, 5.545666694641113, 5.561366558074951, 5.577066898345947, 5.592766761779785, 5.608466625213623, 5.624216556549072, 5.639916896820068, 5.655616760253906, 5.671316623687744, 5.687016487121582, 5.702716827392578], # noqa 501 - depth_data=[0.3828616142272949, 0.38326960802078247, 0.38755351305007935, 0.41376692056655884, 0.46119585633277893, 0.5239244699478149, 0.605522632598877, 0.7151702046394348, 0.8509292006492615, 1.0121876001358032, 1.0288132429122925, 1.0424809455871582, 1.043704867362976, 1.0451328754425049, 1.0457448959350586, 1.0463569164276123, 1.0466628074645996, 1.0469688177108765, 1.0466628074645996, 1.0465608835220337, 1.047070860862732, 1.0463569164276123, 1.0458468198776245, 1.0457448959350586, 1.0444189310073853, 1.0446228981018066, 1.0451328754425049, 1.0442149639129639, 1.044724941253662, 1.0454388856887817, 1.04594886302948] # noqa 501 + time_data=[ + 5.231683254241943, + 5.247383117675781, + 5.263083457946777, + 5.278783321380615, + 5.294483184814453, + 5.310183525085449, + 5.325883388519287, + 5.341583251953125, + 5.357283115386963, + 5.372983455657959, + 5.388683319091797, + 5.404383182525635, + 5.420100212097168, + 5.435766696929932, + 5.4514665603637695, + 5.467166900634766, + 5.4828667640686035, + 5.498566627502441, + 5.514266490936279, + 5.529966831207275, + 5.545666694641113, + 5.561366558074951, + 5.577066898345947, + 5.592766761779785, + 5.608466625213623, + 5.624216556549072, + 5.639916896820068, + 5.655616760253906, + 5.671316623687744, + 5.687016487121582, + 5.702716827392578, + ], # noqa 501 + depth_data=[ + 0.3828616142272949, + 0.38326960802078247, + 0.38755351305007935, + 0.41376692056655884, + 0.46119585633277893, + 0.5239244699478149, + 0.605522632598877, + 0.7151702046394348, + 0.8509292006492615, + 1.0121876001358032, + 1.0288132429122925, + 1.0424809455871582, + 1.043704867362976, + 1.0451328754425049, + 1.0457448959350586, + 1.0463569164276123, + 1.0466628074645996, + 1.0469688177108765, + 1.0466628074645996, + 1.0465608835220337, + 1.047070860862732, + 1.0463569164276123, + 1.0458468198776245, + 1.0457448959350586, + 1.0444189310073853, + 1.0446228981018066, + 1.0451328754425049, + 1.0442149639129639, + 1.044724941253662, + 1.0454388856887817, + 1.04594886302948, + ], # noqa 501 ) - with pytest.raises(ValueError, match="Packet expected 3 sections, found 2 sections"): + with pytest.raises( + ValueError, match='Packet expected 3 sections, found 2 sections' + ): packet_handler.message_parser(NOT_THREE_SECTIONS) - with pytest.raises(ValueError, match="Packet header length of 3 expected found 2 instead"): + with pytest.raises( + ValueError, match='Packet header length of 3 expected found 2 instead' + ): packet_handler.message_parser(HEADER_TWO_ELEMENTS) def test_handle_ros_single(packet_handler: SerialReaderPacketHandler) -> None: test_queue: Queue[float] = Queue(5) - for ros_single, test_value, expected_value in zip(ROS_SINGLE_MSGS, TEST_VALUES, - EXPECTED_VALUES): + for ros_single, test_value, expected_value in zip( + ROS_SINGLE_MSGS, TEST_VALUES, EXPECTED_VALUES + ): packet_handler.handle_ros_single(ros_single) test_queue.put(test_value) assert equal(packet_handler.surface_pressures, test_queue) diff --git a/src/surface/transceiver/test/test_pep257.py b/src/surface/transceiver/test/test_pep257.py index b6808e1d..91de534e 100644 --- a/src/surface/transceiver/test/test_pep257.py +++ b/src/surface/transceiver/test/test_pep257.py @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -from ament_pep257.main import main import pytest +from ament_pep257.main import main @pytest.mark.linter diff --git a/src/surface/transceiver/transceiver/serial_reader.py b/src/surface/transceiver/transceiver/serial_reader.py index 44de2b6d..a4f4469c 100644 --- a/src/surface/transceiver/transceiver/serial_reader.py +++ b/src/surface/transceiver/transceiver/serial_reader.py @@ -10,8 +10,8 @@ from rov_msgs.msg import FloatCommand, FloatData, FloatSerial, FloatSingle -MILLISECONDS_TO_SECONDS = 1/1000 -SECONDS_TO_MINUTES = 1/60 +MILLISECONDS_TO_SECONDS = 1 / 1000 +SECONDS_TO_MINUTES = 1 / 60 MBAR_TO_METER_OF_HEAD = 0.010199773339984 @@ -22,41 +22,48 @@ AVERAGE_QUEUE_LEN = 5 -ROS_PACKET = "ROS:" -ROS_SINGLE = ROS_PACKET + "SINGLE" -SECTION_SEPARATOR = ":" -DATA_SEPARATOR = ";" -COMMA_SEPARATOR = "," +ROS_PACKET = 'ROS:' +ROS_SINGLE = ROS_PACKET + 'SINGLE' +SECTION_SEPARATOR = ':' +DATA_SEPARATOR = ';' +COMMA_SEPARATOR = ',' HEADER_LENGTH = 3 PACKET_SECTIONS = 3 class SerialReader(Node): - def __init__(self) -> None: super().__init__('serial_reader') - self.data_publisher = self.create_publisher(FloatData, 'transceiver_data', - QoSPresetProfiles.SENSOR_DATA.value) + self.data_publisher = self.create_publisher( + FloatData, 'transceiver_data', QoSPresetProfiles.SENSOR_DATA.value + ) - self.ros_single_publisher = self.create_publisher(FloatSingle, 'transceiver_single', - QoSPresetProfiles.SENSOR_DATA.value) + self.ros_single_publisher = self.create_publisher( + FloatSingle, 'transceiver_single', QoSPresetProfiles.SENSOR_DATA.value + ) - self.create_subscription(FloatCommand, 'float_command', self.send_command, - QoSPresetProfiles.DEFAULT.value) + self.create_subscription( + FloatCommand, + 'float_command', + self.send_command, + QoSPresetProfiles.DEFAULT.value, + ) - self.serial_publisher = self.create_publisher(FloatSerial, 'float_serial', - QoSPresetProfiles.SENSOR_DATA.value) + self.serial_publisher = self.create_publisher( + FloatSerial, 'float_serial', QoSPresetProfiles.SENSOR_DATA.value + ) self.serial_packet_handler = SerialReaderPacketHandler() try: - self.serial = Serial("/dev/serial/by-id/usb-Adafruit_Feather_32u4-if00", 115200) + self.serial = Serial( + '/dev/serial/by-id/usb-Adafruit_Feather_32u4-if00', 115200 + ) except SerialException: - self.get_logger().error("Could not get serial device") + self.get_logger().error('Could not get serial device') exit(1) - Thread(target=self.read_serial, daemon=True, - name="Serial Reader").start() + Thread(target=self.read_serial, daemon=True, name='Serial Reader').start() def send_command(self, msg: FloatCommand) -> None: self.serial.write(msg.command.encode()) @@ -75,7 +82,7 @@ def ros_publish(self, packet: str) -> None: """Publish a message from the transceiver.""" self.serial_publisher.publish(FloatSerial(serial=packet)) - if packet[:len(ROS_PACKET)] != ROS_PACKET: + if packet[: len(ROS_PACKET)] != ROS_PACKET: return try: @@ -87,18 +94,17 @@ def ros_publish(self, packet: str) -> None: msg = self.serial_packet_handler.message_parser(packet) self.data_publisher.publish(msg) except Exception as e: - self.get_logger().error(f"Error {e} caught dropping packet") + self.get_logger().error(f'Error {e} caught dropping packet') class SerialReaderPacketHandler: - def __init__(self, queue_size: int = AVERAGE_QUEUE_LEN) -> None: self.surface_pressure = AMBIENT_PRESSURE_DEFAULT self.surface_pressures: Queue[float] = Queue(queue_size) @staticmethod def is_ros_single_message(packet: str) -> bool: - return packet[:len(ROS_SINGLE)] == ROS_SINGLE + return packet[: len(ROS_SINGLE)] == ROS_SINGLE def handle_ros_single(self, packet: str) -> FloatSingle: packet_sections = packet.split(SECTION_SEPARATOR) @@ -107,9 +113,9 @@ def handle_ros_single(self, packet: str) -> FloatSingle: time_ms = int(data.split(COMMA_SEPARATOR)[0]) pressure = float(data.split(COMMA_SEPARATOR)[1]) - float_msg = FloatSingle(team_number=team_number, - time_ms=time_ms, - pressure=pressure) + float_msg = FloatSingle( + team_number=team_number, time_ms=time_ms, pressure=pressure + ) if not self.surface_pressures.full(): self.surface_pressures.put(pressure) @@ -129,15 +135,19 @@ def message_parser(self, packet: str) -> FloatData: packet_sections = packet.split(SECTION_SEPARATOR) if len(packet_sections) != PACKET_SECTIONS: - raise ValueError(f"Packet expected {PACKET_SECTIONS} sections, " - f"found {len(packet_sections)} sections") + raise ValueError( + f'Packet expected {PACKET_SECTIONS} sections, ' + f'found {len(packet_sections)} sections' + ) header = packet_sections[1].split(COMMA_SEPARATOR) data = packet_sections[2] if len(header) != HEADER_LENGTH: - raise ValueError(f"Packet header length of {HEADER_LENGTH} expected " - f"found {len(header)} instead") + raise ValueError( + f'Packet header length of {HEADER_LENGTH} expected ' + f'found {len(header)} instead' + ) msg.team_number = int(header[0]) msg.profile_number = int(header[1]) @@ -146,17 +156,20 @@ def message_parser(self, packet: str) -> FloatData: time_data_list: list[float] = [] depth_data_list: list[float] = [] - for time_reading, pressure_reading in [data.split(COMMA_SEPARATOR) for data in - data.split(DATA_SEPARATOR)]: - + for time_reading, pressure_reading in [ + data.split(COMMA_SEPARATOR) for data in data.split(DATA_SEPARATOR) + ]: if int(time_reading) == 0: continue # Starts out as uint32 - time_data_list.append(int(time_reading) * MILLISECONDS_TO_SECONDS * SECONDS_TO_MINUTES) + time_data_list.append( + int(time_reading) * MILLISECONDS_TO_SECONDS * SECONDS_TO_MINUTES + ) # Starts out as float depth_data_list.append( - (float(pressure_reading) - self.surface_pressure) * MBAR_TO_METER_OF_HEAD + (float(pressure_reading) - self.surface_pressure) + * MBAR_TO_METER_OF_HEAD + PRESSURE_SENSOR_VERTICAL_OFFSET ) diff --git a/src/surface/vehicle_manager/launch/vehicle_manager_launch.py b/src/surface/vehicle_manager/launch/vehicle_manager_launch.py index d4aa26ce..8dbe9e90 100644 --- a/src/surface/vehicle_manager/launch/vehicle_manager_launch.py +++ b/src/surface/vehicle_manager/launch/vehicle_manager_launch.py @@ -4,16 +4,14 @@ def generate_launch_description() -> LaunchDescription: heartbeat_node = Node( - package="vehicle_manager", - executable="connection_manager_node", + package='vehicle_manager', + executable='connection_manager_node', remappings=[ - ("/surface/mavros/state", "/tether/mavros/state"), - ("/surface/pi_heartbeat", "/tether/pi_heartbeat") + ('/surface/mavros/state', '/tether/mavros/state'), + ('/surface/pi_heartbeat', '/tether/pi_heartbeat'), ], emulate_tty=True, - output='screen' + output='screen', ) - return LaunchDescription([ - heartbeat_node - ]) + return LaunchDescription([heartbeat_node]) diff --git a/src/surface/vehicle_manager/setup.py b/src/surface/vehicle_manager/setup.py index 70d29795..c28c2a4a 100644 --- a/src/surface/vehicle_manager/setup.py +++ b/src/surface/vehicle_manager/setup.py @@ -10,12 +10,13 @@ version='1.0.0', packages=find_packages(exclude=['test']), data_files=[ - ('share/ament_index/resource_index/packages', - ['resource/' + PACKAGE_NAME]), + ('share/ament_index/resource_index/packages', ['resource/' + PACKAGE_NAME]), ('share/' + PACKAGE_NAME, ['package.xml']), # Include all launch files. - (os.path.join('share', PACKAGE_NAME, 'launch'), - glob('launch/*launch.[pxy][yma]*')) + ( + os.path.join('share', PACKAGE_NAME, 'launch'), + glob('launch/*launch.[pxy][yma]*'), + ), ], install_requires=['setuptools'], zip_safe=True, @@ -26,7 +27,7 @@ tests_require=['pytest'], entry_points={ 'console_scripts': [ - "connection_manager_node = vehicle_manager.connection_manager_node:main" + 'connection_manager_node = vehicle_manager.connection_manager_node:main' ], }, ) diff --git a/src/surface/vehicle_manager/test/test_flake8.py b/src/surface/vehicle_manager/test/test_flake8.py index 8ae474ef..6ffa8264 100644 --- a/src/surface/vehicle_manager/test/test_flake8.py +++ b/src/surface/vehicle_manager/test/test_flake8.py @@ -13,8 +13,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -from ament_flake8.main import main_with_errors import pytest +from ament_flake8.main import main_with_errors @pytest.mark.flake8 @@ -22,6 +22,6 @@ def test_flake8() -> None: """Tests flake8 on this module.""" error_code, errors = main_with_errors(argv=[]) - assert error_code == 0, \ - f'Found {len(errors)} code style errors / warnings:\n' + \ - '\n'.join(errors) + assert error_code == 0, ( + f'Found {len(errors)} code style errors / warnings:\n' + '\n'.join(errors) + ) diff --git a/src/surface/vehicle_manager/test/test_mypy.py b/src/surface/vehicle_manager/test/test_mypy.py index c2e823a6..f43986b1 100644 --- a/src/surface/vehicle_manager/test/test_mypy.py +++ b/src/surface/vehicle_manager/test/test_mypy.py @@ -1,6 +1,7 @@ """Test mypy on this module.""" import os + import pytest from ament_mypy.main import main @@ -9,6 +10,6 @@ @pytest.mark.linter def test_mypy() -> None: """Tests mypy on this module.""" - path = os.path.join(os.getcwd(), "..", "..", "..", "pyproject.toml") - error_code = main(argv=["--config", path]) + path = os.path.join(os.getcwd(), '..', '..', '..', 'pyproject.toml') + error_code = main(argv=['--config', path]) assert error_code == 0, 'Found code style errors / warnings' diff --git a/src/surface/vehicle_manager/test/test_pep257.py b/src/surface/vehicle_manager/test/test_pep257.py index 62bed98c..67305010 100644 --- a/src/surface/vehicle_manager/test/test_pep257.py +++ b/src/surface/vehicle_manager/test/test_pep257.py @@ -23,6 +23,6 @@ @pytest.mark.pep257 def test_pep257() -> None: """Tests pep257 on this module.""" - path = os.path.join(os.getcwd(), "..", "..", "..", "pyproject.toml") + path = os.path.join(os.getcwd(), '..', '..', '..', 'pyproject.toml') error_code = main(argv=['.', 'test', '--config', path]) assert error_code == 0, 'Found code style errors / warnings' diff --git a/src/surface/vehicle_manager/vehicle_manager/connection_manager_node.py b/src/surface/vehicle_manager/vehicle_manager/connection_manager_node.py index a1b8ee92..55400aba 100644 --- a/src/surface/vehicle_manager/vehicle_manager/connection_manager_node.py +++ b/src/surface/vehicle_manager/vehicle_manager/connection_manager_node.py @@ -14,7 +14,7 @@ @dataclass -class VehicleState(): +class VehicleState: pi_connected: bool = False pixhawk_connected: bool = False armed: bool = False @@ -22,28 +22,24 @@ class VehicleState(): class VehicleManagerNode(Node): def __init__(self) -> None: - super().__init__("connection_manager_node", parameter_overrides=[]) + super().__init__('connection_manager_node', parameter_overrides=[]) self.state_publisher = self.create_publisher( - VehicleStateMsg, "vehicle_state_event", qos_profile_system_default + VehicleStateMsg, 'vehicle_state_event', qos_profile_system_default ) self.mavros_subscription = self.create_subscription( - State, - 'mavros/state', - self.mavros_callback, - 10 + State, 'mavros/state', self.mavros_callback, 10 ) self.mavros_subscription = self.create_subscription( - Heartbeat, - 'pi_heartbeat', - self.heartbeat_callback, - 10 + Heartbeat, 'pi_heartbeat', self.heartbeat_callback, 10 ) self.watchdog_timer = self.create_timer(1, self.watchdog_callback) - self.last_heartbeat: float = 0 # Unix timestamp of the last mavros heartbeat from the pi + self.last_heartbeat: float = ( + 0 # Unix timestamp of the last mavros heartbeat from the pi + ) self.last_subscriber_count = 0 self.poll_subscribers_timer = self.create_timer(1, self.poll_subscribers) @@ -55,30 +51,32 @@ def publish_state(self, state: VehicleState) -> None: VehicleStateMsg( pi_connected=state.pi_connected, pixhawk_connected=state.pixhawk_connected, - armed=state.armed + armed=state.armed, ) ) def mavros_callback(self, msg: State) -> None: - new_state = VehicleState(pi_connected=True, - pixhawk_connected=msg.connected, - armed=msg.armed) + new_state = VehicleState( + pi_connected=True, pixhawk_connected=msg.connected, armed=msg.armed + ) if new_state != self.vehicle_state: self.publish_state(new_state) if not self.vehicle_state.pi_connected: - self.get_logger().info("Pi connected") + self.get_logger().info('Pi connected') if self.vehicle_state.pixhawk_connected and not new_state.pixhawk_connected: - self.get_logger().warn("Pixhawk disconnected") - elif not self.vehicle_state.pixhawk_connected and new_state.pixhawk_connected: - self.get_logger().info("Pixhawk connected") + self.get_logger().warn('Pixhawk disconnected') + elif ( + not self.vehicle_state.pixhawk_connected and new_state.pixhawk_connected + ): + self.get_logger().info('Pixhawk connected') if self.vehicle_state.armed and not new_state.armed: - self.get_logger().info("Pixhawk disarmed") + self.get_logger().info('Pixhawk disarmed') elif not self.vehicle_state.armed and new_state.armed: - self.get_logger().info("Pixhawk armed") + self.get_logger().info('Pixhawk armed') self.vehicle_state = new_state @@ -88,16 +86,18 @@ def heartbeat_callback(self, _: Heartbeat) -> None: if not self.vehicle_state.pi_connected: self.vehicle_state.pi_connected = True self.publish_state(self.vehicle_state) - self.get_logger().info("Pi connected") + self.get_logger().info('Pi connected') def watchdog_callback(self) -> None: - if self.vehicle_state.pi_connected and time.time() - self.last_heartbeat > PI_TIMEOUT: + if ( + self.vehicle_state.pi_connected + and time.time() - self.last_heartbeat > PI_TIMEOUT + ): self.vehicle_state = VehicleState( - pi_connected=False, - pixhawk_connected=False, - armed=False) + pi_connected=False, pixhawk_connected=False, armed=False + ) self.publish_state(self.vehicle_state) - self.get_logger().warn("Pi disconnected") + self.get_logger().warn('Pi disconnected') def poll_subscribers(self) -> None: # Whenever a node subscribes to vehicle state updates, send the current state @@ -118,5 +118,5 @@ def main() -> None: rclpy.spin(vehicle_manager, executor=executor) -if __name__ == "__main__": +if __name__ == '__main__': main()