TOC PREV NEXT INDEX

Put your logo here!


2 USER'S GUIDE

2.1 OVERVIEW

From the point of view of the Event Handler Tool Kit, a VLT application is considered as a server which reacts on a number of different events, where an event is considered as any possible kind of means with which the application can be addressed. In this version the following types of events are supported:

· CCS Commands
· CCS Replies
· CCS Error Replies
· CCS Time-outs
· CCS Timer messages
· CCS Data Base Event messages
· UNIX signals
· UNIX file input
· Synchronization on multiple events

The core of the tool kit is the evhHANDLER class. It is an event dispatcher that receives all the events directed to the application (coming from the outside world or generated internally). It parses the event and searches in an internal dictionary for a list of functions (callbacks) that must be called to handle that particular event. If such a list of functions exist, they are called in sequence. Upon return from the last callback, the dispatcher is ready to manage other events.

The callback lists associated with events are dynamic and the evhHANDLER class provides methods to add and delete elements from the list.

2.2 USAGE OF THE EVH TOOL KIT

For using the EVH Tool Kit similar lines of direction as described in [2] must be followed.

Applications using the EVH Tool Kit, must have the extension ".C" (capital C), which indicates for the GCC compiler that the file is C++ code and should be compiled as such. Extensions of include files remain the same (".h").

In the makefile it is necessary to specify the evh and eccs libraries. Support libraries that must be linked with the code are CCS, fnd, stdc++, g++, iberty.

The general architecture of an application using the EVH Tool Kit is the following:

- Include "evhHANDLER.h"

- Every application has a predefined global event handler. It is accessible through the global variable:

evhHANDLER *evhHandler;

It is possible to use other handlers or to change the default one, but this is out of the scope of this introduction.

- The main function contains the CCS environment initialization and installs the initial callbacks. Then it enters the main loop handled by the event handler using the call:

evhHandler->MainLoop();

The program returns from this function only when the program ends, in case of errors or under special conditions. Then the CCS environment is closed.

- Every handler (called from now on callback) is implemented using a function or a method of a class. These functions will be called by the evhHandler when the corresponding event occurs. While the application is executing the code of the callback, all incoming events are queued and are served only when the callback returns. This means that callbacks must return as soon as possible. It is assumed a typical responce of 100 milliseconds, so callbacks must be able to return within 100 milliseconds. For debugging purposes it is possible to ask the evhHANDLER to check the execution time of callbacks, issuing a warning if it exceeds the specified responce time (by default 100 msec).

- The standard way of designing the application is using an evhTASK class (a class derived from the library-provided class evhTASK) to describe the behaviour of the application:

1. An instance of the class is declared in the main before entering the main loop.

2. The callbacks are methods of the class.

3. The class constructor initializes the callbacks and everything else necessary.

4. The class destructor cleans up everything.

The evhTASK class does not correspond to a UNIX process. All the instances of evhTASK defined in a program's source code are running inside the same UNIX process and no threads are used. The name "TASK" comes from the idea of dedicating a single instance to accomplice a single specific task of the application, such as all the processing involved in the handling of a command.

Due to the event-driven architecture the evhTASK instances are logically running in parallel and independently, although they are not really parallel, since they are inside a single process. For example, if the callbacks are fast to return the control to the main loop (as it is required), everything works fine, but if one of them keeps the control for too much time, no other object can receive events.

The following example shows a simple application to receive a command:

// == evhSample_1.C =============================================

// Include files for ECCS and Evh Toolkit

#include "ECCS.h"

#include "evhHANDLER.h"

// Include for the basic class

#include "evhTASK.h"

/*========================================*/

/* Definition for the APPLICATION class */

/*========================================*/

class APPLICATION: public evhTASK

{

public:

APPLICATION();

// Definition for the callback to be used receiving the

// "SAMPLE" command

evhCB_COMPL_STAT SampleCB(msgMESSAGE &msg, void *udata);

}; /* end class APPLICATION */

// Body of the constructor method.

// It is called whenever an instance is created

APPLICATION::APPLICATION()

{

// This variable is used to define the event:

// a message of type msgTYPE_COMMAND, with

// Command="SAMPLE", no regard for the CommandId

evhMSG_TYPE_KEY key(msgTYPE_COMMAND,"SAMPLE");

// This variable is used to define the callback:

// It is a method of this object, no user parameters

evhOBJ_CALLBACK cb(this,(evhCB_METHOD)&APPLICATION::SampleCB);

// Then the callback is attached to the event handler */

evhHandler->AddCallback(key,cb);

}

// Body for the callback

evhCB_COMPL_STAT APPLICATION::SampleCB(msgMESSAGE &msg, void *udata)

{

// Just print the message buffer on stdout

printf("Received a SAMPLE command: %s", msg.Buffer());

// Send back the reply to the command

msg.LastReply(ccsTRUE);

msg.Buffer("Command received!");

msg.SendReply();

// The callback must remain installed for another command

return evhCB_NO_DELETE;

}

//================================

// main procedure

//================================

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

{

ccsInit(argv[0]);

// Create the instance of APPLICATION.

// The instance must be in a scope section, to be sure

// that is exits out of scope and is then properly deleted

// BEFORE calling ccsExit()

// The call to the macro USE() prevents the GCC compiler

// from issuing a warning: it assumes that app is not used!

{

APPLICATION app; USE(app);

// enter the main loop

evhHandler->MainLoop();

}

ccsExit();

}

// ===============================================

It is suggested to structure the code for each EVH based application in the following way:

1. One include file containing the various classes for each possible event.
2. One file per class defining the methods of the class.
3. One main program for the application initializing the evh main loop.

If a class defines callback functions to handle e.g. a CCS command, it is suggested to give the class the name of this command. What follows is a sample definition for a E.g. a class to handle a CCS command named "SETUP":

// Definition of the SETUP class

class SETUP: public evhTASK

{

public:

SETUP();

// Call back functions for the SETUP class

evhCB_COMPL_STAT HandleSETUP(msgMESSAGE &msg, void *udata);

evhCB_COMPL_STAT HandleRepSETUP(msgMESSAGE &msg, void *udata);

evhCB_COMPL_STAT HandleErrRepSETUP(msgMESSAGE &msg, int code);

private:

msgMESSAGE recvMsg;

evhCOMMAND setupCmd;

}; // End class SETUP

To have a complete working program it is necessary to implement the body for the methods declared here and to create an instance of the class inside the body of the main() function, before entering the MainLoop().

Example 6.2 provides a simple complete application with this structure.

Many examples of design and implementation based on this architecture can be found in the design documents for VLT applications like the TCS workstation modules. Look for example at [3].

2.3 ERROR HANDLING

See description in [2].

2.4 EVENTS AND CALL BACKS

The AddCallback() method of the evhHANDLER class provides the basic mechanism to associate an event with a callback procedure.

Events are defined by a triplet of parameters in the msgMESSAGE data structure for the message associated to the event:

- vltUINT8 msgType

- msgCMD command

- msgCMDID commandId

where msgType identifies the type of message received.

The following table illustrates the message types currently supported:
msgType
Description
msgTYPE_COMMAND
A command has been received.
The message is in an object of type msgMESSAGE.
msgTYPE_REPLY
A reply to a previously sent command has been received.
The message is in an object of type msgMESSAGE.
msgTYPE_ERROR_REPLY
An error reply to a previously sent command has been received.
The message is in an object of type msgMESSAGE.
Retrieve error information from the msgMESSAGE error stack.
msgTYPE_TIMER
An installed time is expired (like a timeout or a cyclical one).
The message is in an object of type msgMESSAGE.
msgTYPE_EVENT
A registered database event has occurred.
The message is in an object of type evtEVENT_MSG.
msgTYPE_ALARM
A registered alarm has been rised and an alarm notification message has been received.
The message is in an object of type alrmMESSAGE.
msgTYPE_ALARM_CONN
An alarm management message has been received..
The message is in an object of type alrmMESSAGE.

The data structure evhMSG_TYPE_KEY holds such a triplet and is the "search key" for the callbacks.

Callbacks are defined by the pointer to the function that must be called and by a generic pointer (void *)to optional user data that should be passed to the callback procedure when it is executed. If the callback procedure is a method of a class, it is also necessary to specify the instance of the class.

A callback procedure MUST have the following prototype:

evhCB_COMPL_STAT CallbackName(msgRAW_MESSAGE &msg, void *udata);

Where msgRAW_MESSAGE can be replaced by any of its subclasses. Typically by a msgMESSAGE.

The data structures evh_CALLBACK and evhOBJ_CALLBACK are used respectively to associate a callback procedure to a simple function or to a method of a class. The evhOBJ_CALLBACK is a subclass of evh_CALLBACK.

As a consequence, the AddCallback() method requires two parameters (see also the previous example):

- the evhMSG_TYPE_KEY

- the evh_CALLBACK (or evhOBJ_CALLBACK)

// Sample callback registration

evhMSG_TYPE_KEY key(msgTYPE_COMMAND,"SAMPLE",0);

evhOBJ_CALLBACK cb(this,(evhCB_METHOD)&APPLICATION::SampleCB,NULL);

evhHandler->AddCallback(key,cb);

The same evhMSG_TYPE_KEY and evhOBJ_CALLBACK instances can be used in subsequent calls to AddCallback() changing one or more fields in the structure using the provided access methods, as in the following example where several techniques are shown:

// Sample callback registration

// I set only the message type. By default command="" and commandId=0

evhMSG_TYPE_KEY key(msgTYPE_COMMAND);

// I set only the object. By default procedure=NULL and udata=NULL

evhOBJ_CALLBACK cb(this);

// 1: I set the command, the command id and the callback

// procedure using the access methods

key.Command("START");

key.CommandId(10);

cb.Proc((evhCB_METHOD)&APPLICATION::StartCB);

evhHandler->AddCallback(key,cb);

// 2: I can set more fields at the same time

key.MsgType(msgTYPE_REPLY).Command("STOP").CommandId(0);

cb.Proc((evhCB_METHOD)&APPLICATION::StartCB);

evhHandler->AddCallback(key,cb);

// 3: I can also set the fields directly on the call to AddCallback()

evhHandler->AddCallback(key.Command("RESET"),

cb.Proc((evhCB_METHOD)&APPLICATION::ResetCB));

A callback procedure MUST return one of the following values:

evhCB_NO_DELETE - Upon return, the callback remains available. If a new event that satisfy the key occurs, the callback function is called again.

evhCB_DELETE - Upon return, the callback is detached.

The value returned from callback function is actually a bit pattern. Other bits are used to return some more information to the main loop function. The actual return value can be build up with an OR expression using the following masks:

evhCB_NO_DELETE or evhCB_DELETE

To tell the main loop that the callback must be deleted. Default - bit not set - is NO_DELETE.

evhCB_SUCCESS or evhCB_FAILURE

To tell the main loop if the callback was successful or not. Default - bit not set - is SUCCESS

In this case the main loop logs an error and returns with FAILURE.

evhCB_RETURN

Set this bit to ask the main loop to return.

evhCB_NOCALLS

Used internally to tell the main loop that there were no callbacks installed for that event. The designer of a callback should never use this bit.

A typical usage of this feature is in the handling of multiple replies: when the Last Reply is received, the callback is detached, otherwise it is let in place to handle the next one:

//Body for the callback to receive replies

evhCB_COMPL_STAT APPLICATION::HandleReplyCB(msgMESSAGE &msg, void *udata)

{

// Just print the message buffer on stdout

printf("Receives a reply: %s", msg.Buffer());

// Test if this is the last one, if so the callback must be detached

if(msg.LastReply() == TRUE)

return evhCB_DELETE;

// The callback must remain installed for another reply

return evhCB_NO_DELETE;

}

These are some examples of callback's return codes:

Return Value
Meaning
evhCB_DELETE
SUCCESS and delete the callback.
evhCB_DELETE | evhCB_SUCCESS

SUCCESS and delete the callback.

evhCB_DELETE | evhCB_FAILURE

FAILURE and delete the callback.

evhCB_NO_DELETE

SUCCESS and do not delete the callback.

evhCB_FAILURE
FAILURE and DO NOT delete the call-back.
0
Corresponds to: evhCB_NO_DELETE | evhCB_SUCCESS

2.5 TIMEOUTS

Whenever an application needs to be waked up after a certain amount of time, an instance of the class

evhTIMEOUT

can be used. When an instance of the class is created, it installs a callback procedure. This will be triggered when the timeout expires. Upon return from the callback procedure, the evhTIMEOUT destroys itself cleaning up all the used resources.

An explicit delete of the timeout object before execution of the callback procedure for the timeout, cleans up the timeout itself.

The steps to use this class are the following (look at the sample program evhSample_3.C):

- Include "evhTIMEOUT.h".

- Declare an instance of the class, including the callback and the required timeout:

evhTIMEOUT *cmd = new evhTIMEOUT(cb,timeout);

or

evhTIMEOUT cmd(cb, timeout);

The second call will be generally never used: it creates an object local to the current function. It is then deleted on function return and the timeout is reset. It is not usually necessary to store the pointer to the timer, unless we want to be able of deleting it explicitly: it is deleted automatically when the timeout callback procedure returns. As a consequence, usually the instruction to create the evhTIMEOUT will simply be: new evhTIMEOUT(cb,timeout);

2.6 TIMERS

Whenever an application needs to be waked up periodically, it can use an instance of the class:

evhTIMER

When an instance of the class is created, it installs a callback procedure. This will be triggered periodically with the defined time interval.

Upon return from the callback procedure, the evhTIMER destroys itself if the callback return value was evhCB_DELETE. If it was evhCB_NO_DELETE the timer keeps running.

An explicit delete of the timer object, cleans up the timer itself. The evhTIMER is a subclass of evhTIMEOUT and all the same considerations applies.

The steps to use this class are the following (look at the sample program evhSample_4.C):

- Include "evhTIMER.h".

- Declare an instance of the class, including the callback, the required timeout and (optionally) the starting time:

new evhTIMEOUT(cb, interval, startTime);

2.7 COMMANDS AND REPLIES

Whenever an application sends a command, it must be able to handle:

- One or more reply to the command.

- An error reply.

- A timeout.

The handling of these events, with all the interactions, is quite complex. For example, if an error reply is received, we have to detach the callbacks for the normal replies and the timeout, because the command is failed.

To make this handling easier, the Evh Tool Kit provides a class where the full protocol is implemented. This class is called:

evhCOMMAND

Using an instance of this class it is possible to define callbacks to be used for the reply, error reply and timeout to a command and to send the command. All the installation/deinstallation of the callbacks is done internally to the class.

The steps to use this class are the following (look at the sample program evhSample_2.C):

- Include "evhCOMMAND.h"

- Declare an instance of the class:

evhCOMMAND cmd;

- Define the callbacks used to handle one or more of the following events: reply, error reply and timeout (for example as members of a TASK class).

- Install the callbacks in the instance of the evhCOMMAND object:

evhOBJ_CALLBACK cb(this);

cmd.Reply(cb.Proc((evhCB_METHOD)&APPLICATION::ReplyCB));

cmd.ErrReply(cb.Proc((evhCB_METHOD)&APPLICATION::ErrReplyCB));

cmd.Timeout(cb.Proc((evhCB_METHOD)&APPLICATION::TimeoutCB),5);

- Send the command:

msgMESSAGE msg;

msg.Command("REPLYME");

msg.Destenv(getenv("RTAPENV"));

msg.Buffer("Hello");

msg.Destproc("evhSample_2bis");

cmd.Send(msg);

After a call to the Send() method the object waits for the replies or for the expiration of the timeout. After the last reply or the timeout a new call to the Send() method can be performed. For more details about the class and the other methods implemented, look at its man page.

2.8 DATABASE EVENTS

Whenever an application needs to monitor the value of a database attribute, it can attach a database event to that attribute and add a callback to be executed whenever the event is triggered.

In order to attach a database event, an instance of the evtEVENT class must be used. Remember that the event remains attached up to when it is explicitly detached or the object is destroyed; this means that it does not make any sence to use temporary variables of type evtEVENT.

Typically an instance of evtEVENT will be a data member of the class providing the callbacks to handle the database event itself:

class APPLICATION: public evhTASK

{

public:

APPLICATION();

evhCB_COMPL_STAT DbEventCB(evtEVENT_MSG &msg, void *udata);

private:

evtEVENT evt;

}; /* end class APPLICATION */

The Attach() of the event can be done at any time it is needed. In this example we do it in the constructor of the class.

When the event is attached, it receives a unique event identifier.

It receives also a unique commandId, that will have to be used to register the callback for the event itself.

Given an instance of evtEVENT already attached (called evt in our example), the identification key for registering a callback on that event is the following:

key.MsgType(msgTYPE_EVENT) // The received message is an event

.Command(evtEVENT_CMD) // The command identifier

.CommandId(evt.CommandId()); // The commandId assigned to the event

What follows is a typical code to attach a database event and register a callback:

APPLICATION::APPLICATION()

{

// Assign the name for the attribute

evt.AttrName(":Appl_data:myPoint.myAttribute");

// Attach the event. Now the commandId is defined

evt.Attach();

// Set the key and callback

evhMSG_TYPE_KEY key;

evhOBJ_CALLBACK cb(this);

key.MsgType(msgTYPE_EVENT)

.Command(evtEVENT_CMD)

.CommandId(evt.CommandId());

cb.Proc((evhCB_METHOD)&APPLICATION::DbEventCB);

evhHandler->AddCallback(key,cb);

}

From this point on, after having entered the main loop, whenever the database event occurs, the method DbEventCB is called.

2.9 MULTIPLE EVENT (SYNCHRONIZATION)

Whenever an application needs to be waked up after a set of events, with no regard for the order in which they occur, an instance of the class:

evhSYNC

can be used.

When an instance of this class is used, it is possible to specify:

- The callback procedure to trigger when all the conditions are satisfied

- An optional timeout and a callback procedure to be triggered if not all the conditions are satisfied when the timeout expires.

- A set of requested conditions.

In the simplest case, a condition is just an event, as described by a evhMSG_TYPE_KEY. It is also possible to specify a flag to be tested for a specific value.

Whenever one of the registered events occurs, a check is made to verify if all the requested events have occurred AND all the (optional) flags have the required value. If the check is positive, the installed callback procedure is triggered.

The steps to use this class are the following (look at the sample program evhSample_5.C):

- Include "evhSYNC.h"

- Declare an instance of the class

- Install the callback procedure, using the method:

ccsCOMPL_STAT Action(evhCALLBACK &newCB = NULL);

- Install the (optional) timeout using the method:

ccsCOMPL_STAT Timeout(evhCALLBACK &cb, eccsTIMEVAL interval);

- Define the conditions under test using the method:

ccsCOMPL_STAT AddCondition(const evhMSG_TYPE_KEY &key,

int *flag = NULL, int value = TRUE);

where evhMSG_TYPE_KEY key is the event definition, int *flag is the pointer to the flag to be checked int value is the expected value for the flag

If the flag is not specified the condition is considered TRUE when the required event occurs, without any additional check.

- Activate the object using the method:

ccsCOMPL_STAT Run();

After the call to the Run() method, the object listens for the requested events and it is not possible to modify the installed callback procedures or trigger conditions.

The method Reset() can be used to force a disable of the monitoring.

The method Check() forces the evhSYNC() to check the conditions. If all the conditions are satisfied, the installed action is triggered.

When the monitoring is disabled (after a Reset(), after a triggering of the installed action or after a timeout) it is possible to change the callback procedures and the actions calling AddCondition() to add new conditions or ClearConditions() to clean up the list of conditions.

2.10 UNIX FILE DESCRIPTORS

It is possible to install callbacks on unix file descriptors ready for reading and/or writing.

This uses the standard unix select() system call to monitor files descriptors, as well as all the CCS events already analyzed.

Since this is heavy in terms of performances, the option for the event handler must be explicitly turned on. This can be done before entering the main loop with the UseSelect() method, like in the following sample main:

// ====================================================

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

{

ccsInit(argv[0]);

// Create the instance of APPLICATION.

// The instance must be in a scope section, to be sure

// that is exits out of scope and is then properly deleted

// BEFORE calling ccsExit()

// The call to the macro USE() prevents the GCC compiler

// from issuing a warning: it assumes that app is not used!

{

APPLICATION app; USE(app);

// Tells the standard event handler to catch messages

// using the select() system call. In this case is

// possible to catch also signals and file IO events

// as normal messages

evhHandler->UseSelect(TRUE);

evhHandler->MainLoop();

}

ccsExit();

}

// ====================================================

The class evhFILEIO is used to attach and detach a file descriptors to the handler. Whenever an attached file descriptor is "ready" (read the standard UNIX documentation about the select(2) system call for more details) an internal command is generated. If there are callbacks associated to that command they are executed.

Consider the following example: we need to monitor if there is input coming from a socket. Suppose that we have already opened the socket and saved the file descriptor in the variable "sfd".

We first need to tell EVH that the file descriptor must be monitored. To do this we need an instance of evhFILEIO and we attach it:

// ===================================================

// Create the instance of evhFILEIO

evhFILEIO *fio = new evhFILEIO(sfd);

// Attach the file descriptor

fio->Attach();

// ===================================================

The evhFILEIO class provides the following public methods:

evhFILEIO(int fd);

Constructor. Gets as parameter the file descriptor to be checked. Only one evhFILEIO can be associated to a file descriptor

~evhFILEIO();

Destructor. Always detach the file descriptor. If an instance of evhFILEIO is deleted, no more commands are generated for that file descriptor.

ccsCOMPL_STAT Attach();

Attach the file descriptor: from this point on a command is generated whenever the file descriptor is "ready".

ccsCOMPL_STAT Detach();

Detach the file descriptor: no more commands are generated

int Fd() const;

Returns the file descriptor associated to this evhFILEIO.

vltLOGICAL Status() const;

Returns ccsTRUE if the file descriptor is currently attached, ccsFALSE if not.

Whenever an attached file descriptor is ready, an event is generated with the following key:

MsgType - msgTYPE_COMMAND

Command - evhFILE_IO_CMD

CommandId - file descriptor

and the callbacks associated to this key are processed. If we consider the previous example, the following sample code installs a callback processed when the socket is ready:

// ===================================================

// sfd contains the file descriptor for an open socket

// Create the instance of evhFILEIO

evhFILEIO *fio = new evhFILEIO(sfd);

// Attach the file descriptor

fio->Attach();

// Prepares the key

evhMSG_TYPE_KEY key(msgTYPE_COMMAND, // the type is command

evhFILE_IO_CMD, // the command is evhFILE_IO_CMD

fio->Fd()); // the command id is the socket

// file descriptor

// Prepares the callback

evhOBJ_CALLBACK cb(this,(evhCB_METHOD)&APPLICATION::SocketCB);

// Attaches the callback

evhHandler->AddCallback(key,cb);

// ===================================================

If someone writes on the socket an it becomes ready for read, the callback function SocketCB() is executed. It could have the following structure:

// ===================================================

evhCB_COMPL_STAT APPLICATION::SocketCB(msgMESSAGE &msg, void *udata)

{

int n = 0;

const int bsize = 256;

char buff[bsize];

printf("Retrieving input from file descriptor: %d\n",

msg.CommandId());

// This loop read from the socket

while((n = read(msg.CommandId(),buff,bsize-1)) > 0)

{

buff[n] = `\0';

printf("%s", buff);

}

return evhCB_NO_DELETE;

}

// ===================================================

2.11 UNIX SIGNALS

It is possible to install callbacks on UNIX signals.

This uses the standard UNIX select() system call and is quite heavy in terms of performances. To avoid overhead, the option for the event handler must be explicitly turned on. This can be done before entering the main loop with the UseSelect() method. Look at section 2.10 for more details.

The class evhSIGNAL is used to attach and detach the monitoring of a signal to the handler.

When a signal is selected, a special signal handler is installed. If the signal occurs, the signal handler is called and sets the environment in a proper way to call the registered callback, if any. Then it returns and the select() is interrupted. The event handler is able to recognize that the select() was interrupted by a signal and calls the corresponding callback.

This means that the callback handling a signal is not called while handling the signal inside the signal handler, but after, in normal code. The callback can in this way do everything and does not suffer the limits of a signal handler.

In principle, any signal can be handled, but some signals are reserved for CCS and have already another handler installed (USR1 and USR2). Signal handling is a difficult topic, and must be dealt with great care.

It is also important to notice that signals handled in this way loose a peculiar characteristic of a signal: they are no more treated as software interrupts and the callback function will be actually executed only when the application returns to the main loop. This behaviour is generally good to handle the SIGCHLD but not in general for all the signals.

If the received signal is a SIGCHLD, the signal handler also performs a wait() on the terminated child. The pid of the died process is sent to the callback in the CommmandId().

Consider the following example: we need to monitor if a child process terminates. Suppose that we have already spawned the process and its PID is stored in the variable "childPid".

We first need to tell EVH that the SIGCHLD must be monitored. To do this we need an instance of evhSIGNAL and we install it:

// ===================================================

// Create the instance of evhSIGNAL

evhSIGNAL *sig = new evhSIGNAL(SIGCHLD);

// Attach the file descriptor

sig->Install();

// ===================================================

The evhSIGNAL class provides the following public methods:

evhSIGNAL(int sig);

Constructor. Gets as parameter the signal to be checked. Only one evhSIGNAL can be associated to a signal.

~evhSIGNAL();

Destructor. If necessary, deinstall the signal handler. If an instance of evhSIGNAL is deleted, no more callbacks are executed for that signal.

ccsCOMPL_STAT Install();

Installs the signal handler: from this point on a command is generated whenever the signal is received.

ccsCOMPL_STAT Deinstall();

Deinstalls the signal handler: no more commands are generated.

int Signal() const;

Returns the signal associated to this evhSIGNAL.

vltLOGICAL Status() const;

Returns ccsTRUE if the signal handler is currently installed, ccsFALSE if not.

Whenever an installed signal is received, an event is generated with the following key:

MsgType - msgTYPE_COMMAND

Command - evhSIGNAL_CMD

CommandId - signal received

and the callbacks associated to this key are processed. The message buffer contains more detailed informations about the signal. In the case of SIGCHLD it contains an integer with the PID of the terminated child process.

If we consider the previous example, the following sample code installs a callback processed when the SIGCHLD is received:

// ===================================================

// Create the instance of evhSIGNAL

evhSIGNAL *sig = new evhSIGNAL(SIGCHLD);

// Attach the signal

sig->Install();

// Prepares the key

evhMSG_TYPE_KEY key(msgTYPE_COMMAND, // the type is command

evhSIGNAL_CMD, // the command is evhSIGNAL_CMD

SIGCHLD); // the command id is the value

// for SIGCHLD

// Prepares the callback

evhOBJ_CALLBACK cb(this,(evhCB_METHOD)&APPLICATION::SigchldCB);

// Attaches the callback

evhHandler->AddCallback(key,cb);

// ===================================================

If the SIGCHLD signal is received, the callback function SigchldCB() is executed. It could have the following structure:

// ===================================================

evhCB_COMPL_STAT APPLICATION::SigchldCB(msgMESSAGE &msg, void *udata)

{

int *pid = (int*)(msg.Buffer());

printf("Received a SIGCHLD signal: the xclock with pid %d is died\n", *pid);

return evhCB_NO_DELETE;

}

// ===================================================

2.12 RESERVED COMMAND NAMES

Inside the EVH Tool Kit some special command names have been used for internally defined commands. All these command names are placed in defines with the format:

evh[cmd name]_CMD

The defines are placed in the include file where the command is defined.

Currently the reserved command names are:

Macro
Value
Include
Interpretation
Standard commands:
evhEXIT_CMD
evhINIT_CMD
evhPING_CMD
evhSTATE_CMD
evhSTATUS_CMD
evhSTANDBY_CMD
evhONLINE_CMD
evhOFF_CMD
evhSELFTST_CMD
evhTEST_CMD
evhSIMULAT_CMD
evhSTOPSIM_CMD
evhVERBOSE_CMD
evhVERSION_CMD
evhSTOP_CMD
evhKILL_CMD
evhBREAK_CMD

EXIT
INIT
PING
STATE
STATUS
STANDBY
ONLINE
OFF
SELFTST
TEST
SIMULAT
STOPSIM
VERBOSE
VERSION
STOP
KILL
BREAK

evhTASK.h


evhSTD_COMMANDS.h
Standard commands that must be supported by any VLT application.
evhFILE_IO_CMD
EVHFDIO
evhFILEIO.h
A file descriptor is ready.
evhSIGNAL_CMD
EVHSIG
evhSIGNAL.h
Received a signal.
evhTIMEOUT_CMD
TIMEOUT
evhTIMEOUT.h
Received a timeout.

The names of the test programs are listed in parenthesis; they can be found in the ~evh/test/ directory. They can be used as basis for new applications made with EVH.

2.13 TRACING

The following information can be logged automatically by an evh application :

· Monitoring of callbacks execution time.
· Logging of every message received.
· Logging of every message sended.

2.13.1 Callbacks execution time.

The monitoring of the callbacks execution time sends, to the standard log, a warning message every time a callback execution time has been greater than a default of 100 ms. The activation of this feature is done by any of the two following actions:

· Set of the environment variable EVH_CBMON to TRUE , previous application execution.
· Call the evhHANDLER::CbMonitorTrace() function before evhHANDLER::MainLoop().

The log message displayed is evhERR_CBTIMEMONITOR and has the following format:

"callback execution time = 120 milliseconds. Default maximum time = 100"

2.13.2 Received messages.

The logging of every message received, sends to the standard log, an informational string with the details of message ( type, environment, etc. ) being received by the evh application. The activation of this feature is done by any of the two following actions:

· Set of the environment variable EVH_TRACE to TRUE , previous application execution.
· Call the evhHANDLER::Trace() function before evhHANDLER::MainLoop().

2.13.3 Sended messages.

The logging of every message sended, sends, to the standard log, an informational string with the details of the message ( type, environment, etc. ) being send by the evh application. Since the messages are actually sended by classes of the eccs module, the activation of this feature is done by :

· Set of the environment variable ECCS_TRACE to TRUE , previous application execution.

2.14 CHECKING OF INCOMING COMMANDS

The VLT software conventions state that every command sent by a process to another one MUST BE CHECKED ON THE SENDING SIDE.

Unfortunately this statement of principle is not enough to enforce the rule.

As a consequence programs that correctly assume that incoming commands have been already checked can crash when they receive not checked and malformed commands.

For debugging purposes, it is then possible to activate command checking for received commands at evhHANDLER level. When the feature is active any incoming command is checked

The activation of this feature is done by any of the two following actions:

· Set of the environment variable EVH_CHECKCMD to TRUE , previous application execution.
· Call the evhHANDLER::CheckCmd() function before entering MainLoop().

This feature, obviously, requires that a CDT for the process is available and cannot be used for formatted binary commands.

2.15 DUMMY PROCESS

The evhDummy program (and its companion control panel evhDummyControl) are provided in order to help in developing test and simulation suites.

The evhDummy program is a server that can dynamically configure to receive commands and send back replies. This means that the commands it can handle and the type, values and time for the reply can be dynamically changed using a set of special commands (the initial configuration is read by a configuration file).

This makes easy to test the interface between a program under test and other processes, simulated via instances of evhDummy. A schell script or the control pannel can be used to dynamically configure the dummy to react in a different way to the commands it receives from the program under test.

More complex needs can be accomplished in the present status by taking the source code of the evhDummy and modifying it adding new callbacks for commands that need a special treatement, like writing in the database (for an example, look at the trkwsDummy.C program used in the TCS Simulation Package - tcssim - to simulate a tracking LCU). In a next version of the class library, the evhDummy's services will be provided by a dedicated class making easier the development of simulation programs.

2.16 EXAMPLES

In chapter 7 a number of example programs are listed; the examples are as follows:

· Receive command and send back reply (evhSample_1.C).
· Sending off commands and receiving replies (evhSample_2.C/evhSample_2bis.C).
· Handling of a timeout (evhSample_3.C).
· Usage of the EVH Timer Feature (evhSample_4.C).
· Usage of the class evhSYNC to handle conditions based over multiple events (evhSample_5.C).
· Handling of DB Events (evhSample_6.C).
· Handling of file I/O (using select feature) (evhSample_7.C).
· Handling of UNIX signals (evhSample_8.C).
· Dynamic handling of commands evhSample_9.C).

2.17 CAVEATS

This section contains solutions to common problems encountered when using ECCS library.

It will be extended taking into account SPRs and frequently asked questions

2.17.1 Local copy of messages

When receiving a CCS Message with an EVH application, a local copy of the message is done. This means that for high performance applications sending very big messages, like data transfer from e.g. a detector LCU to a WS, the application to receive the data should probably be implemented without using the EVH Tool Kit, since it may be desirable e.g. to save the buffers with detector data in files as they come in, without having the overhead of the memory copy.

2.17.2 Strange compiler error: no compatible member...(spr. 960208)

A strange compiler error:

no compatible member functions named `xxxxCB'

is generated when manipulating from a subclass a callback defined in a parent class.

If, for example, we have the following classes:

class BASE

{

virtual evhCB_COMPL_STAT InitCB(msgMESSAGE &msg, void *udata);

};

class DERIVED : public BASE

{

};

In the following constructor for DERIVED we get an error:

DERIVED::DERIVED()

{

......

evhHandler->AddCallback(key.Command(evhONLINE_CMD),

cb.Proc((evhCB_METHOD)&APPLICATION::InitCB));

......

}

A WORK AROUND IS:

replace the line that gives the problem with the following one

evhHandler->AddCallback(key.Command(evhONLINE_CMD),

cb.Proc((evhCB_METHOD)&APPLICATION::prsAH_MAIN_TASK::InitCB));

i.e. add an explicit scope resolution to the method pointer.

Reason for this behaviour of the compiler is not clear and I think that it is problem of the compiler and not of evh itself.

2.17.3 Command aliases

CCS implements aliasing when sending commands.

This means that when a command is sent with checking active, the command name needs not to be the actual 7 bytes long official name of the command, but can also be any of the valid alias names defined on the CDT (see the CCS user manual for details[1]).

If the command is not checked no alias substitution is performed.

It is anyway important to notice that the alias substitution is performed on the sender side before the command is actually sent. The receiver sees always and only the 7 bytes official command name.

This means also that the receiver send replies including in the message body the official command name and NOT the original command name.

This can cause confusion with programs based on the evhHANDLER class since callbacks installed on replies to a command are identified based on the couple (command name, command Id). This is in general not an important problem, since aliases are mainly meant as an help for interactive users.

For the time beeing it is suggested to avoid using alias names for commands sent by evh applications. In the future if a safe, clean and compatible solution is found it will be implemented.



Quadralay Corporation
http://www.webworks.com
Voice: (512) 719-3399
Fax: (512) 719-3606
sales@webworks.com
TOC PREV NEXT INDEX