mbed client

Fork of simple-mbed-client by sandbox

simple-mbed-client.h

Committer:
mbed_demo
Date:
2016-12-02
Revision:
14:3fd6afc63e6a
Parent:
12:26810c6b58e1

File content as of revision 14:3fd6afc63e6a:

/*
 * 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__

#define debug_msg(...) if (debug) output.printf(__VA_ARGS__)

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

using namespace std;

class SimpleResourceBase {
public:
    virtual void update(string v) {}
};

class SimpleMbedClientBase {
public:
    SimpleMbedClientBase(bool aDebug = true)
        : output(USBTX, USBRX), debug(aDebug)
    {

    }

    ~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::TCP;
        options.ServerAddress = "coap://api.connector.mbed.com:5684";

        return options;
    }

    bool init(NetworkInterface* iface) {
        debug_msg("[SMC] Device name %s\r\n", MBED_ENDPOINT_NAME);

        // Create endpoint interface to manage register and unregister
        client->create_interface(iface);

        // 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) {
        debug_msg("[SMC] In mbed_client_setup\r\n");
        if (client) {
            debug_msg("[SMC] [ERROR] mbed_client_setup called, but mbed_client is already instantiated\r\n");
            return false;
        }

        struct MbedClientOptions options = get_default_options();

        Callback<void(string)> updateFp(this, &SimpleMbedClientBase::resource_updated);
        client = new MbedClient(options, updateFp, debug);

        return init(iface);
    }

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

        Callback<void(string)> updateFp(this, &SimpleMbedClientBase::resource_updated);
        client = new MbedClient(options, updateFp, debug);

        return init(iface);
    }

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

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

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

    template<typename T>
    void on_unregistered(T *object, void (T::*member)(void)) {
        Callback<void()> 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)) {
            debug_msg("[SMC] [ERROR] Should be created, but no such route (%s)\r\n", route);
            return false;
        }

        resources[route_str]->set_execute_function(execute_callback_2(fn));
        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)) {
            debug_msg("[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)) {
            debug_msg("[SMC] [ERROR] No such route (%s)\r\n", route_str.c_str());
            return string();
        }

        // otherwise ask mbed Client...
        uint8_t* buffIn = NULL;
        uint32_t sizeIn;
        resources[route_str]->get_value(buffIn, sizeIn);

        string s((char*)buffIn, sizeIn);
        return s;
    }

    bool set(string route_str, string v) {
        // Potentially set() happens in InterruptContext. That's not good.
        if (!resources.count(route_str)) {
            debug_msg("[SMC] [ERROR] No such route (%s)\r\n", route_str.c_str());
            return false;
        }

        if (v.length() == 0) {
            resources[route_str]->clear_value();
        }
        else {
            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) {
            debug_msg("[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) {
            debug_msg("[SMC] [ERROR] mbed_client_define_resource, Route needs to have three segments, split by '/' (%s)\r\n", route);
            return false;
        }

        // segments[1] should be one digit and numeric
        char n = segments.at(1).c_str()[0];
        if (n < '0' || n > '9') {
            debug_msg("[SMC] [ERROR] mbed_client_define_resource, second route segment should be numeric, but was not (%s)\r\n", route);
            return false;
        }

        int inst_id = atoi(segments.at(1).c_str());

        M2MObjectInstance* inst;
        if (objectInstances.count(segments.at(0))) {
            inst = objectInstances[segments.at(0)];
        }
        else {
            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(pair<string, M2MResource*>(route_str, res));

        return true;
    }

    void keep_alive() {
        client->test_update_register();
    }

    void register_update_callback(string route, SimpleResourceBase* simpleResource) {
        updateValues[route] = simpleResource;
    }

    M2MResource* get_resource(string route) {
        if (!resources.count(route)) {
            debug_msg("[SMC] [ERROR] No such route (%s)\r\n", route.c_str());
            return NULL;
        }

        return resources[route];
    }

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;
    }

    void resource_updated(string uri) {
        if (updateValues.count(uri) == 0) return;

        string v = get(uri);
        if (v.empty()) return;

        updateValues[uri]->update(v);
    }

    Serial output;

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

    bool debug;

    map<string, SimpleResourceBase*> updateValues;
};

class SimpleResourceString : public SimpleResourceBase {
public:
    SimpleResourceString(SimpleMbedClientBase* aSimpleClient, string aRoute, Callback<void(string)> aOnUpdate) :
        simpleClient(aSimpleClient), route(aRoute), onUpdate(aOnUpdate) {}

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

    virtual void update(string v) {
        if (onUpdate) onUpdate(v);
    }

    M2MResource* get_resource() {
        return simpleClient->get_resource(route);
    }

private:
    SimpleMbedClientBase* simpleClient;
    string route;
    Callback<void(string)> onUpdate;
};

class SimpleResourceInt : public SimpleResourceBase {
public:
    SimpleResourceInt(SimpleMbedClientBase* aSimpleClient, string aRoute, Callback<void(int)> aOnUpdate) :
        simpleClient(aSimpleClient), route(aRoute), onUpdate(aOnUpdate) {}

    int operator=(int newValue) {
        simpleClient->set(route, newValue);
        return newValue;
    };
    operator int() const {
        string v = simpleClient->get(route);
        if (v.empty()) return 0;

        return atoi((const char*)v.c_str());
    };

    virtual void update(string v) {
        if (!onUpdate) return;

        onUpdate(atoi((const char*)v.c_str()));
    }

    M2MResource* get_resource() {
        return simpleClient->get_resource(route);
    }

private:
    SimpleMbedClientBase* simpleClient;
    string route;
    Callback<void(int)> onUpdate;
};

class SimpleMbedClient : public SimpleMbedClientBase {
public:

    // @todo: macro this up

    SimpleResourceString define_resource(
        const char* route,
        string v,
        M2MBase::Operation opr = M2MBase::GET_PUT_ALLOWED,
        bool observable = true,
        Callback<void(string)> onUpdate = NULL)
    {
        SimpleResourceString* simpleResource = new SimpleResourceString(this, route, onUpdate);
        bool res = define_resource_internal(route, v, opr, observable);
        if (!res) {
            printf("Error while creating %s\n", route);
        }
        else {
            register_update_callback(route, simpleResource);
        }
        return *simpleResource;
    }

    SimpleResourceString define_resource(
        const char* route,
        string v,
        M2MBase::Operation opr,
        bool observable,
        void(*onUpdate)(string))
    {
        Callback<void(string)> fp;
        fp.attach(onUpdate);
        return define_resource(route, v, opr, observable, fp);
    }

    SimpleResourceString define_resource(
        const char* route,
        string v,
        Callback<void(string)> onUpdate)
    {
        return define_resource(route, v, M2MBase::GET_PUT_ALLOWED, true, onUpdate);
    }

    SimpleResourceString define_resource(
        const char* route,
        string v,
        void(*onUpdate)(string))
    {
        Callback<void(string)> fp;
        fp.attach(onUpdate);
        return define_resource(route, v, M2MBase::GET_PUT_ALLOWED, true, fp);
    }

    SimpleResourceInt define_resource(
        const char* route,
        int v,
        M2MBase::Operation opr = M2MBase::GET_PUT_ALLOWED,
        bool observable = true,
        Callback<void(int)> onUpdate = NULL)
    {
        SimpleResourceInt* simpleResource = new SimpleResourceInt(this, route, onUpdate);

        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);
        }
        else {
            register_update_callback(route, simpleResource);
        }
        return *simpleResource;
    }

    SimpleResourceInt define_resource(
        const char* route,
        int v,
        M2MBase::Operation opr,
        bool observable,
        void(*onUpdate)(int))
    {
        Callback<void(int)> fp;
        fp.attach(onUpdate);
        return define_resource(route, v, opr, observable, fp);
    }

    SimpleResourceInt define_resource(
        const char* route,
        int v,
        Callback<void(int)> onUpdate)
    {
        return define_resource(route, v, M2MBase::GET_PUT_ALLOWED, true, onUpdate);
    }

    SimpleResourceInt define_resource(
        const char* route,
        int v,
        void(*onUpdate)(int))
    {
        Callback<void(int)> fp;
        fp.attach(onUpdate);
        return define_resource(route, v, M2MBase::GET_PUT_ALLOWED, true, fp);
    }
};

#endif // __SIMPLE_MBED_CLIENT_H__