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


Forgot your password?
 

CPP: Adding Dynamic Attributes to a Device Server

Do you need to create more or less Attributes depending on a certain device property value? This brief HowTo details how this to implement such a feature. This HowTo is based on code generated by Pogo release 7

 

Creating new attributes at Startup

by Sergi Rubio Manrique, srubio@cells.es

Modifications suggested by R.Bourtembourg and E.Taurel.

 

Do you need to create more or less Attributes depending on a certain device 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 this feature in a Tango class base on code generated by Pogo 7.

Generating your class with Pogo

As an example, we will create a Tango class named DynAttr

with three types of attributes:

  1. StaticAttr: A scalar, Tango::DevShort, READ attribute
  2. LongDynAttr: A scalar, Tango::DevLong, READ_WRITE attribute
  3. DoubleDynAttr: A scalar, Tango::DevDouble, READ_WRITE attribute

Each device belonging to this class has one StaticAttr and a dynamic list of LongDynAttr or DoubleDynAttr attributes. This list of dynamic attributes is defined with a device property named DynAttrList

Therefore, using Pogo, we define a Tango class with three scalar attributes with the definition given above. We also create the DynAttrList property for defining dynamic attribute. This is a vector of strings with a couple of strings for each attribute. The first string is the dynamic attribute type (LongDynAttr or DoubleDynAttr) and the second string is the attribute name.

Pogo display

 

Generate the Tango class source code

Implementing the Dynamic Attributes

Pogo 7 has been modified to help user to create Tango class with dynamic attribute(s). Nevertheless, the code generated by Pogo release 7 needs some changes to fully support dynamic attributes. This means modifying code in non-protected area. Therefore, every time you will ask Pogo to re-generate you class, changes made in the non-protected area will be lost.

Change in the non-protected area

Two files have to be modified. These files are:

  1. DynAttrClass.h
  2. DynAttrClass.cpp

The file DynAttrClass.h needs to be modified in order to add Attribute class constructor which takes the attribute name as input argument. The following code is how the LongDynAttrAttrib and DoubleDynAttrAttrib attribute classes declaration looks like once they have been modified

//	Attribute LongDynAttr class definition
class LongDynAttrAttrib: public Tango::Attr
{
public:
	LongDynAttrAttrib():Attr("LongDynAttr",
	                   Tango::DEV_LONG, Tango::READ_WRITE) {};
	~LongDynAttrAttrib() {};

	LongDynAttrAttrib(const string &att_name):Attr(att_name.c_str(),           // Added code
	                   Tango::DEV_LONG, Tango::READ_WRITE) {};                 // Added code
	
	virtual void read(Tango::DeviceImpl *dev,Tango::Attribute &att)
		{(static_cast<DynAttr *>(dev))->read_LongDynAttr(att);}
	virtual void write(Tango::DeviceImpl *dev,Tango::WAttribute &att)
		{(static_cast<DynAttr *>(dev))->write_LongDynAttr(att);}
	virtual bool is_allowed(Tango::DeviceImpl *dev,Tango::AttReqType ty)
		{return (static_cast<DynAttr *>(dev))->is_LongDynAttr_allowed(ty);}
};


//	Attribute DoubleDynAttr class definition
class DoubleDynAttrAttrib: public Tango::Attr
{
public:
	DoubleDynAttrAttrib():Attr("DoubleDynAttr",
	                   Tango::DEV_DOUBLE, Tango::READ_WRITE) {};
	~DoubleDynAttrAttrib() {};

	DoubleDynAttrAttrib(const string &att_name):Attr(att_name.c_str(),       // Added code
	                   Tango::DEV_DOUBLE, Tango::READ_WRITE) {};             // Added code
	
	virtual void read(Tango::DeviceImpl *dev,Tango::Attribute &att)
		{(static_cast<DynAttr *>(dev))->read_DoubleDynAttr(att);}
	virtual void write(Tango::DeviceImpl *dev,Tango::WAttribute &att)
		{(static_cast<DynAttr *>(dev))->write_DoubleDynAttr(att);}
	virtual bool is_allowed(Tango::DeviceImpl *dev,Tango::AttReqType ty)
		{return (static_cast<DynAttr *>(dev))->is_DoubleDynAttr_allowed(ty);}
};

The file DynAttrClass.cpp has to be modified in order to not create LongDynAttr and DoubleDynAttr attributes. This simply means in the DynAttrClass::attribute_factory() method to comment out all lines related to the LongDynAttr and DoubleDynAttr attributes.

Dynamic attribute implementation

To register device dynamic attribute, you need to call the Tango::DeviceImpl::add_attribute() method for each device dynamic attribute. Pogo generates a method named DynAttr::add_dynamic_attributes()

which has to be used for this purpose. In our example, in this method we have to:

  • analyze the content of the device DynAttrList property
  • create the necessary attributes using the attribute class constructor added above
  • Register them in the device with the Tango::DeviceImpl::add_attribute() method.

Each attribute needs a piece of data to carry the attribute value. In our example, the data number needed for our dynamic attributes is also dynamic and known at run-time (when the property is read). We also need a way to link attribute and the associated data. To do so, we are using map containers defined as device data member. The following code is part of the device class declaration

class DynAttr : public Tango::Device_4Impl
{
    /*----- PROTECTED REGION ID(DynAttr::Data Members) ENABLED START -----*/

protected:
    map<string,Tango::DevLong>	   long_att_data;      // For the dynamic LongDynAttr attr.
    map<string,Tango::DevDouble>   double_att_data;    // For the dynamic DoubleDynAttr attr.

    /*----- PROTECTED REGION END -----*/	//	DynAttr::Data Members

.....

We have two maps. One for each dynamic attribute data types.

The code of the DynAttr::add_dynamic_attributes() method looks like

void DynAttr::add_dynamic_attributes()
{
    /*----- PROTECTED REGION ID(DynAttr::Class::add_dynamic_attributes) ENABLED START -----*/

    if (dynAttrList.empty() == false)
    {
	if ((dynAttrList.size() % 2) != 0)
	{
// Throw exception
	}

	for (unsigned int i = 0;i < dynAttrList.size();i = i + 2)
	{
	    Tango::Attr *att;
	    if (dynAttrList[i] == "LongDynAttr")
	    {
		att = new LongDynAttrAttrib(dynAttrList[i + 1]);
		long_att_data.insert(make_pair(dynAttrList[i + 1],0));
	    }
	    else if (dynAttrList[i] == "DoubleDynAttr")
	    {
		att = new DoubleDynAttrAttrib(dynAttrList[i + 1]);
		double_att_data.insert(make_pair(dynAttrList[i + 1],0.0));
	    }
	    else
	    {
// Throw exception
	    }

	    add_attribute(att);
	}
    }

    /*----- PROTECTED REGION END -----*/	//	DynAttr::Class::add_dynamic_attributes

The code to throw exception has been removed.

Note that the data associated with all LongDynAttr dynamic attributes  are initialized to 0 and the data associated to all DoubleDynAttr dynamic attributes are initialized with 0.0

The code for the LongDynAttr reading/writing is the following

void DynAttr::read_LongDynAttr(Tango::Attribute &attr)
{
    DEBUG_STREAM << "DynAttr::read_LongDynAttr(Tango::Attribute &attr) entering... " << endl;
    /*----- PROTECTED REGION ID(DynAttr::read_LongDynAttr) ENABLED START -----*/

    map<string,Tango::DevLong>::iterator ite;
    if ((ite = long_att_data.find(attr.get_name())) == long_att_data.end())
    {
// throw excpetion
    }

    ite->second++;
    //	Set the attribute value
    attr.set_value(&(ite->second));

    /*----- PROTECTED REGION END -----*/	//	DynAttr::read_LongDynAttr
}

void DynAttr::write_LongDynAttr(Tango::WAttribute &attr)
{
    DEBUG_STREAM << "DynAttr::write_LongDynAttr(Tango::Attribute &attr) entering... " << endl;
	
    //	Retrieve write value
    Tango::DevLong	w_val;
    attr.get_write_value(w_val);
	
    /*----- PROTECTED REGION ID(DynAttr::write_LongDynAttr) ENABLED START -----*/

    map<string,Tango::DevLong>::iterator ite;
    if ((ite = long_att_data.find(attr.get_name())) == long_att_data.end())
    {
// Throw exception
    }

    ite->second = w_val;	

    /*----- PROTECTED REGION END -----*/	//	DynAttr::write_LongDynAttr
}

The code of the read method first retrieves the data associated with this attribute (in the map container), change this value (simply incrementing it) and set the attribute value.

The code of the write method first gets the user value (Pogo generated code), then retrieves the data associated with this attribute in the map container and finally initializes it with the user value.

The code for reading/writing the DoubleDynAttr attribute is very similar and is given below

void DynAttr::read_DoubleDynAttr(Tango::Attribute &attr)
{
    DEBUG_STREAM << "DynAttr::read_DoubleDynAttr(Tango::Attribute &attr) entering... " << endl;
    /*----- PROTECTED REGION ID(DynAttr::read_DoubleDynAttr) ENABLED START -----*/

    map<string,Tango::DevDouble>::iterator ite;
    if ((ite = double_att_data.find(attr.get_name())) == double_att_data.end())
    {
// Throw exception
    }

    ite->second--;
    //	Set the attribute value
    attr.set_value(&(ite->second));

    /*----- PROTECTED REGION END -----*/	//	DynAttr::read_DoubleDynAttr
}

void DynAttr::write_DoubleDynAttr(Tango::WAttribute &attr)
{
    DEBUG_STREAM << "DynAttr::write_DoubleDynAttr(Tango::Attribute &attr) entering... " << endl;
	
    //	Retrieve write value
    Tango::DevDouble	w_val;
    attr.get_write_value(w_val);
	
    /*----- PROTECTED REGION ID(DynAttr::write_DoubleDynAttr) ENABLED START -----*/

    map<string,Tango::DevDouble>::iterator ite;
    if ((ite = double_att_data.find(attr.get_name())) == double_att_data.end())
    {
// Throw exception
    }

    ite->second = w_val;

    /*----- PROTECTED REGION END -----*/	//	DynAttr::write_DoubleDynAttr
}

 Running the server

A Tango device server process hosting this DynAttr class has been defined in the database with two device. The dynamic attributes for these two devices are

property device 1

 

Property device 2

 

As shown by the Pogo screen-shot in the beginning of this HowTo, the Tango class also defines a static attribute for each device named StaticAttr. Running the device server and opening TestDevice panels on each device displays device attribute list:

property device 1

 

Property device 2

 

This method fully supports restarting device(s) or server using the device server process admin device.

Conclusion

Pogo 7 simplifies a lot the code needed to write Tango class with dynamic attributes. Restarting device(s0 from the admin device is also fully supported nevertheless it is still not the end of the story. One of the next Pogo release will fully support dynamic attributes (Not needed to change non-protected area). Stay tuned

 

Document Actions

Updated 15/12/2010

Posted by etaurel at Dec 15, 2010 04:35 PM
Updated the HowTo to reflect Pogo 7 changes