mbed client

Fork of simple-mbed-client by sandbox

simple-mbed-client.h

Committer:
janjongboom
Date:
2016-05-15
Revision:
0:9fa3f3028773
Child:
1:75015f627e89

File content as of revision 0:9fa3f3028773:

/*
 * Copyright (c) 2015 ARM Limited. All rights reserved.
 * SPDX-License-Identifier: Apache-2.0
 * Licensed under the Apache License, Version 2.0 (the License); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an AS IS BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef __SIMPLE_MBED_CLIENT_H__
#define __SIMPLE_MBED_CLIENT_H__

#include <map>
#include <string>
#include <sstream>
#include <vector>
#include "mbed-client-classic/m2mnetwork.h"
#include "mbed-client-wrapper.h"

using namespace std;

class SimpleMbedClientBase {
public:
    SimpleMbedClientBase() : output(USBTX, USBRX) {
        
    }
    ~SimpleMbedClientBase() {}

    struct MbedClientOptions get_default_options() {
        struct MbedClientOptions options;
        options.Manufacturer = "Manufacturer_String";
        options.Type = "Type_String";
        options.ModelNumber = "ModelNumber_String";
        options.SerialNumber = "SerialNumber_String";
        options.DeviceType = "test";
        options.SocketMode = M2MInterface::UDP;
        options.ServerAddress = "coap://api.connector.mbed.com:5684";
    
        return options;
    }

    bool init(NetworkInterface* iface) {
        // does this automatically handle everything? weird yo.
        M2MNetwork network(iface);
        
        output.printf("[SMC] Device name %s\r\n", MBED_ENDPOINT_NAME);
    
        // Create endpoint interface to manage register and unregister
        client->create_interface();
    
        // Create Objects of varying types, see simpleclient.h for more details on implementation.
        M2MSecurity* register_object = client->create_register_object(); // server object specifying connector info
        M2MDevice*   device_object   = client->create_device_object();   // device resources object
    
        // Create list of Objects to register
        M2MObjectList object_list;
    
        // Add objects to list
        object_list.push_back(device_object);
    
        map<string, M2MObject*>::iterator it;
        for (it = objects.begin(); it != objects.end(); it++)
        {
            object_list.push_back(it->second);
        }
    
        // Set endpoint registration object
        client->set_register_object(register_object);
    
        // Issue register command.
        client->test_register(register_object, object_list);
        
        // @todo: no idea if this works
        Ticker updateRegister;
        updateRegister.attach(client, &MbedClient::test_update_register, 25.0f);
    
        return true;
    }

    bool setup(NetworkInterface* iface) {
        output.printf("[SMC] In mbed_client_setup\r\n");
        if (client) {
            output.printf("[SMC] [ERROR] mbed_client_setup called, but mbed_client is already instantiated\r\n");
            return false;
        }
    
        struct MbedClientOptions options = get_default_options();
    
        client = new MbedClient(options);
        return init(iface);
    }

    bool setup(MbedClientOptions options, NetworkInterface* iface) {
        if (client) {
            output.printf("[SMC] [ERROR] mbed_client_setup called, but mbed_client is already instantiated\r\n");
            return false;
        }
        client = new MbedClient(options);
        return init(iface);
    }

    void on_registered(void(*fn)(void)) {
        FunctionPointer fp(fn);
        client->set_registered_function(fp);
    }

    template<typename T>
    void on_registered(T *object, void (T::*member)(void)) { 
        FunctionPointer fp(object, member);
        client->set_registered_function(fp);
    }

    void on_unregistered(void(*fn)(void)) {
        FunctionPointer fp(fn);
        client->set_unregistered_function(fp);
    }

    template<typename T>
    void on_unregistered(T *object, void (T::*member)(void)) { 
        FunctionPointer fp(object, member);
        client->set_unregistered_function(fp);
    }

    bool define_function(const char* route, void(*fn)(void*)) {
        if (!define_resource_internal(route, string(), M2MBase::POST_ALLOWED, false)) {
            return false;
        }
    
        string route_str(route);
        if (!resources.count(route_str)) {
            output.printf("[SMC] [ERROR] Should be created, but no such route (%s)\r\n", route);
            return false;
        }
        
        FunctionPointerArg1<void, void*>* fp = new FunctionPointerArg1<void, void*>(fn);
        resources[route_str]->set_execute_function(execute_callback(fp, &FunctionPointerArg1<void, void*>::call));
        return true;
    }

    bool define_function(const char* route, execute_callback fn) {
        if (!define_resource_internal(route, string(), M2MBase::POST_ALLOWED, false)) {
            return false;
        }
    
        string route_str(route);
        if (!resources.count(route_str)) {
            output.printf("[SMC] [ERROR] Should be created, but no such route (%s)\r\n", route);
            return false;
        }
        // No clue why this is not working?! It works with class member, but not with static function...
        resources[route_str]->set_execute_function(fn);
        return true;
    }
//    
    string get(string route_str) {
        if (!resources.count(route_str)) {
            output.printf("[SMC] [ERROR] No such route (%s)\r\n", route_str.c_str());
            return string();
        }
    
        uint8_t* buffIn;
        uint32_t sizeIn;
        resources[route_str]->get_value(buffIn, sizeIn);
    
        std::string s((char*)buffIn, sizeIn);
        return s;
    }

    bool set(string route_str, string v) {
        if (!resources.count(route_str)) {
            output.printf("[SMC] [ERROR] No such route (%s)\r\n", route_str.c_str());
            return false;
        }
        resources[route_str]->set_value((uint8_t*)v.c_str(), v.length());
        return true;
    }

    bool set(string route, const int& v) {
        stringstream ss;
        ss << v;
        std::string stringified = ss.str();
    
        return set(route, stringified);
    }

    bool define_resource_internal(const char* route, std::string v, M2MBase::Operation opr, bool observable) {
        if (client) {
            output.printf("[SMC] [ERROR] mbed_client_define_resource, Can only define resources before mbed_client_setup is called!\r\n");
            return false;
        }
    
        vector<string> segments = parse_route(route);
        if (segments.size() != 3) {
            output.printf("[SMC] [ERROR] mbed_client_define_resource, Route needs to have three segments, split by '/' (%s)\r\n", route);
            return false;
        }
        
        int inst_id = 0;
//        try {
//            inst_id = stoi(segments.at(1));
//        }
//        catch (const std::invalid_argument& ia) {
//            output.printf("[SMC] [ERROR] mbed_client_define_resource, second route segment should be numeric, but was not (%s)\r\n", route);
//            return false;
//        }

    
        M2MObjectInstance* inst;
        if (objectInstances.count(segments.at(0))) {
            output.printf("Found object... %s\r\n", segments.at(0).c_str());
            inst = objectInstances[segments.at(0)];
        }
        else {
            output.printf("Create new object... %s\r\n", segments.at(0).c_str());
            M2MObject* obj = M2MInterfaceFactory::create_object(segments.at(0).c_str());
            inst = obj->create_object_instance(inst_id);
            objects.insert(std::pair<string, M2MObject*>(segments.at(0), obj));
            objectInstances.insert(std::pair<string, M2MObjectInstance*>(segments.at(0), inst));
        }
    
        // @todo check if the resource exists yet
        M2MResource* res = inst->create_dynamic_resource(segments.at(2).c_str(), "",
            M2MResourceInstance::STRING, observable);
        res->set_operation(opr);
        res->set_value((uint8_t*)v.c_str(), v.length());
    
        string route_str(route);
        resources.insert(std::pair<string, M2MResource*>(route_str, res));
    
        return true;
    }

private:
    vector<string> parse_route(const char* route) {
        string s(route);
        vector<string> v;
        stringstream ss(s);
        string item;
        while (getline(ss, item, '/')) {
            v.push_back(item);
        }
        return v;
    }

    Serial output;
    
    MbedClient* client;
    map<string, M2MObject*> objects;
    map<string, M2MObjectInstance*> objectInstances;
    map<string, M2MResource*> resources;
};

class SimpleResourceString {
public:
    SimpleResourceString(SimpleMbedClientBase* aSimpleClient, string aRoute) :
        simpleClient(aSimpleClient), route(aRoute) {}

    string operator=(const string& newValue) {
        simpleClient->set(route, newValue);
        return newValue;
    };
    operator string() const {
        return simpleClient->get(route);
    };

private:
    SimpleMbedClientBase* simpleClient;
    string route;
};

class SimpleResourceInt {
public:
    SimpleResourceInt(SimpleMbedClientBase* aSimpleClient, string aRoute) :
        simpleClient(aSimpleClient), route(aRoute) {}

    int operator=(int newValue) {
//        printf("newValue %s = %d\n", route.c_str(), newValue);
        simpleClient->set(route, newValue);
        return newValue;
    };
    operator int() const {
//        printf("receiving a value for %s\n", route.c_str());
        string v = simpleClient->get(route);
//        printf("string value is %s\n", v.c_str());
        if (v.empty()) return 0;
    
        return atoi((const char*)v.c_str());
    };

private:
    SimpleMbedClientBase* simpleClient;
    string route;
};

class SimpleMbedClient : public SimpleMbedClientBase {
public:
    SimpleResourceString define_resource(const char* route, string v, M2MBase::Operation opr, bool observable) {
        bool res = define_resource_internal(route, v, opr, observable);
        if (!res) printf("Error while creating %s\n", route);
        return *(new SimpleResourceString(this, route));
    }
    
    SimpleResourceInt define_resource(const char* route, int v, M2MBase::Operation opr, bool observable) {
        stringstream ss;
        ss << v;
        std::string stringified = ss.str();
        bool res = define_resource_internal(route, stringified, opr, observable);
        if (!res) printf("Error while creating %s\n", route);
        return *(new SimpleResourceInt(this, route));
    }
};

#endif // __SIMPLE_MBED_CLIENT_H__