diff --git a/apps/httpd-ws/Makefile.httpd-ws b/apps/httpd-ws/Makefile.httpd-ws new file mode 100644 index 000000000..6c1bd42f0 --- /dev/null +++ b/apps/httpd-ws/Makefile.httpd-ws @@ -0,0 +1 @@ +httpd-ws_src = httpd-ws.c diff --git a/apps/httpd-ws/httpd-ws.c b/apps/httpd-ws/httpd-ws.c new file mode 100644 index 000000000..c28230897 --- /dev/null +++ b/apps/httpd-ws/httpd-ws.c @@ -0,0 +1,478 @@ +/* + * Copyright (c) 2010-2012, Swedish Institute of Computer Science. + * 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 + * A simple webserver for web services + * \author + * Adam Dunkels + * Niclas Finne + * Joakim Eriksson + */ + +#include +#include +#include + +#include "contiki-net.h" +#include "httpd-ws.h" + +#define DEBUG 0 +#if DEBUG +#define PRINTF(...) printf(__VA_ARGS__) +#else +#define PRINTF(...) +#endif + +#ifndef WEBSERVER_CONF_CFS_CONNS +#define CONNS UIP_CONNS +#else /* WEBSERVER_CONF_CFS_CONNS */ +#define CONNS WEBSERVER_CONF_CFS_CONNS +#endif /* WEBSERVER_CONF_CFS_CONNS */ + +#ifndef WEBSERVER_CONF_CFS_URLCONV +#define URLCONV 0 +#else /* WEBSERVER_CONF_CFS_URLCONV */ +#define URLCONV WEBSERVER_CONF_CFS_URLCONV +#endif /* WEBSERVER_CONF_CFS_URLCONV */ + +#if URLCONV +#include "urlconv.h" +#endif /* URLCONV */ + +static struct httpd_ws_state conns[CONNS]; + +PROCESS(httpd_ws_process, "Web server (WS)"); + +#define ISO_nl 0x0a +#define ISO_space 0x20 +#define ISO_period 0x2e +#define ISO_slash 0x2f + +uint16_t http_connections = 0; + +static const char http_10[] = " HTTP/1.0\r\n"; +static const char http_content_type[] = "Content-Type:"; +static const char http_content_type_html[] = "text/html"; +static const char http_content_len[] = "Content-Length:"; +static const char http_header_404[] = + "HTTP/1.0 404 Not found\r\nServer: Contiki\r\nConnection: close\r\n"; +static const char http_header_200[] = + "HTTP/1.0 200 OK\r\nServer: Contiki\r\nConnection: close\r\n"; +static const char html_not_found[] = + "

Page not found

"; +/*---------------------------------------------------------------------------*/ +/* just set all states to unused */ +static void +httpd_state_init(void) +{ + int i; + + for(i = 0; i < CONNS; i++) { + conns[i].state = HTTPD_WS_STATE_UNUSED; + } +} +/*---------------------------------------------------------------------------*/ +static struct httpd_ws_state * +httpd_state_alloc(void) +{ + int i; + + for(i = 0; i < CONNS; i++) { + if(conns[i].state == HTTPD_WS_STATE_UNUSED) { + conns[i].state = HTTPD_WS_STATE_INPUT; + return &conns[i]; + } + } + return NULL; +} +/*---------------------------------------------------------------------------*/ +#define httpd_state_free(s) (s->state = HTTPD_WS_STATE_UNUSED) +/*---------------------------------------------------------------------------*/ +static +PT_THREAD(send_string(struct httpd_ws_state *s, const char *str, uint16_t len)) +{ + PSOCK_BEGIN(&s->sout); + + SEND_STRING(&s->sout, str, len); + + PSOCK_END(&s->sout); +} +/*---------------------------------------------------------------------------*/ +static +PT_THREAD(send_headers(struct httpd_ws_state *s, const char *statushdr)) +{ + PSOCK_BEGIN(&s->sout); + + SEND_STRING(&s->sout, statushdr, strlen(statushdr)); + s->outbuf_pos = snprintf(s->outbuf, sizeof(s->outbuf), + "%s %s\r\n\r\n", http_content_type, + s->content_type == NULL + ? http_content_type_html : s->content_type); + SEND_STRING(&s->sout, s->outbuf, s->outbuf_pos); + s->outbuf_pos = 0; + + PSOCK_END(&s->sout); +} +/*---------------------------------------------------------------------------*/ +static +PT_THREAD(handle_output(struct httpd_ws_state *s)) +{ + PT_BEGIN(&s->outputpt); + + s->content_type = http_content_type_html; + s->script = httpd_ws_get_script(s); + if(s->script == NULL) { + PT_WAIT_THREAD(&s->outputpt, send_headers(s, http_header_404)); + PT_WAIT_THREAD(&s->outputpt, + send_string(s, html_not_found, strlen(html_not_found))); + uip_close(); +/* webserver_log_file(&uip_conn->ripaddr, "404 - not found"); */ + PT_EXIT(&s->outputpt); + } else { + if(s->request_type == HTTPD_WS_POST) { + /* A post has a body that needs to be read */ + s->state = HTTPD_WS_STATE_INPUT; + PT_WAIT_UNTIL(&s->outputpt, s->state == HTTPD_WS_STATE_OUTPUT); + } + PT_WAIT_THREAD(&s->outputpt, send_headers(s, http_header_200)); + PT_WAIT_THREAD(&s->outputpt, s->script(s)); + } + s->script = NULL; + PSOCK_CLOSE(&s->sout); + PT_END(&s->outputpt); +} +/*---------------------------------------------------------------------------*/ +static +PT_THREAD(handle_request(struct httpd_ws_state *s)) +{ + PT_BEGIN(&s->outputpt); + + /* send the request line */ + PT_WAIT_THREAD(&s->outputpt, + send_string(s, s->filename, strlen(s->filename))); + /* send host */ + if(s->outbuf_pos > 0) { + PT_WAIT_THREAD(&s->outputpt, send_string(s, s->outbuf, s->outbuf_pos)); + } + + if(s->content_type != NULL) { + s->outbuf_pos = snprintf(s->outbuf, sizeof(s->outbuf), "%s %s\r\n", + http_content_type, s->content_type); + PT_WAIT_THREAD(&s->outputpt, send_string(s, s->outbuf, s->outbuf_pos)); + } + /* send the extra header(s) */ + if(s->output_extra_headers != NULL) { + s->response_index = 0; + while((s->outbuf_pos = + s->output_extra_headers(s, + s->outbuf, sizeof(s->outbuf), + s->response_index)) > 0) { + PT_WAIT_THREAD(&s->outputpt, send_string(s, s->outbuf, s->outbuf_pos)); + s->response_index++; + } + } + + /* send content length */ + if(s->content_len > 0) { + s->outbuf_pos = snprintf(s->outbuf, sizeof(s->outbuf), "%s %u\r\n", + http_content_len, s->content_len); + } + /* send header separator */ + if(s->outbuf_pos + 2 < sizeof(s->outbuf)) { + s->outbuf[s->outbuf_pos++] = '\r'; + s->outbuf[s->outbuf_pos++] = '\n'; + } + PT_WAIT_THREAD(&s->outputpt, send_string(s, s->outbuf, s->outbuf_pos)); + s->outbuf_pos = 0; + + if(s->script != NULL) { + PT_WAIT_THREAD(&s->outputpt, s->script(s)); + } + s->state = HTTPD_WS_STATE_REQUEST_INPUT; + + PSOCK_CLOSE(&s->sout); + PT_END(&s->outputpt); +} +/*---------------------------------------------------------------------------*/ +static +PT_THREAD(handle_input(struct httpd_ws_state *s)) +{ + PSOCK_BEGIN(&s->sin); + PSOCK_READTO(&s->sin, ISO_space); + + if(strncmp(s->inputbuf, "GET ", 4) == 0) { + s->request_type = HTTPD_WS_GET; + } else if(strncmp(s->inputbuf, "POST ", 5) == 0) { + s->request_type = HTTPD_WS_POST; + s->content_len = 0; + } else if(strncmp(s->inputbuf, "HTTP ", 5) == 0) { + s->request_type = HTTPD_WS_RESPONSE; + } else { + PSOCK_CLOSE_EXIT(&s->sin); + } + PSOCK_READTO(&s->sin, ISO_space); + + /* TODO handle HTTP response */ + + if(s->inputbuf[0] != ISO_slash) { + PSOCK_CLOSE_EXIT(&s->sin); + } + +#if URLCONV + s->inputbuf[PSOCK_DATALEN(&s->sin) - 1] = 0; + urlconv_tofilename(s->filename, s->inputbuf, sizeof(s->filename)); +#else /* URLCONV */ + s->inputbuf[PSOCK_DATALEN(&s->sin) - 1] = 0; + snprintf(s->filename, sizeof(s->filename), "%s", s->inputbuf); +#endif /* URLCONV */ + +/* webserver_log_file(&uip_conn->ripaddr, s->filename); */ + s->state = HTTPD_WS_STATE_OUTPUT; + + while(1) { + PSOCK_READTO(&s->sin, ISO_nl); + + if(s->request_type == HTTPD_WS_POST && + strncmp(s->inputbuf, http_content_len, 15) == 0) { + s->inputbuf[PSOCK_DATALEN(&s->sin) - 2] = 0; + s->content_len = atoi(&s->inputbuf[16]); + } + + /* should have a header callback here check_header(s) */ + + if(PSOCK_DATALEN(&s->sin) > 2) { + s->inputbuf[PSOCK_DATALEN(&s->sin) - 2] = 0; + } else if(s->request_type == HTTPD_WS_POST) { + PSOCK_READBUF_LEN(&s->sin, s->content_len); + s->inputbuf[PSOCK_DATALEN(&s->sin)] = 0; + /* printf("Content: '%s'\nSize:%d\n", s->inputbuf, PSOCK_DATALEN(&s->sin)); */ + s->state = HTTPD_WS_STATE_OUTPUT; + } + } + PSOCK_END(&s->sin); +} +/*---------------------------------------------------------------------------*/ +static void +handle_connection(struct httpd_ws_state *s) +{ + if(s->state == HTTPD_WS_STATE_REQUEST_OUTPUT) { + handle_request(s); + } + handle_input(s); + if(s->state == HTTPD_WS_STATE_OUTPUT) { + handle_output(s); + } +} +/*---------------------------------------------------------------------------*/ +void +httpd_ws_appcall(void *state) +{ + struct httpd_ws_state *s = (struct httpd_ws_state *)state; + + if(uip_closed() || uip_aborted() || uip_timedout()) { + if(s != NULL) { + PRINTF("HTTPD-WS: closed/aborted (%d)\n", http_connections); + http_connections--; + httpd_state_free(s); + } else { + PRINTF("HTTPD-WS: closed/aborted ** NO HTTPD_WS_STATE!!! ** (%d)\n", + http_connections); + } + } else if(uip_connected()) { + if(s == NULL) { + s = httpd_state_alloc(); + if(s == NULL) { + uip_abort(); + PRINTF("HTTPD-WS: aborting - no resource (%d)\n", http_connections); + /* webserver_log_file(&uip_conn->ripaddr, "reset (no memory block)"); */ + return; + } + http_connections++; + + tcp_markconn(uip_conn, s); + s->state = HTTPD_WS_STATE_INPUT; + } else { + /* this is a request that is to be sent! */ + s->state = HTTPD_WS_STATE_REQUEST_OUTPUT; + } + PSOCK_INIT(&s->sin, (uint8_t *)s->inputbuf, sizeof(s->inputbuf) - 1); + PSOCK_INIT(&s->sout, (uint8_t *)s->inputbuf, sizeof(s->inputbuf) - 1); + PT_INIT(&s->outputpt); + timer_set(&s->timer, CLOCK_SECOND * 30); + handle_connection(s); + } else if(s != NULL) { + if(uip_poll()) { + if(timer_expired(&s->timer)) { + uip_abort(); + PRINTF("HTTPD-WS: aborting - http timeout (%d)\n", http_connections); + http_connections--; + httpd_state_free(s); +/* webserver_log_file(&uip_conn->ripaddr, "reset (timeout)"); */ + } else { + PRINTF("HTTPD-WS: uip-poll (%d)\n", http_connections); + } + } else { +/* PRINTF("HTTPD-WS: restart timer %s (%d)\n", s->filename, */ +/* http_connections); */ + timer_restart(&s->timer); + } + handle_connection(s); + } else { + PRINTF("HTTPD-WS: aborting - no state (%d)\n", http_connections); + uip_abort(); + } +} +/*---------------------------------------------------------------------------*/ +void +httpd_ws_init(void) +{ + tcp_listen(UIP_HTONS(80)); + httpd_state_init(); +#if URLCONV + urlconv_init(); +#endif /* URLCONV */ +} +/*---------------------------------------------------------------------------*/ +struct httpd_ws_state * +httpd_ws_request(char request_type, const char *host_ip, const char *host_hdr, + uint16_t port, const char *file, + const char *content_type, uint16_t content_len, + httpd_ws_script_t generator) +{ + struct httpd_ws_state *s; + struct uip_conn *conn; + uip_ipaddr_t *ipaddr; + uip_ipaddr_t addr; + char *request_str; + + /* First check if the host is an IP address. */ + ipaddr = &addr; + if(uiplib_ipaddrconv(host_ip, &addr) == 0) { +#if 0 && UIP_UDP + ipaddr = resolv_lookup(host_ip); + + if(ipaddr == NULL) { + return NULL; + } +#else /* UIP_UDP */ + return NULL; +#endif /* UIP_UDP */ + } + + s = httpd_state_alloc(); + if(s == NULL) { + /* no memory left... do no request... */ + return NULL; + } + http_connections++; + + switch(request_type) { + case HTTPD_WS_POST: + request_str = "POST "; + break; + case HTTPD_WS_PUT: + request_str = "PUT "; + break; + default: + request_str = "GET "; + break; + } + + s->request_type = request_type; + s->content_len = content_len; + s->content_type = content_type; + s->script = generator; + s->state = HTTPD_WS_STATE_REQUEST_OUTPUT; + + /* create a request line for a POST - should check size of it!!! */ + /* Assume post for now */ + snprintf(s->filename, sizeof(s->filename), "%s%s%s", + request_str, file, http_10); + s->outbuf_pos = snprintf(s->outbuf, sizeof(s->outbuf), "Host:%s\r\n", + host_hdr != NULL ? host_hdr : host_ip); + + PROCESS_CONTEXT_BEGIN(&httpd_ws_process); + conn = tcp_connect(ipaddr, uip_htons(port), s); + PROCESS_CONTEXT_END(&httpd_ws_process); + if(conn == NULL) { + PRINTF("HTTPD-WS: aborting... could not allocate tcp connection (%d)\n", + http_connections); + httpd_state_free(s); + http_connections--; + return NULL; + } + PRINTF("HTTPD-WS: created http connection (%d)\n", http_connections); + + return s; +} +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(httpd_ws_process, ev, data) +{ + static struct etimer et; + int i; + + PROCESS_BEGIN(); + + httpd_ws_init(); + + PRINTF("Buffer size, input %d, output\n", + HTTPD_INBUF_SIZE, HTTPD_OUTBUF_SIZE); + + /* Delay 2-4 seconds */ + etimer_set(&et, CLOCK_SECOND * 10); + + /* GC any http session that is too long lived - either because other + end never closed or if any other state cause too long lived http + sessions */ + while(1) { + PROCESS_WAIT_EVENT_UNTIL(ev == tcpip_event || etimer_expired(&et)); + if(ev == tcpip_event) { + httpd_ws_appcall(data); + } else if(etimer_expired(&et)) { + PRINTF("HTTPD States: "); + for(i = 0; i < CONNS; i++) { + PRINTF("%d ", conns[i].state); + if(conns[i].state != HTTPD_WS_STATE_UNUSED && + timer_expired(&conns[i].timer)) { + conns[i].state = HTTPD_WS_STATE_UNUSED; + PRINTF("\n*** RELEASED HTTPD Session\n"); + http_connections--; + } + } + PRINTF("\n"); + etimer_reset(&et); + } + } + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ diff --git a/apps/httpd-ws/httpd-ws.h b/apps/httpd-ws/httpd-ws.h new file mode 100644 index 000000000..bf60df778 --- /dev/null +++ b/apps/httpd-ws/httpd-ws.h @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2010-2012, Swedish Institute of Computer Science. + * 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 + * A simple webserver for web services + * \author + * Adam Dunkels + * Niclas Finne + * Joakim Eriksson + */ + +#ifndef __HTTPD_WS_H__ +#define __HTTPD_WS_H__ + +#include "contiki-net.h" + +#ifndef WEBSERVER_CONF_CFS_PATHLEN +#define HTTPD_PATHLEN 80 +#else /* WEBSERVER_CONF_CFS_CONNS */ +#define HTTPD_PATHLEN WEBSERVER_CONF_CFS_PATHLEN +#endif /* WEBSERVER_CONF_CFS_CONNS */ + +#ifndef WEBSERVER_CONF_INBUF_SIZE +#define HTTPD_INBUF_SIZE (HTTPD_PATHLEN + 90) +#else /* WEBSERVER_CONF_INBUF_SIZE */ +#define HTTPD_INBUF_SIZE WEBSERVER_CONF_INBUF_SIZE +#endif /* WEBSERVER_CONF_INBUF_SIZE */ + +#if HTTPD_INBUF_SIZE < UIP_TCP_MSS || HTTPD_INBUF_SIZE < UIP_RECEIVE_WINDOW +#error HTTPD_INBUF_SIZE is too small. Must be at least a TCP window in size. +#endif + +#ifndef WEBSERVER_CONF_OUTBUF_SIZE +#define HTTPD_OUTBUF_SIZE (UIP_TCP_MSS + 20) +#else /* WEBSERVER_CONF_OUTBUF_SIZE */ +#define HTTPD_OUTBUF_SIZE WEBSERVER_CONF_OUTBUF_SIZE +#endif /* WEBSERVER_CONF_OUTBUF_SIZE */ + +struct httpd_ws_state; +typedef char (* httpd_ws_script_t)(struct httpd_ws_state *s); +typedef int (* httpd_ws_output_headers_t)(struct httpd_ws_state *s, + char *buffer, int buf_size, + int index); + +#define HTTPD_WS_GET 1 +#define HTTPD_WS_POST 2 +#define HTTPD_WS_PUT 3 +#define HTTPD_WS_RESPONSE 4 + +#define HTTPD_WS_STATE_UNUSED 0 +#define HTTPD_WS_STATE_INPUT 1 +#define HTTPD_WS_STATE_OUTPUT 2 +#define HTTPD_WS_STATE_REQUEST_OUTPUT 3 +#define HTTPD_WS_STATE_REQUEST_INPUT 4 + +struct httpd_ws_state { + struct timer timer; + struct psock sin, sout; + struct pt outputpt; + char inputbuf[HTTPD_INBUF_SIZE]; + char filename[HTTPD_PATHLEN]; + const char *content_type; + uint16_t content_len; + char outbuf[HTTPD_OUTBUF_SIZE]; + uint16_t outbuf_pos; + char state; + char request_type; + int response_index; + + httpd_ws_output_headers_t output_extra_headers; + httpd_ws_script_t script; + +#ifdef HTTPD_WS_CONF_USER_STATE + HTTPD_WS_CONF_USER_STATE; +#endif +}; + +void httpd_ws_init(void); +void httpd_ws_appcall(void *state); + +struct httpd_ws_state *httpd_ws_request(char request_type, + const char *host_ip, + const char *host_hdr, + uint16_t port, + const char *file, + const char *content_type, + uint16_t content_len, + httpd_ws_script_t generator); + +#define SEND_STRING(s, str, len) PSOCK_SEND((s), (uint8_t *)(str), (len)) + +httpd_ws_script_t httpd_ws_get_script(struct httpd_ws_state *s); + +PROCESS_NAME(httpd_ws_process); + +#endif /* __HTTPD_WS_H__ */ diff --git a/apps/json/Makefile.json b/apps/json/Makefile.json new file mode 100644 index 000000000..d6fca6413 --- /dev/null +++ b/apps/json/Makefile.json @@ -0,0 +1 @@ +json_src = jsonparse.c jsontree.c diff --git a/tools/cooja/java/se/sics/cooja/ipdistributors/IdIPDistributor.java b/apps/json/json.h similarity index 55% rename from tools/cooja/java/se/sics/cooja/ipdistributors/IdIPDistributor.java rename to apps/json/json.h index 29a98692f..703f6d57c 100644 --- a/tools/cooja/java/se/sics/cooja/ipdistributors/IdIPDistributor.java +++ b/apps/json/json.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, Swedish Institute of Computer Science. + * Copyright (c) 2011-2012, Swedish Institute of Computer Science. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -26,49 +26,45 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: IdIPDistributor.java,v 1.2 2009/09/17 13:20:03 fros4943 Exp $ + * This file is part of the Contiki operating system. */ -package se.sics.cooja.ipdistributors; -import java.util.Vector; -import se.sics.cooja.*; - /** - * Generates IP addresses on the form 10.[id/256 mod 256*256].[id mod 256].1. - * - * Observe! - * - ID must be set before this is called (otherwise IP=0.0.0.0). - * - Only supports 256*256 motes, (IPs will wrap if above). - * - * @author Fredrik Osterlind + * \file + * A few JSON defines used for parsing and generating JSON. + * \author + * Niclas Finne + * Joakim Eriksson */ -@ClassDescription("From ID (10.id.id.1)") -public class IdIPDistributor extends IPDistributor { - private Vector generatedIPAddresses; - /** - * Creates a Id IP distributor. - * @param newMotes All motes which later will be assigned IP numbers. - */ - public IdIPDistributor(Vector newMotes) { - generatedIPAddresses = new Vector(); +#ifndef __JSON_H__ +#define __JSON_H__ - for (int i=0; i < newMotes.size(); i++) { - int moteId = newMotes.get(i).getID(); - generatedIPAddresses.add("10." + - (moteId / 256 % (256*256)) - + "." + - (moteId % 256) - + ".1"); - } +#define JSON_TYPE_ARRAY '[' +#define JSON_TYPE_OBJECT '{' +#define JSON_TYPE_PAIR ':' +#define JSON_TYPE_PAIR_NAME 'N' /* for N:V pairs */ +#define JSON_TYPE_STRING '"' +#define JSON_TYPE_INT 'I' +#define JSON_TYPE_NUMBER '0' +#define JSON_TYPE_ERROR 0 - } +/* how should we handle null vs false - both can be 0? */ +#define JSON_TYPE_NULL 'n' +#define JSON_TYPE_TRUE 't' +#define JSON_TYPE_FALSE 'f' - public String getNextIPAddress() { - if (generatedIPAddresses.size() > 0) - return generatedIPAddresses.remove(0); - else - return "0.0.0.0"; - } +#define JSON_TYPE_CALLBACK 'C' -} +enum { + JSON_ERROR_OK, + JSON_ERROR_SYNTAX, + JSON_ERROR_UNEXPECTED_ARRAY, + JSON_ERROR_UNEXPECTED_END_OF_ARRAY, + JSON_ERROR_UNEXPECTED_OBJECT, + JSON_ERROR_UNEXPECTED_STRING +}; + +#define JSON_CONTENT_TYPE "application/json" + +#endif /* __JSON_H__ */ diff --git a/apps/json/jsonparse.c b/apps/json/jsonparse.c new file mode 100644 index 000000000..8089ae9fb --- /dev/null +++ b/apps/json/jsonparse.c @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2011-2012, Swedish Institute of Computer Science. + * 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. + */ + +#include "jsonparse.h" +#include +#include + +/*--------------------------------------------------------------------*/ +static int +push(struct jsonparse_state *state, char c) +{ + state->stack[state->depth] = c; + state->depth++; + state->vtype = 0; + return state->depth < JSONPARSE_MAX_DEPTH; +} +/*--------------------------------------------------------------------*/ +static char +pop(struct jsonparse_state *state) +{ + if(state->depth == 0) { + return JSON_TYPE_ERROR; + } + state->depth--; + return state->stack[state->depth]; +} +/*--------------------------------------------------------------------*/ +/* will pass by the value and store the start and length of the value for + atomic types */ +/*--------------------------------------------------------------------*/ +static void +atomic(struct jsonparse_state *state, char type) +{ + char c; + + state->vstart = state->pos; + state->vtype = type; + if(type == JSON_TYPE_STRING || type == JSON_TYPE_PAIR_NAME) { + while((c = state->json[state->pos++]) && c != '"') { + if(c == '\\') { + state->pos++; /* skip current char */ + } + } + state->vlen = state->pos - state->vstart - 1; + } else if(type == JSON_TYPE_NUMBER) { + do { + c = state->json[state->pos]; + if((c < '0' || c > '9') && c != '.') { + c = 0; + } else { + state->pos++; + } + } while(c); + /* need to back one step since first char is already gone */ + state->vstart--; + state->vlen = state->pos - state->vstart; + } + /* no other types for now... */ +} +/*--------------------------------------------------------------------*/ +static void +skip_ws(struct jsonparse_state *state) +{ + char c; + + while(state->pos < state->len && + ((c = state->json[state->pos]) == ' ' || c == '\n')) { + state->pos++; + } +} +/*--------------------------------------------------------------------*/ +void +jsonparse_setup(struct jsonparse_state *state, const char *json, int len) +{ + state->json = json; + state->len = len; + state->pos = 0; + state->depth = 0; + state->error = 0; + state->stack[0] = 0; +} +/*--------------------------------------------------------------------*/ +int +jsonparse_next(struct jsonparse_state *state) +{ + char c; + char s; + + skip_ws(state); + c = state->json[state->pos]; + s = jsonparse_get_type(state); + state->pos++; + + switch(c) { + case '{': + push(state, c); + return c; + case '}': + if(s == ':' && state->vtype != 0) { +/* printf("Popping vtype: '%c'\n", state->vtype); */ + pop(state); + s = jsonparse_get_type(state); + } + if(s == '{') { + pop(state); + } else { + state->error = JSON_ERROR_SYNTAX; + return JSON_TYPE_ERROR; + } + return c; + case ']': + if(s == '[') { + pop(state); + } else { + state->error = JSON_ERROR_UNEXPECTED_END_OF_ARRAY; + return JSON_TYPE_ERROR; + } + return c; + case ':': + push(state, c); + return c; + case ',': + /* if x:y ... , */ + if(s == ':' && state->vtype != 0) { + pop(state); + } else if(s == '[') { + /* ok! */ + } else { + state->error = JSON_ERROR_SYNTAX; + return JSON_TYPE_ERROR; + } + return c; + case '"': + if(s == '{' || s == '[' || s == ':') { + atomic(state, c = (s == '{' ? JSON_TYPE_PAIR_NAME : c)); + } else { + state->error = JSON_ERROR_UNEXPECTED_STRING; + return JSON_TYPE_ERROR; + } + return c; + case '[': + if(s == '{' || s == '[' || s == ':') { + push(state, c); + } else { + state->error = JSON_ERROR_UNEXPECTED_ARRAY; + return JSON_TYPE_ERROR; + } + return c; + default: + if(s == ':' || s == '[') { + if(c <= '9' && c >= '0') { + atomic(state, JSON_TYPE_NUMBER); + return JSON_TYPE_NUMBER; + } + } + } + return 0; +} +/*--------------------------------------------------------------------*/ +/* get the json value of the current position + * works only on "atomic" values such as string, number, null, false, true + */ +/*--------------------------------------------------------------------*/ +int +jsonparse_copy_value(struct jsonparse_state *state, char *str, int size) +{ + int i; + + if(state->vtype == 0) { + return 0; + } + size = size <= state->vlen ? (size - 1) : state->vlen; + for(i = 0; i < size; i++) { + str[i] = state->json[state->vstart + i]; + } + str[i] = 0; + return state->vtype; +} +/*--------------------------------------------------------------------*/ +int +jsonparse_get_value_as_int(struct jsonparse_state *state) +{ + if(state->vtype != JSON_TYPE_NUMBER) { + return 0; + } + return atoi(&state->json[state->vstart]); +} +/*--------------------------------------------------------------------*/ +long +jsonparse_get_value_as_long(struct jsonparse_state *state) +{ + if(state->vtype != JSON_TYPE_NUMBER) { + return 0; + } + return atol(&state->json[state->vstart]); +} +/*--------------------------------------------------------------------*/ +/* strcmp - assume no strange chars that needs to be stuffed in string... */ +/*--------------------------------------------------------------------*/ +int +jsonparse_strcmp_value(struct jsonparse_state *state, const char *str) +{ + if(state->vtype == 0) { + return -1; + } + return strncmp(str, &state->json[state->vstart], state->vlen); +} +/*--------------------------------------------------------------------*/ +int +jsonparse_get_len(struct jsonparse_state *state) +{ + return state->vlen; +} +/*--------------------------------------------------------------------*/ +int +jsonparse_get_type(struct jsonparse_state *state) +{ + if(state->depth == 0) { + return 0; + } + return state->stack[state->depth - 1]; +} +/*--------------------------------------------------------------------*/ +int +jsonparse_has_next(struct jsonparse_state *state) +{ + return state->pos < state->len; +} +/*--------------------------------------------------------------------*/ diff --git a/apps/json/jsonparse.h b/apps/json/jsonparse.h new file mode 100644 index 000000000..3821b67db --- /dev/null +++ b/apps/json/jsonparse.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2011-2012, Swedish Institute of Computer Science. + * 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. + */ + +#ifndef __JSONPARSE_H__ +#define __JSONPARSE_H__ + +#include "contiki-conf.h" +#include "json.h" + +#ifdef JSONPARSE_CONF_MAX_DEPTH +#define JSONPARSE_MAX_DEPTH JSONPARSE_CONF_MAX_DEPTH +#else +#define JSONPARSE_MAX_DEPTH 10 +#endif + +struct jsonparse_state { + const char *json; + int pos; + int len; + int depth; + /* for handling atomic values */ + int vstart; + int vlen; + char vtype; + char error; + char stack[JSONPARSE_MAX_DEPTH]; +}; + +/** + * \brief Initialize a JSON parser state. + * \param state A pointer to a JSON parser state + * \param json The string to parse as JSON + * \param len The length of the string to parse + * + * This function initializes a JSON parser state for + * parsing a string as JSON. + */ +void jsonparse_setup(struct jsonparse_state *state, const char *json, + int len); + +/* move to next JSON element */ +int jsonparse_next(struct jsonparse_state *state); + +/* copy the current JSON value into the specified buffer */ +int jsonparse_copy_value(struct jsonparse_state *state, char *buf, + int buf_size); + +/* get the current JSON value parsed as an int */ +int jsonparse_get_value_as_int(struct jsonparse_state *state); + +/* get the current JSON value parsed as a long */ +long jsonparse_get_value_as_long(struct jsonparse_state *state); + +/* get the length of the current JSON value */ +int jsonparse_get_len(struct jsonparse_state *state); + +/* get the type of the current JSON value */ +int jsonparse_get_type(struct jsonparse_state *state); + +/* compare the JSON value with the specified string */ +int jsonparse_strcmp_value(struct jsonparse_state *state, const char *str); + +#endif /* __JSONPARSE_H__ */ diff --git a/apps/json/jsontree.c b/apps/json/jsontree.c new file mode 100644 index 000000000..45f305869 --- /dev/null +++ b/apps/json/jsontree.c @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2011-2012, Swedish Institute of Computer Science. + * 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 + * JSON output generation + * \author + * Niclas Finne + * Joakim Eriksson + */ + +#include "contiki.h" +#include "jsontree.h" +#include "jsonparse.h" +#include + +#define DEBUG 0 +#if DEBUG +#include +#define PRINTF(...) printf(__VA_ARGS__) +#else +#define PRINTF(...) +#endif + +/*---------------------------------------------------------------------------*/ +void +jsontree_write_atom(const struct jsontree_context *js_ctx, const char *text) +{ + if(text == NULL) { + js_ctx->putchar('0'); + } else { + while(*text != '\0') { + js_ctx->putchar(*text++); + } + } +} +/*---------------------------------------------------------------------------*/ +void +jsontree_write_string(const struct jsontree_context *js_ctx, const char *text) +{ + js_ctx->putchar('"'); + if(text != NULL) { + while(*text != '\0') { + if(*text == '"') { + js_ctx->putchar('\\'); + } + js_ctx->putchar(*text++); + } + } + js_ctx->putchar('"'); +} +/*---------------------------------------------------------------------------*/ +void +jsontree_write_int(const struct jsontree_context *js_ctx, int value) +{ + char buf[10]; + int l; + + if(value < 0) { + js_ctx->putchar('-'); + value = -value; + } + + l = sizeof(buf) - 1; + do { + buf[l--] = '0' + (value % 10); + value /= 10; + } while(value > 0 && l >= 0); + + while(++l < sizeof(buf)) { + js_ctx->putchar(buf[l]); + } +} +/*---------------------------------------------------------------------------*/ +void +jsontree_setup(struct jsontree_context *js_ctx, struct jsontree_value *root, + int (* putchar)(int)) +{ + js_ctx->values[0] = root; + js_ctx->putchar = putchar; + js_ctx->path = 0; + jsontree_reset(js_ctx); +} +/*---------------------------------------------------------------------------*/ +void +jsontree_reset(struct jsontree_context *js_ctx) +{ + js_ctx->depth = 0; + js_ctx->index[0] = 0; +} +/*---------------------------------------------------------------------------*/ +const char * +jsontree_path_name(const struct jsontree_context *js_ctx, int depth) +{ + if(depth < js_ctx->depth && js_ctx->values[depth]->type == JSON_TYPE_OBJECT) { + return ((struct jsontree_object *)js_ctx->values[depth])-> + pairs[js_ctx->index[depth]].name; + } + return ""; +} +/*---------------------------------------------------------------------------*/ +int +jsontree_print_next(struct jsontree_context *js_ctx) +{ + struct jsontree_value *v; + int index; + + v = js_ctx->values[js_ctx->depth]; + + /* Default operation after switch is to back up one level */ + switch(v->type) { + case JSON_TYPE_OBJECT: + case JSON_TYPE_ARRAY: { + struct jsontree_array *o = (struct jsontree_array *)v; + struct jsontree_value *ov; + + index = js_ctx->index[js_ctx->depth]; + if(index == 0) { + js_ctx->putchar(v->type); + js_ctx->putchar('\n'); + } + if(index >= o->count) { + js_ctx->putchar('\n'); + js_ctx->putchar(v->type + 2); + /* Default operation: back up one level! */ + break; + } + + if(index > 0) { + js_ctx->putchar(','); + js_ctx->putchar('\n'); + } + if(v->type == JSON_TYPE_OBJECT) { + jsontree_write_string(js_ctx, + ((struct jsontree_object *)o)->pairs[index].name); + js_ctx->putchar(':'); + ov = ((struct jsontree_object *)o)->pairs[index].value; + } else { + ov = o->values[index]; + } + /* TODO check max depth */ + js_ctx->depth++; /* step down to value... */ + js_ctx->index[js_ctx->depth] = 0; /* and init index */ + js_ctx->values[js_ctx->depth] = ov; + /* Continue on this new level */ + return 1; + } + case JSON_TYPE_STRING: + jsontree_write_string(js_ctx, ((struct jsontree_string *)v)->value); + /* Default operation: back up one level! */ + break; + case JSON_TYPE_INT: + jsontree_write_int(js_ctx, ((struct jsontree_int *)v)->value); + /* Default operation: back up one level! */ + break; + case JSON_TYPE_CALLBACK: { /* pre-formatted json string currently */ + struct jsontree_callback *callback; + + callback = (struct jsontree_callback *)v; + if(js_ctx->index[js_ctx->depth] == 0) { + /* First call: reset the callback status */ + js_ctx->callback_state = 0; + } + if(callback->output == NULL) { + jsontree_write_string(js_ctx, ""); + } else if(callback->output(js_ctx)) { + /* The callback wants to output more */ + js_ctx->index[js_ctx->depth]++; + return 1; + } + /* Default operation: back up one level! */ + break; + } + default: + PRINTF("\nError: Illegal json type:'%c'\n", v->type); + return 0; + } + /* Done => back up one level! */ + if(js_ctx->depth > 0) { + js_ctx->depth--; + js_ctx->index[js_ctx->depth]++; + return 1; + } + return 0; +} +/*---------------------------------------------------------------------------*/ +static struct jsontree_value * +find_next(struct jsontree_context *js_ctx) +{ + struct jsontree_value *v; + int index; + + do { + v = js_ctx->values[js_ctx->depth]; + + /* Default operation after switch is to back up one level */ + switch(v->type) { + case JSON_TYPE_OBJECT: + case JSON_TYPE_ARRAY: { + struct jsontree_array *o = (struct jsontree_array *)v; + struct jsontree_value *ov; + + index = js_ctx->index[js_ctx->depth]; + if(index >= o->count) { + /* Default operation: back up one level! */ + break; + } + + if(v->type == JSON_TYPE_OBJECT) { + ov = ((struct jsontree_object *)o)->pairs[index].value; + } else { + ov = o->values[index]; + } + /* TODO check max depth */ + js_ctx->depth++; /* step down to value... */ + js_ctx->index[js_ctx->depth] = 0; /* and init index */ + js_ctx->values[js_ctx->depth] = ov; + /* Continue on this new level */ + return ov; + } + default: + /* Default operation: back up one level! */ + break; + } + /* Done => back up one level! */ + if(js_ctx->depth > 0) { + js_ctx->depth--; + js_ctx->index[js_ctx->depth]++; + } else { + return NULL; + } + } while(1); +} +/*---------------------------------------------------------------------------*/ +struct jsontree_value * +jsontree_find_next(struct jsontree_context *js_ctx, int type) +{ + struct jsontree_value *v; + + while((v = find_next(js_ctx)) != NULL && v->type != type && + js_ctx->path < js_ctx->depth) { + /* search */ + } + js_ctx->callback_state = 0; + return js_ctx->path < js_ctx->depth ? v : NULL; +} +/*---------------------------------------------------------------------------*/ diff --git a/apps/json/jsontree.h b/apps/json/jsontree.h new file mode 100644 index 000000000..8e2a11243 --- /dev/null +++ b/apps/json/jsontree.h @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2011-2012, Swedish Institute of Computer Science. + * 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 + * JSON output generation + * \author + * Niclas Finne + * Joakim Eriksson + */ + +#ifndef __JSONTREE_H__ +#define __JSONTREE_H__ + +#include "contiki-conf.h" +#include "json.h" + +#ifdef JSONTREE_CONF_MAX_DEPTH +#define JSONTREE_MAX_DEPTH JSONTREE_CONF_MAX_DEPTH +#else +#define JSONTREE_MAX_DEPTH 10 +#endif /* JSONTREE_CONF_MAX_DEPTH */ + +struct jsontree_context { + struct jsontree_value *values[JSONTREE_MAX_DEPTH]; + uint16_t index[JSONTREE_MAX_DEPTH]; + int (* putchar)(int); + uint8_t depth; + uint8_t path; + int callback_state; +}; + +struct jsontree_value { + uint8_t type; + /* followed by a value */ +}; + +struct jsontree_string { + uint8_t type; + const char *value; +}; + +struct jsontree_int { + uint8_t type; + int value; +}; + +/* NOTE: the jsontree_callback set will receive a jsonparse state */ +struct jsonparse_state; +struct jsontree_callback { + uint8_t type; + int (* output)(struct jsontree_context *js_ctx); + int (* set)(struct jsontree_context *js_ctx, struct jsonparse_state *parser); +}; + +struct jsontree_pair { + const char *name; + struct jsontree_value *value; +}; + +struct jsontree_object { + uint8_t type; + uint8_t count; + struct jsontree_pair *pairs; +}; + +struct jsontree_array { + uint8_t type; + uint8_t count; + struct jsontree_value **values; +}; + +#define JSONTREE_STRING(text) {JSON_TYPE_STRING, (text)} +#define JSONTREE_PAIR(name, value) {(name), (struct jsontree_value *)(value)} +#define JSONTREE_CALLBACK(output, set) {JSON_TYPE_CALLBACK, (output), (set)} + +#define JSONTREE_OBJECT(name, ...) \ + static struct jsontree_pair jsontree_pair_##name[] = {__VA_ARGS__}; \ + static struct jsontree_object name = { \ + JSON_TYPE_OBJECT, \ + sizeof(jsontree_pair_##name)/sizeof(struct jsontree_pair), \ + jsontree_pair_##name } + +#define JSONTREE_OBJECT_EXT(name, ...) \ + static struct jsontree_pair jsontree_pair_##name[] = {__VA_ARGS__}; \ + struct jsontree_object name = { \ + JSON_TYPE_OBJECT, \ + sizeof(jsontree_pair_##name)/sizeof(struct jsontree_pair), \ + jsontree_pair_##name } + +void jsontree_setup(struct jsontree_context *js_ctx, + struct jsontree_value *root, int (* putchar)(int)); +void jsontree_reset(struct jsontree_context *js_ctx); + +const char *jsontree_path_name(const struct jsontree_context *js_ctx, + int depth); + +void jsontree_write_int(const struct jsontree_context *js_ctx, int value); +void jsontree_write_atom(const struct jsontree_context *js_ctx, + const char *text); +void jsontree_write_string(const struct jsontree_context *js_ctx, + const char *text); +int jsontree_print_next(struct jsontree_context *js_ctx); +struct jsontree_value *jsontree_find_next(struct jsontree_context *js_ctx, + int type); + +#endif /* __JSONTREE_H__ */ diff --git a/examples/ipv6/json-ws/Makefile b/examples/ipv6/json-ws/Makefile new file mode 100644 index 000000000..12aa155e8 --- /dev/null +++ b/examples/ipv6/json-ws/Makefile @@ -0,0 +1,30 @@ +CONTIKI=../../.. + +WITH_UIP6=1 +UIP_CONF_IPV6=1 +SMALL=1 + +PROJECT_SOURCEFILES += json-ws.c + +ifdef WITH_COSM + CFLAGS += -DWITH_COSM=1 +endif + +ifdef WITH_UDP + CFLAGS += -DWITH_UDP=1 + PROJECT_SOURCEFILES += json-ws-udp.c +endif + +APPS += httpd-ws json + +CFLAGS += -DPROJECT_CONF_H=\"project-conf.h\" + +ifeq ($(TARGET),) + -include Makefile.target +endif + +ifneq ($(TARGET),) +all: websense-$(TARGET) +endif + +include $(CONTIKI)/Makefile.include diff --git a/examples/ipv6/json-ws/README-COSM.txt b/examples/ipv6/json-ws/README-COSM.txt new file mode 100644 index 000000000..6e6114351 --- /dev/null +++ b/examples/ipv6/json-ws/README-COSM.txt @@ -0,0 +1,106 @@ +Short description on how to set-up a sensor network for global IPv6 addresses. +NOTE: this assumes that you do not have a native IPv6 connection. + +You will need: +* PC with Ubuntu (Linux) - 11 or 12 versions +* A node for the RPL-Border-Router (examples/ipv6/rpl-border-router) +* A node for the json webservice (examples/ipv6/json-ws) + +Set-up IPv6 tunnel and Border Router +------------------------------------ +1. Ensure that you have gogo6c installed. + +> sudo apt-get install gogoc + +2. Register an account at gogo6 and Freenet6 (http://www.gogo6.com). + The account at Freenet6 is needed by the gogo6c client. + +3. Edit the gogoc.conf and set your own Freenet6 user and password by + changing the lines with "userid" and "passwd". + +4. Start gogoc at command line + +> cd contiki/examples/ipv6/json-ws +> sudo gogoc -f gogoc.conf -n + +This will print your prefix - TSP_PREFIX. +In my case TSP_PREFIX=2001:05c0:1517:e400 (prefixlen is 56). + +5. Connect one of the nodes to the PC (via USB or serial) and program + it with the RPL-border-router (assumes Z1 node). + +> cd contiki/examples/ipv6/rpl-border-router +> make DEFINES=DEFINES=NETSTACK_RDC=nullrdc_driver,NULLRDC_CONF_802154_AUTOACK=1 TARGET=z1 border-router.upload + +6. Run tunslip6 which will forward IP from the RPL network to + the IPv6 tunnel (and to the Internet). + +> cd contiki/examples/ipv6/rpl-border-router +> make connect-router PREFIX=::1/64 + + When you start this you should get a printout from the border-router + which give you the IPv6 address of it. + +Server IPv6 addresses: + 2001:05c0:1517:e400:c30c::10a + fe80::c30c:0:0:10a + +7. Browse using Mozilla Firefox (or any other browser) to the IPv6 address + given by the border router. This will show you the list of other nodes + connected to the RPL network. + + http://[2001:05c0:1517:e400:c30c::10a]/ + + NOTE: this is a global IPv6 address so it should also be reachable from + any machine on the Internet. + +Configuration of COSM submission +-------------------------------- +1. Register a COSM account at https://cosm.com/ + Set-up a feed and create an API key for the feed. + +2. Program the sensor node with (assumes Z1) + +> cd contiki/examples/ipv6/json-ws +> make websense-z1.upload WITH_COSM=1 TARGET=z1 + +3. Check the IPv6 address of the node via the RPL-border router or + by looking at printouts when booting (make login TARGET=z1) + +4. You need to configure the node to push data to the COSM feed and + this can be done in several ways. For convenience a Python script + is included that pushes the configuration to the nodes. + + Edit the file 'setcosm.py' and replace "" and + "" with your COSM API key and COSM feed id. You can then + use this Python script to configure your nodes. + + This is an example that configures the node with IP address + 2001:05c0:1517:e400:c30c::10b to push data to the COSM feed with + stream 1: + +> cd contiki/examples/ipv6/json-ws +> ./setcosm.py [2001:05c0:1517:e400:c30c::10b] 1 + + Another way to configure the nodes is to use a REST add-on for the + web browser to post a COSM configuration to the node. "REST Client" + for Mozilla Firefox is an example of such add-on. + + POST a JSON expression to your node with the following data: + This assumes that you have the feed with id 55180 and want to post + to stream 1 in that feed. The field 'appdata' should be set to the + API key you created at the COSM web site for the feed. + +{ + "host":"[2001:470:1f10:333::2]", + "port":80, + "path":"/v2/feeds/55180/datastreams/1", + "appdata":"", + "interval":120, + "proto":"cosm" +} + + This will configure the node to periodically push temperature data + every other minute. You can use GET to retrieve the data to se that + the node has been successfully configured (the COSM API key will be + visualized as a number of stars). diff --git a/examples/ipv6/json-ws/gogoc.conf b/examples/ipv6/json-ws/gogoc.conf new file mode 100644 index 000000000..8d1fa0071 --- /dev/null +++ b/examples/ipv6/json-ws/gogoc.conf @@ -0,0 +1,351 @@ +#----------------------------------------------------------------------------- +# $Id: gogoc.conf.in,v 1.1 2009/11/20 16:53:12 jasminko Exp $ +#----------------------------------------------------------------------------- + +########################## READ ME! ################################ +# +# Welcome to the gogoCLIENT configuration file. +# In order to use the client, you need to modify the 'userid', 'passwd' and +# 'server' parameters below depending on which of these situations applies: +# +# 1. If you created a Freenet6 account, enter your userid and password below. +# Change the server name to "broker.freenet6.net" and auth_method to 'any'. +# 2. If you would like to use Freenet6 without creating an account, +# do not make any modifications and close this file. +# 3. If this software was provided by your ISP, enter the userid, password and +# server name provided by your ISP below. +# + + +########################## BASIC CONFIGURATION ################################ + +# +# User Identification and Password: +# Specify your user name and password as provided by your ISP or Freenet6. +# If you plan to connect anonymously, leave these values empty. +# NOTE: Change auth_method option if you are using a username/password. +# +# userid= +# passwd= +# +userid= +passwd= + +# +# gogoSERVER: +# Specify a gogoSERVER name or IP address (provided by your ISP or +# Freenet6). An optional port number can be added; the default port number +# is 3653. +# +# Examples: +# server=hostname # FQDN +# server=A.B.C.D # IPv4 address +# server=[X:X::X:X] # IPv6 address +# server=hostname:port_number +# server=A.B.C.D:port_number +# server=[X:X::X:X]:port_number +# +# Freenet6 account holders should enter authenticated.freenet6.net, +# otherwise use anonymous.freenet6.net. +# Your ISP may provide you with a different server name. +# +#server=anonymous.freenet6.net +#server=authenticated.freenet6.net +server=amsterdam.freenet6.net + +# +# Authentication Method: +# +# auth_method=<{anonymous}|{any|passdss-3des-1|digest-md5|plain}> +# +# anonymous: Sends no username or password +# +# any: The most secure method will be used. +# passdss-3des-1: The password is sent encrypted. +# digest-md5: The password is sent encrypted. +# plain: Both username and password are sent as plain text. +# +# Recommended values: +# - any: If you are authenticating a username / password. +# - anonymous: If you are connecting anonymously. +# +#auth_method=anonymous +auth_method=any + + +########################## ROUTING CONFIGURATION ############################## +# Use these parameters when you wish the client to act as a router and provide +# IPv6 connectivity to IPv6-capable devices on your network. + +# +# Local Host Type: +# Change this value to 'router' to enable IPv6 advertisements. +# +# host_type= +# +host_type=router +#host + +# +# Prefix Length: +# Length of the requested prefix. Valid values range between 0 and 64 when +# using V6*V4 tunnel modes, and between 0 and 32 when using V4V6 tunnel mode. +# +# prefixlen= +# +prefixlen=64 + +# +# Advertisement Interface Prefix: +# Name of the interface that will be configured to send router advertisements. +# This is an interface index on Windows (ex: 4) and a name on Linux +# and BSD (ex: eth1 or fxp1). +# +# if_prefix= +# +if_prefix=tun0 + +# +# DNS Server: +# A DNS server list to which the reverse prefix will be delegated. Servers +# are separated by the colon(:) delimiter. +# +# Example: dns_server=ns1.domain:ns2.domain:ns3.domain +# +dns_server= + + +######################### ADVANCED CONFIGURATION ############################## + +# +# gogoCLIENT Installation Directory: +# Directory where the gogoCLIENT will be installed. This value has been +# set during installation. +# +gogoc_dir= + +# +# Auto-Retry Connect, Retry Delay and Max Retry Delay: +# When auto_retry_connect=yes, the gogoCLIENT will attempt to reconnect +# after a disconnection occurred. The time to wait is 'retry_delay' and that +# delay is doubled at every 3 failed consecutive reconnection attempt. +# However, the wait delay will never exceed retry_delay_max. +# +# +# auto_retry_connect= +# retry_delay= +# retry_delay_max= +# +# Recommended values: "yes", 30, 300 +# +auto_retry_connect=yes +retry_delay=30 +retry_delay_max=300 + +# +# Keepalive Feature and Message Interval: +# Indicates if and how often the client will send data to keep the tunnel +# active. +# +# keepalive= +# keepalive_interval= +# +# Recommended values: "yes" and 30 +# +keepalive=yes +keepalive_interval=30 + +# +# Tunnel Encapsulation Mode: +# v6v4: IPv6-in-IPv4 tunnel. +# v6udpv4: IPv6-in-UDP-in-IPv4 tunnel (for clients behind a NAT). +# v6anyv4: Lets the broker choose the best mode for IPv6 tunnel. +# v4v6: IPv4-in-IPv6 tunnel. +# +# Recommended value: v6anyv4 +# +tunnel_mode=v6anyv4 + +# +# Tunnel Interface Name: +# The interface name assigned to the tunnel. This value is O/S dependent. +# +# if_tunnel_v6v4 is the tunnel interface name for v6v4 encapsulation mode +# if_tunnel_v6udpv4 is the tunnel interface name for v6udpv4 encapsulate mode +# if_tunnel_v4v6 is the tunnel interface name for v4v6 encapsulation mode +# +# Default values are set during installation. +# +if_tunnel_v6v4=sit1 +if_tunnel_v6udpv4=sit +if_tunnel_v4v6=sit0 + +# +# Local IP Address of the Client: +# Allows you to set a specific address as the local tunnel endpoint. +# +# client_v4= +# client_v6= +# auto: The gogoCLIENT will find the local IP address endpoint. +# +# Recommended value: auto +# +client_v4=auto +client_v6=auto + +# +# Script Name: +# File name of the script to run to install the tunnel interface. The +# scripts are located in the template directory under the client +# installation directory. +# +# template= +# +# Default value is set during installation. +# +template=linux + +# +# Proxy client: +# Indicates that this client will request a tunnel for another endpoint, +# such as a Cisco router. +# +# proxy_client= +# +# NOTE: NAT traversal is not possible in proxy mode. +# +proxy_client=no + + +############################ BROKER REDIRECTION ############################### + +# +# Broker List File Name: +# The 'broker_list' directive specifies the filename where the broker +# list received during broker redirection will be saved. +# +# broker_list= +# +broker_list=/var/lib/gogoc/tsp-broker-list.txt + +# +# Last Server Used File Name: +# The 'last_server' directive specifies the filename where the address of +# the last broker to which a connection was successfully established will +# be saved. +# +# last_server= +# +last_server=/var/lib/gogoc/tsp-last-server.txt + +# +# Always Use Last Known Working Server: +# The value of the 'always_use_same_server' directive determines whether the +# client should always try to connect to the broker found in the +# 'last_server' directive filename. +# +# always_use_same_server= +# +always_use_same_server=no + + +#################################### LOGGING ################################## + +# +# Log Verbosity Configuration: +# The format is 'log_=level', where possible values for +# 'destination' are: +# +# - console (logging to the console [AKA stdout]) +# - stderr (logging to standard error) +# - file (logging to a file) +# - syslog (logging to syslog [Unix only]) +# +# and 'level' is a digit between 0 and 3. A 'level' value of 0 disables +# logging to the destination, while values 1 to 3 request increasing levels +# of log verbosity and detail. If 'level' is not specified, a value of 1 is +# assumed. +# +# Example: +# log_file=3 (Maximal logging to a file) +# log_stderr=0 (Logging to standard error disabled) +# log_console= (Minimal logging to the console) +# +# - Default configuration on Windows platforms: +# +# log_console=0 +# log_stderr=0 +# log_file=1 +# +# - Default configuration on Unix platforms: +# +# log_console=0 +# log_stderr=1 +# log_file=0 +# log_syslog=0 +# +log_console=3 +log_stderr=0 +#log_file= +#log_syslog= + +# +# Log File Name: +# When logging to file is requested using the 'log_file' directive, the name +# and path of the file to use may be specified using this directive. +# +# log_filename= +# +log_filename=/var/log/gogoc/gogoc.log + +# +# Log File Rotation: +# When logging to file is requested using the 'log_file' directive, log file +# rotation may be enabled. When enabled, the contents of the log file will +# be moved to a backup file just before it reaches the maximum log file size +# specified via this directive. +# +# The name of the backup file is the name of the original log file with +# '.' inserted before the file extension. If the file does not +# have an extension, '.' is appended to the name of the original +# log file. The timestamp specifies when the rotation occurred. +# +# After the contents of the log file have been moved to the backup file, the +# original file is cleared, and logging resumes at the beginning of the file. +# +# log_rotation= +# +log_rotation=yes + +# +# Log File Rotation Size: +# The 'log_rotation_size' directive specifies the maximum size a log file may +# reach before rotation occurs, if enabled. The value is expressed in +# kilobytes. +# +# log_rotation_size=<16|32|128|1024> +# +log_rotation_size=32 + +# +# Deletion of rotated log files: +# The 'log_rotation_delete' directive specifies that no log backup will be +# kept. When rotation occurs, the file is immediately wiped out and a new +# log file is started. +# +# log_rotation_delete= +# +log_rotation_delete=no + +# +# Syslog Logging Facility [Unix Only]: +# When logging to syslog is requested using the 'log_syslog' directive, the +# facility to use may be specified using this directive. +# +# syslog_facility= +# +syslog_facility=USER + + +# end of gogoc.conf +#------------------------------------------------------------------------------ diff --git a/examples/ipv6/json-ws/json-ws-udp.c b/examples/ipv6/json-ws/json-ws-udp.c new file mode 100644 index 000000000..8d158747f --- /dev/null +++ b/examples/ipv6/json-ws/json-ws-udp.c @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2011-2012, Swedish Institute of Computer Science. + * 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. + * + * $Id: dmacro.dm,v 1.11 2006/03/18 19:43:52 nfi Exp $ + */ + +/** + * \file + * Code for sending the JSON data as a UDP packet + * Specify proto = "udp", port = + * host = + * + * \author + * Niclas Finne + * Joakim Eriksson + */ + +#include "contiki.h" +#include "httpd-ws.h" +#include "jsontree.h" +#include "jsonparse.h" +#include "json-ws.h" +#include +#include + +#define DEBUG DEBUG_FULL +#include "net/uip-debug.h" + +static struct uip_udp_conn *client_conn; +static uip_ipaddr_t server_ipaddr; +static uint16_t server_port; + +#define SENDER_PORT 8181 + +/*---------------------------------------------------------------------------*/ +int +json_ws_udp_setup(const char *host, uint16_t port) +{ + + server_port = port; + + if(client_conn != NULL) { + /* this should be a macro uip_udp_conn_free() or something */ + uip_udp_remove(client_conn); + client_conn = NULL; + } + + uip_ipaddr_t *ipaddr; + + /* First check if the host is an IP address. */ + ipaddr = &server_ipaddr; + if(uiplib_ipaddrconv(host, &server_ipaddr) == 0) { +#if 0 && UIP_UDP + ipaddr = resolv_lookup(host); + + if(ipaddr == NULL) { + return 0; + } +#else /* UIP_UDP */ + return 0; +#endif /* UIP_UDP */ + } + + /* new connection with remote host */ + client_conn = udp_new(&server_ipaddr, UIP_HTONS(server_port), NULL); + udp_bind(client_conn, UIP_HTONS(SENDER_PORT)); + + PRINTF("Created a connection with the server "); + PRINT6ADDR(&client_conn->ripaddr); + PRINTF(" local/remote port %u/%u\n", + UIP_HTONS(client_conn->lport), UIP_HTONS(client_conn->rport)); + return 1; +} + +/*---------------------------------------------------------------------------*/ + +static char *udp_buf; +static int pos; +static int size; + +static int +putchar_udp(int c) +{ + if(udp_buf != NULL && pos <= size) { + udp_buf[pos++] = c; + return c; + } + return 0; +} + +/*---------------------------------------------------------------------------*/ +void +json_ws_udp_send(struct jsontree_value *tree, const char *path) +{ + struct jsontree_context json; + /* maxsize = 70 bytes */ + char buf[70]; + + udp_buf = buf; + + /* reset state and set max-size */ + /* NOTE: packet will be truncated at 70 bytes */ + pos = 0; + size = sizeof(buf); + + json.values[0] = (struct json_value *)tree; + jsontree_reset(&json); + find_json_path(&json, path); + json.path = json.depth; + json.putchar = putchar_udp; + while(jsontree_print_next(&json) && json.path <= json.depth); + + printf("Real UDP size: %d\n", pos); + buf[pos] = 0; + + uip_udp_packet_sendto(client_conn, &buf, pos, + &server_ipaddr, UIP_HTONS(server_port)); +} +/*---------------------------------------------------------------------------*/ +void +json_ws_udp_debug(char *string) +{ + int len; + + len = strlen(string); + uip_udp_packet_sendto(client_conn, string, len, + &server_ipaddr, UIP_HTONS(server_port)); +} +/*---------------------------------------------------------------------------*/ diff --git a/examples/ipv6/json-ws/json-ws.c b/examples/ipv6/json-ws/json-ws.c new file mode 100644 index 000000000..6c6177f16 --- /dev/null +++ b/examples/ipv6/json-ws/json-ws.c @@ -0,0 +1,521 @@ +/* + * Copyright (c) 2011-2012, Swedish Institute of Computer Science. + * 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 + * JSON webservice util + * \author + * Niclas Finne + * Joakim Eriksson + * Joel Hoglund + */ + +#include "contiki.h" +#if PLATFORM_HAS_LEDS +#include "dev/leds.h" +#endif +#include "httpd-ws.h" +#include "jsontree.h" +#include "jsonparse.h" +#include "json-ws.h" +#include +#include + +#define DEBUG 0 +#if DEBUG +#define PRINTF(...) printf(__VA_ARGS__) +#else +#define PRINTF(...) +#endif + +#ifdef JSON_WS_CONF_CALLBACK_PROTO +#define CALLBACK_PROTO JSON_WS_CONF_CALLBACK_PROTO +#else +#define CALLBACK_PROTO "http" +#endif /* JSON_WS_CONF_CALLBACK_PROTO */ + +#ifdef JSON_WS_CONF_CALLBACK_PORT +#define CALLBACK_PORT JSON_WS_CONF_CALLBACK_PORT +#else +#define CALLBACK_PORT 8080; +#endif /* JSON_WS_CONF_CALLBACK_PORT */ + +/* Predefined startup-send interval */ +#ifdef JSON_WS_CONF_CALLBACK_INTERVAL +#define SEND_INTERVAL JSON_WS_CONF_CALLBACK_INTERVAL +#else +#define SEND_INTERVAL 120 +#endif + +static const char http_content_type_json[] = "application/json"; + +/* Maximum 40 chars in host name?: 5 x 8 */ +static char callback_host[40] = "[aaaa::1]"; +static uint16_t callback_port = CALLBACK_PORT; +static uint16_t callback_interval = SEND_INTERVAL; +static char callback_path[80] = "/debug/"; +static char callback_appdata[80] = ""; +static char callback_proto[8] = CALLBACK_PROTO; +static const char *callback_json_path = NULL; +static struct jsontree_object *tree; +static struct ctimer periodic_timer; +long json_time_offset = 0; + +/* support for submitting to cosm */ +#if WITH_COSM +extern struct jsontree_callback cosm_value_callback; + +JSONTREE_OBJECT_EXT(cosm_tree, + JSONTREE_PAIR("current_value", &cosm_value_callback)); +#endif /* WITH_COSM */ + +static void periodic(void *ptr); + +/*---------------------------------------------------------------------------*/ +static void +json_copy_string(struct jsonparse_state *parser, char *string, int len) +{ + jsonparse_next(parser); + jsonparse_next(parser); + jsonparse_copy_value(parser, string, len); +} +/*---------------------------------------------------------------------------*/ +static int +cfg_get(struct jsontree_context *js_ctx) +{ + const char *path = jsontree_path_name(js_ctx, js_ctx->depth - 1); + + if(strncmp(path, "host", 4) == 0) { + jsontree_write_string(js_ctx, callback_host); + } else if(strncmp(path, "port", 4) == 0) { + jsontree_write_int(js_ctx, callback_port); + } else if(strncmp(path, "interval", 8) == 0) { + jsontree_write_int(js_ctx, callback_interval); + } else if(strncmp(path, "path", 4) == 0) { + jsontree_write_string(js_ctx, callback_path); + } else if(strncmp(path, "appdata", 7) == 0) { + jsontree_write_string(js_ctx, callback_appdata[0] == '\0' ? "" : "***"); + } else if(strncmp(path, "proto", 5) == 0) { + jsontree_write_string(js_ctx, callback_proto); + } + return 0; +} +static int +cfg_set(struct jsontree_context *js_ctx, struct jsonparse_state *parser) +{ + int type; + int update = 0; + + while((type = jsonparse_next(parser)) != 0) { + if(type == JSON_TYPE_PAIR_NAME) { + if(jsonparse_strcmp_value(parser, "host") == 0) { + json_copy_string(parser, callback_host, sizeof(callback_host)); + update++; + } else if(jsonparse_strcmp_value(parser, "path") == 0) { + json_copy_string(parser, callback_path, sizeof(callback_path)); + update++; + } else if(jsonparse_strcmp_value(parser, "appdata") == 0) { + json_copy_string(parser, callback_appdata, sizeof(callback_appdata)); + update++; + } else if(jsonparse_strcmp_value(parser, "proto") == 0) { + json_copy_string(parser, callback_proto, sizeof(callback_proto)); + update++; + } else if(jsonparse_strcmp_value(parser, "port") == 0) { + jsonparse_next(parser); + jsonparse_next(parser); + callback_port = jsonparse_get_value_as_int(parser); + if(callback_port == 0) { + callback_port = CALLBACK_PORT; + } + update++; + } else if(jsonparse_strcmp_value(parser, "interval") == 0) { + jsonparse_next(parser); + jsonparse_next(parser); + callback_interval = jsonparse_get_value_as_int(parser); + if(callback_interval == 0) { + callback_interval = SEND_INTERVAL; + } + update++; + } + } + } + if(update && callback_json_path != NULL) { +#if WITH_UDP + if(strncmp(callback_proto, "udp", 3) == 0) { + json_ws_udp_setup(callback_host, callback_port); + } +#endif + ctimer_set(&periodic_timer, CLOCK_SECOND * callback_interval, + periodic, NULL); + } + return 0; +} +static struct jsontree_callback cfg_callback = + JSONTREE_CALLBACK(cfg_get, cfg_set); + +JSONTREE_OBJECT_EXT(json_subscribe_callback, + JSONTREE_PAIR("host", &cfg_callback), + JSONTREE_PAIR("port", &cfg_callback), + JSONTREE_PAIR("path", &cfg_callback), + JSONTREE_PAIR("appdata", &cfg_callback), + JSONTREE_PAIR("proto", &cfg_callback), + JSONTREE_PAIR("interval", &cfg_callback)); +/*---------------------------------------------------------------------------*/ +static int +time_get(struct jsontree_context *js_ctx) +{ + /* unix time */ + char buf[20]; + unsigned long time = json_time_offset + clock_seconds(); + + snprintf(buf, 20, "%lu", time); + jsontree_write_atom(js_ctx, buf); + return 0; +} + +static int +time_set(struct jsontree_context *js_ctx, struct jsonparse_state *parser) +{ + int type; + unsigned long time; + + while((type = jsonparse_next(parser)) != 0) { + if(type == JSON_TYPE_PAIR_NAME) { + if(jsonparse_strcmp_value(parser, "time") == 0) { + jsonparse_next(parser); + jsonparse_next(parser); + time = jsonparse_get_value_as_long(parser); + json_time_offset = time - clock_seconds(); + } + } + } + return 0; +} + +struct jsontree_callback json_time_callback = + JSONTREE_CALLBACK(time_get, time_set); +/*---------------------------------------------------------------------------*/ +#if PLATFORM_HAS_LEDS +#include "dev/leds.h" + +static int +ws_leds_get(struct jsontree_context *js_ctx) +{ + char buf[4]; + unsigned char leds = leds_get(); + + snprintf(buf, 4, "%u", leds); + jsontree_write_atom(js_ctx, buf); + return 0; +} + +static int +ws_leds_set(struct jsontree_context *js_ctx, struct jsonparse_state *parser) +{ + int type, old_leds, new_leds; + + while((type = jsonparse_next(parser)) != 0) { + if(type == JSON_TYPE_PAIR_NAME) { + if(jsonparse_strcmp_value(parser, "leds") == 0) { + jsonparse_next(parser); + jsonparse_next(parser); + new_leds = jsonparse_get_value_as_int(parser); + old_leds = leds_get(); + leds_on(~old_leds & new_leds); + leds_off(old_leds & ~new_leds); + } + } + } + return 0; +} + +struct jsontree_callback json_leds_callback = + JSONTREE_CALLBACK(ws_leds_get, ws_leds_set); + +#endif /* PLATFORM_HAS_LEDS */ +/*---------------------------------------------------------------------------*/ +static struct httpd_ws_state *json_putchar_context; +static int +json_putchar(int c) +{ + if(json_putchar_context != NULL && + json_putchar_context->outbuf_pos < HTTPD_OUTBUF_SIZE) { + json_putchar_context->outbuf[json_putchar_context->outbuf_pos++] = c; + return c; + } + return 0; +} +static int putchar_size = 0; +static int +json_putchar_count(int c) +{ + putchar_size++; + return c; +} +/*---------------------------------------------------------------------------*/ +static +PT_THREAD(send_values(struct httpd_ws_state *s)) +{ + json_putchar_context = s; + + PSOCK_BEGIN(&s->sout); + + s->json.putchar = json_putchar; + s->outbuf_pos = 0; + + if(s->json.values[0] == NULL) { + /* Nothing to do */ + + } else if(s->request_type == HTTPD_WS_POST && + s->state == HTTPD_WS_STATE_OUTPUT) { + /* Set value */ + struct jsontree_value *v; + struct jsontree_callback *c; + + while((v = jsontree_find_next(&s->json, JSON_TYPE_CALLBACK)) != NULL) { + c = (struct jsontree_callback *)v; + if(c->set != NULL) { + struct jsonparse_state js; + + jsonparse_setup(&js, s->inputbuf, s->content_len); + c->set(&s->json, &js); + } + } + memcpy(s->outbuf, "{\"Status\":\"OK\"}", 15); + s->outbuf_pos = 15; + + } else { + /* Get value */ + while(jsontree_print_next(&s->json) && s->json.path <= s->json.depth) { + if(s->outbuf_pos >= UIP_TCP_MSS) { + SEND_STRING(&s->sout, s->outbuf, UIP_TCP_MSS); + s->outbuf_pos -= UIP_TCP_MSS; + if(s->outbuf_pos > 0) { + memcpy(s->outbuf, &s->outbuf[UIP_TCP_MSS], s->outbuf_pos); + } + } + } + } + + if(s->outbuf_pos > 0) { + SEND_STRING(&s->sout, s->outbuf, s->outbuf_pos); + s->outbuf_pos = 0; + } + PSOCK_END(&s->sout); +} +/*---------------------------------------------------------------------------*/ +struct jsontree_value * +find_json_path(struct jsontree_context *json, const char *path) +{ + struct jsontree_value *v; + const char *start; + const char *end; + int len; + + v = json->values[0]; + start = path; + do { + end = strchr(start, '/'); + if(end == start) { + break; + } + if(end != NULL) { + len = end - start; + end++; + } else { + len = strlen(start); + } + if(v->type != JSON_TYPE_OBJECT) { + v = NULL; + } else { + struct jsontree_object *o; + int i; + + o = (struct jsontree_object *)v; + v = NULL; + for(i = 0; i < o->count; i++) { + if(strncmp(start, o->pairs[i].name, len) == 0) { + v = o->pairs[i].value; + json->index[json->depth] = i; + json->depth++; + json->values[json->depth] = v; + json->index[json->depth] = 0; + break; + } + } + } + start = end; + } while(end != NULL && *end != '\0' && v != NULL); + json->callback_state = 0; + return v; +} +/*---------------------------------------------------------------------------*/ +static int +calculate_json_size(const char *path, struct jsontree_value *v) +{ + /* check size of JSON expression */ + struct jsontree_context json; + + json.values[0] = (v == NULL) ? (struct jsontree_value *)tree : v; + jsontree_reset(&json); + + if(path != NULL) { + find_json_path(&json, path); + } + + json.path = json.depth; + json.putchar = json_putchar_count; + putchar_size = 0; + while(jsontree_print_next(&json) && json.path <= json.depth); + + return putchar_size; +} +/*---------------------------------------------------------------------------*/ +httpd_ws_script_t +httpd_ws_get_script(struct httpd_ws_state *s) +{ + struct jsontree_value *v; + + s->json.values[0] = v = (struct jsontree_value *)tree; + jsontree_reset(&s->json); + + if(s->filename[1] == '\0') { + /* Default page: show full JSON tree. */ + } else { + v = find_json_path(&s->json, &s->filename[1]); + } + if(v != NULL) { + s->json.path = s->json.depth; + s->content_type = http_content_type_json; + return send_values; + } + return NULL; +} +/*---------------------------------------------------------------------------*/ +#if JSON_POST_EXTRA_HEADER || WITH_COSM +static int +output_headers(struct httpd_ws_state *s, char *buffer, int buffer_size, + int index) +{ + if(index == 0) { +#ifdef JSON_POST_EXTRA_HEADER + return snprintf(buffer, buffer_size, "%s\r\n", JSON_POST_EXTRA_HEADER); + } else if(index == 1) { +#endif +#if WITH_COSM + if(strncmp(callback_proto, "cosm", 4) == 0 && callback_appdata[0] != '\0') { + return snprintf(buffer, buffer_size, "X-PachubeApiKey:%s\r\n", + callback_appdata); + } +#endif + } + return 0; +} +#endif /* JSON_POST_EXTRA_HEADER || WITH_COSM */ +/*---------------------------------------------------------------------------*/ +static void +periodic(void *ptr) +{ + struct httpd_ws_state *s; + int callback_size; + + if(callback_json_path != NULL && strlen(callback_host) > 2) { + ctimer_restart(&periodic_timer); + + if(strncmp(callback_proto, "http", 4) == 0) { + callback_size = calculate_json_size(callback_json_path, NULL); + s = httpd_ws_request(HTTPD_WS_POST, callback_host, NULL, callback_port, + callback_path, http_content_type_json, + callback_size, send_values); + if(s != NULL) { + PRINTF("PERIODIC POST %s\n", callback_json_path); +#if JSON_POST_EXTRA_HEADER + s->output_extra_headers = output_headers; +#endif + s->json.values[0] = (struct jsontree_value *)tree; + jsontree_reset(&s->json); + find_json_path(&s->json, callback_json_path); + s->json.path = s->json.depth; + } else { + PRINTF("PERIODIC CALLBACK FAILED\n"); + } +#if WITH_COSM + } else if(strncmp(callback_proto, "cosm", 4) == 0) { + callback_size = calculate_json_size(NULL, (struct jsontree_value *) + &cosm_tree); + /* printf("JSON Size:%d\n", callback_size); */ + s = httpd_ws_request(HTTPD_WS_PUT, callback_host, "api.pachube.com", + callback_port, callback_path, + http_content_type_json, callback_size, send_values); + /* host = cosm host */ + /* path => path to datastream / data point */ + s->output_extra_headers = output_headers; + s->json.values[0] = (struct jsontree_value *)&cosm_tree; + jsontree_reset(&s->json); + s->json.path = 0; + + PRINTF("PERIODIC cosm callback: %d\n", callback_size); +#endif /* WITH_COSM */ + } +#if WITH_UDP + else { + callback_size = calculate_json_size(callback_json_path, NULL); + PRINTF("PERIODIC UDP size: %d\n", callback_size); + json_ws_udp_send(tree, callback_json_path); + } +#endif /* WITH_UDP */ + } else { + printf("PERIODIC CALLBACK - nothing todo\n"); + } +} +/*---------------------------------------------------------------------------*/ +void +json_ws_init(struct jsontree_object *json) +{ + PRINTF("JSON INIT (callback %s every %u seconds)\n", + CALLBACK_PROTO, SEND_INTERVAL); + tree = json; + ctimer_set(&periodic_timer, CLOCK_SECOND * SEND_INTERVAL, periodic, NULL); + process_start(&httpd_ws_process, NULL); +#if WITH_UDP + if(strncmp(callback_proto, "udp", 3) == 0) { + json_ws_udp_setup(callback_host, callback_port); + } +#endif /* WITH_UDP */ +} +/*---------------------------------------------------------------------------*/ +void +json_ws_set_callback(const char *path) +{ + callback_json_path = path; + ctimer_restart(&periodic_timer); +} +/*---------------------------------------------------------------------------*/ diff --git a/tools/cooja/java/se/sics/cooja/ipdistributors/RandomIPDistributor.java b/examples/ipv6/json-ws/json-ws.h similarity index 62% rename from tools/cooja/java/se/sics/cooja/ipdistributors/RandomIPDistributor.java rename to examples/ipv6/json-ws/json-ws.h index 95000b842..7484b06d9 100644 --- a/tools/cooja/java/se/sics/cooja/ipdistributors/RandomIPDistributor.java +++ b/examples/ipv6/json-ws/json-ws.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, Swedish Institute of Computer Science. + * Copyright (c) 2011-2012, Swedish Institute of Computer Science. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -26,34 +26,33 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: RandomIPDistributor.java,v 1.2 2009/02/18 12:07:19 fros4943 Exp $ + * This file is part of the Contiki operating system. */ -package se.sics.cooja.ipdistributors; -import java.util.Random; -import java.util.Vector; -import se.sics.cooja.*; - /** - * Generates IP addresses randomly on the form 10.10.[1-20].[1-20]. - * Nothing prevents several motes from getting the same IP number. - * - * @author Fredrik Osterlind + * \file + * JSON webservice util + * \author + * Niclas Finne + * Joakim Eriksson + * Joel Hoglund */ -@ClassDescription("Random (10.10.?.?)") -public class RandomIPDistributor extends IPDistributor { - private Random random = new Random(); /* Do not use main random generator for setup */ +#ifndef __JSON_WS_H__ +#define __JSON_WS_H__ - /** - * Creates a random IP distributor. - * @param newMotes All motes which later will be assigned IP numbers. - */ - public RandomIPDistributor(Vector newMotes) { - } +#include "jsontree.h" - public String getNextIPAddress() { - return "" + 10 + "." + 10 + "." + (1+random.nextInt(20)) + "." + (1+random.nextInt(20)); - } +void json_ws_init(struct jsontree_object *json); +void json_ws_set_callback(const char *json_path); +int json_ws_udp_setup(const char *host, uint16_t port); -} +extern struct jsontree_object json_subscribe_callback; +extern struct jsontree_callback json_time_callback; + +#if PLATFORM_HAS_LEDS +extern struct jsontree_callback json_leds_callback; +#endif +extern struct jsontree_object cosm_tree; + +#endif /* __JSON_WS_H__ */ diff --git a/examples/ipv6/json-ws/project-conf.h b/examples/ipv6/json-ws/project-conf.h new file mode 100644 index 000000000..db9b6aefc --- /dev/null +++ b/examples/ipv6/json-ws/project-conf.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2012, Swedish Institute of Computer Science. + * 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. + */ + +#ifndef __PROJECT_CONF_H__ +#define __PROJECT_CONF_H__ + +#include "jsontree.h" +#define HTTPD_WS_CONF_USER_STATE struct jsontree_context json + + +/* #define JSON_WS_CONF_CALLBACK_PROTO "http" | "udp" | "cosm" */ +#define JSON_WS_CONF_CALLBACK_PROTO "http" +#define JSON_WS_CONF_CALLBACK_PORT 80 +#define JSON_WS_CONF_CALLBACK_INTERVAL 120 + +#undef NETSTACK_CONF_RDC +#define NETSTACK_CONF_RDC nullrdc_driver +/* #define NETSTACK_CONF_RDC contikimac_driver */ + +#define CONTIKIMAC_CONF_MAX_PHASE_NEIGHBORS 7 + +#undef NULLRDC_CONF_802154_AUTOACK +#define NULLRDC_CONF_802154_AUTOACK 1 + +/* Reduce code size */ +#undef ENERGEST_CONF_ON +#define ENERGEST_CONF_ON 0 + +/* needs to be ~4 for fragmentation to work */ +#undef QUEUEBUF_CONF_NUM +#define QUEUEBUF_CONF_NUM 4 + +#undef UIP_CONF_DS6_NBR_NBU +#define UIP_CONF_DS6_NBR_NBU 7 +#undef UIP_CONF_DS6_ROUTE_NBU +#define UIP_CONF_DS6_ROUTE_NBU 7 + +#undef UIP_CONF_BUFFER_SIZE +#define UIP_CONF_BUFFER_SIZE 140 + +/* #undef UIP_CONF_RECEIVE_WINDOW */ +/* #define UIP_CONF_RECEIVE_WINDOW 35 */ + +#undef WEBSERVER_CONF_INBUF_SIZE +#define WEBSERVER_CONF_INBUF_SIZE 200 + +#undef WEBSERVER_CONF_OUTBUF_SIZE +#define WEBSERVER_CONF_OUTBUF_SIZE (UIP_TCP_MSS + 20 + 80) + +#undef WEBSERVER_CONF_CFS_CONNS +#define WEBSERVER_CONF_CFS_CONNS 3 + +#endif /* __PROJECT_CONF_H__ */ diff --git a/examples/ipv6/json-ws/setcosm.py b/examples/ipv6/json-ws/setcosm.py new file mode 100755 index 000000000..4fe58057a --- /dev/null +++ b/examples/ipv6/json-ws/setcosm.py @@ -0,0 +1,30 @@ +#!/usr/bin/python + +# python set time code +import httplib,sys + +# edit the key and feed parameters to match your COSM account and feed +key = "" +feed = "" +cosmaddr = "[2001:470:1f10:333::2]" + +print "JSON-WS COSM configuration utility\n Currently set to COSM feed: %s Key: '%s'" % (feed, key) +if len(sys.argv) > 2: + host = sys.argv[1] + stream = sys.argv[2] +else: + print "Usage: ", sys.argv[0], " " + sys.exit() + +print "Setting cosm config at:", host, " feed:", feed, " stream:",stream + +conn = httplib.HTTPConnection(host) +# NAT64 address = +#conn.request("POST","", '{"host":"[2001:778:0:ffff:64:0:d834:e97a]","port":80,"path":"/v2/feeds/55180/datastreams/1","interval":120}') + +requestData = '{"host":"%s","port":80,"path":"/v2/feeds/%s/datastreams/%s","appdata":"%s","interval":120,"proto":"cosm"}' % (cosmaddr, feed, stream, key) +print "Posting to node: ", requestData +conn.request("POST","", requestData) + +res = conn.getresponse() +print res.status, res.reason diff --git a/examples/ipv6/json-ws/websense-sky.c b/examples/ipv6/json-ws/websense-sky.c new file mode 100644 index 000000000..83375bdd6 --- /dev/null +++ b/examples/ipv6/json-ws/websense-sky.c @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2011-2012, Swedish Institute of Computer Science. + * 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 + * Websense for Sky mote + * \author + * Niclas Finne + * Joakim Eriksson + * Joel Hoglund + */ + +#include "contiki.h" +#include "dev/leds.h" +#include "dev/sht11-sensor.h" +#include "jsontree.h" +#include "json-ws.h" +#include + +#define DEBUG 0 +#if DEBUG +#define PRINTF(...) printf(__VA_ARGS__) +#else +#define PRINTF(...) +#endif + +PROCESS(websense_process, "Websense (sky)"); +AUTOSTART_PROCESSES(&websense_process); + +/*---------------------------------------------------------------------------*/ +static CC_INLINE int +get_temp(void) +{ + return ((sht11_sensor.value(SHT11_SENSOR_TEMP) / 10) - 396) / 10; +} +/*---------------------------------------------------------------------------*/ +static int +output_temp(struct jsontree_context *path) +{ + char buf[5]; + snprintf(buf, sizeof(buf), "%3d", get_temp()); + jsontree_write_atom(path, buf); + return 0; +} +static struct jsontree_callback temp_sensor_callback = + JSONTREE_CALLBACK(output_temp, NULL); +/*---------------------------------------------------------------------------*/ + +static struct jsontree_string desc = JSONTREE_STRING("Tmote Sky"); +static struct jsontree_string temp_unit = JSONTREE_STRING("Celcius"); + +JSONTREE_OBJECT(node_tree, + JSONTREE_PAIR("node-type", &desc), + JSONTREE_PAIR("time", &json_time_callback)); + +JSONTREE_OBJECT(temp_sensor_tree, + JSONTREE_PAIR("unit", &temp_unit), + JSONTREE_PAIR("value", &temp_sensor_callback)); + +JSONTREE_OBJECT(rsc_tree, + JSONTREE_PAIR("temperature", &temp_sensor_tree), + JSONTREE_PAIR("leds", &json_leds_callback)); + +/* complete node tree */ +JSONTREE_OBJECT(tree, + JSONTREE_PAIR("node", &node_tree), + JSONTREE_PAIR("rsc", &rsc_tree), + JSONTREE_PAIR("cfg", &json_subscribe_callback)); + +/*---------------------------------------------------------------------------*/ +/* for cosm plugin */ +#if WITH_COSM +/* set COSM value callback to be the temp sensor */ +struct jsontree_callback cosm_value_callback = + JSONTREE_CALLBACK(output_temp, NULL); +#endif + +PROCESS_THREAD(websense_process, ev, data) +{ + static struct etimer timer; + + PROCESS_BEGIN(); + + json_ws_init(&tree); + + SENSORS_ACTIVATE(sht11_sensor); + + json_ws_set_callback("rsc"); + + while(1) { + /* Alive indication with the LED */ + etimer_set(&timer, CLOCK_SECOND * 5); + PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&timer)); + leds_on(LEDS_RED); + etimer_set(&timer, CLOCK_SECOND / 8); + PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&timer)); + leds_off(LEDS_RED); + } + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ diff --git a/examples/ipv6/json-ws/websense-z1.c b/examples/ipv6/json-ws/websense-z1.c new file mode 100644 index 000000000..d6796c9c4 --- /dev/null +++ b/examples/ipv6/json-ws/websense-z1.c @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2011-2012, Swedish Institute of Computer Science. + * 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 + * Websense for Z1 mote + * \author + * Niclas Finne + * Joakim Eriksson + * Joel Hoglund + */ + +#include "contiki.h" +#include "dev/leds.h" +#include "dev/tmp102.h" +#include "dev/z1-phidgets.h" +#include "lib/sensors.h" +#include "jsontree.h" +#include "json-ws.h" + +/* It seems like there normally is an offset of some degrees for the tmp102 */ +#define TMP102_OFFSET -150 + +#define DEBUG 0 +#if DEBUG +#include +#define PRINTF(...) printf(__VA_ARGS__) +#else +#define PRINTF(...) +#endif + +PROCESS(websense_process, "Websense (z1)"); +AUTOSTART_PROCESSES(&websense_process); + +static int +output_sensor(struct jsontree_context *path) +{ + char buf[10]; + int tmp; + int tmp2; + + tmp = tmp102_read_temp_x100(); + tmp2 = tmp - TMP102_OFFSET; + tmp = tmp2 / 100; + + snprintf(buf, sizeof(buf), "%2d.%02d", tmp, tmp2 - (100 * tmp)); + jsontree_write_atom(path, buf); + return 0; +} +static struct jsontree_callback sensor_callback = + JSONTREE_CALLBACK(output_sensor, NULL); + +/*---------------------------------------------------------------------------*/ + +static struct jsontree_string desc = JSONTREE_STRING("Zolertia Z1"); +static struct jsontree_string unit = JSONTREE_STRING("Celcius"); + +JSONTREE_OBJECT(node_tree, + JSONTREE_PAIR("node-type", &desc), + JSONTREE_PAIR("time", &json_time_callback)); + +JSONTREE_OBJECT(sensor_tree, + JSONTREE_PAIR("unit", &unit), + JSONTREE_PAIR("value", &sensor_callback)); + +JSONTREE_OBJECT(rsc_tree, + JSONTREE_PAIR("temperature", &sensor_tree), + JSONTREE_PAIR("leds", &json_leds_callback)); + +/* complete node tree */ +JSONTREE_OBJECT(tree, + JSONTREE_PAIR("node", &node_tree), + JSONTREE_PAIR("rsc", &rsc_tree), + JSONTREE_PAIR("cfg", &json_subscribe_callback)); + +/*---------------------------------------------------------------------------*/ +/* for cosm plugin */ +#if WITH_COSM +/* set COSM value callback to be the temp sensor */ +struct jsontree_callback cosm_value_callback = + JSONTREE_CALLBACK(output_sensor, NULL); +#endif + +PROCESS_THREAD(websense_process, ev, data) +{ + static struct etimer timer; + + PROCESS_BEGIN(); + + json_ws_init(&tree); + + tmp102_init(); + + json_ws_set_callback("rsc"); + + while(1) { + /* Alive indication with the LED */ + etimer_set(&timer, CLOCK_SECOND * 5); + PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&timer)); + leds_on(LEDS_RED); + etimer_set(&timer, CLOCK_SECOND / 8); + PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&timer)); + leds_off(LEDS_RED); + } + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ diff --git a/examples/ipv6/rpl-border-router/Makefile b/examples/ipv6/rpl-border-router/Makefile index c166000bc..43230a17f 100644 --- a/examples/ipv6/rpl-border-router/Makefile +++ b/examples/ipv6/rpl-border-router/Makefile @@ -34,13 +34,17 @@ APPS += $(WITH_WEBSERVER) CFLAGS += -DWEBSERVER=2 endif +ifeq ($(PREFIX),) + PREFIX = aaaa::1/64 +endif + include $(CONTIKI)/Makefile.include $(CONTIKI)/tools/tunslip6: $(CONTIKI)/tools/tunslip6.c (cd $(CONTIKI)/tools && $(MAKE) tunslip6) connect-router: $(CONTIKI)/tools/tunslip6 - sudo $(CONTIKI)/tools/tunslip6 aaaa::1/64 + sudo $(CONTIKI)/tools/tunslip6 $(PREFIX) connect-router-cooja: $(CONTIKI)/tools/tunslip6 - sudo $(CONTIKI)/tools/tunslip6 -a 127.0.0.1 aaaa::1/64 + sudo $(CONTIKI)/tools/tunslip6 -a 127.0.0.1 $(PREFIX) diff --git a/tools/cooja/apps/avrora/src/se/sics/cooja/avrmote/MicaZMoteType.java b/tools/cooja/apps/avrora/src/se/sics/cooja/avrmote/MicaZMoteType.java index 88030e895..afbafb5b1 100644 --- a/tools/cooja/apps/avrora/src/se/sics/cooja/avrmote/MicaZMoteType.java +++ b/tools/cooja/apps/avrora/src/se/sics/cooja/avrmote/MicaZMoteType.java @@ -37,6 +37,7 @@ import java.awt.Dimension; import java.io.File; import java.util.ArrayList; import java.util.Collection; + import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; @@ -44,8 +45,10 @@ import javax.swing.Icon; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextArea; + import org.apache.log4j.Logger; import org.jdom.Element; + import se.sics.cooja.AbstractionLevelDescription; import se.sics.cooja.ClassDescription; import se.sics.cooja.GUI; @@ -68,10 +71,10 @@ import se.sics.cooja.interfaces.Position; /** * AVR-based MicaZ mote types emulated in Avrora. - * + * * @author Joakim Eriksson, Fredrik Osterlind */ -@ClassDescription("MicaZ mote...") +@ClassDescription("MicaZ mote") @AbstractionLevelDescription("Emulated level") public class MicaZMoteType implements MoteType { private static Logger logger = Logger.getLogger(MicaZMoteType.class); diff --git a/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/ESBMoteType.java b/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/ESBMoteType.java index 98585648a..e04bca85f 100644 --- a/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/ESBMoteType.java +++ b/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/ESBMoteType.java @@ -31,13 +31,25 @@ package se.sics.cooja.mspmote; -import java.awt.*; +import java.awt.Container; +import java.awt.Image; +import java.awt.MediaTracker; +import java.awt.Toolkit; import java.io.File; import java.net.URL; -import javax.swing.*; + +import javax.swing.Icon; +import javax.swing.ImageIcon; +import javax.swing.JOptionPane; import org.apache.log4j.Logger; -import se.sics.cooja.*; + +import se.sics.cooja.AbstractionLevelDescription; +import se.sics.cooja.ClassDescription; +import se.sics.cooja.GUI; +import se.sics.cooja.MoteInterface; +import se.sics.cooja.MoteType; +import se.sics.cooja.Simulation; import se.sics.cooja.dialogs.CompileContiki; import se.sics.cooja.dialogs.MessageList; import se.sics.cooja.dialogs.MessageList.MessageContainer; @@ -53,7 +65,7 @@ import se.sics.cooja.mspmote.interfaces.MspMoteID; import se.sics.cooja.mspmote.interfaces.MspSerial; import se.sics.cooja.mspmote.interfaces.TR1001Radio; -@ClassDescription("ESB mote...") +@ClassDescription("ESB mote") @AbstractionLevelDescription("Emulated level") public class ESBMoteType extends MspMoteType { private static Logger logger = Logger.getLogger(ESBMoteType.class); @@ -209,7 +221,7 @@ public class ESBMoteType extends MspMoteType { return new File(parentDir, sourceNoExtension + ".esb"); } - + protected String getTargetName() { return "esb"; } diff --git a/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/Exp5438MoteType.java b/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/Exp5438MoteType.java index 1fb93492c..2cb52bffa 100644 --- a/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/Exp5438MoteType.java +++ b/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/Exp5438MoteType.java @@ -56,13 +56,13 @@ import se.sics.cooja.interfaces.MoteAttributes; import se.sics.cooja.interfaces.Position; import se.sics.cooja.interfaces.RimeAddress; import se.sics.cooja.mspmote.interfaces.Exp5438LED; +import se.sics.cooja.mspmote.interfaces.Msp802154Radio; import se.sics.cooja.mspmote.interfaces.MspClock; import se.sics.cooja.mspmote.interfaces.MspDebugOutput; import se.sics.cooja.mspmote.interfaces.MspMoteID; -import se.sics.cooja.mspmote.interfaces.Msp802154Radio; import se.sics.cooja.mspmote.interfaces.UsciA1Serial; -@ClassDescription("EXP430F5438 mote...") +@ClassDescription("EXP430F5438 mote") @AbstractionLevelDescription("Emulated level") public class Exp5438MoteType extends MspMoteType { private static Logger logger = Logger.getLogger(Exp5438MoteType.class); @@ -190,7 +190,7 @@ public class Exp5438MoteType extends MspMoteType { MspMoteID.class, Msp802154Radio.class, UsciA1Serial.class, - Exp5438LED.class, + Exp5438LED.class, /*Exp5438LCD.class,*/ /* TODO */ MspDebugOutput.class }; @@ -202,7 +202,7 @@ public class Exp5438MoteType extends MspMoteType { return new File(parentDir, sourceNoExtension + ".exp5438"); } - + protected String getTargetName() { return "exp5438"; } diff --git a/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/SkyMoteType.java b/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/SkyMoteType.java index 9640ecb58..6afdae7d8 100644 --- a/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/SkyMoteType.java +++ b/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/SkyMoteType.java @@ -58,18 +58,18 @@ import se.sics.cooja.interfaces.Mote2MoteRelations; import se.sics.cooja.interfaces.MoteAttributes; import se.sics.cooja.interfaces.Position; import se.sics.cooja.interfaces.RimeAddress; +import se.sics.cooja.mspmote.interfaces.Msp802154Radio; import se.sics.cooja.mspmote.interfaces.MspClock; import se.sics.cooja.mspmote.interfaces.MspDebugOutput; import se.sics.cooja.mspmote.interfaces.MspMoteID; +import se.sics.cooja.mspmote.interfaces.MspSerial; import se.sics.cooja.mspmote.interfaces.SkyButton; -import se.sics.cooja.mspmote.interfaces.Msp802154Radio; import se.sics.cooja.mspmote.interfaces.SkyCoffeeFilesystem; import se.sics.cooja.mspmote.interfaces.SkyFlash; import se.sics.cooja.mspmote.interfaces.SkyLED; -import se.sics.cooja.mspmote.interfaces.MspSerial; import se.sics.cooja.mspmote.interfaces.SkyTemperature; -@ClassDescription("Sky mote...") +@ClassDescription("Sky mote") @AbstractionLevelDescription("Emulated level") public class SkyMoteType extends MspMoteType { private static Logger logger = Logger.getLogger(SkyMoteType.class); @@ -229,7 +229,7 @@ public class SkyMoteType extends MspMoteType { return new File(parentDir, sourceNoExtension + ".sky"); } - + protected String getTargetName() { return "sky"; } diff --git a/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/TyndallMoteType.java b/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/TyndallMoteType.java index 2a7b399e8..7bac0f5ac 100644 --- a/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/TyndallMoteType.java +++ b/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/TyndallMoteType.java @@ -55,14 +55,14 @@ import se.sics.cooja.interfaces.Mote2MoteRelations; import se.sics.cooja.interfaces.MoteAttributes; import se.sics.cooja.interfaces.Position; import se.sics.cooja.interfaces.RimeAddress; +import se.sics.cooja.mspmote.interfaces.Msp802154Radio; import se.sics.cooja.mspmote.interfaces.MspClock; import se.sics.cooja.mspmote.interfaces.MspDebugOutput; import se.sics.cooja.mspmote.interfaces.MspMoteID; -import se.sics.cooja.mspmote.interfaces.Msp802154Radio; import se.sics.cooja.mspmote.interfaces.TyndallLED; import se.sics.cooja.mspmote.interfaces.UsciA0Serial; -@ClassDescription("Tyndall mote...") +@ClassDescription("Tyndall mote") @AbstractionLevelDescription("Emulated level") public class TyndallMoteType extends MspMoteType { private static Logger logger = Logger.getLogger(TyndallMoteType.class); @@ -201,7 +201,7 @@ public class TyndallMoteType extends MspMoteType { return new File(parentDir, sourceNoExtension + ".tyndall"); } - + protected String getTargetName() { return "tyndall"; } diff --git a/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/WismoteMoteType.java b/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/WismoteMoteType.java index 0d4fe72e5..61659d1f2 100644 --- a/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/WismoteMoteType.java +++ b/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/WismoteMoteType.java @@ -45,7 +45,7 @@ import se.sics.cooja.mspmote.interfaces.MspDefaultSerial; import se.sics.cooja.mspmote.interfaces.MspLED; import se.sics.cooja.mspmote.interfaces.MspMoteID; -@ClassDescription("Wismote mote...") +@ClassDescription("Wismote mote") @AbstractionLevelDescription("Emulated level") public class WismoteMoteType extends AbstractMspMoteType { diff --git a/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/Z1MoteType.java b/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/Z1MoteType.java index b5a7fdef5..7e1ab8510 100644 --- a/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/Z1MoteType.java +++ b/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/Z1MoteType.java @@ -46,7 +46,7 @@ import se.sics.cooja.mspmote.interfaces.MspDefaultSerial; import se.sics.cooja.mspmote.interfaces.MspLED; import se.sics.cooja.mspmote.interfaces.MspMoteID; -@ClassDescription("Z1 mote...") +@ClassDescription("Z1 mote") @AbstractionLevelDescription("Emulated level") public class Z1MoteType extends AbstractMspMoteType { diff --git a/tools/cooja/apps/powertracker/java/PowerTracker.java b/tools/cooja/apps/powertracker/java/PowerTracker.java index 2ac9f69e1..f67df3baa 100644 --- a/tools/cooja/apps/powertracker/java/PowerTracker.java +++ b/tools/cooja/apps/powertracker/java/PowerTracker.java @@ -69,7 +69,7 @@ import se.sics.cooja.interfaces.Radio; * * @author Fredrik Osterlind, Adam Dunkels */ -@ClassDescription("Mote radio duty cycle...") +@ClassDescription("Mote radio duty cycle") @PluginType(PluginType.SIM_PLUGIN) public class PowerTracker extends VisPlugin { private static Logger logger = Logger.getLogger(PowerTracker.class); diff --git a/tools/cooja/config/cooja_applet.config b/tools/cooja/config/cooja_applet.config index e3460ad22..1d0dc43e6 100644 --- a/tools/cooja/config/cooja_applet.config +++ b/tools/cooja/config/cooja_applet.config @@ -1,5 +1,4 @@ se.sics.cooja.GUI.MOTETYPES = se.sics.cooja.motes.DisturberMoteType se.sics.cooja.contikimote.ContikiMoteType se.sics.cooja.mspmote.ESBMoteType se.sics.cooja.mspmote.SkyMoteType se.sics.cooja.GUI.PLUGINS = se.sics.cooja.plugins.Visualizer se.sics.cooja.plugins.LogListener se.sics.cooja.plugins.MoteInformation se.sics.cooja.plugins.MoteInterfaceViewer se.sics.cooja.plugins.VariableWatcher se.sics.cooja.plugins.EventListener se.sics.cooja.plugins.RadioLogger se.sics.cooja.mspmote.plugins.MspCodeWatcher se.sics.cooja.mspmote.plugins.MspStackWatcher se.sics.cooja.mspmote.plugins.MspCycleWatcher -se.sics.cooja.GUI.IP_DISTRIBUTORS = se.sics.cooja.ipdistributors.RandomIPDistributor se.sics.cooja.ipdistributors.SpatialIPDistributor se.sics.cooja.ipdistributors.IdIPDistributor se.sics.cooja.GUI.POSITIONERS = se.sics.cooja.positioners.RandomPositioner se.sics.cooja.positioners.LinearPositioner se.sics.cooja.positioners.EllipsePositioner se.sics.cooja.positioners.ManualPositioner se.sics.cooja.GUI.RADIOMEDIUMS = se.sics.cooja.radiomediums.UDGM se.sics.cooja.radiomediums.UDGMConstantLoss se.sics.cooja.radiomediums.DirectedGraphMedium se.sics.mrm.MRM se.sics.cooja.radiomediums.SilentRadioMedium diff --git a/tools/cooja/config/cooja_default.config b/tools/cooja/config/cooja_default.config index 8c8cee76d..7f46f6138 100644 --- a/tools/cooja/config/cooja_default.config +++ b/tools/cooja/config/cooja_default.config @@ -4,6 +4,5 @@ se.sics.cooja.contikimote.ContikiMoteType.MOTE_INTERFACES = se.sics.cooja.interf se.sics.cooja.contikimote.ContikiMoteType.C_SOURCES = se.sics.cooja.GUI.MOTETYPES = se.sics.cooja.motes.ImportAppMoteType se.sics.cooja.motes.DisturberMoteType se.sics.cooja.contikimote.ContikiMoteType se.sics.cooja.GUI.PLUGINS = se.sics.cooja.plugins.Visualizer se.sics.cooja.plugins.LogListener se.sics.cooja.plugins.TimeLine se.sics.cooja.plugins.MoteInformation se.sics.cooja.plugins.MoteInterfaceViewer se.sics.cooja.plugins.VariableWatcher se.sics.cooja.plugins.EventListener se.sics.cooja.plugins.RadioLogger se.sics.cooja.plugins.ScriptRunner se.sics.cooja.plugins.Notes se.sics.cooja.plugins.BufferListener -se.sics.cooja.GUI.IP_DISTRIBUTORS = se.sics.cooja.ipdistributors.RandomIPDistributor se.sics.cooja.ipdistributors.SpatialIPDistributor se.sics.cooja.ipdistributors.IdIPDistributor se.sics.cooja.GUI.POSITIONERS = se.sics.cooja.positioners.RandomPositioner se.sics.cooja.positioners.LinearPositioner se.sics.cooja.positioners.EllipsePositioner se.sics.cooja.positioners.ManualPositioner se.sics.cooja.GUI.RADIOMEDIUMS = se.sics.cooja.radiomediums.UDGM se.sics.cooja.radiomediums.UDGMConstantLoss se.sics.cooja.radiomediums.DirectedGraphMedium se.sics.cooja.radiomediums.SilentRadioMedium diff --git a/tools/cooja/java/se/sics/cooja/GUI.java b/tools/cooja/java/se/sics/cooja/GUI.java index bd0985e17..f23b3dce0 100644 --- a/tools/cooja/java/se/sics/cooja/GUI.java +++ b/tools/cooja/java/se/sics/cooja/GUI.java @@ -829,7 +829,7 @@ public class GUI extends Observable { } String description = GUI.getDescriptionOf(moteTypeClass); - menuItem = new JMenuItem(description); + menuItem = new JMenuItem(description + "..."); menuItem.setActionCommand("create mote type"); menuItem.putClientProperty("class", moteTypeClass); /* menuItem.setToolTipText(abstractionLevelDescription);*/ diff --git a/tools/cooja/java/se/sics/cooja/IPDistributor.java b/tools/cooja/java/se/sics/cooja/IPDistributor.java deleted file mode 100644 index 8dc96b800..000000000 --- a/tools/cooja/java/se/sics/cooja/IPDistributor.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2006, Swedish Institute of Computer Science. 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. - * - * $Id: IPDistributor.java,v 1.2 2007/01/10 14:57:42 fros4943 Exp $ - */ - -package se.sics.cooja; - -import java.lang.reflect.Constructor; -import java.util.Vector; -import org.apache.log4j.Logger; - -/** - * A IP distributor is used for determining IP addresses of newly created motes. - * - * @see se.sics.cooja.Positioner - * @author Fredrik Osterlind - */ -public abstract class IPDistributor { - private static Logger logger = Logger.getLogger(IPDistributor.class); - - /** - * This method creates an instance of the given class with the given vector as - * constructor argument. Instead of calling the constructors directly this - * method may be used. - * - * @param ipDistClass - * Class - * @param newMotes - * All motes that later should be assigned IP numbers - * @return IP distributor instance - */ - public static final IPDistributor generateIPDistributor( - Class ipDistClass, Vector newMotes) { - try { - // Generating IP distributor - Constructor constr = ipDistClass - .getConstructor(new Class[] { Vector.class }); - return (IPDistributor) constr.newInstance(new Object[] { newMotes }); - } catch (Exception e) { - logger.fatal("Exception when creating " + ipDistClass + ": " + e); - return null; - } - } - - /** - * Returns the next mote IP address. - * - * @return IP Address - */ - public abstract String getNextIPAddress(); - -} diff --git a/tools/cooja/java/se/sics/cooja/contikimote/ContikiMoteType.java b/tools/cooja/java/se/sics/cooja/contikimote/ContikiMoteType.java index 7c59a81f4..bc9358bfe 100644 --- a/tools/cooja/java/se/sics/cooja/contikimote/ContikiMoteType.java +++ b/tools/cooja/java/se/sics/cooja/contikimote/ContikiMoteType.java @@ -90,7 +90,7 @@ import se.sics.cooja.util.StringUtils; * * @author Fredrik Osterlind */ -@ClassDescription("Cooja mote...") +@ClassDescription("Cooja mote") @AbstractionLevelDescription("OS level") public class ContikiMoteType implements MoteType { private static Logger logger = Logger.getLogger(ContikiMoteType.class); diff --git a/tools/cooja/java/se/sics/cooja/dialogs/AddMoteDialog.java b/tools/cooja/java/se/sics/cooja/dialogs/AddMoteDialog.java index d39871a8e..06126eb88 100644 --- a/tools/cooja/java/se/sics/cooja/dialogs/AddMoteDialog.java +++ b/tools/cooja/java/se/sics/cooja/dialogs/AddMoteDialog.java @@ -31,16 +31,45 @@ package se.sics.cooja.dialogs; -import java.awt.*; -import java.awt.event.*; -import java.beans.*; -import java.text.*; -import java.util.*; -import javax.swing.*; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dialog; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.text.NumberFormat; +import java.text.ParseException; +import java.util.Vector; + +import javax.swing.BorderFactory; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JDialog; +import javax.swing.JFormattedTextField; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; + import org.apache.log4j.Logger; -import se.sics.cooja.*; -import se.sics.cooja.interfaces.*; +import se.sics.cooja.GUI; +import se.sics.cooja.Mote; +import se.sics.cooja.MoteType; +import se.sics.cooja.Positioner; +import se.sics.cooja.Simulation; +import se.sics.cooja.interfaces.MoteID; +import se.sics.cooja.interfaces.Position; /** * A dialog for adding motes. @@ -66,7 +95,7 @@ public class AddMoteDialog extends JDialog { private JFormattedTextField numberOfMotesField, startX, endX, startY, endY, startZ, endZ; - private JComboBox positionDistributionBox /*, ipDistributionBox*/; + private JComboBox positionDistributionBox; /** @@ -176,36 +205,6 @@ public class AddMoteDialog extends JDialog { mainPane.add(smallPane); mainPane.add(Box.createRigidArea(new Dimension(0, 5))); - // IP address distribution - /*smallPane = new JPanel(); - smallPane.setAlignmentX(Component.LEFT_ALIGNMENT); - smallPane.setLayout(new BoxLayout(smallPane, BoxLayout.X_AXIS)); - label = new JLabel("IP Addressing"); - label.setPreferredSize(new Dimension(LABEL_WIDTH, LABEL_HEIGHT)); - - Vector> ipDistributors = simulation.getGUI() - .getRegisteredIPDistributors(); - String[] ipDistributions = new String[ipDistributors.size()]; - for (int i = 0; i < ipDistributions.length; i++) { - ipDistributions[i] = GUI.getDescriptionOf(ipDistributors.get(i)); - } - - comboBox = new JComboBox(ipDistributions); - - comboBox.setSelectedIndex(0); - comboBox.addActionListener(myEventHandler); - comboBox.addFocusListener(myEventHandler); - ipDistributionBox = comboBox; - label.setLabelFor(comboBox); - - smallPane.add(label); - smallPane.add(Box.createHorizontalStrut(10)); - smallPane.add(comboBox); - - mainPane.add(smallPane); - mainPane.add(Box.createRigidArea(new Dimension(0, 5))); - */ - // Position distribution smallPane = new JPanel(); smallPane.setAlignmentX(Component.LEFT_ALIGNMENT); @@ -504,7 +503,7 @@ public class AddMoteDialog extends JDialog { } } - /* Set unique mote id's for all new motes + /* Set unique mote id's for all new motes * TODO ID should be provided differently; not rely on the unsafe MoteID interface */ int nextMoteID = 1; for (Mote m: simulation.getMotes()) { @@ -522,33 +521,6 @@ public class AddMoteDialog extends JDialog { } } - // IP address new motes - /*Class ipDistClass = null; - for (Class ipDistributor : simulation.getGUI() - .getRegisteredIPDistributors()) { - if (GUI.getDescriptionOf(ipDistributor).equals( - ipDistributionBox.getSelectedItem())) { - ipDistClass = ipDistributor; - } - } - - IPDistributor ipDistributor = IPDistributor.generateIPDistributor( - ipDistClass, newMotes); - - if (ipDistributor == null) { - logger.fatal("Could not create IP distributor"); - dispose(); - return; - } - - for (int i = 0; i < newMotes.size(); i++) { - String newIPString = ipDistributor.getNextIPAddress(); - if (newMotes.get(i).getInterfaces().getIPAddress() != null) { - newMotes.get(i).getInterfaces().getIPAddress().setIPString( - newIPString); - } - }*/ - dispose(); } catch (OutOfMemoryError e2) { newMotes = null; diff --git a/tools/cooja/java/se/sics/cooja/ipdistributors/SpatialIPDistributor.java b/tools/cooja/java/se/sics/cooja/ipdistributors/SpatialIPDistributor.java deleted file mode 100644 index 72763d092..000000000 --- a/tools/cooja/java/se/sics/cooja/ipdistributors/SpatialIPDistributor.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2006, Swedish Institute of Computer Science. - * 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. - * - * $Id: SpatialIPDistributor.java,v 1.1 2006/08/21 12:13:07 fros4943 Exp $ - */ - -package se.sics.cooja.ipdistributors; -import java.util.Vector; -import se.sics.cooja.*; - -/** - * Generates spatial IP addresses on the form 10.[z-coord].[y-coord].[x-coord]. - * The smallest coordinate in each interval will be mapped onto address 1, - * and the biggest coordinate onto address 200. - * Nothing prevents several motes from getting the same IP number. - * - * @author Fredrik Osterlind - */ -@ClassDescription("Spatial (10.x.y.z)") -public class SpatialIPDistributor extends IPDistributor { - private double lowestX, biggestX, lowestY, biggestY, lowestZ, biggestZ; - private Vector generatedIPAddresses; - - /** - * Creates a random IP distributor. - * @param newMotes All motes which later will be assigned IP numbers. - */ - public SpatialIPDistributor(Vector newMotes) { - lowestX = newMotes.get(0).getInterfaces().getPosition().getXCoordinate(); - biggestX = newMotes.get(0).getInterfaces().getPosition().getXCoordinate(); - lowestY = newMotes.get(0).getInterfaces().getPosition().getYCoordinate(); - biggestY = newMotes.get(0).getInterfaces().getPosition().getYCoordinate(); - lowestZ = newMotes.get(0).getInterfaces().getPosition().getZCoordinate(); - biggestZ = newMotes.get(0).getInterfaces().getPosition().getZCoordinate(); - - for (int i=0; i < newMotes.size(); i++) { - if (newMotes.get(i).getInterfaces().getPosition().getXCoordinate() < lowestX) - lowestX = newMotes.get(i).getInterfaces().getPosition().getXCoordinate(); - if (newMotes.get(i).getInterfaces().getPosition().getXCoordinate() > biggestX) - biggestX = newMotes.get(i).getInterfaces().getPosition().getXCoordinate(); - - if (newMotes.get(i).getInterfaces().getPosition().getYCoordinate() < lowestY) - lowestY = newMotes.get(i).getInterfaces().getPosition().getYCoordinate(); - if (newMotes.get(i).getInterfaces().getPosition().getYCoordinate() > biggestY) - biggestY = newMotes.get(i).getInterfaces().getPosition().getYCoordinate(); - - if (newMotes.get(i).getInterfaces().getPosition().getZCoordinate() < lowestZ) - lowestZ = newMotes.get(i).getInterfaces().getPosition().getZCoordinate(); - if (newMotes.get(i).getInterfaces().getPosition().getZCoordinate() > biggestZ) - biggestZ = newMotes.get(i).getInterfaces().getPosition().getZCoordinate(); - } - - generatedIPAddresses = new Vector(); - for (int i=0; i < newMotes.size(); i++) { - String ipAddress = "10."; - int partIP; - - // Z coord - partIP = (int) (1 + 199*(newMotes.get(i).getInterfaces().getPosition().getZCoordinate() - lowestZ) / (biggestZ - lowestZ)); - ipAddress = ipAddress.concat(partIP + "."); - - // Y coord - partIP = (int) (1 + 199*(newMotes.get(i).getInterfaces().getPosition().getYCoordinate() - lowestY) / (biggestY - lowestY)); - ipAddress = ipAddress.concat(partIP + "."); - - // X coord - partIP = (int) (1 + 199*(newMotes.get(i).getInterfaces().getPosition().getXCoordinate() - lowestX) / (biggestX - lowestX)); - ipAddress = ipAddress.concat(partIP + ""); - - generatedIPAddresses.add(ipAddress); - } - } - - public String getNextIPAddress() { - if (generatedIPAddresses.size() > 0) - return generatedIPAddresses.remove(0); - else - return "0.0.0.0"; - } - -} diff --git a/tools/cooja/java/se/sics/cooja/motes/ImportAppMoteType.java b/tools/cooja/java/se/sics/cooja/motes/ImportAppMoteType.java index bc850f624..98a0f22f3 100644 --- a/tools/cooja/java/se/sics/cooja/motes/ImportAppMoteType.java +++ b/tools/cooja/java/se/sics/cooja/motes/ImportAppMoteType.java @@ -40,7 +40,9 @@ import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Collection; + import org.jdom.Element; + import se.sics.cooja.AbstractionLevelDescription; import se.sics.cooja.ClassDescription; import se.sics.cooja.Mote; @@ -52,7 +54,7 @@ import se.sics.cooja.util.ArrayUtils; /** * @author Fredrik Osterlind */ -@ClassDescription("Import Java mote...") +@ClassDescription("Import Java mote") @AbstractionLevelDescription("Application level") public class ImportAppMoteType extends AbstractApplicationMoteType { @@ -109,7 +111,7 @@ public class ImportAppMoteType extends AbstractApplicationMoteType { } public boolean configureAndInit(Container parentContainer, - Simulation simulation, boolean visAvailable) + Simulation simulation, boolean visAvailable) throws MoteTypeCreationException { this.simulation = simulation; @@ -256,7 +258,7 @@ public class ImportAppMoteType extends AbstractApplicationMoteType { public boolean isTestSubclass(Class type) { return type.isAssignableFrom(testClass); } - + public Class getTestClass() { return testClass; } diff --git a/tools/cooja/java/se/sics/cooja/plugins/BufferListener.java b/tools/cooja/java/se/sics/cooja/plugins/BufferListener.java index 794d0a0c9..6508da8ba 100644 --- a/tools/cooja/java/se/sics/cooja/plugins/BufferListener.java +++ b/tools/cooja/java/se/sics/cooja/plugins/BufferListener.java @@ -107,7 +107,7 @@ import se.sics.cooja.util.StringUtils; /** * @author Fredrik Osterlind, Niclas Finne */ -@ClassDescription("Buffer view...") +@ClassDescription("Buffer view") @PluginType(PluginType.SIM_PLUGIN) public class BufferListener extends VisPlugin { private static final long serialVersionUID = 1L; @@ -132,7 +132,7 @@ public class BufferListener extends VisPlugin { final static int MAX_BUFFER_SIZE = 128; - private static ArrayList> bufferParsers = + private static ArrayList> bufferParsers = new ArrayList>(); static { registerBufferParser(ByteArrayParser.class); @@ -148,7 +148,7 @@ public class BufferListener extends VisPlugin { /* TODO Hide identical lines? */ - private static ArrayList> bufferTypes = + private static ArrayList> bufferTypes = new ArrayList>(); static { registerBufferType(PacketbufBuffer.class); @@ -787,7 +787,7 @@ public class BufferListener extends VisPlugin { private void updateTitle() { if (buffer != null) { String status = buffer.getStatusString(); - setTitle("Buffer Listener - " + + setTitle("Buffer Listener - " + ((status!=null)?status:GUI.getDescriptionOf(buffer)) + " " + "- " + memoryMonitors.size() + " buffers on " + motes.size() + " motes"); } @@ -872,7 +872,7 @@ public class BufferListener extends VisPlugin { repaintTimeColumn(); } else if ("parser".equals(name)) { String parserClassname = element.getText(); - Class parserClass = + Class parserClass = simulation.getGUI().tryLoadClass(this, Parser.class, parserClassname); if (parserClass == null) { logger.warn("Could not create buffer parser: could not find class: " + parserClassname); @@ -960,7 +960,7 @@ public class BufferListener extends VisPlugin { return; } } - }); + }); } public static class BufferAccess { @@ -1271,7 +1271,7 @@ public class BufferListener extends VisPlugin { @SuppressWarnings("unchecked") public void actionPerformed(ActionEvent e) { Class bpClass = - (Class) + (Class) ((JMenuItem) e.getSource()).getClientProperty("CLASS"); setParser(bpClass); } @@ -1291,7 +1291,7 @@ public class BufferListener extends VisPlugin { @SuppressWarnings("unchecked") public void actionPerformed(ActionEvent e) { Class btClass = - (Class) + (Class) ((JMenuItem) e.getSource()).getClientProperty("CLASS"); Buffer b = createBufferInstance(btClass); @@ -1610,7 +1610,7 @@ public class BufferListener extends VisPlugin { public static class PrintableCharactersParser extends StringParser { public String parseString(BufferAccess ba) { /* TODO Diff? */ - return new String(ba.mem).replaceAll("[^\\p{Print}]", ""); + return new String(ba.mem).replaceAll("[^\\p{Print}]", ""); } } @@ -1692,7 +1692,7 @@ public class BufferListener extends VisPlugin { if (diff != null && diff[x]) { red = true; } - int v = (int)0xff&ba.mem[x]; + int v = 0xff&ba.mem[x]; int h = Math.min(v/16, 15); /* crop */ if (red) { g.setColor(Color.RED); diff --git a/tools/cooja/java/se/sics/cooja/plugins/EventListener.java b/tools/cooja/java/se/sics/cooja/plugins/EventListener.java index 8fec46392..b1919470f 100644 --- a/tools/cooja/java/se/sics/cooja/plugins/EventListener.java +++ b/tools/cooja/java/se/sics/cooja/plugins/EventListener.java @@ -30,16 +30,42 @@ package se.sics.cooja.plugins; import java.awt.Component; -import java.awt.event.*; -import java.util.*; -import javax.swing.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.Collection; +import java.util.Observable; +import java.util.Observer; +import java.util.Vector; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.BorderFactory; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.SwingUtilities; + import org.apache.log4j.Logger; import org.jdom.Element; -import se.sics.cooja.*; -import se.sics.cooja.GUI.PluginConstructionException; +import se.sics.cooja.ClassDescription; +import se.sics.cooja.GUI; +import se.sics.cooja.Mote; +import se.sics.cooja.MoteInterface; +import se.sics.cooja.MoteType; +import se.sics.cooja.PluginType; +import se.sics.cooja.Simulation; +import se.sics.cooja.VisPlugin; import se.sics.cooja.contikimote.ContikiMoteType; -import se.sics.cooja.interfaces.*; +import se.sics.cooja.interfaces.Button; +import se.sics.cooja.interfaces.LED; +import se.sics.cooja.interfaces.Log; +import se.sics.cooja.interfaces.PIR; +import se.sics.cooja.interfaces.Position; +import se.sics.cooja.interfaces.Radio; /** * Allows a user to observe several different parts of the simulator, stopping a @@ -47,7 +73,7 @@ import se.sics.cooja.interfaces.*; * * @author Fredrik Osterlind */ -@ClassDescription("Breakpoints...") +@ClassDescription("Breakpoints") @PluginType(PluginType.SIM_PLUGIN) public class EventListener extends VisPlugin { private static final long serialVersionUID = 1L; @@ -113,7 +139,7 @@ public class EventListener extends VisPlugin { + myParent.mySimulation.getSimulationTime(), new AbstractAction( "View interface visualizer") { public void actionPerformed(ActionEvent e) { - MoteInterfaceViewer plugin = + MoteInterfaceViewer plugin = (MoteInterfaceViewer) mySimulation.getGUI().tryStartPlugin( MoteInterfaceViewer.class, mySimulation.getGUI(), mySimulation, myMote); plugin.setSelectedInterface(GUI.getDescriptionOf(moteInterface.getClass())); diff --git a/tools/cooja/java/se/sics/cooja/plugins/LogListener.java b/tools/cooja/java/se/sics/cooja/plugins/LogListener.java index 4813e33d5..d5c2db387 100644 --- a/tools/cooja/java/se/sics/cooja/plugins/LogListener.java +++ b/tools/cooja/java/se/sics/cooja/plugins/LogListener.java @@ -103,7 +103,7 @@ import se.sics.cooja.util.ArrayQueue; * * @author Fredrik Osterlind, Niclas Finne */ -@ClassDescription("Mote output...") +@ClassDescription("Mote output") @PluginType(PluginType.SIM_STANDARD_PLUGIN) public class LogListener extends VisPlugin implements HasQuickHelp { private static final long serialVersionUID = 3294595371354857261L; @@ -132,7 +132,7 @@ public class LogListener extends VisPlugin implements HasQuickHelp { private ArrayQueue logs = new ArrayQueue(); private Simulation simulation; - + private JTextField filterTextField = null; private JLabel filterLabel = new JLabel("Filter: "); private Color filterTextFieldBackground; @@ -208,18 +208,18 @@ public class LogListener extends VisPlugin implements HasQuickHelp { menuBar.add(editMenu); menuBar.add(showMenu); this.setJMenuBar(menuBar); - + editMenu.add(new JMenuItem(copyAllAction)); editMenu.add(new JMenuItem(copyAllMessagesAction)); editMenu.add(new JMenuItem(copyAction)); editMenu.addSeparator(); editMenu.add(new JMenuItem(clearAction)); - - + + fileMenu.add(new JMenuItem(saveAction)); appendCheckBox = new JCheckBoxMenuItem(appendAction); fileMenu.add(appendCheckBox); - + colorCheckbox = new JCheckBoxMenuItem("Mote-specific coloring"); showMenu.add(colorCheckbox); colorCheckbox.addActionListener(new ActionListener() { @@ -400,7 +400,7 @@ public class LogListener extends VisPlugin implements HasQuickHelp { adjuster.packColumns(); /* Popup menu */ - + JPopupMenu popupMenu = new JPopupMenu(); /* JMenu copyClipboard = new JMenu("Copy to clipboard"); @@ -543,7 +543,7 @@ public class LogListener extends VisPlugin implements HasQuickHelp { updateTitle(); pack(); - + /* XXX HACK: here we set the position and size of the window when it appears on a blank simulation screen. */ this.setLocation(400, 160); this.setSize(gui.getDesktopPane().getWidth() - 400, 240); diff --git a/tools/cooja/java/se/sics/cooja/plugins/Notes.java b/tools/cooja/java/se/sics/cooja/plugins/Notes.java index 942e10e22..3d131eaa6 100644 --- a/tools/cooja/java/se/sics/cooja/plugins/Notes.java +++ b/tools/cooja/java/se/sics/cooja/plugins/Notes.java @@ -56,7 +56,7 @@ import se.sics.cooja.PluginType; import se.sics.cooja.Simulation; import se.sics.cooja.VisPlugin; -@ClassDescription("Notes...") +@ClassDescription("Notes") @PluginType(PluginType.SIM_STANDARD_PLUGIN) public class Notes extends VisPlugin { private static final long serialVersionUID = 1L; @@ -85,7 +85,7 @@ public class Notes extends VisPlugin { public void mousePressed(MouseEvent e) { if (e.isPopupTrigger()) { popup.show(Notes.this, e.getX(), e.getY()); - } + } } public void mouseReleased(MouseEvent e) { if (e.isPopupTrigger()) { @@ -109,11 +109,11 @@ public class Notes extends VisPlugin { public String getNotes() { return notes.getText(); } - + public void setNotes(String text) { this.notes.setText(text); } - + private void setDecorationsVisible(boolean visible) { if (!(Notes.this.getUI() instanceof BasicInternalFrameUI)) { return; @@ -124,7 +124,7 @@ public class Notes extends VisPlugin { ui.getNorthPane().setPreferredSize(null); } else { ui.getNorthPane().setPreferredSize(new Dimension(0,0)); - } + } Notes.this.revalidate(); SwingUtilities.invokeLater(new Runnable() { @@ -135,7 +135,7 @@ public class Notes extends VisPlugin { decorationsVisible = visible; } - + public Collection getConfigXML() { ArrayList config = new ArrayList(); Element element; diff --git a/tools/cooja/java/se/sics/cooja/plugins/RadioLogger.java b/tools/cooja/java/se/sics/cooja/plugins/RadioLogger.java index c5cd4f345..421159c6e 100644 --- a/tools/cooja/java/se/sics/cooja/plugins/RadioLogger.java +++ b/tools/cooja/java/se/sics/cooja/plugins/RadioLogger.java @@ -102,8 +102,8 @@ import se.sics.cooja.util.StringUtils; * * @author Fredrik Osterlind */ -@ClassDescription("Radio messages...") -@PluginType(PluginType.SIM_PLUGIN) +@ClassDescription("Radio messages") +@PluginType(PluginType.SIM_PLUGIN) public class RadioLogger extends VisPlugin { private static Logger logger = Logger.getLogger(RadioLogger.class); private static final long serialVersionUID = -6927091711697081353L; @@ -115,7 +115,7 @@ public class RadioLogger extends VisPlugin { private JSplitPane splitPane; private JTextPane verboseBox = null; - + private final static String[] COLUMN_NAMES = { "Time", "From", @@ -139,7 +139,7 @@ public class RadioLogger extends VisPlugin { public RadioLogger(final Simulation simulationToControl, final GUI gui) { super("Radio messages", gui); setLayout(new BorderLayout()); - + simulation = simulationToControl; radioMedium = simulation.getRadioMedium(); @@ -149,14 +149,14 @@ public class RadioLogger extends VisPlugin { JMenu editMenu = new JMenu("Edit"); JMenu analyzerMenu = new JMenu("Analyzer"); JMenu payloadMenu = new JMenu("Payload"); - + menuBar.add(fileMenu); menuBar.add(editMenu); menuBar.add(analyzerMenu); menuBar.add(payloadMenu); - + this.setJMenuBar(menuBar); - + ArrayList lowpanAnalyzers = new ArrayList(); lowpanAnalyzers.add(new IEEE802154Analyzer(false)); lowpanAnalyzers.add(new IPHCPacketAnalyzer()); @@ -297,7 +297,7 @@ public class RadioLogger extends VisPlugin { public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_SPACE) { showInAllAction.actionPerformed(null); - } else if (e.getKeyCode() == KeyEvent.VK_F && + } else if (e.getKeyCode() == KeyEvent.VK_F && (e.getModifiers() & KeyEvent.CTRL_MASK) != 0) { searchField.setVisible(true); searchField.requestFocus(); @@ -333,14 +333,14 @@ public class RadioLogger extends VisPlugin { editMenu.add(new JMenuItem(copyAction)); editMenu.add(new JSeparator()); editMenu.add(new JMenuItem(clearAction)); - + payloadMenu.add(new JMenuItem(aliasAction)); - + fileMenu.add(new JMenuItem(saveAction)); - + JPopupMenu popupMenu = new JPopupMenu(); - + JMenu focusMenu = new JMenu("Show in"); focusMenu.add(new JMenuItem(showInAllAction)); focusMenu.addSeparator(); @@ -349,7 +349,7 @@ public class RadioLogger extends VisPlugin { popupMenu.add(focusMenu); //a group of radio button menu items - + ButtonGroup group = new ButtonGroup(); JRadioButtonMenuItem rbMenuItem = new JRadioButtonMenuItem( createAnalyzerAction("No Analyzer", "none", null, true)); @@ -366,7 +366,7 @@ public class RadioLogger extends VisPlugin { group.add(rbMenuItem); analyzerMenu.add(rbMenuItem); - + /* Load additional analyzers specified by projects (cooja.config) */ String[] projectAnalyzerSuites = gui.getProjectConfig().getStringArrayValue(RadioLogger.class, "ANALYZERS"); @@ -389,17 +389,17 @@ public class RadioLogger extends VisPlugin { } } } - + dataTable.setComponentPopupMenu(popupMenu); dataTable.setFillsViewportHeight(true); verboseBox = new JTextPane(); - verboseBox.setContentType("text/html"); + verboseBox.setContentType("text/html"); verboseBox.setEditable(false); verboseBox.setComponentPopupMenu(popupMenu); /* Search text field */ - searchField.setVisible(false); + searchField.setVisible(false); searchField.addKeyListener(new KeyAdapter() { public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ENTER) { @@ -413,14 +413,14 @@ public class RadioLogger extends VisPlugin { } } }); - + splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, new JScrollPane(dataTable), new JScrollPane(verboseBox)); splitPane.setOneTouchExpandable(true); splitPane.setDividerLocation(150); add(BorderLayout.NORTH, searchField); add(BorderLayout.CENTER, splitPane); - + TableColumnAdjuster adjuster = new TableColumnAdjuster(dataTable); adjuster.setDynamicAdjustment(true); adjuster.packColumns(); @@ -455,7 +455,7 @@ public class RadioLogger extends VisPlugin { dataTable.scrollRectToVisible(dataTable.getCellRect(dataTable.getRowCount() - 1, 0, true)); } setTitle("Radio messages: " + dataTable.getRowCount() + " messages seen"); - } + } }); } }); @@ -476,7 +476,7 @@ public class RadioLogger extends VisPlugin { if (row < 0) { row = 0; } - + if (!reverse) { row++; } else { @@ -502,10 +502,10 @@ public class RadioLogger extends VisPlugin { } searchField.setBackground(Color.RED); } - + /** * Selects a logged radio packet close to the given time. - * + * * @param time Start time */ public void trySelectTime(final long time) { @@ -520,9 +520,9 @@ public class RadioLogger extends VisPlugin { return; } } - }); + }); } - + private void prepareDataString(RadioConnectionLog conn) { byte[] data; if (conn.packet == null) { @@ -564,7 +564,7 @@ public class RadioLogger extends VisPlugin { conn.data = data.length + ": 0x" + StringUtils.toHex(data, 4); } } - + private boolean analyzePacket(PacketAnalyzer.Packet packet, StringBuffer brief, StringBuffer verbose) { if (analyzers == null) return false; try { @@ -688,7 +688,7 @@ public class RadioLogger extends VisPlugin { } return true; } - + private class RadioConnectionLog { long startTime; long endTime; @@ -697,7 +697,7 @@ public class RadioLogger extends VisPlugin { String data = null; String tooltip = null; - + public String toString() { if (data == null) { RadioLogger.this.prepareDataString(this); @@ -765,7 +765,7 @@ public class RadioLogger extends VisPlugin { } } }; - + private Action copyAction = new AbstractAction("Copy selected") { private static final long serialVersionUID = 8412062977916108054L; @@ -786,7 +786,7 @@ public class RadioLogger extends VisPlugin { clipboard.setContents(stringSelection, null); } }; - + private Action copyAllAction = new AbstractAction("Copy all") { private static final long serialVersionUID = 1905586689441157304L; @@ -805,7 +805,7 @@ public class RadioLogger extends VisPlugin { clipboard.setContents(stringSelection, null); } }; - + private Action saveAction = new AbstractAction("Save to file...") { private static final long serialVersionUID = -3942984643211482179L; @@ -865,7 +865,7 @@ public class RadioLogger extends VisPlugin { if (!(p instanceof TimeLine)) { continue; } - + /* Select simulation time */ TimeLine plugin = (TimeLine) p; plugin.trySelectTime(time); @@ -885,7 +885,7 @@ public class RadioLogger extends VisPlugin { if (!(p instanceof LogListener)) { continue; } - + /* Select simulation time */ LogListener plugin = (LogListener) p; plugin.trySelectTime(time); @@ -918,7 +918,7 @@ public class RadioLogger extends VisPlugin { } String alias = (String) JOptionPane.showInputDialog( - GUI.getTopParentContainer(), + GUI.getTopParentContainer(), "Enter alias for all packets with identical payload.\n" + "An empty string removes the current alias.\n\n" + connections.get(selectedRow).data + "\n", @@ -940,7 +940,7 @@ public class RadioLogger extends VisPlugin { /* Remove current alias */ if (alias.equals("")) { aliases.remove(connections.get(selectedRow).data); - + /* Should be null if empty */ if (aliases.isEmpty()) { aliases = null; @@ -954,7 +954,7 @@ public class RadioLogger extends VisPlugin { repaint(); } }; - + public String getConnectionsString() { StringBuilder sb = new StringBuilder(); RadioConnectionLog[] cs = connections.toArray(new RadioConnectionLog[0]); @@ -963,7 +963,7 @@ public class RadioLogger extends VisPlugin { } return sb.toString(); }; - + public void saveConnectionsToFile(String fileName) { StringUtils.saveToFile(new File(fileName), getConnectionsString()); }; diff --git a/tools/cooja/java/se/sics/cooja/plugins/ScriptRunner.java b/tools/cooja/java/se/sics/cooja/plugins/ScriptRunner.java index 96dc544fd..93e5e7fb4 100644 --- a/tools/cooja/java/se/sics/cooja/plugins/ScriptRunner.java +++ b/tools/cooja/java/se/sics/cooja/plugins/ScriptRunner.java @@ -87,7 +87,7 @@ import se.sics.cooja.VisPlugin; import se.sics.cooja.dialogs.MessageList; import se.sics.cooja.util.StringUtils; -@ClassDescription("Simulation script editor...") +@ClassDescription("Simulation script editor") @PluginType(PluginType.SIM_PLUGIN) public class ScriptRunner extends VisPlugin { private static final long serialVersionUID = 7614358340336799109L; diff --git a/tools/cooja/java/se/sics/cooja/plugins/SimControl.java b/tools/cooja/java/se/sics/cooja/plugins/SimControl.java index 483acdf6b..309bda1b4 100644 --- a/tools/cooja/java/se/sics/cooja/plugins/SimControl.java +++ b/tools/cooja/java/se/sics/cooja/plugins/SimControl.java @@ -63,7 +63,7 @@ import se.sics.cooja.VisPlugin; * * @author Fredrik Osterlind */ -@ClassDescription("Simulation control...") +@ClassDescription("Simulation control") @PluginType(PluginType.SIM_STANDARD_PLUGIN) public class SimControl extends VisPlugin implements HasQuickHelp { private static final int LABEL_UPDATE_INTERVAL = 150; diff --git a/tools/cooja/java/se/sics/cooja/plugins/TimeLine.java b/tools/cooja/java/se/sics/cooja/plugins/TimeLine.java index b70db5093..a392bc948 100644 --- a/tools/cooja/java/se/sics/cooja/plugins/TimeLine.java +++ b/tools/cooja/java/se/sics/cooja/plugins/TimeLine.java @@ -56,7 +56,6 @@ import java.util.Observer; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.Box; -import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JCheckBoxMenuItem; import javax.swing.JComboBox; @@ -70,7 +69,6 @@ import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; -import javax.swing.JSeparator; import javax.swing.JSlider; import javax.swing.JSplitPane; import javax.swing.JToolTip; @@ -107,7 +105,7 @@ import se.sics.cooja.motes.AbstractEmulatedMote; * * @author Fredrik Osterlind */ -@ClassDescription("Timeline...") +@ClassDescription("Timeline") @PluginType(PluginType.SIM_STANDARD_PLUGIN) public class TimeLine extends VisPlugin implements HasQuickHelp { private static final long serialVersionUID = -883154261246961973L; @@ -177,16 +175,16 @@ public class TimeLine extends VisPlugin implements HasQuickHelp { JMenu eventsMenu = new JMenu("Events"); JMenu viewMenu = new JMenu("View"); JMenu zoomMenu = new JMenu("Zoom"); - + menuBar.add(fileMenu); menuBar.add(editMenu); menuBar.add(viewMenu); menuBar.add(zoomMenu); menuBar.add(eventsMenu); menuBar.add(motesMenu); - + this.setJMenuBar(menuBar); - + motesMenu.add(new JMenuItem(addMoteAction)); zoomMenu.add(new JMenuItem(zoomInAction)); zoomMenu.add(new JMenuItem(zoomOutAction)); @@ -203,7 +201,7 @@ public class TimeLine extends VisPlugin implements HasQuickHelp { return radioChannels; } }); - + fileMenu.add(new JMenuItem(saveDataAction)); fileMenu.add(new JMenuItem(statisticsAction)); editMenu.add(new JMenuItem(clearAction)); @@ -270,14 +268,14 @@ public class TimeLine extends VisPlugin implements HasQuickHelp { } }); eventsMenu.add(eventCheckBox); - + /* Box: events to observe */ - + eventCheckboxes = Box.createVerticalBox(); /* eventCheckboxes.add(new JButton(addMoteAction)); eventCheckboxes.add(new JSeparator()); - + JCheckBox eventCheckBox; eventCheckBox = createEventCheckbox("Radio RX/TX", "Show radio transmissions, receptions, and collisions"); eventCheckBox.setSelected(showRadioRXTX); @@ -347,7 +345,7 @@ public class TimeLine extends VisPlugin implements HasQuickHelp { JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); timelineScrollPane.getHorizontalScrollBar().setUnitIncrement(50); - + timelineMoteRuler = new MoteRuler(); timelineScrollPane.setRowHeaderView(timelineMoteRuler); timelineScrollPane.setBackground(Color.WHITE); @@ -420,7 +418,7 @@ public class TimeLine extends VisPlugin implements HasQuickHelp { timer.start(); } }); - + /* XXX HACK: here we set the position and size of the window when it appears on a blank simulation screen. */ this.setLocation(0, gui.getDesktopPane().getHeight() - 166); this.setSize(gui.getDesktopPane().getWidth(), 166); @@ -2513,5 +2511,5 @@ public class TimeLine extends VisPlugin implements HasQuickHelp { "
Shows LED state: red, green, and blue. (Assumes all mote types have exactly three LEDs.)" + "

Watchpoints" + "
Shows triggered watchpoints, currently only supported by MSPSim-based motes. To add watchpoints, use the Msp Code Watcher plugin."; - } + } } diff --git a/tools/cooja/java/se/sics/cooja/plugins/Visualizer.java b/tools/cooja/java/se/sics/cooja/plugins/Visualizer.java index 320942fef..b95d8ac50 100644 --- a/tools/cooja/java/se/sics/cooja/plugins/Visualizer.java +++ b/tools/cooja/java/se/sics/cooja/plugins/Visualizer.java @@ -128,7 +128,7 @@ import se.sics.cooja.plugins.skins.UDGMVisualizerSkin; * @see UDGMVisualizerSkin * @author Fredrik Osterlind */ -@ClassDescription("Network...") +@ClassDescription("Network") @PluginType(PluginType.SIM_STANDARD_PLUGIN) public class Visualizer extends VisPlugin implements HasQuickHelp { private static final long serialVersionUID = 1L;