SOAP function calls in C/C++ language
[Software hints]

Some hints on how to use the SOAP functions in a C/C++ program. More...

C/C++ language SOAP function prototypes

In this documentation, functions are described as follows.

        int MXCommon__GetModuleType(void * _, struct MXCommon__ByteArrayResponse * Response);

Two main differences can be remarked in the function prototypes:

The C function prototype has the following syntax:

int soap_call_MXCommon__GetModuleType(struct soap *soap, const char *soap_endpoint, const char *soap_action, void *_, struct MXCommon__ByteArrayResponse *Response);

Functions to call in C/C++ are called stubs and can be found in the MSXEXXXXStub.h file.

Rules and use of gSOAP calls in C/C++

When programming with gSOAP in C/C++ language, some rules have to be followed to avoid memory leaks, slow socket disconnections and multi-threading compliancy.

Note: Software code pieces in this chapter comes from SOAP calls sample

SOAP calls sample

Here a sample code, and it's makefile, to read the MSX-E internal temperature.
To compile it, use make IP_ADDRESS=YOUR_MSX-E_IP_ADDRESS don't forget to set the INTERFACE_COMMON_LIBRARY_DIR to point on the MSX-E Common Interface_Library directory.

Remark: Don't use blank spaces in the directories and file names.

temperature.c file

#include <unistd.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <MXCommon.nsmap> // this file must be included only once in the project
#include <assert.h>
#include <sys/stat.h>
#include <termios.h>

//-----------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------

/** kbhit for linux.

 @retval 0: No key pressed.
 @retval <>0: Key pressed.
 */
int kbhit (void)
{
        struct termios oldt, newt;
        struct timeval tv;
        fd_set read_fd;
        int status;

        tcgetattr (STDIN_FILENO, &oldt);
        memcpy (&newt, &oldt, sizeof (newt));
        newt.c_lflag &= ~(ICANON | ECHO);
        tcsetattr (STDIN_FILENO, TCSANOW, &newt);

        tv.tv_sec  = 0;
        tv.tv_usec = 0;
        FD_ZERO (&read_fd);
        FD_SET (0, &read_fd);

        status = select (1, &read_fd, NULL, NULL, &tv);
        tcsetattr (STDIN_FILENO, TCSANOW, &oldt);

        if (status < 0)
                return 0;
        else
                return (status);
}

//-----------------------------------------------------------------------------------------

/** getch for linux.

 @retval key: Key number.
 */
int getch (void)
 {
        struct termios oldt, newt;
        int ch;

        tcgetattr (STDIN_FILENO, &oldt);
        memcpy (&newt, &oldt, sizeof (newt));
        newt.c_lflag &= ~(ICANON | ECHO);
        tcsetattr (STDIN_FILENO, TCSANOW, &newt);
        ch = getchar();
        tcsetattr (STDIN_FILENO, TCSANOW, &oldt);

        return (ch);
}

//-----------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------

/** Check if the SOAP call returns an error, display it.

 @param[in] soapContext : SOAP context structure.
 @param[in] soap_endpoint : IP and port number of the system to access.
 @param[in] soap_action : SOAP action required by the Web service.
 @param[in] soap_error: The SOAP function return value.
 @param[in] msxe_error: The MSX-E system return value contained in the response structure (iReturnValue).
 @param[in] msxe_syserrno: The MSX-E system errno value contained in the response structure (syserrno).

 @retval 0: No error.
 @retval 1: Error.
 */
int checkErrors (struct soap *soapContext, const char *soap_endpoint, const char *soap_action, int soap_error, int msxe_error, int msxe_syserrno)
{
        if ((soap_error != 0) || (msxe_error != 0))
        {
                printf ("MSX-E function response error: %d\n", msxe_error);
                printf ("soap error: %d ", soap_error);

                // In case of an SOAP error, display it
                if (soap_error)
                        printf ("message: %s\n", *soap_faultstring (soapContext));
                else
                {
                        // It's not an SOAP error but a system error
                        if ((msxe_error == -1) || ((msxe_error <= -100) && msxe_syserrno))
                        {
                                struct MXCommon__ByteArrayResponse byteArrayResponse;
                                memset (&byteArrayResponse, 0, sizeof (struct MXCommon__ByteArrayResponse));

                                // Get the system error
                                if (soap_call_MXCommon__Strerror (soapContext, soap_endpoint, soap_action, msxe_syserrno, &byteArrayResponse))
                                        printf ("\nsoap_call_MXCommon__Strerror error: %s\n", *soap_faultstring (soapContext));
                                else // Display the system error
                                        printf ("\nSystem error: %s\n", byteArrayResponse.sArray.__ptr);
                        }
                }

                return 1;
        }

        return 0;
}

//-----------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------

int main (void)
{
         struct soap *soapContext;
         unsigned long option = 0;
         struct MXCommon__GetModuleTemperatureValueAndStatusResponse tempResponse;
         int soap_error = 0;
         char *tempStatus[] = {"NOT AVAILABLE", "TOO LOW", "LOW", "NOMINAL", "HIGH", "TOO HIGH"};
         // IP address of the MSX-E and after the ':' is the MSX-E SOAP server port number
         char soap_endpoint[] = IP_ADDRESS":5555";

         memset (&tempResponse, 0, sizeof (struct MXCommon__GetModuleTemperatureValueAndStatusResponse));

         // Allocates and initialize a new runtime context
         if ((soapContext = soap_new ()) == NULL)
            return EXIT_FAILURE;

         // Initializes the SOAP context with options
         soap_init2 (soapContext, SOAP_IO_KEEPALIVE, SOAP_IO_KEEPALIVE);

         // Sets timeouts in seconds
         soapContext->send_timeout = 1;
         soapContext->recv_timeout = 1;
         soapContext->accept_timeout = 1;

         for ( ; ; )
         {
                 soap_error = soap_call_MXCommon__GetModuleTemperatureValueAndStatus (soapContext, soap_endpoint, NULL, option, &tempResponse);

                 // Check for errors, if there are, stop the loop
                 if (checkErrors (soapContext, soap_endpoint, NULL, soap_error, tempResponse.sResponse.iReturnValue, tempResponse.sResponse.syserrno))
                        break;

                // There is no error
                printf ("MSX-E Temperature: %.2lf °C status: ", tempResponse.dTemperatureValue);

                if (tempResponse.ulTemperatureStatus < 6)
                        printf ("%s ", tempStatus[tempResponse.ulTemperatureStatus]);
                else
                        printf ("unknown ");

                printf ("(ESC to quit)\n");

                 // As gSOAP doesn't released automatically the memory that it allocates to
                 // deserialize SOAP data we have to released it explicitly.
                 // There is no need to call the function in each loop cycle,
                 // it depends on the application, and desired performance and
                 // available memory.
                 soap_destroy (soapContext);
                 soap_end (soapContext);

                 // Listen on keyboard actions
                 if (kbhit ())
                         if (getch () == 27) // Check if ESC key has been used
                                 break;
        }

         // Release SOAP
         soap_destroy (soapContext);
         soap_end (soapContext);
         // Close the socket an release the SOAP context
         soap_free (soapContext);

         return EXIT_SUCCESS;
}

Makefile

TOPDIR                          :=$(shell pwd)

CC                                      :=$(CROSS)$(CC)
LD                                      :=$(CROSS)ld
STRIP                           :=$(CROSS)strip

ifneq ($(INTERFACE_COMMON_LIBRARY_DIR),"")
INTERFACE_COMMON_LIBRARY_DIR    =$(TOPDIR)/../../Interface_Library
endif

# The MSX-E IP address
ifneq ($(IP_ADDRESS),"")
IP_ADDRESS                                              =192.168.99.99
endif

# File implementing SOAP client
SOAPSOURCES                     := $(INTERFACE_COMMON_LIBRARY_DIR)/MSXEClient.o $(INTERFACE_COMMON_LIBRARY_DIR)/MSXEC.o $(INTERFACE_COMMON_LIBRARY_DIR)/stdsoap2.o

CFLAGS                          += -pipe -Wall -O0 -Winline -I$(INTERFACE_COMMON_LIBRARY_DIR) -DIP_ADDRESS=\"$(IP_ADDRESS)\"

TEMPERATURE_SRC         := temperature.o
BINS                            := temperature

all: $(BINS)

temperature: $(SOAPSOURCES) $(TEMPERATURE_SRC)
        $(CC) $(CFLAGS) -o $@ $^
        $(STRIP) $@

clean:
        -rm -f $(BINS)
        -rm -f $(INTERFACE_COMMON_LIBRARY_DIR)/*.o
        -rm -f *.o