diff --git a/csp-lmc-common/csp_lmc_common/CspSubarray.py b/csp-lmc-common/csp_lmc_common/CspSubarray.py index d55cc8bfdc0b87de8f7e4e40b2e05d797d045d5d..1b5a65de15f4b9e427e89ac90402d92bb479967a 100644 --- a/csp-lmc-common/csp_lmc_common/CspSubarray.py +++ b/csp-lmc-common/csp_lmc_common/CspSubarray.py @@ -186,14 +186,12 @@ class CspSubarray(SKASubarray): dev_name = evt.device.dev_name() if not evt.err: try: - if "commandprogress" == evt.attr_value.name.lower()[-15:]: + if "cmdprogress" == evt.attr_value.name.lower()[-11:]: if dev_name in self._sc_subarray_fqdn: cmd_name = evt.attr_value.name[:-15] self._sc_subarray_cmd_progress[dev_name][cmd_name] = evt.attr_value.value - elif "cmdtimeoutexpired" == evt.attr_value.name.lower()[-17:]: + elif "timeoutexpiredflag" == evt.attr_value.name.lower(): if dev_name in self._sc_subarray_fqdn: - cmd_name = evt.attr_value.name[:-17] - #self._sc_subarray_timeout_expired[dev_name][cmd_name] = True self._timeout_expired = True else: log_msg = ("Unexpected change event for" @@ -241,12 +239,8 @@ class CspSubarray(SKASubarray): # Can happen evt empty?? if evt: if not evt.err: - if self._sc_subarray_cmd_exec_state[evt.device.dev_name()][evt.cmd_name.lower()] == CmdExecState.RUNNING: - msg = "Device {} is processing command {}".format(evt.device, - evt.cmd_name) - if self._sc_subarray_cmd_exec_state[evt.device.dev_name()][evt.cmd_name.lower()] == CmdExecState.IDLE: - msg = "Device {} ended command {} execution".format(evt.device, evt.cmd_name) - self.logger.info(msg) + self.logger.info("Device {} is processing the command {}".format(evt.device, + evt.cmd_name)) else: msg = "Error!!Command {} ended on device {}.\n".format(evt.cmd_name, evt.device.dev_name()) @@ -510,7 +504,7 @@ class CspSubarray(SKASubarray): self._failure_message[cmd_name] = '' self._sc_subarray_cmd_exec_state[device][cmd_name] = CmdExecState.RUNNING self.logger.info("Device {} State {} expected value {}".format(device, - self._sc_subarray_state[device], + self._sc_subarray_obs_state[device], dev_successful_state)) # flag to signal when configuration ends on a sub-array sub-component device_done = defaultdict(lambda:False) @@ -574,16 +568,23 @@ class CspSubarray(SKASubarray): self._cmd_progress[cmd_name] = 100 elapsed_time = time.time() - starting_time self.logger.info("Scan elapsed time:{}".format(elapsed_time)) - self._cmd_execution_state['scan'] = CmdExecState.IDLE + self._cmd_execution_state[cmd_name] = CmdExecState.IDLE self._last_executed_command = cmd_name + self.logger.info("Subarray obsState is: {} command {} state is {}".format(self._obs_state, cmd_name, self._cmd_execution_state[cmd_name])) - def __configure_scan(self, device_list, **args_dict): + def __configure_scan(self, input_arg, **args_dict): """ Thread target function invoked from Configure method. It monitors the obsState value of each sub-array sub-component configuring the scan, looking for timeout or failure conditions. - :param device_list: the FQDNs of the sub-array sub-components + :param: input_arg: a list with two elements: + - the FQDNs of the sub-array sub-components + - the configuration string + + If the CSP.LMC Subarray device is properly configured, the received + string passed as input argument of the command is stored the + class atribute _valid_scan_configuration. :return: None """ @@ -593,19 +594,18 @@ class CspSubarray(SKASubarray): # In the dictionary keys, is generally used the command name in lower letters self.logger.info("cmd_name: {} dev_state: {}".format(cmd_name, dev_successful_state)) + command_start_time = time.time() self._num_dev_completed_task[cmd_name] = 0 self._list_dev_completed_task[cmd_name] = [] self._cmd_progress[cmd_name] = 0 self._cmd_duration_measured[cmd_name] = 0 - self._abort_command_event.clear() - # sub-component command execution measured time - sc_cmd_duration_measured = defaultdict(lambda:defaultdict(lambda:0)) - command_progress = self._cmd_progress[cmd_name] # flag to signal when configuration ends on a sub-array sub-component device_done = defaultdict(lambda:False) # inside the end-less lop check the obsState of each sub-component + device_list = input_arg[0] self.logger.info("device_list:{}".format(device_list)) while True: + command_progress = 0 for device in device_list: self.logger.info("Command {} obs_state: {}".format(cmd_name, self._sc_subarray_obs_state[device])) @@ -614,6 +614,22 @@ class CspSubarray(SKASubarray): # if the sub-component execution flag is no more RUNNING, the command has # ended with or without success. Go to check next device state. if self._sc_subarray_obs_state[device] == dev_successful_state: + # !!! bad hack: + # if the sub-element subarray state is already READY, sleep for a while to wait + # sub-element moves to CONFIGURING. + # TODO: + # - wait for all sub-elements subarrays move to CONFIGURING, handling possible + # error/timeout conditions + # - when in CONFIGURING, wait for sub-elements subarrays to move to READY + # handling error conditions + + # check the time elapsed from the command start, if less then 0.1 sleep for a while + elapsed_time = time.time() - self._sc_subarray_cmd_starting_time[device] + if elapsed_time < 0.1: + self.logger.info("Device {} already in READY state after {} sec".format(device, + elapsed_time)) + time.sleep(0.3) + continue self.logger.info("Command {} ended with success on device {}.".format(cmd_name, device)) # update the list and number of device that completed the task @@ -623,22 +639,20 @@ class CspSubarray(SKASubarray): # the command self._sc_subarray_cmd_exec_state[device][cmd_name] = CmdExecState.IDLE self._sc_subarray_cmd_progress[device][cmd_name] = 100 - # calculate the real execution time for the command - if sc_cmd_duration_measured[device][cmd_name] >= self._cmd_duration_measured[cmd_name]: - self._cmd_duration_measured[cmd_name] = sc_cmd_duration_measured[device][cmd_name] # command success: step to next device device_done[device] = True + # check for timeout event. A timeout event can be detected in two ways: - # 1- the sub-element implements the 'onTimeoutExpired' attribute configured + # 1- the sub-element implements the 'timeoutExpiredFlag' attribute configured # for change event - # 2- the CspMaster periodically checks the time elapsed from the start + # 2- the CspSubarray periodically checks the time elapsed from the start # of the command: if the value is greater than the sub-element expected time # for command execution, the sub-element command execution state is set # to TIMEOUT # Note: the second check, can be useful if the timeout event is not received # (for example for a temporary connection timeout) elapsed_time = time.time() - self._sc_subarray_cmd_starting_time[device] - sc_cmd_duration_measured[device][cmd_name] = elapsed_time - time.time() + if (elapsed_time > self._sc_subarray_cmd_duration_expected[device][cmd_name] or self._sc_subarray_cmd_exec_state[device][cmd_name] == CmdExecState.TIMEOUT): msg = ("Timeout executing {} command on device {}".format(cmd_name, device)) @@ -653,10 +667,10 @@ class CspSubarray(SKASubarray): device_done[device] = True # update the progress counter inside the loop taking into account the number of devices # executing the command - self._cmd_progress[cmd_name] = command_progress+ self._sc_subarray_cmd_progress[device][cmd_name]/len(device_list) - self.logger.info("Command {} on device {} exec_state {}:".format(cmd_name,device, + command_progress += self._sc_subarray_cmd_progress[device][cmd_name]/len(device_list) + self.logger.debug("Command {} on device {} exec_state {}:".format(cmd_name,device, self._sc_subarray_cmd_exec_state[device][cmd_name])) - self.logger.info("self._sc_subarray_obs_state {}".format(self._sc_subarray_obs_state)) + self._cmd_progress[cmd_name] = command_progress if all(self._sc_subarray_obs_state[device] == ObsState.CONFIGURING for device in device_list): self._obs_state = ObsState.CONFIGURING self.logger.info("Current CspSubarray ObsState:{}".format(self._obs_state)) @@ -667,6 +681,14 @@ class CspSubarray(SKASubarray): # end of the command: the command has been issued on all the sub-element devices # reset the execution flag for the CSP break + # check for global timeout expiration + # may be this check is not necessary + if self._cmd_duration_expected[cmd_name] < (time.time() - command_start_time): + self._timeout_expired = True + self.logger.warn("Device went in timeout during configuration") + break + # TODO: + # check for abort request. time.sleep(0.2) # end of the while loop # check for timeout/failure conditions on each sub-component @@ -677,10 +699,22 @@ class CspSubarray(SKASubarray): self._failure_raised = True # reset sub-component execution flag self._sc_subarray_cmd_exec_state[device][cmd_name] = CmdExecState.IDLE - # update the progress counter at the end of the loop - self._cmd_progress[cmd_name] = command_progress + self._sc_subarray_cmd_progress[device][cmd_name]/len(device_list) + + if self._timeout_expired or self._failure_raised: + # if failure/timeout found check if the CBF subarray is configured. In + # this case the CSP.LMC Subarray obsState is set to READY. + if self._sc_subarray_obs_state[self.CbfSubarray] == ObsState.READY: + self._obs_state = ObsState.READY + #TODO: + # else:... + # self.set_state(tango.DevState.FAULT) + # + if all(self._sc_subarray_obs_state[fqdn] == dev_successful_state for fqdn in device_list): self._obs_state = dev_successful_state + self._valid_scan_configuration = input_arg[1] + self._cmd_duration_measured[cmd_name] = time.time() - command_start_time + self._cmd_progress[cmd_name] = 100 self._last_executed_command = cmd_name # reset the CSP Subarray command execution flag self._cmd_execution_state[cmd_name] = CmdExecState.IDLE @@ -1241,9 +1275,11 @@ class CspSubarray(SKASubarray): self._admin_mode = AdminMode.ONLINE self._obs_mode = ObsMode.IDLE self._obs_state = ObsState.IDLE - # TODO: change the configureDelayExpected attribute in the - # SKAObsDevice base class to RW - self._config_delay_expected = 5 + # _config_delay_expected: inherited from the SKAObsDevice base class + # Note: the _config_delay_expected could be set equal the max among the sub-elements + # sub-arrays expected times for the command + # For tests purpose we set it = 10 + self._config_delay_expected = 10 self._scan_id = 0 # connect to TANGO DB @@ -1883,6 +1919,7 @@ class CspSubarray(SKASubarray): :return: None """ # invoke the constructor for the command thread + self.logger.info("Received Scan command!!") for device in self._sc_subarray_assigned_fqdn: try: proxy = self._sc_subarray_proxies[device] @@ -1898,18 +1935,16 @@ class CspSubarray(SKASubarray): self.logger.info(tango_err.args[0].desc) try: proxy.command_inout_asynch("Scan", argin[0], self._cmd_ended_cb) - # invoke the constructor for the command thread except tango.DevFailed as tango_err: self.logger.info(tango_err.args[0].desc) - + # TODO: add check on the failed device. If CBF + # throw an exception. + # invoke the constructor for the command thread self._command_thread['scan'] = threading.Thread(target=self.__monitor_scan_execution, name="Thread-Scan", args=(self._sc_subarray_assigned_fqdn,)) - self._command_thread['scan'].start() - #self._obs_state = ObsState.SCANNING self._cmd_execution_state['scan'] = CmdExecState.RUNNING - # sleep for a while to let the thread start - time.sleep(0.2) + self._command_thread['scan'].start() # PROTECTED REGION END # // CspSubarray.Scan def _validate_scan_configuration(self, argin): @@ -1931,13 +1966,13 @@ class CspSubarray(SKASubarray): doc_in="A Json-encoded string with the scan configuration.", ) @DebugIt() - @ObsStateCheck('configscan') - @SubarrayRejectCmd('AddSearchBeams', - 'RemoveSearchBeams', - 'AddTimingBeams', - 'RemoveTimingBeams' - 'AddNumOfSearchBeams', - 'RemoveNumOfSearchBeams') + #@ObsStateCheck('configscan') + #@SubarrayRejectCmd('AddSearchBeams', + # 'RemoveSearchBeams', + # 'AddTimingBeams', + # 'RemoveTimingBeams' + # 'AddNumOfSearchBeams', + # 'RemoveNumOfSearchBeams') def Configure(self, argin): """ *TANGO Class method* @@ -1961,27 +1996,55 @@ class CspSubarray(SKASubarray): # the dictionary with the scan configuration try: + # if the stored configuration attribute is not empty, check + # for the received configuration content + if self._valid_scan_configuration: + try: + stored_configuration = json.dumps(self._valid_scan_configuration, sort_keys=True) + received_configuration = json.dumps(argin, sort_keys=True) + # if the received configuration is equal to the last valid configuration and the + # state of the subarray is READY than subarray is not re-configured. + if (stored_configuration == received_configuration) and (self._obs_state == ObsState.READY): + self.logger.info("Subarray is going to use the same configuration") + return + except Exception as e: + self.logger.warn(str(e)) + # go ahead and parse the received configuration self._validate_scan_configuration(argin) except tango.DevFailed as tango_err: - msg = tango_err.args[0].desc + # validation failure + msg = "Failure during validation of configuration:{}".format(tango_err.args[0].desc) self.logger.error(msg) tango.Except.throw_exception("Command failed", msg, - "Configure execution", + "Configure", tango.ErrSeverity.ERR) - # Forward the Configure command to CbfSubarray. + # Forward the Configure command to the sub-elements + # components (subarrays, pst beams) for device in self._sc_subarray_assigned_fqdn: - error_device = 0 + # reset the command progress counter for each + # sub-array component self._sc_subarray_cmd_progress[device]['configurescan'] = 0 self._failure_message['configurescan'] = '' + # NOTE: what happens if a sub-element subarray/PSt beam TANGO + # device is not running? The next calls to proxy should fail + # with a tango.DevFailed exception. if not self._is_sc_subarray_running(device): - error_device += 1 - self._failure_message['configurescan'] += "Device {} not running".format(device) + msg = "Device {} not running".format(device) + if device == self.CbfSubarray: + # throw the exception, configuration can't proceed + tango.Except.throw_exception("Command failed", + msg, + "Configure", + tango.ErrSeverity.ERR) + self._failure_message['configurescan'] += msg + self._sc_subarray_cmd_exec_state[device]['configurescan'] = CmdExecState.FAILED + #skip to next device + continue proxy = self._sc_subarray_proxies[device] - - # subscribes attribute to track command execution and + # subscribe sub-elements attributes to track command execution and # timeout on subarray sub-components - attributes = ['configureCmdProgress', 'configureCmdTimeoutExpired'] + attributes = ['configureScanCmdProgress', 'timeoutExpiredFlag'] for attr in attributes: try: if self._sc_subarray_event_id[attr.lower()] == 0: @@ -1992,31 +2055,52 @@ class CspSubarray(SKASubarray): self._sc_subarray_event_id[device][attr] = event_id except tango.DevFailed as df: self.logger.info(df.args[0].desc) - # Note: CBF/PSS sub-array checks for the validity of its + # NOTE: CBF/PSS sub-array checks for the validity of its # configuration. Failure in configuration throws an exception that is # caught via the _cmd_ended_cb callback + + try: + # read the timeout configured for the operation on the device + self._sc_subarray_duration_expected[device]['configurescan'] = proxy.configureDelayExpected + except AttributeError as attr_err: + self.logger.info("No attribute {} on device {}".format(str(attr_err), device)) try: proxy.command_inout_asynch("ConfigureScan", self._sc_subarray_scan_configuration[device], self._cmd_ended_cb) + self._sc_subarray_cmd_exec_state[device]['configurescan'] = CmdExecState.RUNNING except tango.DevFailed as tango_err: - error_device += 1 + if device == self.CbfSubarray: + msg = "Failure in configuring CBF: {}".format(tango_err-args[0].desc) + # throw the exception, configuration can't proceed + tango.Except.throw_exception("Command failed", + msg, + "Configure", + tango.ErrSeverity.ERR) + self._failure_message['configurescan'] += tango_err.args[0].desc - self._sc_subarray_cmd_exec_state[device]['configurescan'] = CmdExecState.RUNNING - if error_device: self._sc_subarray_cmd_exec_state[device]['configurescan'] = CmdExecState.FAILED + # register the starting time for the command self._sc_subarray_cmd_starting_time[device] = time.time() self.logger.debug("configure starting time: {}".format(self._sc_subarray_cmd_starting_time[device])) - #self._obs_state = ObsState.CONFIGURING - self._cmd_execution_state['configurescan'] = CmdExecState.RUNNING + # end for loop on devices + + # TODO: evaluate the global timeout as the max of the single sub-element + # timeouts + # configure the timeout for the operation + if self._config_delay_expected > 0: + self._cmd_duration_expected['configurescan'] = self._config_delay_expected + self.logger.debug("_config_delay_expected :{}".format(self._config_delay_expected)) # invoke the constructor for the command thread + thread_args = [self._sc_subarray_assigned_fqdn, argin] self._command_thread['configurescan'] = threading.Thread(target=self.__configure_scan, name="Thread-Configure", - args=(self._sc_subarray_assigned_fqdn,)) + args=(thread_args,)) + self._abort_command_event.clear() + self._cmd_execution_state['configurescan'] = CmdExecState.RUNNING + self._cmd_duration_measured['configurescan'] = 0 self._command_thread['configurescan'].start() - # sleep for a while to let the thread start - time.sleep(0.2) # PROTECTED REGION END # // CspSubarray.Configure @AdminModeCheck('AddNumOfSearchBeams') @@ -2210,10 +2294,10 @@ class CspSubarray(SKASubarray): proxy.command_inout_asynch("EndScan", self._cmd_ended_cb) except KeyError as key_err: self.logger.warning("No key {} found".format(key_err)) - self._sc_subarray_cmd_exec_state = CmdExecState.FAILED except tango.DevFailed as tango_err: - self._sc_subarray_cmd_exec_state = CmdExecState.FAILED self.logger.warning(tango_err.args[0].desc) + # TODO: address this case! + # signal the failure raising the failure flag? # set the threading endScan event self._end_scan_event.set() # PROTECTED REGION END # // CspSubarray.EndScan @@ -2266,10 +2350,10 @@ class CspSubarray(SKASubarray): self._command_thread['gotoidle'] = threading.Thread(target=self.__gotoidle, name="Thread-GotoIdle", args=(self._sc_subarray_assigned_fqdn,)) - self._command_thread['gotoidle'].start() self._cmd_execution_state['gotoidle'] = CmdExecState.RUNNING + self._command_thread['gotoidle'].start() # sleep for a while to let the thread start - time.sleep(0.2) + #time.sleep(0.2) # TODO: # add some check on command exeuction: end state has to be IDLE for each # sub-array sub-component @@ -2285,7 +2369,6 @@ class CspSubarray(SKASubarray): @command( ) @DebugIt() - @ObsStateCheck('abort') def Abort(self): # PROTECTED REGION ID(CspSubarray.Abort) ENABLED START # """ @@ -2303,9 +2386,9 @@ class CspSubarray(SKASubarray): proxy.command_inout_asynch("Abort", self._cmd_ended_cb) except KeyError as key_err: self.logger.warning("No key {} found".format(key_err)) - self._sc_subarray_cmd_exec_state = CmdExecState.FAILED + self._sc_subarray_cmd_exec_state[fqdn]['abort'] = CmdExecState.FAILED except tango.DevFailed as tango_err: - self._sc_subarray_cmd_exec_state = CmdExecState.FAILED + self._sc_subarray_cmd_exec_state[fqdn]['abort'] = CmdExecState.FAILED self.logger.warning(tango_err.args[0].desc) self._abort_command_event.set() # PROTECTED REGION END # // CspSubarray.Abort diff --git a/csp-lmc-common/csp_lmc_common/utils/decorators.py b/csp-lmc-common/csp_lmc_common/utils/decorators.py index dd4f254354dff97d06289e8fdf34fb0db8406089..20b0e7a0c2b8daecdadde9d75e88b1e464312d36 100644 --- a/csp-lmc-common/csp_lmc_common/utils/decorators.py +++ b/csp-lmc-common/csp_lmc_common/utils/decorators.py @@ -48,8 +48,7 @@ class AdminModeCheck(object): return admin_mode_check -VALID_OBS_STATE = {'abort': [ObsState.CONFIGURING, ObsState.SCANNING], - 'reset': [ObsState.ABORTED], +VALID_OBS_STATE = {'reset': [ObsState.ABORTED], 'configscan': [ObsState.IDLE, ObsState.READY], 'scan': [ObsState.READY], 'endscan': [ObsState.SCANNING], @@ -76,6 +75,7 @@ class ObsStateCheck(object): dev_instance = args[0] cmd_type = self._args + dev_instance.logger.info("device obs_state: {}".format(dev_instance._obs_state)) # Check the obsState attribute value: valid values for the command to # execute are defined by VALID_OBS_STATE dictionary if dev_instance._obs_state not in VALID_OBS_STATE[cmd_type]: @@ -269,11 +269,11 @@ class CmdInputArgsCheck(object): class SubarrayRejectCmd(object): """ Class designed to be a decorator for the CspSubarray methods. - The *decorator function* control if the execution of a sub-array - command to assign/remove resources conflicts with a command already + The *decorator function* controls if the execution of a sub-array + command to assign/remove/configure resources conflicts with a command already running. The *decorator function* accepts some parameters as input to customize - its functionality + its functionality. """ def __init__(self, *args, **kwargs): @@ -291,13 +291,16 @@ class SubarrayRejectCmd(object): cmd_to_exec = f.__name__ commands_to_reject = self._args dev_instance.logger.info( - "SubarrayRejectCmd decorator: command to execute {}".format(f.__name__)) + "Command to execute: {}".format(f.__name__)) + dev_instance.logger.info( + "Device state : {} obs_state: {}".format(dev_instance.get_state(), dev_instance._obs_state)) # If a sub-array is executing an AddXXX (RemoveXX) command, an exception is # thown only when the requested command is RemoveXXX (AddXXX)remove/add resources or ConfigureScan. # If the requested command is already running, the decorator returns. list_of_running_cmd = [cmd_name for cmd_name, cmd_state in dev_instance._cmd_execution_state.items() if cmd_state == CmdExecState.RUNNING] + dev_instance.logger.info("list of running commands: {}".format(list_of_running_cmd)) if list_of_running_cmd: dev_instance.logger.info("Commands {} are running".format(list_of_running_cmd)) if cmd_to_exec.lower() in list_of_running_cmd: @@ -312,6 +315,7 @@ class SubarrayRejectCmd(object): if any(cmd_name in commands_to_reject for cmd_name in list_of_running_cmd): err_msg = ("Can't execute command {}:" " command {} is already running".format(cmd_to_exec, list_of_running_cmd)) + dev_instance.logger.error(msg) tango.Except.throw_exception("Command failure", err_msg, "SubarrayRejectCmd decorator", diff --git a/csp-lmc-common/tests/CspSubarray_test.py b/csp-lmc-common/tests/CspSubarray_test.py index ee2396eabc05c1cd7deec719761929565b65bba0..8f859d8813f9a2102f33c568fe265fddfd75f9a9 100755 --- a/csp-lmc-common/tests/CspSubarray_test.py +++ b/csp-lmc-common/tests/CspSubarray_test.py @@ -103,11 +103,11 @@ class TestCspSubarray(object): state = tango_context.device.cbfSubarrayState assert state == DevState.OFF - def test_remove_timing_beams_while_add_is_running_state(self, tango_context): - """ Test the execution of the RemovingTimingBeams" while - the AddTimingBeams is already running - """ - tango_context.device.AddTimingBeams([1,2,3]) - with pytest.raises(tango.DevFailed) as df: - tango_context.device.RemoveTimingBeams([1,2]) - assert "Can't execute command" in df.value.args[0].desc + #def test_remove_timing_beams_while_add_is_running_state(self, tango_context): + # """ Test the execution of the RemovingTimingBeams" while + # the AddTimingBeams is already running + # """ + # tango_context.device.AddTimingBeams([1,2,3]) + # with pytest.raises(tango.DevFailed) as df: + # tango_context.device.RemoveTimingBeams([1,2]) + # assert "Can't execute command" in df.value.args[0].desc diff --git a/csp-lmc-mid/csp_lmc_mid/MidCspSubarrayBase.py b/csp-lmc-mid/csp_lmc_mid/MidCspSubarrayBase.py index 17e02f59a230002df500d56e169449228fb73c23..293372987783479f4678328f6430135b3cb81e69 100644 --- a/csp-lmc-mid/csp_lmc_mid/MidCspSubarrayBase.py +++ b/csp-lmc-mid/csp_lmc_mid/MidCspSubarrayBase.py @@ -77,6 +77,7 @@ class MidCspSubarrayBase(CspSubarray): self._sc_subarray_cmd_progress[device][cmd_name] = 0 self._failure_message[cmd_name] = '' device_proxy = self._sc_subarray_proxies[device] + self.logger.info("Going to assign receptors {}".format(receptor_list)) while True: # read the list of receptor IDs assigned to the CbfSubarray @@ -90,7 +91,7 @@ class MidCspSubarrayBase(CspSubarray): # get the ids in receptor_list that are also in receptor receptors_assigned = [value for value in receptor_list if value in receptors] self._num_dev_completed_task[cmd_name] = len(receptors_assigned) - if len(receptors_assigned) == len(receptor_list): + if (len(receptors_assigned) == len(receptor_list)) and (self.get_state() == tango.DevState.ON): self.logger.info("All required receptors assigned!!") self._sc_subarray_cmd_progress[device][cmd_name] = 100 # calculate the real execution time for the command @@ -114,7 +115,7 @@ class MidCspSubarrayBase(CspSubarray): # update the progress counter inside the loop taking into account the number of devices # executing the command self._cmd_progress[cmd_name] = self._sc_subarray_cmd_progress[device][cmd_name] - time.sleep(1) + time.sleep(0.1) # end of the while loop self._last_executed_command = cmd_name @@ -183,7 +184,7 @@ class MidCspSubarrayBase(CspSubarray): # update the progress counter inside the loop taking into account the number of devices # executing the command self._cmd_progress[cmd_name] = self._sc_subarray_cmd_progress[device][cmd_name] - time.sleep(1) + time.sleep(0.1) # end of the while loop self._last_executed_command = cmd_name # update the progress counter at the end of the loop @@ -273,6 +274,18 @@ class MidCspSubarrayBase(CspSubarray): self._sc_subarray_assigned_fqdn.append(self.CbfSubarray) self.logger.debug("Validate scan: {}".format(self._sc_subarray_assigned_fqdn)) + @command( + dtype_in='DevString', + doc_in="A Json-encoded string with the scan configuration.", + ) + @DebugIt() + @ObsStateCheck('configscan') + @SubarrayRejectCmd('AddReceptors', + 'RemoveReceptors', + 'RemoveAllReceptors') + def Configure(self, argin): + CspSubarray.Configure(self, argin) + # PROTECTED REGION END # // MidCspSubarrayBase.private_methods # ---------------- @@ -565,29 +578,36 @@ class MidCspSubarrayBase(CspSubarray): log_msg = "No receptors to assign to the subarray" self.logger.info(log_msg) return - # check if the CspSubarray is already connected to the CbfSubarray - proxy = 0 - if self._is_sc_subarray_running(self.CbfSubarray): - self._cmd_execution_state['addreceptors'] = CmdExecState.RUNNING - proxy = self._sc_subarray_proxies[self.CbfSubarray] - # remove possible receptor repetition - tmp = set(receptor_to_assign) - receptor_to_assign = list(tmp) - # forward the command to the CbfSubarray + + # check if the CbfSubarray TANGO device is already running + # and the proxy registered + if not self._is_sc_subarray_running(self.CbfSubarray): + log_msg = "Device {} is not running!".format(str(self.CbfSubarray)) + self.logger.error(log_msg) + tango.Except.throw_exception("Command failed", + log_msg, + "AddReceptors", + tango.ErrSeverity.ERR) + proxy = self._sc_subarray_proxies[self.CbfSubarray] + # remove possible receptor repetition + tmp = set(receptor_to_assign) + receptor_to_assign = list(tmp) + # forward the command to the CbfSubarray + try: proxy.command_inout_asynch("AddReceptors", receptor_to_assign, self._cmd_ended_cb) - + self._command_thread['addreceptors'] = threading.Thread(target=self.__monitor_add_receptors, name="Thread-AddReceptors", args=(receptor_to_assign,)) self._sc_subarray_cmd_starting_time[self.CbfSubarray] = time.time() + self._cmd_execution_state['addreceptors'] = CmdExecState.RUNNING self._command_thread['addreceptors'].start() - else: - log_msg = "Device {} is not running!".format(str(self.CbfSubarray)) - self.logger.error(log_msg) + except tango.DevFailed as tango_err: tango.Except.throw_exception("Command failed", - log_msg, + tango_err.args[0].desc, "AddReceptors", tango.ErrSeverity.ERR) + # PROTECTED REGION END # // MidCspSubarrayBase.AddReceptors @AdminModeCheck('RemoveReceptors') @@ -616,81 +636,78 @@ class MidCspSubarrayBase(CspSubarray): when an exception is caught during command execution. """ # PROTECTED REGION ID(CspSubarray.RemoveReceptors) ENABLED START # - # check if the CspSubarray is already connected to the CbfSubarray - if self._is_sc_subarray_running(self.CbfSubarray): - try: - proxy = self._sc_subarray_proxies[self.CbfSubarray] - # read from CbfSubarray the list of assigned receptors - # failure in reading attribute raises an AttributeError (not tangoDevFailed!!) - receptors = proxy.receptors - #!!!!!!!!!!!!!!!!! - # 2019-09-20: New images for TANGO and PyTango images has been released. PyTango - # is now compiled with th numpy support. In this case the proxy.receptors call - # does no more return an empty tuple but an empty numpy array. - # Checks on receptors content need to be changed (see below) - # NB: the receptors attribute implemented by the CbfSubarray is declared as RW. - # In this case the read method returns an empty numpy array ([]) whose length is 0. - #!!!!!!!!!!!!!!!!! - - # check if the list of assigned receptors is empty - if len(receptors): - receptors_to_remove = [] - # check if the receptors to remove belong to the subarray - for receptor_id in argin: - if receptor_id in receptors: - receptors_to_remove.append(receptor_id) - if any(receptors_to_remove): - # TODO_ add reading of CbfSubarray removeReceptorsCmdDurationExpected - # attribute, if implemented, otherwise use the default value - - # subscribes attributes to track progress and timeout: if these - # attributes are not implemented at CbfSubarray level, the warning - # is only logged because is not a fatal error. - for attr in ['removeReceptorsCmdProgress', 'cmdTimeoutExpired']: - try: - if self._sc_subarray_event_id[self.CbfSubarray][attr.lower()] == 0: - evt_id = proxy.subscribe_event(attr, tango.EventType.CHANGE_EVENT, - self._attributes_change_evt_cb, stateless=False) - self._sc_subarray_event_id[self.CbfSubarray][attr.lower( - )] = evt_id - except tango.DevFailed as tango_err: - self.logger.info(tango_err.args[0].desc) - # forward the command to CbfSubarray - proxy.command_inout_asynch("RemoveReceptors", receptors_to_remove, self._cmd_ended_cb) - self._cmd_execution_state['removereceptors'] = CmdExecState.RUNNING - self._sc_subarray_cmd_starting_time[self.CbfSubarray] = time.time() - # Note: rembember to put the comma in args=(receptors_to_remove,) otherwise - # the list is received as a numpy array! - self._command_thread['removereceptors'] = threading.Thread(target=self.__monitor_remove_receptors, - name="Thread-RemoveReceptors", - args=(receptors_to_remove,)) - - self._command_thread['removereceptors'].start() - return - self.logger.info( - "No receptor to remove from subarray {}".format(self.get_name())) - self._cmd_execution_state['removereceptors'] = CmdExecState.IDLE - return - except tango.DevFailed as tango_err: - tango.Except.throw_exception("Command failed", - tango_err.args[0].desc, - "RemoveReceptors", - tango.ErrSeverity.ERR) - except AttributeError as attr_err: - log_msg = "RemoveReceptors:" + str(attr_err) - self.logger.error(log_msg) - tango.Except.throw_exception("Command failed", - str(attr_err), - "RemoveReceptors", - tango.ErrSeverity.ERR) - else: + # check if the CspSubarray is already connected to the CbfSubarray + if not self._is_sc_subarray_running(self.CbfSubarray): log_msg = "Subarray {} not registered!".format( str(self.CbfSubarray)) self.logger.error(log_msg) tango.Except.throw_exception("Command failed", log_msg, "RemoveReceptors", tango.ErrSeverity.ERR) + try: + proxy = self._sc_subarray_proxies[self.CbfSubarray] + # read from CbfSubarray the list of assigned receptors + # failure in reading attribute raises an AttributeError (not tangoDevFailed!!) + receptors = proxy.receptors + #!!!!!!!!!!!!!!!!! + # 2019-09-20: New images for TANGO and PyTango images has been released. PyTango + # is now compiled with th numpy support. In this case the proxy.receptors call + # does no more return an empty tuple but an empty numpy array. + # Checks on receptors content need to be changed (see below) + # NB: the receptors attribute implemented by the CbfSubarray is declared as RW. + # In this case the read method returns an empty numpy array ([]) whose length is 0. + #!!!!!!!!!!!!!!!!! + + # check if the list of assigned receptors is empty + if len(receptors): + receptors_to_remove = [] + # check if the receptors to remove belong to the subarray + for receptor_id in argin: + if receptor_id in receptors: + receptors_to_remove.append(receptor_id) + if any(receptors_to_remove): + # TODO: add reading of CbfSubarray removeReceptorsCmdDurationExpected + # attribute, if implemented, otherwise use the default value + + # subscribes attributes to track progress and timeout: if these + # attributes are not implemented at CbfSubarray level, the warning + # is only logged because is not a fatal error. + for attr in ['removeReceptorsCmdProgress', 'timeoutExpiredFlag']: + try: + if self._sc_subarray_event_id[self.CbfSubarray][attr.lower()] == 0: + evt_id = proxy.subscribe_event(attr, tango.EventType.CHANGE_EVENT, + self._attributes_change_evt_cb, stateless=False) + self._sc_subarray_event_id[self.CbfSubarray][attr.lower()] = evt_id + except tango.DevFailed as tango_err: + self.logger.info(tango_err.args[0].desc) + # forward the command to CbfSubarray + proxy.command_inout_asynch("RemoveReceptors", receptors_to_remove, self._cmd_ended_cb) + self._cmd_execution_state['removereceptors'] = CmdExecState.RUNNING + self._sc_subarray_cmd_starting_time[self.CbfSubarray] = time.time() + # Note: rembember to put the comma in args=(receptors_to_remove,) otherwise + # the list is received as a numpy array! + self._command_thread['removereceptors'] = threading.Thread(target=self.__monitor_remove_receptors, + name="Thread-RemoveReceptors", + args=(receptors_to_remove,)) + self._command_thread['removereceptors'].start() + return + self.logger.info( + "No receptor to remove from subarray {}".format(self.get_name())) + self._cmd_execution_state['removereceptors'] = CmdExecState.IDLE + return + except tango.DevFailed as tango_err: + tango.Except.throw_exception("Command failed", + tango_err.args[0].desc, + "RemoveReceptors", + tango.ErrSeverity.ERR) + except AttributeError as attr_err: + log_msg = "RemoveReceptors:" + str(attr_err) + self.logger.error(log_msg) + tango.Except.throw_exception("Command failed", + str(attr_err), + "RemoveReceptors", + tango.ErrSeverity.ERR) # PROTECTED REGION END # // MidCspSubarrayBase.RemoveReceptors @AdminModeCheck('RemoveAllReceptors') @@ -715,23 +732,9 @@ class MidCspSubarrayBase(CspSubarray): when an exception is caught during command execution. """ # PROTECTED REGION ID(CspSubarray.RemoveAllReceptors) ENABLED START # - if self._is_sc_subarray_running(self.CbfSubarray): - try: - proxy = self._sc_subarray_proxies[self.CbfSubarray] - # check if the list of assigned receptors is empty - receptors = proxy.receptors - if len(receptors): - self.RemoveReceptors(receptors[:]) - except tango.DevFailed as df: - log_msg = ("RemoveAllReceptors failure. Reason: {} " - "Desc: {}".format(df.args[0].reason, - df.args[0].desc)) - self.logger.error(log_msg) - tango.Except.re_throw_exception(df, "Command failed", - "CspSubarray RemoveAllReceptors command failed", - "Command()", - tango.ErrSeverity.ERR) - else: + + # check if the CBF Subarray TANGO device is running + if not self._is_sc_subarray_running(self.CbfSubarray): log_msg = "Subarray {} not registered!".format( str(self.CbfSubarray)) self.logger.error(log_msg) @@ -739,6 +742,21 @@ class MidCspSubarrayBase(CspSubarray): log_msg, "RemoveAllReceptors", tango.ErrSeverity.ERR) + try: + proxy = self._sc_subarray_proxies[self.CbfSubarray] + # check if the list of assigned receptors is empty + receptors = proxy.receptors + if len(receptors): + self.RemoveReceptors(receptors[:]) + except tango.DevFailed as df: + log_msg = ("RemoveAllReceptors failure. Reason: {} " + "Desc: {}".format(df.args[0].reason, + df.args[0].desc)) + self.logger.error(log_msg) + tango.Except.re_throw_exception(df, "Command failed", + "CspSubarray RemoveAllReceptors command failed", + "Command()", + tango.ErrSeverity.ERR) # PROTECTED REGION END # // MidCspSubarrayBase.RemoveAllReceptors diff --git a/csp-lmc-mid/tests/MidCspSubarray_test.py b/csp-lmc-mid/tests/MidCspSubarray_test.py index e254a23648d70b3401f6cd84b7be6a496b0e6094..0d77e8f88876f55124153f40444c68acbc4c0387 100755 --- a/csp-lmc-mid/tests/MidCspSubarray_test.py +++ b/csp-lmc-mid/tests/MidCspSubarray_test.py @@ -258,6 +258,25 @@ class TestCspSubarray(object): obs_state = midcsp_subarray01.obsState assert obs_state == ObsState.READY + def test_configureScan_with_subarray_ready(self, midcsp_subarray01, midcsp_master): + """ + Test that the Configure() command is issued when the Subarray + state already READY + """ + obs_state = midcsp_subarray01.obsState + assert obs_state == ObsState.READY + f = open(file_path +"/test_ConfigureScan_basic.json") + obs_state = midcsp_subarray01.obsState + try: + midcsp_subarray01.Configure(f.read().replace("\n", "")) + except tango.DevFailed as tango_err: + print("configure error:", tango_err.args[0].desc) + obs_state = midcsp_subarray01.obsState + f.close() + time.sleep(5) + obs_state = midcsp_subarray01.obsState + assert obs_state == ObsState.READY + def test_remove_receptors_when_ready(self, midcsp_subarray01): """ Test that the complete deallocation of receptors fails