Subscribe events from device with many attributes

Andy
Thanks for your kind explanation;


When I tested with add_dynamic_attribute routine I have two questions;

1. How to set the set_event_abs_change for DEV_BOOLEAN
It gives error that

The property abs_change is not settable for the attribute data type
Setting a valid value (also 'NaN', 'Not specified' and '' - empty string) for any property for this attribute will automatically bring the above-mentioned property to its library defaults
Hint : Check also class level attribute properties
Origin = Device_3Impl::read_attributes_no_except()


2. When I tried to delete the dynamic attribute pointere, it gives abnormal quit.
Is it not recommended?

// below caused abnormal quit!!
//if (a)
// delete a;
Below is the code that I tested

void TOmronServer::add_dynamic_attribute (const DynamicFINSAttribute::Config& cfg)
{
	//DEBUG_STREAM << "TOmronServer::add_dynamic_attributes () for " << cfg.m_name << std::endl;

	DynamicFINSAttribute * a = NULL;
	try
	{
		a = new DynamicFINSAttribute(cfg);

		a->set_disp_level(Tango::OPERATOR);

		if (cfg.m_rw != Tango::WRITE)
		{
			// write only does not need polling && event
			Tango::UserDefaultAttrProp	event_prop;
			event_prop.set_event_abs_change("1");
			//event_prop.set_event_rel_change("1");

			// TANGO::DEV_BOOLEAN - error
			/*
				The property abs_change is not settable for the attribute data type
				Setting a valid value (also 'NaN', 'Not specified' and '' - empty string) for any property for this attribute will automatically bring the above-mentioned property to its library defaults
				Hint : Check also class level attribute properties
				Origin = Device_3Impl::read_attributes_no_except()
			*/

			//- set other attribute properties : label, display format,…
			a->set_default_properties(event_prop);

			a->set_polling_period(1000);
			//a->set_change_event(true, true);
		}

		this->m_dyn_attrs.add(a); 
		return;
	}
	catch (std::bad_alloc)
	{
		ERROR_STREAM << "TOmronServer::add_dynamic_attribute caught bad_alloc" << std::endl;
	}
	catch (Tango::DevFailed &e)
	{
		ERROR_STREAM << "TOmronServer::add_dynamic_attribute caught DevFailed " << e << std::endl;
	}
	catch (…)
	{
		ERROR_STREAM << "TOmronServer::add_dynamic_attribute caught (…)" << std::endl;
	}

	// below caused abnormal quit!!
	//if (a)
	//	delete a;
}
Hi,

About your first question, you don't need to use set_event_abs_change for DEV_BOOLEAN attributes.
If a client subscribes on CHANGE event on a DEV_BOOLEAN attribute, it will receive an event every time the value changes from true to false or from false to true.

About your second question, it seems we don't have enough information to be able to help you.

Where does DynamicFINSAttribute class come from?
Is it a class you created?

Kind regards

Reynald
Rosenberg's Law: Software is easy to make, except when you want it to do something new.
Corollary: The only software that's worth making is software that does something new.
Reynald,

Thanks for your response.
Regarding the second question;

The DynamicFINSAttribute class was actually copied and renamed from "ModbusDataViewer" under Communication.

After digging into existing code, I found critical bug in there.

The original code in
DynamicAttributesHelper.cpp
is

void DynamicAttributesHelper::add (DynamicModbusAttribute * _attr)
	throw (Tango::DevFailed)
{

  // clipped

  //- add it to the device
  try
  {
    this->host_device_->add_attribute( _attr );
  }
  catch(Tango::DevFailed& ex)
  {
	  RETHROW_DEVFAILED(ex,
                      "INTERNAL_ERROR",
	                    "attribute could not be added to the device",
                      "DynamicAttributesHelper::add");
  }
  catch(…)
  {
	  THROW_DEVFAILED("UNKNOWN_ERROR",
	                  "unknown caught while trying to add attribute to the device",
                    "DynamicAttributesHelper::add");
  }
  
  //- ok, everything went fine :
  //- insert the attribute into the list
  std::pair<DynAttrIt, bool> insertion_result;
  insertion_result = this->rep_.insert( DynAttrEntry(_attr->get_name(), _attr) );

The problem is when there's chances to add duplicate attribute.
Then the add_attribute routine releases the attr without raise exception.

So the program abnormally quitted at the code of
_attr->get_name()
.

I'm not sure whether quick fix for this problem as below is the proper one;


	//———- _FIX_BUG –> added from here
	//	below copied from DeviceImpl::add_attribute(Tango::Attr *new_attr) at device.cpp
	string &attr_name = _attr->get_name();
	bool need_free = false;
	long i;

	vector<Tango::Attr *> &attr_list = this->host_device_->get_device_class()->get_class_attr()->get_attr_list();
	long old_attr_nb = attr_list.size();
	for (i = 0;i < old_attr_nb;i++)
	{
		if ((attr_list[i]->get_name() == attr_name) && (attr_list[i]->get_cl_name() == _attr->get_cl_name()))
		{
			need_free = true;
			break;
		}
	}
	//———- _FIX_BUG –> added to here

	//- add it to the device
	try
	{
	this->host_device_->add_attribute( _attr );
	}
	catch(Tango::DevFailed& ex)
	{
		RETHROW_DEVFAILED(ex,
						"INTERNAL_ERROR",
						"attribute could not be added to the device",
						"DynamicAttributesHelper::add");
	}
	catch(…)
	{
		THROW_DEVFAILED("UNKNOWN_ERROR",
						"unknown caught while trying to add attribute to the device",
					"DynamicAttributesHelper::add");
	}
	
	//———- _FIX_BUG –> added from here
	if (need_free)
	{
		//_attr is already freed - no longer available
		return;
	}
	//———- _FIX_BUG –> added to here

	//- ok, everything went fine :
	//- insert the attribute into the list
	std::pair<DynAttrIt, bool> insertion_result;
	insertion_result = this->rep_.insert( DynAttrEntry(_attr->get_name(), _attr) );  // no more abnormal quit here!!!


I wish the add_attribute function returns result value, not just deleting the passed parameter silently.

There are some important lines missing in the code you pasted from DynamicAttributesHelper.cpp file.
(https://sourceforge.net/p/tango-ds/code/HEAD/tree/DeviceClasses/Communication/ModbusDataViewer/trunk/src/DynamicAttributesHelper.cpp)

There are some lines at the beginning of the method which are checking whether the attribute was already added, and if it was already added, the method throws an exception:


  //- check attribute does not already exist
  DynAttrIt it = this->rep_.find(_attr->get_name());
  if (it != this->rep_.end())
  {
	  THROW_DEVFAILED("OPERATION_NOT_ALLOWED",
	                  "attribute already exists",
                    "DynamicAttributesHelper::add");
  }

So it seems there is no chance to add a duplicate attribute with this code.
Rosenberg's Law: Software is easy to make, except when you want it to do something new.
Corollary: The only software that's worth making is software that does something new.
Reynald,

The code that you mentioned is already there.
It was clipped for clarification.

So it seems there is no chance to add a duplicate attribute with this code.

Unfortunately, this could happen when there's more than one device from the same serversmile

To reproduce the case when rep_find throws no exception but the attr_list has the attribute, create two devices of the same server.

Then the first device passed without any problem, but the second device caught the situation.
The call stack is at

TOmronServer.exe!TOmronServerHC_ns::TOmronServerHCClass::device_factory(const Tango::DevVarStringArray * devlist_ptr=0x00000000008fe6f0) 줄 409 C++
TOmronServer.exe!Tango::DServer::init_device() 줄 267 + 0x56 바이트 C++
TOmronServer.exe!Tango::DServer::DServer(Tango::DeviceClass * cl_ptr=0x00000000026ecd70, const char * n=0x00000000026f8360, const char * d=0x00007ff77829adb0, Tango::DevState s=ON, const char * st=0x00007ff77829ad98) 줄 110 C++
TOmronServer.exe!Tango::DServerClass::device_factory(const Tango::DevVarStringArray * devlist_ptr=0x00000000008ff490) 줄 1814 + 0xac 바이트 C++
TOmronServer.exe!Tango::DServerClass::DServerClass(std::basic_string<char,std::char_traits<char>,std::allocator<char> > & s="DServer") 줄 1533 + 0x12 바이트 C++
TOmronServer.exe!Tango::DServerClass::init() 줄 1582 + 0x30 바이트 C++
TOmronServer.exe!Tango::Util::server_init(bool with_window=false) 줄 1861 C++

See the captured picture which shows this case;





Edited 7 years ago
Hi,

Indeed you are right.
It seems the developer should not try to access to the attribute variable passed to add_attribute() after having invoked this method since, as you noticed, add_attribute() might release it if the attribute was previously already added (even by another device).
This is not well documented.
I think the documentation should be improved by giving examples on dynamic attributes management.
I guess this is what you were looking for when you ended up re-using ModbusDataViewer code.
I've never used this ModbusDataViewer device server, but it might be that in practice, it has never been used (or was not supposed to be used) with several devices per device server instance.

Cheers,

Reynald
Rosenberg's Law: Software is easy to make, except when you want it to do something new.
Corollary: The only software that's worth making is software that does something new.
I actually would not recommend the approach from ModbusDataViewer device server in recent Tango code.

The recent versions of Tango have introduced the add_dynamic_attributes() method for a better and more standard way to handle dynamic attributes.
Let's suppose your class is named Example.
If you generate C++ code from POGO for a class named Example, you will notice that POGO generates a method named Example::add_dynamic_attributes() for you in Example.cpp

Pogo will also generate some code to invoke this method during the device creation (after init_device() is executed).
You can see this code in ExampleClass.cpp generated file in ExampleClass::device_factory() method.

In ExampleClass.cpp, you will also notice that there is a method called ExampleClass::erase_dynamic_attributes() which will take care of erasing the dynamic attributes which were created during the previous call to add_dynamic_attributes().

So what you need to do is simply add the code to create the attributes in Example::add_dynamic_attributes() method. Tango will take care of cleaning the created attributes for you the next time the device is created again (typically when you invoke restart device). You don't need to maintain yourself a map containing the list of attributes you dynamically created and you don't need to remove them in Example::delete_device().

Please be aware that if you are using what I suggested above, if you invoke the Example::init command, Example::add_dynamic_attributes() will not be invoked.
It is invoked when you click on restart device from jive though (equivalent to DevRestart command from admin device).

I would recommend this approach since the recent tango code and recently added features like the Interface Change Events have been designed with this approach in mind I think.

Hoping this helps,
Reynald
Rosenberg's Law: Software is easy to make, except when you want it to do something new.
Corollary: The only software that's worth making is software that does something new.
 
Register or login to create to post a reply.