diff --git a/csp-lmc-common/csp_lmc_common/CspSubarray.py b/csp-lmc-common/csp_lmc_common/CspSubarray.py index 889ab06c3dad523f432620c2bbf8cb789250d1cc..d7aaf2e41b5f7ef97a3f7b62dc0b6a4ea553e882 100644 --- a/csp-lmc-common/csp_lmc_common/CspSubarray.py +++ b/csp-lmc-common/csp_lmc_common/CspSubarray.py @@ -319,8 +319,73 @@ class CspSubarray(SKASubarray): apiutil = tango.ApiUtil.instance() apiutil.set_asynch_cb_sub_model(tango.cb_sub_model.PUSH_CALLBACK) self.logger.info(message) + + alignment_thr = threading.Thread(target=self.obs_state_alignment, + name="Thread-Initialization ObsState Alignment", + args=()) + alignment_thr.start() + return (result_code, message) + def obs_state_alignment(self): + + device = self.target + # create a list with all the obs_states of sub-elements + # !!! delete the logger !!! + self.logger.info(f'obs state of CBF: {device._sc_subarray_obs_state[self.CbfSubarray].name}') + + obs_states_list = [device._sc_subarray_obs_state[fqdn].name for fqdn in + device._sc_subarray_fqdn] + # obs_state of cbf_subarray + #cbf_obs_state = device._sc_subarray_obs_state[self.CbfSubarray].name + # allowed coupled states for different sub-elements + allowed_coupled = {'RESOURCING': 'IDLE', + 'RESOURCING': 'EMPTY', + 'CONFIGURING': 'READY', + 'SCANNING': 'READY', + 'ABORTING': 'ABORTED', + 'RESETTING': 'IDLE', + 'RESTARTING': 'EMPTY' + } + transitional_states = allowed_coupled.keys() + timeout = 10 # seconds + + # CASE B: CBF is ON + if device._sc_subarray_state[self.CbfSubarray] == DevState.ON: + # start a loop in case of transitional states (CASE 2) + starting_time = time.time() + exit_loop = False + while time.time() - starting_time < timeout or exit_loop: + # first creates an intersection list + transitional_present = set(transitional_states) & set(obs_states_list) + # CASE 1: Transitional states NOT present + if not transitional_present: + # CASE 1.1: All obsStates are EQUAL + if len(set(obs_states_list)) == 1: + self.state_model._update_obs_state(obs_states_list[0]) + exit_loop = True + # CASE 1.1: obsStates are DIFFERENT + else: + self.state_model._update_obs_state('FAULT') + exit_loop = True + # CASE 2: Transitional states ARE present + else: + state = transitional_present[0] + for sub_elt_obs_state in obs_states_list: + # CASE 2.1: Other obs_states are NOT ALLOWED + if sub_elt_obs_state is not (state or allowed_coupled[state]): + self.state_model._update_obs_state('FAULT') + exit_loop = True + break + # CASE 2.2: Other obs_states are ALLOWED + else: + self.state_model._update_obs_state(state) + # CASE A: CBF is OFF + else: + self.state_model._update_op_state('OFF') + self.state_model._update_obs_state('EMPTY') + + class OnCommand(SKASubarray.OnCommand): def do(self): super().do() @@ -694,26 +759,26 @@ class CspSubarray(SKASubarray): self.logger.info("CspSubarray timeout flag:{}".format(target_device._timeout_expired)) if target_device._abort_obs_event.is_set(): if target_device._timeout_expired or target_device._failure_raised: - return target_device.abort_cmd_obj.failed() + return target_device._abort_cmd_obj.failed() self.logger.info("Abort configure ends with success!!") if all(target_device._sc_subarray_obs_state[fqdn] == ObsState.ABORTED for fqdn in device_list): - return target_device.abort_cmd_obj.succeeded() - return target_device.abort_cmd_obj.abort_monitoring(device_list) + return target_device._abort_cmd_obj.succeeded() + return target_device._abort_cmd_obj.abort_monitoring(device_list) if target_device._timeout_expired or target_device._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 target_device._sc_subarray_obs_state[target_device.CbfSubarray] == ObsState.READY: - return target_device.configure_cmd_obj.succeeded() + return target_device._configure_cmd_obj.succeeded() self.logger.info("Configure ends with failure") - return target_device.configure_cmd_obj.failed() + return target_device._configure_cmd_obj.failed() if all(target_device._sc_subarray_obs_state[fqdn] == ObsState.READY for fqdn in device_list): target_device._valid_scan_configuration = input_arg[1] target_device._cmd_duration_measured[cmd_name] = time.time() - command_start_time target_device._cmd_progress[cmd_name] = 100 target_device._last_executed_command = cmd_name self.logger.info("Configure ends with success!!") - return target_device.configure_cmd_obj.succeeded() + return target_device._configure_cmd_obj.succeeded() def validate_scan_configuration(self, argin): """ @@ -848,9 +913,9 @@ class CspSubarray(SKASubarray): return if target_device._abort_obs_event.is_set(): if target_device._failure_raised or target_device._timeout_expired: - return target_device.abort_cmd_obj.failed() + return target_device._abort_cmd_obj.failed() self.logger.info("Abort of scan command ends with success!") - return target_device.abort_cmd_obj.succeeded() + return target_device._abort_cmd_obj.succeeded() class EndScanCommand(SKASubarray.EndScanCommand): @@ -902,7 +967,7 @@ class CspSubarray(SKASubarray): if target_device._sc_subarray_obs_state[device] == ObsState.IDLE: continue if target_device._sc_subarray_obs_state[device] == ObsState.READY: - (result_code, msg) = target_device.gotoidle_cmd_obj.do() + (result_code, msg) = target_device._gotoidle_cmd_obj.do() return (result_code, msg) for device in devices_to_reset: try: @@ -959,13 +1024,13 @@ class CspSubarray(SKASubarray): # end of the while loop # check for timeout/failure conditions on each sub-component if target_device._failure_raised or target_device._timeout_expired: - return target_device.obsreset_cmd_obj.failed() + return target_device._obsreset_cmd_obj.failed() if all(target_device._sc_subarray_obs_state[fqdn] == dev_successful_state for fqdn in device_list): target_device._cmd_progress[cmd_name] = 100 target_device._last_executed_command = cmd_name self.logger.info("ObsReset ends with success") - return target_device.obsreset_cmd_obj.succeeded() + return target_device._obsreset_cmd_obj.succeeded() class AbortCommand(SKASubarray.AbortCommand): @@ -1040,13 +1105,13 @@ class CspSubarray(SKASubarray): # end of the while loop # check for timeout/failure conditions on each sub-component if target_device._failure_raised or target_device._timeout_expired: - return target_device.abort_cmd_obj.failed() + return target_device._abort_cmd_obj.failed() if all(target_device._sc_subarray_obs_state[fqdn] == ObsState.ABORTED for fqdn in device_list): target_device._cmd_progress[cmd_name] = 100 target_device._last_executed_command = cmd_name self.logger.info("Abort ends with success") - return target_device.abort_cmd_obj.succeeded() + return target_device._abort_cmd_obj.succeeded() class RestartCommand(SKASubarray.RestartCommand): @@ -1123,13 +1188,13 @@ class CspSubarray(SKASubarray): # end of the while loop # check for timeout/failure conditions on each sub-component if target_device._failure_raised or target_device._timeout_expired: - return target_device.restart_cmd_obj.failed() + return target_device._restart_cmd_obj.failed() if all(target_device._sc_subarray_obs_state[fqdn] == ObsState.EMPTY for fqdn in device_list): target_device._cmd_progress[cmd_name] = 100 target_device._last_executed_command = cmd_name self.logger.info("Restart ends with success") - return target_device.restart_cmd_obj.succeeded() + return target_device._restart_cmd_obj.succeeded() ''' class GoToIdleCommand(ActionCommand): @@ -1299,14 +1364,14 @@ class CspSubarray(SKASubarray): self.logger.info("1") target_device._sc_subarray_cmd_exec_state[device][cmd_name] = CmdExecState.IDLE if target_device._failure_raised or target_device._timeout_expired: - return target_device.gotoidle_cmd_obj.failed() + return target_device._gotoidle_cmd_obj.failed() self.logger.info("2") if all(target_device._sc_subarray_obs_state[fqdn] == dev_successful_state for fqdn in device_list): target_device._last_executed_command = cmd_name # reset the CSP Subarray command execution flag target_device._cmd_execution_state[cmd_name] = CmdExecState.IDLE - return target_device.gotoidle_cmd_obj.succeeded() + return target_device._gotoidle_cmd_obj.succeeded() def check_allowed(self): """ @@ -1441,7 +1506,7 @@ class CspSubarray(SKASubarray): # update CSP sub-array SCM #07-2020: with the new base classes, transitions are handled via actions. #if evt.attr_value.name.lower() in ["state", "healthstate", "adminmode", "obsstate"]: - # self.update_subarray_state() + # self.update_subarray_state() if evt.attr_value.name.lower() == "healthstate": self._update_subarray_health_state() except tango.DevFailed as df: @@ -1579,7 +1644,7 @@ class CspSubarray(SKASubarray): # Class protected methods # --------------- - + def update_subarray_state(self): """ Class protected method. @@ -1603,12 +1668,12 @@ class CspSubarray(SKASubarray): # to determine the CSP health state. if self._sc_subarray_state[self.CbfSubarray] == DevState.OFF: if self._sc_subarray_obs_state[self.CbfSubarray] == ObsState.EMPTY: - self.off_cmd_obj.succeeded() + self._off_cmd_obj.succeeded() if self._sc_subarray_state[self.CbfSubarray] == DevState.ON: if self._sc_subarray_obs_state[self.CbfSubarray] == ObsState.EMPTY: - self.on_cmd_obj.succeeded() + self._on_cmd_obj.succeeded() if self._sc_subarray_obs_state[self.CbfSubarray] == ObsState.READY: - self.configure_cmd_obj.succeeded() + self._configure_cmd_obj.succeeded() #self.set_state(self._sc_subarray_state[self.CbfSubarray]) #self.logger.info("Csp subarray state: {} obsState: {}".format(self.get_state(), self.state_model._obs_state)) return True @@ -1854,14 +1919,19 @@ class CspSubarray(SKASubarray): super().init_command_objects() args = (self, self.state_model, self.logger) - self.configure_cmd_obj = self.ConfigureCommand(*args) - self.off_cmd_obj = self.OffCommand(*args) - self.on_cmd_obj = self.OnCommand(*args) - self.scan_cmd_obj = self.ScanCommand(*args) - self.gotoidle_cmd_obj = self.GoToIdleCommand(*args) - self.obsreset_cmd_obj = self.ObsResetCommand(*args) - self.abort_cmd_obj = self.AbortCommand(*args) - self.restart_cmd_obj = self.RestartCommand(*args) + self._configure_cmd_obj = self.ConfigureCommand(*args) + self._off_cmd_obj = self.OffCommand(*args) + self._on_cmd_obj = self.OnCommand(*args) + self._scan_cmd_obj = self.ScanCommand(*args) + self._gotoidle_cmd_obj = self.GoToIdleCommand(*args) + self._obsreset_cmd_obj = self.ObsResetCommand(*args) + self._abort_cmd_obj = self.AbortCommand(*args) + self._restart_cmd_obj = self.RestartCommand(*args) + self._assignresources_cmd_obj = self.AssignResourcesCommand(*args) + self._releaseresources_cmd_obj = self.ReleaseResourcesCommand(*args) + self.register_command_object("AssignResources", self.AssignResourcesCommand(*args)) + self.register_command_object("ReleaseResources", self.ReleaseResourcesCommand(*args)) + self.register_command_object("ReleaseAllResources", self.ReleaseAllResourcesCommand(*args)) self.register_command_object("GoToIdle", self.GoToIdleCommand(*args)) self.register_command_object("Configure", self.ConfigureCommand(*args)) self.register_command_object("Scan", self.ScanCommand(*args)) diff --git a/csp-lmc-common/tests/unit/cspsubarray_unit_test.py b/csp-lmc-common/tests/unit/cspsubarray_unit_test.py index 09460846e10f31ffe76383b471c01d0008c04c89..a760e18f565aa77463ff8fa7afa3bc3419123f54 100644 --- a/csp-lmc-common/tests/unit/cspsubarray_unit_test.py +++ b/csp-lmc-common/tests/unit/cspsubarray_unit_test.py @@ -21,28 +21,103 @@ def test_cspsubarray_state_and_obstate_value_after_initialization(): """ device_under_test = CspSubarray cbf_subarray_fqdn = 'mid_csp_cbf/sub_elt/subarray_01' - cbf_subarray_state_attr = 'State' + pss_subarray_fqdn = 'mid_csp_pss/sub_elt/subarray_01' + pst_subarray_fqdn = 'mid_csp_pst/sub_elt/subarray_01' + + state_attr = 'State' dut_properties = { - 'CspMaster':'mid_csp/elt/master', + 'CspMaster': 'mid_csp/elt/master', 'CbfSubarray': cbf_subarray_fqdn, - 'PssSubarray': 'mid_csp_pss/sub_elt/subarray_01', - 'PstSubarray': 'mid_csp_pst/sub_elt/subarray_01', + 'PssSubarray': pss_subarray_fqdn, + 'PstSubarray': pst_subarray_fqdn } - event_subscription_map = {} cbf_subarray_device_proxy_mock = Mock() + pss_subarray_device_proxy_mock = Mock() + pst_subarray_device_proxy_mock = Mock() + + event_subscription_map_cbf= {} cbf_subarray_device_proxy_mock.subscribe_event.side_effect = ( - lambda attr_name, event_type, callback, *args, **kwargs: event_subscription_map.update({attr_name: callback})) + lambda attr_name, event_type, callback, *args, **kwargs: event_subscription_map_cbf.update({attr_name: callback})) + + event_subscription_map_pss = {} + cbf_subarray_device_proxy_mock.subscribe_event.side_effect = ( + lambda attr_name, event_type, callback, *args, **kwargs: event_subscription_map_pss.update( + {attr_name: callback})) + + event_subscription_map_pst = {} + cbf_subarray_device_proxy_mock.subscribe_event.side_effect = ( + lambda attr_name, event_type, callback, *args, **kwargs: event_subscription_map_pst.update( + {attr_name: callback})) proxies_to_mock = { - cbf_subarray_fqdn: cbf_subarray_device_proxy_mock + cbf_subarray_fqdn: cbf_subarray_device_proxy_mock, + pss_subarray_fqdn: pss_subarray_device_proxy_mock, + pst_subarray_fqdn: pst_subarray_device_proxy_mock } - + # CASE A: CBF is OFF with fake_tango_system(device_under_test, initial_dut_properties=dut_properties, proxies_to_mock=proxies_to_mock) as tango_context: dummy_event = create_dummy_event(cbf_subarray_fqdn, DevState.OFF) - event_subscription_map[cbf_subarray_state_attr](dummy_event) + event_subscription_map_cbf[state_attr](dummy_event) assert tango_context.device.State() == DevState.OFF assert tango_context.device.obsState == ObsState.EMPTY + # CASE B: CBF is ON + # + # CASE 1.1 ObsState are EQUAL + with fake_tango_system(device_under_test, initial_dut_properties=dut_properties, proxies_to_mock=proxies_to_mock) as tango_context: + dummy_event_cbf = create_dummy_obs_event(cbf_subarray_fqdn, ObsState.READY) + dummy_event_pss = create_dummy_obs_event(cbf_subarray_fqdn, ObsState.READY) + dummy_event_pst = create_dummy_obs_event(cbf_subarray_fqdn, ObsState.READY) + + event_subscription_map_cbf[state_attr](dummy_event_cbf) + event_subscription_map_pss[state_attr](dummy_event_pss) + event_subscription_map_pst[state_attr](dummy_event_pst) + + assert tango_context.device.State() == DevState.ON + assert tango_context.device.obsState == ObsState.READY + # + # CASE 1.2 ObsState are DIFFERENT + with fake_tango_system(device_under_test, initial_dut_properties=dut_properties, proxies_to_mock=proxies_to_mock) as tango_context: + dummy_event_cbf = create_dummy_obs_event(cbf_subarray_fqdn, ObsState.READY) + dummy_event_pss = create_dummy_obs_event(cbf_subarray_fqdn, ObsState.FAULT) + dummy_event_pst = create_dummy_obs_event(cbf_subarray_fqdn, ObsState.READY) + + event_subscription_map_cbf[state_attr](dummy_event_cbf) + event_subscription_map_pss[state_attr](dummy_event_pss) + event_subscription_map_pst[state_attr](dummy_event_pst) + + assert tango_context.device.State() == DevState.ON + assert tango_context.device.obsState == ObsState.FAULT + # + # CASE 2.1 Transitional states ALLOWED + with fake_tango_system(device_under_test, initial_dut_properties=dut_properties, proxies_to_mock=proxies_to_mock) as tango_context: + dummy_event_cbf = create_dummy_obs_event(cbf_subarray_fqdn, ObsState.CONFIGURING) + dummy_event_pss = create_dummy_obs_event(cbf_subarray_fqdn, ObsState.READY) + dummy_event_pst = create_dummy_obs_event(cbf_subarray_fqdn, ObsState.READY) + + event_subscription_map_cbf[state_attr](dummy_event_cbf) + event_subscription_map_pss[state_attr](dummy_event_pss) + event_subscription_map_pst[state_attr](dummy_event_pst) + + assert tango_context.device.State() == DevState.ON + assert tango_context.device.obsState == ObsState.CONFIGURING + + dummy_event_cbf = create_dummy_obs_event(cbf_subarray_fqdn, ObsState.READY) + assert tango_context.device.obsState == ObsState.READY + # + # CASE 2.1 Transitional states NOT ALLOWED + with fake_tango_system(device_under_test, initial_dut_properties=dut_properties, proxies_to_mock=proxies_to_mock) as tango_context: + dummy_event_cbf = create_dummy_obs_event(cbf_subarray_fqdn, ObsState.CONFIGURING) + dummy_event_pss = create_dummy_obs_event(cbf_subarray_fqdn, ObsState.READY) + dummy_event_pst = create_dummy_obs_event(cbf_subarray_fqdn, ObsState.FAULT) + + event_subscription_map_cbf[state_attr](dummy_event_cbf) + event_subscription_map_pss[state_attr](dummy_event_pss) + event_subscription_map_pst[state_attr](dummy_event_pst) + + assert tango_context.device.State() == DevState.ON + assert tango_context.device.obsState == ObsState.FAULT + def test_cspsbarray_state_after_On_WITH_exception_raised_by_subelement_subarray(): """ Test the behavior of the CspSubarray when one of the sub-element subarray diff --git a/csp-lmc-mid/csp_lmc_mid/MidCspSubarrayBase.py b/csp-lmc-mid/csp_lmc_mid/MidCspSubarrayBase.py index 1bfe5c72a765c96ea85cbf26a03ffb2aa492bf18..1231ea73ac548739cbf40114a796036d9a03abcd 100644 --- a/csp-lmc-mid/csp_lmc_mid/MidCspSubarrayBase.py +++ b/csp-lmc-mid/csp_lmc_mid/MidCspSubarrayBase.py @@ -645,20 +645,6 @@ class MidCspSubarrayBase(CspSubarray): #def Configure(self, argin): # CspSubarray.Configure(self, argin) - def init_command_objects(self): - """ - Sets up the command objects - """ - super().init_command_objects() - args = (self, self.state_model, self.logger) - - self._assignresources_cmd_obj = self.AssignResourcesCommand(*args) - self._releaseresources_cmd_obj = self.ReleaseResourcesCommand(*args) - self.register_command_object("AssignResources", self.AssignResourcesCommand(*args)) - self.register_command_object("ReleaseResources", self.ReleaseResourcesCommand(*args)) - self.register_command_object("ReleaseAllResources", self.ReleaseAllResourcesCommand(*args)) - - # PROTECTED REGION END # // MidCspSubarrayBase.private_methods # ---------------- # Class Properties