Skip to content
This repository has been archived by the owner on Aug 13, 2020. It is now read-only.

MiniTutorial

Per Persson edited this page Apr 12, 2017 · 4 revisions

Mini Tutorial

Running a first example

Assuming Calvin has been installed and works as expected (See installation instructions), create a file named hello.calvin, and enter the following:

/* Actors */
src : std.Trigger(tick=1.0, data="Hello, world")
snk : io.Log(loglevel="INFO")
/* Connections */
src.data > snk.data

This is an example of CalvinScript which is how an application is described in Calvin. There are two main parts to the script; First, the declaration of actors used in the application, and then a description of how they are connected. In this simple example, there are two actors, called src and snk. The first actor, src is of type std.Trigger() with the sole purpose of generating a tick to the system. To this purpose, it takes two parameters, tick, which is the interval, in seconds, with which to generate the data, and data which is what to output on each tick. The output is then sent on the data port of the actor.

The snk actor is of type io.Log(), it has a single parameter, loglevel, and outputs whatever data it gets on its data port to the runtime log.

The documentation of these types can be easily found using the csdocs command:

$ csdocs std.Trigger
std.Trigger(tick, data): Pass on given _data_ every _tick_ seconds
Outputs: data

$ csdocs io.Log
io.Log(loglevel): Write data to calvin log using specified loglevel.
Inputs: data                                                        

All valid actors can be inspected using csdocs, and they should all have some form of documentation.

Calvin applications are usually deployed to an existing runtime together with the necessary deployment information, but for small examples such as this one, there is a short cut. Excute the following from the same directory as the file is located in:

$ csruntime --host localhost hello.calvin

After a lot of INFO messages listing some information of what Calvin has found in the system w.r.t capabilities, the following should appear (the application id and number of lines will differ slightly):

Deployed application 922a2096-bfd9-48c8-a5a4-ee900a180ca4
2016-07-11 08:20:34,667 INFO     11202-calvin.Log: Hello, world  
2016-07-11 08:20:35,667 INFO     11202-calvin.Log: Hello, world

and then Calvin exits. When starting Calvin this way, with a script, there is a default timeout of 3 seconds before exiting. Normally, Calvin should not exit, but when toying around with small scripts, this may be the preferred way of doing it. The timeout can be changed:

$ csruntime --host localhost -w 10 hello.calvin 

will run for 10 seconds before exiting. Use -w 0 or --keepalive to run forever, but as was previously mentioned, this is not normally how a Calvin application is deployed.

The --host argument is important. For sample scripts, such as this, which only spans a single runtime localhost suffices, but when running a system with several runtimes, it is important that this argument is the IP(v4) address of the host running Calvin.

A second example

We will now have a look at how to make a distributed Calvin application. Create a text file named hello.deployjson and enter (or copy & paste) the following:

{
    "requirements": {
        "src":[{"op":"node_attr_match","kwargs":{"index":["node_name",{"name":"runtime-0"}]},"type":"+"}],
        "snk":[{"op":"node_attr_match","kwargs":{"index":["node_name",{"name":"runtime-1"}]},"type":"+"}]
    }
}

Then, start two Calvin runtimes, either on the same computer, or on different computers (just make sure they can reach each other via IP)

$ csruntime --host 192.168.2.3 --port 5000 --controlport 5001 --name runtime-0 &
$ csruntime --host 192.168.2.3 --port 5002 --controlport 5003 --name runtime-1 &

Of course, you need to replace 192.168.2.3 with the IP of your computer. The --port parameter sets the port Calvin will use for its internal runtime to runtime communication, and --controlport is the port used for accessing the control api, which we will use to deploy the application. The internal communication uses a Calvin-specific protocol, whereas the control api is based on REST over http.

You can also use the script setup_system.sh in extras/docker to setup this same system:

$ ./setup_system.sh -e 192.168.2.3 -r 1 -n dht  

Now, it is time to deploy the application with the deployment requirements we created earlier. The utility cscontrol is a shortcut to the control api. We can deploy an application with it like this:

$ cscontrol http://192.168.2.3:5001 deploy --reqs hello.deployjson hello.calvin

This will deploy the application, with the src actor being placed on runtime-0 and the snk actor being placed on runtime-1. When starting csruntime from the command line, the output will appear in the console, when using the setup_system.sh command, it is logged to a file corresponding to the name of the runtime, i.e. runtime-0.log and runtime-1.log in this case.

For further details about how to configure your system when using multiple runtimes, see Configuration section, particularly the parts about the registry.

Writing a component

CalvinScript allows you to group actors together in a component, which can then be used as a short cut when building scripts. For example, using the logging actor we used earlier, say we want to prepend "mylog:" to each logging entry. The script, amended to include this, would be:

/* Actors */
src : std.Trigger(tick=1.0, data="Hello, world")
prefix: text.PrefixString(prefix="mylog:")
snk : io.Log(loglevel="INFO")
/* Connections */
src.data > prefix.in
prefix.out > snk.data

Then, later, you may find you want that same logging prefix, but on a logger with loglevel "WARNING". Rather than repeating the code, let us wrap it in a component, and parameterize the name and loglevel:

component MyLog(logname, loglevel) data -> {
    prefix: text.PrefixString(prefix=logname)
    snk : io.Log(loglevel=loglevel)

    .data > prefix.in
    prefix.out > snk.data
}

This defines a component named MyLog with two parameters logname and loglevel, one inport data, and no outports. Note how the ports of a component are used (prefixed with . but no actorname.) This component can now be used (almost) as were it an actor.

/* Actors */
src : std.Trigger(tick=1.0, data="Hello, world")
infolog : MyLog(logname="myinfolog:", loglevel="INFO")
warnlog : MyLog(logname="mywarnlog:", loglevel="WARNING")
/* Connections */
src.data > infolog.data
src.data > warnlog.data

Writing an actor

With the currently quite limited (but growing!) selection of actors available, it is inevitable that a calvin developer will eventually end up with a problem which cannot be solved with existing actors, nor with components built from them. Consequently, writing a new actor, from scratch, is necessary. Below is a simple example, see the wiki entry on Actors for more.

Say you have an application where you need to divide two numbers. A straightforward problem which, likely requires two in ports with the numbers, and one output with the result.

from calvin.actor.actor import Actor, condition


class InputDiv(Actor):

    """
    Divides input on port 'dividend' with input on port 'divisor'
    Inputs :
        dividend : integer
        divisor : integer
    Output :
        result
    """

    def init(self):
        pass

    @condition(action_input=['dividend', 'divisor'], action_output=['result'])
    def divide(self, numerator, denumerator):
        result = numerator / denumerator
        return (result,)

    action_priority = (divide,)

To try it out , create a directory tree as follows and save it there:

actors/
    math/
        __init__.py
        InputDiv.py

There should be a tool for this, of course.

By default, calvin only uses pre-installed actors. In order to use this new one, create a file calvin.conf with the contents:

{
  "global": {
    "actor_paths": ["./actors"]
  }
}

This tells calvin to look for additional actors in the ./actors directory.

In the same directory, create a script mathtest.calvin, with

ten : std.Constant(data=10)
five : std.Constant(data=5)
div : math.InputDiv()
out : io.Print()

ten.token > div.dividend
five.token > div.divisor
div.result > out.token

(You will note that this is a rather inefficient way of dividing two numbers.)

The directory structure should now be:

calvin.conf
mathtest.calvin
actors/
    math/
        InputDiv.py

Run the application with

csruntime --host localhost mathtest.calvin

Among the output should be the line

2

which is correct.

But what happens if the divisor is 0? (It will make the application crash.) In order to handle it somewhat gracefully, we add a second action to the actor which checks the input on the ports, and forwards an exception token if the divisor is 0. Edit the file InputDiv.py (or copy & paste) to contain the following:

from calvin.actor.actor import Actor, condition
from calvin.runtime.north.calvin_token import ExceptionToken


class InputDiv(Actor):

    """
    Divides input on port 'dividend' with input on port 'divisor'
    Inputs :
        dividend : integer
        divisor : integer
    Output :
        result
    """

    def init(self):
        pass

    @condition(action_input=['dividend', 'divisor'], action_output=['result'])
    def divide(self, numerator, denumerator):
        if denumerator != 0:
            result = numerator / denumerator
        else:
            result = ExceptionToken("Division by 0")
        return (result,)

    action_priority = (divide,)

Changing the script mathtest.calvin to

ten : std.Constant(data=10)
zero : std.Constant(data=0)
div : math.InputDiv()
out : io.Print()

ten.token > div.dividend
zero.token > div.divisor
div.result > out.token

will give the log entry

<ExceptionToken> Division by 0

which is far better than a crash.