分享

TEP116 包协议簇组件中使用的接口

 幸福的乐土 2012-03-31

目录

[隐藏]

Abstract 摘要

此文档描述了TinyOS 2.x包协议簇组件中使用的接口,还有基本数据链路HTL组件ActiveMessageC的结构和实现。同时也描述了虚拟active message接口AMSenderC,AMReceiverC。

The memo documents the interfaces used by packet protocol components in TinyOS 2.x as well as the structure and implementation of ActiveMessageC, the basic data-link HIL component. It also documents the virtualized active message interfaces AMSenderC and AMReceiverC.

Introduction 介绍

传感节点是以网络为中心的设备。运行在其上的软件的复杂性来源于网络协议和它们之间的交互。TinyOS中,最基本的网络抽象是一个单跳、不可靠的“active message”包,它有一个目的地址,提供同步化的ack,可变长直到预定义的最大长度。同时也拥有一个类型字段,这个字端用来标志它所负载的是哪种上层协议。

Sensor nodes are network-centric devices. Much of their software complexity comes from network protocols and their interactions. In TinyOS, the basic network abstraction is an *active message*, a single-hop, unreliable packet. Active messages have a destination address, provide synchronous acknowledgements, and can be of variable length up to a fixed maximum size. They also have a type field, which is essentially a protocol identifier for components built on top of this abstraction.

在TinyOS 1.x中,GenericComm提供了收发active message的接口::

In TinyOS 1.x, the component GenericComm provides interfaces for transmitting and receiving active messages::

 configuration GenericComm {
   provides {
     interface StdControl as Control;
     interface SendMsg[uint8_t id];
     interface ReceiveMsg[uint8_t id];
     command uint16_t activity();
   }
   uses {
     event result_t sendDone();
   }
 }

这个组件很简单,但是存在一些问题。第一,它有一个activity()命令,这个命令在整个TinyOS树中不止一个调用者。这个命令要求GenericComm为其分配一个定时器,从而浪费了CPU时间和RAM。

This component, while simple, has several issues. First, it has the activity() commmand, which does not have a single caller in the entire TinyOS tree. This command requires GenericComm to allocate a timer, wasting CPU cycles and RAM.

第二,除了它发往的目的节点,它不允许其他节点接收这个包。一些网络协议(例如:MintRoute [1]_, TAG [2]_),为了提高网络性能会监听这些包。这个问题导致了GenericCommPromiscuous组件的产生,它提供的Receive接口并不区别它接收到的包是发送给它还是发送给其他节点的,如何选择这2个实现中的一个充斥着整个应用实现。有一个方法能让接收者同时为不同的协议实现接收语义,但这要求实现一个默认的事件句柄。

Second, it does not allow a node to receive packets besides those destined to it. Several network protocols (e.g., MintRoute [1]_, TAG [2]_) take advantage of snooping on these packets for a variety of improvements in efficiency or performance. This has led to the creation of GenericCommPromiscuous, whose Receive interface does not distinguish between packets received that were addressed to the node and packets received that were addressed to other nodes. Choosing one of the two implementations is a global decision across an application. There is a way to enable both reception semantics at the same time for a different protocols, but they require a creative use of default event handlers.

第三,它假设组件能直接存取包结构。然后,直接存取包结构会带来无法预知的依赖:一个组件为一个头部字段命名,将自己绑定到拥有此命名字段的数据链路层。类似的,在GenericComm之上的组件直接存取包的数据负载。

Third, it assumes that components will directly access the packet structure, the accepted approach in TinyOS 1.x. However, directly accessing packet structures introduces unforseen dependencies: a component that names a header field, for example, binds itself to data link layers that have a field with that name. Similarly, components on top of GenericComm directly access the data payload of a packet.

TEP 111 描述了TinyOS 2.x包缓冲区[3]_。本TEP描述用于访问这些包缓冲区的接口,还有作为基本数据链路层包通信的HIL——ActiveMessageC。

TEP 111 documents the structure of a TinyOS 2.x packet buffer [3]_. This TEP documents the interfaces used to access packet buffers, as well as ActiveMessageC, the basic data-link packet communication HIL.

Communication interfaces 通信接口

包级别的通信有3种接口。“Packet”接口存取各消息字段和数据负载。“Send”接口传输包,可以由其寻址模式来区别。“Receive”接口负载处理包的接收事件。最后,不同的协议有一个不同的分派标志符字段,Receive和Send接口可能会被参数化以便能支持不同的高层协议。

Packet-level communication has three basic classes of interfaces.

  • Packet* interfaces are for accessing message fields and payloads.
  • Send* interfaces are for transmitting packets, and are

distinguished by their addressing scheme. The *Receive* interface is for handling packet reception events. Finally, depending on whether the protocol has a dispatch identifier field, the Receive and Send interfaces may be parameterized in order to support multiple higher-level clients.

Packet interfaces 包接口

TinyOS 2.x 中基本的消息缓冲区类型是message_t,在TEP 111中有详述。message_t向右调整数据链路层包头,因此上层组件能在不同的数据链路层之间传输这些包,而不用移动数据负载字段。这就意味着一个数据链路帧的数据负载区域总是在message_t类型中的某个固定偏移位置。

The basic TinyOS 2.x message buffer type is message_t, which is described in TEP 111. message_t right-justifies data-link headers to the data payload so that higher-level components can pass buffers between different data link layers without having to move data payloads. This means that the data payload of a data link frame is always at a fixed offset of a message_t.


Once protocols layer on top of each other, the data payload for components on top of the data link layer are no longer at a fixed offset. Where a component can put its header or data depends on what headers underlying components introduce. Therefore, in order to be able to find out where it can put its data, it must query the components below it. The Packet interface defines this mechanism::

 interface Packet {
   command void clear(message_t* msg);
   command uint8_t payloadLength(message_t* msg);
   command void setPayLoadLength(message_t* msg, uint8_t len);
   command uint8_t maxPayloadLength();
   command void* getPayload(message_t* msg, uint8_t len);
 }

组件能通过调用Packet的getPayload()来获得指向数据负载区域的指针,调用这个命令需要包括一个指定数据长度的len长度参数。maxPayloadLength将返回数据负载区域的最大长度。当len参数大于maxPayloadLength返回的值时,getPayload必须返回一个NULL指针。

A component can obtain a pointer to its data region within a packet by calling ``getPayload()``. A call to this command includes the length the caller requires. The command ``maxPayloadLength`` returns the maximum length the payload can be: if the ``len`` parameter to ``getPayload`` is greater than the value ``maxPayloadLength`` would return, ``getPayload`` MUST return NULL.

组件能用setPayLoadLength设置其负载长度,用payloadLength得到使用中的数据负载区域的长度。因为Send接口总是在其发送调用中包含一个长度参数,在发送中不需要调用"setPayLoadLength",所以这个命令通常不会被使用。相反,在队列或者其他缓冲区组件中,它用于存储一个包的状态,不需要其他额外的内存分配。

A component can set the payload length with ``setPayLoadLength.`` A component can obtain the size of the data region of packet in use with a call to ``payloadLength``. As Send interfaces always include a length parameter in their send call, ``setPayLoadLength`` is not required for sending, and so is never called in common use cases. Instead, it is a way for queues and other packet buffering components to store the full state of a packet without requiring additional memory allocation.

payloadLength和maxPayloadLength的区别在于包是正在被接收还是正在被发送在接受的情况中,判断已有的包数据负载区域的大小的必要的;在发送的情况中,组件需要知道有多少数据能放入包缓冲区中。payloadLength的返回值必须小于或者等于maxPayloadLength的返回值。

The distinction between ``payloadLength`` and ``maxPayloadLength`` comes from whether the packet is being received or sent. In the receive case, determining the size of the existing data payload is needed; in the send case, a component needs to know how much data it can put in the packet. By definition, the return value of ``payloadLength`` must be less than or equal to the return value of ``maxPayloadLength``.

(不知道此段在说什么@#¥@#¥@%)

The Packet interface assumes that headers have a fixed size. It is difficult to return a pointer into the data region when its position will only be known once the header values are bound.

(下面2段貌似和Interface Packet的代码注释有误,待查证后再翻译)

clear命令为低层协议栈清除所有头部,尾部和元数据。例如,在形如CollectionSenderC[4]_的路由组件中调用clear命令,将会清除所有的collection头部和尾部。CollectionSenderC会在下层递归地调用clear命令,清除链路层头部和尾部。特别是在不同的链路层之间传输包时,clear命令是非常必要的。否则目的链路层可能会错误地解释源链路层的元数据,如将数据包用错误的RF频段发送出去之类的。因为clear命令是为特定的一个链路层处理包,此例中正确的代码应该是在目的链路层调用此命令,而非源链路层调用。

The ``clear`` command clears out all headers, footers, and metadata for lower layers. For example, calling ``clear`` on a routing component, such as CollectionSenderC[4]_, will clear out the collection headers and footers. Furthermore, CollectionSenderC will recursively call ``clear`` on the layer below it, clearing out the link layer headers and footers. Calling ``clear`` is typically necessary when moving a packet across two link layers. Otherwise, the destination link layer may incorrectly interpret metadata from the source link layer, and, for example, transmit the packet on the wrong RF channel. Because ``clear`` prepares a packet for a particular link layer, in this example correct code would call the command on the destination link layer, not the source link layer.


Typically, an incoming call to the Packet interface of a protocol has an accompanying outgoing call to the Packet interface of the component below it. The one exception to this is the data link layer. For example, if there is a network that introduces 16-bit sequence numbers to packets, it might look like this::

 generic module SequenceNumber {
   provides interface Packet;
   uses interface Packet as SubPacket;
 }
 implementation {
   typedef nx_struct seq_header {
     nx_uint16_t seqNo;
   } seq_header_t;
   enum {
     SEQNO_OFFSET = sizeof(seq_header_t),
   };
 
   command void Packet.clear(message_t* msg) {
     void* payload = call SubPacket.getPayload(msg, call SubPacket.maxPayloadLength());
     call SubPacket.clear();
     if (payload != NULL) {
       memset(payload, sizeof(seq_header_t), 0);
     }
   }
 
   command uint8_t Packet.payloadLength(message_t* msg) {
     return SubPacket.payloadLength(msg) - SEQNO_OFFSET;
   }
   
   command void Packet.setPayloadLength(message_t* msg, uint8_t len) {
     SubPacket.setPayloadLength(msg, len + SEQNO_OFFSET);
   }
 
   command uint8_t Packet.maxPayloadLength() {
     return SubPacket.maxPayloadLength(msg) - SEQNO_OFFSET;
   }
 
   command void* Packet.getPayload(message_t* msg, uint8_t len) {
     uint8_t* payload = call SubPacket.getPayload(msg, len + SEQNO_OFFSET);
     if (payload != NULL) {       
       payload += SEQNO_OFFSET;
     }
     return payload;
   } 
 }


The above example is incomplete: it does not include the code for the send path that increments sequence numbers.

实际上,即便是要经过很多组件才能达到数据链路层组件,调用Packet接口命令也是十分有效率的。nesC的内联机制意味着不会有任何的函数调用,因为数据负载的偏移量和长度计算都使用了常量值的偏移位置,所有编译器会将这些调用都转换成一个定长偏移寻址

In practice, calls to Packet are very efficient even if they pass through many components before reaching the data link layer. nesC's inlining means that in almost all cases there will not actually be any function calls, and since payload position and length calculations all use constant offsets, the compiler generally uses constant folding to generate a fixed offset.

包接口提供所有包层协议都会提供的访问数据负载区域的方法。通信层能为包附加额外的头部和尾部,这种操作必须通过接口来完成。接口应该要使用形如“XPacket”的名字,“X”就是能描述该层的名字。例如,active message组件同时提供Packet和AMPacket接口。AMPacket有如下签名:

The Packet interface provides access to the one field all packet layers have, the data payload. Communication layers can add additional header and footer fields, and may need to provide access to these fields. If a packet communication component provides access to header and/or footer fields, it MUST do so through an interface. The interface SHOULD have a name of the form *XPacket*, where *X* is a name that describes the communication layer. For example, active message components provide both the Packet interface and the AMPacket interface. The latter has this signature::

 interface AMPacket {
   command am_addr_t address();
   command am_addr_t destination(message_t* amsg);
   command am_addr_t source(message_t* amsg);
   command void setDestination(message_t* amsg, am_addr_t addr);
   command void setSource(message_t* amsg, am_addr_t addr);
   command bool isForMe(message_t* amsg);
   command am_id_t type(message_t* amsg);
   command void setType(message_t* amsg, am_id_t t);
   command am_group_t group(message_t* amsg);
   command void setGroup(message_t* amsg, am_group_t grp);
   command am_group_t localGroup();
 }

address命令返回节点的本地AM地址。AMPacket为自身的4个字段提供访问器:destination,source, type and group。它同时提供对这四个字段赋值的set命令。同样的理由,Packet接口应该为别的组件提供设置其负载长度的方法。Packet接口应该为所有的字段提供访问和修改器,使得队列和其他组件能够将值存储在包缓冲区中。特别是当一个组件需要将值放入私有包缓冲区内部而将元数据放入message_t的元数据字段时。

The command address() returns the local AM address of the node. AMPacket provides accessors for its four fields, destination, source, type and group. It also provides commands to set these fields, for the same reason that Packet allows a caller to set the payload length. Packet interfaces SHOULD provide accessors and mutators for all of their fields to enable queues and other buffering to store values in a packet buffer. Typically, a component stores these values in the packet buffer itself (where the field is), but when necessary it may use the metadata region of message_t or other locations.

group字段是指AM group,一个逻辑上的网络标识符。链路层只在包的AM group与节点自己的group(localGroup返回)匹配时才会触发一个接收事件。

The group field refers to the AM group, a logical network identifier. Link layers will typically only signal reception for packets whose AM group matches the node's, which ``localGroup`` returns.

Sending interfaces 发送接口

不同的地址模式有不同的发送接口。例如,collection routing等无地址协议提供一个基本的“Send”接口。Active message通信定义了一个AM地址,因此它提供“AMSend”接口。下面是一个基本的,无地址的Send接口::

There are multiple sending interfaces, corresponding to different addressing modes. For example, address-free protocols, such as collection routing, provide the basic ``Send`` interface. Active message communication has a destination of an AM address, so it provides the ``AMSend`` interface. This, for example, is the basic, address-free Send interface::

 interface Send {
   command error_t send(message_t* msg, uint8_t len);
   command error_t cancel(message_t* msg);
   event void sendDone(message_t* msg, error_t error);  
   command uint8_t maxPayloadLength();
   command void* getPayload(message_t* msg, uint8_t len);
 }

下面是AMSend接口:: while this is the AMSend interface::

 interface AMSend {
   command error_t send(am_addr_t addr, message_t* msg, uint8_t len);
   command error_t cancel(message_t* msg);
   event void sendDone(message_t* msg, error_t error);
   command uint8_t maxPayloadLength();
   command void* getPayload(message_t* msg, uint8_t len); 
 }

Send接口必须包含这4个命令和一个事件。maxPayloadLength和getPayload与Packet.maxPayloadLength和Packet.getPayload是功能一至的,这种包含关系使得组件不用再同时连线到Packet和Send接口。

Sending interfaces MUST include these four commands and one event. The duplication of some of the commands in Packet is solely for ease of use: ``maxPayloadLength`` and ``getPayload`` MUST behave identically as ``Packet.maxPayloadLength`` and ``Packet.getPayload.`` Their inclusion is so that components do not have to wire to both Packet and the sending interface for basic use cases.

当调用send时,参数len的值大于最大传输单元(MTU)时,send命令将会返回ESIZE。

When called with a length that is too long for the underlying maximum transfer unit (MTU), the send command MUST return ESIZE.

Send和AMSend有一个明显的深度为1的队列。当前一个send调用返回SUCCESS然后sendDone事件还没有被触发时,调用这2个接口的send命令必须返回EBUSY。(send调用返回SUCCESS只说明成功把数据传给协议的下层组件,此时正在占用信道发送数据;sendDone事件被触发后才说明成功的把数据发送出去了)

The ``Send`` and ``AMSend`` interfaces have an explicit queue of depth one. A call to ``send`` on either of these interfaces MUST return EBUSY if a prior call to ``send`` returned SUCCESS but no ``sendDone`` event has been signaled yet. More explicitly::

 if (call Send.send(...) == SUCCESS &&
     call Send.send(...) == SUCCESS) {
    // This block is unreachable.
 }

需要使用发送队列的系统有2个选择。一,可以使用QueueC(在tos/system中)来存储指向包的指针,将其序列化到发送接口上。二,可以引入一个支持多未决发送任务的发送接口。

Systems that need send queues have two options. They can use a QueueC (found in tos/system) to store pending packet pointers and serialize them onto sending interface, or they can introduce a new sending interface that supports multiple pending transmissions.

cancel命令允许发送者取消当前发送任务。当前没有任何未决sendDone事件时,调用cancel必须返回FAIL。如果当前有未决sendDone事件,那么cancel返回SUCCESS,此时协议层一定不要传输此包,并用ECANCEL错误代码来通知sendDone事件。如果有一个未决的sendDone事件,并且cancel返回FAIL,此时如果cancel没有被调用那么sendDone事件必须发生。

The cancel command allows a sender to cancel the current transmission. A call to cancel when there is no pending sendDone event MUST return FAIL. If there is a pending sendDone event and the cancel returns SUCCESS, then the packet layer MUST NOT transmit the packet and MUST signal sendDone with ECANCEL as its error code. If there is a pending sendDone event and cancel returns FAIL, then sendDone MUST occur as if the cancel was not called.

Receive interface 接收接口

Receive是接收包的interface。它有如下签名::

Receive is the interface for receiving packets. It has this signature::

 interface Receive {
   event message_t* receive(message_t* msg, void* payload, uint8_t len);
 }

receive事件的payload参数必须和Packet.getPayload()返回的一致,len参数必须和Packet.getPayloadLength()所返回的一致。这些参数是一种约定,它们将会被receive句柄所使用,它们的出现避免了调用getPayload。不同于Send,Receive并没有一个传统的getPayload调用。Receive只有一个事件,Receive用户能被多次连线。

The ``receive()`` event's ``payload`` parameter MUST be identical to what a call to the corresponding ``Packet.getPayload()`` would return, and the ``len`` parameter MUST be identical to the length that a call to ``Packet.getPayload`` would return. These parameters are for convenience, as they are commonly used by receive handlers, and their presence removes the need for a call to ``getPayload()``. Unlike Send, Receive does not have a convenience ``getPayload`` call, because doing so prevents fan-in. As Receive has only a single event, users of Receive can be wired multiple times.

Receive有一个“缓冲区交换”机制。事件句柄必须返回一个指向可用缓冲区的指针。这样给上层和下层包层带来了平衡。如果上层并没有立即处理到达的包,它也能给下层立即返回一个可用的缓冲区。这个缓冲区由参数"msg"传递给下层。它只是将缓冲区返回,而不对其做任何处理。遵循这个机制意味着如果数据接收速率大于节点的情况下,上层组件会与下层组件分离,也许会出现丢弃包不处理的情况,但是不会出现阻止其他组件接收包。如果上层不立即返回一个缓冲区,那么当上层处理包不够快时,底层就会出现饿死情况,可能会阻止包的接收。

Receive has a *buffer-swap* policy. The handler of the event MUST return a pointer to a valid message buffer for the signaler to use. This approach enforces an equilibrium between upper and lower packet layers. If an upper layer cannot handle packets as quickly as they are arriving, it still has to return a valid buffer to the lower layer. This buffer could be the ``msg`` parameter passed to it: it just returns the buffer it was given without looking at it. Following this policy means that a data-rate mismatch in an upper-level component will be isolated to that component. It will drop packets, but it will not prevent other components from receiving packets. If an upper layer did not have to return a buffer immediately, then when an upper layer cannot handle packets quickly enough it will end up holding all of them, starving lower layers and possibly preventing packet reception.

使用Receive接口的用户在其处理receive事件时有三个基本选项:

 1) 不做任何处理,返回"msg"
 2) 将某些数据拷贝出"payload"字段,然后返回"msg"
 3) 将msg存储在本地帧中,返回一个指向不同内存区域的“message_t”指针给下层协议使用。

A *user* of the Receive interface has three basic options when it handles a receive event:

 1) Return ``msg`` without touching it.
 2) Copy some data out of ``payload`` and return ``msg``.
 3) Store ``msg`` in its local frame and return a different ``message_t*`` for the lower layer to use.

下面有对应这三种情况的三个简单例子::

These are simple code examples of the three cases::

 // Case 1
 message_t* Receive.receive(message_t* msg, void* payload, uint8_t len) {
   return msg; 
 }  
 // Case 2
 uint16_t value;
 message_t* Receive.receive(message_t* msg, void* payload, uint8_t len) {
   if (len >= sizeof(uint16_t)) {
     nx_uint16_t* nval = (nx_uint16_t*)payload;
     value = *nval;
   }
   return msg;
 }
 //Case 3
 message_t buf;
 message_t* ptr = &buf;
 message_t* Receive.receive(message_t* msg, void* payload, uint8_t len) {
   message_t* tmp = ptr;
   ptr = msg;
   post processTask();
   return tmp;
 }

因为第三种情况,下层协议必须遵循缓冲区交换的语义,使用receive返回的指针。传递到receive的msg指针,在通知receive时间后,一定不能被修改,使用,或者存储其他数据。

Because of case 3), a lower layer MUST respect the buffer swap semantics and use the pointer returned from ``receive``. The pointer passed as a parameter to ``receive`` MUST NOT be touched, used, or stored after the signaling of ``receive.``

Dispatch

A packet protocol MAY have a dispatch identifier. This generally manifests as the protocol component providing parameterized interfaces (rather than a single interface instance). A dispatch identifier allows multiple services to use a protocol independently. If a protocol provides a dispatch mechanism, then each dispatch identifier SHOULD correspond to a single packet format: if an identifier corresponds to multiple packet formats, then there is no way to disambiguate them. Packets whose internal structure depends on their fields (for example, a packet that has a control field which indicates which optional fields are present) do not pose such problems.

HIL: ActiveMessageC

一个平台必须提供ActiveMessageC作为包级别通信的基本HIL。ActiveMessageC提供尽最大可能交付的,单跳的通信抽象。每个active message有一个16位的目的地址和一个8位的类型字段。值为“0xffff”的“AM_BROADCAST_ADDR”是保留地址。ActiveMessageC有如下签名::

A platform MUST provide ActiveMessageC as a basic HIL to packet-level communication. ActiveMessageC provides a best-effort, single-hop communication abstraction. Every active message has a 16-bit destination address and an 8-bit type. There is one reserved destination address, ``AM_BROADCAST_ADDR``, which has the value of ``0xffff``. ActiveMessageC has the following signature::

 configuration ActiveMessageC {
   provides {
     interface Init;
     interface SplitControl;  
 
     interface AMSend[uint8_t id];
     interface Receive[uint8_t id];
     interface Receive as Snoop[uint8_t id];
 
     interface Packet;
     interface AMPacket;
     interface PacketAcknowledgements;
   }
 }

发往此节点的包由Receive接口处理,发往其他节点的包由Snoop接口处理。发往此节点的包,其目的AM地址字段要么是AM广播地址,要么就是此节点AM协议栈的地址。不同的链路层有不同的监听能力。Snoop接口被假设为并不总是在监听,例如一个TDMA或RTS/CTS数据链路层。通过分离这2个接口,ActiveMessage避免了1.x中GenericComm vs GenericCommPromiscuous所遭受的问题。

The Receive interface is for packets destined to the node, while the Snoop interface is for packets destined to other nodes. A packet is destined for a node if its destination AM address is either the AM broadcast address or an address associated with the AM stack. Different link layers have different snooping capabilities. The Snoop interface does not assume always-on listening, for example, in the case of a TDMA or RTS/CTS data link layer. By separating out these two interfaces, ActiveMessageC avoids the complications encountered in 1.x with regards to GenericComm vs. GenericCommPromiscuous.

ActiveMessageC通常只是个连线到特定平台实现的配置。ActiveMessageC的定义留给了那些一个节点拥有2个或者多个不同的射频的平台。这种情况下,平台决定如何将基本的包抽象和具体的硬件向连线。有选择某一射频或者基于地址的分派等方法

ActiveMessageC is usually just a configuration that has pass-through wiring to a chip-specific HAL active message implementation. The definition of ActiveMessageC is left to the platform for when a node has more than one radio. In this case, the platform decides how to map the basic packet abstraction to the hardware underneath. Approaches include choosing one radio or having some form of address-based dispatch.

AM Services: AMSenderC, AMReceiverC, AMSnooperC, AMSnoopingReceiverC

TinyOS 2.x为应用提供了4个虚拟化的单跳通信组件:AMReceiverC, AMSnooperC, AMSnoopingReceiverC, 和AMSenderC。每一个都是泛型组件,有一个active message ID作为参数。这些组件假定ActiveMessageC存在。

TinyOS 2.x provides four component single-hop communication virtualizations to applications: AMReceiverC, AMSnooperC, AMSnoopingReceiverC, and AMSenderC. Each is a generic component that takes an active message ID as a parameter. These components assume the existence of ActiveMessageC.

Dispatch: ``am_id_t`` 分派器 am_id_t

Active message有一个8位的类型字段允许多种协议都使用AM通信而不会造成冲突。遵循协议分派标识符的指导,即在网络中使用的每种am_id_t都应该对应一种包格式。因此与包内容相结合的am_id_t足以识别具体的包格式。

Active messages have an 8-bit type field, which allows multiple protocols to all use AM communication without conflicting. Following the guidelines for protocol dispatch identifiers, each am_id_t used in a network SHOULD have a single packet format, so that the am_id_t, combined with the packet contents, are sufficient to determine the exact packet format.

AMReceiverC

AMReceiverC有如下签名::

AMReceiverC has the following signature::

 generic configuration AMReceiverC(am_id_t t) {
   provides{
     interface Receive;
     interface Packet;
     interface AMPacket;
   }
 }

当AMReceiver.Receive.receive事件被通知时,包层收到了一个正确AM类型,地址是本节点地址或者广播地址的包。注意,因为Receive.receive会交换缓冲区,所以一个程序不能实例化2个有着同样am_id_t的AMReceiver,并且一定不能实例化有着同样am_id_t的AMReceiver和AMSnoopingReceiver。

AMReceiver.Receive.receive is signalled whenever the packet layer receives an active message of the corresponding AM type whose destination address is the local address or the broadcast address. Note that since Receive.receive swaps buffers, a program MUST NOT instantiate two AMReceivers with the same am_id_t and MUST NOT instantiate an AMReceiver and an AMSnoopingReceiver with the same am_id_t.

AMSnooperC

AMSnooper 有着与AMReceiver相同的签名::

AMSnooper has an identical signature to AMReceiver::

 generic configuration AMSnooperC(am_id_t t) {
   provides{
     interface Receive;
     interface Packet;
     interface AMPacket;
   }
 }

当包层收到了一个正确AM类型,地址是本节点地址或者广播地址的包时,AMSnooper.Receive.receive事件会被通知。注意,因为Receive.receive会交换缓冲区,所以一个程序不能实例化2个有着同样am_id_t的AMSnooper,并且一定不能实例化有着同样am_id_t的AMSnooper和AMSnoopingReceiver。

AMSnooper.Receive.receive is signalled whenever the packet layer receives an active message of the corresponding AM type whose destination address is neither to the local address nor the broadcast address. Note that since Receive.receive swaps buffers, a program MUST NOT instantiate two AMSnoopers with the same am_id_t and MUST NOT instantiate an AMSnooper and an AMSnoopingReceiver with the same am_id_t.

AMSnoopingReceiverC

AMSnoopingReceiverC有如下签名:

AMSnoopingReceiverC has an identical signature to AMReceiverC::

 generic configuration AMSnoopingReceiverC(am_id_t t) {
   provides{
     interface Receive;
     interface Packet;
     interface AMPacket;
   }
 }

当包层收到了一个正确AM类型,地址是本节点地址或者广播地址的包时,AMSnoopingReceiverC.Receive.receive事件会被通知。注意,因为Receive.receive会交换缓冲区,所以一个程序不能实例化2个有着同样am_id_t的AMSnooper,并且一定不能实例化有着同样am_id_t的AMSnooperC,AMSnoopingReceiverC和AMReveiverC。

AMSnoopingReceiverC.Receive.receive is signalled whenever the packet layer receives an active message of the corresponding AM type, regardless of destination address. Note that since Receive.receive swaps buffers, a program that instantiates an AMSnoopingReceiverC with a certain am_id_t MUST NOT instantiate another AMSnoopingReceiverC, AMSnooperC, or AMReceiverC with the same am_id_t.

AMSenderC

AMSenderC的签名如下::

AMSenderC has the following signature::

 generic configuration AMSenderC(am_id_t AMId) {
   provides {
     interface AMSend;
     interface Packet;
     interface AMPacket;
     interface PacketAcknowledgements as Acks;
   }
 }

如果在特定的一个AMSenderC有一个发送请求是未决的,那么AMSenderC.AMSend.send将会返回EBUSY。那就是说,每个AMSenderC都有一个深度为1的队列。具体的挂起请求的顺序没有定义,但它必须是公平的,这里的公平就是说,每个未决包的客户端,都会享有一个大约一致的传输带宽。

Because this is a send virtualization, AMSenderC.AMSend.send returns EBUSY only if there is a send request outstanding on this particular AMSenderC. That is, each AMSenderC has a queue of depth one. The exact order in which pending AMSenderC requests are serviced is undefined, but it MUST be fair, where fair means that each client with outstanding packets receives a reasonable approximation of an equal share of the available transmission bandwidth.

Power Management and Local Address 电源管理和本地地址

除了收发包所用的标准数据链路接口,active message层也有控制接口

In addition to standard datapath interfaces for sending and receiving packets, an active message layer also has control interfaces.

Power Management 电源管理

通信虚拟化并不支持电源管理。ActiveMessageC提供SplitControl来明确地管理电源。为了适当地操作包通信,应用程序中的组件需要调用ActiveMessageC.SplitControl.start()命令。ActiveMessageC之下的HAL层实现电源管理技术,比如TDMA调度或者低功耗监听。

The communication virtualizations do not support power management. ActiveMessageC provides SplitControl for explicit power control. For packet communication to operate properly, a component in an application has to call ActiveMessageC.SplitControl.start(). The HAL underneath ActiveMessageC MAY employ power management techniques, such as TDMA scheduling or low power listening, when "on."

Local Active Message Address 本地Active Message地址

应用程序能在运行时改变ActiveMessageC的本地AM地址。这将会改变发送包中的源地址,它所能接收到的包也会发生变化。一个组件连线到ActiveMessageAddressC来在运行时改变AM地址。ActiveMessageAddressC只改变默认射频协议栈(AMSenderC等),如果一个射频有多个协议栈,那么改变他们的地址就要以特定协议栈自己的方式。

An application can change ActiveMessageC's local AM address at runtime. This will change which packets a node receives and the source address it embeds in packets. To change the local AM address at runtime, a component can wire to the component ``ActiveMessageAddressC``. This component only changes the AM address of the default radio stack (AMSenderC, etc.); if a radio has multiple stacks those may have other components for changing their addresses in a stack-specific fashion.

HAL Requirements HAL需求

无线射频芯片“X”必须提供一个有如下签名的包抽象::

A radio chip *X* MUST have a packet abstraction with the following signature::

 provides interface Init;
 provides interface SplitControl;
 provides interface AMSend[am_id_t type];
 provides interface Receive[am_id_t type];
 provides interface Receive as Snoop[am_id_t type];
 provides interface Packet;
 provides interface AMPacket;
 provides interface PacketAcknowledgments;


这个组件应该被命名为“XActiveMessageC”,“X”是无线射频芯片的名字。此组件可能会有其他额外的接口。这些接口可以是特定于芯片的,也可能是芯片独立的。

The component SHOULD be named *XActiveMessageC*, where *X* is the name of the radio chip. The component MAY have additional interfaces. These interfaces can either be chip-specific or chip-independent.

message_t

Active message是单跳包的基本抽象。因此,遵循TEP111[3]的准则,所有的数据链路层actvie message头部都必须为"message_header_t"结构。这能保证接收到的active message能传输到其他的数据链路层(比如UART),而不用移动数据负载区域。这就意味着message_header_t必须符合所有AM字段的需要。下面是CC2420的802.15.4帧头结构::

Active messages are a basic single-hop packet abstraction. Therefore, following TEP 111 [3]_, all data link and active message headers MUST be in the ``message_header_t`` structure of message_t. This ensures that an active message received from one data link layer (e.g., the radio) can be passed to another data link layer (e.g., the UART) without shifting the data payload. This means that the ``message_header_t`` must include all data needed for AM fields, which might introduce headers in addition to those of the data link. For example, this is an example structure for a CC2420 (802.15.4) header::

 typedef nx_struct cc2420_header_t {
   nx_uint8_t length;
   nx_uint16_t fcf;
   nx_uint8_t dsn;
   nx_uint16_t destpan;
   nx_uint16_t dest;
   nx_uint16_t src;
   nx_uint8_t type;
 } cc2420_header_t;

头6个字段都是802.15.4头部。类型字段是为了支持AM分派器而添加到头部字段中的。

The first six fields (length through src) are all 802.15.4 headers. The type field, however, has been added to the header structure in order to support AM dispatch.

Implementation 实现

tinyos-2.x/tos/system中文件提供了本TEP中提到的抽象的具体实现。

The following files in ``tinyos-2.x/tos/system`` provide reference implementations of the abstractions described in this TEP.

 * ``AMSenderC.nc``, ``AMReceiverC.nc``, ``AMSnooperC.nc``,
   and ``AMSnoopingReceiverC.nc`` are implementations of 
   virtualized AM services.
 * ``AMQueueP`` provides a send queue of *n* entries for *n*
   AMSenderC clients, such that each client has a dedicated entry.
 * ``AMQueueImplP`` is the underlying queue implementation,
   which is reusable for different clients (it is also used
   in the serial stack [4]_).
 * ``AMQueueEntryP`` sits on top of ``AMQueueP`` and stores
   the parameters to ``AMSend.send`` in an outstanding
   packet with the ``AMPacket`` interface.

tinyos-2.x/tos/interfaces中下列文件提供了包协议接口的样例:

The following files in ``tinyos-2.x/tos/interfaces`` contain example implementations of packet protocol interfaces:

 * ``Packet.nc`` is the basic interface that almost all
   packet protocols provide.
 * ``Send.nc`` is the transmission interface for address-free 
    protocols.
 * ``AMSend.nc`` is the transmission interface for AM address
    send protocols.
 * ``AMPacket.nc`` is the packet interface for AM-specific
   fields.

CC2420 active message实现在tos/chips/CC2420/CC2420ActiveMessageC.nc中,micaz平台和telos系列有一个ActiveMessageC.nc,这个组件导出了CC2420ActiveMessageC组件的接口。

An active messaging implementation for the CC2420 radio chip can be found in ``tos/chips/CC2420/CC2420ActiveMessageC.nc``. The micaz platform and telos family have an ``ActiveMessageC.nc`` which exports the interfaces of ``CC2420ActiveMessageC``.

Author's Address 作者地址

Philip Levis
358 Gates Hall
Computer Science Laboratory
Stanford University
Stanford, CA 94305
phone - +1 650 725 9046

Citations 引用

.. [1] The MintRoute protocol. ``tinyos-1.x/tos/lib/MintRoute``. Also, A. Woo, T. Tong, and D. Culler. "Taming the Underlying Challenges of Reliable Multihop Routing in Sensor Networks." SenSys 2003.

.. [2] Tiny AGgregation, one protocol of the TinyDB system. ``tinyos-1.x/tos/lib/TinyDB``. Also, S. Madden and M. Franklin and J. Hellerstein and W. Hong. "TinyDB: An Acquisitional Query Processing System for Sensor Networks." Transactions on Database Systems (TODS) 2005.

.. [3] TEP 111: message_t.

.. [4] TEP 113: Serial Communication.

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多