Сервисы и плагины

Сервисы позволяют создавать связанные группы элементов внешнего интерфейса объекта. Такая группа может включать операции, порты и параметры. Плагины предназначены для динамического расширения функциональности компонента и представляет собой реализацию сервиса, которую можно добавить в компонент.

Таким образом, плагин способен расширить интерфейс компонента за счет портов, операций и параметров, которые будут доступны в пространстве имен, совпадающем с именем, под которым был загружен плагин (сервис):

component.portname // порт компонента
component.pluginname.portname // порт плагина

Однако, поскольку плагин имеет доступ к TaskContext исходного компонента, то ничего не препятствует модификации интерфейса, загрузившего его компонента, но это не желательно.

Исполнение кода сервиса занимается ExecutionEngine исходного компонента, однако аналогов startHook, updateHook и т.п. у него нет. Операции пишутся стандартным образом, для обработки сообщений можно использовать callback, задаваемый при вызове addEventPort.

Пусть описан сервис:

    class HelloInterface  // программисткий интерфейс
    {
        public:
            virtual void hello() = 0; 
    }

    class HelloService : public HelloInterface, public RTT::Service  // реализация сервиса
    {
        public:
            HelloService(TaskContext * tc) : 
            Service("hello", tc) 
            {
                addOperation("hello", &hello, this);
            }
            void hello() {
                cout << "Hello, world!" << endl;
            }
    }

    class Hello : public ServiceRequester { // внешний интерфейс для удаленного вызова операций сервиса (опционален)
        public: 
            OperationCaller<void()> hello;

            Hello(TaskContext * owner) :
                ServiceRequester("hello", owner),
                hello("hello")
            {
                addOperationCaller(hello);
            }
    }

Использовать сервисы и плагины можно разным способами.

1. Использование сервиса (плагина) удаленного компонента.

Сервис предоставляется удаленным компонентом, его операции используются в другом компоненте.

Конструктор компонента, предоставляющего сервис:

         ProviderComponent() {
             shared_ptr<HelloService> hello_serv = new HelloService("hello", this);
             this->provides()->addService(hello_serv);
         }

Компонент, использующий сервис:

         class RequesterComponent() {
             shared_ptr<Hello> hello_req; // boost::shared_ptr
         public:
             RequesterComponent() {
                 hello_req = new Hello(this);
                 this->requires()->addServiceRequester(hello_req);
             }
             configureHook() {
                 return hello_req->ready(); // проверить, доступны ли его операции
             }
             updateHook() {
                 hello_req->hello();   
                 this->requires("hello")->hello();
             }
          }

Связывание ServiceRequester и Service разных компонент осуществляется вызовом deployer connectServices(), соединяющим одноименные сервисы. У ServiceRequester есть также метод connectTo().

ops файл:

          loadService("requester_component", "hello");
          requester_component.loadService("hello");

2. Использование сервиса (плагина) текущего компонента при помощи внешнего интерфейса:

Этот способ дает доступ только к элементам интерфейса сервиса, зарегистрированных в OROCOS как внешние.

         class Component() {
             shared_ptr<Hello> hello_req;
         public:
             configureHook() {
                 hello_req = getProvider<Hello>("hello"); //попытается загрузить нужный сервис, если он отсутствует.
                 return hello_req != 0;
             }
             updateHook() {
                 hello_req->hello();   
                 getProvider<Hello>("hello")->hello();
             }
          }

3. Использование сервиса (плагина) текущего компонента при помощи внутреннего интерфейса:

В этом случае не происходит регистрация элементов внешнего интерфейса. Поэтому этот способ больше всего подходит для взаимодействия с элементами сервиса, не предназначенными для использованиями компонентами, не являющимися владельцами сервиса.

         class Component() {
         public:
             shared_ptr<HelloInterface *> hello_if
             configureHook() {
                 hello_if = dynamic_pointer_cast<HelloInterface>(getService("hello"));
                 return hello_if != 0;
             }
             updateHook() {
                 hello_if->hello();
             }
          }

Нет стандартных способов загрузить плагин не под его именем. Т.е. тип жестко связан с именем, Отчасти это можно решить, добавив операции, получающую имя плагина/сервиса в виде параметра:

    loadHello(string& name) {
        hello_req = getProvider<Hello>(name);
        hello_if = dynamic_pointer_cast<HelloInterface>(getService("hello"));
    }