Message format

The following picture depicts the various layers in the WSP message format. The WSP format has been designed to be embedded directly into the MAC layer (although it can be placed on top of the transport layer just as easily).

The payload of a WSP message consists of a 4 byte header containing the application ID, Priority, Reliability Encoding, Deadline and Hopcount fields. The Application ID identifies the protocol and creates separate namespaces for each application run on top of the sensor network. The Priority, Reliabilty and deadline fields are reserved for control on buffer management (not implemented yet). The hopcount indicates how many hops the packet is allowed to traverse. Finally, the encoding can be one of three values:

  • 00: 8bit encoding. Each byte in the message corresponds to a single value. 8bit encoding is well suited for messages containing bytecode as payload (i.e. most configuration messages).
  • 01: UTF-8 like encoding. Variable length encoding of values:
    0-127 0xxxxxxx
    128-2047 110yyyxx 10xxxxxx
    2048-65535 1110yyyy 10yyyyxx 10xxxxxx

    This encoding is especially useful if only a few large values are communicated.

  • 10: 16bit encoding. Every two bytes encoding a single value. This encoding will typically be used for runtime messages containing many sensor readings suchs as e.g. an array of acceleration data.

Note that these encodings only apply to the WSP payload, not the headers nor the footers. When transmitting a sequence of integers the optimal encoding is automatically selected. If all values in the WSP payload are within the range [0-255] then 8bit encoding is used as it is always the smallest and decoding is a cheap operation. If at least one single value exceeds 255 then either UTF-8 encoding or 16bit encoding is used. The runtime system computes how long the message would be if it was encoded in UTF-8 and selects UTF-8 encoding only if it is smaller than twice the number of integers in the payload (the size (in bytes) of a 16bit encoded payload). This gives us a simple lower and upper bound on the size of the WSP payload:

# Integers in payload ≤ Encoded payload size ≤ 2 * # Integers in payload

A WSP payload consists of a handler (the ID of a callback function) and zero or more argument values (parameters of the callback). Upon reception of a message the callback function is executed provided that it is known to the receiver. If it is not known, then the message is ignored.

Handlers can be nested. The figure above shows an example where two handlers (SetLocation and Report) are sequentially composed by the enclosing 'Two' handler. Several built-in handlers embed other handlers. Most notably the handler that tests a content based address (CBA), the routing handler and the flooding handlers.

Built-in handlers

Below most of the built-in (standard) handlers are discussed.

CheckAddressHandler (CBA)

A Content Based Address (CBA) is a predicate on a node (specifically, over NContext). It is typically used to conditionally install code on nodes based on their capabilities (such as availability of a sensor).

The payload of the message should look like this:

hID addressID argcount <arg_0> ... <arg_n> <payload>

where

  • hID is the ID of the CheckAddressHandler
  • addressID is the ID of the content based address to test
  • argcount is the number of paremeters to the address
  • <arg_0>...<arg_n> are argcount parameters to pass to the address.
  • <payload> is an embedded handler which is executed whenever the predicate yields true.

TreeHandler (also: Two-Nine)

Calls a sequence of handlers in left to right order.

Message format:

TREE 1st 2nd 3rd ... H_1 H_2 H_3 ...

The starting point references (1st, 2nd,...) are absolute indexes in the packet and point to H_1, H_2,... respectively.

TWO - NINE are specialised versions of TREE where the 1st Handler offset is left out (since the nr of handlers is known):

TWO 2nd H_1 H_2


THREE 2nd 3rd H_1 H_2 H_3

...

NINE 2nd 3rd ... 9th H_1 H_2 ... H_9

Subscription handler.

Create or remove a subscription. Each new subscription creates a new subscription context (SContext) which stores the meta data and subscription parameters.

The format of the subscription message takes the form:

HID ServID SubsID period subscriber <handler> execprio sendprio param_count <params>

where

  • HID: ID of the SubscriptionHandler
  • ServID: ID of the service to which a subscription is being made
  • SubsID: ID of the subscription
  • period: subscription period (subscriptions are periodic)
  • subscriber: (node) ID of the subscriber
  • <handler>: (deprecated!) ID of the callback handler
  • execprio: execution priority
  • sendprio: send priority
  • param_count: # parameters that follows param_count
  • <params>: initial/new values of the subscription variables

Subscriptions are processed according to the following rules:

  • period != 0 : add or update subscription
  • new SubsID, period == 0 : one-time subscription
  • known SubsID, period == 0 : cancel subscription

Subscription Request Message handler.

A subscription request message is a directive to a node to subscribe to another. This provides the ability to perform 3rd party binding of services which improves their re-usability.

Input message (a subscription request, handlers are marked in bold):

CheckAddr subsAddrId pCountSA <params> SubReq servId subsId period (*) handler
execPrio sendPrio vCount <vars> provAddrId pCountPA cCount <code>

Output message (a subscription, handlers are marked in bold):

CheckAddr provAddrId pCountPA <code()> Subs servId subsId
period <subscriberId> handler execPrio sendPrio vCount <vars>

(*) Note: Subscriber not specified in the request! The node processing the SubReq handler fills in its own ID (i.e. NodeID()).

<code> is a sequence of bytecode which yields:

<paramcount> <param_0> ... <param_n>

Built-in handlers for communication

SetRouteHandler

Adds entry {destination: (cost, nexthop)} into routing table.

Overwrites existing entry for specified destination if cost is lower. Message format:

handlerID destination cost nexthop

where

  • handlerID is the ID of the SetRouteHandler
  • destination is the target node to which we want to route a message
  • cost is the nr of hops between the current node and the destination
  • nexthop is a neighbour node with a path to the destination.

This handler performs an in-place edit of the message, increasing the cost by one and replacing the nexthop ID with its own ID such that flooding this message yields a reverse path from every node in the network to the initiator of the flood.

RouteHandler

Routes packet to destination using unicast communication.

The routing handler forwards packets to neighbour nodes based on the internal routing table, which is a dictionary that maps destination nodeID to (cost (# hops), next hop nodeID)

Message format:

handlerID destination <payload>

where

  • handlerID is the ID of the RouteHandler
  • destination is the nodeID of the target node (i.e. the node to which the message needs to be routed)
  • <payload> is an embedded handler which is executed only on the destination node.

FloodHandler

Implements simple flooding functionality.

If message was not received before then message is reflooded and the contained handler is invoked. Otherwise the message is ignored.

format:

FLOOD sourceID messageID handlerID args ...

where

  • FLOOD is the ID of the FloodHandler
  • sourceID is the ID of the node which initiated the flood
  • messageID is a sequence number
  • handlerID is the ID of an embedded handler
  • args are parameters to the embedded handler.

The floodhandler keeps track of (8bit) sequence numbers per sourceID. The sequence number wraps around 255 back to zero. A message is considered to be new if its sequence number is not within the range of 128 values preceeding the last seen number.

ConfigurationHandler

The configuration handler, used to configure a node.

Used to install and replace addresses, subscriptions and services (state, event generators, event handlers) on the nodes.

The handlers: DefineAddress, DefineEvent and DefineHandler take code blocks as argument. These code blocks can be either of the following:

  • execution bytecode (for a simple interpreter)
  • configuration bytecode (device independent bytecode)
  • a parse-tree (for code generation, similar to config. bytecode)
  • binary code (e.g. an ELF file). Here, configuration bytecode is assumed.

The configuration handlers implements a high-level virtual machine for configuration. The following instructions are available:

DEFA

Define or update a content based address (CBA). Format:

DEFA addrID total length offset <code>

where

  • DEFA: ID of DEFA instruction (0)
  • addrID: ID of address to test
  • total: total size of address being defined (allows fragmentation)
  • length: size of bytecode payload (<code>)
  • offset: starting index of the bytecode payload in this fragment.
  • <code>: bytecode which defines the content based address predicate.

SERV

Create/select a service for configuration. Format:

SERV serviceID count

where

  • SERV: ID of SERV instruction (1)
  • serviceID: ID of the to be configured service.
  • count: number of parts which make up a full service definition state (0/1) + # event generators + # event handlers

The SERV instruction is used to select a service for modification. Any instructions which modify a service will operate on the service specified by the serviceID. If the specified service does not exist yet, it will be created.

Pending change for this instruction
SERV serviceID CSH CSL

where

  • SERV: ID of SERV instruction (1)
  • serviceID: ID of the to be configured service.
  • CSH: Higher order 8 bits of the checksum
  • CSL: Lower order 8 bits of the checksum

Once the service definition matches the checksum, it is final and can be enabled.

DEFG

Creates a new global service variable. Format:

DEFG varID varSize

where

  • DEFG: ID of the DEFG instruction (2)
  • varID: ID of the variable to be defined
  • varSize: Size of the variable. (0: int, 1+: array of size varSize)

DEFGI

Creates a new global service variable and initializes it. Format:

DEFGI varID length <values>

where

  • DEFGI: ID of the DEFGI instruction (3)
  • varID: ID of the variable to be defined
  • length: Size of the variable and # of values that follow (0: int (followed by 1 value), 1+: array of size length)
  • <values>: initial values for the variable/array

DEFE

Define or update an event generator. Format:

DEFE eventID total length offset <code>

where

  • DEFE: ID of the DEFE instruction (4)
  • eventID: ID of the event generator to be defined
  • total: total size of event being defined (for fragmentation)
  • length: size of bytecode payload (<code>)
  • offset: starting index of code segment in this fragment
  • <code>: bytecode which defines the condition and body of the event generator.

DEFH

Define or update an event handler. Format:

DEFH actionID total length offset <code>

where

  • DEFH: ID of the DEFH instruction (5)
  • actionID: ID of the handler to be defined
  • total: total size of event handler being defined
  • length: size of bytecode payload (<code>)
  • offset: starting index of scode segment in this fragment
  • <code>: bytecode which defined the body of the event handler.

INIT

Evaluates embedded bytecode. Format:

INIT length <code>

where

  • INIT: ID of the INIT instruction(7)
  • length: size of bytecode payload (<code>)
  • <code>: bytecode to execute

STATE

Allocates memory for state definition. Format:

STATE size

where

  • STATE: ID of the STATE instruction (8)
  • size: number of bytes to allocate

The STATE instruction must be inserted into each packet before the block of DEFG and/or DEFGI statements.