diff --git a/.gitignore b/.gitignore index 31afb98b3..91721ed08 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ obj_* Makefile.target Makefile.*.defines tools/doxygen/html +tools/readthedocs/_build patches-* tools/serial-io/tunslip6 tools/serial-io/serialdump diff --git a/.travis.yml b/.travis.yml index e91ee6aea..f5a58c609 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ notifications: # Environment setup before test script. Runs for each build before_install: # Check if anything has changed within the docker directory - - DOCKER_CHANGED=`git diff --name-only $TRAVIS_BRANCH...HEAD -- tools/docker | wc -l` + - DOCKER_CHANGED=`git diff --name-only $TRAVIS_COMMIT_RANGE -- tools/docker | wc -l` # If Docker directory has not changed, pull image from Dockerhub. Else, build # image from Dockerifle. This needs to be done for each job. Any build error # will count as Travis test failure. In case this updates develop, push new @@ -20,6 +20,10 @@ before_install: else echo "Docker image changed, build from Dockerfile" docker build tools/docker -t $DOCKER_IMG; + if [ $? != 0 ]; then + echo "Failed to build Docker image" + exit 1 + fi if [ $TRAVIS_SECURE_ENV_VARS == true ] && [ $TRAVIS_PULL_REQUEST == false ] && [ $TRAVIS_BRANCH == 'develop' ]; then echo "This build is for an update of branch develop. Push image to Dockerhub" echo $DOCKERHUB_PASSWD | docker login --username contiker --password-stdin @@ -58,7 +62,7 @@ env: - TEST_NAME='simulation-base' - TEST_NAME='ieee802154' - TEST_NAME='compile-nxp-ports' - - TEST_NAME='doxygen' + - TEST_NAME='documentation' - TEST_NAME='compile-tools' - TEST_NAME='native-runs' - TEST_NAME='ipv6' diff --git a/LICENSE.md b/LICENSE.md index b2f909d44..f4b1a054c 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,41 +1,30 @@ -Contiki-NG is licensed under the 3-clause BSD license. This license gives -everyone the right to use and distribute the code, either in binary or -source code format, as long as the copyright license is retained in -the source code. +Copyright (c) (Year), (Name of copyright holder) +All rights reserved. -The copyright for different parts of the code is held by different -people and organizations, but the code is licensed under the same type -of license. The license text is: +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: -``` -/* - * Copyright (c) (Year), (Name of copyright holder) - * 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. - */ -``` +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. diff --git a/README.md b/README.md index df7d7ff58..e9a1005ef 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,10 @@ Contiki-NG is an open-source, cross-platform operating system for Next-Generation IoT devices. It focuses on dependable (secure and reliable) low-power communication and standard protocols, such as IPv6/6LoWPAN, 6TiSCH, RPL, and CoAP. Contiki-NG comes with extensive documentation, tutorials, a roadmap, release cycle, and well-defined development flow for smooth integration of community contributions. Unless excplicitly stated otherwise, Contiki-NG sources are distributed under -the terms of the [3-clause BSD license](LICENSE.md). +the terms of the [3-clause BSD license](LICENSE.md). This license gives +everyone the right to use and distribute the code, either in binary or +source code format, as long as the copyright license is retained in +the source code. Contiki-NG started as a fork of the Contiki OS and retains some of its original features. diff --git a/arch/cpu/cc26xx-cc13xx/rf-core/prop-mode.c b/arch/cpu/cc26xx-cc13xx/rf-core/prop-mode.c index f4e7f66e7..66b4a4370 100644 --- a/arch/cpu/cc26xx-cc13xx/rf-core/prop-mode.c +++ b/arch/cpu/cc26xx-cc13xx/rf-core/prop-mode.c @@ -45,6 +45,7 @@ #include "net/netstack.h" #include "sys/energest.h" #include "sys/clock.h" +#include "sys/critical.h" #include "sys/rtimer.h" #include "sys/cc.h" #include "lpm.h" @@ -766,6 +767,7 @@ send(const void *payload, unsigned short payload_len) static int read_frame(void *buf, unsigned short buf_len) { + int_master_status_t status; rfc_dataEntryGeneral_t *entry = (rfc_dataEntryGeneral_t *)rx_read_entry; uint8_t *data_ptr = &entry->data; int len = 0; @@ -795,6 +797,14 @@ read_frame(void *buf, unsigned short buf_len) entry->status = DATA_ENTRY_STATUS_PENDING; } + status = critical_enter(); + if(rx_is_full) { + rx_is_full = false; + PRINTF("RXQ was full, re-enabling radio!\n"); + rx_on_prop(); + } + critical_exit(status); + return len; } /*---------------------------------------------------------------------------*/ diff --git a/arch/cpu/cc26xx-cc13xx/rf-core/rf-core.c b/arch/cpu/cc26xx-cc13xx/rf-core/rf-core.c index 2baa883ad..23b78eab4 100644 --- a/arch/cpu/cc26xx-cc13xx/rf-core/rf-core.c +++ b/arch/cpu/cc26xx-cc13xx/rf-core/rf-core.c @@ -76,7 +76,7 @@ /*---------------------------------------------------------------------------*/ /* RF interrupts */ #define RX_FRAME_IRQ IRQ_RX_ENTRY_DONE -#define ERROR_IRQ IRQ_INTERNAL_ERROR +#define ERROR_IRQ (IRQ_INTERNAL_ERROR | IRQ_RX_BUF_FULL) #define RX_NOK_IRQ IRQ_RX_NOK /* Those IRQs are enabled all the time */ @@ -103,6 +103,9 @@ static const rf_core_primary_mode_t *primary_mode = NULL; int32_t rat_offset = 0; static bool rat_offset_known = false; /*---------------------------------------------------------------------------*/ +/* Buffer full flag */ +volatile bool rx_is_full = false; +/*---------------------------------------------------------------------------*/ PROCESS(rf_core_process, "CC13xx / CC26xx RF driver"); /*---------------------------------------------------------------------------*/ #define RF_CORE_CLOCKS_MASK (RFC_PWR_PWMCLKEN_RFC_M | RFC_PWR_PWMCLKEN_CPE_M \ @@ -574,6 +577,16 @@ cc26xx_rf_cpe1_isr(void) return; } } + + if(HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIFG) & IRQ_RX_BUF_FULL) { + PRINTF("\nRF: BUF_FULL\n\n"); + /* set a flag that the buffer is full*/ + rx_is_full = true; + /* make sure read_frame() will be called to make space in RX buffer */ + process_poll(&rf_core_process); + /* Clear the IRQ_RX_BUF_FULL interrupt flag by writing zero to bit */ + HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIFG) = ~(IRQ_RX_BUF_FULL); + } /* Clear INTERNAL_ERROR interrupt flag */ HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIFG) = 0x7FFFFFFF; diff --git a/arch/cpu/cc26xx-cc13xx/rf-core/rf-core.h b/arch/cpu/cc26xx-cc13xx/rf-core/rf-core.h index ac48bc52a..c97ab0f8a 100644 --- a/arch/cpu/cc26xx-cc13xx/rf-core/rf-core.h +++ b/arch/cpu/cc26xx-cc13xx/rf-core/rf-core.h @@ -263,6 +263,9 @@ typedef struct rf_core_primary_mode_s { /* Radio timer register */ #define RATCNT 0x00000004 /*---------------------------------------------------------------------------*/ +/* Buffer full flag */ +extern volatile bool rx_is_full; +/*---------------------------------------------------------------------------*/ /* Make the main driver process visible to mode drivers */ PROCESS_NAME(rf_core_process); /*---------------------------------------------------------------------------*/ diff --git a/examples/6tisch/sixtop/sf-simple.c b/examples/6tisch/sixtop/sf-simple.c index 689c668e7..f7393ab4c 100644 --- a/examples/6tisch/sixtop/sf-simple.c +++ b/examples/6tisch/sixtop/sf-simple.c @@ -108,7 +108,7 @@ print_cell_list(const uint8_t *cell_list, uint16_t cell_list_len) uint16_t i; sf_simple_cell_t cell; - for(i = 0; i < (cell_list_len / sizeof(cell)); i++) { + for(i = 0; i < cell_list_len; i += sizeof(cell)) { read_cell(&cell_list[i], &cell); PRINTF("%u ", cell.timeslot_offset); } @@ -132,7 +132,7 @@ add_links_to_schedule(const linkaddr_t *peer_addr, uint8_t link_option, return; } - for(i = 0; i < (cell_list_len / sizeof(cell)); i++) { + for(i = 0; i < cell_list_len; i += sizeof(cell)) { read_cell(&cell_list[i], &cell); if(cell.timeslot_offset == 0xffff) { continue; @@ -166,7 +166,7 @@ remove_links_to_schedule(const uint8_t *cell_list, uint16_t cell_list_len) return; } - for(i = 0; i < (cell_list_len / sizeof(cell)); i++) { + for(i = 0; i < cell_list_len; i += sizeof(cell)) { read_cell(&cell_list[i], &cell); if(cell.timeslot_offset == 0xffff) { continue; @@ -335,7 +335,7 @@ delete_req_input(const uint8_t *body, uint16_t body_len, if(num_cells > 0 && cell_list_len > 0) { /* ensure before delete */ - for(i = 0, removed_link = 0; i < (cell_list_len / sizeof(cell)); i++) { + for(i = 0, removed_link = 0; i < cell_list_len; i += sizeof(cell)) { read_cell(&cell_list[i], &cell); if(tsch_schedule_get_link_by_timeslot(slotframe, cell.timeslot_offset) != NULL) { diff --git a/examples/libs/deployment/Makefile b/examples/libs/deployment/Makefile new file mode 100644 index 000000000..5ad17e22d --- /dev/null +++ b/examples/libs/deployment/Makefile @@ -0,0 +1,7 @@ +CONTIKI_PROJECT = node +all: $(CONTIKI_PROJECT) + +MODULES += os/services/deployment + +CONTIKI = ../../.. +include $(CONTIKI)/Makefile.include diff --git a/examples/libs/deployment/README.md b/examples/libs/deployment/README.md new file mode 100644 index 000000000..8484c3aee --- /dev/null +++ b/examples/libs/deployment/README.md @@ -0,0 +1,3 @@ +A simple example of how to use the deployment module. Intended for Cooja, +with a Cooja motes, as in the provided simulation file `sim.csc`. For use +in a real deployment, set DEPLOYMENT_MAPPING to your own ID-MAC mapping table. diff --git a/examples/libs/deployment/node.c b/examples/libs/deployment/node.c new file mode 100644 index 000000000..2cbe1f276 --- /dev/null +++ b/examples/libs/deployment/node.c @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2018, RISE SICS. + * 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 Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the Contiki operating system. + * + */ + +/** + * \file + * Benchmark: the root sends requests to all nodes in a randomized + * order, and receives resopnses back. + * \author + * Simon Duquennoy + */ + +#include "contiki.h" +#include "contiki-net.h" +#include "services/deployment/deployment.h" + +/* Log configuration */ +#include "sys/log.h" +#define LOG_MODULE "App" +#define LOG_LEVEL LOG_LEVEL_INFO + +#include "services/deployment/deployment.h" + +/** \brief A mapping table for a 8-node Cooja mote simulation. + * Define your own for any given deployment environment */ +const struct id_mac deployment_cooja8[] = { + { 1, {{0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01}}}, + { 2, {{0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02}}}, + { 3, {{0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03}}}, + { 4, {{0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x04}}}, + { 5, {{0x00,0x05,0x00,0x05,0x00,0x05,0x00,0x05}}}, + { 6, {{0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06}}}, + { 7, {{0x00,0x07,0x00,0x07,0x00,0x07,0x00,0x07}}}, + { 8, {{0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08}}}, + { 0, {{0}}} +}; + +/** \brief An example mapping for Openmotes in Flocklab. + * To use, set DEPLOYMENT_MAPPING to deployment_flocklab_openmotes */ +const struct id_mac deployment_flocklab_openmotes[] = { + { 3, {{0x00,0x12,0x4b,0x00,0x06,0x0d,0x9b,0x29}}}, + { 6, {{0x00,0x12,0x4b,0x00,0x06,0x0d,0x9b,0x34}}}, + { 8, {{0x00,0x12,0x4b,0x00,0x06,0x0d,0x9b,0x1f}}}, + { 15, {{0x00,0x12,0x4b,0x00,0x06,0x0d,0x9b,0x85}}}, + { 16, {{0x00,0x12,0x4b,0x00,0x06,0x0d,0x9b,0x00}}}, + { 18, {{0x00,0x12,0x4b,0x00,0x06,0x0d,0x9b,0x37}}}, + { 22, {{0x00,0x12,0x4b,0x00,0x06,0x0d,0x9b,0x08}}}, + { 23, {{0x00,0x12,0x4b,0x00,0x06,0x0d,0x9b,0x5f}}}, + { 31, {{0x00,0x12,0x4b,0x00,0x06,0x0d,0x9b,0xb1}}}, + { 0, {{0}}} +}; + +/*---------------------------------------------------------------------------*/ +PROCESS(app_process, "App process"); +AUTOSTART_PROCESSES(&app_process); + +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(app_process, ev, data) +{ + static struct etimer timer; + static uip_ipaddr_t ipaddr; + static linkaddr_t lladdr; + static int i; + + PROCESS_BEGIN(); + + if(node_id == ROOT_ID) { + /* We are the root, start a DAG */ + NETSTACK_ROUTING.root_start(); + /* Setup a periodic timer that expires after 10 seconds. */ + etimer_set(&timer, CLOCK_SECOND * 10); + /* Wait until all nodes have joined */ + while(1) { + PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&timer)); + etimer_reset(&timer); + /* Log expected IPv6 addresses of all nodes */ + LOG_INFO("Node list:\n"); + for(i = 0; iMAC) */ +#define DEPLOYMENT_MAPPING deployment_cooja8 +/* Compact address logging (both link-layer and IPv6). + * Shows an abbreviated form that contains the node-id */ +#define LOG_CONF_WITH_COMPACT_ADDR 1 + +#endif /* PROJECT_CONF_H_ */ diff --git a/examples/libs/deployment/sim.csc b/examples/libs/deployment/sim.csc new file mode 100644 index 000000000..9daa11426 --- /dev/null +++ b/examples/libs/deployment/sim.csc @@ -0,0 +1,275 @@ + + + [APPS_DIR]/mrm + [APPS_DIR]/mspsim + [APPS_DIR]/avrora + [APPS_DIR]/serial_socket + [APPS_DIR]/powertracker + + My simulation + 123456 + 1000000 + + org.contikios.cooja.radiomediums.UDGM + 50.0 + 100.0 + 1.0 + 1.0 + + + 40000 + + + org.contikios.cooja.contikimote.ContikiMoteType + mtype90 + Cooja Mote Type #1 + [CONTIKI_DIR]/examples/libs/deployment/node.c + make node.cooja TARGET=cooja + org.contikios.cooja.interfaces.Position + org.contikios.cooja.interfaces.Battery + org.contikios.cooja.contikimote.interfaces.ContikiVib + org.contikios.cooja.contikimote.interfaces.ContikiMoteID + org.contikios.cooja.contikimote.interfaces.ContikiRS232 + org.contikios.cooja.contikimote.interfaces.ContikiBeeper + org.contikios.cooja.interfaces.RimeAddress + org.contikios.cooja.contikimote.interfaces.ContikiIPAddress + org.contikios.cooja.contikimote.interfaces.ContikiRadio + org.contikios.cooja.contikimote.interfaces.ContikiButton + org.contikios.cooja.contikimote.interfaces.ContikiPIR + org.contikios.cooja.contikimote.interfaces.ContikiClock + org.contikios.cooja.contikimote.interfaces.ContikiLED + org.contikios.cooja.contikimote.interfaces.ContikiCFS + org.contikios.cooja.contikimote.interfaces.ContikiEEPROM + org.contikios.cooja.interfaces.Mote2MoteRelations + org.contikios.cooja.interfaces.MoteAttributes + false + + + + org.contikios.cooja.interfaces.Position + 12.478629242391953 + 42.201041276604826 + 0.0 + + + org.contikios.cooja.contikimote.interfaces.ContikiMoteID + 1 + + + org.contikios.cooja.contikimote.interfaces.ContikiRadio + 250.0 + + + org.contikios.cooja.contikimote.interfaces.ContikiEEPROM + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + + mtype90 + + + + org.contikios.cooja.interfaces.Position + 25.625935608473608 + 82.53975431376661 + 0.0 + + + org.contikios.cooja.contikimote.interfaces.ContikiMoteID + 2 + + + org.contikios.cooja.contikimote.interfaces.ContikiRadio + 250.0 + + + org.contikios.cooja.contikimote.interfaces.ContikiEEPROM + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + + mtype90 + + + + org.contikios.cooja.interfaces.Position + 51.615094138350024 + 59.70602651475372 + 0.0 + + + org.contikios.cooja.contikimote.interfaces.ContikiMoteID + 3 + + + org.contikios.cooja.contikimote.interfaces.ContikiRadio + 250.0 + + + org.contikios.cooja.contikimote.interfaces.ContikiEEPROM + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + + mtype90 + + + + org.contikios.cooja.interfaces.Position + 41.04314122620578 + 121.24693889311891 + 0.0 + + + org.contikios.cooja.contikimote.interfaces.ContikiMoteID + 4 + + + org.contikios.cooja.contikimote.interfaces.ContikiRadio + 250.0 + + + org.contikios.cooja.contikimote.interfaces.ContikiEEPROM + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + + mtype90 + + + + org.contikios.cooja.interfaces.Position + 64.9463558635099 + 104.25039302469283 + 0.0 + + + org.contikios.cooja.contikimote.interfaces.ContikiMoteID + 5 + + + org.contikios.cooja.contikimote.interfaces.ContikiRadio + 250.0 + + + org.contikios.cooja.contikimote.interfaces.ContikiEEPROM + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + + mtype90 + + + + org.contikios.cooja.interfaces.Position + 93.59263858654369 + 75.40399148300003 + 0.0 + + + org.contikios.cooja.contikimote.interfaces.ContikiMoteID + 6 + + + org.contikios.cooja.contikimote.interfaces.ContikiRadio + 250.0 + + + org.contikios.cooja.contikimote.interfaces.ContikiEEPROM + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + + mtype90 + + + + org.contikios.cooja.interfaces.Position + 75.6297158696234 + 139.97002035548905 + 0.0 + + + org.contikios.cooja.contikimote.interfaces.ContikiMoteID + 7 + + + org.contikios.cooja.contikimote.interfaces.ContikiRadio + 250.0 + + + org.contikios.cooja.contikimote.interfaces.ContikiEEPROM + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + + mtype90 + + + + org.contikios.cooja.interfaces.Position + 104.34293924684245 + 116.07658566915099 + 0.0 + + + org.contikios.cooja.contikimote.interfaces.ContikiMoteID + 8 + + + org.contikios.cooja.contikimote.interfaces.ContikiRadio + 250.0 + + + org.contikios.cooja.contikimote.interfaces.ContikiEEPROM + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + + mtype90 + + + + org.contikios.cooja.plugins.SimControl + 280 + 2 + 160 + 400 + 0 + + + org.contikios.cooja.plugins.Visualizer + + true + org.contikios.cooja.plugins.skins.IDVisualizerSkin + org.contikios.cooja.plugins.skins.GridVisualizerSkin + org.contikios.cooja.plugins.skins.TrafficVisualizerSkin + org.contikios.cooja.plugins.skins.UDGMVisualizerSkin + 2.4250860844175466 0.0 0.0 2.4250860844175466 35.26895372864869 -46.9106236441515 + + 400 + 3 + 400 + 1 + 1 + + + org.contikios.cooja.plugins.LogListener + + App + + + + 827 + 0 + 665 + 681 + -1 + + + org.contikios.cooja.plugins.TimeLine + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + + + + 500.0 + + 1539 + 1 + 263 + 0 + 709 + + diff --git a/examples/libs/shell/example.c b/examples/libs/shell/example.c index 9d2320c58..51cc98826 100644 --- a/examples/libs/shell/example.c +++ b/examples/libs/shell/example.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Swedish Institute of Computer Science. + * Copyright (c) 2018, RISE SICS. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/examples/libs/simple-energest/example.c b/examples/libs/simple-energest/example.c index e9d6d585c..35d1f0b8d 100644 --- a/examples/libs/simple-energest/example.c +++ b/examples/libs/simple-energest/example.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Swedish Institute of Computer Science. + * Copyright (c) 2018, RISE SICS. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/examples/lwm2m-ipso-objects/project-conf.h b/examples/lwm2m-ipso-objects/project-conf.h index 01f919866..a8603c67b 100644 --- a/examples/lwm2m-ipso-objects/project-conf.h +++ b/examples/lwm2m-ipso-objects/project-conf.h @@ -64,7 +64,7 @@ #define LWM2M_QUEUE_MODE_CONF_INCLUDE_DYNAMIC_ADAPTATION 1 #define LWM2M_QUEUE_MODE_CONF_DEFAULT_CLIENT_AWAKE_TIME 2000 #define LWM2M_QUEUE_MODE_CONF_DEFAULT_CLIENT_SLEEP_TIME 10000 - #define LWM2M_QUEUE_MODE_CONF_DEFAULT_DYNAMIC_ADAPTATION_FLAG 1 - #define LWM2M_QUEUE_MODE_OBJECT_CONF_ENABLED 0 */ + #define LWM2M_QUEUE_MODE_CONF_DEFAULT_DYNAMIC_ADAPTATION_FLAG 0 + #define LWM2M_QUEUE_MODE_OBJECT_CONF_ENABLED 1 */ #endif /* PROJECT_CONF_H_ */ diff --git a/examples/mqtt-client/mqtt-client.c b/examples/mqtt-client/mqtt-client.c index 4e128ae37..be3c97cdb 100644 --- a/examples/mqtt-client/mqtt-client.c +++ b/examples/mqtt-client/mqtt-client.c @@ -493,6 +493,7 @@ publish(void) int len; int remaining = APP_BUFFER_SIZE; int i; + char def_rt_str[64]; seq_nr_value++; @@ -519,7 +520,6 @@ publish(void) buf_ptr += len; /* Put our Default route's string representation in a buffer */ - char def_rt_str[64]; memset(def_rt_str, 0, sizeof(def_rt_str)); ipaddr_sprintf(def_rt_str, sizeof(def_rt_str), uip_ds6_defrt_choose()); diff --git a/os/net/app-layer/coap/coap-blocking-api.c b/os/net/app-layer/coap/coap-blocking-api.c index 1be7cbe7c..9fd691d74 100644 --- a/os/net/app-layer/coap/coap-blocking-api.c +++ b/os/net/app-layer/coap/coap-blocking-api.c @@ -60,37 +60,36 @@ void coap_blocking_request_callback(void *callback_data, coap_message_t *response) { - coap_request_state_t *state = (coap_request_state_t *)callback_data; + coap_blocking_request_state_t *blocking_state = (coap_blocking_request_state_t *)callback_data; - state->response = response; - process_poll(state->process); + blocking_state->state.response = response; + process_poll(blocking_state->process); } /*---------------------------------------------------------------------------*/ PT_THREAD(coap_blocking_request - (coap_request_state_t *state, process_event_t ev, + (coap_blocking_request_state_t *blocking_state, process_event_t ev, coap_endpoint_t *remote_ep, coap_message_t *request, coap_blocking_response_handler_t request_callback)) { - PT_BEGIN(&state->pt); + /* Before PT_BEGIN in order to not be a local variable in the PT_Thread and maintain it */ + coap_request_state_t *state = &blocking_state->state; - static uint32_t res_block; - static uint8_t more; - static uint8_t block_error; + PT_BEGIN(&blocking_state->pt); state->block_num = 0; state->response = NULL; - state->process = PROCESS_CURRENT(); + blocking_state->process = PROCESS_CURRENT(); - more = 0; - res_block = 0; - block_error = 0; + state->more = 0; + state->res_block = 0; + state->block_error = 0; do { request->mid = coap_get_mid(); if((state->transaction = coap_new_transaction(request->mid, remote_ep))) { state->transaction->callback = coap_blocking_request_callback; - state->transaction->callback_data = state; + state->transaction->callback_data = blocking_state; if(state->block_num > 0) { coap_set_header_block2(request, state->block_num, 0, @@ -104,33 +103,46 @@ PT_THREAD(coap_blocking_request coap_send_transaction(state->transaction); LOG_DBG("Requested #%"PRIu32" (MID %u)\n", state->block_num, request->mid); - PT_YIELD_UNTIL(&state->pt, ev == PROCESS_EVENT_POLL); + PT_YIELD_UNTIL(&blocking_state->pt, ev == PROCESS_EVENT_POLL); if(!state->response) { LOG_WARN("Server not responding\n"); - PT_EXIT(&state->pt); + state->status = COAP_REQUEST_STATUS_TIMEOUT; + PT_EXIT(&blocking_state->pt); } - coap_get_header_block2(state->response, &res_block, &more, NULL, NULL); + coap_get_header_block2(state->response, &state->res_block, &state->more, NULL, NULL); - LOG_DBG("Received #%"PRIu32"%s (%u bytes)\n", res_block, more ? "+" : "", + LOG_DBG("Received #%"PRIu32"%s (%u bytes)\n", state->res_block, state->more ? "+" : "", state->response->payload_len); + if(state->more) { + state->status = COAP_REQUEST_STATUS_MORE; + } else { + state->status = COAP_REQUEST_STATUS_RESPONSE; + } - if(res_block == state->block_num) { + if(state->res_block == state->block_num) { request_callback(state->response); ++(state->block_num); } else { LOG_WARN("WRONG BLOCK %"PRIu32"/%"PRIu32"\n", - res_block, state->block_num); - ++block_error; + state->res_block, state->block_num); + ++(state->block_error); } } else { LOG_WARN("Could not allocate transaction buffer"); - PT_EXIT(&state->pt); + PT_EXIT(&blocking_state->pt); } - } while(more && block_error < COAP_MAX_ATTEMPTS); + } while(state->more && (state->block_error) < COAP_MAX_ATTEMPTS); - PT_END(&state->pt); + if((state->block_error) >= COAP_MAX_ATTEMPTS) { + /* failure - now we give up */ + state->status = COAP_REQUEST_STATUS_BLOCK_ERROR; + } else { + /* No more blocks, request finished */ + state->status = COAP_REQUEST_STATUS_FINISHED; + } + PT_END(&blocking_state->pt); } /*---------------------------------------------------------------------------*/ /** @} */ diff --git a/os/net/app-layer/coap/coap-blocking-api.h b/os/net/app-layer/coap/coap-blocking-api.h index d9a916c04..716a261b5 100644 --- a/os/net/app-layer/coap/coap-blocking-api.h +++ b/os/net/app-layer/coap/coap-blocking-api.h @@ -39,31 +39,30 @@ #include "sys/pt.h" #include "coap-transactions.h" +#include "coap-request-state.h" /*---------------------------------------------------------------------------*/ /*- Client Part -------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ -typedef struct coap_request_state { +typedef struct coap_blocking_request_state { + coap_request_state_t state; struct pt pt; struct process *process; - coap_transaction_t *transaction; - coap_message_t *response; - uint32_t block_num; -} coap_request_state_t; +} coap_blocking_request_state_t; typedef void (* coap_blocking_response_handler_t)(coap_message_t *response); PT_THREAD(coap_blocking_request - (coap_request_state_t *state, process_event_t ev, + (coap_blocking_request_state_t *blocking_state, process_event_t ev, coap_endpoint_t *remote, coap_message_t *request, coap_blocking_response_handler_t request_callback)); #define COAP_BLOCKING_REQUEST(server_endpoint, request, chunk_handler) \ { \ - static coap_request_state_t request_state; \ - PT_SPAWN(process_pt, &request_state.pt, \ - coap_blocking_request(&request_state, ev, \ + static coap_blocking_request_state_t blocking_state; \ + PT_SPAWN(process_pt, &blocking_state.pt, \ + coap_blocking_request(&blocking_state, ev, \ server_endpoint, \ request, chunk_handler) \ ); \ diff --git a/os/net/app-layer/coap/coap-callback-api.c b/os/net/app-layer/coap/coap-callback-api.c index 0f0ab2c99..41a2ef846 100644 --- a/os/net/app-layer/coap/coap-callback-api.c +++ b/os/net/app-layer/coap/coap-callback-api.c @@ -54,19 +54,13 @@ #define LOG_MODULE "coap" #define LOG_LEVEL LOG_LEVEL_COAP -/* These should go into the state struct so that we can have multiple - requests */ - -static uint32_t res_block; -static uint8_t more; -static uint8_t block_error; - static void coap_request_callback(void *callback_data, coap_message_t *response); /*---------------------------------------------------------------------------*/ static int -progress_request(coap_request_state_t *state) { +progress_request(coap_callback_request_state_t *callback_state) { + coap_request_state_t *state = &callback_state->state; coap_message_t *request = state->request; request->mid = coap_get_mid(); if((state->transaction = @@ -93,7 +87,9 @@ progress_request(coap_request_state_t *state) { static void coap_request_callback(void *callback_data, coap_message_t *response) { - coap_request_state_t *state = (coap_request_state_t *)callback_data; + coap_callback_request_state_t *callback_state = (coap_callback_request_state_t*)callback_data; + coap_request_state_t *state = &callback_state->state; + uint32_t res_block1; state->response = response; @@ -102,58 +98,70 @@ coap_request_callback(void *callback_data, coap_message_t *response) if(!state->response) { LOG_WARN("Server not responding giving up...\n"); - state->callback(state); + state->status = COAP_REQUEST_STATUS_TIMEOUT; + callback_state->callback(callback_state); return; } /* Got a response */ - coap_get_header_block2(state->response, &res_block, &more, NULL, NULL); + coap_get_header_block2(state->response, &state->res_block, &state->more, NULL, NULL); coap_get_header_block1(state->response, &res_block1, NULL, NULL, NULL); LOG_DBG("Received #%lu%s B1:%lu (%u bytes)\n", - (unsigned long)res_block, (unsigned)more ? "+" : "", + (unsigned long)state->res_block, (unsigned)state->more ? "+" : "", (unsigned long)res_block1, state->response->payload_len); - if(res_block == state->block_num) { + if(state->res_block == state->block_num) { /* Call the callback function as we have more data */ - state->callback(state); + if(state->more) { + state->status = COAP_REQUEST_STATUS_MORE; + } else { + state->status = COAP_REQUEST_STATUS_RESPONSE; + } + callback_state->callback(callback_state); /* this is only for counting BLOCK2 blocks.*/ ++(state->block_num); } else { - LOG_WARN("WRONG BLOCK %"PRIu32"/%"PRIu32"\n", res_block, state->block_num); - ++block_error; + LOG_WARN("WRONG BLOCK %"PRIu32"/%"PRIu32"\n", state->res_block, state->block_num); + ++(state->block_error); } - if(more && block_error < COAP_MAX_ATTEMPTS) { - progress_request(state); + if(state->more) { + if((state->block_error) < COAP_MAX_ATTEMPTS) { + progress_request(callback_state); + } else { + /* failure - now we give up and notify the callback */ + state->status = COAP_REQUEST_STATUS_BLOCK_ERROR; + callback_state->callback(callback_state); + } } else { - /* failure - now we give up and notify the callback */ + /* No more blocks, finish and notify the callback */ + state->status = COAP_REQUEST_STATUS_FINISHED; state->response = NULL; - state->callback(state); + callback_state->callback(callback_state); } } /*---------------------------------------------------------------------------*/ int -coap_send_request(coap_request_state_t *state, coap_endpoint_t *endpoint, +coap_send_request(coap_callback_request_state_t *callback_state, coap_endpoint_t *endpoint, coap_message_t *request, - void (*callback)(coap_request_state_t *state)) + void (*callback)(coap_callback_request_state_t *callback_state)) { - /* can we have these variables shared between multiple requests? */ - /* ripped from blocking request */ - more = 0; - res_block = 0; - block_error = 0; + coap_request_state_t *state = &callback_state->state; + state->more = 0; + state->res_block = 0; + state->block_error = 0; state->block_num = 0; state->response = NULL; state->request = request; state->remote_endpoint = endpoint; - state->callback = callback; + callback_state->callback = callback; - return progress_request(state); + return progress_request(callback_state); } /*---------------------------------------------------------------------------*/ /** @} */ diff --git a/os/net/app-layer/coap/coap-callback-api.h b/os/net/app-layer/coap/coap-callback-api.h index 69ceea4f5..a9019aeb8 100644 --- a/os/net/app-layer/coap/coap-callback-api.h +++ b/os/net/app-layer/coap/coap-callback-api.h @@ -47,35 +47,30 @@ #include "coap-engine.h" #include "coap-transactions.h" +#include "coap-request-state.h" #include "sys/cc.h" /*---------------------------------------------------------------------------*/ /*- Client Part -------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ -typedef struct coap_request_state coap_request_state_t; +typedef struct coap_callback_request_state coap_callback_request_state_t; -struct coap_request_state { - coap_transaction_t *transaction; - coap_message_t *response; - coap_message_t *request; - coap_endpoint_t *remote_endpoint; - uint32_t block_num; - void *user_data; - coap_timer_t coap_timer; - void (*callback)(coap_request_state_t *state); +struct coap_callback_request_state { + coap_request_state_t state; + void (*callback)(coap_callback_request_state_t *state); }; /** * \brief Send a CoAP request to a remote endpoint - * \param state The state to handle the CoAP request + * \param callback_state The callback state to handle the CoAP request * \param endpoint The destination endpoint * \param request The request to be sent * \param callback callback to execute when the response arrives or the timeout expires * \return 1 if there is a transaction available to send, 0 otherwise */ -int coap_send_request(coap_request_state_t *state, coap_endpoint_t *endpoint, +int coap_send_request(coap_callback_request_state_t *callback_state, coap_endpoint_t *endpoint, coap_message_t *request, - void (*callback)(coap_request_state_t *state)); + void (*callback)(coap_callback_request_state_t *callback_state)); #endif /* COAP_CALLBACK_API_H_ */ /** @} */ diff --git a/os/net/app-layer/coap/coap-request-state.h b/os/net/app-layer/coap/coap-request-state.h new file mode 100644 index 000000000..4c5881bb6 --- /dev/null +++ b/os/net/app-layer/coap/coap-request-state.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2018, RISE SICS AB. + * 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 HOLDER 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. + */ + +/** + * \addtogroup coap + * @{ + */ + +/** + * \file + * Common request state for all the APIs + * \author + * Carlos Gonzalo Peces + */ +#ifndef COAP_REQUEST_STATE_H_ +#define COAP_REQUEST_STATE_H_ + +typedef enum { + COAP_REQUEST_STATUS_RESPONSE, /* Response received and no more blocks */ + COAP_REQUEST_STATUS_MORE, /* Response received and there are more blocks */ + COAP_REQUEST_STATUS_FINISHED, /* Request finished */ + COAP_REQUEST_STATUS_TIMEOUT, /* Request Timeout after all retransmissions */ + COAP_REQUEST_STATUS_BLOCK_ERROR /* Blocks in wrong order */ +} coap_request_status_t; + + +typedef struct coap_request_state { + coap_transaction_t *transaction; + coap_message_t *response; + coap_message_t *request; + coap_endpoint_t *remote_endpoint; + uint32_t block_num; + uint32_t res_block; + uint8_t more; + uint8_t block_error; + void *user_data; + coap_request_status_t status; +} coap_request_state_t; + + +#endif /* COAP_REQUEST_STATE_H_ */ +/** @} */ diff --git a/os/net/app-layer/coap/coap-uip.c b/os/net/app-layer/coap/coap-uip.c index 843049d4f..e2be92326 100644 --- a/os/net/app-layer/coap/coap-uip.c +++ b/os/net/app-layer/coap/coap-uip.c @@ -49,6 +49,7 @@ #include "contiki.h" #include "net/ipv6/uip-udp-packet.h" #include "net/ipv6/uiplib.h" +#include "net/routing/routing.h" #include "coap.h" #include "coap-engine.h" #include "coap-endpoint.h" @@ -58,10 +59,6 @@ #include "coap-keystore.h" #include "coap-keystore-simple.h" -#if UIP_CONF_IPV6_RPL -#include "rpl.h" -#endif /* UIP_CONF_IPV6_RPL */ - /* Log configuration */ #include "coap-log.h" #define LOG_MODULE "coap-uip" @@ -260,13 +257,12 @@ coap_endpoint_is_secure(const coap_endpoint_t *ep) int coap_endpoint_is_connected(const coap_endpoint_t *ep) { -#if UIP_CONF_IPV6_RPL #ifndef CONTIKI_TARGET_NATIVE - if(rpl_get_any_dag() == NULL) { + if(!uip_is_addr_linklocal(&ep->ipaddr) + && NETSTACK_ROUTING.node_is_reachable() == 0) { return 0; } #endif -#endif /* UIP_CONF_IPV6_RPL */ #ifdef WITH_DTLS if(ep != NULL && ep->secure != 0) { diff --git a/os/net/ipv6/uip-icmp6.c b/os/net/ipv6/uip-icmp6.c index be33a1101..707a13309 100644 --- a/os/net/ipv6/uip-icmp6.c +++ b/os/net/ipv6/uip-icmp6.c @@ -253,10 +253,6 @@ uip_icmp6_error_output(uint8_t type, uint8_t code, uint32_t param) { void uip_icmp6_send(const uip_ipaddr_t *dest, int type, int code, int payload_len) { - LOG_INFO("Sending ICMPv6 packet to "); - LOG_INFO_6ADDR(&UIP_IP_BUF->destipaddr); - LOG_INFO_(", type %u, code %u, len %u\n", type, code, payload_len); - UIP_IP_BUF->vtc = 0x60; UIP_IP_BUF->tcflow = 0; UIP_IP_BUF->flow = 0; @@ -279,6 +275,10 @@ uip_icmp6_send(const uip_ipaddr_t *dest, int type, int code, int payload_len) UIP_STAT(++uip_stat.icmp.sent); UIP_STAT(++uip_stat.ip.sent); + LOG_INFO("Sending ICMPv6 packet to "); + LOG_INFO_6ADDR(&UIP_IP_BUF->destipaddr); + LOG_INFO_(", type %u, code %u, len %u\n", type, code, payload_len); + tcpip_ipv6_output(); } /*---------------------------------------------------------------------------*/ diff --git a/os/net/ipv6/uip-sr.c b/os/net/ipv6/uip-sr.c index a82491827..133fd6f28 100644 --- a/os/net/ipv6/uip-sr.c +++ b/os/net/ipv6/uip-sr.c @@ -258,7 +258,11 @@ uip_sr_link_snprint(char *buf, int buflen, uip_sr_node_t *link) NETSTACK_ROUTING.get_sr_node_ipaddr(&child_ipaddr, link); NETSTACK_ROUTING.get_sr_node_ipaddr(&parent_ipaddr, link->parent); - index += uiplib_ipaddr_snprint(buf+index, buflen-index, &child_ipaddr); + if(LOG_WITH_COMPACT_ADDR) { + index += log_6addr_compact_snprint(buf+index, buflen-index, &child_ipaddr); + } else { + index += uiplib_ipaddr_snprint(buf+index, buflen-index, &child_ipaddr); + } if(index >= buflen) { return index; } @@ -273,7 +277,11 @@ uip_sr_link_snprint(char *buf, int buflen, uip_sr_node_t *link) if(index >= buflen) { return index; } - index += uiplib_ipaddr_snprint(buf+index, buflen-index, &parent_ipaddr); + if(LOG_WITH_COMPACT_ADDR) { + index += log_6addr_compact_snprint(buf+index, buflen-index, &parent_ipaddr); + } else { + index += uiplib_ipaddr_snprint(buf+index, buflen-index, &parent_ipaddr); + } if(index >= buflen) { return index; } diff --git a/os/net/mac/tsch/tsch-security.c b/os/net/mac/tsch/tsch-security.c index 1c4266626..12f0de6d5 100644 --- a/os/net/mac/tsch/tsch-security.c +++ b/os/net/mac/tsch/tsch-security.c @@ -53,6 +53,10 @@ #include #include +#if LLSEC802154_ENABLED && !LLSEC802154_USES_EXPLICIT_KEYS +#error LLSEC802154_ENABLED set but LLSEC802154_USES_EXPLICIT_KEYS unset +#endif /* LLSEC802154_ENABLED */ + /* The two keys K1 and K2 from 6TiSCH minimal configuration * K1: well-known, used for EBs * K2: secret, used for data and ACK diff --git a/os/net/mac/tsch/tsch-security.h b/os/net/mac/tsch/tsch-security.h index 56c854414..4320d3a70 100644 --- a/os/net/mac/tsch/tsch-security.h +++ b/os/net/mac/tsch/tsch-security.h @@ -54,10 +54,6 @@ * - set LLSEC802154_CONF_USES_EXPLICIT_KEYS * */ -#if LLSEC802154_ENABLED && !LLSEC802154_USES_EXPLICIT_KEYS -#error LLSEC802154_ENABLED set but LLSEC802154_USES_EXPLICIT_KEYS unset -#endif /* LLSEC802154_ENABLED */ - /* K1, defined in 6TiSCH minimal, is well-known (offers no security) and used for EBs only */ #ifdef TSCH_SECURITY_CONF_K1 #define TSCH_SECURITY_K1 TSCH_SECURITY_CONF_K1 diff --git a/os/net/routing/rpl-lite/rpl-neighbor.c b/os/net/routing/rpl-lite/rpl-neighbor.c index a066af313..a0f5d95b0 100644 --- a/os/net/routing/rpl-lite/rpl-neighbor.c +++ b/os/net/routing/rpl-lite/rpl-neighbor.c @@ -94,7 +94,11 @@ rpl_neighbor_snprint(char *buf, int buflen, rpl_nbr_t *nbr) const struct link_stats *stats = rpl_neighbor_get_link_stats(nbr); clock_time_t clock_now = clock_time(); - index += uiplib_ipaddr_snprint(buf+index, buflen-index, rpl_neighbor_get_ipaddr(nbr)); + if(LOG_WITH_COMPACT_ADDR) { + index += log_6addr_compact_snprint(buf+index, buflen-index, rpl_neighbor_get_ipaddr(nbr)); + } else { + index += uiplib_ipaddr_snprint(buf+index, buflen-index, rpl_neighbor_get_ipaddr(nbr)); + } if(index >= buflen) { return index; } diff --git a/os/services/deployment/deployment.c b/os/services/deployment/deployment.c new file mode 100644 index 000000000..087d9766d --- /dev/null +++ b/os/services/deployment/deployment.c @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2018, RISE SICS. + * 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 Institute 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 INSTITUTE 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 INSTITUTE 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. + * + */ + +/** +* \addtogroup deployment +* @{ +*/ + +/** + * \file + * Code managing id<->mac address<->IPv6 address mapping, and doing this + * for different deployment scenarios: Cooja, Nodes, Indriya or Twist testbeds + * + * \author Simon Duquennoy + */ + +#include "contiki.h" +#include "contiki-net.h" +#include "deployment.h" +#include "sys/node-id.h" +#include +#include + +/** + * \brief List of ID<->MAC mapping used for different deployments + */ +extern const struct id_mac DEPLOYMENT_MAPPING[]; +/** + * \brief The number of nodes in the deployment + */ +static int node_count = 0; + +/*---------------------------------------------------------------------------*/ +void +deployment_init(void) +{ + const struct id_mac *curr = DEPLOYMENT_MAPPING; + /* Initialize node_id */ + node_id = deployment_id_from_lladdr((const linkaddr_t *)&linkaddr_node_addr); + /* Count nodes */ + node_count = 0; + while(curr->id != 0) { + node_count++; + curr++; + } +} +/*---------------------------------------------------------------------------*/ +int +deployment_node_count(void) +{ + return node_count; +} +/*---------------------------------------------------------------------------*/ +uint16_t +deployment_id_from_lladdr(const linkaddr_t *lladdr) +{ + const struct id_mac *curr = DEPLOYMENT_MAPPING; + if(lladdr == NULL) { + return 0; + } + while(curr->id != 0) { + /* Assume network-wide unique 16-bit MAC addresses */ + if(linkaddr_cmp(lladdr, &curr->mac)) { + return curr->id; + } + curr++; + } + return 0; +} +/*---------------------------------------------------------------------------*/ +void +deployment_lladdr_from_id(linkaddr_t *lladdr, uint16_t id) +{ + const struct id_mac *curr = DEPLOYMENT_MAPPING; + if(id == 0 || lladdr == NULL) { + return; + } + while(curr->id != 0) { + if(curr->id == id) { + linkaddr_copy(lladdr, &curr->mac); + return; + } + curr++; + } +} +/*---------------------------------------------------------------------------*/ +uint16_t +deployment_id_from_iid(const uip_ipaddr_t *ipaddr) +{ + const linkaddr_t lladdr; + uip_ds6_set_lladdr_from_iid((uip_lladdr_t *)&lladdr, ipaddr); + return deployment_id_from_lladdr(&lladdr); +} +/*---------------------------------------------------------------------------*/ +void +deployment_iid_from_id(uip_ipaddr_t *ipaddr, uint16_t id) +{ + linkaddr_t lladdr; + deployment_lladdr_from_id(&lladdr, id); + uip_ds6_set_addr_iid(ipaddr, (uip_lladdr_t *)&lladdr); +} +/*---------------------------------------------------------------------------*/ +uint16_t +deployment_id_from_index(uint16_t index) +{ + if(index < deployment_node_count()) { + return DEPLOYMENT_MAPPING[index].id; + } else { + return 0; + } +} +/*---------------------------------------------------------------------------*/ + +/** @} */ diff --git a/os/services/deployment/deployment.h b/os/services/deployment/deployment.h new file mode 100644 index 000000000..29cd5214b --- /dev/null +++ b/os/services/deployment/deployment.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2018, RISE SICS. + * 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 Institute 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 INSTITUTE 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 INSTITUTE 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. + * + */ + +/** +* \addtogroup deployment +* @{ +* +* \file + Per-deployment MAC <-> nodeid mapping +* \author Simon Duquennoy +* +*/ + +#ifndef DEPLOYMENT_H_ +#define DEPLOYMENT_H_ + +#include "contiki-conf.h" +#include "sys/node-id.h" +#include "net/ipv6/uip.h" +#include "net/linkaddr.h" + +/** + * \brief ID<->MAC address mapping structure + */ +struct id_mac { + uint16_t id; + linkaddr_t mac; +}; + +/** + * DEPLOYMENT_MAPPING: + * A table of struct id_mac that provides ID-MAC mapping for a deployment. + * Example with four nodes: + * In configuration file: + * \#define DEPLOYMENT_MAPPING custom_array + * In a .c file: + * const struct id_mac custom_array[] = { + { 1, {{0x00,0x12,0x4b,0x00,0x06,0x0d,0xb6,0x14}}}, + { 2, {{0x00,0x12,0x4b,0x00,0x06,0x0d,0xb1,0xe7}}}, + { 3, {{0x00,0x12,0x4b,0x00,0x06,0x0d,0xb4,0x35}}}, + { 4, {{0x00,0x12,0x4b,0x00,0x06,0x0d,0xb1,0xcf}}}, + { 0, {{0}}} + }; + */ + +/** + * Initialize the deployment module + */ +void deployment_init(void); + +/** + * Get the number of nodes for the deployment (length of mapping table) + * + * \return The number of nodes in the deployment + */ +int deployment_node_count(void); + +/** + * Get node ID from a link-layer address, from the deployment mapping table + * + * \param lladdr The link-layer address to look up for + * \return Node ID from a corresponding link-layer address + */ +uint16_t deployment_id_from_lladdr(const linkaddr_t *lladdr); + +/** + * Get node link-layer address from a node ID, from the deployment mapping table + * + * \param lladdr A pointer where to write the link-layer address + * \param id The node ID to look up for + */ +void deployment_lladdr_from_id(linkaddr_t *lladdr, uint16_t id); + +/** + * Get node ID from the IID of an IPv6 address + * + * \param ipaddr The IPv6 (global or link-local) address that contains the IID + * \return Node ID from a corresponding IID + */ +uint16_t deployment_id_from_iid(const uip_ipaddr_t *ipaddr); + +/** + * Get IPv6 IID from node IDs + * + * \param ipaddr The IPv6 where to write the IID + * \param id The node ID + */ +void deployment_iid_from_id(uip_ipaddr_t *ipaddr, uint16_t id); + +/** + * Get node ID from index in mapping table + * + * \param index The index in the deployment mapping table + * \return Node ID at the corresponding index + */ +uint16_t deployment_id_from_index(uint16_t index); + +#endif /* DEPLOYMENT_H_ */ +/** @} */ diff --git a/os/services/deployment/module-macros.h b/os/services/deployment/module-macros.h new file mode 100644 index 000000000..ed2d639ef --- /dev/null +++ b/os/services/deployment/module-macros.h @@ -0,0 +1 @@ +#define BUILD_WITH_DEPLOYMENT 1 diff --git a/os/services/lwm2m/lwm2m-rd-client.c b/os/services/lwm2m/lwm2m-rd-client.c index 63c9443e1..4b4b11528 100644 --- a/os/services/lwm2m/lwm2m-rd-client.c +++ b/os/services/lwm2m/lwm2m-rd-client.c @@ -84,7 +84,7 @@ #define STATE_MACHINE_UPDATE_INTERVAL 500 static struct lwm2m_session_info session_info; -static coap_request_state_t rd_request_state; +static coap_callback_request_state_t rd_request_state; static coap_message_t request[1]; /* This way the message can be treated as pointer as usual. */ @@ -118,7 +118,6 @@ static uint8_t rd_state = 0; static uint8_t rd_flags = FLAG_RD_DATA_UPDATE_ON_DIRTY; static uint64_t wait_until_network_check = 0; static uint64_t last_update; -static uint64_t last_rd_progress = 0; static char query_data[64]; /* allocate some data for queries and updates */ static uint8_t rd_data[128]; /* allocate some data for the RD */ @@ -126,7 +125,7 @@ static uint8_t rd_data[128]; /* allocate some data for the RD */ static uint32_t rd_block1; static uint8_t rd_more; static coap_timer_t rd_timer; -static void (*rd_callback)(coap_request_state_t *state); +static void (*rd_callback)(coap_callback_request_state_t *callback_state); static coap_timer_t block1_timer; @@ -143,7 +142,7 @@ static void queue_mode_awake_timer_callback(coap_timer_t *timer); #endif static void check_periodic_observations(); -static void update_callback(coap_request_state_t *state); +static void update_callback(coap_callback_request_state_t *callback_state); static int set_rd_data(coap_message_t *request) @@ -370,10 +369,11 @@ update_bootstrap_server(void) * TODO */ static void -bootstrap_callback(coap_request_state_t *state) +bootstrap_callback(coap_callback_request_state_t *callback_state) { + coap_request_state_t *state = &callback_state->state; LOG_DBG("Bootstrap callback Response: %d, ", state->response != NULL); - if(state->response) { + if(state->status == COAP_REQUEST_STATUS_RESPONSE) { if(CHANGED_2_04 == state->response->code) { LOG_DBG_("Considered done!\n"); rd_state = BOOTSTRAP_DONE; @@ -383,12 +383,14 @@ bootstrap_callback(coap_request_state_t *state) LOG_DBG_("Failed with code %d. Retrying\n", state->response->code); /* TODO Application callback? */ rd_state = INIT; - } else if(BOOTSTRAP_SENT == rd_state) { /* this can handle double invocations */ - /* Failure! */ - LOG_DBG("Bootstrap failed! Retry?"); + } else if(state->status == COAP_REQUEST_STATUS_TIMEOUT) { + LOG_DBG_("Server not responding! Retry?"); rd_state = DO_BOOTSTRAP; + } else if(state->status == COAP_REQUEST_STATUS_FINISHED) { + LOG_DBG_("Request finished. Ignore\n"); } else { - LOG_DBG("Ignore\n"); + LOG_DBG_("Unexpected error! Retry?"); + rd_state = DO_BOOTSTRAP; } } /*---------------------------------------------------------------------------*/ @@ -427,10 +429,11 @@ block1_rd_callback(coap_timer_t *timer) * Page 65-66 in 07 April 2016 spec. */ static void -registration_callback(coap_request_state_t *state) +registration_callback(coap_callback_request_state_t *callback_state) { - LOG_DBG("Registration callback. Response: %d, ", state->response != NULL); - if(state->response) { + coap_request_state_t *state = &callback_state->state; + LOG_DBG("Registration callback. Status: %d. Response: %d, ", state->status, state->response != NULL); + if(state->status == COAP_REQUEST_STATUS_RESPONSE) { /* check state and possibly set registration to done */ /* If we get a continue - we need to call the rd generator one more time */ if(CONTINUE_2_31 == state->response->code) { @@ -472,10 +475,14 @@ registration_callback(coap_request_state_t *state) } /* TODO Application callback? */ rd_state = INIT; - /* remember last progress time */ - last_rd_progress = coap_timer_uptime(); + } else if(state->status == COAP_REQUEST_STATUS_TIMEOUT) { + LOG_DBG_("Server not responding, trying to reconnect\n"); + rd_state = INIT; + } else if(state->status == COAP_REQUEST_STATUS_FINISHED){ + LOG_DBG_("Request finished. Ignore\n"); } else { - LOG_DBG_("Ignore\n"); + LOG_DBG_("Unexpected error, trying to reconnect\n"); + rd_state = INIT; } } /*---------------------------------------------------------------------------*/ @@ -483,11 +490,12 @@ registration_callback(coap_request_state_t *state) * Page 65-66 in 07 April 2016 spec. */ static void -update_callback(coap_request_state_t *state) +update_callback(coap_callback_request_state_t *callback_state) { - LOG_DBG("Update callback. Response: %d, ", state->response != NULL); + coap_request_state_t *state = &callback_state->state; + LOG_DBG("Update callback. Status: %d. Response: %d, ", state->status, state->response != NULL); - if(state->response) { + if(state->status == COAP_REQUEST_STATUS_RESPONSE) { /* If we get a continue - we need to call the rd generator one more time */ if(CONTINUE_2_31 == state->response->code) { /* We assume that size never change?! */ @@ -522,20 +530,26 @@ update_callback(coap_request_state_t *state) state->response->code); rd_state = DO_REGISTRATION; } - /* remember last progress */ - last_rd_progress = coap_timer_uptime(); + } else if(state->status == COAP_REQUEST_STATUS_TIMEOUT) { + LOG_DBG_("Server not responding, trying to reconnect\n"); + rd_state = INIT; + } else if(state->status == COAP_REQUEST_STATUS_FINISHED){ + LOG_DBG_("Request finished. Ignore\n"); } else { - LOG_DBG("Ignore\n"); + LOG_DBG_("Unexpected error, trying to reconnect\n"); + rd_state = INIT; } } /*---------------------------------------------------------------------------*/ static void -deregister_callback(coap_request_state_t *state) +deregister_callback(coap_callback_request_state_t *callback_state) { - LOG_DBG("Deregister callback. Response Code: %d\n", + coap_request_state_t *state = &callback_state->state; + LOG_DBG("Deregister callback. Status: %d. Response Code: %d\n", + state->status, state->response != NULL ? state->response->code : 0); - if(state->response && (DELETED_2_02 == state->response->code)) { + if(state->status == COAP_REQUEST_STATUS_RESPONSE && (DELETED_2_02 == state->response->code)) { LOG_DBG("Deregistration success\n"); rd_state = DEREGISTERED; perform_session_callback(LWM2M_RD_CLIENT_DEREGISTERED); @@ -548,13 +562,6 @@ deregister_callback(coap_request_state_t *state) } } /*---------------------------------------------------------------------------*/ -static void -recover_from_rd_delay(void) -{ - /* This can be improved in the future... */ - rd_state = INIT; -} -/*---------------------------------------------------------------------------*/ /* CoAP timer callback */ static void periodic_process(coap_timer_t *timer) @@ -611,10 +618,10 @@ periodic_process(coap_timer_t *timer) LOG_INFO_COAP_EP(&session_info.bs_server_ep); LOG_INFO_("] as '%s'\n", query_data); - coap_send_request(&rd_request_state, &session_info.bs_server_ep, - request, bootstrap_callback); - - rd_state = BOOTSTRAP_SENT; + if(coap_send_request(&rd_request_state, &session_info.bs_server_ep, + request, bootstrap_callback)) { + rd_state = BOOTSTRAP_SENT; + } } } break; @@ -710,18 +717,14 @@ periodic_process(coap_timer_t *timer) } LOG_INFO_("' More:%d\n", rd_more); - coap_send_request(&rd_request_state, &session_info.server_ep, - request, registration_callback); - last_rd_progress = coap_timer_uptime(); - rd_state = REGISTRATION_SENT; + if(coap_send_request(&rd_request_state, &session_info.server_ep, + request, registration_callback)){ + rd_state = REGISTRATION_SENT; + } } break; case REGISTRATION_SENT: /* just wait until the callback kicks us to the next state... */ - if(last_rd_progress + MAX_RD_UPDATE_WAIT < coap_timer_uptime()) { - /* Timeout on the update - something is wrong? */ - recover_from_rd_delay(); - } break; case REGISTRATION_DONE: /* All is done! */ @@ -733,10 +736,10 @@ periodic_process(coap_timer_t *timer) ((uint32_t)session_info.lifetime * 500) <= now - last_update) { /* triggered or time to send an update to the server, at half-time! sec vs ms */ prepare_update(request, rd_flags & FLAG_RD_DATA_UPDATE_TRIGGERED); - coap_send_request(&rd_request_state, &session_info.server_ep, request, - update_callback); - last_rd_progress = coap_timer_uptime(); - rd_state = UPDATE_SENT; + if(coap_send_request(&rd_request_state, &session_info.server_ep, request, + update_callback)) { + rd_state = UPDATE_SENT; + } } break; #if LWM2M_QUEUE_MODE_ENABLED @@ -754,27 +757,24 @@ periodic_process(coap_timer_t *timer) LWM2M_QUEUE_MODE_WAKE_UP(); #endif /* LWM2M_QUEUE_MODE_WAKE_UP */ prepare_update(request, rd_flags & FLAG_RD_DATA_UPDATE_TRIGGERED); - coap_send_request(&rd_request_state, &session_info.server_ep, request, - update_callback); - last_rd_progress = coap_timer_uptime(); - rd_state = UPDATE_SENT; + if(coap_send_request(&rd_request_state, &session_info.server_ep, request, + update_callback)) { + rd_state = UPDATE_SENT; + } break; #endif /* LWM2M_QUEUE_MODE_ENABLED */ case UPDATE_SENT: /* just wait until the callback kicks us to the next state... */ - if(last_rd_progress + MAX_RD_UPDATE_WAIT < coap_timer_uptime()) { - /* Timeout on the update - something is wrong? */ - recover_from_rd_delay(); - } break; case DEREGISTER: LOG_INFO("DEREGISTER %s\n", session_info.assigned_ep); coap_init_message(request, COAP_TYPE_CON, COAP_DELETE, 0); coap_set_header_uri_path(request, session_info.assigned_ep); - coap_send_request(&rd_request_state, &session_info.server_ep, request, - deregister_callback); - rd_state = DEREGISTER_SENT; + if(coap_send_request(&rd_request_state, &session_info.server_ep, request, + deregister_callback)) { + rd_state = DEREGISTER_SENT; + } break; case DEREGISTER_SENT: break; diff --git a/os/services/shell/shell-commands.c b/os/services/shell/shell-commands.c index f2f036eda..e8bb4b6b2 100644 --- a/os/services/shell/shell-commands.c +++ b/os/services/shell/shell-commands.c @@ -45,6 +45,7 @@ #include "contiki.h" #include "shell.h" #include "shell-commands.h" +#include "lib/list.h" #include "sys/log.h" #include "dev/watchdog.h" #include "net/ipv6/uip.h" @@ -76,7 +77,8 @@ static uint16_t curr_ping_datalen; #if TSCH_WITH_SIXTOP static shell_command_6top_sub_cmd_t sixtop_sub_cmd = NULL; #endif /* TSCH_WITH_SIXTOP */ - +static struct shell_command_set_t builtin_shell_command_set; +LIST(shell_command_sets); /*---------------------------------------------------------------------------*/ static const char * ds6_nbr_state_to_str(uint8_t state) @@ -345,15 +347,16 @@ PT_THREAD(cmd_log(struct pt *pt, shell_output_func output, char *args)) static PT_THREAD(cmd_help(struct pt *pt, shell_output_func output, char *args)) { - struct shell_command_t *cmd_ptr; - + struct shell_command_set_t *set; + const struct shell_command_t *cmd; PT_BEGIN(pt); SHELL_OUTPUT(output, "Available commands:\n"); - cmd_ptr = shell_commands; - while(cmd_ptr->name != NULL) { - SHELL_OUTPUT(output, "%s\n", cmd_ptr->help); - cmd_ptr++; + /* Note: we explicitly don't expend any code space to deal with shadowing */ + for(set = list_head(shell_command_sets); set != NULL; set = list_item_next(set)) { + for(cmd = set->commands; cmd->name != NULL; ++cmd) { + SHELL_OUTPUT(output, "%s\n", cmd->help); + } } PT_END(pt); @@ -729,12 +732,48 @@ PT_THREAD(cmd_6top(struct pt *pt, shell_output_func output, char *args)) void shell_commands_init(void) { + list_init(shell_command_sets); + list_add(shell_command_sets, &builtin_shell_command_set); /* Set up Ping Reply callback */ uip_icmp6_echo_reply_callback_add(&echo_reply_notification, echo_reply_handler); } /*---------------------------------------------------------------------------*/ -struct shell_command_t shell_commands[] = { +void +shell_command_set_register(struct shell_command_set_t *set) +{ + list_push(shell_command_sets, set); +} +/*---------------------------------------------------------------------------*/ +int +shell_command_set_deregister(struct shell_command_set_t *set) +{ + if(!list_contains(shell_command_sets, set)) { + return !0; + } + list_remove(shell_command_sets, set); + return 0; +} +/*---------------------------------------------------------------------------*/ +const struct shell_command_t * +shell_command_lookup(const char *name) +{ + struct shell_command_set_t *set; + const struct shell_command_t *cmd; + + for(set = list_head(shell_command_sets); + set != NULL; + set = list_item_next(set)) { + for(cmd = set->commands; cmd->name != NULL; ++cmd) { + if(!strcmp(cmd->name, name)) { + return cmd; + } + } + } + return NULL; +} +/*---------------------------------------------------------------------------*/ +const struct shell_command_t builtin_shell_commands[] = { { "help", cmd_help, "'> help': Shows this help" }, { "reboot", cmd_reboot, "'> reboot': Reboot the board by watchdog_reboot()" }, { "ip-addr", cmd_ipaddr, "'> ip-addr': Shows all IPv6 addresses" }, @@ -765,4 +804,8 @@ struct shell_command_t shell_commands[] = { { NULL, NULL, NULL }, }; +static struct shell_command_set_t builtin_shell_command_set = { + .next = NULL, + .commands = builtin_shell_commands, +}; /** @} */ diff --git a/os/services/shell/shell-commands.h b/os/services/shell/shell-commands.h index f89703273..f510e861d 100644 --- a/os/services/shell/shell-commands.h +++ b/os/services/shell/shell-commands.h @@ -53,8 +53,14 @@ struct shell_command_t { const char *help; }; -/* The set of supported commands */ -extern struct shell_command_t shell_commands[]; +struct shell_command_set_t { + struct shell_command_set_t *next; + const struct shell_command_t *const commands; +}; + +void shell_command_set_register(struct shell_command_set_t *); +int shell_command_set_deregister(struct shell_command_set_t *); +const struct shell_command_t *shell_command_lookup(const char *); /** * Initializes Shell-commands module diff --git a/os/services/shell/shell.c b/os/services/shell/shell.c index fcf419880..c219b1903 100644 --- a/os/services/shell/shell.c +++ b/os/services/shell/shell.c @@ -89,7 +89,7 @@ output_prompt(shell_output_func output) PT_THREAD(shell_input(struct pt *pt, shell_output_func output, const char *cmd)) { static char *args; - static struct shell_command_t *cmd_ptr; + static const struct shell_command_t *cmd_descr = NULL; PT_BEGIN(pt); @@ -105,20 +105,14 @@ PT_THREAD(shell_input(struct pt *pt, shell_output_func output, const char *cmd)) args++; } - /* Lookup for command */ - cmd_ptr = shell_commands; - while(cmd_ptr->name != NULL) { - if(strcmp(cmd, cmd_ptr->name) == 0) { - static struct pt cmd_pt; - PT_SPAWN(pt, &cmd_pt, cmd_ptr->func(&cmd_pt, output, args)); - goto done; - } - cmd_ptr++; + cmd_descr = shell_command_lookup(cmd); + if(cmd_descr != NULL) { + static struct pt cmd_pt; + PT_SPAWN(pt, &cmd_pt, cmd_descr->func(&cmd_pt, output, args)); + } else { + SHELL_OUTPUT(output, "Command not found. Type 'help' for a list of commands\n"); } - SHELL_OUTPUT(output, "Command not found. Type 'help' for a list of commands\n"); - -done: output_prompt(output); PT_END(pt); } diff --git a/os/sys/log-conf.h b/os/sys/log-conf.h index 24cf21f71..17d8562cc 100644 --- a/os/sys/log-conf.h +++ b/os/sys/log-conf.h @@ -46,7 +46,8 @@ #ifndef __LOG_CONF_H__ #define __LOG_CONF_H__ -/* Log only the last 16 bytes of link-layer and IPv6 addresses */ +/* Log only the last 16 bytes of link-layer and IPv6 addresses (or, if) + * the deployment module is enabled, the node IDs */ #ifdef LOG_CONF_WITH_COMPACT_ADDR #define LOG_WITH_COMPACT_ADDR LOG_CONF_WITH_COMPACT_ADDR #else /* LOG_CONF_WITH_COMPACT_ADDR */ diff --git a/os/sys/log.c b/os/sys/log.c index 9a02087a2..abb960b98 100644 --- a/os/sys/log.c +++ b/os/sys/log.c @@ -51,6 +51,7 @@ #include "sys/log.h" #include "net/ipv6/ip64-addr.h" #include "net/ipv6/uiplib.h" +#include "deployment/deployment.h" int curr_log_level_rpl = LOG_CONF_LEVEL_RPL; int curr_log_level_tcpip = LOG_CONF_LEVEL_TCPIP; @@ -90,22 +91,36 @@ log_6addr(const uip_ipaddr_t *ipaddr) LOG_OUTPUT("%s", buf); } /*---------------------------------------------------------------------------*/ +int +log_6addr_compact_snprint(char *buf, size_t size, const uip_ipaddr_t *ipaddr) +{ + if(ipaddr == NULL) { + return snprintf(buf, size, "6A-NULL"); + } else { + char *prefix = NULL; + if(uip_is_addr_mcast(ipaddr)) { + prefix = "6M"; + } else if(uip_is_addr_linklocal(ipaddr)) { + prefix = "6L"; + } else { + prefix = "6G"; + } +#if BUILD_WITH_DEPLOYMENT + return snprintf(buf, size, "%s-%03u", prefix, deployment_id_from_iid(ipaddr)); +#else /* BUILD_WITH_DEPLOYMENT */ + return snprintf(buf, size, "%s-%04x", prefix, UIP_HTONS(ipaddr->u16[sizeof(uip_ipaddr_t)/2-1])); +#endif /* BUILD_WITH_DEPLOYMENT */ + } +} +/*---------------------------------------------------------------------------*/ void log_6addr_compact(const uip_ipaddr_t *ipaddr) { - if(ipaddr == NULL) { - LOG_OUTPUT("6A-NULL"); - } else if(uip_is_addr_mcast(ipaddr)) { - LOG_OUTPUT("6M-%04x", UIP_HTONS(ipaddr->u16[sizeof(uip_ipaddr_t)/2-1])); - } else if(uip_is_addr_linklocal(ipaddr)) { - LOG_OUTPUT("6L-%04x", UIP_HTONS(ipaddr->u16[sizeof(uip_ipaddr_t)/2-1])); - } else { - LOG_OUTPUT("6G-%04x", UIP_HTONS(ipaddr->u16[sizeof(uip_ipaddr_t)/2-1])); - } + char buf[8]; + log_6addr_compact_snprint(buf, sizeof(buf), ipaddr); + LOG_OUTPUT("%s", buf); } - #endif /* NETSTACK_CONF_WITH_IPV6 */ - /*---------------------------------------------------------------------------*/ void log_lladdr(const linkaddr_t *lladdr) @@ -130,11 +145,15 @@ log_lladdr_compact(const linkaddr_t *lladdr) if(lladdr == NULL || linkaddr_cmp(lladdr, &linkaddr_null)) { LOG_OUTPUT("LL-NULL"); } else { +#if BUILD_WITH_DEPLOYMENT + LOG_OUTPUT("LL-%04u", deployment_id_from_lladdr(lladdr)); +#else /* BUILD_WITH_DEPLOYMENT */ #if LINKADDR_SIZE == 8 LOG_OUTPUT("LL-%04x", UIP_HTONS(lladdr->u16[LINKADDR_SIZE/2-1])); #elif LINKADDR_SIZE == 2 LOG_OUTPUT("LL-%04x", UIP_HTONS(lladdr->u16)); #endif +#endif /* BUILD_WITH_DEPLOYMENT */ } } /*---------------------------------------------------------------------------*/ diff --git a/os/sys/log.h b/os/sys/log.h index e27a3007e..0f5f840d2 100644 --- a/os/sys/log.h +++ b/os/sys/log.h @@ -194,6 +194,17 @@ void log_6addr(const uip_ipaddr_t *ipaddr); */ void log_6addr_compact(const uip_ipaddr_t *ipaddr); +/** + * Write at most size - 1 characters of the IP address to the output string, + * in a compact representation. The output is always null-terminated, unless + * size is 0. + * + * \param buf A pointer to an output string with at least size bytes. + * \param size The max number of characters to write to the output string. + * \param ipaddr A pointer to a uip_ipaddr_t that will be printed with printf(). + */ +int log_6addr_compact_snprint(char *buf, size_t size, const uip_ipaddr_t *ipaddr); + #endif /* NETSTACK_CONF_WITH_IPV6 */ /** diff --git a/os/sys/node-id.c b/os/sys/node-id.c index 7914256e9..2fd52f8dc 100644 --- a/os/sys/node-id.c +++ b/os/sys/node-id.c @@ -40,12 +40,17 @@ #include "contiki.h" #include "sys/node-id.h" #include "net/linkaddr.h" +#include "services/deployment/deployment.h" uint16_t node_id = 0; void node_id_init(void) { +#if BUILD_WITH_DEPLOYMENT + deployment_init(); +#else /* BUILD_WITH_DEPLOYMENT */ /* Initialize with a default value derived from linkaddr */ node_id = linkaddr_node_addr.u8[LINKADDR_SIZE - 1] + (linkaddr_node_addr.u8[LINKADDR_SIZE - 2] << 8); +#endif /* BUILD_WITH_DEPLOYMENT */ } diff --git a/tests/00-doxygen/Makefile b/tests/00-documentation/Makefile similarity index 60% rename from tests/00-doxygen/Makefile rename to tests/00-documentation/Makefile index 067090b73..ea0aa85b4 100644 --- a/tests/00-doxygen/Makefile +++ b/tests/00-documentation/Makefile @@ -24,35 +24,56 @@ # 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. +TOOLS_DIR = ../../tools -DOCDIR=../../tools/doxygen +DOXYGEN = doxygen +DOXYGEN_DIR = $(TOOLS_DIR)/doxygen +DOXYGEN_LOG = $(DOXYGEN).log +DOXYGEN_ERR = $(DOXYGEN).err + +RAEDTHEDOCS = readthedocs +READTHEDOCS_DIR = $(TOOLS_DIR)/readthedocs +READTHEDOCS_LOG = $(RAEDTHEDOCS).log +READTHEDOCS_ERR = $(RAEDTHEDOCS).err + +CLEAN_TARGETS += $(DOXYGEN_LOG) $(DOXYGEN_ERR) +CLEAN_TARGETS += $(READTHEDOCS_LOG) $(READTHEDOCS_ERR) all: clean summary doxygen: - -@$(MAKE) -C $(DOCDIR) 2> doxygen.err > /dev/null + -@$(MAKE) -C $(DOXYGEN_DIR) 2> $(DOXYGEN_ERR) > /dev/null -summary: doxygen +readthedocs: + -@$(MAKE) -C $(READTHEDOCS_DIR) 2> $(READTHEDOCS_ERR) > /dev/null + +summary: doxygen readthedocs @( \ 1> summary; \ - if [ -s doxygen.err ] ; then \ - echo "Doxygen: TEST FAIL" | tee summary; \ + if [ -s $(DOXYGEN_ERR) ] ; then \ + echo "Doxygen: TEST FAIL" | tee -a summary; \ echo "Errors:"; \ - cat doxygen.err; \ + cat $(DOXYGEN_ERR); \ fi ; \ - if [ -s $(DOCDIR)/doxygen.log ] ; then \ - echo "Doxygen: TEST FAIL" | tee summary; \ + if [ -s $(DOXYGEN_DIR)/doxygen.log ] ; then \ + echo "Doxygen: TEST FAIL" | tee -a summary; \ echo "Warnings:"; \ - cat $(DOCDIR)/doxygen.log; \ + cat $(DOXYGEN_DIR)/doxygen.log; \ + fi ; \ + if [ -s $(READTHEDOCS_ERR) ] ; then \ + echo "Readthedocs: TEST FAIL" | tee -a summary; \ + echo "Errors:"; \ + cat $(READTHEDOCS_ERR); \ fi ; \ if [ ! -s summary ] ; then \ - echo "Doxygen: TEST OK (no warning nor error)" | tee summary; \ + echo "Documentation: TEST OK (no warning nor error)" | tee summary; \ fi ; \ ) - @rm doxygen.err + @rm -f $(CLEAN_TARGETS) @echo "========== Summary ==========" @cat summary clean: - @rm -f summary doxygen.err - @$(MAKE) -C $(DOCDIR) clean + @rm -f summary $(CLEAN_TARGETS) + @$(MAKE) -C $(DOXYGEN_DIR) clean + @$(MAKE) -C $(READTHEDOCS_DIR) clean diff --git a/tests/03-compile-arm-ports-02/Makefile b/tests/03-compile-arm-ports-02/Makefile index ceb1b79e3..7efb1b14b 100644 --- a/tests/03-compile-arm-ports-02/Makefile +++ b/tests/03-compile-arm-ports-02/Makefile @@ -66,6 +66,7 @@ rpl-border-router/openmote-cc2538 \ libs/ipv6-hooks/openmote-cc2538 \ libs/shell/openmote-cc2538 \ libs/simple-energest/openmote-cc2538 \ +libs/deployment/openmote-cc2538 \ TOOLS= diff --git a/tools/cc2538-bsl b/tools/cc2538-bsl index 81cd4cbe5..edb3c8c73 160000 --- a/tools/cc2538-bsl +++ b/tools/cc2538-bsl @@ -1 +1 @@ -Subproject commit 81cd4cbe5e05e4fa97782d1859ed15b769f4bf2b +Subproject commit edb3c8c73c4688ebd336b278450db216512a769b diff --git a/tools/docker/Dockerfile b/tools/docker/Dockerfile index e1d85d003..9d15704ac 100644 --- a/tools/docker/Dockerfile +++ b/tools/docker/Dockerfile @@ -3,9 +3,9 @@ FROM 32bit/ubuntu:16.04 # Tools RUN apt-get update && \ apt-get install -y --no-install-recommends \ - build-essential doxygen git wget unzip python-serial \ + build-essential doxygen git wget unzip python-serial python-pip \ default-jdk ant srecord iputils-tracepath rlwrap \ - mosquitto mosquitto-clients \ + mosquitto mosquitto-clients gdb \ && apt-get clean # Install ARM toolchain @@ -42,6 +42,12 @@ RUN wget https://developer.nordicsemi.com/nRF5_IoT_SDK/nRF5_IoT_SDK_v0.9.x/nrf5_ ENV NRF52_SDK_ROOT /usr/nrf52-sdk +# Install sphinx and sphinx_rtd_theme, required for building and testing the +# readthedocs API documentation +RUN pip install --upgrade pip +RUN pip install setuptools +RUN pip install sphinx_rtd_theme sphinx + # Create user, enable X forwarding, add to group dialout # -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix RUN export uid=1000 gid=1000 && \ diff --git a/tools/readthedocs/Makefile b/tools/readthedocs/Makefile new file mode 100644 index 000000000..ecf5b58d4 --- /dev/null +++ b/tools/readthedocs/Makefile @@ -0,0 +1,19 @@ +# Makefile used to generate documentation with sphinx, which is used on +# readthedocs. Use this Makefile to test readthedocs generation locally on +# your computer. +# +# For this to work, you will need to install sphinx and sphinx_rtd_theme. They +# can be installed through pip +.PHONY = clean all readthedocs + +SPHINX = sphinx-build +SPHINX_CONF_DIR = . +SPHINX_OUT_DIR = _build + +all: readthedocs + +clean: + rm -rf $(SPHINX_OUT_DIR) + +readthedocs: + $(SPHINX) -b html $(SPHINX_CONF_DIR) $(SPHINX_OUT_DIR) diff --git a/tools/readthedocs/api-doc.py b/tools/readthedocs/api-doc.py new file mode 100644 index 000000000..c59b0864e --- /dev/null +++ b/tools/readthedocs/api-doc.py @@ -0,0 +1,65 @@ +# Sphinx extension that builds Contiki-NG documentation and copies it over to +# the sphinx build dir +import subprocess +from sphinx.util import logging +logger = logging.getLogger(__name__) + + +api_doc_defaults = { + 'doxygen_src_dir': '../doxygen', + 'doxygen_out_dir': 'html', + 'doxygen_suppress_out': True, + 'doxygen_build': True, +} + + +def api_doc_build(app, exception): + if exception is not None: + logger.warning('%s exiting without building' % (__name__,)) + return + + if app.config.api_doc_doxygen_build: + make_invocation_suffix = ('> /dev/null' + if app.config.api_doc_doxygen_suppress_out + else '') + make_invocation = ' '.join(('make -C', + app.config.api_doc_doxygen_src_dir, + make_invocation_suffix)) + + logger.info('%s building API docs from "%s"' + % (__name__, app.config.api_doc_doxygen_src_dir)) + logger.info('%s executing "%s"' + % (__name__, make_invocation)) + + subprocess.call(make_invocation, shell=True) + + api_doc_build_dir = "/".join((app.config.api_doc_doxygen_src_dir, + app.config.api_doc_doxygen_out_dir)) + api_doc_static_api_dir = "/".join((app.outdir, '_api')) + + logger.info('%s moving "%s" to "%s"' + % (__name__, api_doc_build_dir, api_doc_static_api_dir)) + subprocess.call(' '.join(('mv', api_doc_build_dir, + api_doc_static_api_dir)), + shell=True) + + # Fundamentally a workaround: Readthedocs doxygen build plain refulses + # to build the same html/*.js files as local builds do. So we ship them + # and we copy them over to the output dir by force, till readthedocs + # starts behaving, hopefully in the near future + subprocess.call(' '.join(('cp js/dynsections.js', + api_doc_static_api_dir)), + shell=True) + + +def setup(app): + for k, v in api_doc_defaults.items(): + config_val = 'api_doc_' + k + logger.debug('Add config value %s: %s' %(config_val, v)) + app.add_config_value(config_val, v, '') + + # We will build and copy the docs after the end of the sphinx build, and. + # only if it has been successful. Regsiter for the build-finished event. + app.connect('build-finished', api_doc_build) + + logger.info('%s initialised' % (__name__,)) diff --git a/tools/readthedocs/conf.py b/tools/readthedocs/conf.py new file mode 100644 index 000000000..50e43737e --- /dev/null +++ b/tools/readthedocs/conf.py @@ -0,0 +1,112 @@ +# -*- coding: utf-8 -*- +# +# Configuration file for the Sphinx documentation builder. +# +# This file does only contain a selection of the most common options. For a +# full list see the documentation: +# http://www.sphinx-doc.org/en/master/config + + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +import os +import sys +sys.path.insert(0, os.path.abspath('.')) + + +# -- Project information ----------------------------------------------------- + +project = u'Contiki-NG' +copyright = u'2018, Contiki-NG maintainers and contributors' +author = u'Contiki-NG maintainers and contributors' + +# The short X.Y version +version = u'' +# The full version, including alpha/beta/rc tags +release = u'' + + +# -- General configuration --------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +# +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'api-doc' +] + +# api-doc configuration +api_doc_doxygen_src_dir = '../doxygen' +api_doc_doxygen_out_dir = 'html' +api_doc_doxygen_suppress_out = True +api_doc_doxygen_build = True + +# Add any paths that contain templates here, relative to this directory. +#templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The master toctree document. +master_doc = 'index' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path . +exclude_patterns = [u'_build', 'Thumbs.db', '.DS_Store'] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'sphinx_rtd_theme' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# +# html_theme_options = {} + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +#html_static_path = ['_static'] + +# Custom sidebar templates, must be a dictionary that maps document names +# to template names. +# +# The default sidebars (for documents that don't match any pattern) are +# defined by theme itself. Builtin themes are using these templates by +# default: ``['localtoc.html', 'relations.html', 'sourcelink.html', +# 'searchbox.html']``. +# +# html_sidebars = {} + + +# -- Options for HTMLHelp output --------------------------------------------- + +# Output file base name for HTML help builder. +htmlhelp_basename = 'Contiki-NGdoc' diff --git a/tools/readthedocs/index.rst b/tools/readthedocs/index.rst new file mode 100644 index 000000000..794c0866b --- /dev/null +++ b/tools/readthedocs/index.rst @@ -0,0 +1,23 @@ +Contiki-NG API documentation! +============================= + +These pages host Contiki-NG's API documentation. + +The primary target audience +consists developers who want to develop an application with Contiki-NG or who +want to modify Contiki-NG itself. + +For guides and tutorials, visit the `Contiki-NG wiki`_. + +Index +===== +* `API Home`_ +* `List of Modules`_ +* `Data Structure Index`_ +* `Index of Files`_ + +.. _Contiki-NG wiki: https://github.com/contiki-ng/contiki-ng/wiki +.. _API Home: _api/index.html +.. _List of Modules: _api/modules.html +.. _Data Structure Index: _api/annotated.html +.. _Index of Files: _api/files.html diff --git a/tools/readthedocs/js/dynsections.js b/tools/readthedocs/js/dynsections.js new file mode 100644 index 000000000..537e3e498 --- /dev/null +++ b/tools/readthedocs/js/dynsections.js @@ -0,0 +1,127 @@ +/* + @licstart The following is the entire license notice for the + JavaScript code in this file. + + Copyright (C) 1997-2017 by Dimitri van Heesch + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + @licend The above is the entire license notice + for the JavaScript code in this file + */ +function toggleVisibility(linkObj) +{ + var base = $(linkObj).attr('id'); + var summary = $('#'+base+'-summary'); + var content = $('#'+base+'-content'); + var trigger = $('#'+base+'-trigger'); + var src=$(trigger).attr('src'); + if (content.is(':visible')===true) { + content.hide(); + summary.show(); + $(linkObj).addClass('closed').removeClass('opened'); + $(trigger).attr('src',src.substring(0,src.length-8)+'closed.png'); + } else { + content.show(); + summary.hide(); + $(linkObj).removeClass('closed').addClass('opened'); + $(trigger).attr('src',src.substring(0,src.length-10)+'open.png'); + } + return false; +} + +function updateStripes() +{ + $('table.directory tr'). + removeClass('even').filter(':visible:even').addClass('even'); +} + +function toggleLevel(level) +{ + $('table.directory tr').each(function() { + var l = this.id.split('_').length-1; + var i = $('#img'+this.id.substring(3)); + var a = $('#arr'+this.id.substring(3)); + if (l