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
Revision 0:b75d784c7c1a, committed 2015-12-24
- Comitter:
- alvarolb
- Date:
- Thu Dec 24 13:18:08 2015 +0000
- Child:
- 1:d54f92accbc3
- Commit message:
- Initial Commit
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ThingerClient.h Thu Dec 24 13:18:08 2015 +0000 @@ -0,0 +1,220 @@ +// 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_CLIENT_H +#define THINGER_CLIENT_H + +#include "thinger/thinger.h" + +using namespace protoson; + +dynamic_memory_allocator alloc; +//circular_memory_allocator<512> alloc; +memory_allocator& protoson::pool = alloc; + +#define THINGER_SERVER "iot.thinger.io" +#define THINGER_PORT 25200 +#define RECONNECTION_TIMEOUT 5 // seconds + +class ThingerClient : public thinger::thinger { +public: + ThingerClient(const char* user, const char* device, const char* device_credential) : + username_(user), device_id_(device), device_password_(device_credential), + temp_data_(NULL), out_size_(0) + {} + + virtual ~ThingerClient() + {} + +protected: + + virtual bool socket_start(const char* host, int port) = 0; + virtual bool socket_stop() = 0; + virtual bool socket_connected() = 0; + virtual size_t socket_read(char* buffer, size_t size) = 0; + virtual size_t socket_write(char* buffer, size_t size) = 0; + virtual size_t socket_available() = 0; + virtual bool connect_network() = 0; + virtual bool network_connected() = 0; + + virtual bool read(char *buffer, size_t size) + { + size_t total_read = 0; + while(total_read<size){ + int read = socket_read(buffer, size-total_read); + if(read<0) return false; + total_read += read; + } + return total_read == size; + } + + // TODO Allow removing this Nagle's algorithm implementation if the underlying device already implements it + virtual bool write(const char *buffer, size_t size, bool flush = false){ + if(size>0){ + temp_data_ = (char*) realloc(temp_data_, out_size_ + size); + memcpy(&temp_data_[out_size_], buffer, size); + out_size_ += size; + } + if(flush && out_size_>0){ + size_t written = socket_write(temp_data_, out_size_); + bool success = written == out_size_; + free(temp_data_); + temp_data_ = NULL; + out_size_ = 0; + return success; + } + return true; + } + + virtual void disconnected(){ + thinger_state_listener(SOCKET_TIMEOUT); + socket_stop(); + thinger_state_listener(SOCKET_DISCONNECTED); + } + + enum THINGER_STATE{ + NETWORK_CONNECTING, + NETWORK_CONNECTED, + NETWORK_CONNECT_ERROR, + SOCKET_CONNECTING, + SOCKET_CONNECTED, + SOCKET_CONNECTION_ERROR, + SOCKET_DISCONNECTED, + SOCKET_TIMEOUT, + THINGER_AUTHENTICATING, + THINGER_AUTHENTICATED, + THINGER_AUTH_FAILED + }; + + virtual void thinger_state_listener(THINGER_STATE state){ + #ifdef _DEBUG_ + switch(state){ + case NETWORK_CONNECTING: + printf("[NETWORK] Starting connection...\n"); + break; + case NETWORK_CONNECTED: + printf("[NETWORK] Connected!\n"); + break; + case NETWORK_CONNECT_ERROR: + printf("[NETWORK] Cannot connect\n!"); + break; + case SOCKET_CONNECTING: + printf("[_SOCKET] Connecting to %s:%d...\n", THINGER_SERVER, THINGER_PORT); + break; + case SOCKET_CONNECTED: + printf("[_SOCKET] Connected!\n"); + break; + case SOCKET_CONNECTION_ERROR: + printf("[_SOCKET] Error while connecting!\n"); + break; + case SOCKET_DISCONNECTED: + printf("[_SOCKET] Is now closed!\n"); + break; + case SOCKET_TIMEOUT: + printf("[_SOCKET] Timeout!\n"); + break; + case THINGER_AUTHENTICATING: + printf("[THINGER] Authenticating. User: %s Device: %s\n", username_, device_id_); + break; + case THINGER_AUTHENTICATED: + printf("[THINGER] Authenticated!\n"); + break; + case THINGER_AUTH_FAILED: + printf("[THINGER] Auth Failed! Check username, device id, or device credentials.\n"); + break; + } + #endif + } + + bool handle_connection() + { + bool network = network_connected(); + + if(!network){ + thinger_state_listener(NETWORK_CONNECTING); + network = connect_network(); + if(!network){ + thinger_state_listener(NETWORK_CONNECT_ERROR); + return false; + } + thinger_state_listener(NETWORK_CONNECTED); + } + + bool client = socket_connected(); + if(!client){ + client = connect_client(); + if(!client){ + return false; + } + } + return network && client; + } + + bool connect_client(){ + bool connected = false; + socket_stop(); // cleanup previous socket + thinger_state_listener(SOCKET_CONNECTING); + if (socket_start(THINGER_SERVER, THINGER_PORT)) { + thinger_state_listener(SOCKET_CONNECTED); + thinger_state_listener(THINGER_AUTHENTICATING); + connected = thinger::thinger::connect(username_, device_id_, device_password_); + if(!connected){ + thinger_state_listener(THINGER_AUTH_FAILED); + socket_stop(); + thinger_state_listener(SOCKET_DISCONNECTED); + } + else{ + thinger_state_listener(THINGER_AUTHENTICATED); + } + } + else{ + thinger_state_listener(SOCKET_CONNECTION_ERROR); + } + return connected; + } + +public: + + void handle(){ + if(handle_connection()){ + #ifdef _DEBUG_ + int available = socket_available(); + if(available>0){ + printf("[THINGER] Available bytes: %d\n", available); + } + #endif + thinger::thinger::handle(us_ticker_read()/1000, available>0); + }else{ + wait(RECONNECTION_TIMEOUT); // get some delay for a connection retry + } + } + +private: + const char* username_; + const char* device_id_; + const char* device_password_; + char * temp_data_; + size_t out_size_; +}; + +#endif \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/thinger/pson.h Thu Dec 24 13:18:08 2015 +0000 @@ -0,0 +1,902 @@ +// 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 PSON_HPP +#define PSON_HPP + +#include <stdint.h> +#include <string.h> +#include <math.h> +#include <stdlib.h> + +namespace protoson { + + class memory_allocator{ + public: + virtual void *allocate(size_t size) = 0; + virtual void deallocate(void *) = 0; + }; + + template<size_t buffer_size> + class circular_memory_allocator : public memory_allocator{ + private: + uint8_t buffer_[buffer_size]; + size_t index_; + public: + circular_memory_allocator() : index_(0) { + } + + virtual void *allocate(size_t size) { + if (index_ + size > buffer_size) { + index_ = 0; + } + void *position = &buffer_[index_]; + index_ += size; + return position; + } + + virtual void deallocate(void *) { + + } + }; + + class dynamic_memory_allocator : public memory_allocator{ + public: + virtual void *allocate(size_t size) { + return malloc(size); + } + + virtual void deallocate(void *ptr) { + free(ptr); + } + }; + + extern memory_allocator& pool; +} + +inline void* operator new(size_t sz, protoson::memory_allocator& a) +{ + return a.allocate(sz); +} + +inline void operator delete(void* p, protoson::memory_allocator& a) +{ + a.deallocate(p); +} + +template<class T> +void destroy(T* p, protoson::memory_allocator& a) +{ + if (p) { + p->~T(); + a.deallocate(p); + } +} + +namespace protoson { + + enum pb_wire_type{ + varint = 0, + fixed_64 = 1, + length_delimited = 2, + fixed_32 = 5, + pson_type = 6 + }; + + template<class T> + class pson_container { + + struct list_item{ + T item_; + list_item* next_; + }; + + public: + + class iterator{ + public: + iterator(list_item *current) : current(current) { + } + + private: + list_item * current; + + public: + bool next(){ + if(current==NULL) return false; + current = current->next_; + return true; + } + + bool has_next(){ + return current!=NULL && current->next_!=NULL; + } + + bool valid(){ + return current!=NULL; + } + + T& item(){ + return current->item_; + } + }; + + private: + list_item* item_; + + public: + iterator begin() const{ + return iterator(item_); + } + + pson_container() : item_(NULL) { + } + + ~pson_container(){ + clear(); + } + + size_t size() const{ + size_t size = 0; + list_item* current = item_; + while(current!=NULL){ + current = current->next_; + size++; + } + return size; + } + + T* operator[](size_t index){ + list_item* current = item_; + size_t current_index = 0; + while(current!=NULL){ + if(current_index==index){ + return ¤t->item_; + } + current = current->next_; + current_index++; + } + return NULL; + } + + void clear(){ + list_item* current = item_; + while(current!=NULL){ + list_item* next = current->next_; + destroy(current, pool); + current = next; + } + item_ = NULL; + } + + T& create_item(){ + list_item* new_list_item = new(pool) list_item(); + if(item_==NULL){ + item_ = new_list_item; + } + else{ + list_item * last = item_; + while(last->next_!=NULL) last = last->next_; + last->next_ = new_list_item; + } + return new_list_item->item_; + } + }; + + class pson_object; + class pson_array; + + class pson { + public: + enum field_type { + null_field = 0, + varint_field = 1, + svarint_field = 2, + float_field = 3, + double_field = 4, + true_field = 5, + false_field = 6, + zero_field = 7, + one_field = 8, + string_field = 9, + empty_string = 10, + bytes_field = 11, + empty_bytes = 12, + object_field = 13, + array_field = 14, + empty = 15, + // a message tag is encoded in a 128-base varint [1-bit][3-bit wire type][4-bit field] + // we have up to 4 bits (0-15) for encoding fields in the first byte + }; + + bool is_boolean() const{ + return field_type_ == true_field || field_type_ == false_field; + } + + bool is_string() const{ + return field_type_ == string_field; + } + + bool is_bytes() const{ + return field_type_ == bytes_field; + } + + bool is_number() const{ + return field_type_ == varint_field || + field_type_ == svarint_field || + field_type_ == float_field || + field_type_ == double_field || + field_type_ == zero_field || + field_type_ == one_field; + } + + bool is_object() const{ + return field_type_ == object_field; + } + + bool is_array() const{ + return field_type_ == array_field; + } + + bool is_null() const{ + return field_type_ == null_field; + } + + bool is_empty() const{ + return field_type_ == empty; + } + + pson() : field_type_(empty), value_(NULL) { + } + + template<class T> + pson(T value) : field_type_(empty), value_(NULL){ + *this = value; + } + + ~pson(){ + if(field_type_==object_field){ + destroy((pson_object *) value_, pool); + }else if(field_type_==array_field) { + destroy((pson_array *) value_, pool); + }else{ + pool.deallocate(value_); + } + } + + template<class T> + void operator=(T value) + { + if(value==0){ + field_type_ = zero_field; + }else if(value==1) { + field_type_ = one_field; + }else{ + if(value<0){ + field_type_ = svarint_field; + }else{ + field_type_ = varint_field; + } + uint64_t uint_value = value>0 ? value : -value; + value_ = pool.allocate(pson::get_varint_size(uint_value)); + pb_encode_varint(uint_value); + } + } + + void operator=(bool value){ + field_type_ = value ? true_field : false_field; + } + + void operator=(float value) { + if(value==(int32_t)value){ + *this = (int32_t) value; + }else{ + field_type_ = float_field; + set(value); + } + } + + void operator=(double value) { + if(value==(int64_t)value) { + *this = (int64_t) value; + }else if(fabs(value-(float)value)<=0.00001){ + field_type_ = float_field; + set((float)value); + }else{ + field_type_ = double_field; + set(value); + } + } + + void operator=(const char *str) { + size_t str_size = strlen(str); + if(str_size==0){ + field_type_ = empty_string; + }else{ + field_type_ = string_field; + memcpy(allocate(str_size+1), str, str_size+1); + } + } + + operator const char *() { + switch(field_type_){ + case string_field: + return (const char*) value_; + case empty: + field_type_ = empty_string; + default: + return ""; + } + } + + void set_bytes(const void* bytes, size_t size) { + if(size>0){ + size_t varint_size = get_varint_size(size); + value_ = pool.allocate(varint_size+size); + pb_encode_varint(size); + memcpy(((uint8_t*)value_)+varint_size, bytes, size); + field_type_ = bytes_field; + }else{ + field_type_ = empty_bytes; + } + } + + bool get_bytes(const void*& bytes, size_t& size){ + switch(field_type_){ + case bytes_field: + size = pb_decode_varint(); + bytes = (uint8_t*) value_ + get_varint_size(size); + return true; + case empty: + field_type_ = empty_bytes; + default: + return false; + } + } + + void* allocate(size_t size){ + value_ = pool.allocate(size); + return value_; + } + + operator pson_object &(); + operator pson_array &(); + pson & operator[](const char *name); + + operator bool(){ + switch(field_type_){ + case zero_field: + case false_field: + return false; + case one_field: + case true_field: + return true; + case empty: + field_type_ = false_field; + default: + return false; + } + } + + template<class T> + operator T() { + switch(field_type_){ + case zero_field: + case false_field: + return 0; + case one_field: + case true_field: + return 1; + case float_field: + return *(float*)value_; + case double_field: + return *(double*)value_; + case varint_field: + return pb_decode_varint(); + case svarint_field: + return -pb_decode_varint(); + case empty: + field_type_ = zero_field; + default: + return 0; + } + } + + void* get_value(){ + return value_; + } + + void set_value(void* value){ + value_ = value; + } + + field_type get_type() const{ + return field_type_; + } + + void set_null(){ + field_type_ = null_field; + } + + void set_type(field_type type){ + field_type_ = type; + } + + uint8_t get_varint_size(uint64_t value) const{ + uint8_t size = 1; + while(value>>=7) size++; + return size; + } + + void pb_encode_varint(uint64_t value) const + { + uint8_t count = 0; + do + { + uint8_t byte = (uint8_t)(value & 0x7F); + value >>= 7; + if(value) byte |= 0x80; + ((uint8_t*)value_)[count] = byte; + count++; + }while(value); + } + + uint64_t pb_decode_varint() const + { + if(value_==NULL) return 0; + uint64_t value = 0; + uint8_t pos = 0; + uint8_t byte = 0; + do{ + byte = ((uint8_t*)value_)[pos]; + value |= (uint64_t)(byte&0x7F) << pos*7; + pos++; + }while(byte>=0x80); + return value; + } + + private: + void* value_; + field_type field_type_; + + template<class T> + void set(T value) { + memcpy(allocate(sizeof(T)), &value, sizeof(T)); + } + }; + + class pson_pair{ + private: + char* name_; + pson value_; + public: + pson_pair() : name_(NULL){ + } + + ~pson_pair(){ + destroy(name_, pool); + } + + void set_name(const char *name) { + size_t name_size = strlen(name) + 1; + memcpy(allocate_name(name_size), name, name_size); + } + + char* allocate_name(size_t size){ + name_ = (char*)pool.allocate(size); + return name_; + } + + pson& value(){ + return value_; + } + + char* name() const{ + return name_; + } + }; + + class pson_object : public pson_container<pson_pair> { + public: + pson &operator[](const char *name) { + for(iterator it=begin(); it.valid(); it.next()){ + if(strcmp(it.item().name(), name)==0){ + return it.item().value(); + } + } + pson_pair & pair = create_item(); + pair.set_name(name); + return pair.value(); + }; + }; + + class pson_array : public pson_container<pson> { + public: + template<class T> + pson_array& add(T item_value){ + create_item() = item_value; + return *this; + } + }; + + inline pson::operator pson_object &() { + if (field_type_ != object_field) { + value_ = new(pool) pson_object; + field_type_ = object_field; + } + return *((pson_object *)value_); + } + + inline pson::operator pson_array &() { + if (field_type_ != array_field) { + value_ = new(pool) pson_array; + field_type_ = array_field; + } + return *((pson_array *)value_); + } + + inline pson &pson::operator[](const char *name) { + return ((pson_object &) *this)[name]; + } + + //////////////////////////// + /////// PSON_DECODER /////// + //////////////////////////// + + class pson_decoder { + + protected: + size_t read_; + + virtual bool read(void* buffer, size_t size){ + read_+=size; + return true; + } + + public: + + pson_decoder() : read_(0) { + + } + + void reset(){ + read_ = 0; + } + + size_t bytes_read(){ + return read_; + } + + bool pb_decode_tag(pb_wire_type& wire_type, uint32_t& field_number) + { + uint32_t temp = pb_decode_varint32(); + wire_type = (pb_wire_type)(temp & 0x07); + field_number = temp >> 3; + return true; + } + + uint32_t pb_decode_varint32() + { + uint32_t varint = 0; + if(try_pb_decode_varint32(varint)){ + return varint; + } + return 0; + } + + bool try_pb_decode_varint32(uint32_t& varint){ + varint = 0; + uint8_t byte; + uint8_t bit_pos = 0; + do{ + if(!read(&byte, 1) || bit_pos>=32){ + return false; + } + varint |= (uint32_t)(byte&0x7F) << bit_pos; + bit_pos += 7; + }while(byte>=0x80); + return true; + } + + uint64_t pb_decode_varint64() + { + uint64_t varint = 0; + uint8_t byte; + uint8_t bit_pos = 0; + do{ + if(!read(&byte, 1) || bit_pos>=64){ + return varint; + } + varint |= (uint32_t)(byte&0x7F) << bit_pos; + bit_pos += 7; + }while(byte>=0x80); + return varint; + } + + bool pb_skip(size_t size){ + uint8_t byte; + bool success = true; + for(size_t i=0; i<size; i++){ + success &= read(&byte, 1); + } + return success; + } + + bool pb_skip_varint(){ + uint8_t byte; + bool success = true; + do{ + success &= read(&byte, 1); + }while(byte>0x80); + return success; + } + + bool pb_read_string(char *str, size_t size){ + bool success = read(str, size); + str[size]=0; + return success; + } + + bool pb_read_varint(pson& value) + { + uint8_t temp[10]; + uint8_t byte=0; + uint8_t bytes_read=0; + do{ + if(!read(&byte, 1)) return false; + temp[bytes_read] = byte; + bytes_read++; + }while(byte>=0x80); + memcpy(value.allocate(bytes_read), temp, bytes_read); + return true; + } + + public: + + void decode(pson_object & object, size_t size){ + size_t start_read = bytes_read(); + while(size-(bytes_read()-start_read)>0){ + decode(object.create_item()); + } + } + + void decode(pson_array & array, size_t size){ + size_t start_read = bytes_read(); + while(size-(bytes_read()-start_read)>0){ + decode(array.create_item()); + } + } + + void decode(pson_pair & pair){ + uint32_t name_size = pb_decode_varint32(); + pb_read_string(pair.allocate_name(name_size+1), name_size); + decode(pair.value()); + } + + void decode(pson& value) { + uint32_t field_number; + pb_wire_type wire_type; + pb_decode_tag(wire_type, field_number); + value.set_type((pson::field_type)field_number); + if(wire_type==length_delimited){ + uint32_t size = pb_decode_varint32(); + switch(field_number){ + case pson::string_field: + pb_read_string((char*)value.allocate(size + 1), size); + break; + case pson::bytes_field: { + uint8_t varint_size = value.get_varint_size(size); + read((char*)value.allocate(size + varint_size) + varint_size, size); + value.pb_encode_varint(size); + } + break; + case pson::object_field: + value.set_value(new (pool) pson_object); + decode(*(pson_object *)value.get_value(), size); + break; + case pson::array_field: + value.set_value(new (pool) pson_array); + decode(*(pson_array *)value.get_value(), size); + break; + default: + pb_skip(size); + break; + } + }else { + switch (field_number) { + case pson::svarint_field: + case pson::varint_field: + pb_read_varint(value); + break; + case pson::float_field: + read(value.allocate(4), 4); + break; + case pson::double_field: + read(value.allocate(8), 8); + break; + default: + break; + } + } + } + }; + + //////////////////////////// + /////// PSON_ENCODER /////// + //////////////////////////// + + class pson_encoder { + + protected: + size_t written_; + + virtual void write(const void* buffer, size_t size){ + written_+=size; + } + + public: + + pson_encoder() : written_(0) { + } + + void reset(){ + written_ = 0; + } + + size_t bytes_written(){ + return written_; + } + + void pb_encode_tag(pb_wire_type wire_type, uint32_t field_number){ + uint64_t tag = ((uint64_t)field_number << 3) | wire_type; + pb_encode_varint(tag); + } + + void pb_encode_varint(uint32_t field, uint64_t value) + { + pb_encode_tag(varint, field); + pb_encode_varint(value); + } + + uint8_t pb_write_varint(void * buffer) + { + uint8_t byte=0; + uint8_t bytes_written=0; + do{ + byte = *((uint8_t*)buffer + bytes_written); + bytes_written++; + }while(byte>=0x80); + write(buffer, bytes_written); + return bytes_written; + } + + void pb_encode_varint(uint64_t value) + { + do + { + uint8_t byte = (uint8_t)(value & 0x7F); + value >>= 7; + if(value>0) byte |= 0x80; + write(&byte, 1); + }while(value>0); + } + + void pb_encode_string(const char* str, uint32_t field_number){ + pb_encode_tag(length_delimited, field_number); + pb_encode_string(str); + } + + void pb_encode_string(const char* str){ + size_t string_size = strlen(str); + pb_encode_varint(string_size); + write(str, string_size); + } + + template<class T> + void pb_encode_submessage(T& element, uint32_t field_number) + { + pb_encode_tag(length_delimited, field_number); + pson_encoder sink; + sink.encode(element); + pb_encode_varint(sink.bytes_written()); + encode(element); + } + + void pb_encode_fixed32(void* value){ + write(value, 4); + } + + void pb_encode_fixed64(void* value){ + write(value, 8); + } + + void pb_encode_fixed32(uint32_t field, void*value) + { + pb_encode_tag(fixed_32, field); + pb_encode_fixed32(value); + } + + void pb_encode_fixed64(uint32_t field, void*value) + { + pb_encode_tag(fixed_64, field); + pb_encode_fixed64(value); + } + + public: + + void encode(pson_object & object){ + pson_container<pson_pair>::iterator it = object.begin(); + while(it.valid()){ + encode(it.item()); + it.next(); + } + } + + void encode(pson_array & array){ + pson_container<pson>::iterator it = array.begin(); + while(it.valid()){ + encode(it.item()); + it.next(); + } + } + + void encode(pson_pair & pair){ + pb_encode_string(pair.name()); + encode(pair.value()); + } + + void encode(pson & value) { + switch (value.get_type()) { + case pson::string_field: + pb_encode_string((const char*)value.get_value(), pson::string_field); + break; + case pson::bytes_field: + pb_encode_tag(length_delimited, pson::bytes_field); + write(((const char *) value.get_value()) + pb_write_varint(value.get_value()), value.pb_decode_varint()); + break; + case pson::svarint_field: + case pson::varint_field: + pb_encode_tag(varint, value.get_type()); + pb_write_varint(value.get_value()); + break; + case pson::float_field: + pb_encode_fixed32(pson::float_field, value.get_value()); + break; + case pson::double_field: + pb_encode_fixed64(pson::double_field, value.get_value()); + break; + case pson::object_field: + pb_encode_submessage(*(pson_object *) value.get_value(), pson::object_field); + break; + case pson::array_field: + pb_encode_submessage(*(pson_array *) value.get_value(), pson::array_field); + break; + default: + pb_encode_tag(varint, value.get_type()); + break; + } + } + }; +} + +#endif
--- /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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/thinger/thinger_decoder.hpp Thu Dec 24 13:18:08 2015 +0000 @@ -0,0 +1,137 @@ +// 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_DECODER_HPP +#define THINGER_DECODER_HPP + +#include "pson.h" +#include "thinger_io.hpp" +#include "thinger_message.hpp" + +namespace thinger{ + + class thinger_decoder : public protoson::pson_decoder{ + public: + void decode(thinger_message& message, size_t size){ + size_t start_read = bytes_read(); + while(size-(bytes_read()-start_read)>0) { + protoson::pb_wire_type wire_type; + uint32_t field_number; + pb_decode_tag(wire_type, field_number); + switch (wire_type) { + case protoson::length_delimited:{ + uint32_t size = pb_decode_varint32(); + void *data = NULL; + switch (field_number) { + /* + case thinger_message::THING_ID: + data = protoson::pool.allocate(size + 1); + pb_read_string((char *) data, size); + message.set_thing_id((const char *) data); + break; + */ + default: + pb_skip(size); + break; + } + } + break; + case protoson::varint: { + switch (field_number) { + case thinger_message::SIGNAL_FLAG: + message.set_signal_flag((thinger_message::signal_flag)pb_decode_varint32()); + break; + case thinger_message::STREAM_ID: + message.set_stream_id(pb_decode_varint32()); + break; + default: + pb_skip_varint(); + break; + } + break; + } + case protoson::pson_type: + switch(field_number){ + case thinger_message::RESOURCE: + protoson::pson_decoder::decode(message.get_resources()); + break; + case thinger_message::PSON_PAYLOAD: + protoson::pson_decoder::decode(((protoson::pson&) message)); + break; + default: + break; + } + break; + case protoson::fixed_32: + pb_skip(4); + break; + case protoson::fixed_64: + pb_skip(8); + break; + default: + break; + } + } + } + }; + + class thinger_read_decoder : public thinger_decoder{ + public: + thinger_read_decoder(thinger_io& io) : io_(io) + {} + + protected: + virtual bool read(void* buffer, size_t size){ + io_.read((char*)buffer, size); + protoson::pson_decoder::read(buffer, size); + return true; + } + + private: + thinger_io& io_; + }; + + class thinger_memory_decoder : public thinger_decoder{ + + public: + thinger_memory_decoder(uint8_t* buffer, size_t size) : buffer_(buffer), size_(size){} + + protected: + virtual bool read(void* buffer, size_t size){ + if(read_+size<=size_){ + memcpy(buffer, buffer_ + read_, size); + return protoson::pson_decoder::read(buffer, size); + }else{ + return false; + } + } + + private: + uint8_t* buffer_; + size_t size_; + + }; + +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/thinger/thinger_encoder.hpp Thu Dec 24 13:18:08 2015 +0000 @@ -0,0 +1,90 @@ +// 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_ENCODER_HPP +#define THINGER_ENCODER_HPP + +#include "thinger_message.hpp" +#include "thinger_io.hpp" + +namespace thinger{ + +class thinger_encoder : public protoson::pson_encoder{ + +protected: + virtual void write(const void *buffer, size_t size){ + protoson::pson_encoder::write(buffer, size); + } + +public: + void encode(thinger_message& message){ + if(message.get_stream_id()!=0){ + pb_encode_varint(thinger_message::STREAM_ID, message.get_stream_id()); + } + if(message.get_signal_flag()!=thinger_message::NONE){ + pb_encode_varint(thinger_message::SIGNAL_FLAG, message.get_signal_flag()); + } + if(message.has_resource()){ + pb_encode_tag(protoson::pson_type, thinger_message::RESOURCE); + protoson::pson_encoder::encode(message.get_resources()); + } + if(message.has_data()){ + pb_encode_tag(protoson::pson_type, thinger_message::PSON_PAYLOAD); + protoson::pson_encoder::encode((protoson::pson&) message); + } + } +}; + +class thinger_write_encoder : public thinger_encoder{ + public: + thinger_write_encoder(thinger_io& io) : io_(io) + {} + + protected: + virtual void write(const void *buffer, size_t size){ + io_.write((const char*)buffer, size); + protoson::pson_encoder::write(buffer, size); + } + + private: + thinger_io& io_; +}; + +class thinger_memory_encoder : public thinger_encoder{ + public: + thinger_memory_encoder(uint8_t* buffer, size_t size) : buffer_(buffer), size_(size){} + + protected: + virtual void write(const void *buffer, size_t size){ + memcpy(buffer_ + written_, buffer, size); + protoson::pson_encoder::write(buffer, size); + } + + private: + uint8_t* buffer_; + size_t size_; +}; + +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/thinger/thinger_io.hpp Thu Dec 24 13:18:08 2015 +0000 @@ -0,0 +1,42 @@ +// 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_IO_HPP +#define THINGER_IO_HPP + +namespace thinger { + + class thinger_io { + public: + thinger_io(){} + virtual ~thinger_io(){} + + public: + virtual bool read(char *buffer, size_t size) = 0; + virtual bool write(const char *buffer, size_t size, bool flush = false) = 0; + }; + +} + +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/thinger/thinger_map.hpp Thu Dec 24 13:18:08 2015 +0000 @@ -0,0 +1,104 @@ +// 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_MAP_H +#define THINGER_MAP_H + +#include <string.h> + +template <class T> +class thinger_map { + +public: + thinger_map() : head_(NULL), last_(NULL) { + + } + + virtual ~thinger_map() { + } + +public: + + struct entry { + entry(const char* key) : key_(key), next_(NULL){ + + } + + const char* key_; + struct entry * next_; + T value_; + }; + +private: + + entry * head_; + entry * last_; + +public: + + T& operator[](const char* key){ + entry * current = head_; + while(current != NULL){ + if(strcmp(key, current->key_)==0){ + return current->value_; + } + current = current->next_; + } + // TODO replace with memory allocator for allowing static memory/dynamic memory + current = new entry(key); + + if(head_==NULL) head_ = current; + if(last_!=NULL) last_->next_ = current; + last_ = current; + return current->value_; + } + + entry* begin(){ + return head_; + } + + entry* end(){ + return last_; + } + + bool empty() + { + return head_ == last_; + } + + T* find(const char* key) + { + if(key==NULL) return NULL; + entry * current = head_; + while(current != NULL){ + if(strcmp(key, current->key_)==0){ + return ¤t->value_; + } + current = current->next_; + } + return NULL; + } + +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/thinger/thinger_message.hpp Thu Dec 24 13:18:08 2015 +0000 @@ -0,0 +1,180 @@ +// 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_MESSAGE_HPP +#define THINGER_MESSAGE_HPP + +#include "pson.h" + +namespace thinger{ + + enum message_type{ + MESSAGE = 1, + KEEP_ALIVE = 2 + }; + + class thinger_message{ + + public: + + // fields for a thinger message (encoded as in protocol buffers) + enum fields{ + STREAM_ID = 1, + SIGNAL_FLAG = 2, + UNUSED = 3, + RESOURCE = 4, + UNUSED2 = 5, + PSON_PAYLOAD = 6 + }; + + // flags for describing a thinger message + enum signal_flag { + // GENERAL USED FLAGS + REQUEST_OK = 1, // the request with the given stream id was successful + REQUEST_ERROR = 2, // the request with the given stream id failed + + // SENT BY THE SERVER + NONE = 0, // default resource action: just execute the given resource + START_STREAM = 3, // enable a streaming resource (with stream_id, and resource filled, sample interval (in payload) is optional) + STOP_STREAM = 4, // stop the streaming resource (with stream_id, and resource filled) + + // SENT BY DEVICE + AUTH = 5, + STREAM_EVENT = 6, // means that the message data is related to a stream event + STREAM_SAMPLE = 7, // means that the message is related to a periodical streaming sample + CALL_ENDPOINT = 8 // call the endpoint with the provided name (endpoint in resource, value passed in payload) + }; + + public: + + /** + * Initialize a default response message setting the same stream id of the source message, + * and initializing the signal flag to ok. All remaining data or fields are empty + */ + thinger_message(thinger_message& other) : + stream_id(other.stream_id), + flag(REQUEST_OK), + resource(NULL), + data(NULL), + data_allocated(false) + {} + + /** + * Initialize a default empty message + */ + thinger_message() : + stream_id(0), + flag(NONE), + resource(NULL), + data(NULL), + data_allocated(false) + {} + + ~thinger_message(){ + // deallocate resource + destroy(resource, protoson::pool); + // deallocate paylaod if was allocated here + if(data_allocated){ + destroy(data, protoson::pool); + } + } + + private: + /// used for identifying a unique stream + uint16_t stream_id; + /// used for setting a stream signal + signal_flag flag; + /// used to identify a device resource + protoson::pson* resource; + /// used to fill a data payload in the message + protoson::pson* data; + /// flag to determine when the payload has been reserved + bool data_allocated; + + public: + + uint16_t get_stream_id(){ + return stream_id; + } + + signal_flag get_signal_flag(){ + return flag; + } + + bool has_data(){ + return data!=NULL; + } + + bool has_resource(){ + return resource!=NULL; + } + + public: + void set_stream_id(uint16_t stream_id) { + thinger_message::stream_id = stream_id; + } + + void set_signal_flag(signal_flag const &flag) { + thinger_message::flag = flag; + } + + public: + + void operator=(const char* str){ + ((protoson::pson &) * this) = str; + } + + operator protoson::pson&(){ + if(data==NULL){ + data = new (protoson::pool) protoson::pson; + data_allocated = true; + } + return *data; + } + + protoson::pson_array& resources(){ + return (protoson::pson_array&)get_resources(); + } + + protoson::pson& get_resources(){ + if(resource==NULL){ + resource = new (protoson::pool) protoson::pson; + } + return *resource; + } + + protoson::pson& get_data(){ + return *this; + } + + void set_data(protoson::pson& pson_data){ + if(data==NULL){ + data = &pson_data; + data_allocated = false; + } + } + + }; +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/thinger/thinger_resource.hpp Thu Dec 24 13:18:08 2015 +0000 @@ -0,0 +1,275 @@ +// 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_RESOURCE_HPP +#define THINGER_RESOURCE_HPP + +#include "thinger_map.hpp" +#include "pson.h" +#include "thinger_message.hpp" + +namespace thinger{ + + +class thinger_resource { + +public: + enum io_type { + none = 0, + run = 1, + pson_in = 2, + pson_out = 3, + pson_in_pson_out = 4 + }; + + enum access_type{ + PRIVATE = 0, + PROTECTED = 1, + PUBLIC = 2, + NONE = 3 + }; + + static int get_streaming_counter(){ + return streaming_count_; + } + +private: + + // calback for function, input, output, or input/output + union callback{ + void (*run)(); + void (*pson_in)(protoson::pson& in); + void (*pson_out)(protoson::pson& out); + void (*pson_in_pson_out)(protoson::pson& in, protoson::pson& out); + }; + + // used for defining the resource + io_type io_type_; + access_type access_type_; + callback callback_; + + // used for allowing resource streaming (both periodically or by events) + uint16_t stream_id_; + + // used for periodic stream events + unsigned long streaming_freq_; + unsigned long last_streaming_; + + // used to know the total number of streams + static unsigned int streaming_count_; + + // TODO change to pointer so it is not using more than a pointer size if not used? + thinger_map<thinger_resource> sub_resources_; + + void enable_streaming(uint16_t stream_id, unsigned long streaming_freq){ + stream_id_ = stream_id; + streaming_freq_ = streaming_freq; + last_streaming_ = 0; + streaming_count_++; + } + +public: + thinger_resource() : io_type_(none), access_type_(PRIVATE), stream_id_(0), streaming_freq_(0), last_streaming_(0) + {} + + void disable_streaming(){ + stream_id_ = 0; + streaming_freq_ = 0; + streaming_count_--; + } + + bool stream_enabled(){ + return stream_id_ > 0; + } + + uint32_t get_stream_id(){ + return stream_id_; + } + + bool stream_required(unsigned long timestamp){ + // sample interval is activated + if(streaming_freq_>0){ + if(timestamp-last_streaming_>=streaming_freq_){ + last_streaming_ = timestamp; + return true; + } + } + return false; + } + + thinger_resource * find(const char* res) + { + return sub_resources_.find(res); + } + + thinger_resource & operator[](const char* res){ + return sub_resources_[res]; + } + + thinger_resource & operator()(access_type type){ + access_type_ = type; + return *this; + } + + io_type get_io_type(){ + return io_type_; + } + + access_type get_access_type(){ + return access_type_; + } + + void fill_api(protoson::pson_object& content){ + if(io_type_!=none){ + content["al"] = access_type_; + content["fn"] = io_type_; + } + thinger_map<thinger_resource>::entry* current = sub_resources_.begin(); + if(current!=NULL){ + protoson::pson_object& actions = content["/"]; + do{ + current->value_.fill_api(actions[current->key_]); + current = current->next_; + }while(current!=NULL); + } + } + + void fill_api_io(protoson::pson_object& content){ + if(io_type_ == pson_in){ + callback_.pson_in(content["in"]); + }else if(io_type_ == pson_out){ + callback_.pson_out(content["out"]); + }else if(io_type_ == pson_in_pson_out){ + callback_.pson_in_pson_out(content["in"], content["out"]); + } + } + +public: + + /** + * Establish a function without input or output parameters + */ + void operator=(void (*run_function)()){ + io_type_ = run; + callback_.run = run_function; + } + + /** + * Establish a function without input or output parameters + */ + void set_function(void (*run_function)()){ + io_type_ = run; + callback_.run = run_function; + } + + /** + * Establish a function with input parameters + */ + void operator<<(void (*in_function)(protoson::pson& in)){ + io_type_ = pson_in; + callback_.pson_in = in_function; + } + + /** + * Establish a function with input parameters + */ + void set_input(void (*in_function)(protoson::pson& in)){ + io_type_ = pson_in; + callback_.pson_in = in_function; + } + + /** + * Establish a function that only generates an output + */ + void operator>>(void (*out_function)(protoson::pson& out)){ + io_type_ = pson_out; + callback_.pson_out = out_function; + } + + /** + * Establish a function that only generates an output + */ + void set_output(void (*out_function)(protoson::pson& out)){ + io_type_ = pson_out; + callback_.pson_out = out_function; + } + + /** + * Establish a function that can receive input parameters and generate an output + */ + void operator=(void (*pson_in_pson_out_function)(protoson::pson& in, protoson::pson& out)){ + io_type_ = pson_in_pson_out; + callback_.pson_in_pson_out = pson_in_pson_out_function; + } + + /** + * Establish a function that can receive input parameters and generate an output + */ + void set_input_output(void (*pson_in_pson_out_function)(protoson::pson& in, protoson::pson& out)){ + io_type_ = pson_in_pson_out; + callback_.pson_in_pson_out = pson_in_pson_out_function; + } + + /** + * Handle a request and fill a possible response + */ + void handle_request(thinger_message& request, thinger_message& response){ + switch(request.get_signal_flag()){ + // default action over the stream (run the resource) + case thinger_message::NONE: + switch (io_type_){ + case run: + callback_.run(); + break; + case pson_in: + callback_.pson_in(request); + break; + case pson_out: + callback_.pson_out(response); + break; + case pson_in_pson_out: + callback_.pson_in_pson_out(request, response); + break; + case none: + break; + } + break; + // flag for starting a resource stream + case thinger_message::START_STREAM: + enable_streaming(request.get_stream_id(), request.get_data()); + break; + // flat for stopping a resource stream + case thinger_message::STOP_STREAM: + disable_streaming(); + break; + default: + break; + } + } +}; + + unsigned int thinger_resource::streaming_count_ = 0; + +} + +#endif