Tooling Integration

Introduction

This page describes in detail how to integrate foreign components with modeled controllers. An architectural diagram of the system shows the components and all the connections between them. TismTool generates source code for instantiating the components and establishing all connections between any pair of components. It expects from a foreign component the same set of interfaces that are also generated/realised for the modeled components.
The examples below show for all 4 programming languages how foreign components could be implemented. These examples are based on the Foreign demo. The Foreign controller has a (foreign) client component called User and a (foreign) server component called Driver.
The pattern followed is that a foreign component contains a behaviour class, that is accessed via a port. So, the 3 involved classes per foreign component are:

  1. the class with the implemented User/Driver behavior
  2. the foreign component itself containing that class
  3. the required c.q. provided port interfacing with the controller

Programming language C#

File user.cs

UserClass

This class implements the 2 functions (GetProvidedInterface() and SetRequiredInterface()) for connecting to the controller. It provides its own port TsmPI__Port that has been created in the constructor. And it receives the port named Port from the controller. Furthermore, the (overridden) TsmRun() function is responsible for all the activity of triggering the controller and waiting for the response. Finally, the provided interface (ICbCtlrServer) has been implemented with the callback function CbStarted().
Note the remark "Do NOT fill in Port.Source !!" . In the mocks (where this code can be copied from) the Source field is filled in on behalf of logging. Filling it here would corrupt the logging.

  public class UserClass : TsmClass, ICbCtlrServer
{
private MyTsmIPort_ICbCtlrServer TsmPI__Port;
private TsmIPort_ICtlrServer Port;

private int iRv;
private bool bCallback = false;

public UserClass( TsmBase oParent, string sInstanceName )
{
TsmPI__Port = new MyTsmIPort_ICbCtlrServer( this );
}

public override TsmInterfacePort GetProvidedInterface( string sPortName )
{
switch( sPortName )
{
case "UserPort":
return TsmPI__Port;
default :
break;
}
string s = "No port '" + sPortName + "' @ UserClass";
throw new TsmException( s );
}

public override void SetRequiredInterface( string sPortName, TsmInterfacePort oProvidedInterface )
{
switch( sPortName )
{
case "UserPort":
Port = (TsmIPort_ICtlrServer) oProvidedInterface;
// Do NOT fill in Port.Source !!
return;
default :
break;
}
string s = "No port '" + sPortName + "' @ UserClass";
throw new TsmException( s );
}

public override void TsmRun( object oObject )
{
iRv = Port.Start(true);

// Wait for response from callback function.
// Use global variable and non-busy polling for synchronisation.
while ( !bCallback )
{
Thread.Sleep( 100 );
}

// Grant time to CbThread to finish its callback activity.
Thread.Sleep( 1000 );
}

// ( UserClientPsm >> Starting ) => ( UserClientPsm >> Idle )
public void CbStarted( float fCbStart )
{
Tsm.Log( "CbStarted" );
bCallback = true;
}
}

UserForeign

This class of the foreign component instantiates the UserClass in its descriptor. It forwards the 2 connecting functions (GetProvidedInterface() and SetRequiredInterface()) to the instantiated class. And, also the TsmRun() function is forwarded to that user class.

  public class UserForeign : TsmPComponent
{
private UserClass m_UserClass;

public UserForeign( TsmBase oParent, string sInstanceName )
{
m_UserClass = new UserClass( this, "UserClass" );
}

public override TsmInterfacePort GetProvidedInterface( string sPortName )
{
switch ( sPortName )
{
case "d_rp_User":
return m_UserClass.GetProvidedInterface( "UserPort" );
default:
break;
}
string s = "No port '" + sPortName + "' @ UserComponent";
throw new TsmException( s );
}

public override void SetRequiredInterface( string sPortName, TsmInterfacePort oProvidedInterface )
{
switch ( sPortName )
{
case "d_rp_User":
m_UserClass.SetRequiredInterface( "UserPort", oProvidedInterface );
return;
default:
break;
}
string s = "No port '" + sPortName + "' @ UserComponent";
throw new TsmException( s );
}

public override void TsmRun( object oObject )
{
m_UserClass.TsmRun( oObject );
}
}

MyTsmIPort_ICbCtlrServer

This class implements the port that is the access point to the foreign component and hence to the component's instantiated UserClass. The port inherits from the modeled interface TsmIPort_ICbCtlrServer, that is the class of the port known by the invoking controller. Note that TsmIPort_ICbCtlrServer on its turn inherits from interface ICbCtlrServer. So, this port realises function CbStarted(), which forwards the call to the UserClass.

  public class MyTsmIPort_ICbCtlrServer : TsmIPort_ICbCtlrServer
{
private UserClass m_UserClass;

public MyTsmIPort_ICbCtlrServer( UserClass oParent )
: base( oParent )
{
m_UserClass = oParent;
}

public override void CbStarted( float fCbStart )
{
m_UserClass.CbStarted( fCbStart );
}
}

File driver.cs

DriverClass

This class implements the 2 functions (GetProvidedInterface() and SetRequiredInterface()) for connecting to the controller. It provides its own port TsmPI__Port that has been created in the constructor. And it receives the port named Port from the controller. Furthermore, the provided interface (IDriverServer) has been implemented with the function Begin().
Note the remark "Do NOT fill in Port.Source !!" . In the mocks (where this code can be copied from) the Source field is filled in on behalf of logging. Filling it here would corrupt the logging.

  public class DriverClass : TsmClass, IDriverServer
{
private MyTsmIPort_IDriverServer TsmPI__Port;
private TsmIPort_ICbDriverServer Port;

private double dPar = 1.0;
private long L10 = 55;

public DriverClass( TsmBase oParent, string sInstanceName )
{
TsmPI__Port = new MyTsmIPort_IDriverServer( this );
}

public override TsmInterfacePort GetProvidedInterface( string sPortName )
{
switch ( sPortName )
{
case "DriverPort":
return TsmPI__Port;
default:
break;
}
string s = "No port '" + sPortName + "' @ DriverClass";
throw new TsmException( s );
}

public override void SetRequiredInterface( string sPortName, TsmInterfacePort oProvidedInterface )
{
switch ( sPortName )
{
case "DriverPort":
Port = (TsmIPort_ICbDriverServer) oProvidedInterface;
// Do NOT fill in Port.Source !!
return;
default:
break;
}
string s = "No port '" + sPortName + "' @ DriverClass";
throw new TsmException( s );
}

// ( DriverServerPsm >> Beginning ) => ( DriverServerPsm >> Idle )
public long Begin( int iPar )
{
Port.CbBegin( dPar );
return L10;
}
}

DriverForeign

This class of the foreign component instantiates the DriverClass in its descriptor. It forwards the 2 connecting functions (GetProvidedInterface() and SetRequiredInterface()) to the instantiated class.

  public class DriverForeign : TsmPComponent
{
private DriverClass m_DriverClass;

public DriverForeign( TsmBase oParent, string sInstanceName )
{
m_DriverClass = new DriverClass( this, "DriverClass" );
}

public override TsmInterfacePort GetProvidedInterface( string sPortName )
{
switch ( sPortName )
{
case "d_pp_Driver":
return m_DriverClass.GetProvidedInterface( "DriverPort" );
default:
break;
}
string s = "No port '" + sPortName + "' @ DriverForeign";
throw new TsmException( s );
}

public override void SetRequiredInterface( string sPortName, TsmInterfacePort oProvidedInterface )
{
switch ( sPortName )
{
case "d_pp_Driver":
m_DriverClass.SetRequiredInterface( "DriverPort", oProvidedInterface );
return;
default:
break;
}
string s = "No port '" + sPortName + "' @ DriverForeign";
throw new TsmException( s );
}
}

MyTsmIPort_IDriverServer

This class implements the port that is the access point to the foreign component and hence to the component's instantiated DriverClass. The port inherits from the modeled interface TsmIPort_IDriverServer, that is the class of the port known by the invoking controller. Note that TsmIPort_IDriverServer on its turn inherits from interface IDriverServer. So, this port realises function Begin(), which forwards the call to the DriverClass.

  public class MyTsmIPort_IDriverServer : TsmIPort_IDriverServer
{
private DriverClass m_DriverClass;

public MyTsmIPort_IDriverServer( DriverClass oParent )
: base( oParent )
{
m_DriverClass = oParent;
}

public override long Begin( int iPar )
{
return m_DriverClass.Begin( iPar );
}
}

Programming language Java

The realisation of the foreign components in Java is almost the same as the realisation in C#. The differences refer mostly to the syntactic sugar.
Therefore, the accompanying remarks are similar to C#.

File UserForeign.java

UserClass

This class implements the 2 functions (GetProvidedInterface() and SetRequiredInterface()) for connecting to the controller. It provides its own port TsmPI__Port that has been created in the constructor. And it receives the port named Port from the controller. Furthermore, the (overridden) TsmRun() function is responsible for all the activity of triggering the controller and waiting for the response. Finally, the provided interface (ICbCtlrServer) has been implemented with the callback function CbStarted().
Note the remark "Do NOT fill in Port.Source !!" . In the mocks (where this code can be copied from) the Source field is filled in on behalf of logging. Filling it here would corrupt the logging.

class UserClass extends TsmClass implements ICbCtlrServer
{
private MyTsmIPort_ICbCtlrServer TsmPI__Port;
private TsmIPort_ICtlrServer Port;

private int iRv = 0;
private boolean bCallback = false;

public UserClass( TsmBase oParent, String sInstanceName )
{
TsmPI__Port = new MyTsmIPort_ICbCtlrServer( this );
}

@Override
public TsmInterfacePort GetProvidedInterface( String sPortName )
{
if ( sPortName.equals( "UserPort" ) )
{
return TsmPI__Port;
}
String s = "No port '" + sPortName + "' @ UserClass";
throw new TsmException( s );
}

@Override
public void SetRequiredInterface( String sPortName, TsmInterfacePort oProvidedInterface )
{
if ( sPortName.equals( "UserPort" ) )
{
Port = (TsmIPort_ICtlrServer) oProvidedInterface;
// Do NOT fill in Port.Source !!
return;
}
String s = "No port '" + sPortName + "' @ UserClass";
throw new TsmException( s );
}

@Override
public void TsmRun( Object oObject )
{
this.iRv = Port.Start(true);

try
{
// Wait for response from callback function.
// Use global variable and non-busy polling for synchronisation.
while ( !bCallback )
{
Thread.sleep( 100 );
}

// Grant time to CbThread to finish its callback activity.
Thread.sleep( 1000 );
} catch (InterruptedException e)
{
e.printStackTrace();
}
}

// ( UserClientPsm >> Starting ) => ( UserClientPsm >> Idle )
public void CbStarted( float fCbStart )
{
Tsm.Log( "CbStarted" );
bCallback = true;
}
}

UserForeign

This class of the foreign component instantiates the UserClass in its descriptor. It forwards the 2 connecting functions (GetProvidedInterface() and SetRequiredInterface()) to the instantiated class. And, also the TsmRun() function is forwarded to that user class.

public class UserForeign extends TsmPComponent
{
private UserClass m_UserClass;

public UserForeign( TsmBase oParent, String sInstanceName )
{
m_UserClass = new UserClass( this, "UserClass" );
}

@Override
public TsmInterfacePort GetProvidedInterface( String sPortName )
{
if ( sPortName.equals( "d_rp_User" ) )
{
return m_UserClass.GetProvidedInterface( "UserPort" );
}
String s = "No port '" + sPortName + "' @ UserForeign";
throw new TsmException( s );
}

@Override
public void SetRequiredInterface( String sPortName, TsmInterfacePort oProvidedInterface )
{
if ( sPortName.equals( "d_rp_User" ) )
{
m_UserClass.SetRequiredInterface( "UserPort", oProvidedInterface );
return;
}
String s = "No port '" + sPortName + "' @ UserForeign";
throw new TsmException( s );
}

@Override
public void TsmRun( Object oObject )
{
m_UserClass.TsmRun( oObject );
}
}

MyTsmIPort_ICbCtlrServer

This class implements the port that is the access point to the foreign component and hence to the component's instantiated UserClass. The port inherits from the modeled interface TsmIPort_ICbCtlrServer, that is the class of the port known by the invoking controller. Note that TsmIPort_ICbCtlrServer on its turn inherits from interface ICbCtlrServer. So, this port realises function CbStarted(), which forwards the call to the UserClass.

class MyTsmIPort_ICbCtlrServer extends TsmIPort_ICbCtlrServer
{
private UserClass m_UserClass;

public MyTsmIPort_ICbCtlrServer( UserClass oParent )
{
super( oParent );
m_UserClass = oParent;
}

@Override
public void CbStarted( float fCbStart )
{
m_UserClass.CbStarted( fCbStart );
}
}

File DriverForeign.java

DriverClass

This class implements the 2 functions (GetProvidedInterface() and SetRequiredInterface()) for connecting to the controller. It provides its own port TsmPI__Port that has been created in the constructor. And it receives the port named Port from the controller. Furthermore, the provided interface (IDriverServer) has been implemented with the function Begin().
Note the remark "Do NOT fill in Port.Source !!" . In the mocks (where this code can be copied from) the Source field is filled in on behalf of logging. Filling it here would corrupt the logging.

class DriverClass extends TsmClass implements IDriverServer
{
private MyTsmIPort_IDriverServer TsmPI__Port;
private TsmIPort_ICbDriverServer Port;

private double dPar = 1.0;
private long L10 = 55;

public DriverClass( TsmBase oParent, String sInstanceName )
{
TsmPI__Port = new MyTsmIPort_IDriverServer( this );
}

@Override
public TsmInterfacePort GetProvidedInterface( String sPortName )
{
if ( sPortName.equals( "DriverPort" ) )
{
return TsmPI__Port;
}
String s = "No port '" + sPortName + "' @ DriverClass";
throw new TsmException( s );
}

@Override
public void SetRequiredInterface( String sPortName, TsmInterfacePort oProvidedInterface )
{
if ( sPortName.equals( "DriverPort" ) )
{
Port = (TsmIPort_ICbDriverServer) oProvidedInterface;
// Do NOT fill in Port.Source !!
return;
}
String s = "No port '" + sPortName + "' @ DriverClass";
throw new TsmException( s );
}

// ( DriverServerPsm >> Beginning ) => ( DriverServerPsm >> Idle )
@Override
public long Begin( int iPar )
{
Port.CbBegin( dPar );
return L10;
}
}

DriverForeign

This class of the foreign component instantiates the DriverClass in its descriptor. It forwards the 2 connecting functions (GetProvidedInterface() and SetRequiredInterface()) to the instantiated class.

public class DriverForeign extends TsmPComponent
{
private DriverClass m_DriverClass;

public DriverForeign( TsmBase oParent, String sInstanceName )
{
m_DriverClass = new DriverClass( this, "DriverClass" );
}

@Override
public TsmInterfacePort GetProvidedInterface( String sPortName )
{
if ( sPortName.equals( "d_pp_Driver" ) )
{
return m_DriverClass.GetProvidedInterface( "DriverPort" );
}
String s = "No port '" + sPortName + "' @ DriverForeign";
throw new TsmException( s );
}

@Override
public void SetRequiredInterface( String sPortName, TsmInterfacePort oProvidedInterface )
{
if ( sPortName.equals( "d_pp_Driver" ) )
{
m_DriverClass.SetRequiredInterface( "DriverPort", oProvidedInterface );
return;
}
String s = "No port '" + sPortName + "' @ DriverForeign";
throw new TsmException( s );
}
}

MyTsmIPort_IDriverServer

This class implements the port that is the access point to the foreign component and hence to the component's instantiated DriverClass. The port inherits from the modeled interface TsmIPort_IDriverServer, that is the class of the port known by the invoking controller. Note that TsmIPort_IDriverServer on its turn inherits from interface IDriverServer. So, this port realises function Begin(), which forwards the call to the DriverClass.

class MyTsmIPort_IDriverServer extends TsmIPort_IDriverServer
{
private DriverClass m_DriverClass;

public MyTsmIPort_IDriverServer( DriverClass oParent )
{
super( oParent );
m_DriverClass = oParent;
}

@Override
public long Begin( int iPar )
{
return m_DriverClass.Begin( iPar );
}
}

Programming language C++

The realisation of the foreign components in C++ is basically the same as the realisation in C# or Java. The main differences concern the subdivision of the definitions in a header file and the implementation in a source file.

File User.h

The header file User.h of the foreign User component contains the definitions of the 3 classes. Note that all functions for MyTsmIPort_ICbCtlrServer are defined here by inline functions.

  class MyTsmIPort_ICbCtlrServer;
class UserClass : public TsmClass, public ICbCtlrServer
{
private:
TsmIPort_ICtlrServer* Port;
MyTsmIPort_ICbCtlrServer* TsmPI__Port;
int iRv;
bool bCallback;

public:
UserClass( TsmBase* oParent, string sInstanceName );
virtual ~UserClass();

TsmInterfacePort* GetProvidedInterface( string sPortName );
void SetRequiredInterface( string sPortName, TsmInterfacePort* oProvidedInterface );
void TsmRun( void* oObject );
void CbStarted( float fCbStart );
};

class UserForeign : public TsmPComponent
{
private:
UserClass* m_UserClass;

public:
UserForeign( TsmBase* oParent, string sInstanceName );
~UserForeign();

TsmInterfacePort* GetProvidedInterface( string sPortName );
void SetRequiredInterface( string sPortName, TsmInterfacePort* oProvidedInterface );
void TsmRun( void* oObject );
};

class MyTsmIPort_ICbCtlrServer : public TsmIPort_ICbCtlrServer
{
private:
UserClass* m_UserClass;

public:
MyTsmIPort_ICbCtlrServer( UserClass* oParent )
: TsmIPort_ICbCtlrServer( oParent )
, m_UserClass( oParent )
{
}

void CbStarted( float fCbStart )
{
m_UserClass->CbStarted( fCbStart );
}
};

File User.cpp

UserClass

This class implements the 2 functions (GetProvidedInterface() and SetRequiredInterface()) for connecting to the controller. It provides its own port TsmPI__Port that has been created in the constructor. And it receives the port named Port from the controller. Furthermore, the (overridden) TsmRun() function is responsible for all the activity of triggering the controller and waiting for the response. Finally, the provided interface (ICbCtlrServer) has been implemented with the callback function CbStarted().
Note the remark "Do NOT fill in Port->Source !!" . In the mocks (where this code can be copied from) the Source field is filled in on behalf of logging. Filling it here would corrupt the logging.

  UserClass::UserClass( TsmBase* oParent, string sInstanceName )
: bCallback( false )
{
TsmPI__Port = new MyTsmIPort_ICbCtlrServer( this );
}

UserClass::~UserClass()
{
delete TsmPI__Port;
}

TsmInterfacePort* UserClass::GetProvidedInterface( string sPortName )
{
if ( sPortName == "UserPort" )
{
return TsmPI__Port;
}
string s = "No port '" + sPortName + "' @ UserClass";
throw TsmException( s );
}

void UserClass::SetRequiredInterface( string sPortName, TsmInterfacePort* oProvidedInterface )
{
if ( sPortName == "UserPort" )
{
Port = static_cast(oProvidedInterface);
// Do NOT fill in Port->Source !!
return;
}
string s = "No port '" + sPortName + "' @ UserClass";
throw TsmException( s );
}

void UserClass::TsmRun( void* oObject )
{
this->iRv = this->Port->Start( true);

// Wait for response from callback function.
// Use global variable and non-busy polling for synchronisation.
while ( !this->bCallback )
{
boost::posix_time::milliseconds ms(100);
boost::this_thread::sleep( ms );
}

// Grant time to CbThread to finish its callback activity.
boost::posix_time::milliseconds ms2(1000);
boost::this_thread::sleep( ms2 );
}

void UserClass::CbStarted( float fCbStart )
{
Tsm::Log( "CbStarted" );
this->bCallback = true;
}

UserForeign

This class of the foreign component instantiates the UserClass in its descriptor. It forwards the 2 connecting functions (GetProvidedInterface() and SetRequiredInterface()) to the instantiated class. And, also the TsmRun() function is forwarded to that user class .

  UserForeign::UserForeign( TsmBase* oParent, string sInstanceName )
{
m_UserClass = new UserClass( this, "UserClass" );
}

UserForeign::~UserForeign()
{
delete m_UserClass;
}

TsmInterfacePort* UserForeign::GetProvidedInterface( string sPortName )
{
if ( sPortName == "d_rp_User" )
{
return m_UserClass->GetProvidedInterface( "UserPort" );
}
string s = "No port '" + sPortName + "' @ UserForeign";
throw TsmException( s );
}

void UserForeign::SetRequiredInterface( string sPortName, TsmInterfacePort* oProvidedInterface )
{
if ( sPortName == "d_rp_User" )
{
m_UserClass->SetRequiredInterface( "UserPort", oProvidedInterface );
return;
}
string s = "No port '" + sPortName + "' @ UserForeign";
throw TsmException( s );
}

void UserForeign::TsmRun( void* oObject )
{
m_UserClass->TsmRun( oObject );
}

File Driver.h(++)

The header file Driver.h of the foreign Driver component contains the definitions of the 3 classes. Note that all functions for MyTsmIPort_IDriverServer are defined here by inline functions.

  class MyTsmIPort_IDriverServer;
class DriverClass : public TsmClass, public IDriverServer
{
private:
MyTsmIPort_IDriverServer* TsmPI__Port;
TsmIPort_ICbDriverServer* Port;

double dPar;
long L10;

public:
DriverClass( TsmBase* oParent, string sInstanceName );
virtual ~DriverClass();

TsmInterfacePort* GetProvidedInterface( string sPortName );
void SetRequiredInterface( string sPortName, TsmInterfacePort* oProvidedInterface );

// ( DriverServerPsm >> Beginning ) => ( DriverServerPsm >> Idle )
long Begin( int iPar );
};

class DriverForeign : public TsmPComponent
{
private:
DriverClass* m_DriverClass;

public:
DriverForeign( TsmBase* oParent, string sInstanceName );
virtual ~DriverForeign();

TsmInterfacePort* GetProvidedInterface( string sPortName );
void SetRequiredInterface( string sPortName, TsmInterfacePort* oProvidedInterface );
};

class MyTsmIPort_IDriverServer : public TsmIPort_IDriverServer
{
private:
DriverClass* m_DriverClass;

public:
MyTsmIPort_IDriverServer( DriverClass* oParent )
: TsmIPort_IDriverServer( oParent )
, m_DriverClass( oParent )
{
}

long Begin( int iPar )
{
return m_DriverClass->Begin( iPar );
}
};

File Driver.cpp

DriverClass

This class implements the 2 functions (GetProvidedInterface() and SetRequiredInterface()) for connecting to the controller. It provides its own port TsmPI__Port that has been created in the constructor. And it receives the port named Port from the controller. Furthermore, the provided interface (IDriverServer) has been implemented with the function Begin().
Note the remark "Do NOT fill in Port->Source !!" . In the mocks (where this code can be copied from) the Source field is filled in on behalf of logging. Filling it here would corrupt the logging.

  DriverClass::DriverClass( TsmBase* oParent, string sInstanceName )
: dPar( 1.0 )
, L10( 55 )
{
TsmPI__Port = new MyTsmIPort_IDriverServer( this );
}

DriverClass::~DriverClass()
{
delete TsmPI__Port;
}

TsmInterfacePort* DriverClass::GetProvidedInterface( string sPortName )
{
if ( sPortName == "DriverPort" )
{
return TsmPI__Port;
}
string s = "No port '" + sPortName + "' @ DriverClass";
throw TsmException( s );
}

void DriverClass::SetRequiredInterface( string sPortName, TsmInterfacePort* oProvidedInterface )
{
if ( sPortName == "DriverPort" )
{
Port = static_cast(oProvidedInterface);
// Do NOT fill in Port->Source !!
return;
}
string s = "No port '" + sPortName + "' @ DriverClass";
throw TsmException( s );
}

// ( DriverServerPsm >> Beginning ) => ( DriverServerPsm >> Idle )
long DriverClass::Begin( int iPar )
{
Port->CbBegin( dPar );
return L10;
}

DriverForeign

This class of the foreign component instantiates the DriverClass in its descriptor. It forwards the 2 connecting functions (GetProvidedInterface() and SetRequiredInterface()) to the instantiated class.

  DriverForeign::DriverForeign( TsmBase* oParent, string sInstanceName )
{
m_DriverClass = new DriverClass( this, "DriverClass" );
}

DriverForeign::~DriverForeign()
{
delete m_DriverClass;
}

TsmInterfacePort* DriverForeign::GetProvidedInterface( string sPortName )
{
if ( sPortName == "d_pp_Driver" )
{
return m_DriverClass->GetProvidedInterface( "DriverPort" );
}
string s = "No port '" + sPortName + "' @ DriverForeign";
throw TsmException( s );
}

void DriverForeign::SetRequiredInterface( string sPortName, TsmInterfacePort* oProvidedInterface )
{
if ( sPortName == "d_pp_Driver" )
{
m_DriverClass->SetRequiredInterface( "DriverPort", oProvidedInterface );
return;
}
string s = "No port '" + sPortName + "' @ DriverForeign";
throw TsmException( s );
}

Programming language C

The realisation of the foreign components in C resembles the realisation in C++. The main differences deal with the emulation of object oriented semantics. The implementation of a component consists of the definitions in an include file and the functions in a source file.

File User.h

The header file User.h of the foreign User component contains the type definitions of the structures for the 3 "classes" and their function prototypes.

typedef struct UserClassStruct
{
#include "TsmClass_fields.h"
void (*CbStarted)( struct UserClassStruct* this, float fCbStart );

struct MyTsmIPort_ICbCtlrServerStruct* TsmPI__Port;
TsmIPort_ICtlrServer* Port;
int iRv;
TsmBool bCallback;
} UserClass;

UserClass* UserClass_Ctor( TsmBase* oParent, char* sInstanceName );
void UserClass_Gtor( struct UserClassStruct* this );
TsmInterfacePort* UserClass_GetProvidedInterface( struct UserClassStruct* this, char* sPortName );
void UserClass_SetRequiredInterface( struct UserClassStruct* this, char* sPortName, TsmInterfacePort* oProvidedInterface );
void UserClass_TsmRun( struct UserClassStruct* this, void* oObject );
void UserClass_CbStarted( struct UserClassStruct* this, float fCbStart );

typedef struct UserForeignStruct
{
#include "TsmPComponent_fields.h"
UserClass* m_UserClass;
} UserForeign;

UserForeign* UserForeign_Ctor( TsmBase* oParent, char* sInstanceName );
TsmInterfacePort* UserForeign_GetProvidedInterface( struct UserForeignStruct* this, char* sPortName );
void UserForeign_SetRequiredInterface( struct UserForeignStruct* this, char* sPortName, TsmInterfacePort* oProvidedInterface );
void UserForeign_TsmRun( struct UserForeignStruct* this, void* oObject );

typedef struct MyTsmIPort_ICbCtlrServerStruct /* copied from TsmIPort_ICbCtlrServerStruct */
{
#include "TsmInterfacePort_fields.h"
void (*CbStarted)( struct MyTsmIPort_ICbCtlrServerStruct* this, float fCbStart );

struct UserClassStruct* m_UserClass;
} MyTsmIPort_ICbCtlrServer;

MyTsmIPort_ICbCtlrServer* MyTsmIPort_ICbCtlrServer_Ctor( TsmBase* oParent );
void MyTsmIPort_ICbCtlrServer_CbStarted( struct MyTsmIPort_ICbCtlrServerStruct* this, float fCbStart );

File User.c

UserClass

This class implements several functions including the 2 functions for connecting to the controller. The constructor UserClass_Ctor shows how an object is created. We will dive into it to explain the object oriented semantics.

  1. Memory for the structure is allocated.
  2. The UserClass inherits from the base class TsmClass, and therefore the new structure is filled with the data of such a class; these data are mostly pointers to functions that serve as class methods.
  3. Pointers to functions are overridden with pointers to our own functions. This happens here for 4 functions. The 2 functions (GetProvidedInterface() and SetRequiredInterface()) take care of connecting to the controller. The TsmRun() function is responsible for all the activity of triggering the controller and waiting for the response. The Gtor() function is for cleanup purpose.
  4. The function pointer CbStarted is filled with the address of our function UserClass_CbStarted, that implements the provided interface (ICbCtlrServer) .
  5. The provided port TsmPI__Port is constructed.
  6. The 2 local variables are initialised.

Note the remark "Do NOT fill in Port->Source !!" . In the mocks (where this code can be copied from) the Source field is filled in on behalf of logging. Filling it here would corrupt the logging.

UserClass* UserClass_Ctor( TsmBase* oParent, char* sInstanceName )
{
UserClass* this = (UserClass*) Tsm_Malloc( sizeof(UserClass) );
TsmClass_Ftor( (TsmClass*) this );
this->GetProvidedInterface = (Tsm_pfClPrvIfc) UserClass_GetProvidedInterface;
this->SetRequiredInterface = (Tsm_pfClReqIfc) UserClass_SetRequiredInterface;
this->TsmRun = (Tsm_pfClVoid) UserClass_TsmRun;
this->Gtor = (Tsm_pfObj) UserClass_Gtor;

this->CbStarted = UserClass_CbStarted;
this->TsmPI__Port = MyTsmIPort_ICbCtlrServer_Ctor( (TsmBase*) this );
this->iRv = 0;
this->bCallback = FALSE;
return this;
}

void UserClass_Gtor( struct UserClassStruct* this )
{
this->TsmPI__Port->Dtor( (TsmObject*) this->TsmPI__Port );
TsmClass_Gtor( (TsmClass*) this );
}

TsmInterfacePort* UserClass_GetProvidedInterface( struct UserClassStruct* this, char* sPortName )
{
char* s;
if ( 0==strcmp( sPortName, "UserPort" ) )
{
return (TsmInterfacePort*) this->TsmPI__Port;
}
s = Tsm_Malloc( TSM_NCH_FULLNAME );
sprintf( s, "No port '%s' @ UserClass", sPortName );
TsmException_Quit( s );
Tsm_Free( s );
return NULL;
}

void UserClass_SetRequiredInterface( struct UserClassStruct* this, char* sPortName, TsmInterfacePort* oProvidedInterface )
{
char* s;
if ( 0==strcmp( sPortName, "UserPort" ) )
{
this->Port = (TsmIPort_ICtlrServer*) oProvidedInterface;
/* Do NOT fill in Port->Source !! */
return;
}
s = Tsm_Malloc( TSM_NCH_FULLNAME );
sprintf( s, "No port '%s' @ UserClass", sPortName );
TsmException_Quit( s );
Tsm_Free( s );
}

void UserClass_TsmRun( struct UserClassStruct* this, void* oObject )
{
this->iRv = this->Port->Start( this->Port, TRUE);

/*
* Wait for response from callback function.
* Use global variable and non-busy polling for synchronisation.
*/
while ( !this->bCallback )
{
OSAL_ThreadSleep( 100 );
}

/* Grant time to CbThread to finish its callback activity. */
OSAL_ThreadSleep( 1000 );
}

/* ( UserClientPsm >> Starting ) => ( UserClientPsm >> Idle ) */
void UserClass_CbStarted( struct UserClassStruct* this, float fCbStart )
{
Tsm_Log( "CbStarted" );
this->bCallback = TRUE;
}

UserForeign

This class of the foreign component instantiates the UserClass. It delegates the 2 connecting functions to the instantiated class. And, also a TsmRun() function is needed for that user class .

UserForeign* UserForeign_Ctor( TsmBase* oParent, char* sInstanceName )
{
UserForeign* this = (UserForeign*) Tsm_Malloc( sizeof(UserForeign) );
TsmPComponent_Ftor( (TsmPComponent*) this );
this->GetProvidedInterface = (Tsm_pfClPrvIfc) UserForeign_GetProvidedInterface;
this->SetRequiredInterface = (Tsm_pfClReqIfc) UserForeign_SetRequiredInterface;
this->TsmRun = (Tsm_pfClVoid) UserForeign_TsmRun;
this->m_UserClass = UserClass_Ctor( (TsmBase*) this, "UserClass" );
return this;
}

TsmInterfacePort* UserForeign_GetProvidedInterface( struct UserForeignStruct* this, char* sPortName )
{
char* s;
if ( 0==strcmp( sPortName, "d_rp_User" ) )
{
return this->m_UserClass->GetProvidedInterface( (TsmClass*) this->m_UserClass, "UserPort" );
}
s = Tsm_Malloc( TSM_NCH_FULLNAME );
sprintf( s, "No port '%s' @ UserForeign", sPortName );
TsmException_Quit( s );
Tsm_Free( s );
return NULL;
}

void UserForeign_SetRequiredInterface( struct UserForeignStruct* this, char* sPortName, TsmInterfacePort* oProvidedInterface )
{
char* s;
if ( 0==strcmp( sPortName, "d_rp_User" ) )
{
this->m_UserClass->SetRequiredInterface( (TsmClass*) this->m_UserClass, "UserPort", oProvidedInterface );
return;
}
s = Tsm_Malloc( TSM_NCH_FULLNAME );
sprintf( s, "No port '%s' @ UserForeign", sPortName );
TsmException_Quit( s );
Tsm_Free( s );
}

void UserForeign_TsmRun( struct UserForeignStruct* this, void* oObject )
{
this->m_UserClass->TsmRun( (TsmClass*) this->m_UserClass, oObject );
}

MyTsmIPort_ICbCtlrServer

This class implements the port that is the access point to the foreign component and hence to the component's instantiated UserClass. The port inherits from the modeled interface TsmIPort_ICbCtlrServer, that is the class of the port known by the invoking controller. Note that TsmIPort_ICbCtlrServer on its turn inherits from class TsmInterfacePort and from interface ICbCtlrServer. Therefore, the fields from TsmInterfacePort are copied to this ; and the function pointer CbStarted is filled with the address of our function MyTsmIPort_ICbCtlrServer_CbStarted, that implements the provided interface ICbCtlrServer. So, this port realises function CbStarted(), which forwards the call to the UserClass.

MyTsmIPort_ICbCtlrServer* MyTsmIPort_ICbCtlrServer_Ctor( TsmBase* oParent )
{
MyTsmIPort_ICbCtlrServer* this = (MyTsmIPort_ICbCtlrServer*) Tsm_Malloc( sizeof(MyTsmIPort_ICbCtlrServer) );
TsmInterfacePort_Ftor( (TsmInterfacePort*) this );
this->CbStarted = MyTsmIPort_ICbCtlrServer_CbStarted;
this->m_UserClass = (struct UserClassStruct*) oParent;
return this;
}

void MyTsmIPort_ICbCtlrServer_CbStarted( struct MyTsmIPort_ICbCtlrServerStruct* this, float fCbStart )
{
this->m_UserClass->CbStarted( this->m_UserClass, fCbStart );
}

File Driver.h

The header file Driver.h of the foreign Driver component contains the type definitions of the structures for the 3 "classes" and their function prototypes.

typedef struct DriverClassStruct
{
#include "TsmClass_fields.h"
struct MyTsmIPort_IDriverServerStruct* TsmPI__Port;
TsmIPort_ICbDriverServer* Port;
double dPar;
long L10;
} DriverClass;

DriverClass* DriverClass_Ctor( TsmBase* oParent, char* sInstanceName );
void DriverClass_Gtor( struct DriverClassStruct* this );
TsmInterfacePort* DriverClass_GetProvidedInterface( struct DriverClassStruct* this, char* sPortName );
void DriverClass_SetRequiredInterface( struct DriverClassStruct* this, char* sPortName, TsmInterfacePort* oProvidedInterface );

typedef struct DriverForeignStruct
{
#include "TsmPComponent_fields.h"
DriverClass* m_DriverClass;
} DriverForeign;

DriverForeign* DriverForeign_Ctor( TsmBase* oParent, char* sInstanceName );
TsmInterfacePort* DriverForeign_GetProvidedInterface( struct DriverForeignStruct* this, char* sPortName );
void DriverForeign_SetRequiredInterface( struct DriverForeignStruct* this, char* sPortName, TsmInterfacePort* oProvidedInterface );

typedef struct MyTsmIPort_IDriverServerStruct /* copied from TsmIPort_IDriverServerStruct */
{
#include "TsmInterfacePort_fields.h"
long (*Begin)( struct MyTsmIPort_IDriverServerStruct* this, int iPar );

struct DriverClassStruct* m_DriverClass;
} MyTsmIPort_IDriverServer;

MyTsmIPort_IDriverServer* MyTsmIPort_IDriverServer_Ctor( TsmBase* oParent );
long MyTsmIPort_IDriverServer_Begin( struct MyTsmIPort_IDriverServerStruct* this, int iPar );

File Driver.c

DriverClass

This class implements several functions including the 2 functions for connecting to the controller. The constructor DriverClass_Ctor shows how an object is created. We will dive into it to explain the object oriented semantics.

  1. Memory for the structure is allocated.
  2. The DriverClass inherits from the base class TsmClass, and therefore the new structure is filled with the data of such a class; these data are mostly pointers to functions that serve as class methods.
  3. Pointers to functions are overridden with pointers to our own functions. This happens here for 3 functions. The 2 functions (GetProvidedInterface() and SetRequiredInterface()) take care of connecting to the controller. The Gtor() function is for cleanup purpose.
  4. Note that the function pointer Begin is not filled with the address of our function DriverClass_Begin . This is a developer's choice of the implementation in MyTsmIPort_IDriverServer.
  5. The provided port TsmPI__Port is constructed.
  6. The 2 local variables are initialised.

Note the remark "Do NOT fill in Port->Source !!" . In the mocks (where this code can be copied from) the Source field is filled in on behalf of logging. Filling it here would corrupt the logging.

DriverClass* DriverClass_Ctor( TsmBase* oParent, char* sInstanceName )
{
DriverClass* this = (DriverClass*) Tsm_Malloc( sizeof(DriverClass) );
TsmClass_Ftor( (TsmClass*) this );
this->GetProvidedInterface = (Tsm_pfClPrvIfc) DriverClass_GetProvidedInterface;
this->SetRequiredInterface = (Tsm_pfClReqIfc) DriverClass_SetRequiredInterface;
this->Gtor = (Tsm_pfObj) DriverClass_Gtor;

this->TsmPI__Port = MyTsmIPort_IDriverServer_Ctor( (TsmBase*) this );
this->dPar = 1.0;
this->L10 = 55;
return this;
}

void DriverClass_Gtor( struct DriverClassStruct* this )
{
this->TsmPI__Port->Dtor( (TsmObject*) this->TsmPI__Port );
TsmClass_Gtor( (TsmClass*) this );
}

TsmInterfacePort* DriverClass_GetProvidedInterface( struct DriverClassStruct* this, char* sPortName )
{
char* s;
if ( 0==strcmp( sPortName, "DriverPort" ) )
{
return (TsmInterfacePort*) this->TsmPI__Port;
}
s = Tsm_Malloc( TSM_NCH_FULLNAME );
sprintf( s, "No port '%s' @ DriverClass", sPortName );
TsmException_Quit( s );
Tsm_Free( s );
return NULL;
}

void DriverClass_SetRequiredInterface( struct DriverClassStruct* this, char* sPortName, TsmInterfacePort* oProvidedInterface )
{
char* s;
if ( 0==strcmp( sPortName, "DriverPort" ) )
{
this->Port = (TsmIPort_ICbDriverServer*) oProvidedInterface;
/* Do NOT fill in Port->Source !! */
return;
}
s = Tsm_Malloc( TSM_NCH_FULLNAME );
sprintf( s, "No port '%s' @ DriverClass", sPortName );
TsmException_Quit( s );
Tsm_Free( s );
}

/* ( DriverServerPsm >> Beginning ) => ( DriverServerPsm >> Idle ) */
long DriverClass_Begin( struct DriverClassStruct* this, int iPar )
{
this->Port->CbBegin( this->Port, this->dPar );
return this->L10;
}

DriverForeign

This class of the foreign component instantiates the DriverClass. It delegates the 2 connecting functions to the instantiated class.

DriverForeign* DriverForeign_Ctor( TsmBase* oParent, char* sInstanceName )
{
DriverForeign* this = (DriverForeign*) Tsm_Malloc( sizeof(DriverForeign) );
TsmPComponent_Ftor( (TsmPComponent*) this );
this->GetProvidedInterface = (Tsm_pfClPrvIfc) DriverForeign_GetProvidedInterface;
this->SetRequiredInterface = (Tsm_pfClReqIfc) DriverForeign_SetRequiredInterface;

this->m_DriverClass = DriverClass_Ctor( (TsmBase*) this, "DriverClass" );
return this;
}

TsmInterfacePort* DriverForeign_GetProvidedInterface( struct DriverForeignStruct* this, char* sPortName )
{
char* s;
if ( 0==strcmp( sPortName, "d_pp_Driver" ) )
{
return this->m_DriverClass->GetProvidedInterface( (TsmClass*) this->m_DriverClass, "DriverPort" );
}
s = Tsm_Malloc( TSM_NCH_FULLNAME );
sprintf( s, "No port '%s' @ DriverForeign", sPortName );
TsmException_Quit( s );
Tsm_Free( s );
return NULL;
}

void DriverForeign_SetRequiredInterface( struct DriverForeignStruct* this, char* sPortName, TsmInterfacePort* oProvidedInterface )
{
char* s;
if ( 0==strcmp( sPortName, "d_pp_Driver" ) )
{
this->m_DriverClass->SetRequiredInterface( (TsmClass*) this->m_DriverClass, "DriverPort", oProvidedInterface );
return;
}
s = Tsm_Malloc( TSM_NCH_FULLNAME );
sprintf( s, "No port '%s' @ DriverForeign", sPortName );
TsmException_Quit( s );
Tsm_Free( s );
}

MyTsmIPort_IDriverServer

This class implements the port that is the access point to the foreign component and hence to the component's instantiated DriverClass. The port inherits from the modeled interface TsmIPort_IDriverServer, that is the class of the port known by the invoking controller. Note that TsmIPort_IDriverServer on its turn inherits from class TsmInterfacePort and from interface IDriverServer. Therefore, the fields from TsmInterfacePort are copied to this ; and the function pointer Begin is filled with the address of our function MyTsmIPort_ IDriverServer_Begin, that implements the provided interface IDriverServer. So, this port realises function Begin(), which forwards the call to the DriverClass.

MyTsmIPort_IDriverServer* MyTsmIPort_IDriverServer_Ctor( TsmBase* oParent )
{
MyTsmIPort_IDriverServer* this = (MyTsmIPort_IDriverServer*) Tsm_Malloc( sizeof(MyTsmIPort_IDriverServer) );
TsmInterfacePort_Ftor( (TsmInterfacePort*) this );
this->Begin = MyTsmIPort_IDriverServer_Begin;
this->m_DriverClass = (DriverClass*) oParent;
return this;
}

long MyTsmIPort_IDriverServer_Begin( struct MyTsmIPort_IDriverServerStruct* this, int iPar )
{
return DriverClass_Begin( this->m_DriverClass, iPar );
}