Refactor GENERIC_RESOURCE macro

Now callback functions get the URI of the request, this allows to use a
single resource for multiple different URIs.
The is_json flag is now gone for the to-string function, instead the
macro has an is_str flag. If set this automagically produces quotes
around the string for json output.
Now from-string functions can return an error-code, 0 for success, -1
for error.
This commit is contained in:
Ralf Schlatterbeck 2016-02-26 07:54:05 +01:00
parent c7daa7c45d
commit c6165a3bcf
9 changed files with 106 additions and 68 deletions

View file

@ -105,6 +105,20 @@ json_parse_variable
return 0; return 0;
} }
static const char *get_uri (void *request)
{
static char buf [MAX_URI_STRING_LENGTH];
const char *uri;
size_t len = coap_get_header_uri_path (request, &uri);
if (len > sizeof (buf) - 1) {
*buf = '\0';
} else {
strncpy (buf, uri, len);
buf [len] = '\0';
}
return buf;
}
void generic_get_handler void generic_get_handler
( void *request ( void *request
, void *response , void *response
@ -112,13 +126,15 @@ void generic_get_handler
, uint16_t preferred_size , uint16_t preferred_size
, int32_t *offset , int32_t *offset
, char *name , char *name
, size_t (*to_str)(const char *name, uint8_t is_json, char *buf, size_t bsize) , int is_str
, size_t (*to_str)(const char *name, const char *uri, char *buf, size_t bsize)
) )
{ {
int success = 1; int success = 1;
char temp [100]; char temp [MAX_GET_STRING_LENGTH];
size_t len = 0; size_t len = 0;
unsigned int accept = -1; unsigned int accept = -1;
const char *uri = get_uri (request);
REST.get_header_accept (request, &accept); REST.get_header_accept (request, &accept);
if ( accept != -1 if ( accept != -1
@ -127,30 +143,40 @@ void generic_get_handler
) )
{ {
success = 0; success = 0;
REST.set_response_status(response, REST.status.NOT_ACCEPTABLE); REST.set_response_status (response, REST.status.NOT_ACCEPTABLE);
return; return;
} }
// TEXT format // TEXT format
if (accept == REST.type.APPLICATION_JSON) { if (accept == REST.type.APPLICATION_JSON) {
len += snprintf len += snprintf
(temp + len, sizeof (temp) - len, "{\n \"%s\" : ", name); ( temp + len
, sizeof (temp) - len
, "{\n \"%s\" : %s"
, name
, is_str ? "\"" : ""
);
if (len > sizeof (temp)) { if (len > sizeof (temp)) {
success = 0; success = 0;
goto out; goto out;
} }
len += to_str (name, 1, temp + len, sizeof (temp) - len); len += to_str (name, uri, temp + len, sizeof (temp) - len);
if (len > sizeof (temp)) { if (len > sizeof (temp)) {
success = 0; success = 0;
goto out; goto out;
} }
len += snprintf (temp + len, sizeof (temp) - len, "\n}\n"); len += snprintf
( temp + len
, sizeof (temp) - len
, "%s\n}\n"
, is_str ? "\"" : ""
);
if (len > sizeof (temp)) { if (len > sizeof (temp)) {
success = 0; success = 0;
goto out; goto out;
} }
} else { // TEXT Format } else { // TEXT Format
len += to_str (name, 0, temp + len, sizeof (temp) - len); len += to_str (name, uri, temp + len, sizeof (temp) - len);
if (len > sizeof (temp)) { if (len > sizeof (temp)) {
success = 0; success = 0;
goto out; goto out;
@ -166,7 +192,7 @@ void generic_get_handler
REST.set_response_payload (response, buffer, len); REST.set_response_payload (response, buffer, len);
out : out :
if (!success) { if (!success) {
REST.set_response_status(response, REST.status.BAD_REQUEST); REST.set_response_status (response, REST.status.BAD_REQUEST);
} }
} }
@ -177,7 +203,7 @@ void generic_put_handler
, uint16_t preferred_size , uint16_t preferred_size
, int32_t *offset , int32_t *offset
, char *name , char *name
, void (*from_str)(const char *name, const char *s) , int (*from_str)(const char *name, const char *uri, const char *s)
) )
{ {
int success = 1; int success = 1;
@ -185,9 +211,10 @@ void generic_put_handler
size_t len = 0; size_t len = 0;
const uint8_t *bytes = NULL; const uint8_t *bytes = NULL;
uint16_t c_ctype; uint16_t c_ctype;
const char *uri = get_uri (request);
REST.get_header_content_type (request, &c_ctype); REST.get_header_content_type (request, &c_ctype);
if (from_str && (len = coap_get_payload(request, &bytes))) { if (from_str && (len = coap_get_payload (request, &bytes))) {
if (c_ctype == REST.type.TEXT_PLAIN) { if (c_ctype == REST.type.TEXT_PLAIN) {
int l = MIN (len, sizeof (temp) - 1); int l = MIN (len, sizeof (temp) - 1);
temp [sizeof (temp) - 1] = 0; temp [sizeof (temp) - 1] = 0;
@ -199,14 +226,17 @@ void generic_put_handler
goto out; goto out;
} }
} }
from_str (name, temp); if (from_str (name, uri, temp) < 0) {
REST.set_response_status(response, REST.status.CHANGED); success = 0;
} else {
REST.set_response_status (response, REST.status.CHANGED);
}
} else { } else {
success = 0; success = 0;
} }
out: out:
if (!success) { if (!success) {
REST.set_response_status(response, REST.status.BAD_REQUEST); REST.set_response_status (response, REST.status.BAD_REQUEST);
} }
} }

View file

@ -50,6 +50,9 @@
#define STR__(s) #s #define STR__(s) #s
#define STR_(s) STR__(s) #define STR_(s) STR__(s)
#define MAX_GET_STRING_LENGTH 100
#define MAX_URI_STRING_LENGTH 30
/* /*
* A macro that extends the resource definition and also sets up the * A macro that extends the resource definition and also sets up the
* necessary handler function that calls format/parse routines that * necessary handler function that calls format/parse routines that
@ -60,7 +63,7 @@
* Yes, this *is* a hack. But I hate boilerplate code. * Yes, this *is* a hack. But I hate boilerplate code.
*/ */
#define GENERIC_RESOURCE(name, title, unit, fs, ts) \ #define GENERIC_RESOURCE(name, title, unit, is_str, fs, ts) \
static void name##_get_handler \ static void name##_get_handler \
( void *request \ ( void *request \
, void *response \ , void *response \
@ -70,7 +73,7 @@
) \ ) \
{ \ { \
generic_get_handler \ generic_get_handler \
(request, response, buffer, ps, offset, STR_(name), ts); \ (request, response, buffer, ps, offset, STR_(name), is_str, ts); \
} \ } \
static void name##_put_handler \ static void name##_put_handler \
( void *request \ ( void *request \
@ -134,7 +137,8 @@ extern void generic_get_handler
, uint16_t preferred_size , uint16_t preferred_size
, int32_t *offset , int32_t *offset
, char *name , char *name
, size_t (*to_str)(const char *name, uint8_t is_json, char *buf, size_t bsize) , int is_str
, size_t (*to_str)(const char *name, const char *uri, char *buf, size_t bsize)
); );
/** /**
@ -157,7 +161,7 @@ extern void generic_put_handler
, uint16_t preferred_size , uint16_t preferred_size
, int32_t *offset , int32_t *offset
, char *name , char *name
, void (*from_str)(const char *name, const char *s) , int (*from_str)(const char *name, const char *uri, const char *s)
); );
/* /*

View file

@ -17,7 +17,7 @@
#include "er-coap.h" #include "er-coap.h"
#include "generic_resource.h" #include "generic_resource.h"
size_t time_to_string (const char *name, uint8_t is_json, char *buf, size_t bs) size_t time_to_string (const char *name, const char *uri, char *buf, size_t bs)
{ {
struct timeval tv; struct timeval tv;
struct tm tm; struct tm tm;
@ -30,12 +30,10 @@ size_t time_to_string (const char *name, uint8_t is_json, char *buf, size_t bs)
return snprintf return snprintf
( buf ( buf
, bs , bs
, "%s%lu-%02u-%02u %02u:%02u:%02u %s%s" , "%lu-%02u-%02u %02u:%02u:%02u %s"
, is_json ? "\"" : ""
, 1900 + tm.tm_year , 1900 + tm.tm_year
, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec , tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec
, tm.tm_zone , tm.tm_zone
, is_json ? "\"" : ""
); );
} }
@ -43,6 +41,7 @@ GENERIC_RESOURCE \
( localtime ( localtime
, Local time , Local time
, formatted time , formatted time
, 1
, NULL , NULL
, time_to_string , time_to_string
); );
@ -51,6 +50,7 @@ GENERIC_RESOURCE \
( utc ( utc
, UTC , UTC
, formatted time , formatted time
, 1
, NULL , NULL
, time_to_string , time_to_string
); );

View file

@ -21,32 +21,30 @@
#include "er-coap.h" #include "er-coap.h"
#include "generic_resource.h" #include "generic_resource.h"
void timestamp_from_string (const char *name, const char *s) int timestamp_from_string (const char *name, const char *uri, const char *s)
{ {
struct timeval tv; struct timeval tv;
// FIXME: Platform has no strtoll (long long)? // FIXME: Platform has no strtoll (long long)?
tv.tv_sec = strtol (s, NULL, 10); tv.tv_sec = strtol (s, NULL, 10);
settimeofday (&tv, NULL); settimeofday (&tv, NULL);
return 0;
} }
size_t size_t
timestamp_to_string (const char *name, uint8_t is_json, char *buf, size_t bsize) timestamp_to_string (const char *name, const char *uri, char *buf, size_t bsize)
{ {
struct timeval tv; struct timeval tv;
char *fmt = "%ld";
if (is_json) {
fmt = "\"%ld\"";
}
gettimeofday (&tv, NULL); gettimeofday (&tv, NULL);
// FIXME: Platform doesn't seem to support long long printing // FIXME: Platform doesn't seem to support long long printing
// We get empty string // We get empty string
return snprintf (buf, bsize, fmt, (long)tv.tv_sec); return snprintf (buf, bsize, "%ld", (long)tv.tv_sec);
} }
GENERIC_RESOURCE GENERIC_RESOURCE
( timestamp ( timestamp
, Time , Time
, s , s
, 1
, timestamp_from_string , timestamp_from_string
, timestamp_to_string , timestamp_to_string
); );

View file

@ -17,13 +17,14 @@
#include "er-coap.h" #include "er-coap.h"
#include "generic_resource.h" #include "generic_resource.h"
void timezone_from_string (const char *name, const char *s) int timezone_from_string (const char *name, const char *uri, const char *s)
{ {
set_tz (s); set_tz (s);
return 0;
} }
size_t size_t
timezone_to_string (const char *name, uint8_t is_json, char *buf, size_t bsize) timezone_to_string (const char *name, const char *uri, char *buf, size_t bsize)
{ {
if (get_tz (buf, bsize) == NULL) { if (get_tz (buf, bsize) == NULL) {
*buf = '\0'; *buf = '\0';
@ -35,6 +36,7 @@ GENERIC_RESOURCE
( timezone ( timezone
, TZ , TZ
, s , s
, 1
, timezone_from_string , timezone_from_string
, timezone_to_string , timezone_to_string
); );

View file

@ -46,27 +46,25 @@ static uint8_t name_to_offset (const char * name)
} }
static size_t static size_t
color_to_string (const char *name, uint8_t is_json, char *buf, size_t bsize) color_to_string (const char *name, const char *uri, char *buf, size_t bsize)
{ {
char *fmt = "%d"; return snprintf (buf, bsize, "%d", color_rgb [name_to_offset (name)]);
if (is_json) {
fmt = "\"%d\"";
}
return snprintf (buf, bsize, fmt, color_rgb [name_to_offset (name)]);
} }
void color_from_string (const char *name, const char *s) int color_from_string (const char *name, const char *uri, const char *s)
{ {
color_rgb [name_to_offset (name)] = atoi (s); color_rgb [name_to_offset (name)] = atoi (s);
Driver.begin(); Driver.begin();
Driver.SetColor(color_rgb [0], color_rgb [1], color_rgb [2]); Driver.SetColor(color_rgb [0], color_rgb [1], color_rgb [2]);
Driver.end(); Driver.end();
return 0;
} }
GENERIC_RESOURCE GENERIC_RESOURCE
( red ( red
, RED_LED , RED_LED
, s , s
, 1
, color_from_string , color_from_string
, color_to_string , color_to_string
); );
@ -75,6 +73,7 @@ GENERIC_RESOURCE
( green ( green
, GREEN_LED , GREEN_LED
, s , s
, 1
, color_from_string , color_from_string
, color_to_string , color_to_string
); );
@ -83,6 +82,7 @@ GENERIC_RESOURCE
( blue ( blue
, BLUE_LED , BLUE_LED
, s , s
, 1
, color_from_string , color_from_string
, color_to_string , color_to_string
); );
@ -136,4 +136,3 @@ void loop (void)
} }
*/ */
} }

View file

@ -17,17 +17,18 @@
#include "generic_resource.h" #include "generic_resource.h"
#include "led_pwm.h" #include "led_pwm.h"
void pwm_from_string (const char *name, const char *s) int pwm_from_string (const char *name, const char *uri, const char *s)
{ {
uint32_t tmp = strtoul (s, NULL, 10); uint32_t tmp = strtoul (s, NULL, 10);
if (tmp > 255) { if (tmp > 255) {
tmp = 255; tmp = 255;
} }
pwm = tmp; pwm = tmp;
return 0;
} }
size_t size_t
pwm_to_string (const char *name, uint8_t is_json, char *buf, size_t bufsize) pwm_to_string (const char *name, const char *uri, char *buf, size_t bufsize)
{ {
return snprintf (buf, bufsize, "%d", pwm); return snprintf (buf, bufsize, "%d", pwm);
} }
@ -36,11 +37,12 @@ GENERIC_RESOURCE \
( led_pwm ( led_pwm
, LED PWM , LED PWM
, duty-cycle , duty-cycle
, 0
, pwm_from_string , pwm_from_string
, pwm_to_string , pwm_to_string
); );
void period_from_string (const char *name, const char *s) int period_from_string (const char *name, const char *uri, const char *s)
{ {
uint32_t tmp = (strtoul (s, NULL, 10) + 50) / 100; uint32_t tmp = (strtoul (s, NULL, 10) + 50) / 100;
if (tmp > 10) { if (tmp > 10) {
@ -50,10 +52,11 @@ void period_from_string (const char *name, const char *s)
tmp = 1; tmp = 1;
} }
period_100ms = tmp; period_100ms = tmp;
return 0;
} }
size_t size_t
period_to_string (const char *name, uint8_t is_json, char *buf, size_t bufsize) period_to_string (const char *name, const char *uri, char *buf, size_t bufsize)
{ {
return snprintf (buf, bufsize, "%d", period_100ms * 100); return snprintf (buf, bufsize, "%d", period_100ms * 100);
} }
@ -62,12 +65,13 @@ GENERIC_RESOURCE \
( led_period ( led_period
, LED Period , LED Period
, ms , ms
, 0
, period_from_string , period_from_string
, period_to_string , period_to_string
); );
size_t size_t
analog2_v (const char *name, uint8_t is_json, char *buf, size_t bufsize) analog2_v (const char *name, const char *uri, char *buf, size_t bufsize)
{ {
return snprintf return snprintf
(buf, bufsize, "%d.%03d", analog2_voltage / 1000, analog2_voltage % 1000); (buf, bufsize, "%d.%03d", analog2_voltage / 1000, analog2_voltage % 1000);
@ -77,12 +81,13 @@ GENERIC_RESOURCE \
( analog2_voltage ( analog2_voltage
, Analog 2 voltage , Analog 2 voltage
, V , V
, 0
, NULL , NULL
, analog2_v , analog2_v
); );
size_t size_t
analog5_v (const char *name, uint8_t is_json, char *buf, size_t bufsize) analog5_v (const char *name, const char *uri, char *buf, size_t bufsize)
{ {
return snprintf return snprintf
(buf, bufsize, "%d.%03d", analog5_voltage / 1000, analog5_voltage % 1000); (buf, bufsize, "%d.%03d", analog5_voltage / 1000, analog5_voltage % 1000);
@ -92,6 +97,7 @@ GENERIC_RESOURCE \
( analog5_voltage ( analog5_voltage
, Analog 5 voltage , Analog 5 voltage
, V , V
, 0
, NULL , NULL
, analog5_v , analog5_v
); );

View file

@ -77,16 +77,12 @@ static uint8_t name_to_offset (const char * name)
} }
static size_t static size_t
color_to_string (const char *name, uint8_t is_json, char *buf, size_t bsize) color_to_string (const char *name, const char *uri, char *buf, size_t bsize)
{ {
char *fmt = "%d"; return snprintf (buf, bsize, "%d", color_rgb [name_to_offset (name)]);
if (is_json) {
fmt = "\"%d\"";
}
return snprintf (buf, bsize, fmt, color_rgb [name_to_offset (name)]);
} }
void color_from_string (const char *name, const char *s) int color_from_string (const char *name, const char *uri, const char *s)
{ {
color_rgb [name_to_offset (name)] = atoi (s); color_rgb [name_to_offset (name)] = atoi (s);
led_strip_begin (); led_strip_begin ();
@ -95,12 +91,14 @@ void color_from_string (const char *name, const char *s)
printf ("Set to R:%d G:%d B:%d\n" printf ("Set to R:%d G:%d B:%d\n"
, color_rgb [0], color_rgb [1], color_rgb [2]) , color_rgb [0], color_rgb [1], color_rgb [2])
; ;
return 0;
} }
GENERIC_RESOURCE GENERIC_RESOURCE
( red ( red
, RED_LED , RED_LED
, s , s
, 1
, color_from_string , color_from_string
, color_to_string , color_to_string
); );
@ -109,6 +107,7 @@ GENERIC_RESOURCE
( green ( green
, GREEN_LED , GREEN_LED
, s , s
, 1
, color_from_string , color_from_string
, color_to_string , color_to_string
); );
@ -117,6 +116,7 @@ GENERIC_RESOURCE
( blue ( blue
, BLUE_LED , BLUE_LED
, s , s
, 1
, color_from_string , color_from_string
, color_to_string , color_to_string
); );

View file

@ -77,74 +77,73 @@ char server_resource [20] = "led/G";
int interval = 10; /* Retransmit interval after no change in value */ int interval = 10; /* Retransmit interval after no change in value */
static size_t static size_t
ip_to_string (const char *name, uint8_t is_json, char *buf, size_t bsize) ip_to_string (const char *name, const char *uri, char *buf, size_t bsize)
{ {
#define IP(x) UIP_NTOHS(server_ipaddr.u16[x]) #define IP(x) UIP_NTOHS(server_ipaddr.u16[x])
char *q = "\"";
if (!is_json) {
q = "";
}
return snprintf return snprintf
( buf, bsize, "%s%x:%x:%x:%x:%x:%x:%x:%x%s" ( buf, bsize, "%x:%x:%x:%x:%x:%x:%x:%x"
, q, IP(0), IP(1), IP(2), IP(3), IP(4), IP(5), IP(6), IP(7), q , IP(0), IP(1), IP(2), IP(3), IP(4), IP(5), IP(6), IP(7)
); );
} }
void ip_from_string (const char *name, const char *s) int ip_from_string (const char *name, const char *uri, const char *s)
{ {
/* Returns 1 if successful, only copy valid address */ /* Returns 1 if successful, only copy valid address */
if (uiplib_ip6addrconv (s, &tmp_addr)) { if (uiplib_ip6addrconv (s, &tmp_addr)) {
uip_ip6addr_copy (&server_ipaddr, &tmp_addr); uip_ip6addr_copy (&server_ipaddr, &tmp_addr);
return 0;
} }
return -1;
} }
GENERIC_RESOURCE GENERIC_RESOURCE
( server_ip ( server_ip
, ip , ip
, ipv6_address , ipv6_address
, 1
, ip_from_string , ip_from_string
, ip_to_string , ip_to_string
); );
static size_t static size_t
resource_to_string (const char *name, uint8_t is_json, char *buf, size_t bsize) resource_to_string (const char *name, const char *uri, char *buf, size_t bsize)
{ {
char *q = "\""; return snprintf (buf, bsize, "%s", server_resource);
if (!is_json) {
q = "";
}
return snprintf (buf, bsize, "%s%s%s", q, server_resource, q);
} }
void resource_from_string (const char *name, const char *s) int resource_from_string (const char *name, const char *uri, const char *s)
{ {
strncpy (server_resource, s, sizeof (server_resource)); strncpy (server_resource, s, sizeof (server_resource));
server_resource [sizeof (server_resource) - 1] = 0; server_resource [sizeof (server_resource) - 1] = 0;
return 0;
} }
GENERIC_RESOURCE GENERIC_RESOURCE
( server_resource ( server_resource
, led-resource , led-resource
, resource-name , resource-name
, 1
, resource_from_string , resource_from_string
, resource_to_string , resource_to_string
); );
static size_t static size_t
interval_to_string (const char *name, uint8_t is_json, char *buf, size_t bsize) interval_to_string (const char *name, const char *uri, char *buf, size_t bsize)
{ {
return snprintf (buf, bsize, "%d", interval); return snprintf (buf, bsize, "%d", interval);
} }
void interval_from_string (const char *name, const char *s) int interval_from_string (const char *name, const char *uri, const char *s)
{ {
interval = atoi (s); interval = atoi (s);
return 0;
} }
GENERIC_RESOURCE GENERIC_RESOURCE
( interval ( interval
, interval , interval
, s , s
, 0
, interval_from_string , interval_from_string
, interval_to_string , interval_to_string
); );