Information
We are currently investigating an issue with the editor of some pages. Please save your work and avoid to create new pages until this banner is gone.
...
Wiki Markup |
---|
Override component lifecycles method
*/
virtual void *execute{*}() throw (acsErrTypeLifeCycle::LifeCycleExImpl);
\\
/* --------------- \[ Action implementator interface \] -------------- */
\\
/** |
Wiki Markup |
---|
Implementation of async. *reset{*}() method - called by invokeAction
*/
virtual ActionRequest
*reset{*}Action(BACICComponent * cob, const int& callbackID, const CBDescIn& descIn,
BACIValue* value, Completion& completion, CBDescOut& descOut);
\\
\\
/* --------------------- \[ CORBA interface \] ----------------------*/
\\
/** |
Property status contains the status of the power supply.
*/
virtual ACS::ROpattern_ptr
status ()
throw(CORBA::SystemException);
The private fields usually consist of all variables that are in the program. We use a smart pointer for each property. The type of each smart pointer is a template based on the type of the related property. The smart pointers for the properties take care of all the CORBA detail in behalf of the developer making the programming of a device server easier, faster and safer.
private:
/// Properties
SmartPropertyPointer<ROdouble> m_readback_sp;
SmartPropertyPointer<RWdouble> m_current_sp;
SmartPropertyPointer<ROpattern> m_status_sp;
};
#endif /* acsexmplPowerSupplyImpl_h */
The execute method is part of the life cycle methods. Ther are four methods for the life cycle. They are defined in acscomponent and can be overridden by the developer.
virtual void initialize()
throw (acsErrTypeLifeCycle::acsErrTypeLifeCycleExImpl);
virtual void execute()
throw (acsErrTypeLifeCycle::acsErrTypeLifeCycleExImpl);
*/
virtual void cleanUp();
virtual void aboutToAbort();
The intialize method is called by the container after instantiating the component i.e. after executing the constructor. The developer should insert here all the code related to the initialization of the component like, for example retrieve connections, read in configuration files orparameters, build up in-memory tables and so on. This method is called before any functional requests can be sent to the component.
The execute method is called by the container after the initialize to signal that the server has to be ready to accept functional requests.
The developer in the execute and the initialize can throw an exception to signal a malfunction to the container. In this case the exception is catched by the container and the component will be released and unloaded.
The aboutToAbort is called by the container in case of an error and an emergency sistuation requests the component to be destroyed. The developer should insert here the code to handle this situation trying to release resources and so on. This method is called in an emergency situation and there is no warranty that the container will wait its termination before destroying the component.
Finally, the cleanUp method is called by the container before destroying the server in a normal situation. The developer should release all the reasource and perform all the clean up here.
As a guideline, we suggest to leave the constructor and the destructor of the server empty moving the code in the life cycle method. This should help in writing more reliable servers.
There is no need to call the parent life cycle method
In the power supply component, execute is the only life cycle method overridden.
Together with the life cycle methods there is the concept of the state of the component i.e. the component passes through different states during its life, since it is built until it is destroyed. The state is transparent to the user and managed by the ComponentStateManager object that is part of the ContainerServices. The state of the component is managed by the container and must not be accessed nor modified by the developer. For completeness I report here the states that a component can assume, as defined in acscomponent.idl:
COMPSTATE_NEW | This is the initial state of the component |
COMPSTATE_INITIALIZING | When the component is executing the initialize method |
COMPSTATE_INITIALIZED | When the initialize method terminates |
COMPSTATE_OPERATIONAL | When the execute method terminates |
COMPSTATE_ERROR | An error occurred |
COMPSTATE_DESTROYING | Before executing the cleanUp method |
COMPSTATE_ABORTING | Before executing the aboutToAbort method |
COMPSTATE_DEFUNCT | Before destroying the component |
Anchor | ||||
---|---|---|---|---|
|
Anchor | ||||
---|---|---|---|---|
|
...
1 | A class constructor is always the method with the name of the class. |
3-5 | In the constructor we create properties. Their names must be composed of the name of the server and property name (for example: TestPowerSupply1:current). In property constructor you must also provide a reference to the component. Here the getComponent() method (inherited from CharacteristicComponentImpl) is used to obtain the Component reference. Every Component has its own instance, which takes care of threads (dispatching). It must have a name and it's Unique Identification Number (UID). UID should always be the same (even when server is restarted) and it will be read from local database. There may not be two servers with the same name and/or same UID! |
10 | The created property is assigned to the smart pointer. This step is necessary if the smart property is created without a pointer to a property in the constructor (see row 5). |
Wiki Markup |
---|
\\
\\
\\
The destructor is empty. The entire cleanup for the properties is managed by the smart pointers.
\\
*PowerSupply{*}::~{*}PowerSupply{*}()
\{
\}
\\
Let us come to actions now. All requests for asynchronous actions go through the Activator, which sends this request back to the device server. This is done through the invokeAction() method, where you define what has to be done when a call is received:
\\
/* --------------- \[ Action implementator interface \] -------------- */
\\
ActionRequest
PowerSupply::invokeAction(int function, BACIComponent* cob, const int& callbackID,
const CBDescIn& descIn, BACIValue* value, Completion& completion,
CBDescOut& descOut)
\{
switch (function)
\{
case *ON{*}_ACTION:
return *on{*}Action(cob, callbackID, descIn, value, completion, descOut);
case *OFF{*}_ACTION:
return *off{*}Action(cob, callbackID, descIn, value, completion, descOut);
case *RESET{*}_ACTION:
return *reset{*}Action(cob, callbackID, descIn, value, completion, descOut);
default:
return reqDestroy;
\}
\}
\\
\\
The implementation of the functions we defined in the previous method:
\\ |
Wiki Markup |
---|
/* ------------------ \[ Action implementations \] ----------------- */ |
...
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="78d44db0-9a27-45e8-9ec5-ef644b673492"><ac:plain-text-body><![CDATA[ | 11 | Here comes real implementation of the action – like communication with physical device, setting it on, off, etc. (for example: a devIO->write(...)). If action will not be completed in descIn.timeout or action is slow in progress, you should first return "working" (return reqInvokeWorking) and also set estimated timeout (descOut.estimated_timeout) (more about it in [R02], part 3.3.1.2). | ]]></ac:plain-text-body></ac:structured-macro> |
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="93ffd88f-9e7c-49ae-a240-720cc6a73dde"><ac:plain-text-body><![CDATA[ | 13 | To inform client about possible errors, set completion (read [R02], part 3.2). In this case everything is ok and we set completion to OkCompletion. | ]]></ac:plain-text-body></ac:structured-macro> |
18 | When action is completed, return done (return reqInvokeDone). |
Wiki Markup |
---|
\\
The offAction() and resetAction() functions are based on the same rule - just replace the name and the part where the device server actually does something.
\\
\\
We have to describe the device server's actions (like on(), off(), reset()). These are asynchronous (usually they take some time and we do not want to block a client while executing) so we have to register the action to the Activator that will then do the rest of the procedure. For example, calling on() will just register that action inside a queue until Activator calls PowerSupply::invokeAction which will in turn, call onAction().
\\
The client must provide a pointer to it's implementation of callbacks and the structure of type CBDescIn (that identifies the callback) as an argument of the registerAction() method. Our device server sends these parameters to Activator and also adds a pointer to itself and a description of an action (integer - ON_ACTION will be replaced with 1, OFF_ACTION with 2 and so on).
\\
Your job is to write one block of a code for each action and change only the last argument of the method registerAction - an integer which is later sent back to the device server, to the method invokeAction(). First argument of registerAction() method - in this case BACIValue::type_null - is a callback type.
\\
void
*PowerSupply::on* (ACS::CBvoid_ptr cb, const ACS::CBDescIn & desc)
throw(CORBA::SystemException)
\{
getComponent()->registerAction(BACIValue::type_null, cb, desc, this, *ON{*}_ACTION);
\}
\\
void
*PowerSupply::off* (ACS::CBvoid_ptr cb, const ACS::CBDescIn & desc)
throw(CORBA::SystemException)
\{
getComponent()->registerAction(BACIValue::type_null, cb, desc, this, *OFF{*}_ACTION);
\}
\\
void
*PowerSupply::reset* (ACS::CBvoid_ptr cb, const ACS::CBDescIn & desc)
throw(CORBA::SystemException)
\{
getComponent()->registerAction(BACIValue::type_null, cb, desc, this, *RESET{*}_ACTION);
\}
\\
throw(...) defines what kind of exceptions are available.
\\
\\
If a client wants to do something with current, readback, and other properties, it must get a reference to these properties. This is done in following code:
\\
ACS::{*}RWdouble{*}_ptr
*PowerSupply::current{*}()
throw(CORBA::SystemException)
\{
if (m_current_sp==0)
return ACS::{*}RWdouble{*}::_nil();
\\
ACS::{*}RWdouble{*}_var prop = ACS::{*}RWdouble{*}::_narrow(m_current_sp->getCORBAReference());
return prop._retn();
\}
\\
ACS::{*}ROdouble{*}_ptr
*PowerSupply::readback{*}()
throw(CORBA::SystemException)
\{
if (m_readback_p==0)
return ACS::{*}ROdouble{*}::_nil();
\\
ACS::{*}ROdouble{*}_var prop = ACS::{*}ROdouble{*}::_narrow(m_readback_sp->getCORBAReference());
return prop._retn();
\}
\\
ACS::{*}ROpattern{*}_ptr
*PowerSupply::status{*}()
throw(CORBA::SystemException)
\{
if (m_status_p==0)
return ACS::{*}ROpattern{*}::_nil();
\\
ACS::{*}ROpattern{*}_var prop = ACS::{*}ROpattern{*}::_narrow(m_status_sp->getCORBAReference());
return prop._retn();
\}
\\
One must write accessors for every property, changing the name of a property (current), variable (m_current_sp) and type of a property (replace all fields where RWdouble appears with ROdouble or ROpattern).
\\
Now we are really near the end. We have to add MACI DLL support functions. For this purpose we use the macro MACI_DLL_SUPPORT_FUNCTIONS(class_name):
\\
/* --------------- \[ MACI DLL support functions \] -----------------*/
\\
#include <maciACSComponentDefines.h>
MACI_DLL_SUPPORT_FUNCTIONS ({*}PowerSupply{*})
\\
/* ----------------------------------------------------------------*/
\\
As we said at the beginning of the paragraph, it is possible to use the properties without smart pointers even if we discourage this implementation. We briefly describe herein the changes in the previous code if smart pointers were not used.
In the include file each property is defined without the templetized smart pointer type and, of course, the baciSmartPropertyPointer.h is not needed. Note that the names of the variables terminate with _p and not with _sp to highlight that we are now using pointers instead of smart pointers.
\\
*ROdouble* m_readback_p*
*ROdouble* m_readback_p;*
*RWdouble* m_current_p;*
\\
In the constructor these pointers are first initialized to 0 then each property is created in the same way they are created with smart pointers. The CHARACTERISTIC_COMPONENT_PROPERTY macro must be implemented for each BACI property. The macro performs error checking and generates a description of the property. This macro must not be used with smart pointers because they take care of all the initializations automatically.
\\
The following is the constructor without using smart pointers for the properties.
\\
*PowerSupply{*}::{*}PowerSupply{*}(const ACE_CString& name,
maci::ContainerServices * containerServices):
CharacteristicComponentImpl(name,containerServices),
*m_readback_p({*}0{*}),*
*m_current_p({*}0{*}),*
*m_status_p({*}0{*}) \{*
m_readback_p = new *ROdouble{*}(name+":{*}readback{*}", getComponent());
CHARACTERISTIC_COMPONENT_PROPERTY(readback, m_readback_p);
\\
m_current_p = new *RWdouble{*}(name+":{*}current{*}", getComponent());
CHARACTERISTIC_COMPONENT_PROPERTY (current, m_current_p);
\\
m_status_p = new *ROpattern{*}(name+":{*}status{*}", getComponent());
CHARACTERISTIC_COMPONENT_PROPERTY(status, m_status_p);
*\}*
\\
Another difference is in the destructor because for each property we must call the destroy method but not the delete.
\\
*PowerSupply{*}::~{*}PowerSupply{*}() \{
...
(m_readback_p) \{ m_readback_p->destroy(); m_readback_p = 0; \}
(m_current_p) \{ m_current_p->destroy(); m_current_p = 0; \}
if (m_status_p) \{ m_status_p->destroy(); m_status_p = 0; \}
\}The rest of the code remains the same apart for the renaming of the variables.
\\
\\
\\
\\ |
Anchor | ||||
---|---|---|---|---|
|
...
Anchor | ||||
---|---|---|---|---|
|
Anchor | ||||
---|---|---|---|---|
|
Wiki Markup |
---|
ACS_TRACE("::PowerSupply::~PowerSupply");
\\
ACS_DEBUG("::PowerSupply::~PowerSupply", "COB destroyed");
\\
ACS_LOG(LM_RUNTIME_CONTEXT, "PowerSupply/DLLOpen",
(LM_ERROR, "Failed to create Component - constructor returned 0!"));
Find out more about this in Logging and Archiving (\[R04\]). |
Anchor | ||||
---|---|---|---|---|
|
Anchor | ||||
---|---|---|---|---|
|
...