OpenTTCN/Training/2008-02-01

From OpenTTCN

Jump to: navigation, search

  OpenTTCN DocZone

  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



Contents

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);

DOC

typedef BinaryString TriAddress;
typedef BinaryString TriMessage;

tciEncode()

DOC

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

DOC

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

DOC

otRegisterAdapter

tri/tri_ext.h:

DOC

  • 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:

DOC

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

DOC

  • 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)

DOC

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)

DOC

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

Table 1: ASN.1 and TTCN-3 type equivalents
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.

Views
Personal tools