Monday, July 16, 2012

Communicating with RaspBerry via GSoap Web Services

According to wikipedia "A Web service is a method of communication between two electronic devices over the Web (Internet)." I would not say over the 'Web', indeed you can communicate within a local area network without the need on going 'Web'.

On this tutorial we are going to explain step by step how to deploy a Web Service on our Rasberry. We are also going to see how to develop a little application in our laptop that will communicate with this Web Service.

The protocol used on a web service is called 'SOAP' and is based on 'XML'. The following diagram explains in a simplified way how a Web Service communication is carried out.


Fig.1 Simplified SOAP exchange diagram 

The most common usage of Web Services is via HTTP, and that is the reason behind its popularity. In fact all the existing web infrastructure (from routers to web servers) fits perfectly on this model, without the need of adapt absolutely anything.

How the xml is formed, is specified on the SOAP protocol. Normally the commercial Web Services API, like the ones used on .Net, Java, or C++, hide the complexity of the protocol, so we can focus on our object s model and the 'dirty' work of converting those object on 'XML' (and viceversa) is completely transparent for us.

1. Downloading GSoap.

GSoap is a open source Web Service API, written in C and C++. You can download the last version from here.

There are 2 important tools shipped on the download, the one that we are going to use is called 'soapcpp2' and you can find it on the following path:

./gsoap-2.8/gsoap/bin/linux386/soapcpp2


2. Defining our service interface.

On this example we are going to create a service that returns the hour from our Raspberry.

The service interface is defined in a similar way than a library interface. We are going to create a header defining the method that will be called:

Fig. 2 Sevice Definition header


2. Generating the server classes.

Now that our service is defined, we have to generate the classes that will handle the request. Fortunately this can be done automatically by using 'soapcpp2'. Go to your project path (in my case ~/workspace/workspace64/GSoapTuto/MyRaspberryTimeServer/src) and execute soapcpp2 with  the following options:


~/Desktop/GSoap/gsoap-2.8/gsoap/bin/linux386/soapcpp2 -S  currentTime.h

-S indicates server-side only files


If everything went ok, several files should have been added to your project:

Fig 3. Auto generated code


Remove the files 'soapcurrentTimeService.cpp' and 'soapcurrentTimeService.h', and add the files 'stdsoap2.cpp' and 'stdsoap2.h'. You can find these last on the downloaded package (/gsoap-2.8/gsoap).


2. Creating a standalone server.


We are almost done with the server, we just need to create a new file 'currentTime.cpp' and create the 'main' method and define what must be done when 'ns__currentTime' is called.


Fig 4. Resultant file structure


currentTime.cpp code:


#include "soapH.h" // include the generated declarations 
#include "currentTime.nsmap" // include the XML namespace mappings 
int main() 
{
   struct soap soap;
   int m, s; // master and slave sockets
   soap_init(&soap);
   m = soap_bind(&soap, "192.168.1.54", 2012, 100);
   if (m < 0)
      soap_print_fault(&soap, stderr);
   else
   {
      fprintf(stderr, "Socket connection successful: master socket = %d\n", m);
      for (int i = 1; ; i++)
      {
         s = soap_accept(&soap);
         if (s < 0)
         {
            soap_print_fault(&soap, stderr);
            break;
         }
         fprintf(stderr, "%d: accepted connection from IP=%d.%d.%d.%d socket=%d", i,
            (soap.ip >> 24)&0xFF, (soap.ip >> 16)&0xFF, (soap.ip >> 8)&0xFF, soap.ip&0xFF, s);
         if (soap_serve(&soap) != SOAP_OK) // process RPC request
            soap_print_fault(&soap, stderr); // print error
         fprintf(stderr, "request served\n");
         soap_destroy(&soap); // clean up class instances
         soap_end(&soap); // clean up everything and close socket
      }
   }
   soap_done(&soap); // close master socket and detach context
}
int ns__currentTime(struct soap *soap, time_t& response) 

   response = time(0); 
   return SOAP_OK; 
}


You will have to change the line 'm = soap_bind(&soap, "192.168.1.54", 2012, 100);' and set the IP of your Raspberry (192.168.1.54 in my case) and the port you want it to listen from (2012 in my case).


If you want to make the resultant program work on Raspberry, you will have to cross-compile it. You can do it by changing the properties of your project and performing the following changes:


Under GCC C++ Compiler, change Command: 'g++' by 'arm-linux-gnueabi-g++'
Under GCC C++ Linker, change Command: 'g++' by 'arm-linux-gnueabi-g++'


You can find more information about cross-compilation for Raspberry on my previous post here.




2. Executing the server on Raspberry


The last thing we have to do is to copy the resultant compiled into Raspberry:

fig 5. Copy the resultant program to raspberry using scp


And finally, execute it:


Fig 6. Raspberry serving our service




3. Developing the client:


Now that we have a Web Service deployed on our RaspBerry, lets communicate with it. The steps to build the client are quite similar to the ones we followed to build the server.

First, we create a client project, containing exactly the same header definition than the one we used for the server (you can copy-paste the file).

From the project src path (~/workspace/workspace64/GSoapTuto/MyRaspberryTimeClient/src in my case), execute the following command to generate the code for the client:


~/Desktop/GSoap/gsoap-2.8/gsoap/bin/linux386/soapcpp2 -i -C  currentTime.h

Add the 'stdsoap2.cpp' and 'stdsoap2.h' as we did for the server. And finally add 'currentTime.cpp' including the main method with the following content:

Fig 7. Resultant client structure


Finally, on change the line 'soap_endpoint = "http://www.yourdomain.com/currentTime.cgi";' on the file 'soapcurrentTimeProxy.cpp' in order to target your Raspberry:

soap_endpoint = "http://192.168.1.54:2012"; in my case


And that's all!! If you fulfilled all the steps, after compiling and executing your client, you should get a beautiful result like 'The time is 1342473837' indicating the date in seconds from your Raspberry.

If you have a look to the Raspberry console, it should display some info about the just served request:

Fig 8. First Raspberry served request!!




5 comments:

  1. Replies
    1. I am still attempting installing gsoap on my Raspberry Pi but if fails during the make as follows:-

      make[4]: Entering directory `/home/pi/Develop/gsoap-2.8/gsoap/src'
      /bin/bash ../../ylwrap soapcpp2_yacc.y y.tab.c soapcpp2_yacc.c y.tab.h soapcpp2_yacc.h y.output soapcpp2_yacc.output -- yacc -d -v
      ../../ylwrap: line 111: yacc: command not found
      make[4]: *** [soapcpp2_yacc.c] Error 1
      make[4]: Leaving directory `/home/pi/Develop/gsoap-2.8/gsoap/src'
      make[3]: *** [all-recursive] Error 1
      make[3]: Leaving directory `/home/pi/Develop/gsoap-2.8/gsoap'
      make[2]: *** [all] Error 2
      make[2]: Leaving directory `/home/pi/Develop/gsoap-2.8/gsoap'
      make[1]: *** [all-recursive] Error 1
      make[1]: Leaving directory `/home/pi/Develop/gsoap-2.8'
      make: *** [all] Error 2

      Delete
  2. Then make could not find flex, so:-
    sudo apt-get install flex ./configure make
    and the next error was not finding the ssl and crypto libraries and so I had to:-
    sudo apt-get install libssl-dev followed by ./configure make sudo make install
    and now I was told:-
    You now have successfully built and installed gsoap.
    Yeah. Now to build my app......:-)

    ReplyDelete
  3. The next issue rears its head.
    It is required that the system has the IP address of the server as well as the name/title of the webservice as parameters rather than genned into and therefore ostensibly hard coded.
    Do you by any chance have any suggestions on how I would go about achieving this without having a different binary for each site where my application runs?

    ReplyDelete