Remote Procedure Call (RPC) over Websockets (uses MbedJSONValue)

Dependents:   RPC_mbed_client RPC_Wifly_HelloWorld RPC_Ethernet_HelloWorld

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers MbedJSONRpc.h Source File

MbedJSONRpc.h

00001 /**
00002 * @author Samuel Mokrani
00003 *
00004 * @section LICENSE
00005 *
00006 * Copyright (c) 2011 mbed
00007 *
00008 * Permission is hereby granted, free of charge, to any person obtaining a copy
00009 * of this software and associated documentation files (the "Software"), to deal
00010 * in the Software without restriction, including without limitation the rights
00011 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00012 * copies of the Software, and to permit persons to whom the Software is
00013 * furnished to do so, subject to the following conditions:
00014 *
00015 * The above copyright notice and this permission notice shall be included in
00016 * all copies or substantial portions of the Software.
00017 *
00018 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00019 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00020 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
00021 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00022 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00023 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
00024 * THE SOFTWARE.
00025 *
00026 * @section DESCRIPTION
00027 *       Main part of MbedJSONRpc: you can register methods or procedures, call a registered method or procedure
00028 *       on a client over a websocket communication, see all methods available on a certain client, listen for 
00029 *       CALL incoming messages to execute a method or procedure.
00030 *
00031 */
00032 
00033 
00034 #ifndef _MbedJSONRPC_H_
00035 #define _MbedJSONRPC_H_
00036 
00037 #include "Websocket.h"
00038 #include "mbed.h"
00039 #include "MbedJSONValue.h"
00040 #include <string>
00041 
00042 #define NB_METH 30
00043 
00044 #define MSG_CALL                    "{\"from\": \"%s\",\"to\": \"%s\",\"msg\": \"CALL\",\"id_msg\": %d,\"method\": \"%s\",\"params\":%s}"
00045 /**< Message skeleton which will be use for a call */
00046 #define MSG_RESULT                  "{\"from\": \"%s\",\"to\": \"%s\",\"msg\": \"RESULT\",\"id_msg\": %d,\"res\":%s}"
00047 /**< Message skeleton which will be use for a result  */
00048 #define MSG_REGISTER                "{\"from\": \"%s\",\"to\": \"gateway\",\"msg\": \"REGISTER\",\"id_msg\": %d,\"fn\":\"%s\"}"
00049 /**< Message skeleton which will be use for register a method or a procedure */
00050 #define MSG_INFO_METHODS            "{\"from\": \"%s\",\"to\": \"%s\",\"msg\": \"INFO_METHODS\",\"id_msg\": %d}"
00051 /**< Message skeleton which will be send to see all methods available of a certain client */
00052 
00053 
00054 /**
00055 * \enum RPC_TYPE
00056 * \brief Type of an RPC transaction
00057 */
00058 enum RPC_TYPE {
00059     JSON_PARSE_ERROR,       /*!< Json parse error */
00060     RPC_PARSE_ERROR,        /*!< rpc parse error (the message doesn't contain good identifiers in a TypeObject MbedJSONValue)*/ 
00061     PROC_NOT_FOUND,         /*!< The user wants to call an inexisting method or procedure */
00062     CLIENT_NOT_CONNECTED,   /*!< The user wants to call a method or procedure on a client not connected (no response from the client within 3s) */
00063     SERVER_NOT_CONNECTED,   /*!< No response from the server within 5s */
00064     ERR_ID,                 /*!< Each messages have an id, if the id of the answer doesn't match with the id of the request, there is an id error */
00065     CALL_OK,                /*!< A call has been successfully executed */
00066     REGISTER_OK             /*!< A request to register a method or procedure is successful */
00067 };
00068 
00069 
00070 
00071 class ObjBase {
00072 public:
00073     virtual void execute(MbedJSONValue& val, MbedJSONValue& res) = 0;
00074 };
00075 
00076 template <class T>
00077 class Obj : public ObjBase {
00078 public:
00079 
00080     Obj(T * ptr, void (T::*fn_ptr)(MbedJSONValue& , MbedJSONValue& )) {
00081         obj_ptr = ptr;
00082         fn = fn_ptr;
00083     };
00084 
00085     virtual void execute(MbedJSONValue& val, MbedJSONValue& res) {
00086         ((*obj_ptr).*fn)(val, res);
00087     }
00088 
00089     //obj
00090     T * obj_ptr;
00091 
00092     //method
00093     void (T::*fn)(MbedJSONValue& , MbedJSONValue& );
00094 
00095 };
00096 
00097 
00098 /** MbedJSONRpc class
00099  *
00100  */
00101 class MbedJSONRpc {
00102 public:
00103 
00104     /**
00105     *   Constructor
00106     *
00107     * @param id Name of the client on the network
00108     * @param ws All communication between clients will be established over this websocket
00109     */
00110     MbedJSONRpc(Websocket * webs) : webs(webs), index(0), index_proc(0) {
00111         std::string path = webs->getPath();
00112         std::string token = "/";
00113         size_t found;
00114         found = path.find(token);
00115         if (found != std::string::npos)
00116             found = path.find(token, found + 1);
00117         if (found != std::string::npos)
00118             path = path.substr(found + 1, std::string::npos);
00119         strcpy(my_id, path.c_str());
00120     };
00121 
00122     /**
00123     * Register a method of an object
00124     *
00125     * @param public_name the method will be seen and called by this name
00126     * @param obj_ptr a pointeur on the object which contains the method to register
00127     * @param fn the method to register (this method must have this signature: void fn(MbedJSONValue& val, MbedJSONValue& res)
00128     * @return if REGISTER_OK, the method is registered, otherwise, there is an error
00129     *
00130     */
00131     template<typename T> RPC_TYPE registerMethod(const char * public_name, T * obj_ptr, void (T::*fn)(MbedJSONValue& val, MbedJSONValue& res)) {
00132         char json[100];
00133         RPC_TYPE t;
00134         Timer tmr;
00135         int id = rand() % 100;
00136         MbedJSONValue tmp;
00137 
00138         sprintf(json, (const char *)MSG_REGISTER, my_id, id, public_name);
00139         webs->send(json);
00140         tmr.start();
00141         t = waitAnswer(tmp, id, json);
00142         if (t != REGISTER_OK)
00143             return t;
00144         if( index == NB_METH )
00145             index = NB_METH - 1;
00146         obj[index] = new Obj<T>(obj_ptr, fn);
00147         name[index++] = public_name;
00148         return REGISTER_OK;
00149     }
00150 
00151 
00152     /**
00153     * Register a procedure
00154     *
00155     * @param public_name the method will be seen and called by this name
00156     * @param fn the procedure to register (this procedure must have this signature: void fn(MbedJSONValue& val, MbedJSONValue& res)
00157     * @return if REGISTER_OK, the procedure is registered, otherwise, there is an error
00158     *
00159     */
00160     RPC_TYPE registerMethod(const char * public_name, void (*fn)(MbedJSONValue& val, MbedJSONValue& res) );
00161     
00162     
00163     /**
00164     * Call a method or procedure on a client
00165     *
00166     * @param fn name of the method or procedure to be called
00167     * @param dest name of the client where will be executed the method or procedure
00168     * @param val Input parameters of the method or procedure "fn"
00169     * @param resp Once the method or procedure executed, the result will be stored in the "resp" variable.
00170     * @return If CALL_OK, the method has been executed and you can access the result with the "resp" variable. 
00171     *         If CLIENT_NOT_CONNECTED, means that the client hasn't answered within 3s.
00172     *         If SERVER_NOT_CONNECTED, means that the server hasn't answered within 5s.
00173     *         Refer to the RPC_TYPE description
00174     *
00175     */
00176     RPC_TYPE call(char * fn, char * dest,  MbedJSONValue& val, MbedJSONValue& resp);
00177     
00178     
00179     /**
00180     * Listen for CALL requests.
00181     * Warning: infinite loop
00182     */
00183     void work();
00184     
00185     /**
00186     * Print by the usb serial port all methods or procedure available on "dest" client
00187     */
00188     void checkMethods(char * dest);
00189 
00190 private:
00191 
00192     typedef void (*FNPTR)(MbedJSONValue& , MbedJSONValue& );
00193 
00194     int methodAlreadyRegistered(char *);
00195     int procAlreadyRegistered(char *);
00196     RPC_TYPE decodeMsg(MbedJSONValue& , int);
00197     RPC_TYPE waitAnswer(MbedJSONValue& , int, char *);
00198 
00199     Websocket * webs;
00200 
00201     ObjBase * obj[NB_METH];
00202     const char * name[NB_METH];
00203     int index;
00204 
00205     FNPTR proc[NB_METH];
00206     const char * name_proc[NB_METH];
00207     int index_proc;
00208 
00209 
00210     char my_id[20];
00211 
00212 };
00213 
00214 
00215 inline void printType(RPC_TYPE t)
00216 {
00217     switch(t)
00218     {
00219         case JSON_PARSE_ERROR: printf("json parse error\r\n"); break;
00220         case RPC_PARSE_ERROR: printf("rpc parse error\r\n"); break;
00221         case PROC_NOT_FOUND: printf("proc or method not found\r\n"); break;
00222         case CLIENT_NOT_CONNECTED: printf("client not connected\r\n"); break;
00223         case SERVER_NOT_CONNECTED: printf("server not connected\r\n"); break;
00224         case ERR_ID: printf("id error\r\n"); break;
00225         case CALL_OK: printf("call ok\r\n"); break;
00226         case REGISTER_OK: printf("register ok\r\n"); break;
00227     }
00228 }
00229 
00230 #endif