DSN New User Programming
Tutorial
June 2007
What
are
Built-ins?
In order to link declarative programs to
hardware such as sensors and actuators, users may specify built-in
predicates. For example, the thermometer
predicate may read values from the underlying temperature
sensor: ! builtin(temperature, TemperatureImplementor).
someOtherPredicate(@Node,Temp) :− temperature(@Node, Temp).
Here ThermometerImplementor
is an
external module written in nesC implementing
the thermometer predicate. Actuation
is exposed similarly:
! builtin(sounder, SounderImplementor).
sounder(@Node, frequency) :− process(@Node, Object).
Here, sounder tuples result in sound
as implemented by the
SounderImplementor
.
Built-ins
also serve other important purposes permit one to easily adopt packaged
functionality. Substantial system services can be incorporated into
declarative programs through the use of the built-in mechanism.
The
Built-in Interface
Implementing a new built-in
requires implementing a handful of nesC interfaces. The
following is the full listing of provides and uses interfaces needed by
a built-in:
configuration MyBuiltinTable {
provides {
interface StdControl;
interface TupleAccessorMutator as Tam;
interface PushSync as LocalPush[Opnum_t operation_id];
interface PullSync as LocalPull;
interface PullSync as FreshPull;
interface PushSync as RadioPush;
interface PullSync as RadioPull;
interface PullSync as UartPull;
interface SystemStateNotify as NotifyFresh;
interface SystemStateNotify as NotifyRadio;
interface SystemStateNotify as NotifyUart;
} uses {
interface ParamStore as getParams;
interface GtupleAccessorMutator as Gam;
interface GenericFunctionCall;
interface Leds;
}
}
implementation {
...
}
It
can be quite overwhelming at first! Fortunately, many of the
interfaces are similar with only slight variations. These
break down into the following categories:- Local
reads and writes:
LocalPush, LocalPull, FreshPull,
NotifyFresh
- Radio reads and writes:
RadioPush,
RadioPull, NotifyRadio
- Uart reads and
writes:
UartPull, NotifyUart
- Support
functionality:
StdControl, Tam, getParams, Gam, GenericFunctionCall, Leds
The
first three categories are all variants of Push/Pull interfaces:
PushSync and PullSync; as well as a helper notification interface:
SystemStateNotify. Reading tuples corresponds to pulling tuples
out of the builtin whereas
writing tuples corresponds to pushing tuples into the builtin.
The different instantations of these interfaces are only in what
tuples are pulled or pushed or what the source of the pulling or
pushing is:
LocalPush
: The source of the pushed tuple is a local rule derivation.LocalPull
: The destination of the pulled tuple is a local rule. FreshPull
: The
destination of the pulled tuple is a local rule. FreshPull
should tuples that are intuitively fresh and have not been
processed before. More specifically, FreshPull tuples initiate
the execution of rule bodies in which they appear, whereas LocalPull
tuples participate in rule execution by joining fresh tuples.NotifyFresh
:
A simple signaling mechanism that informs the runtime daemon whenever
there is a new fresh tuple availble ready for pulling with
FreshPull.RadioPush
: The source of the pushed tuple is the network.
RadioPull
: The destination of the pushed tuple is the network.
NotifyRadio
: A signaling mechanism informing the runtime daemon that there are tuples ready for pulling with RadioPull.UartPull
:
The destination of the pushed tuples is the UART interface. This
interface is used only for diagnostic purposes and can safely be
made a null operation.NotifyUart
: A signaling mechanism informing the runtime daemon that there are tuples ready for pulling with UartPull.
While
there are quite a few interfaces, it is not necessary to implement them
all. In fact, those that are not implemented can be simply wired
to NoopC.nc. The radio interfaces are rarely implemented: only do
so if you're own builtin is to send and receive itself. The uart
interfaces are also only necessary for diagnostic purposes and hence
are optional. The main interfaces of concern are typically the
four local interfaces (Local and Fresh). Please see the examples
of builtins included in the DSN source tree at nesc/extras/builtins
.
Please
note that the LocalPush interface must implement the database
projection functionality. DSN uses an table in-place project
rather than a general project in order to avoid implementation of a
general allocator for projections.
In addition to the push and pull interfaces, several additional support functions are required:
StdControl
: Setup any boot initializations.Tam
: The Tuple Accessor Mutator provides methods to access fields of this implemented builtin type of tuple.Gam
: The General Tuple Accessor Mutator provides methods to access the fields of any general tuple in the DSN system.getParams
: Retrieve parameters for a particular operation id. This is most useful when determining the projection attributes.GenericFunctionCall
: Call user-defined functions.
Leds
: LEDs for diagnostics.
While there are many parts to implement, there are also several examples in the DSN source tree at at nesc/extras/builtins
from which usually many parts can be borrowed liberally.
Built-in Functions
Functions
are also extensible in DSN with the built-in mechanism. The
declaration of functions in snlog is similar to that of builtin
predicates above. There is only one interface necessary for
functions:
configuration MyFunc {
provides {
interface GenericFunctionCall;
}
}
implementation {
...
}
As
functions are similar to stateless tables, the task of implementing a
function is much simpler. GenericFunctionCall receives a list of
arguments and the implementation can processes these and return any
result as output. Examples of functions are available in the DSN
source tree at nesc/extras/funcs
.