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
thinger/thinger.h
- Committer:
- alvarolb
- Date:
- 2015-12-25
- Revision:
- 1:d54f92accbc3
- Parent:
- 0:b75d784c7c1a
File content as of revision 1:d54f92accbc3:
// The MIT License (MIT) // // Copyright (c) 2015 THINGER LTD // Author: alvarolb@gmail.com (Alvaro Luis Bustamante) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #ifndef THINGER_H #define THINGER_H #include "pson.h" #include "thinger_map.hpp" #include "thinger_resource.hpp" #include "thinger_message.hpp" #include "thinger_encoder.hpp" #include "thinger_decoder.hpp" #include "thinger_message.hpp" #include "thinger_io.hpp" #define KEEP_ALIVE_MILLIS 60000 namespace thinger{ using namespace protoson; class thinger : public thinger_io{ public: thinger() : encoder(*this), decoder(*this), last_keep_alive(0), keep_alive_response(true) { } virtual ~thinger(){ } private: thinger_write_encoder encoder; thinger_read_decoder decoder; unsigned long last_keep_alive; bool keep_alive_response; thinger_map<thinger_resource> resources_; protected: /** * Can be override to start reconnection process */ virtual void disconnected(){ // stop all streaming resources after disconnect if(thinger_resource::get_streaming_counter()>0) { thinger_map<thinger_resource>::entry* current = resources_.begin(); while(current!=NULL){ current->value_.disable_streaming(); current = current->next_; } } } /** * Stream a given resource */ void stream_resource(thinger_resource& resource, thinger_message::signal_flag type){ thinger_message message; message.set_stream_id(resource.get_stream_id()); message.set_signal_flag(type); resource.fill_api_io(message.get_data()); send_message(message); } public: thinger_resource & operator[](const char* res){ return resources_[res]; } bool connect(const char* username, const char* device_id, const char* credential) { // reset keep alive status for each connection keep_alive_response = true; thinger_message message; message.set_signal_flag(thinger_message::AUTH); message.resources().add(username).add(device_id).add(credential); if(!send_message(message)) return false; thinger_message response; return read_message(response) && response.get_signal_flag() == thinger_message::REQUEST_OK; } bool call_endpoint(const char* endpoint_name){ thinger_message message; message.set_signal_flag(thinger_message::CALL_ENDPOINT); message.resources().add(endpoint_name); return send_message(message); } bool call_endpoint(const char* endpoint_name, pson& data){ thinger_message message; message.set_signal_flag(thinger_message::CALL_ENDPOINT); message.set_data(data); message.resources().add(endpoint_name); return send_message(message); } bool call_endpoint(const char* endpoint_name, thinger_resource& resource){ thinger_message message; message.set_signal_flag(thinger_message::CALL_ENDPOINT); message.resources().add(endpoint_name); resource.fill_api_io(message.get_data()); return send_message(message); } /** * Stream the given resource. This resource should be previously requested by an external process. * Otherwise, the resource will not be streamed as nothing will be listening for it. */ bool stream(thinger_resource& resource){ if(resource.stream_enabled()){ stream_resource(resource, thinger_message::STREAM_EVENT); return true; } return false; } bool send_message(thinger_message& message) { thinger_encoder sink; sink.encode(message); encoder.pb_encode_varint(MESSAGE); encoder.pb_encode_varint(sink.bytes_written()); encoder.encode(message); return write(NULL, 0, true); } void handle(unsigned long current_time, bool bytes_available) { // handle input if(bytes_available){ handle_input(); } // handle keep alive if(current_time-last_keep_alive>=KEEP_ALIVE_MILLIS){ if(keep_alive_response){ last_keep_alive = current_time; keep_alive_response = false; encoder.pb_encode_varint(KEEP_ALIVE); encoder.pb_encode_varint(0); write(NULL, 0, true); }else{ disconnected(); } } // handle streaming resources if(thinger_resource::get_streaming_counter()>0){ thinger_map<thinger_resource>::entry* current = resources_.begin(); while(current!=NULL){ if(current->value_.stream_required(current_time)){ stream_resource(current->value_, thinger_message::STREAM_SAMPLE); } current = current->next_; } } } bool read_message(thinger_message& message){ uint8_t type = decoder.pb_decode_varint32(); switch (type){ case MESSAGE: { size_t size = decoder.pb_decode_varint32(); decoder.decode(message, size); } break; case KEEP_ALIVE: { size_t size = decoder.pb_decode_varint32(); keep_alive_response = true; } return false; default: return false; } return true; } bool handle_input(){ thinger_message message; if(read_message(message)){ handle_request_received(message); } return true; } private: void handle_request_received(thinger_message& request) { thinger_message response(request); if(!request.has_resource()){ response.set_signal_flag(thinger_message::REQUEST_ERROR); } else{ thinger_resource * thing_resource = NULL; for(pson_array::iterator it = request.resources().begin(); it.valid(); it.next()){ if(!it.item().is_string()){ response.set_signal_flag(thinger_message::REQUEST_ERROR); break; } const char* resource = it.item(); if(it.has_next()){ thing_resource = thing_resource == NULL ? resources_.find(resource) : thing_resource->find(resource); if(thing_resource==NULL) { response.set_signal_flag(thinger_message::REQUEST_ERROR); break; } }else{ if(strcmp("api", resource)==0){ if(thing_resource==NULL){ thinger_map<thinger_resource>::entry* current = resources_.begin(); while(current!=NULL){ current->value_.fill_api(response.get_data()[current->key_]); current = current->next_; } }else{ thing_resource->fill_api_io(response.get_data()); } }else{ thing_resource = thing_resource == NULL ? resources_.find(resource) : thing_resource->find(resource); if(thing_resource==NULL){ response.set_signal_flag(thinger_message::REQUEST_ERROR); }else{ thing_resource->handle_request(request, response); } } } } } send_message(response); } }; } #endif