Thinger.io Client Library for ARM mbed platform. This is a generic library that provides a base class that can be used to other develop hardware specific libraries.

Fork of ThingerClient by Alvaro Luis Bustamante

Committer:
alvarolb
Date:
Thu Dec 24 13:18:08 2015 +0000
Revision:
0:b75d784c7c1a
Child:
1:d54f92accbc3
Initial Commit

Who changed what in which revision?

UserRevisionLine numberNew contents of line
alvarolb 0:b75d784c7c1a 1 // The MIT License (MIT)
alvarolb 0:b75d784c7c1a 2 //
alvarolb 0:b75d784c7c1a 3 // Copyright (c) 2015 THINGER LTD
alvarolb 0:b75d784c7c1a 4 // Author: alvarolb@gmail.com (Alvaro Luis Bustamante)
alvarolb 0:b75d784c7c1a 5 //
alvarolb 0:b75d784c7c1a 6 // Permission is hereby granted, free of charge, to any person obtaining a copy
alvarolb 0:b75d784c7c1a 7 // of this software and associated documentation files (the "Software"), to deal
alvarolb 0:b75d784c7c1a 8 // in the Software without restriction, including without limitation the rights
alvarolb 0:b75d784c7c1a 9 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
alvarolb 0:b75d784c7c1a 10 // copies of the Software, and to permit persons to whom the Software is
alvarolb 0:b75d784c7c1a 11 // furnished to do so, subject to the following conditions:
alvarolb 0:b75d784c7c1a 12 //
alvarolb 0:b75d784c7c1a 13 // The above copyright notice and this permission notice shall be included in
alvarolb 0:b75d784c7c1a 14 // all copies or substantial portions of the Software.
alvarolb 0:b75d784c7c1a 15 //
alvarolb 0:b75d784c7c1a 16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
alvarolb 0:b75d784c7c1a 17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
alvarolb 0:b75d784c7c1a 18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
alvarolb 0:b75d784c7c1a 19 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
alvarolb 0:b75d784c7c1a 20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
alvarolb 0:b75d784c7c1a 21 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
alvarolb 0:b75d784c7c1a 22 // THE SOFTWARE.
alvarolb 0:b75d784c7c1a 23
alvarolb 0:b75d784c7c1a 24 #ifndef THINGER_H
alvarolb 0:b75d784c7c1a 25 #define THINGER_H
alvarolb 0:b75d784c7c1a 26
alvarolb 0:b75d784c7c1a 27 #include "pson.h"
alvarolb 0:b75d784c7c1a 28 #include "thinger_map.hpp"
alvarolb 0:b75d784c7c1a 29 #include "thinger_resource.hpp"
alvarolb 0:b75d784c7c1a 30 #include "thinger_message.hpp"
alvarolb 0:b75d784c7c1a 31 #include "thinger_encoder.hpp"
alvarolb 0:b75d784c7c1a 32 #include "thinger_decoder.hpp"
alvarolb 0:b75d784c7c1a 33 #include "thinger_message.hpp"
alvarolb 0:b75d784c7c1a 34 #include "thinger_io.hpp"
alvarolb 0:b75d784c7c1a 35
alvarolb 0:b75d784c7c1a 36 #define KEEP_ALIVE_MILLIS 60000
alvarolb 0:b75d784c7c1a 37
alvarolb 0:b75d784c7c1a 38 namespace thinger{
alvarolb 0:b75d784c7c1a 39
alvarolb 0:b75d784c7c1a 40 using namespace protoson;
alvarolb 0:b75d784c7c1a 41
alvarolb 0:b75d784c7c1a 42 class thinger : public thinger_io{
alvarolb 0:b75d784c7c1a 43 public:
alvarolb 0:b75d784c7c1a 44 thinger() :
alvarolb 0:b75d784c7c1a 45 encoder(*this),
alvarolb 0:b75d784c7c1a 46 decoder(*this),
alvarolb 0:b75d784c7c1a 47 last_keep_alive(0),
alvarolb 0:b75d784c7c1a 48 keep_alive_response(true)
alvarolb 0:b75d784c7c1a 49 {
alvarolb 0:b75d784c7c1a 50 }
alvarolb 0:b75d784c7c1a 51
alvarolb 0:b75d784c7c1a 52 virtual ~thinger(){
alvarolb 0:b75d784c7c1a 53 }
alvarolb 0:b75d784c7c1a 54
alvarolb 0:b75d784c7c1a 55 private:
alvarolb 0:b75d784c7c1a 56 thinger_write_encoder encoder;
alvarolb 0:b75d784c7c1a 57 thinger_read_decoder decoder;
alvarolb 0:b75d784c7c1a 58 unsigned long last_keep_alive;
alvarolb 0:b75d784c7c1a 59 bool keep_alive_response;
alvarolb 0:b75d784c7c1a 60 thinger_map<thinger_resource> resources_;
alvarolb 0:b75d784c7c1a 61
alvarolb 0:b75d784c7c1a 62 protected:
alvarolb 0:b75d784c7c1a 63 /**
alvarolb 0:b75d784c7c1a 64 * Can be override to start reconnection process
alvarolb 0:b75d784c7c1a 65 */
alvarolb 0:b75d784c7c1a 66 virtual void disconnected(){
alvarolb 0:b75d784c7c1a 67 // stop all streaming resources after disconnect
alvarolb 0:b75d784c7c1a 68 if(thinger_resource::get_streaming_counter()>0) {
alvarolb 0:b75d784c7c1a 69 thinger_map<thinger_resource>::entry* current = resources_.begin();
alvarolb 0:b75d784c7c1a 70 while(current!=NULL){
alvarolb 0:b75d784c7c1a 71 current->value_.disable_streaming();
alvarolb 0:b75d784c7c1a 72 current = current->next_;
alvarolb 0:b75d784c7c1a 73 }
alvarolb 0:b75d784c7c1a 74 }
alvarolb 0:b75d784c7c1a 75 }
alvarolb 0:b75d784c7c1a 76
alvarolb 0:b75d784c7c1a 77 /**
alvarolb 0:b75d784c7c1a 78 * Stream a given resource
alvarolb 0:b75d784c7c1a 79 */
alvarolb 0:b75d784c7c1a 80 void stream_resource(thinger_resource& resource, thinger_message::signal_flag type){
alvarolb 0:b75d784c7c1a 81 thinger_message message;
alvarolb 0:b75d784c7c1a 82 message.set_stream_id(resource.get_stream_id());
alvarolb 0:b75d784c7c1a 83 message.set_signal_flag(type);
alvarolb 0:b75d784c7c1a 84 resource.fill_api_io(message.get_data());
alvarolb 0:b75d784c7c1a 85 send_message(message);
alvarolb 0:b75d784c7c1a 86 }
alvarolb 0:b75d784c7c1a 87
alvarolb 0:b75d784c7c1a 88 public:
alvarolb 0:b75d784c7c1a 89
alvarolb 0:b75d784c7c1a 90 thinger_resource & operator[](const char* res){
alvarolb 0:b75d784c7c1a 91 return resources_[res];
alvarolb 0:b75d784c7c1a 92 }
alvarolb 0:b75d784c7c1a 93
alvarolb 0:b75d784c7c1a 94 bool connect(const char* username, const char* device_id, const char* credential)
alvarolb 0:b75d784c7c1a 95 {
alvarolb 0:b75d784c7c1a 96 // reset keep alive status for each connection
alvarolb 0:b75d784c7c1a 97 keep_alive_response = true;
alvarolb 0:b75d784c7c1a 98
alvarolb 0:b75d784c7c1a 99 thinger_message message;
alvarolb 0:b75d784c7c1a 100 message.set_signal_flag(thinger_message::AUTH);
alvarolb 0:b75d784c7c1a 101 message.resources().add(username).add(device_id).add(credential);
alvarolb 0:b75d784c7c1a 102 if(!send_message(message)) return false;
alvarolb 0:b75d784c7c1a 103
alvarolb 0:b75d784c7c1a 104 thinger_message response;
alvarolb 0:b75d784c7c1a 105 return read_message(response) && response.get_signal_flag() == thinger_message::REQUEST_OK;
alvarolb 0:b75d784c7c1a 106 }
alvarolb 0:b75d784c7c1a 107
alvarolb 0:b75d784c7c1a 108 bool call_endpoint(const char* endpoint_name){
alvarolb 0:b75d784c7c1a 109 thinger_message message;
alvarolb 0:b75d784c7c1a 110 message.set_signal_flag(thinger_message::CALL_ENDPOINT);
alvarolb 0:b75d784c7c1a 111 message.resources().add(endpoint_name);
alvarolb 0:b75d784c7c1a 112 return send_message(message);
alvarolb 0:b75d784c7c1a 113 }
alvarolb 0:b75d784c7c1a 114
alvarolb 0:b75d784c7c1a 115 bool call_endpoint(const char* endpoint_name, pson& data){
alvarolb 0:b75d784c7c1a 116 thinger_message message;
alvarolb 0:b75d784c7c1a 117 message.set_signal_flag(thinger_message::CALL_ENDPOINT);
alvarolb 0:b75d784c7c1a 118 message.set_data(data);
alvarolb 0:b75d784c7c1a 119 message.resources().add(endpoint_name);
alvarolb 0:b75d784c7c1a 120 return send_message(message);
alvarolb 0:b75d784c7c1a 121 }
alvarolb 0:b75d784c7c1a 122
alvarolb 0:b75d784c7c1a 123 bool call_endpoint(const char* endpoint_name, thinger_resource& resource){
alvarolb 0:b75d784c7c1a 124 thinger_message message;
alvarolb 0:b75d784c7c1a 125 message.set_signal_flag(thinger_message::CALL_ENDPOINT);
alvarolb 0:b75d784c7c1a 126 message.resources().add(endpoint_name);
alvarolb 0:b75d784c7c1a 127 resource.fill_api_io(message.get_data());
alvarolb 0:b75d784c7c1a 128 return send_message(message);
alvarolb 0:b75d784c7c1a 129 }
alvarolb 0:b75d784c7c1a 130
alvarolb 0:b75d784c7c1a 131 /**
alvarolb 0:b75d784c7c1a 132 * Stream the given resource. This resource should be previously requested by an external process.
alvarolb 0:b75d784c7c1a 133 * Otherwise, the resource will not be streamed as nothing will be listening for it.
alvarolb 0:b75d784c7c1a 134 */
alvarolb 0:b75d784c7c1a 135 bool stream(thinger_resource& resource){
alvarolb 0:b75d784c7c1a 136 if(resource.stream_enabled()){
alvarolb 0:b75d784c7c1a 137 stream_resource(resource, thinger_message::STREAM_EVENT);
alvarolb 0:b75d784c7c1a 138 return true;
alvarolb 0:b75d784c7c1a 139 }
alvarolb 0:b75d784c7c1a 140 return false;
alvarolb 0:b75d784c7c1a 141 }
alvarolb 0:b75d784c7c1a 142
alvarolb 0:b75d784c7c1a 143 bool send_message(thinger_message& message)
alvarolb 0:b75d784c7c1a 144 {
alvarolb 0:b75d784c7c1a 145 thinger_encoder sink;
alvarolb 0:b75d784c7c1a 146 sink.encode(message);
alvarolb 0:b75d784c7c1a 147 encoder.pb_encode_varint(MESSAGE);
alvarolb 0:b75d784c7c1a 148 encoder.pb_encode_varint(sink.bytes_written());
alvarolb 0:b75d784c7c1a 149 encoder.encode(message);
alvarolb 0:b75d784c7c1a 150 return write(NULL, 0, true);
alvarolb 0:b75d784c7c1a 151 }
alvarolb 0:b75d784c7c1a 152
alvarolb 0:b75d784c7c1a 153 void handle(unsigned long current_time, bool bytes_available)
alvarolb 0:b75d784c7c1a 154 {
alvarolb 0:b75d784c7c1a 155 // handle input
alvarolb 0:b75d784c7c1a 156 if(bytes_available){
alvarolb 0:b75d784c7c1a 157 handle_input();
alvarolb 0:b75d784c7c1a 158 }
alvarolb 0:b75d784c7c1a 159
alvarolb 0:b75d784c7c1a 160 // handle keep alive
alvarolb 0:b75d784c7c1a 161 if(current_time-last_keep_alive>KEEP_ALIVE_MILLIS){
alvarolb 0:b75d784c7c1a 162 if(keep_alive_response){
alvarolb 0:b75d784c7c1a 163 last_keep_alive = current_time;
alvarolb 0:b75d784c7c1a 164 keep_alive_response = false;
alvarolb 0:b75d784c7c1a 165 encoder.pb_encode_varint(KEEP_ALIVE);
alvarolb 0:b75d784c7c1a 166 encoder.pb_encode_varint(0);
alvarolb 0:b75d784c7c1a 167 write(NULL, 0, true);
alvarolb 0:b75d784c7c1a 168 }else{
alvarolb 0:b75d784c7c1a 169 disconnected();
alvarolb 0:b75d784c7c1a 170 }
alvarolb 0:b75d784c7c1a 171 }
alvarolb 0:b75d784c7c1a 172
alvarolb 0:b75d784c7c1a 173 // handle streaming resources
alvarolb 0:b75d784c7c1a 174 if(thinger_resource::get_streaming_counter()>0){
alvarolb 0:b75d784c7c1a 175 thinger_map<thinger_resource>::entry* current = resources_.begin();
alvarolb 0:b75d784c7c1a 176 while(current!=NULL){
alvarolb 0:b75d784c7c1a 177 if(current->value_.stream_required(current_time)){
alvarolb 0:b75d784c7c1a 178 stream_resource(current->value_, thinger_message::STREAM_SAMPLE);
alvarolb 0:b75d784c7c1a 179 }
alvarolb 0:b75d784c7c1a 180 current = current->next_;
alvarolb 0:b75d784c7c1a 181 }
alvarolb 0:b75d784c7c1a 182 }
alvarolb 0:b75d784c7c1a 183 }
alvarolb 0:b75d784c7c1a 184
alvarolb 0:b75d784c7c1a 185 bool read_message(thinger_message& message){
alvarolb 0:b75d784c7c1a 186 uint8_t type = decoder.pb_decode_varint32();
alvarolb 0:b75d784c7c1a 187 switch (type){
alvarolb 0:b75d784c7c1a 188 case MESSAGE: {
alvarolb 0:b75d784c7c1a 189 size_t size = decoder.pb_decode_varint32();
alvarolb 0:b75d784c7c1a 190 decoder.decode(message, size);
alvarolb 0:b75d784c7c1a 191 }
alvarolb 0:b75d784c7c1a 192 break;
alvarolb 0:b75d784c7c1a 193 case KEEP_ALIVE: {
alvarolb 0:b75d784c7c1a 194 size_t size = decoder.pb_decode_varint32();
alvarolb 0:b75d784c7c1a 195 keep_alive_response = true;
alvarolb 0:b75d784c7c1a 196 }
alvarolb 0:b75d784c7c1a 197 return false;
alvarolb 0:b75d784c7c1a 198 default:
alvarolb 0:b75d784c7c1a 199 return false;
alvarolb 0:b75d784c7c1a 200 }
alvarolb 0:b75d784c7c1a 201 return true;
alvarolb 0:b75d784c7c1a 202 }
alvarolb 0:b75d784c7c1a 203
alvarolb 0:b75d784c7c1a 204 bool handle_input(){
alvarolb 0:b75d784c7c1a 205 thinger_message message;
alvarolb 0:b75d784c7c1a 206 if(read_message(message)){
alvarolb 0:b75d784c7c1a 207 handle_request_received(message);
alvarolb 0:b75d784c7c1a 208 }
alvarolb 0:b75d784c7c1a 209 return true;
alvarolb 0:b75d784c7c1a 210 }
alvarolb 0:b75d784c7c1a 211
alvarolb 0:b75d784c7c1a 212 private:
alvarolb 0:b75d784c7c1a 213
alvarolb 0:b75d784c7c1a 214 void handle_request_received(thinger_message& request)
alvarolb 0:b75d784c7c1a 215 {
alvarolb 0:b75d784c7c1a 216 thinger_message response(request);
alvarolb 0:b75d784c7c1a 217 if(!request.has_resource()){
alvarolb 0:b75d784c7c1a 218 response.set_signal_flag(thinger_message::REQUEST_ERROR);
alvarolb 0:b75d784c7c1a 219 }
alvarolb 0:b75d784c7c1a 220 else{
alvarolb 0:b75d784c7c1a 221 thinger_resource * thing_resource = NULL;
alvarolb 0:b75d784c7c1a 222 for(pson_array::iterator it = request.resources().begin(); it.valid(); it.next()){
alvarolb 0:b75d784c7c1a 223 if(!it.item().is_string()){
alvarolb 0:b75d784c7c1a 224 response.set_signal_flag(thinger_message::REQUEST_ERROR);
alvarolb 0:b75d784c7c1a 225 break;
alvarolb 0:b75d784c7c1a 226 }
alvarolb 0:b75d784c7c1a 227 const char* resource = it.item();
alvarolb 0:b75d784c7c1a 228
alvarolb 0:b75d784c7c1a 229 if(it.has_next()){
alvarolb 0:b75d784c7c1a 230 thing_resource = thing_resource == NULL ? resources_.find(resource) : thing_resource->find(resource);
alvarolb 0:b75d784c7c1a 231 if(thing_resource==NULL) {
alvarolb 0:b75d784c7c1a 232 response.set_signal_flag(thinger_message::REQUEST_ERROR);
alvarolb 0:b75d784c7c1a 233 break;
alvarolb 0:b75d784c7c1a 234 }
alvarolb 0:b75d784c7c1a 235 }else{
alvarolb 0:b75d784c7c1a 236 if(strcmp("api", resource)==0){
alvarolb 0:b75d784c7c1a 237 if(thing_resource==NULL){
alvarolb 0:b75d784c7c1a 238 thinger_map<thinger_resource>::entry* current = resources_.begin();
alvarolb 0:b75d784c7c1a 239 while(current!=NULL){
alvarolb 0:b75d784c7c1a 240 current->value_.fill_api(response.get_data()[current->key_]);
alvarolb 0:b75d784c7c1a 241 current = current->next_;
alvarolb 0:b75d784c7c1a 242 }
alvarolb 0:b75d784c7c1a 243 }else{
alvarolb 0:b75d784c7c1a 244 thing_resource->fill_api_io(response.get_data());
alvarolb 0:b75d784c7c1a 245 }
alvarolb 0:b75d784c7c1a 246 }else{
alvarolb 0:b75d784c7c1a 247 thing_resource = thing_resource == NULL ? resources_.find(resource) : thing_resource->find(resource);
alvarolb 0:b75d784c7c1a 248 if(thing_resource==NULL){
alvarolb 0:b75d784c7c1a 249 response.set_signal_flag(thinger_message::REQUEST_ERROR);
alvarolb 0:b75d784c7c1a 250 }else{
alvarolb 0:b75d784c7c1a 251 thing_resource->handle_request(request, response);
alvarolb 0:b75d784c7c1a 252 }
alvarolb 0:b75d784c7c1a 253 }
alvarolb 0:b75d784c7c1a 254 }
alvarolb 0:b75d784c7c1a 255 }
alvarolb 0:b75d784c7c1a 256 }
alvarolb 0:b75d784c7c1a 257 send_message(response);
alvarolb 0:b75d784c7c1a 258 }
alvarolb 0:b75d784c7c1a 259 };
alvarolb 0:b75d784c7c1a 260 }
alvarolb 0:b75d784c7c1a 261
alvarolb 0:b75d784c7c1a 262 #endif