.. _optional-attributes: ============================== How to use optional attributes ============================== -------------------------------- Standard attributes constructors -------------------------------- The `Control System Guidelines`_ define a set of "additional SKA Control Model attributes" which can optionally be used in addition to the "Core SKA Control Model Attributes". These attributes are the :py:attr:`!controlMode`, :py:attr:`!simulationMode` and :py:attr:`!testMode`. The guidelines do not prescribe precisely what each of the attributes should do, instead it is left to developers of Tango devices to use these attributes as appropriate. .. _Control System Guidelines: https://confluence.skatelescope.org/x/y4nIBg The :py:class:`~ska_tango_base.base.base_interface.BaseInterface` does not provide any of these additional attributes by default, instead a standard implementation of these can be created using one of :py:func:`~ska_tango_base.base.base_interface.standard_control_mode()`, :py:func:`~ska_tango_base.base.base_interface.standard_simulation_mode()` or :py:func:`~ska_tango_base.base.base_interface.standard_test_mode()`. These functions return a signal, attribute pair and require that your Tango device inherits from :py:class:`~ska_tango_base.software_bus.SignalBusMixin`. If required, the read/write methods for the attribute can be overridden using the :py:class:`attribute.read/write` decorators to override the behaviour. The default implementation for these methods can be accessed with ``self.__class__..do_read(self)`` and ``self.__class__..do_write(self, value)``. For example: .. code:: python from ska_control_model import SimulationMode from ska_tango_base.base import BaseInterface, standard_simulation_mode from ska_tango_base.software_bus import Signal, attribute_from_signal class MyDevice(BaseInterface): _simulation_mode: Signal[SimulationMode] simulationMode: attribute_from_signal _simulation_mode, simulationMode = standard_simulation_mode() @simulationMode.write def write_simulationMode(self, value: SimulationMode) -> None: if value == SimulationMode.TRUE: self.start_simulation() else: self.stop_simulation() self.__class__.simulationMode.do_write(self, value) ... The :py:class:`~ska_tango_base.base.base_device.SKABaseDevice` includes all the optional attributes provide by the base interface. It also provides override hooks for changing the behaviour of each attribute, so that for example, a subclass only has to override the :py:func:`~ska_tango_base.base.base_device.SKABaseDevice.write_simulationMode` to change the behaviour of the simulationMode attribute. For example, the following behaves similarly as previous example using the :py:class:`~ska_tango_base.base.base_device.SKABaseDevice` .. code:: python from ska_control_model import SimulationMode from ska_tango_base.base import BaseInterface, standard_simulation_mode class MyDevice(SKABaseDevice): def write_simulationMode(self, value: SimulationMode) -> None: if value == SimulationMode.TRUE: self.start_simulation() else: self.stop_simulation() super().write_simulationMode(value) ... --------------------- TestModeOverrideMixin --------------------- The :py:class:`~ska_tango_base.test_mode_mixin.TestModeOverrideMixin` mix-in class provides the optional testMode attribute, along with a default behaviour for this attribute. When the testMode attribute is TEST, it is possible to override the value of attributes marked with the :py:func:`~ska_tango_base.test_mode_mixin.overridable` decorator. For example, the following device has a myAttr attribute which is overridable. .. code:: python from ska_tango_base.base import BaseInterface from ska_tango_base.software_bus import Signal, attribute_from_signal from ska_tango_base.test_mode_mixin import TestModeOverrideMixin, overridable class MyDevice(TestModeOverrideMixin, BaseInterface): _my_attr = Signal[int]() myAttr = attribute_from_signal(_my_attr, dtype=int, abs_change=1) @myAttr.read @overridable def read_myAttr(self) -> ReadAttrType[int]: return self.__class__.myAttr.do_read(self) def init_device(self) -> None: super().init_device() self._my_attr = 0