# SoftBank Robotics documentation What's new in NAOqi 2.8?

# Making Services using QiLang

QiLang helps writing Qi Services. Its purpose is to define the interface of the service, written in the QiLang Interface Definition Language (IDL). Based on this interface, QiLang generates automatically:

- the code for strongly-typed proxies for the client code
- GMock classes for testing.

## Strongly-typed Proxies

Strongly-typed proxies improve the client code by allowing numerous mistakes to be detected at buildtime, instead of at runtime. For example:

    auto ret = myservice.call<void>("workIt", 42, "blah");

will compile but will fail at runtime because the function expects first a string argument and then an integer. Using QiLang allows you to write:

    auto ret = myservice->workIt(42, "blah");

and get a compile-time error because of the invalid arguments.

The code generated by qilang can then be put in a library so that it can be exposed to clients who may compile against it.

If you need details about the qilang syntax, see qilang IDL syntax.

## Creating your Service Project

It is advised to use a template to create your project using a simple line of command, from a QiBuild worktree:

    $ cd <worktree>
    $ qisrc create -i <qilang_repository>/templates/qi-service-cpp MyService

Note

The case used for naming the project is kept only for the name of the service in the service directory, and the name of the classes in the C++ code.

It should produce the following layout of files:

    myservice
    │
    ├── myservice
    │   ├── CMakeLists.txt
    │   ├── qi
    │   │   └── myservice
    │   │       ├── api.hpp
    │   │       └── myservice.idl.qi
    │   └── qiproject.xml
    │
    ├── myservice_impl
    │   ├── CMakeLists.txt
    │   ├── qiproject.xml
    │   ├── src
    │   │   ├── myservice_impl.hpp
    │   │   └── myservice_module.cpp
    │   └── test
    │       └── test_myservice.cpp
    │
    ├── qiproject.xml
    └── README.rst

This layout consists in two QiBuild Projects:

- `myservice` exposes the API defined in `qi/myservice/myservice.idl.qi`, and will generate automatically various C++ files at buildtime, according to what the *.idl.qi files provide. Clients would depend on this project to use the strongly-typed proxies for your service.
- `myservice_impl` is where to put the implementation and the tests for your service. Clients do not need to know about this project to use your service.

To start working quick on your project, remember that:

- `myservice/qi/myservice/myservice.idl.qi` is where your service’s API is defined, using the QiLang IDL.
- `myservice_impl/src/myservice_impl.hpp` is where your implementation lies. It must match the service’s API.
- `myservice_impl/test/test_myservice.cpp` is where to put your automatic tests, to quickly check the sanity of your work.

## Building

1. Only the first time: make sure you have set your host config, that is to say the build config to use to produce binaries compatible with your computer. For example if it is named `linux64`

       $ qibuild set-host-config linux64

   Setting your host config allows QiBuild to select the right binary to run, even when cross-compiling.

2. Only if you are building libqilang by yourself, build the host tools:

       $ qibuild make-host-tools myservice_impl

   Do not forget to call it again every time QiLang source code has changed.

3. Build your project

       $ qibuild configure myservice_impl
       $ qibuild make myservice_impl

4. Run the tests:

       $ qitest run myservice_impl

## Running the Service

### From your Computer

You can run your service on your computer, while registering it on a robot. It should behave the same as services running locally on the robot.

Note

This is the recommended way to run your service while developing it, but it does not replace automatic tests, nor integration test where the service should be deployed and run on the robot.

Simply run `qilaunch` to instantiate and register the service, as exposed in `myservice_impl/src/myservice_module.cpp`:

    $ qibuild run -- qilaunch -n MyService -o myservice_module.MyService --qi-url <robot_endpoint>

### On a Robot

1. Cross-compile your service for the robot.
2. Deploy it:

    $ qibuild deploy --runtime --with-tests --url <login@address:/path/to/deploy> myservice_impl

3. Run it:

    $ ssh <login@address>
    $ /path/to/deploy/bin/qilaunch -n MyService -o myservice_module.MyService

## Using strongly typed proxies in another project

In another project, to benefit from the proxies you generated in `myservice`, follow these steps:

1. Add `myservice` as a `buildtime and runtime dependency`.
2. Add `myservice` as a dependency of your binary:

    qi_create_bin(main "main.cpp" DEPENDS qi myservice)

3. Use the proxy in your code:

    #include <qi/applicationsession.hpp>
    #include <qi/myservice/myservice.hpp>

    qiLogCategory("myclient");

    int main(int argc, char* argv[])
    {
      qi::ApplicationSession app(argc, argv);
      app.start();

      qi::SessionPtr session = app.session();
      mylibrary::MyServicePtr myserv = session->service("MyService");

      for (const auto& value : myserv->workIt("blah", 42))
        qiLogInfo() << "value: " << value;

      return EXIT_SUCCESS;
    }

You can also make asynchronous calls like this:

    auto future = myserv->async().workIt("blah", 42);
    future.connect(mycallback);

### Using a mock-up of the service in another project

When another project depends on the service `MyService`, it usually requires it for various functionalities, that cannot be tested in absence of `MyService`. Mocking `MyService` allows the other project to simulate how `MyService` behaves, and test the reaction of the project according to that behavior.

QiLang automatically generates GMock classes matching the interfaces provided in the IDL files. GMock is a powerful open-source library for writing mock-ups, that can be tuned specifically for every test unit.

For each IDL file specified to build, a file with the same base name is created under the `gmock` folder. For example, `qi/myservice/myservice.idl.qi` will produce `qi/myservice/gmock/myservice.hpp`.

Here is how to write a test using the GMock classes generated by QiLang, from another project in the same worktree:

1. Add `gmock` as a `testtime dependency` in the `qiproject.xml`.
2. In the CMakeLists.txt, create the test binary as following:

    qi_create_gmock(
      test_otherproject
      "test_otherproject.cpp"
      DEPENDS myservice
    )

In `test_otherproject.cpp`, typically:

    #include <gmock/gmock.h>
    #include <qi/session.hpp>
    #include <qi/myservice/gmock/myservice.hpp>

    // Recreating a session cleanly at every test.
    class OtherProjectTest: public testing::Test
    {
      void SetUp() override
      {
        _session = qi::makeSession();
        _session->listenStandalone("tcp://127.0.0.1:0");

        // let a mock-up MyService available on the session
        _myService = boost::make_shared<qi::myservice::MyServiceGMock>(_session);
        _session->registerService("MyService", _myService);

        // by default, myService.workIt returns [1, 2, 3]
        ON_CALL(*_myService, workIt(_, _)).WillByDefault(
                  testing::Return(qi::Future<std::vector<int>>{std::vector<int>{1, 2, 3}}));

        // ... the things useful to test the project
      }

      void TearDown() override
      {
        _myService.reset();
        _session->close();
        _session.reset();
      }

    protected:
      qi::SessionPtr _session;
      boost::shared_ptr<qi::myservice::MyServiceGMock> _myService;
    };

    TEST_F(OtherProjectTest, whatever)
    {
      // ... a test of the other project, indirectly using MyService
    }

## Distributing

Since the service consists in two separate QiBuild Projects, you can distribute the interface (`myservice`) and the implementation (`myserviceimpl`) separately. It is advised to distribute the implementation only on a robot, (in NAOqi Packages, for example) and the interface in an SDK for clients, and consider other services depending on your service like normal clients.

## Understanding the Project in depth

`myservice` project is the only one using QiLang (`libqilang`) as a host dependency, and it can be seen in `myservice/qiproject.xml`:

    <project name="myproject" version="3">
      <depends buildtime="true" runtime="true" names="libqi"/>
      <depends host="true" names="libqilang"/>
    </project>

The IDL file (`myservice/qi/myservice/myservice.idl.qi`) is where you will write the actual API of your service. The file `myservice.idl.qi` may look like that:

    package mylibrary

    interface MyService
      fn emitPing(value: int) // Function
      fn workIt(name: str, value: int) -> Vec<int> // Function with return value
      sig ping(value: int) // Signal
      prop pingEnabled(value: bool) // Property
    end

QiLang will be able to generate the code from this IDL file as soon as the following CMake code calls for it. Your CMakeLists.txt should look like this:

    cmake_minimum_required(VERSION 2.8)
    project(MyService)

    find_package(qibuild) # Always required
    qi_sanitize_compile_flags(HIDDEN_SYMBOLS) # To behave the same way on every platform
    include_directories(".") # Exposes the headers locally

    # Generate the specialized proxies. You can put several IDL files if you want.
    find_package(qilang-tools) # Required for qi_gen_idl
    set(myservice_idl "qi/myservice/myservice.idl.qi")
    qi_gen_idl(myservice_generated CPP "qi.myservice" "${CMAKE_CURRENT_BINARY_DIR}" ${myservice_idl})
    include_directories(${CMAKE_CURRENT_BINARY_DIR}) # Exposes the generated headers locally

    # Install the headers so that people can use your proxies
    qi_install_header(
      ${myservice_generated_INTERFACE}
      "qi/myservice/api.hpp"
      KEEP_RELATIVE_PATHS)

    # Create a lib with the proxies only
    qi_create_lib(myservice
      ${myservice_generated}
      ${myservice_idl} # Makes them visible in the IDE
      DEPENDS
      qi)

    qi_stage_lib(myservice)

    # Create a header-only library providing GMock mockups, useful to simulate this
    # service's behavior from other dependent services.
    qi_stage_header_only_lib(
      myservice_gmock
      "${myservice_GMOCK}"
      DEPENDS
      gmock
    )

The api.hpp file is still needed and contains:

    #ifndef QI_MYSERVICE_API_HPP
    #define QI_MYSERVICE_API_HPP

    #include <qi/macro.hpp>

    #define QI_MYSERVICE_API QI_LIB_API(myservice)

    #endif

The argument of `QI_LIB_API` must be the same as the name of your CMake target, i.e. the library exposing the specialized proxies in this example (“mylibrary”).

You can now focus on the actual implementation of your service, let us have a look at myserviceimpl.hpp:

    #include <qi/session.hpp>
    #include <src/myservice/myservice_p.hpp>

    // Same name as your package in qilang
    namespace mylibrary {
      class MyServiceImpl {
      public:
        MyServiceImpl(const qi::SessionPtr& session) : _session(session) { }

        void emitPing(int value) {
          if (pingEnabled.get())
            QI_EMIT ping(value);
        }

        qi::Future<std::vector<int> > workIt(std::string, int) {
          return std::vector<int>{}; // Immediate or asynchronous tasks are advised
        }

        qi::Signal<int> ping;
        qi::Property<bool> pingEnabled;

      private: // This session is useless here, but most people will need it
        qi::SessionPtr _session;
      };
    } // mylibrary

    // Declare your implementation of MyService
    QI_REGISTER_IMPLEMENTATION_H(mylibrary::MyService, mylibrary::MyServiceImpl)

QI_REGISTER_IMPLEMENTATION_H enables the conversion from boost::shared_ptr<MyServiceImpl> to qi::Object<MyService>. It is only needed for the interfaces declared in the IDL, and is not needed for structs, for example.

The last thing to implement is the module associated with your library. It will serve as a cross-language and unified entry point for your service to be started. The code consists in registering types and functions on the module.

    #include <qi/anymodule.hpp>
    #include "myserviceimpl.hpp"

    REGISTER_MYSERVICE(mylibrary::MyServiceImpl);

    void registerMe(qi::ModuleBuilder* mb) {
      mb->advertiseFactory<mylibrary::MyService, const qi::SessionPtr&>("MyService");
    }

    QI_REGISTER_MODULE("mylibrary_module", &registerMe);

Note

It is strongly advised to add tests to your project, to do so you can refer to Using strongly typed proxies in another project.

## Subpackages and subfolder in includes

QiLang supports subpackages in the IDL files.

Example, for an IDL file written as mylibrary/subpackage/mydata.idl.qi:

    package mylibrary.subpackage

    struct MyData
      number: int
      text: str
    end

In the CMake, you can refer to this IDL file in addition to the other ones. The generated interface header will be found as <mylibrary/subpackage/mydata.hpp>.

Note

IDL files must always be stored in a directory hierarchy matching the package in which they provide definitions. Here the package is mylibrary.subpackage, so the IDL file must be stored in mylibrary/subpackage.

Glossary Site map Index Support Contact Legal Notices