We have discussed at length the reasons to use dual-faced ports, for example here:
Dual-faced test ports: architectural considerations
https://www.eclipse.org/forums/index.php/t/1078877/
Dual faced ports are an early implementation of translation ports as described in
The Testing and Test Control Notation version 3;
TTCN-3 Language Extensions:
Configuration and Deployment Support
5.2 Ports with translation capability
Translation ports (or "ports with translation capability" but I will simply call them translation ports as both your and my time is valuable)
have the same use cases, and the standard describes a sleeker, easier to understand, more feature-rich concept than what has been implemented for dual-faced ports.
So we have decided to implement the standard translation ports, at least partially.
Mind you, the dual-faced ports will continue to be supported and can be used in parallel or as an alternative with translation ports.
Support is present starting from Titan 6.2.0.
First let's see the limitations of the implementation:
type port PortTypeIdmessage
[map to{OuterPortType[","]}+ ]
[connect to {OuterPortType[","]}+]"{"
{
(in{InnerInType [from {OuterInType with InFunction"("")"[","]}+][","]}+|
out{InnerOutType[to {OuterOutType with OutFunction"("")"[","]}+ ][","]}+ |
inout{InOutType[","]}+ |
address AddrType [to {OuterAddrTypewith AddrOutFunction"("")"[","]}+ ]
[from { OuterAddrTypewith AddrInFunction"("")"[","]}+ ] |
map param "("{FormalValuePar [","] }+ ")"|
unmap param "("{ FormalValuePar [","] }+ ")" |
VarInstance) ";"
}+
"}"
The underlined parts are not implemented.
For a detailed description of limitations see ref.guide 4.35 Ports with translation capability.
A new element is the presence of port variables/constants in the port declaration. These variables can be used in and their scope is limited to functions having a port clause that connects them to the port where variables have been declared. Some examples of uses of port variables:
module PortVariables {
type record of integer RoI;
type port VP1 message {
out integer, charstring, RoI;
in integer, charstring, RoI;
} with {
extension "provider"
}
type port NVP1 message map to VP1 {
out charstring to integer with char_to_default_int_template() : charstring with char_to_default_char() : charstring with char_to_default_const_char() : RoI with char_to_default_roi() : integer with char_to_int();
in integer, charstring, RoI;
//port declaration contains port variable declarations:
const charstring c_cs := "DefaultConst"
var charstring cs := "Default";
var template integer i := 3;
var template RoI roi := {1,2,3};
var integer num;
}
function char_to_default_int_template(in charstring input, out integer output) port NVP1 {
if (input == "int template") {
output := valueof(i);
port.setstate(0);
i := 666;
} else {
port.setstate(1);
}
} with {
extension "prototype(fast)";
}
:
:
}
As a complete usage example I will re-use some dual-face port examples published earlier that have been adapted to a translation port.
Let's start with:
Example of SNMP over UDP(Example of SNMP over UDP with ASN.1 and dual-faced test port)
https://www.eclipse.org/forums/index.php/t/1068184/
The main component sends an SNMP message to the translation(earlier dual-faced ) port
which translates it to a UDP message and sends it to the single parallel component which does nothing else but echoes it back.
The translation port receives the echo, decodes it and decides whether it is the expected structure or not.
Here's the declaration of the translation port:
type port SNMP_UDP_PT message map to UDPasp_PT //Translation port
{
out
SNMPv1_Message to ASP_UDP_message with f_enc_SNMPv1_TranslationPort(),
SNMPv2_Message to ASP_UDP_message with f_enc_SNMPv2_TranslationPort(),
SNMPv3_Message to ASP_UDP_message with f_enc_SNMPv3_TranslationPort(),
ASP_UDP_open,
ASP_UDP_close
in
SNMPv1_Message from ASP_UDP_message with f_dec_SNMPv1_TranslationPort(),
SNMPv2_Message from ASP_UDP_message with f_dec_SNMPv2_TranslationPort(),
SNMPv3_Message from ASP_UDP_message with f_dec_SNMPv3_TranslationPort(),
ASP_UDP_open_result
inout ASP_UDP
}
and here's the earlier declaration of the dual-faced port with the same functionality:
type port SNMP_UDP_PT message //DualFace port
{
out
SNMPv1_Message,
SNMPv2_Message,
SNMPv3_Message,
ASP_UDP_open,
ASP_UDP_close
in
SNMPv1_Message,
SNMPv2_Message,
SNMPv3_Message,
ASP_UDP_open_result
}with
{ extension
"user UDPasp_PT
out(
SNMPv1_Message -> ASP_UDP_message: function(f_enc_SNMPv1_DualFace);
SNMPv2_Message -> ASP_UDP_message: function(f_enc_SNMPv2_DualFace);
SNMPv3_Message -> ASP_UDP_message: function(f_enc_SNMPv3_DualFace);
ASP_UDP_open -> ASP_UDP_open : simple;
ASP_UDP_close -> ASP_UDP_close : simple
)
in(
ASP_UDP_message -> SNMPv1_Message : function(f_dec_SNMPv1_DualFace),
SNMPv2_Message : function(f_dec_SNMPv2_DualFace),
SNMPv3_Message : function(f_dec_SNMPv3_DualFace);
ASP_UDP -> - : discard;
ASP_UDP_open_result -> ASP_UDP_open_result : simple
)
"
}
and here you have one of the port functions after and before:
function f_enc_SNMPv1_TranslationPort
( in SNMPv1_Message pl_in,
out ASP_UDP_message pl_out)
{
pl_out.id := 0;
pl_out.data := enc_SNMPv1_Message(pl_in);
pl_out.remote_addr := tsp_connectionParams.remHost;
pl_out.remote_port := tsp_connectionParams.remPort;
port.setstate(0);
}with {extension "prototype(fast)" }
function f_enc_SNMPv1_DualFace
( in SNMPv1_Message pl_in,
out ASP_UDP_message pl_out) return integer
{
pl_out.id := 0;
pl_out.data := enc_SNMPv1_Message(pl_in);
pl_out.remote_addr := tsp_connectionParams.remHost;
pl_out.remote_port := tsp_connectionParams.remPort;
return 0;
}with {extension "prototype(backtrack)" }
The code and logs are attached for your perusing pleasure.
To use the attached code, please extract it to a directory named e.g SNMP, followed by :
cd SNMP/bin
../install.script
make
ttcn3_start ./SNMP SNMP.cfg
A difference compared with the dual faced test ports is that the ports with translation capability can work in two modes, in normal mode and in translation mode. In normal mode, the port behaves as a standard messaging port, while in translation mode it works as it is defined in the ES 202 781 standard clause 5.2 ([21]).
A test port skeleton must be generated for the port type with the translation capability, to ensure that the port can work in both modes.
When used in standard mode, the skeleton has to be replaced by a real test port.
This is the part of the standard that exemplifies dual usage:
typeport TransportPort {
...
}
type portDataPort map to TransportPort {
...
}
typecomponent SystemComponent{
portDataPort dataPort;
portTransportPort transportPort;
}
type component TestComponent{
port DataPort dataPort;
}
testcase TC runson TestComponent system SystemComponent
{
if (PX_TRANSPORT_USED){
// activate translation mode (TransportPort is implicitly referenced via transportPort
// in the map operation)
map(mtc:dataPort, system:transportPort);
}
else{
// activate normal mode (TransportPort is not referenced in the map operation)
map(mtc:dataPort, system:dataPort);
}
}
The port skeleton files are:
SNMP_UDP_PT.cc
SNMP_UDP_PT.hh
They are contained in the package and have been generated with
issued in bin, after creating symlinks with install.script
and they have to be made part of the compilation.
( added to install.script, Makefile etc.)
Best regards
Elemer
]]>