4.6 OBJECT ORIENTED DESIGN AND DEVELOPMENT
ECCS, EVH and Database design4.6.1 Policy and standards
Following the recent development in software technology, the usage of Object Oriented design and development techniques for the VLT project is now strongly encouraged. OO techniques provide a good support for reuse, cleaner design solutions and code easier to maintain.
Being of recent introduction on the VLT, standards have not yet been completely chosen.
For the design, a well established methodology like Rumbaugh's OMD[21] or Booch's Object Oriented Design[22] can be used.
For development, C++ shall be used as the OO programming language. It provides the smoother learning path for C programming. At the moment, C++ can be used only for the Workstation platform and not on LCU, where it is being investigated.
The GNU C++ has been selected as standard compiler. It provides also an implementation of the Draft ANSI C++ Standard Library[23], that implements all the commonly used data structures (lists, queues, stacks, containers). The usage of these classes is mandatory.
All ESO standard conventions[5] apply also to the development of C++ applications; ESO will provide an extension to the rules mentioned in [5] for C++ specific topics.
In addition to the standard tools for design and programming, the VLT common software provides:
· The database loader dbl[3], that gives an object oriented support for the definition of the on-line real-time database for both WS and LCU applications.
Extended CCS can be used alone, but in order to exploit the full advantages of C++ for the development of applications, the usage of EVH is fundamental.
It is suggested to use ECCS alone for a short period of time as a starting point for people without any knowledge of C++ and for the first porting to C++ of already existing CCS applications (not just for recompiling a C application under C++, that should not require changes, but for a real porting that takes profit of extensions provided to C language by C++). This conversion to C++ of existing CCS applications is in general very convenient since it can easily reduce the amount of lines of code by more than one half, without any code restructuring.
To effectively exploit reusability, generic design patterns and class libraries are centrally maintained and coordinated as any other VLT Common Software.
Before designing new solutions or implementing new code, it is of primary importance to verify if a design architecture for the same problem already exist and if the implementation can be based on already available classes. This with the purpose of reducing design and development efforts and of developing uniform and coherent software, that will be easier to maintain after the VLT commissioning phase. The VLT Software Object Oriented Working Group will take care of examining the design of applications (and will help on request) to identify patterns already available and candidates for reuse that must be developed in a general way and put in common libraries. For externally developed software, PDR, FDR, implementation and acceptance are check points for the usage of standard tools, design architecture, classes, etc.
4.6.2 Basic object oriented concepts
This section contains a very brief introduction to the basic object oriented concepts. This is required as a background in order to be able to understand the following sections.
Abstract data type is a key concept in object oriented design. An abstract data type is also called a class. It is the building block of the system.
A class is a representation of a logical entity of the system. Classes are defined by data and functions. The designer objective is to identify (discover or create?) what are the classes that can make up a reusable, extensible and robust software.
This task is highly unstructured and it is based on the designer ability and experience.
A class may be used to represent different elements at different levels of abstraction. For example, a class can describe a physical component of the system like a motor or a valve. On the other hand, a class may represent a more abstract entity, such as a linked list.
The first step on an object oriented design should be to identify the classes involved in the problem.
What follows is an example of a basic abstract data type representing a "step motor", which is a physical object in our system :
STEP_MOTOR(vltLOGICAL st=9, vltFLOAT ss=2){state=st;stepSize=ss);}
int Rotate( vltFLOAT degrees );
The former C++ class contains three data member. state to represent the current state of a given step motor, stepSize to save the size of the steps in a given step motor and params which is a vector to hold all its configuration values. Also the C++ class has one function: Rotate(), to perform a motor rotation.
In an abstract data type or class the basic components are data and functions. Another level of abstraction can be achieved by allowing a class to be composed with instances of other already defined classes. This mechanism named composition is widely used an allows a compact representation of complex classes.
In the step motor example we found that also every motor has ( "is composed of" ) Input-Output Controller unit. This Controller unit is another abstract data type or class in our system. Given this we can expand our basic step motor class via composition with:
STEP_MOTOR(vltLOGICAL st=9, vltFLOAT ss=2){state=st;stepSize=ss);}
int Rotate( vltFLOAT degrees );
IO_CONTROLL *Controller; // New member via Composition
Inheritance allows the specialization and generalization of classes. It is a way of creating new classes by using the ones that are already defined. It also allows the representation of a problem as a set of related classes. With inheritance, the commonalities of different classes can be factorized in one single class (base class) and then a set of specific classes may inherit from that base class.It is a powerful mechanism to simplify and reduce the complexity and size of a system.
Also, inheritance serves as the base for polymorphism and dynamic binding. With polymorphism, different classes might implement different versions of the same function. A typical example for geometric manipulation is the base class "shape" and the subclasses "circle" and "square" both inheriting from shape. On both subclasses there is a function called "Draw()" to draw a specific geometry. Then, dynamic binding allows to program a piece of code in which there is no explicit reference to what type of object of the hierarchy we are drawing. It is simply a call to the generic Draw() function. At run time the system chooses, by itself, which version of Draw() it should call, depending on what type of object the function is being applyed to. These features promote the reuse of code.
As in the case of identifying classes, the identification of the right inheritance relations between classes is a human designer capability.
Same efforts are being made in order to capture common design pattern of classes and their relationships for specific type of domains such as real-time, communications, financial, etc. This will help the software designer to avoid common mistakes on the design work and share some already successful abstractions to solve his particular problem.
Now going back to the step motor, inheritance is added in order to expand the class. Because the designed system deals with many different types of motor, a basic motor class, with all common attributes of every motor type was designed. In that basic class, data such as the serial number, color, size, etc. are stored. Now via inheritance, the step motor class is expanded to contain also those attributes:
class STEP_MOTOR : public MOTOR { //inheritance from "MOTOR" class
STEP_MOTOR(vltLOGICAL st=9, vltFLOAT ss=2){state=st;stepSize=ss);}
int Rotate( vltFLOAT degrees );
IO_CONTROLL *Controller; // New member via Composition
The ": public motor" is saying that STEP_MOTOR inherits every member attribute of class "MOTOR"
With this addition, out class has grown to hold all the complexity required by using the key features of object orientation.
The idea of encapsulation is to separate a class data and functions in two groups. One is the public interface of the class, containing all the data and functions to be used by clients of that class. The other is the private group, where all internal data and functions are kept. The private group can only be used inside the class, so no other class can see it. With this mechanism all class internal complexities are hide inside the class itself. Only the interface is seen from the outside world.
This leads to loosely coupled systems, enhancing the overall quality of the software.
This feature can be used in the step motor class. For example, the designer has decided that the data member StepSize is an internal issue of the step motor itself and that it should be hidden from all other classes in the system. This means that the value of the StepSize parameter can only be set or read from within the class ( .i.e. from a class function ). To the outside world the data member StepSize is not visible. The C++ class "STEP_MOTOR" then became:
class STEP_MOTOR : public MOTOR { //inheritance from "MOTOR" class
STEP_MOTOR(vltLOGICAL st=9, vltFLOAT ss=2){state=st;stepSize=ss);}
int Rotate( vltFLOAT degrees );
IO_CONTROLL *Controller; // New member via Composition
4.6.3 Extended CCS
The ECCS library is a wrapper on top of standard CCS functions. It provides also the building blocks to develop higher level class libraries based on CCS.
Extended CCS has been designed to be used without requiring any particular knowledge of C++ programming. It can be used as the first approach to C++, because the minimal effort spent for understanding the library is immediately refunded by the simplification obtained in the developed code. The architecture of applications developed in C++ using ECCS alone can be exactly the same as of any other CCS application.
The class library provides an interface to all commonly used CCS features (error handling, database access, message system, logging system, event system), but it is not a complete replacement of CCS: complex problems need to be solved by directly accessing standard CCS functions, that can be safely intermixed with ECCS calls.
On the other hand it implements higher level features and true class encapsulation mainly in the following areas:
The ccsERROR data structure used in CCS to implement the concept of "error stack" is replaced by the eccsERROR class (that can also be used whenever a ccsERROR is needed, being a subclass of the ccsERROR structure). This class encapsulates all the properties of an error stack and provides methods to access them.
Based on the assumption that applications (apart from very peculiar cases) never need more than one error stack, ECCS provides a default instance of eccsERROR called stdErr.
As a consequence, all normal applications based on ECCS can rely on this global error stack for all their error handling: errors can always be logged, retrieved and manipulated using stdErr and it can be passed to all standard CCS functions whenever an error stack is required. Temporary instances of eccsERROR class can be created when necessary, like when receiving replies for sent commands.
The eccsERROR_CLASS class provides a standard way to implement error handling inside C++ classes: every class that wants to provide error handling must be derived from this one. This is particularly important since not all the methods in a class can return a ccsCOMPL_STAT and as a consequence it is necessary to have another way of querying an instance of a class for the completion status of the last method called. Typical examples are constructors and access methods.
See the ECCS User Manual[6] for examples of reliable error handling techniques.
Extended CCS is particularly convenient to write real-time database access code.
It provides a set of database access classes that behaves as C native data types: reading and writing from database is done in a transparent way whenever the content of a database variable is used or assigned.
This applies also to database VECTORS and TABLES that can be treated as standard C arrays of scalars and structures.
The CCS message system is encapsulated in the msgMESSAGE class. This both eases and secures the usage of the message system calls. Buffer allocation is completely automatic and optimized.
The items of the data structure describing the command are not accessible directly, but only through interface methods. This allow reliable allocation of buffers and checks for correct values of parameters.
Sending and receiving of messages are implemented as methods of the msgMESSAGE class and whenever a message is received, the object is setup with all the information necessary to send back a reply to the original sender using the same instance.
Every application receiving/sending messages should do it through instances of the msgMESSAGE class, possibly developing specialized subclasses application dependent.
A similar class wrapping has been implemented for the CCS event system, based on two classes:
During development, debugging and testing of applications it is quite often necessary to introduce in the code special features for handling of errors and logging of diagnostic. Extended CCS provides a standardized set of macros and functions that must be used for this purpose.
The output generated by these functions can also be post-processed by the eccsTestDriver utility to produce non-regression tests.
This utility support application's testing based on the concept that any program can be used for non-regression tests, provided that its output is deterministic and always the same from run to run. The test, consisting in the execution of one or more concurrent processes, is executed under the control of the tool, that collects the output produced by all the processes. This output is then filtered for structures that can be different from run to run (like the execution time) and compared with a reference output (generated on a previous run and checked by hand by the author of the test). If the two set of data are the same, the test is considered passed, otherwise failed.
In this way the development of test programs is much easier, since it is not necessary to test inside the program for the expected result of any action and parts of real application programs generating unexpected results can be directly used to test bug fixes and document them.
All the modular tests of ECCS and EVH have been developed using this technique and the code in the test subdirectory of the source's module can be used as an example for implementing new test programs.
Also the tests for all TCS modules (like prs, trkws and msw) have been developed in the same way. The test reports for these modules provide more details about the way testing is performed (look for example in [24][25]).
This test strategy can be integrated with the TAT system[26].
The Extended CCS User Manual [6] describes in great details the implemented features and contains a lot of examples, that are also available with the module's source code and that can be used as application's templates. The reader is strongly encouraged to read this manual before designing any application. The manual contains also a "caveats" section where the more common problems encountered using the library are described.
4.6.4 The Event Handling Toolkit
The event handling toolkit is a major step forward to a full implementation of OO concepts.
The classes in this library implement an event driven architecture that can be used as a skeleton for many coordination applications running at workstation level.
It is based on the consideration that coordination modules receive commands from external applications, analyse and elaborate the incoming data and initiate all the necessary actions sending requests to other coordinating modules and to subsystems. They must be ready to process a new command within a typical "command response time" of 100 milliseconds and actions must be initiated and handled in parallel as much a possible.
It enforces also as much as possible standard rules and guidelines as the ones described in the other sections of this document.
The Event Handling Toolkit User Manual [7] describes in great details the implemented features and contains a lot of examples, that are also available with the module's source code and that can be used as application's templates. The reader is strongly encouraged to read this manual before designing any application. The manual contains also a "caveats" section where the more common problems encountered using the library are described.
The usage of this class library and in particular the design of applications based on it require at least a basic knowledge of principles of Object Oriented Programming, since the concepts of inheritance and polymorphism must be well understood.
4.6.4.1 The evhHANDLER class
The core of the event-driven architecture is based on the services provided by the evhHANDLER class. It is based on the "callback" concept and is similar to many other event-driven toolkits, like the X Window System.
Every process built on the EVH library contains an instance of the evhHANDLER class that can be referenced as a global variable (called evhHandler).
This object provides a MainLoop() method, whose purpose is to receive and parse all the incoming events, like:
After initialization, an application "enters the MainLoop" (i.e. it calls the MainLoop() method) and "exits the MainLoop" (i.e. returns from the function) only when it has completed its work and is ready to terminate.
Whenever an event appears on the queue of CCS events, the evhHandler takes care of receiving and parsing it. Then it searches an internal database for callbacks to be executed on response to that specific event.
A callback is a user function, dynamically registered in the database, to be called when the associated event occurs. All application code is always executed from within a callback and callbacks are always selected on the bases of the received events: this is why this technique is called event-driven.
At initialization time, an application registers a basic set of callbacks, typically the ones to be executed when one of the supported commands is received. The set of installed callbacks can be modified dynamically while the application is running; for example, when a command is sent, callbacks are registered in order to handle reply, error reply and timeout for the command and they are removed when the final reply has been received.
Several callbacks, in principle unlimited, can be registered for the same single event. For synchronization purposes, it is also possible to define callbacks to be invoked after a combination of events.
See the EVH User Manual[7] for details on callback handling and on other methods provided by the evhHANDLER class.
While an application is executing callback's code, all incoming events are queued and can be received only when it returns in the MainLoop. As a consequence, in order to have a soft real-time behaviour (coordinating modules have only soft and not hard real-time requirements), every list of callbacks must return to the event handler within the "command response time" of 100 milliseconds, so that the event handler can process other events. The event handler itself takes care of checking this condition, issuing error messages if it is not met.
The design of every process consists mainly in the design of the independent objects providing the methods to be attached as callbacks to the event-handler. Whenever an object is waiting for commands or for messages and other events from subsystems, the event-handler is ready to receive events and dispatch them to other objects.
4.6.4.2 Application objects: the evhTASK classes
The library provides a set of classes, based on the evhTASK class, to help implementing the handling of logical "TASKS" inside an application, where a logical "task" is the set of all actions related to the handling of an event or a set of related events, such as receiving a command, analyzing it, dispatching new commands to controlled applications, collecting the replies and sending back the final reply to the originator of the first command.
Every application will be based on one or more instances of classes derived from evhTASK, depending on how the logical "tasks" are grouped.
In general a logical "task" can be identified with all the actions related to the reception of an event. If two or more tasks are strictly related to each other (they act on the same data, are mutually exclusive, must be synchronized in some way) it is better to handle them within the same evhTASK instance. If they are completely unrelated, they are better handled by completely independent instances.
Just for the sake of an example, we can describe three different architectural scenarios in which many applications can be fitted:
This is usually a good approach for simple servers, able to understand just a few commands and with no need of complex data/message handling.
A single subclass of evhTASK is created.
It contains all the data required by the application and all the callbacks to handle the events that the application can receive.
The implementation of specific message handling protocols can be done adding as data members instances of support classes, like evhTIMER, evhCOMMAND and so on.
Many of the examples provided with the library are of this kind.
When the application must perform a lot of data handling and/or when the handling of received commands involves sending many commands to other applications and handling synchronization among all these partners it is better to have more evhTASK objects, each one specialized in handling one command or a set of similar commands.
If some data need to be accessed by all these objects, it is a good practice to implement a "data model" of the application, i.e. an object containing all the data that describe the application, and pass it as a reference to the instances of evhTASK. Encapsulation of data through access methods allow to implement synchronization and resource access control. Sharing of data can also be implemented through a proper usage of the on-line database.
The implementation of specific message handling protocols can be done adding as data members instances of support classes, like evhTIMER, evhCOMMAND and so on.
The modules of TCS are examples of this kind.
When an application must be able to perform fully independent tasks, the better solution is to implement each of them as an instance of evhTASK.
The various subclasses of evhTASK already provided within the library have been designed to suit specific architectural requirements and to enforce the implementation of VLT standards. As soon as new classes will be developed to answer to specific application needs, they will be added to the library.
It is very important to use the provided classes, deriving from them subclasses to implement new communication protocols.
What follows is a list of the most important ones:
evhTASK - To implement simple servers, independent from the database. It implements only some very basic commands.
evhDB_TASK - This class maintains state attributes on a specific database point. It must be used to implement standard VLT applications, since it provides the basic structure and handling of its on-line database image.
evhSTD_COMMANDS - Is derived from evhDB_TASK and implements all VLT standard commands. Commands can be overloaded to implement a specific behaviour or deactivated to leave their implementation to other more specific objects. A simple VLT application can just create the application class as a subclass of evhSTD_COMMANDS, adding the handling for its own specific commands. A more complex application can use this class just to handle the simple standard commands and create other instances of subclasses of evhTASK to implement its specific behaviour.
evhDB_CMD_SEND - Is a class specialized in sending commands to a specific counterpart and handling replies. Along with its subclasses it implements complex command handling issues like aborting a previous command or putting a new command in a queue and send it only when the previous one has been already processed, handling of timeouts and errors and so on. A small finite state automata, whose state is maintained in the database, control how commands are sent and can be used by other objects or applications to check what is going on and to synchronize on command completion.
4.6.4.3 High-level communication patterns
The CCS Message System[1] provides the communication protocol that must be used to exchange messages between processes, but it does not provide a way to enforce standard communication patterns, i.e. that under the same conditions two applications communicates with the same ordinate sequence of messages. The only defined rule states that "for every command there must be at least one reply".
On the other hand, having common communication patterns makes applications easier to maintain and debug and makes it easier to define the interface between them.
The EVH class library provides also basic classes to implement and handle standard sequences of messages and events.
Instances of these classes (or new subclasses) usually appear as data members of evhTASK objects or are dynamically created inside callbacks to handle specific events.
In order to explain this concept we take the typical case of an application that receives a command, makes some work on the data, sends new commands to some other applications and, when it has received all the replies, sends back the final reply to the originator of the command.
This is a very common pattern: many VLT coordination applications will have to implement this behavior, handling properly the sending of the command, the reception of the replies and error replies and an action to be triggered if no reply is received within a reasonable period of time.
To help in understanding the example we make use of an "interaction diagram" that shows the messages exchanged by the involved objects versus time. Notice that a message between objects in the same process consist just in a method's call, while messages across processes are real CCS messages.
"Interaction diagrams" are a very useful way to describe a temporal sequence of events and to provide the reader of design documents and user manuals with scenarios of how an application behaves when specific events are received.
The given example shows the sequence of messages exchanged in the normal, successful, case. Other diagrams should be used to depict error cases.
The sample application, able to receive a command and to dispatch new commands to other processes, is implemented with a subclass of evhTASK.
It installs at initialization time a callback for the command it must be able to handle and defines as data members an instances of evhCOMMAND for each command it must send and an instance of evhSYNC to synchronize all the replies.
Whenever the command is received, the event handler calls the HandleCommandCB() callback function
The body of the callback makes all the necessary calculations and uses the instances of evhCOMMAND to send the commands to the partner processes, using the Send() method; it also initializes the evhSYNC with a callback to be executed when all replies have been received, calling the Run() method.
Whenever a reply is received, it is transparently dispatched to the instances of evhCOMMAND and to the evhSYNC. Only when the final reply for the last command has been received, the evhSYNC takes care of calling the CommandCompletedCB()callback function.
This callback just sends back the final reply.
The ErrorInCommandCB() callback is called whenever an error reply is received or in case of timeout, but this case is not shown in the sample interaction diagram.
By using these support classes, it is necessary only to write the code for the 3 callbacks and all the exchange of messages between the processes and error handling are treated transparently in a standard way.
What follows is a list of the most important classes of this type:
evhDB_COMMAND - Is a subclass of evhCOMMMAND, but the destination of the command is retrieved from the database. This makes applications configurable in a standard way and allow special treatments of commands; for example the evhDB_LIST_COMMAND subclass allow to send the same command to a full list of targets (stored on the database) handling the callbacks and replies transparently, as if it was a command sent to only one destination.
evhSYNC - Handle the synchronization on a set of events. The installed callback is executed only when all the expected events have been received.
evhTIMER - Handle a timer for periodical events. At every cycle (i.e. whenever the timer expires) the installed callback is executed.
4.6.5 Application design versus database design
There is a tendency to view the design process as two different activities: database design an application design. On the database, it is thought that through dbl it is possible to use object oriented concepts. On the application side, object orientation is linked to whether C or C++ is used to program the application.
This is far from true. The design process is only one logical task. There is no such separation: database design, application design.
The database is not more than persistence storage. Good design practices do not care too much on where the data is stored (memory, disk, database, files, etc.). This practical issue is left for the final steps of the design process where implementation considerations are incorporated and a decision is taken on which classes are needed on persistence storage.
The recommendation is then, to pay attention to the application design process itself. It should produce a class hierarchy which is simple, consistent and able to represent all the complexity of the problem to be solved. Here the guidelines are then no more than "good object oriented design".
For the subset of classes requiring persistent storage, the on-line database is used. With the object oriented features of dbl, the translation process from a generic class to a database class is a straight forward process. Only minor restrictions to the object model exist on what it can be specified on a dbl class. The choice of C or C++ as the application programming language does not limits the use of an object oriented design.
On the next paragraphs, the mechanics of this translation will be shown.
4.6.5.1 A generic class
A generic class can be formally expressed:
<scope_1><type_1> <function_1> ( <param_list_1> );
<scope_2><type_2><function_2> ( <param_list_2> );
<scope_n><type_n> <function_n> ( <param_list_n> );
where n is number of data members and m is the number of fuction member.
<scope> private | public | protected
<type> <basic_type> | <class_type>
<function> function member name
<param_list> member function parameter list
<basic_type> vltINT8 | vltUINT8 | vltINT16 | vltUINT16 | vltBYTES | .....
<class_type> a predefined class
From this generic class we must derive two related classes. One for dbl specification and the other for the C++ program accessing the class.
4.6.5.2 dbl representation
The translation steps to transform from a generic class to the dbl equivalent database class are:
The elimination of functions and scope of the class definition is done because dbl does not support them. The transformation of the syntax is a very simple step. The dbl manual shows examples of its syntax and how can it is used to define a database class. On the next paragraphs an example will be also given.
4.6.5.3 C++ representation
The process of translating from the generic class to the C++ class is also an straight forward. The actions to perform are:
On step 3.c. when the data member type is a <basic_type> ECCS database access classes are used. There is one ECCS class for every <basic_type> available, see ECCS user manual. Data member of <class_type> are created with the corresponding <class_type> constructors.
4.6.5.4 C representation
On plain C some of the object oriented features of the generic class have to be removed. The scope and inheritance are not easily mapped to C. Besides that, many options can be chosen in order to represent a generic class in C. All the options compromise the use of ECCS basic types database access class. ECCS can be used from a plain C program as long as it is compiled with C++ option.
4.6.5.5 Example
A generic class STEP_MOTOR is part of a design:
class STEP_MOTOR : public motor {
STEP_MOTOR(vltLOGICAL st=9, vltFLOAT ss=2){State=st;StepSize=ss);}
int Rotate( vltFLOAT degrees );
This class is inheriting from the base class motor. Composition is used on member Controller which is of type IOControll class. This two mechanisms expand the behavior of the class being written without adding any extra or duplicate code. All the functionality of the base motor class and of the Controler are implicit in this new class STEP_MOTOR.
The corresponding dbl class obtained from applying rules given on 3.4.2 is :
ATTRIBUTE VECTOR Params( 32,uint8);
ATTRIBUTE IOCONTROLL Controler;
the C++ class derived from applying rules 3.4.3 is :
class STEP_MOTOR : public motor {
STEP_MOTOR(vltLOGICAL st=9, vltFLOAT ss=2, dbSYMADDRESS addr);
eccsDB_VECTOR *Params (32,uint8);
int Rotate( vltFLOAT degrees );
STEP_MOTOR::STEP_MOTOR( vltLOGICAL st, vltFLOAT ss, dbSYMADDRESS addr )
State = new eccsDB_LOGICAL( addr, "State" );
StepSize = new eccsDB_FLOAT( addr, "StepSize");
eccsDB_VECTOR_vltUINT8 Params( addr );
Controller = new IOControll( addr );
int STEP_MOTOR::Rotate( vltFLOAT degrees )
// Read Write access the data members
one of the possible C equivalent code is obtained by mapping the class to a plain C structure. Functions should be taken out of the class and defined somewhere else. The encapsulation keyword public is not allowed. Inheritance is simulated via composition of a new structure member of the base type.
In this example an allocation and deallocation function is defined on every class designed. This allows safer memory handling, concentrating all memory request and freeing. Besides that, this C version is similar to the C++ version. Since the new operator is not available on plain C it was replaced by a pair of instructions as seen on the next lines.
eccsDB_VECTOR *Params (32,uint8);
motor motor; // use composition instead of inheritance
typedef STEP_MOTOR_class STEP_MOTOR;
The constructor and destructor functions are replaced by global C functions accomplishing the task of allocating and deallocating an instance of the class as explained above.
STEP_MOTOR*STEP_MOTOR_alloc(vltLOGICAL st,vltFLOAT ss,dbSYMADDRESS addr);
void STEP_MOTOR_dealloc(STEP_MOTOR *sm_instance);
int Rotate( STEP_MOTOR *sm_instance , vltFLOAT degrees );
//replace C++ constructor function
STEP_MOTOR *STEP_MOTOR_alloc(vltLOGICAL st,vltFLOAT ss,dbSYMADDRESS addr)
instance = ( STEP_MOTOR*)calloc(1,sizeof(STEP_MOTOR));
eccsDB_LOGICAL StateVar( addr, "State" );
eccsDB_FLOAT StepSizeVar( addr, "StepSize");
eccsDB_VECTOR_vltUINT8 Params( addr );
instance->State = &StateVar; //replace for C++ operator new
instance->StepSize = &StepSizeVar; //replace for C++ operator new
instance->Params = &Params; //replace for C++ operator new
instance->Controller = IOControll( addr );
instance->motor = motor( addr );
//replace C++ class destructor function
void STEP_MOTOR_dealloc( STEP_MOTOR *sm_instance)
Controller_dealloc( sm_instance->Controller); // IOControll dealloc.
motor_dealloc( sm_instance->motor ); // class motor dealloc.
int Rotate( STEP_MOTOR *sm_instance, vltFLOAT degrees )
// Read Write access the data members
Quadralay Corporation http://www.webworks.com Voice: (512) 719-3399 Fax: (512) 719-3606 sales@webworks.com |