第三章 TinyOS编程语言nesC
第二节 组件定义
3.2 组件定义
每个组件都有自己的源文件,组件与其源文件名字之间是一一映射的。例如,文件LedC.nc包含组件LedsC的nesC代码,而组件PowerupC的nesC代码包含在文件PowerupC.nc中。组件名字位于全局范围,也就是说,每个组件只有一个定义,相应的,nesC编译器只加载一个名字为LedsC.nc的文件。
组件分为配件和模块两类。
module 模块名{ configuration 配件名{
//组件规范 //组件规范
} }
implementation{ implementation{
// 组件实现 // 组件实现
} }
无论模块还是配件,其定义都包括两个组成部分:规范与实现,如上所示。模块使用关键字module来声明。配件使用关键字configuration来声明。关键字module或configuration后面跟着模块或配件的名字。组件名字后加上“.nc”的扩展名即是存放该组件的源文件名字。组件名后面大括号中的语句描述了该组件的规范。关键字implementation后面大括号中的语句是该组件的实现代码。
TinyOS代码有一个非强制性的编码约定:组件名字皆以C或P结尾。C代表“Common”,即共有的,表明该组件是可以被其他组件自由使用的。P代表“Private”,即私有的,表明在该组件所在目录之外的组件不能使用它,但一般会在所在目录下有一个以C结尾的组件来封装它,以便其他组件使用。例如,对于组件LedsC,任意组件都可以使用它;相对的,其组件LedsP只有与同目录下的文件才能使用它。LedsP被LedsC封装后,便可以供所有其他组件使用。
模块和配件的规范部分具有相同的语法,可以声明一个或多个接口,也可以为空(不声明接口)。模块与配件的主要区别在于实现部分。模块实现部分由类似C的nesC代码组成。模块代码包括声明变量和函数以及调用函数等。配件实现部分由nesC连接(wiring)代码组成。这些连接代码将组件组织起来。这是一种新的编程模式,也使得配件成为nesC与C的主要区别。将模块和配件区别开来的主要原因在于允许用户在系统现有实现代码基础上构建自己的应用程序。比如,通过配件把一个或多个组件封装起来,其中没有一个是自己设计的。其他开发者也可提供一组新的组件库,供应用程序开发者使用。读者将在nesC编程实践的过程中逐渐理解这一点。
模块与配件的实现分别在3.4节与3.5节中着重介绍。本节主要介绍组件的规范部分。
3.2.1 组件规范
大多数情况下,组件需要声明自己提供的服务(或功能)和使用的服务(或功能)。这些通过在组件规范中声明提供接口(provides interface)或使用接口(uses interface)的nesC语句来实现。
使用关键字provides声明组件提供的接口,表明组件能够提供的服务。
使用关键字uses声明组件使用的接口,表明组件需要使用的服务。
接口是为某个服务或抽象声明一组相关的函数。例如,为了控制节点上的二极管(LED),定义接口Leds来声明关闭、打开二极管等相关函数;为了通知应用节点已经启动起来,定义接口Boot来声明一个通知函数(booted)。接口相关说明后面将会详述。
例3.3:
1): module PowerupC{
uses interface Boot;
uses interface Leds;
}
2): configuration Leds{
provides interface Leds;
}
在例3.3中,模块PowerupC的规范中包含了2条语句,关键字uses表明接口Boot和接口Leds是两个使用的接口。PowerupC使用Boot接口来接收节点启动的通知事件,使用Leds接口来打开节点的二极管。相对的,配件LedsC提供了一个接口Leds,用于控制3个二极管的抽象。
组件规范中可以既提供接口也使用接口。在例3.4中,配件MainC的规范部分提供接口Boot,使用接口Init。配件MainC实现了节点的启动顺序。节点启动顺序中的一项主要工作是MainC使用Init接口来初始化软件。例如某模块中有一个状态需要在系统启动前进行初始化,那么该模块需要提供Init接口。当节点启动起来时,MainC通过Boot接口中的事件booted通知其他组件,如模块PowerupC。