mbed client
Fork of simple-mbed-client by
simple-mbed-client.h
- Committer:
- Jan Jongboom
- Date:
- 2016-07-21
- Revision:
- 7:8a05fbad93d1
- Parent:
- 6:a1a766d45957
- Child:
- 9:cfd5d2a7d77d
File content as of revision 7:8a05fbad93d1:
/* * 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::UDP; 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(); FP1<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; } FP1<void, string> updateFp(this, &SimpleMbedClientBase::resource_updated); client = new MbedClient(options, updateFp, debug); 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)) { 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 if (!isdigit(segments.at(1).c_str()[0])) { 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))) { debug_msg("Found object... %s\r\n", segments.at(0).c_str()); inst = objectInstances[segments.at(0)]; } else { debug_msg("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(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, FunctionPointerArg1<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; FunctionPointerArg1<void, string> onUpdate; }; class SimpleResourceInt : public SimpleResourceBase { public: SimpleResourceInt(SimpleMbedClientBase* aSimpleClient, string aRoute, FunctionPointerArg1<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; FunctionPointerArg1<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, FunctionPointerArg1<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)) { FunctionPointerArg1<void, string> fp; fp.attach(onUpdate); return define_resource(route, v, opr, observable, fp); } SimpleResourceString define_resource( const char* route, string v, FunctionPointerArg1<void, string> onUpdate = NULL) { return define_resource(route, v, M2MBase::GET_PUT_ALLOWED, true, onUpdate); } SimpleResourceString define_resource( const char* route, string v, void(*onUpdate)(string)) { FunctionPointerArg1<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, FunctionPointerArg1<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)) { FunctionPointerArg1<void, int> fp; fp.attach(onUpdate); return define_resource(route, v, opr, observable, fp); } SimpleResourceInt define_resource( const char* route, int v, FunctionPointerArg1<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)) { FunctionPointerArg1<void, int> fp; fp.attach(onUpdate); return define_resource(route, v, M2MBase::GET_PUT_ALLOWED, true, fp); } }; #endif // __SIMPLE_MBED_CLIENT_H__