Skip to main content


Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Eclipse Projects » Eclipse Titan » Stateful translation ports
Stateful translation ports [message #1786074] Wed, 25 April 2018 08:14
Elemer Lelik is currently offline Elemer LelikFriend
Messages: 853
Registered: January 2015
Senior Member
Dear all,

The current standard for translation ports describes a stateless port: any message received encoded/decoded and then immediately sent to the upper or lower layer respectively.
With a small addition the user can implement full finite state machines in the translation ports, due to us extending the standard with statefulness.
As this represents a complex behaviour that needs clarification, let me walk you through an example, see the attached code.

The code can work in two modes, set by the boolean test suite parameter MQTT_IPL4_translation.tsp_stateful.
In both modes the code plays the role of an MQTT client which has the following task: connect to the MQTT broker and post a message to a given topic then disconnect.


The signalling involved is as below:

MQTTClient(Titan)                MQTTBroker


    +                               +
    |          connect              |
    +------------------------------->
   (Wait_connack)                   |
    |          connack              |
    <-------------------------------+
    (Connected)                     |
    |          subscribe            |
    +------------------------------->
  (Wait_suback)                     |
    |          publish              |
    +------------------------------->
    |                               |
    |          suback               |
    <-------------------------------+
    |                               |
   (Wait_publish_echo)              |
    |                               |
    |          publish(echo)        |
    <-------------------------------+
    |                               |
    |                               |
    |          unsubscribe          |
    +------------------------------->
   (Wait_unsuback)                  |
    |                               |
    |          unsuback             |
    <-------------------------------+
   (Disconnected)                   |
    |                               |
    |         disconnect            |
    +------------------------------->
	|                               |
    |          TCP conn closed      |
    <-------------------------------+
    +                               +


Let's see what happens first when tsp_stateful is set to false (stateless behaviour of the translation).

As we want a clear separation between the different protocol layers , we will have a PTC which will simulate user protocol/behaviour:
function f_UserBehaviour()  runs on User_CT
{
  alt {
    []User_PCO.receive("Ready") {
      User_PCO.send("Breathe, breathe in the air")
    }
  }
}

When PTC signals it's ready status, the PTC sends the message to be added to the topic.

The testcase executing on the MTC will deal with the MQTT states:
//-----------------------------------------------------------------------------
testcase TC_MQTT_Transl_stateless() runs on MQTT_CT system System_CT {
  //-----------------------------------------------------------------------------

  map(self:MQTT_PCO, system:IPL4_PCO); 
  var User_CT v_userCT := User_CT.create;  
  connect(self:User_PCO, v_userCT:User_PCO)
  v_userCT.start(f_UserBehaviour())




  f_preamble();

  var default v_def := activate(as_default())

  //send connect

  MQTT_PCO.send(t_connect);
  log("-------------------->MQTT connect sent" )


  t.start(10.0) 

  alt 
  { 
    [] MQTT_PCO.receive(t_connack) -> value v_msg	{ 

      log("MQTT connack received<<-------------------- : ",v_msg);

    }

  } 


  User_PCO.send("Ready")
  alt {


    []User_PCO.receive(charstring:?) -> value v_ctrl {}

  }



  //send subscribe
  MQTT_PCO.send(t_subscribe);
  log("-------------------->MQTT subscribe sent" )
  //send publish
  MQTT_PCO.send(t_publish(v_ctrl));
  log("-------------------->MQTT publish sent" )

  t.start(10.0) 

  alt 
  { 
    [] MQTT_PCO.receive(t_suback) -> value v_msg	{ 
      log("MQTT suback received<<-------------------- : ",v_msg);
    }

  } 

  t.start(10.0) 

  alt 
  { 
    [] MQTT_PCO.receive(t_publish_r) -> value v_msg	{ 
      log("MQTT publish response received : ",v_msg);

    }

  } 

  //send unsubscribe
  MQTT_PCO.send(t_unsubscribe);
  log("-------------------->MQTT unsubscribe sent" )
  t.start(5.0) 

  alt 
  { 
    [] MQTT_PCO.receive(t_unsuback) -> value v_msg	{ 
      log("MQTT unsuback received<<-------------------- : ",v_msg);
    }

  } 

  //send disconnect

  MQTT_PCO.send(t_disconnect);
  log("-------------------->MQTT disconnect sent" )

  t.start(10.0) 

  alt 
  { 
    [] MQTT_PCO.receive(MQTT_v3_1_1_Message:?) -> value v_msg	{ 
      log("MQTT response received<<-------------------- : ",v_msg);
    }
    [] MQTT_PCO.receive("Success") {  log("Connection closed");  v_TCPState:=Disconnected; } 
    [] t.timeout  {  log("Timeout") }

  } 


if(v_TCPState!=Disconnected)
{
  f_postamble();
}

setverdict(pass);


  disconnect(self:User_PCO, v_userCT:User_PCO)
  unmap(self:MQTT_PCO, system:IPL4_PCO); 


}



The port translation in the stateless case

type port MQTT_IPL4_PT message map to IPL4asp_PT //Translation port
{
  out  

  charstring             to   ASP_Send   with f_enc_charstring_Translation(),
  MQTT_v3_1_1_Message    to   ASP_Send   with f_enc_MQTT_Translation(),
  integer                to   ASP_Send   with f_set_connId()    
  in      
  MQTT_v3_1_1_Message    from ASP_RecvFrom 	with f_dec_MQTT_Translation(),
  charstring             from ASP_Event 	with f_dec_charstring_Translation(),
  ASP_Event  		 	


  var  integer v_connId      
  var  charstring v_msg 
  var  MQTTClientState v_state:=Idle ;
  var  ASP_Send v_ASP_Send  ;  

} with {extension "internal"}


will use only f_set_connId() , to initialize connection Id,
f_enc_MQTT_Translation(), to translate from MQTT messages to ASP_Send,
f_dec_MQTT_Translation() to translate from ASP_Recv to MQTT messages and .
f_dec_charstring_Translation which translates the TCP connection close into a string message to be sent to the PTC
to signal successful termination of the test case.
Translation itself has no states, states are tracked in the MQTT layer(MTC)

The console log for this scenario:

Executing all items of [EXECUTE] section.                                                                                                  
MC2> MTC@esekilxxen1841: connect result{ errorCode := omit, connId := 1, os_error_code := omit, os_error_text := omit }                    
MTC@esekilxxen1841: v_state:  Idle (0)                                                                                                     
MTC@esekilxxen1841: v_connId:  1                                                                                                           
MTC@esekilxxen1841: -------------------->MQTT connect sent                                                                                 
MTC@esekilxxen1841: v_state:  Wait_connack (1)                                                                                             
MTC@esekilxxen1841: MQTT connack received<<-------------------- : { msg := { connack := { header := { flags := '0000'B }, session_present_flag := '0'B, connect_return_code := 0 } } }                                                                                                  
MTC@esekilxxen1841: v_state:  Connected (2)                                                                                                 
MTC@esekilxxen1841: v_connId:  1                                                                                                            
MTC@esekilxxen1841: -------------------->MQTT subscribe sent                                                                                
MTC@esekilxxen1841: v_state:  Wait_suback (3)
MTC@esekilxxen1841: v_connId:  1
MTC@esekilxxen1841: -------------------->MQTT publish sent
MTC@esekilxxen1841: v_state:  Wait_suback (3)
MTC@esekilxxen1841: MQTT suback received<<-------------------- : { msg := { suback := { header := { flags := '0000'B }, packet_identifier := 52934, payload := { return_code := { 2 } } } } }
MTC@esekilxxen1841: v_state:  Wait_publish_echo (5)
MTC@esekilxxen1841: MQTT publish response received : { msg := { publish := { header := { dup_flag := '0'B, qos_level := AT_MOST_ONCE_DELIVERY (0), retain_flag := '0'B }, topic_name := "testtopic/1xgrh", packet_identifier := omit, payload := '427265617468652C206272656174686520696E2074686520616972'O ("Breathe, breathe in the air") } } }
MTC@esekilxxen1841: v_state:  Wait_publish_echo (5)
MTC@esekilxxen1841: v_connId:  1
MTC@esekilxxen1841: -------------------->MQTT unsubscribe sent
MTC@esekilxxen1841: v_state:  Wait_unsuback (4)
MTC@esekilxxen1841: MQTT unsuback received<<-------------------- : { msg := { unsuback := { header := { flags := '0000'B }, packet_identifier := 52936 } } }
MTC@esekilxxen1841: v_state:  Disconnected (7)
MTC@esekilxxen1841: v_connId:  1
MTC@esekilxxen1841: -------------------->MQTT disconnect sent
MTC@esekilxxen1841: v_state  Disconnected (7)
MTC@esekilxxen1841: Connection closed
MC@esekilxxen1841: Test execution finished.
Execution of [EXECUTE] section finished.


All is well, but it would be preferable to somehow hide the whole MQTT machinery under the hood; the user is not really interested in that but in the user protocol.

So let's set tsp_stateful to true and see what happens:

First of all , we don't need an extra PTC, all behaviour is described in the test case:

//-----------------------------------------------------------------------------
testcase TC_MQTT_Transl_stateful() runs on MQTT_CT system System_CT {
  //-----------------------------------------------------------------------------

    map(self:MQTT_PCO, system:IPL4_PCO); 
  f_preamble();

  var default v_def := activate(as_default())

    MQTT_PCO.send("Breathe, breathe in the air") 


timer t_wait:=1.0
t_wait.start; 

alt {
 [] MQTT_PCO.receive("Success") {  log("Connection closed");  v_TCPState:=Disconnected; } 
[] t_wait.timeout  {   log("Timeout");  setverdict(inconc) }

}

if(v_TCPState!=Disconnected)
{
  f_postamble();
}

setverdict(pass);
  unmap(self:MQTT_PCO, system:IPL4_PCO); 


}



(compare it with the stateless example )


But of course complexity will not disappear , it will only be pushed to the translation:
the previously unused f_enc_charstring_Translation will replicate the state machine:


//---------------------------------------------------------------------
function  f_enc_charstring_Translation
//---------------------------------------------------------------------
( in charstring   pl_in,
  out ASP_Send pl_out) 
port MQTT_IPL4_PT
{

if(tsp_stateful)
{
//save  message
	v_msg:=pl_in;
        timer t:=10.0      
        port.send(t_connect); 
         t.start; 

        alt {

        []port.receive(t_connack) {  v_state:=Connected;}

        []t.timeout;
  
         }   

    port.send(t_subscribe) 
    port.send(t_publish(v_msg)) 

       t.start; 


        alt {

        []port.receive(t_suback) {v_state:=Wait_publish_echo;}
        []t.timeout;
  
         }   
      
        t.start; 
        alt {

        []port.receive(t_publish_r) {}
        []t.timeout;
  
         }   
    port.send(t_unsubscribe);
        t.start; 

        alt {

        []port.receive(t_unsuback) {v_state:=Disconnected  }
        []t.timeout;
  
         }   
  port.send(t_disconnect) 
   
port.setstate(4);
}
else {

 port.setstate(4);  //discard

}

} with {extension "prototype(fast)" }


OK , so this may require some clarifications.


It's surprisingly easy to forget here that "port" in the translation functions refer to the "upper" part, in our case a port of type MQTT_IPL4_PT and never to the "lower" port type, that is
IPl4asp_PT; so one can send and receive only message types that are present on the "out" and "in" list of the port; this means that we cannot send/receive ASP_Send, ASP_Receive
directly.


type port MQTT_IPL4_PT message map to IPL4asp_PT //Translation port
{
  out  
  charstring             to   ASP_Send   with f_enc_charstring_Translation(),
  MQTT_v3_1_1_Message    to   ASP_Send   with f_enc_MQTT_Translation(),
  integer                to   ASP_Send   with f_set_connId()    
  in      
  MQTT_v3_1_1_Message    from ASP_RecvFrom 	with f_dec_MQTT_Translation(),
  charstring             from ASP_Event 	with f_dec_charstring_Translation(),
  ASP_Event  		 	


  var  integer v_connId      
  var  charstring v_msg 
  var  MQTTClientState v_state:=Idle ;
  var  ASP_Send v_ASP_Send  ;  

} with {extension "internal"}





So what happens when we send an MQTT message from within f_enc_charstring_Translation:
 :
      port.send(t_connect); 
 :
 

is that this MQTT message will be rerouted to the port type declaration for analysis and will try to find an appropriate entry, which in our case will be
f_enc_MQTT_Translation , so it ends up translated into ASP_Send and will be sent to the IPL4 port.
Similarly, if we are waiting from within f_enc_charstring_Translation to receive a a message, e.g.
 :
    t.start; 
        alt {

        []port.receive(t_publish_r) {}
        []t.timeout;
  
         }   
 :
 

then this first will go through f_dec_MQTT_Translation , be translated from ASP_RecvFrom into and MQTT message, which will then be matched
in the port.


Another extension to the standard is a new value 4 that was introduced for port.setstate to signify discarding of messages.



So here's the console log:

	Executing all items of [EXECUTE] section.
MC2> MTC@esekilxxen1841: connect result{ errorCode := omit, connId := 1, os_error_code := omit, os_error_text := omit }
MTC@esekilxxen1841: v_state:  Idle (0)
MTC@esekilxxen1841: --------->  connect sent
MTC@esekilxxen1841: v_state:  Wait_connack (1)
MTC@esekilxxen1841: <---------  connack received
MTC@esekilxxen1841: v_state:  Connected (2)
MTC@esekilxxen1841: --------->  subscribe sent
MTC@esekilxxen1841: v_state:  Wait_suback (3)
MTC@esekilxxen1841: --------->  publish sent
MTC@esekilxxen1841: v_state:  Wait_suback (3)
MTC@esekilxxen1841: <---------  suback received
MTC@esekilxxen1841: v_state:  Wait_publish_echo (5)
MTC@esekilxxen1841: <---------  publish echo  received
MTC@esekilxxen1841: v_state:  Wait_publish_echo (5)
MTC@esekilxxen1841: --------->  unsubscribe sent
MTC@esekilxxen1841: v_state:  Wait_unsuback (4)
MTC@esekilxxen1841: <---------  unsuback received
MTC@esekilxxen1841: v_state:  Disconnected (7)
MTC@esekilxxen1841: --------->  disconnect sent
MTC@esekilxxen1841: Connection closed
MC@esekilxxen1841: Test execution finished.
Execution of [EXECUTE] section finished.



Now as you may notice this is a very powerful feature: we managed to decrease the number of components and also the apparent complexity of the code ;
however the feature needs some getting used to and also it's possible to create infinite loops by rerouting the messages in an unfortunate manner.



Detailed logs and code attached as usual.


Best regards
Elemer

Previous Topic:Long Compilation time- OPC-UA
Next Topic:oneM2MTester version 2.0.0 released
Goto Forum:
  


Current Time: Wed Nov 14 19:39:26 GMT 2018

Powered by FUDForum. Page generated in 0.02004 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software

Back to the top