OpenTTCN/Training/2008-02-01
From OpenTTCN
Home | Developer's corner | Knowledge base | Working documents | Documentation | OpenTTCN IDE | Tutorials | Training | How do I | Frequently asked questions | Technical support |
Training blackboard, February 5-6, 2008
Training blackboard
How to start openttcnd
ot start (command-line)
C:\Program Files\OpenTTCN\SDK\include\isl\TTCN3.h:
int otStartSystemServices(const char* installPath,
const char* port, int nowait,
otStatusIndicationListener listener);
Create session
cmd-line:
session create MySession
API:
C:\Program Files\OpenTTCN\SDK\include\isl\TTCN3.h:
int otCreateSession(const char* sessionName);
Communication: adapter - test component
- CORBA-based (TRI-PA, TRI-SA, TCI-CD, TCI-TM) (SDK for C, SDK for Java, SDK for C#)
- tri.h
- TRI-UDP (TRI-PA, TRI-SA) (you need to have CORBA-based TCI-CD)
- tri_udp.h
APIs
- C:\Program Files\OpenTTCN\SDK\include\tci - C
- C:\Program Files\OpenTTCN\SDK\include\tri - C
- C:\Program Files\OpenTTCN\SDK\include\isl - C++
Timer example
C:\Program Files\OpenTTCN\SDK\examples\timer
TCI-TM: Logging
Callback (tci/tci.h):
void tciLog(String message);
triSend()
C:\Program Files\OpenTTCN\SDK\include\tri\tri.h:
TriStatus triSend (const TriComponentId* componentId, const TriPortId* tsiPortId, const TriAddress* sutAddress, const TriMessage* sendMessage);
typedef BinaryString TriAddress; typedef BinaryString TriMessage;
tciEncode()
Enabling adapter debug output
tri/tri_ext.h
otSetSAFlags(OT_SA_FLAG_VERBOSE);
Other ways to debug the adapter
tester run:
--log-encoded-as-text, -t log encoded messages in text format
--log-encoded-as-hex, -h log encoded messages in hex format
TCI-TM:
tci/tci_ext.h:
#define OT_TM_LOG_ENCODED_AS_HEX 0x00000020L #define OT_TM_LOG_ENQUEUED_EVENTS 0x00000040L ... int otSetTMFlags(unsigned long flags);
Other ways to debug the test suite
tester run:
--log-src-line-numbers, -l enable logging of source file line numbers --log-src-file-name, -f enable logging of source file name --print-stack-trace (added recently) --log-enqueued-events, -e log events that arrived to the port queue --log-mismatch-events, -m log mismatching communication statements --smart-mismatch, -M log mismatch that preceded non-pass verdict
More info: tester help more
Also in TCI-TM.
odb (debugger)
Validation of decoder result
tci/tci_ext.h:
#define OT_CD_FLAG_VALIDATE_DECODED_VALUE 0x00000004L
Enabled by default.
To disable (for performance reasons): int otResetCDFlags(unsigned long flags);
triEnqueueMsg
tri/tri.h
void triEnqueueMsg (const TriPortId* tsiPortId, const TriAddress* sutAddress, const TriComponentId* componentId, const TriMessage* receivedMessage);
tciDecode
Procedure-based communication
void triEnqueueCall()
void triEnqueueReply()
void triEnqueueException()
Multicast, broadcast
(Rarely used currently.)
- triSendMC() - multicast
- triSendBC() - broadcast
- same for call, reply, exception
Session
session create dsrc importer2 load dsrc MySuite.mp
isl/TTCN3.h:
otCreateSession(); otDeleteSession(); otListSessions();
otInitAdapter
otRegisterAdapter
tri/tri_ext.h:
- in case there is a conflict related to multiple registrations, a best match rule is used, and the best matching adapter is selected to serve a particular request
Persistent, non-persistent data
persistent:
- precompiled test suites
- module parameters (TTCN-2: test suite parameters)
- sessions created
non-persistent data:
- adapter registrations
Stoppping an adapter
session stop
isl/TTCN3.h:
otStopAdapter()
Stopping openttcnd
ot stop
isl/TTCN3.h:
otStopSystemServices();
Developing example adapter
- C:\courses\wsp5 (developed in this folder)
- you can pick your own
- MS VC++ 2008 Express (free MS compiler)
- gcc (2.56.0 supports 3.3 (this was also tried with 3.2 and it worked), 4.0)
Creating a project in VS++ 2008
- Win32 console application
- C:\courses\wsp5
- Name: simple
- no precompiled header
- after project is created, remove unnecessary stuff like stfafx.h etc.
- remove both from project and file system
- create empty main.cxx and add it to the project
- put the following to main.cxx:
/* for Sleep() call */
#include <windows.h>
#include <tri/tri.h>
#include <isl/TTCN3.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int status = otInitAdapter(0);
if (status)
{
printf("main(): otInitAdapter() failed.\n");
exit(status);
}
/* Register the adapter to OpenTTCN server */
status = otRegisterAdapter("simple", "TRI");
if (status)
{
printf("main(): otRegisterAdapter() failed.\n");
exit(status);
}
/* Enable printout of library diagnostic information */
otSetSAFlags(OT_SA_FLAG_VERBOSE);
printf("Init OK!\n");
/* Enter the infinite loop */
while (1) Sleep(100000);
/* Avoid compiler complaints */
return 0;
}
Group of steps 2 (configuring project settings)
Build|Configuration Manager|Active solution configuration: change Debug -> Release
- assumption that OpenTTCN SDK is installed to:
C:\Program Files\OpenTTCN\SDK
If you have another installation location, then project settings are different
Right-click on simple, then Properties, then:
- Configuration properties|C/C++|General|Additional include directories:
- Add C:\Program Files\OpenTTCN\SDK\include
- Configuration properties|C/C++|Code generation|Runtime library:
- Change /MD (default) to /MT (SDK libraries use this runtime library)
- Configuration properties|Linker|General|Additional library directories:
- Add C:\Program Files\OpenTTCN\SDK\lib
- Configuration properties|Linker|Input|Additional dependencies:
- Add omniDynamic4.lib omniORB4.lib omnithread.lib pthreads-2.7.0-mt.lib openttcn-tcd-mt.lib openttcn-tsa-mt.lib openttcn-isl-mt.lib openttcn-msg-mt.lib ws2_32.lib
Group of steps 3 (adding default content to SA_impl.cxx, PA_impl.cxx)
- Add two more empty source files to the project: PA_impl.cxx and SA_impl.cxx
Content of SA_impl.cxx:
#include <tri/tri.h>
/***************************************************************************
* Implementation of TRI interface (SA, user-provided part).
*/
TriStatus triSend
(const TriComponentId* componentId,
const TriPortId* tsiPortId,
const TriAddress* sutAddress,
const TriMessage* sendMessage)
{
return TRI_ERROR;
}
TriStatus triSAReset()
{
return TRI_OK;
}
TriStatus triExecuteTestCase
(const TriTestCaseId* testCaseId,
const TriPortIdList* tsiPortList)
{
return TRI_OK;
}
TriStatus triMap
(const TriPortId* compPortId,
const TriPortId* tsiPortId)
{
return TRI_OK;
}
TriStatus triUnmap
(const TriPortId* compPortId,
const TriPortId* tsiPortId)
{
return TRI_OK;
}
TriStatus triCall
(const TriComponentId* componentId,
const TriPortId* tsiPortId,
const TriAddress* sutAddress,
const TriSignatureId* signatureId,
const TriParameterList* parameterList)
{
return TRI_ERROR;
}
TriStatus triReply
(const TriComponentId* componentId,
const TriPortId* tsiPortId,
const TriAddress* sutAddress,
const TriSignatureId* signatureId,
const TriParameterList* parameterList,
const TriParameter* returnValue)
{
return TRI_ERROR;
}
TriStatus triRaise
(const TriComponentId* componentId,
const TriPortId* tsiPortId,
const TriAddress* sutAddress,
const TriSignatureId* signatureId,
const TriException* exception)
{
return TRI_ERROR;
}
TriStatus triSUTActionInformal
(const char* description)
{
return TRI_ERROR;
}
TriStatus triSUTActionTemplate
(const TriActionTemplate* templateValue)
{
return TRI_ERROR;
}
Content of PA_impl.cxx:
#include <tri/tri.h>
/***************************************************************************
* Implementation of TRI interface (PA, user-provided part).
*/
TriStatus triPAReset()
{
return TRI_OK;
}
TriStatus triExternalFunction
(const TriFunctionId* functionId, /* in parameter */
TriParameterList* parameterList, /* inout parameter */
TriParameter* returnValue /* out parameter */)
{
return TRI_ERROR;
}
TriStatus triStartTimer
(const TriTimerId* timerId,
TriTimerDuration timerDuration)
{
return TRI_ERROR;
}
TriStatus triStopTimer
(const TriTimerId* timerId)
{
return TRI_ERROR;
}
TriStatus triReadTimer
(const TriTimerId* timerId,
TriTimerDuration* elapsedTime)
{
return TRI_ERROR;
}
TriStatus triTimerRunning
(const TriTimerId* timerId,
unsigned char* running)
{
return TRI_ERROR;
}
Group of steps 4 (ot start, session create)
Start openttcnd daemon:
ot start
Create a session named "simple":
session create simple
Group of steps 5 (adding CD)
Add CD_impl.cxx to the project with the following content:
#include <tci/tci.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <string.h>
/***************************************************************************
* Implementation of TCI interface (CD, user-provided encoding part).
*/
BinaryString tciEncode(TciValue value)
{
BinaryString result;
memset(&result, 0, sizeof(result));
return result;
}
/***************************************************************************
* Implementation of TCI interface (CD, user-provided decoding part).
*/
TciValue tciDecode(BinaryString message, TciType decHypothesis)
{
TciValue result = 0;
return result;
}
Add the following before adapter registration (main.cxx):
otSetEncoder(0, &tciEncode); otSetDecoder(0, &tciDecode);
Add the following include to main.cxx:
#include <tci/tci.h>
Group 6 (background info)
- SUT protocol definition:
Group 7 (example test case)
Added C:\courses\wsp5\simple\ttcn3\Main.ttcn file with the following content:
module Main
{
/*******************************************************************
* Message type definitions
*/
type integer uint8(0..255);
type integer uint16(0..65535);
type record Message
{
uint8 typeCode,
Payload payload optional
}
type record Payload
{
uint16 len,
charstring content
}
type octetstring Raw;
/*******************************************************************
* Other definitions
*/
// Address type used for UDP packets.
type record address
{
charstring host,
integer portField
}
modulepar
{
// IP address and UDP port number of the SUT.
charstring PX_SUT_IP_ADDR := "127.0.0.1";
integer PX_SUT_PORT := 7431;
}
type port PortType mixed
{
inout all;
}
type component ComponentType
{
port PortType p;
timer T_GUARD := 10.0;
var address sut_addr :=
{
host := PX_SUT_IP_ADDR,
portField := PX_SUT_PORT
};
}
type component SystemInterfaceType
{
port PortType tsiPort;
}
template Message GreetingRequest
(in template charstring phrase) :=
{
typeCode := 1,
payload :=
{
len := lengthof(phrase),
content := phrase
}
}
template Message GreetingResponse
(in template charstring phrase)
modifies GreetingRequest :=
{
typeCode := 2
}
altstep DefaultAltstep() runs on ComponentType
{
[] any port.check
{
setverdict(fail);
stop;
}
[] T_GUARD.timeout
{
setverdict(fail);
stop;
}
}
testcase TC_R2D2_001()
runs on ComponentType
system SystemInterfaceType
{
activate(DefaultAltstep());
T_GUARD.start;
map(mtc:p, system:tsiPort);
p.send(GreetingRequest("Hello, world!")) to sut_addr;
p.receive(GreetingResponse("Hello, mankind!")) from sut_addr;
p.send(GreetingRequest("Hello, gentlemen!")) to sut_addr;
p.receive(GreetingResponse("Reformulate")) from sut_addr;
setverdict(pass);
}
}
Group 8 (compile test suite)
importer3 load simple Main.ttcn
Group 9 (run test case)
- start the adapter (Ctrl+F5 in VC++)
- tester run simple TC_R2D2_001
Group 10 (adding encoder)
Add the following to CD_impl.cxx (in place of current tciEncode()):
/* for otReportError() */
#include <isl/TTCN3.h>
#define MAX_BUFFER_SIZE 4096
BinaryString tciEncode(TciValue value)
{
int len = 0;
BinaryString result;
TciType valType = tciGetType(value);
String typeName = tciGetName(valType);
TciTypeClassType typeClass = tciGetTypeClass(valType);
memset(&result, 0, sizeof(result));
if (typeClass == TCI_ADDRESS_TYPE)
{
len = sizeof(TciValue);
result.data = (unsigned char *) malloc(len);
memcpy(result.data, &value, len);
result.bits = len << 3;
}
else if (!strcmp(typeName, "Message"))
{
long typeCode = 0, length = 0;
unsigned char* buffer = 0;
TciValue payload = 0;
char* str = 0;
long str_len = 0;
buffer = (unsigned char *) malloc(MAX_BUFFER_SIZE);
memset(buffer, 0, MAX_BUFFER_SIZE);
len = 0;
typeCode = tciValueToLong(tciGetRecFieldValue(value, "typeCode"));
buffer[len++] = (unsigned char) typeCode;
payload = tciGetRecFieldValue(value, "payload");
/* 'payload' field is optional, so it may be omitted */
if (!tciNotPresent(payload))
{
long length = tciValueToLong(tciGetRecFieldValue(payload, "len"));
/* most significant byte first */
buffer[len++] = (unsigned char) (length >> 8);
buffer[len++] = (unsigned char) (length >> 0);
tciValueToCharstring(&str, &str_len,
tciGetRecFieldValue(payload, "content"));
memcpy(buffer + len, str, str_len);
len += str_len;
free(str);
}
result.data = buffer;
result.bits = len << 3;
}
else
{
otReportError("tciEncode(): Unrecognized value type.");
}
return result;
}
To use this code, you will need to #include "Utilities.h" to CD_impl.c and add files Utilities.h and Utilities.c to your Visual Studio project.
You will need to copy the code of Utilities.h and Utilities.c files referred to in the links of the previous paragraph into your Visual C++ project. Click on the links of the previous paragraph to see the content of both files. Note that these are not the same as in the examples\util directory shipped with the C SDK.
Group 11 (adding triSend handler)
Replace triSend() implementation in SA_impl.cxx with the following fragment:
#include "Utilities.h"
TriStatus triSend
(const TriComponentId* componentId,
const TriPortId* tsiPortId,
const TriAddress* sutAddress,
const TriMessage* sendMessage)
{
TciValue addrValue = *((TciValue *) sutAddress->data);
unsigned long ipAddr = 0;
unsigned short portNumber = 0;
int result =
extractHostAndPortFromAddress(
&ipAddr, &portNumber, addrValue);
if (result) return TRI_ERROR;
result = sendDatagramPacket(
ipAddr,
portNumber,
(char*) sendMessage->data,
sendMessage->bits >> 3);
return (!result) ? TRI_OK : TRI_ERROR;
}
Theory track (continued)
TTCN-2 execution to TTCN-3 adaptation mapping
- triMap(), triUnmap() are in fact called automatically
Signatures
Example of signature definition for procedure-based communication:
signature Signature(
in integer par1,
out bitstring par2,
MyRecordType par3,
inout OS_0_256 par4)
return integer
exception(
BS_0_1024,
RecordType);
Example from TTCN-2 tabular ASP:
signature ASP_Type1(
in integer par1,
in bitstring par2,
in MyRecordType par3,
in OS_0_256 par4);
template ASP_Type1 t1 :=
{
par1 := 1,
par2 := '1101'B,
par3 := { f1 := 1, f2 := 2 },
par4 := 'AF'O
}
...
p.call(t1);
triCall
TriStatus triCall (const TriComponentId* componentId, const TriPortId* tsiPortId, const TriAddress* sutAddress, const TriSignatureId* signatureId, const TriParameterList* parameterList);
triEnqueueReply
void triEnqueueReply (const TriPortId* tsiPortId, const TriAddress* sutAddress, const TriComponentId* componentId, const TriSignatureId* signatureId, const TriParameterList* parameterList, const TriParameter* returnValue);
triEnqueueException
void triEnqueueException (const TriPortId* tsiPortId, const TriAddress* sutAddress, const TriComponentId* componentId, const TriSignatureId* signatureId, const TriException* exception);
Decoding hypothesis for messages
- none by default
- see comment for OT_CD_FLAG_MESSAGES_WITH_HYPOTHESIS flag in tci\tci_ext.h for more details
Decoding hypothesis for exceptions
- none by default
- see comment for OT_CD_FLAG_EXCEPTIONS_WITH_HYPOTHESIS flag in tci\tci_ext.h for more details
Preparing TriParameterList for triEnqueueReply
- consult how many parameters the relevant signature definition in TTCN-3 has
- e.g. four (see Signature definition above)
TriParameterList pl; pl.parList = (TriParameter **) malloc(sizeof(TriParameter*) * 4); pl.length = 4; TriParameter** parList = pl.parList; parList[0] = (TriParameter*) malloc(sizeof(TriParameter)); memset(&(parList[0]->par), 0, sizeof(BinaryString)); parList[0]->mode = TRI_IN; parList[1] = (TriParameter*) malloc(sizeof(TriParameter)); memset(&(parList[1]->par), 0, sizeof(BinaryString)); /* Add code to set the value of parList[1]->par which is a BinaryString and which should contain encoding of the parameter in binary form */ parList[1]->mode = TRI_OUT; /* Do similar thing for the rest of IN parameters as we did for parList[0]. Do similar thing for the rest of INOUT, OUT parameters as we did for parList[1]. */
Preparing TriParameterList for triEnqueueCall
- consult how many parameters the relevant signature definition in TTCN-3 has
- e.g. four (see Signature definition above)
TriParameterList pl; pl.parList = (TriParameter **) malloc(sizeof(TriParameter*) * 4); pl.length = 4; TriParameter** parList = pl.parList; parList[0] = (TriParameter*) malloc(sizeof(TriParameter)); memset(&(parList[0]->par), 0, sizeof(BinaryString)); /* Add code to set the value of parList[0]->par which is a BinaryString and which should contain encoding of the parameter in binary form */ parList[0]->mode = TRI_IN; parList[1] = (TriParameter*) malloc(sizeof(TriParameter)); memset(&(parList[1]->par), 0, sizeof(BinaryString)); parList[1]->mode = TRI_OUT; /* Do similar thing for the rest of IN, INOUT parameters as we did for parList[0]. Do similar thing for the rest of OUT parameters as we did for parList[1]. */
triExecuteTestCase
TriStatus triExecuteTestCase (const TriTestCaseId* testCaseId, const TriPortIdList* tsiPortList);
triExternalFunction
TriStatus triExternalFunction (const TriFunctionId* functionId, TriParameterList* parameterList, /* inout parameter */ TriParameter* returnValue); /* out parameter */
In TTCN-3:
external function F1(
in integer p1,
inout RecType p2,
out boolean p3)
return charstring;
...
var RecType v1;
var boolean v2;
var charstring v3 := F1(2 + 5, v1, v2);
// After call to F1(), v1 may and v2 should contain value assigned by the adapter.
Typical structure of tciEncode()
BinaryString tciEncode(TciValue value)
{
TciType valType = tciGetType(value);
String typeName = tciGetName(valType);
BinaryString result;
memset(&result, 0, sizeof(BinaryString));
if (!strcmp(typeName, "T1"))
{
// Encoding of T1 value
}
else if (!strcmp(typeName, "T2"))
{
// Encoding of T2 value
}
...
else if (!strcmp(typeName, "T30"))
{
// Encoding of T30 value
}
else
{
otReportError("tciEncode(): Cannot recognize typeName");
}
return result;
}
otReportError() is defined in isl/TTCN3.h (see comments there for more details):
int otReportError(const char* errorReason);
otSetEncoder - Setting specific encoder (for performance reasons)
BinaryString myTciEncode(TciValue value)
{
...
}
BinaryString myTciEncode2(TciValue value)
{
...
}
BinaryString myTciEncode3(TciValue value)
{
...
}
int main()
{
...
otSetEncoder(0, &myTciEncode);
...
}
// Problem: tciGetTypeForName() calls
void F()
{
otSetEncoder(tciGetTypeForName("T1"), &myTciEncode2);
otSetEncoder(tciGetTypeForName("MyModule.T2"), &myTciEncode3);
}
- create MTC (TCI-CD request server) (tci/tci_ext.h):
// Creates MTC service TCI-CD query request as a side effect int otTMSelectSession(const char* sessionName);
- or put F() into triExecuteTestcase()
- or check documentation for otRegisterSetCodecFunction() in tci/tci_ext.h
otSetDecoder - Setting specific decoder (for performance reasons)
SUT action (implicit send in TTCN-2)
In TTCN-3:
action("Please dial the number +1234567");
Callback in TRI:
TriStatus triSUTActionInformal (const char* description);
In TTCN-3:
action(myTmplRef);
Callback in TRI:
TriStatus triSUTActionTemplate (const TriActionTemplate* templateValue);
Memory management for return value
- discussed in the preamble for tci/tci.h
Encode and variant attributes
Example of typedef with encode and variant attributes:
type RecType record
{
integer f1,
integer f2
}
with
{
encode "ENC:123",
variant "VAR:123
A:1,
B:2,
bits=3"
}
Retrieval of encode, variant attributes for typedefs:
In tci/tci.h:
/* OpenTTCN Specific: MM4RV is as defined in [RM1]. */ String tciGetTypeEncoding(TciType inst); /* OpenTTCN Specific: MM4RV is as defined in [RM1]. */ String tciGetTypeEncodingVariant(TciType inst);
Example of value with encode and variant attributes:
template RecType t1 := { f1 := 1, f2 := 2}
// with { encode "ABC", variant "DEF" }
...
p.send(t1);
-> tciEncode(t1); (invocation inside the SDK for C code)
Retrieval of encode, variant attributes for values:
In tci/tci.h:
String tciGetValueEncoding(TciValue inst); String tciGetValueEncodingVariant(TciValue inst);
Encode and variant attributes: example of use
TTCN-3 code:
type integer INT_16
with { variant "type=integer;length-in-bits=16;octet-order=msb-first" }
type integer INT_32
with { variant "type=integer;length-in-bits=32;octet-order=msb-first" }
type record Message
{
INT_16 f1,
INT_32 f2
}
with { variant "type=record-container" }
Conventional approach to tciEncode():
BinaryString tciEncode(TciValue value)
{
TciType valType = tciGetType(value);
String typeName = tciGetName(valType);
BinaryString result;
memset(&result, 0, sizeof(BinaryString));
if (!strcmp(typeName, "Message"))
{
// Encode field f1 using hard-coded knowledge that it is 16 bit
// Encode field f2 using hard-coded knowledge that it is 32 bit
}
else
{
otReportError("tciEncode(): Cannot recognize typeName");
}
return result;
}
Approach to tciEncode() using variant info (generic encoder):
BinaryString tciEncode(TciValue value)
{
TciType valType = tciGetType(value);
String variant = tciGetTypeEncodingVariant(TciType inst);
map<string, string> variantItems = parseVariant(variant);
BinaryString result;
memset(&result, 0, sizeof(BinaryString));
if (variantItems["type"] == "record-container")
{
// Call tciEncode() recursively for all fields of the record
// container and concatenate result of encoding into single
// result buffer.
...
}
else if (variantItems["type"] == "integer")
{
int lenInBits = stringToLong(variantItems["length-in-bits"]);
string octetOrder = variantItems["octet-order"];
if (octetOrder == "msb-first")
{
// Encode integer value into number of bits specified in lenInBits,
// most significant byte first
...
}
else if (octetOrder == "lsb-first")
{
// Encode integer value into number of bits specified in lenInBits,
// least significant byte first
...
}
}
else
{
otReportError("tciEncode(): Cannot recognize variant type");
}
return result;
}
Developing example adapter (continued)
SUT binary and source code
- Binary: on flash (SystemUnderTest.exe)
- Binary and source: here
- how to start the adapter: from the command line: start SystemUnderTest.exe 7431
- recovering from the previous day (starting openttcnd daemon):
ot start
- restarting the adapter:
- start VC++
- open "simple" solution from yesterday
- Ctrl+F5 - start the adapter
- run the test case:
tester run simple @all
(@all is used for fast execution of a test case if it is the only one in the test suite; otherwise it runs all test cases defined in the test suite)
- SUT window should show that it received a request and has replied with a valid response
Adding socket listener thread
Add SocketListener.cxx to the project with the following content:
#include "Utilities.h"
#include <tri/tri.h>
#include <tci/tci.h>
#include <isl/TTCN3.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <assert.h>
#include <pthread.h>
#ifndef _WIN32
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#else
#include <winsock2.h>
typedef int socklen_t;
#endif
#define MAX_BUFFER_SIZE 65536
/* UDP port listener thread implementation */
void *runSocketListener(void *p_)
{
int rval;
unsigned char buff[MAX_BUFFER_SIZE];
int len;
int errorCode = 0;
struct sockaddr_in src_addr;
socklen_t addrlen = sizeof src_addr;
char* host = 0;
unsigned short port = 0;
TciValue sutAddrHandle = 0;
TciValue hostField = 0;
TciValue portField = 0;
TriAddress sutAddr;
TriMessage recvMsg;
TriPortId tsiPort;
memset(&tsiPort, 0, sizeof(TriPortId));
if (!_socketDescInitialized)
{
rval = bindSocketToAnyPort(&_socketDesc);
if (rval)
{
otReportError("runSocketListener(): Cannot bind socket.");
return 0;
}
_socketDescInitialized = 1;
}
/* Loop forever */
while (1)
{
/* Receive a packet from the SUT */
memset(&src_addr, 0, sizeof src_addr);
rval = recvfrom(
_socketDesc,
#ifndef _WIN32
buff,
#else
(char *) buff,
#endif
sizeof buff,
0,
(struct sockaddr *) &src_addr,
&addrlen);
/* We will proceed further only if the packet has been received OK */
if (rval == -1)
{
#ifndef _WIN32
printf("runSocketListener(): "
"recvfrom(): Error: %s\n", strerror(errno));
#else
errorCode = WSAGetLastError();
printf("runSocketListener(): "
"recvfrom(): Error: error code = %i\n", errorCode);
#endif
continue;
}
/* Put SUT address information directly into TciValue */
host = inet_ntoa(src_addr.sin_addr);
hostField = charstringToTciValue(host);
port = ntohs(src_addr.sin_port);
portField = longToTciValue(port);
sutAddrHandle = tciNewInstance(tciGetTypeForName("address"));
tciSetRecFieldValue(sutAddrHandle, "host", hostField);
tciSetRecFieldValue(sutAddrHandle, "portField", portField);
memset(&sutAddr, 0, sizeof(sutAddr));
len = sizeof(TciValue);
sutAddr.data = (unsigned char *) malloc(len);
memcpy(sutAddr.data, &sutAddrHandle, len);
sutAddr.bits = len << 3;
/* Put raw encoded frame received from the SUT "as is" to recvMsg */
recvMsg.data = buff;
recvMsg.bits = rval << 3;
buff[rval] = 0;
tsiPort.portName = "tsiPort"; /* hard-coded */
tsiPort.portIndex = -1;
/* Append the message to the port queue on the test execution side */
triEnqueueMsg(&tsiPort, &sutAddr, 0, &recvMsg);
free(sutAddr.data);
}
/* Unreachable code, added to avoid compiler complaints */
return 0;
}
Creating socket listener thread
Add this section before "printf("Init OK!\n");" in main.cxx:
/* Create UDP listener thread */
{
pthread_t thread_ptr;
extern void *runSocketListener(void *p_);
if (pthread_create(&thread_ptr, 0, &runSocketListener, 0) != 0)
{
printf("main(): Unable to start a new thread.\n");
exit(1);
}
}
- do not forget to #include <pthread.h> in main.cxx
Adding decoder for the received message
- replace the default implementation of tciDecode() in CD_impl.cxx with the following fragment of code:
TciValue tciDecode(BinaryString message, TciType decHypothesis)
{
int len;
TciValue result = 0;
TciValue typeCode = 0;
TciValue lenField = 0;
TciValue contentField = 0;
TciValue payload = 0;
long length = 0;
String typeName = "";
if (decHypothesis)
{
typeName = tciGetName(decHypothesis);
}
if (!strcmp(typeName, "address"))
{
return *((TciValue *) message.data);
}
else if (!decHypothesis)
{
/* Assuming decoding of value of Message type */
char* buffer = (char *) message.data;
len = message.bits >> 3;
if (!len)
{
otReportError("tciDecode(): Error: Message content is too short.");
return 0;
}
if (len == 2)
{
otReportError("tciDecode(): Error: Message content is malformed.");
return 0;
}
typeCode = longToTciValue((unsigned char) buffer[0]);
result = tciNewInstance(tciGetTypeForName("Message"));
tciSetRecFieldValue(result, "typeCode", typeCode);
if (len > 2)
{
length =
(((unsigned long) (unsigned char) buffer[1]) << 8) |
(((unsigned long) (unsigned char) buffer[2]) << 0);
lenField = tciNewInstance(tciGetTypeForName("uint16"));
assignLongToTciValue(lenField, length);
contentField = sizedCharstringToTciValue(buffer + 3, len - 3);
payload = tciNewInstance(tciGetTypeForName("Payload"));
tciSetRecFieldValue(payload, "len", lenField);
tciSetRecFieldValue(payload, "content", contentField);
tciSetRecFieldValue(result, "payload", payload);
}
}
else
{
printf("tciDecode(): Error: Unrecognized type '%s'.\n", typeName);
}
return result;
}
Improvement proposal for naming
- naming should reflect the fact that a new instance of TciValue is created to avoid confusion:
- sizedCharstringToTciValue -> newTciValueFromSizedCharstring
- longToTciValue -> newTciValueFromLong
Theory track (continued 2)
Logging options in TCI-TM similar to options of "tester run"
In tci/tci_ext.h, search for OT_TM_LOG_INDENT to get the full list of available options.
Parameter provisioning in TCI-TM
- by default, through callback (defined in tci/tci.h):
TciValue tciGetModulePar (TciModuleParameterIdType parameterId);
- the callback is invoked implicitly multiple times, once per each module parameter, by the SDK when you start a test case or control part
- explicit triggering of callback call sequence (see documentation in tci/tci_ext.h):
int otTMProvisionModuleParameters();
- if you want to use standard OpenTTCN scheme for parameter provisioning, thus bypassing TTCN-3 tciGetModulePar() callback mechanism:
- search for documentation on OT_TM_FLAG_USE_PARAMETERS_FROM_REPOSITORY flag in tci/tci_ext.h - this tells how to do this
Correspondence between TTCN-3 and ASN.1 type system
Source: ETSI ES 201 873-7 V3.1.1 (2005-06), ETSI Standard, Methods for Testing and Specification (MTS); The Testing and Test Control Notation version 3; Part 7: Using ASN.1 with TTCN-3: Clause 8 "ASN.1 and TTCN-3 type equivalents", Subclause 8.1, Table 2
| ASN.1 | TTCN-3 |
|---|---|
| BOOLEAN | boolean |
| INTEGER | integer |
| REAL | float |
| OBJECT IDENTIFIER | objid |
| BIT STRING | bitstring |
| OCTET STRING | octetstring |
| SEQUENCE | record |
| SEQUENCE OF | record of |
| SET | set |
| SET OF | set of |
| ENUMERATED | enumerated |
| CHOICE | union |
| VisibleString | charstring |
| IA5String | charstring |
| UniversalString | universal charstring |
Questions?
Have questions? Add them here.
