Network programming language tutorial

Hello World

Adhering to the programming language tradition, the first program we show is a hello world service. Before we can define our service though, we need to generate a configuration file for our compiler. This is required so that it knows which functions and handlers have been defined by our hardware and virtual nodes.

Let's say we want our scenario to consist of a single node which sends a hello world message to our gateway node. We need to describe the layout of our nodes in the network layout file.

2 4 CommonNodes GenericNode
4 4 CommonNodes Gateway
6 4 LoaderNode2 LoaderNode

Save this into the file HelloWorld.wnl.

This file describes which nodes are present in our scenario. The first two numbers are x and y coordinates respectively (both should be in range [0..15]). The identifier that follows (CommonNodes and LoaderNode2) identify the name of a python unit which contains custom node classes. The last name must match the name of a custom node class in the aforementioned unit. Hence, this file will place a generic node at (2,4) a gateway at (4,4) and a loader at (6,4).

Including the loader node will start a loader application which imports the bytecode file with the same name as the network layout file (if any) which is slightly less work than starting the external loader.

Whenever you modify the network layout file, the simulator should be started and stopped once. This creates a new scenario configuration file which describes all functions and actions present in our simulated nodes. This file is subsequently used by the compiler to generate the bytecode for our scenario.

Run the simulator now: GUISimulator.exe HelloWorld.wnl

The first tab shows a debug-console, it can be used to inspect and monkeypatch the internal datastructures of the simulator in case things really break down. In normal circumstances you don't need this. If the simulator raises any exception, it will be printed here.

The second tab contains a simple log of the last messages sent and received by various nodes. The third and final tab contains a canvas which displays all nodes present in our scenario and a lists of printed messages on the right side. It should display three nodes neatly arranged in a straight line. We will get back to this later, for now close the simulator.

If all went well you now have a HelloWorld.xml file. This file lists all current services, functions, handlers and strings already defined in the network. The functions and handlers are of special interest since these are the ones we can use in our program.

Our first service

So we setup our simulation environment and are ready to define the first program. The following listing presents a service which periodically transmits Hello World! to its subscribers (the line numbers are NOT part of the program).

1 service HelloWorld($Handler)
2   on event greet when True do
3     SendToSubscribers($Handler, "Hello World!")
4 .

This program shows one of the most important statements in the language, the service declaration. Line one shows the served word service followed by an identifier name for the service and a parameter. These parameters are subscription variables. They must start with a dollar and their values are supplied by subscribers to this service. Subscription variables can be used within event generators.

Lines two and three show an example of an event generator. An event generator is a event-condition-action rule. The event name (an identifier, here greet) is given after the keywords on event. Events are always triggered by a timer (more on this later when subscriptions are discussed). The condition can be a boolean expression build of fairly standard operators as found in most programming languages. The following table shows the available operators which can be used in conditions and expressions:

Type syntax description Type syntax description type syntax description
comparison < less Bitwise & bitwise and Arithmetic + addition Logical ! negation
<= at most | bitwise or - subtraction && conjunction
> greater ^ bitwise xor * multiplication || disjunction
>= at least shl shift left / division
== equality shr shift right % modulo
!= inequality

The body of a event generator usually consists of at least one call to SendToSubscribers which notifies all subscribers that an event has occurred. The first argument is the handler (a callback function which will process this event when received). All further arguments to SendToSubscribers are passed along as arguments in the event message and will be processed by the event handler.

The call to SendToSubscribers above can be interpreted as the following remote method invocation (without any return value) on all subscribers:

$Handler("Hello World!")

The easiest part of this program is line four. It contains a single dot which indicates the end of the program, it is placed only at the end of an entire program (/unit), not at the end of each service definition.

Now save this service as 'HelloWorld.wsp' and compile it:

compiler.exe HelloWorld.wsp

This will generate two new files: HelloWorld(1).xml and HelloWorld.wbc. The first stores the new configuration of the network. We can use this configuration file as input to our compiler in case we want to add services to an already running scenario. We can safely ignore this file for the time being. The second file contains the configuration messages with bytecode for the service.

You'll notice that this file is empty, aside from a comment with a version number. Why? Because we have not yet told the compiler where to run this service.

Service deployment

In the previous section we defined our first simple HelloWorld service. In this section we show how to install this service onto nodes.

Let's extend our simple scenario with a deployment statement.

1 service HelloWorld($Handler)
2   on event greet when True do
3     SendToSubscribers($Handler, "Hello World!")
4
5 for [Network|*]
6   install HelloWorld
7 .

This example adds a deployment statement to our program. Line 5 contains an address declaration which identifies which nodes will actually install this service. Line 6 indicates what to deploy, the keyword install followed by the name of a service installs that service on all nodes which satisfy the specified address.

In this case we used the easiest address possible: all nodes in the network ([Network|*]). The more general form of a content based address is:

[<scope>|<hopcount>|<predicate>]

The scope identifies a subset of nodes in the network. It is either Network, which indicates the message is flooded to the entire network, or Cluster, which specifies a predicate which restricts the flood to a partition of the network satisfying said predicate (see addressing for more details).

The hopcount specifies the maximum number of hops a message is allowed to travel. Special values 0 and * indicate that there is no restriction.

The predicate is an expression which yields either true or false. It defines an address predicate which is sent to and stored on all nodes in the network. Whenever a node receives a message, the message can refer to an address predicate to test. If a node fails to satisfy said predicate then the message payload will be ignored.

Save the changes to HelloWorld.wsp and recompile: Compiler.exe HelloWorld.wsp The newly generated HelloWorld.wbc should now contain a single configuration message of the following form:

(0	; BytecodeHandler
  (SERV 0 1)	; HelloWorld
  (DEFE 0 3	; event greet
    PUSHV[0]	; $Handler
    PUSHC[0]	; "Hello World!"
    NTFY))

This message tells a node to execute a configuration message (BytecodeHandler), which defines service zero which has one part (SERV 0 1). This part is event generator zero whose body consists of three bytes (DEFE 0 3 PUSHV[0] PUSHC[0] NTFY). These three bytes correspond to our statement SendToSubscribers($Handler, "Hello World!").

It is interesting to note that our string got converted to the integer 0. The OSAS system replaces all strings by numbers. As a consequence, arithmetic operations on strings have an entirely different result than one would expect. Usually strings are used as simple constants within OSAS to indicate a specific type of result and are only checked for equality or inequality. Actually communicating strings between nodes is wasteful and costly.

By specificying the service deployment we actually install code onto the nodes, however it still doesn't do anything interesting by itself. A service will only execute if anyone is interested in it. To specify interest, we need to create a subscription.

Tying it all together

OSAS subscriptions are third party. That is, an OSAS program tells a (group of) subscriber(s) to subscribe to a (group of) provider(s). Subscriptions determine the parameters, period and non-functional parameters of the event-generator loops of a provider.

Let's suppose we have a special Gateway node which we want to subscribe to the HelloWorld services in the network. First we define a subscription as follows:

1 subscription HelloWorldSubscription
2 to HelloWorld($Handler=print)
3 with (period=5s..30s, deadline=2m, send="Normal", exec="Normal")

Note that in the first line the subscription is given a name. This is used in deployment of the subscription and allows updating of existing subscriptions. Subscribing to a service will cause all event generators of that service to send their events to the subscriber. In the second line, the to clause specifies what service is subscribed to and provides the initial values of all subscription variables. Finally, the third line specifies the triggering interval and non-functional properties of the subscription (see the table below for their values and interpretation).

field value range function
period <number>s (# seconds)
<number>m (# minutes)
<number>h (# hours)
This determines how often the provider fires its event generators (the ECA rules).
Can specify an interval like this: 5s..10m
deadline same as period This is a QoS parameter whichis passed to the communication handlers for optimization.
send "Lowest", "Low", "Normal", "High", "Critical", Send priority. These values are for the generated events.
exec same as send Execution priority.

The interesting mechanism here is that the frequency and parameters to the conditions of the event generators and the QoS properties of the generated events are all determined by the subscriber to these events and not by the provider. The consequence of this is that the eventing interval and the subscription variables can differ per subscriber. For this reason event generators execute within the context of a specific subscriber.

The last step is to deploy the subscription. The following example shows to combined HelloWorld scenario. Lines twelve and thirteen are new.

01 service HelloWorld($Handler)
02   on event greet when True do
03     SendToSubscribers($Handler, "Hello World!")
04
05 subscription HelloWorldSubscription
06 to HelloWorld($Handler=print)
07 with (period=5s..30s, deadline=2m, send="Normal", exec="Normal")
08
09 for [Network|*]
10   install HelloWorld
11
12 for [Network|*|NodeType()=="Gateway"]
13   install HelloWorldSubscription on [Network|*]
14
15 .

To deploy a subscription we add a deployment statement which is similar to deployment of services. The difference lies in the additional on-clause which contains another content based address. The outer content-based address (line twelve) specifies the subscribers. The address in the on-clause (line thirteen) specifies the providers. The address in line twelve shows a simple predicate which tests if the NodeType has been set to gateway.

The NodeType function returns a number which is either flashed onto the node (for hardware nodes) or derived from the name of the class that implements a virtual node. Virtual nodes are nodes which run in a simulation environment on a powerful device such as a pc, laptop or pda. In this example, the gateway node (a virtual node) subscribes onto all nodes in the network.

Testing the service

Compile and run the hello world service:

  • compiler.exe HelloWorld.wsp
  • GUISimulator.exe HelloWorld.wnl

Next, we start the event-loop of the simulated nodes by hitting the start eventloop button in the loader GUI. Next to the canvas, each simulated node will print its id followed by starting service.

The loader presents a tree of all elements defined in the network. Typically, a block of addresses a list of services and a sequence of subscriptions. The first step is to install the addresses. Select them and click upload selection. In case you're dealing with hardware nodes as well you might want to do this several times to increase the upload reliability.

Upload the services in the same manner (either one by one, or all at the same time). In case you're uploading either a lot of services or very large services, uploading one by one is preferred to reduce the intensity of the following flood storm.

The last step is to select your subscription by double clicking it. This loads the subscriptions values into the radial buttons and the slider bar. You can use these to modify the period of the subscription. Finally install the subscription by hitting upload selection (naturally, with the subscription still selected).

The expected result of this program would be that periodically, the simulator prints a number (actually two numbers, the first is the ID of the printing node), three times (Can you explain why three times?).

A small refinement

In the above program we provided the print handler in our subscription. The gateway node also provides a handler named PrintString. The PrintString handler does a reverse lookup of the string id in the modified configuration file (HelloWorld(1).xml) and prints the string instead. Any additional parameters provided to the PrintString handler will be used for string formatting.

Change the program to the one listed below, recompile, restart the simulator. And load the program.

01 service HelloWorld($Handler)
02   on event greet when True do
03     SendToSubscribers($Handler, "Hello World! from %s", NodeID())
04
05 subscription HelloWorldSubscription
06 to HelloWorld($Handler=PrintString)
07 with (period=5s..30s, deadline=2m, send="Normal", exec="Normal")
08
09 for [Network|*]
10   install HelloWorld
11
12 for [Network|*|NodeType()=="Gateway"]
13   install HelloWorldSubscription on [Network|*]
14
15 .

Note that the string formatting is handled by the PrintString handler, you cannot use string formatting in OSAS directly since the string is never transmitted as a string. The payload of the SendToSubscribers call contains only three bytes: the handler, the string id and the node id.