Skip to content

Commit a68afeb

Browse files
committed
Enhance URDF validation by normalizing XSD paths and downloading remote schemas
1 parent 7fbb2e0 commit a68afeb

File tree

2 files changed

+53
-5
lines changed

2 files changed

+53
-5
lines changed

hardware_interface/include/hardware_interface/component_validator.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,6 @@ bool validate_urdf_with_xsd(const std::string & urdf, const std::string & xsd_fi
4343
bool validate_urdf_file_path_with_xsd(
4444
const std::string & urdf_file_path, std::string & xsd_file_path);
4545

46-
} // namespace hardware_interface
47-
4846
/// Validate URDF against an XSD file provides in URDF itself
4947
/**
5048
* \param[in] urdf string with robot's urdf
@@ -60,4 +58,6 @@ bool validate_urdf_with_xsd_tag(const std::string & urdf);
6058
*/
6159
bool extract_ros2_control_xsd_tag(const std::string & urdf, std::string & ros2_control_xsd);
6260

61+
} // namespace hardware_interface
62+
6363
#endif // HARDWARE_INTERFACE__COMPONENT_VALIDATOR_HPP_

hardware_interface/src/component_validator.cpp

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,48 @@ bool validate_urdf_with_xsd_tag(const std::string & urdf)
101101
{
102102
return false;
103103
}
104+
std::string xsd_package_share_dir =
105+
ament_index_cpp::get_package_share_directory("hardware_interface");
106+
// If the extracted XSD is a file URI (e.g. "file:///path/to/schema.xsd"), normalize to a local
107+
// path
108+
if (ros2_control_xsd.find("file") != std::string::npos)
109+
{
110+
ros2_control_xsd.replace(0, 8, xsd_package_share_dir);
111+
}
112+
else if (ros2_control_xsd.find("http") != std::string::npos)
113+
{
114+
{
115+
// Download the remote XSD to a local temporary file and point to it
116+
std::string filename;
117+
auto pos = ros2_control_xsd.find_last_of('/');
118+
if (pos == std::string::npos || pos + 1 >= ros2_control_xsd.size())
119+
{
120+
filename = "ros2_control_schema.xsd";
121+
}
122+
else
123+
{
124+
filename = ros2_control_xsd.substr(pos + 1);
125+
}
126+
std::string tmp_path = std::string("/tmp/") + filename;
127+
128+
// Use curl to fetch the XSD; require curl to be available on PATH.
129+
std::ostringstream cmd;
130+
cmd << "curl -sSfL -o '" << tmp_path << "' '" << ros2_control_xsd << "'";
131+
132+
int rc = std::system(cmd.str().c_str());
133+
if (rc != 0)
134+
{
135+
// failed to download
136+
return false;
137+
}
138+
139+
ros2_control_xsd = tmp_path;
140+
}
141+
}
142+
else
143+
{
144+
return false;
145+
}
104146
return validate_urdf_with_xsd(urdf, ros2_control_xsd);
105147
}
106148

@@ -114,10 +156,11 @@ bool extract_ros2_control_xsd_tag(const std::string & urdf, std::string & ros2_c
114156
xmlDocPtr doc = xmlReadMemory(urdf.c_str(), static_cast<int>(urdf.size()), nullptr, nullptr, 0);
115157
if (!doc)
116158
{
117-
return {};
159+
return false;
118160
}
119161

120162
xmlNodePtr root = xmlDocGetRootElement(doc);
163+
bool found = false;
121164
if (root)
122165
{
123166
auto check = [&](xmlNsPtr ns) -> bool
@@ -131,20 +174,25 @@ bool extract_ros2_control_xsd_tag(const std::string & urdf, std::string & ros2_c
131174
}
132175
return false;
133176
};
134-
if (!check(root->ns))
177+
if (check(root->ns))
178+
{
179+
found = true;
180+
}
181+
else
135182
{
136183
for (xmlNsPtr cur = root->nsDef; cur; cur = cur->next)
137184
{
138185
if (check(cur))
139186
{
187+
found = true;
140188
break;
141189
}
142190
}
143191
}
144192
}
145193

146194
xmlFreeDoc(doc);
147-
return false;
195+
return found;
148196
}
149197

150198
} // namespace hardware_interface

0 commit comments

Comments
 (0)