Loading csp-lmc-common/csp-lmc-common/CspMaster.py +417 −8 Original line number Diff line number Diff line Loading @@ -11,25 +11,47 @@ CSP.LMC Common Class for the CSPMaster TANGO Device. """ # PROTECTED REGION ID (CspMaster.standardlibray_import) ENABLED START # # Python standard library from __future__ import absolute_import import sys import os from future.utils import with_metaclass from collections import defaultdict from enum import IntEnum, unique # PROTECTED REGION END# //CspMaster.standardlibray_import # tango imports# PROTECTED REGION ID (CspMaster.add_path) ENABLED START # # tango imports import tango from tango import DebugIt from tango.server import run from tango.server import Device, DeviceMeta from tango.server import attribute, command from tango.server import device_property from tango import AttrQuality, DispLevel, DevState from tango import AttrWriteType, PipeWriteType from skabase.SKAMaster import SKAMaster from tango import AttrQuality, EventType, DevState from tango import AttrWriteType, DeviceProxy # Additional import # PROTECTED REGION ID(CspMaster.additionnal_import) ENABLED START # # from skabase.SKAMaster import SKAMaster from skabase.auxiliary import utils # PROTECTED REGION END # // CspMaster.additionnal_import # PROTECTED REGION ID (CspMaster.add_path) ENABLED START # # add the path to import global_enum package. 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 cspcommons from cspcommons import HealthState, AdminMode, ObsState import release # PROTECTED REGION END# //CspMaster.add_path __all__ = ["CspMaster", "main"] class CspMaster(SKAMaster): class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): """ CSP.LMC Common Class for the CSPMaster TANGO Device. Loading Loading @@ -66,10 +88,237 @@ class CspMaster(SKAMaster): - Type:'DevVarStringArray' """ __metaclass__ = DeviceMeta # 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 # #---------------- # Event Callback functions # --------------- def __se_smc_change_event_cb(self, evt): """ Class protected callback function. Retrieve the values of the sub-element SCM attributes subscribed for change event at device initialization. :param evt: The event data :return: None """ if not evt.err: dev_name = evt.device.dev_name() try: if dev_name in self._se_fqdn: if evt.attr_value.name.lower() == "state": self._se_state[dev_name] = evt.attr_value.value elif evt.attr_value.name.lower() == "healthstate": self._se_health_state[dev_name] = evt.attr_value.value elif evt.attr_value.name.lower() == "adminmode": self._se_admin_mode[dev_name] = evt.attr_value.value else: log_msg = ("Attribute {} not still " "handled".format(evt.attr_name)) self.dev_logging(log_msg, tango.LogLevel.LOG_WARN) else: log_msg = ("Unexpected change event for" " attribute: {}".format(str(evt.attr_name))) self.dev_logging(log_msg, tango.LogLevel.LOG_WARN) return log_msg = "New value for {} is {}".format(str(evt.attr_name), str(evt.attr_value.value)) self.dev_logging(log_msg, tango.LogLevel.LOG_INFO) # update CSP global state if evt.attr_value.name.lower() in ["state", "healthstate"]: self._update_csp_state() except tango.DevFailed as df: self.dev_logging(str(df.args[0].desc), tango.LogLevel.LOG_ERROR) except Exception as except_occurred: self.dev_logging(str(except_occurred), tango.LogLevel.LOG_ERROR) else: for item in evt.errors: # API_EventTimeout: if sub-element device not reachable it transitions # to UNKNOWN state. if item.reason == "API_EventTimeout": if evt.attr_name.find(dev_name) > 0: self._se_state[dev_name] = tango.DevState.UNKNOWN self._se_health_state[dev_name] = HealthState.UNKNOWN if self._se_to_switch_off[dev_name]: self._se_state[dev_name] = tango.DevState.OFF # update the State and healthState of the CSP Element self._update_csp_state() log_msg = item.reason + ": on attribute " + str(evt.attr_name) self.dev_logging(log_msg, tango.LogLevel.LOG_WARN) # --------------- # Class protected methods # --------------- def _update_csp_state(self): """ Class protected method. Retrieve the State attribute of the CSP sub-elements and aggregate them to build up the CSP global state. :param: None :return: None """ self._update_csp_health_state() # CSP state reflects the status of CBF. Only if CBF is present # CSP can work. The state of PSS and PST sub-elements only contributes # to determine the CSP health state. self.set_state(self._se_state[self.CspCbf]) def _update_csp_health_state(self): """ Class private method. Retrieve the healthState attribute of the CSP sub-elements and aggregate them to build up the CSP health state :param: None :return: None """ # The whole CSP HealthState is OK only if: # - all sub-elements with adminMode OFF-LINE or MAINTENACE are ON AND # - each sub-element HealthState is OK admin_on = 0 health_ok = 0 self._health_state = HealthState.DEGRADED for fqdn in self._fqdn: if self._se_admin_mode[fqdn] not in [AdminMode.OFFLINE, AdminMode.MAINTENANCE]: if fqdn == self.CspCbf: self._health_state = self._se_health_state[self.CspCbf] return continue admin_on += 1 if self._se_health_state[fqdn] == HealthState.OK: health_ok += 1 if admin_on == health_ok: self._healthstate = HealthState.OK return def _timeout_handler_cb(self): pass def _cmd_progress_cb(self, evt): pass def _cmd_ended_cb(self): pass def _connect_to_subelements (self): """ Class private method. Establish a *stateless* connection with each CSP sub-element and if the sub-element adminMod is ON-LINE or MAINTENACE, subscribes the sub-element State, healthState and adminMode attributes. Exceptions are logged. :return: None """ # connect to TANGO DB csp_tango_db = tango.Database() for fqdn in self._se_fqdn: # initialize the list for each dictionary key-name self._se_event_id[fqdn] = {} try: log_msg = "Trying connection to" + str(fqdn) + " device" self.dev_logging(log_msg, int(tango.LogLevel.LOG_INFO)) device_proxy = DeviceProxy(fqdn) # Note: ping() methos is not called. The DeviceProy is registerd even if the # sub-element is not running. As soon as the sub-element Master device # starts the connection is establish. When the sub-element adminMode is # set ONLINE (or MAINTENANCE) the SCM attributes are subscribed. # store the sub-element proxies self._se_proxies[fqdn] = device_proxy # read the sub-element adminMode (memorized) attribute from # the TANGO DB. If the value is ON-LINE or MAINTENACE, the # SCM attributes of the sub-element are subscribed # read the sub-element adminMode value from the TANGO DB attribute_properties = csp_tango_db.get_device_attribute_property(fqdn, {'adminMode': ['__value']}) print("fqdn: {} attribute_properties: {}".format(fqdn, attribute_properties)) admin_mode_memorized = attribute_properties['adminMode']['__value'] self._se_admin_mode[fqdn] = int(admin_mode_memorized[0]) if self._se_admin_mode[fqdn] not in [AdminMode.ONLINE, AdminMode.MAINTENANCE]: return # Subscription of the sub-element State,healthState and adminMode ev_id = device_proxy.subscribe_event("State", EventType.CHANGE_EVENT, self._se_scm_change_event_cb, stateless=True) self._se_event_id[fqdn]['state'] = ev_id ev_id = device_proxy.subscribe_event("healthState", EventType.CHANGE_EVENT, self._se_scm_change_event_cb, stateless=True) self._se_event_id[fqdn]['healthState'] = ev_id ev_id = device_proxy.subscribe_event("adminMode", EventType.CHANGE_EVENT, self._se_scm_change_event_cb, stateless=True) self._se_event_id[fqdn]['adminMode'] = ev_id except tango.DevFailed as df: #for item in df.args: log_msg = ("Failure in connection to {}" " device: {}".format(str(fqdn), str(df.args[0].desc))) self.dev_logging(log_msg, tango.LogLevel.LOG_ERROR) # NOTE: if the exception is thrown, the Device server fails # and exit. In this case we rely on K8s/Docker restart policy # to restart the Device Server. def is_se_device_running (self, subelement_name): """ *Class private method.* Check if the sub-element is exported in the TANGO DB. If the device is not present in the list of the connected sub-elements, a connection with the device is performed. :param: subelement_name : the FQDN of the sub-element :type: `DevString` :return: True if the connection with the subarray is established, False otherwise """ try: proxy = self._se_proxies[subelement_name] proxy.ping() except KeyError as key_err: # Raised when a mapping (dictionary) key is not found in the set # of existing keys. # no proxy registered for the subelement device msg = "Can't retrieve the information of key {}".format(key_err) self.dev_logging(msg, tango.LogLevel.LOG_ERROR) proxy = tango.DeviceProxy(subelement_name) # execute a ping to detect if the device is actually running proxy.ping() self._se_proxies[subelement_name] = proxy except tango.DevFailed as df: msg = "Failure reason: {} Desc: {}".format(str(df.args[0].reason), str(df.args[0].desc)) self.dev_logging(msg, tango.LogLevel.LOG_ERROR) return False return True def _on_cmd_queued(self): pass def _off_cmd_queued(self): pass # PROTECTED REGION END # // CspMaster.class_protected_methods # ----------------- # Device Properties # ----------------- Loading Loading @@ -114,6 +363,8 @@ class CspMaster(SKAMaster): doc= ("The admin mode reported for this device. It may interpret the" "current device condition and condition of all managed devices" "to set this. Most possibly an aggregate attribute."), enum_labels=["ON-LINE", "OFF-LINE", "MAINTENANCE", "NOT-FITTED", "RESERVED", ], ) onCommandProgress = attribute( Loading Loading @@ -535,6 +786,164 @@ class CspMaster(SKAMaster): """Initialises the attributes and properties of the CspMaster.""" SKAMaster.init_device(self) # PROTECTED REGION ID(CspMaster.init_device) ENABLED START # self._build_state = '{}, {}, {}'.format(release.name, release.version, release.description) self._version_id = release.version # set storage and element logging level self._storage_logging_level = int(tango.LogLevel.LOG_INFO) 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) self._health_state = HealthState.OK self._admin_mode = AdminMode.ONLINE # use defaultdict to initialize the sub-element State,healthState # and adminMode. The dictionary uses as keys the sub-element # fqdn, for example # self._se_state[self.CspCbf] # return the State value of the Mid Cbf sub-element. self._se_state = defaultdict(lambda: tango.DevState.DISABLED) self._se_health_state = defaultdict(lambda: HealthState.UNKNOWN) self._se_admin_mode = defaultdict(lambda: AdminMode.NOTFITTED) # initialize attribute values # initialize list with CSP sub-element FQDNs self._se_fqdn = [] #NOTE: # The normal behavior when a Device Property is created is: # - a self.Property attribute is created in the Dev_Impl object # - it takes a value from the Database if it has been defined. # - if not, it takes the default value assigned in Pogo. # - if no value is specified nowhere, the attribute is created # with [] value. self._se_fqdn.append(self.CspCbf) self._se_fqdn.append(self.CspPss) self._se_fqdn.append(self.CspPst) # _se_proxies: the sub-element proxies # implementes s a dictionary: # keys: sub-element FQDN # values: devic eproxy self._se_proxies = {} # Nested default dictionary with list of event ids/sub-element. Need to # store the event ids for each sub-element and attribute to un-subscribe # them at sub-element disconnection. # keys: sub-element FQDN # values: dictionary (keys: attribute name, values: event id) self._se_event_id = defaultdict(lambda: defaultdict(lambda: 0)) # _se_cmd_timer: implement a Timer for each sub-element for each command # implemented as a dictionary: # keys: sub-element FQDN # values: python Timer self._se_cmd_timer = {} # _se_cmd_execution_state: implement the execution state of a long-running # command for each sub-element. # implemented as a default dictionary: # keys: sub-element FQDN # values: defaut dictionary (keys: command cname, values: command state) self._se_cmd_execution_state = defaultdict(lambda: defaultdict(lambda: CmdState.IDLE)) # _se_cmd_starting_time: for each sub-element report the long-running command # starting time # Implemented as dictionary: # keys: the sub-element FQDN # values: starting time self._se_cmd_starting_time = defaultdict(lambda: 0.0) # _se_on_cmd_thread: for each sub-element implement a thread for the On command # Implemented as default dictionary: # keys: the sub-element FQDN # values: the thread self._se_on_cmd_thread = {} # _se_off_cmd_thread: for each sub-element implement a thread for the Off command # Implemented as default dictionary: # keys: the sub-element FQDN # values: the thread self._se_off_cmd_thread = {} # _se_standby_cmd_thread: for each sub-element implement a thread for the # Standby command # Implemented as default dictionary: # keys: the sub-element FQDN # values: the thread self._se_standby_cmd_thread = {} # _se_cmd_progress: for each sub-element report the execution progress # of a long-running command # implemented as a default dictionary: # keys: sub-element FQDN # values: the percentage self._se_cmd_progress = defaultdict(lambda: defaultdict(lambda: 0)) # _se_cmd_duration_expected: for each sub-element, store the duration (in sec.) # configured for a long-running command # Implemented as a nested default dictionary # keys: FQDN # values: default dictionary (keys: command name, values: the duration (in sec)) self._se_cmd_duration_expected = defaultdict(lambda: defaultdict(lambda: 30)) # _se_cmd_duration_measured: for each sub-element report the measured duration # (in sec.) configured for a long-running command # Implemented as a nested default dictionary # keys: FQDN # values: default dictionary (keys: command name, values: the duration (in sec)) self._se_cmd_duration_measured = defaultdict(lambda: defaultdict(lambda: 0)) # _se_timeout_expired: report the timeout flag # Implemented as a default dictionary # keys: FQDN # values: default dictionary (keys: command name, values: True/False)) self._se_timeout_expired = defaultdict(lambda: defaultdict(lambda: False)) # _list_dev_completed_task: for each long-running command report the list of subordinate # components that completed the task # Implemented as a dictionary # keys: the command name ('on', 'off',...) # values: the list of components self._list_dev_completed_task = defaultdict(lambda: []) # _num_dev_completed_task: for each long-running command report the number # of subordinate components that completed the task # Implemented as a dictionary # keys: the command name ('on', 'off',...) # values: the number of components self._num_dev_completed_task = defaultdict(lambda:0) # initialize CSP SearchBeam, TimingBeam and VlbiBeam Capabilities State, # healthState, adminMode and obsState self._search_beam_state = defaultdict(lambda: tango.DevState.DISABLED) self._search_beam_health_state = defaultdict(lambda: tango.DevState.UNKNOWN) self._search_beam_admin_mode = defaultdict(lambda: tango.DevState.NOTFITTED) self._search_beam_obs_state = defaultdict(lambda: tango.ObsState.IDLE) self._timing_beam_state = defaultdict(lambda: tango.DevState.DISABLED) self._timing_beam_health_state = defaultdict(lambda: tango.DevState.UNKNOWN) self._timing_beam_admin_mode = defaultdict(lambda: tango.DevState.NOTFITTED) self._timing_beam_obs_state = defaultdict(lambda: tango.ObsState.IDLE) self._vlbi_beam_state = defaultdict(lambda: tango.DevState.DISABLED) self._vlbi_beam_health_state = defaultdict(lambda: tango.DevState.UNKNOWN) self._vlbi_beam_admin_mode = defaultdict(lambda: tango.DevState.NOTFITTED) self._vlbi_beam_obs_state = defaultdict(lambda: tango.ObsState.IDLE) #initialize Csp capabilities subarray membership affiliation self._timingBeamsMembership = [] self._vlbiBeamsMembership = [] # unassigned CSP SearchBeam, TimingBeam, VlbiBeam Capability IDs self._unassigned_search_beam_id = [] #self._unassigned_search_beam_num = 0 self._reserved_search_beam_id = [] #self._reserved_search_beam_num = 0 self._unassigned_timing_beam_id = [] self._unassigned_vlbi_beam_id = [] # Try connection with sub-elements self._connect_to_subelements() # PROTECTED REGION END # // CspMaster.init_device def always_executed_hook(self): Loading @@ -558,13 +967,13 @@ class CspMaster(SKAMaster): def read_adminMode(self): # PROTECTED REGION ID(CspMaster.adminMode_read) ENABLED START # """Return the adminMode attribute.""" return 0 return self._admin_mode # PROTECTED REGION END # // CspMaster.adminMode_read def write_adminMode(self, value): # PROTECTED REGION ID(CspMaster.adminMode_write) ENABLED START # """Set the adminMode attribute.""" pass self._admin_mode = value # PROTECTED REGION END # // CspMaster.adminMode_write def read_onCommandProgress(self): Loading csp-lmc-common/utils/cspcommons.py 0 → 100644 +42 −0 Original line number Diff line number Diff line from enum import IntEnum, unique @unique class HealthState(IntEnum): OK = 0 DEGRADED = 1 FAILED = 2 UNKNOWN = 3 @unique class AdminMode(IntEnum): ONLINE = 0 OFFLINE = 1 MAINTENANCE = 2 NOTFITTED = 3 RESERVED = 4 @unique class ControlMode(IntEnum): REMOTE = 0 LOCAL = 1 @unique class ObsMode(IntEnum): IDLE = 0 IMAGING = 1 PULSARSEARCH = 2 PULSARTIMING = 3 DYNAMICSPECTRUM = 4 TRANSIENTSEARCH = 5 VLBI = 6 CALIBRATION = 7 @unique class ObsState(IntEnum): IDLE = 0 CONFIGURING = 1 READY = 2 SCANNING = 3 PAUSED = 4 ABORTED = 5 FAULT = 6 csp-lmc-common/utils/release.py 0 → 100755 +20 −0 Original line number Diff line number Diff line # -*- coding: utf-8 -*- # # This file is part of the CentralNode project # # # # Distributed under the terms of the BSD-3-Clause license. # See LICENSE.txt for more info. """Release information for Python Package""" name = """csplmc""" version = "0.1.0" version_info = version.split(".") description = """SKA CSP.LMC Common Software""" author = "INAF-OAA" author_email = "elisabetta.giani@inaf.it" license = """BSD-3-Clause""" url = """https://gitlab.com/ska-telescope/csp-lmc.git""" copyright = """INAF, SKA Telescope""" Loading
csp-lmc-common/csp-lmc-common/CspMaster.py +417 −8 Original line number Diff line number Diff line Loading @@ -11,25 +11,47 @@ CSP.LMC Common Class for the CSPMaster TANGO Device. """ # PROTECTED REGION ID (CspMaster.standardlibray_import) ENABLED START # # Python standard library from __future__ import absolute_import import sys import os from future.utils import with_metaclass from collections import defaultdict from enum import IntEnum, unique # PROTECTED REGION END# //CspMaster.standardlibray_import # tango imports# PROTECTED REGION ID (CspMaster.add_path) ENABLED START # # tango imports import tango from tango import DebugIt from tango.server import run from tango.server import Device, DeviceMeta from tango.server import attribute, command from tango.server import device_property from tango import AttrQuality, DispLevel, DevState from tango import AttrWriteType, PipeWriteType from skabase.SKAMaster import SKAMaster from tango import AttrQuality, EventType, DevState from tango import AttrWriteType, DeviceProxy # Additional import # PROTECTED REGION ID(CspMaster.additionnal_import) ENABLED START # # from skabase.SKAMaster import SKAMaster from skabase.auxiliary import utils # PROTECTED REGION END # // CspMaster.additionnal_import # PROTECTED REGION ID (CspMaster.add_path) ENABLED START # # add the path to import global_enum package. 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 cspcommons from cspcommons import HealthState, AdminMode, ObsState import release # PROTECTED REGION END# //CspMaster.add_path __all__ = ["CspMaster", "main"] class CspMaster(SKAMaster): class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): """ CSP.LMC Common Class for the CSPMaster TANGO Device. Loading Loading @@ -66,10 +88,237 @@ class CspMaster(SKAMaster): - Type:'DevVarStringArray' """ __metaclass__ = DeviceMeta # 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 # #---------------- # Event Callback functions # --------------- def __se_smc_change_event_cb(self, evt): """ Class protected callback function. Retrieve the values of the sub-element SCM attributes subscribed for change event at device initialization. :param evt: The event data :return: None """ if not evt.err: dev_name = evt.device.dev_name() try: if dev_name in self._se_fqdn: if evt.attr_value.name.lower() == "state": self._se_state[dev_name] = evt.attr_value.value elif evt.attr_value.name.lower() == "healthstate": self._se_health_state[dev_name] = evt.attr_value.value elif evt.attr_value.name.lower() == "adminmode": self._se_admin_mode[dev_name] = evt.attr_value.value else: log_msg = ("Attribute {} not still " "handled".format(evt.attr_name)) self.dev_logging(log_msg, tango.LogLevel.LOG_WARN) else: log_msg = ("Unexpected change event for" " attribute: {}".format(str(evt.attr_name))) self.dev_logging(log_msg, tango.LogLevel.LOG_WARN) return log_msg = "New value for {} is {}".format(str(evt.attr_name), str(evt.attr_value.value)) self.dev_logging(log_msg, tango.LogLevel.LOG_INFO) # update CSP global state if evt.attr_value.name.lower() in ["state", "healthstate"]: self._update_csp_state() except tango.DevFailed as df: self.dev_logging(str(df.args[0].desc), tango.LogLevel.LOG_ERROR) except Exception as except_occurred: self.dev_logging(str(except_occurred), tango.LogLevel.LOG_ERROR) else: for item in evt.errors: # API_EventTimeout: if sub-element device not reachable it transitions # to UNKNOWN state. if item.reason == "API_EventTimeout": if evt.attr_name.find(dev_name) > 0: self._se_state[dev_name] = tango.DevState.UNKNOWN self._se_health_state[dev_name] = HealthState.UNKNOWN if self._se_to_switch_off[dev_name]: self._se_state[dev_name] = tango.DevState.OFF # update the State and healthState of the CSP Element self._update_csp_state() log_msg = item.reason + ": on attribute " + str(evt.attr_name) self.dev_logging(log_msg, tango.LogLevel.LOG_WARN) # --------------- # Class protected methods # --------------- def _update_csp_state(self): """ Class protected method. Retrieve the State attribute of the CSP sub-elements and aggregate them to build up the CSP global state. :param: None :return: None """ self._update_csp_health_state() # CSP state reflects the status of CBF. Only if CBF is present # CSP can work. The state of PSS and PST sub-elements only contributes # to determine the CSP health state. self.set_state(self._se_state[self.CspCbf]) def _update_csp_health_state(self): """ Class private method. Retrieve the healthState attribute of the CSP sub-elements and aggregate them to build up the CSP health state :param: None :return: None """ # The whole CSP HealthState is OK only if: # - all sub-elements with adminMode OFF-LINE or MAINTENACE are ON AND # - each sub-element HealthState is OK admin_on = 0 health_ok = 0 self._health_state = HealthState.DEGRADED for fqdn in self._fqdn: if self._se_admin_mode[fqdn] not in [AdminMode.OFFLINE, AdminMode.MAINTENANCE]: if fqdn == self.CspCbf: self._health_state = self._se_health_state[self.CspCbf] return continue admin_on += 1 if self._se_health_state[fqdn] == HealthState.OK: health_ok += 1 if admin_on == health_ok: self._healthstate = HealthState.OK return def _timeout_handler_cb(self): pass def _cmd_progress_cb(self, evt): pass def _cmd_ended_cb(self): pass def _connect_to_subelements (self): """ Class private method. Establish a *stateless* connection with each CSP sub-element and if the sub-element adminMod is ON-LINE or MAINTENACE, subscribes the sub-element State, healthState and adminMode attributes. Exceptions are logged. :return: None """ # connect to TANGO DB csp_tango_db = tango.Database() for fqdn in self._se_fqdn: # initialize the list for each dictionary key-name self._se_event_id[fqdn] = {} try: log_msg = "Trying connection to" + str(fqdn) + " device" self.dev_logging(log_msg, int(tango.LogLevel.LOG_INFO)) device_proxy = DeviceProxy(fqdn) # Note: ping() methos is not called. The DeviceProy is registerd even if the # sub-element is not running. As soon as the sub-element Master device # starts the connection is establish. When the sub-element adminMode is # set ONLINE (or MAINTENANCE) the SCM attributes are subscribed. # store the sub-element proxies self._se_proxies[fqdn] = device_proxy # read the sub-element adminMode (memorized) attribute from # the TANGO DB. If the value is ON-LINE or MAINTENACE, the # SCM attributes of the sub-element are subscribed # read the sub-element adminMode value from the TANGO DB attribute_properties = csp_tango_db.get_device_attribute_property(fqdn, {'adminMode': ['__value']}) print("fqdn: {} attribute_properties: {}".format(fqdn, attribute_properties)) admin_mode_memorized = attribute_properties['adminMode']['__value'] self._se_admin_mode[fqdn] = int(admin_mode_memorized[0]) if self._se_admin_mode[fqdn] not in [AdminMode.ONLINE, AdminMode.MAINTENANCE]: return # Subscription of the sub-element State,healthState and adminMode ev_id = device_proxy.subscribe_event("State", EventType.CHANGE_EVENT, self._se_scm_change_event_cb, stateless=True) self._se_event_id[fqdn]['state'] = ev_id ev_id = device_proxy.subscribe_event("healthState", EventType.CHANGE_EVENT, self._se_scm_change_event_cb, stateless=True) self._se_event_id[fqdn]['healthState'] = ev_id ev_id = device_proxy.subscribe_event("adminMode", EventType.CHANGE_EVENT, self._se_scm_change_event_cb, stateless=True) self._se_event_id[fqdn]['adminMode'] = ev_id except tango.DevFailed as df: #for item in df.args: log_msg = ("Failure in connection to {}" " device: {}".format(str(fqdn), str(df.args[0].desc))) self.dev_logging(log_msg, tango.LogLevel.LOG_ERROR) # NOTE: if the exception is thrown, the Device server fails # and exit. In this case we rely on K8s/Docker restart policy # to restart the Device Server. def is_se_device_running (self, subelement_name): """ *Class private method.* Check if the sub-element is exported in the TANGO DB. If the device is not present in the list of the connected sub-elements, a connection with the device is performed. :param: subelement_name : the FQDN of the sub-element :type: `DevString` :return: True if the connection with the subarray is established, False otherwise """ try: proxy = self._se_proxies[subelement_name] proxy.ping() except KeyError as key_err: # Raised when a mapping (dictionary) key is not found in the set # of existing keys. # no proxy registered for the subelement device msg = "Can't retrieve the information of key {}".format(key_err) self.dev_logging(msg, tango.LogLevel.LOG_ERROR) proxy = tango.DeviceProxy(subelement_name) # execute a ping to detect if the device is actually running proxy.ping() self._se_proxies[subelement_name] = proxy except tango.DevFailed as df: msg = "Failure reason: {} Desc: {}".format(str(df.args[0].reason), str(df.args[0].desc)) self.dev_logging(msg, tango.LogLevel.LOG_ERROR) return False return True def _on_cmd_queued(self): pass def _off_cmd_queued(self): pass # PROTECTED REGION END # // CspMaster.class_protected_methods # ----------------- # Device Properties # ----------------- Loading Loading @@ -114,6 +363,8 @@ class CspMaster(SKAMaster): doc= ("The admin mode reported for this device. It may interpret the" "current device condition and condition of all managed devices" "to set this. Most possibly an aggregate attribute."), enum_labels=["ON-LINE", "OFF-LINE", "MAINTENANCE", "NOT-FITTED", "RESERVED", ], ) onCommandProgress = attribute( Loading Loading @@ -535,6 +786,164 @@ class CspMaster(SKAMaster): """Initialises the attributes and properties of the CspMaster.""" SKAMaster.init_device(self) # PROTECTED REGION ID(CspMaster.init_device) ENABLED START # self._build_state = '{}, {}, {}'.format(release.name, release.version, release.description) self._version_id = release.version # set storage and element logging level self._storage_logging_level = int(tango.LogLevel.LOG_INFO) 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) self._health_state = HealthState.OK self._admin_mode = AdminMode.ONLINE # use defaultdict to initialize the sub-element State,healthState # and adminMode. The dictionary uses as keys the sub-element # fqdn, for example # self._se_state[self.CspCbf] # return the State value of the Mid Cbf sub-element. self._se_state = defaultdict(lambda: tango.DevState.DISABLED) self._se_health_state = defaultdict(lambda: HealthState.UNKNOWN) self._se_admin_mode = defaultdict(lambda: AdminMode.NOTFITTED) # initialize attribute values # initialize list with CSP sub-element FQDNs self._se_fqdn = [] #NOTE: # The normal behavior when a Device Property is created is: # - a self.Property attribute is created in the Dev_Impl object # - it takes a value from the Database if it has been defined. # - if not, it takes the default value assigned in Pogo. # - if no value is specified nowhere, the attribute is created # with [] value. self._se_fqdn.append(self.CspCbf) self._se_fqdn.append(self.CspPss) self._se_fqdn.append(self.CspPst) # _se_proxies: the sub-element proxies # implementes s a dictionary: # keys: sub-element FQDN # values: devic eproxy self._se_proxies = {} # Nested default dictionary with list of event ids/sub-element. Need to # store the event ids for each sub-element and attribute to un-subscribe # them at sub-element disconnection. # keys: sub-element FQDN # values: dictionary (keys: attribute name, values: event id) self._se_event_id = defaultdict(lambda: defaultdict(lambda: 0)) # _se_cmd_timer: implement a Timer for each sub-element for each command # implemented as a dictionary: # keys: sub-element FQDN # values: python Timer self._se_cmd_timer = {} # _se_cmd_execution_state: implement the execution state of a long-running # command for each sub-element. # implemented as a default dictionary: # keys: sub-element FQDN # values: defaut dictionary (keys: command cname, values: command state) self._se_cmd_execution_state = defaultdict(lambda: defaultdict(lambda: CmdState.IDLE)) # _se_cmd_starting_time: for each sub-element report the long-running command # starting time # Implemented as dictionary: # keys: the sub-element FQDN # values: starting time self._se_cmd_starting_time = defaultdict(lambda: 0.0) # _se_on_cmd_thread: for each sub-element implement a thread for the On command # Implemented as default dictionary: # keys: the sub-element FQDN # values: the thread self._se_on_cmd_thread = {} # _se_off_cmd_thread: for each sub-element implement a thread for the Off command # Implemented as default dictionary: # keys: the sub-element FQDN # values: the thread self._se_off_cmd_thread = {} # _se_standby_cmd_thread: for each sub-element implement a thread for the # Standby command # Implemented as default dictionary: # keys: the sub-element FQDN # values: the thread self._se_standby_cmd_thread = {} # _se_cmd_progress: for each sub-element report the execution progress # of a long-running command # implemented as a default dictionary: # keys: sub-element FQDN # values: the percentage self._se_cmd_progress = defaultdict(lambda: defaultdict(lambda: 0)) # _se_cmd_duration_expected: for each sub-element, store the duration (in sec.) # configured for a long-running command # Implemented as a nested default dictionary # keys: FQDN # values: default dictionary (keys: command name, values: the duration (in sec)) self._se_cmd_duration_expected = defaultdict(lambda: defaultdict(lambda: 30)) # _se_cmd_duration_measured: for each sub-element report the measured duration # (in sec.) configured for a long-running command # Implemented as a nested default dictionary # keys: FQDN # values: default dictionary (keys: command name, values: the duration (in sec)) self._se_cmd_duration_measured = defaultdict(lambda: defaultdict(lambda: 0)) # _se_timeout_expired: report the timeout flag # Implemented as a default dictionary # keys: FQDN # values: default dictionary (keys: command name, values: True/False)) self._se_timeout_expired = defaultdict(lambda: defaultdict(lambda: False)) # _list_dev_completed_task: for each long-running command report the list of subordinate # components that completed the task # Implemented as a dictionary # keys: the command name ('on', 'off',...) # values: the list of components self._list_dev_completed_task = defaultdict(lambda: []) # _num_dev_completed_task: for each long-running command report the number # of subordinate components that completed the task # Implemented as a dictionary # keys: the command name ('on', 'off',...) # values: the number of components self._num_dev_completed_task = defaultdict(lambda:0) # initialize CSP SearchBeam, TimingBeam and VlbiBeam Capabilities State, # healthState, adminMode and obsState self._search_beam_state = defaultdict(lambda: tango.DevState.DISABLED) self._search_beam_health_state = defaultdict(lambda: tango.DevState.UNKNOWN) self._search_beam_admin_mode = defaultdict(lambda: tango.DevState.NOTFITTED) self._search_beam_obs_state = defaultdict(lambda: tango.ObsState.IDLE) self._timing_beam_state = defaultdict(lambda: tango.DevState.DISABLED) self._timing_beam_health_state = defaultdict(lambda: tango.DevState.UNKNOWN) self._timing_beam_admin_mode = defaultdict(lambda: tango.DevState.NOTFITTED) self._timing_beam_obs_state = defaultdict(lambda: tango.ObsState.IDLE) self._vlbi_beam_state = defaultdict(lambda: tango.DevState.DISABLED) self._vlbi_beam_health_state = defaultdict(lambda: tango.DevState.UNKNOWN) self._vlbi_beam_admin_mode = defaultdict(lambda: tango.DevState.NOTFITTED) self._vlbi_beam_obs_state = defaultdict(lambda: tango.ObsState.IDLE) #initialize Csp capabilities subarray membership affiliation self._timingBeamsMembership = [] self._vlbiBeamsMembership = [] # unassigned CSP SearchBeam, TimingBeam, VlbiBeam Capability IDs self._unassigned_search_beam_id = [] #self._unassigned_search_beam_num = 0 self._reserved_search_beam_id = [] #self._reserved_search_beam_num = 0 self._unassigned_timing_beam_id = [] self._unassigned_vlbi_beam_id = [] # Try connection with sub-elements self._connect_to_subelements() # PROTECTED REGION END # // CspMaster.init_device def always_executed_hook(self): Loading @@ -558,13 +967,13 @@ class CspMaster(SKAMaster): def read_adminMode(self): # PROTECTED REGION ID(CspMaster.adminMode_read) ENABLED START # """Return the adminMode attribute.""" return 0 return self._admin_mode # PROTECTED REGION END # // CspMaster.adminMode_read def write_adminMode(self, value): # PROTECTED REGION ID(CspMaster.adminMode_write) ENABLED START # """Set the adminMode attribute.""" pass self._admin_mode = value # PROTECTED REGION END # // CspMaster.adminMode_write def read_onCommandProgress(self): Loading
csp-lmc-common/utils/cspcommons.py 0 → 100644 +42 −0 Original line number Diff line number Diff line from enum import IntEnum, unique @unique class HealthState(IntEnum): OK = 0 DEGRADED = 1 FAILED = 2 UNKNOWN = 3 @unique class AdminMode(IntEnum): ONLINE = 0 OFFLINE = 1 MAINTENANCE = 2 NOTFITTED = 3 RESERVED = 4 @unique class ControlMode(IntEnum): REMOTE = 0 LOCAL = 1 @unique class ObsMode(IntEnum): IDLE = 0 IMAGING = 1 PULSARSEARCH = 2 PULSARTIMING = 3 DYNAMICSPECTRUM = 4 TRANSIENTSEARCH = 5 VLBI = 6 CALIBRATION = 7 @unique class ObsState(IntEnum): IDLE = 0 CONFIGURING = 1 READY = 2 SCANNING = 3 PAUSED = 4 ABORTED = 5 FAULT = 6
csp-lmc-common/utils/release.py 0 → 100755 +20 −0 Original line number Diff line number Diff line # -*- coding: utf-8 -*- # # This file is part of the CentralNode project # # # # Distributed under the terms of the BSD-3-Clause license. # See LICENSE.txt for more info. """Release information for Python Package""" name = """csplmc""" version = "0.1.0" version_info = version.split(".") description = """SKA CSP.LMC Common Software""" author = "INAF-OAA" author_email = "elisabetta.giani@inaf.it" license = """BSD-3-Clause""" url = """https://gitlab.com/ska-telescope/csp-lmc.git""" copyright = """INAF, SKA Telescope"""