A simple interface to mbed Device Connector, where you just declare variables to push them to the cloud.

Dependents:   Wifi_Get_Test_V1 simple-mbed-client-example simple-client-app-shield simple-sensor-client

Fork of simple-mbed-client by Jan Jongboom

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 smc_debug_msg(...) if (debug) output.printf(__VA_ARGS__)
00021 
00022 #include <map>
00023 #include <string>
00024 #include <vector>
00025 #include "mbed-client-wrapper.h"
00026 
00027 using namespace std;
00028 
00029 class SimpleResourceBase {
00030 public:
00031     virtual void update(string v) {}
00032     virtual void clear_pending_value() {}
00033 };
00034 
00035 class SimpleMbedClientBase {
00036 public:
00037 
00038     SimpleMbedClientBase(bool aDebug = true)
00039         : output(USBTX, USBRX), evQueue(new EventQueue()), evThread(new Thread()), debug(aDebug)
00040     {
00041         evThread->start(callback(evQueue, &EventQueue::dispatch_forever));
00042     }
00043 
00044     SimpleMbedClientBase(EventQueue* aQueue, bool aDebug = true)
00045         : output(USBTX, USBRX), evQueue(aQueue), debug(aDebug)
00046     {
00047 
00048     }
00049 
00050     ~SimpleMbedClientBase() {
00051         if (evThread) {
00052             delete evQueue;
00053             delete evThread;
00054         }
00055         // if no evThread then evQueue is not owned by us
00056     }
00057 
00058     struct MbedClientOptions get_default_options() {
00059         struct MbedClientOptions options;
00060         options.Manufacturer = "Manufacturer_String";
00061         options.Type = "Type_String";
00062         options.ModelNumber = "ModelNumber_String";
00063         options.SerialNumber = "SerialNumber_String";
00064         options.DeviceType = "test";
00065         options.SocketMode = M2MInterface::TCP;
00066 #ifdef MBED_SERVER_ADDRESS
00067         options.ServerAddress = MBED_SERVER_ADDRESS;
00068 #else
00069         options.ServerAddress = "coap://api.connector.mbed.com:5684";
00070 #endif
00071 
00072         return options;
00073     }
00074 
00075     bool init(NetworkInterface* iface) {
00076         smc_debug_msg("[SMC] Device name %s\r\n", MBED_ENDPOINT_NAME);
00077 
00078         // Create endpoint interface to manage register and unregister
00079         client->create_interface(iface);
00080 
00081         // Create Objects of varying types, see simpleclient.h for more details on implementation.
00082         M2MSecurity* register_object = client->create_register_object(); // server object specifying connector info
00083         M2MDevice*   device_object   = client->create_device_object();   // device resources object
00084 
00085         // Create list of Objects to register
00086         M2MObjectList object_list;
00087 
00088         // Add objects to list
00089         object_list.push_back(device_object);
00090 
00091         map<string, M2MObject*>::iterator it;
00092         for (it = objects.begin(); it != objects.end(); it++)
00093         {
00094             object_list.push_back(it->second);
00095         }
00096 
00097         // Set endpoint registration object
00098         client->set_register_object(register_object);
00099 
00100         // Issue register command.
00101         client->test_register(register_object, object_list);
00102 
00103         // Keep alive ticker (every 25 seconds)
00104         evQueue->call_every(MBED_CONF_SIMPLE_MBED_CLIENT_UPDATE_INTERVAL,
00105             callback(this, &SimpleMbedClientBase::keep_alive));
00106 
00107         return true;
00108     }
00109 
00110     bool setup(NetworkInterface* iface) {
00111         smc_debug_msg("[SMC] In mbed_client_setup\r\n");
00112         if (client) {
00113             smc_debug_msg("[SMC] [ERROR] mbed_client_setup called, but mbed_client is already instantiated\r\n");
00114             return false;
00115         }
00116 
00117         struct MbedClientOptions options = get_default_options();
00118 
00119         Callback<void(string)> updateFp(this, &SimpleMbedClientBase::resource_updated);
00120         client = new MbedClient(options, updateFp, debug);
00121 
00122         return init(iface);
00123     }
00124 
00125     bool setup(MbedClientOptions options, NetworkInterface* iface) {
00126         if (client) {
00127             smc_debug_msg("[SMC] [ERROR] mbed_client_setup called, but mbed_client is already instantiated\r\n");
00128             return false;
00129         }
00130 
00131         Callback<void(string)> updateFp(this, &SimpleMbedClientBase::resource_updated);
00132         client = new MbedClient(options, updateFp, debug);
00133 
00134         return init(iface);
00135     }
00136 
00137     void on_registered(void(*fn)(void)) {
00138         Callback<void()> fp(fn);
00139         client->set_registered_function(fp);
00140     }
00141 
00142     void on_registered(Callback<void()> fp) {
00143         client->set_registered_function(fp);
00144     }
00145 
00146     template<typename T>
00147     void on_registered(T *object, void (T::*member)(void)) {
00148         Callback<void()> fp(object, member);
00149         client->set_registered_function(fp);
00150     }
00151 
00152     void on_unregistered(void(*fn)(void)) {
00153         Callback<void()> fp(fn);
00154         client->set_unregistered_function(fp);
00155     }
00156 
00157     void on_unregistered(Callback<void()> fp) {
00158         client->set_unregistered_function(fp);
00159     }
00160 
00161     template<typename T>
00162     void on_unregistered(T *object, void (T::*member)(void)) {
00163         Callback<void()> fp(object, member);
00164         client->set_unregistered_function(fp);
00165     }
00166 
00167     bool define_function(const char* route, Event<void(void*)> ev) {
00168         if (!define_resource_internal(route, string(), M2MBase::POST_ALLOWED, false)) {
00169             return false;
00170         }
00171 
00172         string route_str(route);
00173         if (!resources.count(route_str)) {
00174             smc_debug_msg("[SMC] [ERROR] Should be created, but no such route (%s)\r\n", route);
00175             return false;
00176         }
00177 
00178         // We need a copy of the event. The original event might go out of scope.
00179         // @todo, do we need to clear this? It's actually meant to live until the end of the program... But it's not nice to alloc and never free.
00180         Event<void(void*)>* copy = new Event<void(void*)>(ev);
00181 
00182         // Event::call is const, which FP1 does not like. Cast it to non-const.
00183         FP1<void, void*> fp(copy, (void (events::Event<void(void*)>::*)(void*))&Event<void(void*)>::call);
00184 
00185         resources[route_str]->set_execute_function(fp);
00186         return true;
00187     }
00188 
00189     bool define_function(const char* route, void(*fn)(void*)) {
00190         if (!define_resource_internal(route, string(), M2MBase::POST_ALLOWED, false)) {
00191             return false;
00192         }
00193 
00194         string route_str(route);
00195         if (!resources.count(route_str)) {
00196             smc_debug_msg("[SMC] [ERROR] Should be created, but no such route (%s)\r\n", route);
00197             return false;
00198         }
00199 
00200         resources[route_str]->set_execute_function(execute_callback_2(fn));
00201         return true;
00202     }
00203 
00204     bool define_function(const char* route, execute_callback fn) {
00205         if (!define_resource_internal(route, string(), M2MBase::POST_ALLOWED, false)) {
00206             return false;
00207         }
00208 
00209         string route_str(route);
00210         if (!resources.count(route_str)) {
00211             smc_debug_msg("[SMC] [ERROR] Should be created, but no such route (%s)\r\n", route);
00212             return false;
00213         }
00214         // No clue why this is not working?! It works with class member, but not with static function...
00215         resources[route_str]->set_execute_function(fn);
00216         return true;
00217     }
00218 
00219     string get(string route_str) {
00220         if (!resources.count(route_str)) {
00221             smc_debug_msg("[SMC] [ERROR] No such route (%s)\r\n", route_str.c_str());
00222             return string();
00223         }
00224 
00225         // otherwise ask mbed Client...
00226         uint8_t* buffIn = NULL;
00227         uint32_t sizeIn;
00228         resources[route_str]->get_value(buffIn, sizeIn);
00229 
00230         string s((char*)buffIn, sizeIn);
00231         return s;
00232     }
00233 
00234     // Note: these `set` calls are async.
00235     // SimpleResource* buffers the value, so when using it through operators you'll get the right value back.
00236     void set(string route_str, string v) {
00237         evQueue->call(callback(this, &SimpleMbedClientBase::internal_set_str), route_str, v);
00238     }
00239 
00240     void set(string route_str, const int& v) {
00241         evQueue->call(callback(this, &SimpleMbedClientBase::internal_set_int), route_str, v);
00242     }
00243 
00244     void set(string route_str, const float& v) {
00245         evQueue->call(callback(this, &SimpleMbedClientBase::internal_set_float), route_str, v);
00246     }
00247 
00248     bool define_resource_internal(const char* route, string v, M2MBase::Operation opr, bool observable) {
00249         if (client) {
00250             smc_debug_msg("[SMC] [ERROR] mbed_client_define_resource, Can only define resources before mbed_client_setup is called!\r\n");
00251             return false;
00252         }
00253 
00254         vector<string> segments = parse_route(route);
00255         if (segments.size() != 3) {
00256             smc_debug_msg("[SMC] [ERROR] mbed_client_define_resource, Route needs to have three segments, split by '/' (%s)\r\n", route);
00257             return false;
00258         }
00259 
00260         // segments[1] should be one digit and numeric
00261         char n = segments.at(1).c_str()[0];
00262         if (n < '0' || n > '9') {
00263             smc_debug_msg("[SMC] [ERROR] mbed_client_define_resource, second route segment should be numeric, but was not (%s)\r\n", route);
00264             return false;
00265         }
00266 
00267         int inst_id = atoi(segments.at(1).c_str());
00268 
00269         M2MObjectInstance* inst;
00270         if (objectInstances.count(segments.at(0))) {
00271             inst = objectInstances[segments.at(0)];
00272         }
00273         else {
00274             M2MObject* obj = M2MInterfaceFactory::create_object(segments.at(0).c_str());
00275             inst = obj->create_object_instance(inst_id);
00276             objects.insert(std::pair<string, M2MObject*>(segments.at(0), obj));
00277             objectInstances.insert(std::pair<string, M2MObjectInstance*>(segments.at(0), inst));
00278         }
00279 
00280         // @todo check if the resource exists yet
00281         M2MResource* res = inst->create_dynamic_resource(segments.at(2).c_str(), "",
00282             M2MResourceInstance::STRING, observable);
00283         res->set_operation(opr);
00284         res->set_value((uint8_t*)v.c_str(), v.length());
00285 
00286         string route_str(route);
00287         resources.insert(pair<string, M2MResource*>(route_str, res));
00288 
00289         return true;
00290     }
00291 
00292     void register_update_callback(string route, SimpleResourceBase* simpleResource) {
00293         updateValues[route] = simpleResource;
00294     }
00295 
00296     M2MResource* get_resource(string route) {
00297         if (!resources.count(route)) {
00298             smc_debug_msg("[SMC] [ERROR] No such route (%s)\r\n", route.c_str());
00299             return NULL;
00300         }
00301 
00302         return resources[route];
00303     }
00304 
00305     EventQueue* eventQueue() const {
00306         return evQueue;
00307     }
00308 
00309 private:
00310     vector<string> parse_route(const char* route) {
00311         const string s(route);
00312         vector<string> v;
00313 
00314         split(s, '/', v);
00315 
00316         return v;
00317     }
00318 
00319     void split(const string& s, char delim, vector<string>& v) {
00320         size_t i = 0;
00321         size_t pos = s.find(delim);
00322         while (pos != string::npos) {
00323             v.push_back(s.substr(i, pos - i));
00324             i = ++pos;
00325             pos = s.find(delim, pos);
00326 
00327             if (pos == string::npos) {
00328                 v.push_back(s.substr(i, s.length()));
00329             }
00330         }
00331     }
00332 
00333     void resource_updated(string uri) {
00334         if (updateValues.count(uri) == 0) return;
00335 
00336         string v = get(uri);
00337         if (v.empty()) return;
00338 
00339         // Schedule this on the other thread, to avoid blocking this thread
00340         evQueue->call(callback(updateValues[uri], &SimpleResourceBase::update), v);
00341     }
00342 
00343     // These operations have side effects, they should not be called immediately,
00344     // but always through the eventqueue
00345     void internal_set_str(string route_str, string v) {
00346         if (!resources.count(route_str)) {
00347             smc_debug_msg("[SMC] [ERROR] No such route (%s)\r\n", route_str.c_str());
00348             return;
00349         }
00350 
00351         if (v.length() == 0) {
00352             resources[route_str]->clear_value();
00353         }
00354         else {
00355             resources[route_str]->set_value((uint8_t*)v.c_str(), v.length());
00356         }
00357 
00358         updateValues[route_str]->clear_pending_value();
00359     }
00360 
00361     void internal_set_int(string route, const int& v) {
00362         char str[13];
00363         sprintf(str, "%d", v);
00364 
00365         internal_set_str(route, string(str));
00366     }
00367 
00368     void internal_set_float(string route, const float& v) {
00369         int size = snprintf(NULL, 0, "%g", v);
00370 
00371         char str[size];
00372         sprintf(str, "%g", v);
00373 
00374         internal_set_str(route, string(str));
00375     }
00376 
00377     void keep_alive() {
00378         client->test_update_register();
00379     }
00380 
00381     Serial output;
00382 
00383     MbedClient* client;
00384     map<string, M2MObject*> objects;
00385     map<string, M2MObjectInstance*> objectInstances;
00386     map<string, M2MResource*> resources;
00387 
00388     EventQueue*     evQueue;
00389     Thread*         evThread;
00390 
00391     bool debug;
00392 
00393     map<string, SimpleResourceBase*> updateValues;
00394 };
00395 
00396 class SimpleResourceString : public SimpleResourceBase {
00397 public:
00398     SimpleResourceString(SimpleMbedClientBase* aSimpleClient, string aRoute, Callback<void(string)> aOnUpdate) :
00399         simpleClient(aSimpleClient), route(aRoute), onUpdate(aOnUpdate), hasPendingValue(false) {}
00400 
00401     string operator=(const string& newValue) {
00402         pendingValue = newValue;
00403         hasPendingValue = true;
00404 
00405         simpleClient->set(route, newValue);
00406         return newValue;
00407     };
00408 
00409     operator string() const {
00410         if (hasPendingValue) {
00411             return pendingValue;
00412         }
00413 
00414         return simpleClient->get(route);
00415     };
00416 
00417     virtual void update(string v) {
00418         if (onUpdate) onUpdate(v);
00419     }
00420 
00421     M2MResource* get_resource() {
00422         return simpleClient->get_resource(route);
00423     }
00424 
00425     virtual void clear_pending_value() {
00426         hasPendingValue = false;
00427     }
00428 
00429 private:
00430     SimpleMbedClientBase* simpleClient;
00431     string route;
00432     Callback<void(string)> onUpdate;
00433 
00434     // set() is async (because on the event queue, so store the pending value here...)
00435     bool hasPendingValue;
00436     string pendingValue;
00437 };
00438 
00439 class SimpleResourceInt : public SimpleResourceBase {
00440 public:
00441     SimpleResourceInt(SimpleMbedClientBase* aSimpleClient, string aRoute, Callback<void(int)> aOnUpdate) :
00442         simpleClient(aSimpleClient), route(aRoute), onUpdate(aOnUpdate), hasPendingValue(false), pendingValue(0) {}
00443 
00444     int operator=(int newValue) {
00445         pendingValue = newValue;
00446         hasPendingValue = true;
00447 
00448         simpleClient->set(route, newValue);
00449         return newValue;
00450     };
00451     operator int() const {
00452         if (hasPendingValue) {
00453             return pendingValue;
00454         }
00455 
00456         string v = simpleClient->get(route);
00457         if (v.empty()) return 0;
00458 
00459         return atoi((const char*)v.c_str());
00460     };
00461 
00462     virtual void update(string v) {
00463         if (!onUpdate) return;
00464 
00465         onUpdate(atoi((const char*)v.c_str()));
00466     }
00467 
00468     M2MResource* get_resource() {
00469         return simpleClient->get_resource(route);
00470     }
00471 
00472     virtual void clear_pending_value() {
00473         hasPendingValue = false;
00474     }
00475 
00476 private:
00477     SimpleMbedClientBase* simpleClient;
00478     string route;
00479     Callback<void(int)> onUpdate;
00480 
00481     // set() is async (because on the event queue, so store the pending value here...)
00482     bool hasPendingValue;
00483     int pendingValue;
00484 };
00485 
00486 class SimpleResourceFloat : public SimpleResourceBase {
00487 public:
00488     SimpleResourceFloat(SimpleMbedClientBase* aSimpleClient, string aRoute, Callback<void(float)> aOnUpdate) :
00489         simpleClient(aSimpleClient), route(aRoute), onUpdate(aOnUpdate), hasPendingValue(false), pendingValue(0) {}
00490 
00491     float operator=(float newValue) {
00492         pendingValue = newValue;
00493         hasPendingValue = true;
00494 
00495         simpleClient->set(route, newValue);
00496         return newValue;
00497     };
00498     operator float() const {
00499         if (hasPendingValue) {
00500             return pendingValue;
00501         }
00502 
00503         string v = simpleClient->get(route);
00504         if (v.empty()) return 0;
00505 
00506         return atof((const char*)v.c_str());
00507     };
00508 
00509     virtual void update(string v) {
00510         if (!onUpdate) return;
00511 
00512         onUpdate(atof((const char*)v.c_str()));
00513     }
00514 
00515     M2MResource* get_resource() {
00516         return simpleClient->get_resource(route);
00517     }
00518 
00519     virtual void clear_pending_value() {
00520         hasPendingValue = false;
00521     }
00522 
00523 private:
00524     SimpleMbedClientBase* simpleClient;
00525     string route;
00526     Callback<void(float)> onUpdate;
00527 
00528     // set() is async (because on the event queue, so store the pending value here...)
00529     bool hasPendingValue;
00530     float pendingValue;
00531 };
00532 
00533 class SimpleMbedClient : public SimpleMbedClientBase {
00534 public:
00535 
00536     SimpleMbedClient(bool aDebug = true)
00537         : SimpleMbedClientBase(aDebug)
00538     {
00539 
00540     }
00541 
00542     SimpleMbedClient(EventQueue* aQueue, bool aDebug = true)
00543         : SimpleMbedClientBase(aQueue, aDebug)
00544     {
00545 
00546     }
00547 
00548     // @todo: macro this up
00549 
00550     // String
00551     SimpleResourceString define_resource(
00552         const char* route,
00553         string v,
00554         M2MBase::Operation opr = M2MBase::GET_PUT_ALLOWED,
00555         bool observable = true,
00556         Callback<void(string)> onUpdate = NULL)
00557     {
00558         SimpleResourceString* simpleResource = new SimpleResourceString(this, route, onUpdate);
00559         bool res = define_resource_internal(route, v, opr, observable);
00560         if (!res) {
00561             printf("Error while creating %s\n", route);
00562         }
00563         else {
00564             register_update_callback(route, simpleResource);
00565         }
00566         return *simpleResource;
00567     }
00568 
00569     SimpleResourceString define_resource(
00570         const char* route,
00571         string v,
00572         M2MBase::Operation opr,
00573         bool observable,
00574         void(*onUpdate)(string))
00575     {
00576         Callback<void(string)> fp;
00577         fp.attach(onUpdate);
00578         return define_resource(route, v, opr, observable, fp);
00579     }
00580 
00581     SimpleResourceString define_resource(
00582         const char* route,
00583         string v,
00584         Callback<void(string)> onUpdate)
00585     {
00586         return define_resource(route, v, M2MBase::GET_PUT_ALLOWED, true, onUpdate);
00587     }
00588 
00589     SimpleResourceString define_resource(
00590         const char* route,
00591         string v,
00592         void(*onUpdate)(string))
00593     {
00594         Callback<void(string)> fp;
00595         fp.attach(onUpdate);
00596         return define_resource(route, v, M2MBase::GET_PUT_ALLOWED, true, fp);
00597     }
00598 
00599     // Int
00600     SimpleResourceInt define_resource(
00601         const char* route,
00602         int v,
00603         M2MBase::Operation opr = M2MBase::GET_PUT_ALLOWED,
00604         bool observable = true,
00605         Callback<void(int)> onUpdate = NULL)
00606     {
00607         SimpleResourceInt* simpleResource = new SimpleResourceInt(this, route, onUpdate);
00608 
00609         char str[13];
00610         sprintf(str, "%d", v);
00611 
00612         bool res = define_resource_internal(route, string(str), opr, observable);
00613         if (!res) {
00614             printf("Error while creating %s\n", route);
00615         }
00616         else {
00617             register_update_callback(route, simpleResource);
00618         }
00619         return *simpleResource;
00620     }
00621 
00622     SimpleResourceInt define_resource(
00623         const char* route,
00624         int v,
00625         M2MBase::Operation opr,
00626         bool observable,
00627         void(*onUpdate)(int))
00628     {
00629         Callback<void(int)> fp;
00630         fp.attach(onUpdate);
00631         return define_resource(route, v, opr, observable, fp);
00632     }
00633 
00634     SimpleResourceInt define_resource(
00635         const char* route,
00636         int v,
00637         Callback<void(int)> onUpdate)
00638     {
00639         return define_resource(route, v, M2MBase::GET_PUT_ALLOWED, true, onUpdate);
00640     }
00641 
00642     SimpleResourceInt define_resource(
00643         const char* route,
00644         int v,
00645         void(*onUpdate)(int))
00646     {
00647         Callback<void(int)> fp;
00648         fp.attach(onUpdate);
00649         return define_resource(route, v, M2MBase::GET_PUT_ALLOWED, true, fp);
00650     }
00651 
00652     // Float
00653     SimpleResourceFloat define_resource(
00654         const char* route,
00655         float v,
00656         M2MBase::Operation opr = M2MBase::GET_PUT_ALLOWED,
00657         bool observable = true,
00658         Callback<void(float)> onUpdate = NULL)
00659     {
00660         SimpleResourceFloat* simpleResource = new SimpleResourceFloat(this, route, onUpdate);
00661 
00662         int size = snprintf(NULL, 0, "%g", v);
00663 
00664         // malloc() would probably be better here
00665         char str[size];
00666         sprintf(str, "%g", v);
00667 
00668         bool res = define_resource_internal(route, string(str), opr, observable);
00669         if (!res) {
00670             printf("Error while creating %s\n", route);
00671         }
00672         else {
00673             register_update_callback(route, simpleResource);
00674         }
00675         return *simpleResource;
00676     }
00677 
00678     SimpleResourceFloat define_resource(
00679         const char* route,
00680         float v,
00681         M2MBase::Operation opr,
00682         bool observable,
00683         void(*onUpdate)(float))
00684     {
00685         Callback<void(float)> fp;
00686         fp.attach(onUpdate);
00687         return define_resource(route, v, opr, observable, fp);
00688     }
00689 
00690     SimpleResourceFloat define_resource(
00691         const char* route,
00692         float v,
00693         Callback<void(float)> onUpdate)
00694     {
00695         return define_resource(route, v, M2MBase::GET_PUT_ALLOWED, true, onUpdate);
00696     }
00697 
00698     SimpleResourceFloat define_resource(
00699         const char* route,
00700         float v,
00701         void(*onUpdate)(float))
00702     {
00703         Callback<void(float)> fp;
00704         fp.attach(onUpdate);
00705         return define_resource(route, v, M2MBase::GET_PUT_ALLOWED, true, fp);
00706     }
00707 };
00708 
00709 #endif // __SIMPLE_MBED_CLIENT_H__