HiDEOS Interface to EPICS

Introduction

A library has been set up to simplify the writing of HiDEOS device support modules for EPICS. All HiDEOS devices use the EPICS asynchronous device support model. By using the library, the programmer will minimize the amount of code that needs to be written. Most of the details of EPICS device support and the HiDEOS interface are hidden from the developer. The library consists of a base class which the user subclasses off of. A macro is used to generate the EPICS DSET.

Overview

Creating an EPICS/HiDEOS device support module requires knowledge of C++. The library makes use of virtual base classes, static member function, and constructors. The base class used to create EPICS/HiDEOS device support modules is the HideosDevice class. Every device support module must do the following: create a new class by deriving off HideosDevice (1), define a constructor (2), define the StartIO() and CompleteIO() routines (3), define a static member function to be called at record initialization (4), define a DSET (5). Each record in the EPICS database which specified HiDEOS devices will have contain it's own HidoesDevice subclass instance. The pointer to HihideoDevice subclass instance is stored in the record private field (DPVT).

(1) Creating a New Class

As stated earlier, the HideosDevice class controls most aspects of initialization and communication with EPICS and HiDEOS. The public interface of HidoesDevice contains methods for retrieving and freeing HiDEOS message buffers (see HiDEOS documentation). Also contained in the class are methods for sending the messages. Two types of send are supported, blocking and non-blocking. The class has no concept of different record types, all communication with record is done through the record base structure dbCommon. All device support derived from the HideosDevice class is automatically assumed to be asynchronous. The HideosDevice class constructor creates a communications interface instance (BPD) for HiDEOS and attempts to bind the record instance to a HiDEOS task. The name of the HiDEOS task will be the first string in the parm field of the link. A method called GetUserParm() exists for easily retrieving a pointer to the remaining data in the parm field. The parm field is viewed as a comma separated list of strings, the first being the name of the HiDEOS task. The HiDEOS task name is the only part of the parm field that is parsed by the HideosDevice class, the rest of the string is left for the user to define.

(2) New Device Constructor

The HideosDevice derived class must define a constructor. The constructor will take the arguments of the HideosDevice constructor and pass it's arguments down to the base class. Remember that the derived class constructor gets called after base class constructor. The first thing to do in this function is to check the device status using the DeviceStatus() method. If the base class constructor failed, then the status returned from DeviceStatus() will indicate the EPICS return code and nothing further needs to be done. If the status indicates success, then the derived class constructor can continue. The next step to do here is to initialize the private data of the derived class and record instance. Almost all device support modules retain some sort of state, this state should be stored in the private section of the derived class.

(3) Defining the I/O Functions

The HideosDevice class contains two pure virtual function - functions which must be written in the subclass. The first, StartIO(dbCommon*), gets called when to EPICS record gets processed. The job of StartIO() is to cast the EPICS base structure dbCommon to the correct record type, create a HiDEOS message with the appropriate data and send it out. The second function which must be written is the CompleteIO(dbCommon*,Message*) function. The dbCommon parameter is the record for which I/O is completing, the Message parameter is the HiDEOS response from the earlier send. The job of CompleteIO() is to cast the dbCommon structure and Message to the actual expected types, decode the response, record any required information in the record, and free the message buffer.

(4) Record Initialization Function

The HideosDevice derived class must define a static member function which is to be called at EPICS record instance initialization time. The function will become part of the DSET by using the DSET definition macro described below. The function takes as an argument the record instance being initialized. As stated earlier, there is one HideosDevice instance per EPICS record instance. The job of this function is to actually create the user's HideosDevice derived object. Remember that each EPICS device support module for HiDEOS contains a HideosDevice derived class, this function will create an instance of that class to latch on the record instance (using the DPVT field of the record). Since this function is called directly from the DSET, the user knows exactly what class instance to create - this is the only point in the flow of execution that this information is known. After creating the instance of the user's class, the function returns using the DeviceStatus() as a return code. Nothing more needs to be done here because the constructor of the user's class will take care of the initialization.

(5) Defining the DSET

DSETs are defined using a special macro. The two parameter needed by the macro are a name and the above described record initialization function. The name parameter is the DSET name used by EPICS record suport to find the device support functions (see EPICS Developer's Guide). See the section below on the actual use of the macro. Currently all the functions in the DSET are defined in the HideosDevice base class except for the record initialization function.

HideosDevice Base Class Public Interface

HideosDevice(dbCommon* rec,link* l);
Construct a HideosDevice instance for record rec using link information l. The instance pointer will be stored in the record's DPVT field. The record will be bound to the HiDEOS task name specified in the link's parm field. All subsequence I/O will be to the HiDEOS task named in the parm field. The card field will identify where the HiDEOS task is running, card=0 is the vxWorks/EPICS board.

virtual long StartIO(dbCommon* rec)=0;
Start the asynchronous transaction using rec. Typically the user will send a message to HiDEOS here and return. The user should return the normal EPICS return codes here or HideosDevice_OK if everything is to complete normally using asynchronous record completion. Two return codes, HideosDevice_EndOK and HideosDevice_EndNC are used to inform the library that processing is complete and asynchronous completion should not done. HideosDevice_EndOK indicates complete with conversion and HideosDevice_EndNC indicates complete without conversion.

virtual long CompleteIO(dbCommon* rec,Message* msg)=0;
Complete the asynchronous transaction using rec. Msg is the message returned from HiDEOS as a result of a StartIO(). The msg can be parsed here and the record updated. Return the standard EPICS codes for conversion/no conversion here if transaction is successful.

long DeviceStatus();
Return the status of the device. The status can be set by any of the base class function, usually the constructor. The user should use this to ensure that the device is functioning. The status stored here is the EPICS return codes.

BPD* GetBPD();
Return the HiDEOS BPD communications descriptor. This is the low level interface to HiDEOS. Each instance of HidoesDevice uses one BPD.

TD GetTD();
This is the HiDEOS task descriptor. Each instance of HideosDevice communicates with one HiDEOS task. The TD holds all the information about the location of the HiDEOS task the HideosDevice is communicating with.

const char* GetUserParm();
Return a pointer to the start of the user defined portion of the link's parm field. The purpose of the function is to easily get access to the user's parm field.

Message* GetMessageBuffer(int type);
int FreeMessageBuffer(Message* msg);
Routines for getting and freeing message buffers. All message buffer should be created using these routines. GetMessageBuffer() takes a integer argument which is a HiDEOS message enumerator (see HiDEOS documents) and returns a reference to that type of message buffer. FreeMessageBuffer() returns a message previously allocated with GetMessageBuffer() to the heap. All HiDEOS message are derived from the Message class, so GetMessageBuffer() always returns the buffer as the messaging base class Message.

int Send(Message* msg);
Send the message msg to the HiDEOS task which this instance of HideosDevice is connected. This is the call typically used in StartIO() to begin a transaction to HiDEOS. This call does not block.

Message* SendBlock(Message* msg);
Send the message msg to the HiDEOS task which this instance of HideosDevice is connected. Additionally, block until a message is returned from the HiDEOS task. The returned Message* is the message that is returned from the connected HiDEOS task. It is the responsibility of the SendBlock() caller to free the returned Message buffer. This is more of a synchonous transaction. The place where this is typically used is in a HideosDevice subclass constructor. The subclass constructor can send and retreive configuration information from HiDEOS tasks during EPICS record initialization.

DSET Creation Macros

Two macros currently exist for automatically generating the EPICS DSET structure.

MAKE_HIDEOS_DSET(name,init_function)
Create a DSET with global symbol "name" as the device name. The "name" should be used to create an entry in the devSup.ascii file so the device is available as a DTYP field choice in DCT. The "init_function" should be the static member function for initialization described above. Remember to use the scope operator in the init_function argument. Since the macro is used outside the context of a class, the init_function must include the class name (example: myDevice::init_func where myDevice is the class derived from HideosDevice and init_func is the static member function for initialization).

MAKE_HIDEOS_AI_DSET(name,init_function)
Same as above macro, but used for the ai/ao records. This macro generates the linear conversion function required by the ai/ao records.

Summary


Argonne National Laboratory Copyright Information
Jim Kowalkowski (jbk@aps.anl.gov)
updated 4/12/95