Trentino Binding Tutorial

Service Component Architecture (SCA) supports many communication protocols. In SCA terms, these are called bindings. Bindings encapsulate the complexities of communication protocols and enable components to be implemented and wired together without a direct dependency on the communication protocols used. What does this mean in reality? It means that an SCA developer can implement a component without polluting the business code with details of how the communication between components will be handled. This separation of concerns has the added advantage of making the communication protocol a pluggable entity that can be changed at any time based on how and where the component is deployed. Bindings are used by services and references. References use bindings to describe the access mechanism used to call a service (which can be a service provided by another SCA composite). Services use bindings to describe the access mechanism that clients (which can be a client from another SCA composite) have to use to call the service. SCA supports the use of multiple different types of bindings. Trentino supports SCA binding via TCP/IP. Trentino will also support interprocess communication binding via shared memory, REST binding, bacnet binding and profinet binding. SCA provides an extensibility mechanism by which an SCA runtime can add support for additional binding types. A binding is defined by a binding element which is a child element of a service or of a reference element in a composite. The following snippet shows the binding element pseudo-schema.



<?xml version="1.0" encoding="ASCII"?>
<!-- Bindings schema snippet -->
<composite ... >
...
  <service ... >
   <interface … />
    <binding uri="xs:anyURI"? name="xs:NCName"? requires="list of xs:QName"?policySets="listof xs:QName"?>
     <wireFormat/>
     <operationSelector/>
     <requires/>
     <policySetAttachment/>
    </binding>
    <callback>
     <binding uri="xs:anyURI"? name="xs:NCName"? requires="list of xs:QName"? policySets="list of xs:QName"?>
      <wireFormat/>
      <operationSelector/>
      <requires/>
      <policySetAttachment/>
     </binding>
    </callback>
  </service>
  <reference ... >
   <interface … />
    <binding uri="xs:anyURI"? name="xs:NCName"? requires="list of xs:QName"?policySets="listof xs:QName"?>
     <wireFormat/>
     <operationSelector/>
     <requires/>
     <policySetAttachment/>
    </binding>
    <callback>
     <binding uri="xs:anyURI"? name="xs:NCName"? requires="list of xs:QName"? policySets="list of xs:QName"?>
      <wireFormat/>
      <operationSelector/>
      <requires/>
      <policySetAttachment/>
     </binding>
    </callback>
  </reference>
</composite>


 

Trentino default (SCA) Binding

Overview

SCA binding for Trentino is done via TCP/IP. Following snippet shows the SCA binding element pseudo-schema.



<binding.sca uri="xs:anyURI"? name="xs:NCName"? requires="list of xs:QName"? policySets="list of xs:QName"?>
 <wireFormat/>?
 <operationSelector/>?
 <requires/>*
 <policySetAttachment/>*
</binding.sca>


 
The following SCDL is an example for the usage of binding.sca in Trentino.


<?xml version="1.0" encoding="utf-8"?>
<composite name="BasicCalculatorRemoteClientComposite" xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912" targetNamespace="BasicCalculatorNamespace">
  <component name="BasicCalculatorRemoteClientImpl">
    <implementation.cpp
        class="Trentino::Example::BasicCalculatorRemoteClient::BasicCalculatorRemoteClientImpl"
		header="TrentinoExampleBasicCalculatorRemoteClientBasicCalculatorImpl.h"
        scope="composite" eagerInit="true"
		library="BasicCalculatorRemoteClient" >
		 <function name="onInit" init="true" />
		</implementation.cpp>
     <reference name="BasicCalculatorRef" >
	 	<binding.sca uri="tcp://localhost:5342/BasicCalculatorImpl/BasicCalculator"/>
    </reference>
  </component>
</composite>


 
Each binding.sca (in Trentino) must have a "uri" attribute. The convention for the attribute is represented below:


tcp://localhost:5342/BasicCalculatorImpl/BasicCalculator


 
"Uri" attribute of binding.sca must begin with "tcp://" which is followed by a valid host and a valid port. Host and port checks are done by Trengen. End-user is informed when there are some errors during validation before deployment.

Creating sample server and client applications, their interfaces, deploying and running

In the next 10 minutes, we want to create a Hello World remote application and deploy it on two Trentino runtimes. The first runtime is a server and the second a client. On the Server, a contribution implementing the Helloworld Service is deployed. On the client, the contribution is having a reference using the service provided by server. The overall deployment diagram of our system is described in the following picture.

Assembly diagram of Helloworld remote

Assembly diagram of Helloworld remote

Create the Helloworld server application

Binding.sca is the default binding for all remotable services. If there are not any bindings specified for a remotable service, we can say that it uses binding.sca. If there are not any binding.sca specified, which port will be used by Trentino runtime? The Trentino runtime hosting the server application has a Binding.conf file. The contents of the Binding.conf is below:



binding.sca.port=5342
binding.sca.dll=TrentinoBindingSCA
binding.sca.thread.number=1
binding.sca.client.timeout=1500 #in miliseconds


The port in the Binding.conf is used if there are not any bindings in the remotable service. It is the port number that ZMQServer listens for a request and opens it for sending the response. End-user may edit the values of the parameters in the Binding configuration file.
Download all necessary files and setup development environment

Download and install the Trentino runtime as described in the Trentino Getting Started guide. It includes all the necessary binaries for developing a remote application. Now create a folder called HelloWorldRemote_Source. At the end of this section, we will end up with the following file structure:

Server folder structure

Server folder structure

Since the application developed in this tutorial is platform independent, we will use Cmake as cross platform build tool. And therefore we need to define a CMakeList.txt file describing our project. Create a file named CMakeList.txt and paste the following code into that file:


cmake_minimum_required(VERSION 2.6)
project(HelloWorldRemote)

set(PNAME HelloWorldRemote)
##############################################################################
# 									MACROS									 #
##############################################################################

#----------------------------------
# MY_LINK_THIRD_PARTY_LIBRARIES 
#----------------------------------
#The libs to be linked to are expected to be passed as additional arguments after 'target'.
#This arguments have to be uncomplete variable names representing the libs
#The macro will append '_DEBUG' respectively '_RELEASE' to the passed arguments
#in order to produce real variable names that are expected to contain the real lib names.
#EXAMPLE: 
#The call MY_LINK_THIRD_PARTY_LIBRARIES(main Boost_FILESYSTEM_LIBRARY) expects the variables
#'Boost_FILESYSTEM_LIBRARY_DEBUG' (if build type is debug) respectively 
#'Boost_FILESYSTEM_LIBRARY_RELEASE' (if build type is release) to be defined
#----------------------------
macro(MY_CREATE_VALIDATE_GENERATE_INSTALL_TARGET installDir)
if (WIN32)
	set(GEN_INSTALL_COMMAND msbuild INSTALL.vcxproj)
else()
	set(GEN_INSTALL_COMMAND make install)
endif()
add_custom_target([Trengen_Validate_Generate_Install] 
COMMAND Trengen	-cont ${installDir}
#create a build folder for the generated code
COMMAND ${CMAKE_COMMAND} -E make_directory ${installDir}Gen_Build
#build the project using cmake
COMMAND ${CMAKE_COMMAND} -E chdir ${installDir}Gen_Build cmake ../${installDir}Gen
#now install the application: on windows, call msbuild on linux call make
COMMAND ${CMAKE_COMMAND} -E chdir ${installDir}Gen_Build ${GEN_INSTALL_COMMAND}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/../
VERBATIM
)	
endmacro(MY_CREATE_VALIDATE_GENERATE_INSTALL_TARGET)



set (TRENTINO_RUNTIME_DIR $ENV{TRENTINO_HOME})
set (DEFAULT_INSTALL_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../)

if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
   set (CMAKE_INSTALL_PREFIX ${DEFAULT_INSTALL_PATH} CACHE STRING "Install path" FORCE)
endif()

##############################################################################
# 								COMPILER OPTIONS  							 #
##############################################################################
if(MSVC)
	add_definitions(-DUNICODE -D_UNICODE)
	add_definitions(/W4)
else()
	add_definitions(-Wall -pedantic)
endif()

##############################################################################
# 							   OUTPUT CONFIGURATION						     #
##############################################################################
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/build/${CMAKE_SYSTEM_PROCESSOR}${CMAKE_SYSTEM_NAME})
set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/build/${CMAKE_SYSTEM_PROCESSOR}${CMAKE_SYSTEM_NAME})



set(SCAAPI_INCLUDE_DIRS ${TRENTINO_RUNTIME_DIR}/include)
set(BOOST_INCLUDE_DIRS ${TRENTINO_RUNTIME_DIR}/include)
#source and header files
FILE (GLOB ${PNAME}_HEADER_FILES *.h *.inl
	  services/*.h services/*.inl)

FILE (GLOB ${PNAME}_SCA_FILES *.xml  *.componentType META-INF/*.xml META-INF/*.composite)
FILE (GLOB ${PNAME}_BINDING_SCA_FILES *.tidl.properties *.java)

FILE (GLOB ${PNAME}_SCA_COMPONENT_TYPES *.componentType )
FILE (GLOB ${PNAME}_SOURCE_FILES *.cpp services/*.cpp)
SET(${PNAME}_SCA_FILES ${${PNAME}_SCA_FILES} ${${PNAME}_SCA_COMPONENT_TYPES})
FILE(GLOB ${PNAME}_EXPORTED_HEADERS *.h *.inl)  
#libs
set( ${PNAME}_THIRD_PARTY_LIBS 
     )
	 
set( ${PNAME}_CUSTOM_LIBS 
     )
	 
set( ${PNAME}_SOURCE_FILES ${${PNAME}_SOURCE_FILES}
						  ${${PNAME}_HEADER_FILES} ${${PNAME}_SCA_FILES})

if (WIN32)
	add_definitions(-DHELLOWORLDREMOTE_EXPORTS)
endif()

if(MSVC_IDE)
	source_group("SCA Files" FILES ${${PNAME}_SCA_FILES})
endif()

include_directories(${SCAAPI_INCLUDE_DIRS} ${BOOST_INCLUDE_DIRS})

add_library(${PNAME} SHARED ${${PNAME}_SOURCE_FILES})
set(${PNAME}_INSTALL_DIR ${PNAME})
set_target_properties(${PNAME} PROPERTIES OUTPUT_NAME ${PNAME})
install(TARGETS ${PNAME} RUNTIME DESTINATION ${${PNAME}_INSTALL_DIR}
                         LIBRARY DESTINATION ${${PNAME}_INSTALL_DIR}
						 ARCHIVE DESTINATION ${${PNAME}_INSTALL_DIR})
install(DIRECTORY META-INF DESTINATION ${${PNAME}_INSTALL_DIR})
install(FILES ${${PNAME}_EXPORTED_HEADERS} ${${PNAME}_BINDING_SCA_FILES} ${${PNAME}_SCA_COMPONENT_TYPES} DESTINATION ${${PNAME}_INSTALL_DIR})



##############################################################################
#                           CUSTOM TARGET FOR VALIDATING AND GENERATING CODE BY TRENGEN#
##############################################################################
MY_CREATE_VALIDATE_GENERATE_INSTALL_TARGET(${${PNAME}_INSTALL_DIR} )


Define the HelloWorld remote interface

A remote application using the SCA Binding should be defined using an interface description language. Trentino default binding defines its own IDL called TIDL (Trentino IDL) that is derived from the CORBA IDL specification. More details about TIDL can found in the next chapter. A TIDL interface is defined in one or more .java classes. TIDL uses part of the java syntax to describe the interfaces. The use of the java syntax makes it easy to extend and parse the interface. It also makes it possible to write TIDL files using existing java editors. Create a file named HelloWord.java in HelloWorldRemote_Source. The file should have the following content:

package Trentino.Example.HelloWorldRemote;

//We should ALWAYS Import org.trentino.tidl.* in a TIDL file
import org.trentino.tidl.*;

/**
* HelloWord Service, receive hello world messages 
* and send wellcome to the client.
**/
public interface HelloWorld extends TInterface{

/**
* The server receive an Hello message and send a 
* wellcome message back to the client who sent 
* the message
* @param name: the name of the client who sent the message
* @return TString a welcome message
**/
TString hello(TString name);
}
Then create a configuration file that describes declare the interface. The configuration file is the entry point for code generators like Trengen, that would like to create C++ artifacts based on a given service definition. The configuration file name should be exactly the qualified name of the HelloWorld interface, without dot. A TIDL configuration file end with tidl.properties. In our case it is: TrentinoExampleHelloWorldRemoteHelloWorld.tidl.properties. It only contains the following line of code:
interface=./HelloWorld.java
Generate the C++ interface

Important: Trengen relies on JDK and not only the JRE (Java Runtime Environment). Make sure that a JDK 1.6.X has been installed.

From the TIDL definition of Helloword, we then generate the corresponding C++ interface. This is done using Trengen. (See Chapter 4 to learn more about Trengen tool). In the command line type:


Trengen -gencppitf Path/to/HelloWorldRemote_Source

The generated artifact is a header file containing the generated interface definition.
Implement the HelloWordRemote interface.

Create a class named HelloWordImpl in a header file named TrentinoExampleHelloWorldRemoteHelloWordImpl.h and copy the following implementation into it.


#ifndef TrentinoExampleHelloWorldRemoteHelloWorldImplH
#define TrentinoExampleHelloWorldRemoteHelloWorldImplH
#ifdef WIN32
   #ifdef HELLOWORLDREMOTE_EXPORTS
      #define HELLOWORLDREMOTE_IMPORT_EXPORT __declspec(dllexport)
   #else
      #define HELLOWORLDREMOTE_IMPORT_EXPORT __declspec(dllimport)
   #endif //HELLOWORLDREMOTE_EXPORTS
   #else
      #define HELLOWORLDREMOTE_IMPORT_EXPORT
#endif //WIN32
//baseclass
#include "TrentinoExampleHelloWorldRemoteHelloWorld.h"


namespace Trentino{
namespace Example{
namespace HelloWorldRemote{

//forward declaration


 //**************************************************************************************
 //                                         HelloWorld
 //**************************************************************************************
 //! \brief implementation of helloword service
 //**************************************************************************************
class HELLOWORLDREMOTE_IMPORT_EXPORT HelloWorldImpl : public HelloWorld{
  //construction
public:
 HelloWorldImpl();
 virtual ~HelloWorldImpl();
  //services
public:
	virtual std::string hello (const std::string& name);
  
}; //class HelloWorld
} //namespace Trentino
} //namespace Example
} //namespace HelloWorldRemote

#endif //TrentinoExampleHelloWorldRemoteHelloWorldImplH

Create the corresponding C++ class TrentinoExampleHelloWorldRemoteHelloWordImpl.cpp and copy the following code into it.



#include "TrentinoExampleHelloWorldRemoteHelloWorldImpl.h"


using namespace Trentino::Example::HelloWorldRemote;

//class HelloWorldImpl

HelloWorldImpl::HelloWorldImpl()
{
}
HelloWorldImpl::~HelloWorldImpl()
{
}

std::string HelloWorldImpl::hello (const std::string& name)
{
  std::string result("Hello ");
  result+=name;
  return name;
}

Describe your HellowordRemote SCA contribution

Now our service is implemented (a C++ implementation is available), we need to create SCA component composite and contribution in other to describe our application and make it deployable into a Trentino runtime.

HelloWorld remote component

We start by defining our SCA component type which represents the configurable aspects of our implementation. A component type is described in a *.componentType xml document, that must be located in the same folder with the component implementation (the location of the component header file), in our case the component type name is TrentinoExampleHelloWorldRemoteHelloWordImpl.componentType. It has the following content:


<?xml version="1.0" encoding="utf-8"?>
<componentType xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912">
      <service name="HelloWorld" >
      <interface.cpp remotable="true" class="Trentino::Example::HelloWorldRemote::HelloWorld" header="TrentinoExampleHelloWorldRemoteHelloWorld.h"/>
    </service>
</componentType> 

Our component type defines the Helloword Service and C++ interface. The interface is defined to be remotable ("remotable" is Trentino's contribution to SCA. It is defined by Trentino). Since no binding is specified for the service, our default sca binding is assumed to be the one used for remote communication. HelloWorld remote composite

Now we can create a composite that has one instance of our previously defined component type For that purpose Trentino requires composites to be located in a folder named META-INF. In our source folder, create META-INF folder and create an xml file named: HelloWorldRemote.composite (composite files always end with .composite), and paste following content into it:


<?xml version="1.0" encoding="utf-8"?>
<composite name="HelloWorldRemoteComposite" xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912" targetNamespace="HelloWorldRemoteNamespace">

  <component name="TrentinoExampleHelloWorldRemoteHelloWorldImpl">

    <implementation.cpp
        class="Trentino::Example::HelloWorldRemote::HelloWorldImpl"
		header="TrentinoExampleHelloWorldRemoteHelloWorldImpl.h"
        scope="composite"
		library="HelloWorldRemote" 
		eagerInit="true"/>
		<service name="HelloWorld" >
      <interface.cpp remotable="true" class="Trentino::Example::HelloWorldRemote::HelloWorld" header="TrentinoExampleHelloWorldRemoteHelloWorld.h"/>
    </service>
  </component>
</composite>

Our composite has a component that is an instance of TrentinoExampleHelloWorldRemoteHelloWorldImpl.componentType. The component is configured with our implementation. The location of the library containing the implementation is also defined. HelloWorld contribution

The last step in your application description is to create a contribution that is an xml document that describes all the artifacts (the so-called deployable elements) to be deployed into and managed together by the runtime. For our simple case, the only available deployable element is the previously created composite and its dependencies (all componentTypes and compiled libraries). The name of the contribution file should be sca-contribution.xml and it should be created in our META-INF folder. Paste the following code into it:


<?xml version="1.0" encoding="utf-8"?>
<contribution xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912" xmlns:sample="HelloWorldRemoteNamespace">
<deployable composite="sample:HelloWorldRemoteComposite"/>
</contribution>

Compile the server application

We would like to do all these steps without an IDE but rather with a console.

  • If you are using windows, then please open your visual studio console.
  • Create a folder named HelloWorldRemote_Build next to HelloWorldRemote_Source
  • Change to the created directory : cd HelloWorldRemote_Build
  • Call Cmake: cmake ../ HelloWorldRemote_Source
  • Now your visual studio solution has been created (On linux your make files)
  • On windows call ‘msbuild INSTALL.vcxproj’ on linux just ‘make install’
  • This will create a folder named HelloWorldRemote next to HelloWorldRemote_Source. The HelloWorldRemote folder contains your compiled and ready for validation contribution
Code validation and generation

Before been deployed into the runtime, a prepared contribution as we did in the last steps will go through a complex validation and code generation process. The validation process make sure that only a consistent and specification conform contribution can be deployed into the runtime. To validate and generate code, type for windows:


msbuild [Trengen_Validate_Generate_Install].vcxproj

For linux, type:

make [Trengen_Validate_Generate_Install]

This last step update your contribution by creating a new library called sca-contribution (.dll or .so). The created library is located in the META-INF folder of your contribution. To further understand what is done during the last step, you can take a look on the custom target [Trengen_Validate_Generate_Install] defined in our cmake file
Create the HelloWorld client application

The steps for creating the client applications are similar to the ones for the server application Please download the source code for the client from our samples examples. And build as described for the server example.

The client application is a component that is initialized directly after deployment (eager initialization). It references the HelloWorld Service created before. It has an initialisation lifecycle method that is called as soon as the component is initialized. That lifecycle method just connects to the HelloWord Server and calls the remote HelloWord Service. Here is the source code of the HelloWorldClient



#include <iostream>
#include "TrentinoExampleHelloWorldRemoteHelloWorldClientImpl.h"


using namespace Trentino::Example::HelloWorldRemote;

//class HelloWorldClientImpl

HelloWorldClientImpl::HelloWorldClientImpl()
{
}
HelloWorldClientImpl::~HelloWorldClientImpl()
{
}

void HelloWorldClientImpl::setHelloWorld(HelloWorld* helloWorld){
mHelloWorld = helloWorld;
callHello();
}

//This method is called by the Runtime as soon as this component is initialized
void HelloWorldClientImpl::callHello ()
{

  try
  {
      std::string name("Trentino");
      std::string response = mHelloWorld->hello(name);
      std::cout << "Remote HelloWorld Service called. Response is:" << response << std::endl;
  }
  catch (std::exception& e)
  {
  	 std::cout << "Exception during call of hello world:" << e.what() << std::endl;
    return;
  }

}

Deploy and run

In this last step start two different instances of the Trentino runtime. In the first instance, deploy the server and wait until the server deployment is completed. In the second instance, deploy the client. The client will print:


Remote HelloWorld Service called. Response is: Hello Trentino

Binding-related generated files

For the server side, the files generated by Trengen can be seen in the following figure:

server-side generated files

server-side generated files

For the client side, the files generated by Trengen can be seen in the following figure:

client-side generated files

client-side generated files

  • ProtobufMessages, Request, Response and Types are common for both client and server. They are the generated protobuf files. Here are the reasons why we use google-protobuf in Trentino:
    • Protobuf is small in size - efficient data storage (far smaller than xml).
    • Protobuf is cheap to process - both at the client and server.
    • Protobuf is platform independent - portable between different programming architectures.
    • Protobuf is extensible - to add new data to old messages.
    • Protobuf is fast.
  • TrentinoGenModelLoader, TrentinoGenProxiesWrappers and TrentinoGenReflx files are also common for both client and server:
    • TrentinoGenModelLoader: contains code responsible for creating the in-memory SCA Model by instantiating SCA Model classes. The created object graph reflects all the information specified in SCDL artifacts in the contribution. It implementes Trentino::Gen::GenContributionInfo::loadModel(). The instantiation doesn‘t happen automatically when sca-contribution.dll is loaded but is triggered by the runtime through a call to the loadModel() function.
    • TrentinoGenProxiesWrappers: Proxies keeps references-related information and wrappers keeps services related information. This is the bridge between invocation and business logic. It includes service header files (mentioned in interface.cpp/@header): this is necessary because proxies inherit service classes and wrappers call methods of the service classes.
    • TrentinoGenReflx: contains reflection code for all the implementation classes in the contribution. It includes headers of the implementation classes (mentioned in implementation.cpp/@header): this is necessary because the generated reflection code uses symbols (methods, types) defined in the business logic code.
  • TrentinoGenBindingProxy is generated in client side. It creates a protobuf request message, sends it to ClientProxyDelegate, gets the response, parses the response from string and finally returns the response.
  • TrentinoGenSCABindingHelpers is generated in server-side. It deserializes the request and serializes the response.
  • BeanUtils is generated in both client and server. It makes the conversion between java types and protobuf types. It is used in TrentinoGenSCABindingHelpers and TrentinoGenBindingProxy.

Binding.sca from development point of view

The following diagram represents the sequence of binding in Trentino. It covers the "boot", "install server" and "install client" commands.

Sequence diagram for binding SCA

Sequence diagram for binding SCA

After "boot" command, BootsTrapper is called. It creates instances of Binding Manager, Contribution Loader, ComponentCPPImpl Manager, ComponentProperty Manager and Invocation Layer. After "install _name_of_the_server_contribution_" command, onDeploy() function of BindingManager is called. This is the start of Binding in Trentino.
  • Binding Manager creates Binding Factory.
  • BindingFactory gets SCABindingFactory.
  • SCABindingFactory creates BindingSCA.
  • BindingManager calls createBinding() method of SCABindingFactory. Then doCreateBinding() function of BindingSCA is called by SCABindingFactory.
  • BindingFactory calls start() method of BindingFactory. Then start() function of BindingSCA is called by BindingFactory.
  • BindingSCA creates instances of SCAContext, BindingSCADataHandler, ZMQServer and InvokerProxy. Then it calls start() function of ZMQServer. Zero MQ is the socket library that acts as a concurrency framework. Trentino uses Zero MQ for the message passing(serialization and deserialization included) via network.
After "install _name_of_the_client_contribution_" command, onInit() or getInstance() function of ComponentCPPImplManager is called (This is decided by the configuration in SCDL).
  • ComponentCPPImplManager creates an instance of Component Implementation of Caller (this is business logic provided by end-user. It is TrentinoExampleHelloWorldRemoteHelloWordImpl in our sample.)
  • Component Implementation object calls a service method which is defined also in Proxy. (Proxy is a generated file.)
  • Proxy invokes Invocation Layer.
  • Invocation Layer calls invoke() function of Wrapper. (Wrapper is also a generated file)
  • Wrapper calls the related service method which is in BindingProxy. (BindingProxy is a generated file)
  • BindingProxy calls send() function of ClientProxyDelegate.
  • ClientProxyDelegate calls send() function of ClientProxyDelegateImpl. ClientProxyDelegateImpl sends the request and gets the related response.
After a request is received in server side, handle() function of BindingSCADataHandler by ZMQServer.
  • BindingSCADataHandler calls the deserializeRequest() function of BindingHelpers. (BindingHelper is a generated file)
  • BindingSCADataHandler serializes the response and sends it via network.

Trentino IPC Binding

Overview

Inter-process communication (IPC) is a set of methods for the exchange of data among multiple threads in one or more processes. IPC methods are divided into methods for

  • message queues,
  • synchronization,
  • shared memory, and
  • remote procedure calls (RPC).
RPC is not in the scope of this binding type. Because other binding types (binding SCA, bacnet binding etc…) are kinds of RPC. Message queues and shared memory are our focus for IPC binding in Trentino. Each thread has its own private memory which is not accessible to other threads. Each thread may want to access a shared memory for fast communication. There can be more than one Trentino runtime running on the same computer. The reason why the end-user has 2 or more Trentinos running in the same computer is that customer may want to isolate services from each other.

Extending bindings in Trentino (using TIDL / IDL)

Binding Framework is a set of interfaces, guidelines and documentation that enables a user not directly involved in Trentino development to extend Trentino with additional Bindings. Siemens develops the core and Trentino users are supposed to develop Bindings as needed. The goal for Binding Framework was to keep it as easy as possible to extend. Thus binding framework in Trentino is a generic module. Extending bindings requires:

  • New .xsd for new binding (in the SCDL part) For example: binding.ipc is not specified in SCA. A new xsd for the binding.ipc should be defined.
  • Update the SCA model (model in Java part). For example: binding.ipc is not specified in SCA. So the new elements (like uri attribute for binding.ipc) should be added to the SCA Java model.
  • Changes should be reflected to SCAMirrorModel (in Java part) and SCA C++ model.
  • Change the runtime model.
  • Make the Binding Layer support the new binding (adapt the new Runtime model binding attributes etc...).
  • New binding should be reflected to generated ModelLoader file. (in java part)
  • Decide your TIDL for interfacing (it is explained in chapter-6).
  • If serialization/deserialization between runtimes is needed, message passing format should be specified and related data converter structures should be generated. For the conversion, BeanUtils (for both server and client side) generation should also be enabled. For example: In binding.sca we used BeanUtils for converting c++ objects to protobuf objects and vice versa.
  • BindingProxy (for client side) and BindingHelpers(for server side) are obligatory classes. Contents may differ. They must be iplemented in every binding. They are the bridges between business logic code and Trentino runtime.