Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add a semantic command interface to "semantic_components" #1945

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
f9081f9
add semantic component command interface
tpoignonec Dec 16, 2024
5585ef6
add led_rgb_device
tpoignonec Dec 16, 2024
7d08efd
add tests for LED device
tpoignonec Dec 16, 2024
b0b64cf
lint
tpoignonec Dec 16, 2024
39ed94c
fix misc typos and fix test errors
tpoignonec Dec 16, 2024
e3b99d3
refactor smr version of semantic command interface to match ros-contr…
Dec 16, 2024
2e2798f
add "validate_custom_names" test case
Dec 16, 2024
6c8b092
Merge branch 'master' into tpo-add_semantic_cmd_interface
tpoignonec Dec 18, 2024
121da64
minor fixes in comments
Dec 20, 2024
ab32971
Change "LEDRgbDevice" to "LedRgbDevice"
Dec 20, 2024
acfcb8c
add release notes
Dec 20, 2024
a71c699
make CI happy
Dec 20, 2024
803a5fb
Merge branch 'master' into tpo-add_semantic_cmd_interface
tpoignonec Dec 20, 2024
342af6a
Merge branch 'master' into tpo-add_semantic_cmd_interface
christophfroehlich Dec 22, 2024
5318268
Merge branch 'master' into tpo-add_semantic_cmd_interface
christophfroehlich Dec 22, 2024
050dbb6
Update controller_interface/include/semantic_components/led_rgb_devic…
tpoignonec Dec 24, 2024
2de80b4
Update controller_interface/include/semantic_components/semantic_comp…
tpoignonec Dec 24, 2024
db6cb8d
Update controller_interface/include/semantic_components/semantic_comp…
tpoignonec Dec 24, 2024
0d0bb54
Update controller_interface/include/semantic_components/semantic_comp…
tpoignonec Dec 24, 2024
f8cb935
Update controller_interface/include/semantic_components/led_rgb_devic…
tpoignonec Dec 24, 2024
a2dfe9a
Merge branch 'master' into tpo-add_semantic_cmd_interface
tpoignonec Dec 24, 2024
ed05c4e
run pre-commit --all
tpoignonec Dec 24, 2024
246ac99
Merge branch 'master' into tpo-add_semantic_cmd_interface
christophfroehlich Jan 2, 2025
89d4020
fix suggested changes for initialization
tpoignonec Jan 3, 2025
790b9d4
Merge branch 'master' into tpo-add_semantic_cmd_interface
christophfroehlich Jan 4, 2025
34a7734
Merge branch 'master' into tpo-add_semantic_cmd_interface
tpoignonec Jan 8, 2025
492fa9b
Merge branch 'master' into tpo-add_semantic_cmd_interface
christophfroehlich Jan 11, 2025
86eb68b
Update controller_interface/include/semantic_components/semantic_comp…
tpoignonec Jan 13, 2025
0d94c77
remove "limits" include
tpoignonec Jan 13, 2025
697bdb7
fix LedRgbDevice::set_values_from_message
Jan 13, 2025
29cb799
expand docstring for SemanticComponentCommandInterface::set_values
Jan 13, 2025
e4f8bdd
Merge branch 'master' into tpo-add_semantic_cmd_interface
tpoignonec Jan 13, 2025
9b8a2fe
Merge branch 'master' into tpo-add_semantic_cmd_interface
tpoignonec Jan 14, 2025
69277d8
make CI happy
Jan 16, 2025
25c5a91
Update controller_interface/include/semantic_components/led_rgb_devic…
tpoignonec Jan 16, 2025
f99afcf
remove virtual declaration (redundant with override keyword)
Jan 16, 2025
70ec8dd
Merge branch 'master' into tpo-add_semantic_cmd_interface
tpoignonec Jan 16, 2025
7eaafde
Merge branch 'master' into tpo-add_semantic_cmd_interface
tpoignonec Jan 22, 2025
609542e
declare "get_command_interface_names" as virtual
tpoignonec Jan 22, 2025
5df7b43
make "set_values_from_message" a pure virtual method
Jan 22, 2025
db374fe
remove constructor SemanticComponentCommandInterface(name, size)
Jan 22, 2025
0fee40a
Update controller_interface/include/semantic_components/semantic_comp…
tpoignonec Jan 27, 2025
e7b078e
force <led_name>/<r/g/b> convention in LED constructor
Jan 27, 2025
19a1b9b
linting of accepted commits
Jan 27, 2025
725caf5
Merge branch 'master' into tpo-add_semantic_cmd_interface
tpoignonec Jan 27, 2025
c21876f
Update controller_interface/include/semantic_components/led_rgb_devic…
tpoignonec Jan 27, 2025
202536e
Merge branch 'master' into tpo-add_semantic_cmd_interface
christophfroehlich Jan 29, 2025
2066d97
Merge branch 'master' into tpo-add_semantic_cmd_interface
christophfroehlich Feb 1, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion controller_interface/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,30 @@ if(BUILD_TESTING)
ament_target_dependencies(test_pose_sensor
geometry_msgs
)

ament_add_gmock(test_gps_sensor test/test_gps_sensor.cpp)
target_link_libraries(test_gps_sensor
controller_interface
hardware_interface::hardware_interface
)

# Semantic component command interface tests

ament_add_gmock(test_semantic_component_command_interface
test/test_semantic_component_command_interface.cpp
)
target_link_libraries(test_semantic_component_command_interface
controller_interface
hardware_interface::hardware_interface
)

ament_add_gmock(test_led_rgb_device test/test_led_rgb_device.cpp)
target_link_libraries(test_led_rgb_device
controller_interface
hardware_interface::hardware_interface
)
ament_target_dependencies(test_led_rgb_device
std_msgs
)
endif()

install(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright (c) 2024, Sherpa Mobile Robotics
//
// 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.

#ifndef SEMANTIC_COMPONENTS__LED_RGB_DEVICE_HPP_
#define SEMANTIC_COMPONENTS__LED_RGB_DEVICE_HPP_

#include <string>
#include <vector>

#include "semantic_components/semantic_component_command_interface.hpp"
#include "std_msgs/msg/color_rgba.hpp"

namespace semantic_components
{
class LedRgbDevice : public SemanticComponentCommandInterface<std_msgs::msg::ColorRGBA>
{
public:
/**
* Constructor for a LED RGB device with interface names set based on device name.
* The constructor sets the command interface names to "<name>/interface_r",
* "<name>/interface_g", "<name>/interface_b".
*
* \param[in] name name of the LED device, used as a prefix for the command interface names
* \param[in] interface_r name of the command interface for the red channel
* \param[in] interface_g name of the command interface for the green channel
* \param[in] interface_b name of the command interface for the blue channel
*/
explicit LedRgbDevice(
const std::string & name, const std::string & interface_r, const std::string & interface_g,
const std::string & interface_b)
: SemanticComponentCommandInterface(
name, {{name + "/" + interface_r}, {name + "/" + interface_g}, {name + "/" + interface_b}})
{
}

/// Set LED states from ColorRGBA message

/**
* Set the values of the LED RGB device from a ColorRGBA message.
*
* \details Sets the values of the red, green, and blue channels from the message.
* If any of the values are out of the range [0, 1], the function fails and returns false.
*
* \param[in] message ColorRGBA message
*
* \return true if all values were set, false otherwise
*/
bool set_values_from_message(const std_msgs::msg::ColorRGBA & message) override
{
if (
message.r < 0 || message.r > 1 || message.g < 0 || message.g > 1 || message.b < 0 ||
message.b > 1)
{
return false;
tpoignonec marked this conversation as resolved.
Show resolved Hide resolved
}
bool all_set = true;
all_set &= command_interfaces_[0].get().set_value(message.r);
all_set &= command_interfaces_[1].get().set_value(message.g);
all_set &= command_interfaces_[2].get().set_value(message.b);
return all_set;
}
};

} // namespace semantic_components

#endif // SEMANTIC_COMPONENTS__LED_RGB_DEVICE_HPP_
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Copyright (c) 2024, Sherpa Mobile Robotics
//
// 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.

#ifndef SEMANTIC_COMPONENTS__SEMANTIC_COMPONENT_COMMAND_INTERFACE_HPP_
#define SEMANTIC_COMPONENTS__SEMANTIC_COMPONENT_COMMAND_INTERFACE_HPP_

#include <string>
#include <vector>

#include "controller_interface/helpers.hpp"
#include "hardware_interface/loaned_command_interface.hpp"

namespace semantic_components
{
template <typename MessageInputType>
class SemanticComponentCommandInterface
{
public:
SemanticComponentCommandInterface(
const std::string & name, const std::vector<std::string> & interface_names)
: name_(name), interface_names_(interface_names)
{
assert(interface_names.size() > 0);
command_interfaces_.reserve(interface_names.size());
tpoignonec marked this conversation as resolved.
Show resolved Hide resolved
}

virtual ~SemanticComponentCommandInterface() = default;

/// Assign loaned command interfaces from the hardware.
/**
* Assign loaned command interfaces on the controller start.
*
* \param[in] command_interfaces vector of command interfaces provided by the controller.
*/
bool assign_loaned_command_interfaces(
std::vector<hardware_interface::LoanedCommandInterface> & command_interfaces)
{
return controller_interface::get_ordered_interfaces(
command_interfaces, interface_names_, "", command_interfaces_);
}

/// Release loaned command interfaces from the hardware.
void release_interfaces() { command_interfaces_.clear(); }

/// Definition of command interface names for the component.
/**
* The function should be used in "command_interface_configuration()" of a controller to provide
* standardized command interface names semantic component.
*
* \default Default implementation defined command interfaces as "name/NR" where NR is number
* from 0 to size of values;
* \return list of strings with command interface names for the semantic component.
*/
const std::vector<std::string> & get_command_interface_names() const { return interface_names_; }

/// Return all values.
/**
* \return true if it gets all the values, else false (i.e., invalid size or if the method
* ``hardware_interface::LoanedCommandInterface::set_value`` fails).
*/
bool set_values(const std::vector<double> & values)
{
// check we have sufficient memory
if (values.size() != command_interfaces_.size())
{
return false;
}
// set values
bool all_set = true;
for (auto i = 0u; i < values.size(); ++i)
{
all_set &= command_interfaces_[i].get().set_value(values[i]);
}
return all_set;
}

/// Set values from MessageInputType
/**
* \return True if all values were set successfully, false otherwise.
*/
virtual bool set_values_from_message(const MessageInputType & /* message */) = 0;

protected:
std::string name_;
std::vector<std::string> interface_names_;
std::vector<std::reference_wrapper<hardware_interface::LoanedCommandInterface>>
command_interfaces_;
};

} // namespace semantic_components

#endif // SEMANTIC_COMPONENTS__SEMANTIC_COMPONENT_COMMAND_INTERFACE_HPP_
87 changes: 87 additions & 0 deletions controller_interface/test/test_led_rgb_device.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Copyright (c) 2024, Sherpa Mobile Robotics
//
// 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 "test_led_rgb_device.hpp"

void LedDeviceTest::SetUp()
{
full_cmd_interface_names_.reserve(size_);
for (const auto & interface_name : interface_names_)
{
full_cmd_interface_names_.emplace_back(device_name_ + '/' + interface_name);
}
}

void LedDeviceTest::TearDown() { led_device_.reset(nullptr); }

TEST_F(LedDeviceTest, validate_all)
{
// Create device
led_device_ = std::make_unique<TestableLedDevice>(
device_name_, interface_names_[0], interface_names_[1], interface_names_[2]);
EXPECT_EQ(led_device_->name_, device_name_);

// Validate reserved space for interface_names_ and command_interfaces_
// As command_interfaces_ are not defined yet, use capacity()
ASSERT_EQ(led_device_->interface_names_.size(), size_);
ASSERT_EQ(led_device_->command_interfaces_.capacity(), size_);

// Validate default interface_names_
EXPECT_TRUE(std::equal(
led_device_->interface_names_.cbegin(), led_device_->interface_names_.cend(),
full_cmd_interface_names_.cbegin(), full_cmd_interface_names_.cend()));

// Get interface names
std::vector<std::string> interface_names = led_device_->get_command_interface_names();

// Assign values to position
hardware_interface::CommandInterface led_r{device_name_, interface_names_[0], &led_values_[0]};
hardware_interface::CommandInterface led_g{device_name_, interface_names_[1], &led_values_[1]};
hardware_interface::CommandInterface led_b{device_name_, interface_names_[2], &led_values_[2]};

// Create command interface vector in jumbled order
std::vector<hardware_interface::LoanedCommandInterface> temp_command_interfaces;
temp_command_interfaces.reserve(3);
temp_command_interfaces.emplace_back(led_r);
temp_command_interfaces.emplace_back(led_g);
temp_command_interfaces.emplace_back(led_b);

// Assign interfaces
led_device_->assign_loaned_command_interfaces(temp_command_interfaces);
EXPECT_EQ(led_device_->command_interfaces_.size(), size_);

// Validate correct assignment
const std::vector<double> test_led_values_cmd = {0.1, 0.2, 0.3};
EXPECT_TRUE(led_device_->set_values(test_led_values_cmd));

EXPECT_EQ(led_values_[0], test_led_values_cmd[0]);
EXPECT_EQ(led_values_[1], test_led_values_cmd[1]);
EXPECT_EQ(led_values_[2], test_led_values_cmd[2]);

// Validate correct assignment from message
std_msgs::msg::ColorRGBA temp_message;
temp_message.r = static_cast<float>(test_led_values_cmd[0]);
temp_message.g = static_cast<float>(test_led_values_cmd[1]);
temp_message.b = static_cast<float>(test_led_values_cmd[2]);
EXPECT_TRUE(led_device_->set_values_from_message(temp_message));

double float_tolerance = 1e-6;
EXPECT_NEAR(led_values_[0], test_led_values_cmd[0], float_tolerance);
EXPECT_NEAR(led_values_[1], test_led_values_cmd[1], float_tolerance);
EXPECT_NEAR(led_values_[2], test_led_values_cmd[2], float_tolerance);

// Release command interfaces
led_device_->release_interfaces();
ASSERT_EQ(led_device_->command_interfaces_.size(), 0);
}
63 changes: 63 additions & 0 deletions controller_interface/test/test_led_rgb_device.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright (c) 2024, Sherpa Mobile Robotics
//
// 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.

#ifndef TEST_LED_RGB_DEVICE_HPP_
#define TEST_LED_RGB_DEVICE_HPP_

#include <gmock/gmock.h>

#include <array>
#include <limits>
#include <memory>
#include <string>
#include <vector>

#include "semantic_components/led_rgb_device.hpp"

class TestableLedDevice : public semantic_components::LedRgbDevice
{
FRIEND_TEST(LedDeviceTest, validate_all);

public:
TestableLedDevice(
const std::string & name, const std::string & interface_r, const std::string & interface_g,
const std::string & interface_b)
: LedRgbDevice{name, interface_r, interface_g, interface_b}
{
}

virtual ~TestableLedDevice() = default;
};

class LedDeviceTest : public ::testing::Test
{
public:
void SetUp();
void TearDown();

protected:
const size_t size_ = 3;
const std::string device_name_ = "test_led_device";

std::vector<std::string> full_cmd_interface_names_;
const std::vector<std::string> interface_names_ = {"r", "g", "b"};

std::array<double, 3> led_values_ = {
{std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(),
std::numeric_limits<double>::quiet_NaN()}};

std::unique_ptr<TestableLedDevice> led_device_;
};

#endif // TEST_LED_RGB_DEVICE_HPP_
Loading