TOC PREV NEXT INDEX

Put your logo here!


2 USER'S GUIDE

2.1 OVERVIEW

The purpose of the Extended CCS library functions is to simplify the usage of the CCS functionalities. Extended CCS is programmed in C++ and is a layer on top of CCS. The usage of the Extended CCS functions does not require any particular knowledge of C++ programming, but for exploiting the advantages of the library to a high degree, a least a fundamental knowledge is an advantage.

Since the Extended CCS function map corresponding features of the standard CCS consult [1] for more information about the features and purpose of the CCS.

2.2 USAGE OF EXTENDED CCS

Applications using the Extended CCS library functions, 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").

All the functions necessary for using Extended CCS are in the eccs library, that must be linked with the code by specifying it in the makefile. Support libraries that must be linked with the code are CCS, fnd, stdc++, g++, iberty.

From the application code itself, only the include file ECCS.h should be specified. This includes all definitions for CCS and Extended CCS.

Look in section 2.6 for caveats and problems mixing C and C++ code.

2.3 USING THE EXTENDED CCS FUNCTIONS

The Extended CCS functions are mappings of the corresponding CCS function calls. Whenever possible the Extended CCS functions have the same name as used by standard CCS calls. The compiler will chose the right one automatically checking what parameters have been used to call the function.

Most of the interfaces to Extended CCS functions uses default values for the input parameters, which means that (under certain conditions) it is possible to take the default value by leaving out the parameter from the function call. An example of this is as follows:

ccsCOMPL_STAT dbRead(DBTYPE *value,

const dbSYMADDRESS pointName,

const char *attrName = dbEMPTY,

ccsERROR *error = NULL)

In this case it would be possible to call the function without specifying e.g. the error parameter. A global definition of the error structure would be used. This is implemented using the overloading feature of C++.

2.4 ERROR HANDLING

As mentioned in the previous example, Extended CCS functions which may encounter an error can use a global definition of the CCS Error structure, named *stdErr. If nothing else is specified in the Extended CCS function calls, this error structure is used. Only in cases where it is necessary more than one error stack, additional error stacks should be defined.

2.5 EXTENDED CCS FEATURES

The Extended CCS functions and definitions supported in this version of the library, falls in 6 categories. Only in case of severe changes compared to the standard CCS call a remark is made. Examples of usage of some of the functions are given later in the chapter. The features of the individual category are described in the following.

2.5.1 Standard Features

New macros:

TRUE (1)/FALSE (0)

IN/OUT:

Used for prototyping of functions. It is possible to write and IN or OUT in the function interface for each parameter without this having any influence.

__FILE_LINE__:

This macro __FILE_LINE__ produces a string in the format: "FileName:Line" with the name and line of the file where it has been called. It is useful in error messages.

USE(<variable>):

The macro USE(<variable>) can be used to prevent the GCC compiler from complaining about an unused variable. Specially with objects, can happens that the compiler thinks that a variable is not used and issues a warning. This macro prevents the warning.

Function prototypes:

ccsInit():

Initialize the process in the CCS resources for the process.

ccsExit ():

Release the CCS resources for the process.

ccsGetMyProcId():

Request the RTAP process ID for an application.

ccsOpenFile():

Open file located in standard search path.

2.5.2 Message System

The Extended CCS functions for the message system are methods of a C++ class which also contains the various data received and sent from the application. This both eases and secures the usage of the message system calls. Buffer allocations are completely automatic and optimized.

The class providing the CCS message system features is called: msgMESSAGE. An instance of this class is declared in the application in the standard way:

msgMESSAGE myMessage;

The items of the data structure describing the command are not accessible directly, but only through interface functions. This allow reliable allocation of buffers and checks for correct values of parameters. This means that every parameter has a function (method) used to set it and one used to read it.

Let us make some examples:

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

// Allocation of a message data structure:

msgMESSAGE myMessage;

// This structure will be used to send a command, so I

// have to set the comman name, dest, environment, dest process

myMessage.Command("RUN");

myMessage.Destenv("wte16");

myMessage.Procname("MYPROC");

myMessage.Buffer("Data sent");

// Finally the command can be sent

myMessage.SendCommand();

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

The name of the function to set a parameter is the same as the parameter itself, with the first letter capitalized. These functions can be chained to set all the parameters in a single call:

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

// Chained settings: equivalent to the previous example

myMessage.Command("RUN").Destenv("wte16").Procname("MYPROC");

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

For all the remaining parameters, the previously set values (or the default ones) are used.

Upon reception of a message, parameters can be read. Parameters are safe inside the data structure up to when it is explicitly reused (unlike in standard CCS).

After a receive, the value of the parameters can be retrieved with the same function name as a hook:

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

// Allocation of a message data structure:

msgMESSAGE myMessage;

// Receive a message

myMessage.Receive().

// Printf the contents of the message

printf("Command %s, buffer: %s\n",

myMessage.Command(), myMessage.Buffer());

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

By default the command ID is automatically set to a new unique value every time a command is sent using SendCommand(). If the command ID is set explicitly to a value, this feature is turned off. To turn it on, set the command ID to 0 with myMessage.CommandId(0): from that point on, the next command sent will have a new unique command ID.

What follows is the public interface of the class, i.e. the set of methods that can be used (a part from the constructor and destructor):

class msgMESSAGE: public msgRAW_MESSAGE {

public:

ccsCOMPL_STAT Parse(const msgHEADER *msg);

ccsCOMPL_STAT Receive(msgTIMEOUT timeout = msgNO_TIMEOUT,

const msgRECEIVEFILTER *filter = NULL);

ccsCOMPL_STAT SendCommand();

ccsCOMPL_STAT SendCommand(msgCHECKFLAG flag);

ccsCOMPL_STAT SendReply(ccsERROR *error = NULL );

ccsCOMPL_STAT SendReply(const char *newBuffer,

msgLENGTH newBuflen=0,

vltLOGICAL lastReply=ccsTRUE);

ccsCOMPL_STAT SendReply(const ccsERROR &errmsg);

//Methods to access safely the values of parameters

vltUINT8 Type() const;

msgMESSAGE &Type(vltUINT8 newType);

const char *TypeName() const;

const char *Command() const;

msgMESSAGE &Command(const msgCMD newCommand);

msgCMDID CommandId() const;

msgMESSAGE &CommandId(msgCMDID newCommandId);

const char *Destenv() const;

msgMESSAGE &Destenv(const ccsENVNAME newDestenv);

const char *Destproc() const;

msgMESSAGE &Destproc(const ccsPROCNAME newDestProc);

const msgPROCESSID &OrgId() const;

msgMESSAGE &OrgId(const msgPROCESSID &newOrgId);

vltLOGICAL LastReply() const;

msgMESSAGE &LastReply(vltLOGICAL newLastReply);

const char *Buffer() const;

msgLENGTH Buflen() const;

msgMESSAGE &Buffer(const char *newBuffer,

msgLENGTH newBuflen=0);

ccsERROR &Errmsg();

msgMESSAGE &Errmsg(const ccsERROR &newErrmsg);

msgCHECKFLAG Check() const;

msgMESSAGE &Check(msgCHECKFLAG flag);

static ccsCOMPL_STAT GlobalCheckOn(msgCHECKFLAG flag);

static ccsCOMPL_STAT GlobalCheckOff();

msgMESSAGE &operator =(const msgMESSAGE &source);

static ccsCOMPL_STAT Trace(vltLOGICAL mode = TRUE);

};

The meaning and usage of most of the methods is intuitive. We describe here only the ones with special behavior. Look at the man page of the class for more details.

The method Parse() gets a pointer to a standard CCS msgHEADER (obtained for example from a call to msgRecvMsg() and parses the message filling the fields in the msgMESSAGE instance.

The method Receive() receives the next message from the CCS message queue and does the Parse() all in one shot.

The method

msgMESSAGE &Buffer(const char *newBuffer, msgLENGTH newBuflen=0);

is used to set the message buffer. If the message buffer is an ASCII string, it is not necessary to pass the message buffer length as second parameter: it is automatically calculated. If the message buffer is binary it is necessary to pass also the length. The buffer is allocated automatically inside the class.

The Check() methods set or return the bitmask for checking when sending commands.

The flag can be set with one of the following values: msgNO_CHECK (def), msgCHECK_CMD and msgCHECK_ALL. The value of the internal flag is overriden both by an explicit set in the SendCommand(msgCHECKFLAG) method and by the class global setting, using the static method GlobalCheckOn(msgCHECKFLAG). If none of these is set, the internal value is used.

The two static methods GlobalCheckOn() and GlobalCheckOff() are used to control command checking at application level. If global checking is turned on using the method GlobalCheckOn(msgCHECKFLAG) all commands sent through eccs functions and class methods are checked according to the value of the flag (msgNO_CHECK, msgCHECK_CMD or msgCHECK_ALL), unless the check flag is explicitly set in the SendCommand() call. If GlobalCheckOff() is called, commands are checked according to the value of their own internal flag.

The static method Trace() is used for debugging purposes. If mode = TRUE, activates a tracing of all the messages received and sent via eccs routines. A description of all messages received/sent including message type, command name and command id is logged using the logData() function. Every message class (msgMESSAGE, evtEVENT_MSG, alrmMESSAGE...) logs specific detailed information (look at the corresponding man page for details).

Typical examples of the usage of this class are:

Receiving a command and sending a reply to it:

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

// (error handling is not shown)

// Declare the message variable

msgMESSAGE myMessage;

// Receive the command (by default with no timeout and no filtering)

myMessage.Receive();

// Check for the command

if( strcmp(myMessage.Command(), "MYCMD) != 0) return;

// Send the last reply back to the sender of the message

// setting at the same time the message buffer to be send

myMessage.SendReply("This is my reply");

// The previous line is equivalent to the following two lines of code

myMessage.Buffer("This is my reply").LastReply(ccsTRUE);

myMessage.SendReply();

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

- Sending a command and receiving the reply:

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

// (error handling is not shown)

// Declare the message variable

msgMESSAGE myMessage;

// Set the command parameters

myMessage.Command("MYCMD");

myMessage.Destenv("wte16");

myMessage.Procname("MYPROC");

myMessage.Buffer("Data sent");

// Send the command

myMessage.SendCommand();

// Receive the reply

int flag = ccsFALSE;

while(flag == ccsFALSE)

{

// (checking of the specific reply is not shown)

myMessage.Receive();

flag = myMessage.LastReply();

printf("Reply buffer: %s\n", myMessage.Buffer());

}

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

The class msgMESSAGE is derived by the pure virtual class msgRAW_MESSAGE. This is the base class to be used for all the messages implemented, when they cannot be reduced to commands or replies. The CCS can produce messages with a special format, not related to commands or replies. Database events are a typical example.

Any CCS message sent in the VLT system can be uniquelly identified by the triplet

(message type, command name, command id)

where the meaning of "command name" and "command id" has to be specified.

For this reason any class used to transport messages must be a subclass of msgRAW_MESSAGE and must provide at least 3 access methods for these data members. For more details on this class, look at the implementation of database events and of the event handling system.

This module provides also a set of plain functions to cover the standard CCS functions, adding default values and support for instances of msgMESSAGE.

2.5.3 Database Access Routines

dbOpen()

Establishes a connection to a database

dbExit():

Close a specific database connection or all.

dbRead()/dbWrite():

Read/write the value of an attribute. Note that the corresponding standard CCS calls are dbReadSymbolic() and dbReadSymbolic().

Note that it is also possible to access tables and vectors using this set of functions. The type of attribute to be read is defined by the type used for DBTYPE parameter while calling the function. It can be a pointer to any of the following types:

vltINT16, vltUINT16, vltINT32, vltUINT32, vltDOUBLE, vltFLOAT, vltPOLAR and vltRECTANGULAR.

It is also possible to read strings arrays and buffers.

2.5.4 Database access classes

These classes provide a transparent access to database attributes as if they where simple internal variables. This means that read and write from the database are done automatically whenever necessary.

2.5.4.1 Basic types

The base class eccsDB_ATTR provide the basic implementation and can be used to access any type. The subclasses implement the specific data types supported by the database and provide cast operators to the access completely transparent.

The following classes are provided to access attributes:

eccsDB_ATTR

eccsDB_LOGICAL

eccsDB_INT8

eccsDB_UINT8

eccsDB_INT16

eccsDB_UINT16

eccsDB_INT32

eccsDB_UINT32

eccsDB_FLOAT

eccsDB_DOUBLE

eccsDB_STRING

The constructor of the classes gets as parameters the point name and attribute name to identify the attribute on the database to wich the instance must be linked. If the attribute name is not given, it is assumed to be part of the point name.

The base class eccsDB_ATTR provides two generic methods, GetValue(buffer,bsize=0) and SetValue(buffer,bsize=0) to read the attribute's content from the database or to write it in the database from a memory buffer.

It can be used to access any kind of database attribute (SCALARS, TABLES, VECTORS and sub-ranges in TABLES and VECTORS), provided that a buffer of the correct size is passed to the GetValue() and SetValue() methods.

If the buffer size is passed at the second (optional) parameter, it is used to check that the given buffer is of the correct size to contain all the data to be accedd from the database. The parameter is optional for backward compatibility reasons, but it is very good practice to use it, in parti

The subclasses can be used to access SCALAR attributes or single elements inside TABLES and VECTORS.

They provide automatic conversion operators.

For example the class eccsDB_LOGICAL provides the operators:

eccsDB_LOGICAL &operator=(vltLOGICAL newValue);

operator vltLOGICAL();

to assign the database attribute to/from a standard vltLOGICAL.

In the following example:

eccsDB_LOGICAL dbAttribute(":Appl_data:myPoint.logical");

vltLOGIVAL log = TRUE;

dbATTRIBUTE = log; // Write on database

log = dbATTRIBUTE; // Read from database

The variable `dbAttribute' is associated to the database attribute ":Appl_data:myPoint.logical".

Then the value of the vltLOGICAL variable `log' is assigned to it (and thus written on the database)

Then the value of the database attribute is read back from the database in the `log' variable.

2.5.4.2 Database vectors

In order to provide access to database vectors, ECCS implements the following classes:

eccsDB_VECTOR_vltINT8
eccsDB_VECTOR_vltUINT8
eccsDB_VECTOR_vltINT16
eccsDB_VECTOR_vltUINT16
eccsDB_VECTOR_vltINT32
eccsDB_VECTOR_vltUINT32
eccsDB_VECTOR_vltLOGICAL
eccsDB_VECTOR_vltDOUBLE
eccsDB_VECTOR_vltFLOAT

eccsDB_VECTOR_vltBYTES4
eccsDB_VECTOR_vltBYTES8
eccsDB_VECTOR_vltBYTES16
eccsDB_VECTOR_vltBYTES12
eccsDB_VECTOR_vltBYTES16
eccsDB_VECTOR_vltBYTES20
eccsDB_VECTOR_vltBYTES32
eccsDB_VECTOR_vltBYTES48
eccsDB_VECTOR_vltBYTES64
eccsDB_VECTOR_vltBYTES80
eccsDB_VECTOR_vltBYTES128
eccsDB_VECTOR_vltBYTES256

that correspond to vectors of the specified types.

From an implementation point of view, access to database vectors is provided by the template class:

eccsDB_VECTOR

All the basic vector types are implemented based on this template and hidden through macros. New vector types can be defined using the template class, but this should not be necessary.

When declaring an instance of a vector, it is only necessary to give the symbolic address of the database attribute containing the vector. When the vector is accessed for the first time, the vector characteristics are read from the database so that it is not necessary to know in advance the size of the vector. A special constructor allows to access a column in a database table as if it was a vector. This is the only supported way to access table columns as vectors.

No check is performed at instantiation time, but only when the vector is accessed for the first time.

For performance considerations, only very limited checks are performed on the given symbolic addresses for database attributes. In most cases the errors logged for malformed addresses will clearly identify the problem, but in some case the errors will be not clear or unexpected results could happen. This is particularly true when trying to exploit non supported expressions, that could only partially work, like when trying to access a table column implicitly, with the basic constructor for vectors, putting the column name directly in the attribute's symbolic address.

The following lines or code are sample database array declarations:

eccsDB_VECTOR_vltINT32 myVector(":PARAMS:VECTORS.vector_int32");

eccsDB_VECTOR_vltBYTES16 myVector(":PARAMS:VECTORS.vector_string32");

The read/write access to the vector can be accomplished via the SetValue(buffer) GetValue(buffer) interface (as in eccsDB_ATTR) to access the whole vector with a single call or via overloaded implementations SetValue(index,buffer), GetValue(index,buffer) to access one element of the array at the time.

The subscript operator [] is implemented for vectors to access one element for read or write with the normal C syntax for array assignement.

The subscripting of vectors of vltBYTESnnn IS allowed (since ver.1.67) but it requires some cautions.

In particular vltBYTESnnn ARE NOT equivalent to char* or unsigned char* and proper types must always be used, introducing casts when necessary. Check the man pages for details and examples.

The following piece of code shows typical database array access:

// The necessary include file

#include "eccsDB_VECTOR.h"

........ some code ......

ccsCOMPL_STAT st;

// Declares the database array

eccsDB_VECTOR_vltINT32 myvector(":PARAMS:VECTORS.vector_int32");

// Declare a standard C memory array to access the whole database array in just one go

// The method RecordCnt() is used to read from database the size of the array

// in order to allocate enough space for it
vltINT32 *array = new vltINT32[myvector.RecordCnt()];

// Read the whole vector to memory.
st = vector.GetValue(array);

// Display its values. The loop size is calculated on the database array size
for(i =0; i<myvector.RecordCnt(); i++)
cout << " " << array[i] << endl;

// Set new values in memory
for(i =0; i<RecordCnt(); i++)
array[i] = i;

// Save the modified array to the database vector in one go

st = vector.SetValue(array);

// Write vector values element by element directly to the database
for(i =0; i<mytable.RecordCnt(); i++)
vector[i] = i;

// Read vector values element by element directly from the database
for(i =0; i<mytable.RecordCnt(); i++)

cout << " " << vector[i] << endl;;

2.5.4.3 Database tables

Since it does not exist a predefined structure for a record of a table, it is not possible to provide pre-defined classes as in the case of vectors (a vector can contain only one of the basic database types, while the record structure of a table is user-defined).

As a consequence tables are implemented using a template class (i.e. a parametrized class) called eccsDB_TABLE.

Whenever an instance of eccsDB_TABLE is created, it is necessary to specify a support class containing the definition of the record structure for the specific table. Assuming for example that we have the class MY_TABLE_RECORD, we declare a database table based on this record format using the following C++ syntax for templates instantiation:

eccsDB_TABLE<MY_TABLE_RECORD> myTable(":Appl_data:tables.myTable");

This defines dynamically a new class called eccsDB_TABLE<MY_TABLE_RECORD> based on the record structure MY_TABLE_RECORD.

The record definition class MY_TABLE_RECORD must be a subclass of eccsDB_TABLE_RECORD and must implement the functions to pack and unpack the values to/from a buffer from/to the class data member associated with each field of the table record.

Usually the provider of a database table should provide also the record definition class to make easy its access.

The class eccsDB_TABLE provides four generic methods to access its records: GetValue( buffer ) , GetValue( index, buffer ), SetValue( buffer ), SetValue( index, buffer ) to read/write from/to full table or only the from/to a record number <index>.

The = and [] operators are overloaded allowing the read/write by record index and assignement to be done using the normal C syntax to read/write one element of the array

It is possible to assign a table type class to another instance of the same class (assignement operators are provided): this correspond to assign the value and is NOT a copy of the objects. The value of the attribute for the object on the right side of the assignement is copied in the attribute for the object on the left side. It is not allowed to assign between different table types classes.

In the case of database records where the first field in the record format is a string (dbBYTESxx), it is also possible to access records by name, i.e. by specifying a string to be searched for exact matching in the first field of the records of the table, instead of giving a record index. More details on access by name are in the man page of the eccsDB_TABLE class.

The following example should make clear the concept underlying table access.

Assume we need to access a table with the following record format:

(vltUINT32 uint32, vltBYTES20 bytes20)

The first thing we need is the record definition class:


#include "eccsDB_TABLE.h"

class myTABLE_CLASS : public eccsDB_TABLE_RECORD {
public:
vltUINT32 uint32;
vltBYTES20 bytes20;

ccsCOMPL_STAT Unpack( void *buffer )

{

memcpy( &uint32, buffer,sizeof(vltUINT32) );

buffer = buffer+sizef(vltUINT32);

memcpy( &bytes20, buffer,sizeof(vltBYTES20));
return SUCESS;
}

ccsCOMPL_STAT Pack( void *buffer )

{

int size;
dbFillBuf( (char**)&buffer, (char*)&uint32 ,&size, dbUINT32 );
dbFillBuf( (char**)&buffer, (char*)&bytes20 ,&size, dbBYTES20 );
return(SUCCESS);
}
};

This class defines a structure containing the two items and the Unpack() and Pack() methods.

These two methods are used to copy data from/to the database to/from the structure. This is due to the fact that data read from the database is placed in a packed buffer not directly suitable for a C structure, due to alignement problems (look at CCS user manual[1] for more details).

The eccsDB_TABLE_RECORD class defines a standard interface to deal with this general problem and should always be used. The provider of a database table should always provide an interface class to access its records from C++.

At this point it is possible to instantiate a table class to access the records of our table:

// Creates an instance of the table
eccsDB_TABLE<myTABLE_CLASS> mytable("<alias>TABLES.mytable");

// Allocate the memory array for the table.
myTABLE_CLASS *array = new myTABLE_CLASS[mytable.RecordCnt()];

// Read the whole table in one go
st = mytable.GetValue(array);

// Display the values readed.
// Notice that values are properly unpacked and easily available
// in the structure
for ( i=0;i<mytable.RecordCnt();i++)
{
cout << "uint32 : " << array[i].uint32 << "\n";
cout << "bytes20 : " << array[i].bytes20 << "\n";
}
cout << endl ;

// Modify values in memory
for ( i=0;i<mytable.RecordCnt();i++)
{
array[i].uint32 = 99;
strcpy( (char*)array[i].bytes20, (char*)"Test String");
}

// Rewrite whole table with changes
st = mytable.SetValue(table_array);

// Record by record access
// Reads every record one by one an assign them to array
for ( i=0;i<mytable.RecordCnt();i++)
array[i] = mytable[i];

// Modify record number 5
array[5].uint32 = 5;
strcpy( (char*)array[5].bytes20, (char*)"This is record 5");

// Write record number 5 direct to the database table
mytable[5] = array[5];

2.5.5 Error System

The ECCS interface to the error system provides the following features:

· a small set of functions and macros to make the error handling easier in a set of commmon situations.
· mapping of CCS functions to use the ECCS default error stack.
· the new class eccsERROR (derived from the structure ccsERROR) to handle error stacks using methods.
· the new class eccsERROR_CLASS used to provide CCS compliant error handling in user defined classes.

2.5.5.1 Convenience functions and macros

The following macros and functions try to integrate in a single instruction the handling for common error patterns.

These macros help reducing the code to be written for error handling.

Since error handling inside classes derived from eccsERRO_CLASS requires a different treatment, two sets of macros are provided, one to be used inside methods of these classes, one to be used elsewhere.

As a general naming rule, the "Class" prefix is for macros to be used inside a method of a class derived from eccsERROR_CLASS, the "Return" prefix means that the macro forces also a return of the current function with FAILURE (ccsCOMPL_STAT return type is assumed).

errFlag():

If the value of the "condition" parameter is ccsFAILURE, the "flag" parameter is set to FAILURE, otherwise it keeps its current value. This macro is usefull when it is necessary to execute a number of statements and check only at the end if any of them is failed, no regard which. "flag" is set to SUCCESS before the sequence of calls and "condition" is the return value after any of the calls. After all the calls, "flag" is FAILURE if at least one is failed.

errAddGeneric():

errClassAddGeneric():

If (stat == FAILURE) a generic message (with name of the file and line) is added to the default error stack. This is useful when the complete description of the error is already on the stack and nothing useful can be added at this level.

errReturnGeneric():

errClassReturnGeneric():

As errAddGeneric() but also returns to the caller with the present status (SUCCESS or FAILURE).

errExitGeneric():

errClassExitGeneric():

As errAddGeneric() but closes also the error stack and exits.

errReturnNullPtr():

errReturnNullPtr():

errClassNullPtr():

errClassReturnNullPtr():

Checks if the given variable name contains a NULL pointer. If so, generates an error and return. It is useful in many cases where function parameters must be tested.

The CCS error functions have been mapped to use the ECCS default error stack. All the following functions have the same behavior and syntax found in CCS, except from the fact that they use the default error stack (and so the ccsERROR* parameter has been dropped) and the not-used "format" parameter in the Add functions has been dropped.

ccsCOMPL_STAT errAdd(ccsMODULEID moduleId, vltINT16 errorNumber,

ccsLOC_ID locId, ...);

ccsCOMPL_STAT errAdd_v(ccsMODULEID moduleId, vltINT16 errorNumber,

ccsLOC_ID locId, va_list va_parList);

ccsCOMPL_STAT errSysAdd(ccsMODULEID moduleId, vltINT16 errorNumber,

ccsLOC_ID locId,char *sysModule=modRTAP, ...);

ccsCOMPL_STAT errSysAdd_v(ccsMODULEID moduleId, vltINT16 errorNumber,

ccsLOC_ID locId, char *sysModule, va_list va_parList);

ccsCOMPL_STAT errCloseStack();

ccsCOMPL_STAT errResetStack();

ccsCOMPL_STAT errPrint();

ccsCOMPL_STAT errDisplay(vltLOGICAL syncFlag=FALSE);

ccsCOMPL_STAT errIsReason(const ccsMODULEID moduleId,

vltINT16 errorNumber,

vltLOGICAL *errFound,

ccsSTACK_ELEM **errDescription = NULL);

2.5.5.2 Class eccsERROR

The class eccsERROR is derived from the standard structure ccsERROR. It provides all the handling of an error stack from within a class, implementing proper methods. It also takes care of the default error stack.

From a user's point of view an object of the class eccsERROR is equivalent to an instance of ccsERROR and can be used whenever a ccsERROR is used, in a fully transparent way.

The implementation of the eccsERROR class provides the following advantages:

- Any time an instance of eccsERROR is deleted (or exit out of scope), it is automatically checked and the stack is closed if it contains errors.

- The defaults error stack is automatically closed when the application exits.

- It is possible to derive new classes to handle specific error conditions and have them installed as the default handler.

- Code is more readable.

What follows is the public interface of the class:

struct eccsERROR: public ccsERROR

{

eccsERROR();

~eccsERROR();

ccsCOMPL_STAT Add(ccsMODULEID moduleId, vltINT16 errorNumber,

ccsLOC_ID locId, ...);

ccsCOMPL_STAT Add_v(ccsMODULEID moduleId, vltINT16 errorNumber,

ccsLOC_ID locId, va_list va_parList);

ccsCOMPL_STAT SysAdd(ccsMODULEID moduleId, vltINT16 errorNumber,

ccsLOC_ID locId, char *sysModule=modRTAP, ...);

ccsCOMPL_STAT SysAdd_v(ccsMODULEID moduleId,vltINT16 errorNumber,

ccsLOC_ID locId, char *sysModule,

va_list va_parList);

ccsCOMPL_STAT Close();

ccsCOMPL_STAT Display(vltLOGICAL syncFlag=FALSE);

ccsCOMPL_STAT Print();

ccsCOMPL_STAT Reset();

ccsCOMPL_STAT IsReason(const ccsMODULEID moduleId,

vltINT16 errorNumber,

vltLOGICAL *errFound,

ccsSTACK_ELEM **errDescription = NULL);

vltLOGICAL IsReason(const ccsMODULEID moduleId,

vltINT16 errorNumber);

eccsERROR &operator =(const ccsERROR &e);

static void DefErrorStack(eccsERROR *err = NULL);

};

Examples:

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

// Calling an ECCS function and handling errors with the

// default error stack

ccsCOMPL_STAT status;

status = ccsInit(argv[0]);

if(status == FAILURE)

{

// Notice that the stack is closed automatically on exit

// and I do not need to close it explicitly

stdErr->Add(....parms....);

exit(1);

}

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

// Calling a standard CCS function and handling errors with the

// default error stack

ccsCOMPL_STAT status;

status = dbMultiRead(listid,stdErr);

if(status == FAILURE)

{

stdErr->Add(....parms....);

// This line is equivalent to the following lines:

// errAdd(...params....); // uses estdErr by default

// errAdd(stdErr, ...params (with format)... );

}

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

2.5.5.3 Class eccsERROR_CLASS

All the classes that want to provide error handling must be derived from the class eccsERROR_CLASS. Since not all the methods in a class can return a ccsCOMPL_STAT, it is neccessary to have another way of querying an instance of a class for the completion status (SUCCESS of FAILURE) of the last method called. Typical examples are constructors and access methods (methods to retrieve the value of a data member).

What follows are the public and protected methods of the class, i.e. the set of methods that can be used from application programs (public interface) and from within methods of subclasses (protected interface):

class eccsERROR_CLASS {

public:

eccsERROR_CLASS(eccsERROR *s = stdErr);

ccsCOMPL_STAT ErrStatus();

eccsERROR &ErrStack();

ccsCOMPL_STAT ErrStackClose();

ccsCOMPL_STAT ErrStackReset();

ccsCOMPL_STAT ObjStatus();

operator ccsCOMPL_STAT();

operator eccsERROR &();

protected:

eccsERROR_CLASS &ErrStatus(ccsCOMPL_STAT s);

eccsERROR_CLASS &ErrReset();

eccsERROR_CLASS &ObjStatus(ccsCOMPL_STAT s);

eccsERROR_CLASS &operator =(const ccsCOMPL_STAT s);

ccsCOMPL_STAT ErrAdd(ccsMODULEID moduleId, vltINT16 errorNumber,

ccsLOC_ID locId, ...);

ccsCOMPL_STAT ErrAdd_v(ccsMODULEID moduleId, vltINT16 errorNumber,

ccsLOC_ID locId, va_list va_parList);

ccsCOMPL_STAT ErrSysAdd(ccsMODULEID moduleId, vltINT16 errorNumber,

ccsLOC_ID locId, char *sysModule=modRTAP, ...);

ccsCOMPL_STAT ErrSysAdd_v(ccsMODULEID moduleId, vltINT16 errorNumber,

ccsLOC_ID locId, char *sysModule,

va_list va_parList);

};

Every instance of eccsERROR_CLASS is connected to an error stack. This error stack is used for the logging of errors and by default is the global stdErr. It is possible to specify another error stack in the constructor.

The following example describes the interface of the class:

// We need a class to handle integer values stored in the database. We

// define the following class derived from eccsERROR_CLASS:

class DBINT: public eccsERROR_CLASS {

public:

DBINT(const dbSYMADDRESS attrName);

DBINT &Value(int newValue);

int Value();

};

In this sample class, none of the methods can return a ccsCOMPL_STAT:

- The constructor gets the attribute we want to access.

- DBINT &Value(int newValue) is used to write a new value in the DB.

- int Value() is used to get the value from the DB.

Since this is a subclass of eccsERROR_CLASS we have the method ErrStatus() to ask the completion status of the last method called.

A user of this class can do in the following way:

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

// In this example I create an instance of DBINT and

// I check for error in the constructor

DBINT myAttr(":PARAMS:SCALARS.scalar_int32_2");

if(myAttr.ErrStatus() == FAILURE)

{

// There has been an error. I gave the name of an attribute

// that does not exist.

// I add a new error on the default stack (the same used

// by the instance of DBINT(

errAdd(.......);

}

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

The public methods provided by the eccsERROR_CLASS are the following:

ccsCOMPL_STAT ErrStatus()

operator ccsCOMPL_STAT()

ccsCOMPL_STAT ErrStackClose();

ccsCOMPL_STAT ErrStackReset();

The ErrStatus() returns the completion status of the last method called. The cast operator is used when an instance of eccsERROR_CLASS is casted to type ccsCOMPL_STAT. The other two methods close or reset the error stack and set the status to SUCCESS

The line:

if (myAttr.ErrStatus() == FAILURE)

is equivalent to:

if ((ccsCOMPLSTAT)myAttr == FAILURE)

The class provide also a way to handle the concept of "object status": a whole object can be in a SUCCESS state or in a FAILURE state.

Consider for example the case of a "disk file" object. If you create such an object to read from a file, and the file does not exist, the object is in a FAILURE state, and all read methods will fail.

The public method:

eccsERROR_CLASS &ObjStatus(ccsCOMPL_STAT s);

is used to query the status of an object while the protected method:

eccsERROR_CLASS &ObjStatus(ccsCOMPL_STAT s);

to set the status of an object to FAILURE or SUCCESS.

2.5.6 Event System

The Extended CCS interface to the Event System is based over two classes:

- the class evtEVENT used to setup events on database attributes.

- the class evtEVENT_MSG used to handle incoming Event Messages.

2.5.6.1 Class evtEVENT

In order to monitor changes in a database attribute, it is necessary to create an instance of class evtEVENT, passing to the constructor the symbolic address of the point to be monitored. After that it is possible to set (and retrieve) the monitoring parameters, using the methods of the class.

An instance of the class is always created with the following default settings for the configuration parameters:

Parameter
Value
Attribute Name
Empty string
Filter
evtANY_WRITE
Environment, Process Number, Process Name
The process itself
User Message
NULL

For more details about the concepts of database events consult the CCS documentation.

The class provides the following public interface:

class evtEVENT: public eccsERROR_CLASS {

public:

evtEVENT(const dbSYMADDRESS attr = NULL, int attach = FALSE);

// Creates an instance of the class to monitor the

// given attribute. If the second parameter is TRUE,

// also Attach() the event

evtEVENT(const evtEVENT &source);

// Copy constructor

virtual ~evtEVENT();

// Destructor: if necessary Detach() the event

virtual int Status();

// Returns TRUE if the event is currently attached,

// FALSE if not

virtual ccsCOMPL_STAT Attach();

virtual ccsCOMPL_STAT Detach();

virtual ccsCOMPL_STAT Disable();

virtual ccsCOMPL_STAT Enable();

// Attach, detach enable or disable the event, using the

// current settings

//Methods to access safely the values of parameters

const char *AttrName() const;

evtEVENT &AttrName(const dbSYMADDRESS newName);

evtFILTER Filter() const;

evtEVENT Filter(evtFILTER newFilter);

const char *Env() const;

evtEVENT &Env(const ccsENVNAME newEnv);

const char *Proc() const;

evtEVENT &Proc(const ccsPROCNAME newProc);

const char *UserMsg() const;

evtEVENT &UserMsg(const ccsPROCNAME newUserMsg);

const evtEVENT_ID &EventId() const;

msgCMDID CommandId() const;

evtEVENT &operator =(const evtEVENT &e);

};

The CommandId() method returns, whenever the event is attached, the same unique command identifier returned by the CommandId() method of the evtEVENT_MSG class (2.5.6.2) when the database event is received and parsed.

Events can be attached on both Workstation and LCU database attributes, but in the case of the LCU it is not possible to specify a diffent process or environment as the receiver of the event of to specify a user message. The object must have the default values for these members.

2.5.6.2 Class evtEVENT_MSG

Whenever the application receives a message related to a database event, it can be received using an instance of the class evtEVENT_MSG. If it is known "a priori" that the next message waiting on the queue is a database event, it is possible to receive and parse it in a single shot, otherwise it is necessary to receive the message with msgRecvMsg() and then parse it properly on the base of the message type.

The following lines of code is a typical example:

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

ccsCOMPL_STAT stat;

msgHEADER *header;

msgMESSAGE message;

evtEVENT_MSG dbevent;

stat = msgRecvMsg(&header);

if(stat == FAILURE) ... error handling ...

switch(header->msgType)

case msgTYPE_COMMAND:

case msgTYPE_REPLY:

case msgTYPE_ERROR_REPLY:

case msgTYPE_TIMER:

stat = message.Parse(header);

... do something ...

break;

case msgTYPE_EVENT:

stat = dbevent.Parse(header);

... do something ...

break;

default:

stat = FAILURE;

}

When a database event is received and parsed, the CommandId() method returns the same command identifier that can is returned by the CommandId() method of the evtEVENT class(2.5.6.1).

What follows is the public interface of the evtEVENT_MSG class:

class evtEVENT_MSG: public msgRAW_MESSAGE {

public:

// Constructors and destructor

evtEVENT_MSG(const msgHEADER *msg = NULL);

evtEVENT_MSG(const evtEVENT_MSG &source);

~evtEVENT_MSG();

ccsCOMPL_STAT Parse(const msgHEADER *msg);

ccsCOMPL_STAT Receive(msgTIMEOUT timeout = msgNO_TIMEOUT,

const msgRECEIVEFILTER *filter = NULL);

// Methods to access safely the values of parameters

// Acces functions are only for read of the values:

// values for data members are set only by Parse()

vltUINT8 Type() const;

const char *Command() const;

msgCMDID CommandId() const;

const evtEVENT_ID &EventId() const;

vltUINT8 Trigger() const;

const msgPROCESSID &TriggerProc() const;

const char *AttrName() const;

dbATTRTYPE AttrType() const;

const evtDATA &EventInfo() const;

const char *UserMsg() const;

// Copy operator

evtEVENT_MSG &operator =(const evtEVENT_MSG &e);

};

2.5.7 Logging System

logData():

The function corresponds to the CCS logData() call. It accepts a variable number of parameters with the same syntax of printf() (variable format). The logging ID is set to 0.

logFITS:

This class provides support for the logging of Operational Logs in FITS format.

Each method supports a given type of operational log:

· Action Records
· Parameters Records
· Unforeseen Event Records
· Comment Records

Each FITS Operational Log is defined in a FITS dictionary and it is identified by a hierarchical keyword.

For details, look at the man page of the logFITS class and at the description of Operational Logs in the CCS User Manual[1].

The class implements the concept of "Logging Level". Every instance can be set to a specific "Logging Level" by calling the Level() method.

All logFITS logging methods accept as parameter (with default value 1) the logging level for that specific message. When that line of code is executed, if the object current logging level is higher or equal that the message level, the message is actually logged, otherwise nothing happens.

2.5.8 Fast Prototyping Functions

These functions can be used during development or for development of prototypes:

eccsLogComment():

Logs a comment on stdout and (if active) on the log system. Same syntax of printf() (variable format).

eccsLogError():

Logs an error on stdout and (if active) on the log system. Same syntax as for printf() (variable format). The first parameter is the error stack.

eccsLogSave():

TRUE or FALSE: turns on/off the logging of comments and errors on the logging system.

ccsCOMPL_STAT eccsLOG_0((const char *logstr, ...))

ccsCOMPL_STAT eccsLOG_1((const char *logstr, ...))

ccsCOMPL_STAT eccsLOG_2((const char *logstr, ...))

ccsCOMPL_STAT eccsLOG_3((const char *logstr, ...))

These macros use eccsLogComment() to log debug messages, depending on the value of the preprocessor symbol eccsLOG_LEVEL. eccsLOG_? log the message only if eccsLOG_LEVEL > ? (eccsLOG_0 always log). The choice is done at compile time and thus very efficiently.

Note the `((` and `))` to enclose the macro call parameters, instead of `(` and `)`.

eccsErrExit():

If (stat == FAILURE) logs an error with the given user message and exit (closing the error stack).

It uses the default error stack stdErr.

eccsErrLog()

If (stat == FAILURE) logs an error with the given usermessage and reset the error stack. It uses the default error stack stdErr

2.5.9 Other classes/functions

This section contains a small description of other classes/functions of general use that are part of the eccs library but are not in a specific cathegory.

2.5.9.1 eccsTIMEVAL

This is a very small class derived from the standard ccsTIMEVAL data structure.

It allows easy and automatic conversion between ccsTIMEVAL data and integers and should always be used whenever ccsTIMEVAL is required.

2.6 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.6.1 Mixing C and C++ code

C and C++ functions can be mixed without problems. It is only necessary to pay attention to what is called name mangling: C++ changes internally the names of functions in order to support overloading. If linking of an application reports symbols not found with awkward names, this is probably the reason.

2.6.1.1 Using C functions from within C++

In order to be able to use C compiled code (such as libraries) from within C++, the prototypes of the functions must be included using the following syntax:

extern "C" {

void CFunctionPrototype();

}

or, for a whole C include file:

extern "C" {

#include "CInclude.h"

}

This tells the C++ compiler that the functions have been compiled with a standard C compiler and symbols must not be mangled. Note that all standard include files provided by the system (stdio.h, string.h,...) and most CCS include files already cope automatically with this problem.

Include files implemented for C code should be structured as follows to make it possible inclusion from both C and C++ code without actually specifying the format:

#ifndef XXX_H

#define XXX_H

/******************************************************************************

* E.S.O. - VLT project

*

* xxx.h

*

* who when what

* -------- -------- ----------------------------------------------

* mrBean 06/01/95 created

*/

/************************************************************************

* Header file for demo purpose

*

*----------------------------------------------------------------------

*/

// The "__cplusplus" flag is set by the GCC compiler automatically when compiling

// files with extension ".C"

#ifdef __cplusplus

extern "C" {

#endif

// Definitions of the include file

#ifdef __cplusplus

}

#endif

#endif /*!XXX_H*/

2.6.1.2 C++ includes

Since C++ includes have the same .h extension of standard C includes, it is a good practice to prevent including them from standard C code. In order to do this, yust put the following lines in the header file:

#ifndef __cplusplus

#error This is a C++ include file and cannot be used from plain C

#endif

2.6.1.3 Using C++ functions from within C

C++ function can also be used from C compiled code or from code written in other languages. This allows to create a C interface to C++ classes, but usually it is not done, since it is much more sinpler to compile everything with the C++ compiler. Guidelines on how to do this can be found in standard C++ documentation.

2.6.2 Using msgMESSAGE to send replies

Whenever a command is received and parsed using an instance of msgMESSAGE class this can be immediately used to send back replies to the originator using the SendReply() methods.

Anyway it is important to remember that the message buffer passed to the SendReply() method is stored in the class instance and overwrites the original message buffer received with the command.

If the original message must be reused after having sent the replies, it is better to copy the received msgMESSAGE in a local variable and use this copy to send the replies.

2.6.3 Multiple callbacks on the event

It is possible to install multiple callbacks on the same event: they will be called in the installation order.

It is important to rememder anyway that the msgRAW_MESSAGE structure passed to the functions when they are called is always the same structure and not a private copy, for performance reasons.

This means that if a callback modifies the received message (for example to send a reply), all the following callbacks will receive the modified data structure.

In this case a local copy should be done in the callback that modifies the message.

2.6.4 Compiler warning: ambiguous conversion from `eccsDB_LOGICAL' to `int'

This warning, or other similar, can be produced when an eccsDB_xxx variable is implicitly converted to the type of the database attribute, for example in an assignment like:

eccsDB_LOGICAL aDbLogical("symbAddr");
vltLOGICAL aLogical = aDbLogical;

In this case, and if is the expected result, it is enough to add an explicit cast to the desired type:

vltLOGICAL aLogical = (vltLOGICAL)aDbLogical;

2.6.5 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 checking for replies based on the command name, in particular when using message filtering: they send a command using an alias and receive replies from a different one (in terms of the string identifying its name).

2.6.6 Accessing single elements in a TABLE

Access to database TABLEs should be done by using the specific eccsDB_TABLE class.

The record or records of interest are assigned to a memory array of table records and are then accessed from there. This optimizes database acces performances and assures that all fields in a record are read/written with an atomic call.

Nevertheless, sometimes it is necessary to access only one single element in a vector or a single cell in a table. This is for example quite a common procedure when accessing tables with a single record, used just like data structures.

In this case a good approach is to use the basic type access classes (eccsDB_LOGICAL, eccsDB_INT32......), specifying the table element in the attribute name, like for example:

eccsDB_INT32 table Element(":Appl_data:myDatabasePoint.myTable(0,5)");

The folloowing examples show the different techniques that can be used to access single table elements:

1) Record access:

myTABLE_RECORD rec = myTable[5]; // read the record

rec.field = value;
other = rec.other;

myTable[5] = rec; // write the record

This technique is more efficient if more fields have to be accessed and ensures that all fields are read/written with an atomic operation

2) Single attribute access with eccsDB_ATTR classes:

eccsDB_INT32 field(":Appl_data:test:point.myTable(5,\"field\")");

field = value;
value = field;

3) Single attribute access with eccs dbRead()/dbWrite():

dbRead(&value,":Appl_data:test:point.myTable(5,\"field\")");
dbWrite(&value,":Appl_data:test:point.myTable(5,\"field\")");

2.6.7 Accessing TABLE columns on LCU environments using eccsDB_VECTOR (spr. 980404)

The eccsDB_VECTOR class provides a specific constructor to access TABLE columns as if they were vectors.

Unfortunately, due to limitations on LCC database, this approach does not work on LCU environments.

It is anyway possible, as a workaround, to use the normal eccsDB_VECTOR constructor passing directly in the pointName parameter the full table column specification, as in the following example.

int main(int argc, char *argv[])
{
stat = ccsInit(argv[0]);

vltINT32 recordCnt;

char columnPath[] = ":PARAMS:TABLES.full_table(,uint32)";

// Allocate the DB vectors as simple vectors
eccsDB_VECTOR<vltUINT32> colUint32(addr);

// Allocate the memory vectors
recordCnt = colUint32.RecordCnt();

vltUINT32 *arrayUint32 = new vltUINT32[recordCnt];
// Read current values
st = SUCCESS;
errFlag(st, colUint32.GetValue(arrayUint32));
eccsLogComment("Reading and printing new table by column:");
for(int i = 0; i < recordCnt; i++)
eccsLogComment("[%2.2d]: %-6.6d\n",i, arrayUint32[i])
stat = ccsExit();
eccsErrLog(stat,"Error in Exit");
} /* end main() */

This approach has some limitations and must be used carefully only is really necessary.

In particular:

· The COMPLETE symbolic address of the table column must be given in the first parameter pointName of the constructor. The attrName parameter cannot be used.
· The = and [] operators and the SetValue(index, buffer) and GetValue(index,buffer) methods cannot be used. Access to single elements in the columns is then not allowed. Only the whole column can be retrieved and store at once with the SetValue(buffer) and GetValue(buffer) methods.



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