Skip to content

Commit

Permalink
Modify libYARP_robotinterface and yarprobotinterface to support passi…
Browse files Browse the repository at this point in the history
…ng using ${portprefix} in parameters
  • Loading branch information
traversaro committed Mar 23, 2022
1 parent ca699cc commit 5f0b766
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class yarp::robotinterface::impl::XMLReaderFileV3::Private
yarp::robotinterface::ActionList readActionsTag(TiXmlElement* actionsElem, yarp::robotinterface::XMLReaderResult& result);

bool PerformInclusions(TiXmlNode* pParent, const std::string& parent_fileName, const std::string& current_path);
void ReplaceAllStrings(std::string& str, const std::string& from, const std::string& to);

XMLReaderFileV3* const parent;

Expand All @@ -65,7 +66,7 @@ class yarp::robotinterface::impl::XMLReaderFileV3::Private
#endif

bool verbose_output;
const yarp::os::Searchable* config;
yarp::os::Property config;
std::string curr_filename;
unsigned int minorVersion;
unsigned int majorVersion;
Expand Down Expand Up @@ -252,9 +253,14 @@ yarp::robotinterface::XMLReaderResult yarp::robotinterface::impl::XMLReaderFileV
result.robot.build() = static_cast<unsigned>(tmp);
#endif

if (robotElem->QueryStringAttribute("portprefix", &result.robot.portprefix()) != TIXML_SUCCESS) {
SYNTAX_WARNING(robotElem->Row()) << R"("robot" element should contain the "portprefix" attribute. Using "name" attribute)";
result.robot.portprefix() = result.robot.name();
// If portprefix is already present in config we use that one
if (!config.check("portprefix"))
{
if (robotElem->QueryStringAttribute("portprefix", &result.robot.portprefix()) != TIXML_SUCCESS) {
SYNTAX_WARNING(robotElem->Row()) << R"("robot" element should contain the "portprefix" attribute. Using "name" attribute)";
result.robot.portprefix() = result.robot.name();
}
config.put("portprefix",result.robot.portprefix());
}

// FIXME DTD >= 4 Make this the default behaviour
Expand Down Expand Up @@ -410,6 +416,17 @@ yarp::robotinterface::ParamList yarp::robotinterface::impl::XMLReaderFileV3::Pri
return yarp::robotinterface::ParamList();
}

void yarp::robotinterface::impl::XMLReaderFileV3::Private::ReplaceAllStrings(std::string& str, const std::string& from, const std::string& to)
{
if(from.empty())
return;
size_t start_pos = 0;
while((start_pos = str.find(from, start_pos)) != std::string::npos) {
str.replace(start_pos, from.length(), to);
start_pos += to.length();
}
}


yarp::robotinterface::Param yarp::robotinterface::impl::XMLReaderFileV3::Private::readParamTag(TiXmlElement* paramElem,
yarp::robotinterface::XMLReaderResult& result)
Expand Down Expand Up @@ -437,14 +454,24 @@ yarp::robotinterface::Param yarp::robotinterface::impl::XMLReaderFileV3::Private
return yarp::robotinterface::Param();
}

// First process extern-name
std::string extern_name;
if (paramElem->QueryStringAttribute("extern-name", &extern_name) == TIXML_SUCCESS && config && config->check(extern_name)) {
if (paramElem->QueryStringAttribute("extern-name", &extern_name) == TIXML_SUCCESS && config.check(extern_name)) {
// FIXME Check DTD >= 3.1
param.value() = config->find(extern_name).toString();
param.value() = config.find(extern_name).toString();
} else {
param.value() = valueText;
}

// After process ${portprefix}
std::string paramValueBefore = param.value();
std::string paramValueAfter = paramValueBefore;
std::string portprefix = config.find("portprefix").toString();
ReplaceAllStrings(paramValueAfter, "${portprefix}", portprefix);
param.value() = paramValueAfter;



// yDebug() << param;
return param;
}
Expand Down Expand Up @@ -732,21 +759,21 @@ yarp::robotinterface::XMLReaderResult yarp::robotinterface::impl::XMLReaderFileV
const yarp::os::Searchable& config,
bool verb)
{
mPriv->config = &config;
mPriv->config.fromString(config.toString());
mPriv->verbose_output = verb;
auto ret = mPriv->readRobotFromFile(filename);
mPriv->config = nullptr;
mPriv->config.clear();
return ret;
}

yarp::robotinterface::XMLReaderResult yarp::robotinterface::impl::XMLReaderFileV3::getRobotFromString(const std::string& xmlString,
const yarp::os::Searchable& config,
bool verb)
{
mPriv->config = &config;
mPriv->config.fromString(config.toString());
mPriv->verbose_output = verb;
auto ret = mPriv->readRobotFromString(xmlString);
mPriv->config = nullptr;
mPriv->config.clear();
return ret;
}

Expand Down
2 changes: 1 addition & 1 deletion tests/libYARP_robotinterface/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ target_link_libraries(harness_robotinterface

set_property(TARGET harness_robotinterface PROPERTY FOLDER "Test")

yarp_catch_discover_tests(harness_robotinterface)
catch_discover_tests(harness_robotinterface)
112 changes: 112 additions & 0 deletions tests/libYARP_robotinterface/RobotinterfaceTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ TEST_CASE("robotinterface::ParamTest", "[yarp::robotinterface]")
}
};


// Dummy device used in "Check valid robot file with two devices" test
namespace yarp::dev {
class RobotInterfaceTestMockDriver;
Expand Down Expand Up @@ -330,6 +331,117 @@ TEST_CASE("robotinterface::XMLReaderTest", "[yarp::robotinterface]")
CHECK(globalState.mockDriverParamValue == "theparam_alt");
}

SECTION("Check valid robot file with portprefix passed via xml")
{
// Reset test flags
globalState.reset();

// Add dummy devices to YARP drivers factory
yarp::dev::Drivers::factory().add(new yarp::dev::DriverCreatorOf<yarp::dev::RobotInterfaceTestMockDriver>("robotinterface_test_mock_device", "", "RobotInterfaceTestMockDriver"));

// Load empty XML configuration file
std::string XMLString = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
"<!DOCTYPE robot PUBLIC \"-//YARP//DTD yarprobotinterface 3.1//EN\" \"http://www.yarp.it/DTD/yarprobotinterfaceV3.1.dtd\">\n"
"<robot name=\"RobotWithOneDevice\" portprefix=\"/RobotWithOneDevice\" build=\"0\">\n"
" <devices>\n"
" <device name=\"dummy_device\" type=\"robotinterface_test_mock_device\">\n"
" <param name=\"theparam\"> ${portprefix}/SomethingInTheMiddle${portprefix} </param>\n"
" </device>\n"
" </devices>\n"
"</robot>\n";

yarp::robotinterface::XMLReader reader;
yarp::os::Property config;
yarp::robotinterface::XMLReaderResult result = reader.getRobotFromString(XMLString, config);

// Check parsing fails on empty string
CHECK(result.parsingIsSuccessful);

// Verify that only one device has been loaded
CHECK(result.robot.devices().size() == 1);

// Verify that the devices were not opened and the attach was not called
CHECK(!globalState.mockDriverWasOpened);
CHECK(!globalState.mockDriverWasClosed);
CHECK(globalState.mockDriverParamValue.empty());

// Start the robot (open the device and call "attach" actions)
bool ok = result.robot.enterPhase(yarp::robotinterface::ActionPhaseStartup);
CHECK(ok);

// Check that the device was opened and attach called
CHECK(globalState.mockDriverWasOpened);
CHECK(!globalState.mockDriverWasClosed);
CHECK(globalState.mockDriverParamValue == "/RobotWithOneDevice/SomethingInTheMiddle/RobotWithOneDevice");

// Stop the robot
ok = result.robot.enterPhase(yarp::robotinterface::ActionPhaseInterrupt1);
CHECK(ok);
ok = result.robot.enterPhase(yarp::robotinterface::ActionPhaseShutdown);
CHECK(ok);

// Check that the device was closed and detach called
CHECK(globalState.mockDriverWasOpened);
CHECK(globalState.mockDriverWasClosed);
CHECK(globalState.mockDriverParamValue == "/RobotWithOneDevice/SomethingInTheMiddle/RobotWithOneDevice");
}

SECTION("Check valid robot file with portprefix overriden via configuration")
{
// Reset test flags
globalState.reset();

// Add dummy devices to YARP drivers factory
yarp::dev::Drivers::factory().add(new yarp::dev::DriverCreatorOf<yarp::dev::RobotInterfaceTestMockDriver>("robotinterface_test_mock_device", "", "RobotInterfaceTestMockDriver"));

// Load empty XML configuration file
std::string XMLString = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
"<!DOCTYPE robot PUBLIC \"-//YARP//DTD yarprobotinterface 3.1//EN\" \"http://www.yarp.it/DTD/yarprobotinterfaceV3.1.dtd\">\n"
"<robot name=\"RobotWithOneDevice\" portprefix=\"/RobotWithOneDevice\" build=\"0\">\n"
" <devices>\n"
" <device name=\"dummy_device\" type=\"robotinterface_test_mock_device\">\n"
" <param name=\"theparam\"> ${portprefix}/SomethingInTheMiddle${portprefix} </param>\n"
" </device>\n"
" </devices>\n"
"</robot>\n";

yarp::robotinterface::XMLReader reader;
yarp::os::Property config;
config.put("portprefix","/RobotWithOneDeviceAlternativePrefix");
yarp::robotinterface::XMLReaderResult result = reader.getRobotFromString(XMLString, config);

// Check parsing fails on empty string
CHECK(result.parsingIsSuccessful);

// Verify that only one device has been loaded
CHECK(result.robot.devices().size() == 1);

// Verify that the devices were not opened and the attach was not called
CHECK(!globalState.mockDriverWasOpened);
CHECK(!globalState.mockDriverWasClosed);
CHECK(globalState.mockDriverParamValue.empty());

// Start the robot (open the device and call "attach" actions)
bool ok = result.robot.enterPhase(yarp::robotinterface::ActionPhaseStartup);
CHECK(ok);

// Check that the device was opened and attach called
CHECK(globalState.mockDriverWasOpened);
CHECK(!globalState.mockDriverWasClosed);
CHECK(globalState.mockDriverParamValue == "/RobotWithOneDeviceAlternativePrefix/SomethingInTheMiddle/RobotWithOneDeviceAlternativePrefix");

// Stop the robot
ok = result.robot.enterPhase(yarp::robotinterface::ActionPhaseInterrupt1);
CHECK(ok);
ok = result.robot.enterPhase(yarp::robotinterface::ActionPhaseShutdown);
CHECK(ok);

// Check that the device was closed and detach called
CHECK(globalState.mockDriverWasOpened);
CHECK(globalState.mockDriverWasClosed);
CHECK(globalState.mockDriverParamValue == "/RobotWithOneDeviceAlternativePrefix/SomethingInTheMiddle/RobotWithOneDeviceAlternativePrefix");
}

SECTION("Check valid robot file with two devices")
{
// Reset test flags
Expand Down

0 comments on commit 5f0b766

Please sign in to comment.