Commit 274caa24 authored by Elisabetta Giani's avatar Elisabetta Giani
Browse files

AT5-257: Define a decorator class to check for administrative mode before executing a command.

Added attributes to report the number and the list of devices that completed a task.
parent 87f1437d
Loading
Loading
Loading
Loading
+93 −66
Original line number Diff line number Diff line
@@ -45,13 +45,21 @@ from skabase.auxiliary import utils
file_path = os.path.dirname(os.path.abspath(__file__))
utils_path = os.path.abspath(os.path.join(file_path, os.pardir)) + "/utils"
sys.path.insert(0, utils_path)
import decorators
from decorators import AdminModeCheck
import cspcommons
from cspcommons import HealthState, AdminMode, ObsState
import release
# PROTECTED REGION END# //CspMaster.add_path

__all__ = ["CspMaster", "main"]
@unique
class CmdExecState(IntEnum):
    IDLE        = 0
    RUNNING     = 1
    QUEUED      = 2


__all__ = ["CspMaster", "main"]

class CspMaster(with_metaclass(DeviceMeta, SKAMaster)):
    """
@@ -91,11 +99,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)):
    
    """
    # PROTECTED REGION ID(CspMaster.class_variable) ENABLED START #
    @unique
    class CmdExecState(IntEnum):
        IDLE        = 0
        RUNNING     = 1
        QUEUED      = 2
    
    # PROTECTED REGION END #    //  CspMaster.class_variable
    # PROTECTED REGION ID(CspMaster.class_protected_methods) ENABLED START #
    
@@ -162,7 +166,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)):
    def _timeout_handler_cb(self, *device_list, **dict_args):
        print("Timeout expired!!")
        cmd_expired = dict_args['cmd_name']
        self._cmd_execution_state[cmd_expired] = self.CmdExecState.IDLE
        self._cmd_execution_state[cmd_expired] = CmdExecState.IDLE
        if cmd_expired == 'on':
            print("timeout_handler is on alive?", self._on_cmd_thread.is_alive())
        for fqdn in device_list:
@@ -170,8 +174,8 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)):
                if self._se_timeout_expired[fqdn][cmd_expired]:
                    print("Timeout expired on device {} for command {} execution".format(fqdn, cmd_expired))
                    # to be sure, reset the command state
                if self._se_cmd_execution_state[fqdn][cmd_expired] == self.CmdExecState.RUNNING:
                    self._se_cmd_execution_state[fqdn][cmd_expired] == self.CmdExecState.IDLE
                if self._se_cmd_execution_state[fqdn][cmd_expired] == CmdExecState.RUNNING:
                    self._se_cmd_execution_state[fqdn][cmd_expired] == CmdExecState.IDLE
            except KeyError as key_err:
                err_msg = "No key {} found in input args".format(str(key_err))
                self.dev_logging(err_msg, tango.LogLevel.LOG_ERROR)   
@@ -252,7 +256,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)):
                    msg += " Desc: {}".format(evt.errors[0].desc)
                    #self.dev_logging(msg, tango.LogLevel.LOG_ERROR)
                    print(msg)
                    self._se_cmd_execution_state[evt.device.dev_name()][evt.cmd_name.lower()] = self.CmdExecState.IDLE
                    self._se_cmd_execution_state[evt.device.dev_name()][evt.cmd_name.lower()] = CmdExecState.IDLE
                    # obsState and obsMode values take on the CbfSubarray's values via
                    # the subscribe/publish mechanism
            else:
@@ -273,7 +277,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)):
    def _update_csp_state(self):
        """
        Class protected method.
        Retrieve the State attribute of the CSP sub-elements and aggregate
        Retrieve the State attribute values of the CSP sub-elements and aggregate
        them to build up the CSP global state.

        :param: None
@@ -288,7 +292,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)):

    def _update_csp_health_state(self):
        """
        Class private method.
        Class protected method.
        Retrieve the healthState attribute of the CSP sub-elements and
        aggregate them to build up the CSP health state

@@ -337,9 +341,10 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)):
                self.dev_logging(log_msg, int(tango.LogLevel.LOG_INFO))
                device_proxy = DeviceProxy(fqdn)
                #device_proxy.ping()
                # Note: ping() method is not called. The DeviceProy is initilaize even if the
                # sub-element is not running. The connection with a sub-element is establish 
                # as soon as the corresponding Master device starts. 
                # Note: ping() method is not called. The DeviceProxy is initialized even if the
                # sub-element is not running (but defined into the TANGO DB!). 
                # The connection with a sub-element is establish as soon as the corresponding
                # Master device starts. 
                
                # store the sub-element proxies
                self._se_proxies[fqdn] = device_proxy
@@ -417,7 +422,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)):
    
    def _issue_on_command(self, device_list):
        """
        Target function called by the thread run() method. 
        Target function called by the _on_cmd_thread run() method. 
        
        :param  subelem_list: argument tuple passed when the target function is called.
                   subelem_list[0]: the FQDN of the device executing the  ON commmand
@@ -428,17 +433,21 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)):
        
        num_of_failed_device = 0
        self._num_dev_completed_task['on'] = 0
        self._list_dev_completed_task['on'] = []
        self._cmd_progress['on'] = 0
        # sub-element command execution measured time
        se_cmd_duration_measured = defaultdict(lambda:defaultdict(lambda:0))
        # timer is started outside the for loop: it has to start only once.
        if not self._cmd_timer.is_alive():
            print("Starting timer!!!")
            self._cmd_timer.start()
        else:
            print("Timer thread is running")
        # loop on the devices and power-on them sequentially
        for device in device_list:
            print("device {} initial state for on command {}".format(device,
                                                                     self._se_cmd_execution_state[device]['on']))
            self._se_cmd_execution_state[device]['on'] = self.CmdExecState.RUNNING
            self._se_cmd_execution_state[device]['on'] = CmdExecState.RUNNING
            se_cmd_duration_measured[device]['on'] = 0
            try:
                device_proxy = self._se_proxies[device] 
@@ -456,17 +465,16 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)):
                self._se_cmd_starting_time[device] = time.time() 
                # loop on the device until the State changes to ON or a timeout occurred
                while True:
                    #state = device_proxy.command_inout("State")
                    #if state == tango.DevState.ON:
                    if self._se_state[device] == tango.DevState.ON:
                        print("Command On executed on device {}.".format(device))
                        print("Command On ended with success on device {}.".format(device))
                        self.dev_logging("Command On executed on device {}.".format(device),
                                         tango.LogLevel.LOG_INFO)
                        # update the list and number of device that completed the task
                        self._num_dev_completed_task['on']  += 1
                        self._list_dev_completed_task['on'].append(device)
                        # reset the value of the attribute reporting the execution state of
                        # the command
                        self._num_dev_completed_task['on']  += 1
                        
                        self._se_cmd_execution_state[device]['on'] = self.CmdExecState.IDLE
                        self._se_cmd_execution_state[device]['on'] = CmdExecState.IDLE
                        se_cmd_duration_measured[device]['on'] = time.time() - self._se_cmd_starting_time[device]
                        # calculate the real execution time for the command
                        self._cmd_duration_measured['on'] += se_cmd_duration_measured[device]['on']
@@ -476,10 +484,12 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)):
                            # if the sub-element onCommandProgress attribute is not subscribed
                            # the attribute tracking the command execution is set to 100 
                        self._se_cmd_progress[device]['on'] = 100
                        # command success: exit from the wait loop and issue the On command
                        # on the next device
                        break
                    # check if sub-element command ended for errors (call to cmd_ended_cb and 
                    # execution state set to IDLE)
                    if self._se_cmd_execution_state[device]['on'] == self.CmdExecState.IDLE:
                    if self._se_cmd_execution_state[device]['on'] == CmdExecState.IDLE:
                        # execution ended for this sub-element, skip to the next one
                        num_of_failed_device += 1
                        print("Inside while loop num_failed =", num_of_failed_device)
@@ -489,17 +499,17 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)):
                    if elapsed_time > self._se_cmd_duration_expected[device]['on']:
                        num_of_failed_device += 1
                        self._se_timeout_expired[device]['on'] = True
                        self._se_cmd_execution_state[device]['on'] = self.CmdExecState.IDLE
                        self._se_cmd_execution_state[device]['on'] = CmdExecState.IDLE
                        # if the CBF command timeout expires, the CSP power-on is stopped
                        # TODO: verify if this behavior conflicts with ICD
                        if device.find("cbf"):
                            self.dev_logging("CBF Timeout during power-on!!! Exit", tango.LogLevel.LOG_ERROR)
                            print("is cmd timer alive?:", self._cmd_timer.is_alive())
                            # remove the timer
                            if self._cmd_timer.is_alive():
                                self._cmd_timer.cancel()
                                self._cmd_timer.join()
                            return
                        # timeout on the sub-element, skip to the next
                        # timeout on the sub-element, skip to the next device
                        break
                    time.sleep(1)
                    # update the progress counter inside the loop taking into account the number of devices
@@ -510,11 +520,12 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)):
                self._cmd_progress['on'] += self._se_cmd_progress[device]['on']/len(device_list)
                if len(device_list) ==  self._num_dev_completed_task['on'] + num_of_failed_device:
                    print("All devices have been handled!")
                    # end of the command: reset the timer, and the command state
                    # end of the command: cancel the timer, and reset the global command state 
                    # attribute
                    print("Removing timer")
                    self._cmd_timer.cancel()
                    self._cmd_timer.join()
                    self._cmd_execution_state['on'] = self.CmdExecState.IDLE 
                    self._cmd_execution_state['on'] = CmdExecState.IDLE 
                    print("is cmd timer alive?:", self._cmd_timer.is_alive())
            except KeyError as key_err:
                msg = "No key {} found".format(str(key_err))
@@ -806,6 +817,11 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)):
             " SearchBeams."),
    )

    numOfDevCompletedTask = attribute(
        dtype='DevUShort',
        label="Number of devices that completed the task",
        doc="Number of devices that completed the task",
    )

    availableCapabilities = attribute(
        dtype=('DevString',),
@@ -982,6 +998,13 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)):
        doc="Report the list of VlbiBeam Capability IDs that are not assigned to any sub-array. The SCM values of a unassigned VlbiBeam Capabiity are:\nState = OFF\nadminMode = ONLINE or MAINTENACE\nobsState = IDLE\nhealthState = OK \nobsMode = IDLE\n",
    )

    listOfDevCompletedTask = attribute(
        dtype=('DevString',),
        max_dim_x=100,
        label="List of devices that completed the task",
        doc="List of devices that completed the task",
    )

    # ---------------
    # General methods
    # ---------------
@@ -989,7 +1012,6 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)):
    def init_device(self):
        """Initialises the attributes and properties of the CspMaster."""
        SKAMaster.init_device(self)      
        print(self.get_state())    
        # PROTECTED REGION ID(CspMaster.init_device) ENABLED START #
        self._build_state = '{}, {}, {}'.format(release.name, release.version, release.description)
        self._version_id = release.version
@@ -998,7 +1020,6 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)):
        self._element_logging_level = int(tango.LogLevel.LOG_INFO)
        # set init values for the CSP Element and Sub-element SCM states
        self.set_state(tango.DevState.INIT)
        print(self.get_state())
        self._health_state = HealthState.OK
        self._admin_mode = AdminMode.ONLINE
        # use defaultdict to initialize the sub-element State,healthState
@@ -1048,15 +1069,16 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)):
        # implemented as a nested default dictionary:
        # keys: sub-element FQDN
        # values: defaut dictionary (keys: command name, values: command state)
        self._se_cmd_execution_state = defaultdict(lambda: defaultdict(lambda: self.CmdExecState.IDLE))
        self._se_cmd_execution_state = defaultdict(lambda: defaultdict(lambda: CmdExecState.IDLE))
        
        
        # _cmd_execution_state: implement the execution state of a long-running
        # command for each sub-element.
        # command for the whole CSP.  Setting this attribute prevent the execution
        # of the same command while it is already running.
        # implemented as a default dictionary:
        # keys: command name
        # values:command state
        self._cmd_execution_state = defaultdict(lambda: self.CmdExecState.IDLE)
        self._cmd_execution_state = defaultdict(lambda: CmdExecState.IDLE)
        
        # _se_cmd_starting_time: for each sub-element report the long-running command 
        # starting time
@@ -1167,14 +1189,12 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)):
        self._unassigned_vlbi_beam_id = []                                                         

        # Try connection with sub-elements
        print(self.get_state())
        self._connect_to_subelements()
                
        # to use the push model in command_inout_asynch (the one with the callback parameter),
        # change the global TANGO model to PUSH_CALLBACK.
        apiutil = tango.ApiUtil.instance()
        apiutil.set_asynch_cb_sub_model(tango.cb_sub_model.PUSH_CALLBACK)
        print(self.get_state())
        # PROTECTED REGION END #    //  CspMaster.init_device

    def always_executed_hook(self):
@@ -1485,6 +1505,12 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)):
        return 0
        # PROTECTED REGION END #    //  CspMaster.unassignedSearchBeamsNum_read

    def read_numOfDevCompletedTask(self):
        # PROTECTED REGION ID(CspMaster.numOfDevCompletedTask_read) ENABLED START #
        """Return the numOfDevCompletedTask attribute."""
        return 0
        # PROTECTED REGION END #    //  CspMaster.numOfDevCompletedTask_read

    def read_availableCapabilities(self):
        # PROTECTED REGION ID(CspMaster.availableCapabilities_read) ENABLED START #
        """Return the availableCapabilities attribute."""
@@ -1635,10 +1661,16 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)):
        return (0,)
        # PROTECTED REGION END #    //  CspMaster.unassignedVlbiBeamIDs_read

    def read_listOfDevCompletedTask(self):
        # PROTECTED REGION ID(CspMaster.listOfDevCompletedTask_read) ENABLED START #
        """Return the listOfDevCompletedTask attribute."""
        return ('',)
        # PROTECTED REGION END #    //  CspMaster.listOfDevCompletedTask_read

    # --------
    # Commands
    # --------
    
    def is_On_allowed(self):
        """
        *TANGO is_allowed method*
@@ -1649,9 +1681,12 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)):
            True if the method is allowed, otherwise False.
        """
        # PROTECTED REGION ID(CspMaster.is_On_allowed) ENABLED START #
        # Note: as per SKA Guidelines, the command is allowed when 
        if self.get_state() not in [tango.DevState.STANDBY, tango.DevState.ON]:
            return False
        return True
    
    
    @command(
        dtype_in='DevVarStringArray',
        doc_in=("If the array length is 0, the command applies to the whole CSP Element."
@@ -1659,6 +1694,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)):
                "the CSP SubElement to switch ON."),
    )
    @DebugIt()
    @AdminModeCheck()
    def On(self, argin):
        # PROTECTED REGION ID(CspMaster.On) ENABLED START #
        """
@@ -1688,24 +1724,6 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)):
                for CBF sub-element or there are no DeviceProxy providing interface
                to the CSP sub-elements or the AdminMode is not correct.
        """
        # Check the AdminMode value: only if it's ONLINE or MAINTENACE
        # the command is callable.
        if self._admin_mode in [AdminMode.OFFLINE, AdminMode.NOTFITTED, AdminMode.RESERVED]:
            # NOTE: when adminMode is NOT_FITTED, OFFLINE or RESERVED device itself
            # sets the TANGO state to DISABLE
            # Add only a check on State value to log a warning message if it
            # is different from DISABLE
            msg_args = (self.get_state(), self._admin_mode)
            if self.get_state() != tango.DevState.DISABLE:
                self.dev_logging("is_On_allowed: incoherent device State {} "
                                 " with adminMode {}".format(*msg_args),
                                 tango.LogLevel.LOG_WARN)
            err_msg = ("On() command can't be issued when State is {} and"
                       " adminMode is {} ".format(*msg_args))
            tango.Except.throw_exception("Command not executable",
                                         err_msg,
                                         "On command execution",
                                         tango.ErrSeverity.ERR)
        device_list = []
        num_of_devices = len(argin)
        if num_of_devices == 0:
@@ -1719,24 +1737,27 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)):
                self.dev_logging("Too many input parameters", tango.LogLevel.LOG_WARN)
            device_list = argin
        
        print(device_list)  
        # If a sub-element device is already executing a power command throws an
        # exception (power commands have to be executed sequentially)
        # TODO: What to do if the required device is performing a software upgrade or is changing its 
        # adminMode? How can CSP.LMC detect this condition?
        if self.CmdExecState.RUNNING in self._cmd_execution_state.values():
        if CmdExecState.RUNNING in self._cmd_execution_state.values():
            print("On command is running!!")
            # Note: need to check in the list of sub-elements, not in the device_list sent to the command!!!
            for device in self._se_fqdn:
                for cmd_name, cmd_state in self._se_cmd_execution_state[device].items():
                    if cmd_state == self.CmdExecState.RUNNING:
                        msg = ("Device {} is already executing the {} command".format(device, cmd_name))
                    if cmd_state in [CmdExecState.RUNNING, CmdExecState.QUEUED]:
                        msg = ("Device {} is already executing the {} command"
                               "Command state: {}".format(device, cmd_name,
                                                          self._se_cmd_execution_state[device][cmd_name]))
                        print(msg)
                        # throw an exception
                        return
        command_timeout = 0
        device_to_remove = []
        for device in device_list:
            print("processin devie:", device)
            print("processin device:", device)
            # if the sub-element device server is not running or the adminMode
            # attribute is not ONLINE or MAINTENANCE, skip to next and add it to 
            # the list of the devices to remove from the input arg list
@@ -1746,6 +1767,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)):
                device_to_remove.append(device)
                print("Devices to remove from the list:", device_to_remove)
                continue
            self._se_cmd_execution_state[device]['on'] = CmdExecState.QUEUED
            # reset the timeout attribute content
            for cmd_name, _ in self._se_timeout_expired[device].items():
                if self._se_timeout_expired[device][cmd_name]:
@@ -1776,14 +1798,17 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)):
            
            # evaluate the total timeout value to execute the whole command
            command_timeout +=  self._se_cmd_duration_expected[device]['on']
        
        # use the greater value for the onCommand duration expected.
        # use the greatest value for the onCommand duration expected.
        if command_timeout > self._cmd_duration_expected['on']:
            self._cmd_duration_expected['on'] = command_timeout
            self.dev_logging("Modified the On command Duration Expected value!!", tango.LogLevel.LOG_INFO)
        # remove the not running device and OFFLINE/RESERVED/NOTFITTED devices from the list
        # remove the 'not running' devices or OFFLINE/RESERVED/NOTFITTED devices from the list
        # of devices to power-on
        for device in device_to_remove:
            device_list.remove(device)
        if not device_list:
            self.dev_logging("No device to power on", tango.LogLevel.LOG_INFO)
            return
        # invoke the constructor for the timer thread
        self._cmd_timer = threading.Timer(self._cmd_duration_expected['on'], self._timeout_handler_cb,
                                          device_list, {'cmd_name':'on'})
@@ -1791,7 +1816,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)):
        self._on_cmd_thread = threading.Thread(target=self._issue_on_command, name="Thread-On",
                                                                   args=(device_list,))
        # start the thread
        self._cmd_execution_state['on'] = self.CmdExecState.RUNNING
        self._cmd_execution_state['on'] = CmdExecState.RUNNING
        self._on_cmd_thread.start()
        # sleep for a while to let the thread start
        time.sleep(0.2)
@@ -1804,6 +1829,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)):
                " CSP SubElement to switch OFF."),
    )
    @DebugIt()
    @AdminModeCheck()
    def Off(self, argin):
        # PROTECTED REGION ID(CspMaster.Off) ENABLED START #
        """
@@ -1830,6 +1856,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)):
                "CSP SubElement to put in STANDBY mode."),
    )
    @DebugIt()
    @AdminModeCheck()
    def Standby(self, argin):
        # PROTECTED REGION ID(CspMaster.Standby) ENABLED START #
        """
+16 −0
Original line number Diff line number Diff line
@@ -481,6 +481,14 @@
      <status abstract="false" inherited="false" concrete="true" concreteHere="true"/>
      <properties description="Report the number of unassigned SearchBeam Capabilities.&#xA;This number does not take in account the number of reserved SearchBeams." label="Number of unassigned SearchBeam Capabilities" unit="" standardUnit="" displayUnit="" format="" maxValue="" minValue="" maxAlarm="" minAlarm="" maxWarning="" minWarning="" deltaTime="" deltaValue=""/>
    </attributes>
    <attributes name="numOfDevCompletedTask" attType="Scalar" rwType="READ" displayLevel="OPERATOR" polledPeriod="0" maxX="" maxY="" allocReadMember="true" isDynamic="false">
      <dataType xsi:type="pogoDsl:UShortType"/>
      <changeEvent fire="false" libCheckCriteria="false"/>
      <archiveEvent fire="false" libCheckCriteria="false"/>
      <dataReadyEvent fire="false" libCheckCriteria="true"/>
      <status abstract="false" inherited="false" concrete="true" concreteHere="true"/>
      <properties description="Number of devices that completed the task" label="Number of devices that completed the task" unit="" standardUnit="" displayUnit="" format="" maxValue="" minValue="" maxAlarm="" minAlarm="" maxWarning="" minWarning="" deltaTime="" deltaValue=""/>
    </attributes>
    <attributes name="maxCapabilities" attType="Spectrum" rwType="READ" displayLevel="OPERATOR" polledPeriod="1000" maxX="20" maxY="" allocReadMember="true">
      <dataType xsi:type="pogoDsl:StringType"/>
      <changeEvent fire="false" libCheckCriteria="false"/>
@@ -669,6 +677,14 @@
      <status abstract="false" inherited="false" concrete="true" concreteHere="true"/>
      <properties description="Report the list of VlbiBeam Capability IDs that are not assigned to any sub-array. The SCM values of a unassigned VlbiBeam Capabiity are:&#xA;State = OFF&#xA;adminMode = ONLINE or MAINTENACE&#xA;obsState = IDLE&#xA;healthState = OK &#xA;obsMode = IDLE&#xA;" label="Unassigned VlbiBeam Capabilities IDs" unit="" standardUnit="" displayUnit="" format="" maxValue="" minValue="" maxAlarm="" minAlarm="" maxWarning="" minWarning="" deltaTime="" deltaValue=""/>
    </attributes>
    <attributes name="listOfDevCompletedTask" attType="Spectrum" rwType="READ" displayLevel="OPERATOR" polledPeriod="0" maxX="100" maxY="" allocReadMember="true" isDynamic="false">
      <dataType xsi:type="pogoDsl:StringType"/>
      <changeEvent fire="false" libCheckCriteria="false"/>
      <archiveEvent fire="false" libCheckCriteria="false"/>
      <dataReadyEvent fire="false" libCheckCriteria="true"/>
      <status abstract="false" inherited="false" concrete="true" concreteHere="true"/>
      <properties description="List of devices that completed the task" label="List of devices that completed the task" unit="" standardUnit="" displayUnit="" format="" maxValue="" minValue="" maxAlarm="" minAlarm="" maxWarning="" minWarning="" deltaTime="" deltaValue=""/>
    </attributes>
    <states name="ON" description="This state could have been called OK or OPERATIONAL. It means that the device is in its operational state. (E.g. the power supply is giving its nominal current, th motor is ON and ready to move, the instrument is operating). This state is modified by the Attribute alarm checking of the DeviceImpl:dev_state method. i.e. if the State is ON and one attribute has its quality factor to ATTR_WARNING or ATTR_ALARM, then the State is modified to ALARM.">
      <status abstract="false" inherited="true" concrete="true"/>
    </states>
+49 −0

File added.

Preview size limit exceeded, changes collapsed.