OpenTTCN/Developer corner/SDK for C, procedure-based communication

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


Example of adapter using procedure-based communication, SDK for ANSI C



In this article we are going to develop an adapter example illustrating procedure-based communication in TTCN-3. We will add relevant functionality to the example we developed in the Creating adapter with ANSI C SDK article, so we will take the final package created in that example as a starting point for our work.

Contents

Defining procedural signature in TTCN-3

First we define a procedural signature substituting a request - response pair defined originally for our message-based communication example. In parameter represents a request, out parameter represents a response, while the return value duplicates the response.

The following fragment of code shall be added to Main.ttcn:

type record InvalidRspFormat { }

type record InvalidRequest { }

signature RemoteProc(
    in charstring req,
    out charstring rsp)
return charstring
exception(
    InvalidRspFormat,
    InvalidRequest);

The defined procedural signature simulates an operation call accepting a request as in parameter and returning the result of the processing in the form of a response contained in out parameter and in the return value. If processing fails, operation call throws an exception.

Defining procedural template in TTCN-3

Next we define parameterized signature templates for call and reply of the following form (file Main.ttcn):

template RemoteProc RemoteProcCall(in charstring p) :=
{
    req := p,
    rsp := omit
}

template RemoteProc RemoteProcReply(in charstring p) :=
{
    req := omit,
    rsp := p
}

Defining procedural testcase in TTCN-3

Next we define a procedural testcase illustrating procedural call and reply (file Main.ttcn):

testcase TC_R2D2_proc_demo()
    runs on ComponentType
    system SystemInterfaceType
{
    activate(DefaultAltstep());
    T_GUARD.start;
    map(mtc:p, system:tsiPort);

    p.call(RemoteProcCall("Hello, world!"), 2.0) to sut_addr
    {
        [] p.getreply(RemoteProcReply("Hello, mankind!")) from sut_addr
        {
            setverdict(pass);
        }
        [] p.getreply
        {
            setverdict(fail);
            stop;
        }
        [] p.catch // also timeout
        {
            setverdict(fail);
            stop;
        }
    }

    p.call(RemoteProcCall("Hello, gentlemen!"), 2.0) to sut_addr
    {
        [] p.getreply(RemoteProcReply("Reformulate")) from sut_addr
        {
            setverdict(pass);
        }
        [] p.getreply
        {
            setverdict(fail);
            stop;
        }
        [] p.catch // also timeout
        {
            setverdict(fail);
            stop;
        }
    }

    setverdict(pass);
}

Adding encoder and decoder to adapter

Parameters of call and reply are encoded and decoded one by one. Hence we need to add encoder and decoder for the charstring type.

Here is a relevant fragment of code of the tciDecode() function (file CD_impl.c):

    else if (!strcmp(typeName, "charstring"))
    {
        /* encoding: length (MSB, LSB), content */

        int length = 0;

        len = message.bits >> 3;

        if (len < 2)
        {
            otReportError("tciDecode(): Error: Message content is too short.");
            return 0;
        }

        length = (((int) (unsigned int) message.data[0]) << 8) |
                 (((int) (unsigned int) message.data[1]) << 0);

        if ((len - 2) != length)
        {
            otReportError("tciDecode(): Error: Inconsistent message length and content size.");
            return 0;
        }

        return charstringToTciValue((const char *) message.data + 2);
    }

Here is a relevant fragment of code of the tciEncode() function (file CD_impl.c):

    else if (!strcmp(typeName, "charstring"))
    {
        /* encoding: length (MSB, LSB), content */

        char* str_ptr = 0;
        long str_len = -1;
        unsigned char* buffer = 0;

        tciValueToCharstring(&str_ptr, &str_len, value);

        if ((!str_ptr) || (str_len < 0))
        {
            otReportError("tciEncode(): Cannot encode charstring.");
        }

        buffer = (unsigned char *) malloc(str_len + 2);

        buffer[0] = (unsigned char) (str_len >> 8);
        buffer[1] = (unsigned char) (str_len >> 0);

        memcpy(buffer + 2, str_ptr, str_len);

        free(str_ptr);

        result.data = buffer;
        result.bits = (str_len + 2) << 3;
    }

Configuring adapter

To make a distinction between message-based and procedure-based mode of using the same communication channel and the same SUT, we need to configure our adapter. We do this by adding an adapter start-up option that we detect during adapter initialization. Here is a piece of code that does the detection (file main.c):

extern int _procCommMode;

int main (int argc, char *argv[])
{
...

    if ((argc == 2) && (!strcmp(argv[1], "--proc-mode"))) _procCommMode = 1;
...
}

We have also added declaration of the _procCommMode flag as a global variable to Utilities.h and Utilities.c.

Utilities.h:

/* 0 - message-based (default), 1 - procedure-based. */
extern int _procCommMode;

Utilities.c:

int _procCommMode = 0;

Adding call handler to adapter

We need to process a TRI call that we receive from the TE to forward it to the SUT in the form of the encoded frame. We do this by implementing a triCall() handler (file SA_impl.c).

#include <isl/TTCN3.h>

#include <stdlib.h>

...

TriStatus triCall
(const TriComponentId* componentId,
 const TriPortId* tsiPortId,
 const TriAddress* sutAddress,
 const TriSignatureId* signatureId,
 const TriParameterList* parameterList)
{
    if (!strcmp(signatureId->objectName, "RemoteProc"))
    {
        /**
         * two parameters:
         *
         * in charstring req - request, encoded as MSB + LSB + content
         * out charstring rsp - not used in this context
         */

        TriParameter* par = 0;
        int enc_len = 0;
        unsigned char* data = 0;
        unsigned char* buffer = 0;

        TciValue addrValue = *((TciValue *) sutAddress->data);

        unsigned long ipAddr = 0;
        unsigned short portNumber = 0;

        int result =
            extractHostAndPortFromAddress(
                &ipAddr, &portNumber, addrValue);

        if (result) return TRI_ERROR;

        if (parameterList->length != 2)
        {
            otReportError("triCall(): Wrong number of parameters.");
            return TRI_ERROR;
        }

        par = parameterList->parList[0];

        data = par->par.data;

        enc_len = par->par.bits >> 3;

        buffer = (unsigned char *) malloc(enc_len + 1);

        buffer[0] = 0x01; /* greeting request code */
        memcpy(buffer + 1, data, enc_len);

        result = sendDatagramPacket(
            ipAddr,
            portNumber,
            (char*) buffer,
            enc_len + 1);

        return (!result) ? TRI_OK : TRI_ERROR;
    }

    return TRI_ERROR;
}

Adding reply handler to adapter

SUT may reply with response to the request, and in procedure-based communication this maps to a TTCN-3 reply to the call. Hence we need to modify our socket listener to enqueue reply or exception rather than message whenever we receive a frame back from the SUT.

Here is a fragment of code that does exactly that (file SocketListener.c, add this before triEnqueueMsg() call):

        if (_procCommMode && (rval >= 3) && (buff[0] == 0x02 /* greeting response */))
        {
            /* Operating in procedure-based communication mode, normal reply */

            TriSignatureId sigId;
            TriParameterList pl;
            TriParameter retVal;
            TriParameter** parList = 0;

            memset(&sigId, 0, sizeof(sigId));
            memset(&pl, 0, sizeof(pl));
            memset(&retVal, 0, sizeof(retVal));

            sigId.objectName = "RemoteProc";

            pl.parList = (TriParameter **) malloc(sizeof(TriParameter*) * 2);
            pl.length = 2;

            parList = pl.parList;

            parList[0] = (TriParameter*) malloc(sizeof(TriParameter));
            memset(&(parList[0]->par), 0, sizeof(BinaryString));
            parList[0]->mode = TRI_IN; /* not used in reply */

            parList[1] = (TriParameter*) malloc(sizeof(TriParameter));
            memset(&(parList[1]->par), 0, sizeof(BinaryString));
            parList[1]->par.bits = recvMsg.bits - 8;
            parList[1]->par.data = recvMsg.data + 1;
            parList[1]->mode = TRI_OUT;

            /* return value duplicates content of out parameter */
            retVal.par.bits = recvMsg.bits - 8;
            retVal.par.data = recvMsg.data + 1;
            retVal.mode = TRI_IN;

            triEnqueueReply(&tsiPort, &sutAddr, 0, &sigId, &pl, &retVal);

            free(parList[0]);
            free(parList[1]);

            free(sutAddr.data);

            continue;
        }
        else if (_procCommMode)
        {
            /* Operating in procedure-based communication mode, exception */

            TriSignatureId sigId;

            memset(&sigId, 0, sizeof(sigId));

            sigId.objectName = "RemoteProc";

            triEnqueueException(&tsiPort, &sutAddr, 0, &sigId, &recvMsg);

            free(sutAddr.data);

            continue;
        }


We also need to configure the decoder to recognize frames without decoding hypothesis as an exception rather than message in procedure-based communication mode. Here is a relevant addition to the tciDecode() handler (file CD_impl.c, add this before else if (!decHypothesis)):

    else if ((!decHypothesis) && (_procCommMode))
    {
        /* procedure-based mode, assuming decoding of an exception */

        unsigned char* buffer = message.data;
        len = message.bits >> 3;

        if (!len)
        {
            otReportError("tciDecode(): Error: Exception content is too short.");
            return 0;
        }

        if ((len == 1) && (buffer[0] == 0xFF))
        {
            result = tciNewInstance(tciGetTypeForName("InvalidRequest"));
        }
        else
        {
            result = tciNewInstance(tciGetTypeForName("InvalidRspFormat"));
        }
    }

Running test case

We need to compile Main.ttcn, start SUT and adapter in procedure-based communication mode. SUT is started as usually. Adapter is started in this way:

Release\SimpleAdapter.exe --proc-mode

Now run the test case using this command:

tester run simple TC_R2D2_proc_demo

Here is an example log output:

*****************************************************************************
*** RUNNING TEST CASE TC_R2D2_proc_demo

Tester : {16:33:28.539} : // Time: 16:33:28.538. Date: 08/May/2009. MOT version: TC: 2.58.0.RC1.0.
mtc : {16:33:28.579} : // CASE TC_R2D2_proc_demo STARTED
mtc : {16:33:28.579} : T_GUARD.start(10.0); // Timer is started: duration 10 s.
mtc : {16:33:28.593} : map(mtc:p, system:tsiPort);
mtc : {16:33:28.621} : p.call(RemoteProc RemoteProcCall := { req := "Hello, world!", rsp := omit }, 2.0) to { host := "127.0.0.1", portField := 7431 }; // Response supervising timer is started: duration 2 s.
mtc : {16:33:28.668} : p.getreply(RemoteProc RemoteProcReply := { rsp := "Hello, mankind!" } value "Hello, mankind!") from { host := "127.0.0.1", portField := 7431 };
mtc : {16:33:28.668} : setverdict(pass);
mtc : {16:33:28.740} : p.call(RemoteProc RemoteProcCall := { req := "Hello, gentlemen!", rsp := omit }, 2.0) to { host := "127.0.0.1", portField := 7431 }; // Response supervising timer is started: duration 2 s.
mtc : {16:33:28.793} : p.getreply(RemoteProc RemoteProcReply := { rsp := "Reformulate" } value "Reformulate") from { host := "127.0.0.1", portField := 7431 };
mtc : {16:33:28.793} : setverdict(pass);
mtc : {16:33:28.793} : setverdict(pass);
mtc : {16:33:28.825} : // CASE TC_R2D2_proc_demo FINISHED
mtc : {16:33:28.825} : // VERDICT TC_R2D2_proc_demo PASS

*****************************************************************************
*** TEST EXECUTION SUMMARY

Pass    Fail    Inconc  None    Error   Total   Duration
1       0       0       0       0       1       00:00:01

Final result

You can download the final version of the adapter, test suite and the SUT that we developed in this article, all in one package, by following this link.

Views
Personal tools