Personal tools
You are here: Home HowTos CPP: Adding Dynamic Attributes to a Device Server
Document Actions

CPP: Adding Dynamic Attributes to a Device Server

by Sergi Rubio last modified 2007-03-12 20:44

Do you need to create more or less Attributes depending on a certain Property value? This brief HowTo implements it with a Tango Device Server.

Creating new attributes at Startup

by Sergi Rubio Manrique, srubio@cells.es

Modifications suggested by R.Bourtembourg (bourtemborg@esrf.fr) and E.Taurel (etaurel@cells.es).

 

Do you need to create more or less Attributes depending on a certain Property? Are you interested in changing the name/type/dimensions of your attributes depending on different applications?

That is common while working with Input/Output devices with a variable number of channels (PLCs, DaqBoards, Timers/Counters), when depending of the operation mode more or less scalar or spectrum attributes could be needed.

This brief HowTo implements it with a Tango Device Server.


Creating the Device Server


We have a Device Server called DynamicDS. This device server provides as many Read/Write Spectrum Attributes as it has been previously specified in a “DynamicSpectrumAttributes” (string array) property:

DynDS1
 

First of all I have created the DeviceServer with the DynamicXXXXXAttributes properties and an Attribute for each kind of attribute you need (scalar, spectrum or image). They will be used as “templates” for the Attributes that are going to added at device startup.

 

Modifying DynamicDSClass.h


Adding a generic constructor to the SpectrumAttributeAttrib Class allows us to play with some parameters of the Attributes (type, size, names, etc ...).
class SpectrumAttributeAttrib: public Tango::SpectrumAttr

{
public:
SpectrumAttributeAttrib():SpectrumAttr("SpectrumAttribute", Tango::DEV_DOUBLE, Tango::READ_WRITE, 1000) {};
SpectrumAttributeAttrib(const char *name, long data_type, Tango::AttrWriteType w_type, long s_size):SpectrumAttr(name,data_type,w_type,s_size) {};
~SpectrumAttributeAttrib() {};
virtual void read(Tango::DeviceImpl *dev,Tango::Attribute &att)
{(static_cast<DynamicDS *>(dev))->read_SpectrumAttribute(att);}
virtual void write(Tango::DeviceImpl *dev,Tango::WAttribute &att)
{(static_cast<DynamicDS *>(dev))->write_SpectrumAttribute(att);}
virtual bool is_allowed(Tango::DeviceImpl *dev,Tango::AttReqType ty)
{return (static_cast<DynamicDS *>(dev))->is_SpectrumAttribute_allowed(ty);}
};

OR USING ONLY ONE CONSTRUCTOR (Would be perfect if POGO did it by default):

SpectrumAttributeAttrib(const char *name="SpectrumAttribute", long data_type=Tango::DEV_DOUBLE, Tango::AttrWriteType w_type=Tango::READ_WRITE, long s_size=1000):SpectrumAttr(name,data_type,w_type,s_size) {};

This class could be added from POGO and modified later (taking advantage of the already defined virtual methods) or can be added manually.  Attr/SpectrumAttr/ImageAttr constructors can be used directly also.

BE CAREFUL!: POGO could overwrite it if you regenerate source!

This is the content of the erase/addDynamicAttributes functions added inside the DynamicDSClass.h file (DynamicDS must be replaced with the name of the class of your device):

class DynamicDSClass : public Tango::DeviceClass
{
public:
// properties member data

// add your own data members here
//------------------------------------
/// Vector added to remember which was the default Attribute list (w/out dynamic attrs)
vector<string> default_Attribute_list;
/// Function that diferentiates dynamic attributes from static ones
bool isDynamicAttribute(string attrName)
{
bool isIn=false;
for (int j=0; j<default_Attribute_list.size(); j++) {
if (attrName==default_Attribute_list[j]) {
isIn=true; j=default_Attribute_list.size();
}
}
if( (attrName == "SpectrumAttribute" ||
attrName == "ScalarAttribute" ||
attrName == "ImageAttribute" ||
!isIn) &&
(attrName!="State" &&
attrName!="Status"))
return true;
else return false;
}
/// Function that removes dynamic attributes from the class attributes list
void eraseDynamicAttributes(vector<Tango::Attr *> &att_list)
{
/// Erasing undesired attributes
vector<Tango::Attr *>::iterator a;
for(a=att_list.begin(); a != att_list.end(); ++a)
{
if(isDynamicAttribute((*a)->get_name()))
{
cout << "Erasing attribute "<<(*a)->get_name()<<endl;
delete (*a);
att_list.erase(a);
--a;
} //else cout << "Standing attribute "<<(*i)->get_name()<<endl;
}
}
eraseDynamicAttributes also removes the attribute template added in Pogo.
	/// Function that adds dynamic attributes to the devices attributes	 lists
void addDynamicAttributes(const Tango::DevVarStringArray *devlist_ptr)
{
Tango::Util *tg = Tango::Util::instance();
cout << "DYNAMIC ATTRIBUTES GENERATION ... addDynamicAttributes(Tango::DevVarStringArray*)" << endl;
for (unsigned long i=0 ; i < devlist_ptr->length() ; i++)
{
int numOfAttr;
Tango::DeviceImpl *mydev = tg->get_device_by_name(((string)(*devlist_ptr)[i]).c_str());

DynamicDS *myds = static_cast<DynamicDS *> (mydev);
numOfAttr = myds->dynamicSpectrumAttributes.size();

cout << "Device "<<((string)(*devlist_ptr)[i]).c_str()<<" will have " << numOfAttr << " Dynamic Attributes." << endl;
cout << "Names are "; for (int j = 0; j <numOfAttr ; j++) cout<<myds->dynamicSpectrumAttributes[j]<<" "; cout<< endl;

for (int j = 0; j <numOfAttr ; j++) {
Tango::Attr *lval, *cspec;
char attrname[256], strtmp[256];
Tango::UserDefaultAttrProp attrprop;
if (myds->dynamicSpectrumAttributes[j]=="") return;
sprintf(attrname,"%s",myds->dynamicSpectrumAttributes[j].c_str());
cspec = new SpectrumAttributeAttrib (attrname,Tango::DEV_LONG,Tango::READ);
sprintf(strtmp,"Dynamic Attribute number %d ",j);
attrprop.set_description(strtmp);
cspec->set_default_properties(attrprop);
myds->add_attribute(cspec);
}
}
}

The same code placed at the DeviceServerClass.cpp or inside a command in DeviceServer.cpp files creates the new attributes at startup or runtime

Modifying DynamicDSClass.cpp


We must read the DynamicSpectrumAttributes property values for each of the devices and construct different names for the attributes. We’ll add this code inside the DynamicDSClass::device_factory(...) method:
void DynamicDSClass::device_factory(const Tango::DevVarStringArray *devlist_ptr)
{
// Erase all the dynamic attributes previously added to the class
//-------------------------------------------------------------
eraseDynamicAttributes(get_class_attr()->get_attr_list());
// Create all devices.(Automatic code generation)
//-------------------------------------------------------------
for (unsigned long i=0 ; i < devlist_ptr->length() ; i++)
{
cout4 << "Device name : " << (*devlist_ptr)[i].in() << endl;
cout << "Creating Device name : " << (*devlist_ptr)[i].in() << endl;

// Create devices and add it into the device list
//----------------------------------------------------
device_list.push_back(new DynamicDS(this, (*devlist_ptr)[i]));
}

// Add your own code here
//-----------------------------------------
addDynamicAttributes(devlist_ptr);

// Export device to the outside world (Automatic code generation)
//-------------------------------------------------------------
for (unsigned long i=1 ; i <= devlist_ptr->length() ; i++)
{
//For exporting it must be done in INVERSE ORDER!!!!! (Because device_list is a LIFO)
cout << "Exporting Device name : " << (*devlist_ptr)[devlist_ptr->length()-i].in() << endl;
// Check before if database used.
//---------------------------------------------
if ((Tango::Util::_UseDb == true) && (Tango::Util::_FileDb == false))
export_device(device_list[device_list.size()-i]);
else
export_device(device_list[device_list.size()-i],(*devlist_ptr)[devlist_ptr->length()-i]);
}
// End of Automatic code generation
//-------------------------------------------------------------
}

The “for” bucle has been split in two parts: devices creation and export.

When a new attribute is added to the class it affects for the current device and all the devices created afterwards, then DynamicAttributes creation must be executed after all the devices have been already created. In fact any device restarted later using the Init() method will add all the dynamic attributes previously added to the rest of devices, this bug has been solved performing dynamic attributes erasing inside the attribute_factory method and before devices creation inside device_factory.

Inside the exporting method has been changed the way is chosen the device to export. Must be exported only the last devices added to device_list, number which is given by the devlist_ptr length.

To avoid the “Template Attribute” to be added to the class you must remove it from the class with all the previously created dynamic attributes at the end of DynamicDSClass::attribute_factory(...) method, in addition we will store a list with the original attributes names to diferentiate dynamic from static methods:
	//	End of Automatic code generation
//-------------------------------------------------------------
vector<Tango::Attr *>::iterator i;
if (!default_Attribute_list.size()) {
printf("Saving default attribute list (not-Dynamic Attributes) ...\n");
for (i=att_list.begin(); i != att_list.end(); ++i) {
default_Attribute_list.push_back((*i)->get_name());
printf("\tAdded %s\n",default_Attribute_list[default_Attribute_list.size()-1].c_str());
}
}
eraseDynamicAttributes(att_list);
}


Modifying DynamicDS.cpp


The pointers to the read values of the dynamic attributes should be initialized by default inside the init_device method as arrays to mantain independent the values of each of the attributes.
attr_SpectrumAttribute_read = new Tango::DevDouble[dynamicSpectrumAttributes.size()];
They must be freed inside the delete method.
if (attr_SpectrumAttribute_read)
delete[] attr_SpectrumAttribute_read;
Adding a line of code for each of the properties at the end of the get_device_properties method we force it to be created in the database and showed by Jive:
i=-1;
if (dev_prop[++i].is_empty()==true) dev_prop[i] << dynamicSpectrumAttributes;
...
get_db_device()->put_property(dev_prop);


All the Read/Write access to the attributes will be managed in the write_ / read_NameOfTemplateAttribute(...) methods. You must link each of the different attribute names to the appropiated internal variables using a vector with identifiers for each attribute name or any method you prefer:

void DynamicDS::read_SpectrumAttribute(Tango::Attribute &attr)
{
DEBUG_STREAM << "DynamicDS::read_SpectrumAttribute(Tango::Attribute &attr) entering... "<< endl;
int index=-1;
char tmp[10];
for (int i=0;i<dynamicSpectrumAttributes.size() && index<0;i++) {
if (!strcmp(attr.get_name().c_str(),dynamicSpectrumAttributes[i].c_str())) index=i;
}
(*(attr_SpectrumAttribute_read+index)) = READ_YOUR_HARDWARE_VALUE(index);
DEBUG_STREAM << "Data Returned defined by attr_SpectrumAttribute_read: " << (*(attr_SpectrumAttribute_read+index))<< endl;
attr.set_value(attr_SpectrumAttribute_read+index);
}

Behaviour


Additional attributes are added after modifying the property value and restarting the Device Server:

DynDS2


Updated, 12th March 2007

Posted by srubio at 2007-03-12 20:49
I've modified the HowTo just to avoid some problems:

- Attributes were added to all the devices created or restarted later: now not static attributes are deleted before device creation.

- Using only one pointer and method for all dynamic attributes could cause mismatching between attribute values: now an array is used to store the values and there's a separate pointer for each dynamic attribute.

- Functions pasted "in the middle of nothing": the erase/addDynamicAttribute methods are now placed inside the DynamicDSClass header.

- The vectors device_list and dev_list_ptr not always have the same size: the export bucle has been modified to avoid problems with it.

Powered by Plone, the Open Source Content Management System

This site conforms to the following standards: