// Copyright (c) 2023 Franka Emika GmbH // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include #include #include #include #include #include #include #include #include const std::string k_position_controller{"position"}; const std::string k_velocity_controller{"velocity"}; const std::string k_effort_controller{"effort"}; const std::string k_joint_name{"joint"}; const size_t k_number_of_joints{7}; const double k_EPS{1e-5}; class MockModel : public franka_hardware::Model {}; class MockRobot : public franka_hardware::Robot { public: MOCK_METHOD(void, initializeContinuousReading, (), (override)); MOCK_METHOD(void, stopRobot, (), (override)); MOCK_METHOD(void, initializeTorqueControl, (), (override)); MOCK_METHOD(franka::RobotState, read, (), (override)); MOCK_METHOD(MockModel*, getModel, (), (override)); }; auto createHardwareInfo() -> hardware_interface::HardwareInfo { hardware_interface::HardwareInfo info; std::unordered_map hw_params; hw_params["robot_ip"] = "dummy_ip"; info.hardware_parameters = hw_params; hardware_interface::InterfaceInfo command_interface, effort_state_interface, position_state_interface, velocity_state_interface; effort_state_interface.name = hardware_interface::HW_IF_EFFORT; position_state_interface.name = hardware_interface::HW_IF_POSITION; velocity_state_interface.name = hardware_interface::HW_IF_VELOCITY; std::vector state_interfaces = { position_state_interface, velocity_state_interface, effort_state_interface}; for (auto i = 0U; i < k_number_of_joints; i++) { hardware_interface::ComponentInfo joint; joint.name = k_joint_name + std::to_string(i + 1); command_interface.name = k_effort_controller; joint.command_interfaces.push_back(command_interface); joint.state_interfaces = state_interfaces; info.joints.push_back(joint); } return info; } TEST(FrankaHardwareInterfaceTest, when_on_init_called_expect_success) { auto mock_robot = std::make_unique(); const hardware_interface::HardwareInfo info = createHardwareInfo(); franka_hardware::FrankaHardwareInterface franka_hardware_interface(std::move(mock_robot)); auto return_type = franka_hardware_interface.on_init(info); EXPECT_EQ(return_type, rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS); } TEST(FrankaHardwareInterfaceTest, given_that_the_robot_interfaces_set_when_read_called_return_ok) { franka::RobotState robot_state; MockModel mock_model; MockModel* model_address = &mock_model; auto mock_robot = std::make_unique(); EXPECT_CALL(*mock_robot, read()).WillOnce(testing::Return(robot_state)); EXPECT_CALL(*mock_robot, getModel()).WillOnce(testing::Return(model_address)); franka_hardware::FrankaHardwareInterface franka_hardware_interface(std::move(mock_robot)); auto time = rclcpp::Time(0, 0); auto duration = rclcpp::Duration(0, 0); auto return_type = franka_hardware_interface.read(time, duration); EXPECT_EQ(return_type, hardware_interface::return_type::OK); } TEST( FrankaHardwareInterfaceTest, given_that_the_robot_interfaces_are_set_when_call_export_state_return_zero_values_and_correct_interface_names) { franka::RobotState robot_state; const size_t state_interface_size = 23; // position, effort and velocity states for every joint + robot state and model auto mock_robot = std::make_unique(); MockModel mock_model; MockModel* model_address = &mock_model; EXPECT_CALL(*mock_robot, getModel()).WillOnce(testing::Return(model_address)); EXPECT_CALL(*mock_robot, read()).WillOnce(testing::Return(robot_state)); franka_hardware::FrankaHardwareInterface franka_hardware_interface(std::move(mock_robot)); const auto hardware_info = createHardwareInfo(); franka_hardware_interface.on_init(hardware_info); auto time = rclcpp::Time(0); auto duration = rclcpp::Duration(0, 0); auto return_type = franka_hardware_interface.read(time, duration); EXPECT_EQ(return_type, hardware_interface::return_type::OK); auto states = franka_hardware_interface.export_state_interfaces(); size_t joint_index = 0; // Get all the states except the last two reserved for robot state for (size_t i = 0; i < states.size() - 2; i++) { if (i % 3 == 0) { joint_index++; } const std::string joint_name = k_joint_name + std::to_string(joint_index); if (i % 3 == 0) { EXPECT_EQ(states[i].get_name(), joint_name + "/" + k_position_controller); } else if (i % 3 == 1) { EXPECT_EQ(states[i].get_name(), joint_name + "/" + k_velocity_controller); } else { EXPECT_EQ(states[i].get_name(), joint_name + "/" + k_effort_controller); } EXPECT_EQ(states[i].get_value(), 0.0); } EXPECT_EQ(states.size(), state_interface_size); } TEST( FrankaHardwareInterfaceTest, given_that_the_robot_interfaces_are_set_when_call_export_state_interface_robot_model_interface_exists) { franka::RobotState robot_state; const size_t state_interface_size = 23; // position, effort and velocity states for every joint + robot state and model auto mock_robot = std::make_unique(); MockModel mock_model; MockModel* model_address = &mock_model; EXPECT_CALL(*mock_robot, read()).WillOnce(testing::Return(robot_state)); EXPECT_CALL(*mock_robot, getModel()).WillOnce(testing::Return(model_address)); franka_hardware::FrankaHardwareInterface franka_hardware_interface(std::move(mock_robot)); const auto hardware_info = createHardwareInfo(); franka_hardware_interface.on_init(hardware_info); auto time = rclcpp::Time(0); auto duration = rclcpp::Duration(0, 0); auto return_type = franka_hardware_interface.read(time, duration); EXPECT_EQ(return_type, hardware_interface::return_type::OK); auto states = franka_hardware_interface.export_state_interfaces(); EXPECT_EQ(states[state_interface_size - 1].get_name(), "panda/robot_model"); // Last state interface is the robot model state EXPECT_NEAR(states[state_interface_size - 1].get_value(), *reinterpret_cast(&model_address), k_EPS); // testing that the casted mock_model ptr // is correctly pushed to state interface } TEST( FrankaHardwareInterfaceTest, given_that_the_robot_interfaces_are_set_when_call_export_state_interface_robot_state_interface_exists) { const size_t state_interface_size = 23; // position, effort and velocity states for every joint + robot state and model auto mock_robot = std::make_unique(); franka::RobotState robot_state; franka::RobotState* robot_state_address = &robot_state; MockModel mock_model; MockModel* model_address = &mock_model; EXPECT_CALL(*mock_robot, read()).WillOnce(testing::Return(robot_state)); EXPECT_CALL(*mock_robot, getModel()).WillOnce(testing::Return(model_address)); franka_hardware::FrankaHardwareInterface franka_hardware_interface(std::move(mock_robot)); const auto hardware_info = createHardwareInfo(); franka_hardware_interface.on_init(hardware_info); auto time = rclcpp::Time(0); auto duration = rclcpp::Duration(0, 0); auto return_type = franka_hardware_interface.read(time, duration); EXPECT_EQ(return_type, hardware_interface::return_type::OK); auto states = franka_hardware_interface.export_state_interfaces(); EXPECT_EQ(states[state_interface_size - 2].get_name(), "panda/robot_state"); // Last state interface is the robot model state EXPECT_NEAR(states[state_interface_size - 2].get_value(), *reinterpret_cast(&robot_state_address), k_EPS); // testing that the casted robot state ptr // is correctly pushed to state interface } TEST(FrankaHardwareInterfaceTest, when_prepare_command_mode_interface_for_stop_effort_interfaces_expect_ok) { auto mock_robot = std::make_unique(); franka_hardware::FrankaHardwareInterface franka_hardware_interface(std::move(mock_robot)); const auto hardware_info = createHardwareInfo(); franka_hardware_interface.on_init(hardware_info); std::vector stop_interface; for (size_t i = 0; i < hardware_info.joints.size(); i++) { const std::string joint_name = k_joint_name + std::to_string(i); stop_interface.push_back(joint_name + "/" + k_effort_controller); } std::vector start_interface = {}; EXPECT_EQ(franka_hardware_interface.prepare_command_mode_switch(start_interface, stop_interface), hardware_interface::return_type::OK); } TEST( FrankaHardwareInterfaceTest, when_prepare_command_mode_interface_is_called_with_invalid_start_interface_number_expect_throw) { auto mock_robot = std::make_unique(); franka_hardware::FrankaHardwareInterface franka_hardware_interface(std::move(mock_robot)); const auto hardware_info = createHardwareInfo(); franka_hardware_interface.on_init(hardware_info); std::vector stop_interface; for (size_t i = 0; i < hardware_info.joints.size(); i++) { const std::string joint_name = k_joint_name + std::to_string(i); stop_interface.push_back(joint_name + "/" + k_effort_controller); } std::vector start_interface = {"fr3_joint1/effort"}; EXPECT_THROW( franka_hardware_interface.prepare_command_mode_switch(start_interface, stop_interface), std::invalid_argument); } TEST(FrankaHardwareInterfaceTest, when_prepare_command_mode_interface_for_start_effort_interfaces_expect_ok) { auto mock_robot = std::make_unique(); franka_hardware::FrankaHardwareInterface franka_hardware_interface(std::move(mock_robot)); const auto hardware_info = createHardwareInfo(); franka_hardware_interface.on_init(hardware_info); std::vector start_interface; for (size_t i = 0; i < hardware_info.joints.size(); i++) { const std::string joint_name = k_joint_name + std::to_string(i); start_interface.push_back(joint_name + "/" + k_effort_controller); } std::vector stop_interface = {}; EXPECT_EQ(franka_hardware_interface.prepare_command_mode_switch(start_interface, stop_interface), hardware_interface::return_type::OK); } TEST( FrankaHardwareInterfaceTest, when_prepare_command_mode_interface_is_called_with_invalid_stop_interface_number_expect_throw) { auto mock_robot = std::make_unique(); franka_hardware::FrankaHardwareInterface franka_hardware_interface(std::move(mock_robot)); const auto hardware_info = createHardwareInfo(); franka_hardware_interface.on_init(hardware_info); std::vector start_interface; for (size_t i = 0; i < hardware_info.joints.size(); i++) { const std::string joint_name = k_joint_name + std::to_string(i); start_interface.push_back(joint_name + "/" + k_effort_controller); } std::vector stop_interface = {"fr3_joint1/effort"}; EXPECT_THROW( franka_hardware_interface.prepare_command_mode_switch(start_interface, stop_interface), std::invalid_argument); } TEST(FrankaHardwareIntefaceTest, when_write_called_expect_ok) { auto mock_robot = std::make_unique(); franka_hardware::FrankaHardwareInterface franka_hardware_interface(std::move(mock_robot)); const auto hardware_info = createHardwareInfo(); franka_hardware_interface.on_init(hardware_info); const auto time = rclcpp::Time(0, 0); const auto duration = rclcpp::Duration(0, 0); EXPECT_EQ(franka_hardware_interface.write(time, duration), hardware_interface::return_type::OK); } TEST(FrankaHardwareInterfaceTest, when_on_activate_called_expect_success) { franka::RobotState robot_state; MockModel mock_model; MockModel* model_address = &mock_model; auto mock_robot = std::make_unique(); EXPECT_CALL(*mock_robot, read()).WillOnce(testing::Return(robot_state)); EXPECT_CALL(*mock_robot, getModel()).WillOnce(testing::Return(model_address)); EXPECT_CALL(*mock_robot, initializeContinuousReading()); franka_hardware::FrankaHardwareInterface franka_hardware_interface(std::move(mock_robot)); EXPECT_EQ(franka_hardware_interface.on_activate(rclcpp_lifecycle::State()), rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS); } TEST(FrankaHardwareInterfaceTest, when_on_deactivate_called_expect_success) { franka::RobotState robot_state; auto mock_robot = std::make_unique(); EXPECT_CALL(*mock_robot, stopRobot()); franka_hardware::FrankaHardwareInterface franka_hardware_interface(std::move(mock_robot)); EXPECT_EQ(franka_hardware_interface.on_deactivate(rclcpp_lifecycle::State()), rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS); } TEST(FrankaHardwareInterfaceTest, given_start_effort_interface_prepared_when_perform_comamnd_mode_switch_called_expect_ok) { auto mock_robot = std::make_unique(); EXPECT_CALL(*mock_robot, stopRobot()); EXPECT_CALL(*mock_robot, initializeTorqueControl()); franka_hardware::FrankaHardwareInterface franka_hardware_interface(std::move(mock_robot)); const auto hardware_info = createHardwareInfo(); franka_hardware_interface.on_init(hardware_info); std::vector start_interface; for (size_t i = 0; i < hardware_info.joints.size(); i++) { const std::string joint_name = k_joint_name + std::to_string(i); start_interface.push_back(joint_name + "/" + k_effort_controller); } std::vector stop_interface = {}; EXPECT_EQ(franka_hardware_interface.prepare_command_mode_switch(start_interface, stop_interface), hardware_interface::return_type::OK); EXPECT_EQ(franka_hardware_interface.perform_command_mode_switch(start_interface, stop_interface), hardware_interface::return_type::OK); } TEST(FrankaHardwareInterfaceTest, given_that_effort_control_started_perform_command_mode_switch_stop_expect_ok) { auto mock_robot = std::make_unique(); EXPECT_CALL(*mock_robot, stopRobot()).Times(2).WillRepeatedly(testing::Return()); EXPECT_CALL(*mock_robot, initializeTorqueControl()); EXPECT_CALL(*mock_robot, initializeContinuousReading()); franka_hardware::FrankaHardwareInterface franka_hardware_interface(std::move(mock_robot)); const auto hardware_info = createHardwareInfo(); franka_hardware_interface.on_init(hardware_info); std::vector start_interface; for (size_t i = 0; i < hardware_info.joints.size(); i++) { const std::string joint_name = k_joint_name + std::to_string(i); start_interface.push_back(joint_name + "/" + k_effort_controller); } std::vector stop_interface = {}; EXPECT_EQ(franka_hardware_interface.prepare_command_mode_switch(start_interface, stop_interface), hardware_interface::return_type::OK); EXPECT_EQ(franka_hardware_interface.perform_command_mode_switch(start_interface, stop_interface), hardware_interface::return_type::OK); for (size_t i = 0; i < hardware_info.joints.size(); i++) { const std::string joint_name = k_joint_name + std::to_string(i); stop_interface.push_back(joint_name + "/" + k_effort_controller); } start_interface.clear(); EXPECT_EQ(franka_hardware_interface.prepare_command_mode_switch(start_interface, stop_interface), hardware_interface::return_type::OK); EXPECT_EQ(franka_hardware_interface.perform_command_mode_switch(start_interface, stop_interface), hardware_interface::return_type::OK); } int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }