[Tinyos-commits] CVS: tinyos-1.x/tos/lib/Drain Drain.h, NONE, 1.1 Drain.nc, NONE, 1.1 DrainC.nc, NONE, 1.1 DrainGroup.nc, NONE, 1.1 DrainGroupManagerM.nc, NONE, 1.1 DrainLinkEst.nc, NONE, 1.1 DrainLinkEstM.nc, NONE, 1.1 DrainM.nc, NONE, 1.1 GroupManager.h, NONE, 1.1 GroupManager.nc, NONE, 1.1 GroupManagerC.nc, NONE, 1.1 GroupManagerM.nc, NONE, 1.1 Grouper.h, NONE, 1.1 GrouperC.nc, NONE, 1.1 GrouperM.nc, NONE, 1.1 README, NONE, 1.1

Gilman Tolle gtolle at users.sourceforge.net
Thu Oct 27 14:31:06 PDT 2005


Update of /cvsroot/tinyos/tinyos-1.x/tos/lib/Drain
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv14825

Added Files:
	Drain.h Drain.nc DrainC.nc DrainGroup.nc DrainGroupManagerM.nc 
	DrainLinkEst.nc DrainLinkEstM.nc DrainM.nc GroupManager.h 
	GroupManager.nc GroupManagerC.nc GroupManagerM.nc Grouper.h 
	GrouperC.nc GrouperM.nc README 
Log Message:
Moved Drain from beta to core.

--- NEW FILE: Drain.h ---
// $Id: Drain.h,v 1.1 2005/10/27 21:31:04 gtolle Exp $

/*									tab:4
 * "Copyright (c) 2000-2005 The Regents of the University  of California.  
 * All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice, the following
 * two paragraphs and the author appear in all copies of this software.
 * 
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
 * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
 */

/**
 * @author Gilman Tolle <get at cs.berkeley.edu>
 */


#ifndef __DRAIN_H__
#define __DRAIN_H__

enum drainMsgs {
  AM_DRAINMSG = 4,
  AM_DRAINBEACONMSG = 7,
  AM_DRAINGROUPREGISTERMSG = 89,
};

enum drainConsts {
  DRAIN_SEND_QUEUE_SIZE = 3, 
  DRAIN_FWD_QUEUE_SIZE = 12,
  DRAIN_MAX_RETRANSMITS = 0, // does nothing, for now.
  DRAIN_MAX_MISSED_BEACONS = 5,
  DRAIN_MAX_BACKOFF = 12,
  DRAIN_UNKNOWN_ACK_EST = 127,
  DRAIN_MAX_ROUTES = 2,
  DRAIN_MAX_TTL = 16,
  DRAIN_INVALID_DEST = 0,
  DRAIN_INVALID_SLOT = 0xFF,
  DRAIN_MAX_CHILDREN = 8,
  DRAIN_QUEUE_SEND = 1,
  DRAIN_QUEUE_FWD = 2,
};

enum drainAddresses {
  DRAIN_GROUP_ALL = 0xFEFFU,
  DRAIN_GROUP_MULTICAST = 0xFEFEU,
  TOS_DEFAULT_ADDR = 0,
};

typedef struct DrainMsg {
  uint8_t type;
  uint8_t ttl;
  uint8_t seqNo;
  uint16_t source;
  uint16_t dest;
  uint8_t data[0];
} DrainMsg;

typedef struct DrainBeaconMsg {
  uint16_t linkSource;

  uint16_t source;
  uint16_t parent;
  uint16_t cost;
  uint8_t  ttl;

  uint8_t  beaconSeqno;
  uint8_t  beaconDelay;
  uint8_t  treeInstance;
  uint16_t beaconOffset;
  
  bool     defaultRoute;
} DrainBeaconMsg;

typedef struct DrainGroupRegisterMsg {
  uint16_t group;
  uint16_t timeout;
} DrainGroupRegisterMsg;

typedef struct DrainRouteEntry {
  uint16_t dest;

  uint16_t nextHop;
  uint16_t nextHopCost;
  uint16_t nextHopLinkEst;

  uint8_t  destDistance;
  uint8_t  treeInstance;
  
  uint8_t  announceSeqno;
  uint8_t  announceDelay;
  uint16_t announceOffset;
  uint16_t announceCountdown;
  bool     sendWaiting:1;
  bool     defaultRoute:1;
  bool     pad:6;
  
  uint16_t  sentPackets;
  uint16_t  successPackets;
  uint16_t  successRate;
  uint8_t   parentSwitches;

} DrainRouteEntry;

#endif



--- NEW FILE: Drain.nc ---
//$Id: Drain.nc,v 1.1 2005/10/27 21:31:04 gtolle Exp $

/*									tab:4
 * "Copyright (c) 2000-2005 The Regents of the University  of California.  
 * All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice, the following
 * two paragraphs and the author appear in all copies of this software.
 * 
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
 * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
 */

/**
 * @author Gilman Tolle <get at cs.berkeley.edu>
 */

interface Drain {
  command result_t buildTree();
  command result_t buildTreeDefaultRoute();
  command result_t buildTreeInstance(uint8_t instance, bool defaultRoute);
}

--- NEW FILE: DrainC.nc ---
//$Id: DrainC.nc,v 1.1 2005/10/27 21:31:04 gtolle Exp $

/*									
 * Copyright (c) 2000-2005 The Regents of the University  of California.  
 * All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice, the following
 * two paragraphs and the author appear in all copies of this software.
 * 
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
 * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
 *
 */

includes Drain;

/**
 * DrainC provides a many-to-one collection routing service.
 * <p>
 * Use the SendMsg interface to send a message up the tree.
 * <p>
 * Why does DrainC provide SendMsg instead of Send? Because it provides an
 * argument for a destination address. This will eventually be used to
 * select between multiple trees when sending a message, but for now,
 * it is used to decide whether to send a message up the tree, to the
 * link-local broadcast address, or to the local serial connection.
 * <p>
 * TOS_DEFAULT_ADDR means "the tree" (defined in Drain.h) <br>
 * TOS_BCAST_ADDR means "the link-local broadcast" <br>
 * TOS_UART_ADDR means "the serial connection" <br>
 * <p>
 * All messages will contain the Drain header.
 * <p>
 * The Send interface is also provided because it provides getBuffer().
 * Don't use Send.send().
 * <p>
 * The Receive interface doesn't do anything currently, but will
 * eventually be used to deliver messages that have been routed down
 * the tree from the root.
 * <p>
 * Intercept and Snoop have the usual meanings.
 * <p>
 * NOTE: Drain provides three Nucleus attributes for status
 * reporting. You do not have to include the Nucleus system to use
 * Drain, but to compile it, you must make sure that the Nucleus
 * directory is in the include path, so Attrs.h can be found.
 * <p>
 * @author Gilman Tolle <get at cs.berkeley.edu>
 */

configuration DrainC {
  
  provides {
    interface StdControl;

    interface Drain;
    interface DrainGroup;

    interface RouteControl;

    interface SendMsg[uint8_t id];
    interface Send[uint8_t id];

    interface Receive[uint8_t id];

    interface Intercept[uint8_t id];
    interface Intercept as Snoop[uint8_t id];
  }
}

implementation {

  components 
    DrainM, 
    DrainLinkEstM,
    DrainGroupManagerM,
    GroupManagerC,
    GenericComm, 
    RandomLFSR,
    TimerC,
    LedsC;

#if defined(_CC2420CONST_H)
  components CC2420RadioC;
#endif

#if defined(_CC1KCONST_H)
  components CC1000RadioC;
#endif

  StdControl = DrainM.StdControl;

  SendMsg = DrainM.SendMsg;
  Send = DrainM.Send;

  Intercept = DrainM.Intercept;
  Snoop = DrainM.Snoop;
  Receive = DrainM.Receive;

  Drain = DrainLinkEstM.Drain;
  DrainGroup = DrainGroupManagerM.DrainGroup;
  RouteControl = DrainLinkEstM;

  DrainM.SubControl -> GenericComm;
  DrainM.SubControl -> DrainLinkEstM;

  DrainM.Leds -> LedsC;

  DrainM.DrainLinkEst -> DrainLinkEstM;

  DrainM.LinkSendMsg -> GenericComm.SendMsg[AM_DRAINMSG];
  DrainM.LinkReceiveMsg -> GenericComm.ReceiveMsg[AM_DRAINMSG];

  DrainM.Timer -> TimerC.Timer[unique("Timer")];
  DrainM.PostFailTimer -> TimerC.Timer[unique("Timer")];

  DrainM.DrainGroup -> DrainGroupManagerM;

  DrainLinkEstM.Timer -> TimerC.Timer[unique("Timer")];

  DrainLinkEstM.Random -> RandomLFSR;

  DrainLinkEstM.SendMsg -> GenericComm.SendMsg[AM_DRAINBEACONMSG];
  DrainLinkEstM.ReceiveMsg -> GenericComm.ReceiveMsg[AM_DRAINBEACONMSG];
  DrainLinkEstM.Leds -> LedsC;

  DrainLinkEstM.DrainGroup -> DrainGroupManagerM;

#if defined(_CC2420CONST_H)
  DrainM.MacControl -> CC2420RadioC;
#endif

#if defined(_CC1KCONST_H)
  DrainM.MacControl -> CC1000RadioC;
#endif
  
  DrainGroupManagerM.Send -> DrainM.Send[AM_DRAINGROUPREGISTERMSG];
  DrainGroupManagerM.SendMsg -> DrainM.SendMsg[AM_DRAINGROUPREGISTERMSG];
  DrainGroupManagerM.Intercept -> DrainM.Intercept[AM_DRAINGROUPREGISTERMSG];
  DrainGroupManagerM.GroupManager -> GroupManagerC;
}

--- NEW FILE: DrainGroup.nc ---
//$Id: DrainGroup.nc,v 1.1 2005/10/27 21:31:04 gtolle Exp $

/*									tab:4
 * "Copyright (c) 2000-2005 The Regents of the University  of California.  
 * All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice, the following
 * two paragraphs and the author appear in all copies of this software.
 * 
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
 * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
 */

/**
 * @author Gilman Tolle <get at cs.berkeley.edu>
 */

interface DrainGroup {
  command result_t joinGroup(uint16_t group, uint16_t timeout);
}

--- NEW FILE: DrainGroupManagerM.nc ---
//$Id: DrainGroupManagerM.nc,v 1.1 2005/10/27 21:31:04 gtolle Exp $

/*									tab:4
 * "Copyright (c) 2000-2005 The Regents of the University  of California.  
 * All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice, the following
 * two paragraphs and the author appear in all copies of this software.
 * 
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
 * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
 */

/**
 * @author Gilman Tolle <get at cs.berkeley.edu>
 */

module DrainGroupManagerM {
  provides interface DrainGroup;

  uses interface Intercept;
  uses interface Send;
  uses interface SendMsg;
  uses interface GroupManager;
}
implementation {
  TOS_Msg msgBuf;
  bool msgBufBusy;

  command result_t DrainGroup.joinGroup(uint16_t group, uint16_t timeout) {
    uint16_t length;

    DrainGroupRegisterMsg *regMsg = (DrainGroupRegisterMsg*) 
      call Send.getBuffer(&msgBuf, &length);

    if (msgBufBusy) { return FAIL; }
    msgBufBusy = TRUE;

    regMsg->group = group;
    regMsg->timeout = timeout; // XXX: pick something good

    if (call SendMsg.send(TOS_DEFAULT_ADDR,
			  sizeof(DrainGroupRegisterMsg),
			  &msgBuf) == FAIL) {
      msgBufBusy = FALSE;
      dbg(DBG_ROUTE, "DrainGroupManagerM: couldn't send group-join %d\n", group);
      return FAIL;
    } else {
      dbg(DBG_ROUTE, "DrainGroupManagerM: joining group %d\n", group);
      call GroupManager.joinGroup(group, timeout);
    }	

    return SUCCESS;
  }

  event result_t Intercept.intercept(TOS_MsgPtr msg, void* payload, 
				     uint16_t payloadLen) {

    DrainGroupRegisterMsg *regMsg = (DrainGroupRegisterMsg*) payload;

    call GroupManager.joinForward(regMsg->group, regMsg->timeout);

    dbg(DBG_ROUTE, "DrainGroupManagerM: becoming forwarder for group %d\n", 
	regMsg->group);

    return SUCCESS;
  }

  event result_t Send.sendDone(TOS_MsgPtr msg, result_t success) {
    // do-nothing
    return SUCCESS;
  }

  event result_t SendMsg.sendDone(TOS_MsgPtr msg, result_t success) {
    if (msg == &msgBuf) {
      msgBufBusy = FALSE;
    }
    return SUCCESS;
  }
}

--- NEW FILE: DrainLinkEst.nc ---
// $Id: DrainLinkEst.nc,v 1.1 2005/10/27 21:31:04 gtolle Exp $

/*									tab:4
 * "Copyright (c) 2000-2003 The Regents of the University  of California.  
 * All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice, the following
 * two paragraphs and the author appear in all copies of this software.
 * 
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
 * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
 *
 * Copyright (c) 2002-2003 Intel Corporation
 * All rights reserved.
 *
 * This file is distributed under the terms in the attached INTEL-LICENSE     
 * file. If you do not find these files, copies can be found by writing to
 * Intel Research Berkeley, 2150 Shattuck Avenue, Suite 1300, Berkeley, CA, 
 * 94704.  Attention:  Intel License Inquiry.
 */

/**
 * @author Gilman Tolle
 */

interface DrainLinkEst {

  /**
   * Given a TOS_MsgPtr, initialize its routing fields to a known
   * state, specifying that the message is originating from this node.
   * This known state can then be used by selectRoute() to fill in
   * the necessary data.
   *
   * @param msg Message to select route for and fill in init data.
   *
   * @return Should always return SUCCESS.
   *
   */

  command result_t initializeFields(TOS_MsgPtr msg, uint8_t id, 
				    uint16_t dest, uint8_t length);

  command result_t forwardFields(TOS_MsgPtr msg);
  
  /**
   * Select a route and fill in all of the necessary routing
   * information to a packet.
   *
   * @param msg Message to select route for and fill in routing information.
   *
   * @return Whether a route was selected succesfully. On FAIL the
   * packet should not be sent.
   *
   */
  
  command result_t selectRoute(TOS_MsgPtr msg);

  /**
   * Inform the link estimator that the message has been sent,
   * possibly successfully, possibly not.
   *
   */

  command result_t messageSent(TOS_MsgPtr msg, result_t success);

  command bool isRoot();
}

--- NEW FILE: DrainLinkEstM.nc ---
//$Id: DrainLinkEstM.nc,v 1.1 2005/10/27 21:31:04 gtolle Exp $

/*								       
 * Copyright (c) 2000-2005 The Regents of the University  of California.  
 * All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice, the following
 * two paragraphs and the author appear in all copies of this software.
 * 
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
 * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
 */

includes Drain;

/**
 * @author Gilman Tolle <get at cs.berkeley.edu>
 */

module DrainLinkEstM {

  provides {
    interface StdControl;
    interface DrainLinkEst;
    interface Drain;

    interface RouteControl;
  }

  uses {
    interface Timer;

    interface Random;

    interface SendMsg;
    interface ReceiveMsg;

    interface DrainGroup;

    interface Leds;
  }
}

implementation {

  DrainRouteEntry routes[DRAIN_MAX_ROUTES];

  uint8_t seqNo;

  bool msgBufBusy;
  TOS_Msg msgBuf;

  bool timerRunning;

#define MAX(a_,b_) (a_ > b_ ? a_ : b_)
#define MIN(a_,b_) (a_ < b_ ? a_ : b_)
#define BIT_GET(x, i) ((x) & (1 << (i)))
#define BIT_SET(x, i) ((x) | (1 << (i)))
#define BIT_CLEAR(x, i) ((x) & ~(1 << (i)))

  uint16_t adjustLQI(uint8_t val);

  DrainRouteEntry* newRoute(uint16_t dest, bool defaultRoute);
  void clearRoute(DrainRouteEntry* route);
  DrainRouteEntry* getRoute(uint16_t dest);
  DrainRouteEntry* getDefaultRoute(uint16_t dest);
  DrainRouteEntry* getReadyRoute();
  void sendRoute(DrainRouteEntry* route);

  bool isGroup(uint16_t dest);
  void startTimer();

  command result_t StdControl.init() {
    
    uint8_t i;

    msgBufBusy = FALSE;
    seqNo = 0;

    for(i = 0; i < DRAIN_MAX_ROUTES; i++) {
      clearRoute(&routes[i]);
    }

    return SUCCESS;
  }

  command result_t StdControl.start() {
    return SUCCESS;
  }

  command result_t StdControl.stop() {
    call Timer.stop();
    return SUCCESS;
  }

  command result_t DrainLinkEst.initializeFields(TOS_MsgPtr Msg, 
						 uint8_t id, 
						 uint16_t dest, 
						 uint8_t length) {
    
    DrainMsg *pMHMsg = (DrainMsg *)&Msg->data[0];

    Msg->addr = 0;
    Msg->length = offsetof(DrainMsg,data) + length;

    if (dest == TOS_DEFAULT_ADDR) {
      if (routes[0].dest == DRAIN_INVALID_DEST) {
	dest = TOS_BCAST_ADDR;
      } else {
	dest = routes[0].dest;
      }
    }
    
    pMHMsg->type = id;
    pMHMsg->ttl = DRAIN_MAX_TTL - 1;
    pMHMsg->seqNo = seqNo++;
    pMHMsg->source = TOS_LOCAL_ADDRESS;
    pMHMsg->dest = dest;
      
    return SUCCESS;
  }

  command result_t DrainLinkEst.forwardFields(TOS_MsgPtr Msg) {

    DrainMsg *pMHMsg = (DrainMsg *)&Msg->data[0];

    Msg->addr = 0;

    pMHMsg->ttl--;
    
    if (pMHMsg->ttl == 0)
      return FAIL;
    
    return SUCCESS;
  }

  command result_t DrainLinkEst.selectRoute(TOS_MsgPtr Msg) {
					    
    DrainMsg* pMHMsg = (DrainMsg *)&Msg->data[0];
    DrainRouteEntry* route;

    if (pMHMsg->dest == TOS_BCAST_ADDR) {
      
      Msg->addr = TOS_BCAST_ADDR;
      
    } else if (pMHMsg->dest == TOS_UART_ADDR) {
      
      Msg->addr = TOS_UART_ADDR;
      
    } else {

      if (isGroup(pMHMsg->dest)) {

	route = getDefaultRoute(pMHMsg->dest);
	
      } else {
	
	route = getRoute(pMHMsg->dest);
	
      }

      if (route != NULL) {
	Msg->addr = route->nextHop;
      } else {
	Msg->addr = TOS_BCAST_ADDR;
      }
    }

    Msg->ack = 0;

    return SUCCESS;
  }

  DrainRouteEntry* getRoute(uint16_t dest) {
    DrainRouteEntry* route;
    uint8_t i;

    for(i = 0; i < DRAIN_MAX_ROUTES; i++) {
      route = &routes[i];
      if (route->dest != DRAIN_INVALID_DEST && route->dest == dest) {
	return route;
      }
    }
    return NULL;
  }

  DrainRouteEntry* getDefaultRoute(uint16_t dest) {
    DrainRouteEntry* route;
    uint8_t i;

    for(i = 0; i < DRAIN_MAX_ROUTES; i++) {
      route = &routes[i];
      if (route->dest != DRAIN_INVALID_DEST &&
	  route->defaultRoute == TRUE) {
	return route;
      }
    }
    return NULL;
  }

  bool isGroup(uint16_t dest) {
    return (dest >= 0xFE00 && dest <= 0xFEFF);
  }

  command result_t DrainLinkEst.messageSent(TOS_MsgPtr msg,
					    result_t success) {
    
    DrainMsg* drainMsg = (DrainMsg*) &msg->data[0];
    DrainRouteEntry* route = getRoute(drainMsg->dest);
    
    route->sentPackets++;
    if (success) {
      route->successPackets++;
    }
    return SUCCESS;
  }

  command bool DrainLinkEst.isRoot() {
    return (getRoute(TOS_LOCAL_ADDRESS) != NULL);
  }

  command result_t Drain.buildTree() {
    return call Drain.buildTreeInstance(call Random.rand() & 0xFF, FALSE);
  }

  command result_t Drain.buildTreeDefaultRoute() {
    return call Drain.buildTreeInstance(call Random.rand() & 0xFF, TRUE);
  }

  command result_t Drain.buildTreeInstance(uint8_t instance, bool defaultRoute) {

    DrainRouteEntry* route = newRoute(TOS_LOCAL_ADDRESS, defaultRoute);

    if (route == NULL) 
      return FAIL;
    
    dbg(DBG_ROUTE, "DrainLinkEstM: buildTree(instance=%d,defaultRoute=%d)\n", 
	instance, defaultRoute);

    route->treeInstance = instance;

    route->announceSeqno = 0;
    route->announceDelay = 1;

    route->nextHop = TOS_LOCAL_ADDRESS;
    route->nextHopCost = 0;
    route->nextHopLinkEst = 0;
    route->destDistance = 0;

    route->sendWaiting = TRUE;

    call Timer.start(TIMER_ONE_SHOT, 10);

    return SUCCESS;
  }

  event TOS_MsgPtr ReceiveMsg.receive(TOS_MsgPtr Msg) {

    DrainBeaconMsg *pRP = (DrainBeaconMsg *)&Msg->data[0];
    DrainRouteEntry *route;
    uint16_t linkEst = 0;
    uint32_t curCost, newCost;
    bool retransmit = FALSE;

#if defined(_CC2420CONST_H)
    linkEst = adjustLQI(Msg->lqi);
#elif defined(PLATFORM_PC)
    linkEst = 
      abs(pRP->linkSource % 10 - TOS_LOCAL_ADDRESS % 10) +
      abs(pRP->linkSource / 10 - TOS_LOCAL_ADDRESS / 10);

//    dbg(DBG_ROUTE, "DrainLinkEstM: source=%d, dest=%d, est=%d\n",
//	pRP->linkSource, TOS_LOCAL_ADDRESS, linkEst);
#else
    linkEst = Msg->strength;
#endif

    dbg(DBG_ROUTE,"DrainLinkEstM: receive DrainBeaconMsg(linkSource=%d,source=%d,parent=%d,cost=%d,ttl=%d,seqno=%d,delay=%d,instance=%d,linkEst=%d)\n",
	pRP->linkSource, pRP->source, pRP->parent, pRP->cost, pRP->ttl,
	pRP->beaconSeqno, pRP->beaconDelay, pRP->treeInstance, linkEst);

    if (pRP->source == TOS_LOCAL_ADDRESS) {
      return Msg;
    }
    
    route = getRoute(pRP->source);

    if (route == NULL) {
      route = newRoute(pRP->source, pRP->defaultRoute);
      if (route == NULL) {
	dbg(DBG_ROUTE, "DrainLinkEstM: no slot for new route to dest=%d\n", pRP->source);
	return Msg;
      }
      dbg(DBG_ROUTE, "DrainLinkEstM: new route 0x%x to dest=%d\n", route, route->dest);
    }

    /*** Retransmission stuff (check for new, set the retransmit timer) ***/

    // If it's a new tree instance, discard the old sequence numbering
    // Always retransmit
    if (pRP->treeInstance != route->treeInstance) {

      if (pRP->beaconSeqno == 0) {

	// reset the sequence number to 1
	route->announceSeqno = 1;

      } else {

	// always take the sequence number
	route->announceSeqno = pRP->beaconSeqno;
      }

      retransmit = TRUE;

    } else {
      // If it's a refinement of my current tree instance, only
      // retransmit if it has a newer sequence number
      // If it wants me to bump my sequence number, do it.

      if (pRP->beaconSeqno == 0) {

	// increment the sequence number
	route->announceSeqno++;
	if (route->announceSeqno == 0)
	  route->announceSeqno++;

	retransmit = TRUE;

      } else if ((int8_t)(pRP->beaconSeqno - route->announceSeqno) > 0) {

	// if it's newer, take the sequence number
	route->announceSeqno = pRP->beaconSeqno;	
	
	retransmit = TRUE;
      }
    }

    /*** Routing stuff ***/
    curCost = (uint32_t) route->nextHopCost + (uint32_t) route->nextHopLinkEst;
    newCost = (uint32_t) pRP->cost + (uint32_t) linkEst;

    /* When to update my route:
     * - If it's a new tree.
     * - If it's from my parent.
     * - If it's from a lower-cost node that's not my child.
     */
    if (pRP->treeInstance != route->treeInstance ||
	pRP->linkSource == route->nextHop ||
	( pRP->parent != TOS_LOCAL_ADDRESS &&
	  newCost < curCost )) {

      if (pRP->treeInstance != route->treeInstance) {
	route->parentSwitches = 0;
      } else {
	if (route->nextHop != pRP->linkSource) {
	  route->parentSwitches++;
	}
      }

      dbg(DBG_ROUTE, "DrainLinkEstM: route update(dest=%d, treeInstance=%d, oldNextHop=%d, nextHop=%d, oldCost=%d, newCost=%d, distance=%d)\n",
	  pRP->source, pRP->treeInstance, route->nextHop, pRP->linkSource,
	  curCost, newCost, DRAIN_MAX_TTL - pRP->ttl);

      if (route->nextHop != pRP->linkSource) {
	route->sentPackets = 0;
	route->successPackets = 0;
      }

      route->dest = pRP->source;
      route->destDistance = DRAIN_MAX_TTL - pRP->ttl;

      route->nextHop = pRP->linkSource;
      route->nextHopCost = pRP->cost;
      route->nextHopLinkEst = linkEst;
      
      route->treeInstance = pRP->treeInstance;
    }

    if (retransmit) {
      dbg(DBG_ROUTE, "DrainLinkEstM: Preparing to retransmit beacon:dest=%d,instance=%d,seqno=%d\n", route->dest, route->treeInstance, route->announceSeqno);

      route->announceDelay = pRP->beaconDelay;
      route->announceOffset = 
	(call Random.rand() % 
	 (1024 * route->announceDelay)) + 1;

      route->sendWaiting = TRUE;
      route->announceCountdown = pRP->beaconOffset + route->announceOffset;

      startTimer();
    }

    return Msg;
  }

  uint16_t adjustLQI(uint8_t val) {
    uint16_t result = (80 - (val - 50));
    result = (((result * result) >> 3) * result) >> 3;

    /* experimenting with alternate scalings for LQI */
//    uint16_t result = 110 - val;
//    result = result * result;

    return result; 
  }

  void startTimer() {
    DrainRouteEntry* route;
    uint16_t minCountdown = 0xFFFF;
    uint8_t i;

    if (timerRunning) {
      dbg(DBG_ROUTE, "startTimer() - timer running. Returning.\n");
      return;
    }

    for(i = 0; i < DRAIN_MAX_ROUTES; i++) {
      route = &routes[i];

      dbg(DBG_ROUTE, "route[%d] 0x%x - dest=%d, sendWaiting=%d\n", i, route, route->dest, route->sendWaiting);

      if (route->dest != DRAIN_INVALID_DEST &&
	  route->sendWaiting == TRUE) {

	dbg(DBG_ROUTE, "route[%d] fires in %d ms\n", i, route->announceCountdown);
	if (route->announceCountdown < minCountdown) {
	  minCountdown = route->announceCountdown;
	}
      }
    }

    if (minCountdown < 0xFFFF) {

      timerRunning = TRUE;
      
      for(i = 0; i < DRAIN_MAX_ROUTES; i++) {
	route = &routes[i];
	if (route->dest != DRAIN_INVALID_DEST &&
	    route->sendWaiting == TRUE) {
	  route->announceCountdown -= minCountdown;
	}
      }
      
      if (minCountdown == 0) {
	minCountdown = 5;
      }

      call Timer.start(TIMER_ONE_SHOT, minCountdown);
    }
  }

  event result_t Timer.fired() {
    DrainRouteEntry *route;

    timerRunning = FALSE;

    route = getReadyRoute();
    if (route != NULL) {
      sendRoute(route);
    }

    startTimer();

    return SUCCESS;
  }

  DrainRouteEntry* getReadyRoute() {
    DrainRouteEntry* route;
    uint8_t i;

    for(i = 0; i < DRAIN_MAX_ROUTES; i++) {
      route = &routes[i];
      if (route->dest != DRAIN_INVALID_DEST && 
	  route->sendWaiting == TRUE &&
	  route->announceCountdown == 0) {
	return route;
      }
    }
    return NULL;
  }

  void sendRoute(DrainRouteEntry* route) {

    TOS_MsgPtr pMsgBuf = &msgBuf;
    DrainBeaconMsg *pRP = (DrainBeaconMsg *)&pMsgBuf->data[0];

#ifdef DRAIN_ENDPOINT_ONLY
    return;
#endif

    dbg(DBG_ROUTE, "DrainLinkEstM: sending route update\n");

    if (route->dest == DRAIN_INVALID_DEST ||
	route->destDistance == DRAIN_MAX_TTL) {
      dbg(DBG_ROUTE, "DrainLinkEstM: couldn't send route (dest=%d, destDistance=%d)\n", 
	  route->dest, route->destDistance);
      route->sendWaiting = FALSE;
      return;
    }

    if (msgBufBusy) {
      return;
    }
    
    atomic msgBufBusy = TRUE;
    
    pRP->linkSource = TOS_LOCAL_ADDRESS;
    
    pRP->source = route->dest;
    pRP->parent = route->nextHop;
    pRP->cost = route->nextHopCost + route->nextHopLinkEst;
    pRP->ttl = DRAIN_MAX_TTL - route->destDistance - 1;
    pRP->treeInstance = route->treeInstance;
    pRP->beaconSeqno = route->announceSeqno;
    pRP->beaconDelay = route->announceDelay;
    pRP->beaconOffset = (route->announceDelay * 1024) - route->announceOffset;
    pRP->defaultRoute = route->defaultRoute;

    if (call SendMsg.send(TOS_BCAST_ADDR, sizeof(DrainBeaconMsg), pMsgBuf)) {

      dbg(DBG_ROUTE,"DrainLinkEstM: send DrainBeaconMsg(linkSource=%d,source=%d,parent=%d,cost=%d,ttl=%d,seqno=%d,delay=%d,instance=%d)\n",
	  pRP->linkSource, pRP->source, pRP->parent, pRP->cost, pRP->ttl,
	  pRP->beaconSeqno, pRP->beaconDelay, pRP->treeInstance);

      route->sendWaiting = FALSE;

    } else {
      dbg(DBG_ROUTE, "send DrainBeaconMsg FAILED\n");
      // how to handle the retries if the radio is busy?
      atomic msgBufBusy = FALSE;
    }
  }
  
  event result_t SendMsg.sendDone(TOS_MsgPtr pMsg, result_t success) {
    if (pMsg == &msgBuf) {
      atomic msgBufBusy = FALSE;
    }
    return SUCCESS;
  }

  DrainRouteEntry* newRoute(uint16_t dest, bool defaultRoute) {
    DrainRouteEntry* route;    
    uint8_t i;
    uint8_t slot = DRAIN_INVALID_SLOT;

    if (defaultRoute) {
      for(i = 0; i < DRAIN_MAX_ROUTES; i++) {
	route = &routes[i];
	if (route->dest != DRAIN_INVALID_DEST &&
	    route->defaultRoute == TRUE) {
	  slot = i;
	  break;
	}
      }
    }
    
    if (slot == DRAIN_INVALID_SLOT) {
      for(i = 0; i < DRAIN_MAX_ROUTES; i++) {
	route = &routes[i];
	if (route->dest == DRAIN_INVALID_DEST) {
	  slot = i;
	  break;
	}
      }
    }

    if (slot == DRAIN_INVALID_SLOT) {
      for(i = 0; i < DRAIN_MAX_ROUTES; i++) {
	route = &routes[i];
	if (route->dest != DRAIN_INVALID_DEST &&
	    route->defaultRoute == FALSE) {
	  slot = i;
	  break;
	}
      }
    }

    if (slot == DRAIN_INVALID_SLOT) {
      return NULL;
    }

    dbg(DBG_ROUTE, "DrainLinkEstM: new route in slot %d 0x%x\n", slot, &routes[slot]);

    route = &routes[slot];

    clearRoute(route);
    route->dest = dest;
    route->defaultRoute = defaultRoute;
    return route;
  }

  void clearRoute(DrainRouteEntry* route) {

    route->dest = DRAIN_INVALID_DEST;
    route->nextHop = TOS_BCAST_ADDR;
    
    route->nextHopCost = 0xFFFF;
    route->nextHopLinkEst = 0xFFFF;
    
    route->destDistance = 0;
    route->treeInstance = 0;

    route->announceSeqno = 1;
    route->announceDelay = 0;

    route->defaultRoute = 0;
  }

  command uint16_t RouteControl.getParent() {
    return routes[0].nextHop;
  }

  command uint8_t RouteControl.getDepth() {
    return routes[0].destDistance;
  }

  command uint16_t RouteControl.getSender(TOS_MsgPtr msg) {
    return 0;
  }

  command uint8_t RouteControl.getOccupancy() {
    return 0;
  }

  command uint8_t RouteControl.getQuality() {
    return 0;
  }

  command result_t RouteControl.setUpdateInterval(uint16_t Interval) {
    return FAIL;
  }

  command result_t RouteControl.manualUpdate() {
    return FAIL;
  }
}


--- NEW FILE: DrainM.nc ---
//$Id: DrainM.nc,v 1.1 2005/10/27 21:31:04 gtolle Exp $

/*								       
 * Copyright (c) 2000-2005 The Regents of the University  of California.  
 * All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice, the following
 * two paragraphs and the author appear in all copies of this software.
 * 
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
 * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
 */

/**
 * @author Gilman Tolle <get at cs.berkeley.edu>
 */

module DrainM {
  provides {
    interface StdControl;

    interface SendMsg[uint8_t id];
    interface Send[uint8_t id];

    interface Receive[uint8_t id];

    interface Intercept[uint8_t id];
    interface Intercept as Snoop[uint8_t id];
  }
  uses {
    interface StdControl as SubControl;
      
    interface Leds;

    interface ReceiveMsg as LinkReceiveMsg;
    interface SendMsg as LinkSendMsg;

    interface DrainLinkEst;
    interface DrainGroup;

    interface Timer;

    interface Timer as PostFailTimer;

#if defined(_CC2420CONST_H) || defined(_CC1KCONST_H)
    interface MacControl;
#endif
  }
}

implementation {

  /** 
   * Drain includes its own queueing, for both sent and forwarded messages. 
   */
  TOS_MsgPtr sendQueue[DRAIN_SEND_QUEUE_SIZE];
  uint8_t sendQueueIn, sendQueueOut, sendQueueCount;

  TOS_Msg fwdBuffers[DRAIN_FWD_QUEUE_SIZE];
  uint8_t fwdQueueIn, fwdQueueOut, fwdQueueCount;

  uint8_t queueChoice;

  bool queuesBusy;
  bool radioBusy;

  bool baseAcks = TRUE;

  uint8_t backoff;

  /** 
   * Drain keeps track of some statistics -- if you're tight on RAM,
   * you can take these out.
   */

  uint16_t sendPackets;
  uint16_t sendDrops;

  uint16_t forwardPackets;
  uint16_t forwardDrops;

  uint16_t linkSendPackets;
  uint16_t linkAckedPackets;
  uint16_t linkBackoffExpires;

  task void QueueServiceTask();

  void initializeBufs();

  bool tooBig(TOS_MsgPtr pMsg, uint8_t payloadLen);
  bool cantSend(TOS_MsgPtr pMsg, uint8_t payloadLen);
  result_t enqueueSend(TOS_MsgPtr pMsg);
  TOS_MsgPtr enqueueForward(TOS_MsgPtr pMsg);

  void inc(uint16_t *val);
  void clear(uint16_t *val);
  task void errorBlink();

  task void enableAck();

  void postService();
  void postServiceCheck();

  command result_t StdControl.init() {
    initializeBufs();
    backoff = 0;
    return call SubControl.init();
  }
  
  void initializeBufs() {
/*
    int n;

    for (n=0; n < DRAIN_FWD_QUEUE_SIZE; n++) {
      fwdQueue[n] = &fwdBuffers[n];
    }
*/

    fwdQueueIn = fwdQueueOut = fwdQueueCount = 0;
    sendQueueIn = sendQueueOut = fwdQueueCount = 0;

    queuesBusy = FALSE;
  }

  command result_t StdControl.start() {
#if defined(_CC2420CONST_H) || defined(_CC1KCONST_H)
#define DRAIN_ACKS_AVAILABLE
    call MacControl.enableAck();
#endif
    return call SubControl.start();
  }
 
  command result_t StdControl.stop() {
    return SUCCESS;
  }

  command void* Send.getBuffer[uint8_t id](TOS_MsgPtr pMsg, uint16_t* length) {
    
    DrainMsg *pMHMsg = (DrainMsg *)pMsg->data;
    
    *length = TOSH_DATA_LENGTH - offsetof(DrainMsg,data);

#if DRAIN_DEBUG_DETAILED
    dbg(DBG_ROUTE, "Drain: getBuffer(pMsg=0x%x,id=%d,len=%d)\n",
	pMsg, id, *length);
#endif

    return (&pMHMsg->data[0]);
  }

  command result_t Send.send[uint8_t id](TOS_MsgPtr pMsg, uint16_t payloadLen) {
    // Please send with a destination address, or TOS_DEFAULT_ADDR
    // if you don't care. 
    // This may be temporary...

    return FAIL;
  } 
  
  command result_t SendMsg.send[uint8_t id](uint16_t dest, uint8_t length, 
					    TOS_MsgPtr pMsg) {

    dbg(DBG_ROUTE, "Drain: netSend(pMsg=0x%x,dest=%d,id=%d,len=%d)\n",
	pMsg, dest, id, length);
    
    if (tooBig(pMsg, length) || cantSend(pMsg, length)) {
      return FAIL;
    }
    
    call DrainLinkEst.initializeFields(pMsg, id, dest, length);

    return enqueueSend(pMsg);
  }

  bool tooBig(TOS_MsgPtr pMsg, uint8_t payloadLen) {
    uint16_t usMHLength = offsetof(DrainMsg,data) + payloadLen;
    
    if (usMHLength > TOSH_DATA_LENGTH) {
      return TRUE;
    }
    return FALSE;
  }

  bool cantSend(TOS_MsgPtr pMsg, uint8_t payloadLen) {

    if (sendQueueCount >= DRAIN_SEND_QUEUE_SIZE) {
      sendDrops++;
      dbg(DBG_ROUTE, "Drain: sendQueueFull(pMsg=0x%x,len=%d)\n",
	  pMsg, payloadLen);
      return TRUE;
    }
    
    return FALSE;
  }

  result_t enqueueSend(TOS_MsgPtr pMsg) {

    postServiceCheck();

#if DRAIN_DEBUG_DETAILED
    dbg(DBG_ROUTE, "Drain: sendEnterQueue(pMsg=0x%x)\n",
	pMsg);
#endif

    sendQueue[sendQueueIn] = pMsg;
    sendQueueCount++;
    if (++sendQueueIn >= DRAIN_SEND_QUEUE_SIZE) 
      sendQueueIn = 0;
    return SUCCESS;
  }

  void postServiceCheck() {
    if (!queuesBusy) { // && !radioBusy) {
      postService();
      queuesBusy = TRUE;
    }
  }

  void postService() {
    if (post QueueServiceTask() == FAIL) {
      call PostFailTimer.start(TIMER_ONE_SHOT, 10);
    }
  }

  event result_t PostFailTimer.fired() {
    postService();
    return SUCCESS;
  }

  event TOS_MsgPtr LinkReceiveMsg.receive(TOS_MsgPtr pMsg) {
    
    DrainMsg *drainMsg = (DrainMsg *)pMsg->data;
    uint16_t payloadLen = pMsg->length - offsetof(DrainMsg,data);
    uint8_t id = drainMsg->type;
    
    dbg(DBG_ROUTE, "Drain: linkReceive(pMsg=0x%x,src=0x%02x,dst=0x%02x)\n", 
        pMsg, drainMsg->source, drainMsg->dest);

#ifdef DRAIN_ENDPOINT_ONLY
    return pMsg;
#endif

    // See if it's for this node.
    if (pMsg->addr != TOS_LOCAL_ADDRESS) { 
      signal Snoop.intercept[id](pMsg, &drainMsg->data[0], payloadLen);
      return pMsg;
    }

    // Give Intercept a chance
    if ((signal Intercept.intercept[id](pMsg, &drainMsg->data[0], 
					payloadLen)) == FAIL) {
      // It's not OK to forward.
      return pMsg;
    }

    // Pass it up if necessary.
    if (drainMsg->dest == TOS_LOCAL_ADDRESS ||
	call DrainLinkEst.isRoot()) {

      dbg(DBG_ROUTE, "Drain: netReceive(pMsg=0x%x,src=0x%02x,dst=0x%02x)\n", 
	  pMsg, drainMsg->source, drainMsg->dest);

      pMsg = signal Receive.receive[id](pMsg, &drainMsg->data[0], payloadLen);

    } else { 

      if (call DrainLinkEst.forwardFields(pMsg)) {
	
	// Enqueue it for forwarding if necessary.
	if (fwdQueueCount < DRAIN_FWD_QUEUE_SIZE) {
	  dbg(DBG_ROUTE, "Drain: netForward(pMsg=0x%x,src=0x%02x,dst=0x%02x)\n", 
	      pMsg, drainMsg->source, drainMsg->dest);
	  pMsg = enqueueForward(pMsg);
	} else {
	  dbg(DBG_ROUTE, "Drain: forwardQueueFull(pMsg=0x%x)\n", pMsg);
	  forwardDrops++;
	}
      }
    }

    return pMsg;
  }

  TOS_MsgPtr enqueueForward(TOS_MsgPtr pMsg) {

    TOS_MsgPtr pNewBuf = pMsg;

    postServiceCheck();

#if DRAIN_DEBUG_DETAILED
    dbg(DBG_ROUTE, "Drain: forwardEnterQueue(pMsg=0x%x)\n", pMsg);
#endif

    memcpy(&fwdBuffers[fwdQueueIn], pMsg, sizeof(TOS_Msg));
    fwdQueueCount++;
    if (++fwdQueueIn >= DRAIN_FWD_QUEUE_SIZE) 
      fwdQueueIn = 0;
    
    return pNewBuf;
  }

  task void QueueServiceTask() {

    TOS_MsgPtr pMsg;

#if DRAIN_DEBUG_DETAILED
    dbg(DBG_ROUTE, "Drain: queueServiceTask\n");
#endif

    // First check send queue. 
    if (sendQueueCount > 0) {

#if DRAIN_DEBUG_DETAILED
      dbg(DBG_ROUTE, "Drain: queueServiceTask(servicing=sendQueue)\n");
#endif

      // We've got a message in the send queue.
      pMsg = sendQueue[sendQueueOut];
      if (queueChoice != DRAIN_QUEUE_SEND) {
	backoff = 0;
      }
      queueChoice = DRAIN_QUEUE_SEND;

    } else if (fwdQueueCount > 0) {

#if DRAIN_DEBUG_DETAILED
      dbg(DBG_ROUTE, "Drain: queueServiceTask(servicing=forwardQueue)\n");
#endif

      // We've got a message in the forward queue.
      pMsg = &fwdBuffers[fwdQueueOut];
      if (queueChoice != DRAIN_QUEUE_FWD) {
	backoff = 0;
      }
      queueChoice = DRAIN_QUEUE_FWD;

    } else {

      queuesBusy = FALSE;

#if DRAIN_DEBUG_DETAILED
      dbg(DBG_ROUTE, "Drain: queueServiceTask(queuesAllEmpty)\n");
#endif

      return;
    }
    
    call DrainLinkEst.selectRoute(pMsg);

    dbg(DBG_ROUTE, "Drain: linkSend(pMsg=0x%x,linkDest=%d,len=%d)\n",
	pMsg, pMsg->addr, pMsg->length);
    
    if (pMsg->length == 1) {
//      call Leds.greenOn();
      dbg(DBG_USR1, "DrainM: LENGTH == 1!!!\n");
    }

    if (call LinkSendMsg.send(pMsg->addr, pMsg->length, pMsg) == SUCCESS) {
#if DRAIN_DEBUG_DETAILED
      dbg(DBG_ROUTE, "Drain: LinkSendMsg succeeded\n");
#endif
      // Wait for the sendDone.
      radioBusy = TRUE;
    } else {
      // The radio didn't accept our message.
#if DRAIN_DEBUG_DETAILED
      dbg(DBG_ROUTE, "Drain: LinkSendMsg failed\n");
#endif
      call Timer.start(TIMER_ONE_SHOT, 10);
    }      
  }

  event result_t Timer.fired() {
    postService();
    return SUCCESS;
  }

  event result_t LinkSendMsg.sendDone(TOS_MsgPtr pMsg, result_t success) {

    result_t forwardResultVal = SUCCESS;
    DrainMsg* mhMsg = (DrainMsg*) pMsg->data;

#if DRAIN_DEBUG_DETAILED
    dbg(DBG_ROUTE, "Drain: sendDone(pMsg=0x%x,success=%d)\n", 
	pMsg, success);  
#endif

    radioBusy = FALSE;

    if (queueChoice == DRAIN_QUEUE_SEND &&
	pMsg != sendQueue[sendQueueOut]) {
      //     call Leds.greenOn();
      postService();
      return SUCCESS;
    }

    if (queueChoice == DRAIN_QUEUE_FWD &&
	pMsg != &fwdBuffers[fwdQueueOut]) {
      postService();
      return SUCCESS;
    }

    if (!success) {
      postService();
      return SUCCESS;
    }

    linkSendPackets++;

#ifdef DRAIN_ACKS_AVAILABLE
    if (pMsg->ack == 1) {
      linkAckedPackets++;
    }
#endif

    if (pMsg->addr == TOS_BCAST_ADDR || pMsg->addr == TOS_UART_ADDR) {

      // It didn't have a destination.

    } else if (mhMsg->dest == pMsg->addr && !baseAcks) {
      
      // It's for the destination. This might be a TOSBase that
      // doesn't know how to ACK packets.

    } else {

      // It did have a destination. Consider retransmitting.

#ifdef DRAIN_ACKS_AVAILABLE
      if (pMsg->ack == 0) {
	
	// It wasn't acked. Try again, up to DRAIN_MAX_BACKOFF
	call DrainLinkEst.messageSent(pMsg, FAIL);

	if (backoff < DRAIN_MAX_BACKOFF) {
	  backoff++;
	  call Timer.start(TIMER_ONE_SHOT, 1 << backoff);
	  return SUCCESS;
	} else {
	  // We seem to have hit max backoff.
	  linkBackoffExpires++;
	  forwardResultVal = FAIL;
	}
      } 
      else 
#endif
      {
	// It was acked.
	call DrainLinkEst.messageSent(pMsg, SUCCESS);
      }
    }

    backoff = 0;
    
    dbg(DBG_ROUTE, "Drain: sendComplete(pMsg=0x%x,result=%d)\n", 
	pMsg, forwardResultVal);
    
    if (queueChoice == DRAIN_QUEUE_SEND) {

      pMsg = sendQueue[sendQueueOut];

      if (forwardResultVal == SUCCESS) {
	sendPackets++;
      }

      signal SendMsg.sendDone[mhMsg->type](pMsg, forwardResultVal);

      sendQueueCount--;
      sendQueue[sendQueueOut] = 0;
      if (++sendQueueOut >= DRAIN_SEND_QUEUE_SIZE)
	sendQueueOut = 0;

    } else if (queueChoice == DRAIN_QUEUE_FWD) {

      pMsg = &fwdBuffers[fwdQueueOut];

      if (forwardResultVal == SUCCESS) {
	forwardPackets++;
      }

      fwdQueueCount--;
      if (++fwdQueueOut >= DRAIN_FWD_QUEUE_SIZE)
	fwdQueueOut = 0;

    } else {

      dbg(DBG_ROUTE, "Drain: ERROR! RECEIVED SEND DONE FOR MESSAGE NOT IN QUEUE (pMsg=0x%x)\n", pMsg);
    }

    postService();
      
    return SUCCESS;
  }

  default event result_t Send.sendDone[uint8_t id](TOS_MsgPtr pMsg, 
						   result_t success) {
    return SUCCESS;
  }

  default event result_t SendMsg.sendDone[uint8_t id](TOS_MsgPtr pMsg, 
						      result_t success) {
    return SUCCESS;
  }

  default event result_t Intercept.intercept[uint8_t id](TOS_MsgPtr pMsg, 
							 void* payload, 
							 uint16_t payloadLen) {
    return SUCCESS;
  }

  default event result_t Snoop.intercept[uint8_t id](TOS_MsgPtr pMsg, 
						     void* payload, 
                                                     uint16_t payloadLen) {
    return SUCCESS;
  }

  default event TOS_MsgPtr Receive.receive[uint8_t id](TOS_MsgPtr pMsg, 
						       void* payload, 
						       uint16_t payloadLen) {
    return pMsg;
  }
}


--- NEW FILE: GroupManager.h ---
//$Id: GroupManager.h,v 1.1 2005/10/27 21:31:04 gtolle Exp $

#ifndef __GROUPMANAGER_H__
#define __GROUPMANAGER_H__

/*									tab:4
 * "Copyright (c) 2000-2005 The Regents of the University  of California.  
 * All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice, the following
 * two paragraphs and the author appear in all copies of this software.
 * 
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
 * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
 */

/**
 * @author Gilman Tolle <get at cs.berkeley.edu>
 */

enum {
  GROUPMANAGER_MAX_GROUPS = 4,
  GROUPMANAGER_INVALID_GROUP = 0,
};

#endif

--- NEW FILE: GroupManager.nc ---
//$Id: GroupManager.nc,v 1.1 2005/10/27 21:31:04 gtolle Exp $

/*									tab:4
 * "Copyright (c) 2000-2005 The Regents of the University  of California.  
 * All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice, the following
 * two paragraphs and the author appear in all copies of this software.
 * 
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
 * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
 */

/**
 * @author Gilman Tolle <get at cs.berkeley.edu>
 */

interface GroupManager {

  command bool isMember(uint16_t groupID);
  command result_t joinGroup(uint16_t groupID, uint16_t timeout);
  command result_t leaveGroup(uint16_t groupID);

  command bool isForwarder(uint16_t groupID);
  command result_t joinForward(uint16_t groupID, uint16_t timeout);
  command result_t leaveForward(uint16_t groupID);
}

--- NEW FILE: GroupManagerC.nc ---
//$Id: GroupManagerC.nc,v 1.1 2005/10/27 21:31:04 gtolle Exp $

/**
 * This component holds a list of groups that have been joined, and
 * provides functions to look up, join, and leave groups.
 *
 * @author Gilman Tolle <get at cs.berkeley.edu>
 */

configuration GroupManagerC {
  provides {
    interface StdControl;
    interface GroupManager;
  }
}
implementation {
  components GroupManagerM;
  components TimerC;

  StdControl = GroupManagerM;
  GroupManager = GroupManagerM;

  GroupManagerM.Timer -> TimerC.Timer[unique("Timer")];
}

--- NEW FILE: GroupManagerM.nc ---
//$Id: GroupManagerM.nc,v 1.1 2005/10/27 21:31:04 gtolle Exp $

/*									tab:4
 * "Copyright (c) 2000-2005 The Regents of the University  of California.  
 * All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice, the following
 * two paragraphs and the author appear in all copies of this software.
 * 
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
 * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
 */

/**
 * @author Gilman Tolle <get at cs.berkeley.edu>
 */

includes GroupManager;

module GroupManagerM {
  provides {
    interface StdControl;
    interface GroupManager;
  }
  uses interface Timer;
}
implementation {

  bool timerActive;

  uint16_t groups[GROUPMANAGER_MAX_GROUPS];
  uint16_t groupTimeouts[GROUPMANAGER_MAX_GROUPS];
  uint16_t forwardGroups[GROUPMANAGER_MAX_GROUPS];
  uint16_t forwardGroupTimeouts[GROUPMANAGER_MAX_GROUPS];

  void clearGroups();
  void setTimer();

  command result_t StdControl.init() { clearGroups(); return SUCCESS; }
  command result_t StdControl.start() { return SUCCESS; }
  command result_t StdControl.stop() { return SUCCESS; }

  void clearGroups() {
     uint8_t i;

     for (i = 0; i < GROUPMANAGER_MAX_GROUPS; i++) {
       groups[i] = GROUPMANAGER_INVALID_GROUP;
       groupTimeouts[i] = 0;
       forwardGroups[i] = GROUPMANAGER_INVALID_GROUP;
       forwardGroupTimeouts[i] = 0;
     }
  }

  command bool GroupManager.isMember(uint16_t groupID) {
    uint8_t i;

    if (groupID == GROUPMANAGER_INVALID_GROUP)
      return FALSE;

    for (i = 0; i < GROUPMANAGER_MAX_GROUPS; i++) {
      if (groups[i] == groupID) {
	dbg(DBG_USR1, "GroupManagerM: Is a member of group %d\n", groupID);	
	return TRUE;
      }
    }
//    dbg(DBG_USR1, "GroupManagerM: Is NOT a member of group %d\n", groupID);	
    return FALSE;
  }

  command result_t GroupManager.joinGroup(uint16_t groupID, 
					  uint16_t timeout) {
    uint8_t i;

    if (groupID == GROUPMANAGER_INVALID_GROUP)
      return FALSE;

    if (call GroupManager.isMember(groupID))
      return SUCCESS;

    for (i = 0; i < GROUPMANAGER_MAX_GROUPS; i++) {
      if (groups[i] == GROUPMANAGER_INVALID_GROUP) {
	groups[i] = groupID;
	groupTimeouts[i] = timeout;
	setTimer();
	dbg(DBG_USR1, "GroupManagerM: Joined group %d\n", groupID);
	return SUCCESS;
      }
    }

    return FAIL;
  }

  command result_t GroupManager.leaveGroup(uint16_t groupID) {
    uint8_t i;

    if (groupID == GROUPMANAGER_INVALID_GROUP)
      return FALSE;

    for (i = 0; i < GROUPMANAGER_MAX_GROUPS; i++) {
      if (groups[i] == groupID) {
	groups[i] = GROUPMANAGER_INVALID_GROUP;
	groupTimeouts[i] = 0;
	dbg(DBG_USR1, "GroupManagerM: Left group %d\n", groupID);
	return SUCCESS;
      }
    }    
    return FAIL;
  }

  command bool GroupManager.isForwarder(uint16_t groupID) {
    uint8_t i;

    if (groupID == GROUPMANAGER_INVALID_GROUP)
      return FALSE;

    for (i = 0; i < GROUPMANAGER_MAX_GROUPS; i++) {
      if (forwardGroups[i] == groupID) {
	dbg(DBG_USR1, "GroupManagerM: Is forwarding for group %d\n", groupID);	
	return TRUE;
      }
    }
//    dbg(DBG_USR1, "GroupManagerM: Is NOT forwarding for group %d\n", groupID);	
    return FALSE;
  }

  command result_t GroupManager.joinForward(uint16_t groupID, uint16_t timeout) {
    uint8_t i;

    if (groupID == GROUPMANAGER_INVALID_GROUP)
      return FALSE;

    if (call GroupManager.isForwarder(groupID))
      return SUCCESS;

    for (i = 0; i < GROUPMANAGER_MAX_GROUPS; i++) {
      if (forwardGroups[i] == GROUPMANAGER_INVALID_GROUP) {
	forwardGroups[i] = groupID;
	forwardGroupTimeouts[i] = timeout;
	setTimer();
	dbg(DBG_USR1, "GroupManagerM: Started forwarding for group %d\n", groupID);
	return SUCCESS;
      }
    }

    return FAIL;
  }

  command result_t GroupManager.leaveForward(uint16_t groupID) {
    uint8_t i;

    if (groupID == GROUPMANAGER_INVALID_GROUP)
      return FALSE;

    for (i = 0; i < GROUPMANAGER_MAX_GROUPS; i++) {
      if (forwardGroups[i] == groupID) {
	forwardGroups[i] = GROUPMANAGER_INVALID_GROUP;
	forwardGroupTimeouts[i] = 0;
	dbg(DBG_USR1, "GroupManagerM: Stopped forwarding for group %d\n", groupID);
	return SUCCESS;
      }
    }
    
    return FAIL;
  }

  void setTimer() {
    uint16_t minTime = -1;
    uint8_t i;

    if (timerActive) {
      return;
    }

    timerActive = FALSE;

    for (i = 0; i < GROUPMANAGER_MAX_GROUPS; i++) {
      if (groups[i] != GROUPMANAGER_INVALID_GROUP) {
	if (groupTimeouts[i] < minTime) {
	  minTime = groupTimeouts[i];
	}
      }

      if (forwardGroups[i] != GROUPMANAGER_INVALID_GROUP) {
	if (forwardGroupTimeouts[i] < minTime) {
	  minTime = forwardGroupTimeouts[i];
	}
      }
    }

    if (minTime < -1) {

      for (i = 0; i < GROUPMANAGER_MAX_GROUPS; i++) {
	if (groups[i] != GROUPMANAGER_INVALID_GROUP) {
	  groupTimeouts[i] -= minTime;
	}
	
	if (forwardGroups[i] != GROUPMANAGER_INVALID_GROUP) {
	  forwardGroupTimeouts[i] -= minTime;
	}
      }
    
      timerActive = TRUE;
      call Timer.start(TIMER_ONE_SHOT, minTime * 1024);
      return;
    }
  }
  
  event result_t Timer.fired() {
    uint8_t i;

    for (i = 0; i < GROUPMANAGER_MAX_GROUPS; i++) {
      if (groupTimeouts[i] == 0)
	groups[i] = GROUPMANAGER_INVALID_GROUP;
      
      if (forwardGroupTimeouts[i] == 0)
	forwardGroups[i] = GROUPMANAGER_INVALID_GROUP;
    }

    timerActive = FALSE;
    setTimer();
    return SUCCESS;
  }
}

--- NEW FILE: Grouper.h ---
//$Id: Grouper.h,v 1.1 2005/10/27 21:31:04 gtolle Exp $

/*									tab:4
 * "Copyright (c) 2000-2005 The Regents of the University  of California.  
 * All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice, the following
 * two paragraphs and the author appear in all copies of this software.
 * 
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
 * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
 */

/**
 * @author Gilman Tolle <get at cs.berkeley.edu>
 */

enum {
    AM_GROUPERCMDMSG = 20,
    SERIAL_ID_LEN = 8,
    GROUPER_JOIN = 1,
    GROUPER_LEAVE = 2,
};

typedef struct GrouperCmdMsg {
  uint8_t serialID[SERIAL_ID_LEN]; 
  uint16_t groupID;
  uint16_t timeout;
  uint8_t op;
} GrouperCmdMsg;

--- NEW FILE: GrouperC.nc ---
//$Id: GrouperC.nc,v 1.1 2005/10/27 21:31:04 gtolle Exp $

/*									tab:4
 * "Copyright (c) 2000-2005 The Regents of the University  of California.  
 * All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice, the following
 * two paragraphs and the author appear in all copies of this software.
 * 
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
 * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
 */

includes Grouper;
includes Ident;

/**
 * This component provides the ability to remotely set group
 * membership for nodes.
 *
 * @author Gilman Tolle <get at cs.berkeley.edu>
 */

configuration GrouperC {
  provides interface StdControl;
}

implementation {

  components 
    GrouperM,
    GroupManagerC,
    DripC, 
    DripStateC,
    GenericComm,
    LedsC;

#if defined(PLATFORM_TELOSB)
  components DS2411C;
#endif

  StdControl = GrouperM;

  GrouperM.ReceiveMsg -> GenericComm.ReceiveMsg[AM_GROUPERCMDMSG];
  GrouperM.Receive -> DripC.Receive[AM_GROUPERCMDMSG];
  GrouperM.Drip -> DripC.Drip[AM_GROUPERCMDMSG];
  DripC.DripState[AM_GROUPERCMDMSG] -> DripStateC.DripState[unique("DripState")];

#if defined(PLATFORM_TELOSB)
  GrouperM.DS2411 -> DS2411C;
#endif

  GrouperM.GroupManager -> GroupManagerC;
  GrouperM.Leds -> LedsC;
}

--- NEW FILE: GrouperM.nc ---
//$Id: GrouperM.nc,v 1.1 2005/10/27 21:31:04 gtolle Exp $

/*									tab:4
 * "Copyright (c) 2000-2005 The Regents of the University  of California.  
 * All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice, the following
 * two paragraphs and the author appear in all copies of this software.
 * 
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
 * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
 */

/**
 * @author Gilman Tolle <get at cs.berkeley.edu>
 */

module GrouperM {
  provides {
    interface StdControl;
  }
  uses {
    interface ReceiveMsg;
    interface Receive;
    interface Drip;

#if defined(PLATFORM_TELOSB)
    interface DS2411;
#endif

    interface Leds;

    interface GroupManager;
  }
}

implementation {

  GrouperCmdMsg cmdMsgCache;

  void processCmd(GrouperCmdMsg *cmdMsg);

  command result_t StdControl.init() {
    return SUCCESS;
  }
  command result_t StdControl.start() {
    call Drip.init();
    return SUCCESS;
  }
  command result_t StdControl.stop() {
    return SUCCESS;
  }

  event TOS_MsgPtr ReceiveMsg.receive(TOS_MsgPtr msg) {
    processCmd((GrouperCmdMsg*) msg->data);
    return msg;
  }

  event TOS_MsgPtr Receive.receive(TOS_MsgPtr msg, void* payload, 
				   uint16_t payloadLen) {
    GrouperCmdMsg *cmdMsg = (GrouperCmdMsg *) payload;
    memcpy(&cmdMsgCache, cmdMsg, sizeof(cmdMsgCache));
    processCmd(cmdMsg);
    return msg;
  }

  void processCmd(GrouperCmdMsg *cmdMsg) {

#if defined(PLATFORM_TELOSB)

    uint8_t i;
    uint8_t id[SERIAL_ID_LEN];
    
    memset(&id[0], 0, SERIAL_ID_LEN);

    call DS2411.copy_id(&id[0]);

    for(i = 0; i < SERIAL_ID_LEN; i++) {
      if (cmdMsg->serialID[i] != id[i]) {
	// not for me
	return;
      }
    }
#endif

    // for me. change my group ID
    switch (cmdMsg->op) {

    case GROUPER_JOIN:
      if (call GroupManager.joinGroup(cmdMsg->groupID, cmdMsg->timeout)) {
	return;
      }
      break;
    case GROUPER_LEAVE:
      if (call GroupManager.leaveGroup(cmdMsg->groupID)) {
	return;
      }
      break;
    default:
      // do nothing.
    }
  }

  event result_t Drip.rebroadcastRequest(TOS_MsgPtr msg, void* payload) {

    GrouperCmdMsg *cmdMsg = (GrouperCmdMsg *) payload;
    memcpy(cmdMsg, &cmdMsgCache, sizeof(cmdMsgCache));
    call Drip.rebroadcast(msg, payload, sizeof(cmdMsgCache));
    return SUCCESS;
  }
}




--- NEW FILE: README ---
Drain is a collection routing layer for TinyOS.
Author: Gilman Tolle <get at cs.berkeley.edu>

== Mote Side ==

Add the directory to your Makefile:

  CFLAGS += -I$(TOSDIR)/../beta/Drain

Wire it into your application:

  includes Drain;

  TestAppM.Send -> DrainC.Send[AM_TESTAPPMSG];
  TestAppM.SendMsg -> DrainC.SendMsg[AM_TESTAPPMSG];

When sending a message, you need to use both interfaces:

  uint16_t bufLen;
  TestAppMsg* msg = (TestAppMsg*)
      call Send.getBuffer(&buf, &bufLen);

  call SendMsg.send(destAddr, sizeof(TestAppMsg), &buf);    

Drain includes a destination address, meaning a node can belong to
more than one collection tree. When you just want to send to the
default collection tree, use TOS_DEFAULT_ADDR, defined in Drain.h to
be 0.

== Host Side ==

Instead of working through node 0, Drain works through a TOSBase (and
only through a TOSBase). For reliability, Drain requires that the
TOSBase have link-layer packet acknowledgements turned on. The Drain
directory includes a version of TOSBase, called DrainBase, that
enables ACKs. Otherwise, it bridges packets exactly like TOSBase.

Choose an address for your DrainBase. You need to do this, because
link-layer ACKs require an address. I tend to use 65534. Compile and
install your DrainBase on a mote.

  make <platform> install,65534

Attach the DrainBase through SerialForwarder

  java net.tinyos.sf.SerialForwarder -comm serial at COM???:telos

Drain doesn't build a routing tree until you run a host-side
tool. This tool is written in Java, and located in
Drain/tools/java/net/tinyos/drain. Add this to your classpath:

  export CLASSPATH="$CLASSPATH;c:/cygwin/opt/tinyos-1.x/beta/Drain/tools/java/"

The Drain java tool needs to know the address of the
DrainBase. Currently, you have to export the MOTEID environment
variable before running the Drain tool. If you forget this, you may
not receive any packets. Just set it and run the tool again.

  export MOTEID=65534

Now, to build a Drain tree, run the tool:

  java net.tinyos.drain.Drain

A tree-building message will propagate through the network, and you
will have a collection tree.

You can also perform this programmatically:

  Drain drain = new Drain();
  drain.buildTree();

Then, you need to receive messages from the Drain tree. Use the
DrainConnector java tool.

  class MyClass implements MessageListener {
   
    public MyClass() {
      private DrainConnector drainConnector = new DrainConnector();
      drainConnector.registerListener(TestAppConsts.AM_TESTAPPMSG, this);
    }
 
    public void messageReceived(int to, Message m) {
      DrainMsg drainMsg = (DrainMsg) m;

      TestAppMsg testAppMsg = 
        new TestAppMsg(drainMsg, 
                       drainMsg.offset_data(0),
                       drainMsg.dataLength() - drainMsg.offset_data(0));

      testAppMsg.get_something();
    }

If you want the tree to adapt to changes in the network, execute the
Drain tool every so often. 30-60 seconds seems like a reasonable
period.

If you want to sniff the packets arriving over the Drain tree, you can
run the DrainSniff tool:

  java net.tinyos.drain.DrainSniff

The output is one packet per line.



	







More information about the Tinyos-commits mailing list