mbed demo / simple-mbed-client

Fork of simple-mbed-client by sandbox

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers simple-mbed-client.h Source File

simple-mbed-client.h

00001 /*
00002  * Copyright (c) 2015 ARM Limited. All rights reserved.
00003  * SPDX-License-Identifier: Apache-2.0
00004  * Licensed under the Apache License, Version 2.0 (the License); you may
00005  * not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  * http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an AS IS BASIS, WITHOUT
00012  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 
00017 #ifndef __SIMPLE_MBED_CLIENT_H__
00018 #define __SIMPLE_MBED_CLIENT_H__
00019 
00020 #define debug_msg(...) if (debug) output.printf(__VA_ARGS__)
00021 
00022 #include <map>
00023 #include <string>
00024 #include <sstream>
00025 #include <vector>
00026 #include "mbed-client-wrapper.h"
00027 
00028 using namespace std;
00029 
00030 class SimpleResourceBase {
00031 public:
00032     virtual void update(string v) {}
00033 };
00034 
00035 class SimpleMbedClientBase {
00036 public:
00037     SimpleMbedClientBase(bool aDebug = true)
00038         : output(USBTX, USBRX), debug(aDebug)
00039     {
00040 
00041     }
00042 
00043     ~SimpleMbedClientBase() {}
00044 
00045     struct MbedClientOptions get_default_options() {
00046         struct MbedClientOptions options;
00047         options.Manufacturer = "Manufacturer_String";
00048         options.Type = "Type_String";
00049         options.ModelNumber = "ModelNumber_String";
00050         options.SerialNumber = "SerialNumber_String";
00051         options.DeviceType = "test";
00052         options.SocketMode = M2MInterface::TCP;
00053         options.ServerAddress = "coap://api.connector.mbed.com:5684";
00054 
00055         return options;
00056     }
00057 
00058     bool init(NetworkInterface* iface) {
00059         debug_msg("[SMC] Device name %s\r\n", MBED_ENDPOINT_NAME);
00060 
00061         // Create endpoint interface to manage register and unregister
00062         client->create_interface(iface);
00063 
00064         // Create Objects of varying types, see simpleclient.h for more details on implementation.
00065         M2MSecurity* register_object = client->create_register_object(); // server object specifying connector info
00066         M2MDevice*   device_object   = client->create_device_object();   // device resources object
00067 
00068         // Create list of Objects to register
00069         M2MObjectList object_list;
00070 
00071         // Add objects to list
00072         object_list.push_back(device_object);
00073 
00074         map<string, M2MObject*>::iterator it;
00075         for (it = objects.begin(); it != objects.end(); it++)
00076         {
00077             object_list.push_back(it->second);
00078         }
00079 
00080         // Set endpoint registration object
00081         client->set_register_object(register_object);
00082 
00083         // Issue register command.
00084         client->test_register(register_object, object_list);
00085 
00086         // @todo: no idea if this works
00087         Ticker updateRegister;
00088         updateRegister.attach(client, &MbedClient::test_update_register, 25.0f);
00089 
00090         return true;
00091     }
00092 
00093     bool setup(NetworkInterface* iface) {
00094         debug_msg("[SMC] In mbed_client_setup\r\n");
00095         if (client) {
00096             debug_msg("[SMC] [ERROR] mbed_client_setup called, but mbed_client is already instantiated\r\n");
00097             return false;
00098         }
00099 
00100         struct MbedClientOptions options = get_default_options();
00101 
00102         Callback<void(string)> updateFp(this, &SimpleMbedClientBase::resource_updated);
00103         client = new MbedClient(options, updateFp, debug);
00104 
00105         return init(iface);
00106     }
00107 
00108     bool setup(MbedClientOptions options, NetworkInterface* iface) {
00109         if (client) {
00110             debug_msg("[SMC] [ERROR] mbed_client_setup called, but mbed_client is already instantiated\r\n");
00111             return false;
00112         }
00113 
00114         Callback<void(string)> updateFp(this, &SimpleMbedClientBase::resource_updated);
00115         client = new MbedClient(options, updateFp, debug);
00116 
00117         return init(iface);
00118     }
00119 
00120     void on_registered(void(*fn)(void)) {
00121         Callback<void()> fp(fn);
00122         client->set_registered_function(fp);
00123     }
00124 
00125     template<typename T>
00126     void on_registered(T *object, void (T::*member)(void)) {
00127         Callback<void()> fp(object, member);
00128         client->set_registered_function(fp);
00129     }
00130 
00131     void on_unregistered(void(*fn)(void)) {
00132         Callback<void()> fp(fn);
00133         client->set_unregistered_function(fp);
00134     }
00135 
00136     template<typename T>
00137     void on_unregistered(T *object, void (T::*member)(void)) {
00138         Callback<void()> fp(object, member);
00139         client->set_unregistered_function(fp);
00140     }
00141 
00142     bool define_function(const char* route, void(*fn)(void*)) {
00143         if (!define_resource_internal(route, string(), M2MBase::POST_ALLOWED, false)) {
00144             return false;
00145         }
00146 
00147         string route_str(route);
00148         if (!resources.count(route_str)) {
00149             debug_msg("[SMC] [ERROR] Should be created, but no such route (%s)\r\n", route);
00150             return false;
00151         }
00152 
00153         resources[route_str]->set_execute_function(execute_callback_2(fn));
00154         return true;
00155     }
00156 
00157     bool define_function(const char* route, execute_callback fn) {
00158         if (!define_resource_internal(route, string(), M2MBase::POST_ALLOWED, false)) {
00159             return false;
00160         }
00161 
00162         string route_str(route);
00163         if (!resources.count(route_str)) {
00164             debug_msg("[SMC] [ERROR] Should be created, but no such route (%s)\r\n", route);
00165             return false;
00166         }
00167         // No clue why this is not working?! It works with class member, but not with static function...
00168         resources[route_str]->set_execute_function(fn);
00169         return true;
00170     }
00171 
00172     string get(string route_str) {
00173         if (!resources.count(route_str)) {
00174             debug_msg("[SMC] [ERROR] No such route (%s)\r\n", route_str.c_str());
00175             return string();
00176         }
00177 
00178         // otherwise ask mbed Client...
00179         uint8_t* buffIn = NULL;
00180         uint32_t sizeIn;
00181         resources[route_str]->get_value(buffIn, sizeIn);
00182 
00183         string s((char*)buffIn, sizeIn);
00184         return s;
00185     }
00186 
00187     bool set(string route_str, string v) {
00188         // Potentially set() happens in InterruptContext. That's not good.
00189         if (!resources.count(route_str)) {
00190             debug_msg("[SMC] [ERROR] No such route (%s)\r\n", route_str.c_str());
00191             return false;
00192         }
00193 
00194         if (v.length() == 0) {
00195             resources[route_str]->clear_value();
00196         }
00197         else {
00198             resources[route_str]->set_value((uint8_t*)v.c_str(), v.length());
00199         }
00200 
00201         return true;
00202     }
00203 
00204     bool set(string route, const int& v) {
00205         stringstream ss;
00206         ss << v;
00207         std::string stringified = ss.str();
00208 
00209         return set(route, stringified);
00210     }
00211 
00212     bool define_resource_internal(const char* route, std::string v, M2MBase::Operation opr, bool observable) {
00213         if (client) {
00214             debug_msg("[SMC] [ERROR] mbed_client_define_resource, Can only define resources before mbed_client_setup is called!\r\n");
00215             return false;
00216         }
00217 
00218         vector<string> segments = parse_route(route);
00219         if (segments.size() != 3) {
00220             debug_msg("[SMC] [ERROR] mbed_client_define_resource, Route needs to have three segments, split by '/' (%s)\r\n", route);
00221             return false;
00222         }
00223 
00224         // segments[1] should be one digit and numeric
00225         char n = segments.at(1).c_str()[0];
00226         if (n < '0' || n > '9') {
00227             debug_msg("[SMC] [ERROR] mbed_client_define_resource, second route segment should be numeric, but was not (%s)\r\n", route);
00228             return false;
00229         }
00230 
00231         int inst_id = atoi(segments.at(1).c_str());
00232 
00233         M2MObjectInstance* inst;
00234         if (objectInstances.count(segments.at(0))) {
00235             inst = objectInstances[segments.at(0)];
00236         }
00237         else {
00238             M2MObject* obj = M2MInterfaceFactory::create_object(segments.at(0).c_str());
00239             inst = obj->create_object_instance(inst_id);
00240             objects.insert(std::pair<string, M2MObject*>(segments.at(0), obj));
00241             objectInstances.insert(std::pair<string, M2MObjectInstance*>(segments.at(0), inst));
00242         }
00243 
00244         // @todo check if the resource exists yet
00245         M2MResource* res = inst->create_dynamic_resource(segments.at(2).c_str(), "",
00246             M2MResourceInstance::STRING, observable);
00247         res->set_operation(opr);
00248         res->set_value((uint8_t*)v.c_str(), v.length());
00249 
00250         string route_str(route);
00251         resources.insert(pair<string, M2MResource*>(route_str, res));
00252 
00253         return true;
00254     }
00255 
00256     void keep_alive() {
00257         client->test_update_register();
00258     }
00259 
00260     void register_update_callback(string route, SimpleResourceBase* simpleResource) {
00261         updateValues[route] = simpleResource;
00262     }
00263 
00264     M2MResource* get_resource(string route) {
00265         if (!resources.count(route)) {
00266             debug_msg("[SMC] [ERROR] No such route (%s)\r\n", route.c_str());
00267             return NULL;
00268         }
00269 
00270         return resources[route];
00271     }
00272 
00273 private:
00274     vector<string> parse_route(const char* route) {
00275         string s(route);
00276         vector<string> v;
00277         stringstream ss(s);
00278         string item;
00279         while (getline(ss, item, '/')) {
00280             v.push_back(item);
00281         }
00282         return v;
00283     }
00284 
00285     void resource_updated(string uri) {
00286         if (updateValues.count(uri) == 0) return;
00287 
00288         string v = get(uri);
00289         if (v.empty()) return;
00290 
00291         updateValues[uri]->update(v);
00292     }
00293 
00294     Serial output;
00295 
00296     MbedClient* client;
00297     map<string, M2MObject*> objects;
00298     map<string, M2MObjectInstance*> objectInstances;
00299     map<string, M2MResource*> resources;
00300 
00301     bool debug;
00302 
00303     map<string, SimpleResourceBase*> updateValues;
00304 };
00305 
00306 class SimpleResourceString : public SimpleResourceBase {
00307 public:
00308     SimpleResourceString(SimpleMbedClientBase* aSimpleClient, string aRoute, Callback<void(string)> aOnUpdate) :
00309         simpleClient(aSimpleClient), route(aRoute), onUpdate(aOnUpdate) {}
00310 
00311     string operator=(const string& newValue) {
00312         simpleClient->set(route, newValue);
00313         return newValue;
00314     };
00315     operator string() const {
00316         return simpleClient->get(route);
00317     };
00318 
00319     virtual void update(string v) {
00320         if (onUpdate) onUpdate(v);
00321     }
00322 
00323     M2MResource* get_resource() {
00324         return simpleClient->get_resource(route);
00325     }
00326 
00327 private:
00328     SimpleMbedClientBase* simpleClient;
00329     string route;
00330     Callback<void(string)> onUpdate;
00331 };
00332 
00333 class SimpleResourceInt : public SimpleResourceBase {
00334 public:
00335     SimpleResourceInt(SimpleMbedClientBase* aSimpleClient, string aRoute, Callback<void(int)> aOnUpdate) :
00336         simpleClient(aSimpleClient), route(aRoute), onUpdate(aOnUpdate) {}
00337 
00338     int operator=(int newValue) {
00339         simpleClient->set(route, newValue);
00340         return newValue;
00341     };
00342     operator int() const {
00343         string v = simpleClient->get(route);
00344         if (v.empty()) return 0;
00345 
00346         return atoi((const char*)v.c_str());
00347     };
00348 
00349     virtual void update(string v) {
00350         if (!onUpdate) return;
00351 
00352         onUpdate(atoi((const char*)v.c_str()));
00353     }
00354 
00355     M2MResource* get_resource() {
00356         return simpleClient->get_resource(route);
00357     }
00358 
00359 private:
00360     SimpleMbedClientBase* simpleClient;
00361     string route;
00362     Callback<void(int)> onUpdate;
00363 };
00364 
00365 class SimpleMbedClient : public SimpleMbedClientBase {
00366 public:
00367 
00368     // @todo: macro this up
00369 
00370     SimpleResourceString define_resource(
00371         const char* route,
00372         string v,
00373         M2MBase::Operation opr = M2MBase::GET_PUT_ALLOWED,
00374         bool observable = true,
00375         Callback<void(string)> onUpdate = NULL)
00376     {
00377         SimpleResourceString* simpleResource = new SimpleResourceString(this, route, onUpdate);
00378         bool res = define_resource_internal(route, v, opr, observable);
00379         if (!res) {
00380             printf("Error while creating %s\n", route);
00381         }
00382         else {
00383             register_update_callback(route, simpleResource);
00384         }
00385         return *simpleResource;
00386     }
00387 
00388     SimpleResourceString define_resource(
00389         const char* route,
00390         string v,
00391         M2MBase::Operation opr,
00392         bool observable,
00393         void(*onUpdate)(string))
00394     {
00395         Callback<void(string)> fp;
00396         fp.attach(onUpdate);
00397         return define_resource(route, v, opr, observable, fp);
00398     }
00399 
00400     SimpleResourceString define_resource(
00401         const char* route,
00402         string v,
00403         Callback<void(string)> onUpdate)
00404     {
00405         return define_resource(route, v, M2MBase::GET_PUT_ALLOWED, true, onUpdate);
00406     }
00407 
00408     SimpleResourceString define_resource(
00409         const char* route,
00410         string v,
00411         void(*onUpdate)(string))
00412     {
00413         Callback<void(string)> fp;
00414         fp.attach(onUpdate);
00415         return define_resource(route, v, M2MBase::GET_PUT_ALLOWED, true, fp);
00416     }
00417 
00418     SimpleResourceInt define_resource(
00419         const char* route,
00420         int v,
00421         M2MBase::Operation opr = M2MBase::GET_PUT_ALLOWED,
00422         bool observable = true,
00423         Callback<void(int)> onUpdate = NULL)
00424     {
00425         SimpleResourceInt* simpleResource = new SimpleResourceInt(this, route, onUpdate);
00426 
00427         stringstream ss;
00428         ss << v;
00429         std::string stringified = ss.str();
00430         bool res = define_resource_internal(route, stringified, opr, observable);
00431         if (!res) {
00432             printf("Error while creating %s\n", route);
00433         }
00434         else {
00435             register_update_callback(route, simpleResource);
00436         }
00437         return *simpleResource;
00438     }
00439 
00440     SimpleResourceInt define_resource(
00441         const char* route,
00442         int v,
00443         M2MBase::Operation opr,
00444         bool observable,
00445         void(*onUpdate)(int))
00446     {
00447         Callback<void(int)> fp;
00448         fp.attach(onUpdate);
00449         return define_resource(route, v, opr, observable, fp);
00450     }
00451 
00452     SimpleResourceInt define_resource(
00453         const char* route,
00454         int v,
00455         Callback<void(int)> onUpdate)
00456     {
00457         return define_resource(route, v, M2MBase::GET_PUT_ALLOWED, true, onUpdate);
00458     }
00459 
00460     SimpleResourceInt define_resource(
00461         const char* route,
00462         int v,
00463         void(*onUpdate)(int))
00464     {
00465         Callback<void(int)> fp;
00466         fp.attach(onUpdate);
00467         return define_resource(route, v, M2MBase::GET_PUT_ALLOWED, true, fp);
00468     }
00469 };
00470 
00471 #endif // __SIMPLE_MBED_CLIENT_H__