Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
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
19 changes: 19 additions & 0 deletions hardware_interface/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export_windows_symbols()
set(THIS_PACKAGE_INCLUDE_DEPENDS
control_msgs
lifecycle_msgs
LibXml2
pluginlib
rclcpp_lifecycle
rcpputils
Expand All @@ -30,6 +31,7 @@ endforeach()

add_library(hardware_interface SHARED
src/component_parser.cpp
src/component_validator.cpp
src/resource_manager.cpp
src/hardware_component.cpp
src/lexical_casts.cpp
Expand All @@ -48,6 +50,7 @@ target_link_libraries(hardware_interface PUBLIC
realtime_tools::realtime_tools
rcutils::rcutils
rcpputils::rcpputils
LibXml2::LibXml2
${joint_limits_TARGETS}
${TinyXML2_LIBRARIES}
${tinyxml2_vendor_LIBRARIES}
Expand All @@ -73,6 +76,8 @@ if(BUILD_TESTING)

find_package(ament_cmake_gmock REQUIRED)
find_package(ros2_control_test_assets REQUIRED)
find_package(ament_index_cpp REQUIRED)
find_package(LibXml2)

ament_add_gmock(test_macros test/test_macros.cpp)
target_include_directories(test_macros PRIVATE include)
Expand Down Expand Up @@ -101,6 +106,14 @@ if(BUILD_TESTING)
ament_add_gmock(test_component_parser test/test_component_parser.cpp)
target_link_libraries(test_component_parser hardware_interface ros2_control_test_assets::ros2_control_test_assets)

ament_add_gmock(test_component_validator test/test_component_validator.cpp)
target_link_libraries(test_component_validator
hardware_interface
ament_index_cpp::ament_index_cpp
ros2_control_test_assets::ros2_control_test_assets
LibXml2::LibXml2
)

add_library(test_hardware_components SHARED
test/test_hardware_components/test_single_joint_actuator.cpp
test/test_hardware_components/test_force_torque_sensor.cpp
Expand All @@ -125,6 +138,12 @@ install(
DIRECTORY include/
DESTINATION include/hardware_interface
)

install(
DIRECTORY schema
DESTINATION share/hardware_interface
)

install(
TARGETS
mock_components
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Copyright 2020 ros2_control Development Team
//
// 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 HARDWARE_INTERFACE__COMPONENT_VALIDATOR_HPP_
#define HARDWARE_INTERFACE__COMPONENT_VALIDATOR_HPP_

#include <libxml/parser.h>
#include <libxml/xmlschemas.h>

#include <string>

#include <ament_index_cpp/get_package_share_directory.hpp>

namespace hardware_interface
{

/// Validate URDF string against an XML Schema Definition (XSD) file.
/**
* \param[in] urdf string with robot's URDF
* \param[in] xsd_file_path path to the XSD file
* \return true if the URDF is valid according to the XSD, false otherwise
*/
bool validate_urdf_with_xsd(const std::string & urdf, const std::string & xsd_file_path);

/// Validate URDF file path against an XML Schema Definition (XSD) file.
/**
* \param[in] urdf_file_path path to URDF file
* \param[in] xsd_file_path path to the XSD file
* \return true if URDF is valid according to the XSD, false otherwise
*
*/
bool validate_urdf_file_path_with_xsd(
const std::string & urdf_file_path, std::string & xsd_file_path);

/// Validate URDF against an XSD file provides in URDF itself
/**
* \param[in] urdf string with robot's urdf
* \return true if the URDF is valid according to the XSD, false otherwise
*/
bool validate_urdf_with_xsd_tag(const std::string & urdf);

/// Validate URDF file path against an XSD file provides in URDF itself
/**
* \param[in] urdf_file_path string
* \return true if the URDF is valid according to the XSD, false otherwise
*/
bool validate_urdf_file_with_xsd_tag(const std::string & urdf_file_path);

/// Extract ROS2 Control XSD tag from URDF
/**
* \param[in] urdf string with robot's urdf contains xmlns:ros2_control
* \param[out] string of xlmns:ros2_control
* \return true if extraction successful else false
*/
bool extract_ros2_control_xsd_tag(const std::string & urdf, std::string & ros2_control_xsd);

} // namespace hardware_interface

#endif // HARDWARE_INTERFACE__COMPONENT_VALIDATOR_HPP_
2 changes: 2 additions & 0 deletions hardware_interface/package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
<exec_depend>rcutils</exec_depend>

<test_depend>ament_cmake_gmock</test_depend>
<test_depend>ament_index_cpp</test_depend>
<test_depend>libxml2</test_depend>
<test_depend>ros2_control_test_assets</test_depend>

<export>
Expand Down
105 changes: 105 additions & 0 deletions hardware_interface/schema/ros2_control.xsd
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified">

<!-- Reusable types -->
<xs:complexType name="paramType">
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="name" type="xs:string" use="required"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>

<xs:complexType name="interfaceType">
<xs:attribute name="name" type="xs:string" use="required"/>
</xs:complexType>

<xs:complexType name="commandInterfaceType">
<xs:sequence>
<xs:element name="param" type="paramType" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="name" type="xs:string" use="required"/>
</xs:complexType>

<xs:complexType name="jointOrGpioType">
<xs:sequence>
<xs:element name="command_interface" type="commandInterfaceType" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="state_interface" type="interfaceType" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="name" type="xs:string" use="required"/>
</xs:complexType>



<!-- Root robot element -->
<xs:element name="robot">
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element ref="link"/>
<xs:element ref="joint"/>
<xs:element ref="ros2_control"/>
<xs:element ref="transmission" minOccurs="0" maxOccurs="unbounded"/>
<xs:element ref="material" minOccurs="0" maxOccurs="unbounded"/>
<!-- Add more URDF elements here as needed -->
</xs:choice>
<xs:attribute name="name" type="xs:string" use="required"/>
</xs:complexType>
</xs:element>


<!-- Minimal link and joint element definitions for validation -->
<xs:element name="link">
<xs:complexType mixed="true">
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:any processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="name" type="xs:string" use="required"/>
</xs:complexType>
</xs:element>

<xs:element name="joint">
<xs:complexType mixed="true">
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:any processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="name" type="xs:string" use="required"/>
<xs:attribute name="type" type="xs:string" use="required"/>
</xs:complexType>
</xs:element>

<xs:element name="transmission" type="xs:anyType"/>
<xs:element name="material" type="xs:anyType"/>


<!-- ros2_control block -->
<xs:element name="ros2_control">
<xs:complexType>
<xs:sequence>
<xs:element name="hardware">
<xs:complexType>
<xs:sequence>
<xs:element name="plugin" type="xs:string"/>
<xs:element name="param" type="paramType" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="joint" type="jointOrGpioType" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="gpio" type="jointOrGpioType" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="sensor" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="state_interface" type="interfaceType" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="name" type="xs:string" use="required"/>
</xs:complexType>
</xs:element>
</xs:choice>
</xs:sequence>
<xs:attribute name="name" type="xs:string" use="required"/>
<xs:attribute name="type" type="xs:string" use="required"/>
</xs:complexType>
</xs:element>

</xs:schema>
Loading
Loading