Network programming language tutorial

Aggregation

Our second example shows a simple aggregation service which maintains a histogram of reported values and reports the value which occurred most every 100 readings.

First, the scenario needs to be described. Let's use a generic node to gather temperature readings from temperature sensors. Put the following content into Aggregation.wnl

2 2 CommonNodes TemperatureNode
2 5 CommonNodes TemperatureNode
3 1 CommonNodes TemperatureNode
4 4 CommonNodes TemperatureNode
6 6 CommonNodes GenericNode
0 2 LoaderNode2 LoaderNode
4 6 CommonNodes Gateway

Run the simulator once to generate the configuration file. Next define the Histogram service (line numbers are NOT part of the program):

01 service Histogram($Handler, $min_readings)
02   define
03     count := 0
04     values[16] := []
05   function OccurredMost() do
06     max := 0;
07     i := 1;
08     while i < 16 do
09       if values[max] < values[i] then max := i fi;
10       i := i + 1
11     od;
12     return (max)
13   function ResetHistogram() do
14     i := 0;
15     while i < 16 do
16       values[i] := 0;
17       i := i + 1
18     od;
19     count := 0
20   on event flush when $min_readings <= count do
21     i := OccurredMost();
22     SendToSubscribers($Handler, count, i, values[i]);
23     ResetHistogram()
24   action AddSensorReading(value) do
25     # divide 16 bit signed int into ranges of 4096
26     i := (value / 4096) + 8;
27     values[i] := values[i] + 1;
28     count := count + 1
29
30 subscription HistogramReport
31 to Histogram($Handler=print, $min_readings=100)
32 with (period=1s, deadline=1m, send="Normal", exec="Normal")
33 .

Several new constructs have been introduced in this example. First, lines two till four show how to define service state. We can define a series of integer variables and/or static arrays. Arrays are typically initialized to all zeroes (i.e. []). An integer in OSAS is 16 bit signed.

Our service contains two helper functions. OccurredMost returns the index of the slot which was reported most often. ResetHistogram clears the array and resets our value counter (i.e. count).

The action defined in lines twenty-four til twenty-eight can be used to report sensor readings to our aggregation service. It divides the integer which can be a value between -32768 and 32767 into 16 slots of 4096 values each and increments the slot which contains the reported value.

Note that an action (/event handler) is not executed in the context of a subscription, therefore it cannot contain SendToSubscribers and it cannot refer to subscription variables. The parameters of a handler should match the values sent by a call to SendToSubscribers in the event generators it is tied to. This is NOT verified by the compiler.

The event generator in lines twenty til twenty-three reports the most frequent range and its frequency to the subscriber whenever we have received at least $min_readings values. The actual value of $min_readings needs to be specified by the subscriber, which is done in lines thirty up to thirty-three.

The OccurredMost function also shows the two main control flow statements

  • Repetition: while <condition> do <stats> od
  • Selection: if <condition> then <stats> [elif <condition> then <statements>]* [else <stats>] fi

Finally, note that comments are written with the hash (#) symbol and that the semicolon (;) is strictly a statement separator and not a terminator.

We save our histogram service to Histogram.wsp and deal with deployment later on.

Tracking temperature

In order to demonstrate our little histogram service we need to generate some values. Let's demonstrate this with a temperature service. We store the following service in Temperature.wsp:

1 service Temperature($Handler)
2   on event measure when True do
3     SendToSubscribers($Handler, Temp())
4 .

Now we create a new unit aggregation.wsp to combine the temperature and histogram service as follows.

01 import Temperature # imports Temperature service
02 import Histogram # imports Histogram service and HistogramReport subscription
03
04 # Link Temperature service to AddSensorReading action
05
06 subscription TemperatureSubscription
07 to Temperature($Handler=AddSensorReading)
08 with (period=500ms, deadline=1m, send="Low", exec="Normal")
09
10 # deploy services
11
12 for [Network|*|HasSysCall(Temp)]
13   install Temperature
14
15 for [Network|*|NodeType()=="GenericNode"]
16   install Histogram
17   install TemperatureSubscription on [Network|*|HasSysCall(Temp)]
18
19 for [Network|*|NodeType()=="Gateway"]
20   install HistogramReport on [Network|*|NodeType()=="GenericNode"]
21 .

This example shows how to import other units and create a scenario specific deployment. Whenever you import a unit, all of its services and subscriptions are imported, but none of its deployment statements. This decoupling of definition and deployment allows one to develop useful library services such as e.g. routing, aggregation or common computations.

Any subscription that is imported but not deployed throws a compiler warning. Note that you are free to redefine these subscriptions or just ignore them altogether.