Next: References
Consider a typical application shown in Figure 1., the application has to stop or start a data acquisition run according to hardware information retrieved from the channel access service, and store some important data into a data base. The application not only has to handle requests to/from different services, but also has to coordinate all requests such that no service will be blocked for a unreasonably long period of time. Furthermore the application has to be modified or rewritten if any of the APIs or services have been changed or the application needs to access a different service.
Figure 2 illustrates how the CDEV layer interacts with user applications and multiple services. In this approach, applications use CDEV to access these services via type-secure, object-oriented class interfaces, rather than through a set of service specific APIs. In general, CDEV is designed to reduce the complexity of control/DAQ software development without compromising performance. For example, the CDEV uses C++ language features (such as inline functions) that minimize the performance overhead of the additional layer in some of the critical sections of the CDEV code.
To simplify the interface to services, a device ( cdevDevice) in CDEV is regarded as a named entity which can only respond to a set of messages such as on, off or get/set attribute. All I/O requests in the system are in the form of messages to devices. A message to a device can be handled either synchronously or asynchronously, and is actually carried out by a request object ( cdevRequestObject) that is created and registered. A device will dispatch messages to a right request object which in turn sends out to the right control package or service. Figure 3 shows all forms of I/O requests and object relationships between a device and request objects. This message based interface simplifies the usage of CDEV, and also makes it simple to put any message based language (such as Tcl [7]) on top of CDEV. To allow the message based interface to work with different services, a new data type cdevData is designed to hold different data types with different tag.
In CDEV a particular control/DAQ service interface is totally separated from applications, and also hides the representation and internal structure of all I/O requests to/from a service. Applications using CDEV communicate with packages or services through a service layer which contains a C++ class derived from cdevService and a class derived from cdevRequestObject which handles all I/O requests. Figure 4. illustrates how CDEV uses an abstract interface for constructing a particular service of cdevService, and how it constructs an I/O request to the services by a cdevRequestObject. This figure also points out the similarity between the design of this part of CDEV and the object-oriented builder pattern [8] which is widely used for a construction process that allows different representation for the object that is constructed. This object-oriented design improves modularity by encapsulating the way a complex object is constructed and represented. Applications need not know anything about the classes that define the cdevService's or cdevRequestObject's internal structure; such structures don't appear in the CDEV interface. Only at run-time CDEV passes messages to a name resolution system called cdevDirectory (see the following section) to decide which subclass of cdevService or which subclass of cdevRequestObject to construct, and CDEV keeps all constructed objects to enable subsequent calls to get to the objects quickly.
In a typical application using multiple services, one or more results due to I/O requests may arrive from different sources (such as channel access ports and CODA communication ports) and it is not desirable to block or continuously poll for incoming I/O events on any individual source. In Figure 5. a system of cdevSystem maintains a set of services derived from cdevSerice. It provides methods of registering and removing these cdevService objects from this set at run-time. It also provides an interface for dispatching the appropriate methods of the cdevService objects associated with incoming I/O events. The system detects and reports the simultaneous occurrence of different types of events on multiple I/O descriptors given by the cdevService objects. When one or more I/O events arrive, the system of cdevSystem returns from the event demultiplexing call and dispatches the appropriate method(s) associated with cdevService subclass objects registered to handle these events. Therefore CDEV handles concurrent event demultiplexing and dispatching efficiently and frees applications from handling events explicitly.
CDEV also eliminates the need for complex synchronization of multiple asynchronous I/O requests in applications. Considering an application in Figure 6, which sends out multiple asynchronous I/O requests through several objects derived from cdevRequestObject within a cdevGroup that maintains a set of cdevTranObj objects associated with each request object. The cdevGroup provides ways to add and remove these transaction objects from the group at run-time. It also dispatches the appropriate methods of the request objects and removes the associated transaction object when I/O requests have finished with notification callbacks. Therefore a cdevGroup object can wait for all I/O requests to finish on a set of I/O request objects. In addition, applications can use cdevCallback to notify itself when an asynchronous I/O request has finished.
CDEV also allows easy integration of new services. Applications in CDEV only use classes derived from cdevService to communicate with services, and use I/O request objects derived from cdevRequestObject to send out I/O requests in the form of messages. A new service hence can be integrated into CDEV by developing two new classes. One is derived from cdevService, which handles I/O events dispatching. Another is derived from cdevRequestObject, which accepts I/O requests in messages and send them out to a service. Once these two classes are implemented and registered into the CDEV name resolution system (see the following section), applications can access the new service without modifying their source code.
First of all CDEV uses a simple set of enumerated values to denote status of all CDEV functions. This was chosen over using the C++ exception handling mechanism because it will be easier to interface to procedural wrappers for C and Fortran users. CDEV also defines a standard error reporting class cdevError which is inherited by cdevSystem. Applications can override the default error reporting function in the cdevError class to redirect error message to an appropriate function.
Secondly, a new data type called cdevData is implemented. In order to let applications using CDEV to communicate with different control services or packages, CDEV has to be able to handle different data types in a uniform fashion. Thus a new C++ data type cdevData is designed to suit this very purpose. The cdevData serves as a repository for data of different types and sizes, accessed by either an integer or character string tag. There is a one to one correspondence between integer tags and character tags so either may be used to insert to retrieve data. Currently a cdevData object can hold any type of int, pointer to a character string, char, short, ushort, uint, long, ulong, float, double, or time_val structure and arrays of same. In the future, any structure which is derived from a common parent class can be added or retrieved from cdevData. Any applications or new service layers which use cdevData for high performance purpose should use integer tags instead of character string tags. Finally, cdevData also inlines a lot of its functions to achieve optimal performance.
Thirdly, CDEV contains a name resolution system to locate which service objects derived from cdevService to call, and what data to pass to the service in an I/O request. CDEV constructs the name resolution system by parsing an ASCII file that is written by a system programmer in DDL (Device Definition Language) format, and creates a table containing information about devices in a control system. The name resolution system that is called cdevDirectory is also derived from cdevDevice so that it can be accessed by applications at run-time through the same message based interface. Table 1. summarizes the messages the name resolution system accepts and results it returns.
Finally CDEV does not define a new network protocol. It uses the network protocols of its underlying services. For examples, applications using CDEV to access a device via channel access use channel access protocol.
#include <cdevSystem.h> #include <cdevDevice.h> #include <cdevRequestObject.h> #include <cdevData.h> #include <cdevGroup.h> static void devcallback (int status, void* arg, cdevRequestObject& obj, cdevData& data) { float fval; if (data.get (``value'', &fval) == CDEV_SUCCESS) printf (``bdl of %s is %f\n'', obj.device().name(), fval); } main (int argc, char **argv) { cdevSystem& system = cdevSystem::defaultSystem (); cdevDevice& dev0 = cdevDevice::attachRef (``magnet0''); cdevDevice& dev1 = cdevDevice::attachRef (``magnet1''); cdevData result0, result1; int status = dev0.send (``get bdl'', 0, result0); // do something with result0 status = dev1.send (``get bdl'', 0, result1); // do something with result1 cdevGroup grp; grp.start (); status = dev0.sendNoBlock (``get current'', 0, result0); status = dev1.sendNoBlock (``get current'', 0, result1); grp.end (); grp.pend (4.0); // do something with result0 and result1 cdevCallback callback (devcallback, 0); cdevGroup grp; grp.start (); status = dev0.sendCallback (``monitorOn bdl'', 0, callback); status = dev1.sendCallback (``monitorOn bdl'', 0, callback); grp.end (); grp.pend (4.0); for (;;) system.pend (); }The main program starts by opening a default cdevSystem system that keeps all information about services and devices. Next it creates two cdevDevices dev0 and dev1 with name of magnet0 and magnet1 respectively. Then two synchronous requests are sent out to the devices, and followed by demonstration of synchronization method of CDEV using cdevGroup. Finally, the program enters a event loop in which the attributes bdl of devices are monitored.
Currently CDEV has a channel access service layer, which has all capabilities EPICS channel access API, tested extensively on HP-UX. There are several applications using CDEV currently in use at CEBAF, and one new service called the model server which supports DIMAD [9] and other modeling engines. CDEV alone without the channel access service layer has been ported to SunOs, Ultrix, VMS and Aix operating systems. The source code for CDEV is available via anonymous ftp from ftp.cebaf.gov in pub/cdev. The web url is http://www.cebaf.gov/cdev.