Loading csp-lmc-common/utils/cspcommons.py +7 −0 Original line number Diff line number Diff line Loading @@ -40,3 +40,10 @@ class ObsState(IntEnum): PAUSED = 4 ABORTED = 5 FAULT = 6 @unique class CmdExecState(IntEnum): IDLE = 0 RUNNING = 1 QUEUED = 2 csp-lmc-common/utils/decorators.py +161 −12 Original line number Diff line number Diff line Loading @@ -7,7 +7,7 @@ sys.path.insert(0, utils_path) import tango import cspcommons from cspcommons import AdminMode from cspcommons import AdminMode, CmdExecState import functools class AdminModeCheck(object): Loading @@ -23,27 +23,176 @@ class AdminModeCheck(object): @functools.wraps(f) def admin_mode_check(*args, **kwargs): # get the device instance dev = args[0] dev_instance = args[0] cmd_to_execute = self._args print("AdminModeCheck command to execute:", cmd_to_execute) # Check the AdminMode value: the command is callable only if the # the administration mode is ONLINE or MAINTENACE if dev._admin_mode in [AdminMode.OFFLINE, AdminMode.NOTFITTED, if dev_instance._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 = (dev.get_state(), AdminMode(dev._admin_mode).name) if dev.get_state() != tango.DevState.DISABLE: dev.dev_logging("is_On_allowed: incoherent device State {} " msg_args = (cmd_to_execute, dev_instance.get_state(), AdminMode(dev_instance._admin_mode).name) if dev_instance.get_state() != tango.DevState.DISABLE: dev_instance.dev_logging("Incoherent device State {} " " with adminMode {}".format(*msg_args), tango.LogLevel.LOG_WARN) err_msg = ("On() command can't be issued when State is {} and" err_msg = ("{} command can't be issued when State is {} and" " adminMode is {} ".format(*msg_args)) tango.Except.throw_exception("Command not executable", tango.Except.throw_exception("Invalid adminMode value", err_msg, "On command execution", "AdminModeCheck decorator", tango.ErrSeverity.ERR) return f(*args, **kwargs) return admin_mode_check class CmdInputArgsCheck(object): """ Class designed to be a decorator for the CspMaster methods. The *decorator function* performs a check on the function input argument, detecting if the corresponding servers are running and the device administrative mode are in the right state to execute a command (i.e ONLINE/MAINTENACE) The *decorator function* accepts some parameters as input to customize its functionality """ def __init__(self, *args, **kwargs): # store the decorators parameters: # args: the list of sub-element attributes to subscribe, to track the # sub-element command progress and detect a command timeout # kwargs: a dictionary: key ="cmd_name", # value = command to execute ('on', 'off'...) # self._args = args self._kwargs = kwargs def __call__(self, f): @functools.wraps(f) def input_args_check(*args, **kwargs): dev_instance = args[0] # get the information passed as arguments of the decorator attrs_to_subscribe = self._args try: exec_cmd = self._kwargs['cmd_name'] except KeyError as key_err: msg =("No key found for {}".fomat(str(key_err))) self.dev_logging(msg, tango.LogLevel.LOG_WARN) # TODO: throw an exception return input_arg = args[1] device_list = [] num_of_devices = len(input_arg) if num_of_devices == 0: # no input argument -> switch on all sub-elements num_of_devices = len(dev_instance._se_fqdn) device_list = dev_instance._se_fqdn else: if num_of_devices > len(dev_instance._se_fqdn): # too many devices specified-> log the warning but go on # with command execution dev_instance.dev_logging("Too many input parameters", tango.LogLevel.LOG_WARN) device_list = input_arg print("CmdInputArgsCheck: devices {} to check:".format(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 CmdExecState.RUNNING in dev_instance._cmd_execution_state.values(): print("command {} is running!!".format(exec_cmd)) # Note: need to check in the list of sub-elements, not in the device_list sent to the command!!! for device in dev_instance._se_fqdn: for cmd_name, cmd_state in dev_instance._se_cmd_execution_state[device].items(): if cmd_state in [CmdExecState.RUNNING, CmdExecState.QUEUED]: msg = ("Device {} is already executing the {} command" "Command state: {}".format(device, cmd_name, dev_instance._se_cmd_execution_state[device][cmd_name])) print(msg) # TODO throw an exception if the command already running is not # the one requested # return if the requested command is already running return device_to_remove = [] # buld the sub-element attribute name storing the command duration # expected value. cmd_time_attr_name = exec_cmd + "DurationExpected" for device in device_list: print("processing 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 if (not dev_instance._is_se_device_running(device) or dev_instance._se_admin_mode[device] not in [AdminMode.ONLINE, AdminMode.MAINTENANCE]): if device.find("cbf") > -1: msg = ("Can't power-on device {} with" " adminMode {}".format(device, AdminMode(dev_instance._se_admin_mode[device]).name)) tango.Except.throw_exception("Command failure", msg, "On()", tango.ErrSeverity.ERR) return device_to_remove.append(device) print("Devices to remove from the list:", device_to_remove) continue for device in device_to_remove: device_list.remove(device) if not device_list: dev_instance.dev_logging("No device to power on", tango.LogLevel.LOG_INFO) return command_timeout = 0 for device in device_list: # set to QUEUED. This state can be useful in case of command abort. dev_instance._se_cmd_execution_state[device][exec_cmd] = CmdExecState.QUEUED # reset the timeout attribute content dev_instance._timeout_expired[exec_cmd] = False # reset the timeout attribute content for each sub-element for cmd_name, _ in dev_instance._se_timeout_expired[device].items(): if dev_instance._se_timeout_expired[device][cmd_name]: msg = ("Device {} expired on command {}." " Clean the timeout counter".format(device, cmd_name)) print(msg) dev_instance._se_timeout_expired[device][cmd_name] = False device_proxy = dev_instance._se_proxies[device] try: # get the sub-element value for the onCommandDurationExpected attribute. # If this attribute is not implemented at sub-element level, # the default value is used. # Note: if onCmdDurationExpected attribute is not implemented, the # call device_proxy.onCmdDurationExpected throw an AttributeError exception # (not tango.DevFailed) #dev_instance._se_cmd_duration_expected[device][exec_cmd] = device_proxy.onCmdDurationExpected dev_instance._se_cmd_duration_expected[device][exec_cmd] = device_proxy.read_attribute(cmd_time_attr_name) except tango.DevFailed as tango_err: # we get here if the attribute is not implemented print(tango_err.args[0].desc) for attr in attrs_to_subscribe: try: if dev_instance._se_event_id[device][attr.lower()] == 0: evt_id = device_proxy.subscribe_event(attr, tango.EventType.CHANGE_EVENT, dev_instance._attributes_change_evt_cb, stateless=False) dev_instance._se_event_id[device][attr.lower()] = evt_id except tango.DevFailed as df: print(df.args[0].desc) # evaluate the total timeout value to execute the whole command # note: if the xxxDurationExpected attribute read fails, it is used the default value # returned from the defauld dictionary used to initialize the _se_cmd_duration_expected # attribute command_timeout += dev_instance._se_cmd_duration_expected[device][exec_cmd] # end of the for loop # use the greatest value for the onCommand duration expected. if command_timeout > dev_instance._cmd_duration_expected[exec_cmd]: dev_instance._cmd_duration_expected[exec_cmd] = command_timeout dev_instance.dev_logging("Modified the {} command Duration Expected value!!".format(exec_cmd), tango.LogLevel.LOG_INFO) print("CmdInputArgsCheck end!!") return f(*args, **kwargs) return input_args_check No newline at end of file Loading
csp-lmc-common/utils/cspcommons.py +7 −0 Original line number Diff line number Diff line Loading @@ -40,3 +40,10 @@ class ObsState(IntEnum): PAUSED = 4 ABORTED = 5 FAULT = 6 @unique class CmdExecState(IntEnum): IDLE = 0 RUNNING = 1 QUEUED = 2
csp-lmc-common/utils/decorators.py +161 −12 Original line number Diff line number Diff line Loading @@ -7,7 +7,7 @@ sys.path.insert(0, utils_path) import tango import cspcommons from cspcommons import AdminMode from cspcommons import AdminMode, CmdExecState import functools class AdminModeCheck(object): Loading @@ -23,27 +23,176 @@ class AdminModeCheck(object): @functools.wraps(f) def admin_mode_check(*args, **kwargs): # get the device instance dev = args[0] dev_instance = args[0] cmd_to_execute = self._args print("AdminModeCheck command to execute:", cmd_to_execute) # Check the AdminMode value: the command is callable only if the # the administration mode is ONLINE or MAINTENACE if dev._admin_mode in [AdminMode.OFFLINE, AdminMode.NOTFITTED, if dev_instance._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 = (dev.get_state(), AdminMode(dev._admin_mode).name) if dev.get_state() != tango.DevState.DISABLE: dev.dev_logging("is_On_allowed: incoherent device State {} " msg_args = (cmd_to_execute, dev_instance.get_state(), AdminMode(dev_instance._admin_mode).name) if dev_instance.get_state() != tango.DevState.DISABLE: dev_instance.dev_logging("Incoherent device State {} " " with adminMode {}".format(*msg_args), tango.LogLevel.LOG_WARN) err_msg = ("On() command can't be issued when State is {} and" err_msg = ("{} command can't be issued when State is {} and" " adminMode is {} ".format(*msg_args)) tango.Except.throw_exception("Command not executable", tango.Except.throw_exception("Invalid adminMode value", err_msg, "On command execution", "AdminModeCheck decorator", tango.ErrSeverity.ERR) return f(*args, **kwargs) return admin_mode_check class CmdInputArgsCheck(object): """ Class designed to be a decorator for the CspMaster methods. The *decorator function* performs a check on the function input argument, detecting if the corresponding servers are running and the device administrative mode are in the right state to execute a command (i.e ONLINE/MAINTENACE) The *decorator function* accepts some parameters as input to customize its functionality """ def __init__(self, *args, **kwargs): # store the decorators parameters: # args: the list of sub-element attributes to subscribe, to track the # sub-element command progress and detect a command timeout # kwargs: a dictionary: key ="cmd_name", # value = command to execute ('on', 'off'...) # self._args = args self._kwargs = kwargs def __call__(self, f): @functools.wraps(f) def input_args_check(*args, **kwargs): dev_instance = args[0] # get the information passed as arguments of the decorator attrs_to_subscribe = self._args try: exec_cmd = self._kwargs['cmd_name'] except KeyError as key_err: msg =("No key found for {}".fomat(str(key_err))) self.dev_logging(msg, tango.LogLevel.LOG_WARN) # TODO: throw an exception return input_arg = args[1] device_list = [] num_of_devices = len(input_arg) if num_of_devices == 0: # no input argument -> switch on all sub-elements num_of_devices = len(dev_instance._se_fqdn) device_list = dev_instance._se_fqdn else: if num_of_devices > len(dev_instance._se_fqdn): # too many devices specified-> log the warning but go on # with command execution dev_instance.dev_logging("Too many input parameters", tango.LogLevel.LOG_WARN) device_list = input_arg print("CmdInputArgsCheck: devices {} to check:".format(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 CmdExecState.RUNNING in dev_instance._cmd_execution_state.values(): print("command {} is running!!".format(exec_cmd)) # Note: need to check in the list of sub-elements, not in the device_list sent to the command!!! for device in dev_instance._se_fqdn: for cmd_name, cmd_state in dev_instance._se_cmd_execution_state[device].items(): if cmd_state in [CmdExecState.RUNNING, CmdExecState.QUEUED]: msg = ("Device {} is already executing the {} command" "Command state: {}".format(device, cmd_name, dev_instance._se_cmd_execution_state[device][cmd_name])) print(msg) # TODO throw an exception if the command already running is not # the one requested # return if the requested command is already running return device_to_remove = [] # buld the sub-element attribute name storing the command duration # expected value. cmd_time_attr_name = exec_cmd + "DurationExpected" for device in device_list: print("processing 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 if (not dev_instance._is_se_device_running(device) or dev_instance._se_admin_mode[device] not in [AdminMode.ONLINE, AdminMode.MAINTENANCE]): if device.find("cbf") > -1: msg = ("Can't power-on device {} with" " adminMode {}".format(device, AdminMode(dev_instance._se_admin_mode[device]).name)) tango.Except.throw_exception("Command failure", msg, "On()", tango.ErrSeverity.ERR) return device_to_remove.append(device) print("Devices to remove from the list:", device_to_remove) continue for device in device_to_remove: device_list.remove(device) if not device_list: dev_instance.dev_logging("No device to power on", tango.LogLevel.LOG_INFO) return command_timeout = 0 for device in device_list: # set to QUEUED. This state can be useful in case of command abort. dev_instance._se_cmd_execution_state[device][exec_cmd] = CmdExecState.QUEUED # reset the timeout attribute content dev_instance._timeout_expired[exec_cmd] = False # reset the timeout attribute content for each sub-element for cmd_name, _ in dev_instance._se_timeout_expired[device].items(): if dev_instance._se_timeout_expired[device][cmd_name]: msg = ("Device {} expired on command {}." " Clean the timeout counter".format(device, cmd_name)) print(msg) dev_instance._se_timeout_expired[device][cmd_name] = False device_proxy = dev_instance._se_proxies[device] try: # get the sub-element value for the onCommandDurationExpected attribute. # If this attribute is not implemented at sub-element level, # the default value is used. # Note: if onCmdDurationExpected attribute is not implemented, the # call device_proxy.onCmdDurationExpected throw an AttributeError exception # (not tango.DevFailed) #dev_instance._se_cmd_duration_expected[device][exec_cmd] = device_proxy.onCmdDurationExpected dev_instance._se_cmd_duration_expected[device][exec_cmd] = device_proxy.read_attribute(cmd_time_attr_name) except tango.DevFailed as tango_err: # we get here if the attribute is not implemented print(tango_err.args[0].desc) for attr in attrs_to_subscribe: try: if dev_instance._se_event_id[device][attr.lower()] == 0: evt_id = device_proxy.subscribe_event(attr, tango.EventType.CHANGE_EVENT, dev_instance._attributes_change_evt_cb, stateless=False) dev_instance._se_event_id[device][attr.lower()] = evt_id except tango.DevFailed as df: print(df.args[0].desc) # evaluate the total timeout value to execute the whole command # note: if the xxxDurationExpected attribute read fails, it is used the default value # returned from the defauld dictionary used to initialize the _se_cmd_duration_expected # attribute command_timeout += dev_instance._se_cmd_duration_expected[device][exec_cmd] # end of the for loop # use the greatest value for the onCommand duration expected. if command_timeout > dev_instance._cmd_duration_expected[exec_cmd]: dev_instance._cmd_duration_expected[exec_cmd] = command_timeout dev_instance.dev_logging("Modified the {} command Duration Expected value!!".format(exec_cmd), tango.LogLevel.LOG_INFO) print("CmdInputArgsCheck end!!") return f(*args, **kwargs) return input_args_check No newline at end of file