doc/tutorial-clock.md

Summary

Maintainability
Test Coverage
# How to create a new service

This guide will show you how to create and test a new service.

In this tutorial, we will implements a timestamp service whose role is
to allow clients to produce synchronized timestamps. It is based on
the [clock service](https://github.com/lugu/qiloop/blob/master/examples/clock)
example.

## Define the service interface

The public interface of a service is described by an IDL file.
Create a file clock.qi.idl with the content:

        package clock

        interface Timestamp
            fn nanoseconds() -> int64
        end

This IDL file will be processed to generate the serializing code.

## Generate the server stub

The IDL file is used to generate the necessary boiler code to
implement the service. Use `qiloop` to generate the server stub:

        qiloop stub --idl clock.qi.idl --output clock_stub_gen.go

## Automate the stub generation

In order to easily update the clock_stub_gen.go file, create a file
called generate.go with the following content:

        //go:generate qiloop stub --idl clock.qi.idl --output clock_stub_gen.go
        package clock

Then execute the command:

        go generate

## Implement the service

The file clock_stub_gen.go defines an interface called
TimestampImplementor which describes the methods to be implemented in
order to create the timestamp service:

        type TimestampImplementor interface {
                Activate(activation bus.Activation, helper TimestampSignalHelper) error
                OnTerminate()
                Nanoseconds() (int64, error)
        }

The `Activate` method is called just before the service registration.
In contains runtime information useful for the service. For example
the `activation` parameter contains a session to connect other
services.

The `OnTerminate` method is called just before the service
terminates.

Finally, the `Nanoseconds` method is the implementation of the
timestamp function. Let's start with creating something which computes
timestamps:

        // Timestamper creates monotonic timestamps.
        type Timestamper time.Time

        // Nanoseconds returns the timestamp.
        func (t Timestamper) Nanoseconds() (int64, error) {
                return time.Since(time.Time(t)).Nanoseconds(), nil
        }

Using this `Timestamper` type, let's implements
the `TimestampImplementor` interface:

        // timestampService implements TimestampImplementor.
        type timestampService struct {
                Timestamper // inherits the Nanoseconds method.
        }

        // Activate is called once the service is online. It provides the
        // implementation important runtime informations.
        func (t timestampService) Activate(activation bus.Activation,
                helper TimestampSignalHelper) error {
                return nil
        }

        // OnTerminate is called when the service is termninated.
        func (t timestampService) OnTerminate() {
        }

The file clock_stub_gen defines a constructor method for Timestamp
object called `TimestampObject`:

        func TimestampObject(impl TimestampImplementor) bus.Actor

It returns a `bus.Actor` type which can be passed to a `bus.Server`.
Let's wrap this function to create a timestamp object based on our
implementation of `TimestampImplementor`:

        // NewTimestampObject creates a timestamp object which can be
        // registered to a bus.Server.
        func NewTimestampObject() bus.Actor {
                return TimestampObject(timestampService{
                        Timestamper(time.Now()),
                })
        }

That's it. The service implementation is completed. Let's use it in a
`main()` function to start the service.

## Create a program

In order to use the timestamp service, we need a program which uses
`NewTimestampObject` and registers it. The `app` package contains an
helper function for this.

    package main

    import (
            "flag"
            "log"
            "os"
            "os/signal"

            "github.com/lugu/qiloop/app"
            "github.com/lugu/qiloop/examples/clock"
    )

    func main() {
            flag.Parse()

            server, err := app.ServerFromFlag("Timestamp", clock.NewTimestampObject())
            if err != nil {
                    log.Fatal(err)
            }
            defer server.Terminate()

            log.Print("Timestamp service running...")

            interrupt := make(chan os.Signal, 1)
            signal.Notify(interrupt, os.Interrupt)

            // wait until the server fails or is interrupted.
            select {
            case err = <-server.WaitTerminate():
                    if err != nil {
                            log.Fatal(err)
                    }
            case <-interrupt:
                    log.Print("interrupt, quitting.")
            }
    }

In order to test it, we need a running instance of QiMessaging. We can
create one with the `qiloop server` command:

        $ qiloop server
        2019/07/15 22:57:09 Listening at tcp://localhost:9559

Now we can start the timestamp service with:

        $ go run ./examples/clock/cmd/service/main.go
    2019/07/15 23:00:20 Timestamp service running...

Let double check if the timestamp service is registered to the service
directory using `qiloop info`:

    $ qiloop info
    [
        {
        "Name": "ServiceDirectory",
        "ServiceId": 1,
        "MachineId": "e9b7594a1f209b898e7a3caea5e3199a407cf5bb08d090419e4fffdeddcf167f",
        "ProcessId": 17179,
        "Endpoints": [
            "tcp://localhost:9559"
        ],
        "SessionId": ""
        },
        {
        "Name": "Timestamp",
        "ServiceId": 2,
        "MachineId": "e9b7594a1f209b898e7a3caea5e3199a407cf5bb08d090419e4fffdeddcf167f",
        "ProcessId": 18596,
        "Endpoints": [
            "unix:///tmp/qiloop-271149288"
        ],
        "SessionId": ""
        }
    ]

Mission completed: a fonctionnal timestamp service! But wait, for a
timestamp to be precise it needs to be locally generated. Let's use
use this service to synchronize a `Timestamper` so we can have precise
and synchronized timestamps.

        // SynchronizedTimestamper returns a locally generated timestamp
        // source synchronized is the remote Timestamp service.
        func SynchronizedTimestamper(session bus.Session) (Timestamper, error) {

                ref := time.Now()

                constructor := Services(session)
                timestampProxy, err := constructor.Timestamp()
                if err != nil {
                        return Timestamper(ref),
                                fmt.Errorf("reference timestamp: %s", err)
                }

                delta1 := time.Since(ref)
                ts, err := timestampProxy.Nanoseconds()
                delta2 := time.Since(ref)

                if err != nil {
                        return Timestamper(ref),
                                fmt.Errorf("reference timestamp: %s", err)
                }

                offset := ((delta1 + delta2) / 2) - time.Duration(ts)
                return Timestamper(ref.Add(offset)), nil
        }

That's it. We have seen how to implement a QiMessaging service and
acces it.

The complete code can be found
[here](https://github.com/lugu/qiloop/blob/master/examples/clock).