Dear All:

Here I propose an idea of porting TinyOS to ANSI C/C++.

1. Objectives

TinyOS uses nesC compiler to implement its unique Event-Driven
Component Model. I must say it's designed and implemented very well,
considering that TinyOS is already the de facto standard in WSN, the
great community, the huge, commercially-proved, high quality code
base, as well as the rich and excellent documentation. But at least
these things are big obstacles for the adoption of TinyOS in the
industry: it's proprietary compiler, tool chain, and learning curve of
nesC programming.

I understand programming on unix/linux is not a die-hard job for geeks
and nerds nowadays. But most programmers of embedded systems are still
working on windows platform, using commercially available, high
quality development tools from different vendors worldwide. I
personally use IAR IDE in most projects and occasionally Code Composer
if other members in the team preferred. Migrating to
nesC/gcc-tool/linux platform will be considered a huge potential risk
in many commercial case, in terms of the tool quality, documentation
quality, the availabity and cost of experienced programmer, as well as
the long-term support of the platform.

In short, I believe many programmers have been keeping eyes on TinyOS
for a long time and dare not to use it in commercial projects. If
there is an ANSI C/C++ implementation, TinyOS will be populated much
faster and much wider in the industry, especially considering "the
internet of the things" is an extremely hot buzz in recent days.

2. Key Concept

I have come to know TinyOS for quite a few years, investing a lot of
time on reading the document and learing to program on some
well-supported platform such as telosb. Until recent days, after a
detailed comparison of nesC and ANSI C/C++, I finally understand the
key concept of TinyOS component model and nesC implementation.
Everything is based on a 'static' assumption, in terms of both
component model, interface concept, and their implementation. There is
no concept equivalent of TinyOS interface, module, configuration in
ANSI C/C++, and this is probably the most important reason for
TinyOS/nesC developers deciding to build their own compiler.

Meanwhile I do complain that this key concept is not well-presented in
TinyOS documentation. The TinyOS programming guide starts with the
discussion of namespace, asynchronous calls, interfaces etc. The
long-time C/C++ programmers will inevitably compare these concepts
with their counterparts in OOP/C++ domain, such as class,
composition/delegation, inheritance, virtual functions, abstract base
class, polymorphism etc. But those understanding or metaphor will be
totally misleading since all implementation of those concepts are
based on 'dynamic' or 'run-time' technique in modern programming
languages. But TinyOS/nesC solely depends on 'static' technique,
something like the technique widely used in Standard Template Library
in mid-1990s. In terms of 'static', there is no concept of 'instance'
or 'objects'. The counterpart of 'object' is struct or class, not the
their instance. The different instance of class at compile time means
difference class/struct type, or the (differnent)
specialization/instantian of template class. I highly recommend TinyOS
developers to clarify this concept in the very beginning of
programming guide or tutorials. It will greatly help programmers to
understand the core design and implementation model of TinyOS/nesC.

3. Using 'static template' in C++ to implement TinyOS Component Model

The most challenging part of implementing TinyOS component-model in
ANSI C/C++ is the implementation of its core concept: interfaces and
modules. The interface means something you can use inside a module
even when its definition is not defined. The module can provide and
use one interface, multiple different interfaces and event multiple
instance of the same interface - they are like the class/object
composition in C++, but definitely not the base class/interface/mixin
inherited and implemented by a class.

The basic idea of implementing such static component model is to
utilize the C++ template technique. That does not neccessarily mean C
can not fulfil the job. We won't use any poweful 'dynamic' technique
in C++ but the template support is only provided by C++ compiler, not
C.

In C++ template implementation, the interface is presented in (nested)
Class/Struct definition (declaration) inside a container class (module
or configuration in TinyOS terminolgy). The multiple interface struct
inside a container is possible. The multiple instance of the same
interface type is also possible by utilizing template
parameter/arguments. The implementation of those interfaces is then
the instantiation/specialization of class templates. C++ support
partial specialization of class template, which is a great feature to
implement polymorphism (generic module/configuration).

All class would be declared as template class, event if they are
singleton ('not generic' module/configuration in Tinyos). This will
eliminate the declaration order dependencies required by the compiler.
Before the main() function starts, all header files are included and
all required struct/function are defined. These feature better
seperate the interface/implementation. Considering most interfaces
defined in TinyOS are bi-directional, they signal events, which means
a declared and called class function is not implemented inside the
module file. Only template class allows this behavior, and you could
easily define/specialize such function in another module or
configuration file, anywhere.

here is a brief comparison of TinyOS module/configuration/interface
concept with their standard C++ template implementation.

interface -> a struct with a group of  STATIC functions. since the
same interface could be used many times in different
modules/configurations. There must be a way to identify/quality the
single interface, here we use the struct name. it's convenient.
module -> a module is a struct with many nested struct (interface).
some of them are provided, i.e. public, and some of them are used
inside, i.e. private. Using class/public/private keyword will help to
avoid unintended call to wrong interface.
module implementation ->  provide the implementation/specialization of
all 'command' methods on all provided interface struct, as well as all
'event' methods on all used interface struct. Use undefined method on
used interface or signal event call on provided interface is OK, just
as the TinyOS modules do. Variable members is possbile, but they
should be declared inside the module body as 'static', and declared
outside the module struct block (i.e. globally) another time to avoid
compiler complaints and link error.
configuration -> configuration is a special header file dedicated to
wired existing module and provide some interface. Therefore, it
declare interface struct as nested struct but do NOT declare the
module it used, instead it including those module file. A TinyOS
'wire' means define/instantiate interface functions or specialize
interface class, using the interface method provided by modules.
parameterized module/interface -> template parameters
generic module/configuration -> template parameters

I understand TinyOS supports 1-to-many wire mechanism - fan out. It
has no direct counterpart in C++ template, but we could have some
workaround solutions such as Adapter/Bridge pattern. This feature is
not much used and is not a deeply rooted feature or concept inside
TinyOS/nesC component model. It's just a convenient feature.

Here is a piece of demo code:

template<typename T=void>
struct dare
{
       // assume we provide this interface
        struct interfaceA : _Msp430Timer<interfaceA>
        {
                static uint16_t get();
        };
        
       // assume we use this interface
        struct interfaceB : _Msp430Timer<interfaceB>
        {
                static uint16_t get();
        };

        static uint16_t m_dare;
};

template<typename T> uint16_t dare<T>::m_dare;

template<T>
uint16_t dare<T>::interfaceB::get()
{
        return dare<T>::m_dare;
};

template<> uint16_t dare<>::interfaceA::get()
{
        return dare<>::interfaceB::get();
}

int main( void )
{
  App<>::interfaceX::boot();
}

The code is simple and straightforward enough to demonstrate the idea.
Assuming the interfaceA is to be provided and interfaceB is to be
used. We can easily call methods on interfaceB and provide
implementation to interfaceA. In fact their are no fundamental
different between the interface used and provided. Parameterized or
Generic module is very easy to implement by adding new parameters into
template parameter list. It is not neccessary to inherit the base
interface class (_Msp430Timer) in nested pattern, but this pattern
provides polymorphism by Curious Recurring Template Pattern, may be
useful in some case. All interface struct are uniquely
qualified/identified by container class name + interface class name.

In configuration file, their is a little difference, You do not
declared the module used as TinyOS do. You just include the module
file. Maybe the programmer should write some comments to clarify the
usage.

When using a generic module/configuration, you including the file,
partially specialize it by providing values in template parameter
list. Thanks to C++ template standard to provide this feature. Then
you can call the functions or provide implementations to interface
call of such (partially specialized) generic module/configuration
class. Just the same as the above code.

4. Comparison

Besides the benefit of working on ANSI C/C++, I also preferred the
template implementation. Though it's complex in the presentation of
code, it's simpler in the techinique and pattern used. The only thing
you need to understand is the basic 'static' idea, quite simple if you
are familiar with C++ templates. No need to understand of a bunch of
TinyOS jargons, modules, interfaces, configuration, parameterized,
generic, wire, etc. They are just template classes with parameters.
And inside the module/configuration, the only decision to be made is
to forward a call to another one, or provide a concrete
specialization/instantiation to it. Very simple and easy to understand
and use.

In template method, simply forwarding a call or an interface required
a lot of code. I am trying to define some useful macros to make life
easier.

nesC also provide some good checking feature, such as sync/async
keywords, unique_count, combine etc. But they are just 'functions',
not the fundamental concept of component model. Without those,
programmers just live more 'carefully'. This seems to be OK at least
for me.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

My teams has two programmers and we are planning to port some
fundamental parts of TinyOS to C++ using the techniques/patterns
mentioned above. We are very glad to hear some advices or some
potential important pitfalls before we begin. Any discussion, advices,
criticism, or comments would be greatly appreciated.

Tianfu Ma (UGlee)
[email protected]
[email protected]
_______________________________________________
Tinyos-help mailing list
[email protected]
https://www.millennium.berkeley.edu/cgi-bin/mailman/listinfo/tinyos-help

Reply via email to