How to use a device with inheritable admin mode
The ska-tango-base package provides HeritableAdminModeMixin that can be used to make a Tango device using the BaseInterface have an admin mode that can be automatically inherited from some parent device. All that is required by the Tango device developer to make this feature available is to inherit from the mixin. For example, the following is a minimal device with an inherited admin mode.
class MyDevice(stb.future.HeritableAdminModeMixin, stb.future.BaseInterface):
def init_device(self) -> None:
super().init_device()
self.init_completed()
The rest of this guide discusses how users of the Tango device can set up and utilise admin mode inheritance.
Configuring a parent
In order to use admin mode inheritance you must specify _where_ to inherit from, this is done by setting the ParentTRL property. The value of the setting can be retrieved from the device via the read-only parentTRL attribute. As with all properties, the device will not be notified if they are changed in the database and the setting can be reloaded from the database using the Init() command. For example,
In [1]: dp = DeviceProxy("foo/bar/1")
In [2]: dp.parentTRL
Out[2]: 'my/parent/1'
In [3]: db = tango.Database()
In [4]: db.put_device_property("foo/bar/1", {'ParentTRL': ['my/parent/2']})
In [5]: dp.parentTRL
Out[5]: 'my/parent/1'
In [6]: dp.Init()
In [7]: dp.parentTRL
Out[7]: 'my/parent/2'
The ParentTRL of a device can be empty (the default), in which case admin mode inheritance cannot be used.
The INHERIT set-point
Devices supporting admin mode inheritance have an additional enum label, "INHERIT" compared to those that do not.
In [8]: dp.get_attribute_config("adminMode").enum_labels
Out[8]: ['ONLINE', 'ENGINEERING', 'INHERIT']
This additional value cannot be read from the device, but can be used as a set-point to instruct the device to inherit from its parent. PyTango will allow you to set this using the string literal "INHERIT", the AdminMode enumeration does not have this value.
In [9]: dp.adminMode = "INHERIT"
In [9]: dp.adminMode
Out[9]: <adminMode.ENGINEERING: 1>
The set-point can be read back using the index operator provided by PyTango to do a raw read_attributes() invocation. Unfortanately, this will be the raw value which can be converted to a string using the enum_labels found in the attribute configuration.
In [10]: dp["adminMode"].w_value
Out[10]: 2
In [11]: dp.get_attribute_config("adminMode").enum_labels[2]
Out[11]: 'INHERIT'
It is also possible to determine if admin mode inheritance is active by reading the inheritAdminMode attribute.
In [12]: dp.inheritAdminMode
Out[12]: True
If your device does not have a ParentTRL set then setting the admin mode to "INHERIT" will be rejected:
In [13]: db.put_device_property("foo/bar/1", {'ParentTRL': ['']})
In [14]: dp.Init()
In [15]: dp.adminMode = "INHERIT"
STB_NotSupported: 'INHERIT' is not supported: No ParentTRL set
(For more detailed information type: tango_error)
Just as with the BaseInterface, the HeritableAdminModeMixin.adminMode attribute is memorized, meaning the set-point is saved to the database and re-applied whenever the device server is restarted or the device is re-initialised.
The default adminMode set-point is "ENGINEERING" for devices with an empty ParentTRL and "INHERIT" for devices with a non-empty ParentTRL.
Warning
If the memorized adminMode is "INHERIT" and the ParentTRL is empty when the device server is started then the adminMode will always return an exception when read:
In [16]: dp.adminMode
STB_NotSupported: 'INHERIT' is not supported: No ParentTRL set
(For more detailed information type: tango_error)
This can be cleared by either assigning "ONLINE" or "ENGINEERING" to the adminMode or by providing a ParentTRL and re-initialising the device with the Init() command.
Similarly, if your device is re-initialised with the Init() command with an invalid database configuration, the adminMode set-point will be rejected and the default of "ENGINEERING" will be used.
Connection status reporting
While your device is inheriting its admin mode, it will monitor the health of the connection to the parent device. The status of this connection can be retrieved via the parentConnectionStatus attribute:
In [17]: dp.parentConnectionStatus
Out[17]: <parentConnectionStatus.ESTABLISHED: 2>
This will return a CommunicationStatus that will be:
DISABLEDif the admin mode is not being inheritedESTABLISHEDif the admin mode is being inherited and we have recieved a value from the parent deviceNOT_ESTABLISHEDif the admin mode is being inherited but we have received error events while trying to monitor the parent device
Initially, while the connection status is NOT_ESTABLISHED the admin mode will be reported as the last known good value:
In [18]: dp.parentConnectionStatus
Out[18]: <parentConnectionStatus.NOT_ESTABLISHED: 1>
In [19]: dp.adminMode
Out[19]: <adminMode.ENGINEERING: 1>
After a continuous period of failing to monitor the parent device the parentConnectionStatus attribute will report a qulaity factor of ATTR_ALARM and the adminMode will report a quality factor of ATTR_INVALID and therefore have no value:
In [20]: dp["parentConnectionStatus"].quality
Out[20]: <AttrQuality.ATTR_ALARM: 2>
In [21]: dp["adminMode"].quality
Out[21]: <AttrQuality.ATTR_INVALID: 1>
In [22]: dp.parentConnectionStatus
Out[22]: <parentConnectionStatus.NOT_ESTABLISHED: 1>
In [23]: print(dp.adminMode)
None
Warning
Unfortunately, due to limitations in Tango the write value of the adminMode will also be None, however, the inheritAdminMode boolean will still correctly report True:
In [24]: print(dp["adminMode"].w_value)
None
In [25]: dp.inheritAdminMode
Out[25]: True
When parentConnectionStatus has a quality factor of ATTR_ALARM, setting the device’s admin mode to "ONLINE" or "ENGINEERING" will stop the device from attempting to communicate with its parent and will clear the alarm:
In [26]: dp.adminMode = "ONLINE"
In [27]: dp.parentConnectionStatus
Out[27]: <parentConnectionStatus.DISABLED: 0>
In [28]: dp.adminMode
Out[28]: <adminMode.ONLINE: 0>
The time it takes for the parent connection to trigger an alarm is configured with the ParentConnectionTimeout property and can be read from the device with the parentConnectionTimeout attribute. As always, Init() is requried to reload this setting from the datbase:
In [29]: dp.parentConnectionTimeout
Out[29]: 60.0
In [30]: db.put_device_property("foo/bar/1", {'ParentConnectionTimeout': ['10']})
In [31]: dp.Init()
In [32]: dp.parentConnectionTimeout
Out[32]: 10.0