Midnight Cow / ThingerIO

Dependencies:   DHT WIZnetInterface mbed-src

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers thinger.h Source File

thinger.h

00001 // The MIT License (MIT)
00002 //
00003 // Copyright (c) 2015 THINGER LTD
00004 // Author: alvarolb@gmail.com (Alvaro Luis Bustamante)
00005 //
00006 // Permission is hereby granted, free of charge, to any person obtaining a copy
00007 // of this software and associated documentation files (the "Software"), to deal
00008 // in the Software without restriction, including without limitation the rights
00009 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00010 // copies of the Software, and to permit persons to whom the Software is
00011 // furnished to do so, subject to the following conditions:
00012 //
00013 // The above copyright notice and this permission notice shall be included in
00014 // all copies or substantial portions of the Software.
00015 //
00016 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00017 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00018 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
00019 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00020 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00021 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
00022 // THE SOFTWARE.
00023 
00024 #ifndef THINGER_H
00025 #define THINGER_H
00026 
00027 #include "pson.h"
00028 #include "thinger_map.hpp"
00029 #include "thinger_resource.hpp"
00030 #include "thinger_message.hpp"
00031 #include "thinger_encoder.hpp"
00032 #include "thinger_decoder.hpp"
00033 #include "thinger_message.hpp"
00034 #include "thinger_io.hpp"
00035 
00036 #define KEEP_ALIVE_MILLIS 60000
00037 
00038 namespace thinger{
00039 
00040     using namespace protoson;
00041 
00042     class thinger : public thinger_io{
00043     public:
00044         thinger() :
00045                 encoder(*this),
00046                 decoder(*this),
00047                 last_keep_alive(0),
00048                 keep_alive_response(true)
00049         {
00050         }
00051 
00052         virtual ~thinger(){
00053         }
00054 
00055     private:
00056         thinger_write_encoder encoder;
00057         thinger_read_decoder decoder;
00058         unsigned long last_keep_alive;
00059         bool keep_alive_response;
00060         thinger_map<thinger_resource> resources_;
00061 
00062     protected:
00063         /**
00064          * Can be override to start reconnection process
00065          */
00066         virtual void disconnected(){}
00067 
00068     public:
00069 
00070         thinger_resource & operator[](const char* res){
00071             return resources_[res];
00072         }
00073 
00074         bool connect(const char* username, const char* device_id, const char* credential)
00075         {
00076             // reset keep alive status for each connection
00077             keep_alive_response = true;
00078 
00079             thinger_message message;
00080             message.resources().add("login").add(username).add(device_id).add(credential);
00081             if(!send_message(message)) return false;
00082 
00083             thinger_message response;
00084             return read_message(response) && response.get_signal_flag() == thinger_message::REQUEST_OK;
00085         }
00086 
00087         bool call_endpoint(const char* endpoint_name){
00088             thinger_message message;
00089             message.resources().add("ep").add(endpoint_name);
00090             return send_message(message);
00091         }
00092 
00093         bool call_endpoint(const char* endpoint_name, pson& data){
00094             thinger_message message;
00095             message.set_data(data);
00096             message.resources().add("ep").add(endpoint_name);
00097             return send_message(message);
00098         }
00099 
00100         bool send_message(thinger_message& message)
00101         {
00102             thinger_encoder sink;
00103             sink.encode(message);
00104             encoder.pb_encode_varint(message.get_message_type());
00105             encoder.pb_encode_varint(sink.bytes_written());
00106             encoder.encode(message);
00107             return write(NULL, 0, true);
00108         }
00109 
00110         void handle(unsigned long current_time, bool bytes_available)
00111         {
00112             // handle input
00113             if(bytes_available){
00114                 handle_input();
00115             }
00116 
00117             // handle keep alive
00118             if(current_time-last_keep_alive>KEEP_ALIVE_MILLIS){
00119                 if(keep_alive_response){
00120                     last_keep_alive = current_time;
00121                     keep_alive_response = false;
00122                     encoder.pb_encode_varint(thinger_message::KEEP_ALIVE);
00123                     encoder.pb_encode_varint(0);
00124                     write(NULL, 0, true);
00125                 }else{
00126                     disconnected();
00127                 }
00128             }
00129         }
00130 
00131         bool read_message(thinger_message& message){
00132             uint8_t message_type = decoder.pb_decode_varint32();
00133             switch (message_type){
00134                 case thinger_message::MESSAGE: {
00135                     size_t size = decoder.pb_decode_varint32();
00136                     decoder.decode(message, size);
00137                 }
00138                     break;
00139                 case thinger_message::KEEP_ALIVE: {
00140                     size_t size = decoder.pb_decode_varint32();
00141                     keep_alive_response = true;
00142                 }
00143                     return false;
00144                 default:
00145                     return false;
00146             }
00147             return true;
00148         }
00149 
00150         bool handle_input(){
00151             thinger_message message;
00152             if(read_message(message)){
00153                 handle_request_received(message);
00154             }
00155             return true;
00156         }
00157 
00158     private:
00159 
00160         void handle_request_received(thinger_message& request)
00161         {
00162             thinger_message response(request);
00163             if(!request.has_resource()){
00164                 response.set_signal_flag(thinger_message::REQUEST_ERROR);
00165             }
00166             else{
00167                 thinger_resource * thing_resource = NULL;
00168                 for(pson_array::iterator it = request.resources().begin(); it.valid(); it.next()){
00169                     if(!it.item().is_string()){
00170                         response.set_signal_flag(thinger_message::REQUEST_ERROR);
00171                         break;
00172                     }
00173                     const char* resource = it.item();
00174 
00175                     if(it.has_next()){
00176                         thing_resource = thing_resource == NULL ? resources_.find(resource) : thing_resource->find(resource);
00177                         if(thing_resource==NULL) {
00178                             response.set_signal_flag(thinger_message::REQUEST_ERROR);
00179                             break;
00180                         }
00181                     }else{
00182                         if(strcmp("api", resource)==0){
00183                             if(thing_resource==NULL){
00184                                 thinger_map<thinger_resource>::entry* current = resources_.begin();
00185                                 while(current!=NULL){
00186                                     current->value_.fill_api(response.get_data()[current->key_]);
00187                                     current = current->next_;
00188                                 }
00189                             }else{
00190                                 thing_resource->fill_api_io(response.get_data());
00191                             }
00192                         }else{
00193                             thing_resource = thing_resource == NULL ? resources_.find(resource) : thing_resource->find(resource);
00194                             if(thing_resource==NULL){
00195                                 response.set_signal_flag(thinger_message::REQUEST_ERROR);
00196                             }else{
00197                                 thing_resource->handle_request(request, response);
00198                             }
00199                         }
00200                     }
00201                 }
00202             }
00203             send_message(response);
00204         }
00205     };
00206 }
00207 
00208 #endif