SoftBank Robotics documentation What's new in NAOqi 2.8?

Porting C++ code from NAOqi1 to NAOqi2

Introduction

This guide describes how to port c++ modules to NAOqi2. It is not necessary to follow all the steps at once, each step can be done independently, but if you start a section, you must do all the subsections.

Writing a NAOqi2 service

To create a module, you used to inherit from ALModule and register all your methods in your constructor:

// myservice.h

class MyService : public AL::ALModule
{
public:
  MyService(boost::shared_ptr<AL::ALBroker> pBroker,
      const std::string& name);

  std::string myMethod(int i) const;

  bool myOtherMethod(int i);
};

// myservice.cpp

MyService::MyService(boost::shared_ptr<AL::ALBroker> pBroker,
    const std::string& name)
  : ALModule(pBroker, pName)
{
  setModuleDescription("This modules does stuff on things");

  functionName("myMethod", getName(), "return a meaningless string");
  addParam("i", "a meaningless integer");
  setReturn("meaningless_string", "the string you asked for");
  BIND_METHOD(MyService::myMethod);

  functionName("myOtherMethod", getName(), "...");
  addParam("i", "...");
  setReturn("value", "...");
  BIND_METHOD(MyService::myOtherMethod);
}

This now becomes:

// myservice.h

class MyService
{
public:
  // you can omit the session if you don't need it
  MyService(qi::SessionPtr session);

  /** return a meaningless string
   * \param i a meaningless string
   * \return the string you asked for
   */
  std::string myMethod(int i) const;

  /** ...
   */
  bool myOtherMethod(int i);

private:
  qi::SessionPtr _session;
};

// myservice.cpp

MyService::MyService(qi::SessionPtr session)
  : _session(session)
{
  // Don't register your methods here
}

// this macro will register your methods
QI_REGISTER_MT_OBJECT(MyService, myMethod, myOtherMethod);

You will not see the documentation of your methods when calling qicli yet.

For more information, or for registering overloaded functions, see guide-cxx-register-classes.

Creating a NAOqi2 module

C++ part

After you created your service, you used to create a module by writing a file like:

// myservicemain.cpp

// lots of #include

#ifndef MYSERVICE_IS_REMOTE
# ifndef ALCALL
#   ifdef _WIN32
#     define ALCALL __declspec(dllexport)
#   else
#     define ALCALL
#   endif
# endif
#else
# define ALCALL
#endif

# ifdef __cplusplus
extern "C"
{
# endif

ALCALL int _createModule(boost::shared_ptr<AL::ALBroker> pBroker)
{
  // init broker with the main broker instance
  // from the parent executable
  AL::ALBrokerManager::setInstance(pBroker->fBrokerManager.lock());
  AL::ALBrokerManager::getInstance()->addBroker(pBroker);


  // create module instances
  AL::ALModule::createModule<MyService>(pBroker, "MyService");

  return 0;
}

ALCALL int _closeModule()
{
  return 0;
}

# ifdef __cplusplus
}
# endif

#ifdef MYSERVICE_IS_REMOTE

int main(int argc, char *argv[])
{
  // pointer on createModule
  TMainType sig;
  sig = &_createModule;

  // call main
  ALTools::mainFunction("MyService", argc, argv, sig);
}

#endif

You can now write a simple main function that connects to Naoqi and registers the service. No more need for MYSERVICE_IS_REMOTE, the service will always be remote.

#include <qi/applicationsession.hpp>
#include <boost/shared_ptr.hpp>

int main(int argc, char* argv[])
{
  qi::ApplicationSession app(argc, argv);
  app.start();
  qi::SessionPtr session = app.session();
  session->registerService("MyService", qi::AnyObject(boost::make_shared<MyService>()));
  app.run();
  return 0;
}

CMake part

Here is what you used to write in NAOqi1:

# Creating module launcher binary
option(MYSERVICE_IS_REMOTE
  "is my service remote?"
  OFF
)

if(MYSERVICE_IS_REMOTE)
  add_definitions(" -DMYSERVICE_IS_REMOTE ")
  qi_create_bin(myservice src/myservicemain.cpp)
  qi_use_lib(myservice stuff stuff2)
  qi_stage_bin(myservice)
else()
  qi_create_lib(
    myservice

    SRC
    src/myservice.h
    src/myservice.cpp
    src/myservicemain.cpp

    MODULE
    SUBFOLDER naoqi
  )
  qi_use_lib(myservice stuff stuff2)
endif()

Here also, you don’t need to care about being remote or not. This now becomes:

qi_create_bin(myservice
  SRC myservice.h myservice.cpp main.cpp
  DEPENDS BOOST QI
  )

Now, see Creating a new application outside Choregraphe using the qi Framework to compile and install your service on your robot.

Calling a service

You can call a NAOqi1 or NAOqi2 service from both NAOqi1 and NAOqi2. In NAOqi1, you used to do:

boost::shared_ptr<ALProxy> myservice = getParentBroker()->getProxy("MyService");
std::string val = myservice.call<std::string>("myMethod", 18);

In NAOqi2, this now becomes:

qi::AnyObject myservice = _session.service("MyService");
std::string val = myservice.call<std::string>("myMethod", 18);
// asynchronous version
qi::Future<std::string> futval = myservice.async<std::string>("myMethod", 18);

It is up to you to store the session somehow. There is no equivalent to getParentBroker() or getRandomBroker().

There are no auto-generated specialized proxies yet in NAOqi2.

Note

If you don’t have a Session (because your module is still in NAOqi1), you can get one from broker->session().

Getting a Session from a Broker

You can get a Session from a broker by using broker->session().

Subscribing to an event from ALMemory

You don’t need to change the way you subscribe to ALMemory events. The difference is that in NAOqi2, you can subscribe to an event even if you are not a registered service yourself.

class MyClass {
public:
  MyClass(SessionPtr session) {
    qi::AnyObject almemory = session->service("ALMemory");
    // keep the object alive
    _subscriber = almemory.call<qi::AnyObject>("subscriber", "Something/MyEvent");
    _subscriber.connect("signal", boost::bind(&MyClass::myCallback, this, _1));
  }
  void myCallback(AL::ALValue value) { /*...*/ }

private:
  qi::AnyObject _subscriber;
};