From 24bb34dcff709dc3cadbf1ecfa6e94d618b8132d Mon Sep 17 00:00:00 2001
From: Charles Lesire-Cabaniols <charles.lesire@onera.fr>
Date: Thu, 3 May 2012 16:35:11 +0200
Subject: [PATCH] Deployer: add a connectOperation method

Allows to directly connect a requested operation to a provided
operation without having to use the connectServices method.

Typical invocation is:
connectOperations("MyComponent.myRequestedService.myRequestedOperation",
  "MyOtherComponent.myProvidedService.myProvidedOperation")
---
 deployment/DeploymentComponent.cpp |   69 +++++++++++++++++++++++++++++++++++-
 deployment/DeploymentComponent.hpp |   17 +++++++++
 deployment/tests/main.cpp          |   40 +++++++++++++++++++++
 3 files changed, 125 insertions(+), 1 deletions(-)

diff --git a/deployment/DeploymentComponent.cpp b/deployment/DeploymentComponent.cpp
index 5c8aa32..6b83a61 100644
--- a/deployment/DeploymentComponent.cpp
+++ b/deployment/DeploymentComponent.cpp
@@ -37,8 +37,9 @@
 #include <rtt/scripting/Scripting.hpp>
 #include <rtt/ConnPolicy.hpp>
 #include <rtt/plugin/PluginLoader.hpp>
-#include <boost/algorithm/string.hpp>
 
+#include <boost/algorithm/string.hpp>
+#include <rtt/base/OperationCallerBaseInvoker.hpp>
 
 #include <cstdio>
 #include <cstdlib>
@@ -154,6 +155,7 @@ namespace OCL
                 .arg("policy", "The connection policy which serves to describe the stream to be created. Use 'ConnPolicy()' to use the default.");
 
         this->addOperation("connectServices", (bool(DeploymentComponent::*)(const std::string&, const std::string&))&DeploymentComponent::connectServices, this, ClientThread).doc("Connect the matching provides/requires services of two Components known to this Component.").arg("One", "The first component.").arg("Two", "The second component.");
+        this->addOperation("connectOperations", &DeploymentComponent::connectOperations, this, ClientThread).doc("Connect the matching provides/requires operations of two Components known to this Component.").arg("Requested", "The requested operation (dot-separated path).").arg("Provided", "The provided operation (dot-separated path).");
 
         this->addOperation("addPeer", cp, this, ClientThread).doc("Add a peer to a Component.").arg("From", "The first component.").arg("To", "The other component.");
         typedef void(DeploymentComponent::*RPFun)(const std::string&);
@@ -304,6 +306,35 @@ namespace OCL
     	return ret;
     }
 
+    ServiceRequester* DeploymentComponent::stringToServiceRequester(string const& names) {
+        std::vector<std::string> strs;
+        boost::split(strs, names, boost::is_any_of("."));
+
+        string component = strs.front();
+        if (!hasPeer(component) && component != this->getName() ) {
+            log(Error) << "No such component: '"<< component <<"'" <<endlog();
+            if ( names.find('.') != string::npos )
+                log(Error)<< " when looking for service '" << names <<"'" <<endlog();
+            return NULL;
+        }
+        // component is peer or self:
+        ServiceRequester* ret = (component != this->getName() ? getPeer(component)->requires() : this->requires());
+
+        // remove component name:
+        strs.erase( strs.begin() );
+
+        // iterate over remainders:
+        while ( !strs.empty() && ret) {
+            ret = ret->requires( strs.front() );
+            if (ret)
+                strs.erase( strs.begin() );
+        }
+        if (!ret) {
+            log(Error) <<"No such service: '"<< strs.front() <<"' while looking for service '"<< names<<"'"<<endlog();
+        }
+        return ret;
+    }
+
     base::PortInterface* DeploymentComponent::stringToPort(string const& names) {
     	std::vector<std::string> strs;
     	boost::split(strs, names, boost::is_any_of("."));
@@ -464,6 +495,42 @@ namespace OCL
         return a->connectServices(b);
     }
 
+    bool DeploymentComponent::connectOperations(const std::string& required, const std::string& provided)
+    {
+        RTT::Logger::In in("DeploymentComponent::connectOperations");
+        // Required service
+        boost::iterator_range<std::string::const_iterator> reqs = boost::algorithm::find_last(required, ".");
+        std::string reqs_name(required.begin(), reqs.begin());
+        std::string rop_name(reqs.begin()+1, required.end());
+        log(Debug) << "Looking for required operation " << rop_name << " in service " << reqs_name << endlog();
+        ServiceRequester* r = this->stringToServiceRequester(reqs_name);
+        // Provided service
+        boost::iterator_range<std::string::const_iterator> pros = boost::algorithm::find_last(provided, ".");
+        std::string pros_name(provided.begin(), pros.begin());
+        std::string pop_name(pros.begin()+1, provided.end());
+        log(Debug) << "Looking for provided operation " << pop_name << " in service " << pros_name << endlog();
+        Service::shared_ptr p = this->stringToService(pros_name);
+        // Requested operation
+        RTT::base::OperationCallerBaseInvoker* rop = r->getOperationCaller(rop_name);
+        if (! rop) {
+            log(Error) << "No requested operation " << rop_name << " found in service " << reqs_name << endlog();
+            return false;
+        }
+        if ( rop->ready() ) {
+            log(Error) << "Requested operation " << rop_name << " already connected to a provided operation!" << endlog();
+            return false;
+        }
+        // Provided operation
+        if (! p->hasOperation(pop_name)) {
+            log(Error) << "No provided operation " << pop_name << " found in service " << pros_name << endlog();
+            return false;
+        }
+        // Connection
+        rop->setImplementation(p->getLocalOperation( pop_name ), r->getServiceOwner()->engine());
+        if ( rop->ready() )
+            log(Debug) << "Successfully set up OperationCaller for operation " << rop_name << endlog();
+        return rop->ready();
+    }
 
     int string_to_oro_sched(const std::string& sched) {
         if ( sched == "ORO_SCHED_OTHER" )
diff --git a/deployment/DeploymentComponent.hpp b/deployment/DeploymentComponent.hpp
index 1305e7d..3a8a1ab 100644
--- a/deployment/DeploymentComponent.hpp
+++ b/deployment/DeploymentComponent.hpp
@@ -210,6 +210,15 @@ namespace OCL
          */
         Service::shared_ptr stringToService(string const& names);
         /**
+         * Converts a dot-separated path to a service to a ServiceRequester
+         * object.
+         * @param name a dot-separated path name to a service. The first
+         * part of the name must be the component name. For example 'Controller.arm'.
+         * @return null if the service could not be found, the service
+         * otherwise
+         */
+        ServiceRequester* stringToServiceRequester(std::string const& names);
+        /**
          * Converts a dot-separated path to a service to a Port
          * object.
          * @param name a dot-separated path name to a port. The first
@@ -338,6 +347,14 @@ namespace OCL
         bool connectServices(const std::string& one, const std::string& other);
 
         /**
+         * Connects a required operation to a provided operation.
+         * @param required The dot-separated name of a required operation
+         * @param provided The dot-separated name of a provided operation
+         * @return true if required is connected to provided
+         */
+        bool connectOperations(const std::string& required, const std::string& provided);
+
+        /**
          * Make one component a peer of the other, in one direction, such
          * that one can use the services of the other.
          *
diff --git a/deployment/tests/main.cpp b/deployment/tests/main.cpp
index 8c931c7..068a825 100644
--- a/deployment/tests/main.cpp
+++ b/deployment/tests/main.cpp
@@ -36,17 +36,57 @@ public:
     }
 };
 
+class HelloProvider
+    : public RTT::TaskContext
+{
+public:
+    bool hello() {
+        RTT::Logger::In("connectOperations Test");
+        log(Info) << "Hello World!" << endlog();
+        return true;
+    }
+
+    HelloProvider()
+    : RTT::TaskContext("HelloProvider")
+    {
+        this->provides("hello_service")->addOperation("hello", &HelloProvider::hello, this);
+    }
+};
+
+class HelloRequester
+    : public RTT::TaskContext
+{
+public:
+    RTT::OperationCaller<bool()> helloworld;
+    HelloRequester()
+    : RTT::TaskContext("HelloRequester"),
+      helloworld("helloworld")
+    {
+        this->requires()->addOperationCaller(helloworld);
+    }
+    void virtual updateHook() {
+        helloworld();
+    }
+};
+
 int ORO_main(int, char**)
 {
+    RTT::Logger::Instance()->setLogLevel(RTT::Logger::Info);
+
     MyTask t1("ComponentA");
     MyTask t2("ComponentB");
     MyTask t3("ComponentC");
 
+    HelloProvider p;
+    HelloRequester r;
+
     {
         DeploymentComponent dc;
         dc.addPeer( &t1 );
         dc.addPeer( &t2 );
         dc.addPeer( &t3 );
+        dc.addPeer( &p );
+        dc.addPeer( &r );
         dc.kickStart("deployment.cpf");
         TaskBrowser tb(&dc);
 
-- 
1.7.5.4

