diff --git a/docker-compose/tango-prometheus-exporter/ska-tango-grafana-exporter b/docker-compose/tango-prometheus-exporter/ska-tango-grafana-exporter index 6e48f0fddf5541bc66d9f57e31297c0027ea97b7..e313399d197d266e49d6da0442ea983c6f92adad 160000 --- a/docker-compose/tango-prometheus-exporter/ska-tango-grafana-exporter +++ b/docker-compose/tango-prometheus-exporter/ska-tango-grafana-exporter @@ -1 +1 @@ -Subproject commit 6e48f0fddf5541bc66d9f57e31297c0027ea97b7 +Subproject commit e313399d197d266e49d6da0442ea983c6f92adad diff --git a/tangostationcontrol/tangostationcontrol/clients/attribute_wrapper.py b/tangostationcontrol/tangostationcontrol/clients/attribute_wrapper.py index e9239a834a248af32886df651a5f2463b4764489..b8e829ede0b5857cc5cc6c4da5d6052118cf57b7 100644 --- a/tangostationcontrol/tangostationcontrol/clients/attribute_wrapper.py +++ b/tangostationcontrol/tangostationcontrol/clients/attribute_wrapper.py @@ -72,7 +72,7 @@ class attribute_wrapper(attribute): if access == AttrWriteType.READ_WRITE: """ if the attribute is of READ_WRITE type, assign the write function to it""" - @only_in_states([DevState.STANDBY, DevState.ON], log=False) + @only_in_states([DevState.STANDBY, DevState.ON, DevState.ALARM], log=False) @fault_on_error() def write_func_wrapper(device, value): """ @@ -86,7 +86,7 @@ class attribute_wrapper(attribute): """ Assign the read function to the attribute""" - @only_in_states([DevState.STANDBY, DevState.ON], log=False) + @only_in_states([DevState.STANDBY, DevState.ON, DevState.ALARM], log=False) @fault_on_error() def read_func_wrapper(device): """ diff --git a/tangostationcontrol/tangostationcontrol/devices/sdp/xst.py b/tangostationcontrol/tangostationcontrol/devices/sdp/xst.py index c0b6f7a98894b7c7d660828231935b63c5b0f5da..3b0f59d242c7d29ac707b7e7361d2e9dce706424 100644 --- a/tangostationcontrol/tangostationcontrol/devices/sdp/xst.py +++ b/tangostationcontrol/tangostationcontrol/devices/sdp/xst.py @@ -108,8 +108,8 @@ class XST(Statistics): FPGA_xst_subband_select_RW = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["FPGA_xst_subband_select_RW"], datatype=numpy.uint32, dims=(8,16), access=AttrWriteType.READ_WRITE) FPGA_xst_subband_select_R = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["FPGA_xst_subband_select_R"], datatype=numpy.uint32, dims=(8,16)) - FPGA_xst_offload_nof_crosslets_RW = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["FPGA_xst_offload_nof_crosslets_RW"], datatype=numpy.uint32, dims=(16,), access=AttrWriteType.READ_WRITE) - FPGA_xst_offload_nof_crosslets_R = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["FPGA_xst_offload_nof_crosslets_R"], datatype=numpy.uint32, dims=(16,)) + FPGA_xst_offload_nof_crosslets_RW = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["FPGA_xst_offload_nof_crosslets_RW"], datatype=numpy.uint32, dims=(16,), access=AttrWriteType.READ_WRITE) + FPGA_xst_offload_nof_crosslets_R = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["FPGA_xst_offload_nof_crosslets_R"], datatype=numpy.uint32, dims=(16,)) # number of packets with valid payloads nof_valid_payloads_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "nof_valid_payloads"}, dims=(XSTCollector.MAX_FPGAS,), datatype=numpy.uint64) diff --git a/tangostationcontrol/tangostationcontrol/toolkit/archiver.py b/tangostationcontrol/tangostationcontrol/toolkit/archiver.py index 8f3e7a25046764eeedaf9fa2380b2fdbe76b0b6b..d6aada7d5c051b7140537dede572337f70dbbc14 100644 --- a/tangostationcontrol/tangostationcontrol/toolkit/archiver.py +++ b/tangostationcontrol/tangostationcontrol/toolkit/archiver.py @@ -7,6 +7,7 @@ from tango import DeviceProxy, AttributeProxy, DevState, DevFailed import time import json import pkg_resources +from functools import wraps logger = logging.getLogger() @@ -23,6 +24,19 @@ def attribute_name_from_url(attribute_name:str): return attribute_name +def attribute_name_url(attribute_name:str, tango_host:str = 'databaseds:10000'): + """ + For some operations Tango devices must be transformed from the form 'domain/family/name/attribute' + to 'tango://db:port/domain/family/name/attribute' + """ + if attribute_name.startswith('tango://'): + return attribute_name + + if len(attribute_name.split('/')) != 4: + raise ValueError(f"Expected attribute name of format 'domain/family/name/attribute', got {attribute_name}") + + return f"tango://{tango_host}/{attribute_name}" + def device_name_url(device_name:str, tango_host:str = 'databaseds:10000'): """ For some operations Tango devices must be transformed from the form 'domain/family/name' @@ -64,9 +78,9 @@ def warn_if_attribute_not_found(): """ def inner(func): @wraps(func) - def warn_wrapper(self, *args, **kwargs): + def warn_wrapper(self, attribute_name, *args, **kwargs): try: - return func(self, *args, **kwargs) + return func(self, attribute_name, *args, **kwargs) except DevFailed as e: if e.args[0].reason == 'Attribute not found': logger.warning(f"Attribute {attribute_name} not found!") @@ -245,15 +259,14 @@ class Archiver(): for a in attrs_list: attr_fullname = f"{device_name}/{a}".lower() attr_proxy = AttributeProxy(attr_fullname) - if attr_proxy.is_polled() is True: # if not polled attribute is also not archived + if attr_proxy.is_polled() and not self.is_attribute_archived(attr_fullname): # if not polled attribute is also not archived try: es = DeviceProxy(es_name or self.get_next_subscriber()) # choose an e.s. or get the first one available - if es.AttributeList is None or not(self.cm.AttributeSearch(a)): - polling_period = attr_proxy.get_poll_period() or self.dev_polling_time - archive_period = global_archive_period or int(attr_proxy.get_property('archive_period')['archive_period'][0]) or self.dev_archive_time - self.add_attribute_to_archiver(attr_fullname,polling_period=polling_period, - event_period=archive_period, es_name = es.name()) - #time.sleep(0.5) + polling_period = attr_proxy.get_poll_period() or self.dev_polling_time + archive_period = global_archive_period or int(attr_proxy.get_property('archive_period')['archive_period'][0]) or self.dev_archive_time + self.add_attribute_to_archiver(attr_fullname,polling_period=polling_period, + event_period=archive_period, es_name = es.name()) + #time.sleep(0.5) except IndexError as e: logger.warning(f"Attribute {attr_fullname} will not be archived because archive event period is not defined!") except Exception as e: @@ -284,7 +297,8 @@ class Archiver(): for a in attrs_list: try: attr_fullname = f"{device_name}/{a}".lower() - self.remove_attribute_from_archiver(attr_fullname) + if self.is_attribute_archived(attr_fullname): + self.remove_attribute_from_archiver(attr_fullname) except Exception as e: raise Exception from e @@ -331,16 +345,10 @@ class Archiver(): """ attribute_name = attribute_name_from_url(attribute_name) attributes = self.cm.AttributeSearch(attribute_name.lower()) - if len(attributes)>1: - # Handle case same attribute_name r/rw - if len(attributes)==2 and (attributes[0].endswith(attributes[1]+'w') or attributes[1].endswith(attributes[0]+'w')): - return True - else: - raise Exception(f"Multiple Attributes Matched: {attributes}") - elif len(attributes)==1: - return True - else: - return False + + # search returns all matches in which attribute_name is part of the name, + # so check whether an exact match is included. + return attribute_name_url(attribute_name) in attributes def update_archiving_attribute(self, attribute_name: str, polling_period: int, event_period: int, strategy: str = 'RUN'): """