Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
05cf679
test added for xml schema validation
sachinkum0009 Aug 17, 2025
c1423ba
test dependencies updated
sachinkum0009 Aug 17, 2025
3c9927b
Apply suggestions from code review
sachinkum0009 Aug 18, 2025
ea66727
urdf files moved to ros2_control_test_assets
sachinkum0009 Aug 18, 2025
f2c0ee6
urdf directory code updated
sachinkum0009 Aug 18, 2025
2dbb056
accidentely removed the code to add schema to directory
sachinkum0009 Aug 18, 2025
d1746be
Add component validator for URDF validation against XSD
sachinkum0009 Nov 4, 2025
e706cc0
Add component validator and tests for URDF validation against XSD
sachinkum0009 Nov 5, 2025
ac39968
test_validate_xml_schema is removed as test_hardware_component is use…
sachinkum0009 Nov 5, 2025
fdbea18
Add LibXml2 to package dependencies in CMakeLists.txt
sachinkum0009 Nov 5, 2025
780ae65
Remove unused Google Test includes from component_validator.hpp
sachinkum0009 Nov 5, 2025
c92a206
Add functions to validate URDF against XSD and extract ROS2 Control X…
sachinkum0009 Nov 5, 2025
5e0dc6a
Enhance URDF validation by normalizing XSD paths and downloading remo…
sachinkum0009 Nov 8, 2025
f71ff75
Add URDF validation function and corresponding test case
sachinkum0009 Nov 8, 2025
80c3b64
Fix URDF validation by correcting XSD path normalization and updating…
sachinkum0009 Nov 9, 2025
a440719
Enhance URDF validation by adding support for remote XSD files and up…
sachinkum0009 Nov 9, 2025
c254ecb
Merge branch 'master' into feat/schema-description
sachinkum0009 Nov 28, 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
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 @@ -29,6 +30,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 @@ -47,6 +49,7 @@ target_link_libraries(hardware_interface PUBLIC
realtime_tools::realtime_tools
rcutils::rcutils
rcpputils::rcpputils
LibXml2::LibXml2
${joint_limits_TARGETS}
${TinyXML2_LIBRARIES}
${pal_statistics_LIBRARIES}
Expand All @@ -71,6 +74,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 @@ -99,6 +104,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 @@ -123,6 +136,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