mirror of
https://github.com/oliverschmidt/contiki.git
synced 2024-12-21 19:29:18 +00:00
* Simple JSON parser and generator.
* Simple HTTP webservice with support for both receiving and sending HTTP requests. * json-ws example that optionally push sensor data to COSM over IPv6.
This commit is contained in:
parent
f177284a73
commit
51b73127e9
1
apps/httpd-ws/Makefile.httpd-ws
Normal file
1
apps/httpd-ws/Makefile.httpd-ws
Normal file
@ -0,0 +1 @@
|
||||
httpd-ws_src = httpd-ws.c
|
478
apps/httpd-ws/httpd-ws.c
Normal file
478
apps/httpd-ws/httpd-ws.c
Normal file
@ -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 <adam@sics.se>
|
||||
* Niclas Finne <nfi@sics.se>
|
||||
* Joakim Eriksson <joakime@sics.se>
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#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[] =
|
||||
"<html><body><h1>Page not found</h1></body></html>";
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* 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();
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
125
apps/httpd-ws/httpd-ws.h
Normal file
125
apps/httpd-ws/httpd-ws.h
Normal file
@ -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 <adam@sics.se>
|
||||
* Niclas Finne <nfi@sics.se>
|
||||
* Joakim Eriksson <joakime@sics.se>
|
||||
*/
|
||||
|
||||
#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__ */
|
1
apps/json/Makefile.json
Normal file
1
apps/json/Makefile.json
Normal file
@ -0,0 +1 @@
|
||||
json_src = jsonparse.c jsontree.c
|
70
apps/json/json.h
Normal file
70
apps/json/json.h
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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
|
||||
* A few JSON defines used for parsing and generating JSON.
|
||||
* \author
|
||||
* Niclas Finne <nfi@sics.se>
|
||||
* Joakim Eriksson <joakime@sics.se>
|
||||
*/
|
||||
|
||||
#ifndef __JSON_H__
|
||||
#define __JSON_H__
|
||||
|
||||
#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'
|
||||
|
||||
#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__ */
|
257
apps/json/jsonparse.c
Normal file
257
apps/json/jsonparse.c
Normal file
@ -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 <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/*--------------------------------------------------------------------*/
|
||||
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;
|
||||
}
|
||||
/*--------------------------------------------------------------------*/
|
91
apps/json/jsonparse.h
Normal file
91
apps/json/jsonparse.h
Normal file
@ -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__ */
|
275
apps/json/jsontree.c
Normal file
275
apps/json/jsontree.c
Normal file
@ -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 <nfi@sics.se>
|
||||
* Joakim Eriksson <joakime@sics.se>
|
||||
*/
|
||||
|
||||
#include "contiki.h"
|
||||
#include "jsontree.h"
|
||||
#include "jsonparse.h"
|
||||
#include <string.h>
|
||||
|
||||
#define DEBUG 0
|
||||
#if DEBUG
|
||||
#include <stdio.h>
|
||||
#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;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
135
apps/json/jsontree.h
Normal file
135
apps/json/jsontree.h
Normal file
@ -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 <nfi@sics.se>
|
||||
* Joakim Eriksson <joakime@sics.se>
|
||||
*/
|
||||
|
||||
#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__ */
|
30
examples/ipv6/json-ws/Makefile
Normal file
30
examples/ipv6/json-ws/Makefile
Normal file
@ -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
|
106
examples/ipv6/json-ws/README-COSM.txt
Normal file
106
examples/ipv6/json-ws/README-COSM.txt
Normal file
@ -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=<TSP_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 "<your-key>" and
|
||||
"<your-feed>" 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":"<insert your COSM API key>",
|
||||
"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).
|
351
examples/ipv6/json-ws/gogoc.conf
Normal file
351
examples/ipv6/json-ws/gogoc.conf
Normal file
@ -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=<your_userid>
|
||||
# passwd=<your_password>
|
||||
#
|
||||
userid=<change to your userid>
|
||||
passwd=<change to your password>
|
||||
|
||||
#
|
||||
# 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|router>
|
||||
#
|
||||
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=<integer>
|
||||
#
|
||||
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=<interface name>
|
||||
#
|
||||
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=<yes|no>
|
||||
# retry_delay=<integer: 0..3600>
|
||||
# retry_delay_max=<integer: 0..3600>
|
||||
#
|
||||
# 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=<yes|no>
|
||||
# keepalive_interval=<integer>
|
||||
#
|
||||
# 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=<auto|A.B.C.D (valid ipv4 address)>
|
||||
# client_v6=<auto|X:X::X:X (valid ipv6 address)>
|
||||
# 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=<checktunnel|freebsd|netbsd|openbsd|linux|windows|darwin|cisco|solaris>
|
||||
#
|
||||
# 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=<yes|no>
|
||||
#
|
||||
# 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=<file_name>
|
||||
#
|
||||
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=<file_name>
|
||||
#
|
||||
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=<yes|no>
|
||||
#
|
||||
always_use_same_server=no
|
||||
|
||||
|
||||
#################################### LOGGING ##################################
|
||||
|
||||
#
|
||||
# Log Verbosity Configuration:
|
||||
# The format is 'log_<destination>=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=<file_name>
|
||||
#
|
||||
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
|
||||
# '.<timestamp>' inserted before the file extension. If the file does not
|
||||
# have an extension, '.<timestamp>' 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=<yes|no>
|
||||
#
|
||||
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=<yes|no>
|
||||
#
|
||||
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=<USER|LOCAL[0-7]>
|
||||
#
|
||||
syslog_facility=USER
|
||||
|
||||
|
||||
# end of gogoc.conf
|
||||
#------------------------------------------------------------------------------
|
154
examples/ipv6/json-ws/json-ws-udp.c
Normal file
154
examples/ipv6/json-ws/json-ws-udp.c
Normal file
@ -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 = <server-udp-port>
|
||||
* host = <ipv6-server-address>
|
||||
*
|
||||
* \author
|
||||
* Niclas Finne <nfi@sics.se>
|
||||
* Joakim Eriksson <joakime@sics.se>
|
||||
*/
|
||||
|
||||
#include "contiki.h"
|
||||
#include "httpd-ws.h"
|
||||
#include "jsontree.h"
|
||||
#include "jsonparse.h"
|
||||
#include "json-ws.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#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));
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
521
examples/ipv6/json-ws/json-ws.c
Normal file
521
examples/ipv6/json-ws/json-ws.c
Normal file
@ -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 <nfi@sics.se>
|
||||
* Joakim Eriksson <joakime@sics.se>
|
||||
* Joel Hoglund <joel@sics.se>
|
||||
*/
|
||||
|
||||
#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 <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
58
examples/ipv6/json-ws/json-ws.h
Normal file
58
examples/ipv6/json-ws/json-ws.h
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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 <nfi@sics.se>
|
||||
* Joakim Eriksson <joakime@sics.se>
|
||||
* Joel Hoglund <joel@sics.se>
|
||||
*/
|
||||
|
||||
#ifndef __JSON_WS_H__
|
||||
#define __JSON_WS_H__
|
||||
|
||||
#include "jsontree.h"
|
||||
|
||||
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__ */
|
79
examples/ipv6/json-ws/project-conf.h
Normal file
79
examples/ipv6/json-ws/project-conf.h
Normal file
@ -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__ */
|
30
examples/ipv6/json-ws/setcosm.py
Executable file
30
examples/ipv6/json-ws/setcosm.py
Executable file
@ -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 = "<your-key>"
|
||||
feed = "<your-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], "<host> <feed-id>"
|
||||
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
|
130
examples/ipv6/json-ws/websense-sky.c
Normal file
130
examples/ipv6/json-ws/websense-sky.c
Normal file
@ -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 <nfi@sics.se>
|
||||
* Joakim Eriksson <joakime@sics.se>
|
||||
* Joel Hoglund <joel@sics.se>
|
||||
*/
|
||||
|
||||
#include "contiki.h"
|
||||
#include "dev/leds.h"
|
||||
#include "dev/sht11-sensor.h"
|
||||
#include "jsontree.h"
|
||||
#include "json-ws.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#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();
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
136
examples/ipv6/json-ws/websense-z1.c
Normal file
136
examples/ipv6/json-ws/websense-z1.c
Normal file
@ -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 <nfi@sics.se>
|
||||
* Joakim Eriksson <joakime@sics.se>
|
||||
* Joel Hoglund <joel@sics.se>
|
||||
*/
|
||||
|
||||
#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 <stdio.h>
|
||||
#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();
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
Loading…
Reference in New Issue
Block a user