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

ThingerClient.h

Committer:
alvarolb
Date:
2015-12-26
Revision:
4:de51256455f7
Parent:
3:58e0153dbb37

File content as of revision 4:de51256455f7:

// 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:
    /** Connect to the given host and port
    * Initialize a socket and connect it to the given host and port
    * \param host the host address to use
    * \param port the host port to connect to
    * \return true on success, or false on failure
    */
    virtual bool    socket_start(const char* host, int port) = 0;
    
    /** Stop the current socket connection
    * \return true on success, or false on failure
    */
    virtual bool    socket_stop() = 0;
    
    /** Check if the socket is connected
    * \return true on socket connected, or false on disconnected
    */
    virtual bool    socket_connected() = 0;
    
    /** Read all specified bytes from socket to buffer
    * \return the number of bytes read
    */
    virtual size_t  socket_read(char* buffer, size_t size) = 0;
    
    /** Write all specified bytes from buffer to socket
    * \return the number of bytes written
    */
    virtual size_t  socket_write(char* buffer, size_t size) = 0;
    
    /** Check the total available data in the socket
    * \return Ideally, the number of bytes available in the socket, or any positive number if there is pending data. 0 Otherwise.
    */
    virtual size_t  socket_available() = 0;
    
    /** Initialize the network interface, i.e., initialize ethernet interface, get ip address, etc.
    * \return the number of bytes read
    */
    virtual bool    connect_network() = 0;
    
    /** Check if the network is still connected
    * \return true if the network is connected, or false ortherwise
    */
    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()) {
            int available = socket_available();
#ifdef _DEBUG_
            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