From 113763f31bf547c765e5a874d098cd9593cfac31 Mon Sep 17 00:00:00 2001 From: softir Date: Mon, 23 Dec 2019 21:56:50 +0100 Subject: [PATCH 01/33] AT5-257: implementation of CspMaster class methods and attributes. Added classes to create enumerated constants for SCM attributes. --- csp-lmc-common/csp-lmc-common/CspMaster.py | 425 ++++++++++++++++++++- csp-lmc-common/utils/cspcommons.py | 42 ++ csp-lmc-common/utils/release.py | 20 + 3 files changed, 479 insertions(+), 8 deletions(-) create mode 100644 csp-lmc-common/utils/cspcommons.py create mode 100755 csp-lmc-common/utils/release.py diff --git a/csp-lmc-common/csp-lmc-common/CspMaster.py b/csp-lmc-common/csp-lmc-common/CspMaster.py index 38130db..123d2f0 100644 --- a/csp-lmc-common/csp-lmc-common/CspMaster.py +++ b/csp-lmc-common/csp-lmc-common/CspMaster.py @@ -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. @@ -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 # ----------------- @@ -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( @@ -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): @@ -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): diff --git a/csp-lmc-common/utils/cspcommons.py b/csp-lmc-common/utils/cspcommons.py new file mode 100644 index 0000000..5af568d --- /dev/null +++ b/csp-lmc-common/utils/cspcommons.py @@ -0,0 +1,42 @@ +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 diff --git a/csp-lmc-common/utils/release.py b/csp-lmc-common/utils/release.py new file mode 100755 index 0000000..198ccab --- /dev/null +++ b/csp-lmc-common/utils/release.py @@ -0,0 +1,20 @@ +# -*- 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""" -- GitLab From af25cd7ac34aa08efd8df2752d00388b90092506 Mon Sep 17 00:00:00 2001 From: Elisabetta Giani Date: Sat, 28 Dec 2019 08:56:04 -0500 Subject: [PATCH 02/33] AT5-257: Implemented timers and timeout handler for long-running commands execution. Asynch execution of the On() command. --- csp-lmc-common/csp-lmc-common/CspMaster.py | 558 +++++++++++++++++---- csp-lmc-common/pogo/CspMaster.xmi | 6 +- 2 files changed, 468 insertions(+), 96 deletions(-) diff --git a/csp-lmc-common/csp-lmc-common/CspMaster.py b/csp-lmc-common/csp-lmc-common/CspMaster.py index 123d2f0..e51d1ba 100644 --- a/csp-lmc-common/csp-lmc-common/CspMaster.py +++ b/csp-lmc-common/csp-lmc-common/CspMaster.py @@ -19,6 +19,8 @@ import os from future.utils import with_metaclass from collections import defaultdict from enum import IntEnum, unique +import threading +import time # PROTECTED REGION END# //CspMaster.standardlibray_import # tango imports# PROTECTED REGION ID (CspMaster.add_path) ENABLED START # @@ -100,7 +102,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): #---------------- # Event Callback functions # --------------- - def __se_smc_change_event_cb(self, evt): + def _se_scm_change_event_cb(self, evt): """ Class protected callback function. Retrieve the values of the sub-element SCM attributes subscribed @@ -115,7 +117,10 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): try: if dev_name in self._se_fqdn: if evt.attr_value.name.lower() == "state": + print("Received event on {} value {}".format(evt.attr_value.name, + evt.attr_value.value)) self._se_state[dev_name] = evt.attr_value.value + self._reset_asynch_cmd_tracking_attr(dev_name) elif evt.attr_value.name.lower() == "healthstate": self._se_health_state[dev_name] = evt.attr_value.value elif evt.attr_value.name.lower() == "adminmode": @@ -145,15 +150,112 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # 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() + self._se_state[dev_name] = tango.DevState.UNKNOWN + self._se_health_state[dev_name] = HealthState.UNKNOWN + # if a timeout is registered for the OFF command, the timeout + # flag is reset, the command execution state is set to IDLE + # and the device state is set to OFF + if self._se_timeout_expired[dev_name]['off']: + self._se_timeout_expired[dev_name]['off'] = False + self._se_cmd_execution_state[dev_name]['off'] = self.CmdExecState.IDLE + 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) + + def _timeout_handler_cb(self, *args, **dict_args): + print("Timeout expired!!") + try: + fqdn = dict_args['device_name'] + cmd_expired = dict_args['cmd_name'] + print("Timeout expired on device {} for command {}".format(fqdn,cmd_expired)) + self._se_timeout_expired[fqdn][cmd_expired] = True + self._se_cmd_execution_state[fqdn][cmd_expired] = self.CmdExecState.IDLE + self._se_cmd_progress[fqdn] = 0 + self._se_on_cmd_thread[fqdn].join() + except KeyError as key_err: + err_msg = "No key {} found in input args".format(str(key_err)) + self.dev_logging(err_msg, tango.LogLevel.LOG_ERROR) + + def _cmd_progress_cb(self, evt): + if not evt.err: + dev_name = evt.device.dev_name() + try: + if dev_name in self._se_fqdn: + self._se_cmd_progress[dev_name][evt.attr_name.lower()] = evt.attr_value.value + 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) + 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: log_msg = item.reason + ": on attribute " + str(evt.attr_name) self.dev_logging(log_msg, tango.LogLevel.LOG_WARN) + + def _cmd_ended_cb(self, evt): + """ + Method immediately executed when the asynchronous invoked + command returns. + + Args: + evt: A CmdDoneEvent object. This class is used to pass data + to the callback method in asynchronous callback model for command + execution. + + It has the following members: + - device : (DeviceProxy) The DeviceProxy object on which the + call was executed. + - cmd_name : (str) The command name + - argout_raw : (DeviceData) The command argout + - argout : The command argout + - err : (bool) A boolean flag set to true if the command + failed. False otherwise + - errors : (sequence) The error stack + - ext + Returns: + None + """ + # NOTE:if we try to access to evt.cmd_name or other paramters, sometime + # the callback crashes withthis error: + # terminate called after throwing an instance of 'boost::python::error_already_set' + # + print("cmd_ended", evt) + try: + # Can happen evt empty?? + if evt: + if not evt.err: + msg = "Device {} is processing command {}".format(evt.device, + evt.cmd_name) + print(msg) + self.dev_logging(msg, tango.LogLevel.LOG_INFO) + else: + msg = "Error in executing command {} ended on device {}.\n".format(evt.cmd_name, + evt.device) + msg += " Desc: {}".format(evt.errors[0].desc) + self.dev_logging(msg, tango.LogLevel.LOG_ERROR) + print(msg) + # obsState and obsMode values take on the CbfSubarray's values via + # the subscribe/publish mechanism + else: + self.dev_logging("cmd_ended callback: evt is empty!!", + tango.LogLevel.LOG_ERRO) + except tango.DevFailed as df: + msg = ("CommandCallback cmd_ended failure - desc: {}" + " reason: {}".format(df.args[0].desc, df.args[0].reason)) + self.dev_logging(msg, tango.LogLevel.LOG_ERROR) + except Exception as ex: + msg = "CommandCallBack cmd_ended general exception: {}".format(str(ex)) + self.dev_logging(msg, tango.LogLevel.LOG_ERROR) # --------------- # Class protected methods # --------------- @@ -191,7 +293,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): admin_on = 0 health_ok = 0 self._health_state = HealthState.DEGRADED - for fqdn in self._fqdn: + for fqdn in self._se_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] @@ -204,14 +306,64 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): self._healthstate = HealthState.OK return - def _timeout_handler_cb(self): - pass - - def _cmd_progress_cb(self, evt): - pass + def _reset_asynch_cmd_tracking_attr(self, dev_name): + """ + Reset the attributes used to track the asynchronous execution of a command + + :parameter:dev_name: the sub-element FQDN + :type: DevString + :return: None + """ + print("_reset_asynch_cmd_tracking_attr") + if self._se_state[dev_name] == tango.DevState.ON: + print(self._se_cmd_execution_state[dev_name]['on]']) + if self._se_cmd_execution_state[dev_name]['on'] == self.CmdExecState.RUNNING: + if self._se_cmd_timer[dev_name].isAlive(): + print("Timer is alive") + self._se_cmd_timer[dev_name].cancel() + print("cancel the Timer") + self._se_cmd_timer[dev_name].join() + print("join the thread") + self._se_on_cmd_thread[dev_name].join() + self._se_cmd_execution_state[dev_name]['on'] = self.CmdExecState.IDLE + print(self._se_cmd_execution_state[dev_name]['on']) + self._se_cmd_duration_measured[dev_name]['on'] = time.time() - self._se_cmd_starting_time[dev_name] + print("measured duration:",self._se_cmd_duration_measured[dev_name]['on'] ) + elif self._se_state[dev_name] == tango.DevState.STANDBY: + if self._se_cmd_execution_state[dev_name]['standby'] == self.CmdExecState.RUNNING: + if self._se_cmd_timer[dev_name].isAlive(): + self._se_cmd_timer[dev_name].cancel() + self._se_cmd_timer[dev_name].join() + self._se_cmd_execution_state[dev_name]['standby'] = self.CmdExecState.IDLE + self._se_cmd_duration_measured[dev_name]['standby'] = time.time() - self._se_cmd_starting_time[dev_name] + else: + print("Unhandled State {}".format(self._se_state[dev_name])) - def _cmd_ended_cb(self): - pass + def _subscribe_scm_attributes(self, device_name): + # Subscription of the sub-element State,healthState and adminMode + try: + device_proxy = self._se_proxies[device_name] + ev_id = device_proxy.subscribe_event("State", + EventType.CHANGE_EVENT, + self._se_scm_change_event_cb, + stateless=True) + self._se_event_id[device_name]['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[device_name]['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[device_name]['adminMode'] = ev_id + except KeyError as key_err: + err_msg = "No proxy found for device {}".format(str(key_err)) + self.dev_logging(err_msg, tango.LogLevel.LOG_ERROR) + tango.Except.throw_exception("Subscription failure", err_msg, "_subscribe_scm_attributes", tango.ErrSeverity.ERR) def _connect_to_subelements (self): """ @@ -228,11 +380,12 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): 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" + print(log_msg) self.dev_logging(log_msg, int(tango.LogLevel.LOG_INFO)) device_proxy = DeviceProxy(fqdn) + print("1") # 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 @@ -240,19 +393,9 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # 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 + # subscription of SCM attributes (State, healthState and adminMode). + # Note: subscription is performed also for devices not ONLINE or MAINTENANCE. In this + # way the CspMaster is able to detect a change in the admin value. ev_id = device_proxy.subscribe_event("State", EventType.CHANGE_EVENT, self._se_scm_change_event_cb, @@ -270,20 +413,31 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): self._se_scm_change_event_cb, stateless=True) self._se_event_id[fqdn]['adminMode'] = ev_id + print("2") + # read the sub-element adminMode (memorized) attribute from + # the TANGO DB. + # Note: a memorized attribute has defined the attribute property '__value' + 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]) + except KeyError as key_err: + log_msg = ("No key {} found".format(str(key_err))) + self.dev_logging(log_msg, tango.LogLevel.LOG_WARN) except tango.DevFailed as df: #for item in df.args: + print("3") 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): + def _is_se_device_running (self, subelement_name): """ *Class private method.* - Check if the sub-element is exported in the TANGO DB. + Check if the sub-element is exported in the TANGO DB (that is the TANGO device server + is running). If the device is not present in the list of the connected sub-elements, a connection with the device is performed. @@ -311,11 +465,53 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): return False return True - def _on_cmd_queued(self): - pass - - def _off_cmd_queued(self): - pass + def _issue_on_command(self, subelem_list): + # subelem_list[0]: the FQDN of the device executing the ON commmand + # subelem_list[1]: the device waiting to execute the ON command. + print("_issue_on_command:",subelem_list) + dev_to_wait_for = subelem_list[0] + dev_to_power_on = subelem_list[1] + + try: + device_proxy = self._se_proxies[dev_to_power_on] + #self._se_cmd_duration_expected[device_name]['on'] = proxy.read_attribute("onCmdDurationExpected") + self._se_cmd_duration_expected[dev_to_power_on]['on'] = 5 + timeout_val = self._se_cmd_duration_expected[dev_to_power_on]['on'] + self._se_cmd_duration_measured[dev_to_power_on]['on'] = 0 + self._se_cmd_timer[dev_to_power_on] = threading.Timer(timeout_val, self._timeout_handler_cb, None, + {'device_name':dev_to_power_on, + 'cmd_name':'on'}) + dev_to_wait_proxy = self._se_proxies[dev_to_power_on] + if dev_to_wait_for: + while True: + time.sleep(2) + state = dev_to_wait_proxy.command_inout("State") + if state == tango.DevState.ON or self._se_timeout_expired[dev_to_wait_for]['on']: + print("Command On executed on device {}." + " Issue ON command on {}").format(dev_to_wait_for, dev_to_power_on) + break + self._se_cmd_execution_state[dev_to_power_on]['on'] = self.CmdExecState.RUNNING + # subscribe the onProgressCommand attribute + try: + if self._se_event_id[dev_to_power_on]['oncommandprogress'] == 0: + evt_id = device_proxy.subscribe_event("onCommandProgress", EventType.CHANGE_EVENT, + self._se_scm_change_event_cb, stateless=False) + self._se_event_id[dev_to_power_on]['oncommandprogress'] = evt_id + except tango.DevFailed as df: + print(df.args[0].desc) + try: + print("Issue asynch command on device {}:".format(dev_to_power_on)) + #device_proxy.command_inout_asynch('On', self._cmd_ended_cb) + self._se_cmd_starting_time[dev_to_power_on] = time.time() + self._se_cmd_timer[dev_to_power_on].start() + return + 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) + except KeyError as key_err: + print("issue_on_command no key {} found".format(str(key_err))) + self._se_cmd_execution_state[dev_to_power_on]['on'] = self.CmdExecState.IDLE + # PROTECTED REGION END # // CspMaster.class_protected_methods @@ -469,29 +665,26 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): doc=("Report the measured duration (in sec.) of the Standby command"), ) - cbfOnCmdTimeoutExpired = attribute( + cbfCmdTimeoutExpired = attribute( dtype='DevBoolean', label="CBF command timeout flag", polling_period=2000, - abs_change=1, doc=("Signal the occurence of a timeout during the execution of commands on" " CBF Sub-element"), ) - pssOnCmdTimeoutExpired = attribute( + pssCmdTimeoutExpired = attribute( dtype='DevBoolean', label="PSS command timeout flag", polling_period=2000, - abs_change=1, doc=("Signal the occurence of a timeout during the execution of commands on " "PSS Sub-element"), ) - pstOnCmdTimeoutExpired = attribute( + pstCmdTimeoutExpired = attribute( dtype='DevBoolean', label="PST command timeout flag", polling_period=2000, - abs_change=1, doc=("Signal the occurence of a timeout during the execution of commands on" " PST Sub-element"), ) @@ -500,7 +693,6 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): dtype='DevState', label="CBF status", polling_period=3000, - abs_change=1, doc=("The CBF sub-element Device State. Allowed values are ON, STANDBY, OFF, " "DISABLED, ALARM, FAULT, UNKNOWN"), ) @@ -509,7 +701,6 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): dtype='DevState', label="PSS status", polling_period=3000, - abs_change=1, doc=("The PSS sub-element Device State. Allowed values are ON, STANDBY, OFF," " DISABLED, ALARM, FAULT, UNKNOWN"), ) @@ -571,7 +762,8 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): polling_period=3000, abs_change=1, doc=("The CBF sub-lement adminMode. Allowed values are ON-LINE, " - "MAINTENANCE, OFF-LINE, NOT-FITTED, RESERVED") + "MAINTENANCE, OFF-LINE, NOT-FITTED, RESERVED"), + enum_labels=["ON-LINE", "OFF-LINE", "MAINTENANCE", "NOT-FITTED", "RESERVED", ], ) cspPssAdminMode = attribute( @@ -581,7 +773,8 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): polling_period=3000, abs_change=1, doc=("The PSS sub-lement adminMode. Allowed values are ON-LINE, " - "MAINTENANCE, OFF-LINE, NOT-FITTED, RESERVED") + "MAINTENANCE, OFF-LINE, NOT-FITTED, RESERVED"), + enum_labels=["ON-LINE", "OFF-LINE", "MAINTENANCE", "NOT-FITTED", "RESERVED", ], ) cspPstAdminMode = attribute( @@ -591,7 +784,8 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): polling_period=3000, abs_change=1, doc=("The PST sub-lement adminMode. Allowed values are ON-LINE, " - "MAINTENANCE, OFF-LINE, NOT-FITTED, RESERVED") + "MAINTENANCE, OFF-LINE, NOT-FITTED, RESERVED"), + enum_labels=["ON-LINE", "OFF-LINE", "MAINTENANCE", "NOT-FITTED", "RESERVED", ], ) unassignedSearchBeamsNum = attribute( @@ -784,7 +978,8 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): def init_device(self): """Initialises the attributes and properties of the CspMaster.""" - SKAMaster.init_device(self) + SKAMaster.init_device(self) + print(self.get_state()) # PROTECTED REGION ID(CspMaster.init_device) ENABLED START # self._build_state = '{}, {}, {}'.format(release.name, release.version, release.description) self._version_id = release.version @@ -793,6 +988,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): self._element_logging_level = int(tango.LogLevel.LOG_INFO) # set init values for the CSP Element and Sub-element SCM states self.set_state(tango.DevState.INIT) + print(self.get_state()) self._health_state = HealthState.OK self._admin_mode = AdminMode.ONLINE # use defaultdict to initialize the sub-element State,healthState @@ -804,10 +1000,6 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): 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: @@ -820,7 +1012,6 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): 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: @@ -846,7 +1037,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # 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)) + self._se_cmd_execution_state = defaultdict(lambda: defaultdict(lambda: self.CmdExecState.IDLE)) # _se_cmd_starting_time: for each sub-element report the long-running command # starting time @@ -918,32 +1109,38 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # 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_health_state = defaultdict(lambda: HealthState.UNKNOWN) self._search_beam_admin_mode = defaultdict(lambda: tango.DevState.NOTFITTED) - self._search_beam_obs_state = defaultdict(lambda: tango.ObsState.IDLE) + self._search_beam_obs_state = defaultdict(lambda: ObsState.IDLE) self._timing_beam_state = defaultdict(lambda: tango.DevState.DISABLED) - self._timing_beam_health_state = defaultdict(lambda: tango.DevState.UNKNOWN) + self._timing_beam_health_state = defaultdict(lambda: HealthState.UNKNOWN) self._timing_beam_admin_mode = defaultdict(lambda: tango.DevState.NOTFITTED) - self._timing_beam_obs_state = defaultdict(lambda: tango.ObsState.IDLE) + self._timing_beam_obs_state = defaultdict(lambda: ObsState.IDLE) self._vlbi_beam_state = defaultdict(lambda: tango.DevState.DISABLED) - self._vlbi_beam_health_state = defaultdict(lambda: tango.DevState.UNKNOWN) + self._vlbi_beam_health_state = defaultdict(lambda: HealthState.UNKNOWN) self._vlbi_beam_admin_mode = defaultdict(lambda: tango.DevState.NOTFITTED) - self._vlbi_beam_obs_state = defaultdict(lambda: tango.ObsState.IDLE) + self._vlbi_beam_obs_state = defaultdict(lambda: ObsState.IDLE) #initialize Csp capabilities subarray membership affiliation - self._timingBeamsMembership = [] - self._vlbiBeamsMembership = [] + self._search_beam_membership = [] + self._timing_beam_membership = [] + self._vlbi_beam_membership = [] # 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 = [] - - + self._unassigned_vlbi_beam_id = [] + # Try connection with sub-elements + print(self.get_state()) self._connect_to_subelements() + # to use the push model in command_inout_asynch (the one with the callback parameter), + # change the global TANGO model to PUSH_CALLBACK. + apiutil = tango.ApiUtil.instance() + apiutil.set_asynch_cb_sub_model(tango.cb_sub_model.PUSH_CALLBACK) + print(self.get_state()) # PROTECTED REGION END # // CspMaster.init_device def always_executed_hook(self): @@ -959,6 +1156,45 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): destructor and by the device Init command. """ # PROTECTED REGION ID(CspMaster.delete_device) ENABLED START # + event_to_remove = {} + for fqdn in self._se_fqdn: + try: + event_to_remove[fqdn] = [] + for attr_name, event_id in self._se_event_id[fqdn].items(): + try: + self._se_proxies[fqdn].unsubscribe_event(event_id) + event_to_remove[fqdn].append(attr_name) + # NOTE: in PyTango unsubscription of not-existing event + # id raises a KeyError exception not a DevFailed !! + except KeyError as key_err: + msg = "Can't retrieve the information of key {}".format(key_err) + self.dev_logging(msg, tango.LogLevel.LOG_ERROR) + 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) + # remove the attribute entry from the fqdn dictionary + for attr_name in event_to_remove[fqdn]: + del self._se_event_id[fqdn][attr_name] + # check if there are still some registered events. + if not self._se_event_id[fqdn]: + # remove the dictionary element when the fqdn dictionary is + # empty + self._se_event_id.pop(fqdn) + else: + # What to do in this case??. Only log (for the moment) + msg = "Still subscribed events: {}".format(self._se_event_id) + self.dev_logging(msg, tango.LogLevel.LOG_WARN) + except KeyError as key_err: + msg = " Can't retrieve the information of key {}".format(key_err) + self.dev_logging(msg, tango.LogLevel.LOG_ERROR) + continue + # clear any list and dict + self._se_fqdn.clear() + self._se_proxies.clear() + self._search_beam_membership.clear() + self._timing_beam_membership.clear() + self._vlbi_beam_membership.clear() # PROTECTED REGION END # // CspMaster.delete_device # ------------------ # Attributes methods @@ -972,26 +1208,54 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): def write_adminMode(self, value): # PROTECTED REGION ID(CspMaster.adminMode_write) ENABLED START # - """Set the adminMode attribute.""" + """ + Write attribute method. + + Set the administration mode for the whole CSP element. + + :param argin: + value: one of the administration mode value (ON-LINE,\ + OFF-LINE, MAINTENANCE, NOT-FITTED, RESERVED). + :type: DevEnum + :return: None + """ + # PROTECTED REGION ID(CspMaster.adminMode_write) ENABLED START # + for fqdn in self._se_fqdn: + try: + if self._is_se_device_running(fqdn): + device_proxy = self._se_proxies[fqdn] + device_proxy.adminMode = value + except tango.DevFailed as df: + log_msg = (("Failure in setting adminMode " + "for device {}: {}".format(str(fqdn), + str(df.args[0].reason)))) + self.dev_logging(log_msg, int(tango.LogLevel.LOG_ERROR)) + #TODO: what happens if one sub-element fails? self._admin_mode = value # PROTECTED REGION END # // CspMaster.adminMode_write def read_onCommandProgress(self): # PROTECTED REGION ID(CspMaster.onCommandProgress_read) ENABLED START # """Return the onCommandProgress attribute.""" - return 0 + return (self._se_cmd_progress[self.CspCbf]['on'] + + self._se_cmd_progress[self.CspPss]['on'] + + self._se_cmd_progress[self.CspPst]['on']) # PROTECTED REGION END # // CspMaster.onCommandProgress_read def read_offCommandProgress(self): # PROTECTED REGION ID(CspMaster.offCommandProgress_read) ENABLED START # """Return the offCommandProgress attribute.""" - return 0 + return (self._se_cmd_progress[self.CspCbf]['off'] + + self._se_cmd_progress[self.CspPss]['off'] + + self._se_cmd_progress[self.CspPst]['off']) # PROTECTED REGION END # // CspMaster.offCommandProgress_read def read_standbyCommandProgress(self): # PROTECTED REGION ID(CspMaster.standbyCommandProgress_read) ENABLED START # """Return the standbyCommandProgress attribute.""" - return 0 + return (self._se_cmd_progress[self.CspCbf]['standby'] + + self._se_cmd_progress[self.CspPss]['standby'] + + self._se_cmd_progress[self.CspPst]['standby']) # PROTECTED REGION END # // CspMaster.standbyCommandProgress_read def read_onCmdDurationExpected(self): @@ -1048,94 +1312,126 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): return 0 # PROTECTED REGION END # // CspMaster.standbyCmdDurationMeasured_read - def read_cbfOnCmdTimeoutExpired(self): + def read_cbfCmdTimeoutExpired(self): # PROTECTED REGION ID(CspMaster.cbfOnCmdTimeoutExpired_read) ENABLED START # """Return the cbfOnCmdTimeoutExpired attribute.""" + + occurred_timeouts = list(self._se_timeout_expired[self.CspCbf].values()).count(True) + if occurred_timeouts: + return True return False # PROTECTED REGION END # // CspMaster.cbfOnCmdTimeoutExpired_read - def read_pssOnCmdTimeoutExpired(self): + def read_pssCmdTimeoutExpired(self): # PROTECTED REGION ID(CspMaster.pssOnCmdTimeoutExpired_read) ENABLED START # """Return the pssOnCmdTimeoutExpired attribute.""" - return False + return self._se_timeout_expired[self.CspPss]['on'] # PROTECTED REGION END # // CspMaster.pssOnCmdTimeoutExpired_read - def read_pstOnCmdTimeoutExpired(self): + def read_pstCmdTimeoutExpired(self): # PROTECTED REGION ID(CspMaster.pstOnCmdTimeoutExpired_read) ENABLED START # """Return the pstOnCmdTimeoutExpired attribute.""" - return False + return self._se_timeout_expired[self.CspPst]['on'] # PROTECTED REGION END # // CspMaster.pstOnCmdTimeoutExpired_read def read_cspCbfState(self): # PROTECTED REGION ID(CspMaster.cspCbfState_read) ENABLED START # """Return the cspCbfState attribute.""" - return tango.DevState.UNKNOWN + return self._se_state[self.CspCbf] # PROTECTED REGION END # // CspMaster.cspCbfState_read def read_cspPssState(self): # PROTECTED REGION ID(CspMaster.cspPssState_read) ENABLED START # """Return the cspPssState attribute.""" - return tango.DevState.UNKNOWN + return self._se_state[self.CspPss] # PROTECTED REGION END # // CspMaster.cspPssState_read def read_cspPstState(self): # PROTECTED REGION ID(CspMaster.cspPstState_read) ENABLED START # """Return the cspPstState attribute.""" - return tango.DevState.UNKNOWN + return self._se_state[self.CspPst] # PROTECTED REGION END # // CspMaster.cspPstState_read def read_cspCbfHealthState(self): # PROTECTED REGION ID(CspMaster.cspCbfHealthState_read) ENABLED START # """Return the cspCbfHealthState attribute.""" - return 0 + return self._se_health_state[self.CspCbf] # PROTECTED REGION END # // CspMaster.cspCbfHealthState_read def read_cspPssHealthState(self): # PROTECTED REGION ID(CspMaster.cspPssHealthState_read) ENABLED START # """Return the cspPssHealthState attribute.""" - return 0 + return self._se_health_state[self.CspPss] # PROTECTED REGION END # // CspMaster.cspPssHealthState_read def read_cspPstHealthState(self): # PROTECTED REGION ID(CspMaster.cspPstHealthState_read) ENABLED START # """Return the cspPstHealthState attribute.""" - return 0 + return self._se_health_state[self.CspPst] # PROTECTED REGION END # // CspMaster.cspPstHealthState_read def read_cbfMasterAddress(self): # PROTECTED REGION ID(CspMaster.cbfMasterAddress_read) ENABLED START # """Return the cbfMasterAddress attribute.""" - return '' + return self.CspCbf # PROTECTED REGION END # // CspMaster.cbfMasterAddress_read def read_pssMasterAddress(self): # PROTECTED REGION ID(CspMaster.pssMasterAddress_read) ENABLED START # """Return the pssMasterAddress attribute.""" - return '' + return self.CspPss # PROTECTED REGION END # // CspMaster.pssMasterAddress_read def read_pstMasterAddress(self): # PROTECTED REGION ID(CspMaster.pstMasterAddress_read) ENABLED START # """Return the pstMasterAddress attribute.""" - return '' + return self.CspPst # PROTECTED REGION END # // CspMaster.pstMasterAddress_read def read_cspCbfAdminMode(self): # PROTECTED REGION ID(CspMaster.cspCbfAdminMode_read) ENABLED START # """Return the cspCbfAdminMode attribute.""" - return 0 + return self._se_admin_mode[self.CspCbf] # PROTECTED REGION END # // CspMaster.cspCbfAdminMode_read def write_cspCbfAdminMode(self, value): # PROTECTED REGION ID(CspMaster.cspCbfAdminMode_write) ENABLED START # - """Set the cspCbfAdminMode attribute.""" - pass + """ + Write attribute method + + Set the CBF sub-element *adminMode* attribute value. + + :parameter:value: one of the administration mode value (ON-LINE,\ + OFF-LINE, MAINTENANCE, NOT-FITTED, RESERVED). + :return: None + :raises: tango.DevFailed: raised when there is no DeviceProxy providing interface \ + to the CBF sub-element Master, or an exception is caught in command execution. + """ + # check if the sub-element administrative mode has already the requested value + if value == self._se_admin_mode[self.CspCbf]: + return + # check if the device is exported in the TANGO DB (that is the server is running) + if self._is_se_device_running(self.CspCbf): + try: + cbf_proxy = self._se_proxies[self.CspCbf] + #cbf_proxy.adminMode = value + cbf_proxy.write_attribute_asynch("adminMode", value) + except tango.DevFailed as df: + tango.Except.throw_exception("Command failed", + str(df.args[0].desc), + "Set cbf admin mode", + tango.ErrSeverity.ERR) + except KeyError as key_err: + msg = "Can't retrieve the information of key {}".format(key_err) + self.dev_logging(msg, tango.LogLevel.LOG_ERROR) + tango.Except.throw_exception("Command failed", msg, + "Set cbf admin mode", tango.ErrSeverity.ERR) # PROTECTED REGION END # // CspMaster.cspCbfAdminMode_write def read_cspPssAdminMode(self): # PROTECTED REGION ID(CspMaster.cspPssAdminMode_read) ENABLED START # """Return the cspPssAdminMode attribute.""" - return 0 + return self._se_admin_mode[self.CspPss] # PROTECTED REGION END # // CspMaster.cspPssAdminMode_read def write_cspPssAdminMode(self, value): @@ -1147,7 +1443,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): def read_cspPstAdminMode(self): # PROTECTED REGION ID(CspMaster.cspPstAdminMode_read) ENABLED START # """Return the cspPstAdminMode attribute.""" - return 0 + return self._se_admin_mode[self.CspPst] # PROTECTED REGION END # // CspMaster.cspPstAdminMode_read def write_cspPstAdminMode(self, value): @@ -1316,7 +1612,19 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # -------- # Commands # -------- + def is_On_allowed(self): + """ + *TANGO is_allowed method* + Command *Off* is allowed when the device *State* is STANDBY. + + Returns: + True if the method is allowed, otherwise False. + """ + # PROTECTED REGION ID(CspMaster.is_On_allowed) ENABLED START # + if self.get_state() not in [tango.DevState.STANDBY, tango.DevState.ON]: + return False + return True @command( dtype_in='DevVarStringArray', doc_in=("If the array length is 0, the command applies to the whole CSP Element." @@ -1349,7 +1657,71 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): for CBF sub-element or there are no DeviceProxy providing interface to the CSP sub-elements or the AdminMode is not correct. """ - pass + # Check the AdminMode value: only if it's ONLINE or MAINTENACE + # the command is callable. + if self._admin_mode in [AdminMode.OFFLINE, AdminMode.NOTFITTED, AdminMode.RESERVED]: + # NOTE: when adminMode is NOT_FITTED, OFFLINE or RESERVED device itself + # sets the TANGO state to DISABLE + # Add only a check on State value to log a warning message if it + # is different from DISABLE + msg_args = (self.get_state(), self._admin_mode) + if self.get_state() != tango.DevState.DISABLE: + self.dev_logging("is_On_allowed: incoherent device State {} " + " with adminMode {}".format(*msg_args), + tango.LogLevel.LOG_WARN) + err_msg = ("On() command can't be issued when State is {} and" + " adminMode is {} ".format(*msg_args)) + tango.Except.throw_exception("Command not executable", + err_msg, + "On command execution", + tango.ErrSeverity.ERR) + device_list = [] + num_of_devices = len(argin) + if num_of_devices == 0: + # no input argument -> switch on all sub-elements + num_of_devices = len(self._se_fqdn) + device_list = self._se_fqdn + else: + if num_of_devices > len(self._se_fqdn): + # too many devices specified-> log the warning but go on + # with command execution + self.dev_logging("Too many input parameters", tango.LogLevel.LOG_WARN) + device_list = argin + for device_name in device_list: + try: + self.dev_logging("device_name:{}".format(device_name), tango.LogLevel.LOG_WARN) + self.dev_logging("device_list:{}".format(device_list), tango.LogLevel.LOG_WARN) + if self._se_cmd_execution_state[device_name]['on'] in [self.CmdExecState.RUNNING, + self.CmdExecState.QUEUED]: + #throw expection + return + # check if the device has gone in timeout in a previous command execution + if list(self._se_timeout_expired[device_name].values()).count(True): + msg = ("Device {} expired on command {}." + " Clean the timeout counter".format(device_name, + self._se_timeout_expired[device_name])) + print(msg) + # reset the attribute content + self._se_timeout_expired[device_name] = {} + se_to_wait_for = "" + index = device_list.index(device_name) + if index: + se_to_wait_for = device_list[index - 1] + args_list = [se_to_wait_for, device_name] + self._se_on_cmd_thread[device_name] = threading.Thread(target=self._issue_on_command, + args=(args_list,)) + self._se_on_cmd_thread[device_name].start() + self._se_cmd_execution_state[device_name]['on'] = self.CmdExecState.QUEUED + + except KeyError as error: + # throw an exception only if: + # - no proxy found for the only specified input device + # - or no proxy found for CBF. + # In all other cases log the error message + err_msg = "No proxy for device: {}".format(str(error)) + self.dev_logging(err_msg, int(tango.LogLevel.LOG_ERROR)) + self._se_cmd_execution_state[device_name]['on'] = self.CmdExecState.IDLE + # PROTECTED REGION END # // CspMaster.On @command( diff --git a/csp-lmc-common/pogo/CspMaster.xmi b/csp-lmc-common/pogo/CspMaster.xmi index 221036e..63a76a4 100644 --- a/csp-lmc-common/pogo/CspMaster.xmi +++ b/csp-lmc-common/pogo/CspMaster.xmi @@ -330,7 +330,7 @@ - + @@ -339,7 +339,7 @@ - + @@ -348,7 +348,7 @@ - + -- GitLab From 87f1437dd018036fd9a3cc2adb463352db8d2c81 Mon Sep 17 00:00:00 2001 From: Elisabetta Giani Date: Sun, 29 Dec 2019 17:01:14 -0500 Subject: [PATCH 03/33] AT5-257: a lot of work on On command. Used only one thread to handle the command. All the stuff (check on state change, time elapsed, etc.) is done inside the thread. --- csp-lmc-common/csp-lmc-common/CspMaster.py | 535 ++++++++++++--------- 1 file changed, 304 insertions(+), 231 deletions(-) diff --git a/csp-lmc-common/csp-lmc-common/CspMaster.py b/csp-lmc-common/csp-lmc-common/CspMaster.py index e51d1ba..97d501f 100644 --- a/csp-lmc-common/csp-lmc-common/CspMaster.py +++ b/csp-lmc-common/csp-lmc-common/CspMaster.py @@ -112,18 +112,19 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): :return: None """ + dev_name = evt.device.dev_name() 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": print("Received event on {} value {}".format(evt.attr_value.name, evt.attr_value.value)) self._se_state[dev_name] = evt.attr_value.value - self._reset_asynch_cmd_tracking_attr(dev_name) + elif evt.attr_value.name.lower() == "healthstate": self._se_health_state[dev_name] = evt.attr_value.value elif evt.attr_value.name.lower() == "adminmode": + print("device: {} value {}".format(dev_name,evt.attr_value.value )) self._se_admin_mode[dev_name] = evt.attr_value.value else: log_msg = ("Attribute {} not still " @@ -152,38 +153,49 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): if item.reason == "API_EventTimeout": self._se_state[dev_name] = tango.DevState.UNKNOWN self._se_health_state[dev_name] = HealthState.UNKNOWN - # if a timeout is registered for the OFF command, the timeout - # flag is reset, the command execution state is set to IDLE - # and the device state is set to OFF - if self._se_timeout_expired[dev_name]['off']: - self._se_timeout_expired[dev_name]['off'] = False - self._se_cmd_execution_state[dev_name]['off'] = self.CmdExecState.IDLE - self._se_state[dev_name] = tango.DevState.OFF + 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) - def _timeout_handler_cb(self, *args, **dict_args): + def _timeout_handler_cb(self, *device_list, **dict_args): print("Timeout expired!!") - try: - fqdn = dict_args['device_name'] - cmd_expired = dict_args['cmd_name'] - print("Timeout expired on device {} for command {}".format(fqdn,cmd_expired)) - self._se_timeout_expired[fqdn][cmd_expired] = True - self._se_cmd_execution_state[fqdn][cmd_expired] = self.CmdExecState.IDLE - self._se_cmd_progress[fqdn] = 0 - self._se_on_cmd_thread[fqdn].join() - except KeyError as key_err: - err_msg = "No key {} found in input args".format(str(key_err)) - self.dev_logging(err_msg, tango.LogLevel.LOG_ERROR) + cmd_expired = dict_args['cmd_name'] + self._cmd_execution_state[cmd_expired] = self.CmdExecState.IDLE + if cmd_expired == 'on': + print("timeout_handler is on alive?", self._on_cmd_thread.is_alive()) + for fqdn in device_list: + try: + if self._se_timeout_expired[fqdn][cmd_expired]: + print("Timeout expired on device {} for command {} execution".format(fqdn, cmd_expired)) + # to be sure, reset the command state + if self._se_cmd_execution_state[fqdn][cmd_expired] == self.CmdExecState.RUNNING: + self._se_cmd_execution_state[fqdn][cmd_expired] == self.CmdExecState.IDLE + except KeyError as key_err: + err_msg = "No key {} found in input args".format(str(key_err)) + self.dev_logging(err_msg, tango.LogLevel.LOG_ERROR) - def _cmd_progress_cb(self, evt): + def _cmd_progress_change_evt_cb(self, evt): + """ + Class protected callback function. + Retrieve the value of the sub-element xxxCommandProgress attribute + subscribed for change event when a long-running command is issued + on the sub-element device. + + :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: - self._se_cmd_progress[dev_name][evt.attr_name.lower()] = evt.attr_value.value + # get the command name (on/off/standby) from the attribute name + # (onCommandProgress/offCommandProgress/standbyCommandProgress)) + # removing the last 15 chars + cmd_name = evt.attr_value.name[:-15] + self._se_cmd_progress[dev_name][cmd_name] = evt.attr_value.value else: log_msg = ("Unexpected change event for" " attribute: {}".format(str(evt.attr_name))) @@ -204,14 +216,13 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): def _cmd_ended_cb(self, evt): """ - Method immediately executed when the asynchronous invoked + Callback function immediately executed when the asynchronous invoked command returns. - Args: - evt: A CmdDoneEvent object. This class is used to pass data + :param evt: a CmdDoneEvent object. This class is used to pass data to the callback method in asynchronous callback model for command execution. - + :type: CmdDoneEvent object It has the following members: - device : (DeviceProxy) The DeviceProxy object on which the call was executed. @@ -222,14 +233,11 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): failed. False otherwise - errors : (sequence) The error stack - ext - Returns: - None + :return: none """ # NOTE:if we try to access to evt.cmd_name or other paramters, sometime # the callback crashes withthis error: # terminate called after throwing an instance of 'boost::python::error_already_set' - # - print("cmd_ended", evt) try: # Can happen evt empty?? if evt: @@ -239,11 +247,12 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): print(msg) self.dev_logging(msg, tango.LogLevel.LOG_INFO) else: - msg = "Error in executing command {} ended on device {}.\n".format(evt.cmd_name, - evt.device) + msg = "Error!!Command {} ended on device {}.\n".format(evt.cmd_name, + evt.device.dev_name()) msg += " Desc: {}".format(evt.errors[0].desc) - self.dev_logging(msg, tango.LogLevel.LOG_ERROR) + #self.dev_logging(msg, tango.LogLevel.LOG_ERROR) print(msg) + self._se_cmd_execution_state[evt.device.dev_name()][evt.cmd_name.lower()] = self.CmdExecState.IDLE # obsState and obsMode values take on the CbfSubarray's values via # the subscribe/publish mechanism else: @@ -256,9 +265,11 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): except Exception as ex: msg = "CommandCallBack cmd_ended general exception: {}".format(str(ex)) self.dev_logging(msg, tango.LogLevel.LOG_ERROR) + # --------------- # Class protected methods # --------------- + def _update_csp_state(self): """ Class protected method. @@ -306,71 +317,12 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): self._healthstate = HealthState.OK return - def _reset_asynch_cmd_tracking_attr(self, dev_name): - """ - Reset the attributes used to track the asynchronous execution of a command - - :parameter:dev_name: the sub-element FQDN - :type: DevString - :return: None - """ - print("_reset_asynch_cmd_tracking_attr") - if self._se_state[dev_name] == tango.DevState.ON: - print(self._se_cmd_execution_state[dev_name]['on]']) - if self._se_cmd_execution_state[dev_name]['on'] == self.CmdExecState.RUNNING: - if self._se_cmd_timer[dev_name].isAlive(): - print("Timer is alive") - self._se_cmd_timer[dev_name].cancel() - print("cancel the Timer") - self._se_cmd_timer[dev_name].join() - print("join the thread") - self._se_on_cmd_thread[dev_name].join() - self._se_cmd_execution_state[dev_name]['on'] = self.CmdExecState.IDLE - print(self._se_cmd_execution_state[dev_name]['on']) - self._se_cmd_duration_measured[dev_name]['on'] = time.time() - self._se_cmd_starting_time[dev_name] - print("measured duration:",self._se_cmd_duration_measured[dev_name]['on'] ) - elif self._se_state[dev_name] == tango.DevState.STANDBY: - if self._se_cmd_execution_state[dev_name]['standby'] == self.CmdExecState.RUNNING: - if self._se_cmd_timer[dev_name].isAlive(): - self._se_cmd_timer[dev_name].cancel() - self._se_cmd_timer[dev_name].join() - self._se_cmd_execution_state[dev_name]['standby'] = self.CmdExecState.IDLE - self._se_cmd_duration_measured[dev_name]['standby'] = time.time() - self._se_cmd_starting_time[dev_name] - else: - print("Unhandled State {}".format(self._se_state[dev_name])) - - def _subscribe_scm_attributes(self, device_name): - # Subscription of the sub-element State,healthState and adminMode - try: - device_proxy = self._se_proxies[device_name] - ev_id = device_proxy.subscribe_event("State", - EventType.CHANGE_EVENT, - self._se_scm_change_event_cb, - stateless=True) - self._se_event_id[device_name]['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[device_name]['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[device_name]['adminMode'] = ev_id - except KeyError as key_err: - err_msg = "No proxy found for device {}".format(str(key_err)) - self.dev_logging(err_msg, tango.LogLevel.LOG_ERROR) - tango.Except.throw_exception("Subscription failure", err_msg, "_subscribe_scm_attributes", tango.ErrSeverity.ERR) - 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. + Establish a *stateless* connection with each CSP sub-element. + Retrieve from the CSP TANGO DB the memorized adminMode value for each + sub-element. Exceptions are logged. :return: None @@ -379,23 +331,21 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): csp_tango_db = tango.Database() for fqdn in self._se_fqdn: - # initialize the list for each dictionary key-name try: log_msg = "Trying connection to" + str(fqdn) + " device" print(log_msg) self.dev_logging(log_msg, int(tango.LogLevel.LOG_INFO)) device_proxy = DeviceProxy(fqdn) - print("1") - # 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. + #device_proxy.ping() + # Note: ping() method is not called. The DeviceProy is initilaize even if the + # sub-element is not running. The connection with a sub-element is establish + # as soon as the corresponding Master device starts. # store the sub-element proxies self._se_proxies[fqdn] = device_proxy # subscription of SCM attributes (State, healthState and adminMode). - # Note: subscription is performed also for devices not ONLINE or MAINTENANCE. In this - # way the CspMaster is able to detect a change in the admin value. + # Note: subscription is performed also for devices not ONLINE or MAINTENANCE. + # In this way the CspMaster is able to detect a change in the admin value. ev_id = device_proxy.subscribe_event("State", EventType.CHANGE_EVENT, self._se_scm_change_event_cb, @@ -413,7 +363,6 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): self._se_scm_change_event_cb, stateless=True) self._se_event_id[fqdn]['adminMode'] = ev_id - print("2") # read the sub-element adminMode (memorized) attribute from # the TANGO DB. # Note: a memorized attribute has defined the attribute property '__value' @@ -426,20 +375,18 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): self.dev_logging(log_msg, tango.LogLevel.LOG_WARN) except tango.DevFailed as df: #for item in df.args: - print("3") log_msg = ("Failure in connection to {}" " device: {}".format(str(fqdn), str(df.args[0].desc))) self.dev_logging(log_msg, tango.LogLevel.LOG_ERROR) - def _is_se_device_running (self, subelement_name): """ - *Class private method.* + *Class protected method.* - Check if the sub-element is exported in the TANGO DB (that is the TANGO device server - is running). - If the device is not present in the list of the connected - sub-elements, a connection with the device is performed. + Check if a sub-element is exported in the TANGO DB (i.e its TANGO + device server is running). + If the device is not 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` @@ -455,63 +402,126 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # 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 + try: + 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: + return False 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 _issue_on_command(self, subelem_list): - # subelem_list[0]: the FQDN of the device executing the ON commmand - # subelem_list[1]: the device waiting to execute the ON command. - print("_issue_on_command:",subelem_list) - dev_to_wait_for = subelem_list[0] - dev_to_power_on = subelem_list[1] - - try: - device_proxy = self._se_proxies[dev_to_power_on] - #self._se_cmd_duration_expected[device_name]['on'] = proxy.read_attribute("onCmdDurationExpected") - self._se_cmd_duration_expected[dev_to_power_on]['on'] = 5 - timeout_val = self._se_cmd_duration_expected[dev_to_power_on]['on'] - self._se_cmd_duration_measured[dev_to_power_on]['on'] = 0 - self._se_cmd_timer[dev_to_power_on] = threading.Timer(timeout_val, self._timeout_handler_cb, None, - {'device_name':dev_to_power_on, - 'cmd_name':'on'}) - dev_to_wait_proxy = self._se_proxies[dev_to_power_on] - if dev_to_wait_for: + def _issue_on_command(self, device_list): + """ + Target function called by the thread run() method. + + :param subelem_list: argument tuple passed when the target function is called. + subelem_list[0]: the FQDN of the device executing the ON commmand + subelem_list[1]: the device waiting to execute the ON command. + """ + print("_issue_on_command:",device_list) + #TODO: order the list alphabetically so that the CBF is always the first element to start + + num_of_failed_device = 0 + self._num_dev_completed_task['on'] = 0 + self._cmd_progress['on'] = 0 + se_cmd_duration_measured = defaultdict(lambda:defaultdict(lambda:0)) + if not self._cmd_timer.is_alive(): + print("Starting timer!!!") + self._cmd_timer.start() + else: + print("Timer thread is running") + for device in device_list: + print("device {} initial state for on command {}".format(device, + self._se_cmd_execution_state[device]['on'])) + self._se_cmd_execution_state[device]['on'] = self.CmdExecState.RUNNING + se_cmd_duration_measured[device]['on'] = 0 + try: + device_proxy = self._se_proxies[device] + print("Issue asynch command on device {}:".format(device)) + + # Note: if the command ends on the sub-element, the _cmd_ended_cb callback is called. This callback + # sets the sub-element execution state to IDLE if an exception is caught during the execution. + # on the sub-element. In both case the command_state = IDLE is right. + # Note: If the command is already running on the sub-element, this device has to return without + # throwing any exception. In this case the current method enters the while loop and the execution + # of the sub-element command is tracked in the right way. + device_proxy.command_inout_asynch('On', self._cmd_ended_cb) + print("device {} excution state {}".format(device,self._se_cmd_execution_state[device]['on'])) + # register the starting time for the command + self._se_cmd_starting_time[device] = time.time() + # loop on the device until the State changes to ON or a timeout occurred while True: - time.sleep(2) - state = dev_to_wait_proxy.command_inout("State") - if state == tango.DevState.ON or self._se_timeout_expired[dev_to_wait_for]['on']: - print("Command On executed on device {}." - " Issue ON command on {}").format(dev_to_wait_for, dev_to_power_on) + #state = device_proxy.command_inout("State") + #if state == tango.DevState.ON: + if self._se_state[device] == tango.DevState.ON: + print("Command On executed on device {}.".format(device)) + self.dev_logging("Command On executed on device {}.".format(device), + tango.LogLevel.LOG_INFO) + # reset the value of the attribute reporting the execution state of + # the command + self._num_dev_completed_task['on'] += 1 + + self._se_cmd_execution_state[device]['on'] = self.CmdExecState.IDLE + se_cmd_duration_measured[device]['on'] = time.time() - self._se_cmd_starting_time[device] + # calculate the real execution time for the command + self._cmd_duration_measured['on'] += se_cmd_duration_measured[device]['on'] + print("measured duration:", se_cmd_duration_measured[device]['on']) + print("evt oncommandprogress:", self._se_event_id[device]['oncommandprogress'] ) + #if self._se_event_id[device]['oncommandprogress'] == 0: + # if the sub-element onCommandProgress attribute is not subscribed + # the attribute tracking the command execution is set to 100 + self._se_cmd_progress[device]['on'] = 100 break - self._se_cmd_execution_state[dev_to_power_on]['on'] = self.CmdExecState.RUNNING - # subscribe the onProgressCommand attribute - try: - if self._se_event_id[dev_to_power_on]['oncommandprogress'] == 0: - evt_id = device_proxy.subscribe_event("onCommandProgress", EventType.CHANGE_EVENT, - self._se_scm_change_event_cb, stateless=False) - self._se_event_id[dev_to_power_on]['oncommandprogress'] = evt_id - except tango.DevFailed as df: - print(df.args[0].desc) - try: - print("Issue asynch command on device {}:".format(dev_to_power_on)) - #device_proxy.command_inout_asynch('On', self._cmd_ended_cb) - self._se_cmd_starting_time[dev_to_power_on] = time.time() - self._se_cmd_timer[dev_to_power_on].start() - return + # check if sub-element command ended for errors (call to cmd_ended_cb and + # execution state set to IDLE) + if self._se_cmd_execution_state[device]['on'] == self.CmdExecState.IDLE: + # execution ended for this sub-element, skip to the next one + num_of_failed_device += 1 + print("Inside while loop num_failed =", num_of_failed_device) + break + # check for timeout event + elapsed_time = time.time() - self._se_cmd_starting_time[device] + if elapsed_time > self._se_cmd_duration_expected[device]['on']: + num_of_failed_device += 1 + self._se_timeout_expired[device]['on'] = True + self._se_cmd_execution_state[device]['on'] = self.CmdExecState.IDLE + # if the CBF command timeout expires, the CSP power-on is stopped + # TODO: verify if this behavior conflicts with ICD + if device.find("cbf"): + self.dev_logging("CBF Timeout during power-on!!! Exit", tango.LogLevel.LOG_ERROR) + print("is cmd timer alive?:", self._cmd_timer.is_alive()) + if self._cmd_timer.is_alive(): + self._cmd_timer.cancel() + self._cmd_timer.join() + return + # timeout on the sub-element, skip to the next + break + time.sleep(1) + # update the progress counter inside the loop taking into account the number of devices + # executing the command + self._cmd_progress['on'] += self._se_cmd_progress[device]['on']/len(device_list) + # end of the while loop + # update the progress counter at the end of the loop + self._cmd_progress['on'] += self._se_cmd_progress[device]['on']/len(device_list) + if len(device_list) == self._num_dev_completed_task['on'] + num_of_failed_device: + print("All devices have been handled!") + # end of the command: reset the timer, and the command state + print("Removing timer") + self._cmd_timer.cancel() + self._cmd_timer.join() + self._cmd_execution_state['on'] = self.CmdExecState.IDLE + print("is cmd timer alive?:", self._cmd_timer.is_alive()) + except KeyError as key_err: + msg = "No key {} found".format(str(key_err)) + self.dev_logging(msg, tango.LogLevel.LOG_WARN) 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) - except KeyError as key_err: - print("issue_on_command no key {} found".format(str(key_err))) - self._se_cmd_execution_state[dev_to_power_on]['on'] = self.CmdExecState.IDLE - # PROTECTED REGION END # // CspMaster.class_protected_methods @@ -1031,14 +1041,23 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # keys: sub-element FQDN # values: python Timer self._se_cmd_timer = {} + self._cmd_timer = 0 # _se_cmd_execution_state: implement the execution state of a long-running # command for each sub-element. - # implemented as a default dictionary: + # implemented as a nested default dictionary: # keys: sub-element FQDN - # values: defaut dictionary (keys: command cname, values: command state) + # values: defaut dictionary (keys: command name, values: command state) self._se_cmd_execution_state = defaultdict(lambda: defaultdict(lambda: self.CmdExecState.IDLE)) + + # _cmd_execution_state: implement the execution state of a long-running + # command for each sub-element. + # implemented as a default dictionary: + # keys: command name + # values:command state + self._cmd_execution_state = defaultdict(lambda: self.CmdExecState.IDLE) + # _se_cmd_starting_time: for each sub-element report the long-running command # starting time # Implemented as dictionary: @@ -1051,12 +1070,13 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # keys: the sub-element FQDN # values: the thread self._se_on_cmd_thread = {} - + self._on_cmd_thread = 0 # _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 = {} + self._off_cmd_thread = 0 # _se_standby_cmd_thread: for each sub-element implement a thread for the # Standby command @@ -1064,14 +1084,21 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # keys: the sub-element FQDN # values: the thread self._se_standby_cmd_thread = {} + self._standby_cmd_thread = 0 # _se_cmd_progress: for each sub-element report the execution progress # of a long-running command - # implemented as a default dictionary: + # implemented as a default nested dictionary: # keys: sub-element FQDN - # values: the percentage + # values: default dictionary (keys: command name, values: the execution percentage) self._se_cmd_progress = defaultdict(lambda: defaultdict(lambda: 0)) + # _cmd_progress: report the execution progress of a long-running command + # implemented as a dictionary: + # keys: command name ('on', 'off'..) + # values: the percentage + self._cmd_progress = 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 @@ -1079,13 +1106,20 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # 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)) - + # _cmd_duration_expected: store the duration (in sec.) configured for + # a long-running command + # Implemented asdefault dictionary + # keys: command name ('on', 'off',..) + # values: the duration (in sec) + self._cmd_duration_expected = defaultdict(lambda: 30) + + # _cmd_duration_measured: report the measured duration (in sec.) for + # a long-running command + # Implemented as default dictionary + # keys: command name ('on', 'off',..) + # values: the duration (in sec) + self._cmd_duration_measured = defaultdict(lambda: 0) + # _se_timeout_expired: report the timeout flag # Implemented as a default dictionary # keys: FQDN @@ -1162,7 +1196,10 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): event_to_remove[fqdn] = [] for attr_name, event_id in self._se_event_id[fqdn].items(): try: - self._se_proxies[fqdn].unsubscribe_event(event_id) + # need to check the event_id value because with the use of defaultdict + # it may happen that a dictionary entry is = 0 (default ditionary value) + if event_id: + self._se_proxies[fqdn].unsubscribe_event(event_id) event_to_remove[fqdn].append(attr_name) # NOTE: in PyTango unsubscription of not-existing event # id raises a KeyError exception not a DevFailed !! @@ -1237,101 +1274,91 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): def read_onCommandProgress(self): # PROTECTED REGION ID(CspMaster.onCommandProgress_read) ENABLED START # """Return the onCommandProgress attribute.""" - return (self._se_cmd_progress[self.CspCbf]['on'] - + self._se_cmd_progress[self.CspPss]['on'] - + self._se_cmd_progress[self.CspPst]['on']) + return int(self._cmd_progress['on']) # PROTECTED REGION END # // CspMaster.onCommandProgress_read def read_offCommandProgress(self): # PROTECTED REGION ID(CspMaster.offCommandProgress_read) ENABLED START # """Return the offCommandProgress attribute.""" - return (self._se_cmd_progress[self.CspCbf]['off'] - + self._se_cmd_progress[self.CspPss]['off'] - + self._se_cmd_progress[self.CspPst]['off']) + return int(self._cmd_progress['off']) # PROTECTED REGION END # // CspMaster.offCommandProgress_read def read_standbyCommandProgress(self): # PROTECTED REGION ID(CspMaster.standbyCommandProgress_read) ENABLED START # """Return the standbyCommandProgress attribute.""" - return (self._se_cmd_progress[self.CspCbf]['standby'] - + self._se_cmd_progress[self.CspPss]['standby'] - + self._se_cmd_progress[self.CspPst]['standby']) + return int(self._cmd_progress['standby']) # PROTECTED REGION END # // CspMaster.standbyCommandProgress_read def read_onCmdDurationExpected(self): # PROTECTED REGION ID(CspMaster.onCmdDurationExpected_read) ENABLED START # """Return the onCmdDurationExpected attribute.""" - return 0 + return self._cmd_duration_expected['on'] # PROTECTED REGION END # // CspMaster.onCmdDurationExpected_read def write_onCmdDurationExpected(self, value): # PROTECTED REGION ID(CspMaster.onCmdDurationExpected_write) ENABLED START # """Set the onCmdDurationExpected attribute.""" - pass + self._cmd_duration_expected['on'] = value # PROTECTED REGION END # // CspMaster.onCmdDurationExpected_write def read_offCmdDurationExpected(self): # PROTECTED REGION ID(CspMaster.offCmdDurationExpected_read) ENABLED START # """Return the offCmdDurationExpected attribute.""" - return 0 + return self._cmd_duration_expected['off'] # PROTECTED REGION END # // CspMaster.offCmdDurationExpected_read def write_offCmdDurationExpected(self, value): # PROTECTED REGION ID(CspMaster.offCmdDurationExpected_write) ENABLED START # """Set the offCmdDurationExpected attribute.""" - pass + self._cmd_duration_expected['off'] = value # PROTECTED REGION END # // CspMaster.offCmdDurationExpected_write def read_standbyCmdDurationExpected(self): # PROTECTED REGION ID(CspMaster.standbyCmdDurationExpected_read) ENABLED START # """Return the standbyCmdDurationExpected attribute.""" - return 0 + return self._cmd_duration_expected['standby'] # PROTECTED REGION END # // CspMaster.standbyCmdDurationExpected_read def write_standbyCmdDurationExpected(self, value): # PROTECTED REGION ID(CspMaster.standbyCmdDurationExpected_write) ENABLED START # """Set the standbyCmdDurationExpected attribute.""" - pass + self._cmd_duration_expected['standby'] = value # PROTECTED REGION END # // CspMaster.standbyCmdDurationExpected_write def read_onCmdDurationMeasured(self): # PROTECTED REGION ID(CspMaster.onCmdDurationMeasured_read) ENABLED START # """Return the onCmdDurationMeasured attribute.""" - return 0 + return int(self._cmd_duration_measured['on']) # PROTECTED REGION END # // CspMaster.onCmdDurationMeasured_read def read_offCmdDurationMeasured(self): # PROTECTED REGION ID(CspMaster.offCmdDurationMeasured_read) ENABLED START # """Return the offCmdDurationMeasured attribute.""" - return 0 + return int(self._cmd_duration_measured['off']) # PROTECTED REGION END # // CspMaster.offCmdDurationMeasured_read def read_standbyCmdDurationMeasured(self): # PROTECTED REGION ID(CspMaster.standbyCmdDurationMeasured_read) ENABLED START # """Return the standbyCmdDurationMeasured attribute.""" - return 0 + return int(self._cmd_duration_measured['standby']) # PROTECTED REGION END # // CspMaster.standbyCmdDurationMeasured_read def read_cbfCmdTimeoutExpired(self): # PROTECTED REGION ID(CspMaster.cbfOnCmdTimeoutExpired_read) ENABLED START # - """Return the cbfOnCmdTimeoutExpired attribute.""" - - occurred_timeouts = list(self._se_timeout_expired[self.CspCbf].values()).count(True) - if occurred_timeouts: - return True - return False + """Return the cbfCmdTimeoutExpired attribute.""" + return (True in self._se_timeout_expired[self.CspCbf].values()) # PROTECTED REGION END # // CspMaster.cbfOnCmdTimeoutExpired_read def read_pssCmdTimeoutExpired(self): # PROTECTED REGION ID(CspMaster.pssOnCmdTimeoutExpired_read) ENABLED START # - """Return the pssOnCmdTimeoutExpired attribute.""" - return self._se_timeout_expired[self.CspPss]['on'] + """Return the pssCmdTimeoutExpired attribute.""" + return (True in self._se_timeout_expired[self.CspPss].values()) # PROTECTED REGION END # // CspMaster.pssOnCmdTimeoutExpired_read def read_pstCmdTimeoutExpired(self): # PROTECTED REGION ID(CspMaster.pstOnCmdTimeoutExpired_read) ENABLED START # """Return the pstOnCmdTimeoutExpired attribute.""" - return self._se_timeout_expired[self.CspPst]['on'] + return (True in self._se_timeout_expired[self.CspPst].values()) # PROTECTED REGION END # // CspMaster.pstOnCmdTimeoutExpired_read def read_cspCbfState(self): @@ -1642,6 +1669,10 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): The command is executed if the *AdminMode* is ONLINE or *MAINTENANCE*. If the AdminMode is *OFFLINE*, *NOT-FITTED* or *RESERVED*, the method throws an exception. + The CSP.LMC commands sub-element transition from STANDBY to ON sequentially. It + waits for the first sub-element (CBF) to complete the transition, then issues the + command to the second sub-element (e.g. PST), waits for PST to complete and then + issues command for PSS. :param argin: The list of sub-element FQDNs to switch-on or an empty list to switch-on the whole @@ -1687,41 +1718,83 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # with command execution self.dev_logging("Too many input parameters", tango.LogLevel.LOG_WARN) device_list = argin - for device_name in device_list: - try: - self.dev_logging("device_name:{}".format(device_name), tango.LogLevel.LOG_WARN) - self.dev_logging("device_list:{}".format(device_list), tango.LogLevel.LOG_WARN) - if self._se_cmd_execution_state[device_name]['on'] in [self.CmdExecState.RUNNING, - self.CmdExecState.QUEUED]: - #throw expection - return - # check if the device has gone in timeout in a previous command execution - if list(self._se_timeout_expired[device_name].values()).count(True): + + # If a sub-element device is already executing a power command throws an + # exception (power commands have to be executed sequentially) + # TODO: What to do if the required device is performing a software upgrade or is changing its + # adminMode? How can CSP.LMC detect this condition? + if self.CmdExecState.RUNNING in self._cmd_execution_state.values(): + print("On command is running!!") + # Note: need to check in the list of sub-elements, not in the device_list sent to the command!!! + for device in self._se_fqdn: + for cmd_name, cmd_state in self._se_cmd_execution_state[device].items(): + if cmd_state == self.CmdExecState.RUNNING: + msg = ("Device {} is already executing the {} command".format(device, cmd_name)) + print(msg) + # throw an exception + return + command_timeout = 0 + device_to_remove = [] + for device in device_list: + print("processin devie:", 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 self._is_se_device_running(device) or + self._se_admin_mode[device] not in [AdminMode.ONLINE, + AdminMode.MAINTENANCE]): + device_to_remove.append(device) + print("Devices to remove from the list:", device_to_remove) + continue + # reset the timeout attribute content + for cmd_name, _ in self._se_timeout_expired[device].items(): + if self._se_timeout_expired[device][cmd_name]: msg = ("Device {} expired on command {}." - " Clean the timeout counter".format(device_name, - self._se_timeout_expired[device_name])) + " Clean the timeout counter".format(device, cmd_name)) print(msg) - # reset the attribute content - self._se_timeout_expired[device_name] = {} - se_to_wait_for = "" - index = device_list.index(device_name) - if index: - se_to_wait_for = device_list[index - 1] - args_list = [se_to_wait_for, device_name] - self._se_on_cmd_thread[device_name] = threading.Thread(target=self._issue_on_command, - args=(args_list,)) - self._se_on_cmd_thread[device_name].start() - self._se_cmd_execution_state[device_name]['on'] = self.CmdExecState.QUEUED - - except KeyError as error: - # throw an exception only if: - # - no proxy found for the only specified input device - # - or no proxy found for CBF. - # In all other cases log the error message - err_msg = "No proxy for device: {}".format(str(error)) - self.dev_logging(err_msg, int(tango.LogLevel.LOG_ERROR)) - self._se_cmd_execution_state[device_name]['on'] = self.CmdExecState.IDLE - + self._se_timeout_expired[device][cmd_name] = False + + device_proxy = self._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) + self._se_cmd_duration_expected[device]['on'] = device_proxy.onCmdDurationExpected + except AttributeError as attr_err: + # we get here if the attribute is not implemented + print(str(attr_err)) + try: + if self._se_event_id[device]['oncommandprogress'] == 0: + evt_id = device_proxy.subscribe_event("onCommandProgress", EventType.CHANGE_EVENT, + self._cmd_progress_change_evt_cb, stateless=False) + self._se_event_id[device]['oncommandprogress'] = evt_id + except tango.DevFailed as df: + print(df.args[0].desc) + + # evaluate the total timeout value to execute the whole command + command_timeout += self._se_cmd_duration_expected[device]['on'] + + # use the greater value for the onCommand duration expected. + if command_timeout > self._cmd_duration_expected['on']: + self._cmd_duration_expected['on'] = command_timeout + self.dev_logging("Modified the On command Duration Expected value!!", tango.LogLevel.LOG_INFO) + # remove the not running device and OFFLINE/RESERVED/NOTFITTED devices from the list + for device in device_to_remove: + device_list.remove(device) + # invoke the constructor for the timer thread + self._cmd_timer = threading.Timer(self._cmd_duration_expected['on'], self._timeout_handler_cb, + device_list, {'cmd_name':'on'}) + # invoke the constructor for the command thread + self._on_cmd_thread = threading.Thread(target=self._issue_on_command, name="Thread-On", + args=(device_list,)) + # start the thread + self._cmd_execution_state['on'] = self.CmdExecState.RUNNING + self._on_cmd_thread.start() + # sleep for a while to let the thread start + time.sleep(0.2) # PROTECTED REGION END # // CspMaster.On @command( -- GitLab From 274caa24b6aef22849a6e9e02e518e453f17c9b0 Mon Sep 17 00:00:00 2001 From: softir Date: Mon, 30 Dec 2019 13:41:38 +0100 Subject: [PATCH 04/33] AT5-257: Define a decorator class to check for administrative mode before executing a command. Added attributes to report the number and the list of devices that completed a task. --- csp-lmc-common/csp-lmc-common/CspMaster.py | 159 ++++++++++++--------- csp-lmc-common/pogo/CspMaster.xmi | 16 +++ csp-lmc-common/utils/decorators.py | 49 +++++++ 3 files changed, 158 insertions(+), 66 deletions(-) create mode 100644 csp-lmc-common/utils/decorators.py diff --git a/csp-lmc-common/csp-lmc-common/CspMaster.py b/csp-lmc-common/csp-lmc-common/CspMaster.py index 97d501f..5301705 100644 --- a/csp-lmc-common/csp-lmc-common/CspMaster.py +++ b/csp-lmc-common/csp-lmc-common/CspMaster.py @@ -45,13 +45,21 @@ from skabase.auxiliary import utils file_path = os.path.dirname(os.path.abspath(__file__)) utils_path = os.path.abspath(os.path.join(file_path, os.pardir)) + "/utils" sys.path.insert(0, utils_path) +import decorators +from decorators import AdminModeCheck import cspcommons from cspcommons import HealthState, AdminMode, ObsState import release # PROTECTED REGION END# //CspMaster.add_path -__all__ = ["CspMaster", "main"] +@unique +class CmdExecState(IntEnum): + IDLE = 0 + RUNNING = 1 + QUEUED = 2 + +__all__ = ["CspMaster", "main"] class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): """ @@ -91,11 +99,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): """ # PROTECTED REGION ID(CspMaster.class_variable) ENABLED START # - @unique - class CmdExecState(IntEnum): - IDLE = 0 - RUNNING = 1 - QUEUED = 2 + # PROTECTED REGION END # // CspMaster.class_variable # PROTECTED REGION ID(CspMaster.class_protected_methods) ENABLED START # @@ -162,7 +166,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): def _timeout_handler_cb(self, *device_list, **dict_args): print("Timeout expired!!") cmd_expired = dict_args['cmd_name'] - self._cmd_execution_state[cmd_expired] = self.CmdExecState.IDLE + self._cmd_execution_state[cmd_expired] = CmdExecState.IDLE if cmd_expired == 'on': print("timeout_handler is on alive?", self._on_cmd_thread.is_alive()) for fqdn in device_list: @@ -170,8 +174,8 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): if self._se_timeout_expired[fqdn][cmd_expired]: print("Timeout expired on device {} for command {} execution".format(fqdn, cmd_expired)) # to be sure, reset the command state - if self._se_cmd_execution_state[fqdn][cmd_expired] == self.CmdExecState.RUNNING: - self._se_cmd_execution_state[fqdn][cmd_expired] == self.CmdExecState.IDLE + if self._se_cmd_execution_state[fqdn][cmd_expired] == CmdExecState.RUNNING: + self._se_cmd_execution_state[fqdn][cmd_expired] == CmdExecState.IDLE except KeyError as key_err: err_msg = "No key {} found in input args".format(str(key_err)) self.dev_logging(err_msg, tango.LogLevel.LOG_ERROR) @@ -252,7 +256,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): msg += " Desc: {}".format(evt.errors[0].desc) #self.dev_logging(msg, tango.LogLevel.LOG_ERROR) print(msg) - self._se_cmd_execution_state[evt.device.dev_name()][evt.cmd_name.lower()] = self.CmdExecState.IDLE + self._se_cmd_execution_state[evt.device.dev_name()][evt.cmd_name.lower()] = CmdExecState.IDLE # obsState and obsMode values take on the CbfSubarray's values via # the subscribe/publish mechanism else: @@ -273,7 +277,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): def _update_csp_state(self): """ Class protected method. - Retrieve the State attribute of the CSP sub-elements and aggregate + Retrieve the State attribute values of the CSP sub-elements and aggregate them to build up the CSP global state. :param: None @@ -288,7 +292,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): def _update_csp_health_state(self): """ - Class private method. + Class protected method. Retrieve the healthState attribute of the CSP sub-elements and aggregate them to build up the CSP health state @@ -337,9 +341,10 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): self.dev_logging(log_msg, int(tango.LogLevel.LOG_INFO)) device_proxy = DeviceProxy(fqdn) #device_proxy.ping() - # Note: ping() method is not called. The DeviceProy is initilaize even if the - # sub-element is not running. The connection with a sub-element is establish - # as soon as the corresponding Master device starts. + # Note: ping() method is not called. The DeviceProxy is initialized even if the + # sub-element is not running (but defined into the TANGO DB!). + # The connection with a sub-element is establish as soon as the corresponding + # Master device starts. # store the sub-element proxies self._se_proxies[fqdn] = device_proxy @@ -417,7 +422,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): def _issue_on_command(self, device_list): """ - Target function called by the thread run() method. + Target function called by the _on_cmd_thread run() method. :param subelem_list: argument tuple passed when the target function is called. subelem_list[0]: the FQDN of the device executing the ON commmand @@ -428,17 +433,21 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): num_of_failed_device = 0 self._num_dev_completed_task['on'] = 0 + self._list_dev_completed_task['on'] = [] self._cmd_progress['on'] = 0 + # sub-element command execution measured time se_cmd_duration_measured = defaultdict(lambda:defaultdict(lambda:0)) + # timer is started outside the for loop: it has to start only once. if not self._cmd_timer.is_alive(): print("Starting timer!!!") self._cmd_timer.start() else: - print("Timer thread is running") + print("Timer thread is running") + # loop on the devices and power-on them sequentially for device in device_list: print("device {} initial state for on command {}".format(device, self._se_cmd_execution_state[device]['on'])) - self._se_cmd_execution_state[device]['on'] = self.CmdExecState.RUNNING + self._se_cmd_execution_state[device]['on'] = CmdExecState.RUNNING se_cmd_duration_measured[device]['on'] = 0 try: device_proxy = self._se_proxies[device] @@ -456,17 +465,16 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): self._se_cmd_starting_time[device] = time.time() # loop on the device until the State changes to ON or a timeout occurred while True: - #state = device_proxy.command_inout("State") - #if state == tango.DevState.ON: if self._se_state[device] == tango.DevState.ON: - print("Command On executed on device {}.".format(device)) + print("Command On ended with success on device {}.".format(device)) self.dev_logging("Command On executed on device {}.".format(device), tango.LogLevel.LOG_INFO) + # update the list and number of device that completed the task + self._num_dev_completed_task['on'] += 1 + self._list_dev_completed_task['on'].append(device) # reset the value of the attribute reporting the execution state of # the command - self._num_dev_completed_task['on'] += 1 - - self._se_cmd_execution_state[device]['on'] = self.CmdExecState.IDLE + self._se_cmd_execution_state[device]['on'] = CmdExecState.IDLE se_cmd_duration_measured[device]['on'] = time.time() - self._se_cmd_starting_time[device] # calculate the real execution time for the command self._cmd_duration_measured['on'] += se_cmd_duration_measured[device]['on'] @@ -476,10 +484,12 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # if the sub-element onCommandProgress attribute is not subscribed # the attribute tracking the command execution is set to 100 self._se_cmd_progress[device]['on'] = 100 + # command success: exit from the wait loop and issue the On command + # on the next device break # check if sub-element command ended for errors (call to cmd_ended_cb and # execution state set to IDLE) - if self._se_cmd_execution_state[device]['on'] == self.CmdExecState.IDLE: + if self._se_cmd_execution_state[device]['on'] == CmdExecState.IDLE: # execution ended for this sub-element, skip to the next one num_of_failed_device += 1 print("Inside while loop num_failed =", num_of_failed_device) @@ -489,17 +499,17 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): if elapsed_time > self._se_cmd_duration_expected[device]['on']: num_of_failed_device += 1 self._se_timeout_expired[device]['on'] = True - self._se_cmd_execution_state[device]['on'] = self.CmdExecState.IDLE + self._se_cmd_execution_state[device]['on'] = CmdExecState.IDLE # if the CBF command timeout expires, the CSP power-on is stopped # TODO: verify if this behavior conflicts with ICD if device.find("cbf"): self.dev_logging("CBF Timeout during power-on!!! Exit", tango.LogLevel.LOG_ERROR) - print("is cmd timer alive?:", self._cmd_timer.is_alive()) + # remove the timer if self._cmd_timer.is_alive(): self._cmd_timer.cancel() self._cmd_timer.join() return - # timeout on the sub-element, skip to the next + # timeout on the sub-element, skip to the next device break time.sleep(1) # update the progress counter inside the loop taking into account the number of devices @@ -510,11 +520,12 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): self._cmd_progress['on'] += self._se_cmd_progress[device]['on']/len(device_list) if len(device_list) == self._num_dev_completed_task['on'] + num_of_failed_device: print("All devices have been handled!") - # end of the command: reset the timer, and the command state + # end of the command: cancel the timer, and reset the global command state + # attribute print("Removing timer") self._cmd_timer.cancel() self._cmd_timer.join() - self._cmd_execution_state['on'] = self.CmdExecState.IDLE + self._cmd_execution_state['on'] = CmdExecState.IDLE print("is cmd timer alive?:", self._cmd_timer.is_alive()) except KeyError as key_err: msg = "No key {} found".format(str(key_err)) @@ -806,6 +817,11 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): " SearchBeams."), ) + numOfDevCompletedTask = attribute( + dtype='DevUShort', + label="Number of devices that completed the task", + doc="Number of devices that completed the task", + ) availableCapabilities = attribute( dtype=('DevString',), @@ -982,14 +998,20 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): doc="Report the list of VlbiBeam Capability IDs that are not assigned to any sub-array. The SCM values of a unassigned VlbiBeam Capabiity are:\nState = OFF\nadminMode = ONLINE or MAINTENACE\nobsState = IDLE\nhealthState = OK \nobsMode = IDLE\n", ) + listOfDevCompletedTask = attribute( + dtype=('DevString',), + max_dim_x=100, + label="List of devices that completed the task", + doc="List of devices that completed the task", + ) + # --------------- # General methods # --------------- def init_device(self): """Initialises the attributes and properties of the CspMaster.""" - SKAMaster.init_device(self) - print(self.get_state()) + 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 @@ -998,7 +1020,6 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): self._element_logging_level = int(tango.LogLevel.LOG_INFO) # set init values for the CSP Element and Sub-element SCM states self.set_state(tango.DevState.INIT) - print(self.get_state()) self._health_state = HealthState.OK self._admin_mode = AdminMode.ONLINE # use defaultdict to initialize the sub-element State,healthState @@ -1048,15 +1069,16 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # implemented as a nested default dictionary: # keys: sub-element FQDN # values: defaut dictionary (keys: command name, values: command state) - self._se_cmd_execution_state = defaultdict(lambda: defaultdict(lambda: self.CmdExecState.IDLE)) + self._se_cmd_execution_state = defaultdict(lambda: defaultdict(lambda: CmdExecState.IDLE)) # _cmd_execution_state: implement the execution state of a long-running - # command for each sub-element. + # command for the whole CSP. Setting this attribute prevent the execution + # of the same command while it is already running. # implemented as a default dictionary: # keys: command name # values:command state - self._cmd_execution_state = defaultdict(lambda: self.CmdExecState.IDLE) + self._cmd_execution_state = defaultdict(lambda: CmdExecState.IDLE) # _se_cmd_starting_time: for each sub-element report the long-running command # starting time @@ -1167,14 +1189,12 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): self._unassigned_vlbi_beam_id = [] # Try connection with sub-elements - print(self.get_state()) self._connect_to_subelements() - + # to use the push model in command_inout_asynch (the one with the callback parameter), # change the global TANGO model to PUSH_CALLBACK. apiutil = tango.ApiUtil.instance() apiutil.set_asynch_cb_sub_model(tango.cb_sub_model.PUSH_CALLBACK) - print(self.get_state()) # PROTECTED REGION END # // CspMaster.init_device def always_executed_hook(self): @@ -1485,6 +1505,12 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): return 0 # PROTECTED REGION END # // CspMaster.unassignedSearchBeamsNum_read + def read_numOfDevCompletedTask(self): + # PROTECTED REGION ID(CspMaster.numOfDevCompletedTask_read) ENABLED START # + """Return the numOfDevCompletedTask attribute.""" + return 0 + # PROTECTED REGION END # // CspMaster.numOfDevCompletedTask_read + def read_availableCapabilities(self): # PROTECTED REGION ID(CspMaster.availableCapabilities_read) ENABLED START # """Return the availableCapabilities attribute.""" @@ -1635,10 +1661,16 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): return (0,) # PROTECTED REGION END # // CspMaster.unassignedVlbiBeamIDs_read + def read_listOfDevCompletedTask(self): + # PROTECTED REGION ID(CspMaster.listOfDevCompletedTask_read) ENABLED START # + """Return the listOfDevCompletedTask attribute.""" + return ('',) + # PROTECTED REGION END # // CspMaster.listOfDevCompletedTask_read # -------- # Commands # -------- + def is_On_allowed(self): """ *TANGO is_allowed method* @@ -1649,9 +1681,12 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): True if the method is allowed, otherwise False. """ # PROTECTED REGION ID(CspMaster.is_On_allowed) ENABLED START # + # Note: as per SKA Guidelines, the command is allowed when if self.get_state() not in [tango.DevState.STANDBY, tango.DevState.ON]: return False return True + + @command( dtype_in='DevVarStringArray', doc_in=("If the array length is 0, the command applies to the whole CSP Element." @@ -1659,6 +1694,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): "the CSP SubElement to switch ON."), ) @DebugIt() + @AdminModeCheck() def On(self, argin): # PROTECTED REGION ID(CspMaster.On) ENABLED START # """ @@ -1688,24 +1724,6 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): for CBF sub-element or there are no DeviceProxy providing interface to the CSP sub-elements or the AdminMode is not correct. """ - # Check the AdminMode value: only if it's ONLINE or MAINTENACE - # the command is callable. - if self._admin_mode in [AdminMode.OFFLINE, AdminMode.NOTFITTED, AdminMode.RESERVED]: - # NOTE: when adminMode is NOT_FITTED, OFFLINE or RESERVED device itself - # sets the TANGO state to DISABLE - # Add only a check on State value to log a warning message if it - # is different from DISABLE - msg_args = (self.get_state(), self._admin_mode) - if self.get_state() != tango.DevState.DISABLE: - self.dev_logging("is_On_allowed: incoherent device State {} " - " with adminMode {}".format(*msg_args), - tango.LogLevel.LOG_WARN) - err_msg = ("On() command can't be issued when State is {} and" - " adminMode is {} ".format(*msg_args)) - tango.Except.throw_exception("Command not executable", - err_msg, - "On command execution", - tango.ErrSeverity.ERR) device_list = [] num_of_devices = len(argin) if num_of_devices == 0: @@ -1718,25 +1736,28 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # with command execution self.dev_logging("Too many input parameters", tango.LogLevel.LOG_WARN) device_list = argin - + + print(device_list) # If a sub-element device is already executing a power command throws an # exception (power commands have to be executed sequentially) # TODO: What to do if the required device is performing a software upgrade or is changing its # adminMode? How can CSP.LMC detect this condition? - if self.CmdExecState.RUNNING in self._cmd_execution_state.values(): + if CmdExecState.RUNNING in self._cmd_execution_state.values(): print("On command is running!!") # Note: need to check in the list of sub-elements, not in the device_list sent to the command!!! for device in self._se_fqdn: for cmd_name, cmd_state in self._se_cmd_execution_state[device].items(): - if cmd_state == self.CmdExecState.RUNNING: - msg = ("Device {} is already executing the {} command".format(device, cmd_name)) + if cmd_state in [CmdExecState.RUNNING, CmdExecState.QUEUED]: + msg = ("Device {} is already executing the {} command" + "Command state: {}".format(device, cmd_name, + self._se_cmd_execution_state[device][cmd_name])) print(msg) # throw an exception return command_timeout = 0 device_to_remove = [] for device in device_list: - print("processin devie:", device) + print("processin device:", device) # if the sub-element device server is not running or the adminMode # attribute is not ONLINE or MAINTENANCE, skip to next and add it to # the list of the devices to remove from the input arg list @@ -1746,6 +1767,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): device_to_remove.append(device) print("Devices to remove from the list:", device_to_remove) continue + self._se_cmd_execution_state[device]['on'] = CmdExecState.QUEUED # reset the timeout attribute content for cmd_name, _ in self._se_timeout_expired[device].items(): if self._se_timeout_expired[device][cmd_name]: @@ -1776,14 +1798,17 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # evaluate the total timeout value to execute the whole command command_timeout += self._se_cmd_duration_expected[device]['on'] - - # use the greater value for the onCommand duration expected. + # use the greatest value for the onCommand duration expected. if command_timeout > self._cmd_duration_expected['on']: self._cmd_duration_expected['on'] = command_timeout self.dev_logging("Modified the On command Duration Expected value!!", tango.LogLevel.LOG_INFO) - # remove the not running device and OFFLINE/RESERVED/NOTFITTED devices from the list + # remove the 'not running' devices or OFFLINE/RESERVED/NOTFITTED devices from the list + # of devices to power-on for device in device_to_remove: device_list.remove(device) + if not device_list: + self.dev_logging("No device to power on", tango.LogLevel.LOG_INFO) + return # invoke the constructor for the timer thread self._cmd_timer = threading.Timer(self._cmd_duration_expected['on'], self._timeout_handler_cb, device_list, {'cmd_name':'on'}) @@ -1791,7 +1816,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): self._on_cmd_thread = threading.Thread(target=self._issue_on_command, name="Thread-On", args=(device_list,)) # start the thread - self._cmd_execution_state['on'] = self.CmdExecState.RUNNING + self._cmd_execution_state['on'] = CmdExecState.RUNNING self._on_cmd_thread.start() # sleep for a while to let the thread start time.sleep(0.2) @@ -1804,6 +1829,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): " CSP SubElement to switch OFF."), ) @DebugIt() + @AdminModeCheck() def Off(self, argin): # PROTECTED REGION ID(CspMaster.Off) ENABLED START # """ @@ -1830,6 +1856,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): "CSP SubElement to put in STANDBY mode."), ) @DebugIt() + @AdminModeCheck() def Standby(self, argin): # PROTECTED REGION ID(CspMaster.Standby) ENABLED START # """ diff --git a/csp-lmc-common/pogo/CspMaster.xmi b/csp-lmc-common/pogo/CspMaster.xmi index 63a76a4..6a5de91 100644 --- a/csp-lmc-common/pogo/CspMaster.xmi +++ b/csp-lmc-common/pogo/CspMaster.xmi @@ -481,6 +481,14 @@ + + + + + + + + @@ -669,6 +677,14 @@ + + + + + + + + diff --git a/csp-lmc-common/utils/decorators.py b/csp-lmc-common/utils/decorators.py new file mode 100644 index 0000000..33edb84 --- /dev/null +++ b/csp-lmc-common/utils/decorators.py @@ -0,0 +1,49 @@ +import sys +import os + +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 tango +import cspcommons +from cspcommons import AdminMode +import functools + +class AdminModeCheck(object): + """ + Class designed to be a decorator for the CspMaster methods. + It checks the adminMode attribute value: if not ONLINE or + MAINTENANCE it throws an exception. + """ + def __init__(self, args=False, kwargs=False): + self._args = args + self._kwargs = kwargs + def __call__(self, f): + @functools.wraps(f) + def admin_mode_check(*args, **kwargs): + # get the device instance + dev = args[0] + # 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, + 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 {} " + " with adminMode {}".format(*msg_args), + tango.LogLevel.LOG_WARN) + err_msg = ("On() command can't be issued when State is {} and" + " adminMode is {} ".format(*msg_args)) + + tango.Except.throw_exception("Command not executable", + err_msg, + "On command execution", + tango.ErrSeverity.ERR) + return f(*args, **kwargs) + return admin_mode_check \ No newline at end of file -- GitLab From e5f16d011898876f6bdd06d0cb678da31da86002 Mon Sep 17 00:00:00 2001 From: softir Date: Fri, 3 Jan 2020 12:52:32 +0100 Subject: [PATCH 05/33] AT5-257: Implemented CmdInputArgsCheck decorator to check the On/Off/Standby commands input argument. --- csp-lmc-common/utils/cspcommons.py | 7 ++ csp-lmc-common/utils/decorators.py | 173 +++++++++++++++++++++++++++-- 2 files changed, 168 insertions(+), 12 deletions(-) diff --git a/csp-lmc-common/utils/cspcommons.py b/csp-lmc-common/utils/cspcommons.py index 5af568d..303377e 100644 --- a/csp-lmc-common/utils/cspcommons.py +++ b/csp-lmc-common/utils/cspcommons.py @@ -40,3 +40,10 @@ class ObsState(IntEnum): PAUSED = 4 ABORTED = 5 FAULT = 6 + +@unique +class CmdExecState(IntEnum): + IDLE = 0 + RUNNING = 1 + QUEUED = 2 + diff --git a/csp-lmc-common/utils/decorators.py b/csp-lmc-common/utils/decorators.py index 33edb84..ca27250 100644 --- a/csp-lmc-common/utils/decorators.py +++ b/csp-lmc-common/utils/decorators.py @@ -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): @@ -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", - err_msg, - "On command execution", - tango.ErrSeverity.ERR) + tango.Except.throw_exception("Invalid adminMode value", + err_msg, + "AdminModeCheck decorator", + tango.ErrSeverity.ERR) return f(*args, **kwargs) - return admin_mode_check \ No newline at end of file + 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 -- GitLab From 0610a340e8467fe33ae768c7043847a09560fdcd Mon Sep 17 00:00:00 2001 From: softir Date: Fri, 3 Jan 2020 12:58:51 +0100 Subject: [PATCH 06/33] AT5-257: Unnique thread function to issue and control power commands execution on sub-elements.CSP.LMC issues sequentially power commands on sub-element. Defined the reservedSearchBeamIDs attribute as Image type attr. Implemented TANGO attributes to signal timeout conditions during power commands execution. Implemented read methods for all TANGO attributes. --- csp-lmc-common/csp-lmc-common/CspMaster.py | 651 +++++++++++---------- 1 file changed, 338 insertions(+), 313 deletions(-) diff --git a/csp-lmc-common/csp-lmc-common/CspMaster.py b/csp-lmc-common/csp-lmc-common/CspMaster.py index 5301705..b3b91df 100644 --- a/csp-lmc-common/csp-lmc-common/CspMaster.py +++ b/csp-lmc-common/csp-lmc-common/CspMaster.py @@ -46,18 +46,12 @@ file_path = os.path.dirname(os.path.abspath(__file__)) utils_path = os.path.abspath(os.path.join(file_path, os.pardir)) + "/utils" sys.path.insert(0, utils_path) import decorators -from decorators import AdminModeCheck +from decorators import AdminModeCheck, CmdInputArgsCheck import cspcommons -from cspcommons import HealthState, AdminMode, ObsState +from cspcommons import HealthState, AdminMode, ObsState, CmdExecState import release # PROTECTED REGION END# //CspMaster.add_path -@unique -class CmdExecState(IntEnum): - IDLE = 0 - RUNNING = 1 - QUEUED = 2 - __all__ = ["CspMaster", "main"] @@ -121,14 +115,15 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): try: if dev_name in self._se_fqdn: if evt.attr_value.name.lower() == "state": - print("Received event on {} value {}".format(evt.attr_value.name, - evt.attr_value.value)) + print("{}: received event on {} value {}".format(dev_name, + evt.attr_value.name, + evt.attr_value.value)) 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": - print("device: {} value {}".format(dev_name,evt.attr_value.value )) + print("device: {} adminMode value {}".format(dev_name,evt.attr_value.value )) self._se_admin_mode[dev_name] = evt.attr_value.value else: log_msg = ("Attribute {} not still " @@ -157,30 +152,16 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): if item.reason == "API_EventTimeout": self._se_state[dev_name] = tango.DevState.UNKNOWN self._se_health_state[dev_name] = HealthState.UNKNOWN - self._se_state[dev_name] = tango.DevState.OFF + # if the device is executing the shutdown the state is set to + # OFF + if self._se_cmd_execution_state[dev_name]['off'] == CmdExecState.RUNNING: + 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) - def _timeout_handler_cb(self, *device_list, **dict_args): - print("Timeout expired!!") - cmd_expired = dict_args['cmd_name'] - self._cmd_execution_state[cmd_expired] = CmdExecState.IDLE - if cmd_expired == 'on': - print("timeout_handler is on alive?", self._on_cmd_thread.is_alive()) - for fqdn in device_list: - try: - if self._se_timeout_expired[fqdn][cmd_expired]: - print("Timeout expired on device {} for command {} execution".format(fqdn, cmd_expired)) - # to be sure, reset the command state - if self._se_cmd_execution_state[fqdn][cmd_expired] == CmdExecState.RUNNING: - self._se_cmd_execution_state[fqdn][cmd_expired] == CmdExecState.IDLE - except KeyError as key_err: - err_msg = "No key {} found in input args".format(str(key_err)) - self.dev_logging(err_msg, tango.LogLevel.LOG_ERROR) - - def _cmd_progress_change_evt_cb(self, evt): + def _attributes_change_evt_cb(self, evt): """ Class protected callback function. Retrieve the value of the sub-element xxxCommandProgress attribute @@ -191,15 +172,22 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): :return: None """ + dev_name = evt.device.dev_name() if not evt.err: - dev_name = evt.device.dev_name() try: - if dev_name in self._se_fqdn: - # get the command name (on/off/standby) from the attribute name - # (onCommandProgress/offCommandProgress/standbyCommandProgress)) - # removing the last 15 chars - cmd_name = evt.attr_value.name[:-15] - self._se_cmd_progress[dev_name][cmd_name] = evt.attr_value.value + if evt.attr_value.name.lower().find("commandprogress") > -1: + if dev_name in self._se_fqdn: + # get the command name (on/off/standby) from the attribute name + # (onCommandProgress/offCommandProgress/standbyCommandProgress)) + # removing the last 15 chars + cmd_name = evt.attr_value.name[:-15] + self._se_cmd_progress[dev_name][cmd_name] = evt.attr_value.value + elif evt.attr_value.name.lower() == "onCmdTimeoutExpired": + self._se_timeout_expired[dev_name]['on'] = True + elif evt.attr_value.name.lower() == "offCmdTimeoutExpired": + self._se_timeout_expired[dev_name]['off'] = True + elif evt.attr_value.name.lower() == "standbyCmdTimeoutExpired": + self._se_timeout_expired[dev_name]['standby'] = True else: log_msg = ("Unexpected change event for" " attribute: {}".format(str(evt.attr_name))) @@ -289,6 +277,8 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # 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]) + if self._admin_mode in [AdminMode.OFFLINE, AdminMode.NOTFITTED, AdminMode.RESERVED]: + self.set_state[tango.DevState.DISABLE] def _update_csp_health_state(self): """ @@ -419,121 +409,166 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): self.dev_logging(msg, tango.LogLevel.LOG_ERROR) return False return True - - def _issue_on_command(self, device_list): + + def _issue_power_command(self, device_list, **args_dict): """ - Target function called by the _on_cmd_thread run() method. + Target function called when the command threads start. + The On, Standby and Off methods issue the command on the sub-element devices + in a separate thread. + Information about the command to execute and the list of devices are passed as + argument when the target function is called' - :param subelem_list: argument tuple passed when the target function is called. - subelem_list[0]: the FQDN of the device executing the ON commmand - subelem_list[1]: the device waiting to execute the ON command. + :param device_list: tuple with the FQDN of the sub-element devices + args_dict: dictionary with information about the command to execute. + The dictionary keys are: + - cmd_name : the TANGO command name to execute + - attr_name: the corresponding command progress attribute to subscribe + - dev_state: the expected finale state for the device transition """ - print("_issue_on_command:",device_list) #TODO: order the list alphabetically so that the CBF is always the first element to start + # the TANGO command to execute + tango_cmd_name = 0 + # the command progress attribute to check subscription + cmd_progress_attr = 0 + # the sub-element device state after successful transition + dev_successful_state = 0 + try: + tango_cmd_name = args_dict['cmd_name'] + cmd_progress_attr = args_dict['attr_name'] + dev_successful_state = args_dict['dev_state'] + except KeyError as key_err: + print("No key: {}".format(str(key_err))) + # reset the CSP and sub-element running flags + self._cmd_execution_state = CmdExecState.IDLE + for device in device_list: + for k, _ in self._se_cmd_execution_state[device].values(): + self._se_cmd_execution_state[device][k] = CmdExecState.IDLE + return + # tango_cmd_name: is the TANGO command name with the capital letter + # In the dictionary keys, is generally used the command name in lower letters + cmd_name = tango_cmd_name.lower() + print("cmd_name: {} attr_name: {} dev_state: {}".format(cmd_name, + cmd_progress_attr, + dev_successful_state)) num_of_failed_device = 0 - self._num_dev_completed_task['on'] = 0 - self._list_dev_completed_task['on'] = [] - self._cmd_progress['on'] = 0 + self._num_dev_completed_task[cmd_name] = 0 + self._list_dev_completed_task[cmd_name] = [] + self._cmd_progress[cmd_name] = 0 # sub-element command execution measured time se_cmd_duration_measured = defaultdict(lambda:defaultdict(lambda:0)) # timer is started outside the for loop: it has to start only once. - if not self._cmd_timer.is_alive(): - print("Starting timer!!!") - self._cmd_timer.start() - else: - print("Timer thread is running") # loop on the devices and power-on them sequentially for device in device_list: - print("device {} initial state for on command {}".format(device, - self._se_cmd_execution_state[device]['on'])) - self._se_cmd_execution_state[device]['on'] = CmdExecState.RUNNING - se_cmd_duration_measured[device]['on'] = 0 + # set the sub-element command execution flag + self._se_cmd_execution_state[device][cmd_name] = CmdExecState.RUNNING + se_cmd_duration_measured[device][cmd_name] = 0 try: device_proxy = self._se_proxies[device] - print("Issue asynch command on device {}:".format(device)) + print("Issue asynch command {} on device {}:".format(cmd_name, device)) - # Note: if the command ends on the sub-element, the _cmd_ended_cb callback is called. This callback - # sets the sub-element execution state to IDLE if an exception is caught during the execution. - # on the sub-element. In both case the command_state = IDLE is right. - # Note: If the command is already running on the sub-element, this device has to return without - # throwing any exception. In this case the current method enters the while loop and the execution - # of the sub-element command is tracked in the right way. - device_proxy.command_inout_asynch('On', self._cmd_ended_cb) - print("device {} excution state {}".format(device,self._se_cmd_execution_state[device]['on'])) + # Note: if the command ends on the sub-element, the _cmd_ended_cb callback + # is called.This callback sets the sub-element execution state to IDLE if + # an exception is caught during the execution on the sub-element. In both + # cases the command_state = IDLE is right. + # Note: If the command is already running on the sub-element, this device has + # to return without throwing any exception. In this case the current method enters + # the while loop and the execution of the sub-element command is tracked in the + # right way. + device_proxy.command_inout_asynch(tango_cmd_name, self._cmd_ended_cb) # register the starting time for the command self._se_cmd_starting_time[device] = time.time() # loop on the device until the State changes to ON or a timeout occurred + print("Device {} State {} expected value {}".format(device, self._se_state[device], dev_successful_state)) while True: - if self._se_state[device] == tango.DevState.ON: - print("Command On ended with success on device {}.".format(device)) - self.dev_logging("Command On executed on device {}.".format(device), + if self._se_state[device] == dev_successful_state: + print("Command {} ended with success on device {}.".format(cmd_name, + device)) + self.dev_logging("Command {} executed on device {}.".format(cmd_name,device), tango.LogLevel.LOG_INFO) # update the list and number of device that completed the task - self._num_dev_completed_task['on'] += 1 - self._list_dev_completed_task['on'].append(device) + self._num_dev_completed_task[cmd_name] += 1 + self._list_dev_completed_task[cmd_name].append(device) # reset the value of the attribute reporting the execution state of # the command - self._se_cmd_execution_state[device]['on'] = CmdExecState.IDLE - se_cmd_duration_measured[device]['on'] = time.time() - self._se_cmd_starting_time[device] + self._se_cmd_execution_state[device][cmd_name] = CmdExecState.IDLE + se_cmd_duration_measured[device][cmd_name] = (time.time() + - self._se_cmd_starting_time[device]) # calculate the real execution time for the command - self._cmd_duration_measured['on'] += se_cmd_duration_measured[device]['on'] - print("measured duration:", se_cmd_duration_measured[device]['on']) - print("evt oncommandprogress:", self._se_event_id[device]['oncommandprogress'] ) - #if self._se_event_id[device]['oncommandprogress'] == 0: - # if the sub-element onCommandProgress attribute is not subscribed - # the attribute tracking the command execution is set to 100 - self._se_cmd_progress[device]['on'] = 100 - # command success: exit from the wait loop and issue the On command - # on the next device + self._cmd_duration_measured[cmd_name] += se_cmd_duration_measured[device][cmd_name] + print("measured duration:", se_cmd_duration_measured[device][cmd_name]) + # The event_id dictionary uses the attribute name in lower case letters as + # dictionary key + print("evt {}: {}".format(cmd_progress_attr, + self._se_event_id[device][cmd_progress_attr.lower()])) + self._se_cmd_progress[device][cmd_name] = 100 + # command success: exit from the wait loop and issue the command + # on the next device of the list + break + # check for other sub-element device State values + if self._se_state[device] in [tango.DevState.FAULT, tango.DevState.ALARM]: + self._se_cmd_execution_state[device][cmd_name] = CmdExecState.IDLE + msg = ("Device {} is {}}".format(device, self.get_status())) + print(msg) + self.dev_logging(msg, tango.LogLevel.LOG_WARN) + num_of_failed_device += 1 break - # check if sub-element command ended for errors (call to cmd_ended_cb and - # execution state set to IDLE) - if self._se_cmd_execution_state[device]['on'] == CmdExecState.IDLE: + # check if sub-element command ended throwing an exception: in this case the + # 'cmd_ended_cb' callback it is invoked. The callback log the exceptionand + # sets the sub-element execution state to IDLE) + if self._se_cmd_execution_state[device][cmd_name] == CmdExecState.IDLE: # execution ended for this sub-element, skip to the next one num_of_failed_device += 1 - print("Inside while loop num_failed =", num_of_failed_device) break - # check for timeout event + # check for timeout event. A timeout event can be detected in two ways: + # 1- the sub-element implements the 'onTimeoutExpired' attribute configured + # for change event + # 2- the CspMaster 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 timeout flag is set + # to True. + # 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._se_cmd_starting_time[device] - if elapsed_time > self._se_cmd_duration_expected[device]['on']: + if (elapsed_time > self._se_cmd_duration_expected[device][cmd_name] or + self._se_timeout_expired[device][cmd_name] == True): + msg = ("Timeout executing {} command on device {}".format(cmd_name, device)) + print(msg) + self.dev_logging(msg, tango.LogLevel.LOG_WARN) num_of_failed_device += 1 - self._se_timeout_expired[device]['on'] = True - self._se_cmd_execution_state[device]['on'] = CmdExecState.IDLE + self._se_timeout_expired[device][cmd_name] = True + self._se_cmd_execution_state[device][cmd_name] = CmdExecState.IDLE # if the CBF command timeout expires, the CSP power-on is stopped # TODO: verify if this behavior conflicts with ICD - if device.find("cbf"): + print("elapsed_time:{} device {}".format(elapsed_time, device)) + if device.find("cbf") > -1: self.dev_logging("CBF Timeout during power-on!!! Exit", tango.LogLevel.LOG_ERROR) - # remove the timer - if self._cmd_timer.is_alive(): - self._cmd_timer.cancel() - self._cmd_timer.join() return # timeout on the sub-element, skip to the next device break time.sleep(1) # update the progress counter inside the loop taking into account the number of devices # executing the command - self._cmd_progress['on'] += self._se_cmd_progress[device]['on']/len(device_list) + self._cmd_progress[cmd_name] += self._se_cmd_progress[device][cmd_name]/len(device_list) # end of the while loop # update the progress counter at the end of the loop - self._cmd_progress['on'] += self._se_cmd_progress[device]['on']/len(device_list) - if len(device_list) == self._num_dev_completed_task['on'] + num_of_failed_device: + self._cmd_progress[cmd_name] += self._se_cmd_progress[device][cmd_name]/len(device_list) + if len(device_list) == self._num_dev_completed_task[cmd_name] + num_of_failed_device: print("All devices have been handled!") - # end of the command: cancel the timer, and reset the global command state - # attribute - print("Removing timer") - self._cmd_timer.cancel() - self._cmd_timer.join() - self._cmd_execution_state['on'] = CmdExecState.IDLE - print("is cmd timer alive?:", self._cmd_timer.is_alive()) + # end of the command: the command has been issued on all the sub-element devices + # reset the execution flag for the CSP + self._cmd_execution_state[cmd_name] = CmdExecState.IDLE + self._last_executed_command = cmd_name + # if one or more sub-elements go in timeout, se the CSP timeout flag to True + if num_of_failed_device > 0: + self._timeout_expired[cmd_name] = True except KeyError as key_err: msg = "No key {} found".format(str(key_err)) self.dev_logging(msg, tango.LogLevel.LOG_WARN) except tango.DevFailed as df: + # It should not happen! Verify msg = "Failure reason: {} Desc: {}".format(str(df.args[0].reason), str(df.args[0].desc)) self.dev_logging(msg, tango.LogLevel.LOG_ERROR) - # PROTECTED REGION END # // CspMaster.class_protected_methods # ----------------- @@ -686,7 +721,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): doc=("Report the measured duration (in sec.) of the Standby command"), ) - cbfCmdTimeoutExpired = attribute( + onCmdTimeoutExpired = attribute( dtype='DevBoolean', label="CBF command timeout flag", polling_period=2000, @@ -694,7 +729,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): " CBF Sub-element"), ) - pssCmdTimeoutExpired = attribute( + offCmdTimeoutExpired = attribute( dtype='DevBoolean', label="PSS command timeout flag", polling_period=2000, @@ -702,7 +737,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): "PSS Sub-element"), ) - pstCmdTimeoutExpired = attribute( + standbyCmdTimeoutExpired = attribute( dtype='DevBoolean', label="PST command timeout flag", polling_period=2000, @@ -715,7 +750,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): label="CBF status", polling_period=3000, doc=("The CBF sub-element Device State. Allowed values are ON, STANDBY, OFF, " - "DISABLED, ALARM, FAULT, UNKNOWN"), + "DISABLE, ALARM, FAULT, UNKNOWN"), ) cspPssState = attribute( @@ -723,7 +758,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): label="PSS status", polling_period=3000, doc=("The PSS sub-element Device State. Allowed values are ON, STANDBY, OFF," - " DISABLED, ALARM, FAULT, UNKNOWN"), + " DISABLE, ALARM, FAULT, UNKNOWN"), ) cspPstState = attribute( @@ -731,7 +766,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): label="PST status", polling_period=3000, doc=("The PST sub-element Device State. Allowed values are ON, STANDBY,OFF," - " DISABLED, ALARM, FAULT, UNKNOWN"), + " DISABLE, ALARM, FAULT, UNKNOWN"), ) cspCbfHealthState = attribute( @@ -971,8 +1006,8 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): ) reservedSearchBeamIDs = attribute( - dtype=('DevUShort',), - max_dim_x=16, + dtype=(('DevUShort',),), + max_dim_x=16, max_dim_y=1000, label="IDs of reserved SeachBeam Capabilities", doc="Report the list of SearchBeam Capability IDs reserved for each sub-array", ) @@ -1027,7 +1062,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # 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_state = defaultdict(lambda: tango.DevState.DISABLE) self._se_health_state = defaultdict(lambda: HealthState.UNKNOWN) self._se_admin_mode = defaultdict(lambda: AdminMode.NOTFITTED) @@ -1057,13 +1092,6 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # 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 = {} - self._cmd_timer = 0 - # _se_cmd_execution_state: implement the execution state of a long-running # command for each sub-element. # implemented as a nested default dictionary: @@ -1071,7 +1099,6 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # values: defaut dictionary (keys: command name, values: command state) self._se_cmd_execution_state = defaultdict(lambda: defaultdict(lambda: CmdExecState.IDLE)) - # _cmd_execution_state: implement the execution state of a long-running # command for the whole CSP. Setting this attribute prevent the execution # of the same command while it is already running. @@ -1087,26 +1114,10 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # 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 = {} - self._on_cmd_thread = 0 - # _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 = {} - self._off_cmd_thread = 0 - - # _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 = {} - self._standby_cmd_thread = 0 + # _command_thread: thread for the command execution + # keys: the command name('on, 'off'...) + # values: thread instance + self._command_thread = {} # _se_cmd_progress: for each sub-element report the execution progress # of a long-running command @@ -1126,7 +1137,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # 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)) + self._se_cmd_duration_expected = defaultdict(lambda: defaultdict(lambda: 20)) # _cmd_duration_expected: store the duration (in sec.) configured for # a long-running command @@ -1143,18 +1154,26 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): self._cmd_duration_measured = defaultdict(lambda: 0) # _se_timeout_expired: report the timeout flag - # Implemented as a default dictionary + # Implemented as a nested default dictionary # keys: FQDN # values: default dictionary (keys: command name, values: True/False)) self._se_timeout_expired = defaultdict(lambda: defaultdict(lambda: False)) + # _timeout_expired: report the timeout flag + # Implemented as a dictionary + # keys: command name ('on', 'off', 'standby'..) + # values: True/False)) + self._timeout_expired = 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: []) - + # the last executed command + self._last_executed_command = "" + # _num_dev_completed_task: for each long-running command report the number # of subordinate components that completed the task # Implemented as a dictionary @@ -1164,15 +1183,17 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # initialize CSP SearchBeam, TimingBeam and VlbiBeam Capabilities State, # healthState, adminMode and obsState - self._search_beam_state = defaultdict(lambda: tango.DevState.DISABLED) + # keys: device FQDN + # values: the attribute value + self._search_beam_state = defaultdict(lambda: tango.DevState.DISABLE) self._search_beam_health_state = defaultdict(lambda: HealthState.UNKNOWN) self._search_beam_admin_mode = defaultdict(lambda: tango.DevState.NOTFITTED) self._search_beam_obs_state = defaultdict(lambda: ObsState.IDLE) - self._timing_beam_state = defaultdict(lambda: tango.DevState.DISABLED) + self._timing_beam_state = defaultdict(lambda: tango.DevState.DISABLE) self._timing_beam_health_state = defaultdict(lambda: HealthState.UNKNOWN) self._timing_beam_admin_mode = defaultdict(lambda: tango.DevState.NOTFITTED) self._timing_beam_obs_state = defaultdict(lambda: ObsState.IDLE) - self._vlbi_beam_state = defaultdict(lambda: tango.DevState.DISABLED) + self._vlbi_beam_state = defaultdict(lambda: tango.DevState.DISABLE) self._vlbi_beam_health_state = defaultdict(lambda: HealthState.UNKNOWN) self._vlbi_beam_admin_mode = defaultdict(lambda: tango.DevState.NOTFITTED) self._vlbi_beam_obs_state = defaultdict(lambda: ObsState.IDLE) @@ -1277,6 +1298,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): :return: None """ # PROTECTED REGION ID(CspMaster.adminMode_write) ENABLED START # + print("set admin mode: fqdn {}".format(self._se_fqdn)) for fqdn in self._se_fqdn: try: if self._is_se_device_running(fqdn): @@ -1289,6 +1311,12 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): self.dev_logging(log_msg, int(tango.LogLevel.LOG_ERROR)) #TODO: what happens if one sub-element fails? self._admin_mode = value + if self._admin_mode in [AdminMode.OFFLINE, AdminMode.NOTFITTED, AdminMode.RESERVED]: + self.set_state(tango.DevState.DISABLE) + else: + if self.get_state() == tango.DevState.DISABLE: + #STANDBY or ON how can be determined?? + self.set_state(tango.DevState.STANDBY) # PROTECTED REGION END # // CspMaster.adminMode_write def read_onCommandProgress(self): @@ -1363,23 +1391,23 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): return int(self._cmd_duration_measured['standby']) # PROTECTED REGION END # // CspMaster.standbyCmdDurationMeasured_read - def read_cbfCmdTimeoutExpired(self): - # PROTECTED REGION ID(CspMaster.cbfOnCmdTimeoutExpired_read) ENABLED START # - """Return the cbfCmdTimeoutExpired attribute.""" - return (True in self._se_timeout_expired[self.CspCbf].values()) + def read_onCmdTimeoutExpired(self): + # PROTECTED REGION ID(CspMaster.onCmdTimeoutExpired_read) ENABLED START # + """Return the onCmdTimeoutExpired attribute.""" + return self._timeout_expired['on'] # PROTECTED REGION END # // CspMaster.cbfOnCmdTimeoutExpired_read - def read_pssCmdTimeoutExpired(self): + def read_offCmdTimeoutExpired(self): # PROTECTED REGION ID(CspMaster.pssOnCmdTimeoutExpired_read) ENABLED START # - """Return the pssCmdTimeoutExpired attribute.""" - return (True in self._se_timeout_expired[self.CspPss].values()) + """Return the offCmdTimeoutExpired attribute.""" + return self._timeout_expired['off'] # PROTECTED REGION END # // CspMaster.pssOnCmdTimeoutExpired_read - def read_pstCmdTimeoutExpired(self): - # PROTECTED REGION ID(CspMaster.pstOnCmdTimeoutExpired_read) ENABLED START # - """Return the pstOnCmdTimeoutExpired attribute.""" - return (True in self._se_timeout_expired[self.CspPst].values()) - # PROTECTED REGION END # // CspMaster.pstOnCmdTimeoutExpired_read + def read_standbyCmdTimeoutExpired(self): + # PROTECTED REGION ID(CspMaster.standbyCmdTimeoutExpired_read) ENABLED START # + """Return the standbyCmdTimeoutExpired attribute.""" + return self._timeout_expired['standby'] + # PROTECTED REGION END # // CspMaster.standbyCmdTimeoutExpired_read def read_cspCbfState(self): # PROTECTED REGION ID(CspMaster.cspCbfState_read) ENABLED START # @@ -1499,16 +1527,14 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): pass # PROTECTED REGION END # // CspMaster.cspPstAdminMode_write - def read_unassignedSearchBeamsNum(self): - # PROTECTED REGION ID(CspMaster.unassignedSearchBeamsNum_read) ENABLED START # - """Return the unassignedSearchBeamsNum attribute.""" - return 0 - # PROTECTED REGION END # // CspMaster.unassignedSearchBeamsNum_read - def read_numOfDevCompletedTask(self): # PROTECTED REGION ID(CspMaster.numOfDevCompletedTask_read) ENABLED START # """Return the numOfDevCompletedTask attribute.""" - return 0 + print(self._num_dev_completed_task) + if not self._last_executed_command: + return 0 + return self._num_dev_completed_task[self._last_executed_command] + # PROTECTED REGION END # // CspMaster.numOfDevCompletedTask_read def read_availableCapabilities(self): @@ -1520,19 +1546,19 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): def read_reportSearchBeamState(self): # PROTECTED REGION ID(CspMaster.reportSearchBeamState_read) ENABLED START # """Return the reportSearchBeamState attribute.""" - return (tango.DevState.UNKNOWN,) + return self._search_beam_state.values() # PROTECTED REGION END # // CspMaster.reportSearchBeamState_read def read_reportSearchBeamHealthState(self): # PROTECTED REGION ID(CspMaster.reportSearchBeamHealthState_read) ENABLED START # """Return the reportSearchBeamHealthState attribute.""" - return (0,) + return self._search_beam_health_state.values() # PROTECTED REGION END # // CspMaster.reportSearchBeamHealthState_read def read_reportSearchBeamAdminMode(self): # PROTECTED REGION ID(CspMaster.reportSearchBeamAdminMode_read) ENABLED START # """Return the reportSearchBeamAdminMode attribute.""" - return (0,) + return self._search_beam_admin_mode.values() # PROTECTED REGION END # // CspMaster.reportSearchBeamAdminMode_read def write_reportSearchBeamAdminMode(self, value): @@ -1544,19 +1570,19 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): def read_reportTimingBeamState(self): # PROTECTED REGION ID(CspMaster.reportTimingBeamState_read) ENABLED START # """Return the reportTimingBeamState attribute.""" - return (tango.DevState.UNKNOWN,) + return self._timing_beam_state.values() # PROTECTED REGION END # // CspMaster.reportTimingBeamState_read def read_reportTimingBeamHealthState(self): # PROTECTED REGION ID(CspMaster.reportTimingBeamHealthState_read) ENABLED START # """Return the reportTimingBeamHealthState attribute.""" - return (0,) + return self._timing_beam_health_state.values() # PROTECTED REGION END # // CspMaster.reportTimingBeamHealthState_read def read_reportTimingBeamAdminMode(self): # PROTECTED REGION ID(CspMaster.reportTimingBeamAdminMode_read) ENABLED START # """Return the reportTimingBeamAdminMode attribute.""" - return (0,) + return self._timing_beam_admin_mode.values() # PROTECTED REGION END # // CspMaster.reportTimingBeamAdminMode_read def write_reportTimingBeamAdminMode(self, value): @@ -1568,19 +1594,19 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): def read_reportVlbiBeamState(self): # PROTECTED REGION ID(CspMaster.reportVlbiBeamState_read) ENABLED START # """Return the reportVlbiBeamState attribute.""" - return (tango.DevState.UNKNOWN,) + return self._vlbi_beam_state.values() # PROTECTED REGION END # // CspMaster.reportVlbiBeamState_read def read_reportVlbiBeamHealthState(self): # PROTECTED REGION ID(CspMaster.reportVlbiBeamHealthState_read) ENABLED START # """Return the reportVlbiBeamHealthState attribute.""" - return (0,) + return self._vlbi_beam_health_state.values() # PROTECTED REGION END # // CspMaster.reportVlbiBeamHealthState_read def read_reportVlbiBeamAdminMode(self): # PROTECTED REGION ID(CspMaster.reportVlbiBeamAdminMode_read) ENABLED START # """Return the reportVlbiBeamAdminMode attribute.""" - return (0,) + return self._vlbi_beam_admin_mode.values() # PROTECTED REGION END # // CspMaster.reportVlbiBeamAdminMode_read def write_reportVlbiBeamAdminMode(self, value): @@ -1592,79 +1618,89 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): def read_cspSubarrayAddresses(self): # PROTECTED REGION ID(CspMaster.cspSubarrayAddresses_read) ENABLED START # """Return the cspSubarrayAddresses attribute.""" - return ('',) + return self.CspSubarrays # PROTECTED REGION END # // CspMaster.cspSubarrayAddresses_read def read_searchBeamAddresses(self): # PROTECTED REGION ID(CspMaster.searchBeamAddresses_read) ENABLED START # """Return the searchBeamAddresses attribute.""" - return ('',) + return self.SearchBeams # PROTECTED REGION END # // CspMaster.searchBeamAddresses_read def read_timingBeamAddresses(self): # PROTECTED REGION ID(CspMaster.timingBeamAddresses_read) ENABLED START # """Return the timingBeamAddresses attribute.""" - return ('',) + return self.TimingBeams # PROTECTED REGION END # // CspMaster.timingBeamAddresses_read def read_vlbiBeamAddresses(self): # PROTECTED REGION ID(CspMaster.vlbiBeamAddresses_read) ENABLED START # """Return the vlbiBeamAddresses attribute.""" - return ('',) + return self.VlbiBeams # PROTECTED REGION END # // CspMaster.vlbiBeamAddresses_read def read_searchBeamMembership(self): # PROTECTED REGION ID(CspMaster.searchBeamMembership_read) ENABLED START # """Return the searchBeamMembership attribute.""" - return (0,) + return self._search_beam_membership # PROTECTED REGION END # // CspMaster.searchBeamMembership_read def read_timingBeamMembership(self): # PROTECTED REGION ID(CspMaster.timingBeamMembership_read) ENABLED START # """Return the timingBeamMembership attribute.""" - return (0,) + return self._timing_beam_membership # PROTECTED REGION END # // CspMaster.timingBeamMembership_read def read_vlbiBeamMembership(self): # PROTECTED REGION ID(CspMaster.vlbiBeamMembership_read) ENABLED START # """Return the vlbiBeamMembership attribute.""" - return (0,) + return self._vlbi_beam_membership # PROTECTED REGION END # // CspMaster.vlbiBeamMembership_read def read_unassignedSearchBeamIDs(self): # PROTECTED REGION ID(CspMaster.unassignedSearchBeamIDs_read) ENABLED START # """Return the unassignedSearchBeamIDs attribute.""" - return (0,) + return self._unassigned_search_beam_id # PROTECTED REGION END # // CspMaster.unassignedSearchBeamIDs_read + + def read_unassignedSearchBeamsNum(self): + # PROTECTED REGION ID(CspMaster.unassignedSearchBeamsNum_read) ENABLED START # + """Return the unassignedSearchBeamsNum attribute.""" + return len(self._unassigned_search_beam_id) + # PROTECTED REGION END # // CspMaster.unassignedSearchBeamsNum_read def read_reservedSearchBeamIDs(self): # PROTECTED REGION ID(CspMaster.reservedSearchBeamIDs_read) ENABLED START # """Return the reservedSearchBeamIDs attribute.""" - return (0,) + return self._reserved_search_beam_id # PROTECTED REGION END # // CspMaster.reservedSearchBeamIDs_read def read_reservedSearchBeamNum(self): # PROTECTED REGION ID(CspMaster.reservedSearchBeamNum_read) ENABLED START # """Return the reservedSearchBeamNum attribute.""" - return (0,) + return len(self._reserved_search_beam_id) # PROTECTED REGION END # // CspMaster.reservedSearchBeamNum_read def read_unassignedTimingBeamIDs(self): # PROTECTED REGION ID(CspMaster.unassignedTimingBeamIDs_read) ENABLED START # """Return the unassignedTimingBeamIDs attribute.""" - return (0,) + return self.unassignedTimingBeamIDs # PROTECTED REGION END # // CspMaster.unassignedTimingBeamIDs_read def read_unassignedVlbiBeamIDs(self): # PROTECTED REGION ID(CspMaster.unassignedVlbiBeamIDs_read) ENABLED START # """Return the unassignedVlbiBeamIDs attribute.""" - return (0,) + return self._unassigned_vlbi_beam_id # PROTECTED REGION END # // CspMaster.unassignedVlbiBeamIDs_read def read_listOfDevCompletedTask(self): # PROTECTED REGION ID(CspMaster.listOfDevCompletedTask_read) ENABLED START # """Return the listOfDevCompletedTask attribute.""" - return ('',) + #TO IMPLEMENT + # report the list of the devices that completed the last executed task + if not self._last_executed_command: + return ('',) + return self._list_dev_completed_task[self._last_executed_command] # PROTECTED REGION END # // CspMaster.listOfDevCompletedTask_read # -------- @@ -1686,7 +1722,6 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): return False return True - @command( dtype_in='DevVarStringArray', doc_in=("If the array length is 0, the command applies to the whole CSP Element." @@ -1694,7 +1729,9 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): "the CSP SubElement to switch ON."), ) @DebugIt() - @AdminModeCheck() + @AdminModeCheck('On') + #@CmdInputArgsCheck(cmd_name = "on", attr_name = "onCommnadProgress") + @CmdInputArgsCheck("onCommandProgress", "onCmdTimeoutExpired", cmd_name = "on") def On(self, argin): # PROTECTED REGION ID(CspMaster.On) ENABLED START # """ @@ -1709,6 +1746,8 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): waits for the first sub-element (CBF) to complete the transition, then issues the command to the second sub-element (e.g. PST), waits for PST to complete and then issues command for PSS. + Command is forwarded to the sub-element devices specified in the input list, + only if their adminMode is ONLINE or MAINTENANCE. :param argin: The list of sub-element FQDNs to switch-on or an empty list to switch-on the whole @@ -1719,105 +1758,27 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): CSP SubElement to switch ON. :type: 'DevVarStringArray' :return: None - :raises: *tango.DevFailed* exception.\n - The exception is thrown when the *On* command fails - for CBF sub-element or there are no DeviceProxy providing interface - to the CSP sub-elements or the AdminMode is not correct. + :raises: *tango.DevFailed* exception when: + - the CSP adminMode is not correct + - one or more sub-element devices are already running a power command + - there is no DeviceProxy providing interface to the CBF sub-element """ - device_list = [] - num_of_devices = len(argin) - if num_of_devices == 0: - # no input argument -> switch on all sub-elements - num_of_devices = len(self._se_fqdn) - device_list = self._se_fqdn - else: - if num_of_devices > len(self._se_fqdn): - # too many devices specified-> log the warning but go on - # with command execution - self.dev_logging("Too many input parameters", tango.LogLevel.LOG_WARN) - device_list = argin - - print(device_list) - # If a sub-element device is already executing a power command throws an - # exception (power commands have to be executed sequentially) - # TODO: What to do if the required device is performing a software upgrade or is changing its - # adminMode? How can CSP.LMC detect this condition? - if CmdExecState.RUNNING in self._cmd_execution_state.values(): - print("On command is running!!") - # Note: need to check in the list of sub-elements, not in the device_list sent to the command!!! - for device in self._se_fqdn: - for cmd_name, cmd_state in self._se_cmd_execution_state[device].items(): - if cmd_state in [CmdExecState.RUNNING, CmdExecState.QUEUED]: - msg = ("Device {} is already executing the {} command" - "Command state: {}".format(device, cmd_name, - self._se_cmd_execution_state[device][cmd_name])) - print(msg) - # throw an exception - return - command_timeout = 0 - device_to_remove = [] - for device in device_list: - print("processin 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 self._is_se_device_running(device) or - self._se_admin_mode[device] not in [AdminMode.ONLINE, - AdminMode.MAINTENANCE]): - device_to_remove.append(device) - print("Devices to remove from the list:", device_to_remove) - continue - self._se_cmd_execution_state[device]['on'] = CmdExecState.QUEUED - # reset the timeout attribute content - for cmd_name, _ in self._se_timeout_expired[device].items(): - if self._se_timeout_expired[device][cmd_name]: - msg = ("Device {} expired on command {}." - " Clean the timeout counter".format(device, cmd_name)) - print(msg) - self._se_timeout_expired[device][cmd_name] = False - - device_proxy = self._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) - self._se_cmd_duration_expected[device]['on'] = device_proxy.onCmdDurationExpected - except AttributeError as attr_err: - # we get here if the attribute is not implemented - print(str(attr_err)) - try: - if self._se_event_id[device]['oncommandprogress'] == 0: - evt_id = device_proxy.subscribe_event("onCommandProgress", EventType.CHANGE_EVENT, - self._cmd_progress_change_evt_cb, stateless=False) - self._se_event_id[device]['oncommandprogress'] = evt_id - except tango.DevFailed as df: - print(df.args[0].desc) - - # evaluate the total timeout value to execute the whole command - command_timeout += self._se_cmd_duration_expected[device]['on'] - # use the greatest value for the onCommand duration expected. - if command_timeout > self._cmd_duration_expected['on']: - self._cmd_duration_expected['on'] = command_timeout - self.dev_logging("Modified the On command Duration Expected value!!", tango.LogLevel.LOG_INFO) - # remove the 'not running' devices or OFFLINE/RESERVED/NOTFITTED devices from the list - # of devices to power-on - for device in device_to_remove: - device_list.remove(device) - if not device_list: - self.dev_logging("No device to power on", tango.LogLevel.LOG_INFO) - return - # invoke the constructor for the timer thread - self._cmd_timer = threading.Timer(self._cmd_duration_expected['on'], self._timeout_handler_cb, - device_list, {'cmd_name':'on'}) - # invoke the constructor for the command thread - self._on_cmd_thread = threading.Thread(target=self._issue_on_command, name="Thread-On", - args=(device_list,)) - # start the thread + # the input list after decorator examination + device_list = argin + # invoke the constructor for the command thread. + # The target thread function is common to all the invoked commands. Specifc information + # are passed as arguments of the function + # args: the list of sub-element FQDNS + # args_dict: dictionary with the specifif command information + print("ON {} STANDBY {}".format( tango.DevState.ON, tango.DevState.STANDBY)) + args_dict = {'cmd_name':'On', 'attr_name': 'onCommandProgress', 'dev_state': tango.DevState.ON} + self._command_thread['on'] = threading.Thread(target=self._issue_power_command, name="Thread-On", + args=(device_list,), + kwargs=args_dict) + # set the CSP command execution running flag self._cmd_execution_state['on'] = CmdExecState.RUNNING - self._on_cmd_thread.start() + # start the thread + self._command_thread['on'].start() # sleep for a while to let the thread start time.sleep(0.2) # PROTECTED REGION END # // CspMaster.On @@ -1829,24 +1790,60 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): " CSP SubElement to switch OFF."), ) @DebugIt() - @AdminModeCheck() + @AdminModeCheck('Off') + #@CmdInputArgsCheck(cmd_name = "off", attr_name = "offcommandprogress") + @CmdInputArgsCheck("offCommandProgress", "offCmdTimeoutExpired", cmd_name = "off") def Off(self, argin): # PROTECTED REGION ID(CspMaster.Off) ENABLED START # """ - Switch-off the CSP sub-elements specified by the input argument. - If no argument is specified, the command is issued to all the CSP - sub-elements. - - :param argin: + *Class TANGO method* - If the array length is 0, the command applies to the whole CSP Element. - If the array length is > 1, each array element specifies the FQDN of the - CSP SubElement to switch OFF. - :type: 'DevVarStringArray' + Power-down the CSP sub-elements specified by the input argument. If no argument is + specified, the command is issued on all the CSP sub-elements. + The command is executed if the *AdminMode* is ONLINE or *MAINTENANCE*. + If the AdminMode is *OFFLINE*, *NOT-FITTED* or *RESERVED*, the method throws an + exception. + The CSP.LMC commands sub-element transition from STANDBY to OFF sequentially.It + waits for the first sub-element (CBF) to complete the transition, then issues the + command to the second sub-element (e.g. PST), waits for PST to complete and then + issues command for PSS. + Command is forwarded to the sub-element devices specified in the input list, + only if their adminMode is ONLINE or MAINTENANCE. + :param argin: + The list of sub-element FQDNs to power-down or an empty list to + power-off the whole CSP Element. + If the array length is 0, the command applies to the whole CSP + Element. If the array length is > 1, each array element specifies + the FQDN of the CSP SubElement to power-off. + :type: 'DevVarStringArray' :return: None + :raises: *tango.DevFailed* exception when: + - the CSP adminMode is not correct + - one or more sub-element devices are already running a power command + - there is no DeviceProxy providing interface to the CBF sub-element """ - pass + # the input list after decorator examination + device_list = argin + # invoke the constructor for the command thread. + # The target thread function is common to all the invoked commands. Specifc information + # are passed as arguments of the function + # args: the list of sub-element FQDNS + # args_dict: dictionary with the specific command information + args_dict = {'cmd_name':'Off', + 'attr_name': 'offCommandProgress', + 'dev_state': tango.DevState.ON} + + self._command_thread['off'] = threading.Thread(target=self._issue_power_command, name="Thread-Off", + args=(device_list,), + kwargs=args_dict) + # set the CSP command execution running flag + self._cmd_execution_state['off'] = CmdExecState.RUNNING + # start the thread + self._command_thread['off'].start() + # sleep for a while to let the thread start + time.sleep(0.2) + # PROTECTED REGION END # // CspMaster.Off @command( @@ -1856,24 +1853,53 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): "CSP SubElement to put in STANDBY mode."), ) @DebugIt() - @AdminModeCheck() + @AdminModeCheck('Standby') + #@CmdInputArgsCheck(cmd_name = "standby", attr_name = "standbycommandprogress") + @CmdInputArgsCheck("standbyCommandProgress", + "standbyCmdTimeoutExpired", cmd_name = "standby") def Standby(self, argin): # PROTECTED REGION ID(CspMaster.Standby) ENABLED START # """ - - Transit CSP or one or more CSP SubElements from ON/OFF to - STANDBY. - - :param argin: 'DevVarStringArray' - - If the array length is 0, the command applies to the whole - CSP Element. - If the array length is > 1, each array element specifies the FQDN of the - CSP SubElement to put in STANDBY mode. - + Transit CSP or one or more CSP SubElements from ON to *STANDBY*. + The command is executed only if the *AdminMode* is ONLINE or *MAINTENANCE*. + If the AdminMode is *OFFLINE*, *NOT-FITTED* or *RESERVED*, the method throws an + exception. + The CSP.LMC commands sub-element transition from ON to STANDBY sequentially. It + waits for the first sub-element (CBF) to complete the transition, then issues the + command to the second sub-element (e.g. PST), waits for PST to complete and then + issues command for PSS. + Command is forwarded to the sub-element devices specified in the input list, + only if their adminMode is ONLINE or MAINTENANCE. + + :param argin: A list with the FQDN of the sub-elements to set in low-power. + If the array length is 0, the command applies to the whole + CSP Element. If the array length is > 1, each array element + specifies the FQDN of the CSP SubElement to put in STANDBY mode. + :type: 'DevVarStringArray' :return: None + :raises: *tango.DevFailed* exception when: + - the CSP adminMode is not correct + - one or more sub-element devices are already running a power command + - there is no DeviceProxy providing interface to the CBF sub-element """ - pass + device_list = argin + # build the dictionary to pass as argument of the thread target function + # The dictionary keys are: + # cmd_name : the TANGO command name to execute + # attr_name: the corresponding command progress attribute to subscribe + # dev_state: the expected finale state for the device transition + args_dict = {'cmd_name':"Standby", 'attr_name':'standbyCommandProgress', + 'dev_state':tango.DevState.STANDBY} + # invoke the constructor for the command thread + self._command_thread['standby'] = threading.Thread(target=self._issue_power_command, + name="Thread-Standby", + args=(device_list,), + kwargs=args_dict) + # start the thread + self._cmd_execution_state['standby'] = CmdExecState.RUNNING + self._command_thread['standby'].start() + # sleep for a while to let the thread start + time.sleep(0.2) # PROTECTED REGION END # // CspMaster.Standby @command( @@ -1892,7 +1918,6 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # Run server # ---------- - def main(args=None, **kwargs): # PROTECTED REGION ID(CspMaster.main) ENABLED START # return run((CspMaster,), args=args, **kwargs) -- GitLab From 5157669559a9254d82cc76d3123f9e9fd21b5417 Mon Sep 17 00:00:00 2001 From: softir Date: Fri, 3 Jan 2020 13:04:23 +0100 Subject: [PATCH 07/33] AT5-257: Defined the reservedSearchBeamIDs TANGO attribute as Image type. --- csp-lmc-common/pogo/CspMaster.xmi | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/csp-lmc-common/pogo/CspMaster.xmi b/csp-lmc-common/pogo/CspMaster.xmi index 6a5de91..c17dbe5 100644 --- a/csp-lmc-common/pogo/CspMaster.xmi +++ b/csp-lmc-common/pogo/CspMaster.xmi @@ -645,14 +645,6 @@ - - - - - - - - @@ -685,6 +677,14 @@ + + + + + + + + -- GitLab From d431fced9bf68fee651115379aae30b2b2e7e85d Mon Sep 17 00:00:00 2001 From: softir Date: Tue, 7 Jan 2020 15:55:21 +0100 Subject: [PATCH 08/33] AT5-257: memorized attributes read at device initialization. Removed some small bugs. --- csp-lmc-common/csp-lmc-common/CspMaster.py | 124 ++++++++++++++------- 1 file changed, 83 insertions(+), 41 deletions(-) diff --git a/csp-lmc-common/csp-lmc-common/CspMaster.py b/csp-lmc-common/csp-lmc-common/CspMaster.py index b3b91df..05914cc 100644 --- a/csp-lmc-common/csp-lmc-common/CspMaster.py +++ b/csp-lmc-common/csp-lmc-common/CspMaster.py @@ -182,11 +182,11 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # removing the last 15 chars cmd_name = evt.attr_value.name[:-15] self._se_cmd_progress[dev_name][cmd_name] = evt.attr_value.value - elif evt.attr_value.name.lower() == "onCmdTimeoutExpired": + elif evt.attr_value.name.lower() == "oncmdtimeoutexpired": self._se_timeout_expired[dev_name]['on'] = True - elif evt.attr_value.name.lower() == "offCmdTimeoutExpired": + elif evt.attr_value.name.lower() == "offcmdtimeoutexpired": self._se_timeout_expired[dev_name]['off'] = True - elif evt.attr_value.name.lower() == "standbyCmdTimeoutExpired": + elif evt.attr_value.name.lower() == "standbycmdtimeoutexpired": self._se_timeout_expired[dev_name]['standby'] = True else: log_msg = ("Unexpected change event for" @@ -292,23 +292,29 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): """ # The whole CSP HealthState is OK only if: - # - all sub-elements with adminMode OFF-LINE or MAINTENACE are ON AND + # - all sub-elements with adminMode ON-LINE or MAINTENACE are ON AND # - each sub-element HealthState is OK - - admin_on = 0 - health_ok = 0 + admin_fqdn = [fqdn for fqdn, admin_value in self._se_admin_mode.items() + if admin_value in [AdminMode.ONLINE, AdminMode.MAINTENANCE]] + health_fqdn = [fqdn for fqdn, health_value in self._se_health_state.items() + if health_value == HealthState.OK] + state_fqdn = [fqdn for fqdn, state_value in self._se_state.items() + if state_value in [tango.DevState.ON, tango.DevState.STANDBY]] + admin_fqdn.sort() + health_fqdn.sort() + state_fqdn.sort() + # default value to DEGRADED self._health_state = HealthState.DEGRADED - for fqdn in self._se_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 + if self.CspCbf not in admin_fqdn: + if admin_fqdn == health_fqdn == state_fqdn: + self._healthstate = HealthState.OK + elif self._se_health_state[self.CspCbf] in [HealthState.FAILED, + HealthState.UNKNOWN, + HealthState.DEGREADED]: + self._healthstate = self._se_health_state[self.CbfSubarray] + else: + # if CBF is not ONLINE/MAINTENANCE .... + self._healthstate = self._se_health_state[self.CspCbf] return def _connect_to_subelements (self): @@ -321,11 +327,11 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): :return: None """ - # connect to TANGO DB - csp_tango_db = tango.Database() + for fqdn in self._se_fqdn: try: + # DeviceProxy to sub-elements log_msg = "Trying connection to" + str(fqdn) + " device" print(log_msg) self.dev_logging(log_msg, int(tango.LogLevel.LOG_INFO)) @@ -341,6 +347,16 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # subscription of SCM attributes (State, healthState and adminMode). # Note: subscription is performed also for devices not ONLINE or MAINTENANCE. # In this way the CspMaster is able to detect a change in the admin value. + + # if the sub-element device is running,the adminMode is updated here + # adminMode has to be subscribed first so that healthState and State are + # updated accordingly with its value! + 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 + ev_id = device_proxy.subscribe_event("State", EventType.CHANGE_EVENT, self._se_scm_change_event_cb, @@ -352,19 +368,8 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): 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 - # read the sub-element adminMode (memorized) attribute from - # the TANGO DB. - # Note: a memorized attribute has defined the attribute property '__value' - 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]) + + except KeyError as key_err: log_msg = ("No key {} found".format(str(key_err))) self.dev_logging(log_msg, tango.LogLevel.LOG_WARN) @@ -1050,9 +1055,21 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # PROTECTED REGION ID(CspMaster.init_device) ENABLED START # self._build_state = '{}, {}, {}'.format(release.name, release.version, release.description) self._version_id = release.version + # connect to TANGO DB + csp_tango_db = tango.Database() + # read the CSP memorized attributes from the TANGO DB. + # Note: a memorized attribute has defined the attribute + # property '__value' + attribute_properties = csp_tango_db.get_device_attribute_property(self.get_name(), + {'adminMode': ['__value'], + 'storageLoggingLevel': ['__value'], + 'elementLoggingLevel': ['__value'], + 'centralLoggingLevel': ['__value'],}) + # set storage and element logging level self._storage_logging_level = int(tango.LogLevel.LOG_INFO) self._element_logging_level = int(tango.LogLevel.LOG_INFO) + self._central_logging_level = int(tango.LogLevel.LOG_WARN) # set init values for the CSP Element and Sub-element SCM states self.set_state(tango.DevState.INIT) self._health_state = HealthState.OK @@ -1065,10 +1082,21 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): self._se_state = defaultdict(lambda: tango.DevState.DISABLE) self._se_health_state = defaultdict(lambda: HealthState.UNKNOWN) self._se_admin_mode = defaultdict(lambda: AdminMode.NOTFITTED) - + + # build a dictionary with the (attr_name, value) of the memorized attributes + # use memorized atrtibute if present, otherwise the default one + memorized_attr_dict = {attr_name : int(value[0]) for attr_name, db_key in attribute_properties.items() + for key, value in db_key.items() + if key == '__value'} + try: + self._admin_mode = memorized_attr_dict['adminMode'] + self._storage_logging_level = memorized_attr_dict['storageLoggingLevel'] + except KeyError as key_err: + self.dev_logging("Key {} not found".format(key_err), tango.LogLevel.INFO) + # initialize list with CSP sub-element FQDNs self._se_fqdn = [] - #NOTE: + # 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. @@ -1078,7 +1106,22 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): self._se_fqdn.append(self.CspCbf) self._se_fqdn.append(self.CspPss) self._se_fqdn.append(self.CspPst) - + + # read the sub-elements adminMode (memorized) attributes from + # the TANGO DB. + # Note: a memorized attribute has defined the attribute property '__value' + for fqdn in self._se_fqdn: + attribute_properties = csp_tango_db.get_device_attribute_property(fqdn, + {'adminMode': ['__value']}) + print("fqdn: {} attribute_properties: {}".format(fqdn, attribute_properties)) + try: + admin_mode_memorized = attribute_properties['adminMode']['__value'] + self._se_admin_mode[fqdn] = int(admin_mode_memorized[0]) + except KeyError as key_err: + msg = ("No key {} found for sub-element {}" + " adminMode attribute".format(key_err, fqdn)) + self.dev_logging(msg, tango.LogLevel.LOG_INFO) + # _se_proxies: the sub-element proxies # implementes s a dictionary: # keys: sub-element FQDN @@ -1245,11 +1288,10 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # NOTE: in PyTango unsubscription of not-existing event # id raises a KeyError exception not a DevFailed !! except KeyError as key_err: - msg = "Can't retrieve the information of key {}".format(key_err) - self.dev_logging(msg, tango.LogLevel.LOG_ERROR) - except tango.DevFailed as df: - msg = ("Failure reason: {} Desc: {}".format(str(df.args[0].reason), - str(df.args[0].desc))) + msg = ("Failure unsubscribing event {} " + "on device {}. Reason: {}".format(event_id, + fqdn, + key_err)) self.dev_logging(msg, tango.LogLevel.LOG_ERROR) # remove the attribute entry from the fqdn dictionary for attr_name in event_to_remove[fqdn]: -- GitLab From f55b8e61e84a9dd143d69d090d751a003debf43e Mon Sep 17 00:00:00 2001 From: softir Date: Tue, 7 Jan 2020 15:58:09 +0100 Subject: [PATCH 09/33] AT5-257: Defined decorator to check obsState value before issuing sub-array commands. --- csp-lmc-common/utils/decorators.py | 50 ++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/csp-lmc-common/utils/decorators.py b/csp-lmc-common/utils/decorators.py index ca27250..2dc16b8 100644 --- a/csp-lmc-common/utils/decorators.py +++ b/csp-lmc-common/utils/decorators.py @@ -7,7 +7,7 @@ sys.path.insert(0, utils_path) import tango import cspcommons -from cspcommons import AdminMode, CmdExecState +from cspcommons import AdminMode, ObsState, CmdExecState import functools class AdminModeCheck(object): @@ -49,6 +49,52 @@ class AdminModeCheck(object): tango.ErrSeverity.ERR) return f(*args, **kwargs) return admin_mode_check + +VALID_OBS_STATE = {'abort' : [ObsState.CONFIGURING, ObsState.SCANNING], + 'reset' : [ObsState.ABORTED], + 'configscan' : [ObsState.IDLE, ObsState.READY], + 'scan' : [ObsState.READY], + 'endscan' : [ObsState.SCANNING], + 'endsb' : [ObsState.READY, ObsState.IDLE], + 'addbeam' : [ObsState.IDLE], + 'removebeam' : [ObsState.IDLE] + } +class StateAndObsStateCheck(object): + """ + Class designed to be a decorator for the CspMaster methods. + It checks the obsMode attribute value + """ + def __init__(self, args=False, kwargs=False): + self._args = args + self._kwargs = kwargs + def __call__(self, f): + @functools.wraps(f) + def obs_state_check(*args, **kwargs): + # get the device instance + dev_instance = args[0] + cmd_to_execute = self._args + # check if the sub-array State is On, otherwise throws an exception + if dev_instance.get_state() != tango.DevState.ON: + msg_args = (cmd_to_execute, dev_instance.get_state()) + err_msg = ("{} command can't be issued when the" + " State is {} ".format(*msg_args)) + tango.Except.throw_exception("Command not executable", + err_msg, + "StateAndObsStateCheck decorator", + tango.ErrSeverity.ERR) + # Check the obsState attribute value: valid values for the command to + # execute are defined by VALID_OBS_STATE dictionary + if dev_instance._obs_state in VALID_OBS_STATE[cmd_to_execute.lower()]: + msg_args = (cmd_to_execute, ObsState(dev_instance._obs_state).name) + err_msg = ("{} command can't be issued when the" + " obsState is {} ".format(*msg_args)) + + tango.Except.throw_exception("Command not executable", + err_msg, + "ObsStateCheck decorator", + tango.ErrSeverity.ERR) + return f(*args, **kwargs) + return obs_state_check class CmdInputArgsCheck(object): """ @@ -195,4 +241,4 @@ class CmdInputArgsCheck(object): print("CmdInputArgsCheck end!!") return f(*args, **kwargs) return input_args_check - \ No newline at end of file + -- GitLab From aded87b6a8db15fe9e2aac3fbcdc5a0f67a03791 Mon Sep 17 00:00:00 2001 From: softir Date: Tue, 7 Jan 2020 15:59:52 +0100 Subject: [PATCH 10/33] AT5-257: first release of the CSP.LMC Common CspSubarray Class. --- csp-lmc-common/csp-lmc-common/CspSubarray.py | 1942 ++++++++++++++++++ 1 file changed, 1942 insertions(+) create mode 100644 csp-lmc-common/csp-lmc-common/CspSubarray.py diff --git a/csp-lmc-common/csp-lmc-common/CspSubarray.py b/csp-lmc-common/csp-lmc-common/CspSubarray.py new file mode 100644 index 0000000..08ef593 --- /dev/null +++ b/csp-lmc-common/csp-lmc-common/CspSubarray.py @@ -0,0 +1,1942 @@ +# -*- coding: utf-8 -*- +# +# This file is part of the CspSubarray project +# +# INAF - SKA Telescope +# +# Distributed under the terms of the GPL license. +# See LICENSE.txt for more info. + +""" CSP.LMC Common CspSubarray + +CSP subarray functionality is modeled via a TANGCSP.LMC Common Class for the CSPSubarray 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 +import threading +import time +import json +# PROTECTED REGION END# //CspMaster.standardlibray_import + +# tango imports +# PROTECTED REGION ID (CspMaster.add_path) ENABLED START # + +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 class_property, device_property +from tango import AttrQuality, DispLevel, DevState +from tango import AttrWriteType, PipeWriteType + +# Additional import +# PROTECTED REGION ID(CspSubarray.additionnal_import) ENABLED START # +from skabase.SKASubarray import SKASubarray +from skabase.auxiliary import utils +# PROTECTED REGION END # // CspSubarray.additionnal_import + +# PROTECTED REGION ID (CspSubarray.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 decorators +from decorators import AdminModeCheck, StateAndObsStateCheck +import cspcommons +from cspcommons import HealthState, AdminMode, ObsState, ObsMode, CmdExecState +import release +# PROTECTED REGION END# //CspSubarray.add_path + +__all__ = ["CspSubarray", "main"] + + +class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): + """ + CSP subarray functionality is modeled via a TANGCSP.LMC Common Class for the CSPSubarray TANGO Device. + + **Properties:** + + - Class Property + PSTBeams + - PST sub-element PSTBeams TANGO devices FQDNs + - Type:'DevVarStringArray' + + - Device Property + + CspMaster + - The TANGO address of the CspMaster. + - Type:'DevString' + + CbfSubarray + - CBF sub-element sub-array TANGO device FQDN + - Type:'DevString' + + PssSubarray + - PST sub-element sub-array TANGO device FQDN. + - Type:'DevString' + + CorrelationCapability + - CSP Subarray *Correlation Inherent Capability*\nTANGO device FQDN + - Type:'DevString' + + PssCapability + - CSP Subarray *Pss Inherent Capability*\nTANGO device FQDN + - Type:'DevString' + + PstCapability + - CSP Subarray *PST Inherent Capability*\nTANGO device FQDN + - Type:'DevString' + + VlbiCapability + - CSP Subarray *VLBI Inherent Capability*\nTANGO device FQDN + - Type:'DevString' + + """ + # PROTECTED REGION ID(CspSubarray.class_variable) ENABLED START # + # PROTECTED REGION END # // CspSubarray.class_variable + + #---------------- + # Event Callback functions + # --------------- + def _se_scm_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 + """ + dev_name = evt.device.dev_name() + if not evt.err: + try: + if dev_name in self._se_subarray_fqdn: + if evt.attr_value.name.lower() == "state": + print("{}: received event on {} value {}".format(dev_name, + evt.attr_value.name, + evt.attr_value.value)) + self._se_subarray_state[dev_name] = evt.attr_value.value + #self._se_state = {key, value for key,value in self._subarray_resources_state.item if key.find("subarray") > -1} + + elif evt.attr_value.name.lower() == "healthstate": + self._se_subarray_health_state[dev_name] = evt.attr_value.value + elif evt.attr_value.name.lower() == "adminmode": + print("device: {} adminMode value {}".format(dev_name,evt.attr_value.value)) + self._se_subarray_admin_mode[dev_name] = evt.attr_value.value + elif evt.attr_value.name.lower() == "obsstate": + print("device: {} obsState value {}".format(dev_name,evt.attr_value.value )) + self._se_subarray_obs_state[dev_name] = evt.attr_value.value + elif evt.attr_value.name.lower() == "obsmode": + print("device: {} obsMode value {}".format(dev_name, evt.attr_value.value )) + self._se_subarray_obs_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 sub-array SCM + if evt.attr_value.name.lower() in ["state", "healthstate"]: + self._update_subarray_state() + if evt.attr_value.name.lower() == "obsstate": + self._update_subarray_obs_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": + self._se_subarray_state[dev_name] = tango.DevState.UNKNOWN + self._se_subaray_health_state[dev_name] = HealthState.UNKNOWN + # TODO how report obsState? + # adminMode can't be change otherwise the State and healthState + # are note updated + # update the State and healthState of the CSP sub-array + self._update_subarray_state() + log_msg = item.reason + ": on attribute " + str(evt.attr_name) + self.dev_logging(log_msg, tango.LogLevel.LOG_WARN) + + def _subarray_resources_scm_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 + """ + dev_name = evt.device.dev_name() + if not evt.err: + try: + if dev_name in self._subarray_resources_fqdn: + if evt.attr_value.name.lower() == "state": + print("{}: received event on {} value {}".format(dev_name, + evt.attr_value.name, + evt.attr_value.value)) + self._subarray_resources_state[dev_name] = evt.attr_value.value + #self._se_state = {key, value for key,value in self._subarray_resources_state.item if key.find("subarray") > -1} + + elif evt.attr_value.name.lower() == "healthstate": + self._subarray_resources_health_state[dev_name] = evt.attr_value.value + elif evt.attr_value.name.lower() == "adminmode": + print("device: {} adminMode value {}".format(dev_name,evt.attr_value.value )) + self._subarray_resources_admin_mode[dev_name] = evt.attr_value.value + elif evt.attr_value.name.lower() == "obsstate": + print("device: {} obsState value {}".format(dev_name,evt.attr_value.value )) + self._subarray_resources_obs_state[dev_name] = evt.attr_value.value + elif evt.attr_value.name.lower() == "obsmode": + print("device: {} obsMode value {}".format(dev_name,evt.attr_value.value )) + self._subarray_resources_obs_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) + 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": + self._subarray_resources_state[dev_name] = tango.DevState.UNKNOWN + self._subarray_resources_health_state[dev_name] = HealthState.UNKNOWN + log_msg = item.reason + ": on attribute " + str(evt.attr_name) + self.dev_logging(log_msg, tango.LogLevel.LOG_WARN) + + def _attributes_change_evt_cb(self, evt): + """ + Class protected callback function. + Retrieve the value of the sub-element xxxCommandProgress attribute + subscribed for change event when a long-running command is issued + on the sub-element device. + + :param evt: The event data + + :return: None + """ + dev_name = evt.device.dev_name() + if not evt.err: + try: + if evt.attr_value.name.lower().find("cmdprogress") > -1: + if dev_name in self._se_subarray_fqdn: + # get the command name (on/off/standby) from the attribute name + # (onCommandProgress/offCommandProgress/standbyCommandProgress)) + # removing the last 15 chars + cmd_name = evt.attr_value.name[:-11] + self._se_subarray_cmd_progress[dev_name][cmd_name] = evt.attr_value.value + elif evt.attr_value.name.lower().find("cmdtimeoutexpired") > -1: + if dev_name in self._se_subarray_fqdn: + cmd_name = evt.attr_value.name[:-17] + self._se_subarray__timeout_expired[dev_name][cmd] = True + 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) + 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: + log_msg = item.reason + ": on attribute " + str(evt.attr_name) + self.dev_logging(log_msg, tango.LogLevel.LOG_WARN) + + def _cmd_ended_cb(self, evt): + """ + Callback function immediately executed when the asynchronous invoked + command returns. + + :param evt: a CmdDoneEvent object. This class is used to pass data + to the callback method in asynchronous callback model for command + execution. + :type: CmdDoneEvent object + It has the following members: + - device : (DeviceProxy) The DeviceProxy object on which the + call was executed. + - cmd_name : (str) The command name + - argout_raw : (DeviceData) The command argout + - argout : The command argout + - err : (bool) A boolean flag set to true if the command + failed. False otherwise + - errors : (sequence) The error stack + - ext + :return: none + """ + # NOTE:if we try to access to evt.cmd_name or other paramters, sometime + # the callback crashes withthis error: + # terminate called after throwing an instance of 'boost::python::error_already_set' + try: + # Can happen evt empty?? + if evt: + if not evt.err: + msg = "Device {} is processing command {}".format(evt.device, + evt.cmd_name) + print(msg) + self.dev_logging(msg, tango.LogLevel.LOG_INFO) + else: + msg = "Error!!Command {} ended on device {}.\n".format(evt.cmd_name, + evt.device.dev_name()) + msg += " Desc: {}".format(evt.errors[0].desc) + #self.dev_logging(msg, tango.LogLevel.LOG_ERROR) + if evt.cmd_name == "ConfigureScan": + # set to IDLE the obstate of the failed sub-array/PSTBeam + self._se_subarray_obs_state[evt.device] = ObsState.IDLE + print(msg) + self._se_cmd_execution_state[evt.device.dev_name()][evt.cmd_name.lower()] = CmdExecState.IDLE + # obsState and obsMode values take on the CbfSubarray's values via + # the subscribe/publish mechanism + else: + self.dev_logging("cmd_ended callback: evt is empty!!", + tango.LogLevel.LOG_ERRO) + except tango.DevFailed as df: + msg = ("CommandCallback cmd_ended failure - desc: {}" + " reason: {}".format(df.args[0].desc, df.args[0].reason)) + self.dev_logging(msg, tango.LogLevel.LOG_ERROR) + except Exception as ex: + msg = "CommandCallBack cmd_ended general exception: {}".format(str(ex)) + self.dev_logging(msg, tango.LogLevel.LOG_ERROR) + + def _timeout_handler_cb(self, args = None, **dict_args): + print("Timeout expired!!") + cmd_expired = dict_args['cmd_name'] + device = dict_args['device'] + self._se_subarray_cmd_exec_state[device][cmd_expired] = CmdExecState.IDLE + self._se_subarray_timeout_expired[device][cmd_expired] = True + + # Class protected methods + # --------------- + + def _update_subarray_state(self): + """ + Class protected method. + Retrieve the State attribute values of the CSP sub-elements and aggregate + them to build up the CSP global state. + + :param: None + + :return: None + """ + self._update_subarray_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_subarray_state[self.CbfSubarray]) + if self._se_subarray_admin_mode[self.CbfSubarray] in [AdminMode.OFFLINE, AdminMode.NOTFITTED, AdminMode.RESERVED]: + self.set_state(tango.DevState.FAULT) + if self._admin_mode in [AdminMode.OFFLINE, AdminMode.NOTFITTED, AdminMode.RESERVED]: + self.set_state[tango.DevState.DISABLE] + + def _update_subarray_health_state(self): + """ + Class protected 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 Subarray HealthState is OK only if: + # - all sub-elements subarrays/PSTBeam have + # - adminMode OFF-LINE or MAINTENACE + # - HealthState is OK + admin_fqdn = [fqdn for fqdn, admin_value in self._se_subarray_admin_mode.items() + if admin_value in [AdminMode.ONLINE, AdminMode.MAINTENANCE]] + health_fqdn = [fqdn for fqdn, health_value in self._se_subarray_health_state.items() + if health_value == HealthState.OK] + state_fqdn = [fqdn for fqdn, state_value in self._se_subarray_state.items() + if state_value in [tango.DevState.ON, tango.DevState.OFF]] + admin_fqdn.sort() + health_fqdn.sort() + state_fqdn.sort() + print("admin_fqdn:",admin_fqdn) + print("state_fqdn:",state_fqdn) + print("health_fqdn:",health_fqdn) + if self.CbfSubarray in admin_fqdn: + if admin_fqdn == health_fqdn == state_fqdn: + self._healthstate = HealthState.OK + elif self._se_subarray_health_state[self.CbfSubarray] in [HealthState.FAILED, HealthState.UNKNOWN]: + self._healthstate = self._se_subarray_health_state[self.CbfSubarray] + else: + self._health_state = HealthState.DEGRADED + else: + self._healthstate = HealthState.FAILED + return + + def _update_subarray_obs_state(self): + """ + *Class private method* + + Set the subarray obsState attribute value. It works only for IMAGING. + :param: + None + Returns: + None + """ + # NOTE: when ObsMode value is set, it should be considered to set the final + # sub-array ObsState value + + online_devices = {key: value for key,value in self._se_subarray_admin_mode.items() + if value in [AdminMode.ONLINE, AdminMode.MAINTENANCE]} + if all (values == ObsState.IDLE for values in self._se_subarray_obs_state.values()): + self._obs_state = ObsState.IDLE + return + + configuring_devices = {key:value for key,value in self._se_subarray_exec_state['config'].items() + if value == CmdExecState.RUNNING} + for fqdn in configuring_devices.keys(): + if self._se_subarray_obs_state[fqdn] == ObsState.READY: + self._se_subarray_cmd_timer[fqdn]['config'].cancel() + self._se_subarray_cmd_timer[fqdn]['config'].join() + self._se_subarray_exec_state[fqdn]['config'] == CmdExecState.IDLE + + if all (values == ObsMode.READY for values in configuring_devices.values()): + self._obs_state = ObsState.READY + return + + scanning_devices = {key:value for key,value in self._se_subarray_exec_state['scan'].items() + if value == CmdExecState.RUNNING} + for fqdn in scanning_devices.keys(): + if self._se_subarray_obs_state[fqdn] == ObsState.READY: + self._se_subarray_cmd_timer[fqdn]['scan'].cancel() + self._se_subarray_cmd_timer[fqdn]['scan'].join() + self._se_subarray_exec_state[fqdn]['scan'] == CmdExecState.IDLE + if all (values == ObsMode.IDLE for values in scanning_devices.values()): + self._obs_mode = ObsMode.IDLE + + def _connect_to_subarrays (self): + """ + Class private method. + Establish a *stateless* connection with each CSP sub-element. + Retrieve from the CSP TANGO DB the memorized adminMode value for each + sub-element. + Exceptions are logged. + + :return: None + """ + + for fqdn in self._se_subarray_fqdn: + try: + log_msg = "Trying connection to" + str(fqdn) + " device" + print(log_msg) + self.dev_logging(log_msg, int(tango.LogLevel.LOG_INFO)) + device_proxy = tango.DeviceProxy(fqdn) + #device_proxy.ping() + # Note: ping() method is not called. The DeviceProxy is initialized even if the + # sub-element is not running (but defined into the TANGO DB!). + # The connection with a sub-element is establish as soon as the corresponding + # Master device starts. + + # store the sub-element proxies + self._se_subarray_proxies[fqdn] = device_proxy + # subscription of SCM attributes (State, healthState and adminMode). + # Note: subscription is performed also for devices not ONLINE or MAINTENANCE. + # In this way the CspMaster is able to detect a change in the admin value. + + # if the sub-element sub-array device is running, the adminMode is updated + # adminMode has to be subscribed first so that healthState, State and obsState + # are updated accordingly with its value! + ev_id = device_proxy.subscribe_event("adminMode", + tango.EventType.CHANGE_EVENT, + self._se_scm_change_event_cb, + stateless=True) + self._subarray_resources_event_id[fqdn]['adminMode'] = ev_id + + ev_id = device_proxy.subscribe_event("State", + tango.EventType.CHANGE_EVENT, + self._se_scm_change_event_cb, + stateless=True) + self._subarray_resources_event_id[fqdn]['state'] = ev_id + + ev_id = device_proxy.subscribe_event("healthState", + tango.EventType.CHANGE_EVENT, + self._se_scm_change_event_cb, + stateless=True) + self._subarray_resources_event_id[fqdn]['healthState'] = ev_id + + ev_id = device_proxy.subscribe_event("obsState", + tango.EventType.CHANGE_EVENT, + self._se_scm_change_event_cb, + stateless=True) + self._subarray_resources_event_id[fqdn]['obsState'] = ev_id + + ev_id = device_proxy.subscribe_event("obsMode", + tango.EventType.CHANGE_EVENT, + self._se_scm_change_event_cb, + stateless=True) + self._subarray_resources_event_id[fqdn]['obsMode'] = ev_id + + except KeyError as key_err: + log_msg = ("No key {} found".format(str(key_err))) + self.dev_logging(log_msg, tango.LogLevel.LOG_WARN) + 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) + + def _is_se_subarray_running (self, device_name): + """ + *Class protected method.* + + Check if a sub-element is exported in the TANGO DB (i.e its TANGO + device server is running). + If the device is not 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_subarray_proxies[device_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) + try: + proxy = tango.DeviceProxy(device_name) + # execute a ping to detect if the device is actually running + proxy.ping() + self._se_subarray_proxies[device_name] = proxy + except tango.DevFailed as df: + return False + 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 + # ---------------- + # Class Properties + # ---------------- + + PSTBeams = class_property( + dtype='DevVarStringArray', + ) + + # ----------------- + # Device Properties + # ----------------- + + CspMaster = device_property( + dtype='DevString', default_value="mid_csp/elt/master" + ) + + CbfSubarray = device_property( + dtype='DevString', + ) + + PssSubarray = device_property( + dtype='DevString', + ) + + CorrelationCapability = device_property( + dtype='DevString', + ) + + PssCapability = device_property( + dtype='DevString', + ) + + PstCapability = device_property( + dtype='DevString', + ) + + VlbiCapability = device_property( + dtype='DevString', + ) + + # ---------- + # Attributes + # ---------- + + scanID = attribute( + dtype='DevULong64', + access=AttrWriteType.READ_WRITE, + ) + + corrInherentCapAddr = attribute( + dtype='DevString', + label="Correlation Inherent Capability Address", + doc="The CSP sub-array Correlation Inherent Capability FQDN.", + ) + + pssInherentCapAddr = attribute( + dtype='DevString', + label="PSS Inherent Capability address", + doc="The CSP sub-array PSS Inherent Capability FQDN.", + ) + + pstInherentCap = attribute( + dtype='DevString', + label="PST Inherent Capability address", + doc="The CSP sub-array PST Inherent Capability FQDN.", + ) + + vlbiInherentCap = attribute( + dtype='DevString', + label="VLBI Inhernt Capabilityaddress", + doc="The CSP sub-array VLBI Inherent Capability FQDN.", + ) + + cbfSubarrayState = attribute( + dtype='DevState', + ) + + pssSubarrayState = attribute( + dtype='DevState', + ) + + cbfSubarrayHealthState = attribute( + dtype='DevEnum', + label="CBF Subarray Health State", + doc="CBF Subarray Health State", + enum_labels=["OK", "DEGRADED", "FAILED", "UNKNOWN", ], + ) + + pssSubarrayHealthState = attribute( + dtype='DevEnum', + label="PSS Subarray Health State", + doc="PSS Subarray Health State", + enum_labels=["OK", "DEGRADED", "FAILED", "UNKNOWN", ], + ) + + cbfSubarrayAdminMode = attribute( + dtype='DevEnum', + label="CBF Subarray Admin Mode", + doc="CBF Subarray Admin Mode", + enum_labels=["ON-LINE", "OFF-LINE", "MAINTENANCE", "NOT-FITTED", "RESERVED",], + ) + + pssSubarrayAdminMode = attribute( + dtype='DevEnum', + label="PSS Subarray Admin Mode", + doc="PSS Subarray Admin Mode", + enum_labels=["ON-LINE", "OFF-LINE", "MAINTENANCE", "NOT-FITTED", "RESERVED",], + ) + cbfSubarrayObsState = attribute( + dtype='DevEnum', + label="CBF Subarray Observing State", + doc="The CBF subarray observing state.", + enum_labels=["IDLE", "CONFIGURING", "READY", "SCANNING", "PAUSED", "ABORTED", "FAULT",], + ) + + pssSubarrayObsState = attribute( + dtype='DevEnum', + label="PSS Subarray Observing State", + doc="The PSS subarray observing state.", + enum_labels=["IDLE", "CONFIGURING", "READY", "SCANNING", "PAUSED", "ABORTED", "FAULT",], + ) + + pssSubarrayAddr = attribute( + dtype='DevString', + label="PSS sub-array address", + doc="The PSS sub-element sub-array FQDN.", + ) + + cbfSubarrayAddr = attribute( + dtype='DevString', + label="CBF sub-array address", + doc="The CBF sub-element sub-array FQDN.", + ) + + validScanConfiguration = attribute( + dtype='DevString', + label="Valid Scan Configuration", + doc="Store the last valid scan configuration.", + ) + + addSearchBeamDurationExpected = attribute( + dtype='DevUShort', + label="AddSearchBeams command duration expected", + doc="The duration expected (in sec) for the AddSearchBeams command.", + ) + + remSearchBeamsDurationExpected = attribute( + dtype='DevUShort', + label="RemoveSearchBeams command duration expected", + doc="The duration expected (in sec) for the RemoveSearchBeams command.", + ) + + addSearchBeamDurationMeasured = attribute( + dtype='DevUShort', + label="AddSearchBeams command duration measured", + doc="The duration measured (in sec) for the AddSearchBeams command.", + ) + + remSearchBeamsDurationMeasured = attribute( + dtype='DevUShort', + label="RemoveSearchBeams command duration measured", + doc="The duration measured (in sec) for the RemoveSearchBeams command.", + ) + + searchBeamsCmdTimeoutExpired = attribute( + dtype='DevBoolean', + label="Add/SearchBeams command timeout flag", + polling_period=1000, + doc="The timeout flag for the Add/SearchBeams command.", + ) + + addTimingBeamDurationExpected = attribute( + dtype='DevUShort', + label="AddTimingBeams command duration expected", + doc="The duration expected (in sec) for the AddTimingBeams command.", + ) + + remTimingBeamsDurationExpected = attribute( + dtype='DevUShort', + label="RemoveTimingBeams command duration expected", + doc="The duration expected (in sec) for the RemoveTimingBeams command.", + ) + + addTimingBeamDurationMeasured = attribute( + dtype='DevUShort', + label="AddTimingBeams command duration measured", + doc="The duration measured (in sec) for the AddTimingBeams command.", + ) + + remTimingBeamsDurationMeasured = attribute( + dtype='DevUShort', + label="EndSB command duration measured", + doc="The duration measured (in sec) for the RemoveTimingBeams command.", + ) + + timingBeamsCmdTimeoutExpired = attribute( + dtype='DevBoolean', + label="Add/TimingBeams command timeout flag", + polling_period=1000, + doc="The timeout flag for the Add/TimingBeams command.", + ) + + endSBDurationExpected = attribute( + dtype='DevUShort', + label="EndSB command duration expected", + doc="The duration expected (in sec) for the EndSB command.", + ) + + endSBDurationMeasured = attribute( + dtype='DevUShort', + label="EndSB command duration measured", + doc="The duration measured (in sec) for the EndSB command.", + ) + + endSBCmdProgress = attribute( + dtype='DevUShort', + label="EndSB command progress percentage", + polling_period=1500, + abs_change=5, + doc="The progress percentage for the EndSB command.", + ) + + endSBCmdTimeoutExpired = attribute( + dtype='DevBoolean', + label="EndSB command timeout flag", + polling_period=1000, + doc="The timeout flag for the EndSB command.", + ) + + endScanDurationExpected = attribute( + dtype='DevUShort', + label="EndScan command duration expected", + doc="The duration expected (in sec) for the EndScan command.", + ) + + endScanDurationMeasured = attribute( + dtype='DevUShort', + label="EndScan command duration measured", + doc="The duration measured (in sec) for the EndScan command.", + ) + + endScanCmdProgress = attribute( + dtype='DevUShort', + label="EndScan command progress percentage", + polling_period=1500, + abs_change=5, + doc="The progress percentage for the EndScan command.", + ) + + endScanCmdTimeoutExpired = attribute( + dtype='DevBoolean', + label="EndScan command timeout flag", + polling_period=1000, + doc="The timeout flag for the EndScan command.", + ) + + addResourcesCmdProgress = attribute( + dtype='DevUShort', + label="Add resources command progress percentage", + polling_period=1500, + abs_change=5, + doc="The progress percentage for the Add resources command.", + ) + + removeResourcesCmdProgress = attribute( + dtype='DevUShort', + label="Remove resources command progress percentage", + polling_period=1500, + abs_change=5, + doc="The progress percentage for the Add/SearchBeams command.", + ) + + scanCmdProgress = attribute( + dtype='DevUShort', + label="Scan command progress percentage", + polling_period=1500, + abs_change=5, + doc="The progress percentage for the Scan command.", + ) + + reservedSearchBeamNum = attribute( + dtype='DevUShort', + label="Number of reserved SearchBeam IDs", + doc="Number of SearchBeam IDs reserved for the CSP sub-array", + ) + + numOfDevCompletedTask = attribute( + dtype='DevUShort', + label="Number of devices that completed the task", + doc="Number of devices that completed the task", + ) + + assignedSearchBeamIDs = attribute( + dtype=('DevUShort',), + max_dim_x=1500, + label="List of assigned Search Beams", + doc="List of assigned Search Beams", + ) + + reservedSearchBeamIDs = attribute( + dtype=('DevUShort',), + max_dim_x=1500, + label="List of reserved SearchBeam IDs", + doc="List of SearchBeam IDs reserved for the CSP sub-array", + ) + + assignedTimingBeamIDs = attribute( + dtype=('DevUShort',), + max_dim_x=16, + label="List of assigned TimingBeam IDs", + doc="List of TimingBeam IDs assigned to the CSP sub-array", + ) + + assignedVlbiBeamIDs = attribute( + dtype=('DevUShort',), + max_dim_x=20, + label="List of assigned VlbiBeam IDs", + doc="List of VlbiBeam IDs assigned to the CSP sub-array", + ) + + assignedSearchBeamsState = attribute( + dtype=('DevState',), + max_dim_x=1500, + label="Assigned SearchBeams State", + doc="State of the assigned SearchBeams", + ) + + assignedTimingBeamsState = attribute( + dtype=('DevState',), + max_dim_x=16, + label="Assigned TimingBeams State", + doc="State of the assigned TimingBeams", + ) + + assignedVlbiBeamsState = attribute( + dtype=('DevState',), + max_dim_x=20, + label="Assigned VlbiBeams State", + doc="State of the assigned VlbiBeams", + ) + + assignedSearchBeamsHealthState = attribute( + dtype=('DevState',), + max_dim_x=1500, + label="Assigned SearchBeams HealthState", + doc="HealthState of the assigned SearchBeams", + ) + + assignedTimingBeamsHealthState = attribute( + dtype=('DevState',), + max_dim_x=16, + label="Assigned TimingBeams HealthState", + doc="HealthState of the assigned TimingBeams", + ) + + assignedVlbiBeamsHealthState = attribute( + dtype=('DevState',), + max_dim_x=20, + label="Assigned VlbiBeams HealthState", + doc="HealthState of the assigned VlbiBeams", + ) + + assignedSearchBeamsObsState = attribute( + dtype=('DevState',), + max_dim_x=1500, + label="Assigned SearchBeams ObsState", + doc="ObsState of the assigned SearchBeams", + ) + + assignedTimingBeamsObsState = attribute( + dtype=('DevState',), + max_dim_x=16, + label="Assigned TimingBeams ObsState", + doc="ObsState of the assigned TimingBeams", + ) + + assignedVlbiBeamsObsState = attribute( + dtype=('DevState',), + max_dim_x=20, + label="Assigned VlbiBeams ObsState", + doc="ObsState of the assigned VlbiBeams", + ) + + assignedSearchBeamsAdminMode = attribute( + dtype=('DevState',), + max_dim_x=1500, + label="Assigned SearchBeams AdminMode", + doc="AdminMode of the assigned SearchBeams", + ) + + assignedTimingBeamsAdminMode = attribute( + dtype=('DevState',), + max_dim_x=16, + label="Assigned TimingBeams AdminMode", + doc="AdminMode of the assigned TimingBeams", + ) + + assignedVlbiBeamsAdminMode = attribute( + dtype=('DevState',), + max_dim_x=20, + label="Assigned VlbiBeams AdminMode", + doc="AdminMode of the assigned VlbiBeams", + ) + + pstBeams = attribute( + dtype=('DevString',), + max_dim_x=16, + label="PSTBeams addresses", + doc="PST sub-element PSTBeam TANGO device FQDNs.", + ) + + listOfDevCompletedTask = attribute( + dtype=('DevString',), + max_dim_x=100, + label="List of devices that completed the task", + doc="List of devices that completed the task", + ) + + cbfOutputLink = attribute(name="cbfOutputLink", + label="cbfOutputLink", + forwarded=True + ) + pssOutputLink = attribute(name="pssOutputLink", + label="cbfOutputLink", + forwarded=True + ) + pstOutputLink = attribute(name="pstOutputLink", + label="cbfOutputLink", + forwarded=True + ) + vlbiOutputLink = attribute(name="vlbiOutputLink", + label="cbfOutputLink", + forwarded=True + ) + # --------------- + # General methods + # --------------- + + def init_device(self): + """Initialises the attributes and properties of the CspSubarray.""" + SKASubarray.init_device(self) + # PROTECTED REGION ID(CspSubarray.init_device) ENABLED START # + self._build_state = '{}, {}, {}'.format(release.name, release.version, release.description) + self._version_id = release.version + # connect to CSP.LMC TANGO DB + csplmc_tango_db = tango.Database() + # 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 + self._obs_mode = ObsMode.IDLE + # 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_subarray_state = defaultdict(lambda: tango.DevState.DISABLE) + self._se_subarray_health_state = defaultdict(lambda: HealthState.UNKNOWN) + self._se_subarray_admin_mode = defaultdict(lambda: AdminMode.NOTFITTED) + self._se_subarray_obs_state = defaultdict(lambda: ObsState.IDLE) + self._se_subarray_obs_mode = defaultdict(lambda: ObsMode.IDLE) + + # initialize list with CSP sub-element sub-array FQDNs. After TimingBeams assignment or + # configuration, this list includes their FQDNs. + self._se_subarray_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_subarray_fqdn.append(self.CbfSubarray) + self._se_subarray_fqdn.append(self.PssSubarray) + + # read the sub-element sub-array adminMode (memorized) attribute from + # the CSP.LMC TANGO DB. + # Note: a memorized attribute has defined the attribute property '__value' + for fqdn in self._se_subarray_fqdn: + attribute_properties = csplmc_tango_db.get_device_attribute_property(fqdn, + {'adminMode': ['__value']}) + print("fqdn: {} attribute_properties: {}".format(fqdn, attribute_properties)) + try: + admin_mode_memorized = attribute_properties['adminMode']['__value'] + self._se_subarray_admin_mode[fqdn] = int(admin_mode_memorized[0]) + except KeyError: + pass + # _se_subarray_proxies: the sub-element sub-array proxies + # implementes as dictionary: + # keys: sub-element sub-arrayFQDN + # values: device proxy + self._se_subarray_proxies = {} + + # use defaultdict to initialize the sub-array resources State, + # healthState, obsState and and adminMode attributes + # keys: resources fqdn + # values: the SCM attribute value + self._subarray_resources_state = defaultdict(lambda:tango.DevState.DISABLE) + self._subarray_resources_health_state = defaultdict(lambda:HealthState.UNKNOWN) + self._subarray_resources_admin_mode = defaultdict(lambda:AdminMode.NOTFITTED) + self._subarray_resources_obs_state = defaultdict(lambda:ObsState.IDLE) + + # Nested default dictionary with list of event ids/CSP sub-array sub-component. Need to + # store the event ids for each CSP sub-array component and attribute to un-subscribe + # them at disconnection. + # keys: sub-component FQDN + # values: dictionary (keys: attribute name, values: event id) + self._subarray_resources_event_id = defaultdict(lambda: defaultdict(lambda: 0)) + + # _se_subarray_cmd_exec_state: implement the execution state of a long-running + # command for each sub-element. + # implemented as a nested default dictionary: + # keys: sub-element FQDN + # values: defaut dictionary (keys: command name, values: command state) + self._se_subarray_cmd_exec_state = defaultdict(lambda: defaultdict(lambda: CmdExecState.IDLE)) + + # _cmd_execution_state: implement the execution state of a long-running + # command for the whole CSP sub-array Setting this attribute prevent the execution + # of the same command while it is already running. + # implemented as a default dictionary: + # keys: command name + # values:command state + self._cmd_execution_state = defaultdict(lambda: CmdExecState.IDLE) + + # _se_cmd_starting_time: for each sub-element report the long-running command + # starting time + # Implemented as dictionary: + # keys: the sub-element sub-array FQDN + # values: starting time + self._se_subarray_cmd_starting_time = defaultdict(lambda: 0.0) + + # _command_thread: thread for the command execution + # keys: the command name('on, 'off'...) + # values: thread instance + self._command_thread = {} + + # of a long-running command + # implemented as a default nested dictionary: + # keys: sub-element sub-array FQDN + # values: default dictionary (keys: command name, values: the execution percentage) + self._se_subarray_cmd_progress = defaultdict(lambda: defaultdict(lambda: 0)) + + # _cmd_progress: report the execution progress of a long-running command + # implemented as a dictionary: + # keys: command name ('addbeams', 'removebeams', 'configure') + # values: the percentage + self._cmd_progress = 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_subarray_cmd_duration_expected = defaultdict(lambda: defaultdict(lambda: 20)) + + # _cmd_duration_expected: store the duration (in sec.) configured for + # a long-running command + # Implemented asdefault dictionary + # keys: command name ('on', 'off',..) + # values: the duration (in sec) + self._cmd_duration_expected = defaultdict(lambda: 30) + + # _cmd_duration_measured: report the measured duration (in sec.) for + # a long-running command + # Implemented as default dictionary + # keys: command name ('on', 'off',..) + # values: the duration (in sec) + self._cmd_duration_measured = defaultdict(lambda: 0) + + # _se_timeout_expired: report the timeout flag + # Implemented as a nested default dictionary + # keys: FQDN + # values: default dictionary (keys: command name, values: True/False)) + self._se_subarray_timeout_expired = defaultdict(lambda: defaultdict(lambda: False)) + + # _timeout_expired: report the timeout flag + # Implemented as a dictionary + # keys: command name ('addsearchbeam',...) + # values: True/False)) + self._timeout_expired = 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: []) + # the last executed command + self._last_executed_command = "" + + # _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) + + # _valid_scan_configuration: the last programmed scan configuration + self._valid_scan_configuration = '' + + # initialize CSP SearchBeam, TimingBeam and VlbiBeam Capabilities State, + # healthState, adminMode and obsState + # keys: device FQDN + # values: the attribute value + self._search_beams_state = defaultdict(lambda: tango.DevState.DISABLE) + self._search_beams_health_state = defaultdict(lambda: HealthState.UNKNOWN) + self._search_beams_admin_mode = defaultdict(lambda: tango.DevState.NOTFITTED) + self._search_beams_obs_state = defaultdict(lambda: ObsState.IDLE) + self._timing_beams_state = defaultdict(lambda: tango.DevState.DISABLE) + self._timing_beams_health_state = defaultdict(lambda: HealthState.UNKNOWN) + self._timing_beams_admin_mode = defaultdict(lambda: tango.DevState.NOTFITTED) + self._timing_beams_obs_state = defaultdict(lambda: ObsState.IDLE) + self._vlbi_beams_state = defaultdict(lambda: tango.DevState.DISABLE) + self._vlbi_beams_health_state = defaultdict(lambda: HealthState.UNKNOWN) + self._vlbi_beams_admin_mode = defaultdict(lambda: tango.DevState.NOTFITTED) + self._vlbi_beams_obs_state = defaultdict(lambda: ObsState.IDLE) + + # unassigned CSP SearchBeam, TimingBeam, VlbiBeam Capability IDs + self._assigned_search_beams = [] + #self._unassigned_search_beam_num = 0 + self._reserved_search_beams = [] + #self._reserved_search_beam_num = 0 + self._assigned_timing_beams= [] + self._assigned_vlbi_beams = [] + + # Try connection with sub-elements + self._connect_to_subarrays() + + # to use the push model in command_inout_asynch (the one with the callback parameter), + # change the global TANGO model to PUSH_CALLBACK. + apiutil = tango.ApiUtil.instance() + apiutil.set_asynch_cb_sub_model(tango.cb_sub_model.PUSH_CALLBACK) + # PROTECTED REGION END # // CspSubarray.init_device + + def always_executed_hook(self): + """Method always executed before any TANGO command is executed.""" + # PROTECTED REGION ID(CspSubarray.always_executed_hook) ENABLED START # + # PROTECTED REGION END # // CspSubarray.always_executed_hook + + def delete_device(self): + """Hook to delete resources allocated in init_device. + + This method allows for any memory or other resources allocated in the + init_device method to be released. This method is called by the device + destructor and by the device Init command. + """ + # PROTECTED REGION ID(CspSubarray.delete_device) ENABLED START # + # PROTECTED REGION END # // CspSubarray.delete_device + # ------------------ + # Attributes methods + # ------------------ + + def read_scanID(self): + # PROTECTED REGION ID(CspSubarray.scanID_read) ENABLED START # + """Return the scanID attribute.""" + return 0 + # PROTECTED REGION END # // CspSubarray.scanID_read + + def write_scanID(self, value): + # PROTECTED REGION ID(CspSubarray.scanID_write) ENABLED START # + """Set the scanID attribute.""" + pass + # PROTECTED REGION END # // CspSubarray.scanID_write + + def read_corrInherentCapAddr(self): + # PROTECTED REGION ID(CspSubarray.corrInherentCapAddr_read) ENABLED START # + """Return the corrInherentCapAddr attribute.""" + return self.CorrelationCapability + # PROTECTED REGION END # // CspSubarray.corrInherentCapAddr_read + + def read_pssInherentCapAddr(self): + # PROTECTED REGION ID(CspSubarray.pssInherentCapAddr_read) ENABLED START # + """Return the pssInherentCapAddr attribute.""" + return self.PssCapability + # PROTECTED REGION END # // CspSubarray.pssInherentCapAddr_read + + def read_pstInherentCap(self): + # PROTECTED REGION ID(CspSubarray.pstInherentCap_read) ENABLED START # + """Return the pstInherentCap attribute.""" + return self.PstCapability + # PROTECTED REGION END # // CspSubarray.pstInherentCap_read + + def read_vlbiInherentCap(self): + # PROTECTED REGION ID(CspSubarray.vlbiInherentCap_read) ENABLED START # + """Return the vlbiInherentCap attribute.""" + return self.VlbiCapability + # PROTECTED REGION END # // CspSubarray.vlbiInherentCap_read + + def read_cbfSubarrayState(self): + # PROTECTED REGION ID(CspSubarray.cbfSubarrayState_read) ENABLED START # + """Return the cbfSubarrayState attribute.""" + return self._se_subarray_state[self.CbfSubarray] + # PROTECTED REGION END # // CspSubarray.cbfSubarrayState_read + + def read_pssSubarrayState(self): + # PROTECTED REGION ID(CspSubarray.pssSubarrayState_read) ENABLED START # + """Return the pssSubarrayState attribute.""" + return self._se_subarray_state[selPssSubarray] + # PROTECTED REGION END # // CspSubarray.pssSubarrayState_read + + def read_cbfSubarrayHealthState(self): + # PROTECTED REGION ID(CspSubarray.cbfSubarrayHealthState_read) ENABLED START # + """Return the cbfSubarrayHealthState attribute.""" + return self._se_subarray_health_state[self.CbfSubarray] + # PROTECTED REGION END # // CspSubarray.cbfSubarrayHealthState_read + + def read_pssSubarrayHealthState(self): + # PROTECTED REGION ID(CspSubarray.pssSubarrayHealthState_read) ENABLED START # + """Return the pssSubarrayHealthState attribute.""" + return self._se_subarray_health_state[self.PssSubarray] + # PROTECTED REGION END # // CspSubarray.pssSubarrayHealthState_read + + def read_cbfSubarrayAdminMode(self): + # PROTECTED REGION ID(CspSubarray.cbfSubarrayHealthState_read) ENABLED START # + """Return the cbfSubarrayHealthState attribute.""" + return self._se_subarray_admin_mode[self.CbfSubarray] + # PROTECTED REGION END # // CspSubarray.cbfSubarrayHealthState_read + + def read_pssSubarrayAdminMode(self): + # PROTECTED REGION ID(CspSubarray.pssSubarrayHealthState_read) ENABLED START # + """Return the pssSubarrayHealthState attribute.""" + return self._se_subarray_admin_mode[self.PssSubarray] + # PROTECTED REGION END # // CspSubarray.pssSubarrayHealthState_read + def read_cbfSubarrayObsState(self): + # PROTECTED REGION ID(CspSubarray.cbfSubarrayObsState_read) ENABLED START # + """Return the cbfSubarrayObsState attribute.""" + return self._se_subarray_obs_state[self.CbfSubarray] + # PROTECTED REGION END # // CspSubarray.cbfSubarrayObsState_read + + def read_pssSubarrayObsState(self): + # PROTECTED REGION ID(CspSubarray.pssSubarrayObsState_read) ENABLED START # + """Return the pssSubarrayObsState attribute.""" + return self._se_subarray_obs_state[self.PssSubarray] + # PROTECTED REGION END # // CspSubarray.pssSubarrayObsState_read + + def read_pssSubarrayAddr(self): + # PROTECTED REGION ID(CspSubarray.pssSubarrayAddr_read) ENABLED START # + """Return the pssSubarrayAddr attribute.""" + return self.PssSubarray + # PROTECTED REGION END # // CspSubarray.pssSubarrayAddr_read + + def read_cbfSubarrayAddr(self): + # PROTECTED REGION ID(CspSubarray.cbfSubarrayAddr_read) ENABLED START # + """Return the cbfSubarrayAddr attribute.""" + return self.CbfSubarray + # PROTECTED REGION END # // CspSubarray.cbfSubarrayAddr_read + + def read_validScanConfiguration(self): + # PROTECTED REGION ID(CspSubarray.validScanConfiguration_read) ENABLED START # + """Return the validScanConfiguration attribute.""" + return self._valid_scan_configuration + # PROTECTED REGION END # // CspSubarray.validScanConfiguration_read + + def read_addSearchBeamDurationExpected(self): + # PROTECTED REGION ID(CspSubarray.addSearchBeamDurationExpected_read) ENABLED START # + """Return the addSearchBeamDurationExpected attribute.""" + return self._cmd_duration_expected['addsearchbeam'] + # PROTECTED REGION END # // CspSubarray.addSearchBeamDurationExpected_read + + def read_remSearchBeamsDurationExpected(self): + # PROTECTED REGION ID(CspSubarray.remSearchBeamsDurationExpected_read) ENABLED START # + """Return the remSearchBeamsDurationExpected attribute.""" + return self._cmd_duration_expected['rmsearchbeam'] + # PROTECTED REGION END # // CspSubarray.remSearchBeamsDurationExpected_read + + def read_addSearchBeamDurationMeasured(self): + # PROTECTED REGION ID(CspSubarray.addSearchBeamDurationMeasured_read) ENABLED START # + """Return the addSearchBeamDurationMeasured attribute.""" + return self._cmd_duration_measured['addsearchbeam'] + # PROTECTED REGION END # // CspSubarray.addSearchBeamDurationMeasured_read + + def read_remSearchBeamsDurationMeasured(self): + # PROTECTED REGION ID(CspSubarray.remSearchBeamsDurationMeasured_read) ENABLED START # + """Return the remSearchBeamsDurationMeasured attribute.""" + return self._cmd_duration_measured['rmsearchbeam'] + # PROTECTED REGION END # // CspSubarray.remSearchBeamsDurationMeasured_read + + def read_searchBeamsCmdTimeoutExpired(self): + # PROTECTED REGION ID(CspSubarray.searchBeamsCmdTimeoutExpired_read) ENABLED START # + """Return the searchBeamsCmdTimeoutExpired attribute.""" + return False + # PROTECTED REGION END # // CspSubarray.searchBeamsCmdTimeoutExpired_read + + def read_addTimingBeamDurationExpected(self): + # PROTECTED REGION ID(CspSubarray.addTimingBeamDurationExpected_read) ENABLED START # + """Return the addTimingBeamDurationExpected attribute.""" + return 0 + # PROTECTED REGION END # // CspSubarray.addTimingBeamDurationExpected_read + + def read_remTimingBeamsDurationExpected(self): + # PROTECTED REGION ID(CspSubarray.remTimingBeamsDurationExpected_read) ENABLED START # + """Return the remTimingBeamsDurationExpected attribute.""" + return 0 + # PROTECTED REGION END # // CspSubarray.remTimingBeamsDurationExpected_read + + def read_addTimingBeamDurationMeasured(self): + # PROTECTED REGION ID(CspSubarray.addTimingBeamDurationMeasured_read) ENABLED START # + """Return the addTimingBeamDurationMeasured attribute.""" + return 0 + # PROTECTED REGION END # // CspSubarray.addTimingBeamDurationMeasured_read + + def read_remTimingBeamsDurationMeasured(self): + # PROTECTED REGION ID(CspSubarray.remTimingBeamsDurationMeasured_read) ENABLED START # + """Return the remTimingBeamsDurationMeasured attribute.""" + return 0 + # PROTECTED REGION END # // CspSubarray.remTimingBeamsDurationMeasured_read + + def read_addResourcesCmdProgress(self): + # PROTECTED REGION ID(CspSubarray.searchBeamsCmdProgress_read) ENABLED START # + """Return the assResourcesCmdProgress attribute.""" + return self._cmd_progress['addbeam'] + # PROTECTED REGION END # // CspSubarray.searchBeamsCmdProgress_read + + def read_removeResourcesCmdProgress(self): + # PROTECTED REGION ID(CspSubarray.timingBeamsCmdProgress_read) ENABLED START # + """Return the removeResourcesCmdProgress attribute.""" + return self._cmd_progress['removebeams'] + # PROTECTED REGION END # // CspSubarray.timingBeamsCmdProgress_read + + def read_scanCmdProgress(self): + # PROTECTED REGION ID(CspSubarray.endSBCmdProgress_read) ENABLED START # + """Return the ScanCmdProgress attribute.""" + return self._cmd_progress['scan'] + # PROTECTED REGION END # // CspSubarray.endSBCmdProgress_read + + def read_timingBeamsCmdTimeoutExpired(self): + # PROTECTED REGION ID(CspSubarray.timingBeamsCmdTimeoutExpired_read) ENABLED START # + """Return the timingBeamsCmdTimeoutExpired attribute.""" + return False + # PROTECTED REGION END # // CspSubarray.timingBeamsCmdTimeoutExpired_read + + def read_endSBDurationExpected(self): + # PROTECTED REGION ID(CspSubarray.endSBDurationExpected_read) ENABLED START # + """Return the endSBDurationExpected attribute.""" + return 0 + # PROTECTED REGION END # // CspSubarray.endSBDurationExpected_read + + def read_endSBDurationMeasured(self): + # PROTECTED REGION ID(CspSubarray.endSBDurationMeasured_read) ENABLED START # + """Return the endSBDurationMeasured attribute.""" + return 0 + # PROTECTED REGION END # // CspSubarray.endSBDurationMeasured_read + + def read_endSBCmdProgress(self): + # PROTECTED REGION ID(CspSubarray.endSBCmdProgress_read) ENABLED START # + """Return the endSBCmdProgress attribute.""" + return self._cmd_progress['endsb'] + # PROTECTED REGION END # // CspSubarray.endSBCmdProgress_read + + def read_endSBCmdTimeoutExpired(self): + # PROTECTED REGION ID(CspSubarray.endSBCmdTimeoutExpired_read) ENABLED START # + """Return the endSBCmdTimeoutExpired attribute.""" + return False + # PROTECTED REGION END # // CspSubarray.endSBCmdTimeoutExpired_read + + def read_endScanDurationExpected(self): + # PROTECTED REGION ID(CspSubarray.endScanDurationExpected_read) ENABLED START # + """Return the endScanDurationExpected attribute.""" + return 0 + # PROTECTED REGION END # // CspSubarray.endScanDurationExpected_read + + def read_endScanDurationMeasured(self): + # PROTECTED REGION ID(CspSubarray.endScanDurationMeasured_read) ENABLED START # + """Return the endScanDurationMeasured attribute.""" + return 0 + # PROTECTED REGION END # // CspSubarray.endScanDurationMeasured_read + + def read_endScanCmdProgress(self): + # PROTECTED REGION ID(CspSubarray.endScanCmdProgress_read) ENABLED START # + """Return the endScanCmdProgress attribute.""" + return self._cmd_progress['endscan'] + # PROTECTED REGION END # // CspSubarray.endScanCmdProgress_read + + def read_endScanCmdTimeoutExpired(self): + # PROTECTED REGION ID(CspSubarray.endScanCmdTimeoutExpired_read) ENABLED START # + """Return the endScanCmdTimeoutExpired attribute.""" + return False + # PROTECTED REGION END # // CspSubarray.endScanCmdTimeoutExpired_read + + def read_reservedSearchBeamNum(self): + # PROTECTED REGION ID(CspSubarray.reservedSearchBeamNum_read) ENABLED START # + """Return the reservedSearchBeamNum attribute.""" + return 0 + # PROTECTED REGION END # // CspSubarray.reservedSearchBeamNum_read + + def read_numOfDevCompletedTask(self): + # PROTECTED REGION ID(CspSubarray.numOfDevCompletedTask_read) ENABLED START # + """Return the numOfDevCompletedTask attribute.""" + return 0 + # PROTECTED REGION END # // CspSubarray.numOfDevCompletedTask_read + + def read_assignedSearchBeamIDs(self): + # PROTECTED REGION ID(CspSubarray.assignedSearchBeamIDs_read) ENABLED START # + """Return the assignedSearchBeamIDs attribute.""" + return _assigned_search_beams + # PROTECTED REGION END # // CspSubarray.assignedSearchBeamIDs_read + + def read_reservedSearchBeamIDs(self): + # PROTECTED REGION ID(CspSubarray.reservedSearchBeamIDs_read) ENABLED START # + """Return the reservedSearchBeamIDs attribute.""" + return _reserved_search_beams + # PROTECTED REGION END # // CspSubarray.reservedSearchBeamIDs_read + + def read_assignedTimingBeamIDs(self): + # PROTECTED REGION ID(CspSubarray.assignedTimingBeamIDs_read) ENABLED START # + """Return the assignedTimingBeamIDs attribute.""" + return _assigned_timing_beams + # PROTECTED REGION END # // CspSubarray.assignedTimingBeamIDs_read + + def read_assignedVlbiBeamIDs(self): + # PROTECTED REGION ID(CspSubarray.assignedVlbiBeam IDs_read) ENABLED START # + """Return the assignedVlbiBeam IDs attribute.""" + return _assigned_vlbi_beams + # PROTECTED REGION END # // CspSubarray.assignedVlbiBeam IDs_read + + def read_assignedSearchBeamsState(self): + # PROTECTED REGION ID(CspSubarray.assignedSearchBeamsState_read) ENABLED START # + """Return the assignedSearchBeamsState attribute.""" + self._search_beams_state = {key:value for key,value in self._subarray_resources_state.items() + if key.find("search") > -1} + return self._search_beams_state.values() + # PROTECTED REGION END # // CspSubarray.assignedSearchBeamsState_read + + def read_assignedTimingBeamsState(self): + # PROTECTED REGION ID(CspSubarray.assignedTimingBeamsState_read) ENABLED START # + """Return the assignedTimingBeamsState attribute.""" + self._timing_beams_state = {key:value for key,value in self._subarray_resources_state.items() + if key.find("timing") > -1} + return self._timing_beams_state.values() + # PROTECTED REGION END # // CspSubarray.assignedTimingBeamsState_read + + def read_assignedVlbiBeamsState(self): + # PROTECTED REGION ID(CspSubarray.assignedVlbiBeamsState_read) ENABLED START # + """Return the assignedVlbiBeamsState attribute.""" + self._vlbi_beams_state = {key:value for key,value in self._subarray_resources_state.items() + if key.find("vlbi") > -1} + return self._vlbi_beams_state.values() + # PROTECTED REGION END # // CspSubarray.assignedVlbiBeamsState_read + + def read_assignedSearchBeamsHealthState(self): + # PROTECTED REGION ID(CspSubarray.assignedSearchBeamsHealthState_read) ENABLED START # + """Return the assignedSearchBeamsHealthState attribute.""" + self._search_beams_health_state = {key:value for key,value in + self._subarray_resources_health_state.item() + if key.find("search") > -1} + return self._search_beams_health_state.values() + # PROTECTED REGION END # // CspSubarray.assignedSearchBeamsHealthState_read + + def read_assignedTimingBeamsHealthState(self): + # PROTECTED REGION ID(CspSubarray.assignedTimingBeamsHealthState_read) ENABLED START # + """Return the assignedTimingBeamsHealthState attribute.""" + self._timing_beams_health_state = {key:value for key,value in + self._subarray_resources_state.items() + if key.find("timing") > -1} + return self._timing_beams_health_state.values() + # PROTECTED REGION END # // CspSubarray.assignedTimingBeamsHealthState_read + + def read_assignedVlbiBeamsHealthState(self): + # PROTECTED REGION ID(CspSubarray.assignedVlbiBeamsHealthState_read) ENABLED START # + """Return the assignedVlbiBeamsHealthState attribute.""" + self._vlbi_beams_health_state = {key:value for key,value + in self._subarray_resources_state.items() + if key.find("vlbi") > -1} + return self._vlbi_beams_health_state.values() + # PROTECTED REGION END # // CspSubarray.assignedVlbiBeamsHealthState_read + + def read_assignedSearchBeamsObsState(self): + # PROTECTED REGION ID(CspSubarray.assignedSearchBeamsObsState_read) ENABLED START # + """Return the assignedSearchBeamsObsState attribute.""" + self._search_beams_obs_state = {key:value for key,value in + self._subarray_resources_health_state.items() + if key.find("search") > -1} + return self._search_beams_obs_state.values() + # PROTECTED REGION END # // CspSubarray.assignedSearchBeamsObsState_read + + def read_assignedTimingBeamsObsState(self): + # PROTECTED REGION ID(CspSubarray.assignedTimingBeamsObsState_read) ENABLED START # + """Return the assignedTimingBeamsObsState attribute.""" + self._timing_beams_obs_state = {key:value for key,value in + self._subarray_resources_state.items() + if key.find("timing") > -1} + return self._timing_beams_obs_state.values() + # PROTECTED REGION END # // CspSubarray.assignedTimingBeamsObsState_read + + def read_assignedVlbiBeamsObsState(self): + # PROTECTED REGION ID(CspSubarray.assignedVlbiBeamsObsState_read) ENABLED START # + """Return the assignedVlbiBeamsObsState attribute.""" + self._vlbi_beams_obs_state = {key:value for key,value in + self._subarray_resources_state.items() + if key.find("vlbi") > -1} + return self._vlbi_beams_obs_state.values() + # PROTECTED REGION END # // CspSubarray.assignedVlbiBeamsObsState_read + + def read_assignedSearchBeamsAdminMode(self): + # PROTECTED REGION ID(CspSubarray.assignedSearchBeamsAdminMode_read) ENABLED START # + """Return the assignedSearchBeamsAdminMode attribute.""" + self._search_beams_admin_mode = {key:value for key,value in + self._subarray_resources_health_state.items() + if key.find("search") > -1} + return self._search_beams_admin_mode.values() + # PROTECTED REGION END # // CspSubarray.assignedSearchBeamsAdminMode_read + + def read_assignedTimingBeamsAdminMode(self): + # PROTECTED REGION ID(CspSubarray.assignedTimingBeamsAdminMode_read) ENABLED START # + """Return the assignedTimingBeamsAdminMode attribute.""" + self._timing_beams_admin_mode = {key:value for key,value in + self._subarray_resources_state.items() + if key.find("timing") > -1} + return self._timing_beams_admin_mode.values() + # PROTECTED REGION END # // CspSubarray.assignedTimingBeamsAdminMode_read + + def read_assignedVlbiBeamsAdminMode(self): + # PROTECTED REGION ID(CspSubarray.assignedVlbiBeamsAdminMode_read) ENABLED START # + """Return the assignedVlbiBeamsAdminMode attribute.""" + self._vlbi_beams_admin_mode = {key:value for key,value in + self._subarray_resources_state.items() + if key.find("vlbi") > -1} + return self._vlbi_beams_admin_mode.values() + # PROTECTED REGION END # // CspSubarray.assignedVlbiBeamsAdminMode_read + + def read_pstBeams(self): + # PROTECTED REGION ID(CspSubarray.pstBeams_read) ENABLED START # + """Return the pstBeams attribute.""" + return self.PstBeams + # PROTECTED REGION END # // CspSubarray.pstBeams_read + + def read_listOfDevCompletedTask(self): + # PROTECTED REGION ID(CspSubarray.listOfDevCompletedTask_read) ENABLED START # + """Return the listOfDevCompletedTask attribute.""" + return ('',) + # PROTECTED REGION END # // CspSubarray.listOfDevCompletedTask_read + + + # -------- + # Commands + # -------- + + @command( + ) + @DebugIt() + @AdminModeCheck('EndScan') + @StateAndObsStateCheck('EndScan') + def EndScan(self): + # PROTECTED REGION ID(CspSubarray.EndScan) ENABLED START # + """ + *Class method* + End the execution of a running scan. After successful execution, the CspSubarray \ + *ObsState* is IDLE. + + raises: + tango.DevFailed: if the subarray *obsState* is not SCANNING or if an exception + is caught during the command execution. + Note: + Still to implement the check on AdminMode values: the command can be processed \ + only when the CspSubarray is *ONLINE* or *MAINTENANCE* + """ + # PROTECTED REGION ID(CspSubarray.EndScan) ENABLED START # + # Check if the EndScan command can be executed. This command is allowed when the + # Subarray State is SCANNING. + if self._obs_state != ObsState.SCANNING: + log_msg = ("Subarray obs_state is {}" + ", not SCANNING".format(ObsState(self._obs_state).name)) + tango.Except.throw_exception("Command failed", + log_msg, + "EndScan", + tango.ErrSeverity.ERR) + proxy = 0 + #TODO:the command is forwarded only to CBF. Future implementation has to + # check the observing mode and depending on this, the command is forwarded to + # the interested sub-elements. + for device in self._se_subarray_fqdn: + if self._subarray_obs_mode in [ObsMode.CORRELATION]: + if self._is_device_running(device): + + try: + proxy = self._se_subarray_proxies[self.device] + # forward asynchrnously the command to the CbfSubarray + proxy.command_inout_asynch("EndScan", self._cmd_ended) + except KeyError as key_err: + msg = " Can't retrieve the information of key {}".format(key_err) + self.dev_logging(msg, tango.LogLevel.LOG_ERROR) + tango.Except.throw_exception("Command failed", + msg, + "EndScan", + tango.ErrSeverity.ERR) + else: + log_msg = "Subarray {} not registered".format(str(self._cbf_subarray_fqdn)) + self.dev_logging(log_msg, tango.LogLevel.LOG_ERROR) + tango.Except.throw_exception("Command failed", + log_msg, + "EndScan", + tango.ErrSeverity.ERR) + # PROTECTED REGION END # // CspSubarray.EndScan + + @command( + dtype_in='DevVarStringArray', + ) + @DebugIt() + @AdminModeCheck('Scan') + @StateAndObsStateCheck('Scan') + def Scan(self, argin): + # PROTECTED REGION ID(CspSubarray.Scan) ENABLED START # + """ + Starts the scan. + + :param argin: 'DevVarStringArray' + + :return:None + """ + pass + # PROTECTED REGION END # // CspSubarray.Scan + + def __validate_scan_configuration(self, json_config): + """ + here is done a preliminary validation of the configuraito: + - look for the sub-element entries + - add eventual information to the sub-element blocks (for example for PSS add the + addresses of the PSS pipelines or the IDs of the SearchBeams) + - set the observing mode for each sub-element sub-array/PSTBeam + + if "cbf" in json_config: + self._se_subarray_obs_mode[self.CbfSubarray] = ObsMode.CORRELATION + scan_configutation[self.CbfSubarray] = json_config["cbf"] + if "pss" in json_config: + scan_configutation[self.PssSubarray] = json_config["pss"] + if "pst" in json_config: + scan_configuration[self.PssSubarray] = json_config["pss"] + return scan_configuration_dict + + Customize the function for each instance + """ + scan_configuration = {} + self._se_subarray_obs_mode[self.CbfSubarray] = ObsMode.CORRELATION + scan_configuration[self.CbfSubarray] = json_config + return scan_configuration + @command( + dtype_in='DevString', + doc_in="A Json-encoded string with the scan configuration.", + ) + @DebugIt() + @AdminModeCheck('ConfigScan') + @StateAndObsStateCheck('ConfigScan') + def ConfigureScan(self, argin): + """ + Note: + Part of this code (the input string parsing) comes from the CBF project\ + developed by J.Jjang (NRC-Canada) + + *Class method.* + + Configure a scan for the subarray.\n + The command can be execuced when the CspSubarray State is *ON* and the \ + ObsState is *IDLE* or *READY*.\n + If the configuration for the scan is not correct (invalid parameters or invalid JSON)\ + the configuration is not applied and the ObsState of the CspSubarray remains IDLE. + + :param argin: a JSON-encoded string with the parameters to configure a scan. + :return: None + :raises: + tango.DevFailed exception if the CspSubarray ObsState is not valid or if an exception\ + is caught during command execution. + Note: + Still to implement the check on AdminMode values: the command can be processed \ + only when the CspSubarray is *ONLINE* or *MAINTENANCE* + """ + # PROTECTED REGION ID(CspSubarray.ConfigureScan) ENABLED START # + + # checks on Stae, adminMode and obsState values are performed inside the + # python decorators + + # the dictionary with the scan configuration + argin_dict = {} + try: + # for test purpose we load the json configuration from an + # external file. + if "load" in argin: + # skip the 'load' chars and remove spaces from the filename + fn = (argin[4:]).strip() + filename = os.path.join(commons_pkg_path, fn) + with open(filename) as json_file: + # load the file into a dictionary + argin_dict = json.load(json_file) + # dump the dictionary into the input string to forward + # to CbfSubarray + argin = json.dumps(argin_dict) + else: + argin_dict = json.loads(argin) + except FileNotFoundError as file_err: + self.dev_logging(str(file_err), tango.LogLevel.LOG_ERROR) + tango.Except.throw_exception("Command failed", + str(file_err), + "ConfigureScan execution", + tango.ErrSeverity.ERR) + except json.JSONDecodeError as e: # argument not a valid JSON object + # this is a fatal error + msg = ("Scan configuration object is not a valid JSON object." + "Aborting configuration:{}".format(str(e))) + self.dev_logging(msg, tango.LogLevel.LOG_ERROR) + tango.Except.throw_exception("Command failed", + msg, + "ConfigureScan execution", + tango.ErrSeverity.ERR) + scan_configuration_dict = self._validate_scan_configuration(argin) + # Forward the ConfigureScan command to CbfSubarray. + # check connection with CbfSubarray + for device in scan_configuration_dict.keys(): + if not self._is_device_running(device): + log_msg = "Device {} not registered!".format(str(self._cbf_subarray_fqdn)) + self.dev_logging(log_msg, tango.LogLevel.LOG_ERROR) + if device == self.CbfSubarray: + tango.Except.throw_exception("Command failed", + log_msg, + "ConfigureScan execution", + tango.ErrSeverity.ERR) + proxy = self._se_subarray_proxies[device] + # Note: CBF/PSS sub-array checks or the validity of its + # configuration. Failure in configuration throws an exception that is + # caught via the cmd_ended_cb callback + self._obs_state = ObsState.CONFIGURING + proxy.command_inout_asynch("ConfigureScan", argin, self._cmd_ended_cb) + # invoke the constructor for the timer thread + self._se_subarray_cmd_timer[device] = threading.Timer(self._se_cmd_duration_expected['config'], + self._timeout_handler_cb, + {'cmd_name':'config'}) + attributes = ['configureCmdProgress', 'configureCmdTimeoutExpired'] + for attr in attributes: + try: + if self._subarray_resources_event_id[attr.lower()] == 0: + event_id = proxy.subscribe_event(attr, tango.EventType.CHANGE_EVENT, + self._attributes_change_evt_cb, stateless=False) + self._subarray_resources_event_id[device][attr] = event_id + except tango.DevFailed as df: + print(df.args[0].desc) + # PROTECTED REGION END # // CspSubarray.ConfigureScan + + @command( + dtype_in='DevUShort', + doc_in="The number of SearchBeams Capabilities to assign to the subarray.", + ) + @DebugIt() + @AdminModeCheck('AddBeam') + @StateAndObsStateCheck('AddBeam') + def AddNumOfSearchBeams(self, argin): + # PROTECTED REGION ID(CspSubarray.AddNumOfSearchBeams) ENABLED START # + + + # PROTECTED REGION END # // CspSubarray.ConfigureScan + + # PROTECTED REGION END # // CspSubarray.AddNumOfSearchBeams + pass + @command( + dtype_in='DevUShort', + doc_in="The number of SearchBeam Capabilities to remove from the \nCSP sub-array.\nAll the assigned SearcBbeams are removed from the CSP sub-array if the\ninput number is equal to the max number of SearchBeam \nCapabilities for the specified Csp sub-array instance (1500 for MID,\n500 for LOW)", + ) + @DebugIt() + @AdminModeCheck('RemoveBeam') + @StateAndObsStateCheck('RemoveBeam') + def RemoveNumOfSearchBeams(self, argin): + # PROTECTED REGION ID(CspSubarray.RemoveNumOfSearchBeams) ENABLED START # + """ + + Remove the specified number of SearchBeam Capabilities + from the subarray. + + :param argin: 'DevUShort' + + The number of SearchBeam Capabilities to remove from the + CSP sub-array. + All the assigned SearcBbeams are removed from the CSP sub-array if the + input number is equal to the max number of SearchBeam + Capabilities for the specified Csp sub-array instance (1500 for MID, + 500 for LOW) + + :return:None + """ + pass + # PROTECTED REGION END # // CspSubarray.RemoveNumOfSearchBeams + + @command( + dtype_in='DevVarUShortArray', + doc_in="The list of TimingBeam Capability IDs to assign to the CSP sub-array.", + ) + @DebugIt() + @AdminModeCheck('AddBeam') + @StateAndObsStateCheck('AddBeam') + def AddTimingBeams(self, argin): + # PROTECTED REGION ID(CspSubarray.AddTimingBeams) ENABLED START # + """ + + Add the specified TimingBeam Capability IDs to the CSP + sub-array. + + :param argin: 'DevVarUShortArray' + The list of TimingBeam Capability IDs to assign to the CSP sub-array. + + :return:None + """ + pass + # PROTECTED REGION END # // CspSubarray.AddTimingBeams + + @command( + dtype_in='DevVarUShortArray', + doc_in="The list of SearchBeam Capability IDs to assign to the CSP sub-array.", + ) + @DebugIt() + @AdminModeCheck('AddBeam') + @StateAndObsStateCheck('AddBeam') + def AddSearchBeamsID(self, argin): + # PROTECTED REGION ID(CspSubarray.AddSearchBeamsID) ENABLED START # + """ + + Add the specified SeachBeam Capability IDs o the CSP + sub-array. + + :param argin: 'DevVarUShortArray' + The list of SearchBeam Capability IDs to assign to the CSP sub-array. + + :return:None + """ + pass + # PROTECTED REGION END # // CspSubarray.AddSearchBeamsID + + @command( + dtype_in='DevVarUShortArray', + doc_in="The list of SearchBeam Capability IDs to remove from the CSP sub-array.", + ) + @DebugIt() + @AdminModeCheck('RemoveBeam') + @StateAndObsStateCheck('RemoveBeam') + def RemoveSearchBeamsID(self, argin): + # PROTECTED REGION ID(CspSubarray.RemoveSearchBeamsID) ENABLED START # + """ + + Remove the specified Search Beam Capability IDs from the + CSP sub-array. + + :param argin: 'DevVarUShortArray' + The list of SearchBeam Capability IDs to remove from the CSP sub-array. + + :return:None + """ + pass + # PROTECTED REGION END # // CspSubarray.RemoveSearchBeamsID + + @command( + dtype_in='DevVarUShortArray', + doc_in="The list of Timing Beams IDs to remove from the sub-array.", + ) + @DebugIt() + @AdminModeCheck('RemoveBeam') + @StateAndObsStateCheck('RemoveBeam') + def RemoveTimingBeams(self, argin): + # PROTECTED REGION ID(CspSubarray.RemoveTimingBeams) ENABLED START # + """ + Remove the specified Timing Beams from the sub-array. + + :param argin: 'DevVarUShortArray' + The list of Timing Beams IDs to remove from the sub-array. + + :return:None + """ + pass + # PROTECTED REGION END # // CspSubarray.RemoveTimingBeams + + @command( + ) + @DebugIt() + @AdminModeCheck('EndSB') + @StateAndObsStateCheck('EndSB') + def EndSB(self): + # PROTECTED REGION ID(CspSubarray.EndSB) ENABLED START # + """ + Set the subarray obsState to IDLE. + + :return:None + """ + pass + # PROTECTED REGION END # // CspSubarray.EndSB + +# ---------- +# Run server +# ---------- + + +def main(args=None, **kwargs): + # PROTECTED REGION ID(CspSubarray.main) ENABLED START # + return run((CspSubarray,), args=args, **kwargs) + # PROTECTED REGION END # // CspSubarray.main + +if __name__ == '__main__': + main() -- GitLab From 9ee8f3df8f8ec1d11612a26e89431e19150e5700 Mon Sep 17 00:00:00 2001 From: softir Date: Tue, 7 Jan 2020 16:00:56 +0100 Subject: [PATCH 11/33] AT5-257: first release of the CSP.LMC Common CspSubarray Class. --- csp-lmc-common/pogo/CspSubarray.xmi | 885 ++++++++++++++++++++++++++++ 1 file changed, 885 insertions(+) create mode 100644 csp-lmc-common/pogo/CspSubarray.xmi diff --git a/csp-lmc-common/pogo/CspSubarray.xmi b/csp-lmc-common/pogo/CspSubarray.xmi new file mode 100644 index 0000000..c5d0d78 --- /dev/null +++ b/csp-lmc-common/pogo/CspSubarray.xmi @@ -0,0 +1,885 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 4 + + + + + localhost + + + + + + + + + mid_csp/elt/master + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- GitLab From c7411ecf7e7897163f75eb26f0ea821955d2f986 Mon Sep 17 00:00:00 2001 From: Elisabetta Giani Date: Wed, 8 Jan 2020 17:29:24 -0500 Subject: [PATCH 12/33] AT5-257: removed some bugs from decorators. More comments. --- csp-lmc-common/csp-lmc-common/CspMaster.py | 120 +++++++++++---------- csp-lmc-common/utils/decorators.py | 120 +++++++++++---------- 2 files changed, 125 insertions(+), 115 deletions(-) diff --git a/csp-lmc-common/csp-lmc-common/CspMaster.py b/csp-lmc-common/csp-lmc-common/CspMaster.py index 05914cc..bfb5845 100644 --- a/csp-lmc-common/csp-lmc-common/CspMaster.py +++ b/csp-lmc-common/csp-lmc-common/CspMaster.py @@ -97,12 +97,18 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # PROTECTED REGION END # // CspMaster.class_variable # PROTECTED REGION ID(CspMaster.class_protected_methods) ENABLED START # + + # !! NOTE !!: + # In methods and attributes of the class: + # 'se' prefix stands for 'sub-element + # 'cb' suffix stands for 'callback' + #---------------- # Event Callback functions # --------------- def _se_scm_change_event_cb(self, evt): """ - Class protected callback function. + Class callback function. Retrieve the values of the sub-element SCM attributes subscribed for change event at device initialization. @@ -163,9 +169,9 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): def _attributes_change_evt_cb(self, evt): """ - Class protected callback function. - Retrieve the value of the sub-element xxxCommandProgress attribute - subscribed for change event when a long-running command is issued + Class callback function. + Retrieve the value of the sub-element xxxCommandProgress and xxxcmdTimeoutExpired + attributes subscribed for change event when a long-running command is issued on the sub-element device. :param evt: The event data @@ -228,7 +234,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): :return: none """ # NOTE:if we try to access to evt.cmd_name or other paramters, sometime - # the callback crashes withthis error: + # the callback crashes with this error: # terminate called after throwing an instance of 'boost::python::error_already_set' try: # Can happen evt empty?? @@ -242,7 +248,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): msg = "Error!!Command {} ended on device {}.\n".format(evt.cmd_name, evt.device.dev_name()) msg += " Desc: {}".format(evt.errors[0].desc) - #self.dev_logging(msg, tango.LogLevel.LOG_ERROR) + self.dev_logging(msg, tango.LogLevel.LOG_ERROR) print(msg) self._se_cmd_execution_state[evt.device.dev_name()][evt.cmd_name.lower()] = CmdExecState.IDLE # obsState and obsMode values take on the CbfSubarray's values via @@ -259,33 +265,32 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): self.dev_logging(msg, tango.LogLevel.LOG_ERROR) # --------------- - # Class protected methods + # Class methods # --------------- def _update_csp_state(self): """ - Class protected method. - Retrieve the State attribute values of the CSP sub-elements and aggregate - them to build up the CSP global state. + Class method. + Retrieve the *State* attribute value of the CBF sub-element and build up + the CSP global state: only if CBF sub-element is present CSP can work. + The *State* of of PSS and PST sub-elements (if ONLINE/MAINTENANCE) only + contributes to determine the CSP *healthState* :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]) if self._admin_mode in [AdminMode.OFFLINE, AdminMode.NOTFITTED, AdminMode.RESERVED]: self.set_state[tango.DevState.DISABLE] def _update_csp_health_state(self): """ - Class protected method. - Retrieve the healthState attribute of the CSP sub-elements and - aggregate them to build up the CSP health state - + Class method. + Retrieve the *healthState* and *adminMode* attributes of the CSP + sub-elements and aggregate them to build up the CSP *healthState*. + :param: None :return: None @@ -310,7 +315,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): self._healthstate = HealthState.OK elif self._se_health_state[self.CspCbf] in [HealthState.FAILED, HealthState.UNKNOWN, - HealthState.DEGREADED]: + HealthState.DEGRADED]: self._healthstate = self._se_health_state[self.CbfSubarray] else: # if CBF is not ONLINE/MAINTENANCE .... @@ -319,16 +324,12 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): def _connect_to_subelements (self): """ - Class private method. - Establish a *stateless* connection with each CSP sub-element. - Retrieve from the CSP TANGO DB the memorized adminMode value for each - sub-element. - Exceptions are logged. - + Class method. + Establish a *stateless* connection with each CSP sub-element and + subscribe for the sub-element SCM attributes. + :return: None """ - - for fqdn in self._se_fqdn: try: # DeviceProxy to sub-elements @@ -345,7 +346,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # store the sub-element proxies self._se_proxies[fqdn] = device_proxy # subscription of SCM attributes (State, healthState and adminMode). - # Note: subscription is performed also for devices not ONLINE or MAINTENANCE. + # Note: subscription is performed also for devices not ONLINE/MAINTENANCE. # In this way the CspMaster is able to detect a change in the admin value. # if the sub-element device is running,the adminMode is updated here @@ -368,8 +369,6 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): self._se_scm_change_event_cb, stateless=True) self._se_event_id[fqdn]['healthState'] = ev_id - - except KeyError as key_err: log_msg = ("No key {} found".format(str(key_err))) self.dev_logging(log_msg, tango.LogLevel.LOG_WARN) @@ -378,10 +377,10 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): log_msg = ("Failure in connection to {}" " device: {}".format(str(fqdn), str(df.args[0].desc))) self.dev_logging(log_msg, tango.LogLevel.LOG_ERROR) - + def _is_se_device_running (self, subelement_name): """ - *Class protected method.* + *Class method.* Check if a sub-element is exported in the TANGO DB (i.e its TANGO device server is running). @@ -417,21 +416,20 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): def _issue_power_command(self, device_list, **args_dict): """ - Target function called when the command threads start. - The On, Standby and Off methods issue the command on the sub-element devices - in a separate thread. - Information about the command to execute and the list of devices are passed as - argument when the target function is called' + Target function called when the power command threads start. + The *On*, *Standby* and *Off* methods issue the command on the sub-element + devices in a separate thread. + The target function accepts as input arguments the command to execute and + the list of devices to command. :param device_list: tuple with the FQDN of the sub-element devices args_dict: dictionary with information about the command to execute. The dictionary keys are: - cmd_name : the TANGO command name to execute - attr_name: the corresponding command progress attribute to subscribe - - dev_state: the expected finale state for the device transition + - dev_state: the expected end state for the device transition """ #TODO: order the list alphabetically so that the CBF is always the first element to start - # the TANGO command to execute tango_cmd_name = 0 # the command progress attribute to check subscription @@ -462,7 +460,6 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): self._cmd_progress[cmd_name] = 0 # sub-element command execution measured time se_cmd_duration_measured = defaultdict(lambda:defaultdict(lambda:0)) - # timer is started outside the for loop: it has to start only once. # loop on the devices and power-on them sequentially for device in device_list: # set the sub-element command execution flag @@ -472,14 +469,16 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): device_proxy = self._se_proxies[device] print("Issue asynch command {} on device {}:".format(cmd_name, device)) - # Note: if the command ends on the sub-element, the _cmd_ended_cb callback - # is called.This callback sets the sub-element execution state to IDLE if - # an exception is caught during the execution on the sub-element. In both - # cases the command_state = IDLE is right. - # Note: If the command is already running on the sub-element, this device has - # to return without throwing any exception. In this case the current method enters - # the while loop and the execution of the sub-element command is tracked in the - # right way. + # Note: when the command ends on the sub-element, the _cmd_ended_cb callback + # is called. This callback sets the sub-element execution state to FAULT if + # an exception is caught during the execution on the sub-element. + # + # !!Note!!: + # If the command is issued while the same command is already running on a + # sub-element, the sub-element should return without throwing any exception + # (see "SKA System Control Guidelines"). + # In this case the current method enters the while loop and the execution of the + # sub-element command is tracked in the right way. device_proxy.command_inout_asynch(tango_cmd_name, self._cmd_ended_cb) # register the starting time for the command self._se_cmd_starting_time[device] = time.time() @@ -513,7 +512,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # check for other sub-element device State values if self._se_state[device] in [tango.DevState.FAULT, tango.DevState.ALARM]: self._se_cmd_execution_state[device][cmd_name] = CmdExecState.IDLE - msg = ("Device {} is {}}".format(device, self.get_status())) + msg = ("Device {} is {}".format(device, self.get_status())) print(msg) self.dev_logging(msg, tango.LogLevel.LOG_WARN) num_of_failed_device += 1 @@ -546,8 +545,12 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # if the CBF command timeout expires, the CSP power-on is stopped # TODO: verify if this behavior conflicts with ICD print("elapsed_time:{} device {}".format(elapsed_time, device)) - if device.find("cbf") > -1: + if device == self.CspCbf: self.dev_logging("CBF Timeout during power-on!!! Exit", tango.LogLevel.LOG_ERROR) + self._se_timeout_expired[device][cmd_name] = True + self._se_cmd_execution_state[device][cmd_name] = CmdExecState.IDLE + self._timeout_expired[cmd_name] = True + self._cmd_execution_state[cmd_name] = CmdExecState.IDLE return # timeout on the sub-element, skip to the next device break @@ -562,11 +565,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): print("All devices have been handled!") # end of the command: the command has been issued on all the sub-element devices # reset the execution flag for the CSP - self._cmd_execution_state[cmd_name] = CmdExecState.IDLE - self._last_executed_command = cmd_name - # if one or more sub-elements go in timeout, se the CSP timeout flag to True - if num_of_failed_device > 0: - self._timeout_expired[cmd_name] = True + break except KeyError as key_err: msg = "No key {} found".format(str(key_err)) self.dev_logging(msg, tango.LogLevel.LOG_WARN) @@ -574,6 +573,14 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # It should not happen! Verify msg = "Failure reason: {} Desc: {}".format(str(df.args[0].reason), str(df.args[0].desc)) self.dev_logging(msg, tango.LogLevel.LOG_ERROR) + # out of the for loop + self._cmd_execution_state[cmd_name] = CmdExecState.IDLE + self._last_executed_command = cmd_name + # if one or more sub-elements go in timeout, se the CSP timeout flag to True + for device in device_list: + if self._se_timeout_expired[device][cmd_name] == True: + self._timeout_expired[cmd_name] = True + self._se_timeout_expired[device][cmd_name] = False # PROTECTED REGION END # // CspMaster.class_protected_methods # ----------------- @@ -1092,7 +1099,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): self._admin_mode = memorized_attr_dict['adminMode'] self._storage_logging_level = memorized_attr_dict['storageLoggingLevel'] except KeyError as key_err: - self.dev_logging("Key {} not found".format(key_err), tango.LogLevel.INFO) + self.dev_logging("Key {} not found".format(key_err), tango.LogLevel.LOG_INFO) # initialize list with CSP sub-element FQDNs self._se_fqdn = [] @@ -1811,8 +1818,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # The target thread function is common to all the invoked commands. Specifc information # are passed as arguments of the function # args: the list of sub-element FQDNS - # args_dict: dictionary with the specifif command information - print("ON {} STANDBY {}".format( tango.DevState.ON, tango.DevState.STANDBY)) + # args_dict: dictionary with the specific command information args_dict = {'cmd_name':'On', 'attr_name': 'onCommandProgress', 'dev_state': tango.DevState.ON} self._command_thread['on'] = threading.Thread(target=self._issue_power_command, name="Thread-On", args=(device_list,), diff --git a/csp-lmc-common/utils/decorators.py b/csp-lmc-common/utils/decorators.py index 2dc16b8..aa1f4eb 100644 --- a/csp-lmc-common/utils/decorators.py +++ b/csp-lmc-common/utils/decorators.py @@ -118,18 +118,21 @@ class CmdInputArgsCheck(object): def __call__(self, f): @functools.wraps(f) def input_args_check(*args, **kwargs): + # the CspMaster device instance dev_instance = args[0] - # get the information passed as arguments of the decorator + # get the information passed as arguments to the decorator attrs_to_subscribe = self._args try: - exec_cmd = self._kwargs['cmd_name'] + cmd_to_exec = 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 + msg =("No key found for {}".format(str(key_err))) + dev_instance.dev_logging(msg, tango.LogLevel.LOG_WARN) + tango.Except.throw_exception("Command failure", + "Error in input decorator args", + "CmdInputArgsCheck decorator", + tango.ErrSeverity.ERR) input_arg = args[1] - device_list = [] + device_list = input_arg num_of_devices = len(input_arg) if num_of_devices == 0: # no input argument -> switch on all sub-elements @@ -140,31 +143,40 @@ class CmdInputArgsCheck(object): # 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 + # exception only if the requested command is different from the one + # is already running (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 + list_of_running_cmd = [cmd_name for cmd_name, cmd_state in dev_instance._cmd_execution_state.items() + if cmd_state == CmdExecState.RUNNING] + if list_of_running_cmd: + # if a command is running, check if its the requested one + if len(list_of_running_cmd) > 1: + # should not happen!! throw an exception + err_msg = "{} power commands are running. Something strange!".format(len(list_of_running_cmd)) + tango.Except.throw_exception("Command failure", + err_msg, + "CmdInputArgsCheck decorator", + tango.ErrSeverity.ERR) + if list_of_running_cmd[0] == cmd_to_exec: + # the requested command is already running + # return with no exception (see SKA Guidelines) + msg = ("Command {} is already running".format(cmd_to_exec)) + dev_instance.dev_logging(msg, tango.LogLevel.LOG_INFO) + return + else: + # another power command is already running + err_msg = ("Can't execute command {}:" + " power command {} is already running".format(cmd_to_exec, list_of_running_cmd[0])) + tango.Except.throw_exception("Command failure", + err_msg, + "CmdInputArgsCheck decorator", + tango.ErrSeverity.ERR) + # check for devices that are not ONLINE/MAINTENANCE 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 @@ -173,16 +185,15 @@ class CmdInputArgsCheck(object): 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)) + if device == dev_instance.CspCbf: + msg = ("Can't execute command {} with " + "device {} {}".format(cmd_to_exec, + device, + AdminMode(dev_instance._se_admin_mode[device]).name)) tango.Except.throw_exception("Command failure", msg, - "On()", + "CmdInputArgsCheck decorator", tango.ErrSeverity.ERR) - - return device_to_remove.append(device) print("Devices to remove from the list:", device_to_remove) continue @@ -192,53 +203,46 @@ class CmdInputArgsCheck(object): dev_instance.dev_logging("No device to power on", tango.LogLevel.LOG_INFO) return command_timeout = 0 + # build the sub-element attribute name storing the command duration + # expected value. + cmd_time_attr_name = cmd_to_exec + "DurationExpected" 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 + dev_instance._se_cmd_execution_state[device][cmd_to_exec] = 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 + dev_instance._timeout_expired[cmd_to_exec] = 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 + # call device_proxy.onCmdDurationExpected throws 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) + #dev_instance._se_cmd_duration_expected[device][cmd_to_exec] = device_proxy.onCmdDurationExpected + dev_instance._se_cmd_duration_expected[device][cmd_to_exec] = 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) + dev_instance.dev_logging((tango_err.args[0].desc), tango.LogLevel.LOG_INFO) 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) - + except tango.DevFailed as tango_err: + dev_instance.dev_logging((tango_err.args[0].desc), tango.LogLevel.LOG_INFO) # 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 - + command_timeout += dev_instance._se_cmd_duration_expected[device][cmd_to_exec] + # device loop end # 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), + if command_timeout > dev_instance._cmd_duration_expected[cmd_to_exec]: + dev_instance._cmd_duration_expected[cmd_to_exec] = command_timeout + dev_instance.dev_logging("Modified the {} command Duration Expected value!!".format(cmd_to_exec), tango.LogLevel.LOG_INFO) - print("CmdInputArgsCheck end!!") return f(*args, **kwargs) return input_args_check -- GitLab From 6c31e10575e8e711ef919c591ec239f3ec8443ae Mon Sep 17 00:00:00 2001 From: softir Date: Mon, 13 Jan 2020 18:06:28 +0100 Subject: [PATCH 13/33] AT5-262: Added TANGO devices to perform the monitoring of the CSP.LMC Capabilities. CspMaster implements capabilty attributes as forwarded attributes. Added alarm flags for each power command. Added dsconfig file to configure CSP.LMC TANGO DB. Added docker and docker-compose files to build the docker images and containers. --- csp-lmc-common/Pipfile | 35 + .../csp-lmc-common/CspCapabilityMonitor.py | 364 ++++++ csp-lmc-common/csp-lmc-common/CspMaster.py | 650 +++++----- csp-lmc-common/pogo/CspCapabilityMonitor.py | 227 ++++ csp-lmc-common/pogo/CspCapabilityMonitor.xmi | 211 ++++ csp-lmc-common/pogo/CspMaster.xmi | 245 ++-- .../DeviceTestMaster/DeviceTestMaster.py | 211 ++++ .../simulators/DeviceTestMaster/README.md | 6 + .../simulators/pogo/DeviceTestMaster.xmi | 344 +++++ csp-lmc-common/utils/cspcommons.py | 2 + csp-lmc-common/utils/decorators.py | 6 +- docker/.make/.make-release-support | 106 ++ docker/.make/Makefile.mk | 137 ++ docker/.release | 22 + docker/Dockerfile | 10 + docker/Makefile | 198 +++ docker/config/csplmc_dsconfig.json | 420 ++++++ docker/config/csplmc_dsconfig.json.sav | 406 ++++++ docker/config/midcbf_dsconfig.json | 1121 +++++++++++++++++ docker/csp-lmc.yml | 133 ++ docker/csp-tangodb.yml | 50 + docker/mid-cbf-mcs.yml | 288 +++++ 22 files changed, 4707 insertions(+), 485 deletions(-) create mode 100644 csp-lmc-common/Pipfile create mode 100644 csp-lmc-common/csp-lmc-common/CspCapabilityMonitor.py create mode 100644 csp-lmc-common/pogo/CspCapabilityMonitor.py create mode 100644 csp-lmc-common/pogo/CspCapabilityMonitor.xmi create mode 100644 csp-lmc-common/simulators/DeviceTestMaster/DeviceTestMaster.py create mode 100644 csp-lmc-common/simulators/DeviceTestMaster/README.md create mode 100644 csp-lmc-common/simulators/pogo/DeviceTestMaster.xmi create mode 100644 docker/.make/.make-release-support create mode 100644 docker/.make/Makefile.mk create mode 100644 docker/.release create mode 100644 docker/Dockerfile create mode 100644 docker/Makefile create mode 100644 docker/config/csplmc_dsconfig.json create mode 100644 docker/config/csplmc_dsconfig.json.sav create mode 100644 docker/config/midcbf_dsconfig.json create mode 100644 docker/csp-lmc.yml create mode 100644 docker/csp-tangodb.yml create mode 100644 docker/mid-cbf-mcs.yml diff --git a/csp-lmc-common/Pipfile b/csp-lmc-common/Pipfile new file mode 100644 index 0000000..358cd26 --- /dev/null +++ b/csp-lmc-common/Pipfile @@ -0,0 +1,35 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +# numpy and pytango versions must match those in the ska-python-builder image, +# otherwise pytango will be recompiled. +numpy = "==1.17.2" +pytango = "==9.3.1" +# itango is added to make it easier to exercise the device in a CLI session, +# but it's not mandatory. If you remove itango, you should also remove the +# 'RUN ipython profile create' line from Dockerfile. +itango = "*" +# If you want to debug devices running in containers, add pydevd to the dependencies +# pydevd = "*" + +[dev-packages] +docutils = "*" +MarkupSafe = "*" +Pygments = "*" +pylint = "*" +pytest = "*" +pytest-cov = "*" +pytest-pylint = "*" +pytest-json-report = "*" +python-dotenv = ">=0.5.1" +ptvsd = "*" +Sphinx = "*" +sphinx_rtd_theme = "*" +sphinx-autobuild = "*" +sphinxcontrib-websupport = "*" + +[requires] +python_version = "3" diff --git a/csp-lmc-common/csp-lmc-common/CspCapabilityMonitor.py b/csp-lmc-common/csp-lmc-common/CspCapabilityMonitor.py new file mode 100644 index 0000000..c10e819 --- /dev/null +++ b/csp-lmc-common/csp-lmc-common/CspCapabilityMonitor.py @@ -0,0 +1,364 @@ +# -*- coding: utf-8 -*- +# +# This file is part of the CspCapabilityMonitor project +# +# INAF, SKA Telescope +# +# Distributed under the terms of the GPL license. +# See LICENSE.txt for more info. + +""" CSP.LMC Common Class + +CSP.LMC Common Class designed to monitoring the SCM +attributes of the CSP Capabilities: SearchBeams, TimingBeams, +VlbiBeams, Receptors/Stations. +""" +# 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 +# PROTECTED REGION END# //CspMaster.standardlibray_import +# 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 +# Additional import +# PROTECTED REGION ID(CspCapabilityMonitor.additionnal_import) ENABLED START # + +from skabase.SKABaseDevice import SKABaseDevice +from skabase.auxiliary import utils +# 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, ObsMode + +# PROTECTED REGION END # // CspCapabilityMonitor.additionnal_import + +__all__ = ["CspCapabilityMonitor", "main"] + +class CspCapabilityMonitor(with_metaclass(DeviceMeta,SKABaseDevice)): + """ + CSP.LMC Common Class designed to monitoring the SCM + attributes of the CSP Capabilities: SearchBeams, TimingBeams, + VlbiBeams, Receptors/Stations. + + **Properties:** + + - Device Property + + CapabilityDevices + - The list of the CSP Capability devices FQDNs monitored\nby the instance. + - Type:'DevVarStringArray' + + + """ + # PROTECTED REGION ID(CspCapabilityMonitor.class_variable) ENABLED START # + # PROTECTED REGION END # // CspCapabilityMonitor.class_variable + + # ----------------- + # Device Properties + # ----------------- + + CapabilityDevices = device_property( + dtype='DevVarStringArray', + ) + + # ---------- + # Attributes + # ---------- + + numOfUnassignedIDs = attribute( + dtype='DevUShort', + ) + + capabilityState = attribute( + dtype=('DevState',), + max_dim_x=1500, + label="Capability State", + doc="Report the State of the capabilities.", + ) + + capabilityHealthState = attribute( + dtype=('DevUShort',), + max_dim_x=1500, + ) + + capabilityAdminMode = attribute( + dtype=('DevUShort',), + access=AttrWriteType.READ_WRITE, + max_dim_x=1500, + ) + + capabilityObsState = attribute( + dtype=('DevUShort',), + max_dim_x=1500, + ) + + unassignedIDs = attribute( + dtype=('DevUShort',), + max_dim_x=1500, + ) + + cspCapabilityAddresses = attribute( + dtype=('DevString',), + max_dim_x=1500, + label="CSP.LMC Capability devices FQDNs", + doc="The FQDNs of the CSP.LMC Capability devices: SearchBeams, TimingBeams, VlbiBeams,\nReceptors (MID instance), Stations (LOW instance)", + ) + + capabilityMembership = attribute( + dtype=('DevUShort',), + max_dim_x=1500, + label="Capability sub-array affiliation", + ) + + # --------------- + # General methods + # --------------- + + def init_device(self): + """Initialises the attributes and properties of the CspCapabilityMonitor.""" + SKABaseDevice.init_device(self) + # PROTECTED REGION ID(CspCapabilityMonitor.init_device) ENABLED START # + self.set_state(tango.DevState.INIT) + self._capability_fqdn = self.CapabilityDevices + print("self._capability_fqdn:", self._capability_fqdn) + self._capability_proxies = {} + self._event_id = defaultdict(lambda: defaultdict(lambda: 0)) + self._capability_state = {} + self._capability_health_state = {} + self._capability_admin_mode = {} + self._capability_obs_state = {} + self._capability_membership = {} + self._capability_proxies = {} + self._unassigned_ids = [0]* len(self._capability_fqdn) + self._capability_state = {fqdn:tango.DevState.DISABLE for fqdn in self._capability_fqdn} + self._capability_health_state = {fqdn:HealthState.UNKNOWN for fqdn in self._capability_fqdn} + self._capability_admin_mode = {fqdn:AdminMode.NOTFITTED for fqdn in self._capability_fqdn} + self._capability_obs_state = {fqdn:ObsState.IDLE for fqdn in self._capability_fqdn} + self._capability_membership = {fqdn:0 for fqdn in self._capability_fqdn} + self._csp_tango_db = 0 + + # establish connection with the CSP.LMC TANGO DB + self._csp_tango_db = tango.Database() + attribute_properties = self._csp_tango_db.get_device_attribute_property(self.get_name(), + {'adminMode': ['__value']}) + admin_mode_memorized = attribute_properties['adminMode']['__value'] + self._admin_mode = int(admin_mode_memorized[0]) + self._connect_to_capability() + self._healthState = HealthState.OK + self.set_state(tango.DevState.ON) + + def _connect_to_capability(self): + # connect to DB and read the adminMode of each capability + + for fqdn in self.CapabilityDevices: + attribute_properties = self._csp_tango_db.get_device_attribute_property(fqdn, {'adminMode': ['__value'],}) + try: + admin_mode_memorized = attribute_properties['adminMode']['__value'] + self._capability_admin_mode[fqdn] = int(admin_mode_memorized[0]) + except KeyError as key_err: + print(key_err) + try: + device_proxy = tango.DeviceProxy(fqdn) + self._capability_proxies[fqdn] = device_proxy + ev_id = device_proxy.subscribe_event("adminMode", + EventType.CHANGE_EVENT, + self._attributes_change_event_cb, + stateless=True) + self._event_id[fqdn] = ev_id + + ev_id = device_proxy.subscribe_event("State", + EventType.CHANGE_EVENT, + self._attributes_change_event_cb, + stateless=True) + self._event_id[fqdn] = ev_id + + ev_id = device_proxy.subscribe_event("healthState", + EventType.CHANGE_EVENT, + self._attributes_change_event_cb, + stateless=True) + self._event_id[fqdn] = ev_id + + ev_id = device_proxy.subscribe_event("obsState", + EventType.CHANGE_EVENT, + self._attributes_change_event_cb, + stateless=True) + self._event_id[fqdn]= ev_id + + ev_id = device_proxy.subscribe_event("subarrayMemebership", + EventType.CHANGE_EVENT, + self._attributes_change_event_cb, + stateless=True) + self._event_id[fqdn]['subarrayMembership'] = ev_id + except tango.DevFailed as tango_err: + print(tango_err.args[0].desc) + + def _attributes_change_event_cb(self, evt): + dev_name = evt.device.dev_name() + if not evt.err: + try: + if dev_name in self.CapabilityDevices: + if evt.attr_value.name.lower() == "state": + print("{}: received event on {} value {}".format(dev_name, + evt.attr_value.name, + evt.attr_value.value)) + self._capability_state[dev_name] = evt.attr_value.value + + elif evt.attr_value.name.lower() == "healthstate": + self._capability_health_state[dev_name] = evt.attr_value.value + elif evt.attr_value.name.lower() == "adminmode": + print("device: {} adminMode value {}".format(dev_name,evt.attr_value.value )) + self._capability_admin_mode[dev_name] = evt.attr_value.value + elif evt.attr_value.name.lower() == "obsstate": + print("device: {} adminMode value {}".format(dev_name,evt.attr_value.value )) + self._capability_obsstate[dev_name] = evt.attr_value.value + elif evt.attr_value.name.lower() == "subarrayMembershp": + self._capability_membership[dev_name] = evt.attr_value.value + else: + print("Attribute {} not still " + "handled".format(evt.attr_name)) + else: + print("Unexpected change event for" + " attribute: {}".format(str(evt.attr_name))) + return + log_msg = "New value for {} is {}".format(str(evt.attr_name), + str(evt.attr_value.value)) + 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": + self._capability_state[dev_name] = tango.DevState.DISABLED + self._capability_health_state[dev_name] = HealthState.UNKNOWN + self._capability_admin_mode[dev_name] = AdminMode.NOTFITTED + self._capability_health_state[dev_name] = ObsState.IDLE + log_msg = item.reason + ": on attribute " + str(evt.attr_name) + print(log_msg) + + def always_executed_hook(self): + """Method always executed before any TANGO command is executed.""" + # PROTECTED REGION ID(CspCapabilityMonitor.always_executed_hook) ENABLED START # + # PROTECTED REGION END # // CspCapabilityMonitor.always_executed_hook + + def delete_device(self): + """Hook to delete resources allocated in init_device. + + This method allows for any memory or other resources allocated in the + init_device method to be released. This method is called by the device + destructor and by the device Init command. + """ + # PROTECTED REGION ID(CspCapabilityMonitor.delete_device) ENABLED START # + event_to_remove = [] + for fqdn in self._capability_fqdn: + try: + self._capability_proxies[fqdn].unsubscribe_event(self._event_id[fqdn]) + event_to_remove.append(fqdn) + except KeyError as key_err: + msg = ("Failure unsubscribing event {} " + "on device {}. Reason: {}".format(event_id, + fqdn, + key_err)) + self.dev_logging(msg, tango.LogLevel.LOG_ERROR) + # remove the events id from the list + for k in _event_id[fqdn].remove(k): + if self._event_id[fqdn]: + msg = "Still subscribed events: {}".format(self._event_id) + self.dev_logging(msg, tango.LogLevel.LOG_WARN) + else: + # remove the dictionary element + self._event_id.pop(fqdn) + + self._capability_fqdn.clear() + self._capability_state.clear() + self._capability_health_state.clear() + self._capability_admin_mode.clear() + self._capability_obs_state.clear() + self._capability_membership.clear() + # PROTECTED REGION END # // CspCapabilityMonitor.delete_device + # ------------------ + # Attributes methods + # ------------------ + + def read_numOfUnassignedIDs(self): + # PROTECTED REGION ID(CspCapabilityMonitor.numOfUnassignedIDs_read) ENABLED START # + """Return the numOfUnassignedIDs attribute.""" + return len(self._unassigned_ids) + # PROTECTED REGION END # // CspCapabilityMonitor.numOfUnassignedIDs_read + + def read_capabilityState(self): + # PROTECTED REGION ID(CspCapabilityMonitor.capabilityState_read) ENABLED START # + """Return the capabilityState attribute.""" + return list(self._capability_state.values()) + # PROTECTED REGION END # // CspCapabilityMonitor.capabilityState_read + + def read_capabilityHealthState(self): + # PROTECTED REGION ID(CspCapabilityMonitor.capabilityHealthState_read) ENABLED START # + """Return the capabilityHealthState attribute.""" + return list(self._capability_health_state.values()) + + # PROTECTED REGION END # // CspCapabilityMonitor.capabilityHealthState_read + + def read_capabilityAdminMode(self): + # PROTECTED REGION ID(CspCapabilityMonitor.capabilityAdminMode_read) ENABLED START # + """Return the capabilityAdminMode attribute.""" + return list(self._capability_admin_mode.values()) + # PROTECTED REGION END # // CspCapabilityMonitor.capabilityAdminMode_read + + def write_capabilityAdminMode(self, value): + # PROTECTED REGION ID(CspCapabilityMonitor.capabilityAdminMode_write) ENABLED START # + """Set the capabilityAdminMode attribute.""" + pass + # PROTECTED REGION END # // CspCapabilityMonitor.capabilityAdminMode_write + + def read_capabilityObsState(self): + # PROTECTED REGION ID(CspCapabilityMonitor.capabilityObsState_read) ENABLED START # + """Return the capabilityObsState attribute.""" + return list(self._capability_obs_state.values()) + # PROTECTED REGION END # // CspCapabilityMonitor.capabilityObsState_read + + def read_unassignedIDs(self): + # PROTECTED REGION ID(CspCapabilityMonitor.unassignedIDs_read) ENABLED START # + """Return the unassignedIDs attribute.""" + return self._unassigned_ids + # PROTECTED REGION END # // CspCapabilityMonitor.unassignedIDs_read + + def read_cspCapabilityAddresses(self): + # PROTECTED REGION ID(CspCapabilityMonitor.capabilityAddresses_read) ENABLED START # + return self.CapabilityDevices + # PROTECTED REGION END # // CspCapabilityMonitor.capabilityAddresses_read + + def read_capabilityMembership(self): + # PROTECTED REGION ID(CspCapabilityMonitor.membership_read) ENABLED START # + """Return the membership attribute.""" + return list(self._capability_membership.values()) + # PROTECTED REGION END # // CspCapabilityMonitor.membership_read + # -------- + # Commands + # -------- + +# ---------- +# Run server +# ---------- + +def main(args=None, **kwargs): + # PROTECTED REGION ID(CspCapabilityMonitor.main) ENABLED START # + return run((CspCapabilityMonitor,), args=args, **kwargs) + # PROTECTED REGION END # // CspCapabilityMonitor.main + +if __name__ == '__main__': + main() diff --git a/csp-lmc-common/csp-lmc-common/CspMaster.py b/csp-lmc-common/csp-lmc-common/CspMaster.py index bfb5845..7004a11 100644 --- a/csp-lmc-common/csp-lmc-common/CspMaster.py +++ b/csp-lmc-common/csp-lmc-common/CspMaster.py @@ -90,6 +90,18 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): VlbiBeams - TANGO FQDN of the CSP.LMC Vlbi Capabilities - Type:'DevVarStringArray' + + SearchBeamsMonitor + - TANGO Device to monitor the CSP SearchBeams Capability\ndevices. + - Type:'DevString' + + TimingBeamsMonitor + - TANGO Device to monitor the CSP TimingBeams Capability\ndevices. + - Type:'DevString' + + VlbiBeamsMonitor + - TANGO Device to monitor the CSP VlbiBeams Capability\ndevices. + - Type:'DevString' """ # PROTECTED REGION ID(CspMaster.class_variable) ENABLED START # @@ -97,12 +109,10 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # PROTECTED REGION END # // CspMaster.class_variable # PROTECTED REGION ID(CspMaster.class_protected_methods) ENABLED START # - # !! NOTE !!: # In methods and attributes of the class: # 'se' prefix stands for 'sub-element # 'cb' suffix stands for 'callback' - #---------------- # Event Callback functions # --------------- @@ -156,8 +166,13 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # API_EventTimeout: if sub-element device not reachable it transitions # to UNKNOWN state. if item.reason == "API_EventTimeout": - self._se_state[dev_name] = tango.DevState.UNKNOWN - self._se_health_state[dev_name] = HealthState.UNKNOWN + # only if the device is ONLINE/MAINTENANCE, its State is set to + # UNKNOWN when there is a timeout on connection, otherwise its + # State should be reported always as DISABLE + if self._se_admin_mode[dev_name] in [AdminMode.ONLINE, + AdminMode.MAINTENANCE]: + self._se_state[dev_name] = tango.DevState.UNKNOWN + self._se_health_state[dev_name] = HealthState.UNKNOWN # if the device is executing the shutdown the state is set to # OFF if self._se_cmd_execution_state[dev_name]['off'] == CmdExecState.RUNNING: @@ -250,7 +265,8 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): msg += " Desc: {}".format(evt.errors[0].desc) self.dev_logging(msg, tango.LogLevel.LOG_ERROR) print(msg) - self._se_cmd_execution_state[evt.device.dev_name()][evt.cmd_name.lower()] = CmdExecState.IDLE + self._se_cmd_execution_state[evt.device.dev_name()][evt.cmd_name.lower()] = CmdExecState.FAILED + self._alarm_message[evt.cmd_name] = msg # obsState and obsMode values take on the CbfSubarray's values via # the subscribe/publish mechanism else: @@ -263,7 +279,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): except Exception as ex: msg = "CommandCallBack cmd_ended general exception: {}".format(str(ex)) self.dev_logging(msg, tango.LogLevel.LOG_ERROR) - + # --------------- # Class methods # --------------- @@ -377,42 +393,43 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): log_msg = ("Failure in connection to {}" " device: {}".format(str(fqdn), str(df.args[0].desc))) self.dev_logging(log_msg, tango.LogLevel.LOG_ERROR) - - def _is_se_device_running (self, subelement_name): + + def _is_device_running(self, device_fqdn, proxy_list=None): """ *Class method.* - Check if a sub-element is exported in the TANGO DB (i.e its TANGO + Check if a TANGO device is exported in the TANGO DB (i.e its TANGO device server is running). - If the device is not in the list of the connected sub-elements, - a connection with the device is performed. + If the device is not in the list of proxies a DeviceProxy on + the device is performed. - :param: subelement_name : the FQDN of the sub-element + :param: device_fqdn : the FQDN of the sub-element :type: `DevString` + :param proxy_list: the list with the proxies for the device + :type: list of TANGO proxies :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) - try: - proxy = tango.DeviceProxy(subelement_name) + print(proxy_list) + if proxy_list: + try: + proxy = proxy_list[device_fqdn] # execute a ping to detect if the device is actually running proxy.ping() - self._se_proxies[subelement_name] = proxy - except tango.DevFailed as df: - return False - 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 True + 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) + try: + proxy = tango.DeviceProxy(device_fqdn) + proxy.ping() + proxy_list[device_fqdn] = proxy + except tango.DevFailed: return False - return True + return True def _issue_power_command(self, device_list, **args_dict): """ @@ -465,6 +482,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # set the sub-element command execution flag self._se_cmd_execution_state[device][cmd_name] = CmdExecState.RUNNING se_cmd_duration_measured[device][cmd_name] = 0 + self._alarm_message[cmd_name] = '' try: device_proxy = self._se_proxies[device] print("Issue asynch command {} on device {}:".format(cmd_name, device)) @@ -511,16 +529,23 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): break # check for other sub-element device State values if self._se_state[device] in [tango.DevState.FAULT, tango.DevState.ALARM]: - self._se_cmd_execution_state[device][cmd_name] = CmdExecState.IDLE - msg = ("Device {} is {}".format(device, self.get_status())) - print(msg) - self.dev_logging(msg, tango.LogLevel.LOG_WARN) + self._se_cmd_execution_state[device][cmd_name] = CmdExecState.FAILED + self._alarm_message[device] = ("Device {} is {}".format(device, self.get_status())) + print(self._alarm_message) + self.dev_logging(self._alarm_message[device], tango.LogLevel.LOG_WARN) num_of_failed_device += 1 break # check if sub-element command ended throwing an exception: in this case the - # 'cmd_ended_cb' callback it is invoked. The callback log the exceptionand - # sets the sub-element execution state to IDLE) - if self._se_cmd_execution_state[device][cmd_name] == CmdExecState.IDLE: + # 'cmd_ended_cb' callback is invoked. The callback log the exception and + # sets the sub-element execution state to FAILED. + # NOTE: as per the "SKA Control Guidelines", a sub-element shall not throw an + # exception if the sub-element is already in the requested final state or if the + # command is already running. + # A different behaviour causes a wrong behavior of the current function. If for + # example the sub-element raises an exception if the device is already ON when On + # command is issued, the cmd_end_cb calbback is invoked and the alarm flag is set: + # the device results failed, but this is not true. + if self._se_cmd_execution_state[device][cmd_name] == CmdExecState.FAILED: # execution ended for this sub-element, skip to the next one num_of_failed_device += 1 break @@ -529,25 +554,25 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # for change event # 2- the CspMaster 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 timeout flag is set - # to True. + # 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._se_cmd_starting_time[device] if (elapsed_time > self._se_cmd_duration_expected[device][cmd_name] or - self._se_timeout_expired[device][cmd_name] == True): + self._se_cmd_execution_state[device][cmd_name] == CmdExecState.TIMEOUT): msg = ("Timeout executing {} command on device {}".format(cmd_name, device)) print(msg) - self.dev_logging(msg, tango.LogLevel.LOG_WARN) + self.dev_logging(msg, tango.LogLevel.LOG_WARN) + self._se_cmd_execution_state[device][cmd_name] = CmdExecState.TIMEOUT num_of_failed_device += 1 - self._se_timeout_expired[device][cmd_name] = True self._se_cmd_execution_state[device][cmd_name] = CmdExecState.IDLE # if the CBF command timeout expires, the CSP power-on is stopped # TODO: verify if this behavior conflicts with ICD print("elapsed_time:{} device {}".format(elapsed_time, device)) if device == self.CspCbf: self.dev_logging("CBF Timeout during power-on!!! Exit", tango.LogLevel.LOG_ERROR) - self._se_timeout_expired[device][cmd_name] = True + self._timeout_expired[cmd_name] = True self._se_cmd_execution_state[device][cmd_name] = CmdExecState.IDLE self._timeout_expired[cmd_name] = True self._cmd_execution_state[cmd_name] = CmdExecState.IDLE @@ -574,13 +599,62 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): msg = "Failure reason: {} Desc: {}".format(str(df.args[0].reason), str(df.args[0].desc)) self.dev_logging(msg, tango.LogLevel.LOG_ERROR) # out of the for loop + # reset the CSP command execution flag self._cmd_execution_state[cmd_name] = CmdExecState.IDLE self._last_executed_command = cmd_name - # if one or more sub-elements go in timeout, se the CSP timeout flag to True + # if one or more sub-elements goes in timeout or alarm, set the CSP + # corresponding attribute for device in device_list: - if self._se_timeout_expired[device][cmd_name] == True: + # reset the CSP sub-element command execution flag + if self._se_cmd_execution_state[device][cmd_name] == CmdExecState.TIMEOUT: + # set the CSP timeout flag self._timeout_expired[cmd_name] = True - self._se_timeout_expired[device][cmd_name] = False + if self._se_cmd_execution_state[device][cmd_name] == CmdExecState.FAILED: + # set the CSP timeout flag + self._alarm_raised[cmd_name] = True + # reset the CSP sub-element command execution flag + self._se_cmd_execution_state[device][cmd_name] = CmdExecState.IDLE + + def _se_write_adminMode(self, value, device_fqdn): + """ + *Class method.* + + Set the administrative mode of the specified device. + :param: subelement_name : the FQDN of the sub-element + :type: `DevString` + :return: True if the connection with the subarray is established, + False otherwise + """ + # check if the device is exported in the TANGO DB (that is the server is running) + if value not in [AdminMode.ONLINE, AdminMode.MAINTENANCE, AdminMode.OFFLINE, + AdminMode.NOTFITTED, AdminMode.RESERVED]: + msg = "Invalid {} value for adminMode attribute".format(value) + tango.Except.throw_exception("Command failed", + msg, + "write_adminMode", + tango.ErrSeverity.ERR) + # check if the sub-element administrative mode has already the requested value + if value == self._se_admin_mode[device_fqdn]: + return + if self._is_device_running(device_fqdn, self._se_proxies): + try: + proxy = self._se_proxies[device_fqdn] + proxy.write_attribute_asynch('adminMode', value) + # TODO: add checks for timeout/errors + except KeyError as key_err: + msg = "Can't retrieve the information of key {}".format(key_err) + self.dev_logging(msg, tango.LogLevel.LOG_ERROR) + tango.Except.throw_exception("DevFailed excpetion", msg, + "write adminMode", tango.ErrSeverity.ERR) + def _connect_capabilities_monitor(self): + """ + """ + # build the list with the Capability monitor devices + capability_monitor_device = [self.SearchBeamsMonitor, + self.TimingBeamsMonitor, + self.VlbiBeamsMonitor] + for fqdn in capability_monitor_device: + self._capability_proxy[fqdn] = tango.DeviceProxy(fqdn) # PROTECTED REGION END # // CspMaster.class_protected_methods # ----------------- @@ -615,6 +689,18 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): dtype='DevVarStringArray', ) + SearchBeamsMonitor = device_property( + dtype='DevString', + ) + + TimingBeamsMonitor = device_property( + dtype='DevString', + ) + + VlbiBeamsMonitor = device_property( + dtype='DevString', + ) + # ---------- # Attributes # ---------- @@ -856,20 +942,51 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): enum_labels=["ON-LINE", "OFF-LINE", "MAINTENANCE", "NOT-FITTED", "RESERVED", ], ) - unassignedSearchBeamsNum = attribute( - dtype='DevUShort', - label="Number of unassigned SearchBeam Capabilities", - doc=("Report the number of unassigned SearchBeam Capabilities." - " This number does not take in account the number of *reserved*" - " SearchBeams."), - ) - numOfDevCompletedTask = attribute( dtype='DevUShort', label="Number of devices that completed the task", doc="Number of devices that completed the task", ) + onCmdAlarm = attribute( + dtype='DevBoolean', + label="CBF command alarm flag", + polling_period=1000, + doc="Alarm flag set when the On command fails with error(s).", + ) + + onAlarmMessage = attribute( + dtype='DevString', + label="On execution alarm message", + doc="Alarm message when the On command fails with error(s).", + ) + + offCmdAlarm = attribute( + dtype='DevBoolean', + label="Off execution alarm flag", + polling_period=1000, + doc="Alarm flag set when the Off command fails with error(s).", + ) + + offAlarmMessage = attribute( + dtype='DevString', + label="Off execution alarm message", + doc="Alarm message when the Off command fails with error(s).", + ) + + standbyCmdAlarm = attribute( + dtype='DevBoolean', + label="Standby execution alarm message", + polling_period=1000, + doc="Alarm flag set when the Standby command fails with error(s).", + ) + + standbyAlarmMessage = attribute( + dtype='DevString', + label="Standby execution alarm message", + doc="Alarm message when the Standby command fails with error(s).", + ) + availableCapabilities = attribute( dtype=('DevString',), max_dim_x=20, @@ -960,98 +1077,102 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): max_dim_x=16, doc="CSPSubarrays FQDN", ) - - searchBeamAddresses = attribute( + + listOfDevCompletedTask = attribute( dtype=('DevString',), - max_dim_x=1500, - label="SearchBeamCapabilities FQDNs", - doc="TANGO FQDNs of the CSP.LMC SearchBeam Capabilities", + max_dim_x=100, + label="List of devices that completed the task", + doc="List of devices that completed the task", ) - - timingBeamAddresses = attribute( - dtype=('DevString',), - max_dim_x=16, - label="TimingBeam Capabilities FQDN", - doc="TANGO FQDNs of the CSP.LMC TimingBeam Capabilities", + reportSearchBeamState = attribute(name="reportSearchBeamState", + label="SearchBeam Capabilities State", + forwarded=True ) - - vlbiBeamAddresses = attribute( - dtype=('DevString',), - max_dim_x=20, - label="VLBIBeam Capabilities FQDNs", - doc="TANGO FQDNs of the CSP.LMC VlbiBeam Capabilities", + reportSearchBeamHealthState = attribute(name="reportSearchBeamHealthState", + label="SearchBeam Capabilities healthState", + forwarded=True ) - - searchBeamMembership = attribute( - dtype=('DevUShort',), - max_dim_x=1500, - label="SearchBeam Memebership", - doc="Reports the sub-array affiliation for each SearchBeam Capabiity.", + reportSearchBeamObsState = attribute(name="reportSearchBeamObsState", + label="SearchBeam Capabilities obsState", + forwarded=True ) - - timingBeamMembership = attribute( - dtype=('DevUShort',), - max_dim_x=16, - label="TimingBeam Membership", - doc="Report the sub-array affiliation for each TimingBeam Capability.", + reportSearchBeamAdminMode = attribute(name="reportSearchBeamAdminMode", + label="SearchBeam Capabilities adminMode", + forwarded=True ) - - vlbiBeamMembership = attribute( - dtype=('DevUShort',), - max_dim_x=20, - label="VLBI Beam membership", - doc="Report the sub-array affiliation for each VlbiBbeam Capability.", + reportTimingBeamState = attribute(name="reportTimingBeamState", + label="TimingBeam Capabilities State", + forwarded=True ) - - unassignedSearchBeamIDs = attribute( - dtype=('DevUShort',), - max_dim_x=1500, - label="Unassigned SeachBeam Capabilities IDs", - doc=("Report the list of SearchBeam Capability IDs that are not assigned to any sub-array." - "SearchBeams are assigned to a sub-array in groups of three because each PSS LRU " - "can process up to three SearchBeams at time. A SearchBeam is marked as *reserved* to" - " a sub-array if the other SearchBeams of the group are already assigned to that sub-array." - " The IDs of the SearchBeams marked as *reserved*, are not included in this list." - " The SCM values of a unassigned SearchBeam Capabiity are " - " State = OFF adminMode = ONLINE or MAINTENACE obsState = IDLE healthState = OK " - "obsMode = IDLE (reserved flag = False)"), - ) - - reservedSearchBeamIDs = attribute( - dtype=(('DevUShort',),), - max_dim_x=16, max_dim_y=1000, + reportTimingBeamHealthState = attribute(name="reportTimingBeamHealthState", + label="TimingBeam Capabilities healthState", + forwarded=True + ) + reportTimingBeamObsState = attribute(name="reportTimingBeamObsState", + label="TimingBeam Capabilities obsState", + forwarded=True + ) + reportTimingBeamAdminMode = attribute(name="reportTimingBeamAdminMode", + label="TimingBeam Capabilities adminMode", + forwarded=True + ) + reportVlbiBeamState = attribute(name="reportVlbiBeamState", + label="VlbiBeam Capabilities State", + forwarded=True + ) + reportVlbiBeamHealthState = attribute(name="reportVlbiBeamHealthState", + label="VlbiBeam Capabilities healthState", + forwarded=True + ) + reportVlbiBeamObsState = attribute(name="reportVlbiBeamObsState", + label="VlbiBeam Capabilities obsState", + forwarded=True + ) + reportVlbiBeamAdminMode = attribute(name="reportVlbiBeamAdminMode", + label="VlbiBeam Capabilities adminMode", + forwarded=True + ) + searchBeamAddresses = attribute(name="searchBeamAddresses", + label="SearchBeams Capability devices addresses", + forwarded=True + ) + timingBeamAddresses = attribute(name="timingBeamAddresses", + label="TimingBeams Capability devices addresses", + forwarded=True + ) + vlbiBeamAddresses = attribute(name="vlbiBeamAddresses", + label="VlbiBeams Capability devices addresses", + forwarded=True + ) + reservedSearchBeamIDs = attribute(name="reservedSearchBeamIDs", label="IDs of reserved SeachBeam Capabilities", - doc="Report the list of SearchBeam Capability IDs reserved for each sub-array", + forwarded=True ) - - reservedSearchBeamNum = attribute( - dtype=('DevUShort',), - max_dim_x=16, - label="Number of reserved SeachBeam Capabilities", - doc="Report the number of SearchBeam Capabilities reserved for each sub-array", + unassignedVlbiBeamIDs = attribute(name="unassignedVlbiBeamIDs", + label="Unassigned VlbiBeam Capabilities IDs", + forwarded=True ) - - unassignedTimingBeamIDs = attribute( - dtype=('DevUShort',), - max_dim_x=16, + unassignedTimingBeamIDs = attribute(name="unassignedTimingBeamIDs", label="Unassigned TimingBeam Capabilities IDs", - doc="Report the list of TimingBeam Capability IDs that are not assigned to any sub-array. The SCM values of a unassigned VlbiBeam Capabiity are:\nState = OFF\nadminMode = ONLINE or MAINTENACE\nobsState = IDLE\nhealthState = OK \nobsMode = IDLE\n", + forwarded=True ) - - unassignedVlbiBeamIDs = attribute( - dtype=('DevUShort',), - max_dim_x=20, - label="Unassigned VlbiBeam Capabilities IDs", - doc="Report the list of VlbiBeam Capability IDs that are not assigned to any sub-array. The SCM values of a unassigned VlbiBeam Capabiity are:\nState = OFF\nadminMode = ONLINE or MAINTENACE\nobsState = IDLE\nhealthState = OK \nobsMode = IDLE\n", + unassignedSearchBeamIDs = attribute(name="unassignedSearchBeamIDs", + label="Unassigned SeachBeam Capabilities IDs", + forwarded=True ) - listOfDevCompletedTask = attribute( - dtype=('DevString',), - max_dim_x=100, - label="List of devices that completed the task", - doc="List of devices that completed the task", + searchBeamMembership = attribute(name="searchBeamMembership", + label="SearchBeam Membership", + forwarded=True + ) + timingBeamMembership = attribute(name="timingBeamMembership", + label="TimingBeam Membership", + forwarded=True + ) + vlbiBeamMembership = attribute(name="vlbiBeamMembership", + label="VlbiBeam Membership", + forwarded=True ) - # --------------- # General methods # --------------- @@ -1203,18 +1324,24 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # values: the duration (in sec) self._cmd_duration_measured = defaultdict(lambda: 0) - # _se_timeout_expired: report the timeout flag - # Implemented as a nested default dictionary - # keys: FQDN - # values: default dictionary (keys: command name, values: True/False)) - self._se_timeout_expired = defaultdict(lambda: defaultdict(lambda: False)) - # _timeout_expired: report the timeout flag # Implemented as a dictionary # keys: command name ('on', 'off', 'standby'..) - # values: True/False)) + # values: True/False self._timeout_expired = defaultdict(lambda: False) + # _alarm_raised: report the alarm flag + # Implemented as a dictionary + # keys: command name ('on', 'off', 'standby'..) + # values: True/False + self._alarm_raised = defaultdict(lambda: False) + + # _alarm_message: report the alarm message + # Implemented as a dictionary + # keys: command name ('on', 'off', 'standby'..) + # values: the message + self._alarm_message = defaultdict(lambda: '') + # _list_dev_completed_task: for each long-running command report the list of subordinate # components that completed the task # Implemented as a dictionary @@ -1231,36 +1358,15 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # 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 - # keys: device FQDN - # values: the attribute value - self._search_beam_state = defaultdict(lambda: tango.DevState.DISABLE) - self._search_beam_health_state = defaultdict(lambda: HealthState.UNKNOWN) - self._search_beam_admin_mode = defaultdict(lambda: tango.DevState.NOTFITTED) - self._search_beam_obs_state = defaultdict(lambda: ObsState.IDLE) - self._timing_beam_state = defaultdict(lambda: tango.DevState.DISABLE) - self._timing_beam_health_state = defaultdict(lambda: HealthState.UNKNOWN) - self._timing_beam_admin_mode = defaultdict(lambda: tango.DevState.NOTFITTED) - self._timing_beam_obs_state = defaultdict(lambda: ObsState.IDLE) - self._vlbi_beam_state = defaultdict(lambda: tango.DevState.DISABLE) - self._vlbi_beam_health_state = defaultdict(lambda: HealthState.UNKNOWN) - self._vlbi_beam_admin_mode = defaultdict(lambda: tango.DevState.NOTFITTED) - self._vlbi_beam_obs_state = defaultdict(lambda: ObsState.IDLE) - #initialize Csp capabilities subarray membership affiliation - self._search_beam_membership = [] - self._timing_beam_membership = [] - self._vlbi_beam_membership = [] - # 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 = [] - + # _capability_proxy: dictionary + # keys: the Capability Monitor devices FQDNs + # values: DeviceProxy proxies + self._capability_proxy = {} # Try connection with sub-elements self._connect_to_subelements() + # start CSP Capbilities monitoring + self._connect_capabilities_monitor() + # to use the push model in command_inout_asynch (the one with the callback parameter), # change the global TANGO model to PUSH_CALLBACK. @@ -1350,15 +1456,15 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): print("set admin mode: fqdn {}".format(self._se_fqdn)) for fqdn in self._se_fqdn: try: - if self._is_se_device_running(fqdn): - device_proxy = self._se_proxies[fqdn] - device_proxy.adminMode = value + self._se_write_adminMode(value, fqdn) except tango.DevFailed as df: + log_msg = (("Failure in setting adminMode " "for device {}: {}".format(str(fqdn), str(df.args[0].reason)))) - self.dev_logging(log_msg, int(tango.LogLevel.LOG_ERROR)) + self.dev_logging(log_msg, int(tango.LogLevel.LOG_ERROR)) #TODO: what happens if one sub-element fails? + # add check on timeout command execution self._admin_mode = value if self._admin_mode in [AdminMode.OFFLINE, AdminMode.NOTFITTED, AdminMode.RESERVED]: self.set_state(tango.DevState.DISABLE) @@ -1531,25 +1637,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): :raises: tango.DevFailed: raised when there is no DeviceProxy providing interface \ to the CBF sub-element Master, or an exception is caught in command execution. """ - # check if the sub-element administrative mode has already the requested value - if value == self._se_admin_mode[self.CspCbf]: - return - # check if the device is exported in the TANGO DB (that is the server is running) - if self._is_se_device_running(self.CspCbf): - try: - cbf_proxy = self._se_proxies[self.CspCbf] - #cbf_proxy.adminMode = value - cbf_proxy.write_attribute_asynch("adminMode", value) - except tango.DevFailed as df: - tango.Except.throw_exception("Command failed", - str(df.args[0].desc), - "Set cbf admin mode", - tango.ErrSeverity.ERR) - except KeyError as key_err: - msg = "Can't retrieve the information of key {}".format(key_err) - self.dev_logging(msg, tango.LogLevel.LOG_ERROR) - tango.Except.throw_exception("Command failed", msg, - "Set cbf admin mode", tango.ErrSeverity.ERR) + self._se_write_adminMode(value, self.CspCbf) # PROTECTED REGION END # // CspMaster.cspCbfAdminMode_write def read_cspPssAdminMode(self): @@ -1561,7 +1649,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): def write_cspPssAdminMode(self, value): # PROTECTED REGION ID(CspMaster.cspPssAdminMode_write) ENABLED START # """Set the cspPssAdminMode attribute.""" - pass + self._se_write_adminMode(value, self.CspPss) # PROTECTED REGION END # // CspMaster.cspPssAdminMode_write def read_cspPstAdminMode(self): @@ -1573,7 +1661,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): def write_cspPstAdminMode(self, value): # PROTECTED REGION ID(CspMaster.cspPstAdminMode_write) ENABLED START # """Set the cspPstAdminMode attribute.""" - pass + self._se_write_adminMode(value, self.CspPst) # PROTECTED REGION END # // CspMaster.cspPstAdminMode_write def read_numOfDevCompletedTask(self): @@ -1585,6 +1673,41 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): return self._num_dev_completed_task[self._last_executed_command] # PROTECTED REGION END # // CspMaster.numOfDevCompletedTask_read + def read_onCmdAlarm(self): + # PROTECTED REGION ID(CspMaster.onCmdAlarm_read) ENABLED START # + """Return the onCmdAlarm attribute.""" + return self._alarm_expired['on'] + # PROTECTED REGION END # // CspMaster.onCmdAlarm_read + + def read_onAlarmMessage(self): + # PROTECTED REGION ID(CspMaster.onAlarmMessage_read) ENABLED START # + """Return the onAlarmMessage attribute.""" + return self._alarm_message['on'] + # PROTECTED REGION END # // CspMaster.onAlarmMessage_read + + def read_offCmdAlarm(self): + # PROTECTED REGION ID(CspMaster.offCmdAlarm_read) ENABLED START # + """Return the offCmdAlarm attribute.""" + return self._alarm_expired['off'] + # PROTECTED REGION END # // CspMaster.offCmdAlarm_read + + def read_offAlarmMessage(self): + # PROTECTED REGION ID(CspMaster.offAlarmMessage_read) ENABLED START # + """Return the offAlarmMessage attribute.""" + return self._alarm_message['off'] + # PROTECTED REGION END # // CspMaster.offAlarmMessage_read + + def read_standbyCmdAlarm(self): + # PROTECTED REGION ID(CspMaster.standbyCmdAlarm_read) ENABLED START # + """Return the standbyCmdAlarm attribute.""" + return self._alarm_expired['standby'] + # PROTECTED REGION END # // CspMaster.standbyCmdAlarm_read + + def read_standbyAlarmMessage(self): + # PROTECTED REGION ID(CspMaster.standbyAlarmMessage_read) ENABLED START # + """Return the standbyAlarmMessage attribute.""" + return self._alarm_message['standby'] + # PROTECTED REGION END # // CspMaster.standbyAlarmMessage_read def read_availableCapabilities(self): # PROTECTED REGION ID(CspMaster.availableCapabilities_read) ENABLED START # @@ -1592,156 +1715,11 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): return ('',) # PROTECTED REGION END # // CspMaster.availableCapabilities_read - def read_reportSearchBeamState(self): - # PROTECTED REGION ID(CspMaster.reportSearchBeamState_read) ENABLED START # - """Return the reportSearchBeamState attribute.""" - return self._search_beam_state.values() - # PROTECTED REGION END # // CspMaster.reportSearchBeamState_read - - def read_reportSearchBeamHealthState(self): - # PROTECTED REGION ID(CspMaster.reportSearchBeamHealthState_read) ENABLED START # - """Return the reportSearchBeamHealthState attribute.""" - return self._search_beam_health_state.values() - # PROTECTED REGION END # // CspMaster.reportSearchBeamHealthState_read - - def read_reportSearchBeamAdminMode(self): - # PROTECTED REGION ID(CspMaster.reportSearchBeamAdminMode_read) ENABLED START # - """Return the reportSearchBeamAdminMode attribute.""" - return self._search_beam_admin_mode.values() - # PROTECTED REGION END # // CspMaster.reportSearchBeamAdminMode_read - - def write_reportSearchBeamAdminMode(self, value): - # PROTECTED REGION ID(CspMaster.reportSearchBeamAdminMode_write) ENABLED START # - """Set the reportSearchBeamAdminMode attribute.""" - pass - # PROTECTED REGION END # // CspMaster.reportSearchBeamAdminMode_write - - def read_reportTimingBeamState(self): - # PROTECTED REGION ID(CspMaster.reportTimingBeamState_read) ENABLED START # - """Return the reportTimingBeamState attribute.""" - return self._timing_beam_state.values() - # PROTECTED REGION END # // CspMaster.reportTimingBeamState_read - - def read_reportTimingBeamHealthState(self): - # PROTECTED REGION ID(CspMaster.reportTimingBeamHealthState_read) ENABLED START # - """Return the reportTimingBeamHealthState attribute.""" - return self._timing_beam_health_state.values() - # PROTECTED REGION END # // CspMaster.reportTimingBeamHealthState_read - - def read_reportTimingBeamAdminMode(self): - # PROTECTED REGION ID(CspMaster.reportTimingBeamAdminMode_read) ENABLED START # - """Return the reportTimingBeamAdminMode attribute.""" - return self._timing_beam_admin_mode.values() - # PROTECTED REGION END # // CspMaster.reportTimingBeamAdminMode_read - - def write_reportTimingBeamAdminMode(self, value): - # PROTECTED REGION ID(CspMaster.reportTimingBeamAdminMode_write) ENABLED START # - """Set the reportTimingBeamAdminMode attribute.""" - pass - # PROTECTED REGION END # // CspMaster.reportTimingBeamAdminMode_write - - def read_reportVlbiBeamState(self): - # PROTECTED REGION ID(CspMaster.reportVlbiBeamState_read) ENABLED START # - """Return the reportVlbiBeamState attribute.""" - return self._vlbi_beam_state.values() - # PROTECTED REGION END # // CspMaster.reportVlbiBeamState_read - - def read_reportVlbiBeamHealthState(self): - # PROTECTED REGION ID(CspMaster.reportVlbiBeamHealthState_read) ENABLED START # - """Return the reportVlbiBeamHealthState attribute.""" - return self._vlbi_beam_health_state.values() - # PROTECTED REGION END # // CspMaster.reportVlbiBeamHealthState_read - - def read_reportVlbiBeamAdminMode(self): - # PROTECTED REGION ID(CspMaster.reportVlbiBeamAdminMode_read) ENABLED START # - """Return the reportVlbiBeamAdminMode attribute.""" - return self._vlbi_beam_admin_mode.values() - # PROTECTED REGION END # // CspMaster.reportVlbiBeamAdminMode_read - - def write_reportVlbiBeamAdminMode(self, value): - # PROTECTED REGION ID(CspMaster.reportVlbiBeamAdminMode_write) ENABLED START # - """Set the reportVlbiBeamAdminMode attribute.""" - pass - # PROTECTED REGION END # // CspMaster.reportVlbiBeamAdminMode_write - def read_cspSubarrayAddresses(self): # PROTECTED REGION ID(CspMaster.cspSubarrayAddresses_read) ENABLED START # """Return the cspSubarrayAddresses attribute.""" return self.CspSubarrays # PROTECTED REGION END # // CspMaster.cspSubarrayAddresses_read - - def read_searchBeamAddresses(self): - # PROTECTED REGION ID(CspMaster.searchBeamAddresses_read) ENABLED START # - """Return the searchBeamAddresses attribute.""" - return self.SearchBeams - # PROTECTED REGION END # // CspMaster.searchBeamAddresses_read - - def read_timingBeamAddresses(self): - # PROTECTED REGION ID(CspMaster.timingBeamAddresses_read) ENABLED START # - """Return the timingBeamAddresses attribute.""" - return self.TimingBeams - # PROTECTED REGION END # // CspMaster.timingBeamAddresses_read - - def read_vlbiBeamAddresses(self): - # PROTECTED REGION ID(CspMaster.vlbiBeamAddresses_read) ENABLED START # - """Return the vlbiBeamAddresses attribute.""" - return self.VlbiBeams - # PROTECTED REGION END # // CspMaster.vlbiBeamAddresses_read - - def read_searchBeamMembership(self): - # PROTECTED REGION ID(CspMaster.searchBeamMembership_read) ENABLED START # - """Return the searchBeamMembership attribute.""" - return self._search_beam_membership - # PROTECTED REGION END # // CspMaster.searchBeamMembership_read - - def read_timingBeamMembership(self): - # PROTECTED REGION ID(CspMaster.timingBeamMembership_read) ENABLED START # - """Return the timingBeamMembership attribute.""" - return self._timing_beam_membership - # PROTECTED REGION END # // CspMaster.timingBeamMembership_read - - def read_vlbiBeamMembership(self): - # PROTECTED REGION ID(CspMaster.vlbiBeamMembership_read) ENABLED START # - """Return the vlbiBeamMembership attribute.""" - return self._vlbi_beam_membership - # PROTECTED REGION END # // CspMaster.vlbiBeamMembership_read - - def read_unassignedSearchBeamIDs(self): - # PROTECTED REGION ID(CspMaster.unassignedSearchBeamIDs_read) ENABLED START # - """Return the unassignedSearchBeamIDs attribute.""" - return self._unassigned_search_beam_id - # PROTECTED REGION END # // CspMaster.unassignedSearchBeamIDs_read - - def read_unassignedSearchBeamsNum(self): - # PROTECTED REGION ID(CspMaster.unassignedSearchBeamsNum_read) ENABLED START # - """Return the unassignedSearchBeamsNum attribute.""" - return len(self._unassigned_search_beam_id) - # PROTECTED REGION END # // CspMaster.unassignedSearchBeamsNum_read - - def read_reservedSearchBeamIDs(self): - # PROTECTED REGION ID(CspMaster.reservedSearchBeamIDs_read) ENABLED START # - """Return the reservedSearchBeamIDs attribute.""" - return self._reserved_search_beam_id - # PROTECTED REGION END # // CspMaster.reservedSearchBeamIDs_read - - def read_reservedSearchBeamNum(self): - # PROTECTED REGION ID(CspMaster.reservedSearchBeamNum_read) ENABLED START # - """Return the reservedSearchBeamNum attribute.""" - return len(self._reserved_search_beam_id) - # PROTECTED REGION END # // CspMaster.reservedSearchBeamNum_read - - def read_unassignedTimingBeamIDs(self): - # PROTECTED REGION ID(CspMaster.unassignedTimingBeamIDs_read) ENABLED START # - """Return the unassignedTimingBeamIDs attribute.""" - return self.unassignedTimingBeamIDs - # PROTECTED REGION END # // CspMaster.unassignedTimingBeamIDs_read - - def read_unassignedVlbiBeamIDs(self): - # PROTECTED REGION ID(CspMaster.unassignedVlbiBeamIDs_read) ENABLED START # - """Return the unassignedVlbiBeamIDs attribute.""" - return self._unassigned_vlbi_beam_id - # PROTECTED REGION END # // CspMaster.unassignedVlbiBeamIDs_read - def read_listOfDevCompletedTask(self): # PROTECTED REGION ID(CspMaster.listOfDevCompletedTask_read) ENABLED START # """Return the listOfDevCompletedTask attribute.""" diff --git a/csp-lmc-common/pogo/CspCapabilityMonitor.py b/csp-lmc-common/pogo/CspCapabilityMonitor.py new file mode 100644 index 0000000..5d93858 --- /dev/null +++ b/csp-lmc-common/pogo/CspCapabilityMonitor.py @@ -0,0 +1,227 @@ +# -*- coding: utf-8 -*- +# +# This file is part of the CspCapabilityMonitor project +# +# INAF, SKA Telescope +# +# Distributed under the terms of the GPL license. +# See LICENSE.txt for more info. + +""" CSP.LMC Common Class + +CSP.LMC Common Class designed to monitoring the SCM +attributes of the CSP Capabilities: SearchBeams, TimingBeams, +VlbiBeams, Receptors/Stations. +""" + +# PyTango 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 SKABaseDevice import SKABaseDevice +# Additional import +# PROTECTED REGION ID(CspCapabilityMonitor.additionnal_import) ENABLED START # +# PROTECTED REGION END # // CspCapabilityMonitor.additionnal_import + +__all__ = ["CspCapabilityMonitor", "main"] + + +class CspCapabilityMonitor(SKABaseDevice): + """ + CSP.LMC Common Class designed to monitoring the SCM + attributes of the CSP Capabilities: SearchBeams, TimingBeams, + VlbiBeams, Receptors/Stations. + + **Properties:** + + - Device Property + + + + + + CapabilityDevices + - The list of the CSP Capability devices FQDNs monitored\nby the instance. + - Type:'DevVarStringArray' + + """ + __metaclass__ = DeviceMeta + # PROTECTED REGION ID(CspCapabilityMonitor.class_variable) ENABLED START # + # PROTECTED REGION END # // CspCapabilityMonitor.class_variable + + # ----------------- + # Device Properties + # ----------------- + + + + + + + CapabilityDevices = device_property( + dtype='DevVarStringArray', + ) + + # ---------- + # Attributes + # ---------- + + + + + + + + + + + + numOfUnassignedIDs = attribute( + dtype='DevUShort', + ) + + capabilityState = attribute( + dtype=('DevState',), + max_dim_x=1500, + label="Capability State", + doc="Report the State of the capabilities.", + ) + + capabilityHealthState = attribute( + dtype=('DevUShort',), + max_dim_x=1500, + ) + + capabilityAdminMode = attribute( + dtype=('DevUShort',), + access=AttrWriteType.READ_WRITE, + max_dim_x=1500, + ) + + capabilityObsState = attribute( + dtype=('DevUShort',), + max_dim_x=1500, + ) + + unassignedIDs = attribute( + dtype=('DevUShort',), + max_dim_x=1500, + ) + + capabilityAddresses = attribute( + dtype=('DevString',), + max_dim_x=1500, + label="CSP.LMC Capability devices FQDNs", + doc="The FQDNs of the CSP.LMC Capability devices: SearchBeams, TimingBeams, VlbiBeams,\nReceptors (MID instance), Stations (LOW instance)", + ) + + membership = attribute( + dtype=('DevUShort',), + max_dim_x=1500, + label="Capabiity sub-array affilitiation", + ) + + # --------------- + # General methods + # --------------- + + def init_device(self): + """Initialises the attributes and properties of the CspCapabilityMonitor.""" + SKABaseDevice.init_device(self) + # PROTECTED REGION ID(CspCapabilityMonitor.init_device) ENABLED START # + # PROTECTED REGION END # // CspCapabilityMonitor.init_device + + def always_executed_hook(self): + """Method always executed before any TANGO command is executed.""" + # PROTECTED REGION ID(CspCapabilityMonitor.always_executed_hook) ENABLED START # + # PROTECTED REGION END # // CspCapabilityMonitor.always_executed_hook + + def delete_device(self): + """Hook to delete resources allocated in init_device. + + This method allows for any memory or other resources allocated in the + init_device method to be released. This method is called by the device + destructor and by the device Init command. + """ + # PROTECTED REGION ID(CspCapabilityMonitor.delete_device) ENABLED START # + # PROTECTED REGION END # // CspCapabilityMonitor.delete_device + # ------------------ + # Attributes methods + # ------------------ + + def read_numOfUnassignedIDs(self): + # PROTECTED REGION ID(CspCapabilityMonitor.numOfUnassignedIDs_read) ENABLED START # + """Return the numOfUnassignedIDs attribute.""" + return 0 + # PROTECTED REGION END # // CspCapabilityMonitor.numOfUnassignedIDs_read + + def read_capabilityState(self): + # PROTECTED REGION ID(CspCapabilityMonitor.capabilityState_read) ENABLED START # + """Return the capabilityState attribute.""" + return (PyTango.DevState.UNKNOWN,) + # PROTECTED REGION END # // CspCapabilityMonitor.capabilityState_read + + def read_capabilityHealthState(self): + # PROTECTED REGION ID(CspCapabilityMonitor.capabilityHealthState_read) ENABLED START # + """Return the capabilityHealthState attribute.""" + return (0,) + # PROTECTED REGION END # // CspCapabilityMonitor.capabilityHealthState_read + + def read_capabilityAdminMode(self): + # PROTECTED REGION ID(CspCapabilityMonitor.capabilityAdminMode_read) ENABLED START # + """Return the capabilityAdminMode attribute.""" + return (0,) + # PROTECTED REGION END # // CspCapabilityMonitor.capabilityAdminMode_read + + def write_capabilityAdminMode(self, value): + # PROTECTED REGION ID(CspCapabilityMonitor.capabilityAdminMode_write) ENABLED START # + """Set the capabilityAdminMode attribute.""" + pass + # PROTECTED REGION END # // CspCapabilityMonitor.capabilityAdminMode_write + + def read_capabilityObsState(self): + # PROTECTED REGION ID(CspCapabilityMonitor.capabilityObsState_read) ENABLED START # + """Return the capabilityObsState attribute.""" + return (0,) + # PROTECTED REGION END # // CspCapabilityMonitor.capabilityObsState_read + + def read_unassignedIDs(self): + # PROTECTED REGION ID(CspCapabilityMonitor.unassignedIDs_read) ENABLED START # + """Return the unassignedIDs attribute.""" + return (0,) + # PROTECTED REGION END # // CspCapabilityMonitor.unassignedIDs_read + + def read_capabilityAddresses(self): + # PROTECTED REGION ID(CspCapabilityMonitor.capabilityAddresses_read) ENABLED START # + """Return the capabilityAddresses attribute.""" + return ('',) + # PROTECTED REGION END # // CspCapabilityMonitor.capabilityAddresses_read + + def read_membership(self): + # PROTECTED REGION ID(CspCapabilityMonitor.membership_read) ENABLED START # + """Return the membership attribute.""" + return (0,) + # PROTECTED REGION END # // CspCapabilityMonitor.membership_read + + + # -------- + # Commands + # -------- + +# ---------- +# Run server +# ---------- + + +def main(args=None, **kwargs): + # PROTECTED REGION ID(CspCapabilityMonitor.main) ENABLED START # + return run((CspCapabilityMonitor,), args=args, **kwargs) + # PROTECTED REGION END # // CspCapabilityMonitor.main + +if __name__ == '__main__': + main() diff --git a/csp-lmc-common/pogo/CspCapabilityMonitor.xmi b/csp-lmc-common/pogo/CspCapabilityMonitor.xmi new file mode 100644 index 0000000..eacbf01 --- /dev/null +++ b/csp-lmc-common/pogo/CspCapabilityMonitor.xmi @@ -0,0 +1,211 @@ + + + + + + + + + + + + 4 + + + + + + + + + + + + + + + + + localhost + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/csp-lmc-common/pogo/CspMaster.xmi b/csp-lmc-common/pogo/CspMaster.xmi index c17dbe5..106650b 100644 --- a/csp-lmc-common/pogo/CspMaster.xmi +++ b/csp-lmc-common/pogo/CspMaster.xmi @@ -58,16 +58,16 @@ - - + + - - + + - - + + @@ -330,31 +330,31 @@ - + - + - + - + - + - + @@ -489,97 +489,67 @@ - - - - - - - - - - - - - - - - - - - - - - - - + + - - + - - + + - - + - - + + - + - - + + - - + - - + + - - + - - + + - + - - + + - - - - + + - - + + - - - - + + @@ -589,102 +559,83 @@ - + - + - - - - - + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - - - - - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/csp-lmc-common/simulators/DeviceTestMaster/DeviceTestMaster.py b/csp-lmc-common/simulators/DeviceTestMaster/DeviceTestMaster.py new file mode 100644 index 0000000..7610a9c --- /dev/null +++ b/csp-lmc-common/simulators/DeviceTestMaster/DeviceTestMaster.py @@ -0,0 +1,211 @@ +# -*- coding: utf-8 -*- +# +# This file is part of the DeviceTestMaster project +# +# +# +# Distributed under the terms of the GPL license. +# See LICENSE.txt for more info. + +""" CSP.LMC subelement Test Master Tango device prototype + +Test TANGO device class to test connection with the CSPMaster prototype. +It simulates the CbfMaster sub-element. +""" +from __future__ import absolute_import +import sys +import os +import time + +file_path = os.path.dirname(os.path.abspath(__file__)) +print(file_path) +module_path = os.path.abspath(os.path.join(file_path, os.pardir)) + "/../utils" +print(module_path) +sys.path.insert(0, module_path) + +# 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 +# Additional import +# PROTECTED REGION ID(DeviceTestMaster.additionnal_import) ENABLED START # +from future.utils import with_metaclass +import threading +from cspcommons import HealthState +from skabase.SKAMaster.SKAMaster import SKAMaster +# PROTECTED REGION END # // DeviceTestMaster.additionnal_import + +__all__ = ["DeviceTestMaster", "main"] + + +class DeviceTestMaster(with_metaclass(DeviceMeta,SKAMaster)): + """ + DeviceTestMaster TANGO device class to test connection with the CSPMaster prototype + """ + # PROTECTED REGION ID(DeviceTestMaster.class_variable) ENABLED START # + # PROTECTED REGION END # // DeviceTestMaster.class_variable + + # ----------------- + # Device Properties + # ----------------- + + # ---------- + # Attributes + # ---------- + + onCommandProgress = attribute( + dtype='uint16', + label="Command progress percentage", + max_value=100, + min_value=0, + polling_period=1000, + abs_change=5, + rel_change=2, + doc="Percentage progress implemented for commands that result in state/mode transitions for a large \nnumber of components and/or are executed in stages (e.g power up, power down)", + ) + + # --------------- + # General methods + # --------------- + + def init_subelement(self): + """ + Simulate the sub-element device initialization + """ + self.set_state(tango.DevState.STANDBY) + + def on_subelement(self): + """ + Simulate the sub-element transition from STANDBY to ON + """ + print("Executing the On command...wait") + time.sleep(10) + self.set_state(tango.DevState.ON) + self._health_state = HealthState.DEGRADED.value + print("End On command...wait") + + def standby_subelement(self): + """ + Simulate the sub-element transition from ON to STANDBY + """ + print("Executing the Standby command...wait") + time.sleep(10) + self.set_state(tango.DevState.STANDBY) + self._health_state = HealthState.DEGRADED.value + print("End Standby command...wait") + + def off_subelement(self): + """ + Simulate the sub-element transition from STANDBY to OFF + """ + self.set_state(tango.DevState.OFF) + self._health_state = HealthState.UNKNOWN.value + + def init_device(self): + SKAMaster.init_device(self) + # PROTECTED REGION ID(DeviceTestMaster.init_device) ENABLED START # + + self.set_state(tango.DevState.INIT) + self._health_state = HealthState.UNKNOWN.value + + # start a timer to simulate device intialization + thread = threading.Timer(1, self.init_subelement) + thread.start() + + # PROTECTED REGION END # // DeviceTestMaster.init_device + + def always_executed_hook(self): + # PROTECTED REGION ID(DeviceTestMaster.always_executed_hook) ENABLED START # + pass + # PROTECTED REGION END # // DeviceTestMaster.always_executed_hook + + def delete_device(self): + # PROTECTED REGION ID(DeviceTestMaster.delete_device) ENABLED START # + pass + # PROTECTED REGION END # // DeviceTestMaster.delete_device + + # ------------------ + # Attributes methods + # ------------------ + + def read_onCommandProgress(self): + # PROTECTED REGION ID(DeviceTestMaster.onCommandProgress_read) ENABLED START # + return 0 + # PROTECTED REGION END # // DeviceTestMaster.onCommandProgress_read + + # -------- + # Commands + # -------- + + @command( + doc_in="If the array length is0, the command apllies to the whole\nCSP Element.\nIf the array length is > 1, each array element specifies the FQDN of the\nCSP SubElement to switch ON.", + ) + @DebugIt() + def On(self): + # PROTECTED REGION ID(DeviceTestMaster.On) ENABLED START # + print("Sono qui") + thread = threading.Timer(2, self.on_subelement) + thread.start() + # PROTECTED REGION END # // DeviceTestMaster.On + + @command( + dtype_in=('str',), + doc_in="If the array length is0, the command apllies to the whole\nCSP Element.\nIf the array length is > 1, each array element specifies the FQDN of the\nCSP SubElement to switch OFF.", + ) + @DebugIt() + def Off(self): + # PROTECTED REGION ID(DeviceTestMaster.Off) ENABLED START # + thread = threading.Timer(1, self.off_subelement) + thread.start() + # PROTECTED REGION END # // DeviceTestMaster.Off + + @command( + doc_in="If the array length is0, the command apllies to the whole\nCSP Element.\n\ + If the array length is > 1, each array element specifies the FQDN of the\n\ + CSP SubElement to switch OFF.", + ) + + @DebugIt() + def Standby(self): + # PROTECTED REGION ID(DeviceTestMaster.Standby) ENABLED START # + thread = threading.Timer(2, self.standby_subelement) + thread.start() + # PROTECTED REGION END # // DeviceTestMaster.Standby + + def is_Off_allowed(self): + # PROTECTED REGION ID(DeviceTestMaster.is_Off_allowed) ENABLED START # + if self.get_state() not in [DevState.STANDBY]: + return False + return True + # PROTECTED REGION END # // DeviceTestMaster.is_Off_allowed. + + def is_Standby_allowed(self): + # PROTECTED REGION ID(DeviceTestMaster.is_Standby_allowed) ENABLED START # + if self.get_state() not in [DevState.ON, DevState.OFF]: + return False + return True + # PROTECTED REGION END # // DeviceTestMaster.is_Standby_allowed + + def is_On_allowed(self): + # PROTECTED REGION ID(DeviceTestMaster.is_On_allowed) ENABLED START # + if self.get_state() not in [DevState.STANDBY]: + return False + return True + # PROTECTED REGION END # // DeviceTestMaster.is_On_allowed + +# ---------- +# Run server +# ---------- + +def main(args=None, **kwargs): + # PROTECTED REGION ID(DeviceTestMaster.main) ENABLED START # + return run((DeviceTestMaster,), args=args, **kwargs) + # PROTECTED REGION END # // DeviceTestMaster.main + +if __name__ == '__main__': + main() diff --git a/csp-lmc-common/simulators/DeviceTestMaster/README.md b/csp-lmc-common/simulators/DeviceTestMaster/README.md new file mode 100644 index 0000000..d2f084c --- /dev/null +++ b/csp-lmc-common/simulators/DeviceTestMaster/README.md @@ -0,0 +1,6 @@ +This device is no more supported. +It was implemented at the beginning of the project to test the +basic CSP.LMC functionalities. +The mid-csp-mcs project provides the Mid-CBF TANGO Devices that are +now currently used by the CSP.LMC prototype. + diff --git a/csp-lmc-common/simulators/pogo/DeviceTestMaster.xmi b/csp-lmc-common/simulators/pogo/DeviceTestMaster.xmi new file mode 100644 index 0000000..09d22a0 --- /dev/null +++ b/csp-lmc-common/simulators/pogo/DeviceTestMaster.xmi @@ -0,0 +1,344 @@ + + + + + + + + + + + + + 4 + + + + + + + + + + + + + localhost + + + + + + + + + 16 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/csp-lmc-common/utils/cspcommons.py b/csp-lmc-common/utils/cspcommons.py index 303377e..0c8243e 100644 --- a/csp-lmc-common/utils/cspcommons.py +++ b/csp-lmc-common/utils/cspcommons.py @@ -46,4 +46,6 @@ class CmdExecState(IntEnum): IDLE = 0 RUNNING = 1 QUEUED = 2 + TIMEOUT = 3 + FAILED = 4 diff --git a/csp-lmc-common/utils/decorators.py b/csp-lmc-common/utils/decorators.py index aa1f4eb..145b8cf 100644 --- a/csp-lmc-common/utils/decorators.py +++ b/csp-lmc-common/utils/decorators.py @@ -182,7 +182,7 @@ class CmdInputArgsCheck(object): # 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 + if (not dev_instance._is_device_running(device, dev_instance._se_proxies) or dev_instance._se_admin_mode[device] not in [AdminMode.ONLINE, AdminMode.MAINTENANCE]): if device == dev_instance.CspCbf: @@ -209,8 +209,10 @@ class CmdInputArgsCheck(object): 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][cmd_to_exec] = CmdExecState.QUEUED - # reset the timeout attribute content + # reset the timeout and alarm attribute content dev_instance._timeout_expired[cmd_to_exec] = False + # TODO: how check if the alarm condition has been reset by AlarmHandler? + dev_instance._alarm_raised[cmd_to_exec] = False device_proxy = dev_instance._se_proxies[device] try: # get the sub-element value for the onCommandDurationExpected attribute. diff --git a/docker/.make/.make-release-support b/docker/.make/.make-release-support new file mode 100644 index 0000000..f1f3a2c --- /dev/null +++ b/docker/.make/.make-release-support @@ -0,0 +1,106 @@ +#!/bin/bash +# +# Copyright 2015 Xebia Nederland B.V. +# Modifications copyright 2019 SKA Organisation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +function hasChanges() { + test -n "$(git status -s .)" +} + +function getRelease() { + awk -F= '/^release=/{print $2}' .release +} + +function getBaseTag() { + sed -n -e "s/^tag=\(.*\)$(getRelease)\$/\1/p" .release +} + +function getTag() { + if [ -z "$1" ] ; then + awk -F= '/^tag/{print $2}' .release + else + echo "$(getBaseTag)$1" + fi +} + +function setRelease() { + if [ -n "$1" ] ; then + sed -i.x -e "s/^tag=.*/tag=$(getTag $1)/" .release + sed -i.x -e "s/^release=.*/release=$1/g" .release + rm -f .release.x + runPreTagCommand "$1" + else + echo "ERROR: missing release version parameter " >&2 + return 1 + fi +} + +function runPreTagCommand() { + if [ -n "$1" ] ; then + COMMAND=$(sed -n -e "s/@@RELEASE@@/$1/g" -e 's/^pre_tag_command=\(.*\)/\1/p' .release) + if [ -n "$COMMAND" ] ; then + if ! OUTPUT=$(bash -c "$COMMAND" 2>&1) ; then echo $OUTPUT >&2 && exit 1 ; fi + fi + else + echo "ERROR: missing release version parameter " >&2 + return 1 + fi +} + +function tagExists() { + tag=${1:-$(getTag)} + test -n "$tag" && test -n "$(git tag | grep "^$tag\$")" +} + +function differsFromRelease() { + tag=$(getTag) + ! tagExists $tag || test -n "$(git diff --shortstat -r $tag .)" +} + +function getVersion() { + result=$(getRelease) + + if differsFromRelease; then + result="$result-$(git log -n 1 --format=%h .)" + fi + + if hasChanges ; then + result="$result-dirty" + fi + echo $result +} + +function nextPatchLevel() { + version=${1:-$(getRelease)} + major_and_minor=$(echo $version | cut -d. -f1,2) + patch=$(echo $version | cut -d. -f3) + version=$(printf "%s.%d" $major_and_minor $(($patch + 1))) + echo $version +} + +function nextMinorLevel() { + version=${1:-$(getRelease)} + major=$(echo $version | cut -d. -f1); + minor=$(echo $version | cut -d. -f2); + version=$(printf "%d.%d.0" $major $(($minor + 1))) ; + echo $version +} + +function nextMajorLevel() { + version=${1:-$(getRelease)} + major=$(echo $version | cut -d. -f1); + version=$(printf "%d.0.0" $(($major + 1))) + echo $version +} diff --git a/docker/.make/Makefile.mk b/docker/.make/Makefile.mk new file mode 100644 index 0000000..2ce814e --- /dev/null +++ b/docker/.make/Makefile.mk @@ -0,0 +1,137 @@ +# +# Copyright 2015 Xebia Nederland B.V. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +ifeq ($(strip $(PROJECT)),) + NAME=$(shell basename $(CURDIR)) +else + NAME=$(PROJECT) +endif + +RELEASE_SUPPORT := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST))))/.make-release-support + +ifeq ($(strip $(DOCKER_REGISTRY_HOST)),) + DOCKER_REGISTRY_HOST = nexus.engageska-portugal.pt +endif + +ifeq ($(strip $(DOCKER_REGISTRY_USER)),) + DOCKER_REGISTRY_USER = ska-docker +endif + +IMAGE=$(DOCKER_REGISTRY_HOST)/$(DOCKER_REGISTRY_USER)/$(NAME) + +#VERSION = release version + git sha +VERSION=$(shell . $(RELEASE_SUPPORT) ; getVersion) + +#BASE_VERSION +BASE_VERSION=$(shell . $(RELEASE_SUPPORT) ; getRelease) + +#TAG = project name + release version +TAG=$(shell . $(RELEASE_SUPPORT); getTag) + +#DEFAULT_TAG = image name + BASE_VERSION +DEFAULT_TAG=$(IMAGE):$(BASE_VERSION) + + +SHELL=/bin/bash + +DOCKER_BUILD_CONTEXT=../csp-lmc-common +DOCKER_FILE_PATH=Dockerfile + +.PHONY: pre-build docker-build post-build build release patch-release minor-release major-release tag check-status check-release showver \ + push pre-push do-push post-push + +build: pre-build docker-build post-build ## build the application image + +pre-build: + +post-build: + +pre-push: + +post-push: + +docker-build: .release + @echo "Building image: $(IMAGE):$(VERSION)" + @echo "NAME: $(NAME)" + docker build $(DOCKER_BUILD_ARGS) -t $(IMAGE):$(VERSION) $(DOCKER_BUILD_CONTEXT) -f $(DOCKER_FILE_PATH) --build-arg DOCKER_REGISTRY_HOST=$(DOCKER_REGISTRY_HOST) --build-arg DOCKER_REGISTRY_USER=$(DOCKER_REGISTRY_USER) + @DOCKER_MAJOR=$(shell docker -v | sed -e 's/.*version //' -e 's/,.*//' | cut -d\. -f1) ; \ + DOCKER_MINOR=$(shell docker -v | sed -e 's/.*version //' -e 's/,.*//' | cut -d\. -f2) ; \ + if [ $$DOCKER_MAJOR -eq 1 ] && [ $$DOCKER_MINOR -lt 10 ] ; then \ + echo docker tag -f $(IMAGE):$(VERSION) $(IMAGE):latest ;\ + docker tag -f $(IMAGE):$(VERSION) $(IMAGE):latest ;\ + else \ + echo docker tag $(IMAGE):$(VERSION) $(IMAGE):latest ;\ + docker tag $(IMAGE):$(VERSION) $(IMAGE):latest ; \ + fi + +release: check-status check-release build push + +push: pre-push do-push post-push ## push the image to the Docker registry + +do-push: ## Push the image tagged as $(IMAGE):$(VERSION) and $(DEFAULT_TAG) + @echo -e "Tagging: $(IMAGE):$(VERSION) -> $(DEFAULT_TAG)" + docker tag $(IMAGE):$(VERSION) $(DEFAULT_TAG) + @echo -e "Pushing: $(IMAGE):$(VERSION)" + docker push $(IMAGE):$(VERSION) + @echo -e "Pushing: $(DEFAULT_TAG)" + docker push $(DEFAULT_TAG) + +tag_latest: do-push ## Tag the images as latest + @echo "Tagging: $(DEFAULT_TAG) -> $(IMAGE):latest" + @docker tag $(DEFAULT_TAG) $(IMAGE):latest + +push_latest: tag_latest ## Push the image tagged as :latest + @echo "Pushing: $(IMAGE):latest" + @docker push $(IMAGE):latest + +snapshot: build push + +showver: .release + @. $(RELEASE_SUPPORT); getVersion + +bump-patch-release: VERSION := $(shell . $(RELEASE_SUPPORT); nextPatchLevel) +bump-patch-release: .release tag + +bump-minor-release: VERSION := $(shell . $(RELEASE_SUPPORT); nextMinorLevel) +bump-minor-release: .release tag + +bump-major-release: VERSION := $(shell . $(RELEASE_SUPPORT); nextMajorLevel) +bump-major-release: .release tag + +patch-release: tag-patch-release release + @echo $(VERSION) + +minor-release: tag-minor-release release + @echo $(VERSION) + +major-release: tag-major-release release + @echo $(VERSION) + +tag: TAG=$(shell . $(RELEASE_SUPPORT); getTag $(VERSION)) +tag: check-status +# @. $(RELEASE_SUPPORT) ; ! tagExists $(TAG) || (echo "ERROR: tag $(TAG) for version $(VERSION) already tagged in git" >&2 && exit 1) ; + @. $(RELEASE_SUPPORT) ; setRelease $(VERSION) +# git add . +# git commit -m "bumped to version $(VERSION)" ; +# git tag $(TAG) ; +# @ if [ -n "$(shell git remote -v)" ] ; then git push --tags ; else echo 'no remote to push tags to' ; fi + +check-status: + @. $(RELEASE_SUPPORT) ; ! hasChanges || (echo "ERROR: there are still outstanding changes" >&2 && exit 1) ; + +check-release: .release + @. $(RELEASE_SUPPORT) ; tagExists $(TAG) || (echo "ERROR: version not yet tagged in git. make [minor,major,patch]-release." >&2 && exit 1) ; + @. $(RELEASE_SUPPORT) ; ! differsFromRelease $(TAG) || (echo "ERROR: current directory differs from tagged $(TAG). make [minor,major,patch]-release." ; exit 1) diff --git a/docker/.release b/docker/.release new file mode 100644 index 0000000..83148a0 --- /dev/null +++ b/docker/.release @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# +# This file is part of the CSP.LMC prototype project +# +# +# Distributed under the terms of the BSD-3-Clause license. +# See LICENSE.txt for more info. + +"""Release information for Python Package""" + +name = """tangods-cspmaster""" +version = "0.3.0" +version_info = version.split(".") +description = """SKA CSP.LMC Common Classe""" +author = "E.G" +author_email = "elisabetta.giani@inaf.it" +license = """BSD-3-Clause""" +url = """www.tango-controls.org""" +copyright = """""" + +release=0.3.0 +tag=csp-lmc-common-0.3.0 diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..cac2a6a --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,10 @@ +FROM nexus.engageska-portugal.pt/ska-docker/ska-python-buildenv:0.2.2 AS buildenv +FROM nexus.engageska-portugal.pt/ska-docker/ska-python-runtime:0.2.2 AS runtime + +# create ipython profile to so that itango doesn't fail if ipython hasn't run yet +RUN ipython profile create +#install lmc-base-classes +USER root +RUN DEBIAN_FRONTEND=noninteractive pip3 install https://nexus.engageska-portugal.pt/repository/pypi/packages/lmcbaseclasses/0.1.3+163bf057/lmcbaseclasses-0.1.3+163bf057.tar.gz + +CMD ["/venv/bin/python", "/app/csp-lmc-common/csp-lmc-common/CspMaster.py"] diff --git a/docker/Makefile b/docker/Makefile new file mode 100644 index 0000000..142895a --- /dev/null +++ b/docker/Makefile @@ -0,0 +1,198 @@ +# +# Project makefile for a Tango project. You should normally only need to modify +# DOCKER_REGISTRY_USER and PROJECT below. +# + +# +# DOCKER_REGISTRY_HOST, DOCKER_REGISTRY_USER and PROJECT are combined to define +# the Docker tag for this project. The definition below inherits the standard +# value for DOCKER_REGISTRY_HOST (=rnexus.engageska-portugal.pt) and overwrites +# DOCKER_REGISTRY_USER and PROJECT to give a final Docker tag of +# nexus.engageska-portugal.pt/tango-example/csplmc +# + +DOCKER_REGISTRY_USER:=ska-docker +PROJECT = csp-lmc-common + +# +# include makefile to pick up the standard Make targets, e.g., 'make build' +# build, 'make push' docker push procedure, etc. The other Make targets +# ('make interactive', 'make test', etc.) are defined in this file. +# +include .make/Makefile.mk + +# +# IMAGE_TO_TEST defines the tag of the Docker image to test +# +IMAGE_TO_TEST = $(DOCKER_REGISTRY_HOST)/$(DOCKER_REGISTRY_USER)/$(PROJECT):latest + +# +# CACHE_VOLUME is the name of the Docker volume used to cache eggs and wheels +# used during the test procedure. The volume is not used during the build +# procedure +# +CACHE_VOLUME = $(PROJECT)-test-cache + +# optional docker run-time arguments +DOCKER_RUN_ARGS = + +# +# Never use the network=host mode when running CI jobs, and add extra +# distinguishing identifiers to the network name and container names to +# prevent collisions with jobs from the same project running at the same +# time. +# +ifneq ($(CI_JOB_ID),) +NETWORK_MODE := tangonet-$(CI_JOB_ID) +CONTAINER_NAME_PREFIX := $(PROJECT)-$(CI_JOB_ID)- +else +CONTAINER_NAME_PREFIX := $(PROJECT)- +endif + +COMPOSE_FILES := $(wildcard *.yml) +COMPOSE_FILE_ARGS := $(foreach yml,$(COMPOSE_FILES),-f $(yml)) + +ifeq ($(OS),Windows_NT) + $(error Sorry, Windows is not supported yet) +else + UNAME_S := $(shell uname -s) + ifeq ($(UNAME_S),Linux) + DISPLAY ?= :0.0 + NETWORK_MODE ?= host + XAUTHORITY_MOUNT := /tmp/.X11-unix:/tmp/.X11-unix + XAUTHORITY ?= /hosthome/.Xauthority + # /bin/sh (=dash) does not evaluate 'docker network' conditionals correctly + SHELL := /bin/bash + endif + ifeq ($(UNAME_S),Darwin) + IF_INTERFACE := $(shell netstat -nr | awk '{ if ($$1 ~/default/) { print $$6} }') + DISPLAY := $(shell ifconfig $(IF_INTERFACE) | awk '{ if ($$1 ~/inet$$/) { print $$2} }'):0 + # network_mode = host doesn't work on MacOS, so fix to the internal network + NETWORK_MODE := tangonet + XAUTHORITY_MOUNT := $(HOME):/hosthome:ro + XAUTHORITY := /hosthome/.Xauthority + endif +endif + +# +# When running in network=host mode, point devices at a port on the host +# machine rather than at the container. +# +ifeq ($(NETWORK_MODE),host) +TANGO_HOST := $(shell hostname):10000 +MYSQL_HOST := $(shell hostname):3306 +else +# distinguish the bridge network from others by adding the project name +NETWORK_MODE := $(NETWORK_MODE)-$(PROJECT) +TANGO_HOST := $(CONTAINER_NAME_PREFIX)databaseds:10000 +MYSQL_HOST := $(CONTAINER_NAME_PREFIX)tangodb:3306 +endif + + +DOCKER_COMPOSE_ARGS := DISPLAY=$(DISPLAY) XAUTHORITY=$(XAUTHORITY) TANGO_HOST=$(TANGO_HOST) \ + NETWORK_MODE=$(NETWORK_MODE) XAUTHORITY_MOUNT=$(XAUTHORITY_MOUNT) MYSQL_HOST=$(MYSQL_HOST) \ + DOCKER_REGISTRY_HOST=$(DOCKER_REGISTRY_HOST) DOCKER_REGISTRY_USER=$(DOCKER_REGISTRY_USER) \ + CONTAINER_NAME_PREFIX=$(CONTAINER_NAME_PREFIX) COMPOSE_IGNORE_ORPHANS=true + +# +# Defines a default make target so that help is printed if make is called +# without a target +# +.DEFAULT_GOAL := help + +# +# defines a function to copy the ./test-harness directory into the container +# and then runs the requested make target in the container. The container is: +# +# 1. attached to the network of the docker-compose test system +# 2. uses a persistent volume to cache Python eggs and wheels so that fewer +# downloads are required +# 3. uses a transient volume as a working directory, in which untarred files +# and test output can be written in the container and subsequently copied +# to the host +# +make = tar -c test-harness/ | \ + docker run -i --rm --network=$(NETWORK_MODE) \ + -e TANGO_HOST=$(TANGO_HOST) \ + -v $(CACHE_VOLUME):/home/tango/.cache \ + --volumes-from=$(CONTAINER_NAME_PREFIX)rsyslog-csplmc:rw \ + -v /build -w /build -u tango $(DOCKER_RUN_ARGS) $(IMAGE_TO_TEST) \ + bash -c "sudo chown -R tango:tango /build && \ + tar x --strip-components 1 --warning=all && \ + make TANGO_HOST=$(TANGO_HOST) $1" + +test: DOCKER_RUN_ARGS = --volumes-from=$(BUILD) +test: build up ## test the application + @echo "BUILD: $(BUILD)" + $(INIT_CACHE) + $(call make,test); \ + status=$$?; \ + rm -fr build; \ + #docker-compose $(COMPOSE_FILE_ARGS) logs; + docker cp $(BUILD):/build .; \ + docker rm -f -v $(BUILD); \ + $(MAKE) down; \ + exit $$status + +lint: DOCKER_RUN_ARGS = --volumes-from=$(BUILD) +lint: build up ## lint the application (static code analysis) + $(INIT_CACHE) + $(call make,lint); \ + status=$$?; \ + docker cp $(BUILD):/build .; \ + $(MAKE) down; \ + exit $$status + +pull: ## download the application image + docker pull $(IMAGE_TO_TEST) + +up: build ## start develop/test environment +ifneq ($(NETWORK_MODE),host) + docker network inspect $(NETWORK_MODE) &> /dev/null || ([ $$? -ne 0 ] && docker network create $(NETWORK_MODE)) +endif + #$(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) pull + #to pull only the mid-cbf-mcs image remove comment on row below. + #docker pull $(DOCKER_REGISTRY_HOST)/$(DOCKER_REGISTRY_USER)/mid-cbf-mcs:latest + $(DOCKER_COMPOSE_ARGS) docker-compose -f csp-tangodb.yml up -d + # put a sleep to wait TANGO DB + @sleep 10 + $(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) up + +piplock: build ## overwrite Pipfile.lock with the image version + docker run $(IMAGE_TO_TEST) cat /app/Pipfile.lock > $(CURDIR)/Pipfile.lock + +interactive: up +interactive: ## start an interactive session using the project image (caution: R/W mounts source directory to /app) + docker run --rm -it -p 3000:3000 --name=$(CONTAINER_NAME_PREFIX)dev -e TANGO_HOST=$(TANGO_HOST) --network=$(NETWORK_MODE) \ + -v $(CURDIR):/app $(IMAGE_TO_TEST) /bin/bash + +down: ## stop develop/test environment and any interactive session + docker ps | grep $(CONTAINER_NAME_PREFIX)dev && docker stop $(PROJECT)-dev || true + $(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) down +ifneq ($(NETWORK_MODE),host) + docker network inspect $(NETWORK_MODE) &> /dev/null && ([ $$? -eq 0 ] && docker network rm $(NETWORK_MODE)) || true +endif + +dsconfigdump: up ## dump the entire configuration to the file dsconfig.json + docker exec -it $(CONTAINER_NAME_PREFIX)dsconfigdump python -m dsconfig.dump + docker exec -it $(CONTAINER_NAME_PREFIX)dsconfigdump python -m dsconfig.dump > dsconfig.json + +dsconfigadd: up ## Add a configuration json file (environment variable DSCONFIG_JSON_FILE) to the database + -docker exec -it $(CONTAINER_NAME_PREFIX)dsconfigdump json2tango -u -w -a $(DSCONFIG_JSON_FILE) + +dsconfigcheck: up ## check a json file (environment variable DSCONFIG_JSON_FILE) according to the project lib-maxiv-dsconfig json schema + -docker exec -it $(CONTAINER_NAME_PREFIX)dsconfigdump json2tango -a $(DSCONFIG_JSON_FILE) + +help: ## show this help. + @grep -hE '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' + +.PHONY: all test up down help + +# Creates Docker volume for use as a cache, if it doesn't exist already +INIT_CACHE = \ + docker volume ls | grep $(CACHE_VOLUME) || \ + docker create --name $(CACHE_VOLUME) -v $(CACHE_VOLUME):/cache $(IMAGE_TO_TEST) + +# http://cakoose.com/wiki/gnu_make_thunks +BUILD_GEN = $(shell docker create -v /build $(IMAGE_TO_TEST)) +BUILD = $(eval BUILD := $(BUILD_GEN))$(BUILD) diff --git a/docker/config/csplmc_dsconfig.json b/docker/config/csplmc_dsconfig.json new file mode 100644 index 0000000..feb0ce6 --- /dev/null +++ b/docker/config/csplmc_dsconfig.json @@ -0,0 +1,420 @@ +{ + "classes": { + "CspSubarray": { + "properties": { + "PstBeam": [ + "mid_csp_pst/sub_elt/beam_01" + ] + } + } + }, + "servers": { + "CspMaster": { + "commaster": { + "CspMaster": { + "common/elt/master": { + "attribute_properties": { + "adminMode": { + "abs_change": [ + "-1", + "1" + ], + "__value": [ + "0" + ] + }, + "healthState": { + "abs_change": [ + "-1", + "1" + ] + }, + "reportSearchBeamState": { + "__root_att": [ + "common/elt/search_beams_monitor/capabilityState" + ] + }, + "reportTimingBeamState": { + "__root_att": [ + "common/elt/timing_beams_monitor/capabilityState" + ] + }, + "reportVlbiBeamState": { + "__root_att": [ + "common/elt/vlbi_beams_monitor/capabilityState" + ] + }, + "reportSearchBeamHealthState": { + "__root_att": [ + "common/elt/search_beams_monitor/capabilityHealthState" + ] + }, + "reportTimingBeamHealthState":{ + "__root_att": [ + "common/elt/timing_beams_monitor/capabilityHealthState" + ] + }, + "reportVlbiBeamHealthState": { + "__root_att": [ + "common/elt/vlbi_beams_monitor/capabilityHealthState" + ] + }, + "reportSearchBeamObsState": { + "__root_att": [ + "common/elt/search_beams_monitor/capabilityObsState" + ] + }, + "reportTimingBeamObsState":{ + "__root_att": [ + "common/elt/timing_beams_monitor/capabilityObsState" + ] + }, + "reportVlbiBeamObsState": { + "__root_att": [ + "common/elt/vlbi_beams_monitor/capabilityObsState" + ] + }, + "reportSearchBeamAdminMode": { + "__root_att": [ + "common/elt/search_beams_monitor/capabilityAdminMode" + ] + }, + "reportTimingBeamAdminMode":{ + "__root_att": [ + "common/elt/timing_beams_monitor/capabilityAdminMode" + ] + }, + "reportVlbiBeamAdminMode": { + "__root_att": [ + "common/elt/vlbi_beams_monitor/capabilityAdminMode" + ] + }, + "searchBeamAddresses": { + "__root_att": [ + "common/elt/search_beams_monitor/cspCapabilityAddresses" + ] + }, + "timingBeamAddresses": { + "__root_att": [ + "common/elt/timing_beams_monitor/cspCapabilityAddresses" + ] + }, + "vlbiBeamAddresses": { + "__root_att": [ + "common/elt/vlbi_beams_monitor/cspCapabilityAddresses" + ] + }, + "reservedSearchBeamIDs": { + "__root_att": [ + "common/elt/search_beams_monitor/reservedIDs" + ] + }, + "unassignedSearchBeamIDs": { + "__root_att": [ + "common/elt/search_beams_monitor/unassignedIDs" + ] + }, + "unassignedTimingBeamIDs": { + "__root_att": [ + "common/elt/timing_beams_monitor/unassignedIDs" + ] + }, + "unassignedVlbiBeamIDs": { + "__root_att": [ + "common/elt/vlbi_beams_monitor/unassignedIDs" + ] + }, + "unassignedSearchBeamNum": { + "__root_att": [ + "common/elt/search_beams_monitor/numOfUnassignedIDs" + ] + }, + "unassignedTimingBeamNum": { + "__root_att": [ + "common/elt/timing_beams_monitor/numOfUnassignedIDs" + ] + }, + "unassignedVlbiBeamNum": { + "__root_att": [ + "common/elt/vlbi_beams_monitor/numOfUnassignedIDs" + ] + }, + "searchBeamMembership": { + "__root_att": [ + "common/elt/search_beams_monitor/capabilityMembership" + ] + }, + "timingBeamMembership": { + "__root_att": [ + "common/elt/timing_beams_monitor/capabilityMembership" + ] + }, + "vlbiBeamMembership": { + "__root_att": [ + "common/elt/vlbi_beams_monitor/capabilityMembership" + ] + } + }, + "properties": { + "CspCbf": [ + "mid_csp_cbf/sub_elt/master" + ], + "CspPss": [ + "mid_csp_pss/sub_elt/master" + ], + "CspPst": [ + "mid_csp_pst/sub_elt/master" + ], + "SearchBeamsMonitor":[ + "common/elt/search_beams_monitor" + ], + "TimingBeamsMonitor":[ + "common/elt/timing_beams_monitor" + ], + "VlbiBeamsMonitor":[ + "common/elt/vlbi_beams_monitor" + ], + "CspSubarrays": [ + "common/elt/subarray_01", + "common/elt/subarray_02" + ], + "MaxCapabilities": [ + "Subarray:16", + "VlbiBeam:20", + "TimingBeam:16", + "SearchBeam:1500" + ], + "polled_attr": [ + "healthstate", + "1000", + "adminmode", + "1000", + "state", + "1000", + "csppststate", + "1000", + "cspcbfstate", + "1000", + "csppsthealthstate", + "1000", + "pssadminmode", + "1000", + "cbfadminmode", + "1000", + "csppssstate", + "1000", + "pstadminmode", + "1000", + "commandprogress", + "3000", + "cspcbfhealthstate", + "1000", + "csppsshealthstate", + "1000" + ] + } + } + } + } + }, + "CspCapabilityMonitor": { + "searchbeams": { + "CspCapabilityMonitor": { + "common/elt/search_beams_monitor": { + "attribute_properties": { + "adminMode": { + "__value": [ + "0" + ] + } + }, + "properties": { + "CapabilityDevices": [ + "common/search_beams/0001", + "common/search_beams/0002", + "common/search_beams/0003" + ] + } + } + } + }, + "timingbeams": { + "CspCapabilityMonitor": { + "common/elt/timing_beams_monitor": { + "attribute_properties": { + "adminMode": { + "__value": [ + "0" + ] + } + }, + "properties": { + "CapabilityDevices": [ + "common/timing_beams/01", + "common/timing_beams/02", + "common/timing_beams/03" + ] + } + } + } + }, + "vlbibeams": { + "CspCapabilityMonitor": { + "common/elt/vlbi_beams_monitor": { + "attribute_properties": { + "adminMode": { + "__value": [ + "0" + ] + } + }, + "properties": { + "CapabilityDevices": [ + "common/vlbi_beams/01", + "common/vlbi_beams/02", + "common/vlbi_beams/03" + ] + } + } + } + } + }, + "CspSubarray": { + "comsub1": { + "CspSubarray": { + "common/elt/subarray_01": { + "attribute_properties": { + "adminMode": { + "abs_change": [ + "-1", + "1" + ] + }, + "healthState": { + "abs_change": [ + "-1", + "1" + ] + }, + "obsState": { + "abs_change": [ + "-1", + "1" + ] + }, + "cbfOutputLink": { + "__root_att": [ + "mid_csp_cbf/sub_elt/subarray_01/outputLinksDistribution" + ] + } + }, + "properties": { + "CspMaster": [ + "common/elt/master" + ], + "CbfSubarray": [ + "mid_csp_cbf/sub_elt/subarray_01" + ], + "PssSubarray": [ + "mid_csp_pss/sub_elt/subarray_01" + ], + "SubID": [ + "1" + ], + "polled_attr": [ + "state", + "1000", + "healthstate", + "1000", + "adminmode", + "1000", + "obsstate", + "1000", + "obsmode", + "1000" + ] + } + } + } + }, + "comsub2": { + "CspSubarray": { + "common/elt/subarray_02": { + "attribute_properties": { + "adminMode": { + "abs_change": [ + "-1", + "1" + ] + }, + "healthState": { + "abs_change": [ + "-1", + "1" + ] + }, + "obsState": { + "abs_change": [ + "-1", + "1" + ] + }, + "cbfOutputLink": { + "__root_att": [ + "mid_csp_cbf/sub_elt/subarray_02/outputLinksDistribution" + ] + } + }, + "properties": { + "CspMaster": [ + "common/elt/master" + ], + "SubID": [ + "2" + ], + "CbfSubarray": [ + "mid_csp_cbf/sub_elt/subarray_02" + ], + "PssSubarray": [ + "mid_csp_pss/sub_elt/subarray_02" + ], + "polled_attr": [ + "state", + "1000", + "healthstate", + "1000", + "adminmode", + "1000", + "obsstate", + "1000", + "obsmode", + "1000" + ] + } + } + } + } + }, + "DataBaseds": { + "2": { + "DataBase": { + "sys/database/2": {} + } + } + }, + "TangoAccessControl": { + "1": { + "TangoAccessControl": { + "sys/access_control/1": {} + } + } + }, + "TangoTest": { + "test": { + "TangoTest": { + "sys/tg_test/1": {} + } + } + } + } +} diff --git a/docker/config/csplmc_dsconfig.json.sav b/docker/config/csplmc_dsconfig.json.sav new file mode 100644 index 0000000..0cf8bec --- /dev/null +++ b/docker/config/csplmc_dsconfig.json.sav @@ -0,0 +1,406 @@ +{ + "classes": { + "CspSubarray": { + "properties": { + "PstBeam": [ + "mid_csp_pst/sub_elt/beam_01" + ] + } + } + }, + "servers": { + "CspMaster": { + "commaster": { + "CspMaster": { + "common/elt/master": { + "attribute_properties": { + "adminMode": { + "abs_change": [ + "-1", + "1" + ], + "__value": [ + "0" + ] + }, + "healthState": { + "abs_change": [ + "-1", + "1" + ] + }, + "reportSearchBeamState": { + "__root_att": [ + "common/elt/search_beams_monitor/capabilityState" + ] + }, + "reportTimingBeamState": { + "__root_att": [ + "common/elt/timing_beams_monitor/capabilityState" + ] + }, + "reportVlbiBeamState": { + "__root_att": [ + "common/elt/vlbi_beams_monitor/capabilityState" + ] + }, + "reportSearchBeamHealthState": { + "__root_att": [ + "common/elt/search_beams_monitor/capabilityHealthState" + ] + }, + "reportTimingBeamHealthState":{ + "__root_att": [ + "common/elt/timing_beams_monitor/capabilityHealthState" + ] + }, + "reportVlbiBeamHealthState": { + "__root_att": [ + "common/elt/vlbi_beams_monitor/capabilityHealthState" + ] + }, + "reportSearchBeamObsState": { + "__root_att": [ + "common/elt/search_beams_monitor/capabilityObsState" + ] + }, + "reportTimingBeamObsState":{ + "__root_att": [ + "common/elt/timing_beams_monitor/capabilityObsState" + ] + }, + "reportVlbiBeamObsState": { + "__root_att": [ + "common/elt/vlbi_beams_monitor/capabilityObsState" + ] + }, + "reportSearchBeamAdminMode": { + "__root_att": [ + "common/elt/search_beams_monitor/capabilityAdminMode" + ] + }, + "reportTimingBeamAdminMode":{ + "__root_att": [ + "common/elt/timing_beams_monitor/capabilityAdminMode" + ] + }, + "reportVlbiBeamAdminMode": { + "__root_att": [ + "common/elt/vlbi_beams_monitor/capabilityAdminMode" + ] + }, + "searchBeamAddresses": { + "__root_att": [ + "common/elt/search_beams_monitor/capabilityAddresses" + ] + }, + "timingBeamAddresses": { + "__root_att": [ + "common/elt/timing_beams_monitor/capabilityAddresses" + ] + }, + "vlbiBeamAddresses": { + "__root_att": [ + "common/elt/vlbi_beams_monitor/capabilityAddresses" + ] + }, + "reservedSearchBeamIDs": { + "__root_att": [ + "common/elt/search_beams_monitor/reservedIDs" + ] + }, + "unassignedSearchBeamIDs": { + "__root_att": [ + "common/elt/search_beams_monitor/unassignedIDs" + ] + }, + "unassignedTimingBeamIDs": { + "__root_att": [ + "common/elt/timing_beams_monitor/unassignedIDs" + ] + }, + "unassignedVlbiBeamIDs": { + "__root_att": [ + "common/elt/vlbi_beams_monitor/unassignedIDs" + ] + }, + "searchBeamMembership": { + "__root_att": [ + "common/elt/search_beams_monitor/capabilityMembership" + ] + }, + "timingBeamMembership": { + "__root_att": [ + "common/elt/timing_beams_monitor/capabilityMembership" + ] + }, + "vlbiBeamMembership": { + "__root_att": [ + "common/elt/vlbi_beams_monitor/capabilityMembership" + ] + } + }, + "properties": { + "CspCbf": [ + "mid_csp_cbf/sub_elt/master" + ], + "CspPss": [ + "mid_csp_pss/sub_elt/master" + ], + "CspPst": [ + "mid_csp_pst/sub_elt/master" + ], + "SearchBeamsMonitor":[ + "common/elt/search_beams_monitor" + ], + "TimingBeamsMonitor":[ + "common/elt/timing_beams_monitor" + ], + "VlbiBeamsMonitor":[ + "common/elt/vlbi_beams_monitor" + ], + "CspSubarrays": [ + "common/elt/subarray_01", + "common/elt/subarray_02" + ], + "MaxCapabilities": [ + "Subarray:16", + "VlbiBeam:20", + "TimingBeam:16", + "SearchBeam:1500" + ], + "polled_attr": [ + "healthstate", + "1000", + "adminmode", + "1000", + "state", + "1000", + "csppststate", + "1000", + "cspcbfstate", + "1000", + "csppsthealthstate", + "1000", + "pssadminmode", + "1000", + "cbfadminmode", + "1000", + "csppssstate", + "1000", + "pstadminmode", + "1000", + "commandprogress", + "3000", + "cspcbfhealthstate", + "1000", + "csppsshealthstate", + "1000" + ] + } + } + } + } + }, + "CspCapabilityMonitor": { + "searchbeams": { + "CspCapabilityMonitor": { + "common/elt/search_beams_monitor": { + "attribute_properties": { + "adminMode": { + "__value": [ + "0" + ] + } + }, + "properties": { + "CapabilityDevices": [ + "common/search_beams/0001", + "common/search_beams/0002", + "common/search_beams/0003" + ] + } + } + } + }, + "timingbeams": { + "CspCapabilityMonitor": { + "common/elt/timing_beams_monitor": { + "attribute_properties": { + "adminMode": { + "__value": [ + "0" + ] + } + }, + "properties": { + "CapabilityDevices": [ + "common/timing_beams/01", + "common/timing_beams/02", + "common/timing_beams/03" + ] + } + } + } + }, + "vlbibeams": { + "CspCapabilityMonitor": { + "common/elt/vlbi_beams_monitor": { + "attribute_properties": { + "adminMode": { + "__value": [ + "0" + ] + } + }, + "properties": { + "CapabilityDevices": [ + "VlbiBeams": [ + "common/vlbi_beams/01", + "common/vlbi_beams/02", + "common/vlbi_beams/03" + ] + } + } + } + } + }, + "CspSubarray": { + "comsub1": { + "CspSubarray": { + "common/elt/subarray_01": { + "attribute_properties": { + "adminMode": { + "abs_change": [ + "-1", + "1" + ] + }, + "healthState": { + "abs_change": [ + "-1", + "1" + ] + }, + "obsState": { + "abs_change": [ + "-1", + "1" + ] + }, + "cbfOutputLink": { + "__root_att": [ + "mid_csp_cbf/sub_elt/subarray_01/outputLinksDistribution" + ] + } + }, + "properties": { + "CspMaster": [ + "common/elt/master" + ], + "CbfSubarray": [ + "mid_csp_cbf/sub_elt/subarray_01" + ], + "PssSubarray": [ + "mid_csp_pss/sub_elt/subarray_01" + ], + "SubID": [ + "1" + ], + "polled_attr": [ + "state", + "1000", + "healthstate", + "1000", + "adminmode", + "1000", + "obsstate", + "1000", + "obsmode", + "1000" + ] + } + } + } + }, + "comsub2": { + "CspSubarray": { + "common/elt/subarray_02": { + "attribute_properties": { + "adminMode": { + "abs_change": [ + "-1", + "1" + ] + }, + "healthState": { + "abs_change": [ + "-1", + "1" + ] + }, + "obsState": { + "abs_change": [ + "-1", + "1" + ] + }, + "cbfOutputLink": { + "__root_att": [ + "mid_csp_cbf/sub_elt/subarray_02/outputLinksDistribution" + ] + } + }, + "properties": { + "CspMaster": [ + "common/elt/master" + ], + "SubID": [ + "2" + ], + "CbfSubarray": [ + "mid_csp_cbf/sub_elt/subarray_02" + ], + "PssSubarray": [ + "mid_csp_pss/sub_elt/subarray_02" + ], + "polled_attr": [ + "state", + "1000", + "healthstate", + "1000", + "adminmode", + "1000", + "obsstate", + "1000", + "obsmode", + "1000" + ] + } + } + } + } + }, + "DataBaseds": { + "2": { + "DataBase": { + "sys/database/2": {} + } + } + }, + "TangoAccessControl": { + "1": { + "TangoAccessControl": { + "sys/access_control/1": {} + } + } + }, + "TangoTest": { + "test": { + "TangoTest": { + "sys/tg_test/1": {} + } + } + } + } +} diff --git a/docker/config/midcbf_dsconfig.json b/docker/config/midcbf_dsconfig.json new file mode 100644 index 0000000..1b76326 --- /dev/null +++ b/docker/config/midcbf_dsconfig.json @@ -0,0 +1,1121 @@ +{ + "servers": { + "CbfMaster": { + "master": { + "CbfMaster": { + "mid_csp_cbf/sub_elt/master": { + "attribute_properties": { + "adminMode": { + "abs_change": [ + "-1", + "1" + ], + "__value":[ + "0" + ] + }, + "healthState": { + "abs_change": [ + "-1", + "1" + ] + } + }, + "properties": { + "CbfSubarray": [ + "mid_csp_cbf/sub_elt/subarray_01", + "mid_csp_cbf/sub_elt/subarray_02" + ], + "FSP": [ + "mid_csp_cbf/fsp/01", + "mid_csp_cbf/fsp/02", + "mid_csp_cbf/fsp/03", + "mid_csp_cbf/fsp/04" + ], + "MaxCapabilities": [ + "VCC:4", + "FSP:4", + "Subarray:2" + ], + "VCC": [ + "mid_csp_cbf/vcc/001", + "mid_csp_cbf/vcc/002", + "mid_csp_cbf/vcc/003", + "mid_csp_cbf/vcc/004" + ], + "polled_attr": [ + "reportfspstate", + "1000", + "reportvccadminmode", + "1000", + "reportvcchealthstate", + "1000", + "receptortovcc", + "1000", + "reportvccsubarraymembership", + "1000", + "reportfspsubarraymembership", + "1000", + "reportfsphealthstate", + "1000", + "healthstate", + "1000", + "adminmode", + "1000", + "subarrayscanid", + "1000", + "reportfspadminmode", + "1000", + "commandprogress", + "2000", + "reportsubarrayhealthstate", + "1000", + "reportvccstate", + "1000", + "reportsubarrayadminmode", + "1000", + "vcctoreceptor", + "3000", + "reportsubarraystate", + "3000", + "state", + "1000" + ] + } + } + } + } + }, + "CbfSubarrayMulti": { + "cbfSubarray-01": { + "CbfSubarray": { + "mid_csp_cbf/sub_elt/subarray_01": { + "attribute_properties": { + "adminMode": { + "abs_change": [ + "-1", + "1" + ] + }, + "healthState": { + "abs_change": [ + "-1", + "1" + ] + }, + "obsState": { + "abs_change": [ + "-1", + "1" + ] + }, + "scanID": { + "abs_change": [ + "-1", + "1" + ] + } + }, + "properties": { + "CbfMasterAddress": [ + "mid_csp_cbf/sub_elt/master" + ], + "FSP": [ + "mid_csp_cbf/fsp/01", + "mid_csp_cbf/fsp/02", + "mid_csp_cbf/fsp/03", + "mid_csp_cbf/fsp/04" + ], + "FspSubarray": [ + "mid_csp_cbf/fspSubarray/01_01", + "mid_csp_cbf/fspSubarray/02_01", + "mid_csp_cbf/fspSubarray/03_01", + "mid_csp_cbf/fspSubarray/04_01" + ], + "SW1Address": [ + "mid_csp_cbf/sw1/01" + ], + "SW2Address": [ + "mid_csp_cbf/sw2/01" + ], + "SubID": [ + "1" + ], + "VCC": [ + "mid_csp_cbf/vcc/001", + "mid_csp_cbf/vcc/002", + "mid_csp_cbf/vcc/003", + "mid_csp_cbf/vcc/004" + ], + "polled_attr": [ + "state", + "1000", + "healthstate", + "1000", + "adminmode", + "1000", + "fsphealthstate", + "1000", + "fspstate", + "1000", + "vccstate", + "1000", + "vcchealthstate", + "1000", + "obsstate", + "1000", + "scanid", + "1000", + "outputLinksDistribution", + "1000" + ] + } + } + }, + "SearchWindow": { + "mid_csp_cbf/sw1/01": {}, + "mid_csp_cbf/sw2/01": {} + } + }, + "cbfSubarray-02": { + "CbfSubarray": { + "mid_csp_cbf/sub_elt/subarray_02": { + "attribute_properties": { + "adminMode": { + "abs_change": [ + "-1", + "1" + ] + }, + "healthState": { + "abs_change": [ + "-1", + "1" + ] + }, + "obsState": { + "abs_change": [ + "-1", + "1" + ] + }, + "scanID": { + "abs_change": [ + "-1", + "1" + ] + } + }, + "properties": { + "CbfMasterAddress": [ + "mid_csp_cbf/sub_elt/master" + ], + "FSP": [ + "mid_csp_cbf/fsp/01", + "mid_csp_cbf/fsp/02", + "mid_csp_cbf/fsp/03", + "mid_csp_cbf/fsp/04" + ], + "FspSubarray": [ + "mid_csp_cbf/fspSubarray/01_02", + "mid_csp_cbf/fspSubarray/02_02", + "mid_csp_cbf/fspSubarray/03_02", + "mid_csp_cbf/fspSubarray/04_02" + ], + "SW1Address": [ + "mid_csp_cbf/sw1/02" + ], + "SW2Address": [ + "mid_csp_cbf/sw2/02" + ], + "SubID": [ + "2" + ], + "VCC": [ + "mid_csp_cbf/vcc/001", + "mid_csp_cbf/vcc/002", + "mid_csp_cbf/vcc/003", + "mid_csp_cbf/vcc/004" + ], + "polled_attr": [ + "scanid", + "1000", + "healthstate", + "1000", + "state", + "1000", + "obsstate", + "1000", + "vcchealthstate", + "1000", + "adminmode", + "1000", + "fspstate", + "1000", + "fsphealthstate", + "1000", + "vccstate", + "1000", + "outputLinksDistribution", + "1000" + ] + } + } + }, + "SearchWindow": { + "mid_csp_cbf/sw1/02": {}, + "mid_csp_cbf/sw2/02": {} + } + } + }, + "DataBaseds": { + "2": { + "DataBase": { + "sys/database/2": {} + } + } + }, + "FspMulti": { + "fsp-01": { + "Fsp": { + "mid_csp_cbf/fsp/01": { + "attribute_properties": { + "adminMode": { + "abs_change": [ + "-1", + "1" + ] + }, + "healthState": { + "abs_change": [ + "-1", + "1" + ] + }, + "subarrayMembership": { + "abs_change": [ + "-1", + "1" + ] + } + }, + "properties": { + "CorrelationAddress": [ + "mid_csp_cbf/fsp_corr/01" + ], + "FspID": [ + "1" + ], + "PSSAddress": [ + "mid_csp_cbf/fsp_pss/01" + ], + "PSTAddress": [ + "mid_csp_cbf/fsp_pst/01" + ], + "VLBIAddress": [ + "mid_csp_cbf/fsp_vlbi/01" + ], + "FspSubarray": [ + "mid_csp_cbf/fspSubarray/01_01", + "mid_csp_cbf/fspSubarray/01_02" + ], + "polled_attr": [ + "adminmode", + "1000", + "healthstate", + "1000", + "state", + "1000", + "subarraymembership", + "1000" + ] + } + } + }, + "FspCorr": { + "mid_csp_cbf/fsp_corr/01": {} + }, + "FspPss": { + "mid_csp_cbf/fsp_pss/01": {} + }, + "FspPst": { + "mid_csp_cbf/fsp_pst/01": {} + }, + "FspSubarray": { + "mid_csp_cbf/fspSubarray/01_01": { + "properties": { + "CbfMasterAddress": [ + "mid_csp_cbf/sub_elt/master" + ], + "CbfSubarrayAddress": [ + "mid_csp_cbf/sub_elt/subarray_01" + ], + "FspID": [ + "1" + ], + "SubID": [ + "1" + ], + "VCC": [ + "mid_csp_cbf/vcc/001", + "mid_csp_cbf/vcc/002", + "mid_csp_cbf/vcc/003", + "mid_csp_cbf/vcc/004" + ] + } + }, + "mid_csp_cbf/fspSubarray/01_02": { + "properties": { + "CbfMasterAddress": [ + "mid_csp_cbf/sub_elt/master" + ], + "CbfSubarrayAddress": [ + "mid_csp_cbf/sub_elt/subarray_02" + ], + "FspID": [ + "1" + ], + "SubID": [ + "2" + ], + "VCC": [ + "mid_csp_cbf/vcc/001", + "mid_csp_cbf/vcc/002", + "mid_csp_cbf/vcc/003", + "mid_csp_cbf/vcc/004" + ] + } + } + }, + "FspVlbi": { + "mid_csp_cbf/fsp_vlbi/01": {} + } + }, + "fsp-02": { + "Fsp": { + "mid_csp_cbf/fsp/02": { + "attribute_properties": { + "adminMode": { + "abs_change": [ + "-1", + "1" + ] + }, + "healthState": { + "abs_change": [ + "-1", + "1" + ] + }, + "subarrayMembership": { + "abs_change": [ + "-1", + "1" + ] + } + }, + "properties": { + "CorrelationAddress": [ + "mid_csp_cbf/fsp_corr/02" + ], + "FspID": [ + "2" + ], + "PSSAddress": [ + "mid_csp_cbf/fsp_pss/02" + ], + "PSTAddress": [ + "mid_csp_cbf/fsp_pst/02" + ], + "VLBIAddress": [ + "mid_csp_cbf/fsp_vlbi/02" + ], + "FspSubarray": [ + "mid_csp_cbf/fspSubarray/02_01", + "mid_csp_cbf/fspSubarray/02_02" + ], + "polled_attr": [ + "adminmode", + "1000", + "healthstate", + "1000", + "state", + "1000", + "subarraymembership", + "1000" + ] + } + } + }, + "FspCorr": { + "mid_csp_cbf/fsp_corr/02": {} + }, + "FspPss": { + "mid_csp_cbf/fsp_pss/02": {} + }, + "FspPst": { + "mid_csp_cbf/fsp_pst/02": {} + }, + "FspSubarray": { + "mid_csp_cbf/fspSubarray/02_01": { + "properties": { + "CbfMasterAddress": [ + "mid_csp_cbf/sub_elt/master" + ], + "CbfSubarrayAddress": [ + "mid_csp_cbf/sub_elt/subarray_01" + ], + "FspID": [ + "2" + ], + "SubID": [ + "1" + ], + "VCC": [ + "mid_csp_cbf/vcc/001", + "mid_csp_cbf/vcc/002", + "mid_csp_cbf/vcc/003", + "mid_csp_cbf/vcc/004" + ] + } + }, + "mid_csp_cbf/fspSubarray/02_02": { + "properties": { + "CbfMasterAddress": [ + "mid_csp_cbf/sub_elt/master" + ], + "CbfSubarrayAddress": [ + "mid_csp_cbf/sub_elt/subarray_02" + ], + "FspID": [ + "2" + ], + "SubID": [ + "2" + ], + "VCC": [ + "mid_csp_cbf/vcc/001", + "mid_csp_cbf/vcc/002", + "mid_csp_cbf/vcc/003", + "mid_csp_cbf/vcc/004" + ] + } + } + }, + "FspVlbi": { + "mid_csp_cbf/fsp_vlbi/02": {} + } + }, + "fsp-03": { + "Fsp": { + "mid_csp_cbf/fsp/03": { + "attribute_properties": { + "adminMode": { + "abs_change": [ + "-1", + "1" + ] + }, + "healthState": { + "abs_change": [ + "-1", + "1" + ] + }, + "subarrayMembership": { + "abs_change": [ + "-1", + "1" + ] + } + }, + "properties": { + "CorrelationAddress": [ + "mid_csp_cbf/fsp_corr/03" + ], + "FspID": [ + "3" + ], + "PSSAddress": [ + "mid_csp_cbf/fsp_pss/03" + ], + "PSTAddress": [ + "mid_csp_cbf/fsp_pst/03" + ], + "VLBIAddress": [ + "mid_csp_cbf/fsp_vlbi/03" + ], + "FspSubarray": [ + "mid_csp_cbf/fspSubarray/03_01", + "mid_csp_cbf/fspSubarray/03_02" + ], + "polled_attr": [ + "adminmode", + "1000", + "healthstate", + "1000", + "state", + "1000", + "subarraymembership", + "1000" + ] + } + } + }, + "FspCorr": { + "mid_csp_cbf/fsp_corr/03": {} + }, + "FspPss": { + "mid_csp_cbf/fsp_pss/03": {} + }, + "FspPst": { + "mid_csp_cbf/fsp_pst/03": {} + }, + "FspSubarray": { + "mid_csp_cbf/fspSubarray/03_01": { + "properties": { + "CbfMasterAddress": [ + "mid_csp_cbf/sub_elt/master" + ], + "CbfSubarrayAddress": [ + "mid_csp_cbf/sub_elt/subarray_01" + ], + "FspID": [ + "3" + ], + "SubID": [ + "1" + ], + "VCC": [ + "mid_csp_cbf/vcc/001", + "mid_csp_cbf/vcc/002", + "mid_csp_cbf/vcc/003", + "mid_csp_cbf/vcc/004" + ] + } + }, + "mid_csp_cbf/fspSubarray/03_02": { + "properties": { + "CbfMasterAddress": [ + "mid_csp_cbf/sub_elt/master" + ], + "CbfSubarrayAddress": [ + "mid_csp_cbf/sub_elt/subarray_02" + ], + "FspID": [ + "3" + ], + "SubID": [ + "2" + ], + "VCC": [ + "mid_csp_cbf/vcc/001", + "mid_csp_cbf/vcc/002", + "mid_csp_cbf/vcc/003", + "mid_csp_cbf/vcc/004" + ] + } + } + }, + "FspVlbi": { + "mid_csp_cbf/fsp_vlbi/03": {} + } + }, + "fsp-04": { + "Fsp": { + "mid_csp_cbf/fsp/04": { + "attribute_properties": { + "adminMode": { + "abs_change": [ + "-1", + "1" + ] + }, + "healthState": { + "abs_change": [ + "-1", + "1" + ] + }, + "subarrayMembership": { + "abs_change": [ + "-1", + "1" + ] + } + }, + "properties": { + "CorrelationAddress": [ + "mid_csp_cbf/fsp_corr/04" + ], + "FspID": [ + "4" + ], + "PSSAddress": [ + "mid_csp_cbf/fsp_pss/04" + ], + "PSTAddress": [ + "mid_csp_cbf/fsp_pst/04" + ], + "VLBIAddress": [ + "mid_csp_cbf/fsp_vlbi/04" + ], + "FspSubarray": [ + "mid_csp_cbf/fspSubarray/04_01", + "mid_csp_cbf/fspSubarray/04_02" + ], + "polled_attr": [ + "adminmode", + "1000", + "healthstate", + "1000", + "state", + "1000", + "subarraymembership", + "1000" + ] + } + } + }, + "FspCorr": { + "mid_csp_cbf/fsp_corr/04": {} + }, + "FspPss": { + "mid_csp_cbf/fsp_pss/04": {} + }, + "FspPst": { + "mid_csp_cbf/fsp_pst/04": {} + }, + "FspSubarray": { + "mid_csp_cbf/fspSubarray/04_01": { + "properties": { + "CbfMasterAddress": [ + "mid_csp_cbf/sub_elt/master" + ], + "CbfSubarrayAddress": [ + "mid_csp_cbf/sub_elt/subarray_01" + ], + "FspID": [ + "4" + ], + "SubID": [ + "1" + ], + "VCC": [ + "mid_csp_cbf/vcc/001", + "mid_csp_cbf/vcc/002", + "mid_csp_cbf/vcc/003", + "mid_csp_cbf/vcc/004" + ] + } + }, + "mid_csp_cbf/fspSubarray/04_02": { + "properties": { + "CbfMasterAddress": [ + "mid_csp_cbf/sub_elt/master" + ], + "CbfSubarrayAddress": [ + "mid_csp_cbf/sub_elt/subarray_02" + ], + "FspID": [ + "4" + ], + "SubID": [ + "2" + ], + "VCC": [ + "mid_csp_cbf/vcc/001", + "mid_csp_cbf/vcc/002", + "mid_csp_cbf/vcc/003", + "mid_csp_cbf/vcc/004" + ] + } + } + }, + "FspVlbi": { + "mid_csp_cbf/fsp_vlbi/04": {} + } + } + }, + "TangoAccessControl": { + "1": { + "TangoAccessControl": { + "sys/access_control/1": {} + } + } + }, + "TangoTest": { + "test": { + "TangoTest": { + "sys/tg_test/1": {} + } + } + }, + "VccMulti": { + "vcc-001": { + "Vcc": { + "mid_csp_cbf/vcc/001": { + "attribute_properties": { + "adminMode": { + "abs_change": [ + "-1", + "1" + ] + }, + "healthState": { + "abs_change": [ + "-1", + "1" + ] + }, + "subarrayMembership": { + "abs_change": [ + "-1", + "1" + ] + } + }, + "properties": { + "Band1And2Address": [ + "mid_csp_cbf/vcc_band12/001" + ], + "Band3Address": [ + "mid_csp_cbf/vcc_band3/001" + ], + "Band4Address": [ + "mid_csp_cbf/vcc_band4/001" + ], + "Band5Address": [ + "mid_csp_cbf/vcc_band5/001" + ], + "SW1Address": [ + "mid_csp_cbf/vcc_sw1/001" + ], + "SW2Address": [ + "mid_csp_cbf/vcc_sw2/001" + ], + "VccID": [ + "1" + ], + "polled_attr": [ + "state", + "1000", + "healthstate", + "1000", + "adminmode", + "1000", + "subarraymembership", + "1000" + ] + } + } + }, + "VccBand1And2": { + "mid_csp_cbf/vcc_band12/001": {} + }, + "VccBand3": { + "mid_csp_cbf/vcc_band3/001": {} + }, + "VccBand4": { + "mid_csp_cbf/vcc_band4/001": {} + }, + "VccBand5": { + "mid_csp_cbf/vcc_band5/001": {} + }, + "VccSearchWindow": { + "mid_csp_cbf/vcc_sw1/001": {}, + "mid_csp_cbf/vcc_sw2/001": {} + } + }, + "vcc-002": { + "Vcc": { + "mid_csp_cbf/vcc/002": { + "attribute_properties": { + "adminMode": { + "abs_change": [ + "-1", + "1" + ] + }, + "healthState": { + "abs_change": [ + "-1", + "1" + ] + }, + "subarrayMembership": { + "abs_change": [ + "-1", + "1" + ] + } + }, + "properties": { + "Band1And2Address": [ + "mid_csp_cbf/vcc_band12/002" + ], + "Band3Address": [ + "mid_csp_cbf/vcc_band3/002" + ], + "Band4Address": [ + "mid_csp_cbf/vcc_band4/002" + ], + "Band5Address": [ + "mid_csp_cbf/vcc_band5/002" + ], + "SW1Address": [ + "mid_csp_cbf/vcc_sw1/002" + ], + "SW2Address": [ + "mid_csp_cbf/vcc_sw2/002" + ], + "VccID": [ + "2" + ], + "polled_attr": [ + "state", + "1000", + "healthstate", + "1000", + "adminmode", + "1000", + "subarraymembership", + "1000" + ] + } + } + }, + "VccBand1And2": { + "mid_csp_cbf/vcc_band12/002": {} + }, + "VccBand3": { + "mid_csp_cbf/vcc_band3/002": {} + }, + "VccBand4": { + "mid_csp_cbf/vcc_band4/002": {} + }, + "VccBand5": { + "mid_csp_cbf/vcc_band5/002": {} + }, + "VccSearchWindow": { + "mid_csp_cbf/vcc_sw1/002": {}, + "mid_csp_cbf/vcc_sw2/002": {} + } + }, + "vcc-003": { + "Vcc": { + "mid_csp_cbf/vcc/003": { + "attribute_properties": { + "adminMode": { + "abs_change": [ + "-1", + "1" + ] + }, + "healthState": { + "abs_change": [ + "-1", + "1" + ] + }, + "subarrayMembership": { + "abs_change": [ + "-1", + "1" + ] + } + }, + "properties": { + "Band1And2Address": [ + "mid_csp_cbf/vcc_band12/003" + ], + "Band3Address": [ + "mid_csp_cbf/vcc_band3/003" + ], + "Band4Address": [ + "mid_csp_cbf/vcc_band4/003" + ], + "Band5Address": [ + "mid_csp_cbf/vcc_band5/003" + ], + "SW1Address": [ + "mid_csp_cbf/vcc_sw1/003" + ], + "SW2Address": [ + "mid_csp_cbf/vcc_sw2/003" + ], + "VccID": [ + "3" + ], + "polled_attr": [ + "state", + "1000", + "healthstate", + "1000", + "adminmode", + "1000", + "subarraymembership", + "1000" + ] + } + } + }, + "VccBand1And2": { + "mid_csp_cbf/vcc_band12/003": {} + }, + "VccBand3": { + "mid_csp_cbf/vcc_band3/003": {} + }, + "VccBand4": { + "mid_csp_cbf/vcc_band4/003": {} + }, + "VccBand5": { + "mid_csp_cbf/vcc_band5/003": {} + }, + "VccSearchWindow": { + "mid_csp_cbf/vcc_sw1/003": {}, + "mid_csp_cbf/vcc_sw2/003": {} + } + }, + "vcc-004": { + "Vcc": { + "mid_csp_cbf/vcc/004": { + "attribute_properties": { + "adminMode": { + "abs_change": [ + "-1", + "1" + ] + }, + "healthState": { + "abs_change": [ + "-1", + "1" + ] + }, + "subarrayMembership": { + "abs_change": [ + "-1", + "1" + ] + } + }, + "properties": { + "Band1And2Address": [ + "mid_csp_cbf/vcc_band12/004" + ], + "Band3Address": [ + "mid_csp_cbf/vcc_band3/004" + ], + "Band4Address": [ + "mid_csp_cbf/vcc_band4/004" + ], + "Band5Address": [ + "mid_csp_cbf/vcc_band5/004" + ], + "SW1Address": [ + "mid_csp_cbf/vcc_sw1/004" + ], + "SW2Address": [ + "mid_csp_cbf/vcc_sw2/004" + ], + "VccID": [ + "4" + ], + "polled_attr": [ + "state", + "1000", + "healthstate", + "1000", + "adminmode", + "1000", + "subarraymembership", + "1000" + ] + } + } + }, + "VccBand1And2": { + "mid_csp_cbf/vcc_band12/004": {} + }, + "VccBand3": { + "mid_csp_cbf/vcc_band3/004": {} + }, + "VccBand4": { + "mid_csp_cbf/vcc_band4/004": {} + }, + "VccBand5": { + "mid_csp_cbf/vcc_band5/004": {} + }, + "VccSearchWindow": { + "mid_csp_cbf/vcc_sw1/004": {}, + "mid_csp_cbf/vcc_sw2/004": {} + } + } + }, + "TmCspSubarrayLeafNodeTest": { + "tm": { + "TmCspSubarrayLeafNodeTest": { + "ska_mid/tm_leaf_node/csp_subarray_01": { + "attribute_properties": { + "dopplerPhaseCorrection": { + "abs_change": [ + "-1", + "1" + ] + } + }, + "properties": { + "CspMasterAddress": [ + "mid_csp/elt/master" + ], + "CspSubarrayAddress": [ + "mid_csp/elt/subarray_01" + ], + + "polled_attr": [ + "delaymodel", + "1000", + "visdestinationaddress", + "1000", + "dopplerphasecorrection", + "1000" + ] + } + } + } + }, + "tm2": { + "TmCspSubarrayLeafNodeTest": { + "ska_mid/tm_leaf_node/csp_subarray_02": { + "attribute_properties": { + "dopplerPhaseCorrection": { + "abs_change": [ + "-1", + "1" + ] + } + }, + "properties": { + "CspMasterAddress": [ + "mid_csp/elt/master" + ], + "CspSubarrayAddress": [ + "mid_csp/elt/subarray_02" + ], + + "polled_attr": [ + "delaymodel", + "1000", + "visdestinationaddress", + "1000", + "dopplerphasecorrection", + "1000" + ] + } + } + } + } + } + } +} diff --git a/docker/csp-lmc.yml b/docker/csp-lmc.yml new file mode 100644 index 0000000..f8e9a29 --- /dev/null +++ b/docker/csp-lmc.yml @@ -0,0 +1,133 @@ +# +# Docker compose file for TANGO database and database device server +# +# Defines: +# - tangodb: MariaDB database with TANGO schema +# - databaseds: TANGO database device server +# - rsyslog-csplmc: rsyslog service for logger +# - cspmaster: CspMaster device +# +# Requires: +# - None +# +version: '2.2' + +services: + csp_dsconfig: + image: nexus.engageska-portugal.pt/ska-docker/tango-dsconfig:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}csp_dsconfig + depends_on: + - databaseds + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=60 --strict -- + json2tango -w -a -u csplmc/config/csplmc_dsconfig.json && sleep infinity" + volumes: + - .:/csplmc + + cspsubarray01: + image: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}/csp-lmc-common:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}cspsubarray01 + depends_on: + - csp_dsconfig + - cspmaster + - rsyslog-csplmc + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=30 --strict -- + retry --max=5 -- tango_admin --ping-device common/elt/master &&\ + /venv/bin/python /app/csp-lmc-common/CspSubarray.py comsub1" + volumes_from: + - rsyslog-csplmc:rw + + cspsubarray02: + image: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}/csp-lmc-common:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}cspsubarray02 + depends_on: + - csp_dsconfig + - cspmaster + - rsyslog-csplmc + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=30 --strict -- + retry --max=5 -- tango_admin --ping-device common/elt/master &&\ + /venv/bin/python /app/csp-lmc-common/CspSubarray.py comsub2" + volumes_from: + - rsyslog-csplmc:rw + + cspmaster: + image: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}/csp-lmc-common:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}cspmaster + depends_on: + - csp_dsconfig + - cbfmaster + - rsyslog-csplmc + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=30 --strict -- + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/sub_elt/master &&\ + retry --max=5 -- tango_admin --ping-device common/elt/search_beams_monitor &&\ + retry --max=5 -- tango_admin --ping-device common/elt/timing_beams_monitor &&\ + retry --max=5 -- tango_admin --ping-device common/elt/vlbi_beams_monitor &&\ + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/sub_elt/master &&\ + /venv/bin/python /app/csp-lmc-common/CspMaster.py commaster" + volumes_from: + - rsyslog-csplmc:rw + + searchbeam: + image: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}/csp-lmc-common:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}searchbeam + depends_on: + - csp_dsconfig + - rsyslog-csplmc + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=30 --strict -- + /venv/bin/python /app/csp-lmc-common/CspCapabilityMonitor.py searchbeams" + volumes_from: + - rsyslog-csplmc:rw + + timingbeam: + image: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}/csp-lmc-common:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}timingbeam + depends_on: + - csp_dsconfig + - rsyslog-csplmc + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=30 --strict -- + /venv/bin/python /app/csp-lmc-common/CspCapabilityMonitor.py timingbeams" + volumes_from: + - rsyslog-csplmc:rw + + vlbibeam: + image: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}/csp-lmc-common:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}vlbibeam + depends_on: + - csp_dsconfig + - rsyslog-csplmc + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=30 --strict -- + /venv/bin/python /app/csp-lmc-common/CspCapabilityMonitor.py vlbibeams" + volumes_from: + - rsyslog-csplmc:rw + + rsyslog-csplmc: + image: jumanjiman/rsyslog + container_name: ${CONTAINER_NAME_PREFIX}rsyslog-csplmc + network_mode: ${NETWORK_MODE} diff --git a/docker/csp-tangodb.yml b/docker/csp-tangodb.yml new file mode 100644 index 0000000..64a6922 --- /dev/null +++ b/docker/csp-tangodb.yml @@ -0,0 +1,50 @@ +# +# Docker compose file for TANGO database and database device server +# +# Defines: +# - tangodb: MariaDB database with TANGO schema +# - databaseds: TANGO database device server +# +# Requires: +# - None +# +version: '2.2' +volumes: + tangodb: {} + +services: + tangodb: + image: nexus.engageska-portugal.pt/ska-docker/tango-db:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}tangodb + environment: + - MYSQL_ROOT_PASSWORD=secret + - MYSQL_DATABASE=tango + - MYSQL_USER=tango + - MYSQL_PASSWORD=tango + volumes: + - tangodb:/var/lib/mysql + + databaseds: + image: nexus.engageska-portugal.pt/ska-docker/tango-cpp:latest + depends_on: + - tangodb + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}databaseds + environment: + - MYSQL_HOST=${MYSQL_HOST} + - MYSQL_DATABASE=tango + - MYSQL_USER=tango + - MYSQL_PASSWORD=tango + - TANGO_HOST=${TANGO_HOST} + entrypoint: + - /usr/local/bin/wait-for-it.sh + - ${MYSQL_HOST} + - --timeout=70 + - --strict + - -- + - /usr/local/bin/DataBaseds + - "2" + - -ORBendPoint + - giop:tcp::10000 + diff --git a/docker/mid-cbf-mcs.yml b/docker/mid-cbf-mcs.yml new file mode 100644 index 0000000..490c97e --- /dev/null +++ b/docker/mid-cbf-mcs.yml @@ -0,0 +1,288 @@ +# +# Docker compose file for TANGO database and database device server +# +# Defines: +# - tangodb: MariaDB database with TANGO schema +# - databaseds: TANGO database device server +# - rsyslog-cbf-csplmc: rsyslog-cbf service for logger +# - cspmaster: CspMaster device +# +# Requires: +# - None +# +version: '2.2' + +services: + cbf_dsconfig: + image: nexus.engageska-portugal.pt/ska-docker/tango-dsconfig:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}cbf_dsconfig + depends_on: + - databaseds + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=60 --strict -- + json2tango -w -a -u csplmc/config/midcbf_dsconfig.json && sleep infinity" + volumes: + - .:/csplmc + + cbfmaster: + image: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}/mid-cbf-mcs:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}cbfmaster + depends_on: + - databaseds + - cbf_dsconfig + - vcc001 + - vcc002 + - vcc003 + - vcc004 + - rsyslog-cbf + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=60 --strict -- + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/vcc/001 &&\ + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/vcc/002 &&\ + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/vcc/003 &&\ + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/vcc/004 &&\ + /venv/bin/python /app/tangods/CbfMaster/CbfMaster/CbfMaster.py master" + volumes_from: + - rsyslog-cbf:rw + + + cbfsubarray01: + image: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}/mid-cbf-mcs:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}cbfsubarray01 + depends_on: + - cbf_dsconfig + - vcc001 + - vcc002 + - vcc003 + - vcc004 + - cbfmaster + - rsyslog-cbf + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=60 --strict -- + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/sub_elt/master &&\ + /venv/bin/python /app/tangods/CbfSubarray/CbfSubarrayMulti/CbfSubarrayMulti.py cbfSubarray-01" + volumes_from: + - rsyslog-cbf:rw + + + cbfsubarray02: + image: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}/mid-cbf-mcs:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}cbfsubarray02 + depends_on: + - cbf_dsconfig + - vcc001 + - vcc002 + - vcc003 + - vcc004 + - cbfmaster + - rsyslog-cbf + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=60 --strict -- + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/sub_elt/master &&\ + /venv/bin/python /app/tangods/CbfSubarray/CbfSubarrayMulti/CbfSubarrayMulti.py cbfSubarray-02" + volumes_from: + - rsyslog-cbf:rw + + + vcc001: + image: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}/mid-cbf-mcs:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}vcc001 + depends_on: + - databaseds + - cbf_dsconfig + - rsyslog-cbf + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=60 --strict -- + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/fsp/01 &&\ + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/fsp/02 &&\ + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/fsp/03 &&\ + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/fsp/04 &&\ + /venv/bin/python /app/tangods/Vcc/VccMulti/VccMulti.py vcc-001" + volumes_from: + - rsyslog-cbf:rw + + + vcc002: + image: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}/mid-cbf-mcs:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}vcc002 + depends_on: + - databaseds + - cbf_dsconfig + - rsyslog-cbf + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=60 --strict -- + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/fsp/01 &&\ + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/fsp/02 &&\ + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/fsp/03 &&\ + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/fsp/04 &&\ + /venv/bin/python /app/tangods/Vcc/VccMulti/VccMulti.py vcc-002" + volumes_from: + - rsyslog-cbf:rw + + + vcc003: + image: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}/mid-cbf-mcs:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}vcc003 + depends_on: + - databaseds + - cbf_dsconfig + - rsyslog-cbf + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=60 --strict -- + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/fsp/01 &&\ + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/fsp/02 &&\ + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/fsp/03 &&\ + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/fsp/04 &&\ + /venv/bin/python /app/tangods/Vcc/VccMulti/VccMulti.py vcc-003" + volumes_from: + - rsyslog-cbf:rw + + + vcc004: + image: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}/mid-cbf-mcs:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}vcc004 + depends_on: + - databaseds + - cbf_dsconfig + - rsyslog-cbf + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=60 --strict -- + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/fsp/01 &&\ + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/fsp/02 &&\ + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/fsp/03 &&\ + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/fsp/04 &&\ + /venv/bin/python /app/tangods/Vcc/VccMulti/VccMulti.py vcc-004" + volumes_from: + - rsyslog-cbf:rw + + + fsp01: + image: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}/mid-cbf-mcs:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}fsp01 + depends_on: + - databaseds + - cbf_dsconfig + - rsyslog-cbf + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=60 --strict -- + /venv/bin/python /app/tangods/Fsp/FspMulti/FspMulti.py fsp-01" + volumes_from: + - rsyslog-cbf:rw + + fsp02: + image: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}/mid-cbf-mcs:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}fsp02 + depends_on: + - databaseds + - cbf_dsconfig + - rsyslog-cbf + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=60 --strict -- + /venv/bin/python /app/tangods/Fsp/FspMulti/FspMulti.py fsp-02" + volumes_from: + - rsyslog-cbf:rw + + fsp03: + image: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}/mid-cbf-mcs:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}fsp03 + depends_on: + - databaseds + - cbf_dsconfig + - rsyslog-cbf + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=60 --strict -- + /venv/bin/python /app/tangods/Fsp/FspMulti/FspMulti.py fsp-03" + volumes_from: + - rsyslog-cbf:rw + + + fsp04: + image: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}/mid-cbf-mcs:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}fsp04 + depends_on: + - databaseds + - cbf_dsconfig + - rsyslog-cbf + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=60 --strict -- + /venv/bin/python /app/tangods/Fsp/FspMulti/FspMulti.py fsp-04" + volumes_from: + - rsyslog-cbf:rw + + + tmcspsubarrayleafnodetest: + image: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}/mid-cbf-mcs:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}tmcspsubarrayleafnodetest + depends_on: + - cspmaster + - databaseds + - rsyslog-cbf + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=60 --strict -- + retry --max=10 -- tango_admin --ping-device mid_csp/elt/subarray_01 && + /venv/bin/python /app/tangods/TmCspSubarrayLeafNodeTest/TmCspSubarrayLeafNodeTest.py tm" + volumes_from: + - rsyslog-cbf:rw + + tmcspsubarrayleafnodetest2: + image: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}/mid-cbf-mcs:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}tmcspsubarrayleafnodetest2 + depends_on: + - cspmaster + - databaseds + - rsyslog-cbf + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=60 --strict -- + retry --max=10 -- tango_admin --ping-device mid_csp/elt/subarray_02 && + /venv/bin/python /app/tangods/TmCspSubarrayLeafNodeTest/TmCspSubarrayLeafNodeTest.py tm2" + volumes_from: + - rsyslog-cbf:rw + + rsyslog-cbf: + image: jumanjiman/rsyslog + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}rsyslog-cbf + + -- GitLab From 657a6c6b6b808ba805d346e3deda0c200c8a95d1 Mon Sep 17 00:00:00 2001 From: Elisabetta Giani Date: Mon, 13 Jan 2020 18:12:51 -0500 Subject: [PATCH 14/33] AT5-262: PSS Master simulator --- .../DeviceTestMaster/DeviceTestMaster.py | 136 +++++++- .../DeviceTestMaster/PssMasterSimulator.py | 109 +++++++ .../simulators/pogo/DeviceTestMaster.py | 292 +++++++++++++++++ .../simulators/pogo/DeviceTestMaster.xmi | 84 ++--- .../simulators/pogo/PssMasterSimulator.py | 132 ++++++++ .../simulators/pogo/PssMasterSimulator.xmi | 302 ++++++++++++++++++ docker/config/csplmc_dsconfig.json | 74 +++++ docker/csp-lmc.yml | 15 + 8 files changed, 1071 insertions(+), 73 deletions(-) create mode 100644 csp-lmc-common/simulators/DeviceTestMaster/PssMasterSimulator.py create mode 100644 csp-lmc-common/simulators/pogo/DeviceTestMaster.py create mode 100644 csp-lmc-common/simulators/pogo/PssMasterSimulator.py create mode 100644 csp-lmc-common/simulators/pogo/PssMasterSimulator.xmi diff --git a/csp-lmc-common/simulators/DeviceTestMaster/DeviceTestMaster.py b/csp-lmc-common/simulators/DeviceTestMaster/DeviceTestMaster.py index 7610a9c..327d491 100644 --- a/csp-lmc-common/simulators/DeviceTestMaster/DeviceTestMaster.py +++ b/csp-lmc-common/simulators/DeviceTestMaster/DeviceTestMaster.py @@ -14,6 +14,7 @@ It simulates the CbfMaster sub-element. """ from __future__ import absolute_import import sys +from collections import defaultdict import os import time @@ -36,7 +37,7 @@ from tango import AttrWriteType, PipeWriteType # PROTECTED REGION ID(DeviceTestMaster.additionnal_import) ENABLED START # from future.utils import with_metaclass import threading -from cspcommons import HealthState +from cspcommons import HealthState, AdminMode from skabase.SKAMaster.SKAMaster import SKAMaster # PROTECTED REGION END # // DeviceTestMaster.additionnal_import @@ -59,16 +60,46 @@ class DeviceTestMaster(with_metaclass(DeviceMeta,SKAMaster)): # ---------- onCommandProgress = attribute( - dtype='uint16', + dtype='DevUShort', label="Command progress percentage", + polling_period=1000, + abs_change=5, max_value=100, min_value=0, + doc="Percentage progress implemented for commands that result in state/mode transitions for a large \nnumber of components and/or are executed in stages (e.g power up, power down)", + ) + + offCommandProgress = attribute( + dtype='DevDouble', + polling_period=1000, + abs_change=5, + ) + + standbyCommandProgress = attribute( + dtype='DevDouble', polling_period=1000, abs_change=5, - rel_change=2, - doc="Percentage progress implemented for commands that result in state/mode transitions for a large \nnumber of components and/or are executed in stages (e.g power up, power down)", ) + onDurationExpected = attribute( + dtype='DevUShort', + access=AttrWriteType.READ_WRITE, + memorized=True, + ) + + offDurationExpected = attribute( + dtype='DevUShort', + access=AttrWriteType.READ_WRITE, + memorized=True, + ) + + standbyDurationExpected = attribute( + dtype='DevUShort', + access=AttrWriteType.READ_WRITE, + memorized=True, + ) + + # --------------- # General methods # --------------- @@ -77,6 +108,7 @@ class DeviceTestMaster(with_metaclass(DeviceMeta,SKAMaster)): """ Simulate the sub-element device initialization """ + time.sleep(3) self.set_state(tango.DevState.STANDBY) def on_subelement(self): @@ -84,9 +116,15 @@ class DeviceTestMaster(with_metaclass(DeviceMeta,SKAMaster)): Simulate the sub-element transition from STANDBY to ON """ print("Executing the On command...wait") - time.sleep(10) + while True: + elapsed_time = time.time() - self._start_time + if elapsed_time > self._duration_expected['on']: + break + if elapsed_time > 1: + self._cmd_progress['standby'] = elapsed_time* 100/self._duration_expected['on'] + self.push_change_event("onCmdProgress", self._cmd_progress['on']) self.set_state(tango.DevState.ON) - self._health_state = HealthState.DEGRADED.value + self._health_state = HealthState.DEGRADED print("End On command...wait") def standby_subelement(self): @@ -94,24 +132,49 @@ class DeviceTestMaster(with_metaclass(DeviceMeta,SKAMaster)): Simulate the sub-element transition from ON to STANDBY """ print("Executing the Standby command...wait") - time.sleep(10) + while True: + elapsed_time = time.time() - self._start_time + if elapsed_time > self._duration_expected['standby']: + break + if elapsed_time > 1: + self._cmd_progress['standby'] = elapsed_time* 100/self._duration_expected['standby'] + self.push_change_event("standbyCmdProgress", self._cmd_progress['standby']) self.set_state(tango.DevState.STANDBY) - self._health_state = HealthState.DEGRADED.value print("End Standby command...wait") def off_subelement(self): """ Simulate the sub-element transition from STANDBY to OFF """ + while True: + elapsed_time = time.time() - self._start_time + if elapsed_time > self._duration_expected['off']: + break + if elapsed_time > 1: + self._cmd_progress['off'] = elapsed_time* 100/self._duration_expected['ff'] + self.push_change_event("offCmdProgress", self._cmd_progress['off']) self.set_state(tango.DevState.OFF) - self._health_state = HealthState.UNKNOWN.value + self._health_state = HealthState.UNKNOWN def init_device(self): SKAMaster.init_device(self) # PROTECTED REGION ID(DeviceTestMaster.init_device) ENABLED START # self.set_state(tango.DevState.INIT) - self._health_state = HealthState.UNKNOWN.value + self._health_state = HealthState.UNKNOWN + self._admin_mode = AdminMode.NOTFITTED + self._duration_expected = defaultdict(lambda:10) + self._cmd_progress = defaultdict(lambda:0) + + csp_tango_db = tango.Database() + # read the CSP memorized attributes from the TANGO DB. + # Note: a memorized attribute has defined the attribute + # property '__value' + attribute_properties = csp_tango_db.get_device_attribute_property(self.get_name(), + {'adminMode': ['__value'], + 'on': ['__value'], + 'elementLoggingLevel': ['__value'], + 'centralLoggingLevel': ['__value'],}) # start a timer to simulate device intialization thread = threading.Timer(1, self.init_subelement) @@ -135,9 +198,58 @@ class DeviceTestMaster(with_metaclass(DeviceMeta,SKAMaster)): def read_onCommandProgress(self): # PROTECTED REGION ID(DeviceTestMaster.onCommandProgress_read) ENABLED START # - return 0 + """Return the onCommandProgress attribute.""" + return self._cmd_progress['on'] # PROTECTED REGION END # // DeviceTestMaster.onCommandProgress_read + def read_offCommandProgress(self): + # PROTECTED REGION ID(DeviceTestMaster.offCommandProgress_read) ENABLED START # + """Return the offCommandProgress attribute.""" + return self._cmd_progress['off'] + # PROTECTED REGION END # // DeviceTestMaster.offCommandProgress_read + + def read_standbyCommandProgress(self): + # PROTECTED REGION ID(DeviceTestMaster.standbyCommandProgress_read) ENABLED START # + """Return the standbyCommandProgress attribute.""" + return self._cmd_progress['standby'] + # PROTECTED REGION END # // DeviceTestMaster.standbyCommandProgress_read + + def read_onDurationExpected(self): + # PROTECTED REGION ID(DeviceTestMaster.onDurationExpected_read) ENABLED START # + """Return the onDurationExpected attribute.""" + return self._duration_expected['on'] + # PROTECTED REGION END # // DeviceTestMaster.onDurationExpected_read + + def write_onDurationExpected(self, value): + # PROTECTED REGION ID(DeviceTestMaster.onDurationExpected_write) ENABLED START # + """Set the onDurationExpected attribute.""" + self._duration_expected['on'] = value + # PROTECTED REGION END # // DeviceTestMaster.onDurationExpected_write + + def read_offDurationExpected(self): + # PROTECTED REGION ID(DeviceTestMaster.offDurationExpected_read) ENABLED START # + """Return the offDurationExpected attribute.""" + return self._duration_expected['off'] + # PROTECTED REGION END # // DeviceTestMaster.offDurationExpected_read + + def write_offDurationExpected(self, value): + # PROTECTED REGION ID(DeviceTestMaster.offDurationExpected_write) ENABLED START # + """Set the offDurationExpected attribute.""" + self._duration_expected['off'] = value + # PROTECTED REGION END # // DeviceTestMaster.offDurationExpected_write + + def read_standbyDurationExpected(self): + # PROTECTED REGION ID(DeviceTestMaster.standbyDurationExpected_read) ENABLED START # + """Return the standbyDurationExpected attribute.""" + return self._duration_expected['standby'] + # PROTECTED REGION END # // DeviceTestMaster.standbyDurationExpected_read + + def write_standbyDurationExpected(self, value): + # PROTECTED REGION ID(DeviceTestMaster.standbyDurationExpected_write) ENABLED START # + """Set the standbyDurationExpected attribute.""" + elf._duration_expected['standby'] = value + # PROTECTED REGION END # // DeviceTestMaster.standbyDurationExpected_write + # -------- # Commands # -------- @@ -148,7 +260,7 @@ class DeviceTestMaster(with_metaclass(DeviceMeta,SKAMaster)): @DebugIt() def On(self): # PROTECTED REGION ID(DeviceTestMaster.On) ENABLED START # - print("Sono qui") + print("Processing On command") thread = threading.Timer(2, self.on_subelement) thread.start() # PROTECTED REGION END # // DeviceTestMaster.On diff --git a/csp-lmc-common/simulators/DeviceTestMaster/PssMasterSimulator.py b/csp-lmc-common/simulators/DeviceTestMaster/PssMasterSimulator.py new file mode 100644 index 0000000..a69157e --- /dev/null +++ b/csp-lmc-common/simulators/DeviceTestMaster/PssMasterSimulator.py @@ -0,0 +1,109 @@ +# -*- coding: utf-8 -*- +# +# This file is part of the PssMasterSimulator project +# +# INAF-SKA Telescope +# +# Distributed under the terms of the GPL license. +# See LICENSE.txt for more info. + +""" PssMaster Class simulator + +""" + +from __future__ import absolute_import +import sys +from collections import defaultdict +import os +import time + +file_path = os.path.dirname(os.path.abspath(__file__)) +print(file_path) +module_path = os.path.abspath(os.path.join(file_path, os.pardir)) + "/../utils" +print(module_path) +sys.path.insert(0, module_path) + +# 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 +# Additional import +# PROTECTED REGION ID(DeviceTestMaster.additionnal_import) ENABLED START # +from future.utils import with_metaclass +import threading +from cspcommons import HealthState, AdminMode +from DeviceTestMaster import DeviceTestMaster +# PROTECTED REGION END # // DeviceTestMaster.additionnal_import + +__all__ = ["PssMasterSimulator", "main"] + + +class PssMasterSimulator(with_metaclass(DeviceMeta,DeviceTestMaster)): + """ + + **Properties:** + + - Device Property + """ + # PROTECTED REGION ID(PssMasterSimulator.class_variable) ENABLED START # + # PROTECTED REGION END # // PssMasterSimulator.class_variable + + # ----------------- + # Device Properties + # ----------------- + + # ---------- + # Attributes + # ---------- + + + # --------------- + # General methods + # --------------- + + def init_device(self): + """Initialises the attributes and properties of the PssMasterSimulator.""" + DeviceTestMaster.init_device(self) + # PROTECTED REGION ID(PssMasterSimulator.init_device) ENABLED START # + # PROTECTED REGION END # // PssMasterSimulator.init_device + + def always_executed_hook(self): + """Method always executed before any TANGO command is executed.""" + # PROTECTED REGION ID(PssMasterSimulator.always_executed_hook) ENABLED START # + # PROTECTED REGION END # // PssMasterSimulator.always_executed_hook + + def delete_device(self): + """Hook to delete resources allocated in init_device. + + This method allows for any memory or other resources allocated in the + init_device method to be released. This method is called by the device + destructor and by the device Init command. + """ + # PROTECTED REGION ID(PssMasterSimulator.delete_device) ENABLED START # + # PROTECTED REGION END # // PssMasterSimulator.delete_device + # ------------------ + # Attributes methods + # ------------------ + + + # -------- + # Commands + # -------- + +# ---------- +# Run server +# ---------- + + +def main(args=None, **kwargs): + # PROTECTED REGION ID(PssMasterSimulator.main) ENABLED START # + return run((PssMasterSimulator,), args=args, **kwargs) + # PROTECTED REGION END # // PssMasterSimulator.main + +if __name__ == '__main__': + main() diff --git a/csp-lmc-common/simulators/pogo/DeviceTestMaster.py b/csp-lmc-common/simulators/pogo/DeviceTestMaster.py new file mode 100644 index 0000000..83f0d74 --- /dev/null +++ b/csp-lmc-common/simulators/pogo/DeviceTestMaster.py @@ -0,0 +1,292 @@ +# -*- coding: utf-8 -*- +# +# This file is part of the DeviceTestMaster project +# +# +# +# Distributed under the terms of the GPL license. +# See LICENSE.txt for more info. + +""" CSP.LMC subelement Test Master Tango device prototype + +Test TANGO device class to test connection with the CSPMaster prototype. +It simulates the CbfMaster sub-element. +""" + +# PyTango 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 SKAMaster import SKAMaster +# Additional import +# PROTECTED REGION ID(DeviceTestMaster.additionnal_import) ENABLED START # +# PROTECTED REGION END # // DeviceTestMaster.additionnal_import + +__all__ = ["DeviceTestMaster", "main"] + + +class DeviceTestMaster(SKAMaster): + """ + Test TANGO device class to test connection with the CSPMaster prototype. + It simulates the CbfMaster sub-element. + + **Properties:** + + - Device Property + + + + + + + + + """ + __metaclass__ = DeviceMeta + # PROTECTED REGION ID(DeviceTestMaster.class_variable) ENABLED START # + # PROTECTED REGION END # // DeviceTestMaster.class_variable + + # ----------------- + # Device Properties + # ----------------- + + + + + + + + + + # ---------- + # Attributes + # ---------- + + + + + + + + + + + + + + + + onCommandProgress = attribute( + dtype='DevUShort', + label="Command progress percentage", + polling_period=1000, + abs_change=5, + max_value=100, + min_value=0, + doc="Percentage progress implemented for commands that result in state/mode transitions for a large \nnumber of components and/or are executed in stages (e.g power up, power down)", + ) + + offCommandProgress = attribute( + dtype='DevDouble', + polling_period=1000, + abs_change=5, + ) + + standbyCommandProgress = attribute( + dtype='DevDouble', + polling_period=1000, + abs_change=5, + ) + + onDurationExpected = attribute( + dtype='DevUShort', + access=AttrWriteType.READ_WRITE, + memorized=True, + ) + + offDurationExpected = attribute( + dtype='DevUShort', + access=AttrWriteType.READ_WRITE, + memorized=True, + ) + + standbyDurationExpected = attribute( + dtype='DevUShort', + access=AttrWriteType.READ_WRITE, + memorized=True, + ) + + + + # --------------- + # General methods + # --------------- + + def init_device(self): + """Initialises the attributes and properties of the DeviceTestMaster.""" + SKAMaster.init_device(self) + # PROTECTED REGION ID(DeviceTestMaster.init_device) ENABLED START # + # PROTECTED REGION END # // DeviceTestMaster.init_device + + def always_executed_hook(self): + """Method always executed before any TANGO command is executed.""" + # PROTECTED REGION ID(DeviceTestMaster.always_executed_hook) ENABLED START # + # PROTECTED REGION END # // DeviceTestMaster.always_executed_hook + + def delete_device(self): + """Hook to delete resources allocated in init_device. + + This method allows for any memory or other resources allocated in the + init_device method to be released. This method is called by the device + destructor and by the device Init command. + """ + # PROTECTED REGION ID(DeviceTestMaster.delete_device) ENABLED START # + # PROTECTED REGION END # // DeviceTestMaster.delete_device + # ------------------ + # Attributes methods + # ------------------ + + def read_onCommandProgress(self): + # PROTECTED REGION ID(DeviceTestMaster.onCommandProgress_read) ENABLED START # + """Return the onCommandProgress attribute.""" + return 0 + # PROTECTED REGION END # // DeviceTestMaster.onCommandProgress_read + + def read_offCommandProgress(self): + # PROTECTED REGION ID(DeviceTestMaster.offCommandProgress_read) ENABLED START # + """Return the offCommandProgress attribute.""" + return 0.0 + # PROTECTED REGION END # // DeviceTestMaster.offCommandProgress_read + + def read_standbyCommandProgress(self): + # PROTECTED REGION ID(DeviceTestMaster.standbyCommandProgress_read) ENABLED START # + """Return the standbyCommandProgress attribute.""" + return 0.0 + # PROTECTED REGION END # // DeviceTestMaster.standbyCommandProgress_read + + def read_onDurationExpected(self): + # PROTECTED REGION ID(DeviceTestMaster.onDurationExpected_read) ENABLED START # + """Return the onDurationExpected attribute.""" + return 0 + # PROTECTED REGION END # // DeviceTestMaster.onDurationExpected_read + + def write_onDurationExpected(self, value): + # PROTECTED REGION ID(DeviceTestMaster.onDurationExpected_write) ENABLED START # + """Set the onDurationExpected attribute.""" + pass + # PROTECTED REGION END # // DeviceTestMaster.onDurationExpected_write + + def read_offDurationExpected(self): + # PROTECTED REGION ID(DeviceTestMaster.offDurationExpected_read) ENABLED START # + """Return the offDurationExpected attribute.""" + return 0 + # PROTECTED REGION END # // DeviceTestMaster.offDurationExpected_read + + def write_offDurationExpected(self, value): + # PROTECTED REGION ID(DeviceTestMaster.offDurationExpected_write) ENABLED START # + """Set the offDurationExpected attribute.""" + pass + # PROTECTED REGION END # // DeviceTestMaster.offDurationExpected_write + + def read_standbyDurationExpected(self): + # PROTECTED REGION ID(DeviceTestMaster.standbyDurationExpected_read) ENABLED START # + """Return the standbyDurationExpected attribute.""" + return 0 + # PROTECTED REGION END # // DeviceTestMaster.standbyDurationExpected_read + + def write_standbyDurationExpected(self, value): + # PROTECTED REGION ID(DeviceTestMaster.standbyDurationExpected_write) ENABLED START # + """Set the standbyDurationExpected attribute.""" + pass + # PROTECTED REGION END # // DeviceTestMaster.standbyDurationExpected_write + + + # -------- + # Commands + # -------- + + @command( + dtype_in='DevVarStringArray', + doc_in="If the array length is0, the command apllies to the whole\nCSP Element.\nIf the array length is > 1, each array element specifies the FQDN of the\nCSP SubElement to switch ON.", + ) + @DebugIt() + def On(self, argin): + # PROTECTED REGION ID(DeviceTestMaster.On) ENABLED START # + """ + Transit CSP or one or more CSP SubElements fromSTANDBY to ON + + :param argin: 'DevVarStringArray' + + If the array length is0, the command apllies to the whole + CSP Element. + If the array length is > 1, each array element specifies the FQDN of the + CSP SubElement to switch ON. + + :return:None + """ + pass + # PROTECTED REGION END # // DeviceTestMaster.On + + @command( + dtype_in='DevVarStringArray', + doc_in="If the array length is0, the command apllies to the whole\nCSP Element.\nIf the array length is > 1, each array element specifies the FQDN of the\nCSP SubElement to switch OFF.", + ) + @DebugIt() + def Off(self, argin): + # PROTECTED REGION ID(DeviceTestMaster.Off) ENABLED START # + """ + Transit CSP or one or more CSP SubElements from ON to OFF. + + :param argin: 'DevVarStringArray' + + If the array length is0, the command apllies to the whole + CSP Element. + If the array length is > 1, each array element specifies the FQDN of the + CSP SubElement to switch OFF. + + :return:None + """ + pass + # PROTECTED REGION END # // DeviceTestMaster.Off + + @command( + dtype_in='DevVarStringArray', + doc_in="If the array length is0, the command apllies to the whole\nCSP Element.\nIf the array length is > 1, each array element specifies the FQDN of the\nCSP SubElement to put in STANDBY mode.", + ) + @DebugIt() + def Standby(self, argin): + # PROTECTED REGION ID(DeviceTestMaster.Standby) ENABLED START # + """ + + Transit CSP or one or more CSP SubElements from ON/OFF to + STANDBY. + + :param argin: 'DevVarStringArray' + + If the array length is0, the command apllies to the whole + CSP Element. + If the array length is > 1, each array element specifies the FQDN of the + CSP SubElement to put in STANDBY mode. + + :return:None + """ + pass + # PROTECTED REGION END # // DeviceTestMaster.Standby + +# ---------- +# Run server +# ---------- + + +def main(args=None, **kwargs): + # PROTECTED REGION ID(DeviceTestMaster.main) ENABLED START # + return run((DeviceTestMaster,), args=args, **kwargs) + # PROTECTED REGION END # // DeviceTestMaster.main + +if __name__ == '__main__': + main() diff --git a/csp-lmc-common/simulators/pogo/DeviceTestMaster.xmi b/csp-lmc-common/simulators/pogo/DeviceTestMaster.xmi index 09d22a0..a6e162a 100644 --- a/csp-lmc-common/simulators/pogo/DeviceTestMaster.xmi +++ b/csp-lmc-common/simulators/pogo/DeviceTestMaster.xmi @@ -1,10 +1,10 @@ - - + + - - + + @@ -212,54 +212,34 @@ - + - + - - - - - - - - - - - - - - - - + + + - - + + + - - - - - - - - - + @@ -267,7 +247,7 @@ - + @@ -275,15 +255,7 @@ - - - - - - - - - + @@ -291,29 +263,19 @@ - - - - - - - - - - + + - - - + + - - + + - - - + + diff --git a/csp-lmc-common/simulators/pogo/PssMasterSimulator.py b/csp-lmc-common/simulators/pogo/PssMasterSimulator.py new file mode 100644 index 0000000..a915d1f --- /dev/null +++ b/csp-lmc-common/simulators/pogo/PssMasterSimulator.py @@ -0,0 +1,132 @@ +# -*- coding: utf-8 -*- +# +# This file is part of the PssMasterSimulator project +# +# INAF-SKA Telescope +# +# Distributed under the terms of the GPL license. +# See LICENSE.txt for more info. + +""" PssMaster Class simulator + +""" + +# PyTango 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 DeviceTestMaster import DeviceTestMaster +# Additional import +# PROTECTED REGION ID(PssMasterSimulator.additionnal_import) ENABLED START # +# PROTECTED REGION END # // PssMasterSimulator.additionnal_import + +__all__ = ["PssMasterSimulator", "main"] + + +class PssMasterSimulator(DeviceTestMaster): + """ + + **Properties:** + + - Device Property + + + + + + + + + """ + __metaclass__ = DeviceMeta + # PROTECTED REGION ID(PssMasterSimulator.class_variable) ENABLED START # + # PROTECTED REGION END # // PssMasterSimulator.class_variable + + # ----------------- + # Device Properties + # ----------------- + + + + + + + + + + # ---------- + # Attributes + # ---------- + + + + + + + + + + + + + + + + + + + + + + + + # --------------- + # General methods + # --------------- + + def init_device(self): + """Initialises the attributes and properties of the PssMasterSimulator.""" + DeviceTestMaster.init_device(self) + # PROTECTED REGION ID(PssMasterSimulator.init_device) ENABLED START # + # PROTECTED REGION END # // PssMasterSimulator.init_device + + def always_executed_hook(self): + """Method always executed before any TANGO command is executed.""" + # PROTECTED REGION ID(PssMasterSimulator.always_executed_hook) ENABLED START # + # PROTECTED REGION END # // PssMasterSimulator.always_executed_hook + + def delete_device(self): + """Hook to delete resources allocated in init_device. + + This method allows for any memory or other resources allocated in the + init_device method to be released. This method is called by the device + destructor and by the device Init command. + """ + # PROTECTED REGION ID(PssMasterSimulator.delete_device) ENABLED START # + # PROTECTED REGION END # // PssMasterSimulator.delete_device + # ------------------ + # Attributes methods + # ------------------ + + + # -------- + # Commands + # -------- + +# ---------- +# Run server +# ---------- + + +def main(args=None, **kwargs): + # PROTECTED REGION ID(PssMasterSimulator.main) ENABLED START # + return run((PssMasterSimulator,), args=args, **kwargs) + # PROTECTED REGION END # // PssMasterSimulator.main + +if __name__ == '__main__': + main() diff --git a/csp-lmc-common/simulators/pogo/PssMasterSimulator.xmi b/csp-lmc-common/simulators/pogo/PssMasterSimulator.xmi new file mode 100644 index 0000000..a4a3f08 --- /dev/null +++ b/csp-lmc-common/simulators/pogo/PssMasterSimulator.xmi @@ -0,0 +1,302 @@ + + + + + + + + + + + + + + 4 + + + + + + + + + + + + + localhost + + + + + + + + + 16 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docker/config/csplmc_dsconfig.json b/docker/config/csplmc_dsconfig.json index feb0ce6..d448c1b 100644 --- a/docker/config/csplmc_dsconfig.json +++ b/docker/config/csplmc_dsconfig.json @@ -217,6 +217,80 @@ } } }, + "PssMasterSimulator": { + "pss": { + "PssMasterSimulator": { + "mid_csp_pss/sub_elt/master": { + "attribute_properties": { + "adminMode": { + "abs_change": [ + "-1", + "1" + ], + "__value": [ + "0" + ] + }, + "healthState": { + "abs_change": [ + "-1", + "1" + ] + }, + "onDurationExpected": { + "__value": [ + "10" + ] + }, + "offDurationExpected": { + "__value": [ + "12" + ] + }, + "standbyDurationExpected": { + "__value": [ + "5" + ] + }, + "onCommandProgress": { + "abs_change": [ + "-5", + "5" + ] + }, + "offCommandProgress": { + "abs_change": [ + "-5", + "5" + ] + }, + "standbyCommandProgress": { + "abs_change": [ + "-5", + "5" + ] + } + }, + "properties": { + "polled_attr": [ + "state", + "1000", + "healthstate", + "1000", + "adminmode", + "1000", + "onCommandProgress", + "1000", + "offCommandProgress", + "1000", + "standbyCommandProgress", + "1000" + ] + } + } + } + } + }, "CspCapabilityMonitor": { "searchbeams": { "CspCapabilityMonitor": { diff --git a/docker/csp-lmc.yml b/docker/csp-lmc.yml index f8e9a29..f7577a0 100644 --- a/docker/csp-lmc.yml +++ b/docker/csp-lmc.yml @@ -126,6 +126,21 @@ services: /venv/bin/python /app/csp-lmc-common/CspCapabilityMonitor.py vlbibeams" volumes_from: - rsyslog-csplmc:rw + + pssmaster: + image: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}/csp-lmc-common:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}pssmaster + depends_on: + - csp_dsconfig + - rsyslog-csplmc + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=30 --strict -- + /venv/bin/python /app/simulators/DeviceTestMaster/PssMasterSimulator.py pss" + volumes_from: + - rsyslog-csplmc:rw rsyslog-csplmc: image: jumanjiman/rsyslog -- GitLab From 3ca96847aef694a981761994e46ac69aaa83eb6d Mon Sep 17 00:00:00 2001 From: Elisabetta Giani Date: Mon, 13 Jan 2020 18:13:47 -0500 Subject: [PATCH 15/33] AT5-262: change to the healthState update logic. Note: forwarded attributes reporting the CSP Capability addresses don't work: fault when forwarded attributes are array of strings --- csp-lmc-common/csp-lmc-common/CspMaster.py | 48 ++++++++++++---------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/csp-lmc-common/csp-lmc-common/CspMaster.py b/csp-lmc-common/csp-lmc-common/CspMaster.py index 7004a11..c4352fc 100644 --- a/csp-lmc-common/csp-lmc-common/CspMaster.py +++ b/csp-lmc-common/csp-lmc-common/CspMaster.py @@ -137,6 +137,9 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): self._se_state[dev_name] = evt.attr_value.value elif evt.attr_value.name.lower() == "healthstate": + print("{}: received event on {} value {}".format(dev_name, + evt.attr_value.name, + evt.attr_value.value)) self._se_health_state[dev_name] = evt.attr_value.value elif evt.attr_value.name.lower() == "adminmode": print("device: {} adminMode value {}".format(dev_name,evt.attr_value.value )) @@ -155,7 +158,9 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): 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"]: + # healthState and State are updated accordingly to the updated values of + # sub-elements healthState, State and adminMode + if evt.attr_value.name.lower() in ["state", "healthstate", "adminmode"]: self._update_csp_state() except tango.DevFailed as df: self.dev_logging(str(df.args[0].desc), tango.LogLevel.LOG_ERROR) @@ -315,27 +320,29 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # The whole CSP HealthState is OK only if: # - all sub-elements with adminMode ON-LINE or MAINTENACE are ON AND # - each sub-element HealthState is OK - admin_fqdn = [fqdn for fqdn, admin_value in self._se_admin_mode.items() - if admin_value in [AdminMode.ONLINE, AdminMode.MAINTENANCE]] - health_fqdn = [fqdn for fqdn, health_value in self._se_health_state.items() - if health_value == HealthState.OK] - state_fqdn = [fqdn for fqdn, state_value in self._se_state.items() - if state_value in [tango.DevState.ON, tango.DevState.STANDBY]] - admin_fqdn.sort() - health_fqdn.sort() - state_fqdn.sort() + # default value to DEGRADED self._health_state = HealthState.DEGRADED - if self.CspCbf not in admin_fqdn: - if admin_fqdn == health_fqdn == state_fqdn: - self._healthstate = HealthState.OK + # build the list of all the sub-elements ONLINE/MAINTENANCE + admin_fqdn = [fqdn for fqdn, admin_value in self._se_admin_mode.items() + if admin_value in [AdminMode.ONLINE, AdminMode.MAINTENANCE]] + # build the list of sub-elements with State ON/STANDBY and ONLINE/MAINTENANCE + state_fqdn = [fqdn for fqdn in admin_fqdn if self._se_state[fqdn] in [tango.DevState.ON, + tango.DevState.STANDBY]] + # build the list with the healthState of the devices contributing to + # determine the CSP healthState + health_list = [self._se_health_state[fqdn] for fqdn in state_fqdn] + + if self.CspCbf in admin_fqdn: + if all(value == HealthState.OK for value in health_list): + self._health_state = HealthState.OK elif self._se_health_state[self.CspCbf] in [HealthState.FAILED, HealthState.UNKNOWN, HealthState.DEGRADED]: - self._healthstate = self._se_health_state[self.CbfSubarray] + self._health_state = self._se_health_state[self.CspCbf] else: # if CBF is not ONLINE/MAINTENANCE .... - self._healthstate = self._se_health_state[self.CspCbf] + self._health_state = self._se_health_state[self.CspCbf] return def _connect_to_subelements (self): @@ -365,9 +372,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # Note: subscription is performed also for devices not ONLINE/MAINTENANCE. # In this way the CspMaster is able to detect a change in the admin value. - # if the sub-element device is running,the adminMode is updated here - # adminMode has to be subscribed first so that healthState and State are - # updated accordingly with its value! + ev_id = device_proxy.subscribe_event("adminMode", EventType.CHANGE_EVENT, self._se_scm_change_event_cb, @@ -1667,7 +1672,6 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): def read_numOfDevCompletedTask(self): # PROTECTED REGION ID(CspMaster.numOfDevCompletedTask_read) ENABLED START # """Return the numOfDevCompletedTask attribute.""" - print(self._num_dev_completed_task) if not self._last_executed_command: return 0 return self._num_dev_completed_task[self._last_executed_command] @@ -1676,7 +1680,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): def read_onCmdAlarm(self): # PROTECTED REGION ID(CspMaster.onCmdAlarm_read) ENABLED START # """Return the onCmdAlarm attribute.""" - return self._alarm_expired['on'] + return self._alarm_raised['on'] # PROTECTED REGION END # // CspMaster.onCmdAlarm_read def read_onAlarmMessage(self): @@ -1688,7 +1692,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): def read_offCmdAlarm(self): # PROTECTED REGION ID(CspMaster.offCmdAlarm_read) ENABLED START # """Return the offCmdAlarm attribute.""" - return self._alarm_expired['off'] + return self._alarm_raised['off'] # PROTECTED REGION END # // CspMaster.offCmdAlarm_read def read_offAlarmMessage(self): @@ -1700,7 +1704,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): def read_standbyCmdAlarm(self): # PROTECTED REGION ID(CspMaster.standbyCmdAlarm_read) ENABLED START # """Return the standbyCmdAlarm attribute.""" - return self._alarm_expired['standby'] + return self._alarm_raised['standby'] # PROTECTED REGION END # // CspMaster.standbyCmdAlarm_read def read_standbyAlarmMessage(self): -- GitLab From 16e72889d920bffcbf549b5fb247b19cb0c7d967 Mon Sep 17 00:00:00 2001 From: softir Date: Tue, 14 Jan 2020 12:59:32 +0100 Subject: [PATCH 16/33] AT5-262: Change to command progress counter: now it work properly. Removed availableCapabilities from CSP.LMC Common interface (to overload in MID/LOW instances). --- csp-lmc-common/csp-lmc-common/CspMaster.py | 68 ++++++++-------------- csp-lmc-common/pogo/CspMaster.xmi | 5 +- 2 files changed, 28 insertions(+), 45 deletions(-) diff --git a/csp-lmc-common/csp-lmc-common/CspMaster.py b/csp-lmc-common/csp-lmc-common/CspMaster.py index c4352fc..69c9f9f 100644 --- a/csp-lmc-common/csp-lmc-common/CspMaster.py +++ b/csp-lmc-common/csp-lmc-common/CspMaster.py @@ -405,9 +405,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): Check if a TANGO device is exported in the TANGO DB (i.e its TANGO device server is running). - If the device is not in the list of proxies a DeviceProxy on - the device is performed. - + :param: device_fqdn : the FQDN of the sub-element :type: `DevString` :param proxy_list: the list with the proxies for the device @@ -415,26 +413,22 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): :return: True if the connection with the subarray is established, False otherwise """ - print(proxy_list) - if proxy_list: - try: - proxy = proxy_list[device_fqdn] - # execute a ping to detect if the device is actually running - proxy.ping() - return True - 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) - try: - proxy = tango.DeviceProxy(device_fqdn) + if not proxy_list: + return False + try: + proxy = proxy_list[device_fqdn] + # ping the device to control if is alive proxy.ping() - proxy_list[device_fqdn] = proxy - except tango.DevFailed: + 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) + return False + except tango.DevFailed: return False - return True + return True def _issue_power_command(self, device_list, **args_dict): """ @@ -487,6 +481,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # set the sub-element command execution flag self._se_cmd_execution_state[device][cmd_name] = CmdExecState.RUNNING se_cmd_duration_measured[device][cmd_name] = 0 + self._se_cmd_progress[device][cmd_name] = 0 self._alarm_message[cmd_name] = '' try: device_proxy = self._se_proxies[device] @@ -507,6 +502,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): self._se_cmd_starting_time[device] = time.time() # loop on the device until the State changes to ON or a timeout occurred print("Device {} State {} expected value {}".format(device, self._se_state[device], dev_successful_state)) + command_progress = self._cmd_progress[cmd_name] while True: if self._se_state[device] == dev_successful_state: print("Command {} ended with success on device {}.".format(cmd_name, @@ -587,10 +583,10 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): time.sleep(1) # update the progress counter inside the loop taking into account the number of devices # executing the command - self._cmd_progress[cmd_name] += self._se_cmd_progress[device][cmd_name]/len(device_list) + self._cmd_progress[cmd_name] = command_progress + self._se_cmd_progress[device][cmd_name]/len(device_list) # end of the while loop # update the progress counter at the end of the loop - self._cmd_progress[cmd_name] += self._se_cmd_progress[device][cmd_name]/len(device_list) + self._cmd_progress[cmd_name] = command_progress + (self._se_cmd_progress[device][cmd_name]/len(device_list)) if len(device_list) == self._num_dev_completed_task[cmd_name] + num_of_failed_device: print("All devices have been handled!") # end of the command: the command has been issued on all the sub-element devices @@ -655,10 +651,10 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): """ """ # build the list with the Capability monitor devices - capability_monitor_device = [self.SearchBeamsMonitor, - self.TimingBeamsMonitor, - self.VlbiBeamsMonitor] - for fqdn in capability_monitor_device: + self._capability_fqdn = [self.SearchBeamsMonitor, + self.TimingBeamsMonitor, + self.VlbiBeamsMonitor] + for fqdn in self._capability_fqdn: self._capability_proxy[fqdn] = tango.DeviceProxy(fqdn) # PROTECTED REGION END # // CspMaster.class_protected_methods @@ -992,13 +988,6 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): doc="Alarm message when the Standby command fails with error(s).", ) - availableCapabilities = attribute( - dtype=('DevString',), - max_dim_x=20, - doc=("A list of available number of instances of each capability type, e.g." - " `CORRELATOR:512`, `PSS-BEAMS:4`."), - ) - reportSearchBeamState = attribute( dtype=('DevState',), max_dim_x=1500, @@ -1430,9 +1419,6 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # clear any list and dict self._se_fqdn.clear() self._se_proxies.clear() - self._search_beam_membership.clear() - self._timing_beam_membership.clear() - self._vlbi_beam_membership.clear() # PROTECTED REGION END # // CspMaster.delete_device # ------------------ # Attributes methods @@ -1713,12 +1699,6 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): return self._alarm_message['standby'] # PROTECTED REGION END # // CspMaster.standbyAlarmMessage_read - def read_availableCapabilities(self): - # PROTECTED REGION ID(CspMaster.availableCapabilities_read) ENABLED START # - """Return the availableCapabilities attribute.""" - return ('',) - # PROTECTED REGION END # // CspMaster.availableCapabilities_read - def read_cspSubarrayAddresses(self): # PROTECTED REGION ID(CspMaster.cspSubarrayAddresses_read) ENABLED START # """Return the cspSubarrayAddresses attribute.""" @@ -1801,6 +1781,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # are passed as arguments of the function # args: the list of sub-element FQDNS # args_dict: dictionary with the specific command information + print("Send command to device", device_list) args_dict = {'cmd_name':'On', 'attr_name': 'onCommandProgress', 'dev_state': tango.DevState.ON} self._command_thread['on'] = threading.Thread(target=self._issue_power_command, name="Thread-On", args=(device_list,), @@ -1808,6 +1789,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # set the CSP command execution running flag self._cmd_execution_state['on'] = CmdExecState.RUNNING # start the thread + print("starting thread") self._command_thread['on'].start() # sleep for a while to let the thread start time.sleep(0.2) diff --git a/csp-lmc-common/pogo/CspMaster.xmi b/csp-lmc-common/pogo/CspMaster.xmi index 106650b..d428255 100644 --- a/csp-lmc-common/pogo/CspMaster.xmi +++ b/csp-lmc-common/pogo/CspMaster.xmi @@ -544,11 +544,12 @@ - + - + + -- GitLab From c61b7fe600d5217fb5e0c5e31ea156f1e18a8630 Mon Sep 17 00:00:00 2001 From: softir Date: Tue, 14 Jan 2020 13:02:53 +0100 Subject: [PATCH 17/33] AT5-262: implementation of the MidCspMasterBase class. Still in progress:work has to be done to generate and add the CSP.LMC Common package import. --- csp-lmc-mid/csp-lmc-mid/MidCspMasterBase.py | 519 +++++++++++++++ csp-lmc-mid/pogo/MidCspMasterBase.py | 349 ++++++++++ csp-lmc-mid/pogo/MidCspMasterBase.xmi | 667 ++++++++++++++++++++ 3 files changed, 1535 insertions(+) create mode 100644 csp-lmc-mid/csp-lmc-mid/MidCspMasterBase.py create mode 100644 csp-lmc-mid/pogo/MidCspMasterBase.py create mode 100644 csp-lmc-mid/pogo/MidCspMasterBase.xmi diff --git a/csp-lmc-mid/csp-lmc-mid/MidCspMasterBase.py b/csp-lmc-mid/csp-lmc-mid/MidCspMasterBase.py new file mode 100644 index 0000000..5c95e8d --- /dev/null +++ b/csp-lmc-mid/csp-lmc-mid/MidCspMasterBase.py @@ -0,0 +1,519 @@ +# -*- coding: utf-8 -*- +# +# This file is part of the MidCspMasterBase project +# +# INAF-SKA Telescope +# +# Distributed under the terms of the GPL license. +# See LICENSE.txt for more info. + +""" MidCspMasterBase class + +The base class for MID CspMAster. +Fuctionality to monitor CSP.LMC Capabilities are +implemented in separate TANGO Devices. +""" + +# 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 +# PROTECTED REGION END# //CspMaster.standardlibray_import + +# tango imports# PROTECTED REGION ID (CspMaster.add_path) ENABLED START # + +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, 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)) + "/../csp-lmc-common/utils" +# TO REMOVE once csp-lmc-common python package has been released! +package_path = os.path.abspath(os.path.join(file_path, os.pardir)) + "/../csp-lmc-common/csp-lmc-common" +print("utilpath:", utils_path) +print("package_path:", package_path) +sys.path.insert(0, utils_path) +sys.path.insert(0, package_path) +import cspcommons +from cspcommons import HealthState, AdminMode +from CspMaster import CspMaster +# PROTECTED REGION END# //CspMaster.add_path +__all__ = ["MidCspMasterBase", "main"] + + +class MidCspMasterBase(with_metaclass(DeviceMeta, CspMaster)): + """ + The base class for MID CspMAster. + Fuctionality to monitor CSP.LMC Capabilities are + implemented in separate TANGO Devices. + + **Properties:** + + - Device Property + + """ + __metaclass__ = DeviceMeta + # PROTECTED REGION ID(MidCspMasterBase.class_variable) ENABLED START # + # PROTECTED REGION END # // MidCspMasterBase.class_variable + + # ----------------- + # Device Properties + # ----------------- + + # ---------- + # Attributes + # ---------- + + availableCapabilities = attribute( + dtype=('DevString',), + max_dim_x=20, + doc="A list of available number of instances of each capability type, e.g. `CORRELATOR:512`, `PSS-BEAMS:4`.", + ) + """ + *Class attribute* + + The list of available instances of each capability type. + + Note: + This attribute is defined in SKAMaster Class from which CspMaster class inherits.\ + To override the attribute *read* method, the *availableCapabilities* attribute \ + is added again\ + ("overload" button enabled in POGO). + """ + + receptorMembership = attribute( + dtype=('DevUShort',), + max_dim_x=197, + label="Receptor Memebership", + doc="The receptors affiliation to MID CSP sub-arrays.", + ) + + unassignedReceptorIDs = attribute( + dtype=('DevUShort',), + max_dim_x=197, + label="Unassigned receptors IDs", + doc="The list of available receptors IDs.", + ) + reportSearchBeamState = attribute(name="reportSearchBeamState", + label="SearchBeam Capabilities State", + forwarded=True + ) + reportSearchBeamHealthState = attribute(name="reportSearchBeamHealthState", + label="SearchBeam Capabilities healthState", + forwarded=True + ) + reportSearchBeamObsState = attribute(name="reportSearchBeamObsState", + label="SearchBeam Capabilities obsState", + forwarded=True + ) + reportSearchBeamAdminMode = attribute(name="reportSearchBeamAdminMode", + label="SearchBeam Capabilities adminMode", + forwarded=True + ) + reportTimingBeamState = attribute(name="reportTimingBeamState", + label="TimingBeam Capabilities State", + forwarded=True + ) + reportTimingBeamHealthState = attribute(name="reportTimingBeamHealthState", + label="TimingBeam Capabilities healthState", + forwarded=True + ) + reportTimingBeamObsState = attribute(name="reportTimingBeamObsState", + label="TimingBeam Capabilities obsState", + forwarded=True + ) + reportTimingBeamAdminMode = attribute(name="reportTimingBeamAdminMode", + label="TimingBeam Capabilities adminMode", + forwarded=True + ) + reportVlbiBeamState = attribute(name="reportVlbiBeamState", + label="VlbiBeam Capabilities State", + forwarded=True + ) + reportVlbiBeamHealthState = attribute(name="reportVlbiBeamHealthState", + label="VlbiBeam Capabilities healthState", + forwarded=True + ) + reportVlbiBeamObsState = attribute(name="reportVlbiBeamObsState", + label="VlbiBeam Capabilities obsState", + forwarded=True + ) + reportVlbiBeamAdminMode = attribute(name="reportVlbiBeamAdminMode", + label="VlbiBeam Capabilities adminMode", + forwarded=True + ) + searchBeamAddresses = attribute(name="searchBeamAddresses", + label="SearchBeams Capability devices addresses", + forwarded=True + ) + timingBeamAddresses = attribute(name="timingBeamAddresses", + label="TimingBeams Capability devices addresses", + forwarded=True + ) + vlbiBeamAddresses = attribute(name="vlbiBeamAddresses", + label="VlbiBeams Capability devices addresses", + forwarded=True + ) + reservedSearchBeamIDs = attribute(name="reservedSearchBeamIDs", + label="IDs of reserved SeachBeam Capabilities", + forwarded=True + ) + unassignedVlbiBeamIDs = attribute(name="unassignedVlbiBeamIDs", + label="Unassigned VlbiBeam Capabilities IDs", + forwarded=True + ) + unassignedTimingBeamIDs = attribute(name="unassignedTimingBeamIDs", + label="Unassigned TimingBeam Capabilities IDs", + forwarded=True + ) + unassignedSearchBeamIDs = attribute(name="unassignedSearchBeamIDs", + label="Unassigned SeachBeam Capabilities IDs", + forwarded=True + ) + reservedSearchBeamNum = attribute(name="reservedSearchBeamNum", + label="Number of reserved SeachBeam Capabilities", + forwarded=True + ) + searchBeamMembership = attribute(name="searchBeamMembership", + label="SearchBeam Membership", + forwarded=True + ) + timingBeamMembership = attribute(name="timingBeamMembership", + label="TimingBeam Membership", + forwarded=True + ) + vlbiBeamMembership = attribute(name="vlbiBeamMembership", + label="VlbiBeam Membership", + forwarded=True + ) + vccCapabilityAddress = attribute(name="vccCapabilityAddress", + label="vccCapabilityAddress", + forwarded=True + ) + fspCapabilityAddress = attribute(name="fspCapabilityAddress", + label="fspCapabilityAddress", + forwarded=True + ) + reportVCCState = attribute(name="reportVCCState", + label="reportVCCState", + forwarded=True + ) + reportVCCHealthState = attribute(name="reportVCCHealthState", + label="reportVCCHealthState", + forwarded=True + ) + reportVCCAdminMode = attribute(name="reportVCCAdminMode", + label="reportVCCAdminMode", + forwarded=True + ) + reportFSPState = attribute(name="reportFSPState", + label="reportFSPState", + forwarded=True + ) + reportFSPHealthState = attribute(name="reportFSPHealthState", + label="reportFSPHealthState", + forwarded=True + ) + reportFSPAdminMode = attribute(name="reportFSPAdminMode", + label="reportFSPAdminMode", + forwarded=True + ) + fspMembership = attribute(name="fspMembership", + label="fspMembership", + forwarded=True + ) + vccMembership = attribute(name="vccMembership", + label="vccMembership", + forwarded=True + ) + # + # Class private methods + # + def __get_maxnum_of_receptors(self): + """ + Get the maximum number of receptors that can be used for observations. + This number can be less than 197. + """ + + capability_dict = {} + if self._is_device_running(self.CspCbf, self._se_proxies): + try: + proxy = self._se_proxies[self.CspCbf] + vcc_to_receptor = proxy.vccToReceptor + self._vcc_to_receptor_map = dict([int(ID) for ID in pair.split(":")] + for pair in vcc_to_receptor) + # get the number of each Capability type allocated by CBF + cbf_max_capabilities = proxy.maxCapabilities + for capability in cbf_max_capabilities: + cap_type, cap_num = capability.split(':') + capability_dict[cap_type] = int(cap_num) + self._receptors_maxnum = capability_dict["VCC"] + self._receptorsMembership = [0]* self._receptors_maxnum + except KeyError as key_err: + log_msg = "Error: no key found for {}".format(str(key_err)) + self.dev_logging(log_msg, int(tango.LogLevel.LOG_ERROR)) + + except AttributeError as attr_err: + log_msg = "Error reading{}: {}".format(str(attr_err.args[0]), + attr_err.__doc__) + self.dev_logging(log_msg, int(tango.LogLevel.LOG_ERROR)) + except tango.DevFailed as df: + log_msg = "Error: " + str(df.args[0].reason) + self.dev_logging(log_msg, int(tango.LogLevel.LOG_ERROR)) + + # --------------- + # General methods + # --------------- + + def init_device(self): + """Initialises the attributes and properties of the MidCspMasterBase.""" + CspMaster.init_device(self) + print("Receptors:", self._max_capabilities["Receptors"]) + + # PROTECTED REGION ID(MidCspMasterBase.init_device) ENABLED START # + # NOTE: VCC (Receptors) and FSP capabilities are implemented at + # CBF sub-element level. Need to evaluate if these capabilities + # have to be implemented also at CSP level. + # To retieve the information on the number of instances provided + # by CBF the CSP master has to connect to the Cbf Master. For this + # reason the __get_maxnum_of_receptors() method gas to be called + # after connection. + self._vcc_to_receptor_map = {} + self._receptorsMembership = [0] * self._max_capabilities["Receptors"] + self._available_capabilities = defaultdict(lambda:0) + self._unassigned_receptor_id = [] + + # read the mapping between VCC IDs and receptor IDs + if self._is_device_running(self.CspCbf, self._se_proxies): + proxy = self._se_proxies[self.CspCbf] + vcc_to_receptor = proxy.vccToReceptor + self._vcc_to_receptor_map = dict([int(ID) for ID in pair.split(":")] + for pair in vcc_to_receptor) + + # PROTECTED REGION END # // MidCspMasterBase.init_device + + def always_executed_hook(self): + """Method always executed before any TANGO command is executed.""" + # PROTECTED REGION ID(MidCspMasterBase.always_executed_hook) ENABLED START # + # PROTECTED REGION END # // MidCspMasterBase.always_executed_hook + + def delete_device(self): + """Hook to delete resources allocated in init_device. + + This method allows for any memory or other resources allocated in the + init_device method to be released. This method is called by the device + destructor and by the device Init command. + """ + # PROTECTED REGION ID(MidCspMasterBase.delete_device) ENABLED START # + CspMaster.delete_device(self) + self._vcc_to_receptor_map.clear() + self._receptorsMembership.clear() + # PROTECTED REGION END # // MidCspMasterBase.delete_device + # ------------------ + # Attributes methods + # ------------------ + + def read_availableCapabilities(self): + # PROTECTED REGION ID(MidCspMasterBase.availableCapabilities_read) ENABLED START # + """ + Override read attribute method. + + :return: + A list of strings with the number of available resources for each + capability/resource type. + Example: + ["Receptors:95", "SearchBeam:1000", "TimingBeam:16", "VlbiBeam:20"] + :raises: + tango.DevFailed + """ + # PROTECTED REGION ID(CspMaster.availableCapabilities_read) ENABLED START # + try: + #proxy = tango.DeviceProxy(self.get_name()) + unassigned_receptors = self.read_unassignedReceptorIDs() + print("unassigned_receptors:", unassigned_receptors) + # NOTE: if there is no available receptor, this call returns an array + # [0] whose length is 1 (not 0) + + cap_type = [keys for keys,values in self._max_capabilities.items()] + print("cap_type:", cap_type) + if not unassigned_receptors: + self._available_capabilities["Receptors"] = 0 + else: + self._available_capabilities["Receptors"] = len(unassigned_receptors) + index = 1 + for fqdn in self._capability_fqdn: + print("fqdn:", fqdn) + if self._is_device_running(fqdn, self._capability_proxy): + self._available_capabilities[cap_type[index]] = self._capability_proxy[fqdn].numOfUnassignedIDs + index += 1 + except tango.DevFailed as df: + msg = "Attribute reading failure: {}".format(df.args[0].desc) + self.dev_logging(msg, tango.LogLevel.LOG_ERROR) + tango.Except.throw_exception("Attribute reading failure", + df.args[0].desc, + "read_availableCapabilities", + tango.ErrSeverity.ERR) + except AttributeError as attr_err: + msg = "Error in reading {}: {} ".format(str(attr_err.args[0]), + attr_err.__doc__) + tango.Except.throw_exception("Attribute reading failure", + msg, + "read_availableCapabilities", + tango.ErrSeverity.ERR) + return utils.convert_dict_to_list(self._available_capabilities) + # PROTECTED REGION END # // MidCspMasterBase.availableCapabilities_read + + def read_receptorMembership(self): + # PROTECTED REGION ID(MidCspMasterBase.receptorMembership_read) ENABLED START # + """ + Class attribute method. + + :return: + The subarray affiliation of the receptors. + """ + # PROTECTED REGION ID(CspMaster.receptorMembership_read) ENABLED START # + if self._is_device_running(self.CspCbf, self._se_proxies): + try: + proxy = self._se_proxies[self.CspCbf] + vcc_membership = proxy.reportVccSubarrayMembership + for vcc_id, receptorID in self._vcc_to_receptor_map.items(): + self._receptorsMembership[receptorID - 1] = vcc_membership[vcc_id - 1] + except tango.DevFailed as df: + tango.Except.re_throw_exception(df, + "CommandFailed", + "read_receptorsMembership failed", + "Command()") + except KeyError as key_err: + msg = "Can't retrieve the information of key {}".format(key_err) + self.dev_logging(msg, tango.LogLevel.LOG_ERROR) + tango.Except.throw_exception("Attribute reading failure", + msg, + "read_receptorMembership", + tango.ErrSeverity.ERR) + except AttributeError as attr_err: + msg = "Error in reading {}: {} ".format(str(attr_err.args[0]), attr_err.__doc__) + tango.Except.throw_exception("Attribute reading failure", + msg, + "read_receptorMembership", + tango.ErrSeverity.ERR) + return self._receptorsMembership + # PROTECTED REGION END # // CspMaster.receptorMembership_read + + # PROTECTED REGION END # // MidCspMasterBase.receptorMembership_read + + def read_unassignedReceptorIDs(self): + # PROTECTED REGION ID(MidCspMasterBase.unassignedReceptorIDs_read) ENABLED START # + """ + Class attribute method. + + Returns: + The list of the available receptors IDs. + The list includes all the receptors that are not assigned to any subarray and, + from the side of CSP, are considered "full working". This means:\n + * a valid link connection receptor-VCC\n + * the connected VCC healthState OK + + *Type*: array of DevUShort + Raises: + tango.DevFailed: if there is no DeviceProxy providing interface to the\ + CBF sub-element Master Device or an error is caught during\ + command execution. + """ + # PROTECTED REGION ID(CspMaster.availableReceptorIDs_read) ENABLED START # + self._unassigned_receptor_id.clear() + if not self._is_device_running(self.CspCbf, self._se_proxies): + return self._unassigned_receptor_id + try: + proxy = self._se_proxies[self.CspCbf] + # get the State and sub-array affiliation of the VCC + # Note: Mid CBF should also provide information about receptors! + vcc_state = proxy.reportVCCState + vcc_membership = proxy.reportVccSubarrayMembership + # check if the VCC-receptor map is already defined + if not self._vcc_to_receptor_map: + vcc_to_receptor = proxy.vccToReceptor + self._vcc_to_receptor_map = dict([int(ID) for ID in pair.split(":")] + for pair in vcc_to_receptor) + # get the list with the IDs of the available VCC + for vcc_id, receptorID in self._vcc_to_receptor_map.items(): + try: + if vcc_state[vcc_id - 1] not in [tango.DevState.UNKNOWN]: + # skip the vcc already assigned to a sub-array + if vcc_membership[vcc_id - 1] != 0: + continue + # OSS: valid receptorIDs are in [1,197] range + # receptorID = 0 means the link connection between + # the receptor and the VCC is off + if receptorID > 0: + self._unassigned_receptor_id.append(receptorID) + else: + log_msg = ("Link problem with receptor connected" + " to Vcc {}".format(vcc_id + 1)) + self.dev_logging(log_msg, tango.LogLevel.LOG_WARN) + except KeyError as key_err: + log_msg = ("No key {} found while accessing VCC {}".format(str(key_err), + vcc_id)) + self.dev_logging(log_msg, tango.LogLevel.LOG_WARN) + except IndexError as idx_error: + log_msg = ("Error accessing VCC" + " element {}: {}".format(vcc_id, + str(idx_error))) + self.dev_logging(log_msg, tango.LogLevel.LOG_WARN) + except KeyError as key_err: + log_msg = "Can't retrieve the information of key {}".format(key_err) + tango.Except.throw_exception("Attribute reading failure", + log_msg, + "read_availableReceptorIDs", + tango.ErrSeverity.ERR) + except tango.DevFailed as df: + log_msg = "Error in read_unassignedReceptorIDs: {}".format(df.args[0].reason) + self.dev_logging(log_msg, int(tango.LogLevel.LOG_ERROR)) + tango.Except.throw_exception("Attribute reading failure", + log_msg, + "read_unassignedReceptorIDs", + tango.ErrSeverity.ERR) + except AttributeError as attr_err: + msg = "Error in reading {}: {} ".format(str(attr_err.args[0]), + attr_err.__doc__) + tango.Except.throw_exception("Attribute reading failure", + msg, + "read_unassignedReceptorIDs", + tango.ErrSeverity.ERR) + + return self._unassigned_receptor_id + + # PROTECTED REGION END # // MidCspMasterBase.unassignedReceptorIDs_read + + + # -------- + # Commands + # -------- + +# ---------- +# Run server +# ---------- + + +def main(args=None, **kwargs): + # PROTECTED REGION ID(MidCspMasterBase.main) ENABLED START # + return run((MidCspMasterBase,), args=args, **kwargs) + # PROTECTED REGION END # // MidCspMasterBase.main + +if __name__ == '__main__': + main() diff --git a/csp-lmc-mid/pogo/MidCspMasterBase.py b/csp-lmc-mid/pogo/MidCspMasterBase.py new file mode 100644 index 0000000..7eeec89 --- /dev/null +++ b/csp-lmc-mid/pogo/MidCspMasterBase.py @@ -0,0 +1,349 @@ +# -*- coding: utf-8 -*- +# +# This file is part of the MidCspMasterBase project +# +# INAF-SKA Telescope +# +# Distributed under the terms of the GPL license. +# See LICENSE.txt for more info. + +""" MidCspMasterBase class + +The base class for MID CspMAster. +Fuctionality to monitor CSP.LMC Capabilities are +implemented in separate TANGO Devices. +""" + +# PyTango 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 CspMaster import CspMaster +# Additional import +# PROTECTED REGION ID(MidCspMasterBase.additionnal_import) ENABLED START # +# PROTECTED REGION END # // MidCspMasterBase.additionnal_import + +__all__ = ["MidCspMasterBase", "main"] + + +class MidCspMasterBase(CspMaster): + """ + The base class for MID CspMAster. + Fuctionality to monitor CSP.LMC Capabilities are + implemented in separate TANGO Devices. + + **Properties:** + + - Device Property + + + + + + + + + + + + + + + + """ + __metaclass__ = DeviceMeta + # PROTECTED REGION ID(MidCspMasterBase.class_variable) ENABLED START # + # PROTECTED REGION END # // MidCspMasterBase.class_variable + + # ----------------- + # Device Properties + # ----------------- + + + + + + + + + + + + + + + + + # ---------- + # Attributes + # ---------- + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + availableCapabilities = attribute( + dtype=('DevString',), + max_dim_x=20, + doc="A list of available number of instances of each capability type, e.g. `CORRELATOR:512`, `PSS-BEAMS:4`.", + ) + + + + receptorMembership = attribute( + dtype=('DevUShort',), + max_dim_x=197, + label="Receptor Memebership", + doc="The receptors affiliation to MID CSP sub-arrays.", + ) + + unassignedReceptorIDs = attribute( + dtype=('DevUShort',), + max_dim_x=197, + label="Unassigned receptors IDs", + doc="The list of available receptors IDs.", + ) + + reportSearchBeamState = attribute(name="reportSearchBeamState", + label="SearchBeam Capabilities State", + forwarded=True + ) + reportSearchBeamHealthState = attribute(name="reportSearchBeamHealthState", + label="SearchBeam Capabilities healthState", + forwarded=True + ) + reportSearchBeamObsState = attribute(name="reportSearchBeamObsState", + label="SearchBeam Capabilities obsState", + forwarded=True + ) + reportSearchBeamAdminMode = attribute(name="reportSearchBeamAdminMode", + label="SearchBeam Capabilities adminMode", + forwarded=True + ) + reportTimingBeamState = attribute(name="reportTimingBeamState", + label="TimingBeam Capabilities State", + forwarded=True + ) + reportTimingBeamHealthState = attribute(name="reportTimingBeamHealthState", + label="TimingBeam Capabilities healthState", + forwarded=True + ) + reportTimingBeamObsState = attribute(name="reportTimingBeamObsState", + label="TimingBeam Capabilities obsState", + forwarded=True + ) + reportTimingBeamAdminMode = attribute(name="reportTimingBeamAdminMode", + label="TimingBeam Capabilities adminMode", + forwarded=True + ) + reportVlbiBeamState = attribute(name="reportVlbiBeamState", + label="VlbiBeam Capabilities State", + forwarded=True + ) + reportVlbiBeamHealthState = attribute(name="reportVlbiBeamHealthState", + label="VlbiBeam Capabilities healthState", + forwarded=True + ) + reportVlbiBeamObsState = attribute(name="reportVlbiBeamObsState", + label="VlbiBeam Capabilities obsState", + forwarded=True + ) + reportVlbiBeamAdminMode = attribute(name="reportVlbiBeamAdminMode", + label="VlbiBeam Capabilities adminMode", + forwarded=True + ) + searchBeamAddresses = attribute(name="searchBeamAddresses", + label="SearchBeams Capability devices addresses", + forwarded=True + ) + timingBeamAddresses = attribute(name="timingBeamAddresses", + label="TimingBeams Capability devices addresses", + forwarded=True + ) + vlbiBeamAddresses = attribute(name="vlbiBeamAddresses", + label="VlbiBeams Capability devices addresses", + forwarded=True + ) + reservedSearchBeamIDs = attribute(name="reservedSearchBeamIDs", + label="IDs of reserved SeachBeam Capabilities", + forwarded=True + ) + unassignedVlbiBeamIDs = attribute(name="unassignedVlbiBeamIDs", + label="Unassigned VlbiBeam Capabilities IDs", + forwarded=True + ) + unassignedTimingBeamIDs = attribute(name="unassignedTimingBeamIDs", + label="Unassigned TimingBeam Capabilities IDs", + forwarded=True + ) + unassignedSearchBeamIDs = attribute(name="unassignedSearchBeamIDs", + label="Unassigned SeachBeam Capabilities IDs", + forwarded=True + ) + reservedSearchBeamNum = attribute(name="reservedSearchBeamNum", + label="Number of reserved SeachBeam Capabilities", + forwarded=True + ) + searchBeamMembership = attribute(name="searchBeamMembership", + label="SearchBeam Membership", + forwarded=True + ) + timingBeamMembership = attribute(name="timingBeamMembership", + label="TimingBeam Membership", + forwarded=True + ) + vlbiBeamMembership = attribute(name="vlbiBeamMembership", + label="VlbiBeam Membership", + forwarded=True + ) + vccCapabilityAddress = attribute(name="vccCapabilityAddress", + label="vccCapabilityAddress", + forwarded=True + ) + fspCapabilityAddress = attribute(name="fspCapabilityAddress", + label="fspCapabilityAddress", + forwarded=True + ) + reportVCCState = attribute(name="reportVCCState", + label="reportVCCState", + forwarded=True + ) + reportVCCHealthState = attribute(name="reportVCCHealthState", + label="reportVCCHealthState", + forwarded=True + ) + reportVCCAdminMode = attribute(name="reportVCCAdminMode", + label="reportVCCAdminMode", + forwarded=True + ) + reportFSPState = attribute(name="reportFSPState", + label="reportFSPState", + forwarded=True + ) + reportFSPHealthState = attribute(name="reportFSPHealthState", + label="reportFSPHealthState", + forwarded=True + ) + reportFSPAdminMode = attribute(name="reportFSPAdminMode", + label="reportFSPAdminMode", + forwarded=True + ) + fspMembership = attribute(name="fspMembership", + label="fspMembership", + forwarded=True + ) + vccMembership = attribute(name="vccMembership", + label="vccMembership", + forwarded=True + ) + # --------------- + # General methods + # --------------- + + def init_device(self): + """Initialises the attributes and properties of the MidCspMasterBase.""" + CspMaster.init_device(self) + # PROTECTED REGION ID(MidCspMasterBase.init_device) ENABLED START # + # PROTECTED REGION END # // MidCspMasterBase.init_device + + def always_executed_hook(self): + """Method always executed before any TANGO command is executed.""" + # PROTECTED REGION ID(MidCspMasterBase.always_executed_hook) ENABLED START # + # PROTECTED REGION END # // MidCspMasterBase.always_executed_hook + + def delete_device(self): + """Hook to delete resources allocated in init_device. + + This method allows for any memory or other resources allocated in the + init_device method to be released. This method is called by the device + destructor and by the device Init command. + """ + # PROTECTED REGION ID(MidCspMasterBase.delete_device) ENABLED START # + # PROTECTED REGION END # // MidCspMasterBase.delete_device + # ------------------ + # Attributes methods + # ------------------ + + def read_availableCapabilities(self): + # PROTECTED REGION ID(MidCspMasterBase.availableCapabilities_read) ENABLED START # + """Return the availableCapabilities attribute.""" + return ('',) + # PROTECTED REGION END # // MidCspMasterBase.availableCapabilities_read + + def read_receptorMembership(self): + # PROTECTED REGION ID(MidCspMasterBase.receptorMembership_read) ENABLED START # + """Return the receptorMembership attribute.""" + return (0,) + # PROTECTED REGION END # // MidCspMasterBase.receptorMembership_read + + def read_unassignedReceptorIDs(self): + # PROTECTED REGION ID(MidCspMasterBase.unassignedReceptorIDs_read) ENABLED START # + """Return the unassignedReceptorIDs attribute.""" + return (0,) + # PROTECTED REGION END # // MidCspMasterBase.unassignedReceptorIDs_read + + + # -------- + # Commands + # -------- + +# ---------- +# Run server +# ---------- + + +def main(args=None, **kwargs): + # PROTECTED REGION ID(MidCspMasterBase.main) ENABLED START # + return run((MidCspMasterBase,), args=args, **kwargs) + # PROTECTED REGION END # // MidCspMasterBase.main + +if __name__ == '__main__': + main() diff --git a/csp-lmc-mid/pogo/MidCspMasterBase.xmi b/csp-lmc-mid/pogo/MidCspMasterBase.xmi new file mode 100644 index 0000000..bfbc69e --- /dev/null +++ b/csp-lmc-mid/pogo/MidCspMasterBase.xmi @@ -0,0 +1,667 @@ + + + + + + + + + + + + + + 4 + + + + + + + + + + + + + localhost + + + + + + + + + 16 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- GitLab From d818139fbd75c63cbbb93efe49a96a9c11fc7543 Mon Sep 17 00:00:00 2001 From: softir Date: Tue, 14 Jan 2020 13:06:45 +0100 Subject: [PATCH 18/33] AT5-262: removed a subtle bug from CmdInputArgsCheck decorator. --- csp-lmc-common/utils/decorators.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/csp-lmc-common/utils/decorators.py b/csp-lmc-common/utils/decorators.py index 145b8cf..23ece92 100644 --- a/csp-lmc-common/utils/decorators.py +++ b/csp-lmc-common/utils/decorators.py @@ -133,11 +133,19 @@ class CmdInputArgsCheck(object): tango.ErrSeverity.ERR) input_arg = args[1] device_list = input_arg + # Note: device list is a reference to args[1]: changing + # device_list content, args[1] changes accordingly! 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 + # !!Note!!: + # need to copy inside device_list to maintain the reference to args[1] + # if we do + # device_list = dev_instance._se_fqdn + # we get a new variable address and we lose the reference to args[1] + for fqdn in dev_instance._se_fqdn: + device_list.append(fqdn) else: if num_of_devices > len(dev_instance._se_fqdn): # too many devices specified-> log the warning but go on @@ -207,6 +215,7 @@ class CmdInputArgsCheck(object): # expected value. cmd_time_attr_name = cmd_to_exec + "DurationExpected" for device in device_list: + print("Processing device:", device) # set to QUEUED. This state can be useful in case of command abort. dev_instance._se_cmd_execution_state[device][cmd_to_exec] = CmdExecState.QUEUED # reset the timeout and alarm attribute content @@ -222,7 +231,9 @@ class CmdInputArgsCheck(object): # call device_proxy.onCmdDurationExpected throws an AttributeError exception # (not tango.DevFailed) #dev_instance._se_cmd_duration_expected[device][cmd_to_exec] = device_proxy.onCmdDurationExpected - dev_instance._se_cmd_duration_expected[device][cmd_to_exec] = device_proxy.read_attribute(cmd_time_attr_name) + # read_Attribute returns a DeviceAttribute object + device_attr = device_proxy.read_attribute(cmd_time_attr_name) + dev_instance._se_cmd_duration_expected[device][cmd_to_exec] = device_attr.value except tango.DevFailed as tango_err: # we get here if the attribute is not implemented dev_instance.dev_logging((tango_err.args[0].desc), tango.LogLevel.LOG_INFO) @@ -238,13 +249,16 @@ class CmdInputArgsCheck(object): # 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 + print("_se_cmd_duration_expected:",dev_instance._se_cmd_duration_expected[device][cmd_to_exec] ) command_timeout += dev_instance._se_cmd_duration_expected[device][cmd_to_exec] + print("command:", command_timeout) # device loop end # use the greatest value for the onCommand duration expected. if command_timeout > dev_instance._cmd_duration_expected[cmd_to_exec]: dev_instance._cmd_duration_expected[cmd_to_exec] = command_timeout dev_instance.dev_logging("Modified the {} command Duration Expected value!!".format(cmd_to_exec), tango.LogLevel.LOG_INFO) + print("decorator end") return f(*args, **kwargs) return input_args_check -- GitLab From bf4a086b5a344690d803311d6fc87818a4b5d052 Mon Sep 17 00:00:00 2001 From: softir Date: Tue, 14 Jan 2020 13:08:23 +0100 Subject: [PATCH 19/33] AT5-262: added more useful functionality to the sub-element Master simulator devices. --- .../DeviceTestMaster/DeviceTestMaster.py | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/csp-lmc-common/simulators/DeviceTestMaster/DeviceTestMaster.py b/csp-lmc-common/simulators/DeviceTestMaster/DeviceTestMaster.py index 327d491..b84211c 100644 --- a/csp-lmc-common/simulators/DeviceTestMaster/DeviceTestMaster.py +++ b/csp-lmc-common/simulators/DeviceTestMaster/DeviceTestMaster.py @@ -121,10 +121,14 @@ class DeviceTestMaster(with_metaclass(DeviceMeta,SKAMaster)): if elapsed_time > self._duration_expected['on']: break if elapsed_time > 1: - self._cmd_progress['standby'] = elapsed_time* 100/self._duration_expected['on'] - self.push_change_event("onCmdProgress", self._cmd_progress['on']) + self._cmd_progress['on'] = elapsed_time* 100/self._duration_expected['on'] + print(self._cmd_progress['on']) + self.push_change_event("onCommandProgress", int(self._cmd_progress['on'])) + time.sleep(1) self.set_state(tango.DevState.ON) self._health_state = HealthState.DEGRADED + self.push_change_event("onCommandProgress", 100) + print("Final state:", self.get_state()) print("End On command...wait") def standby_subelement(self): @@ -138,8 +142,11 @@ class DeviceTestMaster(with_metaclass(DeviceMeta,SKAMaster)): break if elapsed_time > 1: self._cmd_progress['standby'] = elapsed_time* 100/self._duration_expected['standby'] - self.push_change_event("standbyCmdProgress", self._cmd_progress['standby']) + print(self._cmd_progress['standby']) + self.push_change_event("standbyCommandProgress", int(self._cmd_progress['standby'])) + time.sleep(1) self.set_state(tango.DevState.STANDBY) + self.push_change_event("standbyCommandProgress", 100) print("End Standby command...wait") def off_subelement(self): @@ -152,9 +159,11 @@ class DeviceTestMaster(with_metaclass(DeviceMeta,SKAMaster)): break if elapsed_time > 1: self._cmd_progress['off'] = elapsed_time* 100/self._duration_expected['ff'] - self.push_change_event("offCmdProgress", self._cmd_progress['off']) - self.set_state(tango.DevState.OFF) + self.push_change_event("offCommandProgress", int(self._cmd_progress['off'])) + time.sleep(1) self._health_state = HealthState.UNKNOWN + self.set_state(tango.DevState.OFF) + self.push_change_event("offCommandProgress", 100) def init_device(self): SKAMaster.init_device(self) @@ -176,6 +185,7 @@ class DeviceTestMaster(with_metaclass(DeviceMeta,SKAMaster)): 'elementLoggingLevel': ['__value'], 'centralLoggingLevel': ['__value'],}) + self._admin_mode = int(attribute_properties['adminMode']['__value'][0]) # start a timer to simulate device intialization thread = threading.Timer(1, self.init_subelement) thread.start() @@ -199,19 +209,19 @@ class DeviceTestMaster(with_metaclass(DeviceMeta,SKAMaster)): def read_onCommandProgress(self): # PROTECTED REGION ID(DeviceTestMaster.onCommandProgress_read) ENABLED START # """Return the onCommandProgress attribute.""" - return self._cmd_progress['on'] + return int(self._cmd_progress['on']) # PROTECTED REGION END # // DeviceTestMaster.onCommandProgress_read def read_offCommandProgress(self): # PROTECTED REGION ID(DeviceTestMaster.offCommandProgress_read) ENABLED START # """Return the offCommandProgress attribute.""" - return self._cmd_progress['off'] + return int(self._cmd_progress['off']) # PROTECTED REGION END # // DeviceTestMaster.offCommandProgress_read def read_standbyCommandProgress(self): # PROTECTED REGION ID(DeviceTestMaster.standbyCommandProgress_read) ENABLED START # """Return the standbyCommandProgress attribute.""" - return self._cmd_progress['standby'] + return int(self._cmd_progress['standby']) # PROTECTED REGION END # // DeviceTestMaster.standbyCommandProgress_read def read_onDurationExpected(self): @@ -261,6 +271,8 @@ class DeviceTestMaster(with_metaclass(DeviceMeta,SKAMaster)): def On(self): # PROTECTED REGION ID(DeviceTestMaster.On) ENABLED START # print("Processing On command") + self._cmd_progress['on'] = 0 + self._start_time = time.time() thread = threading.Timer(2, self.on_subelement) thread.start() # PROTECTED REGION END # // DeviceTestMaster.On @@ -272,6 +284,8 @@ class DeviceTestMaster(with_metaclass(DeviceMeta,SKAMaster)): @DebugIt() def Off(self): # PROTECTED REGION ID(DeviceTestMaster.Off) ENABLED START # + self._cmd_progress['off'] = 0 + self._start_time = time.time() thread = threading.Timer(1, self.off_subelement) thread.start() # PROTECTED REGION END # // DeviceTestMaster.Off @@ -285,6 +299,8 @@ class DeviceTestMaster(with_metaclass(DeviceMeta,SKAMaster)): @DebugIt() def Standby(self): # PROTECTED REGION ID(DeviceTestMaster.Standby) ENABLED START # + self._cmd_progress['standby'] = 0 + self._start_time = time.time() thread = threading.Timer(2, self.standby_subelement) thread.start() # PROTECTED REGION END # // DeviceTestMaster.Standby -- GitLab From cb028f5d5c74ed6a62550682fe5850c61ae92d96 Mon Sep 17 00:00:00 2001 From: softir Date: Wed, 15 Jan 2020 11:37:29 +0100 Subject: [PATCH 20/33] AT5-262: Update lmcbaseclasses to 0.2.0 version. --- .gitignore | 2 ++ csp-lmc-common/csp-lmc-common/CspMaster.py | 6 ++-- csp-lmc-common/pogo/CspMaster.xmi | 42 +++++++++++++--------- 3 files changed, 31 insertions(+), 19 deletions(-) diff --git a/.gitignore b/.gitignore index 87548b3..4009e56 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +*.sav +*.swp *.py[cod] *.egg-info *.eggs diff --git a/csp-lmc-common/csp-lmc-common/CspMaster.py b/csp-lmc-common/csp-lmc-common/CspMaster.py index 69c9f9f..85dcb61 100644 --- a/csp-lmc-common/csp-lmc-common/CspMaster.py +++ b/csp-lmc-common/csp-lmc-common/CspMaster.py @@ -474,6 +474,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): self._num_dev_completed_task[cmd_name] = 0 self._list_dev_completed_task[cmd_name] = [] self._cmd_progress[cmd_name] = 0 + self._cmd_duration_measuredcmd_name] = 0 # sub-element command execution measured time se_cmd_duration_measured = defaultdict(lambda:defaultdict(lambda:0)) # loop on the devices and power-on them sequentially @@ -517,8 +518,6 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): self._se_cmd_execution_state[device][cmd_name] = CmdExecState.IDLE se_cmd_duration_measured[device][cmd_name] = (time.time() - self._se_cmd_starting_time[device]) - # calculate the real execution time for the command - self._cmd_duration_measured[cmd_name] += se_cmd_duration_measured[device][cmd_name] print("measured duration:", se_cmd_duration_measured[device][cmd_name]) # The event_id dictionary uses the attribute name in lower case letters as # dictionary key @@ -567,7 +566,6 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): self.dev_logging(msg, tango.LogLevel.LOG_WARN) self._se_cmd_execution_state[device][cmd_name] = CmdExecState.TIMEOUT num_of_failed_device += 1 - self._se_cmd_execution_state[device][cmd_name] = CmdExecState.IDLE # if the CBF command timeout expires, the CSP power-on is stopped # TODO: verify if this behavior conflicts with ICD print("elapsed_time:{} device {}".format(elapsed_time, device)) @@ -585,6 +583,8 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # executing the command self._cmd_progress[cmd_name] = command_progress + self._se_cmd_progress[device][cmd_name]/len(device_list) # end of the while loop + # calculate the real execution time for the command + self._cmd_duration_measured[cmd_name] += se_cmd_duration_measured[device][cmd_name] # update the progress counter at the end of the loop self._cmd_progress[cmd_name] = command_progress + (self._se_cmd_progress[device][cmd_name]/len(device_list)) if len(device_list) == self._num_dev_completed_task[cmd_name] + num_of_failed_device: diff --git a/csp-lmc-common/pogo/CspMaster.xmi b/csp-lmc-common/pogo/CspMaster.xmi index d428255..46ea1fc 100644 --- a/csp-lmc-common/pogo/CspMaster.xmi +++ b/csp-lmc-common/pogo/CspMaster.xmi @@ -4,7 +4,7 @@ - + @@ -12,19 +12,6 @@ 4 - - - - - - - - - - - - localhost - @@ -70,6 +57,16 @@ + + + + 4 + + + + + console::cout + @@ -537,6 +534,13 @@ + + + + + + + @@ -544,11 +548,10 @@ - + - @@ -568,6 +571,13 @@ + + + + + + + -- GitLab From f999dab9ec5c5e3fbb62e2654e7481dc1c622327 Mon Sep 17 00:00:00 2001 From: softir Date: Wed, 15 Jan 2020 11:39:34 +0100 Subject: [PATCH 21/33] AT5-262: renamed csp-lmc-common csp_lmc_common. Moved docker and utils folders. --- .../{csp-lmc-common => csp_lmc_common}/CspCapabilityMonitor.py | 0 csp-lmc-common/{csp-lmc-common => csp_lmc_common}/CspMaster.py | 0 csp-lmc-common/{csp-lmc-common => csp_lmc_common}/CspSubarray.py | 0 csp-lmc-common/{csp-lmc-common => csp_lmc_common}/__init__.py | 0 csp-lmc-common/{ => csp_lmc_common}/utils/cspcommons.py | 0 csp-lmc-common/{ => csp_lmc_common}/utils/decorators.py | 0 csp-lmc-common/{ => csp_lmc_common}/utils/release.py | 0 {docker => csp-lmc-common/docker}/.make/.make-release-support | 0 {docker => csp-lmc-common/docker}/.make/Makefile.mk | 0 {docker => csp-lmc-common/docker}/.release | 0 {docker => csp-lmc-common/docker}/Dockerfile | 0 {docker => csp-lmc-common/docker}/Makefile | 0 {docker => csp-lmc-common/docker}/config/csplmc_dsconfig.json | 0 {docker => csp-lmc-common/docker}/config/csplmc_dsconfig.json.sav | 0 {docker => csp-lmc-common/docker}/config/midcbf_dsconfig.json | 0 {docker => csp-lmc-common/docker}/csp-lmc.yml | 0 {docker => csp-lmc-common/docker}/csp-tangodb.yml | 0 {docker => csp-lmc-common/docker}/mid-cbf-mcs.yml | 0 18 files changed, 0 insertions(+), 0 deletions(-) rename csp-lmc-common/{csp-lmc-common => csp_lmc_common}/CspCapabilityMonitor.py (100%) rename csp-lmc-common/{csp-lmc-common => csp_lmc_common}/CspMaster.py (100%) rename csp-lmc-common/{csp-lmc-common => csp_lmc_common}/CspSubarray.py (100%) rename csp-lmc-common/{csp-lmc-common => csp_lmc_common}/__init__.py (100%) rename csp-lmc-common/{ => csp_lmc_common}/utils/cspcommons.py (100%) rename csp-lmc-common/{ => csp_lmc_common}/utils/decorators.py (100%) rename csp-lmc-common/{ => csp_lmc_common}/utils/release.py (100%) rename {docker => csp-lmc-common/docker}/.make/.make-release-support (100%) rename {docker => csp-lmc-common/docker}/.make/Makefile.mk (100%) rename {docker => csp-lmc-common/docker}/.release (100%) rename {docker => csp-lmc-common/docker}/Dockerfile (100%) rename {docker => csp-lmc-common/docker}/Makefile (100%) rename {docker => csp-lmc-common/docker}/config/csplmc_dsconfig.json (100%) rename {docker => csp-lmc-common/docker}/config/csplmc_dsconfig.json.sav (100%) rename {docker => csp-lmc-common/docker}/config/midcbf_dsconfig.json (100%) rename {docker => csp-lmc-common/docker}/csp-lmc.yml (100%) rename {docker => csp-lmc-common/docker}/csp-tangodb.yml (100%) rename {docker => csp-lmc-common/docker}/mid-cbf-mcs.yml (100%) diff --git a/csp-lmc-common/csp-lmc-common/CspCapabilityMonitor.py b/csp-lmc-common/csp_lmc_common/CspCapabilityMonitor.py similarity index 100% rename from csp-lmc-common/csp-lmc-common/CspCapabilityMonitor.py rename to csp-lmc-common/csp_lmc_common/CspCapabilityMonitor.py diff --git a/csp-lmc-common/csp-lmc-common/CspMaster.py b/csp-lmc-common/csp_lmc_common/CspMaster.py similarity index 100% rename from csp-lmc-common/csp-lmc-common/CspMaster.py rename to csp-lmc-common/csp_lmc_common/CspMaster.py diff --git a/csp-lmc-common/csp-lmc-common/CspSubarray.py b/csp-lmc-common/csp_lmc_common/CspSubarray.py similarity index 100% rename from csp-lmc-common/csp-lmc-common/CspSubarray.py rename to csp-lmc-common/csp_lmc_common/CspSubarray.py diff --git a/csp-lmc-common/csp-lmc-common/__init__.py b/csp-lmc-common/csp_lmc_common/__init__.py similarity index 100% rename from csp-lmc-common/csp-lmc-common/__init__.py rename to csp-lmc-common/csp_lmc_common/__init__.py diff --git a/csp-lmc-common/utils/cspcommons.py b/csp-lmc-common/csp_lmc_common/utils/cspcommons.py similarity index 100% rename from csp-lmc-common/utils/cspcommons.py rename to csp-lmc-common/csp_lmc_common/utils/cspcommons.py diff --git a/csp-lmc-common/utils/decorators.py b/csp-lmc-common/csp_lmc_common/utils/decorators.py similarity index 100% rename from csp-lmc-common/utils/decorators.py rename to csp-lmc-common/csp_lmc_common/utils/decorators.py diff --git a/csp-lmc-common/utils/release.py b/csp-lmc-common/csp_lmc_common/utils/release.py similarity index 100% rename from csp-lmc-common/utils/release.py rename to csp-lmc-common/csp_lmc_common/utils/release.py diff --git a/docker/.make/.make-release-support b/csp-lmc-common/docker/.make/.make-release-support similarity index 100% rename from docker/.make/.make-release-support rename to csp-lmc-common/docker/.make/.make-release-support diff --git a/docker/.make/Makefile.mk b/csp-lmc-common/docker/.make/Makefile.mk similarity index 100% rename from docker/.make/Makefile.mk rename to csp-lmc-common/docker/.make/Makefile.mk diff --git a/docker/.release b/csp-lmc-common/docker/.release similarity index 100% rename from docker/.release rename to csp-lmc-common/docker/.release diff --git a/docker/Dockerfile b/csp-lmc-common/docker/Dockerfile similarity index 100% rename from docker/Dockerfile rename to csp-lmc-common/docker/Dockerfile diff --git a/docker/Makefile b/csp-lmc-common/docker/Makefile similarity index 100% rename from docker/Makefile rename to csp-lmc-common/docker/Makefile diff --git a/docker/config/csplmc_dsconfig.json b/csp-lmc-common/docker/config/csplmc_dsconfig.json similarity index 100% rename from docker/config/csplmc_dsconfig.json rename to csp-lmc-common/docker/config/csplmc_dsconfig.json diff --git a/docker/config/csplmc_dsconfig.json.sav b/csp-lmc-common/docker/config/csplmc_dsconfig.json.sav similarity index 100% rename from docker/config/csplmc_dsconfig.json.sav rename to csp-lmc-common/docker/config/csplmc_dsconfig.json.sav diff --git a/docker/config/midcbf_dsconfig.json b/csp-lmc-common/docker/config/midcbf_dsconfig.json similarity index 100% rename from docker/config/midcbf_dsconfig.json rename to csp-lmc-common/docker/config/midcbf_dsconfig.json diff --git a/docker/csp-lmc.yml b/csp-lmc-common/docker/csp-lmc.yml similarity index 100% rename from docker/csp-lmc.yml rename to csp-lmc-common/docker/csp-lmc.yml diff --git a/docker/csp-tangodb.yml b/csp-lmc-common/docker/csp-tangodb.yml similarity index 100% rename from docker/csp-tangodb.yml rename to csp-lmc-common/docker/csp-tangodb.yml diff --git a/docker/mid-cbf-mcs.yml b/csp-lmc-common/docker/mid-cbf-mcs.yml similarity index 100% rename from docker/mid-cbf-mcs.yml rename to csp-lmc-common/docker/mid-cbf-mcs.yml -- GitLab From 7e2ac89488ba3a33b0754530e2ff1b7334d5cdd6 Mon Sep 17 00:00:00 2001 From: softir Date: Thu, 16 Jan 2020 10:22:26 +0100 Subject: [PATCH 22/33] AT5-262: Use new verion of the lmcbaseclasses package (version>= 0.2.0). Replace usage of dev_logging() with logger(). Removed the path change. Moved the AdminModeCheck decorator before the is_XXX_allowed methods. Updated Dockerfile to use last version of lmc base classes. --- csp-lmc-common/csp_lmc_common/CspMaster.py | 150 ++++++++++-------- .../csp_lmc_common/utils/__init__.py | 0 .../csp_lmc_common/utils/decorators.py | 47 +++--- .../csp_lmc_common/utils/release.py | 4 +- csp-lmc-common/docker/.make/Makefile.mk | 2 +- csp-lmc-common/docker/Dockerfile | 3 +- csp-lmc-common/setup.py | 64 ++++++++ csp-lmc-mid/csp-lmc-mid/MidCspMasterBase.py | 28 ++-- 8 files changed, 175 insertions(+), 123 deletions(-) create mode 100644 csp-lmc-common/csp_lmc_common/utils/__init__.py diff --git a/csp-lmc-common/csp_lmc_common/CspMaster.py b/csp-lmc-common/csp_lmc_common/CspMaster.py index 85dcb61..26e7eb7 100644 --- a/csp-lmc-common/csp_lmc_common/CspMaster.py +++ b/csp-lmc-common/csp_lmc_common/CspMaster.py @@ -13,12 +13,10 @@ 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 import threading import time # PROTECTED REGION END# //CspMaster.standardlibray_import @@ -42,14 +40,10 @@ from skabase.auxiliary import utils # 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 decorators -from decorators import AdminModeCheck, CmdInputArgsCheck -import cspcommons -from cspcommons import HealthState, AdminMode, ObsState, CmdExecState -import release + +from .utils.decorators import AdminModeCheck, CmdInputArgsCheck +from .utils.cspcommons import HealthState, AdminMode, ObsState, CmdExecState +from . import release # PROTECTED REGION END# //CspMaster.add_path @@ -147,25 +141,25 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): else: log_msg = ("Attribute {} not still " "handled".format(evt.attr_name)) - self.dev_logging(log_msg, tango.LogLevel.LOG_WARN) + self.logger.warn(log_msg) else: log_msg = ("Unexpected change event for" " attribute: {}".format(str(evt.attr_name))) - self.dev_logging(log_msg, tango.LogLevel.LOG_WARN) + self.logger.warn(log_msg) 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) + self.logger.info(log_msg) # update CSP global state # healthState and State are updated accordingly to the updated values of # sub-elements healthState, State and adminMode if evt.attr_value.name.lower() in ["state", "healthstate", "adminmode"]: self._update_csp_state() except tango.DevFailed as df: - self.dev_logging(str(df.args[0].desc), tango.LogLevel.LOG_ERROR) + self.logger.error(str(df.args[0].desc)) except Exception as except_occurred: - self.dev_logging(str(except_occurred), tango.LogLevel.LOG_ERROR) + self.logger.error(str(except_occurred)) else: for item in evt.errors: # API_EventTimeout: if sub-element device not reachable it transitions @@ -185,7 +179,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # 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) + self.logger.warn(log_msg) def _attributes_change_evt_cb(self, evt): """ @@ -217,20 +211,20 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): else: log_msg = ("Unexpected change event for" " attribute: {}".format(str(evt.attr_name))) - self.dev_logging(log_msg, tango.LogLevel.LOG_WARN) + self.logger.warn(log_msg) 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) + self.logger.info(log_msg) except tango.DevFailed as df: - self.dev_logging(str(df.args[0].desc), tango.LogLevel.LOG_ERROR) + self.logger.error(str(df.args[0].desc)) except Exception as except_occurred: - self.dev_logging(str(except_occurred), tango.LogLevel.LOG_ERROR) + self.logger.error(str(except_occurred)) else: for item in evt.errors: log_msg = item.reason + ": on attribute " + str(evt.attr_name) - self.dev_logging(log_msg, tango.LogLevel.LOG_WARN) + self.logger.warn(log_msg) def _cmd_ended_cb(self, evt): """ @@ -262,28 +256,25 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): if not evt.err: msg = "Device {} is processing command {}".format(evt.device, evt.cmd_name) - print(msg) - self.dev_logging(msg, tango.LogLevel.LOG_INFO) + self.logger.info(msg) else: msg = "Error!!Command {} ended on device {}.\n".format(evt.cmd_name, evt.device.dev_name()) msg += " Desc: {}".format(evt.errors[0].desc) - self.dev_logging(msg, tango.LogLevel.LOG_ERROR) - print(msg) + self.logger.error(msg) self._se_cmd_execution_state[evt.device.dev_name()][evt.cmd_name.lower()] = CmdExecState.FAILED self._alarm_message[evt.cmd_name] = msg # obsState and obsMode values take on the CbfSubarray's values via # the subscribe/publish mechanism else: - self.dev_logging("cmd_ended callback: evt is empty!!", - tango.LogLevel.LOG_ERRO) + self.logger.error("cmd_ended callback: evt is empty!!") except tango.DevFailed as df: msg = ("CommandCallback cmd_ended failure - desc: {}" " reason: {}".format(df.args[0].desc, df.args[0].reason)) - self.dev_logging(msg, tango.LogLevel.LOG_ERROR) + self.logger.error(msg) except Exception as ex: msg = "CommandCallBack cmd_ended general exception: {}".format(str(ex)) - self.dev_logging(msg, tango.LogLevel.LOG_ERROR) + self.logger.error(msg) # --------------- # Class methods @@ -304,7 +295,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): self._update_csp_health_state() self.set_state(self._se_state[self.CspCbf]) if self._admin_mode in [AdminMode.OFFLINE, AdminMode.NOTFITTED, AdminMode.RESERVED]: - self.set_state[tango.DevState.DISABLE] + self.set_state(tango.DevState.DISABLE) def _update_csp_health_state(self): """ @@ -357,8 +348,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): try: # DeviceProxy to sub-elements log_msg = "Trying connection to" + str(fqdn) + " device" - print(log_msg) - self.dev_logging(log_msg, int(tango.LogLevel.LOG_INFO)) + self.logger.info(log_msg) device_proxy = DeviceProxy(fqdn) #device_proxy.ping() # Note: ping() method is not called. The DeviceProxy is initialized even if the @@ -392,12 +382,12 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): self._se_event_id[fqdn]['healthState'] = ev_id except KeyError as key_err: log_msg = ("No key {} found".format(str(key_err))) - self.dev_logging(log_msg, tango.LogLevel.LOG_WARN) + self.logger.warn(log_msg) 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) + self.logger.error(log_msg) def _is_device_running(self, device_fqdn, proxy_list=None): """ @@ -424,7 +414,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # 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) + self.logger.error(msg) return False except tango.DevFailed: return False @@ -474,7 +464,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): self._num_dev_completed_task[cmd_name] = 0 self._list_dev_completed_task[cmd_name] = [] self._cmd_progress[cmd_name] = 0 - self._cmd_duration_measuredcmd_name] = 0 + self._cmd_duration_measured[cmd_name] = 0 # sub-element command execution measured time se_cmd_duration_measured = defaultdict(lambda:defaultdict(lambda:0)) # loop on the devices and power-on them sequentially @@ -506,10 +496,9 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): command_progress = self._cmd_progress[cmd_name] while True: if self._se_state[device] == dev_successful_state: - print("Command {} ended with success on device {}.".format(cmd_name, - device)) - self.dev_logging("Command {} executed on device {}.".format(cmd_name,device), - tango.LogLevel.LOG_INFO) + self.logger.info("Command {} ended with success on device {}.".format(cmd_name, + device)) + self.logger.info("Command {} executed on device {}.".format(cmd_name,device)) # update the list and number of device that completed the task self._num_dev_completed_task[cmd_name] += 1 self._list_dev_completed_task[cmd_name].append(device) @@ -518,11 +507,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): self._se_cmd_execution_state[device][cmd_name] = CmdExecState.IDLE se_cmd_duration_measured[device][cmd_name] = (time.time() - self._se_cmd_starting_time[device]) - print("measured duration:", se_cmd_duration_measured[device][cmd_name]) - # The event_id dictionary uses the attribute name in lower case letters as - # dictionary key - print("evt {}: {}".format(cmd_progress_attr, - self._se_event_id[device][cmd_progress_attr.lower()])) + self.logger.info("measured duration:", se_cmd_duration_measured[device][cmd_name]) self._se_cmd_progress[device][cmd_name] = 100 # command success: exit from the wait loop and issue the command # on the next device of the list @@ -531,8 +516,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): if self._se_state[device] in [tango.DevState.FAULT, tango.DevState.ALARM]: self._se_cmd_execution_state[device][cmd_name] = CmdExecState.FAILED self._alarm_message[device] = ("Device {} is {}".format(device, self.get_status())) - print(self._alarm_message) - self.dev_logging(self._alarm_message[device], tango.LogLevel.LOG_WARN) + self.logger.warn(self._alarm_message[device]) num_of_failed_device += 1 break # check if sub-element command ended throwing an exception: in this case the @@ -563,14 +547,14 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): self._se_cmd_execution_state[device][cmd_name] == CmdExecState.TIMEOUT): msg = ("Timeout executing {} command on device {}".format(cmd_name, device)) print(msg) - self.dev_logging(msg, tango.LogLevel.LOG_WARN) + self.logger.warn(msg) self._se_cmd_execution_state[device][cmd_name] = CmdExecState.TIMEOUT num_of_failed_device += 1 # if the CBF command timeout expires, the CSP power-on is stopped # TODO: verify if this behavior conflicts with ICD print("elapsed_time:{} device {}".format(elapsed_time, device)) if device == self.CspCbf: - self.dev_logging("CBF Timeout during power-on!!! Exit", tango.LogLevel.LOG_ERROR) + self.logger.error("CBF Timeout during power-on!!! Exit") self._timeout_expired[cmd_name] = True self._se_cmd_execution_state[device][cmd_name] = CmdExecState.IDLE self._timeout_expired[cmd_name] = True @@ -588,17 +572,17 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # update the progress counter at the end of the loop self._cmd_progress[cmd_name] = command_progress + (self._se_cmd_progress[device][cmd_name]/len(device_list)) if len(device_list) == self._num_dev_completed_task[cmd_name] + num_of_failed_device: - print("All devices have been handled!") + self.logger.info("All devices have been handled!") # end of the command: the command has been issued on all the sub-element devices # reset the execution flag for the CSP break except KeyError as key_err: msg = "No key {} found".format(str(key_err)) - self.dev_logging(msg, tango.LogLevel.LOG_WARN) + self.logger.warn(msg) except tango.DevFailed as df: # It should not happen! Verify msg = "Failure reason: {} Desc: {}".format(str(df.args[0].reason), str(df.args[0].desc)) - self.dev_logging(msg, tango.LogLevel.LOG_ERROR) + self.logger.warn(msg) # out of the for loop # reset the CSP command execution flag self._cmd_execution_state[cmd_name] = CmdExecState.IDLE @@ -644,7 +628,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # TODO: add checks for timeout/errors except KeyError as key_err: msg = "Can't retrieve the information of key {}".format(key_err) - self.dev_logging(msg, tango.LogLevel.LOG_ERROR) + self.logger.error(msg) tango.Except.throw_exception("DevFailed excpetion", msg, "write adminMode", tango.ErrSeverity.ERR) def _connect_capabilities_monitor(self): @@ -1214,7 +1198,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): self._admin_mode = memorized_attr_dict['adminMode'] self._storage_logging_level = memorized_attr_dict['storageLoggingLevel'] except KeyError as key_err: - self.dev_logging("Key {} not found".format(key_err), tango.LogLevel.LOG_INFO) + self.logger.info("Key {} not found".format(key_err)) # initialize list with CSP sub-element FQDNs self._se_fqdn = [] @@ -1235,14 +1219,14 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): for fqdn in self._se_fqdn: attribute_properties = csp_tango_db.get_device_attribute_property(fqdn, {'adminMode': ['__value']}) - print("fqdn: {} attribute_properties: {}".format(fqdn, attribute_properties)) + self.logger.debug("fqdn: {} attribute_properties: {}".format(fqdn, attribute_properties)) try: admin_mode_memorized = attribute_properties['adminMode']['__value'] self._se_admin_mode[fqdn] = int(admin_mode_memorized[0]) except KeyError as key_err: msg = ("No key {} found for sub-element {}" " adminMode attribute".format(key_err, fqdn)) - self.dev_logging(msg, tango.LogLevel.LOG_INFO) + self.logger.info(msg) # _se_proxies: the sub-element proxies # implementes s a dictionary: @@ -1399,7 +1383,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): "on device {}. Reason: {}".format(event_id, fqdn, key_err)) - self.dev_logging(msg, tango.LogLevel.LOG_ERROR) + self.logger.error(msg) # remove the attribute entry from the fqdn dictionary for attr_name in event_to_remove[fqdn]: del self._se_event_id[fqdn][attr_name] @@ -1411,10 +1395,10 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): else: # What to do in this case??. Only log (for the moment) msg = "Still subscribed events: {}".format(self._se_event_id) - self.dev_logging(msg, tango.LogLevel.LOG_WARN) + self.logger.warn(msg) except KeyError as key_err: msg = " Can't retrieve the information of key {}".format(key_err) - self.dev_logging(msg, tango.LogLevel.LOG_ERROR) + self.logger.error(msg) continue # clear any list and dict self._se_fqdn.clear() @@ -1444,7 +1428,6 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): :return: None """ # PROTECTED REGION ID(CspMaster.adminMode_write) ENABLED START # - print("set admin mode: fqdn {}".format(self._se_fqdn)) for fqdn in self._se_fqdn: try: self._se_write_adminMode(value, fqdn) @@ -1453,7 +1436,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): log_msg = (("Failure in setting adminMode " "for device {}: {}".format(str(fqdn), str(df.args[0].reason)))) - self.dev_logging(log_msg, int(tango.LogLevel.LOG_ERROR)) + self.logger.error(log_msg) #TODO: what happens if one sub-element fails? # add check on timeout command execution self._admin_mode = value @@ -1717,12 +1700,12 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # -------- # Commands # -------- - + @AdminModeCheck('On') def is_On_allowed(self): """ *TANGO is_allowed method* - Command *Off* is allowed when the device *State* is STANDBY. + Command *On* is allowed when the device *State* is STANDBY. Returns: True if the method is allowed, otherwise False. @@ -1739,9 +1722,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): " If the array length is > 1, each array element specifies the FQDN of " "the CSP SubElement to switch ON."), ) - @DebugIt() - @AdminModeCheck('On') - #@CmdInputArgsCheck(cmd_name = "on", attr_name = "onCommnadProgress") + @DebugIt() @CmdInputArgsCheck("onCommandProgress", "onCmdTimeoutExpired", cmd_name = "on") def On(self, argin): # PROTECTED REGION ID(CspMaster.On) ENABLED START # @@ -1795,6 +1776,22 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): time.sleep(0.2) # PROTECTED REGION END # // CspMaster.On + @AdminModeCheck('Off') + def is_Off_allowed(self): + """ + *TANGO is_allowed method* + + Command *Off* is allowed when the device *State* is STANDBY. + + Returns: + True if the method is allowed, otherwise False. + """ + # PROTECTED REGION ID(CspMaster.is_On_allowed) ENABLED START # + # Note: as per SKA Guidelines, the command is allowed when + if self.get_state() not in [tango.DevState.STANDBY, tango.DevState.OFF]: + return False + return True + @command( dtype_in='DevVarStringArray', doc_in=("If the array length is 0, the command applies to the whole CSP Element." @@ -1802,8 +1799,6 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): " CSP SubElement to switch OFF."), ) @DebugIt() - @AdminModeCheck('Off') - #@CmdInputArgsCheck(cmd_name = "off", attr_name = "offcommandprogress") @CmdInputArgsCheck("offCommandProgress", "offCmdTimeoutExpired", cmd_name = "off") def Off(self, argin): # PROTECTED REGION ID(CspMaster.Off) ENABLED START # @@ -1858,6 +1853,23 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # PROTECTED REGION END # // CspMaster.Off + @AdminModeCheck('Standby') + def is_Standby_allowed(self): + """ + *TANGO is_allowed method* + + Command *Standby* is allowed when the device *State* is ON. + + Returns: + True if the method is allowed, otherwise False. + """ + # PROTECTED REGION ID(CspMaster.is_On_allowed) ENABLED START # + # Note: as per SKA Guidelines, the command is allowed when + if self.get_state() not in [tango.DevState.STANDBY, tango.DevState.ON]: + return False + return True + + @command( dtype_in='DevVarStringArray', doc_in=("If the array length is 0, the command applies to the whole CSP Element. " @@ -1865,8 +1877,6 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): "CSP SubElement to put in STANDBY mode."), ) @DebugIt() - @AdminModeCheck('Standby') - #@CmdInputArgsCheck(cmd_name = "standby", attr_name = "standbycommandprogress") @CmdInputArgsCheck("standbyCommandProgress", "standbyCmdTimeoutExpired", cmd_name = "standby") def Standby(self, argin): diff --git a/csp-lmc-common/csp_lmc_common/utils/__init__.py b/csp-lmc-common/csp_lmc_common/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/csp-lmc-common/csp_lmc_common/utils/decorators.py b/csp-lmc-common/csp_lmc_common/utils/decorators.py index 23ece92..9f8d6c4 100644 --- a/csp-lmc-common/csp_lmc_common/utils/decorators.py +++ b/csp-lmc-common/csp_lmc_common/utils/decorators.py @@ -1,13 +1,8 @@ import sys import os -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 tango -import cspcommons -from cspcommons import AdminMode, ObsState, CmdExecState +from .cspcommons import AdminMode, ObsState, CmdExecState import functools class AdminModeCheck(object): @@ -16,16 +11,14 @@ class AdminModeCheck(object): It checks the adminMode attribute value: if not ONLINE or MAINTENANCE it throws an exception. """ - def __init__(self, args=False, kwargs=False): - self._args = args - self._kwargs = kwargs + def __init__(self, cmd_to_execute): + self._cmd_to_execute = cmd_to_execute def __call__(self, f): @functools.wraps(f) def admin_mode_check(*args, **kwargs): # get the device instance dev_instance = args[0] - cmd_to_execute = self._args - print("AdminModeCheck command to execute:", cmd_to_execute) + dev_instance.logger.debug("AdminModeCheck command to execute:", self._cmd_to_execute) # Check the AdminMode value: the command is callable only if the # the administration mode is ONLINE or MAINTENACE @@ -35,11 +28,11 @@ class AdminModeCheck(object): # 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 = (cmd_to_execute, dev_instance.get_state(), AdminMode(dev_instance._admin_mode).name) + #msg_args = (cmd_to_execute, dev_instance.get_state(), dev_instance._admin_mode.name) + msg_args = (self._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) + dev_instance.logger.warn("Command {}: incoherent device State {} " + " with adminMode {}".format(*msg_args)) err_msg = ("{} command can't be issued when State is {} and" " adminMode is {} ".format(*msg_args)) @@ -126,7 +119,7 @@ class CmdInputArgsCheck(object): cmd_to_exec = self._kwargs['cmd_name'] except KeyError as key_err: msg =("No key found for {}".format(str(key_err))) - dev_instance.dev_logging(msg, tango.LogLevel.LOG_WARN) + dev_instance.logger.warn(msg) tango.Except.throw_exception("Command failure", "Error in input decorator args", "CmdInputArgsCheck decorator", @@ -150,8 +143,8 @@ class CmdInputArgsCheck(object): 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) - print("CmdInputArgsCheck: devices {} to check:".format(device_list)) + dev_instance.logger.warn("Too many input parameters") + dev_instance.logger.debug("CmdInputArgsCheck: devices {} to check:".format(device_list)) # If a sub-element device is already executing a power command throws an # exception only if the requested command is different from the one # is already running (power commands have to be executed sequentially). @@ -173,7 +166,7 @@ class CmdInputArgsCheck(object): # the requested command is already running # return with no exception (see SKA Guidelines) msg = ("Command {} is already running".format(cmd_to_exec)) - dev_instance.dev_logging(msg, tango.LogLevel.LOG_INFO) + dev_instance.logger.info(msg) return else: # another power command is already running @@ -203,19 +196,19 @@ class CmdInputArgsCheck(object): "CmdInputArgsCheck decorator", tango.ErrSeverity.ERR) device_to_remove.append(device) - print("Devices to remove from the list:", device_to_remove) + dev_instance.logger.debug("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) + dev_instance.logger.info("No device to power on") return command_timeout = 0 # build the sub-element attribute name storing the command duration # expected value. cmd_time_attr_name = cmd_to_exec + "DurationExpected" for device in device_list: - print("Processing device:", device) + dev_instance.logger.debug("Processing device:", device) # set to QUEUED. This state can be useful in case of command abort. dev_instance._se_cmd_execution_state[device][cmd_to_exec] = CmdExecState.QUEUED # reset the timeout and alarm attribute content @@ -236,7 +229,7 @@ class CmdInputArgsCheck(object): dev_instance._se_cmd_duration_expected[device][cmd_to_exec] = device_attr.value except tango.DevFailed as tango_err: # we get here if the attribute is not implemented - dev_instance.dev_logging((tango_err.args[0].desc), tango.LogLevel.LOG_INFO) + dev_instance.logger.info(tango_err.args[0].desc) for attr in attrs_to_subscribe: try: if dev_instance._se_event_id[device][attr.lower()] == 0: @@ -244,21 +237,17 @@ class CmdInputArgsCheck(object): dev_instance._attributes_change_evt_cb, stateless=False) dev_instance._se_event_id[device][attr.lower()] = evt_id except tango.DevFailed as tango_err: - dev_instance.dev_logging((tango_err.args[0].desc), tango.LogLevel.LOG_INFO) + dev_instance.logger.info(tango_err.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 - print("_se_cmd_duration_expected:",dev_instance._se_cmd_duration_expected[device][cmd_to_exec] ) command_timeout += dev_instance._se_cmd_duration_expected[device][cmd_to_exec] - print("command:", command_timeout) # device loop end # use the greatest value for the onCommand duration expected. if command_timeout > dev_instance._cmd_duration_expected[cmd_to_exec]: dev_instance._cmd_duration_expected[cmd_to_exec] = command_timeout - dev_instance.dev_logging("Modified the {} command Duration Expected value!!".format(cmd_to_exec), - tango.LogLevel.LOG_INFO) - print("decorator end") + dev_instance.logger.info("Modified the {} command Duration Expected value!!".format(cmd_to_exec)) return f(*args, **kwargs) return input_args_check diff --git a/csp-lmc-common/csp_lmc_common/utils/release.py b/csp-lmc-common/csp_lmc_common/utils/release.py index 198ccab..bf07f0a 100755 --- a/csp-lmc-common/csp_lmc_common/utils/release.py +++ b/csp-lmc-common/csp_lmc_common/utils/release.py @@ -9,8 +9,8 @@ """Release information for Python Package""" -name = """csplmc""" -version = "0.1.0" +name = """csp-lmc-common""" +version = "0.3.0" version_info = version.split(".") description = """SKA CSP.LMC Common Software""" author = "INAF-OAA" diff --git a/csp-lmc-common/docker/.make/Makefile.mk b/csp-lmc-common/docker/.make/Makefile.mk index 2ce814e..c819659 100644 --- a/csp-lmc-common/docker/.make/Makefile.mk +++ b/csp-lmc-common/docker/.make/Makefile.mk @@ -47,7 +47,7 @@ DEFAULT_TAG=$(IMAGE):$(BASE_VERSION) SHELL=/bin/bash -DOCKER_BUILD_CONTEXT=../csp-lmc-common +DOCKER_BUILD_CONTEXT=.. DOCKER_FILE_PATH=Dockerfile .PHONY: pre-build docker-build post-build build release patch-release minor-release major-release tag check-status check-release showver \ diff --git a/csp-lmc-common/docker/Dockerfile b/csp-lmc-common/docker/Dockerfile index cac2a6a..84e8b1d 100644 --- a/csp-lmc-common/docker/Dockerfile +++ b/csp-lmc-common/docker/Dockerfile @@ -5,6 +5,5 @@ FROM nexus.engageska-portugal.pt/ska-docker/ska-python-runtime:0.2.2 AS runtime RUN ipython profile create #install lmc-base-classes USER root -RUN DEBIAN_FRONTEND=noninteractive pip3 install https://nexus.engageska-portugal.pt/repository/pypi/packages/lmcbaseclasses/0.1.3+163bf057/lmcbaseclasses-0.1.3+163bf057.tar.gz - +RUN DEBIAN_FRONTEND=noninteractive pip3 install https://nexus.engageska-portugal.pt/repository/pypi/packages/lmcbaseclasses/0.2.0+6bb55a6e/lmcbaseclasses-0.2.0+6bb55a6e.tar.gz CMD ["/venv/bin/python", "/app/csp-lmc-common/csp-lmc-common/CspMaster.py"] diff --git a/csp-lmc-common/setup.py b/csp-lmc-common/setup.py index e69de29..2d66002 100644 --- a/csp-lmc-common/setup.py +++ b/csp-lmc-common/setup.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# This file is part of the CSP.LMC project +# +# Distributed under the terms of the BSD-3-Clause license. +# See LICENSE.txt for more info. + +import os +import sys +from setuptools import setup, find_packages + +setup_dir = os.path.dirname(os.path.abspath(__file__)) + +# make sure we use latest info from local code +sys.path.insert(0, setup_dir) + +INFO = {} +with open("README.md") as file: + long_description = file.read() + +RELEASE_FILENAME = os.path.join(setup_dir, 'csp_lmc_common','release.py') +exec(open(RELEASE_FILENAME).read(), INFO) + +setup( + name=INFO['name'], + version=INFO['version'], + description=INFO['description'], + author=INFO['author'], + author_email=INFO['author_email'], + packages=find_packages(), + license=INFO['license'], + url=INFO['url'], + long_description=long_description, + keywords="csp lmc ska tango", + platforms="All Platforms", + include_package_data=True, + install_requires = [ + 'pytango >=9.3.1', + 'future' + ], + setup_requires=[ + 'pytest-runner', + 'sphinx', + 'recommonmark' + ], + test_suite="test", + entry_points={'console_scripts':['CspMaster = CspMaster:main']}, + classifiers=[ + "Development Status :: 3 - Alpha", + "Programming Language :: Python :: 3", + "Operating System :: POSIX :: Linux", + "Intended Audience :: Developers", + "License :: Other/Proprietary License", + "Topic::Scientific/Enineering :: Astronomy", + ], + tests_require=[ + 'pytest', + 'pytest-cov', + 'pytest-json-report', + 'pycodestyle', + ], + extras_require={ + }) diff --git a/csp-lmc-mid/csp-lmc-mid/MidCspMasterBase.py b/csp-lmc-mid/csp-lmc-mid/MidCspMasterBase.py index 5c95e8d..b12a97c 100644 --- a/csp-lmc-mid/csp-lmc-mid/MidCspMasterBase.py +++ b/csp-lmc-mid/csp-lmc-mid/MidCspMasterBase.py @@ -39,20 +39,12 @@ from tango import AttrWriteType, DeviceProxy from skabase.SKAMaster import SKAMaster from skabase.auxiliary import utils # PROTECTED REGION END # // CspMaster.additionnal_import - +file_path = os.path.dirname(os.path.abspath(__file__)) +print(file_path) # 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)) + "/../csp-lmc-common/utils" -# TO REMOVE once csp-lmc-common python package has been released! -package_path = os.path.abspath(os.path.join(file_path, os.pardir)) + "/../csp-lmc-common/csp-lmc-common" -print("utilpath:", utils_path) -print("package_path:", package_path) -sys.path.insert(0, utils_path) -sys.path.insert(0, package_path) -import cspcommons -from cspcommons import HealthState, AdminMode -from CspMaster import CspMaster +from csp_lmc_common.utils.cspcommons import HealthState, AdminMode +from csp_lmc_common.CspMaster import CspMaster # PROTECTED REGION END# //CspMaster.add_path __all__ = ["MidCspMasterBase", "main"] @@ -267,15 +259,15 @@ class MidCspMasterBase(with_metaclass(DeviceMeta, CspMaster)): self._receptorsMembership = [0]* self._receptors_maxnum except KeyError as key_err: log_msg = "Error: no key found for {}".format(str(key_err)) - self.dev_logging(log_msg, int(tango.LogLevel.LOG_ERROR)) + self.logger.error(log_msg) except AttributeError as attr_err: log_msg = "Error reading{}: {}".format(str(attr_err.args[0]), attr_err.__doc__) - self.dev_logging(log_msg, int(tango.LogLevel.LOG_ERROR)) + self.logger.error(log_msg) except tango.DevFailed as df: log_msg = "Error: " + str(df.args[0].reason) - self.dev_logging(log_msg, int(tango.LogLevel.LOG_ERROR)) + self.logger.error(log_msg) # --------------- # General methods @@ -351,20 +343,18 @@ class MidCspMasterBase(with_metaclass(DeviceMeta, CspMaster)): # [0] whose length is 1 (not 0) cap_type = [keys for keys,values in self._max_capabilities.items()] - print("cap_type:", cap_type) if not unassigned_receptors: self._available_capabilities["Receptors"] = 0 else: self._available_capabilities["Receptors"] = len(unassigned_receptors) index = 1 for fqdn in self._capability_fqdn: - print("fqdn:", fqdn) if self._is_device_running(fqdn, self._capability_proxy): self._available_capabilities[cap_type[index]] = self._capability_proxy[fqdn].numOfUnassignedIDs index += 1 except tango.DevFailed as df: msg = "Attribute reading failure: {}".format(df.args[0].desc) - self.dev_logging(msg, tango.LogLevel.LOG_ERROR) + self.dev_logger.error(msg) tango.Except.throw_exception("Attribute reading failure", df.args[0].desc, "read_availableCapabilities", @@ -401,7 +391,7 @@ class MidCspMasterBase(with_metaclass(DeviceMeta, CspMaster)): "Command()") except KeyError as key_err: msg = "Can't retrieve the information of key {}".format(key_err) - self.dev_logging(msg, tango.LogLevel.LOG_ERROR) + self.logger.error(msg) tango.Except.throw_exception("Attribute reading failure", msg, "read_receptorMembership", -- GitLab From b2479f2080e655e6e2584b0111f4f00edbe8bd1a Mon Sep 17 00:00:00 2001 From: softir Date: Thu, 16 Jan 2020 15:54:20 +0100 Subject: [PATCH 23/33] AT5-262: renamed utils/release.py ../release.py --- csp-lmc-common/csp_lmc_common/{utils => }/release.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename csp-lmc-common/csp_lmc_common/{utils => }/release.py (100%) diff --git a/csp-lmc-common/csp_lmc_common/utils/release.py b/csp-lmc-common/csp_lmc_common/release.py similarity index 100% rename from csp-lmc-common/csp_lmc_common/utils/release.py rename to csp-lmc-common/csp_lmc_common/release.py -- GitLab From 3ca467d5ffedcc90039dab4099e9273bf8924f6b Mon Sep 17 00:00:00 2001 From: Elisabetta Giani Date: Fri, 17 Jan 2020 07:40:11 -0500 Subject: [PATCH 24/33] AT5-262: Move to new lmcbaseclasses release (>= 0.2.0). Removed unused inherited attributes. --- .../csp_lmc_common/CspCapabilityMonitor.py | 22 +++++------ csp-lmc-common/csp_lmc_common/CspMaster.py | 37 +++++++------------ .../csp_lmc_common/utils/decorators.py | 30 +++++++-------- csp-lmc-common/docker/Dockerfile | 3 +- csp-lmc-common/docker/csp-lmc.yml | 16 +++----- csp-lmc-common/pogo/CspCapabilityMonitor.xmi | 31 ++++++++++------ .../DeviceTestMaster/DeviceTestMaster.py | 2 +- .../DeviceTestMaster/PssMasterSimulator.py | 3 +- 8 files changed, 67 insertions(+), 77 deletions(-) diff --git a/csp-lmc-common/csp_lmc_common/CspCapabilityMonitor.py b/csp-lmc-common/csp_lmc_common/CspCapabilityMonitor.py index c10e819..f15b7d2 100644 --- a/csp-lmc-common/csp_lmc_common/CspCapabilityMonitor.py +++ b/csp-lmc-common/csp_lmc_common/CspCapabilityMonitor.py @@ -35,12 +35,7 @@ from tango import AttrWriteType, PipeWriteType from skabase.SKABaseDevice import SKABaseDevice from skabase.auxiliary import utils -# 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, ObsMode +from .utils.cspcommons import HealthState, AdminMode, ObsState # PROTECTED REGION END # // CspCapabilityMonitor.additionnal_import @@ -153,8 +148,11 @@ class CspCapabilityMonitor(with_metaclass(DeviceMeta,SKABaseDevice)): self._csp_tango_db = tango.Database() attribute_properties = self._csp_tango_db.get_device_attribute_property(self.get_name(), {'adminMode': ['__value']}) - admin_mode_memorized = attribute_properties['adminMode']['__value'] - self._admin_mode = int(admin_mode_memorized[0]) + try: + admin_mode_memorized = attribute_properties['adminMode']['__value'] + self._admin_mode = int(admin_mode_memorized[0]) + except keyError as key_err: + self.logger.info("No {} key found".format(str(key_err))) self._connect_to_capability() self._healthState = HealthState.OK self.set_state(tango.DevState.ON) @@ -235,9 +233,9 @@ class CspCapabilityMonitor(with_metaclass(DeviceMeta,SKABaseDevice)): log_msg = "New value for {} is {}".format(str(evt.attr_name), str(evt.attr_value.value)) except tango.DevFailed as df: - self.dev_logging(str(df.args[0].desc), tango.LogLevel.LOG_ERROR) + self.logger.error(str(df.args[0].desc)) except Exception as except_occurred: - self.dev_logging(str(except_occurred), tango.LogLevel.LOG_ERROR) + self.logger.error(str(except_occurred)) else: for item in evt.errors: # API_EventTimeout: if sub-element device not reachable it transitions @@ -273,12 +271,12 @@ class CspCapabilityMonitor(with_metaclass(DeviceMeta,SKABaseDevice)): "on device {}. Reason: {}".format(event_id, fqdn, key_err)) - self.dev_logging(msg, tango.LogLevel.LOG_ERROR) + self.logger.error(msg) # remove the events id from the list for k in _event_id[fqdn].remove(k): if self._event_id[fqdn]: msg = "Still subscribed events: {}".format(self._event_id) - self.dev_logging(msg, tango.LogLevel.LOG_WARN) + self.logger.warn(msg) else: # remove the dictionary element self._event_id.pop(fqdn) diff --git a/csp-lmc-common/csp_lmc_common/CspMaster.py b/csp-lmc-common/csp_lmc_common/CspMaster.py index 26e7eb7..91669f4 100644 --- a/csp-lmc-common/csp_lmc_common/CspMaster.py +++ b/csp-lmc-common/csp_lmc_common/CspMaster.py @@ -320,8 +320,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # build the list of sub-elements with State ON/STANDBY and ONLINE/MAINTENANCE state_fqdn = [fqdn for fqdn in admin_fqdn if self._se_state[fqdn] in [tango.DevState.ON, tango.DevState.STANDBY]] - # build the list with the healthState of the devices contributing to - # determine the CSP healthState + # build the list with the healthState of ONLINE/MAINTENANCE devices health_list = [self._se_health_state[fqdn] for fqdn in state_fqdn] if self.CspCbf in admin_fqdn: @@ -510,7 +509,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): self.logger.info("measured duration:", se_cmd_duration_measured[device][cmd_name]) self._se_cmd_progress[device][cmd_name] = 100 # command success: exit from the wait loop and issue the command - # on the next device of the list + # on the next device in the list break # check for other sub-element device State values if self._se_state[device] in [tango.DevState.FAULT, tango.DevState.ALARM]: @@ -525,10 +524,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # NOTE: as per the "SKA Control Guidelines", a sub-element shall not throw an # exception if the sub-element is already in the requested final state or if the # command is already running. - # A different behaviour causes a wrong behavior of the current function. If for - # example the sub-element raises an exception if the device is already ON when On - # command is issued, the cmd_end_cb calbback is invoked and the alarm flag is set: - # the device results failed, but this is not true. + # A different implementation could cause a wrong behavior of the current function. if self._se_cmd_execution_state[device][cmd_name] == CmdExecState.FAILED: # execution ended for this sub-element, skip to the next one num_of_failed_device += 1 @@ -639,7 +635,11 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): self.TimingBeamsMonitor, self.VlbiBeamsMonitor] for fqdn in self._capability_fqdn: - self._capability_proxy[fqdn] = tango.DeviceProxy(fqdn) + try: + self._capability_proxy[fqdn] = tango.DeviceProxy(fqdn) + except tango.Except as tango_err: + self.logger.warn(tango_err.args[0].desc) + # PROTECTED REGION END # // CspMaster.class_protected_methods # ----------------- @@ -1167,15 +1167,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # Note: a memorized attribute has defined the attribute # property '__value' attribute_properties = csp_tango_db.get_device_attribute_property(self.get_name(), - {'adminMode': ['__value'], - 'storageLoggingLevel': ['__value'], - 'elementLoggingLevel': ['__value'], - 'centralLoggingLevel': ['__value'],}) - - # set storage and element logging level - self._storage_logging_level = int(tango.LogLevel.LOG_INFO) - self._element_logging_level = int(tango.LogLevel.LOG_INFO) - self._central_logging_level = int(tango.LogLevel.LOG_WARN) + {'adminMode': ['__value']}) # set init values for the CSP Element and Sub-element SCM states self.set_state(tango.DevState.INIT) self._health_state = HealthState.OK @@ -1196,7 +1188,6 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): if key == '__value'} try: self._admin_mode = memorized_attr_dict['adminMode'] - self._storage_logging_level = memorized_attr_dict['storageLoggingLevel'] except KeyError as key_err: self.logger.info("Key {} not found".format(key_err)) @@ -1229,9 +1220,9 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): self.logger.info(msg) # _se_proxies: the sub-element proxies - # implementes s a dictionary: + # implemented as dictionary: # keys: sub-element FQDN - # values: devic eproxy + # values: device proxy self._se_proxies = {} # Nested default dictionary with list of event ids/sub-element. Need to @@ -1344,8 +1335,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): self._connect_to_subelements() # start CSP Capbilities monitoring self._connect_capabilities_monitor() - - + # to use the push model in command_inout_asynch (the one with the callback parameter), # change the global TANGO model to PUSH_CALLBACK. apiutil = tango.ApiUtil.instance() @@ -1868,8 +1858,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): if self.get_state() not in [tango.DevState.STANDBY, tango.DevState.ON]: return False return True - - + @command( dtype_in='DevVarStringArray', doc_in=("If the array length is 0, the command applies to the whole CSP Element. " diff --git a/csp-lmc-common/csp_lmc_common/utils/decorators.py b/csp-lmc-common/csp_lmc_common/utils/decorators.py index 9f8d6c4..0bac189 100644 --- a/csp-lmc-common/csp_lmc_common/utils/decorators.py +++ b/csp-lmc-common/csp_lmc_common/utils/decorators.py @@ -22,13 +22,11 @@ class AdminModeCheck(object): # Check the AdminMode value: the command is callable only if the # the administration mode is ONLINE or MAINTENACE - 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 + if dev_instance._admin_mode not in [AdminMode.ONLINE, AdminMode.MAINTENANCE]: + # NOTE: when adminMode is not ONLINE/MAINTENANCE the device State has to be + # DISABLE # Add only a check on State value to log a warning message if it # is different from DISABLE - #msg_args = (cmd_to_execute, dev_instance.get_state(), dev_instance._admin_mode.name) msg_args = (self._cmd_to_execute, dev_instance.get_state(), AdminMode(dev_instance._admin_mode).name) if dev_instance.get_state() != tango.DevState.DISABLE: dev_instance.logger.warn("Command {}: incoherent device State {} " @@ -93,8 +91,8 @@ 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 + detecting if the corresponding servers are running and the + administrative mode of the devices has the right value to execute a command (i.e ONLINE/MAINTENACE) The *decorator function* accepts some parameters as input to customize its functionality @@ -145,9 +143,9 @@ class CmdInputArgsCheck(object): # with command execution dev_instance.logger.warn("Too many input parameters") dev_instance.logger.debug("CmdInputArgsCheck: devices {} to check:".format(device_list)) - # If a sub-element device is already executing a power command throws an - # exception only if the requested command is different from the one - # is already running (power commands have to be executed sequentially). + # If a sub-element device is already executing a power command, an exception is + # thown only when the requested command is different from the one + # already running (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? @@ -179,10 +177,11 @@ class CmdInputArgsCheck(object): # check for devices that are not ONLINE/MAINTENANCE device_to_remove = [] for device in device_list: - print("processing device:", device) + self.logger.debug("CmdInputArgsCheack-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 + # attribute is not ONLINE or MAINTENANCE: + # - schedule the device for removing from the input arg list + # - skip to next device and if (not dev_instance._is_device_running(device, dev_instance._se_proxies) or dev_instance._se_admin_mode[device] not in [AdminMode.ONLINE, AdminMode.MAINTENANCE]): @@ -239,9 +238,8 @@ class CmdInputArgsCheck(object): except tango.DevFailed as tango_err: dev_instance.logger.info(tango_err.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 + # note: if the xxxDurationExpected attribute read fails, it is used the default value + # (xxxDurationExpected initialized as defaultdict) command_timeout += dev_instance._se_cmd_duration_expected[device][cmd_to_exec] # device loop end # use the greatest value for the onCommand duration expected. diff --git a/csp-lmc-common/docker/Dockerfile b/csp-lmc-common/docker/Dockerfile index 84e8b1d..429d8ad 100644 --- a/csp-lmc-common/docker/Dockerfile +++ b/csp-lmc-common/docker/Dockerfile @@ -6,4 +6,5 @@ RUN ipython profile create #install lmc-base-classes USER root RUN DEBIAN_FRONTEND=noninteractive pip3 install https://nexus.engageska-portugal.pt/repository/pypi/packages/lmcbaseclasses/0.2.0+6bb55a6e/lmcbaseclasses-0.2.0+6bb55a6e.tar.gz -CMD ["/venv/bin/python", "/app/csp-lmc-common/csp-lmc-common/CspMaster.py"] + +CMD ["/venv/bin/python", "-m"," csp_lmc_common.CspMaster" ] diff --git a/csp-lmc-common/docker/csp-lmc.yml b/csp-lmc-common/docker/csp-lmc.yml index f7577a0..7908280 100644 --- a/csp-lmc-common/docker/csp-lmc.yml +++ b/csp-lmc-common/docker/csp-lmc.yml @@ -40,7 +40,7 @@ services: command: > sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=30 --strict -- retry --max=5 -- tango_admin --ping-device common/elt/master &&\ - /venv/bin/python /app/csp-lmc-common/CspSubarray.py comsub1" + /venv/bin/python /app/csp_lmc_common/CspSubarray.py comsub1" volumes_from: - rsyslog-csplmc:rw @@ -57,7 +57,7 @@ services: command: > sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=30 --strict -- retry --max=5 -- tango_admin --ping-device common/elt/master &&\ - /venv/bin/python /app/csp-lmc-common/CspSubarray.py comsub2" + /venv/bin/python /app/csp_lmc_common/CspSubarray.py comsub2" volumes_from: - rsyslog-csplmc:rw @@ -74,11 +74,7 @@ services: command: > sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=30 --strict -- retry --max=5 -- tango_admin --ping-device mid_csp_cbf/sub_elt/master &&\ - retry --max=5 -- tango_admin --ping-device common/elt/search_beams_monitor &&\ - retry --max=5 -- tango_admin --ping-device common/elt/timing_beams_monitor &&\ - retry --max=5 -- tango_admin --ping-device common/elt/vlbi_beams_monitor &&\ - retry --max=5 -- tango_admin --ping-device mid_csp_cbf/sub_elt/master &&\ - /venv/bin/python /app/csp-lmc-common/CspMaster.py commaster" + /venv/bin/python -m csp_lmc_common.CspMaster commaster" volumes_from: - rsyslog-csplmc:rw @@ -93,7 +89,7 @@ services: - TANGO_HOST=${TANGO_HOST} command: > sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=30 --strict -- - /venv/bin/python /app/csp-lmc-common/CspCapabilityMonitor.py searchbeams" + /venv/bin/python -m csp_lmc_common.CspCapabilityMonitor searchbeams" volumes_from: - rsyslog-csplmc:rw @@ -108,7 +104,7 @@ services: - TANGO_HOST=${TANGO_HOST} command: > sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=30 --strict -- - /venv/bin/python /app/csp-lmc-common/CspCapabilityMonitor.py timingbeams" + /venv/bin/python -m csp_lmc_common.CspCapabilityMonitor timingbeams" volumes_from: - rsyslog-csplmc:rw @@ -123,7 +119,7 @@ services: - TANGO_HOST=${TANGO_HOST} command: > sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=30 --strict -- - /venv/bin/python /app/csp-lmc-common/CspCapabilityMonitor.py vlbibeams" + /venv/bin/python -m csp_lmc_common.CspCapabilityMonitor vlbibeams" volumes_from: - rsyslog-csplmc:rw diff --git a/csp-lmc-common/pogo/CspCapabilityMonitor.xmi b/csp-lmc-common/pogo/CspCapabilityMonitor.xmi index eacbf01..80ebee4 100644 --- a/csp-lmc-common/pogo/CspCapabilityMonitor.xmi +++ b/csp-lmc-common/pogo/CspCapabilityMonitor.xmi @@ -15,22 +15,19 @@ - - - - - - - + + + - - + + - localhost + 4 - + - + + console::cout @@ -126,6 +123,11 @@ + + + + + @@ -182,6 +184,11 @@ + + + + + diff --git a/csp-lmc-common/simulators/DeviceTestMaster/DeviceTestMaster.py b/csp-lmc-common/simulators/DeviceTestMaster/DeviceTestMaster.py index b84211c..90c5d22 100644 --- a/csp-lmc-common/simulators/DeviceTestMaster/DeviceTestMaster.py +++ b/csp-lmc-common/simulators/DeviceTestMaster/DeviceTestMaster.py @@ -37,7 +37,7 @@ from tango import AttrWriteType, PipeWriteType # PROTECTED REGION ID(DeviceTestMaster.additionnal_import) ENABLED START # from future.utils import with_metaclass import threading -from cspcommons import HealthState, AdminMode +from csp_lmc_common.utils.cspcommons import HealthState, AdminMode from skabase.SKAMaster.SKAMaster import SKAMaster # PROTECTED REGION END # // DeviceTestMaster.additionnal_import diff --git a/csp-lmc-common/simulators/DeviceTestMaster/PssMasterSimulator.py b/csp-lmc-common/simulators/DeviceTestMaster/PssMasterSimulator.py index a69157e..407dc38 100644 --- a/csp-lmc-common/simulators/DeviceTestMaster/PssMasterSimulator.py +++ b/csp-lmc-common/simulators/DeviceTestMaster/PssMasterSimulator.py @@ -19,7 +19,7 @@ import time file_path = os.path.dirname(os.path.abspath(__file__)) print(file_path) -module_path = os.path.abspath(os.path.join(file_path, os.pardir)) + "/../utils" +module_path = os.path.abspath(os.path.join(file_path, os.pardir)) + "/../csp_lmc_common/utils" print(module_path) sys.path.insert(0, module_path) @@ -37,6 +37,7 @@ from tango import AttrWriteType, PipeWriteType from future.utils import with_metaclass import threading from cspcommons import HealthState, AdminMode +#from csp_lmc_common.util.cspcommons import HealthState, AdminMode from DeviceTestMaster import DeviceTestMaster # PROTECTED REGION END # // DeviceTestMaster.additionnal_import -- GitLab From 86cc8cd879487bf1d709b7442e2e60bada9b3508 Mon Sep 17 00:00:00 2001 From: Elisabetta Giani Date: Fri, 17 Jan 2020 07:41:57 -0500 Subject: [PATCH 25/33] AT5-262: Multiclass server for MID CspMaster. Added docker and docker-compose files to build the csp-lmc-mid image. Added device simulator for PstMaster. --- .../DeviceTestMaster/PstMasterSimulator.py | 109 +++ csp-lmc-mid/Pipfile | 35 + csp-lmc-mid/csp-lmc-mid/MidCspMaster.py | 16 + csp-lmc-mid/csp-lmc-mid/MidCspMasterBase.py | 1 - .../docker/.make/.make-release-support | 106 +++ .../.make/.make-release-support.katversion | 106 +++ csp-lmc-mid/docker/.make/Makefile.mk | 141 ++++ csp-lmc-mid/docker/.release | 4 + csp-lmc-mid/docker/Dockerfile | 11 + csp-lmc-mid/docker/Makefile | 198 ++++++ csp-lmc-mid/docker/csp-lmc.yml | 144 ++++ csp-lmc-mid/docker/csp-tangodb.yml | 50 ++ csp-lmc-mid/docker/csplmc_dsconfig.json | 645 ++++++++++++++++++ csp-lmc-mid/docker/mid-cbf-mcs.yml | 288 ++++++++ csp-lmc-mid/docker/scripts/kat-get-version.py | 44 ++ 15 files changed, 1897 insertions(+), 1 deletion(-) create mode 100644 csp-lmc-common/simulators/DeviceTestMaster/PstMasterSimulator.py create mode 100644 csp-lmc-mid/Pipfile create mode 100644 csp-lmc-mid/csp-lmc-mid/MidCspMaster.py create mode 100644 csp-lmc-mid/docker/.make/.make-release-support create mode 100644 csp-lmc-mid/docker/.make/.make-release-support.katversion create mode 100644 csp-lmc-mid/docker/.make/Makefile.mk create mode 100644 csp-lmc-mid/docker/.release create mode 100644 csp-lmc-mid/docker/Dockerfile create mode 100644 csp-lmc-mid/docker/Makefile create mode 100644 csp-lmc-mid/docker/csp-lmc.yml create mode 100644 csp-lmc-mid/docker/csp-tangodb.yml create mode 100644 csp-lmc-mid/docker/csplmc_dsconfig.json create mode 100644 csp-lmc-mid/docker/mid-cbf-mcs.yml create mode 100755 csp-lmc-mid/docker/scripts/kat-get-version.py diff --git a/csp-lmc-common/simulators/DeviceTestMaster/PstMasterSimulator.py b/csp-lmc-common/simulators/DeviceTestMaster/PstMasterSimulator.py new file mode 100644 index 0000000..84377a5 --- /dev/null +++ b/csp-lmc-common/simulators/DeviceTestMaster/PstMasterSimulator.py @@ -0,0 +1,109 @@ +# -*- coding: utf-8 -*- +# +# This file is part of the PssMasterSimulator project +# +# INAF-SKA Telescope +# +# Distributed under the terms of the GPL license. +# See LICENSE.txt for more info. + +""" PssMaster Class simulator + +""" + +from __future__ import absolute_import +import sys +from collections import defaultdict +import os +import time + +file_path = os.path.dirname(os.path.abspath(__file__)) +print(file_path) +module_path = os.path.abspath(os.path.join(file_path, os.pardir)) + "/../utils" +print(module_path) +sys.path.insert(0, module_path) + +# 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 +# Additional import +# PROTECTED REGION ID(DeviceTestMaster.additionnal_import) ENABLED START # +from future.utils import with_metaclass +import threading +from csp_lmc_common.utils.cspcommons import HealthState, AdminMode +from DeviceTestMaster import DeviceTestMaster +# PROTECTED REGION END # // DeviceTestMaster.additionnal_import + +__all__ = ["PstMasterSimulator", "main"] + + +class PssMasterSimulator(with_metaclass(DeviceMeta,DeviceTestMaster)): + """ + + **Properties:** + + - Device Property + """ + # PROTECTED REGION ID(PssMasterSimulator.class_variable) ENABLED START # + # PROTECTED REGION END # // PssMasterSimulator.class_variable + + # ----------------- + # Device Properties + # ----------------- + + # ---------- + # Attributes + # ---------- + + + # --------------- + # General methods + # --------------- + + def init_device(self): + """Initialises the attributes and properties of the PssMasterSimulator.""" + DeviceTestMaster.init_device(self) + # PROTECTED REGION ID(PssMasterSimulator.init_device) ENABLED START # + # PROTECTED REGION END # // PssMasterSimulator.init_device + + def always_executed_hook(self): + """Method always executed before any TANGO command is executed.""" + # PROTECTED REGION ID(PssMasterSimulator.always_executed_hook) ENABLED START # + # PROTECTED REGION END # // PssMasterSimulator.always_executed_hook + + def delete_device(self): + """Hook to delete resources allocated in init_device. + + This method allows for any memory or other resources allocated in the + init_device method to be released. This method is called by the device + destructor and by the device Init command. + """ + # PROTECTED REGION ID(PssMasterSimulator.delete_device) ENABLED START # + # PROTECTED REGION END # // PssMasterSimulator.delete_device + # ------------------ + # Attributes methods + # ------------------ + + + # -------- + # Commands + # -------- + +# ---------- +# Run server +# ---------- + + +def main(args=None, **kwargs): + # PROTECTED REGION ID(PssMasterSimulator.main) ENABLED START # + return run((PssMasterSimulator,), args=args, **kwargs) + # PROTECTED REGION END # // PssMasterSimulator.main + +if __name__ == '__main__': + main() diff --git a/csp-lmc-mid/Pipfile b/csp-lmc-mid/Pipfile new file mode 100644 index 0000000..358cd26 --- /dev/null +++ b/csp-lmc-mid/Pipfile @@ -0,0 +1,35 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +# numpy and pytango versions must match those in the ska-python-builder image, +# otherwise pytango will be recompiled. +numpy = "==1.17.2" +pytango = "==9.3.1" +# itango is added to make it easier to exercise the device in a CLI session, +# but it's not mandatory. If you remove itango, you should also remove the +# 'RUN ipython profile create' line from Dockerfile. +itango = "*" +# If you want to debug devices running in containers, add pydevd to the dependencies +# pydevd = "*" + +[dev-packages] +docutils = "*" +MarkupSafe = "*" +Pygments = "*" +pylint = "*" +pytest = "*" +pytest-cov = "*" +pytest-pylint = "*" +pytest-json-report = "*" +python-dotenv = ">=0.5.1" +ptvsd = "*" +Sphinx = "*" +sphinx_rtd_theme = "*" +sphinx-autobuild = "*" +sphinxcontrib-websupport = "*" + +[requires] +python_version = "3" diff --git a/csp-lmc-mid/csp-lmc-mid/MidCspMaster.py b/csp-lmc-mid/csp-lmc-mid/MidCspMaster.py new file mode 100644 index 0000000..7d77633 --- /dev/null +++ b/csp-lmc-mid/csp-lmc-mid/MidCspMaster.py @@ -0,0 +1,16 @@ +from tango.server import run +import os +import sys +#file_path = os.path.dirname(os.path.abspath(__file__)) +#pkg_path = os.path.abspath(os.path.join(file_path, "./")) +#sys.path.insert(0, pkg_path) + +from MidCspMasterBase import MidCspMasterBase +from csp_lmc_common.CspCapabilityMonitor import CspCapabilityMonitor + +def main(args=None, **kwargs): + return run(classes=(MidCspMasterBase, CspCapabilityMonitor), args=args, **kwargs) + + +if __name__ == '__main__': + main() diff --git a/csp-lmc-mid/csp-lmc-mid/MidCspMasterBase.py b/csp-lmc-mid/csp-lmc-mid/MidCspMasterBase.py index b12a97c..008216e 100644 --- a/csp-lmc-mid/csp-lmc-mid/MidCspMasterBase.py +++ b/csp-lmc-mid/csp-lmc-mid/MidCspMasterBase.py @@ -60,7 +60,6 @@ class MidCspMasterBase(with_metaclass(DeviceMeta, CspMaster)): - Device Property """ - __metaclass__ = DeviceMeta # PROTECTED REGION ID(MidCspMasterBase.class_variable) ENABLED START # # PROTECTED REGION END # // MidCspMasterBase.class_variable diff --git a/csp-lmc-mid/docker/.make/.make-release-support b/csp-lmc-mid/docker/.make/.make-release-support new file mode 100644 index 0000000..f1f3a2c --- /dev/null +++ b/csp-lmc-mid/docker/.make/.make-release-support @@ -0,0 +1,106 @@ +#!/bin/bash +# +# Copyright 2015 Xebia Nederland B.V. +# Modifications copyright 2019 SKA Organisation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +function hasChanges() { + test -n "$(git status -s .)" +} + +function getRelease() { + awk -F= '/^release=/{print $2}' .release +} + +function getBaseTag() { + sed -n -e "s/^tag=\(.*\)$(getRelease)\$/\1/p" .release +} + +function getTag() { + if [ -z "$1" ] ; then + awk -F= '/^tag/{print $2}' .release + else + echo "$(getBaseTag)$1" + fi +} + +function setRelease() { + if [ -n "$1" ] ; then + sed -i.x -e "s/^tag=.*/tag=$(getTag $1)/" .release + sed -i.x -e "s/^release=.*/release=$1/g" .release + rm -f .release.x + runPreTagCommand "$1" + else + echo "ERROR: missing release version parameter " >&2 + return 1 + fi +} + +function runPreTagCommand() { + if [ -n "$1" ] ; then + COMMAND=$(sed -n -e "s/@@RELEASE@@/$1/g" -e 's/^pre_tag_command=\(.*\)/\1/p' .release) + if [ -n "$COMMAND" ] ; then + if ! OUTPUT=$(bash -c "$COMMAND" 2>&1) ; then echo $OUTPUT >&2 && exit 1 ; fi + fi + else + echo "ERROR: missing release version parameter " >&2 + return 1 + fi +} + +function tagExists() { + tag=${1:-$(getTag)} + test -n "$tag" && test -n "$(git tag | grep "^$tag\$")" +} + +function differsFromRelease() { + tag=$(getTag) + ! tagExists $tag || test -n "$(git diff --shortstat -r $tag .)" +} + +function getVersion() { + result=$(getRelease) + + if differsFromRelease; then + result="$result-$(git log -n 1 --format=%h .)" + fi + + if hasChanges ; then + result="$result-dirty" + fi + echo $result +} + +function nextPatchLevel() { + version=${1:-$(getRelease)} + major_and_minor=$(echo $version | cut -d. -f1,2) + patch=$(echo $version | cut -d. -f3) + version=$(printf "%s.%d" $major_and_minor $(($patch + 1))) + echo $version +} + +function nextMinorLevel() { + version=${1:-$(getRelease)} + major=$(echo $version | cut -d. -f1); + minor=$(echo $version | cut -d. -f2); + version=$(printf "%d.%d.0" $major $(($minor + 1))) ; + echo $version +} + +function nextMajorLevel() { + version=${1:-$(getRelease)} + major=$(echo $version | cut -d. -f1); + version=$(printf "%d.0.0" $(($major + 1))) + echo $version +} diff --git a/csp-lmc-mid/docker/.make/.make-release-support.katversion b/csp-lmc-mid/docker/.make/.make-release-support.katversion new file mode 100644 index 0000000..f1f3a2c --- /dev/null +++ b/csp-lmc-mid/docker/.make/.make-release-support.katversion @@ -0,0 +1,106 @@ +#!/bin/bash +# +# Copyright 2015 Xebia Nederland B.V. +# Modifications copyright 2019 SKA Organisation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +function hasChanges() { + test -n "$(git status -s .)" +} + +function getRelease() { + awk -F= '/^release=/{print $2}' .release +} + +function getBaseTag() { + sed -n -e "s/^tag=\(.*\)$(getRelease)\$/\1/p" .release +} + +function getTag() { + if [ -z "$1" ] ; then + awk -F= '/^tag/{print $2}' .release + else + echo "$(getBaseTag)$1" + fi +} + +function setRelease() { + if [ -n "$1" ] ; then + sed -i.x -e "s/^tag=.*/tag=$(getTag $1)/" .release + sed -i.x -e "s/^release=.*/release=$1/g" .release + rm -f .release.x + runPreTagCommand "$1" + else + echo "ERROR: missing release version parameter " >&2 + return 1 + fi +} + +function runPreTagCommand() { + if [ -n "$1" ] ; then + COMMAND=$(sed -n -e "s/@@RELEASE@@/$1/g" -e 's/^pre_tag_command=\(.*\)/\1/p' .release) + if [ -n "$COMMAND" ] ; then + if ! OUTPUT=$(bash -c "$COMMAND" 2>&1) ; then echo $OUTPUT >&2 && exit 1 ; fi + fi + else + echo "ERROR: missing release version parameter " >&2 + return 1 + fi +} + +function tagExists() { + tag=${1:-$(getTag)} + test -n "$tag" && test -n "$(git tag | grep "^$tag\$")" +} + +function differsFromRelease() { + tag=$(getTag) + ! tagExists $tag || test -n "$(git diff --shortstat -r $tag .)" +} + +function getVersion() { + result=$(getRelease) + + if differsFromRelease; then + result="$result-$(git log -n 1 --format=%h .)" + fi + + if hasChanges ; then + result="$result-dirty" + fi + echo $result +} + +function nextPatchLevel() { + version=${1:-$(getRelease)} + major_and_minor=$(echo $version | cut -d. -f1,2) + patch=$(echo $version | cut -d. -f3) + version=$(printf "%s.%d" $major_and_minor $(($patch + 1))) + echo $version +} + +function nextMinorLevel() { + version=${1:-$(getRelease)} + major=$(echo $version | cut -d. -f1); + minor=$(echo $version | cut -d. -f2); + version=$(printf "%d.%d.0" $major $(($minor + 1))) ; + echo $version +} + +function nextMajorLevel() { + version=${1:-$(getRelease)} + major=$(echo $version | cut -d. -f1); + version=$(printf "%d.0.0" $(($major + 1))) + echo $version +} diff --git a/csp-lmc-mid/docker/.make/Makefile.mk b/csp-lmc-mid/docker/.make/Makefile.mk new file mode 100644 index 0000000..d2cb631 --- /dev/null +++ b/csp-lmc-mid/docker/.make/Makefile.mk @@ -0,0 +1,141 @@ +# +# Copyright 2015 Xebia Nederland B.V. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +ifeq ($(strip $(PROJECT)),) + NAME=$(shell basename $(CURDIR)) +else + NAME=$(PROJECT) +endif + +RELEASE_SUPPORT := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST))))/.make-release-support + +ifeq ($(strip $(DOCKER_REGISTRY_HOST)),) + DOCKER_REGISTRY_HOST = nexus.engageska-portugal.pt +endif + +ifeq ($(strip $(DOCKER_REGISTRY_USER)),) + DOCKER_REGISTRY_USER = ska-docker +endif + +IMAGE=$(DOCKER_REGISTRY_HOST)/$(DOCKER_REGISTRY_USER)/$(NAME) + +#VERSION = release version + git sha +VERSION=$(shell . $(RELEASE_SUPPORT) ; getVersion) + +#BASE_VERSION +BASE_VERSION=$(shell . $(RELEASE_SUPPORT) ; getRelease) + +#TAG = project name + release version +TAG=$(shell . $(RELEASE_SUPPORT); getTag) + +#DEFAULT_TAG = image name + BASE_VERSION +DEFAULT_TAG=$(IMAGE):$(BASE_VERSION) + + +SHELL=/bin/bash + +DOCKER_BUILD_CONTEXT=.. +DOCKER_FILE_PATH=Dockerfile + +.PHONY: pre-build docker-build post-build build release patch-release minor-release major-release tag check-status check-release showver \ + push pre-push do-push post-push + +build: pre-build docker-build post-build ## build the application image + +pre-build: + +post-build: + +pre-push: + +post-push: + +#docker-build: .release +docker-build: + @echo "$(PWD)" + rm $(PWD)/.release + $(PWD)/scripts/kat-get-version.py > .release + @echo "Building image: $(IMAGE):$(VERSION)" + @echo "NAME: $(NAME)" + docker build $(DOCKER_BUILD_ARGS) -t $(IMAGE):$(VERSION) $(DOCKER_BUILD_CONTEXT) -f $(DOCKER_FILE_PATH) --build-arg DOCKER_REGISTRY_HOST=$(DOCKER_REGISTRY_HOST) --build-arg DOCKER_REGISTRY_USER=$(DOCKER_REGISTRY_USER) + @DOCKER_MAJOR=$(shell docker -v | sed -e 's/.*version //' -e 's/,.*//' | cut -d\. -f1) ; \ + DOCKER_MINOR=$(shell docker -v | sed -e 's/.*version //' -e 's/,.*//' | cut -d\. -f2) ; \ + if [ $$DOCKER_MAJOR -eq 1 ] && [ $$DOCKER_MINOR -lt 10 ] ; then \ + echo docker tag -f $(IMAGE):$(VERSION) $(IMAGE):latest ;\ + docker tag -f $(IMAGE):$(VERSION) $(IMAGE):latest ;\ + else \ + echo docker tag $(IMAGE):$(VERSION) $(IMAGE):latest ;\ + docker tag $(IMAGE):$(VERSION) $(IMAGE):latest ; \ + fi + +release: check-status check-release build push + +push: pre-push do-push post-push ## push the image to the Docker registry + +do-push: ## Push the image tagged as $(IMAGE):$(VERSION) and $(DEFAULT_TAG) + @echo -e "Tagging: $(IMAGE):$(VERSION) -> $(DEFAULT_TAG)" + docker tag $(IMAGE):$(VERSION) $(DEFAULT_TAG) + @echo -e "Pushing: $(IMAGE):$(VERSION)" + docker push $(IMAGE):$(VERSION) + @echo -e "Pushing: $(DEFAULT_TAG)" + docker push $(DEFAULT_TAG) + +tag_latest: do-push ## Tag the images as latest + @echo "Tagging: $(DEFAULT_TAG) -> $(IMAGE):latest" + @docker tag $(DEFAULT_TAG) $(IMAGE):latest + +push_latest: tag_latest ## Push the image tagged as :latest + @echo "Pushing: $(IMAGE):latest" + @docker push $(IMAGE):latest + +snapshot: build push + +showver: .release + @. $(RELEASE_SUPPORT); getVersion + +bump-patch-release: VERSION := $(shell . $(RELEASE_SUPPORT); nextPatchLevel) +bump-patch-release: .release tag + +bump-minor-release: VERSION := $(shell . $(RELEASE_SUPPORT); nextMinorLevel) +bump-minor-release: .release tag + +bump-major-release: VERSION := $(shell . $(RELEASE_SUPPORT); nextMajorLevel) +bump-major-release: .release tag + +patch-release: tag-patch-release release + @echo $(VERSION) + +minor-release: tag-minor-release release + @echo $(VERSION) + +major-release: tag-major-release release + @echo $(VERSION) + +tag: TAG=$(shell . $(RELEASE_SUPPORT); getTag $(VERSION)) +tag: check-status +# @. $(RELEASE_SUPPORT) ; ! tagExists $(TAG) || (echo "ERROR: tag $(TAG) for version $(VERSION) already tagged in git" >&2 && exit 1) ; + @. $(RELEASE_SUPPORT) ; setRelease $(VERSION) +# git add . +# git commit -m "bumped to version $(VERSION)" ; +# git tag $(TAG) ; +# @ if [ -n "$(shell git remote -v)" ] ; then git push --tags ; else echo 'no remote to push tags to' ; fi + +check-status: + @. $(RELEASE_SUPPORT) ; ! hasChanges || (echo "ERROR: there are still outstanding changes" >&2 && exit 1) ; + +check-release: .release + @. $(RELEASE_SUPPORT) ; tagExists $(TAG) || (echo "ERROR: version not yet tagged in git. make [minor,major,patch]-release." >&2 && exit 1) ; + @. $(RELEASE_SUPPORT) ; ! differsFromRelease $(TAG) || (echo "ERROR: current directory differs from tagged $(TAG). make [minor,major,patch]-release." ; exit 1) diff --git a/csp-lmc-mid/docker/.release b/csp-lmc-mid/docker/.release new file mode 100644 index 0000000..f3edec2 --- /dev/null +++ b/csp-lmc-mid/docker/.release @@ -0,0 +1,4 @@ +UFFA +0.1.dev28+at5.262.b2479f2.dirty +AT5.262 +None diff --git a/csp-lmc-mid/docker/Dockerfile b/csp-lmc-mid/docker/Dockerfile new file mode 100644 index 0000000..e6f3771 --- /dev/null +++ b/csp-lmc-mid/docker/Dockerfile @@ -0,0 +1,11 @@ +FROM nexus.engageska-portugal.pt/ska-docker/ska-python-buildenv:0.2.2 AS buildenv +FROM nexus.engageska-portugal.pt/ska-docker/ska-python-runtime:0.2.2 AS runtime + +# create ipython profile to so that itango doesn't fail if ipython hasn't run yet +RUN ipython profile create +#install lmc-base-classes +USER root +RUN DEBIAN_FRONTEND=noninteractive pip3 install https://nexus.engageska-portugal.pt/repository/pypi/packages/lmcbaseclasses/0.2.0+6bb55a6e/lmcbaseclasses-0.2.0+6bb55a6e.tar.gz + +#CMD ["/venv/bin/python -m"," csp_lmc_common.CspMaster" ] +CMD ["/bin/bash"] diff --git a/csp-lmc-mid/docker/Makefile b/csp-lmc-mid/docker/Makefile new file mode 100644 index 0000000..47075be --- /dev/null +++ b/csp-lmc-mid/docker/Makefile @@ -0,0 +1,198 @@ +# +# Project makefile for a Tango project. You should normally only need to modify +# DOCKER_REGISTRY_USER and PROJECT below. +# + +# +# DOCKER_REGISTRY_HOST, DOCKER_REGISTRY_USER and PROJECT are combined to define +# the Docker tag for this project. The definition below inherits the standard +# value for DOCKER_REGISTRY_HOST (=rnexus.engageska-portugal.pt) and overwrites +# DOCKER_REGISTRY_USER and PROJECT to give a final Docker tag of +# nexus.engageska-portugal.pt/tango-example/csplmc +# + +DOCKER_REGISTRY_USER:=ska-docker +PROJECT = mid-csp-lmc + +# +# include makefile to pick up the standard Make targets, e.g., 'make build' +# build, 'make push' docker push procedure, etc. The other Make targets +# ('make interactive', 'make test', etc.) are defined in this file. +# +include .make/Makefile.mk + +# +# IMAGE_TO_TEST defines the tag of the Docker image to test +# +IMAGE_TO_TEST = $(DOCKER_REGISTRY_HOST)/$(DOCKER_REGISTRY_USER)/$(PROJECT):latest + +# +# CACHE_VOLUME is the name of the Docker volume used to cache eggs and wheels +# used during the test procedure. The volume is not used during the build +# procedure +# +CACHE_VOLUME = $(PROJECT)-test-cache + +# optional docker run-time arguments +DOCKER_RUN_ARGS = + +# +# Never use the network=host mode when running CI jobs, and add extra +# distinguishing identifiers to the network name and container names to +# prevent collisions with jobs from the same project running at the same +# time. +# +ifneq ($(CI_JOB_ID),) +NETWORK_MODE := tangonet-$(CI_JOB_ID) +CONTAINER_NAME_PREFIX := $(PROJECT)-$(CI_JOB_ID)- +else +CONTAINER_NAME_PREFIX := $(PROJECT)- +endif + +COMPOSE_FILES := $(wildcard *.yml) +COMPOSE_FILE_ARGS := $(foreach yml,$(COMPOSE_FILES),-f $(yml)) + +ifeq ($(OS),Windows_NT) + $(error Sorry, Windows is not supported yet) +else + UNAME_S := $(shell uname -s) + ifeq ($(UNAME_S),Linux) + DISPLAY ?= :0.0 + NETWORK_MODE ?= host + XAUTHORITY_MOUNT := /tmp/.X11-unix:/tmp/.X11-unix + XAUTHORITY ?= /hosthome/.Xauthority + # /bin/sh (=dash) does not evaluate 'docker network' conditionals correctly + SHELL := /bin/bash + endif + ifeq ($(UNAME_S),Darwin) + IF_INTERFACE := $(shell netstat -nr | awk '{ if ($$1 ~/default/) { print $$6} }') + DISPLAY := $(shell ifconfig $(IF_INTERFACE) | awk '{ if ($$1 ~/inet$$/) { print $$2} }'):0 + # network_mode = host doesn't work on MacOS, so fix to the internal network + NETWORK_MODE := tangonet + XAUTHORITY_MOUNT := $(HOME):/hosthome:ro + XAUTHORITY := /hosthome/.Xauthority + endif +endif + +# +# When running in network=host mode, point devices at a port on the host +# machine rather than at the container. +# +ifeq ($(NETWORK_MODE),host) +TANGO_HOST := $(shell hostname):10000 +MYSQL_HOST := $(shell hostname):3306 +else +# distinguish the bridge network from others by adding the project name +NETWORK_MODE := $(NETWORK_MODE)-$(PROJECT) +TANGO_HOST := $(CONTAINER_NAME_PREFIX)databaseds:10000 +MYSQL_HOST := $(CONTAINER_NAME_PREFIX)tangodb:3306 +endif + + +DOCKER_COMPOSE_ARGS := DISPLAY=$(DISPLAY) XAUTHORITY=$(XAUTHORITY) TANGO_HOST=$(TANGO_HOST) \ + NETWORK_MODE=$(NETWORK_MODE) XAUTHORITY_MOUNT=$(XAUTHORITY_MOUNT) MYSQL_HOST=$(MYSQL_HOST) \ + DOCKER_REGISTRY_HOST=$(DOCKER_REGISTRY_HOST) DOCKER_REGISTRY_USER=$(DOCKER_REGISTRY_USER) \ + CONTAINER_NAME_PREFIX=$(CONTAINER_NAME_PREFIX) COMPOSE_IGNORE_ORPHANS=true + +# +# Defines a default make target so that help is printed if make is called +# without a target +# +.DEFAULT_GOAL := help + +# +# defines a function to copy the ./test-harness directory into the container +# and then runs the requested make target in the container. The container is: +# +# 1. attached to the network of the docker-compose test system +# 2. uses a persistent volume to cache Python eggs and wheels so that fewer +# downloads are required +# 3. uses a transient volume as a working directory, in which untarred files +# and test output can be written in the container and subsequently copied +# to the host +# +make = tar -c test-harness/ | \ + docker run -i --rm --network=$(NETWORK_MODE) \ + -e TANGO_HOST=$(TANGO_HOST) \ + -v $(CACHE_VOLUME):/home/tango/.cache \ + --volumes-from=$(CONTAINER_NAME_PREFIX)rsyslog-csplmc:rw \ + -v /build -w /build -u tango $(DOCKER_RUN_ARGS) $(IMAGE_TO_TEST) \ + bash -c "sudo chown -R tango:tango /build && \ + tar x --strip-components 1 --warning=all && \ + make TANGO_HOST=$(TANGO_HOST) $1" + +test: DOCKER_RUN_ARGS = --volumes-from=$(BUILD) +test: build up ## test the application + @echo "BUILD: $(BUILD)" + $(INIT_CACHE) + $(call make,test); \ + status=$$?; \ + rm -fr build; \ + #docker-compose $(COMPOSE_FILE_ARGS) logs; + docker cp $(BUILD):/build .; \ + docker rm -f -v $(BUILD); \ + $(MAKE) down; \ + exit $$status + +lint: DOCKER_RUN_ARGS = --volumes-from=$(BUILD) +lint: build up ## lint the application (static code analysis) + $(INIT_CACHE) + $(call make,lint); \ + status=$$?; \ + docker cp $(BUILD):/build .; \ + $(MAKE) down; \ + exit $$status + +pull: ## download the application image + docker pull $(IMAGE_TO_TEST) + +up: build ## start develop/test environment +ifneq ($(NETWORK_MODE),host) + docker network inspect $(NETWORK_MODE) &> /dev/null || ([ $$? -ne 0 ] && docker network create $(NETWORK_MODE)) +endif + #$(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) pull + #to pull only the mid-cbf-mcs image remove comment on row below. + #docker pull $(DOCKER_REGISTRY_HOST)/$(DOCKER_REGISTRY_USER)/mid-cbf-mcs:latest + $(DOCKER_COMPOSE_ARGS) docker-compose -f csp-tangodb.yml up -d + # put a sleep to wait TANGO DB + @sleep 10 + $(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) up + +piplock: build ## overwrite Pipfile.lock with the image version + docker run $(IMAGE_TO_TEST) cat /app/Pipfile.lock > $(CURDIR)/Pipfile.lock + +interactive: up +interactive: ## start an interactive session using the project image (caution: R/W mounts source directory to /app) + docker run --rm -it -p 3000:3000 --name=$(CONTAINER_NAME_PREFIX)dev -e TANGO_HOST=$(TANGO_HOST) --network=$(NETWORK_MODE) \ + -v $(CURDIR):/app $(IMAGE_TO_TEST) /bin/bash + +down: ## stop develop/test environment and any interactive session + docker ps | grep $(CONTAINER_NAME_PREFIX)dev && docker stop $(PROJECT)-dev || true + $(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) down +ifneq ($(NETWORK_MODE),host) + docker network inspect $(NETWORK_MODE) &> /dev/null && ([ $$? -eq 0 ] && docker network rm $(NETWORK_MODE)) || true +endif + +dsconfigdump: up ## dump the entire configuration to the file dsconfig.json + docker exec -it $(CONTAINER_NAME_PREFIX)dsconfigdump python -m dsconfig.dump + docker exec -it $(CONTAINER_NAME_PREFIX)dsconfigdump python -m dsconfig.dump > dsconfig.json + +dsconfigadd: up ## Add a configuration json file (environment variable DSCONFIG_JSON_FILE) to the database + -docker exec -it $(CONTAINER_NAME_PREFIX)dsconfigdump json2tango -u -w -a $(DSCONFIG_JSON_FILE) + +dsconfigcheck: up ## check a json file (environment variable DSCONFIG_JSON_FILE) according to the project lib-maxiv-dsconfig json schema + -docker exec -it $(CONTAINER_NAME_PREFIX)dsconfigdump json2tango -a $(DSCONFIG_JSON_FILE) + +help: ## show this help. + @grep -hE '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' + +.PHONY: all test up down help + +# Creates Docker volume for use as a cache, if it doesn't exist already +INIT_CACHE = \ + docker volume ls | grep $(CACHE_VOLUME) || \ + docker create --name $(CACHE_VOLUME) -v $(CACHE_VOLUME):/cache $(IMAGE_TO_TEST) + +# http://cakoose.com/wiki/gnu_make_thunks +BUILD_GEN = $(shell docker create -v /build $(IMAGE_TO_TEST)) +BUILD = $(eval BUILD := $(BUILD_GEN))$(BUILD) diff --git a/csp-lmc-mid/docker/csp-lmc.yml b/csp-lmc-mid/docker/csp-lmc.yml new file mode 100644 index 0000000..7908280 --- /dev/null +++ b/csp-lmc-mid/docker/csp-lmc.yml @@ -0,0 +1,144 @@ +# +# Docker compose file for TANGO database and database device server +# +# Defines: +# - tangodb: MariaDB database with TANGO schema +# - databaseds: TANGO database device server +# - rsyslog-csplmc: rsyslog service for logger +# - cspmaster: CspMaster device +# +# Requires: +# - None +# +version: '2.2' + +services: + csp_dsconfig: + image: nexus.engageska-portugal.pt/ska-docker/tango-dsconfig:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}csp_dsconfig + depends_on: + - databaseds + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=60 --strict -- + json2tango -w -a -u csplmc/config/csplmc_dsconfig.json && sleep infinity" + volumes: + - .:/csplmc + + cspsubarray01: + image: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}/csp-lmc-common:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}cspsubarray01 + depends_on: + - csp_dsconfig + - cspmaster + - rsyslog-csplmc + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=30 --strict -- + retry --max=5 -- tango_admin --ping-device common/elt/master &&\ + /venv/bin/python /app/csp_lmc_common/CspSubarray.py comsub1" + volumes_from: + - rsyslog-csplmc:rw + + cspsubarray02: + image: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}/csp-lmc-common:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}cspsubarray02 + depends_on: + - csp_dsconfig + - cspmaster + - rsyslog-csplmc + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=30 --strict -- + retry --max=5 -- tango_admin --ping-device common/elt/master &&\ + /venv/bin/python /app/csp_lmc_common/CspSubarray.py comsub2" + volumes_from: + - rsyslog-csplmc:rw + + cspmaster: + image: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}/csp-lmc-common:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}cspmaster + depends_on: + - csp_dsconfig + - cbfmaster + - rsyslog-csplmc + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=30 --strict -- + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/sub_elt/master &&\ + /venv/bin/python -m csp_lmc_common.CspMaster commaster" + volumes_from: + - rsyslog-csplmc:rw + + searchbeam: + image: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}/csp-lmc-common:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}searchbeam + depends_on: + - csp_dsconfig + - rsyslog-csplmc + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=30 --strict -- + /venv/bin/python -m csp_lmc_common.CspCapabilityMonitor searchbeams" + volumes_from: + - rsyslog-csplmc:rw + + timingbeam: + image: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}/csp-lmc-common:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}timingbeam + depends_on: + - csp_dsconfig + - rsyslog-csplmc + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=30 --strict -- + /venv/bin/python -m csp_lmc_common.CspCapabilityMonitor timingbeams" + volumes_from: + - rsyslog-csplmc:rw + + vlbibeam: + image: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}/csp-lmc-common:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}vlbibeam + depends_on: + - csp_dsconfig + - rsyslog-csplmc + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=30 --strict -- + /venv/bin/python -m csp_lmc_common.CspCapabilityMonitor vlbibeams" + volumes_from: + - rsyslog-csplmc:rw + + pssmaster: + image: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}/csp-lmc-common:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}pssmaster + depends_on: + - csp_dsconfig + - rsyslog-csplmc + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=30 --strict -- + /venv/bin/python /app/simulators/DeviceTestMaster/PssMasterSimulator.py pss" + volumes_from: + - rsyslog-csplmc:rw + + rsyslog-csplmc: + image: jumanjiman/rsyslog + container_name: ${CONTAINER_NAME_PREFIX}rsyslog-csplmc + network_mode: ${NETWORK_MODE} diff --git a/csp-lmc-mid/docker/csp-tangodb.yml b/csp-lmc-mid/docker/csp-tangodb.yml new file mode 100644 index 0000000..64a6922 --- /dev/null +++ b/csp-lmc-mid/docker/csp-tangodb.yml @@ -0,0 +1,50 @@ +# +# Docker compose file for TANGO database and database device server +# +# Defines: +# - tangodb: MariaDB database with TANGO schema +# - databaseds: TANGO database device server +# +# Requires: +# - None +# +version: '2.2' +volumes: + tangodb: {} + +services: + tangodb: + image: nexus.engageska-portugal.pt/ska-docker/tango-db:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}tangodb + environment: + - MYSQL_ROOT_PASSWORD=secret + - MYSQL_DATABASE=tango + - MYSQL_USER=tango + - MYSQL_PASSWORD=tango + volumes: + - tangodb:/var/lib/mysql + + databaseds: + image: nexus.engageska-portugal.pt/ska-docker/tango-cpp:latest + depends_on: + - tangodb + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}databaseds + environment: + - MYSQL_HOST=${MYSQL_HOST} + - MYSQL_DATABASE=tango + - MYSQL_USER=tango + - MYSQL_PASSWORD=tango + - TANGO_HOST=${TANGO_HOST} + entrypoint: + - /usr/local/bin/wait-for-it.sh + - ${MYSQL_HOST} + - --timeout=70 + - --strict + - -- + - /usr/local/bin/DataBaseds + - "2" + - -ORBendPoint + - giop:tcp::10000 + diff --git a/csp-lmc-mid/docker/csplmc_dsconfig.json b/csp-lmc-mid/docker/csplmc_dsconfig.json new file mode 100644 index 0000000..257c9da --- /dev/null +++ b/csp-lmc-mid/docker/csplmc_dsconfig.json @@ -0,0 +1,645 @@ +{ + "classes": { + "CspSubarray": { + "properties": { + "PstBeam": [ + "mid_csp_pst/sub_elt/beam_01" + ] + } + } + }, + "servers": { + "MidCspMaster": { + "master": { + "CspCapabilityMonitor": { + "mid_csp/capability_monitor/receptors": { + "properties": { + "CapabilityDevices": [ + "mid_csp/receptors/002", + "mid_csp/receptors/003", + "mid_csp/receptors/004", + "mid_csp/receptors/001" + ], + "SkaLevel": [ + "2" + ] + } + }, + "mid_csp/capability_monitor/search_beams": { + "properties": { + "CapabilityDevices": [ + "mid_csp/search_beams/0001", + "mid_csp/search_beams/0004", + "mid_csp/search_beams/0003", + "mid_csp/search_beams/0002" + ], + "SkaLevel": [ + "2" + ] + } + }, + "mid_csp/capability_monitor/timing_beams": { + "properties": { + "CapabilityDevices": [ + "mid_csp/timing_beams/01", + "mid_csp/timing_beams/02", + "mid_csp/timing_beams/03", + "mid_csp/timing_beams/04" + ], + "SkaLevel": [ + "2" + ] + } + }, + "mid_csp/capability_monitor/vlbi_beams": { + "properties": { + "CapabilityDevices": [ + "mid_csp/vlbi_beams/01", + "mid_csp/vlbi_beams/04", + "mid_csp/vlbi_beams/03", + "mid_csp/vlbi_beams/02" + ], + "SkaLevel": [ + "2" + ] + } + } + }, + "MidCspMasterBase": { + "mid_csp/elt/master": { + "attribute_properties": { + "adminMode": { + "abs_change": [ + "-1", + "1" + ], + "__value": [ + "0" + ] + }, + "healthState": { + "abs_change": [ + "-1", + "1" + ] + }, + "reportSearchBeamState": { + "__root_att": [ + "mid_csp/capability_monitor/search_beams/capabilityState" + ] + }, + "reportTimingBeamState": { + "__root_att": [ + "mid_csp/capability_monitor/timing_beams/capabilityState" + ] + }, + "reportVlbiBeamState": { + "__root_att": [ + "mid_csp/capability_monitor/vlbi_beams/capabilityState" + ] + }, + "reportSearchBeamHealthState": { + "__root_att": [ + "mid_csp/capability_monitor/search_beams/capabilityHealthState" + ] + }, + "reportTimingBeamHealthState":{ + "__root_att": [ + "mid_csp/capability_monitor/timing_beams/capabilityHealthState" + ] + }, + "reportVlbiBeamHealthState": { + "__root_att": [ + "mid_csp/capability_monitor/vlbi_beams/capabilityHealthState" + ] + }, + "reportSearchBeamObsState": { + "__root_att": [ + "mid_csp/capability_monitor/search_beams/capabilityObsState" + ] + }, + "reportTimingBeamObsState":{ + "__root_att": [ + "mid_csp/capability_monitor/timing_beams/capabilityObsState" + ] + }, + "reportVlbiBeamObsState": { + "__root_att": [ + "mid_csp/capability_monitor/vlbi_beams/capabilityObsState" + ] + }, + "reportSearchBeamAdminMode": { + "__root_att": [ + "mid_csp/capability_monitor/search_beams/capabilityAdminMode" + ] + }, + "reportTimingBeamAdminMode":{ + "__root_att": [ + "mid_csp/capability_monitor/timig_beams/capabilityAdminMode" + ] + }, + "reportVlbiBeamAdminMode": { + "__root_att": [ + "mid_csp/capability_monitor/vlbi_beams/capabilityAdminMode" + ] + }, + "searchBeamAddresses": { + "__root_att": [ + "mid_csp/capability_monitor/search_beams/cspCapabilityAddresses" + ] + }, + "timingBeamAddresses": { + "__root_att": [ + "mid_csp/capability_monitor/timig_beams/cspCapabilityAddresses" + ] + }, + "vlbiBeamAddresses": { + "__root_att": [ + "mid_csp/capability_monitor/vlbi_beams/cspCapabilityAddresses" + ] + }, + "reservedSearchBeamIDs": { + "__root_att": [ + "mid_csp/capability_monitor/search_beams/reservedIDs" + ] + }, + "unassignedSearchBeamIDs": { + "__root_att": [ + "mid_csp/capability_monitor/search_beams/unassignedIDs" + ] + }, + "unassignedTimingBeamIDs": { + "__root_att": [ + "mid_csp/capability_monitor/timing_beams/unassignedIDs" + ] + }, + "unassignedVlbiBeamIDs": { + "__root_att": [ + "mid_csp/capability_monitor/vlbi_beams/unassignedIDs" + ] + }, + "unassignedSearchBeamNum": { + "__root_att": [ + "mid_csp/capability_monitor/search_beams/numOfUnassignedIDs" + ] + }, + "unassignedTimingBeamNum": { + "__root_att": [ + "mid_csp/capability_monitor/timing_beams/numOfUnassignedIDs" + ] + }, + "unassignedVlbiBeamNum": { + "__root_att": [ + "mid_csp/capability_monitor/vlbi_beams/numOfUnassignedIDs" + ] + }, + "searchBeamMembership": { + "__root_att": [ + "mid_csp/capability_monitor/search_beams/capabilityMembership" + ] + }, + "timingBeamMembership": { + "__root_att": [ + "mid_csp/capability_monitor/timing_beams/capabilityMembership" + ] + }, + "vlbiBeamMembership": { + "__root_att": [ + "mid_csp/capability_monitor/vlbi_beams/capabilityMembership" + ] + }, + "fspMembership": { + "__root_att": [ + "mid_csp_cbf/sub_elt/master/reportFSPSubarrayMembership" + ] + }, + "reportFSPAdminMode": { + "__root_att": [ + "mid_csp_cbf/sub_elt/master/reportFSPAdminMode" + ] + }, + "reportFSPHealthState": { + "__root_att": [ + "mid_csp_cbf/sub_elt/master/reportFSPHealthState" + ] + }, + "reportFSPState": { + "__root_att": [ + "mid_csp_cbf/sub_elt/master/reportFSPState" + ] + }, + "reportVCCAdminMode": { + "__root_att": [ + "mid_csp_cbf/sub_elt/master/reportVCCAdminMode" + ] + }, + "reportVCCHealthState": { + "__root_att": [ + "mid_csp_cbf/sub_elt/master/reportVCCHealthState" + ] + }, + "reportVCCState": { + "__root_att": [ + "mid_csp_cbf/sub_elt/master/reportVCCState" + ] + }, + "vccMembership": { + "__root_att": [ + "mid_csp_cbf/sub_elt/master/reportVCCSubarrayMembership" + ] + } + }, + "properties": { + "CspCbf": [ + "mid_csp_cbf/sub_elt/master" + ], + "CspPss": [ + "mid_csp_pss/sub_elt/master" + ], + "CspPst": [ + "mid_csp_pst/sub_elt/master" + ], + "CspSubarrays": [ + "mid_csp/elt/subarray_01", + "mid_csp/elt/subarray_02" + ], + "MaxCapabilities": [ + "VlbiBeams:20", + "TimingBeams:16", + "SearchBeams:1500", + "Receptors:197" + ], + "SearchBeams": [ + "mid_csp/search_beams/0001", + "mid_csp/search_beams/0002", + "mid_csp/search_beams/0003", + "mid_csp/search_beams/0004" + ], + "SearchBeamsMonitor": [ + "mid_csp/capability_monitor/search_beams" + ], + "SkaLevel": [ + "1" + ], + "TimingBeams": [ + "mid_csp/timing_beams/01", + "mid_csp/timing_beams/02", + "mid_csp/timing_beams/03", + "mid_csp/timing_beams/04" + ], + "TimingBeamsMonitor": [ + "mid_csp/capability_monitor/timing_beams" + ], + "VlbiBeams": [ + "mid_csp/vlbi_beams/01", + "mid_csp/vlbi_beams/02", + "mid_csp/vlbi_beams/03", + "mid_csp/vlbi_beams/04" + ], + "VlbiBeamsMonitor": [ + "mid_csp/capability_monitor/vlbi_beams" + ], + "polled_attr": [ + "csppssadminmode", + "3000", + "csppstadminmode", + "cspcbfadminmode", + "3000", + "1000", + "standbycmdalarm", + "1000", + "offcmdalarm", + "1000", + "3000", + "oncmdalarm", + "adminmode", + "1000", + "oncommandprogress", + "3000", + "offcommandprogress", + "3000", + "standbycommandprogress", + "3000", + "oncmddurationexpected", + "3000", + "offcmddurationexpected", + "3000", + "standbycmddurationexpected", + "3000", + "oncmddurationmeasured", + "3000", + "offcmddurationmeasured", + "3000", + "standbycmddurationmeasured", + "3000", + "oncmdtimeoutexpired", + "2000", + "offcmdtimeoutexpired", + "2000", + "standbycmdtimeoutexpired", + "2000", + "cspcbfstate", + "3000", + "csppssstate", + "3000", + "csppststate", + "3000", + "cspcbfhealthstate", + "3000", + "csppsshealthstate", + "3000", + "3000", + "csppsthealthstate" + ] + } + } + } + } + }, + "PssMasterSimulator": { + "pss": { + "PssMasterSimulator": { + "mid_csp_pss/sub_elt/master": { + "attribute_properties": { + "adminMode": { + "abs_change": [ + "-1", + "1" + ], + "__value": [ + "0" + ] + }, + "healthState": { + "abs_change": [ + "-1", + "1" + ] + }, + "onDurationExpected": { + "__value": [ + "10" + ] + }, + "offDurationExpected": { + "__value": [ + "12" + ] + }, + "standbyDurationExpected": { + "__value": [ + "5" + ] + }, + "onCommandProgress": { + "abs_change": [ + "-5", + "5" + ] + }, + "offCommandProgress": { + "abs_change": [ + "-5", + "5" + ] + }, + "standbyCommandProgress": { + "abs_change": [ + "-5", + "5" + ] + } + }, + "properties": { + "polled_attr": [ + "state", + "1000", + "healthstate", + "1000", + "adminmode", + "1000", + "onCommandProgress", + "1000", + "offCommandProgress", + "1000", + "standbyCommandProgress", + "1000" + ] + } + } + } + } + }, + "PstMasterSimulator": { + "pst": { + "PstMasterSimulator": { + "mid_csp_pst/sub_elt/master": { + "attribute_properties": { + "adminMode": { + "__value": [ + "0" + ], + "abs_change": [ + "-1", + "1" + ] + }, + "healthState": { + "abs_change": [ + "-1", + "1" + ] + }, + "offCommandProgress": { + "abs_change": [ + "-5", + "5" + ] + }, + "offDurationExpected": { + "__value": [ + "12" + ] + }, + "onCommandProgress": { + "abs_change": [ + "-5", + "5" + ] + }, + "onDurationExpected": { + "__value": [ + "10" + ] + }, + "standbyCommandProgress": { + "abs_change": [ + "-5", + "5" + ] + }, + "standbyDurationExpected": { + "__value": [ + "5" + ] + } + }, + "properties": { + "polled_attr": [ + "state", + "1000", + "healthstate", + "1000", + "adminmode", + "1000", + "onCommandProgress", + "1000", + "offCommandProgress", + "1000", + "standbyCommandProgress", + "1000" + ] + } + } + } + } + }, + "CspSubarray": { + "comsub1": { + "CspSubarray": { + "common/elt/subarray_01": { + "attribute_properties": { + "adminMode": { + "abs_change": [ + "-1", + "1" + ] + }, + "healthState": { + "abs_change": [ + "-1", + "1" + ] + }, + "obsState": { + "abs_change": [ + "-1", + "1" + ] + }, + "cbfOutputLink": { + "__root_att": [ + "mid_csp_cbf/sub_elt/subarray_01/outputLinksDistribution" + ] + } + }, + "properties": { + "CspMaster": [ + "common/elt/master" + ], + "CbfSubarray": [ + "mid_csp_cbf/sub_elt/subarray_01" + ], + "PssSubarray": [ + "mid_csp_pss/sub_elt/subarray_01" + ], + "SubID": [ + "1" + ], + "polled_attr": [ + "state", + "1000", + "healthstate", + "1000", + "adminmode", + "1000", + "obsstate", + "1000", + "obsmode", + "1000" + ] + } + } + } + }, + "comsub2": { + "CspSubarray": { + "common/elt/subarray_02": { + "attribute_properties": { + "adminMode": { + "abs_change": [ + "-1", + "1" + ] + }, + "healthState": { + "abs_change": [ + "-1", + "1" + ] + }, + "obsState": { + "abs_change": [ + "-1", + "1" + ] + }, + "cbfOutputLink": { + "__root_att": [ + "mid_csp_cbf/sub_elt/subarray_02/outputLinksDistribution" + ] + } + }, + "properties": { + "CspMaster": [ + "common/elt/master" + ], + "SubID": [ + "2" + ], + "CbfSubarray": [ + "mid_csp_cbf/sub_elt/subarray_02" + ], + "PssSubarray": [ + "mid_csp_pss/sub_elt/subarray_02" + ], + "polled_attr": [ + "state", + "1000", + "healthstate", + "1000", + "adminmode", + "1000", + "obsstate", + "1000", + "obsmode", + "1000" + ] + } + } + } + } + }, + "DataBaseds": { + "2": { + "DataBase": { + "sys/database/2": {} + } + } + }, + "TangoAccessControl": { + "1": { + "TangoAccessControl": { + "sys/access_control/1": {} + } + } + }, + "TangoTest": { + "test": { + "TangoTest": { + "sys/tg_test/1": {} + } + } + } + } +} diff --git a/csp-lmc-mid/docker/mid-cbf-mcs.yml b/csp-lmc-mid/docker/mid-cbf-mcs.yml new file mode 100644 index 0000000..490c97e --- /dev/null +++ b/csp-lmc-mid/docker/mid-cbf-mcs.yml @@ -0,0 +1,288 @@ +# +# Docker compose file for TANGO database and database device server +# +# Defines: +# - tangodb: MariaDB database with TANGO schema +# - databaseds: TANGO database device server +# - rsyslog-cbf-csplmc: rsyslog-cbf service for logger +# - cspmaster: CspMaster device +# +# Requires: +# - None +# +version: '2.2' + +services: + cbf_dsconfig: + image: nexus.engageska-portugal.pt/ska-docker/tango-dsconfig:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}cbf_dsconfig + depends_on: + - databaseds + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=60 --strict -- + json2tango -w -a -u csplmc/config/midcbf_dsconfig.json && sleep infinity" + volumes: + - .:/csplmc + + cbfmaster: + image: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}/mid-cbf-mcs:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}cbfmaster + depends_on: + - databaseds + - cbf_dsconfig + - vcc001 + - vcc002 + - vcc003 + - vcc004 + - rsyslog-cbf + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=60 --strict -- + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/vcc/001 &&\ + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/vcc/002 &&\ + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/vcc/003 &&\ + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/vcc/004 &&\ + /venv/bin/python /app/tangods/CbfMaster/CbfMaster/CbfMaster.py master" + volumes_from: + - rsyslog-cbf:rw + + + cbfsubarray01: + image: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}/mid-cbf-mcs:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}cbfsubarray01 + depends_on: + - cbf_dsconfig + - vcc001 + - vcc002 + - vcc003 + - vcc004 + - cbfmaster + - rsyslog-cbf + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=60 --strict -- + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/sub_elt/master &&\ + /venv/bin/python /app/tangods/CbfSubarray/CbfSubarrayMulti/CbfSubarrayMulti.py cbfSubarray-01" + volumes_from: + - rsyslog-cbf:rw + + + cbfsubarray02: + image: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}/mid-cbf-mcs:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}cbfsubarray02 + depends_on: + - cbf_dsconfig + - vcc001 + - vcc002 + - vcc003 + - vcc004 + - cbfmaster + - rsyslog-cbf + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=60 --strict -- + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/sub_elt/master &&\ + /venv/bin/python /app/tangods/CbfSubarray/CbfSubarrayMulti/CbfSubarrayMulti.py cbfSubarray-02" + volumes_from: + - rsyslog-cbf:rw + + + vcc001: + image: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}/mid-cbf-mcs:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}vcc001 + depends_on: + - databaseds + - cbf_dsconfig + - rsyslog-cbf + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=60 --strict -- + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/fsp/01 &&\ + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/fsp/02 &&\ + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/fsp/03 &&\ + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/fsp/04 &&\ + /venv/bin/python /app/tangods/Vcc/VccMulti/VccMulti.py vcc-001" + volumes_from: + - rsyslog-cbf:rw + + + vcc002: + image: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}/mid-cbf-mcs:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}vcc002 + depends_on: + - databaseds + - cbf_dsconfig + - rsyslog-cbf + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=60 --strict -- + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/fsp/01 &&\ + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/fsp/02 &&\ + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/fsp/03 &&\ + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/fsp/04 &&\ + /venv/bin/python /app/tangods/Vcc/VccMulti/VccMulti.py vcc-002" + volumes_from: + - rsyslog-cbf:rw + + + vcc003: + image: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}/mid-cbf-mcs:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}vcc003 + depends_on: + - databaseds + - cbf_dsconfig + - rsyslog-cbf + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=60 --strict -- + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/fsp/01 &&\ + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/fsp/02 &&\ + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/fsp/03 &&\ + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/fsp/04 &&\ + /venv/bin/python /app/tangods/Vcc/VccMulti/VccMulti.py vcc-003" + volumes_from: + - rsyslog-cbf:rw + + + vcc004: + image: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}/mid-cbf-mcs:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}vcc004 + depends_on: + - databaseds + - cbf_dsconfig + - rsyslog-cbf + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=60 --strict -- + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/fsp/01 &&\ + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/fsp/02 &&\ + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/fsp/03 &&\ + retry --max=5 -- tango_admin --ping-device mid_csp_cbf/fsp/04 &&\ + /venv/bin/python /app/tangods/Vcc/VccMulti/VccMulti.py vcc-004" + volumes_from: + - rsyslog-cbf:rw + + + fsp01: + image: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}/mid-cbf-mcs:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}fsp01 + depends_on: + - databaseds + - cbf_dsconfig + - rsyslog-cbf + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=60 --strict -- + /venv/bin/python /app/tangods/Fsp/FspMulti/FspMulti.py fsp-01" + volumes_from: + - rsyslog-cbf:rw + + fsp02: + image: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}/mid-cbf-mcs:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}fsp02 + depends_on: + - databaseds + - cbf_dsconfig + - rsyslog-cbf + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=60 --strict -- + /venv/bin/python /app/tangods/Fsp/FspMulti/FspMulti.py fsp-02" + volumes_from: + - rsyslog-cbf:rw + + fsp03: + image: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}/mid-cbf-mcs:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}fsp03 + depends_on: + - databaseds + - cbf_dsconfig + - rsyslog-cbf + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=60 --strict -- + /venv/bin/python /app/tangods/Fsp/FspMulti/FspMulti.py fsp-03" + volumes_from: + - rsyslog-cbf:rw + + + fsp04: + image: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}/mid-cbf-mcs:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}fsp04 + depends_on: + - databaseds + - cbf_dsconfig + - rsyslog-cbf + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=60 --strict -- + /venv/bin/python /app/tangods/Fsp/FspMulti/FspMulti.py fsp-04" + volumes_from: + - rsyslog-cbf:rw + + + tmcspsubarrayleafnodetest: + image: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}/mid-cbf-mcs:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}tmcspsubarrayleafnodetest + depends_on: + - cspmaster + - databaseds + - rsyslog-cbf + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=60 --strict -- + retry --max=10 -- tango_admin --ping-device mid_csp/elt/subarray_01 && + /venv/bin/python /app/tangods/TmCspSubarrayLeafNodeTest/TmCspSubarrayLeafNodeTest.py tm" + volumes_from: + - rsyslog-cbf:rw + + tmcspsubarrayleafnodetest2: + image: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}/mid-cbf-mcs:latest + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}tmcspsubarrayleafnodetest2 + depends_on: + - cspmaster + - databaseds + - rsyslog-cbf + environment: + - TANGO_HOST=${TANGO_HOST} + command: > + sh -c "wait-for-it.sh ${TANGO_HOST} --timeout=60 --strict -- + retry --max=10 -- tango_admin --ping-device mid_csp/elt/subarray_02 && + /venv/bin/python /app/tangods/TmCspSubarrayLeafNodeTest/TmCspSubarrayLeafNodeTest.py tm2" + volumes_from: + - rsyslog-cbf:rw + + rsyslog-cbf: + image: jumanjiman/rsyslog + network_mode: ${NETWORK_MODE} + container_name: ${CONTAINER_NAME_PREFIX}rsyslog-cbf + + diff --git a/csp-lmc-mid/docker/scripts/kat-get-version.py b/csp-lmc-mid/docker/scripts/kat-get-version.py new file mode 100755 index 0000000..04c0840 --- /dev/null +++ b/csp-lmc-mid/docker/scripts/kat-get-version.py @@ -0,0 +1,44 @@ +#!/venv/bin/python + +################################################################################ +# Copyright (c) 2014-2018, National Research Foundation (Square Kilometre Array) +# +# Licensed under the BSD 3-Clause License (the "License"); you may not use +# this file except in compliance with the License. You may obtain a copy +# of the License at +# +# https://opensource.org/licenses/BSD-3-Clause +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +################################################################################ + +"""Script to get the current version string of a Python package.""" + +import os +import argparse + +from katversion.version import get_version_from_file, get_git_cleaned_branch_name, get_version_from_module +from katversion import get_version + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument('-p', '--path', dest='path', action='store', + help='Path of SCM checkout. If not given the' + ' current directory is used.') + args = parser.parse_args() + + if args.path: + path = args.path + else: + # If path was not given us the current working directory. This is the + # way git smudge uses this file. + path = os.getcwd() + print("UFFA") + print(get_version(path, "mid-csp-lmc")) + print(get_git_cleaned_branch_name(path)) + print(get_version_from_module("csp-lmc")) -- GitLab From 709068de57359597be579fa88d36dc11df4ab090 Mon Sep 17 00:00:00 2001 From: softir Date: Sun, 19 Jan 2020 12:27:35 +0100 Subject: [PATCH 26/33] AT5-262: add to CspSubarray a TANGO device to perform monitoring of the allocated resources. --- .../CspSubarrayResourcesMonitor.py | 283 ++++++++++++++++++ .../pogo/CspSubarrayResourcesMonitor.xmi | 231 ++++++++++++++ 2 files changed, 514 insertions(+) create mode 100644 csp-lmc-common/csp_lmc_common/CspSubarrayResourcesMonitor.py create mode 100644 csp-lmc-common/pogo/CspSubarrayResourcesMonitor.xmi diff --git a/csp-lmc-common/csp_lmc_common/CspSubarrayResourcesMonitor.py b/csp-lmc-common/csp_lmc_common/CspSubarrayResourcesMonitor.py new file mode 100644 index 0000000..c5e2e46 --- /dev/null +++ b/csp-lmc-common/csp_lmc_common/CspSubarrayResourcesMonitor.py @@ -0,0 +1,283 @@ +# -*- coding: utf-8 -*- +# +# This file is part of the CspSubarrayResourcesMonitor project +# +# INAF, SKA Telescope +# +# Distributed under the terms of the GPL license. +# See LICENSE.txt for more info. + +""" CSP Subarray Resource Monitor + +The class implementes the monitoring of all the resources +allocated to the CSP Subarray. +""" + +# 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 SKABaseDevice import SKABaseDevice +# Additional import +# PROTECTED REGION ID(CspSubarrayResourcesMonitor.additionnal_import) ENABLED START # +# PROTECTED REGION END # // CspSubarrayResourcesMonitor.additionnal_import + +__all__ = ["CspSubarrayResourcesMonitor", "main"] + + +class CspSubarrayResourcesMonitor(SKABaseDevice): + """ + The class implementes the monitoring of all the resources + allocated to the CSP Subarray. + + **Properties:** + + - Device Property + + + + + """ + __metaclass__ = DeviceMeta + # PROTECTED REGION ID(CspSubarrayResourcesMonitor.class_variable) ENABLED START # + # PROTECTED REGION END # // CspSubarrayResourcesMonitor.class_variable + + # ----------------- + # Device Properties + # ----------------- + + + + + + # ---------- + # Attributes + # ---------- + + + + + + + + + + + assignedSearchBeamsState = attribute( + dtype=('DevState',), + max_dim_x=1500, + label="Assigned SearchBeams State", + polling_period=3000, + doc="State of the assigned SearchBeams", + ) + + assignedTimingBeamsState = attribute( + dtype=('DevState',), + max_dim_x=16, + label="Assigned TimingBeams State", + polling_period=3000, + doc="State of the assigned TimingBeams", + ) + + assignedVlbiBeamsState = attribute( + dtype=('DevState',), + max_dim_x=20, + label="Assigned VlbiBeams State", + polling_period=3000, + doc="State of the assigned VlbiBeams", + ) + + assignedSearchBeamsHealthState = attribute( + dtype=('DevState',), + max_dim_x=1500, + label="Assigned SearchBeams HealthState", + polling_period=3000, + doc="HealthState of the assigned SearchBeams", + ) + + assignedTimingBeamsHealthState = attribute( + dtype=('DevState',), + max_dim_x=16, + label="Assigned TimingBeams HealthState", + polling_period=3000, + doc="HealthState of the assigned TimingBeams", + ) + + assignedVlbiBeamsHealthState = attribute( + dtype=('DevState',), + max_dim_x=20, + label="Assigned VlbiBeams HealthState", + polling_period=3000, + doc="HealthState of the assigned VlbiBeams", + ) + + assignedSearchBeamsObsState = attribute( + dtype=('DevState',), + max_dim_x=1500, + label="Assigned SearchBeams ObsState", + polling_period=3000, + doc="ObsState of the assigned SearchBeams", + ) + + assignedTimingBeamsObsState = attribute( + dtype=('DevState',), + max_dim_x=16, + label="Assigned TimingBeams ObsState", + polling_period=3000, + doc="ObsState of the assigned TimingBeams", + ) + + assignedVlbiBeamsObsState = attribute( + dtype=('DevState',), + max_dim_x=20, + label="Assigned VlbiBeams ObsState", + polling_period=3000, + doc="ObsState of the assigned VlbiBeams", + ) + + assignedSearchBeamsAdminMode = attribute( + dtype=('DevState',), + max_dim_x=1500, + label="Assigned SearchBeams AdminMode", + polling_period=3000, + doc="AdminMode of the assigned SearchBeams", + ) + + assignedTimingBeamsAdminMode = attribute( + dtype=('DevState',), + max_dim_x=16, + label="Assigned TimingBeams AdminMode", + polling_period=3000, + doc="AdminMode of the assigned TimingBeams", + ) + + assignedVlbiBeamsAdminMode = attribute( + dtype=('DevState',), + max_dim_x=20, + label="Assigned VlbiBeams AdminMode", + polling_period=3000, + doc="AdminMode of the assigned VlbiBeams", + ) + + # --------------- + # General methods + # --------------- + + def init_device(self): + """Initialises the attributes and properties of the CspSubarrayResourcesMonitor.""" + SKABaseDevice.init_device(self) + # PROTECTED REGION ID(CspSubarrayResourcesMonitor.init_device) ENABLED START # + # PROTECTED REGION END # // CspSubarrayResourcesMonitor.init_device + + def always_executed_hook(self): + """Method always executed before any TANGO command is executed.""" + # PROTECTED REGION ID(CspSubarrayResourcesMonitor.always_executed_hook) ENABLED START # + # PROTECTED REGION END # // CspSubarrayResourcesMonitor.always_executed_hook + + def delete_device(self): + """Hook to delete resources allocated in init_device. + + This method allows for any memory or other resources allocated in the + init_device method to be released. This method is called by the device + destructor and by the device Init command. + """ + # PROTECTED REGION ID(CspSubarrayResourcesMonitor.delete_device) ENABLED START # + # PROTECTED REGION END # // CspSubarrayResourcesMonitor.delete_device + # ------------------ + # Attributes methods + # ------------------ + + def read_assignedSearchBeamsState(self): + # PROTECTED REGION ID(CspSubarrayResourcesMonitor.assignedSearchBeamsState_read) ENABLED START # + """Return the assignedSearchBeamsState attribute.""" + return (tango.DevState.UNKNOWN,) + # PROTECTED REGION END # // CspSubarrayResourcesMonitor.assignedSearchBeamsState_read + + def read_assignedTimingBeamsState(self): + # PROTECTED REGION ID(CspSubarrayResourcesMonitor.assignedTimingBeamsState_read) ENABLED START # + """Return the assignedTimingBeamsState attribute.""" + return (tango.DevState.UNKNOWN,) + # PROTECTED REGION END # // CspSubarrayResourcesMonitor.assignedTimingBeamsState_read + + def read_assignedVlbiBeamsState(self): + # PROTECTED REGION ID(CspSubarrayResourcesMonitor.assignedVlbiBeamsState_read) ENABLED START # + """Return the assignedVlbiBeamsState attribute.""" + return (tango.DevState.UNKNOWN,) + # PROTECTED REGION END # // CspSubarrayResourcesMonitor.assignedVlbiBeamsState_read + + def read_assignedSearchBeamsHealthState(self): + # PROTECTED REGION ID(CspSubarrayResourcesMonitor.assignedSearchBeamsHealthState_read) ENABLED START # + """Return the assignedSearchBeamsHealthState attribute.""" + return (tango.DevState.UNKNOWN,) + # PROTECTED REGION END # // CspSubarrayResourcesMonitor.assignedSearchBeamsHealthState_read + + def read_assignedTimingBeamsHealthState(self): + # PROTECTED REGION ID(CspSubarrayResourcesMonitor.assignedTimingBeamsHealthState_read) ENABLED START # + """Return the assignedTimingBeamsHealthState attribute.""" + return (tango.DevState.UNKNOWN,) + # PROTECTED REGION END # // CspSubarrayResourcesMonitor.assignedTimingBeamsHealthState_read + + def read_assignedVlbiBeamsHealthState(self): + # PROTECTED REGION ID(CspSubarrayResourcesMonitor.assignedVlbiBeamsHealthState_read) ENABLED START # + """Return the assignedVlbiBeamsHealthState attribute.""" + return (tango.DevState.UNKNOWN,) + # PROTECTED REGION END # // CspSubarrayResourcesMonitor.assignedVlbiBeamsHealthState_read + + def read_assignedSearchBeamsObsState(self): + # PROTECTED REGION ID(CspSubarrayResourcesMonitor.assignedSearchBeamsObsState_read) ENABLED START # + """Return the assignedSearchBeamsObsState attribute.""" + return (tango.DevState.UNKNOWN,) + # PROTECTED REGION END # // CspSubarrayResourcesMonitor.assignedSearchBeamsObsState_read + + def read_assignedTimingBeamsObsState(self): + # PROTECTED REGION ID(CspSubarrayResourcesMonitor.assignedTimingBeamsObsState_read) ENABLED START # + """Return the assignedTimingBeamsObsState attribute.""" + return (tango.DevState.UNKNOWN,) + # PROTECTED REGION END # // CspSubarrayResourcesMonitor.assignedTimingBeamsObsState_read + + def read_assignedVlbiBeamsObsState(self): + # PROTECTED REGION ID(CspSubarrayResourcesMonitor.assignedVlbiBeamsObsState_read) ENABLED START # + """Return the assignedVlbiBeamsObsState attribute.""" + return (tango.DevState.UNKNOWN,) + # PROTECTED REGION END # // CspSubarrayResourcesMonitor.assignedVlbiBeamsObsState_read + + def read_assignedSearchBeamsAdminMode(self): + # PROTECTED REGION ID(CspSubarrayResourcesMonitor.assignedSearchBeamsAdminMode_read) ENABLED START # + """Return the assignedSearchBeamsAdminMode attribute.""" + return (tango.DevState.UNKNOWN,) + # PROTECTED REGION END # // CspSubarrayResourcesMonitor.assignedSearchBeamsAdminMode_read + + def read_assignedTimingBeamsAdminMode(self): + # PROTECTED REGION ID(CspSubarrayResourcesMonitor.assignedTimingBeamsAdminMode_read) ENABLED START # + """Return the assignedTimingBeamsAdminMode attribute.""" + return (tango.DevState.UNKNOWN,) + # PROTECTED REGION END # // CspSubarrayResourcesMonitor.assignedTimingBeamsAdminMode_read + + def read_assignedVlbiBeamsAdminMode(self): + # PROTECTED REGION ID(CspSubarrayResourcesMonitor.assignedVlbiBeamsAdminMode_read) ENABLED START # + """Return the assignedVlbiBeamsAdminMode attribute.""" + return (tango.DevState.UNKNOWN,) + # PROTECTED REGION END # // CspSubarrayResourcesMonitor.assignedVlbiBeamsAdminMode_read + + + # -------- + # Commands + # -------- + +# ---------- +# Run server +# ---------- + + +def main(args=None, **kwargs): + # PROTECTED REGION ID(CspSubarrayResourcesMonitor.main) ENABLED START # + return run((CspSubarrayResourcesMonitor,), args=args, **kwargs) + # PROTECTED REGION END # // CspSubarrayResourcesMonitor.main + +if __name__ == '__main__': + main() diff --git a/csp-lmc-common/pogo/CspSubarrayResourcesMonitor.xmi b/csp-lmc-common/pogo/CspSubarrayResourcesMonitor.xmi new file mode 100644 index 0000000..6aa94cc --- /dev/null +++ b/csp-lmc-common/pogo/CspSubarrayResourcesMonitor.xmi @@ -0,0 +1,231 @@ + + + + + + + + + + + + 4 + + + + + + + + + 4 + + + + + console::cout + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- GitLab From d521b9f2c4a75fb578d3402389c2147a4d188ea2 Mon Sep 17 00:00:00 2001 From: softir Date: Sun, 19 Jan 2020 12:28:39 +0100 Subject: [PATCH 27/33] AT5-262: implementing code review advices. --- csp-lmc-common/csp_lmc_common/CspMaster.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/csp-lmc-common/csp_lmc_common/CspMaster.py b/csp-lmc-common/csp_lmc_common/CspMaster.py index 91669f4..d73bdb1 100644 --- a/csp-lmc-common/csp_lmc_common/CspMaster.py +++ b/csp-lmc-common/csp_lmc_common/CspMaster.py @@ -195,7 +195,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): dev_name = evt.device.dev_name() if not evt.err: try: - if evt.attr_value.name.lower().find("commandprogress") > -1: + if "commandprogress" == evt.attr_value.name.lower()[-15:]: if dev_name in self._se_fqdn: # get the command name (on/off/standby) from the attribute name # (onCommandProgress/offCommandProgress/standbyCommandProgress)) @@ -1188,6 +1188,9 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): if key == '__value'} try: self._admin_mode = memorized_attr_dict['adminMode'] + if self._admin_mode not in [AdminMode.ONLINE, AdminMode.MAINTENANCE]: + self._health_state = HealthState.UNKNOWN + self.set_state(tango.DevState.DISABLE) except KeyError as key_err: self.logger.info("Key {} not found".format(key_err)) -- GitLab From 347dd9613a7a5c980cb7c27930924fe82d7e91fc Mon Sep 17 00:00:00 2001 From: softir Date: Sun, 19 Jan 2020 12:31:35 +0100 Subject: [PATCH 28/33] AT5-262: Add support for lmcbaseclasses version >= 0.2.0. Variables renaming. --- csp-lmc-common/csp_lmc_common/CspSubarray.py | 601 +++++++++---------- csp-lmc-common/pogo/CspSubarray.xmi | 161 +++-- 2 files changed, 366 insertions(+), 396 deletions(-) diff --git a/csp-lmc-common/csp_lmc_common/CspSubarray.py b/csp-lmc-common/csp_lmc_common/CspSubarray.py index 08ef593..51f4c79 100644 --- a/csp-lmc-common/csp_lmc_common/CspSubarray.py +++ b/csp-lmc-common/csp_lmc_common/CspSubarray.py @@ -101,15 +101,23 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): """ # PROTECTED REGION ID(CspSubarray.class_variable) ENABLED START # # PROTECTED REGION END # // CspSubarray.class_variable - + # !! NOTE !!: + # In methods and attributes of the class: + # 'sc' prefix stands for 'sub-component' + # 'cb' suffix stands for 'callback' #---------------- # Event Callback functions # --------------- - def _se_scm_change_event_cb(self, evt): + def _sc_scm_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. + Retrieve the values of the sub-array sub-component SCM attributes subscribed + at device connection. + Sub-array sub-components are: + - the CBF sub-array (assigned at initialization!) + - the PSS sub-array if SearchBeams are assigned to the sub-array + - the PSTBeams if TimingBeams are assigned to the sub-array + These values are used to report the whole Csp Subarray State and healthState. :param evt: The event data @@ -118,67 +126,67 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): dev_name = evt.device.dev_name() if not evt.err: try: - if dev_name in self._se_subarray_fqdn: + if dev_name in self._sc_subarray_fqdn: if evt.attr_value.name.lower() == "state": - print("{}: received event on {} value {}".format(dev_name, - evt.attr_value.name, - evt.attr_value.value)) - self._se_subarray_state[dev_name] = evt.attr_value.value - #self._se_state = {key, value for key,value in self._subarray_resources_state.item if key.find("subarray") > -1} - + self._sc_subarray_state[dev_name] = evt.attr_value.value elif evt.attr_value.name.lower() == "healthstate": - self._se_subarray_health_state[dev_name] = evt.attr_value.value + self._sc_subarray_health_state[dev_name] = evt.attr_value.value elif evt.attr_value.name.lower() == "adminmode": - print("device: {} adminMode value {}".format(dev_name,evt.attr_value.value)) - self._se_subarray_admin_mode[dev_name] = evt.attr_value.value + self.logger.debug("device: {} adminMode value {}".format(dev_name,evt.attr_value.value)) + self._sc_subarray_admin_mode[dev_name] = evt.attr_value.value elif evt.attr_value.name.lower() == "obsstate": - print("device: {} obsState value {}".format(dev_name,evt.attr_value.value )) - self._se_subarray_obs_state[dev_name] = evt.attr_value.value + self.logger.debug("device: {} obsState value {}".format(dev_name,evt.attr_value.value )) + self._sc_subarray_obs_state[dev_name] = evt.attr_value.value elif evt.attr_value.name.lower() == "obsmode": - print("device: {} obsMode value {}".format(dev_name, evt.attr_value.value )) - self._se_subarray_obs_mode[dev_name] = evt.attr_value.value + self.logger.debug("device: {} obsMode value {}".format(dev_name, evt.attr_value.value )) + self._sc_subarray_obs_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) + self.logger.(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) + self.logger.(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) + self.logger.(log_msg, tango.LogLevel.LOG_INFO) # update CSP sub-array SCM if evt.attr_value.name.lower() in ["state", "healthstate"]: self._update_subarray_state() if evt.attr_value.name.lower() == "obsstate": self._update_subarray_obs_state() except tango.DevFailed as df: - self.dev_logging(str(df.args[0].desc), tango.LogLevel.LOG_ERROR) + self.logger.(str(df.args[0].desc), tango.LogLevel.LOG_ERROR) except Exception as except_occurred: - self.dev_logging(str(except_occurred), tango.LogLevel.LOG_ERROR) + self.logger.(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": - self._se_subarray_state[dev_name] = tango.DevState.UNKNOWN - self._se_subaray_health_state[dev_name] = HealthState.UNKNOWN - # TODO how report obsState? - # adminMode can't be change otherwise the State and healthState - # are note updated + # only if the device is ONLINE/MAINTENANCE, its State is set to + # UNKNOWN when there is a timeout on connection, otherwise its + # State should be reported always as DISABLE + if self._sc_admin_mode[dev_name] in [AdminMode.ONLINE, + AdminMode.MAINTENANCE]: + self._sc_subarray_state[dev_name] = tango.DevState.UNKNOWN + self._sc_subarray_health_state[dev_name] = HealthState.UNKNOWN + # TODO how report obsState? + # adminMode can't be change otherwise the State and healthState + # are note updated # update the State and healthState of the CSP sub-array self._update_subarray_state() log_msg = item.reason + ": on attribute " + str(evt.attr_name) - self.dev_logging(log_msg, tango.LogLevel.LOG_WARN) + self.logger.(log_msg, tango.LogLevel.LOG_WARN) def _subarray_resources_scm_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. + Retrieve the SCM attributes of all CSP Capabilities assigned to + the sub-array. :param evt: The event data @@ -189,40 +197,37 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): try: if dev_name in self._subarray_resources_fqdn: if evt.attr_value.name.lower() == "state": - print("{}: received event on {} value {}".format(dev_name, - evt.attr_value.name, - evt.attr_value.value)) self._subarray_resources_state[dev_name] = evt.attr_value.value - #self._se_state = {key, value for key,value in self._subarray_resources_state.item if key.find("subarray") > -1} + #self._sc_state = {key, value for key,value in self._subarray_resources_state.item if key.find("subarray") > -1} elif evt.attr_value.name.lower() == "healthstate": self._subarray_resources_health_state[dev_name] = evt.attr_value.value elif evt.attr_value.name.lower() == "adminmode": - print("device: {} adminMode value {}".format(dev_name,evt.attr_value.value )) + self.logger.debug("device: {} adminMode value {}".format(dev_name,evt.attr_value.value )) self._subarray_resources_admin_mode[dev_name] = evt.attr_value.value elif evt.attr_value.name.lower() == "obsstate": - print("device: {} obsState value {}".format(dev_name,evt.attr_value.value )) + self.logger.debug("device: {} obsState value {}".format(dev_name,evt.attr_value.value )) self._subarray_resources_obs_state[dev_name] = evt.attr_value.value elif evt.attr_value.name.lower() == "obsmode": - print("device: {} obsMode value {}".format(dev_name,evt.attr_value.value )) + self.logger.debug("device: {} obsMode value {}".format(dev_name,evt.attr_value.value )) self._subarray_resources_obs_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) + self.logger.warn(log_msg) else: log_msg = ("Unexpected change event for" " attribute: {}".format(str(evt.attr_name))) - self.dev_logging(log_msg, tango.LogLevel.LOG_WARN) + self.logger.warn(log_msg) 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) + self.logger.info(log_msg) except tango.DevFailed as df: - self.dev_logging(str(df.args[0].desc), tango.LogLevel.LOG_ERROR) + self.logger.error(str(df.args[0].desc)) except Exception as except_occurred: - self.dev_logging(str(except_occurred), tango.LogLevel.LOG_ERROR) + self.logger.error(str(except_occurred)) else: for item in evt.errors: # API_EventTimeout: if sub-element device not reachable it transitions @@ -231,7 +236,7 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): self._subarray_resources_state[dev_name] = tango.DevState.UNKNOWN self._subarray_resources_health_state[dev_name] = HealthState.UNKNOWN log_msg = item.reason + ": on attribute " + str(evt.attr_name) - self.dev_logging(log_msg, tango.LogLevel.LOG_WARN) + self.logger.warn(log_msg) def _attributes_change_evt_cb(self, evt): """ @@ -247,34 +252,34 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): dev_name = evt.device.dev_name() if not evt.err: try: - if evt.attr_value.name.lower().find("cmdprogress") > -1: - if dev_name in self._se_subarray_fqdn: + if "commandprogress" == evt.attr_value.name.lower()[-15:]: + if dev_name in self._sc_subarray_fqdn: # get the command name (on/off/standby) from the attribute name # (onCommandProgress/offCommandProgress/standbyCommandProgress)) # removing the last 15 chars cmd_name = evt.attr_value.name[:-11] - self._se_subarray_cmd_progress[dev_name][cmd_name] = evt.attr_value.value - elif evt.attr_value.name.lower().find("cmdtimeoutexpired") > -1: - if dev_name in self._se_subarray_fqdn: + self._sc_subarray_cmd_progress[dev_name][cmd_name] = evt.attr_value.value + elif if "cmdtimeoutexpired" == evt.attr_value.name.lower()[-17:]: + if dev_name in self._sc_subarray_fqdn: cmd_name = evt.attr_value.name[:-17] - self._se_subarray__timeout_expired[dev_name][cmd] = True + self._sc_subarray__timeout_expired[dev_name][cmd] = True else: log_msg = ("Unexpected change event for" " attribute: {}".format(str(evt.attr_name))) - self.dev_logging(log_msg, tango.LogLevel.LOG_WARN) + self.logger.warn(log_msg) 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) + self.logger.info(log_msg) except tango.DevFailed as df: - self.dev_logging(str(df.args[0].desc), tango.LogLevel.LOG_ERROR) + self.logger.error(str(df.args[0].desc)) except Exception as except_occurred: - self.dev_logging(str(except_occurred), tango.LogLevel.LOG_ERROR) + self.logger.error(str(except_occurred)) else: for item in evt.errors: log_msg = item.reason + ": on attribute " + str(evt.attr_name) - self.dev_logging(log_msg, tango.LogLevel.LOG_WARN) + self.logger.warn(log_msg) def _cmd_ended_cb(self, evt): """ @@ -306,37 +311,34 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): if not evt.err: msg = "Device {} is processing command {}".format(evt.device, evt.cmd_name) - print(msg) - self.dev_logging(msg, tango.LogLevel.LOG_INFO) + self.logger.info(msg) else: msg = "Error!!Command {} ended on device {}.\n".format(evt.cmd_name, evt.device.dev_name()) msg += " Desc: {}".format(evt.errors[0].desc) - #self.dev_logging(msg, tango.LogLevel.LOG_ERROR) + #self.logger.(msg, tango.LogLevel.LOG_ERROR) if evt.cmd_name == "ConfigureScan": # set to IDLE the obstate of the failed sub-array/PSTBeam - self._se_subarray_obs_state[evt.device] = ObsState.IDLE - print(msg) - self._se_cmd_execution_state[evt.device.dev_name()][evt.cmd_name.lower()] = CmdExecState.IDLE + self._sc_subarray_obs_state[evt.device] = ObsState.IDLE + self.logger.info(msg) + self._sc_cmd_execution_state[evt.device.dev_name()][evt.cmd_name.lower()] = CmdExecState.IDLE # obsState and obsMode values take on the CbfSubarray's values via # the subscribe/publish mechanism else: - self.dev_logging("cmd_ended callback: evt is empty!!", - tango.LogLevel.LOG_ERRO) + self.logger.error("cmd_ended callback: evt is empty!!") except tango.DevFailed as df: msg = ("CommandCallback cmd_ended failure - desc: {}" " reason: {}".format(df.args[0].desc, df.args[0].reason)) - self.dev_logging(msg, tango.LogLevel.LOG_ERROR) + self.logger.error(msg) except Exception as ex: msg = "CommandCallBack cmd_ended general exception: {}".format(str(ex)) - self.dev_logging(msg, tango.LogLevel.LOG_ERROR) + self.logger.error(msg) def _timeout_handler_cb(self, args = None, **dict_args): - print("Timeout expired!!") cmd_expired = dict_args['cmd_name'] device = dict_args['device'] - self._se_subarray_cmd_exec_state[device][cmd_expired] = CmdExecState.IDLE - self._se_subarray_timeout_expired[device][cmd_expired] = True + self._sc_subarray_cmd_exec_state[device][cmd_expired] = CmdExecState.IDLE + self._sc_subarray_timeout_expired[device][cmd_expired] = True # Class protected methods # --------------- @@ -355,10 +357,12 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): # 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_subarray_state[self.CbfSubarray]) - if self._se_subarray_admin_mode[self.CbfSubarray] in [AdminMode.OFFLINE, AdminMode.NOTFITTED, AdminMode.RESERVED]: - self.set_state(tango.DevState.FAULT) - if self._admin_mode in [AdminMode.OFFLINE, AdminMode.NOTFITTED, AdminMode.RESERVED]: + self.set_state(self._sc_subarray_state[self.CbfSubarray]) + if self._sc_subarray_admin_mode[self.CbfSubarray] not in [AdminMode.ONLINE, + AdminMode.MAINTENANCE]: + self.set_state(tango.DevState.DISABLE) + if self._admin_mode not in [AdminMode.AdminMode.ONLINE, + AdminMode.MAINTENANCE]: self.set_state[tango.DevState.DISABLE] def _update_subarray_health_state(self): @@ -371,147 +375,123 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): :return: None """ - - # The whole CSP Subarray HealthState is OK only if: - # - all sub-elements subarrays/PSTBeam have - # - adminMode OFF-LINE or MAINTENACE - # - HealthState is OK - admin_fqdn = [fqdn for fqdn, admin_value in self._se_subarray_admin_mode.items() + # if the Subarray is OFF (no assigned resources) or DISABLE, + # its health state is UNKNOWN. + # Note: when the Subarray adminMode is set OFFLINE/RESERVED/NOTFITTED + # all its allocated resources are released and its State moves to DISABLE. + # + if self._state in [tango.DevState.OFF, tango.DevState.DISABLE]: + self._health_state = HealthState.UNKNOWN + + # The whole CspSubarray HealthState is OK if is ON and all its assigned sub-components + # (CBF and PSS subarrays as well PST Beams) are OK. + # - CbfSubarray ON (receptors/stations assigned) + # - PssSubarray ON + + # default value to DEGRADED + self._health_state = HealthState.DEGRADED + + # build the list of all the Csp Subarray sub-components ONLINE/MAINTENANCE + admin_fqdn = [fqdn for fqdn, admin_value in self._sc_subarray_admin_mode.items() if admin_value in [AdminMode.ONLINE, AdminMode.MAINTENANCE]] - health_fqdn = [fqdn for fqdn, health_value in self._se_subarray_health_state.items() - if health_value == HealthState.OK] - state_fqdn = [fqdn for fqdn, state_value in self._se_subarray_state.items() - if state_value in [tango.DevState.ON, tango.DevState.OFF]] - admin_fqdn.sort() - health_fqdn.sort() - state_fqdn.sort() - print("admin_fqdn:",admin_fqdn) - print("state_fqdn:",state_fqdn) - print("health_fqdn:",health_fqdn) + # build the list of sub-elements with State ON + state_fqdn = [fqdn for fqdn in admin_fqdn if self._sc_state[fqdn] == tango.DevState.ON] + # build the list with the healthState of ONLINE/MAINTENANCE devices + health_list = [self._sc_health_state[fqdn] for fqdn in state_fqdn] + if self.CbfSubarray in admin_fqdn: - if admin_fqdn == health_fqdn == state_fqdn: - self._healthstate = HealthState.OK - elif self._se_subarray_health_state[self.CbfSubarray] in [HealthState.FAILED, HealthState.UNKNOWN]: - self._healthstate = self._se_subarray_health_state[self.CbfSubarray] - else: - self._health_state = HealthState.DEGRADED + if all(value == HealthState.OK for value in health_list): + self._health_state = HealthState.OK + elif self._sc_health_state[self.CbfSubarray] in [HealthState.FAILED, + HealthState.UNKNOWN, + HealthState.DEGRADED]: + self._health_state = self._sc_health_state[self.CbfSubarray] else: - self._healthstate = HealthState.FAILED + # if CBF Subarray is not ONLINE/MAINTENANCE .... + self._health_state = self._sc_health_state[self.CbfSubarray] return - def _update_subarray_obs_state(self): + def _connect_to_subarray_subcomponent(self, fqdn): """ - *Class private method* - - Set the subarray obsState attribute value. It works only for IMAGING. - :param: - None - Returns: - None + Class private method. + Establish a *stateless* connection with a CSP Subarray sub-component + device (CBF Subarray, PSS Subarray , PST Beams). + Exceptions are logged. + :param fqdn: + the CspSubarray sub-component FQDN + :return: None """ - # NOTE: when ObsMode value is set, it should be considered to set the final - # sub-array ObsState value - online_devices = {key: value for key,value in self._se_subarray_admin_mode.items() - if value in [AdminMode.ONLINE, AdminMode.MAINTENANCE]} - if all (values == ObsState.IDLE for values in self._se_subarray_obs_state.values()): - self._obs_state = ObsState.IDLE + # check if the device has already been addedd + if fqdn in self._sc_subarray_fqdn: return + # read the sub-componet adminMode (memorized) attribute from + # the CSP.LMC TANGO DB. + attribute_properties = csplmc_tango_db.get_device_attribute_property(fqdn, + {'adminMode': ['__value']}) + self.logger.info("fqdn: {} attribute_properties: {}".format(fqdn, attribute_properties)) + try: + admin_mode_memorized = attribute_properties['adminMode']['__value'] + self._sc_subarray_admin_mode[fqdn] = int(admin_mode_memorized[0]) + except KeyError: + pass - configuring_devices = {key:value for key,value in self._se_subarray_exec_state['config'].items() - if value == CmdExecState.RUNNING} - for fqdn in configuring_devices.keys(): - if self._se_subarray_obs_state[fqdn] == ObsState.READY: - self._se_subarray_cmd_timer[fqdn]['config'].cancel() - self._se_subarray_cmd_timer[fqdn]['config'].join() - self._se_subarray_exec_state[fqdn]['config'] == CmdExecState.IDLE - - if all (values == ObsMode.READY for values in configuring_devices.values()): - self._obs_state = ObsState.READY - return + try: + log_msg = "Trying connection to" + str(fqdn) + " device" + self.logger.info(log_msg) + device_proxy = tango.DeviceProxy(fqdn) + # Note: The DeviceProxy is initialized even if the sub-component + # device is not running (but defined into the TANGO DB! If not defined in the + # TANGO DB a exception is throw). + # The connection with a sub-element is establish as soon as the corresponding + # device starts. + # append the FQDN to the list and store the sub-element proxies + self._sc_subarray_fqdn.append(fqdn) + self._sc_subarray_proxies[fqdn] = device_proxy - scanning_devices = {key:value for key,value in self._se_subarray_exec_state['scan'].items() - if value == CmdExecState.RUNNING} - for fqdn in scanning_devices.keys(): - if self._se_subarray_obs_state[fqdn] == ObsState.READY: - self._se_subarray_cmd_timer[fqdn]['scan'].cancel() - self._se_subarray_cmd_timer[fqdn]['scan'].join() - self._se_subarray_exec_state[fqdn]['scan'] == CmdExecState.IDLE - if all (values == ObsMode.IDLE for values in scanning_devices.values()): - self._obs_mode = ObsMode.IDLE - - def _connect_to_subarrays (self): - """ - Class private method. - Establish a *stateless* connection with each CSP sub-element. - Retrieve from the CSP TANGO DB the memorized adminMode value for each - sub-element. - Exceptions are logged. - - :return: None - """ - - for fqdn in self._se_subarray_fqdn: - try: - log_msg = "Trying connection to" + str(fqdn) + " device" - print(log_msg) - self.dev_logging(log_msg, int(tango.LogLevel.LOG_INFO)) - device_proxy = tango.DeviceProxy(fqdn) - #device_proxy.ping() - # Note: ping() method is not called. The DeviceProxy is initialized even if the - # sub-element is not running (but defined into the TANGO DB!). - # The connection with a sub-element is establish as soon as the corresponding - # Master device starts. - - # store the sub-element proxies - self._se_subarray_proxies[fqdn] = device_proxy - # subscription of SCM attributes (State, healthState and adminMode). - # Note: subscription is performed also for devices not ONLINE or MAINTENANCE. - # In this way the CspMaster is able to detect a change in the admin value. + # subscription of SCM attributes (State, healthState and adminMode). + # Note: subscription is performed also for devices not ONLINE or MAINTENANCE. + # In this way the CspMaster is able to detect a change in the admin value. - # if the sub-element sub-array device is running, the adminMode is updated - # adminMode has to be subscribed first so that healthState, State and obsState - # are updated accordingly with its value! - ev_id = device_proxy.subscribe_event("adminMode", - tango.EventType.CHANGE_EVENT, - self._se_scm_change_event_cb, - stateless=True) - self._subarray_resources_event_id[fqdn]['adminMode'] = ev_id + ev_id = device_proxy.subscribe_event("adminMode", + tango.EventType.CHANGE_EVENT, + self._sc_scm_change_event_cb, + stateless=True) + self._sc_subarray_event_id[fqdn]['adminMode'] = ev_id - ev_id = device_proxy.subscribe_event("State", - tango.EventType.CHANGE_EVENT, - self._se_scm_change_event_cb, - stateless=True) - self._subarray_resources_event_id[fqdn]['state'] = ev_id - - ev_id = device_proxy.subscribe_event("healthState", - tango.EventType.CHANGE_EVENT, - self._se_scm_change_event_cb, - stateless=True) - self._subarray_resources_event_id[fqdn]['healthState'] = ev_id - - ev_id = device_proxy.subscribe_event("obsState", - tango.EventType.CHANGE_EVENT, - self._se_scm_change_event_cb, - stateless=True) - self._subarray_resources_event_id[fqdn]['obsState'] = ev_id + ev_id = device_proxy.subscribe_event("State", + tango.EventType.CHANGE_EVENT, + self._sc_scm_change_event_cb, + stateless=True) + self._sc_subarray_event_id[fqdn]['state'] = ev_id + + ev_id = device_proxy.subscribe_event("healthState", + tango.EventType.CHANGE_EVENT, + self._sc_scm_change_event_cb, + stateless=True) + self._sc_subarray_event_id[fqdn]['healthState'] = ev_id + + ev_id = device_proxy.subscribe_event("obsState", + tango.EventType.CHANGE_EVENT, + self._sc_scm_change_event_cb, + stateless=True) + self._sc_subarray_event_id[fqdn]['obsState'] = ev_id - ev_id = device_proxy.subscribe_event("obsMode", - tango.EventType.CHANGE_EVENT, - self._se_scm_change_event_cb, - stateless=True) - self._subarray_resources_event_id[fqdn]['obsMode'] = ev_id + ev_id = device_proxy.subscribe_event("obsMode", + tango.EventType.CHANGE_EVENT, + self._sc_scm_change_event_cb, + stateless=True) + self._sc_subarray_event_id[fqdn]['obsMode'] = ev_id - except KeyError as key_err: - log_msg = ("No key {} found".format(str(key_err))) - self.dev_logging(log_msg, tango.LogLevel.LOG_WARN) - 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) + except KeyError as key_err: + log_msg = ("No key {} found".format(str(key_err))) + self.logger.warn(log_msg) + except tango.DevFailed as df: + log_msg = ("Failure in connection to {}" + " device: {}".format(str(fqdn), str(df.args[0].desc))) + self.logger.error(log_msg) - def _is_se_subarray_running (self, device_name): + def _is_sc_subarray_running (self, device_name): """ *Class protected method.* @@ -526,24 +506,24 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): False otherwise """ try: - proxy = self._se_subarray_proxies[device_name] + proxy = self._sc_subarray_proxies[device_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) + self.logger.warn(msg) try: proxy = tango.DeviceProxy(device_name) # execute a ping to detect if the device is actually running proxy.ping() - self._se_subarray_proxies[device_name] = proxy + self._sc_subarray_proxies[device_name] = proxy except tango.DevFailed as df: return False 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) + self.logger.warn(msg) return False return True # ---------------- @@ -710,12 +690,6 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): doc="The duration measured (in sec) for the RemoveSearchBeams command.", ) - searchBeamsCmdTimeoutExpired = attribute( - dtype='DevBoolean', - label="Add/SearchBeams command timeout flag", - polling_period=1000, - doc="The timeout flag for the Add/SearchBeams command.", - ) addTimingBeamDurationExpected = attribute( dtype='DevUShort', @@ -740,14 +714,7 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): label="EndSB command duration measured", doc="The duration measured (in sec) for the RemoveTimingBeams command.", ) - - timingBeamsCmdTimeoutExpired = attribute( - dtype='DevBoolean', - label="Add/TimingBeams command timeout flag", - polling_period=1000, - doc="The timeout flag for the Add/TimingBeams command.", - ) - + endSBDurationExpected = attribute( dtype='DevUShort', label="EndSB command duration expected", @@ -795,12 +762,6 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): doc="The progress percentage for the EndScan command.", ) - endScanCmdTimeoutExpired = attribute( - dtype='DevBoolean', - label="EndScan command timeout flag", - polling_period=1000, - doc="The timeout flag for the EndScan command.", - ) addResourcesCmdProgress = attribute( dtype='DevUShort', @@ -838,6 +799,29 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): doc="Number of devices that completed the task", ) + cmdTimeoutExpired = attribute( + dtype='DevBoolean', + label="CspSubarray command execution timeout flag", + polling_period=1000, + doc="The timeout flag for a CspSubarray command.", + ) + + cmdAlarmRaised = attribute( + dtype='DevBoolean', + label="CspSubarray alarm flag", + polling_period=1000, + doc="The alarm flag for a CspSubarray command.", + ) + + + pstOutputLink = attribute( + dtype='DevString', + label="PST output link", + doc="The output link for PST products.", + ) + + + assignedSearchBeamIDs = attribute( dtype=('DevUShort',), max_dim_x=1500, @@ -972,10 +956,7 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): label="cbfOutputLink", forwarded=True ) - pstOutputLink = attribute(name="pstOutputLink", - label="cbfOutputLink", - forwarded=True - ) + vlbiOutputLink = attribute(name="vlbiOutputLink", label="cbfOutputLink", forwarded=True @@ -992,55 +973,51 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): self._version_id = release.version # connect to CSP.LMC TANGO DB csplmc_tango_db = tango.Database() - # 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._health_state = HealthState.UNKNOWN self._admin_mode = AdminMode.ONLINE self._obs_mode = ObsMode.IDLE + self._obs_state = ObsState.IDLE + # connect to TANGO DB + # 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] + # self._sc_state[self.CspCbf] # return the State value of the Mid Cbf sub-element. - self._se_subarray_state = defaultdict(lambda: tango.DevState.DISABLE) - self._se_subarray_health_state = defaultdict(lambda: HealthState.UNKNOWN) - self._se_subarray_admin_mode = defaultdict(lambda: AdminMode.NOTFITTED) - self._se_subarray_obs_state = defaultdict(lambda: ObsState.IDLE) - self._se_subarray_obs_mode = defaultdict(lambda: ObsMode.IDLE) - - # initialize list with CSP sub-element sub-array FQDNs. After TimingBeams assignment or - # configuration, this list includes their FQDNs. - self._se_subarray_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_subarray_fqdn.append(self.CbfSubarray) - self._se_subarray_fqdn.append(self.PssSubarray) + self._sc_subarray_state = defaultdict(lambda: tango.DevState.DISABLE) + self._sc_subarray_health_state = defaultdict(lambda: HealthState.UNKNOWN) + self._sc_subarray_admin_mode = defaultdict(lambda: AdminMode.NOTFITTED) + self._sc_subarray_obs_state = defaultdict(lambda: ObsState.IDLE) + self._sc_subarray_obs_mode = defaultdict(lambda: ObsMode.IDLE) + self._csp_tango_db = tango.Database() + # read the CSP memorized attributes from the TANGO DB. + # Note: a memorized attribute has defined the attribute + # property '__value' + attribute_properties = self._csp_tango_db.get_device_attribute_property(self.get_name(), + {'adminMode': ['__value']}) + # build a dictionary with the (attr_name, value) of the memorized attributes + # use memorized atrtibute if present, otherwise the default one + memorized_attr_dict = {attr_name : int(value[0]) for attr_name, db_key in attribute_properties.items() + for key, value in db_key.items() + if key == '__value'} + try: + self._admin_mode = memorized_attr_dict['adminMode'] + if self._admin_mode not in [AdminMode.ONLINE, AdminMode.MAINTENANCE]: + self._health_state = HealthState.UNKNOWN + self.set_state(tango.DevState.DISABLE) + + except KeyError as key_err: + self.logger.info("Key {} not found".format(key_err)) + - # read the sub-element sub-array adminMode (memorized) attribute from - # the CSP.LMC TANGO DB. - # Note: a memorized attribute has defined the attribute property '__value' - for fqdn in self._se_subarray_fqdn: - attribute_properties = csplmc_tango_db.get_device_attribute_property(fqdn, - {'adminMode': ['__value']}) - print("fqdn: {} attribute_properties: {}".format(fqdn, attribute_properties)) - try: - admin_mode_memorized = attribute_properties['adminMode']['__value'] - self._se_subarray_admin_mode[fqdn] = int(admin_mode_memorized[0]) - except KeyError: - pass - # _se_subarray_proxies: the sub-element sub-array proxies + # list of sub-array sub-component FQDNs + self._sc_subarray_fqdn = [] + # _sc_subarray_proxies: the sub-element sub-array proxies # implementes as dictionary: # keys: sub-element sub-arrayFQDN # values: device proxy - self._se_subarray_proxies = {} + self._sc_subarray_proxies = {} # use defaultdict to initialize the sub-array resources State, # healthState, obsState and and adminMode attributes @@ -1056,14 +1033,14 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): # them at disconnection. # keys: sub-component FQDN # values: dictionary (keys: attribute name, values: event id) - self._subarray_resources_event_id = defaultdict(lambda: defaultdict(lambda: 0)) + self._sc_subarray_event_id = defaultdict(lambda: defaultdict(lambda: 0)) - # _se_subarray_cmd_exec_state: implement the execution state of a long-running + # _sc_subarray_cmd_exec_state: implement the execution state of a long-running # command for each sub-element. # implemented as a nested default dictionary: # keys: sub-element FQDN # values: defaut dictionary (keys: command name, values: command state) - self._se_subarray_cmd_exec_state = defaultdict(lambda: defaultdict(lambda: CmdExecState.IDLE)) + self._sc_subarray_cmd_exec_state = defaultdict(lambda: defaultdict(lambda: CmdExecState.IDLE)) # _cmd_execution_state: implement the execution state of a long-running # command for the whole CSP sub-array Setting this attribute prevent the execution @@ -1073,12 +1050,12 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): # values:command state self._cmd_execution_state = defaultdict(lambda: CmdExecState.IDLE) - # _se_cmd_starting_time: for each sub-element report the long-running command + # _sc_cmd_starting_time: for each sub-element report the long-running command # starting time # Implemented as dictionary: # keys: the sub-element sub-array FQDN # values: starting time - self._se_subarray_cmd_starting_time = defaultdict(lambda: 0.0) + self._sc_subarray_cmd_starting_time = defaultdict(lambda: 0.0) # _command_thread: thread for the command execution # keys: the command name('on, 'off'...) @@ -1089,7 +1066,7 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): # implemented as a default nested dictionary: # keys: sub-element sub-array FQDN # values: default dictionary (keys: command name, values: the execution percentage) - self._se_subarray_cmd_progress = defaultdict(lambda: defaultdict(lambda: 0)) + self._sc_subarray_cmd_progress = defaultdict(lambda: defaultdict(lambda: 0)) # _cmd_progress: report the execution progress of a long-running command # implemented as a dictionary: @@ -1097,12 +1074,12 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): # values: the percentage self._cmd_progress = defaultdict(lambda: 0) - # _se_cmd_duration_expected: for each sub-element, store the duration (in sec.) + # _sc_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_subarray_cmd_duration_expected = defaultdict(lambda: defaultdict(lambda: 20)) + self._sc_subarray_cmd_duration_expected = defaultdict(lambda: defaultdict(lambda: 20)) # _cmd_duration_expected: store the duration (in sec.) configured for # a long-running command @@ -1118,11 +1095,11 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): # values: the duration (in sec) self._cmd_duration_measured = defaultdict(lambda: 0) - # _se_timeout_expired: report the timeout flag + # _sc_timeout_expired: report the timeout flag # Implemented as a nested default dictionary # keys: FQDN # values: default dictionary (keys: command name, values: True/False)) - self._se_subarray_timeout_expired = defaultdict(lambda: defaultdict(lambda: False)) + self._sc_subarray_timeout_expired = defaultdict(lambda: defaultdict(lambda: False)) # _timeout_expired: report the timeout flag # Implemented as a dictionary @@ -1174,8 +1151,10 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): self._assigned_timing_beams= [] self._assigned_vlbi_beams = [] - # Try connection with sub-elements - self._connect_to_subarrays() + # Try connection with the CBF sub-array + self._connect_to_subarray_subcomponent(self.CbfSubarray) + # Try connection with the PSS sub-array + self._connect_to_subarray_subcomponent(self.PssSubarray) # to use the push model in command_inout_asynch (the one with the callback parameter), # change the global TANGO model to PUSH_CALLBACK. @@ -1240,48 +1219,48 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): def read_cbfSubarrayState(self): # PROTECTED REGION ID(CspSubarray.cbfSubarrayState_read) ENABLED START # """Return the cbfSubarrayState attribute.""" - return self._se_subarray_state[self.CbfSubarray] + return self._sc_subarray_state[self.CbfSubarray] # PROTECTED REGION END # // CspSubarray.cbfSubarrayState_read def read_pssSubarrayState(self): # PROTECTED REGION ID(CspSubarray.pssSubarrayState_read) ENABLED START # """Return the pssSubarrayState attribute.""" - return self._se_subarray_state[selPssSubarray] + return self._sc_subarray_state[selPssSubarray] # PROTECTED REGION END # // CspSubarray.pssSubarrayState_read def read_cbfSubarrayHealthState(self): # PROTECTED REGION ID(CspSubarray.cbfSubarrayHealthState_read) ENABLED START # """Return the cbfSubarrayHealthState attribute.""" - return self._se_subarray_health_state[self.CbfSubarray] + return self._sc_subarray_health_state[self.CbfSubarray] # PROTECTED REGION END # // CspSubarray.cbfSubarrayHealthState_read def read_pssSubarrayHealthState(self): # PROTECTED REGION ID(CspSubarray.pssSubarrayHealthState_read) ENABLED START # """Return the pssSubarrayHealthState attribute.""" - return self._se_subarray_health_state[self.PssSubarray] + return self._sc_subarray_health_state[self.PssSubarray] # PROTECTED REGION END # // CspSubarray.pssSubarrayHealthState_read def read_cbfSubarrayAdminMode(self): # PROTECTED REGION ID(CspSubarray.cbfSubarrayHealthState_read) ENABLED START # """Return the cbfSubarrayHealthState attribute.""" - return self._se_subarray_admin_mode[self.CbfSubarray] + return self._sc_subarray_admin_mode[self.CbfSubarray] # PROTECTED REGION END # // CspSubarray.cbfSubarrayHealthState_read def read_pssSubarrayAdminMode(self): # PROTECTED REGION ID(CspSubarray.pssSubarrayHealthState_read) ENABLED START # """Return the pssSubarrayHealthState attribute.""" - return self._se_subarray_admin_mode[self.PssSubarray] + return self._sc_subarray_admin_mode[self.PssSubarray] # PROTECTED REGION END # // CspSubarray.pssSubarrayHealthState_read def read_cbfSubarrayObsState(self): # PROTECTED REGION ID(CspSubarray.cbfSubarrayObsState_read) ENABLED START # """Return the cbfSubarrayObsState attribute.""" - return self._se_subarray_obs_state[self.CbfSubarray] + return self._sc_subarray_obs_state[self.CbfSubarray] # PROTECTED REGION END # // CspSubarray.cbfSubarrayObsState_read def read_pssSubarrayObsState(self): # PROTECTED REGION ID(CspSubarray.pssSubarrayObsState_read) ENABLED START # """Return the pssSubarrayObsState attribute.""" - return self._se_subarray_obs_state[self.PssSubarray] + return self._sc_subarray_obs_state[self.PssSubarray] # PROTECTED REGION END # // CspSubarray.pssSubarrayObsState_read def read_pssSubarrayAddr(self): @@ -1326,12 +1305,6 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): return self._cmd_duration_measured['rmsearchbeam'] # PROTECTED REGION END # // CspSubarray.remSearchBeamsDurationMeasured_read - def read_searchBeamsCmdTimeoutExpired(self): - # PROTECTED REGION ID(CspSubarray.searchBeamsCmdTimeoutExpired_read) ENABLED START # - """Return the searchBeamsCmdTimeoutExpired attribute.""" - return False - # PROTECTED REGION END # // CspSubarray.searchBeamsCmdTimeoutExpired_read - def read_addTimingBeamDurationExpected(self): # PROTECTED REGION ID(CspSubarray.addTimingBeamDurationExpected_read) ENABLED START # """Return the addTimingBeamDurationExpected attribute.""" @@ -1374,12 +1347,6 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): return self._cmd_progress['scan'] # PROTECTED REGION END # // CspSubarray.endSBCmdProgress_read - def read_timingBeamsCmdTimeoutExpired(self): - # PROTECTED REGION ID(CspSubarray.timingBeamsCmdTimeoutExpired_read) ENABLED START # - """Return the timingBeamsCmdTimeoutExpired attribute.""" - return False - # PROTECTED REGION END # // CspSubarray.timingBeamsCmdTimeoutExpired_read - def read_endSBDurationExpected(self): # PROTECTED REGION ID(CspSubarray.endSBDurationExpected_read) ENABLED START # """Return the endSBDurationExpected attribute.""" @@ -1398,11 +1365,6 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): return self._cmd_progress['endsb'] # PROTECTED REGION END # // CspSubarray.endSBCmdProgress_read - def read_endSBCmdTimeoutExpired(self): - # PROTECTED REGION ID(CspSubarray.endSBCmdTimeoutExpired_read) ENABLED START # - """Return the endSBCmdTimeoutExpired attribute.""" - return False - # PROTECTED REGION END # // CspSubarray.endSBCmdTimeoutExpired_read def read_endScanDurationExpected(self): # PROTECTED REGION ID(CspSubarray.endScanDurationExpected_read) ENABLED START # @@ -1574,7 +1536,24 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): """Return the pstBeams attribute.""" return self.PstBeams # PROTECTED REGION END # // CspSubarray.pstBeams_read + + def read_cmdTimeoutExpired(self): + # PROTECTED REGION ID(CspSubarray.cmdTimeoutExpired_read) ENABLED START # + """Return the cmdTimeoutExpired attribute.""" + return False + # PROTECTED REGION END # // CspSubarray.cmdTimeoutExpired_read + + def read_cmdAlarmRaised(self): + # PROTECTED REGION ID(CspSubarray.cmdAlarmRaised_read) ENABLED START # + """Return the cmdAlarmRaised attribute.""" + return False + # PROTECTED REGION END # // CspSubarray.cmdAlarmRaised_read + def read_pstOutputLink(self): + # PROTECTED REGION ID(CspSubarray.pstOutputLink_read) ENABLED START # + """Return the pstOutputLink attribute.""" + return '' + # PROTECTED REGION END # // CspSubarray.pstOutputLink_read def read_listOfDevCompletedTask(self): # PROTECTED REGION ID(CspSubarray.listOfDevCompletedTask_read) ENABLED START # """Return the listOfDevCompletedTask attribute.""" @@ -1619,24 +1598,24 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): #TODO:the command is forwarded only to CBF. Future implementation has to # check the observing mode and depending on this, the command is forwarded to # the interested sub-elements. - for device in self._se_subarray_fqdn: + for device in self._sc_subarray_fqdn: if self._subarray_obs_mode in [ObsMode.CORRELATION]: if self._is_device_running(device): try: - proxy = self._se_subarray_proxies[self.device] + proxy = self._sc_subarray_proxies[self.device] # forward asynchrnously the command to the CbfSubarray proxy.command_inout_asynch("EndScan", self._cmd_ended) except KeyError as key_err: msg = " Can't retrieve the information of key {}".format(key_err) - self.dev_logging(msg, tango.LogLevel.LOG_ERROR) + self.logger.(msg, tango.LogLevel.LOG_ERROR) tango.Except.throw_exception("Command failed", msg, "EndScan", tango.ErrSeverity.ERR) else: log_msg = "Subarray {} not registered".format(str(self._cbf_subarray_fqdn)) - self.dev_logging(log_msg, tango.LogLevel.LOG_ERROR) + self.logger.(log_msg, tango.LogLevel.LOG_ERROR) tango.Except.throw_exception("Command failed", log_msg, "EndScan", @@ -1670,7 +1649,7 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): - set the observing mode for each sub-element sub-array/PSTBeam if "cbf" in json_config: - self._se_subarray_obs_mode[self.CbfSubarray] = ObsMode.CORRELATION + self._sc_subarray_obs_mode[self.CbfSubarray] = ObsMode.CORRELATION scan_configutation[self.CbfSubarray] = json_config["cbf"] if "pss" in json_config: scan_configutation[self.PssSubarray] = json_config["pss"] @@ -1681,7 +1660,7 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): Customize the function for each instance """ scan_configuration = {} - self._se_subarray_obs_mode[self.CbfSubarray] = ObsMode.CORRELATION + self._sc_subarray_obs_mode[self.CbfSubarray] = ObsMode.CORRELATION scan_configuration[self.CbfSubarray] = json_config return scan_configuration @command( @@ -1737,7 +1716,7 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): else: argin_dict = json.loads(argin) except FileNotFoundError as file_err: - self.dev_logging(str(file_err), tango.LogLevel.LOG_ERROR) + self.logger.(str(file_err), tango.LogLevel.LOG_ERROR) tango.Except.throw_exception("Command failed", str(file_err), "ConfigureScan execution", @@ -1746,7 +1725,7 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): # this is a fatal error msg = ("Scan configuration object is not a valid JSON object." "Aborting configuration:{}".format(str(e))) - self.dev_logging(msg, tango.LogLevel.LOG_ERROR) + self.logger.(msg, tango.LogLevel.LOG_ERROR) tango.Except.throw_exception("Command failed", msg, "ConfigureScan execution", @@ -1757,20 +1736,20 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): for device in scan_configuration_dict.keys(): if not self._is_device_running(device): log_msg = "Device {} not registered!".format(str(self._cbf_subarray_fqdn)) - self.dev_logging(log_msg, tango.LogLevel.LOG_ERROR) + self.logger.(log_msg, tango.LogLevel.LOG_ERROR) if device == self.CbfSubarray: tango.Except.throw_exception("Command failed", log_msg, "ConfigureScan execution", tango.ErrSeverity.ERR) - proxy = self._se_subarray_proxies[device] + proxy = self._sc_subarray_proxies[device] # Note: CBF/PSS sub-array checks or the validity of its # configuration. Failure in configuration throws an exception that is # caught via the cmd_ended_cb callback self._obs_state = ObsState.CONFIGURING proxy.command_inout_asynch("ConfigureScan", argin, self._cmd_ended_cb) # invoke the constructor for the timer thread - self._se_subarray_cmd_timer[device] = threading.Timer(self._se_cmd_duration_expected['config'], + self._sc_subarray_cmd_timer[device] = threading.Timer(self._sc_cmd_duration_expected['config'], self._timeout_handler_cb, {'cmd_name':'config'}) attributes = ['configureCmdProgress', 'configureCmdTimeoutExpired'] diff --git a/csp-lmc-common/pogo/CspSubarray.xmi b/csp-lmc-common/pogo/CspSubarray.xmi index c5d0d78..7dd83d2 100644 --- a/csp-lmc-common/pogo/CspSubarray.xmi +++ b/csp-lmc-common/pogo/CspSubarray.xmi @@ -1,10 +1,10 @@ - + - + @@ -15,14 +15,6 @@ - - - - - - - - @@ -32,11 +24,6 @@ 4 - - - - localhost - @@ -70,6 +57,16 @@ + + + + 4 + + + + + console::cout + @@ -286,6 +283,15 @@ + + + + + + + + + @@ -303,11 +309,6 @@ - - - - - @@ -333,11 +334,6 @@ - - - - - @@ -358,11 +354,6 @@ - - - - - @@ -517,162 +508,161 @@ - + - - + - - + + - + - + - + - + - + - + - + - + - + - + - - + - - + + - + - + - + - + - + - + - + - - + + - + + - + - + + - + - + + - + - + - + - + - - + + - + - - + + - - - + + - - + + - - + @@ -832,15 +822,19 @@ + + + + + + + - - - @@ -870,16 +864,13 @@ - - - -- GitLab From 5482dab1b43c57c4fe3c77892235052ebdff6b0a Mon Sep 17 00:00:00 2001 From: softir Date: Mon, 20 Jan 2020 12:36:53 +0100 Subject: [PATCH 29/33] AT5-262: Removed some minor bugs. --- csp-lmc-common/csp_lmc_common/CspMaster.py | 44 ++++++++----------- .../csp_lmc_common/utils/decorators.py | 2 +- 2 files changed, 20 insertions(+), 26 deletions(-) diff --git a/csp-lmc-common/csp_lmc_common/CspMaster.py b/csp-lmc-common/csp_lmc_common/CspMaster.py index d73bdb1..34680f4 100644 --- a/csp-lmc-common/csp_lmc_common/CspMaster.py +++ b/csp-lmc-common/csp_lmc_common/CspMaster.py @@ -125,18 +125,18 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): try: if dev_name in self._se_fqdn: if evt.attr_value.name.lower() == "state": - print("{}: received event on {} value {}".format(dev_name, + self.logger.debug("{}: received event on {} value {}".format(dev_name, evt.attr_value.name, evt.attr_value.value)) self._se_state[dev_name] = evt.attr_value.value elif evt.attr_value.name.lower() == "healthstate": - print("{}: received event on {} value {}".format(dev_name, + self.logger.debug("{}: received event on {} value {}".format(dev_name, evt.attr_value.name, evt.attr_value.value)) self._se_health_state[dev_name] = evt.attr_value.value elif evt.attr_value.name.lower() == "adminmode": - print("device: {} adminMode value {}".format(dev_name,evt.attr_value.value )) + self.logger.debug("device: {} adminMode value {}".format(dev_name,evt.attr_value.value )) self._se_admin_mode[dev_name] = evt.attr_value.value else: log_msg = ("Attribute {} not still " @@ -437,16 +437,13 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): #TODO: order the list alphabetically so that the CBF is always the first element to start # the TANGO command to execute tango_cmd_name = 0 - # the command progress attribute to check subscription - cmd_progress_attr = 0 # the sub-element device state after successful transition dev_successful_state = 0 try: tango_cmd_name = args_dict['cmd_name'] - cmd_progress_attr = args_dict['attr_name'] dev_successful_state = args_dict['dev_state'] except KeyError as key_err: - print("No key: {}".format(str(key_err))) + self.logger.warn("No key: {}".format(str(key_err))) # reset the CSP and sub-element running flags self._cmd_execution_state = CmdExecState.IDLE for device in device_list: @@ -456,14 +453,14 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # tango_cmd_name: is the TANGO command name with the capital letter # In the dictionary keys, is generally used the command name in lower letters cmd_name = tango_cmd_name.lower() - print("cmd_name: {} attr_name: {} dev_state: {}".format(cmd_name, - cmd_progress_attr, - dev_successful_state)) + self.logger.debug("cmd_name: {} dev_state: {}".format(cmd_name, + dev_successful_state)) num_of_failed_device = 0 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._alarm_message[cmd_name] = '' # sub-element command execution measured time se_cmd_duration_measured = defaultdict(lambda:defaultdict(lambda:0)) # loop on the devices and power-on them sequentially @@ -472,10 +469,10 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): self._se_cmd_execution_state[device][cmd_name] = CmdExecState.RUNNING se_cmd_duration_measured[device][cmd_name] = 0 self._se_cmd_progress[device][cmd_name] = 0 - self._alarm_message[cmd_name] = '' + try: device_proxy = self._se_proxies[device] - print("Issue asynch command {} on device {}:".format(cmd_name, device)) + self.logger.debug("Issue asynch command {} on device {}:".format(cmd_name, device)) # Note: when the command ends on the sub-element, the _cmd_ended_cb callback # is called. This callback sets the sub-element execution state to FAULT if @@ -491,7 +488,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # register the starting time for the command self._se_cmd_starting_time[device] = time.time() # loop on the device until the State changes to ON or a timeout occurred - print("Device {} State {} expected value {}".format(device, self._se_state[device], dev_successful_state)) + self.logger.debug("Device {} State {} expected value {}".format(device, self._se_state[device], dev_successful_state)) command_progress = self._cmd_progress[cmd_name] while True: if self._se_state[device] == dev_successful_state: @@ -504,9 +501,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # reset the value of the attribute reporting the execution state of # the command self._se_cmd_execution_state[device][cmd_name] = CmdExecState.IDLE - se_cmd_duration_measured[device][cmd_name] = (time.time() - - self._se_cmd_starting_time[device]) - self.logger.info("measured duration:", se_cmd_duration_measured[device][cmd_name]) + self._se_cmd_progress[device][cmd_name] = 100 # command success: exit from the wait loop and issue the command # on the next device in the list @@ -514,8 +509,8 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # check for other sub-element device State values if self._se_state[device] in [tango.DevState.FAULT, tango.DevState.ALARM]: self._se_cmd_execution_state[device][cmd_name] = CmdExecState.FAILED - self._alarm_message[device] = ("Device {} is {}".format(device, self.get_status())) - self.logger.warn(self._alarm_message[device]) + self._alarm_message[cmd_name] += ("Device {} is {}".format(device, self.get_status())) + self.logger.warn(self._alarm_message[cmd_name]) num_of_failed_device += 1 break # check if sub-element command ended throwing an exception: in this case the @@ -542,13 +537,12 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): if (elapsed_time > self._se_cmd_duration_expected[device][cmd_name] or self._se_cmd_execution_state[device][cmd_name] == CmdExecState.TIMEOUT): msg = ("Timeout executing {} command on device {}".format(cmd_name, device)) - print(msg) self.logger.warn(msg) self._se_cmd_execution_state[device][cmd_name] = CmdExecState.TIMEOUT num_of_failed_device += 1 # if the CBF command timeout expires, the CSP power-on is stopped # TODO: verify if this behavior conflicts with ICD - print("elapsed_time:{} device {}".format(elapsed_time, device)) + self.logger.debug("elapsed_time:{} device {}".format(elapsed_time, device)) if device == self.CspCbf: self.logger.error("CBF Timeout during power-on!!! Exit") self._timeout_expired[cmd_name] = True @@ -564,6 +558,9 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): self._cmd_progress[cmd_name] = command_progress + self._se_cmd_progress[device][cmd_name]/len(device_list) # end of the while loop # calculate the real execution time for the command + se_cmd_duration_measured[device][cmd_name] = (time.time() + - self._se_cmd_starting_time[device]) + self.logger.info("measured duration:{}".format(se_cmd_duration_measured[device][cmd_name])) self._cmd_duration_measured[cmd_name] += se_cmd_duration_measured[device][cmd_name] # update the progress counter at the end of the loop self._cmd_progress[cmd_name] = command_progress + (self._se_cmd_progress[device][cmd_name]/len(device_list)) @@ -1755,15 +1752,13 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # are passed as arguments of the function # args: the list of sub-element FQDNS # args_dict: dictionary with the specific command information - print("Send command to device", device_list) - args_dict = {'cmd_name':'On', 'attr_name': 'onCommandProgress', 'dev_state': tango.DevState.ON} + args_dict = {'cmd_name':'On', 'dev_state': tango.DevState.ON} self._command_thread['on'] = threading.Thread(target=self._issue_power_command, name="Thread-On", args=(device_list,), kwargs=args_dict) # set the CSP command execution running flag self._cmd_execution_state['on'] = CmdExecState.RUNNING # start the thread - print("starting thread") self._command_thread['on'].start() # sleep for a while to let the thread start time.sleep(0.2) @@ -1831,7 +1826,6 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # args: the list of sub-element FQDNS # args_dict: dictionary with the specific command information args_dict = {'cmd_name':'Off', - 'attr_name': 'offCommandProgress', 'dev_state': tango.DevState.ON} self._command_thread['off'] = threading.Thread(target=self._issue_power_command, name="Thread-Off", @@ -1902,7 +1896,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # cmd_name : the TANGO command name to execute # attr_name: the corresponding command progress attribute to subscribe # dev_state: the expected finale state for the device transition - args_dict = {'cmd_name':"Standby", 'attr_name':'standbyCommandProgress', + args_dict = {'cmd_name':'Standby', 'dev_state':tango.DevState.STANDBY} # invoke the constructor for the command thread self._command_thread['standby'] = threading.Thread(target=self._issue_power_command, diff --git a/csp-lmc-common/csp_lmc_common/utils/decorators.py b/csp-lmc-common/csp_lmc_common/utils/decorators.py index 0bac189..68b3684 100644 --- a/csp-lmc-common/csp_lmc_common/utils/decorators.py +++ b/csp-lmc-common/csp_lmc_common/utils/decorators.py @@ -177,7 +177,7 @@ class CmdInputArgsCheck(object): # check for devices that are not ONLINE/MAINTENANCE device_to_remove = [] for device in device_list: - self.logger.debug("CmdInputArgsCheack-processing device:", device) + dev_instance.logger.debug("CmdInputArgsCheack-processing device:", device) # if the sub-element device server is not running or the adminMode # attribute is not ONLINE or MAINTENANCE: # - schedule the device for removing from the input arg list -- GitLab From c3927775ebfa7d3fa795c4b9287ee4fc9d840f5c Mon Sep 17 00:00:00 2001 From: softir Date: Mon, 20 Jan 2020 12:38:02 +0100 Subject: [PATCH 30/33] AT5-262: first implementation of ConfigScan() method. --- csp-lmc-common/csp_lmc_common/CspSubarray.py | 505 ++++++++++--------- 1 file changed, 267 insertions(+), 238 deletions(-) diff --git a/csp-lmc-common/csp_lmc_common/CspSubarray.py b/csp-lmc-common/csp_lmc_common/CspSubarray.py index 51f4c79..758ac30 100644 --- a/csp-lmc-common/csp_lmc_common/CspSubarray.py +++ b/csp-lmc-common/csp_lmc_common/CspSubarray.py @@ -44,14 +44,10 @@ from skabase.auxiliary import utils # PROTECTED REGION ID (CspSubarray.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 decorators -from decorators import AdminModeCheck, StateAndObsStateCheck -import cspcommons -from cspcommons import HealthState, AdminMode, ObsState, ObsMode, CmdExecState -import release + +from .utils.decorators import AdminModeCheck, StateAndObsStateCheck +from .utils.cspcommons import HealthState, AdminMode, ObsState, ObsMode, CmdExecState +from . import release # PROTECTED REGION END# //CspSubarray.add_path __all__ = ["CspSubarray", "main"] @@ -143,25 +139,25 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): else: log_msg = ("Attribute {} not still " "handled".format(evt.attr_name)) - self.logger.(log_msg, tango.LogLevel.LOG_WARN) + self.logger.warn(log_msg) else: log_msg = ("Unexpected change event for" " attribute: {}".format(str(evt.attr_name))) - self.logger.(log_msg, tango.LogLevel.LOG_WARN) + self.logger.warn(log_msg) return log_msg = "New value for {} is {}".format(str(evt.attr_name), str(evt.attr_value.value)) - self.logger.(log_msg, tango.LogLevel.LOG_INFO) + self.logger.info(log_msg) # update CSP sub-array SCM if evt.attr_value.name.lower() in ["state", "healthstate"]: self._update_subarray_state() - if evt.attr_value.name.lower() == "obsstate": - self._update_subarray_obs_state() + #if evt.attr_value.name.lower() == "obsstate": + # self._update_subarray_obs_state() except tango.DevFailed as df: - self.logger.(str(df.args[0].desc), tango.LogLevel.LOG_ERROR) + self.logger.error(str(df.args[0].desc)) except Exception as except_occurred: - self.logger.(str(except_occurred), tango.LogLevel.LOG_ERROR) + self.logger.error(str(except_occurred)) else: for item in evt.errors: # API_EventTimeout: if sub-element device not reachable it transitions @@ -180,64 +176,9 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): # update the State and healthState of the CSP sub-array self._update_subarray_state() log_msg = item.reason + ": on attribute " + str(evt.attr_name) - self.logger.(log_msg, tango.LogLevel.LOG_WARN) - - def _subarray_resources_scm_change_event_cb(self, evt): - """ - Class protected callback function. - Retrieve the SCM attributes of all CSP Capabilities assigned to - the sub-array. - - :param evt: The event data - - :return: None - """ - dev_name = evt.device.dev_name() - if not evt.err: - try: - if dev_name in self._subarray_resources_fqdn: - if evt.attr_value.name.lower() == "state": - self._subarray_resources_state[dev_name] = evt.attr_value.value - #self._sc_state = {key, value for key,value in self._subarray_resources_state.item if key.find("subarray") > -1} - - elif evt.attr_value.name.lower() == "healthstate": - self._subarray_resources_health_state[dev_name] = evt.attr_value.value - elif evt.attr_value.name.lower() == "adminmode": - self.logger.debug("device: {} adminMode value {}".format(dev_name,evt.attr_value.value )) - self._subarray_resources_admin_mode[dev_name] = evt.attr_value.value - elif evt.attr_value.name.lower() == "obsstate": - self.logger.debug("device: {} obsState value {}".format(dev_name,evt.attr_value.value )) - self._subarray_resources_obs_state[dev_name] = evt.attr_value.value - elif evt.attr_value.name.lower() == "obsmode": - self.logger.debug("device: {} obsMode value {}".format(dev_name,evt.attr_value.value )) - self._subarray_resources_obs_mode[dev_name] = evt.attr_value.value - else: - log_msg = ("Attribute {} not still " - "handled".format(evt.attr_name)) - self.logger.warn(log_msg) - else: - log_msg = ("Unexpected change event for" - " attribute: {}".format(str(evt.attr_name))) - self.logger.warn(log_msg) - return - - log_msg = "New value for {} is {}".format(str(evt.attr_name), - str(evt.attr_value.value)) - self.logger.info(log_msg) - except tango.DevFailed as df: - self.logger.error(str(df.args[0].desc)) - except Exception as except_occurred: - self.logger.error(str(except_occurred)) - else: - for item in evt.errors: - # API_EventTimeout: if sub-element device not reachable it transitions - # to UNKNOWN state. - if item.reason == "API_EventTimeout": - self._subarray_resources_state[dev_name] = tango.DevState.UNKNOWN - self._subarray_resources_health_state[dev_name] = HealthState.UNKNOWN - log_msg = item.reason + ": on attribute " + str(evt.attr_name) self.logger.warn(log_msg) + def _attributes_change_evt_cb(self, evt): """ Class protected callback function. @@ -259,7 +200,7 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): # removing the last 15 chars cmd_name = evt.attr_value.name[:-11] self._sc_subarray_cmd_progress[dev_name][cmd_name] = evt.attr_value.value - elif if "cmdtimeoutexpired" == evt.attr_value.name.lower()[-17:]: + elif "cmdtimeoutexpired" == evt.attr_value.name.lower()[-17:]: if dev_name in self._sc_subarray_fqdn: cmd_name = evt.attr_value.name[:-17] self._sc_subarray__timeout_expired[dev_name][cmd] = True @@ -316,12 +257,8 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): msg = "Error!!Command {} ended on device {}.\n".format(evt.cmd_name, evt.device.dev_name()) msg += " Desc: {}".format(evt.errors[0].desc) - #self.logger.(msg, tango.LogLevel.LOG_ERROR) - if evt.cmd_name == "ConfigureScan": - # set to IDLE the obstate of the failed sub-array/PSTBeam - self._sc_subarray_obs_state[evt.device] = ObsState.IDLE self.logger.info(msg) - self._sc_cmd_execution_state[evt.device.dev_name()][evt.cmd_name.lower()] = CmdExecState.IDLE + self._sc_cmd_execution_state[evt.device.dev_name()][evt.cmd_name.lower()] = CmdExecState.FAULT # obsState and obsMode values take on the CbfSubarray's values via # the subscribe/publish mechanism else: @@ -333,12 +270,6 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): except Exception as ex: msg = "CommandCallBack cmd_ended general exception: {}".format(str(ex)) self.logger.error(msg) - - def _timeout_handler_cb(self, args = None, **dict_args): - cmd_expired = dict_args['cmd_name'] - device = dict_args['device'] - self._sc_subarray_cmd_exec_state[device][cmd_expired] = CmdExecState.IDLE - self._sc_subarray_timeout_expired[device][cmd_expired] = True # Class protected methods # --------------- @@ -380,7 +311,7 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): # Note: when the Subarray adminMode is set OFFLINE/RESERVED/NOTFITTED # all its allocated resources are released and its State moves to DISABLE. # - if self._state in [tango.DevState.OFF, tango.DevState.DISABLE]: + if self.get_state() in [tango.DevState.OFF, tango.DevState.DISABLE]: self._health_state = HealthState.UNKNOWN # The whole CspSubarray HealthState is OK if is ON and all its assigned sub-components @@ -395,20 +326,20 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): admin_fqdn = [fqdn for fqdn, admin_value in self._sc_subarray_admin_mode.items() if admin_value in [AdminMode.ONLINE, AdminMode.MAINTENANCE]] # build the list of sub-elements with State ON - state_fqdn = [fqdn for fqdn in admin_fqdn if self._sc_state[fqdn] == tango.DevState.ON] + state_fqdn = [fqdn for fqdn in admin_fqdn if self._sc_subarray_state[fqdn] == tango.DevState.ON] # build the list with the healthState of ONLINE/MAINTENANCE devices - health_list = [self._sc_health_state[fqdn] for fqdn in state_fqdn] + health_list = [self._sc_subarray_health_state[fqdn] for fqdn in state_fqdn] if self.CbfSubarray in admin_fqdn: if all(value == HealthState.OK for value in health_list): self._health_state = HealthState.OK - elif self._sc_health_state[self.CbfSubarray] in [HealthState.FAILED, + elif self._sc_subarray_health_state[self.CbfSubarray] in [HealthState.FAILED, HealthState.UNKNOWN, HealthState.DEGRADED]: self._health_state = self._sc_health_state[self.CbfSubarray] else: # if CBF Subarray is not ONLINE/MAINTENANCE .... - self._health_state = self._sc_health_state[self.CbfSubarray] + self._health_state = self._sc_subarray_health_state[self.CbfSubarray] return def _connect_to_subarray_subcomponent(self, fqdn): @@ -427,7 +358,7 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): return # read the sub-componet adminMode (memorized) attribute from # the CSP.LMC TANGO DB. - attribute_properties = csplmc_tango_db.get_device_attribute_property(fqdn, + attribute_properties = self._csp_tango_db.get_device_attribute_property(fqdn, {'adminMode': ['__value']}) self.logger.info("fqdn: {} attribute_properties: {}".format(fqdn, attribute_properties)) try: @@ -735,13 +666,6 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): doc="The progress percentage for the EndSB command.", ) - endSBCmdTimeoutExpired = attribute( - dtype='DevBoolean', - label="EndSB command timeout flag", - polling_period=1000, - doc="The timeout flag for the EndSB command.", - ) - endScanDurationExpected = attribute( dtype='DevUShort', label="EndScan command duration expected", @@ -820,8 +744,6 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): doc="The output link for PST products.", ) - - assignedSearchBeamIDs = attribute( dtype=('DevUShort',), max_dim_x=1500, @@ -952,15 +874,15 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): label="cbfOutputLink", forwarded=True ) - pssOutputLink = attribute(name="pssOutputLink", - label="cbfOutputLink", - forwarded=True - ) + #pssOutputLink = attribute(name="pssOutputLink", + # label="cbfOutputLink", + # forwarded=True + #) - vlbiOutputLink = attribute(name="vlbiOutputLink", - label="cbfOutputLink", - forwarded=True - ) + #vlbiOutputLink = attribute(name="vlbiOutputLink", + # label="cbfOutputLink", + # forwarded=True + #) # --------------- # General methods # --------------- @@ -983,7 +905,7 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): # use defaultdict to initialize the sub-element State,healthState # and adminMode. The dictionary uses as keys the sub-element # fqdn, for example - # self._sc_state[self.CspCbf] + # self._sc_subarray_state[self.CspCbf] # return the State value of the Mid Cbf sub-element. self._sc_subarray_state = defaultdict(lambda: tango.DevState.DISABLE) self._sc_subarray_health_state = defaultdict(lambda: HealthState.UNKNOWN) @@ -1010,24 +932,18 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): except KeyError as key_err: self.logger.info("Key {} not found".format(key_err)) - # list of sub-array sub-component FQDNs self._sc_subarray_fqdn = [] + + # list of sub-component FQDNs assigned to the sub-array + self._sc_subarray_assigned_fqdn = [] + # _sc_subarray_proxies: the sub-element sub-array proxies # implementes as dictionary: # keys: sub-element sub-arrayFQDN # values: device proxy self._sc_subarray_proxies = {} - # use defaultdict to initialize the sub-array resources State, - # healthState, obsState and and adminMode attributes - # keys: resources fqdn - # values: the SCM attribute value - self._subarray_resources_state = defaultdict(lambda:tango.DevState.DISABLE) - self._subarray_resources_health_state = defaultdict(lambda:HealthState.UNKNOWN) - self._subarray_resources_admin_mode = defaultdict(lambda:AdminMode.NOTFITTED) - self._subarray_resources_obs_state = defaultdict(lambda:ObsState.IDLE) - # Nested default dictionary with list of event ids/CSP sub-array sub-component. Need to # store the event ids for each CSP sub-array component and attribute to un-subscribe # them at disconnection. @@ -1036,20 +952,12 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): self._sc_subarray_event_id = defaultdict(lambda: defaultdict(lambda: 0)) # _sc_subarray_cmd_exec_state: implement the execution state of a long-running - # command for each sub-element. + # command for each sub-array sub-component. # implemented as a nested default dictionary: # keys: sub-element FQDN # values: defaut dictionary (keys: command name, values: command state) self._sc_subarray_cmd_exec_state = defaultdict(lambda: defaultdict(lambda: CmdExecState.IDLE)) - # _cmd_execution_state: implement the execution state of a long-running - # command for the whole CSP sub-array Setting this attribute prevent the execution - # of the same command while it is already running. - # implemented as a default dictionary: - # keys: command name - # values:command state - self._cmd_execution_state = defaultdict(lambda: CmdExecState.IDLE) - # _sc_cmd_starting_time: for each sub-element report the long-running command # starting time # Implemented as dictionary: @@ -1068,12 +976,6 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): # values: default dictionary (keys: command name, values: the execution percentage) self._sc_subarray_cmd_progress = defaultdict(lambda: defaultdict(lambda: 0)) - # _cmd_progress: report the execution progress of a long-running command - # implemented as a dictionary: - # keys: command name ('addbeams', 'removebeams', 'configure') - # values: the percentage - self._cmd_progress = defaultdict(lambda: 0) - # _sc_cmd_duration_expected: for each sub-element, store the duration (in sec.) # configured for a long-running command # Implemented as a nested default dictionary @@ -1081,6 +983,26 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): # values: default dictionary (keys: command name, values: the duration (in sec)) self._sc_subarray_cmd_duration_expected = defaultdict(lambda: defaultdict(lambda: 20)) + # _sc_timeout_expired: report the timeout flag + # Implemented as a nested default dictionary + # keys: FQDN + # values: default dictionary (keys: command name, values: True/False)) + self._sc_subarray_timeout_expired = defaultdict(lambda: defaultdict(lambda: False)) + + # _cmd_execution_state: implement the execution state of a long-running + # command for the whole CSP sub-array Setting this attribute prevent the execution + # of the same command while it is already running. + # implemented as a default dictionary: + # keys: command name + # values:command state + self._cmd_execution_state = defaultdict(lambda: CmdExecState.IDLE) + + # _cmd_progress: report the execution progress of a long-running command + # implemented as a dictionary: + # keys: command name ('addbeams', 'removebeams', 'configure') + # values: the percentage + self._cmd_progress = defaultdict(lambda: 0) + # _cmd_duration_expected: store the duration (in sec.) configured for # a long-running command # Implemented asdefault dictionary @@ -1095,17 +1017,11 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): # values: the duration (in sec) self._cmd_duration_measured = defaultdict(lambda: 0) - # _sc_timeout_expired: report the timeout flag - # Implemented as a nested default dictionary - # keys: FQDN - # values: default dictionary (keys: command name, values: True/False)) - self._sc_subarray_timeout_expired = defaultdict(lambda: defaultdict(lambda: False)) - # _timeout_expired: report the timeout flag - # Implemented as a dictionary - # keys: command name ('addsearchbeam',...) - # values: True/False)) - self._timeout_expired = defaultdict(lambda: False) + self._timeout_expired = False + + # _alarm_raised: report the alarm flag + self._alarm_raised = False # _list_dev_completed_task: for each long-running command report the list of subordinate # components that completed the task @@ -1126,23 +1042,6 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): # _valid_scan_configuration: the last programmed scan configuration self._valid_scan_configuration = '' - # initialize CSP SearchBeam, TimingBeam and VlbiBeam Capabilities State, - # healthState, adminMode and obsState - # keys: device FQDN - # values: the attribute value - self._search_beams_state = defaultdict(lambda: tango.DevState.DISABLE) - self._search_beams_health_state = defaultdict(lambda: HealthState.UNKNOWN) - self._search_beams_admin_mode = defaultdict(lambda: tango.DevState.NOTFITTED) - self._search_beams_obs_state = defaultdict(lambda: ObsState.IDLE) - self._timing_beams_state = defaultdict(lambda: tango.DevState.DISABLE) - self._timing_beams_health_state = defaultdict(lambda: HealthState.UNKNOWN) - self._timing_beams_admin_mode = defaultdict(lambda: tango.DevState.NOTFITTED) - self._timing_beams_obs_state = defaultdict(lambda: ObsState.IDLE) - self._vlbi_beams_state = defaultdict(lambda: tango.DevState.DISABLE) - self._vlbi_beams_health_state = defaultdict(lambda: HealthState.UNKNOWN) - self._vlbi_beams_admin_mode = defaultdict(lambda: tango.DevState.NOTFITTED) - self._vlbi_beams_obs_state = defaultdict(lambda: ObsState.IDLE) - # unassigned CSP SearchBeam, TimingBeam, VlbiBeam Capability IDs self._assigned_search_beams = [] #self._unassigned_search_beam_num = 0 @@ -1365,7 +1264,6 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): return self._cmd_progress['endsb'] # PROTECTED REGION END # // CspSubarray.endSBCmdProgress_read - def read_endScanDurationExpected(self): # PROTECTED REGION ID(CspSubarray.endScanDurationExpected_read) ENABLED START # """Return the endScanDurationExpected attribute.""" @@ -1384,12 +1282,6 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): return self._cmd_progress['endscan'] # PROTECTED REGION END # // CspSubarray.endScanCmdProgress_read - def read_endScanCmdTimeoutExpired(self): - # PROTECTED REGION ID(CspSubarray.endScanCmdTimeoutExpired_read) ENABLED START # - """Return the endScanCmdTimeoutExpired attribute.""" - return False - # PROTECTED REGION END # // CspSubarray.endScanCmdTimeoutExpired_read - def read_reservedSearchBeamNum(self): # PROTECTED REGION ID(CspSubarray.reservedSearchBeamNum_read) ENABLED START # """Return the reservedSearchBeamNum attribute.""" @@ -1564,7 +1456,135 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): # -------- # Commands # -------- - + + def __issue_observing_cmd(self, device_list, **args_dict): + tango_cmd_name = 0 + # the sub-element device state after successful transition + dev_successful_state = 0 + try: + tango_cmd_name = args_dict['cmd_name'] + dev_successful_state = args_dict['dev_state'] + except KeyError as key_err: + self.logger.warn("No key: {}".format(str(key_err))) + return + # tango_cmd_name: is the TANGO command name with the capital letter + # In the dictionary keys, is generally used the command name in lower letters + cmd_name = tango_cmd_name.lower() + self.logger.info("cmd_name: {} dev_state: {}".format(cmd_name, + dev_successful_state)) + 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 + # sub-component command execution measured time + sc_cmd_duration_measured = defaultdict(lambda:defaultdict(lambda:0)) + # loop on the devices and power-on them sequentially + for device in device_list: + # set the sub-element command execution flag + self._sc_cmd_execution_state[device][cmd_name] = CmdExecState.RUNNING + sc_cmd_duration_measured[device][cmd_name] = 0 + self._sc_cmd_progress[device][cmd_name] = 0 + self._alarm_message[cmd_name] = '' + try: + device_proxy = self._sc_subarray_proxies[device] + self.logger.debug("Issue asynch command {} on device {}:".format(cmd_name, + device)) + + except KeyError as key_err: + msg = "No key {} found".format(str(key_err)) + self.logger.warn(msg) + except tango.DevFailed as df: + # It should not happen! Verify + msg = "Failure reason: {} Desc: {}".format(str(df.args[0].reason), str(df.args[0].desc)) + self.logger.warn(msg) + + self.logerror.info("Device {} State {} expected value {}".format(device, self._se_state[device], dev_successful_state)) + command_progress = self._cmd_progress[cmd_name] + # inside the end-less lop chek the obsState of each sub-component + device_ended_command= [] + while True: + for device in device_list: + if device in device_ended_command: + continue + # 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: + self.logger.info("Command {} ended with success on device {}.".format(cmd_name, + device)) + # update the list and number of device that completed the task + self._num_dev_completed_task[cmd_name] += 1 + self._list_dev_completed_task[cmd_name].append(device) + # reset the value of the attribute reporting the execution state of + # the command + self._sc_cmd_execution_state[device][cmd_name] = CmdExecState.IDLE + self._sc_cmd_progress[device][cmd_name] = 100 + # command success: step to next devic + device_ended_command.append(device) + continue + # check for obsState failure + if self._sc_obs_state[device] in [ObsState.FAULT, ObsState.ABORTED]: + self._sc_cmd_execution_state[device][cmd_name] = CmdExecState.FAILED + self._alarm_message[cmd_name] += ("Device {} is {}".format(device, self.get_status())) + self.logger.warn(self._alarm_message[device]) + device_ended_command.append(device) + continue + # check if sub-element command ended throwing an exception: in this case the + # 'cmd_ended_cb' callback is invoked. + if self._se_cmd_execution_state[device][cmd_name] == CmdExecState.FAILED: + # execution ended for this sub-element, skip to the next one + device_ended_command.append(device) + continue + # check for timeout event. A timeout event can be detected in two ways: + # 1- the sub-element implements the 'onTimeoutExpired' attribute configured + # for change event + # 2- the CspMaster 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._se_cmd_starting_time[device] + if (elapsed_time > self._sc_cmd_duration_expected[device][cmd_name] or + self._sc_cmd_execution_state[device][cmd_name] == CmdExecState.TIMEOUT): + msg = ("Timeout executing {} command on device {}".format(cmd_name, device)) + self.logger.warn(msg) + self._se_cmd_execution_state[device][cmd_name] = CmdExecState.TIMEOUT + + # if the CBF command timeout expires, the CSP power-on is stopped + # TODO: verify if this behavior conflicts with ICD + self.logger.info("elapsed_time:{} device {}".format(elapsed_time, device)) + device_ended_command.append(device) + continue + # 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_cmd_progress[device][cmd_name]/len(device_list) + if len(device_list) == len(device_ended_command): + self.logger.info("All devices have been handled!") + # end of the command: the command has been issued on all the sub-element devices + # reset the execution flag for the CSP + break + time.sleep(1) + # end of the while loop + self._last_executed_command = cmd_name + # if one or more sub-elements goes in timeout or alarm, set the CSP Subarray + # corresponding attribute + for device in device_list: + # reset the CSP sub-element command execution flag + if self._se_cmd_execution_state[device][cmd_name] == CmdExecState.TIMEOUT: + # set the CSP timeout flag + self._timeout_expired[cmd_name] = True + if self._se_cmd_execution_state[device][cmd_name] == CmdExecState.FAILED: + # set the CSP timeout flag + self._alarm_raised[cmd_name] = True + # reset the CSP sub-element command execution flag + self._se_cmd_execution_state[device][cmd_name] = CmdExecState.IDLE + # calculate the real execution time for the command + self._cmd_duration_measured[cmd_name] += sc_cmd_duration_measured[device][cmd_name] + # update the progress counter at the end of the loop + self._cmd_progress[cmd_name] = command_progress + (self._se_cmd_progress[device][cmd_name]/len(device_list)) + if all(value == dev_successful_state for value in self._sc_subarray_obs_state): + self._obs_state = dev_successful_state + @command( ) @DebugIt() @@ -1584,44 +1604,14 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): Still to implement the check on AdminMode values: the command can be processed \ only when the CspSubarray is *ONLINE* or *MAINTENANCE* """ - # PROTECTED REGION ID(CspSubarray.EndScan) ENABLED START # - # Check if the EndScan command can be executed. This command is allowed when the - # Subarray State is SCANNING. - if self._obs_state != ObsState.SCANNING: - log_msg = ("Subarray obs_state is {}" - ", not SCANNING".format(ObsState(self._obs_state).name)) - tango.Except.throw_exception("Command failed", - log_msg, - "EndScan", - tango.ErrSeverity.ERR) - proxy = 0 - #TODO:the command is forwarded only to CBF. Future implementation has to - # check the observing mode and depending on this, the command is forwarded to - # the interested sub-elements. for device in self._sc_subarray_fqdn: - if self._subarray_obs_mode in [ObsMode.CORRELATION]: - if self._is_device_running(device): - - try: - proxy = self._sc_subarray_proxies[self.device] - # forward asynchrnously the command to the CbfSubarray - proxy.command_inout_asynch("EndScan", self._cmd_ended) - except KeyError as key_err: - msg = " Can't retrieve the information of key {}".format(key_err) - self.logger.(msg, tango.LogLevel.LOG_ERROR) - tango.Except.throw_exception("Command failed", - msg, - "EndScan", - tango.ErrSeverity.ERR) - else: - log_msg = "Subarray {} not registered".format(str(self._cbf_subarray_fqdn)) - self.logger.(log_msg, tango.LogLevel.LOG_ERROR) - tango.Except.throw_exception("Command failed", - log_msg, - "EndScan", - tango.ErrSeverity.ERR) + try: + proxy = self._sc_subarray_proxies[device] + except KeyError as key_err: + self.logger.warn("No key {} found".format(key_err)) + proxy.command_inout_asynch("EndScan", self._cmd_ended_cb) # PROTECTED REGION END # // CspSubarray.EndScan - + @command( dtype_in='DevVarStringArray', ) @@ -1637,9 +1627,24 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): :return:None """ - pass - # PROTECTED REGION END # // CspSubarray.Scan - + # invoke the constructor for the command thread + self._cmd_progress['scan'] = 0 + for device in self._sc_subarray_fqdn: + try: + proxy = self._sc_subarray_proxies[device] + if not self._sc_subarray_event_id[device]['scancmdprogress']: + evt_id = proxy.subscribe_event("scanCmdProgress", + tango.EventType.CHANGE_EVENT, + self._sc_attributes_change_event_cb, + stateless=False) + self._sc_subarray_event_id[device]['scancmdprogress'] = evt_id + except KeyError as key_err: + self.logger.warn("No key {} found".format(key_err)) + except tango.DevFailed as tango_err: + self.logger.info(tango_err.args[0].desc) + proxy.command_inout_asynch("Scan", self._cmd_ended_cb) + + # PROTECTED REGION END # // CspSubarray.Scan def __validate_scan_configuration(self, json_config): """ here is done a preliminary validation of the configuraito: @@ -1654,15 +1659,31 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): if "pss" in json_config: scan_configutation[self.PssSubarray] = json_config["pss"] if "pst" in json_config: - scan_configuration[self.PssSubarray] = json_config["pss"] + scan_configuration[self.PssSubarray] = json_config["pst"] return scan_configuration_dict Customize the function for each instance """ - scan_configuration = {} - self._sc_subarray_obs_mode[self.CbfSubarray] = ObsMode.CORRELATION - scan_configuration[self.CbfSubarray] = json_config - return scan_configuration + if "cbf" in json_config: + if self._sc_subarray_state[self.CbfSubarray] != tango.DevState.ON: + pass + # throw exception + self._sc_subarray_obs_mode[self.CbfSubarray] = ObsMode.CORRELATION + self._sc_scan_configuration[self.CbfSubarray] = json_config["cbf"] + self._sc_subarray_assigned_fqdn.append(self.CbfSubarray) + if "pss" in json_config: + if self._sc_subarray_state[self.PssSubarray] != tango.DevState.ON: + pass + # throw exception + #self._sc_subarray_obs_mode[self.PssSubarray] = ?? + self._sc_subarray_assigned_fqdn.append(self.PssSubarray) + self._sc_scan_configuration[self.PssSubarray] = json_config["pss"] + if "searchbeams" not in self._sc_scan_configuration[self.PssSubarray]: + self._sc_scan_configuration[self.PssSubarray]["searchbeams"] = self._assigned_search_beams + if "pst" in json_config: + + pass + @command( dtype_in='DevString', doc_in="A Json-encoded string with the scan configuration.", @@ -1716,7 +1737,7 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): else: argin_dict = json.loads(argin) except FileNotFoundError as file_err: - self.logger.(str(file_err), tango.LogLevel.LOG_ERROR) + self.logger.error(file_err) tango.Except.throw_exception("Command failed", str(file_err), "ConfigureScan execution", @@ -1725,33 +1746,20 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): # this is a fatal error msg = ("Scan configuration object is not a valid JSON object." "Aborting configuration:{}".format(str(e))) - self.logger.(msg, tango.LogLevel.LOG_ERROR) + self.logger.error(msg) tango.Except.throw_exception("Command failed", msg, "ConfigureScan execution", tango.ErrSeverity.ERR) - scan_configuration_dict = self._validate_scan_configuration(argin) + self.__validate_scan_configuration(argin) + self._obs_state = ObsState.CONFIGURING # Forward the ConfigureScan command to CbfSubarray. - # check connection with CbfSubarray - for device in scan_configuration_dict.keys(): - if not self._is_device_running(device): - log_msg = "Device {} not registered!".format(str(self._cbf_subarray_fqdn)) - self.logger.(log_msg, tango.LogLevel.LOG_ERROR) - if device == self.CbfSubarray: - tango.Except.throw_exception("Command failed", - log_msg, - "ConfigureScan execution", - tango.ErrSeverity.ERR) + self._timeout_expired['configurescan'] = False + self._alarm_raised['configurescan'] = False + device_to_configure = [] + for device in self._sc_subarray_assigned_fqdn: + json_config = json.dumps(self._sc_scan_configuration[device]) proxy = self._sc_subarray_proxies[device] - # Note: CBF/PSS sub-array checks or the validity of its - # configuration. Failure in configuration throws an exception that is - # caught via the cmd_ended_cb callback - self._obs_state = ObsState.CONFIGURING - proxy.command_inout_asynch("ConfigureScan", argin, self._cmd_ended_cb) - # invoke the constructor for the timer thread - self._sc_subarray_cmd_timer[device] = threading.Timer(self._sc_cmd_duration_expected['config'], - self._timeout_handler_cb, - {'cmd_name':'config'}) attributes = ['configureCmdProgress', 'configureCmdTimeoutExpired'] for attr in attributes: try: @@ -1760,7 +1768,20 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): self._attributes_change_evt_cb, stateless=False) self._subarray_resources_event_id[device][attr] = event_id except tango.DevFailed as df: - print(df.args[0].desc) + self.logger.info(df.args[0].desc) + # 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 + proxy.command_inout_asynch("ConfigureScan", argin, self._cmd_ended_cb) + # register the starting time for the command + self._sc_cmd_starting_time[device] = time.time() + args_dict = {'cmd_name':"ConfigureScan", + 'obs_state':ObsState.READY} + # invoke the constructor for the command thread + self._command_thread['configurescan'] = threading.Thread(target=self.__issue_observing_cmd, + name="Thread-ConfigureScan", + args=(self._sc_subarray_assigned_fqdn,), + kwargs=args_dict) # PROTECTED REGION END # // CspSubarray.ConfigureScan @command( @@ -1772,12 +1793,10 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): @StateAndObsStateCheck('AddBeam') def AddNumOfSearchBeams(self, argin): # PROTECTED REGION ID(CspSubarray.AddNumOfSearchBeams) ENABLED START # - - - # PROTECTED REGION END # // CspSubarray.ConfigureScan - + self._cmd_execution_state['addsearchbeams'] = CmdExecState.RUNNING # PROTECTED REGION END # // CspSubarray.AddNumOfSearchBeams pass + @command( dtype_in='DevUShort', doc_in="The number of SearchBeam Capabilities to remove from the \nCSP sub-array.\nAll the assigned SearcBbeams are removed from the CSP sub-array if the\ninput number is equal to the max number of SearchBeam \nCapabilities for the specified Csp sub-array instance (1500 for MID,\n500 for LOW)", @@ -1803,6 +1822,10 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): :return:None """ + if self._cmd_execution_state['addsearchbeams'] == CmdExecState.RUNNING: + # throw an exception + self.logger.warn("ASearchBeams command is already running!!") + return pass # PROTECTED REGION END # // CspSubarray.RemoveNumOfSearchBeams @@ -1825,6 +1848,7 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): :return:None """ + self._cmd_execution_state['addtimingbeams'] = CmdExecState.RUNNING pass # PROTECTED REGION END # // CspSubarray.AddTimingBeams @@ -1847,6 +1871,7 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): :return:None """ + self._cmd_execution_state['addsearchbeams'] = CmdExecState.RUNNING pass # PROTECTED REGION END # // CspSubarray.AddSearchBeamsID @@ -1889,6 +1914,10 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): :return:None """ + if self._cmd_execution_state['addtimingbeams'] == CmdExecState.RUNNING: + # throw an exception + self.logger.warn("ADDtimingBeams command is already running!!") + return pass # PROTECTED REGION END # // CspSubarray.RemoveTimingBeams -- GitLab From dea603fcee0823bc2296933f89b2f3198940eb47 Mon Sep 17 00:00:00 2001 From: softir Date: Thu, 23 Jan 2020 13:13:28 +0100 Subject: [PATCH 31/33] AT5-262: minor changes. --- .../csp_lmc_common/CspCapabilityMonitor.py | 2 +- csp-lmc-common/csp_lmc_common/CspMaster.py | 18 +++++++----------- csp-lmc-mid/csp-lmc-mid/MidCspMasterBase.py | 19 ++++++++----------- 3 files changed, 16 insertions(+), 23 deletions(-) diff --git a/csp-lmc-common/csp_lmc_common/CspCapabilityMonitor.py b/csp-lmc-common/csp_lmc_common/CspCapabilityMonitor.py index f15b7d2..62d9931 100644 --- a/csp-lmc-common/csp_lmc_common/CspCapabilityMonitor.py +++ b/csp-lmc-common/csp_lmc_common/CspCapabilityMonitor.py @@ -151,7 +151,7 @@ class CspCapabilityMonitor(with_metaclass(DeviceMeta,SKABaseDevice)): try: admin_mode_memorized = attribute_properties['adminMode']['__value'] self._admin_mode = int(admin_mode_memorized[0]) - except keyError as key_err: + except KeyError as key_err: self.logger.info("No {} key found".format(str(key_err))) self._connect_to_capability() self._healthState = HealthState.OK diff --git a/csp-lmc-common/csp_lmc_common/CspMaster.py b/csp-lmc-common/csp_lmc_common/CspMaster.py index 34680f4..acaa175 100644 --- a/csp-lmc-common/csp_lmc_common/CspMaster.py +++ b/csp-lmc-common/csp_lmc_common/CspMaster.py @@ -21,8 +21,7 @@ import threading import time # 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 @@ -32,19 +31,15 @@ from tango.server import device_property from tango import AttrQuality, EventType, DevState from tango import AttrWriteType, DeviceProxy # Additional import -# PROTECTED REGION ID(CspMaster.additionnal_import) ENABLED START # +# PROTECTED REGION ID(CspMaster.additional_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. from .utils.decorators import AdminModeCheck, CmdInputArgsCheck from .utils.cspcommons import HealthState, AdminMode, ObsState, CmdExecState from . import release -# PROTECTED REGION END# //CspMaster.add_path +# PROTECTED REGION END# //CspMaster.additional_import __all__ = ["CspMaster", "main"] @@ -506,8 +501,8 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): # command success: exit from the wait loop and issue the command # on the next device in the list break - # check for other sub-element device State values - if self._se_state[device] in [tango.DevState.FAULT, tango.DevState.ALARM]: + # check for other sub-element FAULT values + if self._se_state[device] == tango.DevState.FAULT: self._se_cmd_execution_state[device][cmd_name] = CmdExecState.FAILED self._alarm_message[cmd_name] += ("Device {} is {}".format(device, self.get_status())) self.logger.warn(self._alarm_message[cmd_name]) @@ -523,6 +518,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): if self._se_cmd_execution_state[device][cmd_name] == CmdExecState.FAILED: # execution ended for this sub-element, skip to the next one num_of_failed_device += 1 + # skip to next device break # check for timeout event. A timeout event can be detected in two ways: # 1- the sub-element implements the 'onTimeoutExpired' attribute configured @@ -1366,7 +1362,7 @@ class CspMaster(with_metaclass(DeviceMeta, SKAMaster)): if event_id: self._se_proxies[fqdn].unsubscribe_event(event_id) event_to_remove[fqdn].append(attr_name) - # NOTE: in PyTango unsubscription of not-existing event + # NOTE: in tango unsubscription of not-existing event # id raises a KeyError exception not a DevFailed !! except KeyError as key_err: msg = ("Failure unsubscribing event {} " diff --git a/csp-lmc-mid/csp-lmc-mid/MidCspMasterBase.py b/csp-lmc-mid/csp-lmc-mid/MidCspMasterBase.py index 008216e..39fa80b 100644 --- a/csp-lmc-mid/csp-lmc-mid/MidCspMasterBase.py +++ b/csp-lmc-mid/csp-lmc-mid/MidCspMasterBase.py @@ -16,14 +16,13 @@ implemented in separate TANGO Devices. # 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 # PROTECTED REGION END# //CspMaster.standardlibray_import -# tango imports# PROTECTED REGION ID (CspMaster.add_path) ENABLED START # +# tango imports import tango from tango import DebugIt @@ -34,18 +33,14 @@ from tango.server import device_property from tango import AttrQuality, EventType, DevState from tango import AttrWriteType, DeviceProxy # Additional import -# PROTECTED REGION ID(CspMaster.additionnal_import) ENABLED START # +# PROTECTED REGION ID(MidCspMaster.additional_import) ENABLED START # # from skabase.SKAMaster import SKAMaster from skabase.auxiliary import utils -# PROTECTED REGION END # // CspMaster.additionnal_import -file_path = os.path.dirname(os.path.abspath(__file__)) -print(file_path) -# PROTECTED REGION ID (CspMaster.add_path) ENABLED START # -# add the path to import global_enum package. +# import CSP.LMC Common package from csp_lmc_common.utils.cspcommons import HealthState, AdminMode from csp_lmc_common.CspMaster import CspMaster -# PROTECTED REGION END# //CspMaster.add_path +# PROTECTED REGION END # // MidCspMaster.additionnal_import __all__ = ["MidCspMasterBase", "main"] @@ -427,7 +422,8 @@ class MidCspMasterBase(with_metaclass(DeviceMeta, CspMaster)): # PROTECTED REGION ID(CspMaster.availableReceptorIDs_read) ENABLED START # self._unassigned_receptor_id.clear() if not self._is_device_running(self.CspCbf, self._se_proxies): - return self._unassigned_receptor_id + return [0] + #return self._unassigned_receptor_id[0] try: proxy = self._se_proxies[self.CspCbf] # get the State and sub-array affiliation of the VCC @@ -484,7 +480,8 @@ class MidCspMasterBase(with_metaclass(DeviceMeta, CspMaster)): msg, "read_unassignedReceptorIDs", tango.ErrSeverity.ERR) - + if not len(self._unassigned_receptor_id): + return [0] return self._unassigned_receptor_id # PROTECTED REGION END # // MidCspMasterBase.unassignedReceptorIDs_read -- GitLab From 0f78a01e6987d1f85b34f8bf41ead42ffc49d446 Mon Sep 17 00:00:00 2001 From: softir Date: Thu, 23 Jan 2020 13:14:47 +0100 Subject: [PATCH 32/33] AT5-262: renamed device proeprty and attributes for inherent capabilities. Implemented ConfigureScan method and obsState update. Implemented decorator to reject configuring during add/remove resources operations. Implemented is_XXX_allowed methods for all TANGO methods. --- csp-lmc-common/csp_lmc_common/CspSubarray.py | 623 +++++++++++------- .../csp_lmc_common/utils/decorators.py | 76 ++- csp-lmc-common/pogo/CspSubarray.xmi | 31 +- 3 files changed, 452 insertions(+), 278 deletions(-) diff --git a/csp-lmc-common/csp_lmc_common/CspSubarray.py b/csp-lmc-common/csp_lmc_common/CspSubarray.py index 758ac30..b5b966d 100644 --- a/csp-lmc-common/csp_lmc_common/CspSubarray.py +++ b/csp-lmc-common/csp_lmc_common/CspSubarray.py @@ -11,7 +11,7 @@ CSP subarray functionality is modeled via a TANGCSP.LMC Common Class for the CSPSubarray TANGO Device. """ -# PROTECTED REGION ID (CspMaster.standardlibray_import) ENABLED START # +# PROTECTED REGION ID (CspSubarray.standardlibray_import) ENABLED START # # Python standard library from __future__ import absolute_import import sys @@ -22,11 +22,9 @@ from enum import IntEnum, unique import threading import time import json -# PROTECTED REGION END# //CspMaster.standardlibray_import +# PROTECTED REGION END# //CspSubarray.standardlibray_import # tango imports -# PROTECTED REGION ID (CspMaster.add_path) ENABLED START # - import tango from tango import DebugIt from tango.server import run @@ -78,19 +76,19 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): - PST sub-element sub-array TANGO device FQDN. - Type:'DevString' - CorrelationCapability + SubarrayProcModeCorrelation - CSP Subarray *Correlation Inherent Capability*\nTANGO device FQDN - Type:'DevString' - PssCapability + SubarrayProcModePss - CSP Subarray *Pss Inherent Capability*\nTANGO device FQDN - Type:'DevString' - PstCapability + SubarrayProcModePst - CSP Subarray *PST Inherent Capability*\nTANGO device FQDN - Type:'DevString' - VlbiCapability + SubarrayProcModeVlbi - CSP Subarray *VLBI Inherent Capability*\nTANGO device FQDN - Type:'DevString' @@ -150,10 +148,10 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): str(evt.attr_value.value)) self.logger.info(log_msg) # update CSP sub-array SCM - if evt.attr_value.name.lower() in ["state", "healthstate"]: + if evt.attr_value.name.lower() in ["state", "healthstate", "adminmode"]: self._update_subarray_state() - #if evt.attr_value.name.lower() == "obsstate": - # self._update_subarray_obs_state() + if evt.attr_value.name.lower() == "obsstate": + self._update_subarray_obs_state() except tango.DevFailed as df: self.logger.error(str(df.args[0].desc)) except Exception as except_occurred: @@ -166,7 +164,7 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): # only if the device is ONLINE/MAINTENANCE, its State is set to # UNKNOWN when there is a timeout on connection, otherwise its # State should be reported always as DISABLE - if self._sc_admin_mode[dev_name] in [AdminMode.ONLINE, + if self._sc_subarray_admin_mode[dev_name] in [AdminMode.ONLINE, AdminMode.MAINTENANCE]: self._sc_subarray_state[dev_name] = tango.DevState.UNKNOWN self._sc_subarray_health_state[dev_name] = HealthState.UNKNOWN @@ -178,10 +176,9 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): log_msg = item.reason + ": on attribute " + str(evt.attr_name) self.logger.warn(log_msg) - def _attributes_change_evt_cb(self, evt): """ - Class protected callback function. + *Class callback function.* Retrieve the value of the sub-element xxxCommandProgress attribute subscribed for change event when a long-running command is issued on the sub-element device. @@ -195,10 +192,7 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): try: if "commandprogress" == evt.attr_value.name.lower()[-15:]: if dev_name in self._sc_subarray_fqdn: - # get the command name (on/off/standby) from the attribute name - # (onCommandProgress/offCommandProgress/standbyCommandProgress)) - # removing the last 15 chars - cmd_name = evt.attr_value.name[:-11] + 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:]: if dev_name in self._sc_subarray_fqdn: @@ -250,15 +244,19 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): # Can happen evt empty?? if evt: if not evt.err: - msg = "Device {} is processing command {}".format(evt.device, + 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) else: msg = "Error!!Command {} ended on device {}.\n".format(evt.cmd_name, evt.device.dev_name()) msg += " Desc: {}".format(evt.errors[0].desc) self.logger.info(msg) - self._sc_cmd_execution_state[evt.device.dev_name()][evt.cmd_name.lower()] = CmdExecState.FAULT + self._sc_subarray_cmd_exec_state[evt.device.dev_name()][evt.cmd_name.lower()] = CmdExecState.FAILED + self._alarm_message[evt.cmd_name.lower()] += msg # obsState and obsMode values take on the CbfSubarray's values via # the subscribe/publish mechanism else: @@ -292,7 +290,7 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): if self._sc_subarray_admin_mode[self.CbfSubarray] not in [AdminMode.ONLINE, AdminMode.MAINTENANCE]: self.set_state(tango.DevState.DISABLE) - if self._admin_mode not in [AdminMode.AdminMode.ONLINE, + if self._admin_mode not in [AdminMode.ONLINE, AdminMode.MAINTENANCE]: self.set_state[tango.DevState.DISABLE] @@ -336,15 +334,40 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): elif self._sc_subarray_health_state[self.CbfSubarray] in [HealthState.FAILED, HealthState.UNKNOWN, HealthState.DEGRADED]: - self._health_state = self._sc_health_state[self.CbfSubarray] + self._health_state = self._sc_subarray_health_state[self.CbfSubarray] else: # if CBF Subarray is not ONLINE/MAINTENANCE .... self._health_state = self._sc_subarray_health_state[self.CbfSubarray] return - + + def _update_subarray_obs_state(self): + """ + Class protected method. + Retrieve the State attribute values of the CSP sub-elements and aggregate + them to build up the CSP global state. + + :param: None + + :return: None + """ + # update the CspSubarray ObsMode only when no command is running on sub-array + # sub-components + exec_state_list = [] + for fqdn in self._sc_subarray_assigned_fqdn: + device_exec_state_list = [value for value in self._sc_subarray_cmd_exec_state[fqdn].values()] + exec_state_list.extend(device_exec_state_list) + # when command are not running, the CbfSubarray osbState reflects the + # CSP Subarray obsState. + # If CbfSubarray obsState is READY and PssSubarray IDLE, the CspSubarray + # onsState is READY (it can performs IMAGING!) + if all(value == CmdExecState.IDLE for value in exec_state_list): + self._obs_state = self._sc_subarray_obs_state[self.CbfSubarray] + self.logger.debug("All sub-array sub-component are IDLE." + "Subarray obsState:", self._obs_state) + def _connect_to_subarray_subcomponent(self, fqdn): """ - Class private method. + Class method. Establish a *stateless* connection with a CSP Subarray sub-component device (CBF Subarray, PSS Subarray , PST Beams). Exceptions are logged. @@ -365,10 +388,9 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): admin_mode_memorized = attribute_properties['adminMode']['__value'] self._sc_subarray_admin_mode[fqdn] = int(admin_mode_memorized[0]) except KeyError: - pass - + self.logger.warn("No key {} found".format(str(key_error))) try: - log_msg = "Trying connection to" + str(fqdn) + " device" + log_msg = "Trying connection to " + str(fqdn) + " device" self.logger.info(log_msg) device_proxy = tango.DeviceProxy(fqdn) # Note: The DeviceProxy is initialized even if the sub-component @@ -457,6 +479,138 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): self.logger.warn(msg) return False return True + + def _is_subarray_composition_allowed(self): + if self.get_state() in [tango.DevState.ON, tango.DevState.OFF]: + return True + return False + + def _is_subarray_configuring_allowed(self): + if self.get_state() == tango.DevState.ON: + return True + return False + + # ---------------- + # Class private methods + # ---------------- + + def __configure_scan(self, device_list, **args_dict): + tango_cmd_name = 0 + # the sub-element device state after successful transition + dev_successful_state = 0 + try: + tango_cmd_name = args_dict['cmd_name'] + dev_successful_state = args_dict['obs_state'] + except KeyError as key_err: + self.logger.warn("No key: {}".format(str(key_err))) + return + # tango_cmd_name: is the TANGO command name with the capital letter + # In the dictionary keys, is generally used the command name in lower letters + cmd_name = tango_cmd_name.lower() + self.logger.info("cmd_name: {} dev_state: {}".format(cmd_name, + dev_successful_state)) + 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 + # sub-component command execution measured time + sc_cmd_duration_measured = defaultdict(lambda:defaultdict(lambda:0)) + # loop on the devices and issue asynchrnously the ConfigureScan command + for device in device_list: + proxy = self._sc_subarray_proxies[device] + sc_cmd_duration_measured[device][cmd_name] = 0 + self._sc_subarray_cmd_progress[device][cmd_name] = 0 + self._alarm_message[cmd_name] = '' + # 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 + proxy.command_inout_asynch("ConfigureScan", self._sc_subarray_scan_configuration[device], self._cmd_ended_cb) + self._sc_subarray_cmd_exec_state[device][cmd_name] = CmdExecState.RUNNING + self._obs_state = ObsState.CONFIGURING + # register the starting time for the command + self._sc_subarray_cmd_starting_time[device] = time.time() + self.logger.info("Device {} State {} expected value {}".format(device, + self._sc_subarray_state[device], + dev_successful_state)) + 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 + while True: + for device in device_list: + print("__configure_scan obs_state:", self._sc_subarray_obs_state[device]) + if device_done[device] == True: + continue + # 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: + self.logger.info("Command {} ended with success on device {}.".format(cmd_name, + device)) + # update the list and number of device that completed the task + self._num_dev_completed_task[cmd_name] += 1 + self._list_dev_completed_task[cmd_name].append(device) + # reset the value of the attribute reporting the execution state of + # 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 + self._cmd_duration_measured[cmd_name] += sc_cmd_duration_measured[device][cmd_name] + # command success: step to next device + device_done[device] = True + # check if sub-element command ended throwing an exception: in this case the + # 'cmd_ended_cb' callback is invoked. + if self._sc_subarray_cmd_exec_state[device][cmd_name] == CmdExecState.FAILED: + # execution ended for this sub-element, skip to the next one + device_done[device] = True + #if self._sc_subarray_obs_state[device] in [ObsState.ABORTED, ObsState.FAULT]: + # num_of_failed_device += 1 + # continue + # check for timeout event. A timeout event can be detected in two ways: + # 1- the sub-element implements the 'onTimeoutExpired' attribute configured + # for change event + # 2- the CspMaster 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] + 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)) + self.logger.warn(msg) + self._sc_subarray_cmd_exec_state[device][cmd_name] = CmdExecState.TIMEOUT + device_done[device] = True + self.logger.info("elapsed_time:{} device {}".format(elapsed_time, device)) + # 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) + print("__configure_scan:", self._sc_subarray_cmd_exec_state[device][cmd_name]) + if all(value == True for value in device_done.values()): + self.logger.info("All devices have been handled!") + if (all(self._sc_subarray_obs_state[device] == ObsState.READY for device in device_list)): + self._obs_state = ObsState.READY + # end of the command: the command has been issued on all the sub-element devices + # reset the execution flag for the CSP + break + time.sleep(1) + # end of the while loop + # check for timeout/alarm conditions on each sub-component + for device in device_list: + if self._sc_subarray_cmd_exec_state[device][cmd_name] == CmdExecState.TIMEOUT: + self._timeout_expired = True + if self._sc_subarray_cmd_exec_state[device][cmd_name] == CmdExecState.FAILED: + self._alarm_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 all(self._sc_subarray_obs_state[fqdn] == dev_successful_state for fqdn in device_list): + self._obs_state = dev_successful_state + self._last_executed_command = cmd_name + # reset the CSP Subarray command execution flag + self._cmd_execution_state[cmd_name] = CmdExecState.IDLE + # ---------------- # Class Properties # ---------------- @@ -470,7 +624,7 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): # ----------------- CspMaster = device_property( - dtype='DevString', default_value="mid_csp/elt/master" + dtype='DevString', ) CbfSubarray = device_property( @@ -481,50 +635,50 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): dtype='DevString', ) - CorrelationCapability = device_property( + SubarrayProcModeCorrelation = device_property( dtype='DevString', ) - PssCapability = device_property( + SubarrayProcModePss = device_property( dtype='DevString', ) - PstCapability = device_property( + SubarrayProcModePst = device_property( dtype='DevString', ) - VlbiCapability = device_property( + SubarrayProcModeVlbi = device_property( dtype='DevString', ) # ---------- # Attributes # ---------- - + scanID = attribute( dtype='DevULong64', access=AttrWriteType.READ_WRITE, ) - corrInherentCapAddr = attribute( + procModeCorrelationAddr = attribute( dtype='DevString', label="Correlation Inherent Capability Address", doc="The CSP sub-array Correlation Inherent Capability FQDN.", ) - pssInherentCapAddr = attribute( + procModePssAddr = attribute( dtype='DevString', label="PSS Inherent Capability address", doc="The CSP sub-array PSS Inherent Capability FQDN.", ) - pstInherentCap = attribute( + procModePstAddr = attribute( dtype='DevString', label="PST Inherent Capability address", doc="The CSP sub-array PST Inherent Capability FQDN.", ) - vlbiInherentCap = attribute( + procModeVlbiAddr = attribute( dtype='DevString', label="VLBI Inhernt Capabilityaddress", doc="The CSP sub-array VLBI Inherent Capability FQDN.", @@ -894,7 +1048,6 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): self._build_state = '{}, {}, {}'.format(release.name, release.version, release.description) self._version_id = release.version # connect to CSP.LMC TANGO DB - csplmc_tango_db = tango.Database() self.set_state(tango.DevState.INIT) self._health_state = HealthState.UNKNOWN self._admin_mode = AdminMode.ONLINE @@ -958,7 +1111,7 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): # values: defaut dictionary (keys: command name, values: command state) self._sc_subarray_cmd_exec_state = defaultdict(lambda: defaultdict(lambda: CmdExecState.IDLE)) - # _sc_cmd_starting_time: for each sub-element report the long-running command + # _sc_subarray_cmd_starting_time: for each sub-element report the long-running command # starting time # Implemented as dictionary: # keys: the sub-element sub-array FQDN @@ -976,18 +1129,20 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): # values: default dictionary (keys: command name, values: the execution percentage) self._sc_subarray_cmd_progress = defaultdict(lambda: defaultdict(lambda: 0)) - # _sc_cmd_duration_expected: for each sub-element, store the duration (in sec.) + # _sc_subarray_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._sc_subarray_cmd_duration_expected = defaultdict(lambda: defaultdict(lambda: 20)) - # _sc_timeout_expired: report the timeout flag - # Implemented as a nested default dictionary + # _sc_subarray_scan_configuration: report the scan configuration + # for each sub-array sub-component (CBF, PSS subarray, PSTBeams) + # Implemented as default dictionary # keys: FQDN - # values: default dictionary (keys: command name, values: True/False)) - self._sc_subarray_timeout_expired = defaultdict(lambda: defaultdict(lambda: False)) + # values: the scan confiration as JSON formatted string + + self._sc_subarray_scan_configuration = defaultdict(lambda:'') # _cmd_execution_state: implement the execution state of a long-running # command for the whole CSP sub-array Setting this attribute prevent the execution @@ -1023,6 +1178,12 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): # _alarm_raised: report the alarm flag self._alarm_raised = False + # _alarm_message: report the alarm message + # Implemented as a dictionary + # keys: command name ('addserachbeams', 'configurescan'..) + # values: the message + self._alarm_message = defaultdict(lambda: '') + # _list_dev_completed_task: for each long-running command report the list of subordinate # components that completed the task # Implemented as a dictionary @@ -1035,7 +1196,7 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): # _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',...) + # keys: the command name ('addbeams, 'configurescan',...) # values: the number of components self._num_dev_completed_task = defaultdict(lambda:0) @@ -1074,6 +1235,43 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): destructor and by the device Init command. """ # PROTECTED REGION ID(CspSubarray.delete_device) ENABLED START # + #release the allocated event resources + for fqdn in self._sc_subarray_fqdn: + event_to_remove = [] + try: + for event_id in self._sc_subarray_event_id[fqdn]: + try: + self._sc_subarrays_proxies[fqdn].unsubscribe_event(event_id) + # self._se_subarray_event_id[fqdn].remove(event_id) + # in Pyton3 can't remove the element from the list while looping on it. + # Store the unsubscribed events in a temporary list and remove them later. + event_to_remove.append(event_id) + except tango.DevFailed as df: + msg = ("Unsubscribe event failure.Reason: {}. " + "Desc: {}".format(df.args[0].reason, df.args[0].desc)) + self.logger.error(msg) + except KeyError as key_err: + # NOTE: in PyTango unsubscription of a not-existing event id raises a + # KeyError exception not a DevFailed!! + msg = "Unsubscribe event failure. Reason: {}".format(str(key_err)) + self.dev_logging(msg, tango.LogLevel.LOG_ERROR) + # remove the events from the list + for k in event_to_remove: + self._se_subarray_event_id[fqdn].remove(k) + # check if there are still some registered events. What to do in this case?? + if self._se_subarray_event_id[fqdn]: + msg = "Still subscribed events: {}".format(self._se_subarray_event_id) + self.dev_logging(msg, tango.LogLevel.LOG_WARN) + else: + # delete the dictionary entry + self._se_subarray_event_id.pop(fqdn) + except KeyError as key_err: + msg = " Can't retrieve the information of key {}".format(key_err) + self.dev_logging(msg, tango.LogLevel.LOG_ERROR) + # clear the subarrays list and dictionary + self._se_subarrays_fqdn.clear() + self._se_subarrays_proxies.clear() + # PROTECTED REGION END # // CspSubarray.delete_device # ------------------ # Attributes methods @@ -1091,28 +1289,28 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): pass # PROTECTED REGION END # // CspSubarray.scanID_write - def read_corrInherentCapAddr(self): + def read_procModeCorrelationAddr(self): # PROTECTED REGION ID(CspSubarray.corrInherentCapAddr_read) ENABLED START # - """Return the corrInherentCapAddr attribute.""" - return self.CorrelationCapability + """Return the procModeCorrelationAddr attribute.""" + return self.SubarrayProcModeCorrelation # PROTECTED REGION END # // CspSubarray.corrInherentCapAddr_read - def read_pssInherentCapAddr(self): + def read_procModePssAddr(self): # PROTECTED REGION ID(CspSubarray.pssInherentCapAddr_read) ENABLED START # - """Return the pssInherentCapAddr attribute.""" - return self.PssCapability + """Return the procModePssAddr attribute.""" + return self.SubarrayProcModePss # PROTECTED REGION END # // CspSubarray.pssInherentCapAddr_read - def read_pstInherentCap(self): + def read_procModePstAddr(self): # PROTECTED REGION ID(CspSubarray.pstInherentCap_read) ENABLED START # - """Return the pstInherentCap attribute.""" - return self.PstCapability + """Return the procModePstAddr( attribute.""" + return self.SubarrayProcModePst # PROTECTED REGION END # // CspSubarray.pstInherentCap_read - def read_vlbiInherentCap(self): + def read_procModeVlbiAddr(self): # PROTECTED REGION ID(CspSubarray.vlbiInherentCap_read) ENABLED START # - """Return the vlbiInherentCap attribute.""" - return self.VlbiCapability + """Return the procModeVlbiAddr attribute.""" + return self.SubarrayProcModeVlbi # PROTECTED REGION END # // CspSubarray.vlbiInherentCap_read def read_cbfSubarrayState(self): @@ -1150,6 +1348,7 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): """Return the pssSubarrayHealthState attribute.""" return self._sc_subarray_admin_mode[self.PssSubarray] # PROTECTED REGION END # // CspSubarray.pssSubarrayHealthState_read + def read_cbfSubarrayObsState(self): # PROTECTED REGION ID(CspSubarray.cbfSubarrayObsState_read) ENABLED START # """Return the cbfSubarrayObsState attribute.""" @@ -1456,140 +1655,15 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): # -------- # Commands # -------- - - def __issue_observing_cmd(self, device_list, **args_dict): - tango_cmd_name = 0 - # the sub-element device state after successful transition - dev_successful_state = 0 - try: - tango_cmd_name = args_dict['cmd_name'] - dev_successful_state = args_dict['dev_state'] - except KeyError as key_err: - self.logger.warn("No key: {}".format(str(key_err))) - return - # tango_cmd_name: is the TANGO command name with the capital letter - # In the dictionary keys, is generally used the command name in lower letters - cmd_name = tango_cmd_name.lower() - self.logger.info("cmd_name: {} dev_state: {}".format(cmd_name, - dev_successful_state)) - 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 - # sub-component command execution measured time - sc_cmd_duration_measured = defaultdict(lambda:defaultdict(lambda:0)) - # loop on the devices and power-on them sequentially - for device in device_list: - # set the sub-element command execution flag - self._sc_cmd_execution_state[device][cmd_name] = CmdExecState.RUNNING - sc_cmd_duration_measured[device][cmd_name] = 0 - self._sc_cmd_progress[device][cmd_name] = 0 - self._alarm_message[cmd_name] = '' - try: - device_proxy = self._sc_subarray_proxies[device] - self.logger.debug("Issue asynch command {} on device {}:".format(cmd_name, - device)) - - except KeyError as key_err: - msg = "No key {} found".format(str(key_err)) - self.logger.warn(msg) - except tango.DevFailed as df: - # It should not happen! Verify - msg = "Failure reason: {} Desc: {}".format(str(df.args[0].reason), str(df.args[0].desc)) - self.logger.warn(msg) - - self.logerror.info("Device {} State {} expected value {}".format(device, self._se_state[device], dev_successful_state)) - command_progress = self._cmd_progress[cmd_name] - # inside the end-less lop chek the obsState of each sub-component - device_ended_command= [] - while True: - for device in device_list: - if device in device_ended_command: - continue - # 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: - self.logger.info("Command {} ended with success on device {}.".format(cmd_name, - device)) - # update the list and number of device that completed the task - self._num_dev_completed_task[cmd_name] += 1 - self._list_dev_completed_task[cmd_name].append(device) - # reset the value of the attribute reporting the execution state of - # the command - self._sc_cmd_execution_state[device][cmd_name] = CmdExecState.IDLE - self._sc_cmd_progress[device][cmd_name] = 100 - # command success: step to next devic - device_ended_command.append(device) - continue - # check for obsState failure - if self._sc_obs_state[device] in [ObsState.FAULT, ObsState.ABORTED]: - self._sc_cmd_execution_state[device][cmd_name] = CmdExecState.FAILED - self._alarm_message[cmd_name] += ("Device {} is {}".format(device, self.get_status())) - self.logger.warn(self._alarm_message[device]) - device_ended_command.append(device) - continue - # check if sub-element command ended throwing an exception: in this case the - # 'cmd_ended_cb' callback is invoked. - if self._se_cmd_execution_state[device][cmd_name] == CmdExecState.FAILED: - # execution ended for this sub-element, skip to the next one - device_ended_command.append(device) - continue - # check for timeout event. A timeout event can be detected in two ways: - # 1- the sub-element implements the 'onTimeoutExpired' attribute configured - # for change event - # 2- the CspMaster 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._se_cmd_starting_time[device] - if (elapsed_time > self._sc_cmd_duration_expected[device][cmd_name] or - self._sc_cmd_execution_state[device][cmd_name] == CmdExecState.TIMEOUT): - msg = ("Timeout executing {} command on device {}".format(cmd_name, device)) - self.logger.warn(msg) - self._se_cmd_execution_state[device][cmd_name] = CmdExecState.TIMEOUT - - # if the CBF command timeout expires, the CSP power-on is stopped - # TODO: verify if this behavior conflicts with ICD - self.logger.info("elapsed_time:{} device {}".format(elapsed_time, device)) - device_ended_command.append(device) - continue - # 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_cmd_progress[device][cmd_name]/len(device_list) - if len(device_list) == len(device_ended_command): - self.logger.info("All devices have been handled!") - # end of the command: the command has been issued on all the sub-element devices - # reset the execution flag for the CSP - break - time.sleep(1) - # end of the while loop - self._last_executed_command = cmd_name - # if one or more sub-elements goes in timeout or alarm, set the CSP Subarray - # corresponding attribute - for device in device_list: - # reset the CSP sub-element command execution flag - if self._se_cmd_execution_state[device][cmd_name] == CmdExecState.TIMEOUT: - # set the CSP timeout flag - self._timeout_expired[cmd_name] = True - if self._se_cmd_execution_state[device][cmd_name] == CmdExecState.FAILED: - # set the CSP timeout flag - self._alarm_raised[cmd_name] = True - # reset the CSP sub-element command execution flag - self._se_cmd_execution_state[device][cmd_name] = CmdExecState.IDLE - # calculate the real execution time for the command - self._cmd_duration_measured[cmd_name] += sc_cmd_duration_measured[device][cmd_name] - # update the progress counter at the end of the loop - self._cmd_progress[cmd_name] = command_progress + (self._se_cmd_progress[device][cmd_name]/len(device_list)) - if all(value == dev_successful_state for value in self._sc_subarray_obs_state): - self._obs_state = dev_successful_state - + + @AdminModeCheck('EndScan') + @ObsStateCheck('endscan') + def is_EndScan_allowed(self): + return self._is_subarray_configuring_allowed() + @command( ) @DebugIt() - @AdminModeCheck('EndScan') - @StateAndObsStateCheck('EndScan') def EndScan(self): # PROTECTED REGION ID(CspSubarray.EndScan) ENABLED START # """ @@ -1611,13 +1685,16 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): self.logger.warn("No key {} found".format(key_err)) proxy.command_inout_asynch("EndScan", self._cmd_ended_cb) # PROTECTED REGION END # // CspSubarray.EndScan - + + @AdminModeCheck('Scan') + @ObsStateCheck('scan') + def is_Scan_allowed(self): + return self._is_subarray_configuring_allowed() + @command( dtype_in='DevVarStringArray', ) @DebugIt() - @AdminModeCheck('Scan') - @StateAndObsStateCheck('Scan') def Scan(self, argin): # PROTECTED REGION ID(CspSubarray.Scan) ENABLED START # """ @@ -1683,21 +1760,26 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): if "pst" in json_config: pass - + + @AdminModeCheck('ConfigureScan') + def is_ConfigureScan_allowed(self): + return self._is_subarray_configuring_allowed() + @command( dtype_in='DevString', doc_in="A Json-encoded string with the scan configuration.", ) @DebugIt() - @AdminModeCheck('ConfigScan') - @StateAndObsStateCheck('ConfigScan') + @ObsStateCheck('configscan') + @SubarrayRejectCmd(['AddSearchBeams', + 'RemoveSearchBeams', + 'AddTimingBeams', + 'RemoveTimingBeams' + 'AddNumOfSearchBeams', + 'RemoveNumOfSearchBeams']) def ConfigureScan(self, argin): """ - Note: - Part of this code (the input string parsing) comes from the CBF project\ - developed by J.Jjang (NRC-Canada) - - *Class method.* + *TANGO Class method* Configure a scan for the subarray.\n The command can be execuced when the CspSubarray State is *ON* and the \ @@ -1727,6 +1809,7 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): if "load" in argin: # skip the 'load' chars and remove spaces from the filename fn = (argin[4:]).strip() + # !!!Note!! set the path to the data file filename = os.path.join(commons_pkg_path, fn) with open(filename) as json_file: # load the file into a dictionary @@ -1751,59 +1834,76 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): msg, "ConfigureScan execution", tango.ErrSeverity.ERR) - self.__validate_scan_configuration(argin) - self._obs_state = ObsState.CONFIGURING + self._validate_scan_configuration(argin) + # Forward the ConfigureScan command to CbfSubarray. - self._timeout_expired['configurescan'] = False - self._alarm_raised['configurescan'] = False - device_to_configure = [] + #self._timeout_expired = False + #self._alarm_raised = False for device in self._sc_subarray_assigned_fqdn: - json_config = json.dumps(self._sc_scan_configuration[device]) + #json_config = json.dumps(self._sc_subarray_scan_configuration[device]) + #print("json_config:", json_config) proxy = self._sc_subarray_proxies[device] attributes = ['configureCmdProgress', 'configureCmdTimeoutExpired'] for attr in attributes: try: - if self._subarray_resources_event_id[attr.lower()] == 0: - event_id = proxy.subscribe_event(attr, tango.EventType.CHANGE_EVENT, - self._attributes_change_evt_cb, stateless=False) - self._subarray_resources_event_id[device][attr] = event_id + if self._sc_subarray_event_id[attr.lower()] == 0: + event_id = proxy.subscribe_event(attr, + tango.EventType.CHANGE_EVENT, + self._attributes_change_evt_cb, + stateless=False) + 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 - # configuration. Failure in configuration throws an exception that is - # caught via the cmd_ended_cb callback - proxy.command_inout_asynch("ConfigureScan", argin, self._cmd_ended_cb) - # register the starting time for the command - self._sc_cmd_starting_time[device] = time.time() + self.logger.info(df.args[0].desc) args_dict = {'cmd_name':"ConfigureScan", 'obs_state':ObsState.READY} # invoke the constructor for the command thread - self._command_thread['configurescan'] = threading.Thread(target=self.__issue_observing_cmd, + self._command_thread['configurescan'] = threading.Thread(target=self.__configure_scan, name="Thread-ConfigureScan", args=(self._sc_subarray_assigned_fqdn,), kwargs=args_dict) + self._command_thread['configurescan'].start() + # sleep for a while to let the thread start + time.sleep(0.2) # PROTECTED REGION END # // CspSubarray.ConfigureScan - + + @AdminModeCheck('AddNumOfSearchBeams') + @ObsStateCheck('addresources') + def is_AddNumOfSearchBeams_allowed(self): + return self._is_subarray_composition_allowed() + @command( dtype_in='DevUShort', doc_in="The number of SearchBeams Capabilities to assign to the subarray.", ) @DebugIt() - @AdminModeCheck('AddBeam') - @StateAndObsStateCheck('AddBeam') + @SubarrayRejectCmd(['RemoveSearchBeams', + 'RemoveNumOfSearchBeams', + 'ConfigureScan']) def AddNumOfSearchBeams(self, argin): # PROTECTED REGION ID(CspSubarray.AddNumOfSearchBeams) ENABLED START # self._cmd_execution_state['addsearchbeams'] = CmdExecState.RUNNING + # connect to CspMaster and check if enough beams are available + # forward the command to PSS + # wait for PSS number of assigned pipelines == number of required beams + # get the PSS beams IDs + # if map exists, retrieve the SearchBeam IDs + # get the corresponding SearchBeams FQDNs + # open proxies to devices + # subscribe SearchBeams attributes # PROTECTED REGION END # // CspSubarray.AddNumOfSearchBeams pass + @AdminModeCheck('RemoveNumOfSearchBeams') + @ObsStateCheck('removeresources') + def is_RemoveNumOfSearchBeams_allowed(self): + return self._is_subarray_composition_allowed() + @command( dtype_in='DevUShort', doc_in="The number of SearchBeam Capabilities to remove from the \nCSP sub-array.\nAll the assigned SearcBbeams are removed from the CSP sub-array if the\ninput number is equal to the max number of SearchBeam \nCapabilities for the specified Csp sub-array instance (1500 for MID,\n500 for LOW)", ) @DebugIt() - @AdminModeCheck('RemoveBeam') - @StateAndObsStateCheck('RemoveBeam') + @SubarrayRejectCmd(['AddSearchBeams', 'ConfigureScan']) def RemoveNumOfSearchBeams(self, argin): # PROTECTED REGION ID(CspSubarray.RemoveNumOfSearchBeams) ENABLED START # """ @@ -1822,20 +1922,21 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): :return:None """ - if self._cmd_execution_state['addsearchbeams'] == CmdExecState.RUNNING: - # throw an exception - self.logger.warn("ASearchBeams command is already running!!") - return pass # PROTECTED REGION END # // CspSubarray.RemoveNumOfSearchBeams + + @AdminModeCheck('AddTimingBeams') + @ObsStateCheck('addresources') + def is_AddTimingBeams_allowed(self): + return self._is_subarray_composition_allowed() + @command( dtype_in='DevVarUShortArray', doc_in="The list of TimingBeam Capability IDs to assign to the CSP sub-array.", ) @DebugIt() - @AdminModeCheck('AddBeam') - @StateAndObsStateCheck('AddBeam') + @SubarrayRejectCmd(['RemoveTimingBeams', 'ConfigureScan']) def AddTimingBeams(self, argin): # PROTECTED REGION ID(CspSubarray.AddTimingBeams) ENABLED START # """ @@ -1848,19 +1949,28 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): :return:None """ - self._cmd_execution_state['addtimingbeams'] = CmdExecState.RUNNING + # read the CSP Master list of CSP TimingBeams addresses + # get the CSP TimingBeams addresses from IDs + # map timing beams ids to PST Beams ids + # write the CSP TimingBeams membership + # get the PSTBeams addresses + pass # PROTECTED REGION END # // CspSubarray.AddTimingBeams + @AdminModeCheck('AddSearchBeams') + @ObsStateCheck('addresources') + def is_AddSearchBeams_allowed(self): + return self._is_subarray_composition_allowed() + @command( dtype_in='DevVarUShortArray', doc_in="The list of SearchBeam Capability IDs to assign to the CSP sub-array.", ) @DebugIt() - @AdminModeCheck('AddBeam') - @StateAndObsStateCheck('AddBeam') - def AddSearchBeamsID(self, argin): - # PROTECTED REGION ID(CspSubarray.AddSearchBeamsID) ENABLED START # + @SubarrayRejectCmd(['RemoveSearchBeams', 'ConfigureScan']) + def AddSearchBeams(self, argin): + # PROTECTED REGION ID(CspSubarray.AddSearchBeams) ENABLED START # """ Add the specified SeachBeam Capability IDs o the CSP @@ -1871,17 +1981,26 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): :return:None """ + # read the CSP Master list of CSP SearchBeams addresses + # get the CSP SearchBeams addresses from IDs + # map search beams ids to PSS beams IDs + # issue the AddSearchBeams command on PSS sub-array + # wait until PSS list of beams is equal to the sent one self._cmd_execution_state['addsearchbeams'] = CmdExecState.RUNNING pass - # PROTECTED REGION END # // CspSubarray.AddSearchBeamsID + # PROTECTED REGION END # // CspSubarray.AddSearchBeams + @AdminModeCheck('RemoveSearchBeamsID') + @ObsStateCheck('removeresources') + def is_RemoveSearchBeams_allowed(self): + return self._is_subarray_composition_allowed() + @command( dtype_in='DevVarUShortArray', doc_in="The list of SearchBeam Capability IDs to remove from the CSP sub-array.", ) @DebugIt() - @AdminModeCheck('RemoveBeam') - @StateAndObsStateCheck('RemoveBeam') + @SubarrayRejectCmd(['AddSearchBeams', 'ConfigureScan']) def RemoveSearchBeamsID(self, argin): # PROTECTED REGION ID(CspSubarray.RemoveSearchBeamsID) ENABLED START # """ @@ -1896,14 +2015,18 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): """ pass # PROTECTED REGION END # // CspSubarray.RemoveSearchBeamsID - + + @AdminModeCheck('RemoveTimingBeams') + @ObsStateCheck('removeresources') + def is_RemoveTimingBeams_allowed(self): + return self._is_subarray_composition_allowed() + @command( dtype_in='DevVarUShortArray', doc_in="The list of Timing Beams IDs to remove from the sub-array.", ) @DebugIt() - @AdminModeCheck('RemoveBeam') - @StateAndObsStateCheck('RemoveBeam') + @SubarrayRejectCmd(['AddTimingBeams', 'ConfigureScan']) def RemoveTimingBeams(self, argin): # PROTECTED REGION ID(CspSubarray.RemoveTimingBeams) ENABLED START # """ @@ -1914,18 +2037,18 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): :return:None """ - if self._cmd_execution_state['addtimingbeams'] == CmdExecState.RUNNING: - # throw an exception - self.logger.warn("ADDtimingBeams command is already running!!") - return pass # PROTECTED REGION END # // CspSubarray.RemoveTimingBeams - + + @AdminModeCheck('EndSB') + @ObsStateCheck('endsb') + def is_EndSB_allowed(self): + return self._is_subarray_configuring_allowed() + @command( ) @DebugIt() - @AdminModeCheck('EndSB') - @StateAndObsStateCheck('EndSB') + @SubarrayRejectCmd(['ConfigureScan']) def EndSB(self): # PROTECTED REGION ID(CspSubarray.EndSB) ENABLED START # """ @@ -1933,7 +2056,13 @@ class CspSubarray(with_metaclass(DeviceMeta,SKASubarray)): :return:None """ - pass + device_list = self._sc_subarray_assigned_fqdn + if not any(self._sc_subarray_assigned_fqdn): + print("Assigne list is empty") + device_list = self._sc_subarray_fqdn + for fqdn in device_list: + proxy = self._sc_subarray_proxies[fqdn] + proxy.command_inout_asynch("EndSB", self._cmd_ended_cb) # PROTECTED REGION END # // CspSubarray.EndSB # ---------- diff --git a/csp-lmc-common/csp_lmc_common/utils/decorators.py b/csp-lmc-common/csp_lmc_common/utils/decorators.py index 68b3684..45e5507 100644 --- a/csp-lmc-common/csp_lmc_common/utils/decorators.py +++ b/csp-lmc-common/csp_lmc_common/utils/decorators.py @@ -47,10 +47,10 @@ VALID_OBS_STATE = {'abort' : [ObsState.CONFIGURING, ObsState.SCANNING], 'scan' : [ObsState.READY], 'endscan' : [ObsState.SCANNING], 'endsb' : [ObsState.READY, ObsState.IDLE], - 'addbeam' : [ObsState.IDLE], - 'removebeam' : [ObsState.IDLE] + 'addresources' : [ObsState.IDLE], + 'removeresources' : [ObsState.IDLE] } -class StateAndObsStateCheck(object): +class ObsStateCheck(object): """ Class designed to be a decorator for the CspMaster methods. It checks the obsMode attribute value @@ -63,20 +63,12 @@ class StateAndObsStateCheck(object): def obs_state_check(*args, **kwargs): # get the device instance dev_instance = args[0] - cmd_to_execute = self._args - # check if the sub-array State is On, otherwise throws an exception - if dev_instance.get_state() != tango.DevState.ON: - msg_args = (cmd_to_execute, dev_instance.get_state()) - err_msg = ("{} command can't be issued when the" - " State is {} ".format(*msg_args)) - tango.Except.throw_exception("Command not executable", - err_msg, - "StateAndObsStateCheck decorator", - tango.ErrSeverity.ERR) + cmd_type = self._args + # Check the obsState attribute value: valid values for the command to # execute are defined by VALID_OBS_STATE dictionary - if dev_instance._obs_state in VALID_OBS_STATE[cmd_to_execute.lower()]: - msg_args = (cmd_to_execute, ObsState(dev_instance._obs_state).name) + if dev_instance._obs_state not in VALID_OBS_STATE[cmd_type]: + msg_args = (f.__name__, ObsState(dev_instance._obs_state).name) err_msg = ("{} command can't be issued when the" " obsState is {} ".format(*msg_args)) @@ -249,3 +241,57 @@ class CmdInputArgsCheck(object): return f(*args, **kwargs) return input_args_check +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 + running. + 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 command to reject + # + self._args = args + self._kwargs = kwargs + print(args) + def __call__(self, f): + @functools.wraps(f) + def input_args_check(*args, **kwargs): + # the CspSubarray device instance + dev_instance = args[0] + cmd_to_exec = f.__name__ + commands_to_reject = self._args + dev_instance.logger.debug("SubarrayRejectCmd: function {}".format(f.__name__)) + # 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] + print(list_of_running_cmd) + if cmd_to_exec.lower() in list_of_running_cmd: + # the requested command is already running + # return with no exception (see SKA Guidelines) + msg = ("Command {} is already running".format(cmd_to_exec)) + dev_instance.logger.info(msg) + return + 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)) + tango.Except.throw_exception("Command failure", + err_msg, + "SubarrayRejectCmd decorator", + tango.ErrSeverity.ERR) + # reset alarm/timeout condition + if dev_instance._alarm_raised: + dev_instance.logger.info("A previous alarm condition is present") + dev_instance._alarm_raised = False + if dev_instance._timeout_expired: + dev_instance.logger.info("A previous timeout condition is present") + dev_instance._timeout_expired = False + # TODO: how check if the alarm condition has been reset by AlarmHandler? + return f(*args, **kwargs) + return input_args_check diff --git a/csp-lmc-common/pogo/CspSubarray.xmi b/csp-lmc-common/pogo/CspSubarray.xmi index 7dd83d2..57d63aa 100644 --- a/csp-lmc-common/pogo/CspSubarray.xmi +++ b/csp-lmc-common/pogo/CspSubarray.xmi @@ -1,7 +1,7 @@ - + @@ -28,11 +28,6 @@ - - - - mid_csp/elt/master - @@ -41,19 +36,19 @@ - + - + - + - + @@ -67,6 +62,10 @@ console::cout + + + + @@ -247,7 +246,7 @@ - + @@ -256,7 +255,7 @@ - + @@ -372,7 +371,7 @@ - + @@ -380,7 +379,7 @@ - + @@ -388,7 +387,7 @@ - + @@ -396,7 +395,7 @@ - + -- GitLab From 975d242f3d4dfa17d748a987e679ac5b7ae2f55c Mon Sep 17 00:00:00 2001 From: softir Date: Thu, 23 Jan 2020 13:17:35 +0100 Subject: [PATCH 33/33] AT5-262: Add/Remove receptors to/from a MID CspSubarray. Overwrite method to validate the scan configuration. --- csp-lmc-mid/csp-lmc-mid/MidCspSubarrayBase.py | 652 +++++++++++++ csp-lmc-mid/pogo/MidCspSubarrayBase.xmi | 919 ++++++++++++++++++ 2 files changed, 1571 insertions(+) create mode 100644 csp-lmc-mid/csp-lmc-mid/MidCspSubarrayBase.py create mode 100644 csp-lmc-mid/pogo/MidCspSubarrayBase.xmi diff --git a/csp-lmc-mid/csp-lmc-mid/MidCspSubarrayBase.py b/csp-lmc-mid/csp-lmc-mid/MidCspSubarrayBase.py new file mode 100644 index 0000000..7377aff --- /dev/null +++ b/csp-lmc-mid/csp-lmc-mid/MidCspSubarrayBase.py @@ -0,0 +1,652 @@ +# -*- coding: utf-8 -*- +# +# This file is part of the MidCspSubarrayBase project +# +# INAF, SKA Telescope +# +# Distributed under the terms of the GPL license. +# See LICENSE.txt for more info. + +""" MidCspSubarrayBase + +The base class for MID CspSubarray. +Fuctionality to monitor assigned CSP.LMC Capabilities, +as well as inherent Capabilities, are implemented in +separate TANGO Devices. +""" +# PROTECTED REGION ID (MidCspSubarrayBase.standardlibray_import) ENABLED START # +# Python standard library +import sys +import os +from future.utils import with_metaclass +from collections import defaultdict +import threading +import time + +import json +# PROTECTED REGION END# //MidCspSubarrayBase.standardlibray_import + +# 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, EventType, DevState +from tango import AttrWriteType, DeviceProxy +# Additional import +# PROTECTED REGION ID(MidCspMaster.additional_import) ENABLED START # +from skabase.SKAMaster import SKAMaster +from skabase.auxiliary import utils +# import CSP.LMC Common package +from csp_lmc_common.utils.cspcommons import HealthState, AdminMode +from csp_lmc_common.CspSubarray import CspSubarray +# PROTECTED REGION END # // MidCspSubarrayBase.additionnal_import + +__all__ = ["MidCspSubarrayBase", "main"] + + +class MidCspSubarrayBase(with_metaclass(DeviceMeta, CspSubarray)): + """ + The base class for MID CspSubarray. + Fuctionality to monitor assigned CSP.LMC Capabilities, + as well as inherent Capabilities, are implemented in + separate TANGO Devices. + + **Properties:** + + - Class Property + + - Device Property + """ + + # PROTECTED REGION ID(MidCspSubarrayBase.class_variable) ENABLED START # + # PROTECTED REGION END # // MidCspSubarrayBase.class_variable + # PROTECTED REGION ID(MidCspSubarrayBase.private_methods) ENABLED START # + def __monitor_add_receptors(self, receptor_list, args_dict= None): + cmd_name = 'addreceptors' + device = self.CbfSubarray + 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 + # sub-component command execution measured time + self._sc_subarray_cmd_progress[device][cmd_name] = 0 + self._alarm_message[cmd_name] = '' + device_proxy = self._sc_subarray_proxies[device] + + while True: + # read the list of receptor IDs assigned to the CbfSubarray + try: + # Note: with numpy support in PyTango, receptors is a + # numpy array + receptors = device_proxy.receptors + except tango.DevFailed as tango_err: + self.logger.warn("__monitor_add_receptors:",tango_err.args[0].desc) + if len(receptors): + # 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): + self.logger.info("All required receptors asigned!!") + self._sc_subarray_cmd_progress[device][cmd_name] = 100 + # calculate the real execution time for the command + self._cmd_duration_measured[cmd_name] = (time.time() - self._sc_subarray_cmd_starting_time[device]) + break + # check if sub-element command ended throwing an exception: in this case the + # 'cmd_ended_cb' callback is invoked. + if self._sc_subarray_cmd_exec_state[device][cmd_name] == CmdExecState.FAILED: + self._alarm_raised = True + break + elapsed_time = time.time() - self._sc_subarray_cmd_starting_time[device] + 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)) + self.logger.warn(msg) + self._sc_subarray_cmd_exec_state[device][cmd_name] = CmdExecState.TIMEOUT + break + # 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) + # end of the while loop + + self._last_executed_command = cmd_name + # update the progress counter at the end of the loop + self._cmd_progress[cmd_name] = self._sc_subarray_cmd_progress[device][cmd_name] + # check for error conditions + if self._sc_subarray_state[self.CbfSubarray] != tango.DevState.ON: + msg= ("AddReceptors: device {} is in {}" + " State".format(device,self._sc_subarray_state[self.CbfSubarray])) + self.logger.warn(msg) + if self._sc_subarray_cmd_exec_state[device][cmd_name] == CmdExecState.TIMEOUT: + self._timeout_expired = True + # reset the command execution state + self._sc_subarray_cmd_exec_state[device][cmd_name] = CmdExecState.IDLE + self._cmd_execution_state[cmd_name] = CmdExecState.IDLE + # PROTECTED REGION END # // MidCspSubarrayBase.private_methods + + def __monitor_remove_receptors(self, receptor_list, args_dict= None): + cmd_name = 'removereceptors' + device = self.CbfSubarray + 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 + # sub-component command execution measured time + self._sc_subarray_cmd_progress[device][cmd_name] = 0 + self._alarm_message[cmd_name] = '' + device_proxy = self._sc_subarray_proxies[device] + while True: + # read the list of receptor IDs assigned to the CbfSubarray + try: + receptors = device_proxy.receptors + except AttributeError as attr_err: + self.logger.warn("RemoveReceptors:",str(attr_err)) + if len(receptors): + # get the ids in receptor_list that are no more present in receptors + receptors_removed = [value for value in receptor_list if value not in receptors] + self._num_dev_completed_task[cmd_name] = len(receptors) - len(receptor_list) + if len(receptors_removed) == len(receptor_list): + self._sc_subarray_cmd_progress[device][cmd_name] = 100 + # calculate the real execution time for the command + self._cmd_duration_measured[cmd_name] = (time.time() - self._sc_subarray_cmd_starting_time[device]) + self.logger.info("Receptors {} have been successfully removed".format(receptor_list)) + break + # check if sub-element command ended throwing an exception: in this case the + # 'cmd_ended_cb' callback is invoked. + if self._sc_subarray_cmd_exec_state[device][cmd_name] == CmdExecState.FAILED: + break + elapsed_time = time.time() - self._sc_subarray_cmd_starting_time[device] + 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)) + self.logger.warn(msg) + self._sc_subarray_cmd_exec_state[device][cmd_name] = CmdExecState.TIMEOUT + break + # 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) + # end of the while loop + self._last_executed_command = cmd_name + # update the progress counter at the end of the loop + self._cmd_progress[cmd_name] = self._sc_subarray_cmd_progress[device][cmd_name] + # check for error conditions + if self._sc_subarray_state[self.CbfSubarray] not in [tango.DevState.OFF, + tango.DevState.ON]: + self._alarm_message[cmd_name] += ("Device {} is in {} State".format(device, + self._sc_subarray_state[self.CbfSubarray])) + self.logger.warn(self._alarm_message[device]) + self._alarm_raised = True + if self._sc_subarray_cmd_exec_state[device][cmd_name] == CmdExecState.TIMEOUT: + self._timeout_expired = True + # reset the command exeuction state + self._sc_subarray_cmd_exec_state[device][cmd_name] = CmdExecState.IDLE + self._cmd_execution_state[cmd_name] = CmdExecState.IDLE + + def _validate_scan_configuration(self, json_config): + """ + Overwritten method. + Validate the MID scan configuration file. + Currently it only copies the received configuration because it does not + still exist any "cbf" block inside the JSON script. + :param json_config: the input JSON formatted string with the configuration + for a scan + """ + self.logger.debug("Validate scan configuration for MID CSP") + #self._sc_subarray_obs_mode[self.CbfSubarray] = ObsMode.IMAGING + #self._sc_scan_configuration[self.CbfSubarray] = json_config["cbf"] + self._sc_subarray_scan_configuration[self.CbfSubarray] = json_config + self._sc_subarray_assigned_fqdn.append(self.CbfSubarray) + + # PROTECTED REGION END # // MidCspSubarrayBase.private_methods + # ---------------- + # Class Properties + # ---------------- + + + # ----------------- + # Device Properties + # ----------------- + # ---------- + # Attributes + # ---------- + assignedFsp = attribute( + dtype=('DevUShort',), + max_dim_x=27, + label="List of assigned FSPs", + doc="List of assigned FSPs.", + ) + + assignedVcc = attribute( + dtype=('DevUShort',), + max_dim_x=197, + label="List of assigned VCCs", + doc="List of assigned VCCs.", + ) + + cbfOutputLink = attribute(name="cbfOutputLink", + label="cbfOutputLink", + forwarded=True + ) + pssOutputLink = attribute(name="pssOutputLink", + label="cbfOutputLink", + forwarded=True + ) + vlbiOutputLink = attribute(name="vlbiOutputLink", + label="cbfOutputLink", + forwarded=True + ) + assignedVccState = attribute(name="assignedVccState", + label="State of the assigned VCC", + forwarded=True + ) + assignedVccHealthState = attribute(name="assignedVccHealthState", + label="HealthState of the assigned VCC", + forwarded=True + ) + assignedFspState = attribute(name="assignedFspState", + label="State of the assigned FSPs", + forwarded=True + ) + assignedFspHealthState = attribute(name="assignedFspHealthState", + label="HealthState of the assigned FSPs.", + forwarded=True + ) + assignedReceptors = attribute(name="assignedReceptors", + label="The list of assigned receptor IDs.", + forwarded=True + ) + assignedVccObsState = attribute(name="assignedVccObsState", + label="ObsState of the assigned VCCs", + forwarded=True + ) + assignedFspObsState = attribute(name="assignedFspObsState", + label="ObsState of the assigned FSPs.", + forwarded=True + ) + assignedVccAdminMode = attribute(name="assignedVccAdminMode", + label="AdminMode of the assigned VCCs.", + forwarded=True + ) + assignedFspAdminMode = attribute(name="assignedFspAdminMode", + label="AdminMode of the assigned FSPs.", + forwarded=True + ) + # --------------- + # General methods + # --------------- + + def init_device(self): + """Initialises the attributes and properties of the MidCspSubarrayBase.""" + CspSubarray.init_device(self) + # PROTECTED REGION ID(MidCspSubarrayBase.init_device) ENABLED START # + self._assigned_vcc = [] + self._assigned_fsp = [] + self._receptor_to_vcc_map = {} + self._receptor_id_list = [] + + + # PROTECTED REGION END # // MidCspSubarrayBase.init_device + + def always_executed_hook(self): + """Method always executed before any TANGO command is executed.""" + # PROTECTED REGION ID(MidCspSubarrayBase.always_executed_hook) ENABLED START # + # PROTECTED REGION END # // MidCspSubarrayBase.always_executed_hook + + def delete_device(self): + """Hook to delete resources allocated in init_device. + + This method allows for any memory or other resources allocated in the + init_device method to be released. This method is called by the device + destructor and by the device Init command. + """ + # PROTECTED REGION ID(MidCspSubarrayBase.delete_device) ENABLED START # + # PROTECTED REGION END # // MidCspSubarrayBase.delete_device + # ------------------ + # Attributes methods + # ------------------ + + def read_assignedFsp(self): + # PROTECTED REGION ID(MidCspSubarrayBase.assignedFsp_read) ENABLED START # + """Return the assignedFsp attribute.""" + return (0,) + # PROTECTED REGION END # // MidCspSubarrayBase.assignedFsp_read + + def read_assignedVcc(self): + # PROTECTED REGION ID(MidCspSubarrayBase.assignedVcc_read) ENABLED START # + """ + *Attribute method* + + :returns: + The list of VCC IDs assigned to the subarray. + + *Type*: array of DevUShort. + """ + # PROTECTED REGION ID(CspSubarray.vcc_read) ENABLED START # + self._assigned_vcc.clear() + # get the map VCC-receptor from CspMaster + if not self._receptor_to_vcc_map: + try: + csp_proxy = tango.DeviceProxy(self.CspMaster) + csp_proxy.ping() + cbf_master_addr = csp_proxy.cbfMasterAddress + cbf_master_proxy = tango.DeviceProxy(cbf_master_addr) + cbf_master_proxy.ping() + # build the list of receptor ids + receptor_to_vcc = cbf_master_proxy.receptorToVcc + self._receptor_to_vcc_map = dict([int(ID) for ID in pair.split(":")] + for pair in receptor_to_vcc) + # build the list of the installed receptors + self._receptor_id_list = list(self._receptor_to_vcc_map.keys()) + except tango.DevFailed as tango_err: + self.logger.warn(tango_err.args[0].desc) + try: + assigned_receptors = self._sc_subarray_proxies[self.CbfSubarray].receptors + # NOTE: if receptors attribute is empty, assigned_receptors is an empty numpy array + # and it will just be skipped by the for loop + for receptor_id in assigned_receptors: + vcc_id = self._receptor_to_vcc_map[receptor_id] + self._assigned_vcc.append(vcc_id) + except KeyError as key_err: + msg = "No {} found".format(key_err) + tango.Except.throw_exception("Read attribute failure", + msg, + "read_vcc", + tango.ErrSeverity.ERR) + except tango.DevFailed as df: + tango.Except.throw_exception("Read attribute failure", + df.args[0].desc, + "read_vcc", + tango.ErrSeverity.ERR) + return self._assigned_vcc + # PROTECTED REGION END # // MidCspSubarrayBase.assignedVcc_read + + + # -------- + # Commands + # -------- + @AdminModeCheck('AddReceptors') + @ObsStateCheck('addresources') + def is_AddReceptors_allowed(self): + return self._is_subarray_composition_allowed() + + @command( + dtype_in='DevVarUShortArray', + doc_in="List of the receptor IDs to add to the subarray.", + ) + @DebugIt() + #@SubarrayRejectCmd(['RemoveReceptors', 'Configure']) + def AddReceptors(self, argin): + # PROTECTED REGION ID(MidCspSubarrayBase.AddReceptors) ENABLED START # + """ + *Class method* + + Add the specified receptor IDs to the subarray. + + The command can be executed only if the CspSubarray *ObsState* is *IDLE*. + + :param argin: the list of receptor IDs + :type: array of DevUShort + :return: + None + :raises: + tango.DevFailed: if the CbfSubarray is not available or if an exception\ + is caught during command execution. + Note: + Still to implement the check on AdminMode values: the command can be processed \ + only when the CspSubarray is *ONLINE* or *MAINTENANCE* + """ + # PROTECTED REGION ID(CspSubarray.AddReceptors) ENABLED START # + # Each vcc_id map to a vcc_fqdn inside CbfMaster, for example: + # vcc_id = 1 -> mid_csp_cbf/vcc/vcc_001 + # vcc_id = 2 -> mid_csp_cbf/vcc/vcc_002 + # ..... + # vcc_id = 17 -> mid_csp_cbf/vcc/vcc_017 + # vcc_id and receptor_id is not the same. The map between vcc_id and receptor_id + # is built by CbfMaster and exported as attribute. + # The max number of VCC allocated is defined by the VCC property of the CBF Master. + + # the list of available receptor IDs. This number is mantained by the CspMaster + # and reported on request. + available_receptors = [] + # the list of receptor to assign to the subarray + receptor_to_assign = [] + try: + # access to CspMaster to get information about the list of available receptors + # and the receptors affiliation to subarrays. + csp_master_proxy = tango.DeviceProxy(self.CspMaster) + csp_master_proxy.ping() + available_receptors = csp_master_proxy.unassignedReceptorIDs + if not self._receptor_to_vcc_map: + cbf_master_addr = csp_master_proxy.cbfMasterAddress + cbf_master_proxy = tango.DeviceProxy(cbf_master_addr) + cbf_master_proxy.ping() + # build the list of receptor ids + receptor_to_vcc = cbf_master_proxy.receptorToVcc + self._receptor_to_vcc_map = dict([int(ID) for ID in pair.split(":")] + for pair in receptor_to_vcc) + # build the list of the installed receptors + self._receptor_id_list = list(self._receptor_to_vcc_map.keys()) + print("available_receptors:", available_receptors) + print("available_receptors:", type(available_receptors)) + if not any(available_receptors): + log_msg = "No available receptor to add to subarray {}".format(self.SubID) + self.dev_logging(log_msg, tango.LogLevel.LOG_WARN) + return + receptor_membership = csp_master_proxy.receptorMembership + print("receptor_membership:", receptor_membership) + except tango.DevFailed as df: + msg = "Failure in getting receptors information:" + str(df.args[0].reason) + tango.Except.throw_exception("Command failed", msg, + "AddReceptors", tango.ErrSeverity.ERR) + except AttributeError as attr_err: + msg = "Failure in reading {}: {}".format(str(attr_err.args[0]), attr_err.__doc__) + tango.Except.throw_exception("Command failed", msg, + "AddReceptors", tango.ErrSeverity.ERR) + for receptorId in argin: + print("receptorId:", receptorId) + print("self._receptor_id_list:", self._receptor_id_list) + # check if the specified receptor id is a valid number (that is, belongs to the list + # of provided receptors) + if receptorId in self._receptor_id_list: + # check if the receptor id is one of the available receptor Ids + if receptorId in available_receptors: + receptor_to_assign.append(receptorId) + else: + # retrieve the subarray owner + sub_id = receptor_membership[receptorId - 1] + log_msg = "Receptor {} already assigned to subarray {}".format(str(receptorId), + str(sub_id)) + self.logger.info(log_msg) + else: + log_msg = "Invalid receptor id: {}".format(str(receptorId)) + self.logger.warn(log_msg) + + # check if the list of receptors to assign is empty + if not receptor_to_assign: + log_msg = "The required receptors are already assigned to a 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 + proxy.command_inout_asynch("AddReceptors", receptor_to_assign, self._cmd_ended_cb) + self._sc_subarray_cmd_starting_time['addreceptors'] = time.time() + self._command_thread['addreceptors'] = threading.Thread(target=self.__monitor_add_receptors, + name="Thread-AddReceptors", + args=(receptor_to_assign,)) + self._sc_subarray_cmd_starting_time['addreceptors'] = time.time() + self._command_thread['addreceptors'].start() + else: + log_msg = "Device {} is not running!".format(str(self._cbf_subarray_fqdn)) + self.logger.error(log_msg) + tango.Except.throw_exception("Command failed", + log_msg, + "AddReceptors", + tango.ErrSeverity.ERR) + # PROTECTED REGION END # // MidCspSubarrayBase.AddReceptors + @AdminModeCheck('RemoveReceptors') + @ObsStateCheck('removeresources') + def is_RemoveReceptors_allowed(self): + + return self._is_subarray_composition_allowed() + + @command( + dtype_in='DevVarUShortArray', + doc_in="The list with the receptor IDs to remove", + ) + @DebugIt() + @SubarrayRejectCmd(['AddReceptors', 'Configure']) + def RemoveReceptors(self, argin): + # PROTECTED REGION ID(MidCspSubarrayBase.RemoveReceptors) ENABLED START # + """ + Remove the receptor IDs from the subarray. + + :param argin: The list of the receptor IDs to remove from the subarray. + Type: array of DevUShort + :returns: + None + :raises: + tango.DevFailed: raised if the subarray *obState* attribute is not IDLE, or \ + 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['removereceptors'] = 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() + 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: + print("ATTRERROR") + 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: + 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) + + # PROTECTED REGION END # // MidCspSubarrayBase.RemoveReceptors + + @command( + ) + @DebugIt() + def RemoveAllReceptors(self): + # PROTECTED REGION ID(MidCspSubarrayBase.RemoveAllReceptors) ENABLED START # + """ + *Class method.* + + Remove all the assigned receptors from the subarray. + Returns: + None + Raises: + tango.DevFailed: raised if the subarray *obState* attribute is not IDLE or READY, or \ + 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.dev_logging(log_msg, tango.LogLevel.LOG_ERROR) + tango.Except.re_throw_exception(df, "Command failed", + "CspSubarray RemoveAllReceptors command failed", + "Command()", + tango.ErrSeverity.ERR) + else: + log_msg = "Subarray {} not registered!".format(str(self._cbf_subarray_fqdn)) + self.dev_logging(log_msg, tango.LogLevel.LOG_ERROR) + tango.Except.throw_exception("Command failed", + log_msg, + "RemoveAllReceptors", + tango.ErrSeverity.ERR) + + # PROTECTED REGION END # // MidCspSubarrayBase.RemoveAllReceptors + +# ---------- +# Run server +# ---------- + + +def main(args=None, **kwargs): + # PROTECTED REGION ID(MidCspSubarrayBase.main) ENABLED START # + return run((MidCspSubarrayBase,), args=args, **kwargs) + # PROTECTED REGION END # // MidCspSubarrayBase.main + +if __name__ == '__main__': + main() diff --git a/csp-lmc-mid/pogo/MidCspSubarrayBase.xmi b/csp-lmc-mid/pogo/MidCspSubarrayBase.xmi new file mode 100644 index 0000000..182a7ab --- /dev/null +++ b/csp-lmc-mid/pogo/MidCspSubarrayBase.xmi @@ -0,0 +1,919 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 4 + + + + + + + + + + + + + + + + + 4 + + + + + console::cout + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- GitLab