A SALOME component is a CORBA component as described in the previous chapter, using services of the SALOME kernel. This component also has to be “declared” to SALOME (i.e. SALOME resource files need to be supplied or completed with information about this component). The following operations need to be carried out:
The IDL description is similar to the description of a standard CORBA component with the following special features:
Notes
We will reuse the alglin.idl file and adapt it:
alglin.idl
#include "SALOME_Component.idl"
module Engines {
typedef sequence<double> vecteur;
interface AlgLin : EngineComponent
{
void addvec(out vecteur C, in vecteur A, in vecteur B);
double prdscl(in vecteur A, in vecteur B);
};
};
Similar modifications are made on the IDL FreeFem.idl file:
FreeFem.idl
#include "SALOME_Component.idl"
module Solveur {
struct Bord {
string X;
string Y;
long n;
};
typedef sequence<Bord> seqBords;
interface FreeFemComponent : Engines::EngineComponent
{
void Bords(in seqBords B);
void Flux(in string u1, in string u2);
void Convection(in string cond_init, in double dt, in long n);
};
};
Before they can be used in SALOME, components must be registered in one of the SALOME modules catalogs. This catalog is distributed in several files (general catalog, user catalog). It will be assumed that the component will be declared in a user’s personal catalog. The component catalog file is an XML file that contains:
This catalog can be completed by hand or a utility supplied by SALOME can be used that generates information in point 1 above (nevertheless, this file must be edited to enter information in point 2). This tool is available in the SALOME graphic interface (Tools->Catalog Generator menu) indicating the name of the catalog (XML file) and the name of the IDL file of the component.
In this section, it will be assumed that the IDL file defines a CORBA class: A and that the C++ implementation class A_impl will be used.
Proceed as follows to adapt the standard CORBA / C++ implementation classes (as seen in the previous chapter):
Insert the SALOMEconfig.h file that contains a number of useful definitions to make the code of the implementation class independent from the CORBA version used:
#include <SALOMEconfig.h>
then insert the SALOME_Component_i.hxx file that contains the interface of the C++ implementation class of the SALOME basic component:
#include "SALOME_Component_i.hxx"
for the CORBA class that is implemented, add the following line:
#include CORBA_SERVER_HEADER(A)
CORBA_SERVER_HEADER is a macro defined in SALOMEconfig.h that makes CORBA inclusion file names independent from the CORBA implementation used.
for each CORBA class used in the implementation class, add the following line:
#include CORBA_CLIENT_HEADER(<CORBA class name>)
CORBA_CLIENT_HEADER is a macro defined in SALOMEconfig.h that makes CORBA inclusion file names independent from the CORBA implementation used.
derive the implementation class from the class of the basic SALOME component:
class A_impl :
public POA_Engines::A,
public Engines_Component_i {
define the (sole) constructor as follows in the C++ header file (.hxx):
A_impl(CORBA::ORB_ptr orb,
PortableServer::POA_ptr poa,
PortableServer::ObjectId * contId,
const char *instanceName,
const char *interfaceName);
and in the C++ implementation file (.cxx):
A_impl:: A_impl
(CORBA::ORB_ptr orb,
PortableServer::POA_ptr poa,
PortableServer::ObjectId * contId,
const char *instanceName,
const char *interfaceName) :
Engines_Component_i(orb, poa, contId,
instanceName, interfaceName)
{
_thisObj = this ;
_id = _poa->activate_object(_thisObj);
}
This constructor may possibly be responsible for creation and initialisation of the internal object associated with the CORBA component.
If structures are defined by the IDL file in the Engines module, adapt declarations of the implementation class methods.
The above example illustrates the modifications made on example 6.
The following needs to be inserted in each service of the component (i.e. in each method of the implementation class called during a CORBA request), before the component can be controlled from supervision.
at the beginning, the instruction:
beginService(<nom du service>);
at the end, the instruction:
endService(<nom du service>);
These two instructions notify the SALOME supervision that the component service has actually received the CORBA request (beginService) and that execution of the service has actually terminated (endService).
Put beginService and endService in the methods of the implementation class of a component declares to SALOME that the component is “supervisable”. This is not an assurance that this component can be used without precautions in the case in which the component has an internal state modified by one or more services.
Consider a component with an internal variable Quantity and two services:
A priori, it is impossible to know the value of Quantity after the calculation graph on the following figure has been executed.
Calculation graph containing parallel branches
The constructor of the implementation class must be modified as follows, so as to signal use of the notification (that will cause opening of a connection to an events channel):
A_impl:: A_impl
(CORBA::ORB_ptr orb,
PortableServer::POA_ptr poa,
PortableServer::ObjectId * contId,
const char *instanceName,
const char *interfaceName) :
Engines_Component_i(orb, poa, contId,
instanceName, interfaceName, 1)
{
_thisObj = this ;
_id = _poa->activate_object(_thisObj);
}
in which the parameter “1” has been added to the end of the call to the Engines_Component_i of the constructor. The component can then use the instruction:
void sendMessage(const char *event_type, const char *message);
to send messages indicating progress with the calculation or an abnormal situation, etc. These messages will be visible to the SALOME user. The first parameter indicates the message type (“warning”, “step”, “trace”, “verbose”), and the second parameter indicates the message contents (character string).
A “C” function has to be provided with an imposed name and code, for a component to be loaded and initialised by a container:
extern "C"
{
PortableServer::ObjectId * <nom du composant>Engine_factory(
CORBA::ORB_ptr orb,
PortableServer::POA_ptr poa,
PortableServer::ObjectId * contId,
const char *instanceName,
const char *interfaceName)
{
<classe d'implementation> * O
= new <classe d'implementation>(orb, poa, contId,
instanceName, interfaceName);
return O->getId() ;
}
}
<component name> (or CORBA class name, A in this case) and <implementation class> (A_impl here) are specific to each component. This function is called by the container when a component is loaded. It creates a CORBA object that will receive requests to the component and will forward them to the different services of the component.
We will use the implementation files algin_i.hxx and algin_i.cxx again and adapt them:
alglin_i.hxx
#ifndef ALGLIN_I_ #define ALGLIN_I_ #include "alglin.hxx" #include <SALOMEconfig.h> #include CORBA_SERVER_HEADER(alglin) #include "SALOME_Component_i.hxx" class AlgLin_i : public POA_Engines::AlgLin, public Engines_Component_i { private: alglin A_interne; public: AlgLin_i(CORBA::ORB_ptr orb, PortableServer::POA_ptr poa, PortableServer::ObjectId * contId, const char *instanceName, const char *interfaceName); virtual ~AlgLin_i(); void addvec(Engines::vecteur_out C, const Engines::vecteur& A, const Engines::vecteur& B); CORBA::Double prdscl(const Engines::vecteur& A, const Engines::vecteur& B); Engines::vecteur* create_vector(CORBA::Long n); void destroy_vector(const Engines::vecteur& V); }; extern "C" PortableServer::ObjectId * AlgLinEngine_factory(CORBA::ORB_ptr orb , PortableServer::POA_ptr poa , PortableServer::ObjectId * contId , const char *instanceName , const char *interfaceName ); #endif
alglin_i.cxx
#include "alglin_i.hxx" #include <iostream> AlgLin_i:: AlgLin_i(CORBA::ORB_ptr orb, PortableServer::POA_ptr poa, PortableServer::ObjectId * contId, const char *instanceName, const char *interfaceName) : Engines_Component_i(orb, poa, contId, instanceName, interfaceName, 1) { MESSAGE("activate object"); _thisObj = this ; _id = _poa->activate_object(_thisObj); } AlgLin_i::~AlgLin_i() { } void AlgLin_i::addvec(Engines::vecteur_out C, const Engines::vecteur& A, const Engines::vecteur& B) { beginService("addvec"); long i, n = A.length(); if (n != B.length()) sendMessage("warning", "vecteurs de longueur differente"); vecteur A_(n); vecteur B_(n); vecteur C_(n); double *xA = A_.x(); double *xB = B_.x(); double *xC = C_.x(); for (i=0; i<n; i++) { xA[i] = A[i]; xB[i] = B[i]; } A_interne.addvec(&C_, &A_, &B_); C = new Engines::vecteur; C->length(n); for (i=0; i<n; i++) { (*C)[i] = xC[i]; } endService("addvec"); } CORBA::Double AlgLin_i::prdscl(const Engines::vecteur& A, const Engines::vecteur& B) { beginService("prdscl"); long i, n = A.length(); if (n != B.length()) sendMessage("warning", "vecteurs de longueur differente"); vecteur A_(n); vecteur B_(n); double *xA = A_.x(); double *xB = B_.x(); for (i=0; i<n; i++) { xA[i] = A[i]; xB[i] = B[i]; } CORBA::Double d = A_interne.prdscl(&A_, &B_); endService("prdscl"); return d; } extern "C" { PortableServer::ObjectId * AlgLinEngine_factory( CORBA::ORB_ptr orb, PortableServer::POA_ptr poa, PortableServer::ObjectId * contId, const char *instanceName, const char *interfaceName) { MESSAGE("PortableServer::ObjectId * AlgLinEngine_factory()"); AlgLin_i * O = new AlgLin_i(orb, poa, contId, instanceName, interfaceName); return O->getId() ; } }
In this section, it is assumed that the IDL file defines a CORBA class: B and that we will use the python implementation class: B.
The procedure is similar to the case of the C++ server interface:
import the class of the basic component:
from SALOME_ComponentPy import *
derive the implementation class from the class of the basic SALOME component:
class B(Engines__POA.B,
SALOME_ComponentPy_i):
The constructor of the implementation class must begin by:
def __init__(self, orb, poa, this, containerName,
instanceName, interfaceName):
SALOME_ComponentPy_i.__init__(self, orb, poa, this,
containerName, instanceName,
interfaceName, False)
Before the component can be controlled from supervision, the following have to be inserted in each service of the component (i.e. in each method of the implementation class called during a CORBA request).
at the beginning, the instruction:
beginService(<service name>);
at the end, the instruction:
endService(<service name>);
These two instructions notify the SALOME supervision that the component service has actually received the CORBA request (beginService) and that execution of the service has actually terminated (endService). Same comment as in the C++ case (Enable component supervision).
The implementation class constructor should be modified as follows to signal use of the notification:
def __init__(self, orb, poa, this, containerName,
instanceName, interfaceName):
SALOME_ComponentPy_i.__init__(self, orb, poa, this,
containerName, instanceName,
interfaceName, True)
in which the parameter “1” is added to the end of the call to the SALOME_ComponentPy_i of the constructor. The component can then use the following instruction:
sendMessage(event_type, message);
to send messages providing information about progress with the calculation or an abnormal situation, etc. These messages will be visible to the SALOME user. The first parameter indicates the message type (“warning”, “step”, “trace”, “verbose”), and the second parameter indicates the message contents (character string).
Consider the FreeFem.py implementation file and adapt it:
FreeFemComponent.py
from SALOME_ComponentPy import *
import FreeFem
class FreeFemComponent(Engines__POA.FreeFemComponent,
SALOME_ComponentPy_i):
def __init__(self, orb, poa, this, containerName,
instanceName, interfaceName):
SALOME_ComponentPy_i.__init__(self, orb, poa, this, containerName,
instanceName, interfaceName, False)
self.F = FreeFem.FreeFem();
def Bords(self, b):
self.F.Bords(b);
def Flux(self, u1, u2):
self.F.Flux(u1, u2);
def Convection(self, cond_init, dt, n):
self.F.Convection(cond_init, dt, n)
Autotools like SALOME will be used.
This part will be done with reference to CORBA [CORBA] specifications, and particularly IDL specifications – languages [LANG] or the different CORBA manuals, for example [HENVIN] (C++) and [PyCorba] (python).
This part will be done with reference to MED and MEDMemory documentation [MEDDoc] and [MEDMemory].