Arco Recipes


Programming IceC in X86

Overview

IceC is an object oriented communication middleware, written in C/C++, with a low use of the resources. This middleware is thought to small microcontrollers with limited resources, but is compatible with different architectures, in this example you are going to see how to program IceC in the architecture x86.

Ingredients

In order to follow the next recipe you will need the following requirements:

To install the required packages you have to execute the next command.

console
$ sudo apt install icec smart-tranducer

An IceC server in x86

The server will consist in an object that implement the IBool interface of the st.ice module, located in the path /usr/share/slice/st/.

The first step of the program must be implement the set function of the interface, in this example case, the implementation will be print a message with the new value in the stdout.

snippet.c
void st_IBoolI_set(st_IBoolPtr self, Ice_Bool v, Ice_String sourceAddr)
{
    printf("The new value is %s\n", v ? "true" : "false");
    fflush(NULL);
}

The work flow in an IceC server will always be the same:

  1. Declare an ice communicator, an object adapter and the servants (an object that implements and ice interface) that the server is going to serve.

    snippet.c
    Ice_Communicator ic;
    Ice_ObjectAdapter adapter;
    st_IBool servant;

  2. Initialize the communicator and the endpoints of the communicator (TCPEndpoint or UDPEndpoint).

    snippet.c
    Ice_initialize(&ic);
    TCPEndpoint_init(&ic);

  3. Create and activate the object adapter with their respective endpoints (ip address and port where the server is going to be listening).

    snippet.c
    Ice_Communicator_createObjectAdapterWithEndpoints(&ic, "Adapter", endp, &adapter);
    Ice_ObjectAdapter_activate(&adapter);

  4. Initialize the servants with their respective types.

    snippet.c
    st_IBool_init(&servant);

  5. Add the servants to the object adapter.

    snippet.c
    char *servant_identity = "ServantIBool";
    Ice_ObjectAdapter_add(&adapter, (Ice_ObjectPtr)&servant, servant_identity);

  6. Print the apropiate proxies with the structure "[identity] -e [ice_encoding_version] -[invocation_mode]:[endpoints]".

    snippet.c
    printf("Proxy ready: '%s -e 1.0 -o:%s'\n", servant_identity, endp);

  7. Wait for calls to objects

    snippet.c
    Ice_Communicator_waitForShutdown(&ic);

When you print the proxy is very important to consider some issues. Firstly IceC only support the version 1.0, if you want to communicate an Ice program with another written with IceC both have to work with the 1.0 version. Secondly, if you declare a tcp endpoint you have to print a proxy with a tcp invocation mode, the different invocations mode are -o(oneway) -t(twoway) for TCP and -d(datagram) for UDP.

After organize the code, your program should look like this one:

#include <stdio.h>
#include <IceC/IceC.h>
#include <IceC/platforms/x86/TCPEndpoint.h>

#include "st.h"


void st_IBoolI_set(st_IBoolPtr self, Ice_Bool v, Ice_String sourceAddr)
{
    printf("The new value is %s\n", v ? "true" : "false");
    fflush(NULL);
}

int main()
{
    Ice_Communicator ic;
    Ice_ObjectAdapter adapter;
    st_IBool servant;
    char *endp = "tcp -h 127.0.0.1 -p 10000";
    char *servant_identity = "ServantIBool";

    Ice_initialize(&ic);
    TCPEndpoint_init(&ic);

    Ice_Communicator_createObjectAdapterWithEndpoints(&ic, "Adapter", endp, &adapter);
    Ice_ObjectAdapter_activate(&adapter);

    st_IBool_init(&servant);
    Ice_ObjectAdapter_add(&adapter, (Ice_ObjectPtr)&servant, servant_identity);

    printf("Proxy ready: '%s -e 1.0 -o:%s'\n", servant_identity, endp);
    fflush(NULL);

    Ice_Communicator_waitForShutdown(&ic);
    return 0;
}

Compile the program with IceC

To simplify the task of compiling, it’s going to be used a Makefile with the tool make.

The first task to do is define the variables that are going to be used in the compilation process, in this case we must define the compiler, some paths and the compilation flags that we will need.

snippet.
ICEC_SRC = /usr/src/IceC
DIRSLICE = /usr/share/slice/st/
TARGET = example_x86
SLICE = st

CC = gcc
CFLAGS += -I$(ICEC_SRC) #Flag to include the source code of IceC
CFLAGS += -Wall -Wextra -pedantic -Wno-variadic-macros -Wno-unused-parameter \
	   -Wno-unused-function

Following we are going to used a powerfull tool of make, vpath. Thanks to it, we can indicate a set of directories where make will search for source files or prerequisites that are missing. In order to use IceC, it’s necessary to obtain the object code of certain source files, with vpath the folder where that files are located can be indicated.

snippet.
vpath %.c $(ICEC_SRC)
vpath %.c $(ICEC_SRC)/platforms/x86

Finally, we must include all the rules with their respective prerequisites to compile our program.

snippet.
all: $(SLICE).h $(TARGET)

$(SLICE).h: 
	slice2c $(DIRSLICE)$(SLICE).ice

$(TARGET): $(TARGET).c IceC.o IceUtil.o TCPEndpoint.o port.o
	$(CC) $(CFLAGS) $^ -o $@

run:
	./$(DIREXE)$(TARGET)

clean: 
	find -name "*~" -print -delete
	find -name "*.o" -print -delete
	$(RM) $(TARGET) $(SLICE).h

As you can see, the two main files that are produce with this Makefile are the header file (in this case st.h) with the IceC interfaces and the executable. In order to obtain the executable, we have to link our source code with some object files of IceC. Because of the previous vpath, make will see the .o files like a prerequisites and will search the .c files in the paths that we indicated, producing the object files and compiling the program.

The entire Makefile will be the following.

ICEC_SRC = /usr/src/IceC
DIRSLICE = /usr/share/slice/st/
TARGET = example_x86
SLICE = st

CC = gcc
CFLAGS += -I$(ICEC_SRC) 
CFLAGS += -Wall -Wextra -pedantic -Wno-variadic-macros -Wno-unused-parameter \
	   -Wno-unused-function



vpath %.c $(ICEC_SRC)
vpath %.c $(ICEC_SRC)/platforms/x86

all: $(SLICE).h $(TARGET)

$(SLICE).h: 
	slice2c $(DIRSLICE)$(SLICE).ice

$(TARGET): $(TARGET).c IceC.o IceUtil.o TCPEndpoint.o port.o
	$(CC) $(CFLAGS) $^ -o $@

run:
	./$(DIREXE)$(TARGET)

clean: 
	find -name "*~" -print -delete
	find -name "*.o" -print -delete
	$(RM) $(TARGET) $(SLICE).h

Executing the server

Once developed the Makefile, compile and execute the program is as simple as execute the next commands in your shell.

console
$ make
$ make run

If you want to test your server you can use the st-client of the smart-transducer package.

console
$ st-client -t bool -p "ServantIBool -e 1.0 -o:tcp -h 127.0.0.1 -p 10000" 1

If all is correct the server must show the following message.

console
Proxy ready: 'ServantIBool -e 1.0 -o:tcp -h 127.0.0.1 -p 10000'
The new value is true