1.扇入与扇出
我们先前接触的配件PowerupAppC中的两条连接语句分别形成了两个一对一映射关系。如图3-1所示。
除了一对一映射,TinyOS程序中还存在其他的映射关系。
例如,配件CC2420TRansmitC组织了一个针对无线通信芯片CC2420的发送分组抽象。在该配件中,前两条连接语句将本配件的Init接口分别连接到了Alarm.Init和CC2420TransmitP.Init,形成了一个端点连接到两个不同端点的一对多映射关系。这是一种典型的多连接。在这种一对多连接模式下,只有一个调用点,即组件CC2420TransmitC的Init.init()。该调用点将扇出两个被调用者,分别是组件Alarm中的Init.init()和组件CC2420TransmitP中的Init.init()。这种端点间形成的一对多的映射关系称之为扇出(fan-out)。如图3-2所示。
图3-1 一对一映射关系
图3-2 一对多映射关系
扇出是一个组件通过某类型的接口与多个组件相连。与扇出相反,当多个不同组件通过某类型接口同时连接到同一组件上时,连接语句会在端点之间形成多对一的映射关系,这种情形称为扇入(fan-in)。如图3-3所示。
图3-3 多对一映射关系
例3.21:
接口StandCtrol中有两个命令:start()和stop()。下面的连接语句基于StandCrol接口形成了扇入。
扇入实际上就是多个调用者使用同一个被调用者(函数)。这种情形在C中也会遇到,比如在多处调用某个系统函数,因此不难理解。
我们前面介绍过分阶段接口,接口中有命令也有与命令相对应的完成事件,比如接口Send中的命令send()及相应的完成事件sendDone;再如接口SpiltControl中的命令start()(stop())及相应的完成事件startDone()(stopDone())。基于分阶段接口形成的连接,如果在某命令上形成的连接是扇入(多个调用者)的。那么,在通知接口事件时,就会形成扇出(即多个被调用者)。这一点与基于单阶段接口(如StandCtrol)形成的映射关系有所不同。
接口StandCtrol是单阶段的。基于该接口的服务或功能,在接口命令start(stop)返回时就会启动(停止)。上面例子中的语句在StandCtrol接口形成了扇入。相对的,基于SplitCtrol接口的服务,只有通知了startDone(stopDone)事件后才能保证启动(停止)。下面语句形成的连接下,不仅有启动命令也有完成事件,因此既形成了扇入也形成了扇出。
(www.xing528.com)
假如A或B中只有一个调用了SplitCrol.start(),当C通知SplitCrol.startDone事件时,由于A和B都连接到SplitCtrol接口上了,因此,组件A的SplitCtrol.startDone()和B的SplitCtrol.startDone()都会被调用,实现中不能确定A还是B调用了命令SplitCrol.start()。
推广开来,根据不同应用中的组件组织情况,导致组件之间的连接关系不是简单的一对一,而会存在n对k的映射关系。设n是接口的使用者,k是接口的提供者,任意一个接口的提供者通知事件都会调用n个使用者的事件处理程序,任意一个接口的使用者调用命令,都会调用k个提供者中的命令。
2.多连接带来的好处
多连接使得组件的一个接口函数实现是独立于它所依赖的组件的数目的。例如,组件MainC将启动顺序抽象为两个接口:SoftwareInit和Boot。在启动时,组件MainC调用SoftwareInit.init(),使得启动开始前能够初始化软件组件。在启动顺序结束时,MainC使用Boot.booted()通知节点已启动的事件。由于在一个实际应用中会有多个软件组件需要初始化,比如即便是非常简单的应用RadioCountToLeds都有10个软件组件需要初始化,所以一个合适的做法就是,将这些需要初始化的软件组件通过接口Init连接到MainC.SoftwareInit。这样仅需要在组件MainC中调用SoftwareInit一次,就可以基于扇出调用这些组件的初始化代码,以实现相应的初始化。
还有一种做法是在MainC组件中使用多个Init接口,将这些接口分别与那些软件组件连接上,然后再按照某个次序分别调用它们。显然,相对于扇出的做法,这种做法不仅麻烦而且容易出错。
3.组合函数
扇出带来一个有趣的问题:执行Call SoftwareInit.init(),实际上调用了多个不同的函数,那么返回值应该是什么呢?
nesC提供了组合函数来确定返回值。数据类型可以具有其相应的组合函数。扇出会调用N个函数,这N个函数具有相同类型的返回值,通过N-1次调用组合函数将这N个返回值组合为一个。nesC编译器编译应用时,自动生成使用了组合函数的扇出函数。例如,TinyOS下的错误类型error_t就是一个常用返回值类型,它的组合函数是ecombine。该组合函数定义入下:
如果e1和e2的值相同,ecommbine就返回那个值,否则返回FAIL。因此,如果组合了两个SUCCESS,组合函数返回值是SUCCESS,如果组合了两个EXXX,返回值是EXXX,。其他的组合返回FAIL。
使用nesC属性(后面将会介绍)将组合函数绑定到error_t。
例3.22:编译如下代码:
编译器生成如下代码:
组合函数应具有组合性和可交换性,从而确保扇出调用的返回值不会依赖执行命令(事件)的顺序。
有些返回值没有组合函数,或者是由于编程者的疏忽,或者是由于数据类型的语义。后一种情况的例子包括诸如数据指针等,如果两个调用都返回到某个分组的指针,没有办法能将其组合成为一个单一指针。如果程序中扇出的调用的返回值不能被组合,nesC编译器会生成一个没有组合或者没有组合函数的警告。编程者一定要重视这样的警告。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。