Dynamic attributes with external devices

Hi,

is it possible to create and manage dynamic attributes with external devices ?

I follow “HOW TO ADD DYNAMIC ATTRIBUTES TO A DEVICE CLASS”
(https://tango-controls.readthedocs.io/en/latest/tutorials-and-howtos/how-tos/how-to-dynamic-attributes-device-class.html?highlight=dynamic%20attributes)
to learn dynamic attribute, but i do not see how to do it .

What is the difference with list where each entry as single device name (vector managedDevice_List; vector ignoredDevice_List) ?
And by configuring group of managed devices (Tango::Group*) or device names (Tango::DevString*) ?

Thanks,
JCM

The code creating the dynamic attributes must be part of the device server exposing these dynamic attributes but the creation can be triggered by an external event (command received, attribute written, event received, device property written followed by a device restart). This code must be written by the device server programmer following the documentation you found.
In many cases, the dynamic attributes are created during the startup of the device, based on some configuration device properties.
There is a dedicated method add_dynamic_attributes() generated by POGO which can be used to add the code to create the dynamic attributes and which is called during the device startup, after the device properties have been read.
But you could also add dynamic attributes in a command execution method for instance.
POGO can also be used to create some dynamic attributes. If you declare some dynamic attributes in POGO, you can define some dynamic attribute “templates” for some attributes of a specific type and you can already initialize the properties these attributes will have in common.
POGO will then generate some methods which can be invoked to create these kinds of attributes more easily.

Sorry but I have no idea of what you are talking about here. Where did you find these managedDevice_List and ignoreDevice_List?
Cheers,
Reynald

Thanks Reynald for your detailed answer.

It was just an idea without using dynamic attributes. What I meant was that we need to be able to access attributes from other devices (at the option of the user) by configuring one or more properties (Array of strings) corresponding to the devices.
It is then enough to build: groups of devices, lists of attributes to read … with the method read_attribute () for example.

Regards.

I understand. What you mentioned is indeed the classical way to deal with the use case you just described.
Dynamic attributes are another concept. Dynamic attributes are attributes which are different from the classical static attributes created by POGO.
Dynamic attributes can be created on the fly with the names and types you want (with some limitations).

Hi Reynald,
I wonder if there is any issue if I want to fill a dynamic attribute with external attributes values.

For example, I would like to :

  • create an attribute with a (static) property maxBufferSize,
  • a dynamic scalar attribute A/B/C/bufferSize less or equal to maxBufferSize
  • a spectrum dynamic attribute A/B/C/buffer of size A/B/C/bufferSize.

Then, I would like to fill A/B/C/buffer with D/E/F/externalAttribute by subscribing to these attribute values.

Do you see any problem with this behaviour or should it work?
Thank you.

Hi Philippe,

When A/B/C/buffer dynamic attribute is created, it is important for it to have the same maximum X data size as D/E/F/externalAttribute, unless you are not interested in copying the full externalAttribute buffer.
If you create an attribute with maxDimX < maxDimX(D/E/F/externalAttribute), there is a risk that at some point D/E/F/externalAttribute will contain more values than A/B/C/buffer can handle and Tango will probably complain when you will try to set the A/B/C/buffer attribute value.

For your use case, if you intend to have A/B/C/buffer attribute having exactly the same values as D/E/F/externalAttribute, it would be more logical to use a forwarded attribute to avoid intermediate copies.
But please be aware, there are some known bugs (and probably not yet known bugs too :wink: ) with the forwarded attributes.
For instance:

Kind regards,
Reynald

Thank you for the explanation.
I should have precised that A/B/C/buffer is a spectrum and D/E/F/externalAttribute is a scalar static attribute.

The way I see to do this is then to :

  • create a dynamic attribute A/B/C/buffer at device startup according to device static property bufferSize
  • fill dynamic spectrum attribute A/B/C/buffer each time a new value of scalar D/E/F/externalAttribute static attribute is available. The way I see to do this is to subscribe A/B/C/buffer to D/E/F/externalAttribute but forwarded attribute does not seem to be the way since dimension of A/B/C/buffer and D/E/F/externalAttribute differs.

Am I wrong?
Is it the good way to do this?

Hi Philippe,
correct me if I’m wrong, but basically you want to fill the A/B/C/buffer entries with the scalar value in D/E/F/externalAttribute any time this value changes, up to the maxBufferSize? If this is the case you can subscribe to the change event for D/E/F/externalAttribute and push it into A/B/C/buffer…
Cheers,
Lorenzo

[quote=“lorenzo”]Hi Philippe,
correct me if I’m wrong, but basically you want to fill the A/B/C/buffer entries with the scalar value in D/E/F/externalAttribute any time this value changes, up to the maxBufferSize? If this is the case you can subscribe to the change event for D/E/F/externalAttribute and push it into A/B/C/buffer…
[/quote]
Indeed Lorenzeo, that was the way I saw it, I wondered if there is any issue to be awaited with this behavior since we are not used to dynamic attributes.
I understand it is possible.

You can forget about forwarded attributes in your case. I didn’t understand well you use case.

At first sight, I don’t expect any issue with the behavior you described.
You just have to know how you will deal with errors.
If D/E/F is not reachable for a while, you will receive error events. You have to think about how you will deal with these errors.
It looks like what you want is getting the history of the last A/B/C/bufferSize values of D/E/F/externalAttribute.
You know you could also get that by reading D/E/F/externalAttribute history from D/E/F polling buffer history if D/E/F/externalAttribute is polled.
You can tune the poll ring depth property to configure the size of the polling buffer history.
You will get a timestamp associated with each value in the polling buffer history.

If you subscribe to change events, you will receive events only when the values will change (depending on your event settings).
You just have to configure the system for what you want to achieve.

If you keep the idea of creating A/B/C/Buffer attribute, you could initialize it by reading D/E/F/externalAttribute polling buffer history. That way, if A/B/C is restarted, it will retrieve the history and fill up the buffer immediately with the latest values. In this case, depending on how the polling buffer is filled, the polling buffer history might contain consecutive values having the same values.

Hoping this helps,
Reynald

Yeah, I discovered this following the links to issue on this topic yesterday, thank you for this precision, it will be useful to be conscious of awaited problems with this solution.

Yes, it helps a lot. :smiley:
I’m not sure adjusting the buffer size to big values will be possible without performance tradeoffs on libera hardware, which is why I though about a 2d device with independant buffer.
But perhaps this solution is possible, we will keep this in mind to check.

Hi,

to resume about dynamic attributes use, the way i understand it allows to get a set of unknown values through device properties during ‘Init’ command.
Following this way, I should be able, in a device server, to :

  1. use a ‘dynAttrList’ property containing for example the list “A/B/C/attr1, A/B/C/attr2, D/E/F/attr1”

  2. with this property, create 3 dynamic attributes to store last value of respectively A/B/C/attr1, A/B/C/attr2, and D/E/F/attr1
    Then I would use following method to create these dynamic attributes:

void DynAttr :: add_dynamic_attributes()
{
    for (unsigned int i = 0; i <dynAttrList.size (); i = i + 1)
    {
        // convert dynAttrList [i] to local attribute named attr
        // attr[0] = A_B_C_attr1
        // attr[1] = A_B_C_attr2
        // attr[2] = D_E_F_attr1
        // ...
        add_LongDynAttr_dynamic_attribute (attr[i]);
}

Is this the good way to do this?

Best wishes.

Yes, you are on the right track! This will create some Tango::DevLong dynamic attributes with the names you gave (A_B_C_attr1, A_B_C_attr2, D_E_F_attr1,…) :smiley:

Then you will have to put some smart code into DynAttr::read_LongDynAttr(Tango::Attribute &attr) method to set the attribute value for the corresponding attribute name that you can get with attr.get_name() method.

Hi Reynald,

I put my code in the DynAttr::read_LongDynAttr(Tango::Attribute &attr) method to set the attribute value but it is called only if another client accesses the attribute. The atkpanel does not display the updated value ;(
Is this normal?

Also, the DynAttr::add_dynamic_attributes() method is not executed by init_device(). The ‘init’ command is not enough, i have to stop my DS and restart it if changed the list of attributes ;(
Is this normal?

thank you so much

The code in DynAttr::read_LongDynAttr(Tango::Attribute &attr) is called only when any client tries to read one of your created dynamic attributes. This method can be called regularly as well if your dynamic attributes are polled.

Do you mean the value of your attribute? Or do you mean the atkpanel does not display the newly created dynamic attributes.
atkpanel does not listen to interface change events, so it is not notified when new attributes are created. If you create new attributes, you need to restart atkpanel to see the current device interface with the newly created attributes.
Can you please share the code of your DynAttr::read_LongDynAttr(Tango::Attribute &attr) method so we can understand what you actually asked the device server to do?

Yes, this is normal. DynAttr::add_dynamic_attributes() is invoked by DynAttrClass::device_factory() method, which is called when the device are created.

If you change the list of attributes for one device, the right approach is to do a “restart device” (available from the right-click menu on your device on jive, there is a DevRestart command on the admin device to do that too).
If you change the list of attributes for many devices from your device server, you should restart the server (there is a RestartServer command on the admin device or you can simply restart the device server from astor).

Hoping this helps,
Reynald

thanks, i try.

i mean the value of my attributes.
extract of my DynAttr::read_LongDynAttr(Tango::Attribute &attr) method :smiley:


Tango::DevDouble	*att_value = get_DoubleDynAttr_data_ptr(attr.get_name());
string &att_name = attr.get_name();
/* previously done in the method add_dynamic_attributes(): 
add_DoubleDynAttr_dynamic_attribute(bufferMean);
add_DoubleDynAttr_dynamic_attribute(bufferStd);
add_DoubleDynAttr_dynamic_attribute(bufferPeak);*/
 
for (int i=0; i<numberOfDynamicAttributes; i++)
  {
    if (std::strcmp(att_name.c_str(),bufMean[[i]i])==0)
      {
	*att_value=theMeanValue;// theMeanValue is the value to set in dynamic attribute
	attr.set_value(att_value);
	break;
      }
    if (std::strcmp(att_name.c_str(),bufPeak[[i]i])==0)
      {
	*att_value=thePeakValue;// thePeakValue is the value to set in dynamic attribute
	attr.set_value(att_value);
	break;
      }
    if (std::strcmp(att_name.c_str(),bufStd[[i]i])==0)
      {
	*att_value=theSdValue;// theSdValue is the value to set in dynamic attribute
	attr.set_value(att_value);
	break;
      }
  }

Thanks

Maybe your problem comes from the fact that you have declared in POGO you would push events by code for these attributes and you actually don’t push events.

So atkpanel subscribes to events for these attributes and simply waits for these events to update the displayed values.
But if the device server does not push any event, atkpanel will never receive any update.

To identify if atkpanel is actually listening to events on your attribute or doing some remote polling of the attribute, you can have a look at the ATK Diagnostic window (available from atkpanel’s View menu and then “Diagnostic…” menu item).

If you see Event enabled set to true for your attributes, this means that atkpanel will not remotely poll these attributes and simply wait for events to come. This is probably what happens in your case?

I precise i have not declared in POGO i you would push events by code for these attributes.
In my code i subscribe to events and a callback method is triggered when the events are received (push model).
In the ATK Diagnostic window, i see Event enabled set to true for all my attributes.

Are your attributes polled in your device server?

YES
on my attributes, with jive, i set polling and absolute value on Change event.
See also the ATK diagnostic window.