From e487b2c75fc95cee0178e22dd4aa810b364a8c8a Mon Sep 17 00:00:00 2001 From: Muk Date: Wed, 23 Oct 2024 21:18:13 -0700 Subject: [PATCH 01/10] set up UI --- .../drive_and_dribbler_widget.py | 275 ++++++++++++++---- 1 file changed, 220 insertions(+), 55 deletions(-) diff --git a/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget.py b/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget.py index 68bdbd2738..c8360f61ab 100644 --- a/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget.py +++ b/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget.py @@ -16,17 +16,26 @@ def __init__(self, proto_unix_io: ProtoUnixIO) -> None: self.input_a = time.time() self.constants = tbots_cpp.create2021RobotConstants() QWidget.__init__(self) + layout = QVBoxLayout() + self.drive_widget = QStackedWidget() self.proto_unix_io = proto_unix_io - # Add widgets to layout - layout.addWidget(self.setup_direct_velocity("Drive")) + # create swappable widget system using stacked widgets + self.direct_velocity_widget = self.setup_direct_velocity("Drive - Direct Velocity") + self.per_motor_widget = self.setup_per_motor("Drive - Per Motor") + self.drive_widget.addWidget(self.direct_velocity_widget) + self.drive_widget.addWidget(self.per_motor_widget) + + layout.addWidget(self.setup_drive_switch("Drive Mode Switch")) + layout.addWidget(self.drive_widget) layout.addWidget(self.setup_dribbler("Dribbler")) self.enabled = True - self.setLayout(layout) + self.toggle_control_mode(True) + self.toggle_dribbler_sliders(True) def refresh(self) -> None: """Refresh the widget and send the a MotorControl message with the current values""" @@ -54,6 +63,38 @@ def value_change(self, value: float) -> str: value_str = "%.2f" % value return value_str + def setup_drive_switch(self, title: str) -> QGroupBox: + """Create a widget to switch between per-motor and velocity control modes + + :param title: group box name + """ + group_box = QGroupBox(title) + dbox = QVBoxLayout() + + # Each button disables itself after being pressed. + self.use_per_motor = QPushButton("Per-Motor") + self.use_per_motor.clicked.connect(self.switch_to_motor) + + self.use_direct_velocity = QPushButton("Velocity Control") + self.use_direct_velocity.clicked.connect(self.switch_to_velocity) + + dbox.addWidget( + self.use_direct_velocity, alignment=Qt.AlignmentFlag.AlignCenter + ) + dbox.addWidget( + self.use_per_motor, alignment=Qt.AlignmentFlag.AlignCenter + ) + + group_box.setLayout(dbox) + + return group_box + + def switch_to_velocity(self): + self.toggle_control_mode(True) + + def switch_to_motor(self): + self.toggle_control_mode(False) + def setup_direct_velocity(self, title: str) -> QGroupBox: """Create a widget to control the direct velocity of the robot's motors @@ -121,6 +162,90 @@ def setup_direct_velocity(self, title: str) -> QGroupBox: return group_box + def setup_per_motor(self, title: str) -> QGroupBox: + """Create a widget to control the rotation rate of each motor + + :param title: the name of the group box + """ + group_box = QGroupBox(title) + dbox = QVBoxLayout() + + ( + fl_layout, + self.front_left_motor_slider, + self.front_left_motor_label, + ) = common_widgets.create_float_slider( + "Front Left Motor (m/s)", + 2, + -self.constants.robot_max_speed_m_per_s, + self.constants.robot_max_speed_m_per_s, + 1, + ) + ( + fr_layout, + self.front_right_motor_slider, + self.front_right_motor_label, + ) = common_widgets.create_float_slider( + "Front Right Motor (m/s)", + 2, + -self.constants.robot_max_speed_m_per_s, + self.constants.robot_max_speed_m_per_s, + 1, + ) + ( + bl_layout, + self.back_left_motor_slider, + self.back_left_motor_label, + ) = common_widgets.create_float_slider( + "Back Left Motor (m/s)", + 2, + -self.constants.robot_max_speed_m_per_s, + self.constants.robot_max_speed_m_per_s, + 1, + ) + ( + br_layout, + self.back_right_motor_slider, + self.back_right_motor_label, + ) = common_widgets.create_float_slider( + "Back Right Motor (m/s)", + 2, + -self.constants.robot_max_speed_m_per_s, + self.constants.robot_max_speed_m_per_s, + 1, + ) + + # Add listener functions for each motor's slider to update labels with slider values + common_widgets.enable_slider( + self.front_left_motor_slider, self.front_left_motor_label, self.value_change + ) + common_widgets.enable_slider( + self.front_right_motor_slider, self.front_right_motor_label, self.value_change + ) + common_widgets.enable_slider( + self.back_left_motor_slider, self.back_left_motor_label, self.value_change + ) + common_widgets.enable_slider( + self.back_right_motor_slider, self.back_right_motor_label, self.value_change + ) + + # Stop and Reset button for per-motor sliders + self.stop_and_reset_per_motor = QPushButton("Stop and Reset") + self.stop_and_reset_per_motor.clicked.connect(self.reset_motor_sliders) + + # Adding layouts to the box + dbox.addLayout(fl_layout) + dbox.addLayout(fr_layout) + dbox.addLayout(bl_layout) + dbox.addLayout(br_layout) + dbox.addWidget( + self.stop_and_reset_per_motor, alignment=Qt.AlignmentFlag.AlignCenter + ) + + group_box.setLayout(dbox) + + return group_box + def setup_dribbler(self, title: str) -> QGroupBox: """Create a widget to control the dribbler RPM @@ -159,74 +284,113 @@ def setup_dribbler(self, title: str) -> QGroupBox: return group_box - def toggle_all(self, enable: bool) -> None: - """Disables or enables all sliders and buttons depending on boolean parameter + def toggle_control_mode(self, use_direct: bool) -> None: + """Switches between 'Direct Velocity' and 'Per Motor' control modes. - Updates listener functions and stylesheets accordingly + :param use_direct: True to enable Direct Velocity mode, False to enable Per Motor mode. + """ + # reset sliders + self.reset_motor_sliders() + self.reset_direct_sliders() + self.disconnect_direct_sliders() + self.disconnect_motor_sliders() + + if use_direct: + # Show the direct velocity widget + self.drive_widget.setCurrentWidget(self.direct_velocity_widget) + + # Enable direct sliders and disable motor sliders + common_widgets.enable_slider( + self.x_velocity_slider, self.x_velocity_label, self.value_change + ) + common_widgets.enable_slider( + self.y_velocity_slider, self.y_velocity_label, self.value_change + ) + common_widgets.enable_slider( + self.angular_velocity_slider, self.angular_velocity_label, self.value_change + ) + + common_widgets.disable_slider(self.front_left_motor_slider) + common_widgets.disable_slider(self.front_right_motor_slider) + common_widgets.disable_slider(self.back_left_motor_slider) + common_widgets.disable_slider(self.back_right_motor_slider) + + common_widgets.change_button_state(self.stop_and_reset_direct, True) + common_widgets.change_button_state(self.use_direct_velocity, True) + common_widgets.change_button_state(self.stop_and_reset_per_motor, False) + common_widgets.change_button_state(self.use_per_motor, False) - :param enable: boolean parameter, True is enable and False is disable + else: + # Show the per motor widget + self.drive_widget.setCurrentWidget(self.per_motor_widget) + + # Enable motor sliders and disable direct sliders + common_widgets.enable_slider( + self.front_left_motor_slider, self.front_left_motor_label, self.value_change + ) + common_widgets.enable_slider( + self.front_right_motor_slider, self.front_right_motor_label, self.value_change + ) + common_widgets.enable_slider( + self.back_left_motor_slider, self.back_left_motor_label, self.value_change + ) + common_widgets.enable_slider( + self.back_right_motor_slider, self.back_right_motor_label, self.value_change + ) + + common_widgets.disable_slider(self.x_velocity_slider) + common_widgets.disable_slider(self.y_velocity_slider) + common_widgets.disable_slider(self.angular_velocity_slider) + + common_widgets.change_button_state(self.stop_and_reset_per_motor, True) + common_widgets.change_button_state(self.use_per_motor, True) + common_widgets.change_button_state(self.stop_and_reset_direct, False) + common_widgets.change_button_state(self.use_direct_velocity, False) + + def toggle_dribbler_sliders(self, enable: bool) -> None: + """Enables or disables dribbler sliders. + + :param enable: True to enable, False to disable """ if enable: - if not self.enabled: - # disconnect all sliders - self.disconnect_sliders() - - # enable all sliders by adding listener to update label with slider value - common_widgets.enable_slider( - self.x_velocity_slider, self.x_velocity_label, self.value_change - ) - common_widgets.enable_slider( - self.y_velocity_slider, self.y_velocity_label, self.value_change - ) - common_widgets.enable_slider( - self.angular_velocity_slider, - self.angular_velocity_label, - self.value_change, - ) - common_widgets.enable_slider( - self.dribbler_speed_rpm_slider, - self.dribbler_speed_rpm_label, - self.value_change, - ) - - # enable buttons - common_widgets.change_button_state(self.stop_and_reset_dribbler, True) - common_widgets.change_button_state(self.stop_and_reset_direct, True) - - # change enabled field - self.enabled = True + common_widgets.enable_slider( + self.dribbler_speed_rpm_slider, + self.dribbler_speed_rpm_label, + self.value_change, + ) + common_widgets.change_button_state(self.stop_and_reset_dribbler, True) else: - if self.enabled: - # reset slider values and disconnect - self.reset_all_sliders() - self.disconnect_sliders() - - # disable all sliders by adding listener to keep slider value the same - common_widgets.disable_slider(self.x_velocity_slider) - common_widgets.disable_slider(self.y_velocity_slider) - common_widgets.disable_slider(self.angular_velocity_slider) - common_widgets.disable_slider(self.dribbler_speed_rpm_slider) - - # disable buttons - common_widgets.change_button_state(self.stop_and_reset_dribbler, False) - common_widgets.change_button_state(self.stop_and_reset_direct, False) - - # change enabled field - self.enabled = False - - def disconnect_sliders(self) -> None: - """Disconnect listener for changing values for all sliders""" + common_widgets.disable_slider(self.dribbler_speed_rpm_slider) + common_widgets.change_button_state(self.stop_and_reset_dribbler, False) + + def disconnect_direct_sliders(self) -> None: + """Disconnect listener for changing values for motor sliders""" self.x_velocity_slider.valueChanged.disconnect() self.y_velocity_slider.valueChanged.disconnect() self.angular_velocity_slider.valueChanged.disconnect() + + def disconnect_dribbler_sliders(self) -> None: self.dribbler_speed_rpm_slider.valueChanged.disconnect() + def disconnect_motor_sliders(self) -> None: + self.front_left_motor_slider.valueChanged.disconnect() + self.front_right_motor_slider.valueChanged.disconnect() + self.back_left_motor_slider.valueChanged.disconnect() + self.back_right_motor_slider.valueChanged.disconnect() + def reset_direct_sliders(self) -> None: """Reset direct sliders back to 0""" self.x_velocity_slider.setValue(0) self.y_velocity_slider.setValue(0) self.angular_velocity_slider.setValue(0) + def reset_motor_sliders(self) -> None: + """Reset direct sliders back to 0""" + self.front_left_motor_slider.setValue(0) + self.front_right_motor_slider.setValue(0) + self.back_left_motor_slider.setValue(0) + self.back_right_motor_slider.setValue(0) + def reset_dribbler_slider(self) -> None: """Reset the dribbler slider back to 0""" self.dribbler_speed_rpm_slider.setValue(0) @@ -234,4 +398,5 @@ def reset_dribbler_slider(self) -> None: def reset_all_sliders(self) -> None: """Reset all sliders back to 0""" self.reset_direct_sliders() + self.reset_motor_sliders() self.reset_dribbler_slider() From d0eb031feeb8263fcfb324ac2a00b8c825c77811 Mon Sep 17 00:00:00 2001 From: Muk Date: Sat, 26 Oct 2024 15:21:33 -0700 Subject: [PATCH 02/10] Progress so far --- .../drive_and_dribbler_widget.py | 80 ++++++++++++------- 1 file changed, 50 insertions(+), 30 deletions(-) diff --git a/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget.py b/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget.py index c8360f61ab..a3f5c65a16 100644 --- a/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget.py +++ b/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget.py @@ -5,6 +5,14 @@ from software.thunderscope.common import common_widgets from proto.import_all_protos import * from software.thunderscope.proto_unix_io import ProtoUnixIO +from enum import IntEnum + + +class DriveMode(IntEnum): + """Enum for the 2 drive modes (direct velocity and per-motor)""" + + VELOCITY = 0 + MOTOR = 1 class DriveAndDribblerWidget(QWidget): @@ -28,11 +36,12 @@ def __init__(self, proto_unix_io: ProtoUnixIO) -> None: self.drive_widget.addWidget(self.direct_velocity_widget) self.drive_widget.addWidget(self.per_motor_widget) - layout.addWidget(self.setup_drive_switch("Drive Mode Switch")) + layout.addWidget(self.setup_drive_switch_radio("Drive Mode Switch")) layout.addWidget(self.drive_widget) layout.addWidget(self.setup_dribbler("Dribbler")) self.enabled = True + self.per_motor = False self.setLayout(layout) self.toggle_control_mode(True) self.toggle_dribbler_sliders(True) @@ -42,16 +51,29 @@ def refresh(self) -> None: motor_control = MotorControl() motor_control.dribbler_speed_rpm = int(self.dribbler_speed_rpm_slider.value()) - motor_control.direct_velocity_control.velocity.x_component_meters = ( - self.x_velocity_slider.value() - ) - motor_control.direct_velocity_control.velocity.y_component_meters = ( - self.y_velocity_slider.value() - ) - motor_control.direct_velocity_control.angular_velocity.radians_per_second = ( - self.angular_velocity_slider.value() - ) - + if not self.per_motor: + motor_control.direct_velocity_control.velocity.x_component_meters = ( + self.x_velocity_slider.value() + ) + motor_control.direct_velocity_control.velocity.y_component_meters = ( + self.y_velocity_slider.value() + ) + motor_control.direct_velocity_control.angular_velocity.radians_per_second = ( + self.angular_velocity_slider.value() + ) + else: + motor_control.direct_per_wheel_control.front_left_wheel_velocity = ( + self.front_left_motor_slider.value() + ) + motor_control.direct_per_wheel_control.front_right_wheel_velocity = ( + self.front_right_motor_slider.value() + ) + motor_control.direct_per_wheel_control.back_left_wheel_velocity = ( + self.back_left_motor_slider.value() + ) + motor_control.direct_per_wheel_control.back_right_wheel_velocity = ( + self.back_right_motor_slider.value() + ) self.proto_unix_io.send_proto(MotorControl, motor_control) def value_change(self, value: float) -> str: @@ -63,28 +85,27 @@ def value_change(self, value: float) -> str: value_str = "%.2f" % value return value_str - def setup_drive_switch(self, title: str) -> QGroupBox: - """Create a widget to switch between per-motor and velocity control modes + def setup_drive_switch_radio(self, title: str) -> QGroupBox: + """Create a radio button widget to switch between per-motor and velocity control modes :param title: group box name """ group_box = QGroupBox(title) dbox = QVBoxLayout() - - # Each button disables itself after being pressed. - self.use_per_motor = QPushButton("Per-Motor") - self.use_per_motor.clicked.connect(self.switch_to_motor) - - self.use_direct_velocity = QPushButton("Velocity Control") - self.use_direct_velocity.clicked.connect(self.switch_to_velocity) - - dbox.addWidget( - self.use_direct_velocity, alignment=Qt.AlignmentFlag.AlignCenter + self.connect_options_group = QButtonGroup() + radio_button_names = ["Velocity Control", "Per Motor Control"] + self.connect_options_box, self.connect_options = common_widgets.create_radio( + radio_button_names, self.connect_options_group ) - dbox.addWidget( - self.use_per_motor, alignment=Qt.AlignmentFlag.AlignCenter + self.use_direct_velocity = self.connect_options[DriveMode.VELOCITY] + self.use_per_motor = self.connect_options[DriveMode.MOTOR] + self.use_direct_velocity.clicked.connect( + self.switch_to_velocity ) - + self.use_per_motor.clicked.connect( + self.switch_to_motor + ) + dbox.addWidget(self.connect_options_box) group_box.setLayout(dbox) return group_box @@ -167,6 +188,7 @@ def setup_per_motor(self, title: str) -> QGroupBox: :param title: the name of the group box """ + group_box = QGroupBox(title) dbox = QVBoxLayout() @@ -297,6 +319,7 @@ def toggle_control_mode(self, use_direct: bool) -> None: if use_direct: # Show the direct velocity widget + self.per_motor = False self.drive_widget.setCurrentWidget(self.direct_velocity_widget) # Enable direct sliders and disable motor sliders @@ -316,11 +339,10 @@ def toggle_control_mode(self, use_direct: bool) -> None: common_widgets.disable_slider(self.back_right_motor_slider) common_widgets.change_button_state(self.stop_and_reset_direct, True) - common_widgets.change_button_state(self.use_direct_velocity, True) common_widgets.change_button_state(self.stop_and_reset_per_motor, False) - common_widgets.change_button_state(self.use_per_motor, False) else: + self.per_motor = True # Show the per motor widget self.drive_widget.setCurrentWidget(self.per_motor_widget) @@ -343,9 +365,7 @@ def toggle_control_mode(self, use_direct: bool) -> None: common_widgets.disable_slider(self.angular_velocity_slider) common_widgets.change_button_state(self.stop_and_reset_per_motor, True) - common_widgets.change_button_state(self.use_per_motor, True) common_widgets.change_button_state(self.stop_and_reset_direct, False) - common_widgets.change_button_state(self.use_direct_velocity, False) def toggle_dribbler_sliders(self, enable: bool) -> None: """Enables or disables dribbler sliders. From 61282d33f960f1b593f1cc917e4467657e21fcca Mon Sep 17 00:00:00 2001 From: Muk Date: Sat, 26 Oct 2024 15:56:52 -0700 Subject: [PATCH 03/10] per motor control works on robot --- .../robot_diagnostics/drive_and_dribbler_widget.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget.py b/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget.py index a3f5c65a16..4a0d2d66bb 100644 --- a/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget.py +++ b/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget.py @@ -52,6 +52,7 @@ def refresh(self) -> None: motor_control.dribbler_speed_rpm = int(self.dribbler_speed_rpm_slider.value()) if not self.per_motor: + motor_control.ClearField("direct_per_wheel_control") motor_control.direct_velocity_control.velocity.x_component_meters = ( self.x_velocity_slider.value() ) @@ -62,6 +63,7 @@ def refresh(self) -> None: self.angular_velocity_slider.value() ) else: + motor_control.ClearField("direct_velocity_control") motor_control.direct_per_wheel_control.front_left_wheel_velocity = ( self.front_left_motor_slider.value() ) @@ -74,6 +76,7 @@ def refresh(self) -> None: motor_control.direct_per_wheel_control.back_right_wheel_velocity = ( self.back_right_motor_slider.value() ) + self.proto_unix_io.send_proto(MotorControl, motor_control) def value_change(self, value: float) -> str: From 2b88ee68a0a266e5e01447616a4509f08fb35821 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci-lite[bot]" <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Date: Sat, 26 Oct 2024 23:41:09 +0000 Subject: [PATCH 04/10] [pre-commit.ci lite] apply automatic fixes --- .../drive_and_dribbler_widget.py | 41 +++++++++++-------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget.py b/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget.py index 4a0d2d66bb..233e092716 100644 --- a/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget.py +++ b/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget.py @@ -31,7 +31,9 @@ def __init__(self, proto_unix_io: ProtoUnixIO) -> None: self.proto_unix_io = proto_unix_io # create swappable widget system using stacked widgets - self.direct_velocity_widget = self.setup_direct_velocity("Drive - Direct Velocity") + self.direct_velocity_widget = self.setup_direct_velocity( + "Drive - Direct Velocity" + ) self.per_motor_widget = self.setup_per_motor("Drive - Per Motor") self.drive_widget.addWidget(self.direct_velocity_widget) self.drive_widget.addWidget(self.per_motor_widget) @@ -59,9 +61,7 @@ def refresh(self) -> None: motor_control.direct_velocity_control.velocity.y_component_meters = ( self.y_velocity_slider.value() ) - motor_control.direct_velocity_control.angular_velocity.radians_per_second = ( - self.angular_velocity_slider.value() - ) + motor_control.direct_velocity_control.angular_velocity.radians_per_second = self.angular_velocity_slider.value() else: motor_control.ClearField("direct_velocity_control") motor_control.direct_per_wheel_control.front_left_wheel_velocity = ( @@ -102,12 +102,8 @@ def setup_drive_switch_radio(self, title: str) -> QGroupBox: ) self.use_direct_velocity = self.connect_options[DriveMode.VELOCITY] self.use_per_motor = self.connect_options[DriveMode.MOTOR] - self.use_direct_velocity.clicked.connect( - self.switch_to_velocity - ) - self.use_per_motor.clicked.connect( - self.switch_to_motor - ) + self.use_direct_velocity.clicked.connect(self.switch_to_velocity) + self.use_per_motor.clicked.connect(self.switch_to_motor) dbox.addWidget(self.connect_options_box) group_box.setLayout(dbox) @@ -191,7 +187,6 @@ def setup_per_motor(self, title: str) -> QGroupBox: :param title: the name of the group box """ - group_box = QGroupBox(title) dbox = QVBoxLayout() @@ -245,7 +240,9 @@ def setup_per_motor(self, title: str) -> QGroupBox: self.front_left_motor_slider, self.front_left_motor_label, self.value_change ) common_widgets.enable_slider( - self.front_right_motor_slider, self.front_right_motor_label, self.value_change + self.front_right_motor_slider, + self.front_right_motor_label, + self.value_change, ) common_widgets.enable_slider( self.back_left_motor_slider, self.back_left_motor_label, self.value_change @@ -333,7 +330,9 @@ def toggle_control_mode(self, use_direct: bool) -> None: self.y_velocity_slider, self.y_velocity_label, self.value_change ) common_widgets.enable_slider( - self.angular_velocity_slider, self.angular_velocity_label, self.value_change + self.angular_velocity_slider, + self.angular_velocity_label, + self.value_change, ) common_widgets.disable_slider(self.front_left_motor_slider) @@ -351,16 +350,24 @@ def toggle_control_mode(self, use_direct: bool) -> None: # Enable motor sliders and disable direct sliders common_widgets.enable_slider( - self.front_left_motor_slider, self.front_left_motor_label, self.value_change + self.front_left_motor_slider, + self.front_left_motor_label, + self.value_change, ) common_widgets.enable_slider( - self.front_right_motor_slider, self.front_right_motor_label, self.value_change + self.front_right_motor_slider, + self.front_right_motor_label, + self.value_change, ) common_widgets.enable_slider( - self.back_left_motor_slider, self.back_left_motor_label, self.value_change + self.back_left_motor_slider, + self.back_left_motor_label, + self.value_change, ) common_widgets.enable_slider( - self.back_right_motor_slider, self.back_right_motor_label, self.value_change + self.back_right_motor_slider, + self.back_right_motor_label, + self.value_change, ) common_widgets.disable_slider(self.x_velocity_slider) From 7aa12bda1ee88a6c5e2c4470127ffdc59c52c813 Mon Sep 17 00:00:00 2001 From: Muk Date: Sat, 2 Nov 2024 16:09:13 -0700 Subject: [PATCH 05/10] per motor updated based on comments --- .../drive_and_dribbler_widget.py | 53 ++++++++----------- 1 file changed, 22 insertions(+), 31 deletions(-) diff --git a/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget.py b/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget.py index 4a0d2d66bb..ab0bc80320 100644 --- a/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget.py +++ b/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget.py @@ -10,7 +10,6 @@ class DriveMode(IntEnum): """Enum for the 2 drive modes (direct velocity and per-motor)""" - VELOCITY = 0 MOTOR = 1 @@ -40,10 +39,10 @@ def __init__(self, proto_unix_io: ProtoUnixIO) -> None: layout.addWidget(self.drive_widget) layout.addWidget(self.setup_dribbler("Dribbler")) - self.enabled = True - self.per_motor = False + self.drive_mode = DriveMode.MOTOR + self.use_direct_velocity.setChecked(True) self.setLayout(layout) - self.toggle_control_mode(True) + self.toggle_drive_mode(True) self.toggle_dribbler_sliders(True) def refresh(self) -> None: @@ -51,29 +50,29 @@ def refresh(self) -> None: motor_control = MotorControl() motor_control.dribbler_speed_rpm = int(self.dribbler_speed_rpm_slider.value()) - if not self.per_motor: + if not self.drive_mode == DriveMode.VELOCITY: motor_control.ClearField("direct_per_wheel_control") - motor_control.direct_velocity_control.velocity.x_component_meters = ( + motor_control.direct_velocity_drive.velocity.x_component_meters = ( self.x_velocity_slider.value() ) - motor_control.direct_velocity_control.velocity.y_component_meters = ( + motor_control.direct_velocity_drive.velocity.y_component_meters = ( self.y_velocity_slider.value() ) - motor_control.direct_velocity_control.angular_velocity.radians_per_second = ( + motor_control.direct_velocity_drive.angular_velocity.radians_per_second = ( self.angular_velocity_slider.value() ) else: motor_control.ClearField("direct_velocity_control") - motor_control.direct_per_wheel_control.front_left_wheel_velocity = ( + motor_control.direct_per_wheel_drive.front_left_wheel_velocity = ( self.front_left_motor_slider.value() ) - motor_control.direct_per_wheel_control.front_right_wheel_velocity = ( + motor_control.direct_per_wheel_drive.front_right_wheel_velocity = ( self.front_right_motor_slider.value() ) - motor_control.direct_per_wheel_control.back_left_wheel_velocity = ( + motor_control.direct_per_wheel_drive.back_left_wheel_velocity = ( self.back_left_motor_slider.value() ) - motor_control.direct_per_wheel_control.back_right_wheel_velocity = ( + motor_control.direct_per_wheel_drive.back_right_wheel_velocity = ( self.back_right_motor_slider.value() ) @@ -89,12 +88,12 @@ def value_change(self, value: float) -> str: return value_str def setup_drive_switch_radio(self, title: str) -> QGroupBox: - """Create a radio button widget to switch between per-motor and velocity control modes + """Create a radio button widget to switch between per-motor and velocity drive modes - :param title: group box name + :param title: vbox name """ group_box = QGroupBox(title) - dbox = QVBoxLayout() + vbox = QVBoxLayout() self.connect_options_group = QButtonGroup() radio_button_names = ["Velocity Control", "Per Motor Control"] self.connect_options_box, self.connect_options = common_widgets.create_radio( @@ -103,22 +102,16 @@ def setup_drive_switch_radio(self, title: str) -> QGroupBox: self.use_direct_velocity = self.connect_options[DriveMode.VELOCITY] self.use_per_motor = self.connect_options[DriveMode.MOTOR] self.use_direct_velocity.clicked.connect( - self.switch_to_velocity + lambda: self.toggle_drive_mode(True) ) self.use_per_motor.clicked.connect( - self.switch_to_motor + lambda: self.toggle_drive_mode(False) ) - dbox.addWidget(self.connect_options_box) - group_box.setLayout(dbox) + vbox.addWidget(self.connect_options_box) + group_box.setLayout(vbox) return group_box - def switch_to_velocity(self): - self.toggle_control_mode(True) - - def switch_to_motor(self): - self.toggle_control_mode(False) - def setup_direct_velocity(self, title: str) -> QGroupBox: """Create a widget to control the direct velocity of the robot's motors @@ -309,10 +302,10 @@ def setup_dribbler(self, title: str) -> QGroupBox: return group_box - def toggle_control_mode(self, use_direct: bool) -> None: - """Switches between 'Direct Velocity' and 'Per Motor' control modes. + def toggle_drive_mode(self, use_drive_mode: IntEnum) -> None: + """Switches between 'Direct Velocity' and 'Per Motor' drive modes. - :param use_direct: True to enable Direct Velocity mode, False to enable Per Motor mode. + :param use_drive_mode: DriveMode.VELOCITY or DriveMode.MOTOR, switch to that mode. """ # reset sliders self.reset_motor_sliders() @@ -320,9 +313,8 @@ def toggle_control_mode(self, use_direct: bool) -> None: self.disconnect_direct_sliders() self.disconnect_motor_sliders() - if use_direct: + if use_drive_mode == DriveMode.VELOCITY: # Show the direct velocity widget - self.per_motor = False self.drive_widget.setCurrentWidget(self.direct_velocity_widget) # Enable direct sliders and disable motor sliders @@ -345,7 +337,6 @@ def toggle_control_mode(self, use_direct: bool) -> None: common_widgets.change_button_state(self.stop_and_reset_per_motor, False) else: - self.per_motor = True # Show the per motor widget self.drive_widget.setCurrentWidget(self.per_motor_widget) From 4e02b056fcff7f7143ff5843766d877e1c89391f Mon Sep 17 00:00:00 2001 From: Muk Date: Mon, 4 Nov 2024 01:45:50 -0800 Subject: [PATCH 06/10] per motor ready for second test --- .../drive_and_dribbler_widget.py | 10 +- .../drive_and_dribbler_widget_BACKUP_15256.py | 440 ++++++++++++++++++ .../drive_and_dribbler_widget_BASE_15256.py | 425 +++++++++++++++++ .../drive_and_dribbler_widget_LOCAL_15256.py | 416 +++++++++++++++++ .../drive_and_dribbler_widget_REMOTE_15256.py | 432 +++++++++++++++++ 5 files changed, 1719 insertions(+), 4 deletions(-) create mode 100644 src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget_BACKUP_15256.py create mode 100644 src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget_BASE_15256.py create mode 100644 src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget_LOCAL_15256.py create mode 100644 src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget_REMOTE_15256.py diff --git a/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget.py b/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget.py index ab0bc80320..7ab773f8ee 100644 --- a/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget.py +++ b/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget.py @@ -39,10 +39,11 @@ def __init__(self, proto_unix_io: ProtoUnixIO) -> None: layout.addWidget(self.drive_widget) layout.addWidget(self.setup_dribbler("Dribbler")) - self.drive_mode = DriveMode.MOTOR + # default to direct velocity + self.drive_mode = DriveMode.VELOCITY self.use_direct_velocity.setChecked(True) + self.toggle_drive_mode(DriveMode.VELOCITY) self.setLayout(layout) - self.toggle_drive_mode(True) self.toggle_dribbler_sliders(True) def refresh(self) -> None: @@ -102,10 +103,10 @@ def setup_drive_switch_radio(self, title: str) -> QGroupBox: self.use_direct_velocity = self.connect_options[DriveMode.VELOCITY] self.use_per_motor = self.connect_options[DriveMode.MOTOR] self.use_direct_velocity.clicked.connect( - lambda: self.toggle_drive_mode(True) + lambda: self.toggle_drive_mode(DriveMode.VELOCITY) ) self.use_per_motor.clicked.connect( - lambda: self.toggle_drive_mode(False) + lambda: self.toggle_drive_mode(DriveMode.MOTOR) ) vbox.addWidget(self.connect_options_box) group_box.setLayout(vbox) @@ -314,6 +315,7 @@ def toggle_drive_mode(self, use_drive_mode: IntEnum) -> None: self.disconnect_motor_sliders() if use_drive_mode == DriveMode.VELOCITY: + # Show the direct velocity widget self.drive_widget.setCurrentWidget(self.direct_velocity_widget) diff --git a/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget_BACKUP_15256.py b/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget_BACKUP_15256.py new file mode 100644 index 0000000000..ac2f3e977c --- /dev/null +++ b/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget_BACKUP_15256.py @@ -0,0 +1,440 @@ +from pyqtgraph.Qt.QtCore import Qt +from pyqtgraph.Qt.QtWidgets import * +import time +import software.python_bindings as tbots_cpp +from software.thunderscope.common import common_widgets +from proto.import_all_protos import * +from software.thunderscope.proto_unix_io import ProtoUnixIO +from enum import IntEnum + + +class DriveMode(IntEnum): + """Enum for the 2 drive modes (direct velocity and per-motor)""" + VELOCITY = 0 + MOTOR = 1 + + +class DriveAndDribblerWidget(QWidget): + def __init__(self, proto_unix_io: ProtoUnixIO) -> None: + """Initialize the widget to control the robot's motors + + :param proto_unix_io: the proto_unix_io object + """ + self.input_a = time.time() + self.constants = tbots_cpp.create2021RobotConstants() + QWidget.__init__(self) + + layout = QVBoxLayout() + self.drive_widget = QStackedWidget() + + self.proto_unix_io = proto_unix_io + + # create swappable widget system using stacked widgets + self.direct_velocity_widget = self.setup_direct_velocity( + "Drive - Direct Velocity" + ) + self.per_motor_widget = self.setup_per_motor("Drive - Per Motor") + self.drive_widget.addWidget(self.direct_velocity_widget) + self.drive_widget.addWidget(self.per_motor_widget) + + layout.addWidget(self.setup_drive_switch_radio("Drive Mode Switch")) + layout.addWidget(self.drive_widget) + layout.addWidget(self.setup_dribbler("Dribbler")) + + self.drive_mode = DriveMode.MOTOR + self.use_direct_velocity.setChecked(True) + self.setLayout(layout) + self.toggle_drive_mode(True) + self.toggle_dribbler_sliders(True) + + def refresh(self) -> None: + """Refresh the widget and send the a MotorControl message with the current values""" + motor_control = MotorControl() + motor_control.dribbler_speed_rpm = int(self.dribbler_speed_rpm_slider.value()) + + if not self.drive_mode == DriveMode.VELOCITY: + motor_control.ClearField("direct_per_wheel_control") + motor_control.direct_velocity_drive.velocity.x_component_meters = ( + self.x_velocity_slider.value() + ) + motor_control.direct_velocity_drive.velocity.y_component_meters = ( + self.y_velocity_slider.value() + ) +<<<<<<< HEAD + motor_control.direct_velocity_drive.angular_velocity.radians_per_second = ( + self.angular_velocity_slider.value() + ) +======= + motor_control.direct_velocity_control.angular_velocity.radians_per_second = self.angular_velocity_slider.value() +>>>>>>> 2b88ee68a0a266e5e01447616a4509f08fb35821 + else: + motor_control.ClearField("direct_velocity_control") + motor_control.direct_per_wheel_drive.front_left_wheel_velocity = ( + self.front_left_motor_slider.value() + ) + motor_control.direct_per_wheel_drive.front_right_wheel_velocity = ( + self.front_right_motor_slider.value() + ) + motor_control.direct_per_wheel_drive.back_left_wheel_velocity = ( + self.back_left_motor_slider.value() + ) + motor_control.direct_per_wheel_drive.back_right_wheel_velocity = ( + self.back_right_motor_slider.value() + ) + + self.proto_unix_io.send_proto(MotorControl, motor_control) + + def value_change(self, value: float) -> str: + """Converts the given float value to a string label + + :param value: float value to be converted + """ + value = float(value) + value_str = "%.2f" % value + return value_str + + def setup_drive_switch_radio(self, title: str) -> QGroupBox: + """Create a radio button widget to switch between per-motor and velocity drive modes + + :param title: vbox name + """ + group_box = QGroupBox(title) + vbox = QVBoxLayout() + self.connect_options_group = QButtonGroup() + radio_button_names = ["Velocity Control", "Per Motor Control"] + self.connect_options_box, self.connect_options = common_widgets.create_radio( + radio_button_names, self.connect_options_group + ) + self.use_direct_velocity = self.connect_options[DriveMode.VELOCITY] + self.use_per_motor = self.connect_options[DriveMode.MOTOR] +<<<<<<< HEAD + self.use_direct_velocity.clicked.connect( + lambda: self.toggle_drive_mode(True) + ) + self.use_per_motor.clicked.connect( + lambda: self.toggle_drive_mode(False) + ) + vbox.addWidget(self.connect_options_box) + group_box.setLayout(vbox) +======= + self.use_direct_velocity.clicked.connect(self.switch_to_velocity) + self.use_per_motor.clicked.connect(self.switch_to_motor) + dbox.addWidget(self.connect_options_box) + group_box.setLayout(dbox) +>>>>>>> 2b88ee68a0a266e5e01447616a4509f08fb35821 + + return group_box + + def setup_direct_velocity(self, title: str) -> QGroupBox: + """Create a widget to control the direct velocity of the robot's motors + + :param title: the name of the slider + """ + group_box = QGroupBox(title) + dbox = QVBoxLayout() + + ( + x_layout, + self.x_velocity_slider, + self.x_velocity_label, + ) = common_widgets.create_float_slider( + "X (m/s)", + 2, + -self.constants.robot_max_speed_m_per_s, + self.constants.robot_max_speed_m_per_s, + 1, + ) + ( + y_layout, + self.y_velocity_slider, + self.y_velocity_label, + ) = common_widgets.create_float_slider( + "Y (m/s)", + 2, + -self.constants.robot_max_speed_m_per_s, + self.constants.robot_max_speed_m_per_s, + 1, + ) + ( + dps_layout, + self.angular_velocity_slider, + self.angular_velocity_label, + ) = common_widgets.create_float_slider( + "θ (rad/s)", + 2, + -self.constants.robot_max_ang_speed_rad_per_s, + self.constants.robot_max_ang_speed_rad_per_s, + 1, + ) + + # add listener functions for sliders to update label with slider value + common_widgets.enable_slider( + self.x_velocity_slider, self.x_velocity_label, self.value_change + ) + common_widgets.enable_slider( + self.y_velocity_slider, self.y_velocity_label, self.value_change + ) + common_widgets.enable_slider( + self.angular_velocity_slider, self.angular_velocity_label, self.value_change + ) + + self.stop_and_reset_direct = QPushButton("Stop and Reset") + self.stop_and_reset_direct.clicked.connect(self.reset_direct_sliders) + + dbox.addLayout(x_layout) + dbox.addLayout(y_layout) + dbox.addLayout(dps_layout) + dbox.addWidget( + self.stop_and_reset_direct, alignment=Qt.AlignmentFlag.AlignCenter + ) + + group_box.setLayout(dbox) + + return group_box + + def setup_per_motor(self, title: str) -> QGroupBox: + """Create a widget to control the rotation rate of each motor + + :param title: the name of the group box + """ + group_box = QGroupBox(title) + dbox = QVBoxLayout() + + ( + fl_layout, + self.front_left_motor_slider, + self.front_left_motor_label, + ) = common_widgets.create_float_slider( + "Front Left Motor (m/s)", + 2, + -self.constants.robot_max_speed_m_per_s, + self.constants.robot_max_speed_m_per_s, + 1, + ) + ( + fr_layout, + self.front_right_motor_slider, + self.front_right_motor_label, + ) = common_widgets.create_float_slider( + "Front Right Motor (m/s)", + 2, + -self.constants.robot_max_speed_m_per_s, + self.constants.robot_max_speed_m_per_s, + 1, + ) + ( + bl_layout, + self.back_left_motor_slider, + self.back_left_motor_label, + ) = common_widgets.create_float_slider( + "Back Left Motor (m/s)", + 2, + -self.constants.robot_max_speed_m_per_s, + self.constants.robot_max_speed_m_per_s, + 1, + ) + ( + br_layout, + self.back_right_motor_slider, + self.back_right_motor_label, + ) = common_widgets.create_float_slider( + "Back Right Motor (m/s)", + 2, + -self.constants.robot_max_speed_m_per_s, + self.constants.robot_max_speed_m_per_s, + 1, + ) + + # Add listener functions for each motor's slider to update labels with slider values + common_widgets.enable_slider( + self.front_left_motor_slider, self.front_left_motor_label, self.value_change + ) + common_widgets.enable_slider( + self.front_right_motor_slider, + self.front_right_motor_label, + self.value_change, + ) + common_widgets.enable_slider( + self.back_left_motor_slider, self.back_left_motor_label, self.value_change + ) + common_widgets.enable_slider( + self.back_right_motor_slider, self.back_right_motor_label, self.value_change + ) + + # Stop and Reset button for per-motor sliders + self.stop_and_reset_per_motor = QPushButton("Stop and Reset") + self.stop_and_reset_per_motor.clicked.connect(self.reset_motor_sliders) + + # Adding layouts to the box + dbox.addLayout(fl_layout) + dbox.addLayout(fr_layout) + dbox.addLayout(bl_layout) + dbox.addLayout(br_layout) + dbox.addWidget( + self.stop_and_reset_per_motor, alignment=Qt.AlignmentFlag.AlignCenter + ) + + group_box.setLayout(dbox) + + return group_box + + def setup_dribbler(self, title: str) -> QGroupBox: + """Create a widget to control the dribbler RPM + + :param title: the name of the slider + """ + group_box = QGroupBox(title) + dbox = QVBoxLayout() + + ( + dribbler_layout, + self.dribbler_speed_rpm_slider, + self.dribbler_speed_rpm_label, + ) = common_widgets.create_float_slider( + "RPM", + 1, + self.constants.indefinite_dribbler_speed_rpm, + -self.constants.indefinite_dribbler_speed_rpm, + 1, + ) + + # add listener function to update label with slider value + common_widgets.enable_slider( + self.dribbler_speed_rpm_slider, + self.dribbler_speed_rpm_label, + self.value_change, + ) + + self.stop_and_reset_dribbler = QPushButton("Stop and Reset") + self.stop_and_reset_dribbler.clicked.connect(self.reset_dribbler_slider) + + dbox.addLayout(dribbler_layout) + dbox.addWidget( + self.stop_and_reset_dribbler, alignment=Qt.AlignmentFlag.AlignCenter + ) + group_box.setLayout(dbox) + + return group_box + + def toggle_drive_mode(self, use_drive_mode: IntEnum) -> None: + """Switches between 'Direct Velocity' and 'Per Motor' drive modes. + + :param use_drive_mode: DriveMode.VELOCITY or DriveMode.MOTOR, switch to that mode. + """ + # reset sliders + self.reset_motor_sliders() + self.reset_direct_sliders() + self.disconnect_direct_sliders() + self.disconnect_motor_sliders() + + if use_drive_mode == DriveMode.VELOCITY: + # Show the direct velocity widget + self.drive_widget.setCurrentWidget(self.direct_velocity_widget) + + # Enable direct sliders and disable motor sliders + common_widgets.enable_slider( + self.x_velocity_slider, self.x_velocity_label, self.value_change + ) + common_widgets.enable_slider( + self.y_velocity_slider, self.y_velocity_label, self.value_change + ) + common_widgets.enable_slider( + self.angular_velocity_slider, + self.angular_velocity_label, + self.value_change, + ) + + common_widgets.disable_slider(self.front_left_motor_slider) + common_widgets.disable_slider(self.front_right_motor_slider) + common_widgets.disable_slider(self.back_left_motor_slider) + common_widgets.disable_slider(self.back_right_motor_slider) + + common_widgets.change_button_state(self.stop_and_reset_direct, True) + common_widgets.change_button_state(self.stop_and_reset_per_motor, False) + + else: + # Show the per motor widget + self.drive_widget.setCurrentWidget(self.per_motor_widget) + + # Enable motor sliders and disable direct sliders + common_widgets.enable_slider( + self.front_left_motor_slider, + self.front_left_motor_label, + self.value_change, + ) + common_widgets.enable_slider( + self.front_right_motor_slider, + self.front_right_motor_label, + self.value_change, + ) + common_widgets.enable_slider( + self.back_left_motor_slider, + self.back_left_motor_label, + self.value_change, + ) + common_widgets.enable_slider( + self.back_right_motor_slider, + self.back_right_motor_label, + self.value_change, + ) + + common_widgets.disable_slider(self.x_velocity_slider) + common_widgets.disable_slider(self.y_velocity_slider) + common_widgets.disable_slider(self.angular_velocity_slider) + + common_widgets.change_button_state(self.stop_and_reset_per_motor, True) + common_widgets.change_button_state(self.stop_and_reset_direct, False) + + def toggle_dribbler_sliders(self, enable: bool) -> None: + """Enables or disables dribbler sliders. + + :param enable: True to enable, False to disable + """ + if enable: + common_widgets.enable_slider( + self.dribbler_speed_rpm_slider, + self.dribbler_speed_rpm_label, + self.value_change, + ) + common_widgets.change_button_state(self.stop_and_reset_dribbler, True) + else: + common_widgets.disable_slider(self.dribbler_speed_rpm_slider) + common_widgets.change_button_state(self.stop_and_reset_dribbler, False) + + def disconnect_direct_sliders(self) -> None: + """Disconnect listener for changing values for motor sliders""" + self.x_velocity_slider.valueChanged.disconnect() + self.y_velocity_slider.valueChanged.disconnect() + self.angular_velocity_slider.valueChanged.disconnect() + + def disconnect_dribbler_sliders(self) -> None: + self.dribbler_speed_rpm_slider.valueChanged.disconnect() + + def disconnect_motor_sliders(self) -> None: + self.front_left_motor_slider.valueChanged.disconnect() + self.front_right_motor_slider.valueChanged.disconnect() + self.back_left_motor_slider.valueChanged.disconnect() + self.back_right_motor_slider.valueChanged.disconnect() + + def reset_direct_sliders(self) -> None: + """Reset direct sliders back to 0""" + self.x_velocity_slider.setValue(0) + self.y_velocity_slider.setValue(0) + self.angular_velocity_slider.setValue(0) + + def reset_motor_sliders(self) -> None: + """Reset direct sliders back to 0""" + self.front_left_motor_slider.setValue(0) + self.front_right_motor_slider.setValue(0) + self.back_left_motor_slider.setValue(0) + self.back_right_motor_slider.setValue(0) + + def reset_dribbler_slider(self) -> None: + """Reset the dribbler slider back to 0""" + self.dribbler_speed_rpm_slider.setValue(0) + + def reset_all_sliders(self) -> None: + """Reset all sliders back to 0""" + self.reset_direct_sliders() + self.reset_motor_sliders() + self.reset_dribbler_slider() diff --git a/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget_BASE_15256.py b/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget_BASE_15256.py new file mode 100644 index 0000000000..4a0d2d66bb --- /dev/null +++ b/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget_BASE_15256.py @@ -0,0 +1,425 @@ +from pyqtgraph.Qt.QtCore import Qt +from pyqtgraph.Qt.QtWidgets import * +import time +import software.python_bindings as tbots_cpp +from software.thunderscope.common import common_widgets +from proto.import_all_protos import * +from software.thunderscope.proto_unix_io import ProtoUnixIO +from enum import IntEnum + + +class DriveMode(IntEnum): + """Enum for the 2 drive modes (direct velocity and per-motor)""" + + VELOCITY = 0 + MOTOR = 1 + + +class DriveAndDribblerWidget(QWidget): + def __init__(self, proto_unix_io: ProtoUnixIO) -> None: + """Initialize the widget to control the robot's motors + + :param proto_unix_io: the proto_unix_io object + """ + self.input_a = time.time() + self.constants = tbots_cpp.create2021RobotConstants() + QWidget.__init__(self) + + layout = QVBoxLayout() + self.drive_widget = QStackedWidget() + + self.proto_unix_io = proto_unix_io + + # create swappable widget system using stacked widgets + self.direct_velocity_widget = self.setup_direct_velocity("Drive - Direct Velocity") + self.per_motor_widget = self.setup_per_motor("Drive - Per Motor") + self.drive_widget.addWidget(self.direct_velocity_widget) + self.drive_widget.addWidget(self.per_motor_widget) + + layout.addWidget(self.setup_drive_switch_radio("Drive Mode Switch")) + layout.addWidget(self.drive_widget) + layout.addWidget(self.setup_dribbler("Dribbler")) + + self.enabled = True + self.per_motor = False + self.setLayout(layout) + self.toggle_control_mode(True) + self.toggle_dribbler_sliders(True) + + def refresh(self) -> None: + """Refresh the widget and send the a MotorControl message with the current values""" + motor_control = MotorControl() + motor_control.dribbler_speed_rpm = int(self.dribbler_speed_rpm_slider.value()) + + if not self.per_motor: + motor_control.ClearField("direct_per_wheel_control") + motor_control.direct_velocity_control.velocity.x_component_meters = ( + self.x_velocity_slider.value() + ) + motor_control.direct_velocity_control.velocity.y_component_meters = ( + self.y_velocity_slider.value() + ) + motor_control.direct_velocity_control.angular_velocity.radians_per_second = ( + self.angular_velocity_slider.value() + ) + else: + motor_control.ClearField("direct_velocity_control") + motor_control.direct_per_wheel_control.front_left_wheel_velocity = ( + self.front_left_motor_slider.value() + ) + motor_control.direct_per_wheel_control.front_right_wheel_velocity = ( + self.front_right_motor_slider.value() + ) + motor_control.direct_per_wheel_control.back_left_wheel_velocity = ( + self.back_left_motor_slider.value() + ) + motor_control.direct_per_wheel_control.back_right_wheel_velocity = ( + self.back_right_motor_slider.value() + ) + + self.proto_unix_io.send_proto(MotorControl, motor_control) + + def value_change(self, value: float) -> str: + """Converts the given float value to a string label + + :param value: float value to be converted + """ + value = float(value) + value_str = "%.2f" % value + return value_str + + def setup_drive_switch_radio(self, title: str) -> QGroupBox: + """Create a radio button widget to switch between per-motor and velocity control modes + + :param title: group box name + """ + group_box = QGroupBox(title) + dbox = QVBoxLayout() + self.connect_options_group = QButtonGroup() + radio_button_names = ["Velocity Control", "Per Motor Control"] + self.connect_options_box, self.connect_options = common_widgets.create_radio( + radio_button_names, self.connect_options_group + ) + self.use_direct_velocity = self.connect_options[DriveMode.VELOCITY] + self.use_per_motor = self.connect_options[DriveMode.MOTOR] + self.use_direct_velocity.clicked.connect( + self.switch_to_velocity + ) + self.use_per_motor.clicked.connect( + self.switch_to_motor + ) + dbox.addWidget(self.connect_options_box) + group_box.setLayout(dbox) + + return group_box + + def switch_to_velocity(self): + self.toggle_control_mode(True) + + def switch_to_motor(self): + self.toggle_control_mode(False) + + def setup_direct_velocity(self, title: str) -> QGroupBox: + """Create a widget to control the direct velocity of the robot's motors + + :param title: the name of the slider + """ + group_box = QGroupBox(title) + dbox = QVBoxLayout() + + ( + x_layout, + self.x_velocity_slider, + self.x_velocity_label, + ) = common_widgets.create_float_slider( + "X (m/s)", + 2, + -self.constants.robot_max_speed_m_per_s, + self.constants.robot_max_speed_m_per_s, + 1, + ) + ( + y_layout, + self.y_velocity_slider, + self.y_velocity_label, + ) = common_widgets.create_float_slider( + "Y (m/s)", + 2, + -self.constants.robot_max_speed_m_per_s, + self.constants.robot_max_speed_m_per_s, + 1, + ) + ( + dps_layout, + self.angular_velocity_slider, + self.angular_velocity_label, + ) = common_widgets.create_float_slider( + "θ (rad/s)", + 2, + -self.constants.robot_max_ang_speed_rad_per_s, + self.constants.robot_max_ang_speed_rad_per_s, + 1, + ) + + # add listener functions for sliders to update label with slider value + common_widgets.enable_slider( + self.x_velocity_slider, self.x_velocity_label, self.value_change + ) + common_widgets.enable_slider( + self.y_velocity_slider, self.y_velocity_label, self.value_change + ) + common_widgets.enable_slider( + self.angular_velocity_slider, self.angular_velocity_label, self.value_change + ) + + self.stop_and_reset_direct = QPushButton("Stop and Reset") + self.stop_and_reset_direct.clicked.connect(self.reset_direct_sliders) + + dbox.addLayout(x_layout) + dbox.addLayout(y_layout) + dbox.addLayout(dps_layout) + dbox.addWidget( + self.stop_and_reset_direct, alignment=Qt.AlignmentFlag.AlignCenter + ) + + group_box.setLayout(dbox) + + return group_box + + def setup_per_motor(self, title: str) -> QGroupBox: + """Create a widget to control the rotation rate of each motor + + :param title: the name of the group box + """ + + group_box = QGroupBox(title) + dbox = QVBoxLayout() + + ( + fl_layout, + self.front_left_motor_slider, + self.front_left_motor_label, + ) = common_widgets.create_float_slider( + "Front Left Motor (m/s)", + 2, + -self.constants.robot_max_speed_m_per_s, + self.constants.robot_max_speed_m_per_s, + 1, + ) + ( + fr_layout, + self.front_right_motor_slider, + self.front_right_motor_label, + ) = common_widgets.create_float_slider( + "Front Right Motor (m/s)", + 2, + -self.constants.robot_max_speed_m_per_s, + self.constants.robot_max_speed_m_per_s, + 1, + ) + ( + bl_layout, + self.back_left_motor_slider, + self.back_left_motor_label, + ) = common_widgets.create_float_slider( + "Back Left Motor (m/s)", + 2, + -self.constants.robot_max_speed_m_per_s, + self.constants.robot_max_speed_m_per_s, + 1, + ) + ( + br_layout, + self.back_right_motor_slider, + self.back_right_motor_label, + ) = common_widgets.create_float_slider( + "Back Right Motor (m/s)", + 2, + -self.constants.robot_max_speed_m_per_s, + self.constants.robot_max_speed_m_per_s, + 1, + ) + + # Add listener functions for each motor's slider to update labels with slider values + common_widgets.enable_slider( + self.front_left_motor_slider, self.front_left_motor_label, self.value_change + ) + common_widgets.enable_slider( + self.front_right_motor_slider, self.front_right_motor_label, self.value_change + ) + common_widgets.enable_slider( + self.back_left_motor_slider, self.back_left_motor_label, self.value_change + ) + common_widgets.enable_slider( + self.back_right_motor_slider, self.back_right_motor_label, self.value_change + ) + + # Stop and Reset button for per-motor sliders + self.stop_and_reset_per_motor = QPushButton("Stop and Reset") + self.stop_and_reset_per_motor.clicked.connect(self.reset_motor_sliders) + + # Adding layouts to the box + dbox.addLayout(fl_layout) + dbox.addLayout(fr_layout) + dbox.addLayout(bl_layout) + dbox.addLayout(br_layout) + dbox.addWidget( + self.stop_and_reset_per_motor, alignment=Qt.AlignmentFlag.AlignCenter + ) + + group_box.setLayout(dbox) + + return group_box + + def setup_dribbler(self, title: str) -> QGroupBox: + """Create a widget to control the dribbler RPM + + :param title: the name of the slider + """ + group_box = QGroupBox(title) + dbox = QVBoxLayout() + + ( + dribbler_layout, + self.dribbler_speed_rpm_slider, + self.dribbler_speed_rpm_label, + ) = common_widgets.create_float_slider( + "RPM", + 1, + self.constants.indefinite_dribbler_speed_rpm, + -self.constants.indefinite_dribbler_speed_rpm, + 1, + ) + + # add listener function to update label with slider value + common_widgets.enable_slider( + self.dribbler_speed_rpm_slider, + self.dribbler_speed_rpm_label, + self.value_change, + ) + + self.stop_and_reset_dribbler = QPushButton("Stop and Reset") + self.stop_and_reset_dribbler.clicked.connect(self.reset_dribbler_slider) + + dbox.addLayout(dribbler_layout) + dbox.addWidget( + self.stop_and_reset_dribbler, alignment=Qt.AlignmentFlag.AlignCenter + ) + group_box.setLayout(dbox) + + return group_box + + def toggle_control_mode(self, use_direct: bool) -> None: + """Switches between 'Direct Velocity' and 'Per Motor' control modes. + + :param use_direct: True to enable Direct Velocity mode, False to enable Per Motor mode. + """ + # reset sliders + self.reset_motor_sliders() + self.reset_direct_sliders() + self.disconnect_direct_sliders() + self.disconnect_motor_sliders() + + if use_direct: + # Show the direct velocity widget + self.per_motor = False + self.drive_widget.setCurrentWidget(self.direct_velocity_widget) + + # Enable direct sliders and disable motor sliders + common_widgets.enable_slider( + self.x_velocity_slider, self.x_velocity_label, self.value_change + ) + common_widgets.enable_slider( + self.y_velocity_slider, self.y_velocity_label, self.value_change + ) + common_widgets.enable_slider( + self.angular_velocity_slider, self.angular_velocity_label, self.value_change + ) + + common_widgets.disable_slider(self.front_left_motor_slider) + common_widgets.disable_slider(self.front_right_motor_slider) + common_widgets.disable_slider(self.back_left_motor_slider) + common_widgets.disable_slider(self.back_right_motor_slider) + + common_widgets.change_button_state(self.stop_and_reset_direct, True) + common_widgets.change_button_state(self.stop_and_reset_per_motor, False) + + else: + self.per_motor = True + # Show the per motor widget + self.drive_widget.setCurrentWidget(self.per_motor_widget) + + # Enable motor sliders and disable direct sliders + common_widgets.enable_slider( + self.front_left_motor_slider, self.front_left_motor_label, self.value_change + ) + common_widgets.enable_slider( + self.front_right_motor_slider, self.front_right_motor_label, self.value_change + ) + common_widgets.enable_slider( + self.back_left_motor_slider, self.back_left_motor_label, self.value_change + ) + common_widgets.enable_slider( + self.back_right_motor_slider, self.back_right_motor_label, self.value_change + ) + + common_widgets.disable_slider(self.x_velocity_slider) + common_widgets.disable_slider(self.y_velocity_slider) + common_widgets.disable_slider(self.angular_velocity_slider) + + common_widgets.change_button_state(self.stop_and_reset_per_motor, True) + common_widgets.change_button_state(self.stop_and_reset_direct, False) + + def toggle_dribbler_sliders(self, enable: bool) -> None: + """Enables or disables dribbler sliders. + + :param enable: True to enable, False to disable + """ + if enable: + common_widgets.enable_slider( + self.dribbler_speed_rpm_slider, + self.dribbler_speed_rpm_label, + self.value_change, + ) + common_widgets.change_button_state(self.stop_and_reset_dribbler, True) + else: + common_widgets.disable_slider(self.dribbler_speed_rpm_slider) + common_widgets.change_button_state(self.stop_and_reset_dribbler, False) + + def disconnect_direct_sliders(self) -> None: + """Disconnect listener for changing values for motor sliders""" + self.x_velocity_slider.valueChanged.disconnect() + self.y_velocity_slider.valueChanged.disconnect() + self.angular_velocity_slider.valueChanged.disconnect() + + def disconnect_dribbler_sliders(self) -> None: + self.dribbler_speed_rpm_slider.valueChanged.disconnect() + + def disconnect_motor_sliders(self) -> None: + self.front_left_motor_slider.valueChanged.disconnect() + self.front_right_motor_slider.valueChanged.disconnect() + self.back_left_motor_slider.valueChanged.disconnect() + self.back_right_motor_slider.valueChanged.disconnect() + + def reset_direct_sliders(self) -> None: + """Reset direct sliders back to 0""" + self.x_velocity_slider.setValue(0) + self.y_velocity_slider.setValue(0) + self.angular_velocity_slider.setValue(0) + + def reset_motor_sliders(self) -> None: + """Reset direct sliders back to 0""" + self.front_left_motor_slider.setValue(0) + self.front_right_motor_slider.setValue(0) + self.back_left_motor_slider.setValue(0) + self.back_right_motor_slider.setValue(0) + + def reset_dribbler_slider(self) -> None: + """Reset the dribbler slider back to 0""" + self.dribbler_speed_rpm_slider.setValue(0) + + def reset_all_sliders(self) -> None: + """Reset all sliders back to 0""" + self.reset_direct_sliders() + self.reset_motor_sliders() + self.reset_dribbler_slider() diff --git a/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget_LOCAL_15256.py b/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget_LOCAL_15256.py new file mode 100644 index 0000000000..ab0bc80320 --- /dev/null +++ b/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget_LOCAL_15256.py @@ -0,0 +1,416 @@ +from pyqtgraph.Qt.QtCore import Qt +from pyqtgraph.Qt.QtWidgets import * +import time +import software.python_bindings as tbots_cpp +from software.thunderscope.common import common_widgets +from proto.import_all_protos import * +from software.thunderscope.proto_unix_io import ProtoUnixIO +from enum import IntEnum + + +class DriveMode(IntEnum): + """Enum for the 2 drive modes (direct velocity and per-motor)""" + VELOCITY = 0 + MOTOR = 1 + + +class DriveAndDribblerWidget(QWidget): + def __init__(self, proto_unix_io: ProtoUnixIO) -> None: + """Initialize the widget to control the robot's motors + + :param proto_unix_io: the proto_unix_io object + """ + self.input_a = time.time() + self.constants = tbots_cpp.create2021RobotConstants() + QWidget.__init__(self) + + layout = QVBoxLayout() + self.drive_widget = QStackedWidget() + + self.proto_unix_io = proto_unix_io + + # create swappable widget system using stacked widgets + self.direct_velocity_widget = self.setup_direct_velocity("Drive - Direct Velocity") + self.per_motor_widget = self.setup_per_motor("Drive - Per Motor") + self.drive_widget.addWidget(self.direct_velocity_widget) + self.drive_widget.addWidget(self.per_motor_widget) + + layout.addWidget(self.setup_drive_switch_radio("Drive Mode Switch")) + layout.addWidget(self.drive_widget) + layout.addWidget(self.setup_dribbler("Dribbler")) + + self.drive_mode = DriveMode.MOTOR + self.use_direct_velocity.setChecked(True) + self.setLayout(layout) + self.toggle_drive_mode(True) + self.toggle_dribbler_sliders(True) + + def refresh(self) -> None: + """Refresh the widget and send the a MotorControl message with the current values""" + motor_control = MotorControl() + motor_control.dribbler_speed_rpm = int(self.dribbler_speed_rpm_slider.value()) + + if not self.drive_mode == DriveMode.VELOCITY: + motor_control.ClearField("direct_per_wheel_control") + motor_control.direct_velocity_drive.velocity.x_component_meters = ( + self.x_velocity_slider.value() + ) + motor_control.direct_velocity_drive.velocity.y_component_meters = ( + self.y_velocity_slider.value() + ) + motor_control.direct_velocity_drive.angular_velocity.radians_per_second = ( + self.angular_velocity_slider.value() + ) + else: + motor_control.ClearField("direct_velocity_control") + motor_control.direct_per_wheel_drive.front_left_wheel_velocity = ( + self.front_left_motor_slider.value() + ) + motor_control.direct_per_wheel_drive.front_right_wheel_velocity = ( + self.front_right_motor_slider.value() + ) + motor_control.direct_per_wheel_drive.back_left_wheel_velocity = ( + self.back_left_motor_slider.value() + ) + motor_control.direct_per_wheel_drive.back_right_wheel_velocity = ( + self.back_right_motor_slider.value() + ) + + self.proto_unix_io.send_proto(MotorControl, motor_control) + + def value_change(self, value: float) -> str: + """Converts the given float value to a string label + + :param value: float value to be converted + """ + value = float(value) + value_str = "%.2f" % value + return value_str + + def setup_drive_switch_radio(self, title: str) -> QGroupBox: + """Create a radio button widget to switch between per-motor and velocity drive modes + + :param title: vbox name + """ + group_box = QGroupBox(title) + vbox = QVBoxLayout() + self.connect_options_group = QButtonGroup() + radio_button_names = ["Velocity Control", "Per Motor Control"] + self.connect_options_box, self.connect_options = common_widgets.create_radio( + radio_button_names, self.connect_options_group + ) + self.use_direct_velocity = self.connect_options[DriveMode.VELOCITY] + self.use_per_motor = self.connect_options[DriveMode.MOTOR] + self.use_direct_velocity.clicked.connect( + lambda: self.toggle_drive_mode(True) + ) + self.use_per_motor.clicked.connect( + lambda: self.toggle_drive_mode(False) + ) + vbox.addWidget(self.connect_options_box) + group_box.setLayout(vbox) + + return group_box + + def setup_direct_velocity(self, title: str) -> QGroupBox: + """Create a widget to control the direct velocity of the robot's motors + + :param title: the name of the slider + """ + group_box = QGroupBox(title) + dbox = QVBoxLayout() + + ( + x_layout, + self.x_velocity_slider, + self.x_velocity_label, + ) = common_widgets.create_float_slider( + "X (m/s)", + 2, + -self.constants.robot_max_speed_m_per_s, + self.constants.robot_max_speed_m_per_s, + 1, + ) + ( + y_layout, + self.y_velocity_slider, + self.y_velocity_label, + ) = common_widgets.create_float_slider( + "Y (m/s)", + 2, + -self.constants.robot_max_speed_m_per_s, + self.constants.robot_max_speed_m_per_s, + 1, + ) + ( + dps_layout, + self.angular_velocity_slider, + self.angular_velocity_label, + ) = common_widgets.create_float_slider( + "θ (rad/s)", + 2, + -self.constants.robot_max_ang_speed_rad_per_s, + self.constants.robot_max_ang_speed_rad_per_s, + 1, + ) + + # add listener functions for sliders to update label with slider value + common_widgets.enable_slider( + self.x_velocity_slider, self.x_velocity_label, self.value_change + ) + common_widgets.enable_slider( + self.y_velocity_slider, self.y_velocity_label, self.value_change + ) + common_widgets.enable_slider( + self.angular_velocity_slider, self.angular_velocity_label, self.value_change + ) + + self.stop_and_reset_direct = QPushButton("Stop and Reset") + self.stop_and_reset_direct.clicked.connect(self.reset_direct_sliders) + + dbox.addLayout(x_layout) + dbox.addLayout(y_layout) + dbox.addLayout(dps_layout) + dbox.addWidget( + self.stop_and_reset_direct, alignment=Qt.AlignmentFlag.AlignCenter + ) + + group_box.setLayout(dbox) + + return group_box + + def setup_per_motor(self, title: str) -> QGroupBox: + """Create a widget to control the rotation rate of each motor + + :param title: the name of the group box + """ + + group_box = QGroupBox(title) + dbox = QVBoxLayout() + + ( + fl_layout, + self.front_left_motor_slider, + self.front_left_motor_label, + ) = common_widgets.create_float_slider( + "Front Left Motor (m/s)", + 2, + -self.constants.robot_max_speed_m_per_s, + self.constants.robot_max_speed_m_per_s, + 1, + ) + ( + fr_layout, + self.front_right_motor_slider, + self.front_right_motor_label, + ) = common_widgets.create_float_slider( + "Front Right Motor (m/s)", + 2, + -self.constants.robot_max_speed_m_per_s, + self.constants.robot_max_speed_m_per_s, + 1, + ) + ( + bl_layout, + self.back_left_motor_slider, + self.back_left_motor_label, + ) = common_widgets.create_float_slider( + "Back Left Motor (m/s)", + 2, + -self.constants.robot_max_speed_m_per_s, + self.constants.robot_max_speed_m_per_s, + 1, + ) + ( + br_layout, + self.back_right_motor_slider, + self.back_right_motor_label, + ) = common_widgets.create_float_slider( + "Back Right Motor (m/s)", + 2, + -self.constants.robot_max_speed_m_per_s, + self.constants.robot_max_speed_m_per_s, + 1, + ) + + # Add listener functions for each motor's slider to update labels with slider values + common_widgets.enable_slider( + self.front_left_motor_slider, self.front_left_motor_label, self.value_change + ) + common_widgets.enable_slider( + self.front_right_motor_slider, self.front_right_motor_label, self.value_change + ) + common_widgets.enable_slider( + self.back_left_motor_slider, self.back_left_motor_label, self.value_change + ) + common_widgets.enable_slider( + self.back_right_motor_slider, self.back_right_motor_label, self.value_change + ) + + # Stop and Reset button for per-motor sliders + self.stop_and_reset_per_motor = QPushButton("Stop and Reset") + self.stop_and_reset_per_motor.clicked.connect(self.reset_motor_sliders) + + # Adding layouts to the box + dbox.addLayout(fl_layout) + dbox.addLayout(fr_layout) + dbox.addLayout(bl_layout) + dbox.addLayout(br_layout) + dbox.addWidget( + self.stop_and_reset_per_motor, alignment=Qt.AlignmentFlag.AlignCenter + ) + + group_box.setLayout(dbox) + + return group_box + + def setup_dribbler(self, title: str) -> QGroupBox: + """Create a widget to control the dribbler RPM + + :param title: the name of the slider + """ + group_box = QGroupBox(title) + dbox = QVBoxLayout() + + ( + dribbler_layout, + self.dribbler_speed_rpm_slider, + self.dribbler_speed_rpm_label, + ) = common_widgets.create_float_slider( + "RPM", + 1, + self.constants.indefinite_dribbler_speed_rpm, + -self.constants.indefinite_dribbler_speed_rpm, + 1, + ) + + # add listener function to update label with slider value + common_widgets.enable_slider( + self.dribbler_speed_rpm_slider, + self.dribbler_speed_rpm_label, + self.value_change, + ) + + self.stop_and_reset_dribbler = QPushButton("Stop and Reset") + self.stop_and_reset_dribbler.clicked.connect(self.reset_dribbler_slider) + + dbox.addLayout(dribbler_layout) + dbox.addWidget( + self.stop_and_reset_dribbler, alignment=Qt.AlignmentFlag.AlignCenter + ) + group_box.setLayout(dbox) + + return group_box + + def toggle_drive_mode(self, use_drive_mode: IntEnum) -> None: + """Switches between 'Direct Velocity' and 'Per Motor' drive modes. + + :param use_drive_mode: DriveMode.VELOCITY or DriveMode.MOTOR, switch to that mode. + """ + # reset sliders + self.reset_motor_sliders() + self.reset_direct_sliders() + self.disconnect_direct_sliders() + self.disconnect_motor_sliders() + + if use_drive_mode == DriveMode.VELOCITY: + # Show the direct velocity widget + self.drive_widget.setCurrentWidget(self.direct_velocity_widget) + + # Enable direct sliders and disable motor sliders + common_widgets.enable_slider( + self.x_velocity_slider, self.x_velocity_label, self.value_change + ) + common_widgets.enable_slider( + self.y_velocity_slider, self.y_velocity_label, self.value_change + ) + common_widgets.enable_slider( + self.angular_velocity_slider, self.angular_velocity_label, self.value_change + ) + + common_widgets.disable_slider(self.front_left_motor_slider) + common_widgets.disable_slider(self.front_right_motor_slider) + common_widgets.disable_slider(self.back_left_motor_slider) + common_widgets.disable_slider(self.back_right_motor_slider) + + common_widgets.change_button_state(self.stop_and_reset_direct, True) + common_widgets.change_button_state(self.stop_and_reset_per_motor, False) + + else: + # Show the per motor widget + self.drive_widget.setCurrentWidget(self.per_motor_widget) + + # Enable motor sliders and disable direct sliders + common_widgets.enable_slider( + self.front_left_motor_slider, self.front_left_motor_label, self.value_change + ) + common_widgets.enable_slider( + self.front_right_motor_slider, self.front_right_motor_label, self.value_change + ) + common_widgets.enable_slider( + self.back_left_motor_slider, self.back_left_motor_label, self.value_change + ) + common_widgets.enable_slider( + self.back_right_motor_slider, self.back_right_motor_label, self.value_change + ) + + common_widgets.disable_slider(self.x_velocity_slider) + common_widgets.disable_slider(self.y_velocity_slider) + common_widgets.disable_slider(self.angular_velocity_slider) + + common_widgets.change_button_state(self.stop_and_reset_per_motor, True) + common_widgets.change_button_state(self.stop_and_reset_direct, False) + + def toggle_dribbler_sliders(self, enable: bool) -> None: + """Enables or disables dribbler sliders. + + :param enable: True to enable, False to disable + """ + if enable: + common_widgets.enable_slider( + self.dribbler_speed_rpm_slider, + self.dribbler_speed_rpm_label, + self.value_change, + ) + common_widgets.change_button_state(self.stop_and_reset_dribbler, True) + else: + common_widgets.disable_slider(self.dribbler_speed_rpm_slider) + common_widgets.change_button_state(self.stop_and_reset_dribbler, False) + + def disconnect_direct_sliders(self) -> None: + """Disconnect listener for changing values for motor sliders""" + self.x_velocity_slider.valueChanged.disconnect() + self.y_velocity_slider.valueChanged.disconnect() + self.angular_velocity_slider.valueChanged.disconnect() + + def disconnect_dribbler_sliders(self) -> None: + self.dribbler_speed_rpm_slider.valueChanged.disconnect() + + def disconnect_motor_sliders(self) -> None: + self.front_left_motor_slider.valueChanged.disconnect() + self.front_right_motor_slider.valueChanged.disconnect() + self.back_left_motor_slider.valueChanged.disconnect() + self.back_right_motor_slider.valueChanged.disconnect() + + def reset_direct_sliders(self) -> None: + """Reset direct sliders back to 0""" + self.x_velocity_slider.setValue(0) + self.y_velocity_slider.setValue(0) + self.angular_velocity_slider.setValue(0) + + def reset_motor_sliders(self) -> None: + """Reset direct sliders back to 0""" + self.front_left_motor_slider.setValue(0) + self.front_right_motor_slider.setValue(0) + self.back_left_motor_slider.setValue(0) + self.back_right_motor_slider.setValue(0) + + def reset_dribbler_slider(self) -> None: + """Reset the dribbler slider back to 0""" + self.dribbler_speed_rpm_slider.setValue(0) + + def reset_all_sliders(self) -> None: + """Reset all sliders back to 0""" + self.reset_direct_sliders() + self.reset_motor_sliders() + self.reset_dribbler_slider() diff --git a/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget_REMOTE_15256.py b/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget_REMOTE_15256.py new file mode 100644 index 0000000000..233e092716 --- /dev/null +++ b/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget_REMOTE_15256.py @@ -0,0 +1,432 @@ +from pyqtgraph.Qt.QtCore import Qt +from pyqtgraph.Qt.QtWidgets import * +import time +import software.python_bindings as tbots_cpp +from software.thunderscope.common import common_widgets +from proto.import_all_protos import * +from software.thunderscope.proto_unix_io import ProtoUnixIO +from enum import IntEnum + + +class DriveMode(IntEnum): + """Enum for the 2 drive modes (direct velocity and per-motor)""" + + VELOCITY = 0 + MOTOR = 1 + + +class DriveAndDribblerWidget(QWidget): + def __init__(self, proto_unix_io: ProtoUnixIO) -> None: + """Initialize the widget to control the robot's motors + + :param proto_unix_io: the proto_unix_io object + """ + self.input_a = time.time() + self.constants = tbots_cpp.create2021RobotConstants() + QWidget.__init__(self) + + layout = QVBoxLayout() + self.drive_widget = QStackedWidget() + + self.proto_unix_io = proto_unix_io + + # create swappable widget system using stacked widgets + self.direct_velocity_widget = self.setup_direct_velocity( + "Drive - Direct Velocity" + ) + self.per_motor_widget = self.setup_per_motor("Drive - Per Motor") + self.drive_widget.addWidget(self.direct_velocity_widget) + self.drive_widget.addWidget(self.per_motor_widget) + + layout.addWidget(self.setup_drive_switch_radio("Drive Mode Switch")) + layout.addWidget(self.drive_widget) + layout.addWidget(self.setup_dribbler("Dribbler")) + + self.enabled = True + self.per_motor = False + self.setLayout(layout) + self.toggle_control_mode(True) + self.toggle_dribbler_sliders(True) + + def refresh(self) -> None: + """Refresh the widget and send the a MotorControl message with the current values""" + motor_control = MotorControl() + motor_control.dribbler_speed_rpm = int(self.dribbler_speed_rpm_slider.value()) + + if not self.per_motor: + motor_control.ClearField("direct_per_wheel_control") + motor_control.direct_velocity_control.velocity.x_component_meters = ( + self.x_velocity_slider.value() + ) + motor_control.direct_velocity_control.velocity.y_component_meters = ( + self.y_velocity_slider.value() + ) + motor_control.direct_velocity_control.angular_velocity.radians_per_second = self.angular_velocity_slider.value() + else: + motor_control.ClearField("direct_velocity_control") + motor_control.direct_per_wheel_control.front_left_wheel_velocity = ( + self.front_left_motor_slider.value() + ) + motor_control.direct_per_wheel_control.front_right_wheel_velocity = ( + self.front_right_motor_slider.value() + ) + motor_control.direct_per_wheel_control.back_left_wheel_velocity = ( + self.back_left_motor_slider.value() + ) + motor_control.direct_per_wheel_control.back_right_wheel_velocity = ( + self.back_right_motor_slider.value() + ) + + self.proto_unix_io.send_proto(MotorControl, motor_control) + + def value_change(self, value: float) -> str: + """Converts the given float value to a string label + + :param value: float value to be converted + """ + value = float(value) + value_str = "%.2f" % value + return value_str + + def setup_drive_switch_radio(self, title: str) -> QGroupBox: + """Create a radio button widget to switch between per-motor and velocity control modes + + :param title: group box name + """ + group_box = QGroupBox(title) + dbox = QVBoxLayout() + self.connect_options_group = QButtonGroup() + radio_button_names = ["Velocity Control", "Per Motor Control"] + self.connect_options_box, self.connect_options = common_widgets.create_radio( + radio_button_names, self.connect_options_group + ) + self.use_direct_velocity = self.connect_options[DriveMode.VELOCITY] + self.use_per_motor = self.connect_options[DriveMode.MOTOR] + self.use_direct_velocity.clicked.connect(self.switch_to_velocity) + self.use_per_motor.clicked.connect(self.switch_to_motor) + dbox.addWidget(self.connect_options_box) + group_box.setLayout(dbox) + + return group_box + + def switch_to_velocity(self): + self.toggle_control_mode(True) + + def switch_to_motor(self): + self.toggle_control_mode(False) + + def setup_direct_velocity(self, title: str) -> QGroupBox: + """Create a widget to control the direct velocity of the robot's motors + + :param title: the name of the slider + """ + group_box = QGroupBox(title) + dbox = QVBoxLayout() + + ( + x_layout, + self.x_velocity_slider, + self.x_velocity_label, + ) = common_widgets.create_float_slider( + "X (m/s)", + 2, + -self.constants.robot_max_speed_m_per_s, + self.constants.robot_max_speed_m_per_s, + 1, + ) + ( + y_layout, + self.y_velocity_slider, + self.y_velocity_label, + ) = common_widgets.create_float_slider( + "Y (m/s)", + 2, + -self.constants.robot_max_speed_m_per_s, + self.constants.robot_max_speed_m_per_s, + 1, + ) + ( + dps_layout, + self.angular_velocity_slider, + self.angular_velocity_label, + ) = common_widgets.create_float_slider( + "θ (rad/s)", + 2, + -self.constants.robot_max_ang_speed_rad_per_s, + self.constants.robot_max_ang_speed_rad_per_s, + 1, + ) + + # add listener functions for sliders to update label with slider value + common_widgets.enable_slider( + self.x_velocity_slider, self.x_velocity_label, self.value_change + ) + common_widgets.enable_slider( + self.y_velocity_slider, self.y_velocity_label, self.value_change + ) + common_widgets.enable_slider( + self.angular_velocity_slider, self.angular_velocity_label, self.value_change + ) + + self.stop_and_reset_direct = QPushButton("Stop and Reset") + self.stop_and_reset_direct.clicked.connect(self.reset_direct_sliders) + + dbox.addLayout(x_layout) + dbox.addLayout(y_layout) + dbox.addLayout(dps_layout) + dbox.addWidget( + self.stop_and_reset_direct, alignment=Qt.AlignmentFlag.AlignCenter + ) + + group_box.setLayout(dbox) + + return group_box + + def setup_per_motor(self, title: str) -> QGroupBox: + """Create a widget to control the rotation rate of each motor + + :param title: the name of the group box + """ + group_box = QGroupBox(title) + dbox = QVBoxLayout() + + ( + fl_layout, + self.front_left_motor_slider, + self.front_left_motor_label, + ) = common_widgets.create_float_slider( + "Front Left Motor (m/s)", + 2, + -self.constants.robot_max_speed_m_per_s, + self.constants.robot_max_speed_m_per_s, + 1, + ) + ( + fr_layout, + self.front_right_motor_slider, + self.front_right_motor_label, + ) = common_widgets.create_float_slider( + "Front Right Motor (m/s)", + 2, + -self.constants.robot_max_speed_m_per_s, + self.constants.robot_max_speed_m_per_s, + 1, + ) + ( + bl_layout, + self.back_left_motor_slider, + self.back_left_motor_label, + ) = common_widgets.create_float_slider( + "Back Left Motor (m/s)", + 2, + -self.constants.robot_max_speed_m_per_s, + self.constants.robot_max_speed_m_per_s, + 1, + ) + ( + br_layout, + self.back_right_motor_slider, + self.back_right_motor_label, + ) = common_widgets.create_float_slider( + "Back Right Motor (m/s)", + 2, + -self.constants.robot_max_speed_m_per_s, + self.constants.robot_max_speed_m_per_s, + 1, + ) + + # Add listener functions for each motor's slider to update labels with slider values + common_widgets.enable_slider( + self.front_left_motor_slider, self.front_left_motor_label, self.value_change + ) + common_widgets.enable_slider( + self.front_right_motor_slider, + self.front_right_motor_label, + self.value_change, + ) + common_widgets.enable_slider( + self.back_left_motor_slider, self.back_left_motor_label, self.value_change + ) + common_widgets.enable_slider( + self.back_right_motor_slider, self.back_right_motor_label, self.value_change + ) + + # Stop and Reset button for per-motor sliders + self.stop_and_reset_per_motor = QPushButton("Stop and Reset") + self.stop_and_reset_per_motor.clicked.connect(self.reset_motor_sliders) + + # Adding layouts to the box + dbox.addLayout(fl_layout) + dbox.addLayout(fr_layout) + dbox.addLayout(bl_layout) + dbox.addLayout(br_layout) + dbox.addWidget( + self.stop_and_reset_per_motor, alignment=Qt.AlignmentFlag.AlignCenter + ) + + group_box.setLayout(dbox) + + return group_box + + def setup_dribbler(self, title: str) -> QGroupBox: + """Create a widget to control the dribbler RPM + + :param title: the name of the slider + """ + group_box = QGroupBox(title) + dbox = QVBoxLayout() + + ( + dribbler_layout, + self.dribbler_speed_rpm_slider, + self.dribbler_speed_rpm_label, + ) = common_widgets.create_float_slider( + "RPM", + 1, + self.constants.indefinite_dribbler_speed_rpm, + -self.constants.indefinite_dribbler_speed_rpm, + 1, + ) + + # add listener function to update label with slider value + common_widgets.enable_slider( + self.dribbler_speed_rpm_slider, + self.dribbler_speed_rpm_label, + self.value_change, + ) + + self.stop_and_reset_dribbler = QPushButton("Stop and Reset") + self.stop_and_reset_dribbler.clicked.connect(self.reset_dribbler_slider) + + dbox.addLayout(dribbler_layout) + dbox.addWidget( + self.stop_and_reset_dribbler, alignment=Qt.AlignmentFlag.AlignCenter + ) + group_box.setLayout(dbox) + + return group_box + + def toggle_control_mode(self, use_direct: bool) -> None: + """Switches between 'Direct Velocity' and 'Per Motor' control modes. + + :param use_direct: True to enable Direct Velocity mode, False to enable Per Motor mode. + """ + # reset sliders + self.reset_motor_sliders() + self.reset_direct_sliders() + self.disconnect_direct_sliders() + self.disconnect_motor_sliders() + + if use_direct: + # Show the direct velocity widget + self.per_motor = False + self.drive_widget.setCurrentWidget(self.direct_velocity_widget) + + # Enable direct sliders and disable motor sliders + common_widgets.enable_slider( + self.x_velocity_slider, self.x_velocity_label, self.value_change + ) + common_widgets.enable_slider( + self.y_velocity_slider, self.y_velocity_label, self.value_change + ) + common_widgets.enable_slider( + self.angular_velocity_slider, + self.angular_velocity_label, + self.value_change, + ) + + common_widgets.disable_slider(self.front_left_motor_slider) + common_widgets.disable_slider(self.front_right_motor_slider) + common_widgets.disable_slider(self.back_left_motor_slider) + common_widgets.disable_slider(self.back_right_motor_slider) + + common_widgets.change_button_state(self.stop_and_reset_direct, True) + common_widgets.change_button_state(self.stop_and_reset_per_motor, False) + + else: + self.per_motor = True + # Show the per motor widget + self.drive_widget.setCurrentWidget(self.per_motor_widget) + + # Enable motor sliders and disable direct sliders + common_widgets.enable_slider( + self.front_left_motor_slider, + self.front_left_motor_label, + self.value_change, + ) + common_widgets.enable_slider( + self.front_right_motor_slider, + self.front_right_motor_label, + self.value_change, + ) + common_widgets.enable_slider( + self.back_left_motor_slider, + self.back_left_motor_label, + self.value_change, + ) + common_widgets.enable_slider( + self.back_right_motor_slider, + self.back_right_motor_label, + self.value_change, + ) + + common_widgets.disable_slider(self.x_velocity_slider) + common_widgets.disable_slider(self.y_velocity_slider) + common_widgets.disable_slider(self.angular_velocity_slider) + + common_widgets.change_button_state(self.stop_and_reset_per_motor, True) + common_widgets.change_button_state(self.stop_and_reset_direct, False) + + def toggle_dribbler_sliders(self, enable: bool) -> None: + """Enables or disables dribbler sliders. + + :param enable: True to enable, False to disable + """ + if enable: + common_widgets.enable_slider( + self.dribbler_speed_rpm_slider, + self.dribbler_speed_rpm_label, + self.value_change, + ) + common_widgets.change_button_state(self.stop_and_reset_dribbler, True) + else: + common_widgets.disable_slider(self.dribbler_speed_rpm_slider) + common_widgets.change_button_state(self.stop_and_reset_dribbler, False) + + def disconnect_direct_sliders(self) -> None: + """Disconnect listener for changing values for motor sliders""" + self.x_velocity_slider.valueChanged.disconnect() + self.y_velocity_slider.valueChanged.disconnect() + self.angular_velocity_slider.valueChanged.disconnect() + + def disconnect_dribbler_sliders(self) -> None: + self.dribbler_speed_rpm_slider.valueChanged.disconnect() + + def disconnect_motor_sliders(self) -> None: + self.front_left_motor_slider.valueChanged.disconnect() + self.front_right_motor_slider.valueChanged.disconnect() + self.back_left_motor_slider.valueChanged.disconnect() + self.back_right_motor_slider.valueChanged.disconnect() + + def reset_direct_sliders(self) -> None: + """Reset direct sliders back to 0""" + self.x_velocity_slider.setValue(0) + self.y_velocity_slider.setValue(0) + self.angular_velocity_slider.setValue(0) + + def reset_motor_sliders(self) -> None: + """Reset direct sliders back to 0""" + self.front_left_motor_slider.setValue(0) + self.front_right_motor_slider.setValue(0) + self.back_left_motor_slider.setValue(0) + self.back_right_motor_slider.setValue(0) + + def reset_dribbler_slider(self) -> None: + """Reset the dribbler slider back to 0""" + self.dribbler_speed_rpm_slider.setValue(0) + + def reset_all_sliders(self) -> None: + """Reset all sliders back to 0""" + self.reset_direct_sliders() + self.reset_motor_sliders() + self.reset_dribbler_slider() From 95c8b8a989c600979e07ba66aae8f283254cbe48 Mon Sep 17 00:00:00 2001 From: Muk Date: Mon, 4 Nov 2024 01:58:16 -0800 Subject: [PATCH 07/10] removed backups --- .../drive_and_dribbler_widget_BACKUP_15256.py | 440 ------------------ .../drive_and_dribbler_widget_BASE_15256.py | 425 ----------------- .../drive_and_dribbler_widget_LOCAL_15256.py | 416 ----------------- .../drive_and_dribbler_widget_REMOTE_15256.py | 432 ----------------- 4 files changed, 1713 deletions(-) delete mode 100644 src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget_BACKUP_15256.py delete mode 100644 src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget_BASE_15256.py delete mode 100644 src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget_LOCAL_15256.py delete mode 100644 src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget_REMOTE_15256.py diff --git a/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget_BACKUP_15256.py b/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget_BACKUP_15256.py deleted file mode 100644 index ac2f3e977c..0000000000 --- a/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget_BACKUP_15256.py +++ /dev/null @@ -1,440 +0,0 @@ -from pyqtgraph.Qt.QtCore import Qt -from pyqtgraph.Qt.QtWidgets import * -import time -import software.python_bindings as tbots_cpp -from software.thunderscope.common import common_widgets -from proto.import_all_protos import * -from software.thunderscope.proto_unix_io import ProtoUnixIO -from enum import IntEnum - - -class DriveMode(IntEnum): - """Enum for the 2 drive modes (direct velocity and per-motor)""" - VELOCITY = 0 - MOTOR = 1 - - -class DriveAndDribblerWidget(QWidget): - def __init__(self, proto_unix_io: ProtoUnixIO) -> None: - """Initialize the widget to control the robot's motors - - :param proto_unix_io: the proto_unix_io object - """ - self.input_a = time.time() - self.constants = tbots_cpp.create2021RobotConstants() - QWidget.__init__(self) - - layout = QVBoxLayout() - self.drive_widget = QStackedWidget() - - self.proto_unix_io = proto_unix_io - - # create swappable widget system using stacked widgets - self.direct_velocity_widget = self.setup_direct_velocity( - "Drive - Direct Velocity" - ) - self.per_motor_widget = self.setup_per_motor("Drive - Per Motor") - self.drive_widget.addWidget(self.direct_velocity_widget) - self.drive_widget.addWidget(self.per_motor_widget) - - layout.addWidget(self.setup_drive_switch_radio("Drive Mode Switch")) - layout.addWidget(self.drive_widget) - layout.addWidget(self.setup_dribbler("Dribbler")) - - self.drive_mode = DriveMode.MOTOR - self.use_direct_velocity.setChecked(True) - self.setLayout(layout) - self.toggle_drive_mode(True) - self.toggle_dribbler_sliders(True) - - def refresh(self) -> None: - """Refresh the widget and send the a MotorControl message with the current values""" - motor_control = MotorControl() - motor_control.dribbler_speed_rpm = int(self.dribbler_speed_rpm_slider.value()) - - if not self.drive_mode == DriveMode.VELOCITY: - motor_control.ClearField("direct_per_wheel_control") - motor_control.direct_velocity_drive.velocity.x_component_meters = ( - self.x_velocity_slider.value() - ) - motor_control.direct_velocity_drive.velocity.y_component_meters = ( - self.y_velocity_slider.value() - ) -<<<<<<< HEAD - motor_control.direct_velocity_drive.angular_velocity.radians_per_second = ( - self.angular_velocity_slider.value() - ) -======= - motor_control.direct_velocity_control.angular_velocity.radians_per_second = self.angular_velocity_slider.value() ->>>>>>> 2b88ee68a0a266e5e01447616a4509f08fb35821 - else: - motor_control.ClearField("direct_velocity_control") - motor_control.direct_per_wheel_drive.front_left_wheel_velocity = ( - self.front_left_motor_slider.value() - ) - motor_control.direct_per_wheel_drive.front_right_wheel_velocity = ( - self.front_right_motor_slider.value() - ) - motor_control.direct_per_wheel_drive.back_left_wheel_velocity = ( - self.back_left_motor_slider.value() - ) - motor_control.direct_per_wheel_drive.back_right_wheel_velocity = ( - self.back_right_motor_slider.value() - ) - - self.proto_unix_io.send_proto(MotorControl, motor_control) - - def value_change(self, value: float) -> str: - """Converts the given float value to a string label - - :param value: float value to be converted - """ - value = float(value) - value_str = "%.2f" % value - return value_str - - def setup_drive_switch_radio(self, title: str) -> QGroupBox: - """Create a radio button widget to switch between per-motor and velocity drive modes - - :param title: vbox name - """ - group_box = QGroupBox(title) - vbox = QVBoxLayout() - self.connect_options_group = QButtonGroup() - radio_button_names = ["Velocity Control", "Per Motor Control"] - self.connect_options_box, self.connect_options = common_widgets.create_radio( - radio_button_names, self.connect_options_group - ) - self.use_direct_velocity = self.connect_options[DriveMode.VELOCITY] - self.use_per_motor = self.connect_options[DriveMode.MOTOR] -<<<<<<< HEAD - self.use_direct_velocity.clicked.connect( - lambda: self.toggle_drive_mode(True) - ) - self.use_per_motor.clicked.connect( - lambda: self.toggle_drive_mode(False) - ) - vbox.addWidget(self.connect_options_box) - group_box.setLayout(vbox) -======= - self.use_direct_velocity.clicked.connect(self.switch_to_velocity) - self.use_per_motor.clicked.connect(self.switch_to_motor) - dbox.addWidget(self.connect_options_box) - group_box.setLayout(dbox) ->>>>>>> 2b88ee68a0a266e5e01447616a4509f08fb35821 - - return group_box - - def setup_direct_velocity(self, title: str) -> QGroupBox: - """Create a widget to control the direct velocity of the robot's motors - - :param title: the name of the slider - """ - group_box = QGroupBox(title) - dbox = QVBoxLayout() - - ( - x_layout, - self.x_velocity_slider, - self.x_velocity_label, - ) = common_widgets.create_float_slider( - "X (m/s)", - 2, - -self.constants.robot_max_speed_m_per_s, - self.constants.robot_max_speed_m_per_s, - 1, - ) - ( - y_layout, - self.y_velocity_slider, - self.y_velocity_label, - ) = common_widgets.create_float_slider( - "Y (m/s)", - 2, - -self.constants.robot_max_speed_m_per_s, - self.constants.robot_max_speed_m_per_s, - 1, - ) - ( - dps_layout, - self.angular_velocity_slider, - self.angular_velocity_label, - ) = common_widgets.create_float_slider( - "θ (rad/s)", - 2, - -self.constants.robot_max_ang_speed_rad_per_s, - self.constants.robot_max_ang_speed_rad_per_s, - 1, - ) - - # add listener functions for sliders to update label with slider value - common_widgets.enable_slider( - self.x_velocity_slider, self.x_velocity_label, self.value_change - ) - common_widgets.enable_slider( - self.y_velocity_slider, self.y_velocity_label, self.value_change - ) - common_widgets.enable_slider( - self.angular_velocity_slider, self.angular_velocity_label, self.value_change - ) - - self.stop_and_reset_direct = QPushButton("Stop and Reset") - self.stop_and_reset_direct.clicked.connect(self.reset_direct_sliders) - - dbox.addLayout(x_layout) - dbox.addLayout(y_layout) - dbox.addLayout(dps_layout) - dbox.addWidget( - self.stop_and_reset_direct, alignment=Qt.AlignmentFlag.AlignCenter - ) - - group_box.setLayout(dbox) - - return group_box - - def setup_per_motor(self, title: str) -> QGroupBox: - """Create a widget to control the rotation rate of each motor - - :param title: the name of the group box - """ - group_box = QGroupBox(title) - dbox = QVBoxLayout() - - ( - fl_layout, - self.front_left_motor_slider, - self.front_left_motor_label, - ) = common_widgets.create_float_slider( - "Front Left Motor (m/s)", - 2, - -self.constants.robot_max_speed_m_per_s, - self.constants.robot_max_speed_m_per_s, - 1, - ) - ( - fr_layout, - self.front_right_motor_slider, - self.front_right_motor_label, - ) = common_widgets.create_float_slider( - "Front Right Motor (m/s)", - 2, - -self.constants.robot_max_speed_m_per_s, - self.constants.robot_max_speed_m_per_s, - 1, - ) - ( - bl_layout, - self.back_left_motor_slider, - self.back_left_motor_label, - ) = common_widgets.create_float_slider( - "Back Left Motor (m/s)", - 2, - -self.constants.robot_max_speed_m_per_s, - self.constants.robot_max_speed_m_per_s, - 1, - ) - ( - br_layout, - self.back_right_motor_slider, - self.back_right_motor_label, - ) = common_widgets.create_float_slider( - "Back Right Motor (m/s)", - 2, - -self.constants.robot_max_speed_m_per_s, - self.constants.robot_max_speed_m_per_s, - 1, - ) - - # Add listener functions for each motor's slider to update labels with slider values - common_widgets.enable_slider( - self.front_left_motor_slider, self.front_left_motor_label, self.value_change - ) - common_widgets.enable_slider( - self.front_right_motor_slider, - self.front_right_motor_label, - self.value_change, - ) - common_widgets.enable_slider( - self.back_left_motor_slider, self.back_left_motor_label, self.value_change - ) - common_widgets.enable_slider( - self.back_right_motor_slider, self.back_right_motor_label, self.value_change - ) - - # Stop and Reset button for per-motor sliders - self.stop_and_reset_per_motor = QPushButton("Stop and Reset") - self.stop_and_reset_per_motor.clicked.connect(self.reset_motor_sliders) - - # Adding layouts to the box - dbox.addLayout(fl_layout) - dbox.addLayout(fr_layout) - dbox.addLayout(bl_layout) - dbox.addLayout(br_layout) - dbox.addWidget( - self.stop_and_reset_per_motor, alignment=Qt.AlignmentFlag.AlignCenter - ) - - group_box.setLayout(dbox) - - return group_box - - def setup_dribbler(self, title: str) -> QGroupBox: - """Create a widget to control the dribbler RPM - - :param title: the name of the slider - """ - group_box = QGroupBox(title) - dbox = QVBoxLayout() - - ( - dribbler_layout, - self.dribbler_speed_rpm_slider, - self.dribbler_speed_rpm_label, - ) = common_widgets.create_float_slider( - "RPM", - 1, - self.constants.indefinite_dribbler_speed_rpm, - -self.constants.indefinite_dribbler_speed_rpm, - 1, - ) - - # add listener function to update label with slider value - common_widgets.enable_slider( - self.dribbler_speed_rpm_slider, - self.dribbler_speed_rpm_label, - self.value_change, - ) - - self.stop_and_reset_dribbler = QPushButton("Stop and Reset") - self.stop_and_reset_dribbler.clicked.connect(self.reset_dribbler_slider) - - dbox.addLayout(dribbler_layout) - dbox.addWidget( - self.stop_and_reset_dribbler, alignment=Qt.AlignmentFlag.AlignCenter - ) - group_box.setLayout(dbox) - - return group_box - - def toggle_drive_mode(self, use_drive_mode: IntEnum) -> None: - """Switches between 'Direct Velocity' and 'Per Motor' drive modes. - - :param use_drive_mode: DriveMode.VELOCITY or DriveMode.MOTOR, switch to that mode. - """ - # reset sliders - self.reset_motor_sliders() - self.reset_direct_sliders() - self.disconnect_direct_sliders() - self.disconnect_motor_sliders() - - if use_drive_mode == DriveMode.VELOCITY: - # Show the direct velocity widget - self.drive_widget.setCurrentWidget(self.direct_velocity_widget) - - # Enable direct sliders and disable motor sliders - common_widgets.enable_slider( - self.x_velocity_slider, self.x_velocity_label, self.value_change - ) - common_widgets.enable_slider( - self.y_velocity_slider, self.y_velocity_label, self.value_change - ) - common_widgets.enable_slider( - self.angular_velocity_slider, - self.angular_velocity_label, - self.value_change, - ) - - common_widgets.disable_slider(self.front_left_motor_slider) - common_widgets.disable_slider(self.front_right_motor_slider) - common_widgets.disable_slider(self.back_left_motor_slider) - common_widgets.disable_slider(self.back_right_motor_slider) - - common_widgets.change_button_state(self.stop_and_reset_direct, True) - common_widgets.change_button_state(self.stop_and_reset_per_motor, False) - - else: - # Show the per motor widget - self.drive_widget.setCurrentWidget(self.per_motor_widget) - - # Enable motor sliders and disable direct sliders - common_widgets.enable_slider( - self.front_left_motor_slider, - self.front_left_motor_label, - self.value_change, - ) - common_widgets.enable_slider( - self.front_right_motor_slider, - self.front_right_motor_label, - self.value_change, - ) - common_widgets.enable_slider( - self.back_left_motor_slider, - self.back_left_motor_label, - self.value_change, - ) - common_widgets.enable_slider( - self.back_right_motor_slider, - self.back_right_motor_label, - self.value_change, - ) - - common_widgets.disable_slider(self.x_velocity_slider) - common_widgets.disable_slider(self.y_velocity_slider) - common_widgets.disable_slider(self.angular_velocity_slider) - - common_widgets.change_button_state(self.stop_and_reset_per_motor, True) - common_widgets.change_button_state(self.stop_and_reset_direct, False) - - def toggle_dribbler_sliders(self, enable: bool) -> None: - """Enables or disables dribbler sliders. - - :param enable: True to enable, False to disable - """ - if enable: - common_widgets.enable_slider( - self.dribbler_speed_rpm_slider, - self.dribbler_speed_rpm_label, - self.value_change, - ) - common_widgets.change_button_state(self.stop_and_reset_dribbler, True) - else: - common_widgets.disable_slider(self.dribbler_speed_rpm_slider) - common_widgets.change_button_state(self.stop_and_reset_dribbler, False) - - def disconnect_direct_sliders(self) -> None: - """Disconnect listener for changing values for motor sliders""" - self.x_velocity_slider.valueChanged.disconnect() - self.y_velocity_slider.valueChanged.disconnect() - self.angular_velocity_slider.valueChanged.disconnect() - - def disconnect_dribbler_sliders(self) -> None: - self.dribbler_speed_rpm_slider.valueChanged.disconnect() - - def disconnect_motor_sliders(self) -> None: - self.front_left_motor_slider.valueChanged.disconnect() - self.front_right_motor_slider.valueChanged.disconnect() - self.back_left_motor_slider.valueChanged.disconnect() - self.back_right_motor_slider.valueChanged.disconnect() - - def reset_direct_sliders(self) -> None: - """Reset direct sliders back to 0""" - self.x_velocity_slider.setValue(0) - self.y_velocity_slider.setValue(0) - self.angular_velocity_slider.setValue(0) - - def reset_motor_sliders(self) -> None: - """Reset direct sliders back to 0""" - self.front_left_motor_slider.setValue(0) - self.front_right_motor_slider.setValue(0) - self.back_left_motor_slider.setValue(0) - self.back_right_motor_slider.setValue(0) - - def reset_dribbler_slider(self) -> None: - """Reset the dribbler slider back to 0""" - self.dribbler_speed_rpm_slider.setValue(0) - - def reset_all_sliders(self) -> None: - """Reset all sliders back to 0""" - self.reset_direct_sliders() - self.reset_motor_sliders() - self.reset_dribbler_slider() diff --git a/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget_BASE_15256.py b/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget_BASE_15256.py deleted file mode 100644 index 4a0d2d66bb..0000000000 --- a/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget_BASE_15256.py +++ /dev/null @@ -1,425 +0,0 @@ -from pyqtgraph.Qt.QtCore import Qt -from pyqtgraph.Qt.QtWidgets import * -import time -import software.python_bindings as tbots_cpp -from software.thunderscope.common import common_widgets -from proto.import_all_protos import * -from software.thunderscope.proto_unix_io import ProtoUnixIO -from enum import IntEnum - - -class DriveMode(IntEnum): - """Enum for the 2 drive modes (direct velocity and per-motor)""" - - VELOCITY = 0 - MOTOR = 1 - - -class DriveAndDribblerWidget(QWidget): - def __init__(self, proto_unix_io: ProtoUnixIO) -> None: - """Initialize the widget to control the robot's motors - - :param proto_unix_io: the proto_unix_io object - """ - self.input_a = time.time() - self.constants = tbots_cpp.create2021RobotConstants() - QWidget.__init__(self) - - layout = QVBoxLayout() - self.drive_widget = QStackedWidget() - - self.proto_unix_io = proto_unix_io - - # create swappable widget system using stacked widgets - self.direct_velocity_widget = self.setup_direct_velocity("Drive - Direct Velocity") - self.per_motor_widget = self.setup_per_motor("Drive - Per Motor") - self.drive_widget.addWidget(self.direct_velocity_widget) - self.drive_widget.addWidget(self.per_motor_widget) - - layout.addWidget(self.setup_drive_switch_radio("Drive Mode Switch")) - layout.addWidget(self.drive_widget) - layout.addWidget(self.setup_dribbler("Dribbler")) - - self.enabled = True - self.per_motor = False - self.setLayout(layout) - self.toggle_control_mode(True) - self.toggle_dribbler_sliders(True) - - def refresh(self) -> None: - """Refresh the widget and send the a MotorControl message with the current values""" - motor_control = MotorControl() - motor_control.dribbler_speed_rpm = int(self.dribbler_speed_rpm_slider.value()) - - if not self.per_motor: - motor_control.ClearField("direct_per_wheel_control") - motor_control.direct_velocity_control.velocity.x_component_meters = ( - self.x_velocity_slider.value() - ) - motor_control.direct_velocity_control.velocity.y_component_meters = ( - self.y_velocity_slider.value() - ) - motor_control.direct_velocity_control.angular_velocity.radians_per_second = ( - self.angular_velocity_slider.value() - ) - else: - motor_control.ClearField("direct_velocity_control") - motor_control.direct_per_wheel_control.front_left_wheel_velocity = ( - self.front_left_motor_slider.value() - ) - motor_control.direct_per_wheel_control.front_right_wheel_velocity = ( - self.front_right_motor_slider.value() - ) - motor_control.direct_per_wheel_control.back_left_wheel_velocity = ( - self.back_left_motor_slider.value() - ) - motor_control.direct_per_wheel_control.back_right_wheel_velocity = ( - self.back_right_motor_slider.value() - ) - - self.proto_unix_io.send_proto(MotorControl, motor_control) - - def value_change(self, value: float) -> str: - """Converts the given float value to a string label - - :param value: float value to be converted - """ - value = float(value) - value_str = "%.2f" % value - return value_str - - def setup_drive_switch_radio(self, title: str) -> QGroupBox: - """Create a radio button widget to switch between per-motor and velocity control modes - - :param title: group box name - """ - group_box = QGroupBox(title) - dbox = QVBoxLayout() - self.connect_options_group = QButtonGroup() - radio_button_names = ["Velocity Control", "Per Motor Control"] - self.connect_options_box, self.connect_options = common_widgets.create_radio( - radio_button_names, self.connect_options_group - ) - self.use_direct_velocity = self.connect_options[DriveMode.VELOCITY] - self.use_per_motor = self.connect_options[DriveMode.MOTOR] - self.use_direct_velocity.clicked.connect( - self.switch_to_velocity - ) - self.use_per_motor.clicked.connect( - self.switch_to_motor - ) - dbox.addWidget(self.connect_options_box) - group_box.setLayout(dbox) - - return group_box - - def switch_to_velocity(self): - self.toggle_control_mode(True) - - def switch_to_motor(self): - self.toggle_control_mode(False) - - def setup_direct_velocity(self, title: str) -> QGroupBox: - """Create a widget to control the direct velocity of the robot's motors - - :param title: the name of the slider - """ - group_box = QGroupBox(title) - dbox = QVBoxLayout() - - ( - x_layout, - self.x_velocity_slider, - self.x_velocity_label, - ) = common_widgets.create_float_slider( - "X (m/s)", - 2, - -self.constants.robot_max_speed_m_per_s, - self.constants.robot_max_speed_m_per_s, - 1, - ) - ( - y_layout, - self.y_velocity_slider, - self.y_velocity_label, - ) = common_widgets.create_float_slider( - "Y (m/s)", - 2, - -self.constants.robot_max_speed_m_per_s, - self.constants.robot_max_speed_m_per_s, - 1, - ) - ( - dps_layout, - self.angular_velocity_slider, - self.angular_velocity_label, - ) = common_widgets.create_float_slider( - "θ (rad/s)", - 2, - -self.constants.robot_max_ang_speed_rad_per_s, - self.constants.robot_max_ang_speed_rad_per_s, - 1, - ) - - # add listener functions for sliders to update label with slider value - common_widgets.enable_slider( - self.x_velocity_slider, self.x_velocity_label, self.value_change - ) - common_widgets.enable_slider( - self.y_velocity_slider, self.y_velocity_label, self.value_change - ) - common_widgets.enable_slider( - self.angular_velocity_slider, self.angular_velocity_label, self.value_change - ) - - self.stop_and_reset_direct = QPushButton("Stop and Reset") - self.stop_and_reset_direct.clicked.connect(self.reset_direct_sliders) - - dbox.addLayout(x_layout) - dbox.addLayout(y_layout) - dbox.addLayout(dps_layout) - dbox.addWidget( - self.stop_and_reset_direct, alignment=Qt.AlignmentFlag.AlignCenter - ) - - group_box.setLayout(dbox) - - return group_box - - def setup_per_motor(self, title: str) -> QGroupBox: - """Create a widget to control the rotation rate of each motor - - :param title: the name of the group box - """ - - group_box = QGroupBox(title) - dbox = QVBoxLayout() - - ( - fl_layout, - self.front_left_motor_slider, - self.front_left_motor_label, - ) = common_widgets.create_float_slider( - "Front Left Motor (m/s)", - 2, - -self.constants.robot_max_speed_m_per_s, - self.constants.robot_max_speed_m_per_s, - 1, - ) - ( - fr_layout, - self.front_right_motor_slider, - self.front_right_motor_label, - ) = common_widgets.create_float_slider( - "Front Right Motor (m/s)", - 2, - -self.constants.robot_max_speed_m_per_s, - self.constants.robot_max_speed_m_per_s, - 1, - ) - ( - bl_layout, - self.back_left_motor_slider, - self.back_left_motor_label, - ) = common_widgets.create_float_slider( - "Back Left Motor (m/s)", - 2, - -self.constants.robot_max_speed_m_per_s, - self.constants.robot_max_speed_m_per_s, - 1, - ) - ( - br_layout, - self.back_right_motor_slider, - self.back_right_motor_label, - ) = common_widgets.create_float_slider( - "Back Right Motor (m/s)", - 2, - -self.constants.robot_max_speed_m_per_s, - self.constants.robot_max_speed_m_per_s, - 1, - ) - - # Add listener functions for each motor's slider to update labels with slider values - common_widgets.enable_slider( - self.front_left_motor_slider, self.front_left_motor_label, self.value_change - ) - common_widgets.enable_slider( - self.front_right_motor_slider, self.front_right_motor_label, self.value_change - ) - common_widgets.enable_slider( - self.back_left_motor_slider, self.back_left_motor_label, self.value_change - ) - common_widgets.enable_slider( - self.back_right_motor_slider, self.back_right_motor_label, self.value_change - ) - - # Stop and Reset button for per-motor sliders - self.stop_and_reset_per_motor = QPushButton("Stop and Reset") - self.stop_and_reset_per_motor.clicked.connect(self.reset_motor_sliders) - - # Adding layouts to the box - dbox.addLayout(fl_layout) - dbox.addLayout(fr_layout) - dbox.addLayout(bl_layout) - dbox.addLayout(br_layout) - dbox.addWidget( - self.stop_and_reset_per_motor, alignment=Qt.AlignmentFlag.AlignCenter - ) - - group_box.setLayout(dbox) - - return group_box - - def setup_dribbler(self, title: str) -> QGroupBox: - """Create a widget to control the dribbler RPM - - :param title: the name of the slider - """ - group_box = QGroupBox(title) - dbox = QVBoxLayout() - - ( - dribbler_layout, - self.dribbler_speed_rpm_slider, - self.dribbler_speed_rpm_label, - ) = common_widgets.create_float_slider( - "RPM", - 1, - self.constants.indefinite_dribbler_speed_rpm, - -self.constants.indefinite_dribbler_speed_rpm, - 1, - ) - - # add listener function to update label with slider value - common_widgets.enable_slider( - self.dribbler_speed_rpm_slider, - self.dribbler_speed_rpm_label, - self.value_change, - ) - - self.stop_and_reset_dribbler = QPushButton("Stop and Reset") - self.stop_and_reset_dribbler.clicked.connect(self.reset_dribbler_slider) - - dbox.addLayout(dribbler_layout) - dbox.addWidget( - self.stop_and_reset_dribbler, alignment=Qt.AlignmentFlag.AlignCenter - ) - group_box.setLayout(dbox) - - return group_box - - def toggle_control_mode(self, use_direct: bool) -> None: - """Switches between 'Direct Velocity' and 'Per Motor' control modes. - - :param use_direct: True to enable Direct Velocity mode, False to enable Per Motor mode. - """ - # reset sliders - self.reset_motor_sliders() - self.reset_direct_sliders() - self.disconnect_direct_sliders() - self.disconnect_motor_sliders() - - if use_direct: - # Show the direct velocity widget - self.per_motor = False - self.drive_widget.setCurrentWidget(self.direct_velocity_widget) - - # Enable direct sliders and disable motor sliders - common_widgets.enable_slider( - self.x_velocity_slider, self.x_velocity_label, self.value_change - ) - common_widgets.enable_slider( - self.y_velocity_slider, self.y_velocity_label, self.value_change - ) - common_widgets.enable_slider( - self.angular_velocity_slider, self.angular_velocity_label, self.value_change - ) - - common_widgets.disable_slider(self.front_left_motor_slider) - common_widgets.disable_slider(self.front_right_motor_slider) - common_widgets.disable_slider(self.back_left_motor_slider) - common_widgets.disable_slider(self.back_right_motor_slider) - - common_widgets.change_button_state(self.stop_and_reset_direct, True) - common_widgets.change_button_state(self.stop_and_reset_per_motor, False) - - else: - self.per_motor = True - # Show the per motor widget - self.drive_widget.setCurrentWidget(self.per_motor_widget) - - # Enable motor sliders and disable direct sliders - common_widgets.enable_slider( - self.front_left_motor_slider, self.front_left_motor_label, self.value_change - ) - common_widgets.enable_slider( - self.front_right_motor_slider, self.front_right_motor_label, self.value_change - ) - common_widgets.enable_slider( - self.back_left_motor_slider, self.back_left_motor_label, self.value_change - ) - common_widgets.enable_slider( - self.back_right_motor_slider, self.back_right_motor_label, self.value_change - ) - - common_widgets.disable_slider(self.x_velocity_slider) - common_widgets.disable_slider(self.y_velocity_slider) - common_widgets.disable_slider(self.angular_velocity_slider) - - common_widgets.change_button_state(self.stop_and_reset_per_motor, True) - common_widgets.change_button_state(self.stop_and_reset_direct, False) - - def toggle_dribbler_sliders(self, enable: bool) -> None: - """Enables or disables dribbler sliders. - - :param enable: True to enable, False to disable - """ - if enable: - common_widgets.enable_slider( - self.dribbler_speed_rpm_slider, - self.dribbler_speed_rpm_label, - self.value_change, - ) - common_widgets.change_button_state(self.stop_and_reset_dribbler, True) - else: - common_widgets.disable_slider(self.dribbler_speed_rpm_slider) - common_widgets.change_button_state(self.stop_and_reset_dribbler, False) - - def disconnect_direct_sliders(self) -> None: - """Disconnect listener for changing values for motor sliders""" - self.x_velocity_slider.valueChanged.disconnect() - self.y_velocity_slider.valueChanged.disconnect() - self.angular_velocity_slider.valueChanged.disconnect() - - def disconnect_dribbler_sliders(self) -> None: - self.dribbler_speed_rpm_slider.valueChanged.disconnect() - - def disconnect_motor_sliders(self) -> None: - self.front_left_motor_slider.valueChanged.disconnect() - self.front_right_motor_slider.valueChanged.disconnect() - self.back_left_motor_slider.valueChanged.disconnect() - self.back_right_motor_slider.valueChanged.disconnect() - - def reset_direct_sliders(self) -> None: - """Reset direct sliders back to 0""" - self.x_velocity_slider.setValue(0) - self.y_velocity_slider.setValue(0) - self.angular_velocity_slider.setValue(0) - - def reset_motor_sliders(self) -> None: - """Reset direct sliders back to 0""" - self.front_left_motor_slider.setValue(0) - self.front_right_motor_slider.setValue(0) - self.back_left_motor_slider.setValue(0) - self.back_right_motor_slider.setValue(0) - - def reset_dribbler_slider(self) -> None: - """Reset the dribbler slider back to 0""" - self.dribbler_speed_rpm_slider.setValue(0) - - def reset_all_sliders(self) -> None: - """Reset all sliders back to 0""" - self.reset_direct_sliders() - self.reset_motor_sliders() - self.reset_dribbler_slider() diff --git a/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget_LOCAL_15256.py b/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget_LOCAL_15256.py deleted file mode 100644 index ab0bc80320..0000000000 --- a/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget_LOCAL_15256.py +++ /dev/null @@ -1,416 +0,0 @@ -from pyqtgraph.Qt.QtCore import Qt -from pyqtgraph.Qt.QtWidgets import * -import time -import software.python_bindings as tbots_cpp -from software.thunderscope.common import common_widgets -from proto.import_all_protos import * -from software.thunderscope.proto_unix_io import ProtoUnixIO -from enum import IntEnum - - -class DriveMode(IntEnum): - """Enum for the 2 drive modes (direct velocity and per-motor)""" - VELOCITY = 0 - MOTOR = 1 - - -class DriveAndDribblerWidget(QWidget): - def __init__(self, proto_unix_io: ProtoUnixIO) -> None: - """Initialize the widget to control the robot's motors - - :param proto_unix_io: the proto_unix_io object - """ - self.input_a = time.time() - self.constants = tbots_cpp.create2021RobotConstants() - QWidget.__init__(self) - - layout = QVBoxLayout() - self.drive_widget = QStackedWidget() - - self.proto_unix_io = proto_unix_io - - # create swappable widget system using stacked widgets - self.direct_velocity_widget = self.setup_direct_velocity("Drive - Direct Velocity") - self.per_motor_widget = self.setup_per_motor("Drive - Per Motor") - self.drive_widget.addWidget(self.direct_velocity_widget) - self.drive_widget.addWidget(self.per_motor_widget) - - layout.addWidget(self.setup_drive_switch_radio("Drive Mode Switch")) - layout.addWidget(self.drive_widget) - layout.addWidget(self.setup_dribbler("Dribbler")) - - self.drive_mode = DriveMode.MOTOR - self.use_direct_velocity.setChecked(True) - self.setLayout(layout) - self.toggle_drive_mode(True) - self.toggle_dribbler_sliders(True) - - def refresh(self) -> None: - """Refresh the widget and send the a MotorControl message with the current values""" - motor_control = MotorControl() - motor_control.dribbler_speed_rpm = int(self.dribbler_speed_rpm_slider.value()) - - if not self.drive_mode == DriveMode.VELOCITY: - motor_control.ClearField("direct_per_wheel_control") - motor_control.direct_velocity_drive.velocity.x_component_meters = ( - self.x_velocity_slider.value() - ) - motor_control.direct_velocity_drive.velocity.y_component_meters = ( - self.y_velocity_slider.value() - ) - motor_control.direct_velocity_drive.angular_velocity.radians_per_second = ( - self.angular_velocity_slider.value() - ) - else: - motor_control.ClearField("direct_velocity_control") - motor_control.direct_per_wheel_drive.front_left_wheel_velocity = ( - self.front_left_motor_slider.value() - ) - motor_control.direct_per_wheel_drive.front_right_wheel_velocity = ( - self.front_right_motor_slider.value() - ) - motor_control.direct_per_wheel_drive.back_left_wheel_velocity = ( - self.back_left_motor_slider.value() - ) - motor_control.direct_per_wheel_drive.back_right_wheel_velocity = ( - self.back_right_motor_slider.value() - ) - - self.proto_unix_io.send_proto(MotorControl, motor_control) - - def value_change(self, value: float) -> str: - """Converts the given float value to a string label - - :param value: float value to be converted - """ - value = float(value) - value_str = "%.2f" % value - return value_str - - def setup_drive_switch_radio(self, title: str) -> QGroupBox: - """Create a radio button widget to switch between per-motor and velocity drive modes - - :param title: vbox name - """ - group_box = QGroupBox(title) - vbox = QVBoxLayout() - self.connect_options_group = QButtonGroup() - radio_button_names = ["Velocity Control", "Per Motor Control"] - self.connect_options_box, self.connect_options = common_widgets.create_radio( - radio_button_names, self.connect_options_group - ) - self.use_direct_velocity = self.connect_options[DriveMode.VELOCITY] - self.use_per_motor = self.connect_options[DriveMode.MOTOR] - self.use_direct_velocity.clicked.connect( - lambda: self.toggle_drive_mode(True) - ) - self.use_per_motor.clicked.connect( - lambda: self.toggle_drive_mode(False) - ) - vbox.addWidget(self.connect_options_box) - group_box.setLayout(vbox) - - return group_box - - def setup_direct_velocity(self, title: str) -> QGroupBox: - """Create a widget to control the direct velocity of the robot's motors - - :param title: the name of the slider - """ - group_box = QGroupBox(title) - dbox = QVBoxLayout() - - ( - x_layout, - self.x_velocity_slider, - self.x_velocity_label, - ) = common_widgets.create_float_slider( - "X (m/s)", - 2, - -self.constants.robot_max_speed_m_per_s, - self.constants.robot_max_speed_m_per_s, - 1, - ) - ( - y_layout, - self.y_velocity_slider, - self.y_velocity_label, - ) = common_widgets.create_float_slider( - "Y (m/s)", - 2, - -self.constants.robot_max_speed_m_per_s, - self.constants.robot_max_speed_m_per_s, - 1, - ) - ( - dps_layout, - self.angular_velocity_slider, - self.angular_velocity_label, - ) = common_widgets.create_float_slider( - "θ (rad/s)", - 2, - -self.constants.robot_max_ang_speed_rad_per_s, - self.constants.robot_max_ang_speed_rad_per_s, - 1, - ) - - # add listener functions for sliders to update label with slider value - common_widgets.enable_slider( - self.x_velocity_slider, self.x_velocity_label, self.value_change - ) - common_widgets.enable_slider( - self.y_velocity_slider, self.y_velocity_label, self.value_change - ) - common_widgets.enable_slider( - self.angular_velocity_slider, self.angular_velocity_label, self.value_change - ) - - self.stop_and_reset_direct = QPushButton("Stop and Reset") - self.stop_and_reset_direct.clicked.connect(self.reset_direct_sliders) - - dbox.addLayout(x_layout) - dbox.addLayout(y_layout) - dbox.addLayout(dps_layout) - dbox.addWidget( - self.stop_and_reset_direct, alignment=Qt.AlignmentFlag.AlignCenter - ) - - group_box.setLayout(dbox) - - return group_box - - def setup_per_motor(self, title: str) -> QGroupBox: - """Create a widget to control the rotation rate of each motor - - :param title: the name of the group box - """ - - group_box = QGroupBox(title) - dbox = QVBoxLayout() - - ( - fl_layout, - self.front_left_motor_slider, - self.front_left_motor_label, - ) = common_widgets.create_float_slider( - "Front Left Motor (m/s)", - 2, - -self.constants.robot_max_speed_m_per_s, - self.constants.robot_max_speed_m_per_s, - 1, - ) - ( - fr_layout, - self.front_right_motor_slider, - self.front_right_motor_label, - ) = common_widgets.create_float_slider( - "Front Right Motor (m/s)", - 2, - -self.constants.robot_max_speed_m_per_s, - self.constants.robot_max_speed_m_per_s, - 1, - ) - ( - bl_layout, - self.back_left_motor_slider, - self.back_left_motor_label, - ) = common_widgets.create_float_slider( - "Back Left Motor (m/s)", - 2, - -self.constants.robot_max_speed_m_per_s, - self.constants.robot_max_speed_m_per_s, - 1, - ) - ( - br_layout, - self.back_right_motor_slider, - self.back_right_motor_label, - ) = common_widgets.create_float_slider( - "Back Right Motor (m/s)", - 2, - -self.constants.robot_max_speed_m_per_s, - self.constants.robot_max_speed_m_per_s, - 1, - ) - - # Add listener functions for each motor's slider to update labels with slider values - common_widgets.enable_slider( - self.front_left_motor_slider, self.front_left_motor_label, self.value_change - ) - common_widgets.enable_slider( - self.front_right_motor_slider, self.front_right_motor_label, self.value_change - ) - common_widgets.enable_slider( - self.back_left_motor_slider, self.back_left_motor_label, self.value_change - ) - common_widgets.enable_slider( - self.back_right_motor_slider, self.back_right_motor_label, self.value_change - ) - - # Stop and Reset button for per-motor sliders - self.stop_and_reset_per_motor = QPushButton("Stop and Reset") - self.stop_and_reset_per_motor.clicked.connect(self.reset_motor_sliders) - - # Adding layouts to the box - dbox.addLayout(fl_layout) - dbox.addLayout(fr_layout) - dbox.addLayout(bl_layout) - dbox.addLayout(br_layout) - dbox.addWidget( - self.stop_and_reset_per_motor, alignment=Qt.AlignmentFlag.AlignCenter - ) - - group_box.setLayout(dbox) - - return group_box - - def setup_dribbler(self, title: str) -> QGroupBox: - """Create a widget to control the dribbler RPM - - :param title: the name of the slider - """ - group_box = QGroupBox(title) - dbox = QVBoxLayout() - - ( - dribbler_layout, - self.dribbler_speed_rpm_slider, - self.dribbler_speed_rpm_label, - ) = common_widgets.create_float_slider( - "RPM", - 1, - self.constants.indefinite_dribbler_speed_rpm, - -self.constants.indefinite_dribbler_speed_rpm, - 1, - ) - - # add listener function to update label with slider value - common_widgets.enable_slider( - self.dribbler_speed_rpm_slider, - self.dribbler_speed_rpm_label, - self.value_change, - ) - - self.stop_and_reset_dribbler = QPushButton("Stop and Reset") - self.stop_and_reset_dribbler.clicked.connect(self.reset_dribbler_slider) - - dbox.addLayout(dribbler_layout) - dbox.addWidget( - self.stop_and_reset_dribbler, alignment=Qt.AlignmentFlag.AlignCenter - ) - group_box.setLayout(dbox) - - return group_box - - def toggle_drive_mode(self, use_drive_mode: IntEnum) -> None: - """Switches between 'Direct Velocity' and 'Per Motor' drive modes. - - :param use_drive_mode: DriveMode.VELOCITY or DriveMode.MOTOR, switch to that mode. - """ - # reset sliders - self.reset_motor_sliders() - self.reset_direct_sliders() - self.disconnect_direct_sliders() - self.disconnect_motor_sliders() - - if use_drive_mode == DriveMode.VELOCITY: - # Show the direct velocity widget - self.drive_widget.setCurrentWidget(self.direct_velocity_widget) - - # Enable direct sliders and disable motor sliders - common_widgets.enable_slider( - self.x_velocity_slider, self.x_velocity_label, self.value_change - ) - common_widgets.enable_slider( - self.y_velocity_slider, self.y_velocity_label, self.value_change - ) - common_widgets.enable_slider( - self.angular_velocity_slider, self.angular_velocity_label, self.value_change - ) - - common_widgets.disable_slider(self.front_left_motor_slider) - common_widgets.disable_slider(self.front_right_motor_slider) - common_widgets.disable_slider(self.back_left_motor_slider) - common_widgets.disable_slider(self.back_right_motor_slider) - - common_widgets.change_button_state(self.stop_and_reset_direct, True) - common_widgets.change_button_state(self.stop_and_reset_per_motor, False) - - else: - # Show the per motor widget - self.drive_widget.setCurrentWidget(self.per_motor_widget) - - # Enable motor sliders and disable direct sliders - common_widgets.enable_slider( - self.front_left_motor_slider, self.front_left_motor_label, self.value_change - ) - common_widgets.enable_slider( - self.front_right_motor_slider, self.front_right_motor_label, self.value_change - ) - common_widgets.enable_slider( - self.back_left_motor_slider, self.back_left_motor_label, self.value_change - ) - common_widgets.enable_slider( - self.back_right_motor_slider, self.back_right_motor_label, self.value_change - ) - - common_widgets.disable_slider(self.x_velocity_slider) - common_widgets.disable_slider(self.y_velocity_slider) - common_widgets.disable_slider(self.angular_velocity_slider) - - common_widgets.change_button_state(self.stop_and_reset_per_motor, True) - common_widgets.change_button_state(self.stop_and_reset_direct, False) - - def toggle_dribbler_sliders(self, enable: bool) -> None: - """Enables or disables dribbler sliders. - - :param enable: True to enable, False to disable - """ - if enable: - common_widgets.enable_slider( - self.dribbler_speed_rpm_slider, - self.dribbler_speed_rpm_label, - self.value_change, - ) - common_widgets.change_button_state(self.stop_and_reset_dribbler, True) - else: - common_widgets.disable_slider(self.dribbler_speed_rpm_slider) - common_widgets.change_button_state(self.stop_and_reset_dribbler, False) - - def disconnect_direct_sliders(self) -> None: - """Disconnect listener for changing values for motor sliders""" - self.x_velocity_slider.valueChanged.disconnect() - self.y_velocity_slider.valueChanged.disconnect() - self.angular_velocity_slider.valueChanged.disconnect() - - def disconnect_dribbler_sliders(self) -> None: - self.dribbler_speed_rpm_slider.valueChanged.disconnect() - - def disconnect_motor_sliders(self) -> None: - self.front_left_motor_slider.valueChanged.disconnect() - self.front_right_motor_slider.valueChanged.disconnect() - self.back_left_motor_slider.valueChanged.disconnect() - self.back_right_motor_slider.valueChanged.disconnect() - - def reset_direct_sliders(self) -> None: - """Reset direct sliders back to 0""" - self.x_velocity_slider.setValue(0) - self.y_velocity_slider.setValue(0) - self.angular_velocity_slider.setValue(0) - - def reset_motor_sliders(self) -> None: - """Reset direct sliders back to 0""" - self.front_left_motor_slider.setValue(0) - self.front_right_motor_slider.setValue(0) - self.back_left_motor_slider.setValue(0) - self.back_right_motor_slider.setValue(0) - - def reset_dribbler_slider(self) -> None: - """Reset the dribbler slider back to 0""" - self.dribbler_speed_rpm_slider.setValue(0) - - def reset_all_sliders(self) -> None: - """Reset all sliders back to 0""" - self.reset_direct_sliders() - self.reset_motor_sliders() - self.reset_dribbler_slider() diff --git a/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget_REMOTE_15256.py b/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget_REMOTE_15256.py deleted file mode 100644 index 233e092716..0000000000 --- a/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget_REMOTE_15256.py +++ /dev/null @@ -1,432 +0,0 @@ -from pyqtgraph.Qt.QtCore import Qt -from pyqtgraph.Qt.QtWidgets import * -import time -import software.python_bindings as tbots_cpp -from software.thunderscope.common import common_widgets -from proto.import_all_protos import * -from software.thunderscope.proto_unix_io import ProtoUnixIO -from enum import IntEnum - - -class DriveMode(IntEnum): - """Enum for the 2 drive modes (direct velocity and per-motor)""" - - VELOCITY = 0 - MOTOR = 1 - - -class DriveAndDribblerWidget(QWidget): - def __init__(self, proto_unix_io: ProtoUnixIO) -> None: - """Initialize the widget to control the robot's motors - - :param proto_unix_io: the proto_unix_io object - """ - self.input_a = time.time() - self.constants = tbots_cpp.create2021RobotConstants() - QWidget.__init__(self) - - layout = QVBoxLayout() - self.drive_widget = QStackedWidget() - - self.proto_unix_io = proto_unix_io - - # create swappable widget system using stacked widgets - self.direct_velocity_widget = self.setup_direct_velocity( - "Drive - Direct Velocity" - ) - self.per_motor_widget = self.setup_per_motor("Drive - Per Motor") - self.drive_widget.addWidget(self.direct_velocity_widget) - self.drive_widget.addWidget(self.per_motor_widget) - - layout.addWidget(self.setup_drive_switch_radio("Drive Mode Switch")) - layout.addWidget(self.drive_widget) - layout.addWidget(self.setup_dribbler("Dribbler")) - - self.enabled = True - self.per_motor = False - self.setLayout(layout) - self.toggle_control_mode(True) - self.toggle_dribbler_sliders(True) - - def refresh(self) -> None: - """Refresh the widget and send the a MotorControl message with the current values""" - motor_control = MotorControl() - motor_control.dribbler_speed_rpm = int(self.dribbler_speed_rpm_slider.value()) - - if not self.per_motor: - motor_control.ClearField("direct_per_wheel_control") - motor_control.direct_velocity_control.velocity.x_component_meters = ( - self.x_velocity_slider.value() - ) - motor_control.direct_velocity_control.velocity.y_component_meters = ( - self.y_velocity_slider.value() - ) - motor_control.direct_velocity_control.angular_velocity.radians_per_second = self.angular_velocity_slider.value() - else: - motor_control.ClearField("direct_velocity_control") - motor_control.direct_per_wheel_control.front_left_wheel_velocity = ( - self.front_left_motor_slider.value() - ) - motor_control.direct_per_wheel_control.front_right_wheel_velocity = ( - self.front_right_motor_slider.value() - ) - motor_control.direct_per_wheel_control.back_left_wheel_velocity = ( - self.back_left_motor_slider.value() - ) - motor_control.direct_per_wheel_control.back_right_wheel_velocity = ( - self.back_right_motor_slider.value() - ) - - self.proto_unix_io.send_proto(MotorControl, motor_control) - - def value_change(self, value: float) -> str: - """Converts the given float value to a string label - - :param value: float value to be converted - """ - value = float(value) - value_str = "%.2f" % value - return value_str - - def setup_drive_switch_radio(self, title: str) -> QGroupBox: - """Create a radio button widget to switch between per-motor and velocity control modes - - :param title: group box name - """ - group_box = QGroupBox(title) - dbox = QVBoxLayout() - self.connect_options_group = QButtonGroup() - radio_button_names = ["Velocity Control", "Per Motor Control"] - self.connect_options_box, self.connect_options = common_widgets.create_radio( - radio_button_names, self.connect_options_group - ) - self.use_direct_velocity = self.connect_options[DriveMode.VELOCITY] - self.use_per_motor = self.connect_options[DriveMode.MOTOR] - self.use_direct_velocity.clicked.connect(self.switch_to_velocity) - self.use_per_motor.clicked.connect(self.switch_to_motor) - dbox.addWidget(self.connect_options_box) - group_box.setLayout(dbox) - - return group_box - - def switch_to_velocity(self): - self.toggle_control_mode(True) - - def switch_to_motor(self): - self.toggle_control_mode(False) - - def setup_direct_velocity(self, title: str) -> QGroupBox: - """Create a widget to control the direct velocity of the robot's motors - - :param title: the name of the slider - """ - group_box = QGroupBox(title) - dbox = QVBoxLayout() - - ( - x_layout, - self.x_velocity_slider, - self.x_velocity_label, - ) = common_widgets.create_float_slider( - "X (m/s)", - 2, - -self.constants.robot_max_speed_m_per_s, - self.constants.robot_max_speed_m_per_s, - 1, - ) - ( - y_layout, - self.y_velocity_slider, - self.y_velocity_label, - ) = common_widgets.create_float_slider( - "Y (m/s)", - 2, - -self.constants.robot_max_speed_m_per_s, - self.constants.robot_max_speed_m_per_s, - 1, - ) - ( - dps_layout, - self.angular_velocity_slider, - self.angular_velocity_label, - ) = common_widgets.create_float_slider( - "θ (rad/s)", - 2, - -self.constants.robot_max_ang_speed_rad_per_s, - self.constants.robot_max_ang_speed_rad_per_s, - 1, - ) - - # add listener functions for sliders to update label with slider value - common_widgets.enable_slider( - self.x_velocity_slider, self.x_velocity_label, self.value_change - ) - common_widgets.enable_slider( - self.y_velocity_slider, self.y_velocity_label, self.value_change - ) - common_widgets.enable_slider( - self.angular_velocity_slider, self.angular_velocity_label, self.value_change - ) - - self.stop_and_reset_direct = QPushButton("Stop and Reset") - self.stop_and_reset_direct.clicked.connect(self.reset_direct_sliders) - - dbox.addLayout(x_layout) - dbox.addLayout(y_layout) - dbox.addLayout(dps_layout) - dbox.addWidget( - self.stop_and_reset_direct, alignment=Qt.AlignmentFlag.AlignCenter - ) - - group_box.setLayout(dbox) - - return group_box - - def setup_per_motor(self, title: str) -> QGroupBox: - """Create a widget to control the rotation rate of each motor - - :param title: the name of the group box - """ - group_box = QGroupBox(title) - dbox = QVBoxLayout() - - ( - fl_layout, - self.front_left_motor_slider, - self.front_left_motor_label, - ) = common_widgets.create_float_slider( - "Front Left Motor (m/s)", - 2, - -self.constants.robot_max_speed_m_per_s, - self.constants.robot_max_speed_m_per_s, - 1, - ) - ( - fr_layout, - self.front_right_motor_slider, - self.front_right_motor_label, - ) = common_widgets.create_float_slider( - "Front Right Motor (m/s)", - 2, - -self.constants.robot_max_speed_m_per_s, - self.constants.robot_max_speed_m_per_s, - 1, - ) - ( - bl_layout, - self.back_left_motor_slider, - self.back_left_motor_label, - ) = common_widgets.create_float_slider( - "Back Left Motor (m/s)", - 2, - -self.constants.robot_max_speed_m_per_s, - self.constants.robot_max_speed_m_per_s, - 1, - ) - ( - br_layout, - self.back_right_motor_slider, - self.back_right_motor_label, - ) = common_widgets.create_float_slider( - "Back Right Motor (m/s)", - 2, - -self.constants.robot_max_speed_m_per_s, - self.constants.robot_max_speed_m_per_s, - 1, - ) - - # Add listener functions for each motor's slider to update labels with slider values - common_widgets.enable_slider( - self.front_left_motor_slider, self.front_left_motor_label, self.value_change - ) - common_widgets.enable_slider( - self.front_right_motor_slider, - self.front_right_motor_label, - self.value_change, - ) - common_widgets.enable_slider( - self.back_left_motor_slider, self.back_left_motor_label, self.value_change - ) - common_widgets.enable_slider( - self.back_right_motor_slider, self.back_right_motor_label, self.value_change - ) - - # Stop and Reset button for per-motor sliders - self.stop_and_reset_per_motor = QPushButton("Stop and Reset") - self.stop_and_reset_per_motor.clicked.connect(self.reset_motor_sliders) - - # Adding layouts to the box - dbox.addLayout(fl_layout) - dbox.addLayout(fr_layout) - dbox.addLayout(bl_layout) - dbox.addLayout(br_layout) - dbox.addWidget( - self.stop_and_reset_per_motor, alignment=Qt.AlignmentFlag.AlignCenter - ) - - group_box.setLayout(dbox) - - return group_box - - def setup_dribbler(self, title: str) -> QGroupBox: - """Create a widget to control the dribbler RPM - - :param title: the name of the slider - """ - group_box = QGroupBox(title) - dbox = QVBoxLayout() - - ( - dribbler_layout, - self.dribbler_speed_rpm_slider, - self.dribbler_speed_rpm_label, - ) = common_widgets.create_float_slider( - "RPM", - 1, - self.constants.indefinite_dribbler_speed_rpm, - -self.constants.indefinite_dribbler_speed_rpm, - 1, - ) - - # add listener function to update label with slider value - common_widgets.enable_slider( - self.dribbler_speed_rpm_slider, - self.dribbler_speed_rpm_label, - self.value_change, - ) - - self.stop_and_reset_dribbler = QPushButton("Stop and Reset") - self.stop_and_reset_dribbler.clicked.connect(self.reset_dribbler_slider) - - dbox.addLayout(dribbler_layout) - dbox.addWidget( - self.stop_and_reset_dribbler, alignment=Qt.AlignmentFlag.AlignCenter - ) - group_box.setLayout(dbox) - - return group_box - - def toggle_control_mode(self, use_direct: bool) -> None: - """Switches between 'Direct Velocity' and 'Per Motor' control modes. - - :param use_direct: True to enable Direct Velocity mode, False to enable Per Motor mode. - """ - # reset sliders - self.reset_motor_sliders() - self.reset_direct_sliders() - self.disconnect_direct_sliders() - self.disconnect_motor_sliders() - - if use_direct: - # Show the direct velocity widget - self.per_motor = False - self.drive_widget.setCurrentWidget(self.direct_velocity_widget) - - # Enable direct sliders and disable motor sliders - common_widgets.enable_slider( - self.x_velocity_slider, self.x_velocity_label, self.value_change - ) - common_widgets.enable_slider( - self.y_velocity_slider, self.y_velocity_label, self.value_change - ) - common_widgets.enable_slider( - self.angular_velocity_slider, - self.angular_velocity_label, - self.value_change, - ) - - common_widgets.disable_slider(self.front_left_motor_slider) - common_widgets.disable_slider(self.front_right_motor_slider) - common_widgets.disable_slider(self.back_left_motor_slider) - common_widgets.disable_slider(self.back_right_motor_slider) - - common_widgets.change_button_state(self.stop_and_reset_direct, True) - common_widgets.change_button_state(self.stop_and_reset_per_motor, False) - - else: - self.per_motor = True - # Show the per motor widget - self.drive_widget.setCurrentWidget(self.per_motor_widget) - - # Enable motor sliders and disable direct sliders - common_widgets.enable_slider( - self.front_left_motor_slider, - self.front_left_motor_label, - self.value_change, - ) - common_widgets.enable_slider( - self.front_right_motor_slider, - self.front_right_motor_label, - self.value_change, - ) - common_widgets.enable_slider( - self.back_left_motor_slider, - self.back_left_motor_label, - self.value_change, - ) - common_widgets.enable_slider( - self.back_right_motor_slider, - self.back_right_motor_label, - self.value_change, - ) - - common_widgets.disable_slider(self.x_velocity_slider) - common_widgets.disable_slider(self.y_velocity_slider) - common_widgets.disable_slider(self.angular_velocity_slider) - - common_widgets.change_button_state(self.stop_and_reset_per_motor, True) - common_widgets.change_button_state(self.stop_and_reset_direct, False) - - def toggle_dribbler_sliders(self, enable: bool) -> None: - """Enables or disables dribbler sliders. - - :param enable: True to enable, False to disable - """ - if enable: - common_widgets.enable_slider( - self.dribbler_speed_rpm_slider, - self.dribbler_speed_rpm_label, - self.value_change, - ) - common_widgets.change_button_state(self.stop_and_reset_dribbler, True) - else: - common_widgets.disable_slider(self.dribbler_speed_rpm_slider) - common_widgets.change_button_state(self.stop_and_reset_dribbler, False) - - def disconnect_direct_sliders(self) -> None: - """Disconnect listener for changing values for motor sliders""" - self.x_velocity_slider.valueChanged.disconnect() - self.y_velocity_slider.valueChanged.disconnect() - self.angular_velocity_slider.valueChanged.disconnect() - - def disconnect_dribbler_sliders(self) -> None: - self.dribbler_speed_rpm_slider.valueChanged.disconnect() - - def disconnect_motor_sliders(self) -> None: - self.front_left_motor_slider.valueChanged.disconnect() - self.front_right_motor_slider.valueChanged.disconnect() - self.back_left_motor_slider.valueChanged.disconnect() - self.back_right_motor_slider.valueChanged.disconnect() - - def reset_direct_sliders(self) -> None: - """Reset direct sliders back to 0""" - self.x_velocity_slider.setValue(0) - self.y_velocity_slider.setValue(0) - self.angular_velocity_slider.setValue(0) - - def reset_motor_sliders(self) -> None: - """Reset direct sliders back to 0""" - self.front_left_motor_slider.setValue(0) - self.front_right_motor_slider.setValue(0) - self.back_left_motor_slider.setValue(0) - self.back_right_motor_slider.setValue(0) - - def reset_dribbler_slider(self) -> None: - """Reset the dribbler slider back to 0""" - self.dribbler_speed_rpm_slider.setValue(0) - - def reset_all_sliders(self) -> None: - """Reset all sliders back to 0""" - self.reset_direct_sliders() - self.reset_motor_sliders() - self.reset_dribbler_slider() From 90a4e51b0d86ea75cd4488705ee738907b3baf87 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci-lite[bot]" <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 10:07:51 +0000 Subject: [PATCH 08/10] [pre-commit.ci lite] apply automatic fixes --- .../drive_and_dribbler_widget.py | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget.py b/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget.py index 7ab773f8ee..80af3d40aa 100644 --- a/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget.py +++ b/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget.py @@ -10,6 +10,7 @@ class DriveMode(IntEnum): """Enum for the 2 drive modes (direct velocity and per-motor)""" + VELOCITY = 0 MOTOR = 1 @@ -30,7 +31,9 @@ def __init__(self, proto_unix_io: ProtoUnixIO) -> None: self.proto_unix_io = proto_unix_io # create swappable widget system using stacked widgets - self.direct_velocity_widget = self.setup_direct_velocity("Drive - Direct Velocity") + self.direct_velocity_widget = self.setup_direct_velocity( + "Drive - Direct Velocity" + ) self.per_motor_widget = self.setup_per_motor("Drive - Per Motor") self.drive_widget.addWidget(self.direct_velocity_widget) self.drive_widget.addWidget(self.per_motor_widget) @@ -185,7 +188,6 @@ def setup_per_motor(self, title: str) -> QGroupBox: :param title: the name of the group box """ - group_box = QGroupBox(title) dbox = QVBoxLayout() @@ -239,7 +241,9 @@ def setup_per_motor(self, title: str) -> QGroupBox: self.front_left_motor_slider, self.front_left_motor_label, self.value_change ) common_widgets.enable_slider( - self.front_right_motor_slider, self.front_right_motor_label, self.value_change + self.front_right_motor_slider, + self.front_right_motor_label, + self.value_change, ) common_widgets.enable_slider( self.back_left_motor_slider, self.back_left_motor_label, self.value_change @@ -315,7 +319,6 @@ def toggle_drive_mode(self, use_drive_mode: IntEnum) -> None: self.disconnect_motor_sliders() if use_drive_mode == DriveMode.VELOCITY: - # Show the direct velocity widget self.drive_widget.setCurrentWidget(self.direct_velocity_widget) @@ -327,7 +330,9 @@ def toggle_drive_mode(self, use_drive_mode: IntEnum) -> None: self.y_velocity_slider, self.y_velocity_label, self.value_change ) common_widgets.enable_slider( - self.angular_velocity_slider, self.angular_velocity_label, self.value_change + self.angular_velocity_slider, + self.angular_velocity_label, + self.value_change, ) common_widgets.disable_slider(self.front_left_motor_slider) @@ -344,16 +349,24 @@ def toggle_drive_mode(self, use_drive_mode: IntEnum) -> None: # Enable motor sliders and disable direct sliders common_widgets.enable_slider( - self.front_left_motor_slider, self.front_left_motor_label, self.value_change + self.front_left_motor_slider, + self.front_left_motor_label, + self.value_change, ) common_widgets.enable_slider( - self.front_right_motor_slider, self.front_right_motor_label, self.value_change + self.front_right_motor_slider, + self.front_right_motor_label, + self.value_change, ) common_widgets.enable_slider( - self.back_left_motor_slider, self.back_left_motor_label, self.value_change + self.back_left_motor_slider, + self.back_left_motor_label, + self.value_change, ) common_widgets.enable_slider( - self.back_right_motor_slider, self.back_right_motor_label, self.value_change + self.back_right_motor_slider, + self.back_right_motor_label, + self.value_change, ) common_widgets.disable_slider(self.x_velocity_slider) From eb5dc3e691be6aaab98151d89eff26036f32476a Mon Sep 17 00:00:00 2001 From: Muk Date: Sat, 9 Nov 2024 13:35:47 -0800 Subject: [PATCH 09/10] changed 'drive' to 'control' --- .../drive_and_dribbler_widget.py | 55 +++++++++---------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget.py b/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget.py index 7ab773f8ee..9730a17493 100644 --- a/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget.py +++ b/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget.py @@ -8,7 +8,7 @@ from enum import IntEnum -class DriveMode(IntEnum): +class ControlMode(IntEnum): """Enum for the 2 drive modes (direct velocity and per-motor)""" VELOCITY = 0 MOTOR = 1 @@ -25,24 +25,22 @@ def __init__(self, proto_unix_io: ProtoUnixIO) -> None: QWidget.__init__(self) layout = QVBoxLayout() - self.drive_widget = QStackedWidget() + self.control_widget = QStackedWidget() self.proto_unix_io = proto_unix_io # create swappable widget system using stacked widgets - self.direct_velocity_widget = self.setup_direct_velocity("Drive - Direct Velocity") - self.per_motor_widget = self.setup_per_motor("Drive - Per Motor") - self.drive_widget.addWidget(self.direct_velocity_widget) - self.drive_widget.addWidget(self.per_motor_widget) + self.direct_velocity_widget = self.setup_direct_velocity("Control - Direct Velocity") + self.per_motor_widget = self.setup_per_motor("Control - Per Motor") + self.control_widget.addWidget(self.direct_velocity_widget) + self.control_widget.addWidget(self.per_motor_widget) layout.addWidget(self.setup_drive_switch_radio("Drive Mode Switch")) - layout.addWidget(self.drive_widget) + layout.addWidget(self.control_widget) layout.addWidget(self.setup_dribbler("Dribbler")) # default to direct velocity - self.drive_mode = DriveMode.VELOCITY - self.use_direct_velocity.setChecked(True) - self.toggle_drive_mode(DriveMode.VELOCITY) + self.use_direct_velocity.click() self.setLayout(layout) self.toggle_dribbler_sliders(True) @@ -50,30 +48,29 @@ def refresh(self) -> None: """Refresh the widget and send the a MotorControl message with the current values""" motor_control = MotorControl() motor_control.dribbler_speed_rpm = int(self.dribbler_speed_rpm_slider.value()) - - if not self.drive_mode == DriveMode.VELOCITY: + if self.control_mode == ControlMode.MOTOR: motor_control.ClearField("direct_per_wheel_control") - motor_control.direct_velocity_drive.velocity.x_component_meters = ( + motor_control.direct_velocity_control.velocity.x_component_meters = ( self.x_velocity_slider.value() ) - motor_control.direct_velocity_drive.velocity.y_component_meters = ( + motor_control.direct_velocity_control.velocity.y_component_meters = ( self.y_velocity_slider.value() ) - motor_control.direct_velocity_drive.angular_velocity.radians_per_second = ( + motor_control.direct_velocity_control.angular_velocity.radians_per_second = ( self.angular_velocity_slider.value() ) else: motor_control.ClearField("direct_velocity_control") - motor_control.direct_per_wheel_drive.front_left_wheel_velocity = ( + motor_control.direct_per_wheel_control.front_left_wheel_velocity = ( self.front_left_motor_slider.value() ) - motor_control.direct_per_wheel_drive.front_right_wheel_velocity = ( + motor_control.direct_per_wheel_control.front_right_wheel_velocity = ( self.front_right_motor_slider.value() ) - motor_control.direct_per_wheel_drive.back_left_wheel_velocity = ( + motor_control.direct_per_wheel_control.back_left_wheel_velocity = ( self.back_left_motor_slider.value() ) - motor_control.direct_per_wheel_drive.back_right_wheel_velocity = ( + motor_control.direct_per_wheel_control.back_right_wheel_velocity = ( self.back_right_motor_slider.value() ) @@ -100,13 +97,13 @@ def setup_drive_switch_radio(self, title: str) -> QGroupBox: self.connect_options_box, self.connect_options = common_widgets.create_radio( radio_button_names, self.connect_options_group ) - self.use_direct_velocity = self.connect_options[DriveMode.VELOCITY] - self.use_per_motor = self.connect_options[DriveMode.MOTOR] + self.use_direct_velocity = self.connect_options[ControlMode.VELOCITY] + self.use_per_motor = self.connect_options[ControlMode.MOTOR] self.use_direct_velocity.clicked.connect( - lambda: self.toggle_drive_mode(DriveMode.VELOCITY) + lambda: self.toggle_control_mode(ControlMode.VELOCITY) ) self.use_per_motor.clicked.connect( - lambda: self.toggle_drive_mode(DriveMode.MOTOR) + lambda: self.toggle_control_mode(ControlMode.MOTOR) ) vbox.addWidget(self.connect_options_box) group_box.setLayout(vbox) @@ -303,21 +300,23 @@ def setup_dribbler(self, title: str) -> QGroupBox: return group_box - def toggle_drive_mode(self, use_drive_mode: IntEnum) -> None: + def toggle_control_mode(self, use_control_mode: IntEnum) -> None: """Switches between 'Direct Velocity' and 'Per Motor' drive modes. - :param use_drive_mode: DriveMode.VELOCITY or DriveMode.MOTOR, switch to that mode. + :param use_control_mode: ControlMode.VELOCITY or ControlMode.MOTOR, switch to that mode. """ + + self.control_mode = use_control_mode # reset sliders self.reset_motor_sliders() self.reset_direct_sliders() self.disconnect_direct_sliders() self.disconnect_motor_sliders() - if use_drive_mode == DriveMode.VELOCITY: + if use_control_mode == ControlMode.VELOCITY: # Show the direct velocity widget - self.drive_widget.setCurrentWidget(self.direct_velocity_widget) + self.control_widget.setCurrentWidget(self.direct_velocity_widget) # Enable direct sliders and disable motor sliders common_widgets.enable_slider( @@ -340,7 +339,7 @@ def toggle_drive_mode(self, use_drive_mode: IntEnum) -> None: else: # Show the per motor widget - self.drive_widget.setCurrentWidget(self.per_motor_widget) + self.control_widget.setCurrentWidget(self.per_motor_widget) # Enable motor sliders and disable direct sliders common_widgets.enable_slider( From b9ec6fdd03b2904cefe03e433ba7fd80e7290aa5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci-lite[bot]" <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Date: Sat, 9 Nov 2024 21:49:22 +0000 Subject: [PATCH 10/10] [pre-commit.ci lite] apply automatic fixes --- .../drive_and_dribbler_widget.py | 36 ++++++++++++------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget.py b/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget.py index 9730a17493..2d6d8f4840 100644 --- a/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget.py +++ b/src/software/thunderscope/robot_diagnostics/drive_and_dribbler_widget.py @@ -10,6 +10,7 @@ class ControlMode(IntEnum): """Enum for the 2 drive modes (direct velocity and per-motor)""" + VELOCITY = 0 MOTOR = 1 @@ -30,7 +31,9 @@ def __init__(self, proto_unix_io: ProtoUnixIO) -> None: self.proto_unix_io = proto_unix_io # create swappable widget system using stacked widgets - self.direct_velocity_widget = self.setup_direct_velocity("Control - Direct Velocity") + self.direct_velocity_widget = self.setup_direct_velocity( + "Control - Direct Velocity" + ) self.per_motor_widget = self.setup_per_motor("Control - Per Motor") self.control_widget.addWidget(self.direct_velocity_widget) self.control_widget.addWidget(self.per_motor_widget) @@ -56,9 +59,7 @@ def refresh(self) -> None: motor_control.direct_velocity_control.velocity.y_component_meters = ( self.y_velocity_slider.value() ) - motor_control.direct_velocity_control.angular_velocity.radians_per_second = ( - self.angular_velocity_slider.value() - ) + motor_control.direct_velocity_control.angular_velocity.radians_per_second = self.angular_velocity_slider.value() else: motor_control.ClearField("direct_velocity_control") motor_control.direct_per_wheel_control.front_left_wheel_velocity = ( @@ -182,7 +183,6 @@ def setup_per_motor(self, title: str) -> QGroupBox: :param title: the name of the group box """ - group_box = QGroupBox(title) dbox = QVBoxLayout() @@ -236,7 +236,9 @@ def setup_per_motor(self, title: str) -> QGroupBox: self.front_left_motor_slider, self.front_left_motor_label, self.value_change ) common_widgets.enable_slider( - self.front_right_motor_slider, self.front_right_motor_label, self.value_change + self.front_right_motor_slider, + self.front_right_motor_label, + self.value_change, ) common_widgets.enable_slider( self.back_left_motor_slider, self.back_left_motor_label, self.value_change @@ -305,7 +307,6 @@ def toggle_control_mode(self, use_control_mode: IntEnum) -> None: :param use_control_mode: ControlMode.VELOCITY or ControlMode.MOTOR, switch to that mode. """ - self.control_mode = use_control_mode # reset sliders self.reset_motor_sliders() @@ -314,7 +315,6 @@ def toggle_control_mode(self, use_control_mode: IntEnum) -> None: self.disconnect_motor_sliders() if use_control_mode == ControlMode.VELOCITY: - # Show the direct velocity widget self.control_widget.setCurrentWidget(self.direct_velocity_widget) @@ -326,7 +326,9 @@ def toggle_control_mode(self, use_control_mode: IntEnum) -> None: self.y_velocity_slider, self.y_velocity_label, self.value_change ) common_widgets.enable_slider( - self.angular_velocity_slider, self.angular_velocity_label, self.value_change + self.angular_velocity_slider, + self.angular_velocity_label, + self.value_change, ) common_widgets.disable_slider(self.front_left_motor_slider) @@ -343,16 +345,24 @@ def toggle_control_mode(self, use_control_mode: IntEnum) -> None: # Enable motor sliders and disable direct sliders common_widgets.enable_slider( - self.front_left_motor_slider, self.front_left_motor_label, self.value_change + self.front_left_motor_slider, + self.front_left_motor_label, + self.value_change, ) common_widgets.enable_slider( - self.front_right_motor_slider, self.front_right_motor_label, self.value_change + self.front_right_motor_slider, + self.front_right_motor_label, + self.value_change, ) common_widgets.enable_slider( - self.back_left_motor_slider, self.back_left_motor_label, self.value_change + self.back_left_motor_slider, + self.back_left_motor_label, + self.value_change, ) common_widgets.enable_slider( - self.back_right_motor_slider, self.back_right_motor_label, self.value_change + self.back_right_motor_slider, + self.back_right_motor_label, + self.value_change, ) common_widgets.disable_slider(self.x_velocity_slider)