def test_verify_config_with_signal_with_negative_index():
    """Should fail because a signal's index is negative."""
    config = copy.deepcopy(VALID_CONFIG)
    config[translator.Signal.LEFT_WHEEL][translator.INDEX_KEY] = -1

    try:
        translator.verify_config(config)
        assert False, "verify_config() should have failed"
    except Exception as error:
        assert isinstance(error, translator.VerificationError)
        assert "index must be non-negative" in error.message
def test_verify_config_with_signal_without_index():
    """Should fail because a signal does not have an index."""
    config = copy.deepcopy(VALID_CONFIG)
    del config[translator.Signal.LEFT_WHEEL][translator.INDEX_KEY]

    try:
        translator.verify_config(config)
        assert False, "verify_config() should have failed"
    except Exception as error:
        assert isinstance(error, translator.VerificationError)
        assert "must include an index" in error.message
def test_verify_config_with_missing_signal():
    """Should fail because a signal is missing."""
    config = copy.deepcopy(VALID_CONFIG)
    del config[translator.Signal.LEFT_WHEEL]

    try:
        translator.verify_config(config)
        assert False, "verify_config() should have failed"
    except Exception as error:
        assert isinstance(error, translator.VerificationError)
        assert "is missing from config" in error.message
def test_verify_config_with_signal_with_invalid_type():
    """Should fail because a signal's type is invalid."""
    config = copy.deepcopy(VALID_CONFIG)
    config[translator.Signal.LEFT_WHEEL][translator.TYPE_KEY] = "invalid"
    print(config)

    try:
        translator.verify_config(config)
        assert False, "verify_config() should have failed"
    except Exception as error:
        assert isinstance(error, translator.VerificationError)
        assert "type must be 'button' or 'axis'" in error.message
def test_verify_config_with_signal_with_non_float_accept():
    """Should fail because a signal's accept is not a float."""
    config = copy.deepcopy(VALID_CONFIG)
    config[translator.Signal.STOP_ROUTINE][
        translator.TYPE_KEY
    ] = translator.AXIS_TYPE
    config[translator.Signal.STOP_ROUTINE][translator.ACCEPT_KEY] = "invalid"

    try:
        translator.verify_config(config)
        assert False, "verify_config() should have failed"
    except Exception as error:
        assert isinstance(error, translator.VerificationError)
        assert "accept must be a float" in error.message
Beispiel #6
0
def main(passed_args=None):
    """Main entry point for the ROS node."""
    try:
        rclpy.init(args=passed_args)
        node = rclpy.create_node(NODE)

        # Declare "type", "index", and "accept" parameters for each signal.
        # These parameters are usually loaded from files in the config/
        # directory.
        for signal in translator.Signal:
            node.declare_parameter(
                SUBPARAMETER_FORMAT.format(signal.value,
                                           translator.TYPE_KEY), )
            node.declare_parameter(
                SUBPARAMETER_FORMAT.format(signal.value,
                                           translator.INDEX_KEY), )
            node.declare_parameter(
                SUBPARAMETER_FORMAT.format(signal.value,
                                           translator.ACCEPT_KEY), )

        # Convert all available parameters into a config map, then verify that
        # the map is valid.
        config = {}
        for signal in translator.Signal:
            signal_type = node.get_parameter(
                SUBPARAMETER_FORMAT.format(signal.value,
                                           translator.TYPE_KEY), ).value
            signal_index = node.get_parameter(
                SUBPARAMETER_FORMAT.format(signal.value,
                                           translator.INDEX_KEY), ).value
            signal_accept = node.get_parameter(
                SUBPARAMETER_FORMAT.format(signal.value,
                                           translator.ACCEPT_KEY), ).value
            if (signal_type is not None or signal_index is not None
                    or signal_accept is not None):
                config[signal] = {}
                if signal_type is not None:
                    config[signal][translator.TYPE_KEY] = signal_type
                if signal_index is not None:
                    config[signal][translator.INDEX_KEY] = signal_index
                if signal_accept is not None:
                    config[signal][translator.ACCEPT_KEY] = signal_accept
        translator.verify_config(config)

        # Create publishers for each type of action.
        wheel_actions_publisher = node.create_publisher(
            geometry_msgs.msg.Twist,
            WHEEL_ACTIONS_TOPIC,
            QUEUE_SIZE,
        )
        front_arm_actions_publisher = node.create_publisher(
            std_msgs.msg.Float32,
            FRONT_ARM_ACTIONS_TOPIC,
            QUEUE_SIZE,
        )
        back_arm_actions_publisher = node.create_publisher(
            std_msgs.msg.Float32,
            BACK_ARM_ACTIONS_TOPIC,
            QUEUE_SIZE,
        )
        back_front_actions_publisher = node.create_publisher(
            std_msgs.msg.Float32,
            FRONT_DRUM_ACTIONS_TOPIC,
            QUEUE_SIZE,
        )
        back_drum_actions_publisher = node.create_publisher(
            std_msgs.msg.Float32,
            BACK_DRUM_ACTIONS_TOPIC,
            QUEUE_SIZE,
        )
        routine_actions_publisher = node.create_publisher(
            std_msgs.msg.Int8,
            ROUTINE_ACTIONS_TOPIC,
            QUEUE_SIZE,
        )

        def process_message(create_command, message):
            """Callback to create and process a command from a message.

            The possible values for different action types are viewable in
            command.py.
            """
            command = create_command(message)

            if command.wheel_action is not None:
                wheel_action = geometry_msgs.msg.Twist()
                wheel_action.linear.x = command.wheel_action.linear_x
                wheel_action.angular.z = command.wheel_action.angular_z
                wheel_actions_publisher.publish(wheel_action)

            if command.front_arm_action is not None:
                front_arm_action = std_msgs.msg.Float32()
                front_arm_action.data = command.front_arm_action.value
                front_arm_actions_publisher.publish(front_arm_action)

            if command.back_arm_action is not None:
                back_arm_action = std_msgs.msg.Float32()
                back_arm_action.data = command.back_arm_action.value
                back_arm_actions_publisher.publish(back_arm_action)

            if command.front_drum_action is not None:
                front_drum_action = std_msgs.msg.Float32()
                front_drum_action.data = command.front_drum_action.value
                back_front_actions_publisher.publish(front_drum_action)

            if command.back_drum_action is not None:
                back_drum_action = std_msgs.msg.Float32()
                back_drum_action.data = command.back_drum_action.value
                back_drum_actions_publisher.publish(back_drum_action)

            if command.routine_action is not None:
                routine_action = std_msgs.msg.Int8()
                routine_action.data = command.routine_action.value
                routine_actions_publisher.publish(routine_action)

        # Subscribe to the joy topic and use the process_message() function to
        # handle messages. This function is pre-loaded with a create_command()
        # function (which is pre-loaded with a read_signal() function).
        node.create_subscription(
            sensor_msgs.msg.Joy,
            JOY_TOPIC,
            functools.partial(
                process_message,
                functools.partial(
                    translator.create_command,
                    functools.partial(
                        translator.read_signal,
                        config,
                    ),
                ),
            ),
            QUEUE_SIZE,
        )

        # Spin!
        rclpy.spin(node)
    except translator.VerificationError as error:
        node.get_logger().error(str(error))
        exit(1)
    except KeyboardInterrupt:
        pass
def test_verify_config_with_valid_config():
    """Should do nothing because the configuration is valid."""
    config = copy.deepcopy(VALID_CONFIG)

    translator.verify_config(config)