Contiki 3.x
res_config.c
1 /**
2  * @file res_config
3  * Config ressource
4  * Serves the current config, and supports setting the config.
5  * Arthur Fabre 2015
6  */
7 
8 #include "er-server.h"
9 #include "rest-engine.h"
10 #include "er-coap.h"
11 #include "pb_decode.h"
12 #include "pb_encode.h"
13 #include "settings.pb.h"
14 #include "store.h"
15 #include <inttypes.h>
16 #include <stdlib.h>
17 #include <sampler.h>
18 
19 #define DEBUG_ON
20 #include "debug.h"
21 
22 /**
23  * Get handler for Config.
24  * Returns the curent configuration.
25  */
26 static void res_get_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset);
27 
28 /**
29  * Post handler for Config.
30  * Sets the current config.
31  */
32 static void res_post_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset);
33 
34 /**
35  * Buffer to store a config. Used for both get and post - requests will fail if they are interleaved.
36  */
37 static uint8_t config_buffer[SensorConfig_size];
38 
39 /**
40  * Length of the configuration buffer.
41  */
42 static uint8_t config_len;
43 
44 /**
45  * Config ressource.
46  */
47 RESOURCE(res_config, "Config", res_get_handler, res_post_handler, NULL, NULL);
48 
49 void res_get_handler(void* request, void* response, uint8_t *payload_buffer, uint16_t preferred_size, int32_t *offset) {
50  uint8_t payload_len;
51  int16_t current_offset = *offset;
52 
53  DEBUG("Serving request! Offset %d, PrefSize %d\n", current_offset, preferred_size);
54 
55  // Only get data if this is the first request of a blockwise transfer
56  if (current_offset == 0) {
57 
58  config_len = store_get_raw_config(config_buffer);
59 
60  if (!config_len) {
61  // 500 internal error
62  DEBUG("Unable to get config!\n");
63  REST.set_response_status(response, REST.status.INTERNAL_SERVER_ERROR);
64  return;
65  }
66  }
67 
68  DEBUG("Got config\n");
69 
70  // If whatever we have to send fits in one block, just send that
71  if (config_len - current_offset <= preferred_size) {
72  DEBUG("Request fits in a single block\n");
73 
74  payload_len = config_len - current_offset;
75 
76  // Indicates this is the last chunk
77  *offset = -1;
78 
79  // Otherwise copy data in chunks to the buffer
80  } else {
81  DEBUG("Request will be split into chunks\n");
82 
83  payload_len = preferred_size;
84 
85  *offset += preferred_size;
86  }
87 
88  memcpy(payload_buffer, config_buffer + current_offset, payload_len);
89 
90  REST.set_header_content_type(response, REST.type.APPLICATION_OCTET_STREAM);
91  REST.set_response_payload(response, payload_buffer, payload_len);
92 }
93 
94 void res_post_handler(void* request, void* response, uint8_t *payload_buffer, uint16_t preferred_size, int32_t *offset) {
95  uint8_t *incoming;
96  size_t incoming_len;
97  pb_istream_t pb_istream;
98  SensorConfig config;
99  coap_packet_t *coap_req = (coap_packet_t *)request;
100 
101  DEBUG("Config post request!\n");
102 
103  // Check there's a payload, and get it
104  if ((incoming_len = REST.get_request_payload(request, (const uint8_t **)&incoming))) {
105 
106  if (coap_req->block1_num * coap_req->block1_size + incoming_len <= SensorConfig_size) {
107 
108  DEBUG("Got config payload\n");
109 
110  // Store the payload in our static buffer
111  memcpy(config_buffer + coap_req->block1_num * coap_req->block1_size, incoming, incoming_len);
112  config_len = coap_req->block1_num * coap_req->block1_size + incoming_len;
113 
114  // If this is the last packet
115  if (!coap_req->block1_more) {
116 
117  DEBUG("Last (or only) config block, saving...\n");
118 
119  pb_istream = pb_istream_from_buffer(config_buffer, config_len);
120 
121  // Check it was sucesfully decoded.
122  if (!pb_decode_delimited(&pb_istream, SensorConfig_fields, &config)) {
123  DEBUG("Failed to decode config!\n");
124  REST.set_response_status(response, REST.status.BAD_REQUEST);
125  return;
126  }
127 
128  // Check the config is sane
129  if (!sampler_check_config(&config)) {
130  DEBUG("Unsane config!\n");
131  REST.set_response_status(response, REST.status.BAD_REQUEST);
132  return;
133  }
134 
135  // Save it
136  if (!store_save_config(&config)) {
137  DEBUG("Failed to save config!\n");
138  REST.set_response_status(response, REST.status.INTERNAL_SERVER_ERROR);
139  return;
140  }
141 
142  DEBUG("Config saved and decoded\n");
143 
145  }
146 
147  REST.set_response_status(response, REST.status.CHANGED);
148  coap_set_header_block1(response, coap_req->block1_num, 0, coap_req->block1_size);
149 
150  } else {
151  DEBUG("Config post request too big!\n");
152  REST.set_response_status(response, REST.status.REQUEST_ENTITY_TOO_LARGE);
153  return;
154  }
155 
156  } else {
157  DEBUG("Unable to get payload!\n");
158  REST.set_response_status(response, REST.status.BAD_REQUEST);
159  return;
160  }
161 }
bool store_save_config(SensorConfig *config)
Save the configuration to flash.
Definition: store.c:220
An abstraction layer for RESTful Web services (Erbium).
bool sampler_check_config(SensorConfig *config)
Check a config for sanity.
Definition: sampler.c:266
#define NULL
The null pointer.
Convenience layer for storing readings and the config.
uint8_t store_get_raw_config(uint8_t buffer[SensorConfig_size])
Get the configuration from the flash.
Definition: store.c:262
void sampler_refresh_config(void)
Get the Sampler to reload it's config from flash.
Definition: sampler.c:261
RESOURCE(res_routes,"Routes", res_get_handler, NULL, NULL, NULL)
Route resource.
An implementation of the Constrained Application Protocol (RFC).
Process that periodically takes samples from onboard sensors and stores them in flash.