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

Revision:
0:b75d784c7c1a
Child:
1:d54f92accbc3
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thinger/thinger.h	Thu Dec 24 13:18:08 2015 +0000
@@ -0,0 +1,262 @@
+// 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