Merge pull request #158 from g-oikonomou/trickle-library
Trickle Timer Library (RFC 6206)
This commit is contained in:
commit
d87f0cf42a
|
@ -59,7 +59,7 @@ include $(CONTIKI)/core/net/mac/Makefile.mac
|
||||||
SYSTEM = process.c procinit.c autostart.c elfloader.c profile.c \
|
SYSTEM = process.c procinit.c autostart.c elfloader.c profile.c \
|
||||||
timetable.c timetable-aggregate.c compower.c serial-line.c
|
timetable.c timetable-aggregate.c compower.c serial-line.c
|
||||||
THREADS = mt.c
|
THREADS = mt.c
|
||||||
LIBS = memb.c mmem.c timer.c list.c etimer.c ctimer.c energest.c rtimer.c stimer.c \
|
LIBS = memb.c mmem.c timer.c list.c etimer.c ctimer.c energest.c rtimer.c stimer.c trickle-timer.c \
|
||||||
print-stats.c ifft.c crc16.c random.c checkpoint.c ringbuf.c settings.c
|
print-stats.c ifft.c crc16.c random.c checkpoint.c ringbuf.c settings.c
|
||||||
DEV = nullradio.c
|
DEV = nullradio.c
|
||||||
|
|
||||||
|
|
407
core/lib/trickle-timer.c
Executable file
407
core/lib/trickle-timer.c
Executable file
|
@ -0,0 +1,407 @@
|
||||||
|
/**
|
||||||
|
* \addtogroup trickle-timer
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file
|
||||||
|
* Trickle timer library implementation.
|
||||||
|
* \author
|
||||||
|
* George Oikonomou - <oikonomou@users.sourceforge.net>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2012, George Oikonomou - <oikonomou@users.sourceforge.net>
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the copyright holder nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived
|
||||||
|
* from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||||
|
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
#include "contiki-conf.h"
|
||||||
|
#include "lib/trickle-timer.h"
|
||||||
|
#include "sys/ctimer.h"
|
||||||
|
#include "sys/cc.h"
|
||||||
|
#include "lib/random.h"
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
#define DEBUG 0
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
#include <stdio.h>
|
||||||
|
#define PRINTF(...) printf(__VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define PRINTF(...)
|
||||||
|
#endif
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
/**
|
||||||
|
* \brief Wide randoms for platforms using a 4-byte wide clock
|
||||||
|
* (see ::TRICKLE_TIMER_WIDE_RAND)
|
||||||
|
*/
|
||||||
|
#if TRICKLE_TIMER_WIDE_RAND
|
||||||
|
#define tt_rand() wide_rand()
|
||||||
|
#else
|
||||||
|
#define tt_rand() random_rand()
|
||||||
|
#endif
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
/* Declarations of variables of local interest */
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
static struct trickle_timer *loctt; /* Pointer to a struct for local use */
|
||||||
|
static clock_time_t loc_clock; /* A local, general-purpose placeholder */
|
||||||
|
|
||||||
|
static void fire(void *ptr);
|
||||||
|
static void double_interval(void *ptr);
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
/* Local utilities and functions to be used as ctimer callbacks */
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
#if TRICKLE_TIMER_WIDE_RAND
|
||||||
|
/* Returns a 4-byte wide, unsigned random number */
|
||||||
|
static uint32_t
|
||||||
|
wide_rand()
|
||||||
|
{
|
||||||
|
return ((uint32_t)random_rand() << 16 | random_rand());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
/*
|
||||||
|
* Returns the maximum sane Imax value for a given Imin
|
||||||
|
*
|
||||||
|
* This function is a variant of a fairly standard 'Count Leading Zeros'. It
|
||||||
|
* has three flavours. The most suitable one for a specific platform can be
|
||||||
|
* configured by changing the value of TRICKLE_TIMER_CONF_MAX_IMAX_WIDTH
|
||||||
|
* in the platform's contiki-conf.h
|
||||||
|
*/
|
||||||
|
#if TRICKLE_TIMER_ERROR_CHECKING
|
||||||
|
static uint8_t
|
||||||
|
max_imax(clock_time_t value)
|
||||||
|
{
|
||||||
|
uint8_t zeros = 0;
|
||||||
|
#if (TRICKLE_TIMER_MAX_IMAX_WIDTH==TRICKLE_TIMER_MAX_IMAX_GENERIC)
|
||||||
|
uint8_t i;
|
||||||
|
clock_time_t mask = 0xFFFF;
|
||||||
|
|
||||||
|
value--;
|
||||||
|
|
||||||
|
for(i = sizeof(clock_time_t) << 2; i > 0; i >>= 1) {
|
||||||
|
if((value & (mask <<= i)) == 0) {
|
||||||
|
zeros += i;
|
||||||
|
value <<= i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif (TRICKLE_TIMER_MAX_IMAX_WIDTH==TRICKLE_TIMER_MAX_IMAX_16_BIT)
|
||||||
|
if((value & 0xFF00) == 0) {
|
||||||
|
zeros += 8;
|
||||||
|
value <<= 8;
|
||||||
|
}
|
||||||
|
if((value & 0xF000) == 0) {
|
||||||
|
zeros += 4;
|
||||||
|
value <<= 4;
|
||||||
|
}
|
||||||
|
if((value & 0xC000) == 0) {
|
||||||
|
zeros += 2;
|
||||||
|
value <<= 2;
|
||||||
|
}
|
||||||
|
if((value & 0x8000) == 0) {
|
||||||
|
zeros++;
|
||||||
|
}
|
||||||
|
#elif (TRICKLE_TIMER_MAX_IMAX_WIDTH==TRICKLE_TIMER_MAX_IMAX_32_BIT)
|
||||||
|
if((value & 0xFFFF0000) == 0) {
|
||||||
|
zeros += 16;
|
||||||
|
value <<= 16;
|
||||||
|
}
|
||||||
|
if((value & 0xFF000000) == 0) {
|
||||||
|
zeros += 8;
|
||||||
|
value <<= 8;
|
||||||
|
}
|
||||||
|
if((value & 0xF0000000) == 0) {
|
||||||
|
zeros += 4;
|
||||||
|
value <<= 4;
|
||||||
|
}
|
||||||
|
if((value & 0xC0000000) == 0) {
|
||||||
|
zeros += 2;
|
||||||
|
value <<= 2;
|
||||||
|
}
|
||||||
|
if((value & 0x80000000) == 0) {
|
||||||
|
zeros += 1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return zeros - 1; /* Always non-negative due to the range of 'value' */
|
||||||
|
}
|
||||||
|
#endif /* TRICKLE_TIMER_ERROR_CHECKING */
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
/* Returns a random time point t in [I/2 , I) */
|
||||||
|
static clock_time_t
|
||||||
|
get_t(clock_time_t i_cur)
|
||||||
|
{
|
||||||
|
i_cur >>= 1;
|
||||||
|
|
||||||
|
PRINTF("trickle_timer get t: [%lu, %lu)\n", (unsigned long)i_cur,
|
||||||
|
(unsigned long)(i_cur << 1));
|
||||||
|
|
||||||
|
return i_cur + (tt_rand() % i_cur);
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
static void
|
||||||
|
schedule_for_end(struct trickle_timer *tt)
|
||||||
|
{
|
||||||
|
/* Reset our ctimer, schedule interval_end to run at time I */
|
||||||
|
clock_time_t now = clock_time();
|
||||||
|
|
||||||
|
loc_clock = TRICKLE_TIMER_INTERVAL_END(tt) - now;
|
||||||
|
|
||||||
|
PRINTF("trickle_timer sched for end: at %lu, end in %ld\n",
|
||||||
|
(unsigned long)clock_time(), (signed long)loc_clock);
|
||||||
|
|
||||||
|
/* Interval's end will happen in loc_clock ticks. Make sure this isn't in
|
||||||
|
* the past... */
|
||||||
|
if(loc_clock > (TRICKLE_TIMER_CLOCK_MAX >> 1)) {
|
||||||
|
loc_clock = 0; /* Interval ended in the past, schedule for in 0 */
|
||||||
|
PRINTF("trickle_timer doubling: Was in the past. Compensating\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
ctimer_set(&tt->ct, loc_clock, double_interval, tt);
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
/* This is used as a ctimer callback, thus its argument must be void *. ptr is
|
||||||
|
* a pointer to the struct trickle_timer that fired */
|
||||||
|
static void
|
||||||
|
double_interval(void *ptr)
|
||||||
|
{
|
||||||
|
clock_time_t last_end;
|
||||||
|
|
||||||
|
/* 'cast' ptr to a struct trickle_timer */
|
||||||
|
loctt = (struct trickle_timer *)ptr;
|
||||||
|
|
||||||
|
loctt->c = 0;
|
||||||
|
|
||||||
|
PRINTF("trickle_timer doubling: at %lu, (was for %lu), ",
|
||||||
|
(unsigned long)clock_time(),
|
||||||
|
(unsigned long)TRICKLE_TIMER_INTERVAL_END(loctt));
|
||||||
|
|
||||||
|
/* Remember the previous interval's end (absolute time), before we double */
|
||||||
|
last_end = TRICKLE_TIMER_INTERVAL_END(loctt);
|
||||||
|
|
||||||
|
/* Double the interval if we have to */
|
||||||
|
if(loctt->i_cur <= TRICKLE_TIMER_INTERVAL_MAX(loctt) >> 1) {
|
||||||
|
/* If I <= Imax/2, we double */
|
||||||
|
loctt->i_cur <<= 1;
|
||||||
|
PRINTF("I << 1 = %lu\n", (unsigned long)loctt->i_cur);
|
||||||
|
} else {
|
||||||
|
/* We may have I > Imax/2 but I <> Imax, in which case we set to Imax
|
||||||
|
* This will happen when I didn't start as Imin (before the first reset) */
|
||||||
|
loctt->i_cur = TRICKLE_TIMER_INTERVAL_MAX(loctt);
|
||||||
|
PRINTF("I = Imax = %lu\n", (unsigned long)loctt->i_cur);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Random t in [I/2, I) */
|
||||||
|
loc_clock = get_t(loctt->i_cur);
|
||||||
|
|
||||||
|
PRINTF("trickle_timer doubling: t=%lu\n", (unsigned long)loc_clock);
|
||||||
|
|
||||||
|
#if TRICKLE_TIMER_COMPENSATE_DRIFT
|
||||||
|
/* Schedule for t ticks after the previous interval's end, not after now. If
|
||||||
|
* that is in the past, schedule in 0 */
|
||||||
|
loc_clock = (last_end + loc_clock) - clock_time();
|
||||||
|
PRINTF("trickle_timer doubling: at %lu, in %ld ticks\n",
|
||||||
|
(unsigned long)clock_time(), (signed long)loc_clock);
|
||||||
|
if(loc_clock > (TRICKLE_TIMER_CLOCK_MAX >> 1)) {
|
||||||
|
/* Oops, that's in the past */
|
||||||
|
loc_clock = 0;
|
||||||
|
PRINTF("trickle_timer doubling: Was in the past. Compensating\n");
|
||||||
|
}
|
||||||
|
ctimer_set(&loctt->ct, loc_clock, fire, loctt);
|
||||||
|
|
||||||
|
/* Store the actual interval start (absolute time), we need it later.
|
||||||
|
* We pretend that it started at the same time when the last one ended */
|
||||||
|
loctt->i_start = last_end;
|
||||||
|
#else
|
||||||
|
/* Assumed that the previous interval's end is 'now' and schedule in t ticks
|
||||||
|
* after 'now', ignoring potential offsets */
|
||||||
|
ctimer_set(&loctt->ct, loc_clock, fire, loctt);
|
||||||
|
/* Store the actual interval start (absolute time), we need it later */
|
||||||
|
loctt->i_start = loctt->ct.etimer.timer.start;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
PRINTF("trickle_timer doubling: Last end %lu, new end %lu, for %lu, I=%lu\n",
|
||||||
|
(unsigned long)last_end,
|
||||||
|
(unsigned long)TRICKLE_TIMER_INTERVAL_END(loctt),
|
||||||
|
(unsigned long)(loctt->ct.etimer.timer.start +
|
||||||
|
loctt->ct.etimer.timer.interval),
|
||||||
|
(unsigned long)(loctt->i_cur));
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
/* Called by the ctimer module at time t within the current interval. ptr is
|
||||||
|
* a pointer to the struct trickle_timer of interest */
|
||||||
|
static void
|
||||||
|
fire(void *ptr)
|
||||||
|
{
|
||||||
|
/* 'cast' c to a struct trickle_timer */
|
||||||
|
loctt = (struct trickle_timer *)ptr;
|
||||||
|
|
||||||
|
PRINTF("trickle_timer fire: at %lu (was for %lu)\n",
|
||||||
|
(unsigned long)clock_time(),
|
||||||
|
(unsigned long)(loctt->ct.etimer.timer.start +
|
||||||
|
loctt->ct.etimer.timer.interval));
|
||||||
|
|
||||||
|
if(loctt->cb) {
|
||||||
|
/*
|
||||||
|
* Call the protocol's TX callback, with the suppression status as an
|
||||||
|
* argument.
|
||||||
|
*/
|
||||||
|
PRINTF("trickle_timer fire: Suppression Status %u (%u < %u)\n",
|
||||||
|
TRICKLE_TIMER_PROTO_TX_ALLOW(loctt), loctt->c, loctt->k);
|
||||||
|
loctt->cb(loctt->cb_arg, TRICKLE_TIMER_PROTO_TX_ALLOW(loctt));
|
||||||
|
}
|
||||||
|
|
||||||
|
schedule_for_end(loctt);
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
/* New trickle interval, either due to a newly set trickle timer or due to an
|
||||||
|
* inconsistency. Schedule 'fire' to be called in t ticks. */
|
||||||
|
static void
|
||||||
|
new_interval(struct trickle_timer *tt)
|
||||||
|
{
|
||||||
|
tt->c = 0;
|
||||||
|
|
||||||
|
/* Random t in [I/2, I) */
|
||||||
|
loc_clock = get_t(tt->i_cur);
|
||||||
|
|
||||||
|
ctimer_set(&tt->ct, loc_clock, fire, tt);
|
||||||
|
|
||||||
|
/* Store the actual interval start (absolute time), we need it later */
|
||||||
|
tt->i_start = tt->ct.etimer.timer.start;
|
||||||
|
PRINTF("trickle_timer new interval: at %lu, ends %lu, ",
|
||||||
|
(unsigned long)clock_time(),
|
||||||
|
(unsigned long)TRICKLE_TIMER_INTERVAL_END(tt));
|
||||||
|
PRINTF("t=%lu, I=%lu\n", (unsigned long)loc_clock, (unsigned long)tt->i_cur);
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
/* Functions to be called by the protocol implementation */
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
void
|
||||||
|
trickle_timer_consistency(struct trickle_timer *tt)
|
||||||
|
{
|
||||||
|
if(tt->c < 0xFF) {
|
||||||
|
tt->c++;
|
||||||
|
}
|
||||||
|
PRINTF("trickle_timer consistency: c=%u\n", tt->c);
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
void
|
||||||
|
trickle_timer_inconsistency(struct trickle_timer *tt)
|
||||||
|
{
|
||||||
|
/* "If I is equal to Imin when Trickle hears an "inconsistent" transmission,
|
||||||
|
* Trickle does nothing." */
|
||||||
|
if(tt->i_cur != tt->i_min) {
|
||||||
|
PRINTF("trickle_timer inconsistency\n");
|
||||||
|
tt->i_cur = tt->i_min;
|
||||||
|
|
||||||
|
new_interval(tt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
uint8_t
|
||||||
|
trickle_timer_config(struct trickle_timer *tt, clock_time_t i_min,
|
||||||
|
uint8_t i_max, uint8_t k)
|
||||||
|
{
|
||||||
|
#if TRICKLE_TIMER_ERROR_CHECKING
|
||||||
|
/*
|
||||||
|
* Although in theory Imin=1 is a valid value, it would break get_t() and
|
||||||
|
* doesn't make sense anyway. Thus Valid Imin values are in the range:
|
||||||
|
* 1 < Imin <= (TRICKLE_TIMER_CLOCK_MAX >> 1) + 1
|
||||||
|
*/
|
||||||
|
if(TRICKLE_TIMER_IMIN_IS_BAD(i_min)) {
|
||||||
|
PRINTF("trickle_timer config: Bad Imin value\n");
|
||||||
|
return TRICKLE_TIMER_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(tt == NULL || i_max == 0 || k == 0) {
|
||||||
|
PRINTF("trickle_timer config: Bad arguments\n");
|
||||||
|
return TRICKLE_TIMER_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If clock_time_t is not wide enough to store Imin << Imax, we adjust Imax
|
||||||
|
*
|
||||||
|
* This means that 'we' are likely to have a different Imax than 'them'
|
||||||
|
* See RFC 6206, sec 6.3 for the consequences of this situation
|
||||||
|
*/
|
||||||
|
if(TRICKLE_TIMER_IPAIR_IS_BAD(i_min, i_max)) {
|
||||||
|
PRINTF("trickle_timer config: %lu << %u would exceed clock boundaries. ",
|
||||||
|
(unsigned long)i_min, i_max);
|
||||||
|
|
||||||
|
/* For this Imin, get the maximum sane Imax */
|
||||||
|
i_max = max_imax(i_min);
|
||||||
|
PRINTF("trickle_timer config: Using Imax=%u\n", i_max);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
tt->i_min = i_min;
|
||||||
|
tt->i_max = i_max;
|
||||||
|
tt->i_max_abs = i_min << i_max;
|
||||||
|
tt->k = k;
|
||||||
|
|
||||||
|
PRINTF("trickle_timer config: Imin=%lu, Imax=%u, k=%u\n",
|
||||||
|
(unsigned long)tt->i_min, tt->i_max, tt->k);
|
||||||
|
|
||||||
|
return TRICKLE_TIMER_SUCCESS;
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
uint8_t
|
||||||
|
trickle_timer_set(struct trickle_timer *tt, trickle_timer_cb_t proto_cb,
|
||||||
|
void *ptr)
|
||||||
|
{
|
||||||
|
#if TRICKLE_TIMER_ERROR_CHECKING
|
||||||
|
/* Sanity checks */
|
||||||
|
if(tt == NULL || proto_cb == NULL) {
|
||||||
|
PRINTF("trickle_timer set: Bad arguments\n");
|
||||||
|
return TRICKLE_TIMER_ERROR;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
tt->cb = proto_cb;
|
||||||
|
tt->cb_arg = ptr;
|
||||||
|
|
||||||
|
/* Random I in [Imin , Imax] */
|
||||||
|
tt->i_cur = tt->i_min +
|
||||||
|
(tt_rand() % (TRICKLE_TIMER_INTERVAL_MAX(tt) - tt->i_min + 1));
|
||||||
|
|
||||||
|
PRINTF("trickle_timer set: I=%lu in [%lu , %lu]\n", (unsigned long)tt->i_cur,
|
||||||
|
(unsigned long)tt->i_min,
|
||||||
|
(unsigned long)TRICKLE_TIMER_INTERVAL_MAX(tt));
|
||||||
|
|
||||||
|
new_interval(tt);
|
||||||
|
|
||||||
|
PRINTF("trickle_timer set: at %lu, ends %lu, t=%lu in [%lu , %lu)\n",
|
||||||
|
(unsigned long)tt->i_start,
|
||||||
|
(unsigned long)TRICKLE_TIMER_INTERVAL_END(tt),
|
||||||
|
(unsigned long)tt->ct.etimer.timer.interval,
|
||||||
|
(unsigned long)tt->i_cur >> 1, (unsigned long)tt->i_cur);
|
||||||
|
|
||||||
|
return TRICKLE_TIMER_SUCCESS;
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
/** @} */
|
491
core/lib/trickle-timer.h
Executable file
491
core/lib/trickle-timer.h
Executable file
|
@ -0,0 +1,491 @@
|
||||||
|
/** \addtogroup lib
|
||||||
|
* @{ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \defgroup trickle-timer Trickle timers
|
||||||
|
*
|
||||||
|
* This library implements timers which behave in accordance with RFC 6206
|
||||||
|
* "The Trickle Algorithm" (http://tools.ietf.org/html/rfc6206)
|
||||||
|
*
|
||||||
|
* Protocols wishing to use trickle timers, may use this library instead of
|
||||||
|
* implementing the trickle algorithm internally.
|
||||||
|
*
|
||||||
|
* The protocol implementation will declare one (or more) variable(s) of type
|
||||||
|
* struct ::trickle_timer and will then populate its fields by calling
|
||||||
|
* trickle_timer_config(). trickle_timer_set() will start the timer.
|
||||||
|
*
|
||||||
|
* When the timer reaches time t within the current trickle interval, the
|
||||||
|
* library will call a protocol-provided callback, which will signal to the
|
||||||
|
* protocol that it is time to TX (see algorithm step 4 in the RFC).
|
||||||
|
*
|
||||||
|
* The proto does not need to check the suppression conditions. This is done by
|
||||||
|
* the library and if TX must be suppressed, the callback won't be called at
|
||||||
|
* all.
|
||||||
|
*
|
||||||
|
* The library also provides functions to be called when the protocol hears a
|
||||||
|
* 'consistent' or 'inconsistent' message and when an 'external event' occurs
|
||||||
|
* (in this context, those terms have the exact same meaning as in the RFC).
|
||||||
|
*
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file
|
||||||
|
* Trickle timer library header file.
|
||||||
|
*
|
||||||
|
* \author
|
||||||
|
* George Oikonomou - <oikonomou@users.sourceforge.net>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2012, George Oikonomou - <oikonomou@users.sourceforge.net>
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the copyright holder nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived
|
||||||
|
* from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||||
|
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
#ifndef __TRICKLE_TIMER_H__
|
||||||
|
#define __TRICKLE_TIMER_H__
|
||||||
|
|
||||||
|
#include "contiki-conf.h"
|
||||||
|
#include "sys/ctimer.h"
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
/* Trickle Timer Library Constants */
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
/**
|
||||||
|
* \name Trickle Timer Library: Constants
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
/**
|
||||||
|
* \brief Set as value of k to disable suppression
|
||||||
|
*/
|
||||||
|
#define TRICKLE_TIMER_INFINITE_REDUNDANCY 0x00
|
||||||
|
|
||||||
|
#define TRICKLE_TIMER_ERROR 0
|
||||||
|
#define TRICKLE_TIMER_SUCCESS 1
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Use as the value of TRICKLE_TIMER_MAX_IMAX_WIDTH to enable the
|
||||||
|
* generic 'Find max Imax' routine
|
||||||
|
*/
|
||||||
|
#define TRICKLE_TIMER_MAX_IMAX_GENERIC 0
|
||||||
|
/**
|
||||||
|
* \brief Use as the value of TRICKLE_TIMER_MAX_IMAX_WIDTH to enable the 16-bit
|
||||||
|
* 'Find max Imax' routine
|
||||||
|
*/
|
||||||
|
#define TRICKLE_TIMER_MAX_IMAX_16_BIT 1
|
||||||
|
/**
|
||||||
|
* \brief Use as the value of TRICKLE_TIMER_MAX_IMAX_WIDTH to enable the 32-bit
|
||||||
|
* 'Find max Imax' routine
|
||||||
|
*/
|
||||||
|
#define TRICKLE_TIMER_MAX_IMAX_32_BIT 2
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Constants used as values for the \e suppress param of
|
||||||
|
* trickle_timer_cb_t
|
||||||
|
*/
|
||||||
|
#define TRICKLE_TIMER_TX_SUPPRESS 0
|
||||||
|
#define TRICKLE_TIMER_TX_OK 1
|
||||||
|
/** @} */
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
/**
|
||||||
|
* \brief Controls whether the library will try to compensate for time drifts
|
||||||
|
* caused by getting called later than scheduled.
|
||||||
|
*
|
||||||
|
* 1: Enabled (default). 0: Disabled
|
||||||
|
*
|
||||||
|
* To override the default, define TRICKLE_TIMER_CONF_COMPENSATE_DRIFT in
|
||||||
|
* contiki-conf.h
|
||||||
|
*
|
||||||
|
* Bear in mind that this controls the behaviour of the entire library (i.e.
|
||||||
|
* all trickle timers) and not of an individual timer
|
||||||
|
*/
|
||||||
|
#ifdef TRICKLE_TIMER_CONF_COMPENSATE_DRIFT
|
||||||
|
#define TRICKLE_TIMER_COMPENSATE_DRIFT TRICKLE_TIMER_CONF_COMPENSATE_DRIFT
|
||||||
|
#else
|
||||||
|
#define TRICKLE_TIMER_COMPENSATE_DRIFT 1
|
||||||
|
#endif
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
/**
|
||||||
|
* \brief Turns on support for 4-byte wide, unsigned random numbers
|
||||||
|
*
|
||||||
|
* We need this for platforms which have a 4-byte wide clock_time_t. For those
|
||||||
|
* platforms and if Imin << Imax is GT 0xFFFF, random_rand alone is not always
|
||||||
|
* enough to generate a correct t in [I/2, I). Specifically, we need wide
|
||||||
|
* randoms when I > 0x1FFFF.
|
||||||
|
*
|
||||||
|
* For platforms with a 2-byte wide clock_time_t, this can be defined as 0
|
||||||
|
* to reduce code footprint and increase speed.
|
||||||
|
*/
|
||||||
|
#ifdef TRICKLE_TIMER_CONF_WIDE_RAND
|
||||||
|
#define TRICKLE_TIMER_WIDE_RAND TRICKLE_TIMER_CONF_WIDE_RAND
|
||||||
|
#else
|
||||||
|
#define TRICKLE_TIMER_WIDE_RAND 1
|
||||||
|
#endif
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
/**
|
||||||
|
* \brief Selects a flavor for the 'Find maximum Imax' (max_imax) function.
|
||||||
|
*
|
||||||
|
* When configuring a new trickle timer, the library verifies that the (Imin ,
|
||||||
|
* Imax) pair will fit in the platform's clock_time_t boundaries. If this is
|
||||||
|
* not the case, Imax will be adjusted down. In order to achieve this, we use
|
||||||
|
* an internal function which is a slight variant of a standard 'Count Leading
|
||||||
|
* Zeros'.
|
||||||
|
*
|
||||||
|
* This functionality is disabled when ::TRICKLE_TIMER_ERROR_CHECKING is 0
|
||||||
|
*
|
||||||
|
* This define helps us generate, at the pre-processing stage, the desired
|
||||||
|
* version of this function. We start with a generic version by default. The
|
||||||
|
* platform's contiki-conf.h can change this to use the 16- or 32-bit specific
|
||||||
|
* flavor by defining TRICKLE_TIMER_CONF_MAX_IMAX_WIDTH.
|
||||||
|
*
|
||||||
|
* Depending on the toolchain, the generic variant may actually result in a
|
||||||
|
* smaller code size. Do your own experiments.
|
||||||
|
*
|
||||||
|
* TRICKLE_TIMER_MAX_IMAX_GENERIC (0): Generic function which will work
|
||||||
|
* regardless whether the platform uses a 16 or 32 bit wide clock type
|
||||||
|
*
|
||||||
|
* TRICKLE_TIMER_MAX_IMAX_16_BIT (1): You can select this in your
|
||||||
|
* contiki-conf.h if your platform's clock_time_t is 16 bits wide
|
||||||
|
*
|
||||||
|
* TRICKLE_TIMER_MAX_IMAX_32_BIT (2): You can select this in your
|
||||||
|
* contiki-conf.h if your platform's clock_time_t is 32 bits wide
|
||||||
|
*/
|
||||||
|
#ifdef TRICKLE_TIMER_CONF_MAX_IMAX_WIDTH
|
||||||
|
#define TRICKLE_TIMER_MAX_IMAX_WIDTH TRICKLE_TIMER_CONF_MAX_IMAX_WIDTH
|
||||||
|
#else
|
||||||
|
#define TRICKLE_TIMER_MAX_IMAX_WIDTH TRICKLE_TIMER_MAX_IMAX_GENERIC
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Enables/Disables error checking
|
||||||
|
*
|
||||||
|
* 1: Enabled (default). The library checks the validity of Imin and Imax
|
||||||
|
* 0: Disabled. All error checking is turned off. This reduces code size.
|
||||||
|
*/
|
||||||
|
#ifdef TRICKLE_TIMER_CONF_ERROR_CHECKING
|
||||||
|
#define TRICKLE_TIMER_ERROR_CHECKING TRICKLE_TIMER_CONF_ERROR_CHECKING
|
||||||
|
#else
|
||||||
|
#define TRICKLE_TIMER_ERROR_CHECKING 1
|
||||||
|
#endif
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
/* Trickle Timer Library Macros */
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
/**
|
||||||
|
* \name Trickle Timer Library: Macros
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* \brief cross-platform method to get the maximum clock_time_t value
|
||||||
|
*/
|
||||||
|
#define TRICKLE_TIMER_CLOCK_MAX ((clock_time_t)~0)
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Checks if the trickle timer's suppression feature is enabled
|
||||||
|
*
|
||||||
|
* \param tt A pointer to a ::trickle_timer structure
|
||||||
|
*
|
||||||
|
* \retval non-zero Suppression is enabled
|
||||||
|
* \retval 0 Suppression is disabled
|
||||||
|
*/
|
||||||
|
#define TRICKLE_TIMER_SUPPRESSION_ENABLED(tt) \
|
||||||
|
((tt)->k != TRICKLE_TIMER_INFINITE_REDUNDANCY)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Checks if the trickle timer's suppression feature is disabled
|
||||||
|
*
|
||||||
|
* \param tt A pointer to a ::trickle_timer structure
|
||||||
|
*
|
||||||
|
* \retval non-zero Suppression is disabled
|
||||||
|
* \retval 0 Suppression is enabled
|
||||||
|
*/
|
||||||
|
#define TRICKLE_TIMER_SUPPRESSION_DISABLED(tt) \
|
||||||
|
((tt)->k == TRICKLE_TIMER_INFINITE_REDUNDANCY)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Determines whether the protocol must go ahead with a transmission
|
||||||
|
*
|
||||||
|
* \param tt A pointer to a ::trickle_timer structure
|
||||||
|
*
|
||||||
|
* \retval non-zero Go ahead with TX
|
||||||
|
* \retval 0 Suppress
|
||||||
|
*/
|
||||||
|
#define TRICKLE_TIMER_PROTO_TX_ALLOW(tt) \
|
||||||
|
(TRICKLE_TIMER_SUPPRESSION_DISABLED(tt) || ((tt)->c < (tt)->k))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Determines whether the protocol must suppress a transmission
|
||||||
|
*
|
||||||
|
* \param tt A pointer to a ::trickle_timer structure
|
||||||
|
*
|
||||||
|
* \retval non-zero Suppress
|
||||||
|
* \retval 0 Go ahead with TX
|
||||||
|
*/
|
||||||
|
#define TRICKLE_TIMER_PROTO_TX_SUPPRESS(tt) \
|
||||||
|
(TRICKLE_TIMER_SUPPRESSION_ENABLED(tt) && ((tt)->c >= (tt)->k))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Returns a timer's maximum interval size (Imin << Imax) as a number of
|
||||||
|
* clock ticks
|
||||||
|
*
|
||||||
|
* \param tt A pointer to a ::trickle_timer structure
|
||||||
|
*
|
||||||
|
* \return Maximum trickle interval length in clock ticks
|
||||||
|
*/
|
||||||
|
#define TRICKLE_TIMER_INTERVAL_MAX(tt) ((tt)->i_max_abs)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Returns the current trickle interval's end (absolute time in ticks)
|
||||||
|
*
|
||||||
|
* \param tt A pointer to a ::trickle_timer structure
|
||||||
|
*
|
||||||
|
* \return The absolute number of clock ticks when the current trickle interval
|
||||||
|
* will expire
|
||||||
|
*/
|
||||||
|
#define TRICKLE_TIMER_INTERVAL_END(tt) ((tt)->i_start + (tt)->i_cur)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Checks whether an Imin value is suitable considering the various
|
||||||
|
* restrictions imposed by our platform's clock as well as by the library itself
|
||||||
|
*
|
||||||
|
* \param imin An Imin value in clock ticks
|
||||||
|
*
|
||||||
|
* \retval 1 The Imin value is valid
|
||||||
|
* \retval 0 The Imin value is invalid
|
||||||
|
*/
|
||||||
|
#define TRICKLE_TIMER_IMIN_IS_OK(imin) \
|
||||||
|
((imin > 1) && (i_min <= (TRICKLE_TIMER_CLOCK_MAX >> 1)))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Checks whether an Imin value is invalid considering the various
|
||||||
|
* restrictions imposed by our platform's clock as well as by the library itself
|
||||||
|
*
|
||||||
|
* \param imin An Imin value in clock ticks
|
||||||
|
*
|
||||||
|
* \retval 1 The Imin value is invalid
|
||||||
|
* \retval 0 The Imin value is valid
|
||||||
|
*/
|
||||||
|
#define TRICKLE_TIMER_IMIN_IS_BAD(imin) \
|
||||||
|
((imin < 2) || (i_min > (TRICKLE_TIMER_CLOCK_MAX >> 1)))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Checks whether Imin << Imax is unsuitable considering the boundaries
|
||||||
|
* of our platform's clock_time_t
|
||||||
|
*
|
||||||
|
* \param i_min Imin value
|
||||||
|
* \param i_max Maximum number of doublings
|
||||||
|
*
|
||||||
|
* \retval non-zero The pair would exceed clock boundaries
|
||||||
|
* \retval 0 The pair is suitable for the platform
|
||||||
|
*
|
||||||
|
* Timers scheduled far in the future can be perceived as being scheduled in
|
||||||
|
* the past.
|
||||||
|
* Thus, we limit Imin << Imax to be LEQ(TRICKLE_TIMER_CLOCK_MAX >> 1) + 1
|
||||||
|
*/
|
||||||
|
#define TRICKLE_TIMER_IPAIR_IS_BAD(i_min, i_max) \
|
||||||
|
((TRICKLE_TIMER_CLOCK_MAX >> (i_max + 1)) < i_min - 1)
|
||||||
|
/** @} */
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
/* Trickle Timer Library Data Representation */
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
/**
|
||||||
|
* \name Trickle Timer Library: Data Representation
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief typedef for a callback function to be defined in the protocol's
|
||||||
|
* implementation.
|
||||||
|
*
|
||||||
|
* Those callbaks are stored as a function pointer inside a ::trickle_timer
|
||||||
|
* structure and are called by the library at time t within the current trickle
|
||||||
|
* interval.
|
||||||
|
*
|
||||||
|
* Some protocols may rely on multiple trickle timers. For this reason, this
|
||||||
|
* function's argument will be an opaque pointer, aiming to help the protocol
|
||||||
|
* determine which timer triggered the call.
|
||||||
|
*
|
||||||
|
* The \e suppress argument is used so that the library can signal the protocol
|
||||||
|
* whether it should TX or suppress
|
||||||
|
*/
|
||||||
|
typedef void (* trickle_timer_cb_t)(void *ptr, uint8_t suppress);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \struct trickle_timer
|
||||||
|
*
|
||||||
|
* A trickle timer.
|
||||||
|
*
|
||||||
|
* This structure is used for declaring a trickle timer. In order for the timer
|
||||||
|
* to start running, the protocol must first populate the structure's fields
|
||||||
|
* by calling trickle_timer_set(). Protocol implementations must NOT modify the
|
||||||
|
* contents of this structure directly.
|
||||||
|
*
|
||||||
|
* Protocol developers must also be careful to specify the values of Imin and
|
||||||
|
* Imax in such a way that the maximum interval size does not exceed the
|
||||||
|
* boundaries of clock_time_t
|
||||||
|
*/
|
||||||
|
struct trickle_timer {
|
||||||
|
clock_time_t i_min; /**< Imin: Clock ticks */
|
||||||
|
clock_time_t i_cur; /**< I: Current interval in clock_ticks */
|
||||||
|
clock_time_t i_start; /**< Start of this interval (absolute clock_time) */
|
||||||
|
clock_time_t i_max_abs; /**< Maximum interval size in clock ticks (and not in
|
||||||
|
number of doublings). This is a cached value of
|
||||||
|
Imin << Imax used internally, so that we can
|
||||||
|
have direct access to the maximum interval size
|
||||||
|
without having to calculate it all the time */
|
||||||
|
struct ctimer ct; /**< A \ref ctimer used internally */
|
||||||
|
trickle_timer_cb_t cb; /**< Protocol's own callback, invoked at time t
|
||||||
|
within the current interval */
|
||||||
|
void *cb_arg; /**< Opaque pointer to be used as the argument of the
|
||||||
|
protocol's callback */
|
||||||
|
uint8_t i_max; /**< Imax: Max number of doublings */
|
||||||
|
uint8_t k; /**< k: Redundancy Constant */
|
||||||
|
uint8_t c; /**< c: Consistency Counter */
|
||||||
|
};
|
||||||
|
/** @} */
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
/* Trickle Timer Library Functions */
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
/**
|
||||||
|
* \name Trickle Timer Library: Functions called by protocol implementations
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Configure a trickle timer
|
||||||
|
* \param tt A pointer to a ::trickle_timer structure
|
||||||
|
* \param i_min The timer's Imin configuration parameter, in units of
|
||||||
|
* clock_time_t
|
||||||
|
* \param i_max The timer's Imax configuration parameter (maximum number of
|
||||||
|
* doublings), specified as number of doublings
|
||||||
|
* \param k The timer's K (redundancy constant). If the value of K
|
||||||
|
* equals #TRICKLE_TIMER_INFINITE_REDUNDANCY, message
|
||||||
|
* suppression will be disabled
|
||||||
|
* \retval 0 Error (Bad argument)
|
||||||
|
* \retval non-zero Success.
|
||||||
|
*
|
||||||
|
* This function is used to set the initial configuration for a trickle timer.
|
||||||
|
* A trickle timer MUST be configured before the protocol calls
|
||||||
|
* trickle_timer_set().
|
||||||
|
*
|
||||||
|
* If Imin<<Imax would exceed the platform's clock_time_t boundaries, this
|
||||||
|
* function adjusts Imax to the maximum permitted value for the provided Imin.
|
||||||
|
* This means that in a network with heterogenous hardware, 'we' are likely to
|
||||||
|
* have a different Imax than 'some of them'. See RFC 6206, sec 6.3 for the
|
||||||
|
* consequences of this situation
|
||||||
|
*/
|
||||||
|
uint8_t trickle_timer_config(struct trickle_timer *tt, clock_time_t i_min,
|
||||||
|
uint8_t i_max, uint8_t k);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Start a previously configured trickle timer
|
||||||
|
* \param tt A pointer to a ::trickle_timer structure
|
||||||
|
* \param proto_cb A pointer to a callback function, which will be invoked at
|
||||||
|
* at time t within the current trickle interval
|
||||||
|
* \param ptr An opaque pointer which will be passed as the argument to
|
||||||
|
* proto_cb when the timer fires.
|
||||||
|
* \retval 0 Error (tt was null or the timer was not configured properly)
|
||||||
|
* \retval non-zero Success.
|
||||||
|
*
|
||||||
|
* This function is used to set a trickle timer. The protocol implementation
|
||||||
|
* must use this function ONLY to initialise a new trickle timer and NOT to
|
||||||
|
* reset it as a result of external events or inconsistencies.
|
||||||
|
*
|
||||||
|
* The protocol implementation must configure the trickle timer by calling
|
||||||
|
* trickle_timer_config() before calling this function.
|
||||||
|
*/
|
||||||
|
uint8_t trickle_timer_set(struct trickle_timer *tt,
|
||||||
|
trickle_timer_cb_t proto_cb, void *ptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Stop a running trickle timer.
|
||||||
|
* \param tt A pointer to a ::trickle_timer structure
|
||||||
|
*
|
||||||
|
* This function stops a running trickle timer that was previously started with
|
||||||
|
* trickle_timer_start(). After this function has been called, the trickle
|
||||||
|
* timer will no longer call the protocol's callback and its interval will not
|
||||||
|
* double any more. In order to resume the trickle timer, the user application
|
||||||
|
* must call trickle_timer_set().
|
||||||
|
*
|
||||||
|
* The protocol implementation must NOT use trickle_timer_stop(), _set() cycles
|
||||||
|
* to reset a timer manually. Instead, in response to events or inconsistencies,
|
||||||
|
* the corresponding functions must be used
|
||||||
|
*/
|
||||||
|
#define trickle_timer_stop(tt) ctimer_stop((tt)->ct)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief To be called by the protocol when it hears a consistent
|
||||||
|
* transmission
|
||||||
|
* \param tt A pointer to a ::trickle_timer structure
|
||||||
|
*
|
||||||
|
* When the trickle-based protocol hears a consistent transmission it must call
|
||||||
|
* this function to increment trickle's consistency counter, which is later
|
||||||
|
* used to determine whether the protocol must suppress or go ahead with its
|
||||||
|
* own transmissions.
|
||||||
|
*
|
||||||
|
* As the trickle timer library implementation may change in the future to
|
||||||
|
* perform further tasks upon reception of a consistent transmission, the
|
||||||
|
* protocol's implementation MUST use this call to increment the consistency
|
||||||
|
* counter instead of directly writing to the structure's field.
|
||||||
|
*/
|
||||||
|
void trickle_timer_consistency(struct trickle_timer *tt);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief To be called by the protocol when it hears an inconsistent
|
||||||
|
* transmission
|
||||||
|
* \param tt A pointer to a ::trickle_timer structure
|
||||||
|
*
|
||||||
|
* When the protocol hears an inconsistent transmission, it must call this
|
||||||
|
* function to notify the library that the timer must be reset.
|
||||||
|
*
|
||||||
|
* Before resetting the timer, the library will perform a set of checks.
|
||||||
|
* Therefore, it is important that the protocol calls this function instead of
|
||||||
|
* trying to reset the timer by itself.
|
||||||
|
*/
|
||||||
|
void trickle_timer_inconsistency(struct trickle_timer *tt);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief To be called by the protocol when an external event occurs that
|
||||||
|
* should trigger a timer reset
|
||||||
|
* \param tt A pointer to a ::trickle_timer structure
|
||||||
|
*
|
||||||
|
* When an external event occurs that should result in a timer reset, the
|
||||||
|
* protocol implementation must call this function to notify the library.
|
||||||
|
*
|
||||||
|
* Before resetting the timer, the library will perform a set of checks.
|
||||||
|
* Therefore, it is important that the protocol calls this function instead of
|
||||||
|
* trying to reset the timer by itself.
|
||||||
|
*/
|
||||||
|
#define trickle_timer_reset_event(tt) trickle_timer_inconsistency(tt)
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
#endif /* __TRICKLE_TIMER_H__ */
|
||||||
|
/** @} */
|
||||||
|
/** @} */
|
9
examples/trickle-library/Makefile
Normal file
9
examples/trickle-library/Makefile
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
UIP_CONF_IPV6=1
|
||||||
|
|
||||||
|
CONTIKI_PROJECT = trickle-library
|
||||||
|
|
||||||
|
all: $(CONTIKI_PROJECT)
|
||||||
|
|
||||||
|
CONTIKI = ../..
|
||||||
|
|
||||||
|
include $(CONTIKI)/Makefile.include
|
194
examples/trickle-library/trickle-library.c
Normal file
194
examples/trickle-library/trickle-library.c
Normal file
|
@ -0,0 +1,194 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2012, George Oikonomou - <oikonomou@users.sourceforge.net>
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the copyright holder nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived
|
||||||
|
* from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||||
|
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Example trickle-based protocol demonstrating the functionality of the
|
||||||
|
* trickle (trickle_timer) library (RFC 6206) */
|
||||||
|
#include "contiki.h"
|
||||||
|
#include "contiki-lib.h"
|
||||||
|
#include "contiki-net.h"
|
||||||
|
|
||||||
|
#include "lib/trickle-timer.h"
|
||||||
|
#include "dev/leds.h"
|
||||||
|
#include "lib/random.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define DEBUG DEBUG_PRINT
|
||||||
|
#include "net/uip-debug.h"
|
||||||
|
|
||||||
|
/* Trickle variables and constants */
|
||||||
|
static struct trickle_timer tt;
|
||||||
|
|
||||||
|
#define IMIN 16 /* ticks */
|
||||||
|
#define IMAX 10 /* doublings */
|
||||||
|
#define REDUNDANCY_CONST 2
|
||||||
|
|
||||||
|
/* Networking */
|
||||||
|
#define TRICKLE_PROTO_PORT 30001
|
||||||
|
static struct uip_udp_conn *trickle_conn;
|
||||||
|
static uip_ipaddr_t ipaddr; /* destination: link-local all-nodes multicast */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For this 'protocol', nodes exchange a token (1 byte) at a frequency
|
||||||
|
* governed by trickle. A node detects an inconsistency when it receives a
|
||||||
|
* token different than the one it knows.
|
||||||
|
* In this case, either:
|
||||||
|
* - 'they' have a 'newer' token and we also update our own value, or
|
||||||
|
* - 'we' have a 'newer' token, in which case we trigger an inconsistency
|
||||||
|
* without updating our value.
|
||||||
|
* In this context, 'newer' is defined in serial number arithmetic terms.
|
||||||
|
*
|
||||||
|
* Every NEW_TOKEN_INTERVAL clock ticks each node will generate a new token
|
||||||
|
* with probability 1/NEW_TOKEN_PROB. This is controlled by etimer et.
|
||||||
|
*/
|
||||||
|
#define NEW_TOKEN_INTERVAL 10 * CLOCK_SECOND
|
||||||
|
#define NEW_TOKEN_PROB 0x80
|
||||||
|
static uint8_t token;
|
||||||
|
static struct etimer et; /* Used to periodically generate inconsistencies */
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
PROCESS(trickle_protocol_process, "Trickle Protocol process");
|
||||||
|
AUTOSTART_PROCESSES(&trickle_protocol_process);
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
static void
|
||||||
|
tcpip_handler(void)
|
||||||
|
{
|
||||||
|
leds_on(LEDS_GREEN);
|
||||||
|
if(uip_newdata()) {
|
||||||
|
PRINTF("At %lu (I=%lu, c=%u): ",
|
||||||
|
(unsigned long)clock_time(), (unsigned long)tt.i_cur, tt.c);
|
||||||
|
PRINTF("Our token=0x%02x, theirs=0x%02x\n", token,
|
||||||
|
((uint8_t *)uip_appdata)[0]);
|
||||||
|
if(token == ((uint8_t *)uip_appdata)[0]) {
|
||||||
|
PRINTF("Consistent RX\n");
|
||||||
|
trickle_timer_consistency(&tt);
|
||||||
|
} else {
|
||||||
|
if((signed char)(token - ((uint8_t *)uip_appdata)[0]) < 0) {
|
||||||
|
PRINTF("Theirs is newer. Update\n");
|
||||||
|
token = ((uint8_t *)uip_appdata)[0];
|
||||||
|
} else {
|
||||||
|
PRINTF("They are behind\n");
|
||||||
|
}
|
||||||
|
trickle_timer_inconsistency(&tt);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Here tt.ct.etimer.timer.{start + interval} points to time t in the
|
||||||
|
* current interval. However, between t and I it points to the interval's
|
||||||
|
* end so if you're going to use this, do so with caution.
|
||||||
|
*/
|
||||||
|
PRINTF("At %lu: Trickle inconsistency. Scheduled TX for %lu\n",
|
||||||
|
(unsigned long)clock_time(),
|
||||||
|
(unsigned long)(tt.ct.etimer.timer.start +
|
||||||
|
tt.ct.etimer.timer.interval));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
leds_off(LEDS_GREEN);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
static void
|
||||||
|
trickle_tx(void *ptr, uint8_t suppress)
|
||||||
|
{
|
||||||
|
/* *ptr is a pointer to the trickle_timer that triggered this callback. In
|
||||||
|
* his example we know that ptr points to tt. However, we pretend that we did
|
||||||
|
* not know (which would be the case if we e.g. had multiple trickle timers)
|
||||||
|
* and cast it to a local struct trickle_timer* */
|
||||||
|
struct trickle_timer *loc_tt = (struct trickle_timer *)ptr;
|
||||||
|
|
||||||
|
if(suppress == TRICKLE_TIMER_TX_SUPPRESS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
leds_on(LEDS_RED);
|
||||||
|
|
||||||
|
PRINTF("At %lu (I=%lu, c=%u): ",
|
||||||
|
(unsigned long)clock_time(), (unsigned long)loc_tt->i_cur,
|
||||||
|
loc_tt->c);
|
||||||
|
PRINTF("Trickle TX token 0x%02x\n", token);
|
||||||
|
|
||||||
|
/* Instead of changing ->ripaddr around by ourselves, we could have used
|
||||||
|
* uip_udp_packet_sendto which would have done it for us. However it puts an
|
||||||
|
* extra ~20 bytes on stack and the cc2x3x micros hate it, so we stick with
|
||||||
|
* send() */
|
||||||
|
|
||||||
|
/* Destination IP: link-local all-nodes multicast */
|
||||||
|
uip_ipaddr_copy(&trickle_conn->ripaddr, &ipaddr);
|
||||||
|
uip_udp_packet_send(trickle_conn, &token, sizeof(token));
|
||||||
|
|
||||||
|
/* Restore to 'accept incoming from any IP' */
|
||||||
|
uip_create_unspecified(&trickle_conn->ripaddr);
|
||||||
|
|
||||||
|
leds_off(LEDS_RED);
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
PROCESS_THREAD(trickle_protocol_process, ev, data)
|
||||||
|
{
|
||||||
|
PROCESS_BEGIN();
|
||||||
|
|
||||||
|
PRINTF("Trickle protocol started\n");
|
||||||
|
|
||||||
|
uip_create_linklocal_allnodes_mcast(&ipaddr); /* Store for later */
|
||||||
|
|
||||||
|
trickle_conn = udp_new(NULL, UIP_HTONS(TRICKLE_PROTO_PORT), NULL);
|
||||||
|
udp_bind(trickle_conn, UIP_HTONS(TRICKLE_PROTO_PORT));
|
||||||
|
|
||||||
|
PRINTF("Connection: local/remote port %u/%u\n",
|
||||||
|
UIP_HTONS(trickle_conn->lport), UIP_HTONS(trickle_conn->rport));
|
||||||
|
|
||||||
|
token = 0;
|
||||||
|
|
||||||
|
trickle_timer_config(&tt, IMIN, IMAX, REDUNDANCY_CONST);
|
||||||
|
trickle_timer_set(&tt, trickle_tx, &tt);
|
||||||
|
/*
|
||||||
|
* At this point trickle is started and is running the first interval. All
|
||||||
|
* nodes 'agree' that token == 0. This will change when one of them randomly
|
||||||
|
* decides to generate a new one
|
||||||
|
*/
|
||||||
|
etimer_set(&et, NEW_TOKEN_INTERVAL);
|
||||||
|
|
||||||
|
while(1) {
|
||||||
|
PROCESS_YIELD();
|
||||||
|
if(ev == tcpip_event) {
|
||||||
|
tcpip_handler();
|
||||||
|
} else if(etimer_expired(&et)) {
|
||||||
|
/* Periodically (and randomly) generate a new token. This will trigger
|
||||||
|
* a trickle inconsistency */
|
||||||
|
if((random_rand() & NEW_TOKEN_PROB) == 0) {
|
||||||
|
token++;
|
||||||
|
PRINTF("At %lu: Generating a new token 0x%02x\n",
|
||||||
|
(unsigned long)clock_time(), token);
|
||||||
|
trickle_timer_reset_event(&tt);
|
||||||
|
}
|
||||||
|
etimer_set(&et, NEW_TOKEN_INTERVAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PROCESS_END();
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
154
examples/trickle-library/trickle-library.csc
Normal file
154
examples/trickle-library/trickle-library.csc
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<simconf>
|
||||||
|
<project EXPORT="discard">[CONTIKI_DIR]/tools/cooja/apps/mrm</project>
|
||||||
|
<project EXPORT="discard">[CONTIKI_DIR]/tools/cooja/apps/mspsim</project>
|
||||||
|
<project EXPORT="discard">[CONTIKI_DIR]/tools/cooja/apps/avrora</project>
|
||||||
|
<project EXPORT="discard">[CONTIKI_DIR]/tools/cooja/apps/serial_socket</project>
|
||||||
|
<project EXPORT="discard">[CONTIKI_DIR]/tools/cooja/apps/collect-view</project>
|
||||||
|
<project EXPORT="discard">[CONTIKI_DIR]/tools/cooja/apps/powertracker</project>
|
||||||
|
<simulation>
|
||||||
|
<title>Example Demonstrating the Trickle Library's Functionality</title>
|
||||||
|
<delaytime>0</delaytime>
|
||||||
|
<randomseed>generated</randomseed>
|
||||||
|
<motedelay_us>1000000</motedelay_us>
|
||||||
|
<radiomedium>
|
||||||
|
se.sics.cooja.radiomediums.UDGM
|
||||||
|
<transmitting_range>50.0</transmitting_range>
|
||||||
|
<interference_range>100.0</interference_range>
|
||||||
|
<success_ratio_tx>1.0</success_ratio_tx>
|
||||||
|
<success_ratio_rx>1.0</success_ratio_rx>
|
||||||
|
</radiomedium>
|
||||||
|
<events>
|
||||||
|
<logoutput>40000</logoutput>
|
||||||
|
</events>
|
||||||
|
<motetype>
|
||||||
|
se.sics.cooja.mspmote.SkyMoteType
|
||||||
|
<identifier>sky1</identifier>
|
||||||
|
<description>trickle-tester</description>
|
||||||
|
<source EXPORT="discard">[CONTIKI_DIR]/examples/trickle-library/trickle-library.c</source>
|
||||||
|
<commands EXPORT="discard">make trickle-library.sky TARGET=sky</commands>
|
||||||
|
<firmware EXPORT="copy">[CONTIKI_DIR]/examples/trickle-library/trickle-library.sky</firmware>
|
||||||
|
<moteinterface>se.sics.cooja.interfaces.Position</moteinterface>
|
||||||
|
<moteinterface>se.sics.cooja.interfaces.RimeAddress</moteinterface>
|
||||||
|
<moteinterface>se.sics.cooja.interfaces.IPAddress</moteinterface>
|
||||||
|
<moteinterface>se.sics.cooja.interfaces.Mote2MoteRelations</moteinterface>
|
||||||
|
<moteinterface>se.sics.cooja.interfaces.MoteAttributes</moteinterface>
|
||||||
|
<moteinterface>se.sics.cooja.mspmote.interfaces.MspClock</moteinterface>
|
||||||
|
<moteinterface>se.sics.cooja.mspmote.interfaces.MspMoteID</moteinterface>
|
||||||
|
<moteinterface>se.sics.cooja.mspmote.interfaces.SkyButton</moteinterface>
|
||||||
|
<moteinterface>se.sics.cooja.mspmote.interfaces.SkyFlash</moteinterface>
|
||||||
|
<moteinterface>se.sics.cooja.mspmote.interfaces.SkyCoffeeFilesystem</moteinterface>
|
||||||
|
<moteinterface>se.sics.cooja.mspmote.interfaces.SkyByteRadio</moteinterface>
|
||||||
|
<moteinterface>se.sics.cooja.mspmote.interfaces.MspSerial</moteinterface>
|
||||||
|
<moteinterface>se.sics.cooja.mspmote.interfaces.SkyLED</moteinterface>
|
||||||
|
<moteinterface>se.sics.cooja.mspmote.interfaces.MspDebugOutput</moteinterface>
|
||||||
|
<moteinterface>se.sics.cooja.mspmote.interfaces.SkyTemperature</moteinterface>
|
||||||
|
</motetype>
|
||||||
|
<mote>
|
||||||
|
<breakpoints />
|
||||||
|
<interface_config>
|
||||||
|
se.sics.cooja.interfaces.Position
|
||||||
|
<x>96.8286491032791</x>
|
||||||
|
<y>44.83363764767495</y>
|
||||||
|
<z>0.0</z>
|
||||||
|
</interface_config>
|
||||||
|
<interface_config>
|
||||||
|
se.sics.cooja.mspmote.interfaces.MspMoteID
|
||||||
|
<id>1</id>
|
||||||
|
</interface_config>
|
||||||
|
<motetype_identifier>sky1</motetype_identifier>
|
||||||
|
</mote>
|
||||||
|
<mote>
|
||||||
|
<breakpoints />
|
||||||
|
<interface_config>
|
||||||
|
se.sics.cooja.interfaces.Position
|
||||||
|
<x>26.625418506201424</x>
|
||||||
|
<y>62.32118900834971</y>
|
||||||
|
<z>0.0</z>
|
||||||
|
</interface_config>
|
||||||
|
<interface_config>
|
||||||
|
se.sics.cooja.mspmote.interfaces.MspMoteID
|
||||||
|
<id>2</id>
|
||||||
|
</interface_config>
|
||||||
|
<motetype_identifier>sky1</motetype_identifier>
|
||||||
|
</mote>
|
||||||
|
<mote>
|
||||||
|
<breakpoints />
|
||||||
|
<interface_config>
|
||||||
|
se.sics.cooja.interfaces.Position
|
||||||
|
<x>12.373988266345922</x>
|
||||||
|
<y>40.21870711164037</y>
|
||||||
|
<z>0.0</z>
|
||||||
|
</interface_config>
|
||||||
|
<interface_config>
|
||||||
|
se.sics.cooja.mspmote.interfaces.MspMoteID
|
||||||
|
<id>3</id>
|
||||||
|
</interface_config>
|
||||||
|
<motetype_identifier>sky1</motetype_identifier>
|
||||||
|
</mote>
|
||||||
|
<mote>
|
||||||
|
<breakpoints />
|
||||||
|
<interface_config>
|
||||||
|
se.sics.cooja.interfaces.Position
|
||||||
|
<x>38.44294323221424</x>
|
||||||
|
<y>17.14724376428426</y>
|
||||||
|
<z>0.0</z>
|
||||||
|
</interface_config>
|
||||||
|
<interface_config>
|
||||||
|
se.sics.cooja.mspmote.interfaces.MspMoteID
|
||||||
|
<id>4</id>
|
||||||
|
</interface_config>
|
||||||
|
<motetype_identifier>sky1</motetype_identifier>
|
||||||
|
</mote>
|
||||||
|
<mote>
|
||||||
|
<breakpoints />
|
||||||
|
<interface_config>
|
||||||
|
se.sics.cooja.interfaces.Position
|
||||||
|
<x>68.38248149463341</x>
|
||||||
|
<y>23.506083749222842</y>
|
||||||
|
<z>0.0</z>
|
||||||
|
</interface_config>
|
||||||
|
<interface_config>
|
||||||
|
se.sics.cooja.mspmote.interfaces.MspMoteID
|
||||||
|
<id>5</id>
|
||||||
|
</interface_config>
|
||||||
|
<motetype_identifier>sky1</motetype_identifier>
|
||||||
|
</mote>
|
||||||
|
</simulation>
|
||||||
|
<plugin>
|
||||||
|
se.sics.cooja.plugins.Visualizer
|
||||||
|
<plugin_config>
|
||||||
|
<skin>se.sics.cooja.plugins.skins.UDGMVisualizerSkin</skin>
|
||||||
|
<skin>se.sics.cooja.plugins.skins.TrafficVisualizerSkin</skin>
|
||||||
|
<skin>se.sics.cooja.plugins.skins.IDVisualizerSkin</skin>
|
||||||
|
<viewport>5.862188489126289 0.0 0.0 5.862188489126289 -4.083221885224075 -86.33855683341153</viewport>
|
||||||
|
</plugin_config>
|
||||||
|
<width>642</width>
|
||||||
|
<z>0</z>
|
||||||
|
<height>369</height>
|
||||||
|
<location_x>447</location_x>
|
||||||
|
<location_y>10</location_y>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
se.sics.cooja.plugins.LogListener
|
||||||
|
<plugin_config>
|
||||||
|
<filter />
|
||||||
|
<coloring />
|
||||||
|
<hidedebug />
|
||||||
|
</plugin_config>
|
||||||
|
<width>1235</width>
|
||||||
|
<z>2</z>
|
||||||
|
<height>285</height>
|
||||||
|
<location_x>4</location_x>
|
||||||
|
<location_y>389</location_y>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
se.sics.cooja.plugins.SimControl
|
||||||
|
<width>318</width>
|
||||||
|
<z>1</z>
|
||||||
|
<height>192</height>
|
||||||
|
<location_x>60</location_x>
|
||||||
|
<location_y>60</location_y>
|
||||||
|
</plugin>
|
||||||
|
</simconf>
|
||||||
|
|
Loading…
Reference in a new issue