diff --git a/CDB/LOFAR_ConfigDb.json b/CDB/LOFAR_ConfigDb.json index 98c3a994428d5a23a93ae842f3cdadf609cb7eca..c7fc852c35cb9fcff76b24ab0e10664e17489156 100644 --- a/CDB/LOFAR_ConfigDb.json +++ b/CDB/LOFAR_ConfigDb.json @@ -1,5 +1,12 @@ { "servers": { + "docker_device": { + "LTS": { + "Docker": { + "LTS/Docker/1": {} + } + } + }, "Femto": { "CS999": { "Femto": { @@ -758,22 +765,22 @@ "5.0" ], "FPGA_sst_offload_hdr_eth_destination_mac_RW_default": [ - "6c:2b:59:97:cb:de", - "6c:2b:59:97:cb:de", - "6c:2b:59:97:cb:de", - "6c:2b:59:97:cb:de", - "6c:2b:59:97:cb:de", - "6c:2b:59:97:cb:de", - "6c:2b:59:97:cb:de", - "6c:2b:59:97:cb:de", - "6c:2b:59:97:cb:de", - "6c:2b:59:97:cb:de", - "6c:2b:59:97:cb:de", - "6c:2b:59:97:cb:de", - "6c:2b:59:97:cb:de", - "6c:2b:59:97:cb:de", - "6c:2b:59:97:cb:de", - "6c:2b:59:97:cb:de" + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd" ], "FPGA_sst_offload_hdr_ip_destination_address_RW_default": [ "10.99.250.250", @@ -816,6 +823,85 @@ } } }, + "XST": { + "LTS": { + "XST": { + "LTS/XST/1": { + "properties": { + "Statistics_Client_UDP_Port": [ + "5002" + ], + "Statistics_Client_TCP_Port": [ + "5102" + ], + "OPC_Server_Name": [ + "dop36.astron.nl" + ], + "OPC_Server_Port": [ + "4840" + ], + "OPC_Time_Out": [ + "5.0" + ], + "FPGA_xst_offload_hdr_eth_destination_mac_RW_default": [ + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd" + ], + "FPGA_xst_offload_hdr_ip_destination_address_RW_default": [ + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250" + ], + "FPGA_xst_offload_hdr_udp_destination_port_RW_default": [ + "5002", + "5002", + "5002", + "5002", + "5002", + "5002", + "5002", + "5002", + "5002", + "5002", + "5002", + "5002", + "5002", + "5002", + "5002", + "5002" + ] + } + } + } + } + }, "UNB2": { "LTS": { "UNB2": { diff --git a/CDB/integration_ConfigDb.json b/CDB/integration_ConfigDb.json index debeae5263b85b15821ed515521223751ff9d37c..a73e1f0f10a3d834d9af63ab81a75936f0183843 100644 --- a/CDB/integration_ConfigDb.json +++ b/CDB/integration_ConfigDb.json @@ -107,22 +107,22 @@ "5.0" ], "FPGA_sst_offload_hdr_eth_destination_mac_RW_default": [ - "6c:2b:59:97:cb:de", - "6c:2b:59:97:cb:de", - "6c:2b:59:97:cb:de", - "6c:2b:59:97:cb:de", - "6c:2b:59:97:cb:de", - "6c:2b:59:97:cb:de", - "6c:2b:59:97:cb:de", - "6c:2b:59:97:cb:de", - "6c:2b:59:97:cb:de", - "6c:2b:59:97:cb:de", - "6c:2b:59:97:cb:de", - "6c:2b:59:97:cb:de", - "6c:2b:59:97:cb:de", - "6c:2b:59:97:cb:de", - "6c:2b:59:97:cb:de", - "6c:2b:59:97:cb:de" + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd" ], "FPGA_sst_offload_hdr_ip_destination_address_RW_default": [ "10.99.250.250", @@ -164,6 +164,104 @@ } } } + }, + "XST": { + "LTS": { + "XST": { + "LTS/XST/1": { + "properties": { + "Statistics_Client_UDP_Port": [ + "5002" + ], + "Statistics_Client_TCP_Port": [ + "5102" + ], + "OPC_Server_Name": [ + "sdptr-sim" + ], + "OPC_Server_Port": [ + "4840" + ], + "OPC_Time_Out": [ + "5.0" + ], + "FPGA_xst_offload_hdr_eth_destination_mac_RW_default": [ + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd", + "6c:2b:59:97:be:dd" + ], + "FPGA_xst_offload_hdr_ip_destination_address_RW_default": [ + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250", + "10.99.250.250" + ], + "FPGA_xst_offload_hdr_udp_destination_port_RW_default": [ + "5002", + "5002", + "5002", + "5002", + "5002", + "5002", + "5002", + "5002", + "5002", + "5002", + "5002", + "5002", + "5002", + "5002", + "5002", + "5002" + ] + } + } + } + } + }, + "UNB2": { + "LTS": { + "UNB2": { + "LTS/UNB2/1": { + "properties": { + "OPC_Server_Name": [ + "despi.astron.nl" + ], + "OPC_Server_Port": [ + "4842" + ], + "OPC_Time_Out": [ + "5.0" + ] + } + } + } + } } } } diff --git a/CDB/sdp-sim-config.json b/CDB/sdp-sim-config.json index 31232e7701074e19044660af1fd27c6c025b4f81..f733a85a6c570ccdc25646d894bace08c78e9acf 100644 --- a/CDB/sdp-sim-config.json +++ b/CDB/sdp-sim-config.json @@ -24,12 +24,25 @@ "SST": { "LTS/SST/1": { "properties": { - "Statistics_Client_UDP_Port": [ - "5001" + "OPC_Server_Name": [ + "sdptr-sim" ], - "Statistics_Client_TCP_Port": [ - "5101" + "OPC_Server_Port": [ + "4840" ], + "OPC_Time_Out": [ + "5.0" + ] + } + } + } + } + }, + "XST": { + "LTS": { + "XST": { + "LTS/XST/1": { + "properties": { "OPC_Server_Name": [ "sdptr-sim" ], diff --git a/CDB/unb2-sim-config.json b/CDB/unb2-sim-config.json new file mode 100644 index 0000000000000000000000000000000000000000..a98fa27492e3835867b214cfd2789caf949de460 --- /dev/null +++ b/CDB/unb2-sim-config.json @@ -0,0 +1,23 @@ +{ + "servers": { + "UNB2": { + "LTS": { + "UNB2": { + "LTS/UNB2/1": { + "properties": { + "OPC_Server_Name": [ + "unb2-sim" + ], + "OPC_Server_Port": [ + "4844" + ], + "OPC_Time_Out": [ + "5.0" + ] + } + } + } + } + } + } +} diff --git a/devices/clients/attribute_wrapper.py b/devices/clients/attribute_wrapper.py index 12e5c83516e2c68c2216aca5ba9b39a1fa6f4f8c..4cb389824750cb9d01fc836e8d65caf3656d59a4 100644 --- a/devices/clients/attribute_wrapper.py +++ b/devices/clients/attribute_wrapper.py @@ -29,7 +29,14 @@ class attribute_wrapper(attribute): # see also https://pytango.readthedocs.io/en/stable/server_api/server.html?highlight=devlong#module-tango.server for # more details about type conversion Python/numpy -> PyTango if "numpy" not in str(datatype) and datatype != str: - raise TypeError("Attribute needs to be a Tango-supported numpy or str type, but has type \"%s\"" % (datatype,)) + raise ValueError("Attribute needs to be a Tango-supported numpy or str type, but has type \"%s\"" % (datatype,)) + + """ + Numpy has a depracated string type called numpy.str_. + this behaves differently from numpy.str (which is literally just an str. + """ + if datatype == numpy.str_: + raise Exception("numpy.str_ type not supported, please use numpy.str instead") self.comms_id = comms_id # store data that can be used to identify the comms interface to use. not used by the wrapper itself self.comms_annotation = comms_annotation # store data that can be used by the comms interface. not used by the wrapper itself @@ -37,10 +44,7 @@ class attribute_wrapper(attribute): self.init_value = init_value is_scalar = dims == (1,) - # tango doesn't recognise numpy.str_, for consistencies sake we convert it here and hide this from the top level - # NOTE: discuss, idk if this is an important detail somewhere else - if datatype is numpy.str_ or datatype is numpy.str: - datatype = str + self.numpy_type = datatype # tango changes our attribute to their representation (E.g numpy.int64 becomes "DevLong64") diff --git a/devices/clients/docker_client.py b/devices/clients/docker_client.py new file mode 100644 index 0000000000000000000000000000000000000000..c5b0e8b81f69e7f83ae381468b6bcd738f9ec296 --- /dev/null +++ b/devices/clients/docker_client.py @@ -0,0 +1,71 @@ +import logging +import docker + +from .comms_client import CommClient + +logger = logging.getLogger() + +class DockerClient(CommClient): + """ + Controls & queries running docker containers. + """ + + def start(self): + super().start() + + def __init__(self, base_url, fault_func, streams): + super().__init__(fault_func, streams) + + self.base_url = base_url + + def connect(self): + """ + Function used to connect to the client. + """ + if not self.connected: + self.client = docker.DockerClient(self.base_url) + + return super().connect() + + def ping(self): + return True + + def disconnect(self): + self.client = None + + return super().disconnect() + + def setup_value_conversion(self, attribute): + """ + gives the client access to the attribute_wrapper object in order to access all data it could potentially need. + the OPC ua read/write functions require the dimensionality and the type to be known + """ + return + + def setup_attribute(self, annotation, attribute): + """ + MANDATORY function: is used by the attribute wrapper to get read/write functions. must return the read and write functions + """ + + container_name = annotation["container"] + + # get all the necessary data to set up the read/write functions from the attribute_wrapper + self.setup_value_conversion(attribute) + + def read_function(): + try: + container = self.client.containers.get(container_name) + except docker.errors.NotFound: + return False + + return container.status == 'running' + + def write_function(value): + container = self.client.containers.get(container_name) + + if value: + container.start() + else: + container.stop() + + return read_function, write_function diff --git a/devices/clients/opcua_client.py b/devices/clients/opcua_client.py index 6b687837a393a97727a231cea698fb9137485946..41d6d0e740de5bc0aabfb7a39b8afd4be6309483 100644 --- a/devices/clients/opcua_client.py +++ b/devices/clients/opcua_client.py @@ -21,7 +21,7 @@ numpy_to_OPCua_dict = { numpy.float32: opcua.ua.VariantType.Float, numpy.double: opcua.ua.VariantType.Double, numpy.float64: opcua.ua.VariantType.Double, - str: opcua.ua.VariantType.String + numpy.str: opcua.ua.VariantType.String } # <class 'numpy.bool_'> @@ -114,7 +114,8 @@ class OPCUAConnection(CommClient): ping the client to make sure the connection with the client is still functional. """ try: - self.client.send_hello() + #self.client.send_hello() # <-- this crashes when communicating with open62541 v1.2.2+ + pass except Exception as e: raise Exception("Lost connection to server %s: %s", self._servername(), e) diff --git a/devices/clients/statistics_client.py b/devices/clients/statistics_client.py index 05aa28d39ebb6c6f7ea5dc4fb8e0c908856046b1..eb37e9dc24b7cc80e557d9c5b2b060d73e652564 100644 --- a/devices/clients/statistics_client.py +++ b/devices/clients/statistics_client.py @@ -137,7 +137,10 @@ class StatisticsClient(CommClient): elif annotation["type"] == "replicator": if parameter == "clients": def read_function(): - return numpy.array(self.tcp.clients(),dtype=numpy.str_) + return numpy.array(self.tcp.clients(),dtype=numpy.str) + elif parameter == "nof_bytes_sent": + def read_function(): + return numpy.uint64(self.tcp.nof_bytes_sent) elif parameter == "nof_packets_sent": def read_function(): return numpy.uint64(self.tcp.nof_packets_sent) diff --git a/devices/clients/tcp_replicator.py b/devices/clients/tcp_replicator.py index 37d5a4f78221c1e1d234447f250dc0c4a27b8ad0..5ac6e492d977cf14452d4f97bd213c0d12af7cbb 100644 --- a/devices/clients/tcp_replicator.py +++ b/devices/clients/tcp_replicator.py @@ -60,6 +60,7 @@ class TCPReplicator(Thread, StatisticsClientThread): # statistics self.nof_packets_sent = 0 + self.nof_bytes_sent = 0 """Reserve asyncio event loop attribute but don't create it yet. This event loop is created inside the new Thread, the result is that @@ -277,11 +278,11 @@ class TCPReplicator(Thread, StatisticsClientThread): self._loop.create_task(self._transmit(packet)) async def _transmit(self, data): - logger.debug("Transmitting") for client in self._connected_clients: client.transport.write(data) self.nof_packets_sent += 1 + self.nof_bytes_sent += len(data) async def _disconnect(self): with self.disconnect_condition: diff --git a/devices/clients/udp_receiver.py b/devices/clients/udp_receiver.py index bf86c363bd55d461c5c80d3b11060f19cf4e970e..8a9d1429945cdd5c41c47bf45edc5034c1cafa0c 100644 --- a/devices/clients/udp_receiver.py +++ b/devices/clients/udp_receiver.py @@ -46,6 +46,8 @@ class UDPReceiver(Thread, StatisticsClientThread): self.parameters = { # Number of packets we received "nof_packets_received": numpy.uint64(0), + # Number of bytes we received + "nof_bytes_received": numpy.uint64(0), # Number of packets we had to drop due to a full queue "nof_packets_dropped": numpy.uint64(0), # Packets are at most 9000 bytes, the largest payload (well, MTU) of an Ethernet Jumbo frame @@ -90,6 +92,7 @@ class UDPReceiver(Thread, StatisticsClientThread): packet, _, _, _ = self.sock.recvmsg(9000) self.parameters["nof_packets_received"] += numpy.uint64(1) + self.parameters["nof_bytes_received"] += numpy.uint64(len(packet)) self.parameters["last_packet"] = numpy.frombuffer(packet, dtype=numpy.uint8) self.parameters["last_packet_timestamp"] = numpy.uint64(int(time.time())) diff --git a/devices/common/baselines.py b/devices/common/baselines.py new file mode 100644 index 0000000000000000000000000000000000000000..b9b0ca8038c0d881d602df37f99203d733f283fc --- /dev/null +++ b/devices/common/baselines.py @@ -0,0 +1,59 @@ +""" + Baseline calculation functions. +""" + +import math + +def nr_baselines(nr_inputs: int) -> int: + """ Return the number of baselines (unique pairs) that exist between a given number of inputs. """ + return nr_inputs * (nr_inputs + 1) // 2 + +""" + + Baselines are ordered like: + 0-0, 1-0, 1-1, 2-0, 2-1, 2-2, ... + + if + b = baseline + x = stat1 (major) + y = stat2 (minor) + x >= y + then + b_xy = x * (x + 1) / 2 + y + let + u := b_x0 + then + u = x * (x + 1) / 2 + 8u = 4x^2 + 4x + 8u + 1 = 4x^2 + 4x + 1 = (2x + 1)^2 + sqrt(8u + 1) = 2x + 1 + x = (sqrt(8u + 1) - 1) / 2 + + Let us define + x'(b) = (sqrt(8b + 1) - 1) / 2 + which increases monotonically and is a continuation of y(b). + + Because y simply increases by 1 when b increases enough, we + can just take the floor function to obtain the discrete y(b): + x(b) = floor(x'(b)) + = floor(sqrt(8b + 1) - 1) / 2) + +""" + +def baseline_index(major: int, minor: int) -> int: + """ Provide a total ordering of baselines: give the unique array index for the baseline (major,minor), + with major >= minor. """ + + if major < minor: + raise ValueError(f"major < minor: {major} < {minor}. Since we do not store the conjugates this will lead to processing errors.") + + return major * (major + 1) // 2 + minor + +def baseline_from_index(index: int) -> tuple: + """ Return the (major,minor) input pair given a baseline index. """ + + major = int((math.sqrt(float(8 * index + 1)) - 0.99999) / 2) + minor = index - baseline_index(major,0) + + return (major,minor) + diff --git a/devices/common/lofar_logging.py b/devices/common/lofar_logging.py index aed0353461d75ae6ad46b4b10ad51289fb08b553..c605d8cf927f890083dafc3ec85a16c1dab70d9d 100644 --- a/devices/common/lofar_logging.py +++ b/devices/common/lofar_logging.py @@ -4,6 +4,7 @@ from tango.server import Device import sys import traceback import socket +import time from .lofar_git import get_version @@ -33,6 +34,38 @@ class TangoLoggingHandler(logging.Handler): self.flush() +class LogSuppressErrorSpam(logging.Formatter): + """ + Suppress specific errors from spamming the logs, by only letting them through periodically. + """ + + def __init__(self, error_suppress_interval = 3600): + """ Suppress subsequent errors for `error_suppress_interval` seconds. """ + + super().__init__() + + # last time we logged an error + self.last_error_log_time = 0 + + # suppression interval at which we report errors + self.error_suppress_interval = error_suppress_interval + + def is_error_to_suppress(self, record): + # Errors occuring by not being able to connect to the ELK stack, f.e. because it is down. + return record.name == "LogProcessingWorker" and record.msg == "An error occurred while sending events: %s" + + def filter(self, record): + if self.is_error_to_suppress(record): + # filter out error if it occurred within our suppression interval + now = time.time() + + if now - self.last_error_log_time < self.error_suppress_interval: + return False + + self.last_error_log_time = now + + return True + class LogAnnotator(logging.Formatter): """ Annotates log records with: @@ -91,6 +124,7 @@ def configure_logger(logger: logging.Logger=None, log_extra=None): # configure log messages formatter = LogstashFormatter(extra=log_extra, tags=["python", "lofar"]) handler.setFormatter(formatter) + handler.addFilter(LogSuppressErrorSpam()) handler.addFilter(LogAnnotator()) # install the handler @@ -103,6 +137,7 @@ def configure_logger(logger: logging.Logger=None, log_extra=None): # Log to Tango try: handler = TangoLoggingHandler() + handler.addFilter(LogSuppressErrorSpam()) handler.addFilter(LogAnnotator()) logger.addHandler(handler) except Exception: @@ -120,6 +155,7 @@ def configure_logger(logger: logging.Logger=None, log_extra=None): formatter = logging.Formatter(fmt = '%(asctime)s.%(msecs)d %(levelname)s - HOST="{}" DEVICE="%(tango_device)s" PID="%(process)d" TNAME="%(threadName)s" FILE="%(pathname)s" LINE="%(lineno)d" FUNC="%(funcName)s" MSG="%(message)s"'.format(hostname), datefmt = '%Y-%m-%dT%H:%M:%S') handler.setFormatter(formatter) + handler.addFilter(LogSuppressErrorSpam()) handler.addFilter(LogAnnotator()) logger.addHandler(handler) diff --git a/devices/devices/abstract_device.py b/devices/devices/abstract_device.py new file mode 100644 index 0000000000000000000000000000000000000000..5b65c3a6c02fd487920e02efed2394de275f1a9d --- /dev/null +++ b/devices/devices/abstract_device.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# +# This file is part of the XXX project +# +# +# +# Distributed under the terms of the APACHE license. +# See LICENSE.txt for more info. + +"""Abstract Device Meta for LOFAR2.0 + +""" + +from abc import ABCMeta +import logging + +from tango.server import DeviceMeta + +logger = logging.getLogger() + + +class AbstractDeviceMetas(DeviceMeta, ABCMeta): + """Collects meta classes to allow hardware_device to be both a Device and an ABC. """ + + def __new__(mcs, name, bases, namespace, **kwargs): + cls = ABCMeta.__new__(mcs, name, bases, namespace, **kwargs) + cls = DeviceMeta.__new__(type(cls), name, bases, namespace) + return cls diff --git a/devices/devices/docker_device.py b/devices/devices/docker_device.py new file mode 100644 index 0000000000000000000000000000000000000000..5ff0ec366c436a2dfc75d4cd479219a04c6938d3 --- /dev/null +++ b/devices/devices/docker_device.py @@ -0,0 +1,167 @@ +# -*- coding: utf-8 -*- +# +# This file is part of the Docker project +# +# +# +# Distributed under the terms of the APACHE license. +# See LICENSE.txt for more info. + +""" Docker Device Server for LOFAR2.0 + +""" + +# TODO(Corne): Remove sys.path.append hack once packaging is in place! +import os, sys +currentdir = os.path.dirname(os.path.realpath(__file__)) +parentdir = os.path.dirname(currentdir) +sys.path.append(parentdir) + +# PyTango imports +from tango import DebugIt +from tango.server import run, command +from tango.server import device_property, attribute +from tango import AttrWriteType +import numpy +# Additional import + +from device_decorators import * + +from clients.docker_client import DockerClient +from clients.attribute_wrapper import attribute_wrapper +from devices.hardware_device import hardware_device +from common.lofar_logging import device_logging_to_python, log_exceptions +from common.lofar_git import get_version + +__all__ = ["Docker", "main"] + +@device_logging_to_python() +class Docker(hardware_device): + """ + + **Properties:** + + - Device Property + OPC_Server_Name + - Type:'DevString' + OPC_Server_Port + - Type:'DevULong' + OPC_Time_Out + - Type:'DevDouble' + """ + + # ----------------- + # Device Properties + # ----------------- + + Docker_Base_URL = device_property( + dtype='DevString', + mandatory=False, + default_value="unix:///var/run/docker.sock" + ) + + # ---------- + # Attributes + # ---------- + version_R = attribute(dtype=str, access=AttrWriteType.READ, fget=lambda self: get_version()) + archiver_maria_db_R = attribute_wrapper(comms_annotation={"container": "archiver-maria-db"}, datatype=numpy.bool_) + archiver_maria_db_RW = attribute_wrapper(comms_annotation={"container": "archiver-maria-db"}, datatype=numpy.bool_, access=AttrWriteType.READ_WRITE) + databaseds_R = attribute_wrapper(comms_annotation={"container": "databaseds"}, datatype=numpy.bool_) + databaseds_RW = attribute_wrapper(comms_annotation={"container": "databaseds"}, datatype=numpy.bool_, access=AttrWriteType.READ_WRITE) + device_recv_R = attribute_wrapper(comms_annotation={"container": "device-recv"}, datatype=numpy.bool_) + device_recv_RW = attribute_wrapper(comms_annotation={"container": "device-recv"}, datatype=numpy.bool_, access=AttrWriteType.READ_WRITE) + device_sdp_R = attribute_wrapper(comms_annotation={"container": "device-sdp"}, datatype=numpy.bool_) + device_sdp_RW = attribute_wrapper(comms_annotation={"container": "device-sdp"}, datatype=numpy.bool_, access=AttrWriteType.READ_WRITE) + device_sst_R = attribute_wrapper(comms_annotation={"container": "device-sst"}, datatype=numpy.bool_) + device_sst_RW = attribute_wrapper(comms_annotation={"container": "device-sst"}, datatype=numpy.bool_, access=AttrWriteType.READ_WRITE) + device_xst_R = attribute_wrapper(comms_annotation={"container": "device-xst"}, datatype=numpy.bool_) + device_xst_RW = attribute_wrapper(comms_annotation={"container": "device-xst"}, datatype=numpy.bool_, access=AttrWriteType.READ_WRITE) + device_unb2_R = attribute_wrapper(comms_annotation={"container": "device-unb2"}, datatype=numpy.bool_) + device_unb2_RW = attribute_wrapper(comms_annotation={"container": "device-unb2"}, datatype=numpy.bool_, access=AttrWriteType.READ_WRITE) + device_docker_R = attribute_wrapper(comms_annotation={"container": "device-docker"}, datatype=numpy.bool_) + # device_docker_RW is not available, as we cannot start our own container` + dsconfig_R = attribute_wrapper(comms_annotation={"container": "dsconfig"}, datatype=numpy.bool_) + dsconfig_RW = attribute_wrapper(comms_annotation={"container": "dsconfig"}, datatype=numpy.bool_, access=AttrWriteType.READ_WRITE) + elk_R = attribute_wrapper(comms_annotation={"container": "elk"}, datatype=numpy.bool_) + elk_RW = attribute_wrapper(comms_annotation={"container": "elk"}, datatype=numpy.bool_, access=AttrWriteType.READ_WRITE) + grafana_R = attribute_wrapper(comms_annotation={"container": "grafana"}, datatype=numpy.bool_) + grafana_RW = attribute_wrapper(comms_annotation={"container": "grafana"}, datatype=numpy.bool_, access=AttrWriteType.READ_WRITE) + hdbpp_cm_R = attribute_wrapper(comms_annotation={"container": "hdbpp-cm"}, datatype=numpy.bool_) + hdbpp_cm_RW = attribute_wrapper(comms_annotation={"container": "hdbpp-cm"}, datatype=numpy.bool_, access=AttrWriteType.READ_WRITE) + hdbpp_es_R = attribute_wrapper(comms_annotation={"container": "hdbpp-es"}, datatype=numpy.bool_) + hdbpp_es_RW = attribute_wrapper(comms_annotation={"container": "hdbpp-es"}, datatype=numpy.bool_, access=AttrWriteType.READ_WRITE) + itango_R = attribute_wrapper(comms_annotation={"container": "itango"}, datatype=numpy.bool_) + itango_RW = attribute_wrapper(comms_annotation={"container": "itango"}, datatype=numpy.bool_, access=AttrWriteType.READ_WRITE) + jupyter_R = attribute_wrapper(comms_annotation={"container": "jupyter"}, datatype=numpy.bool_) + jupyter_RW = attribute_wrapper(comms_annotation={"container": "jupyter"}, datatype=numpy.bool_, access=AttrWriteType.READ_WRITE) + prometheus_R = attribute_wrapper(comms_annotation={"container": "prometheus"}, datatype=numpy.bool_) + prometheus_RW = attribute_wrapper(comms_annotation={"container": "prometheus"}, datatype=numpy.bool_, access=AttrWriteType.READ_WRITE) + tangodb_R = attribute_wrapper(comms_annotation={"container": "tangodb"}, datatype=numpy.bool_) + tangodb_RW = attribute_wrapper(comms_annotation={"container": "tangodb"}, datatype=numpy.bool_, access=AttrWriteType.READ_WRITE) + tango_prometheus_exporter_R = attribute_wrapper(comms_annotation={"container": "tango-prometheus-exporter"}, datatype=numpy.bool_) + tango_prometheus_exporter_RW = attribute_wrapper(comms_annotation={"container": "tango-prometheus-exporter"}, datatype=numpy.bool_, access=AttrWriteType.READ_WRITE) + tango_rest_R = attribute_wrapper(comms_annotation={"container": "tango-rest"}, datatype=numpy.bool_) + tango_rest_RW = attribute_wrapper(comms_annotation={"container": "tango-rest"}, datatype=numpy.bool_, access=AttrWriteType.READ_WRITE) + + @log_exceptions() + 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 (a Tango built-in). + """ + self.debug_stream("Shutting down...") + + self.Off() + self.debug_stream("Shut down. Good bye.") + + # -------- + # overloaded functions + # -------- + @log_exceptions() + def configure_for_off(self): + """ user code here. is called when the state is set to OFF """ + # Stop keep-alive + try: + self.opcua_connection.stop() + except Exception as e: + self.warn_stream("Exception while stopping OPC ua connection in configure_for_off function: {}. Exception ignored".format(e)) + + @log_exceptions() + def configure_for_initialise(self): + """ user code here. is called when the state is set to INIT """ + + # set up the Docker client + self.docker_client = DockerClient(self.Docker_Base_URL, self.Fault, self) + + # map an access helper class + for i in self.attr_list(): + try: + i.set_comm_client(self.docker_client) + except Exception as e: + # use the pass function instead of setting read/write fails + i.set_pass_func() + self.warn_stream("error while setting the attribute {} read/write function. {}".format(i, e)) + + self.docker_client.start() + + # -------- + # Commands + # -------- + + +# ---------- +# Run server +# ---------- +def main(args=None, **kwargs): + """Main function of the Docker module.""" + + from common.lofar_logging import configure_logger + configure_logger() + + return run((Docker,), args=args, **kwargs) + + +if __name__ == '__main__': + main() diff --git a/devices/devices/hardware_device.py b/devices/devices/hardware_device.py index 589eaa7cf9b06ce4b0a4d3d068d8eb17dd7e9eb8..f8f6ca50d7e02f5a8694c2ec4f9135dd874cd516 100644 --- a/devices/devices/hardware_device.py +++ b/devices/devices/hardware_device.py @@ -20,18 +20,15 @@ from tango import DevState, DebugIt, Attribute, DeviceProxy from clients.attribute_wrapper import attribute_wrapper from common.lofar_logging import log_exceptions -import logging +from devices.abstract_device import AbstractDeviceMetas +from devices.device_decorators import only_in_states, fault_on_error +import logging __all__ = ["hardware_device"] -from devices.device_decorators import only_in_states, fault_on_error - logger = logging.getLogger() -class AbstractDeviceMetas(DeviceMeta, ABCMeta): - ''' Collects meta classes to allow hardware_device to be both a Device and an ABC. ''' - pass #@log_exceptions() class hardware_device(Device, metaclass=AbstractDeviceMetas): diff --git a/devices/devices/recv.py b/devices/devices/recv.py index 1bf57f0b420083ef961c8c340d88e226342a8848..6f1de6aedc9e6db463c2edcd7a1a8bdf3daf7c2e 100644 --- a/devices/devices/recv.py +++ b/devices/devices/recv.py @@ -108,7 +108,7 @@ class RECV(hardware_device): RCU_Pwr_dig_R = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_Pwr_dig_R"], datatype=numpy.bool_, dims=(32,)) RCU_temperature_R = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_temperature_R"], datatype=numpy.float64, dims=(32,)) RCU_translator_busy_R = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_translator_busy_R"], datatype=numpy.bool_) - RCU_version_R = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_version_R"], datatype=numpy.str_, dims=(32,)) + RCU_version_R = attribute_wrapper(comms_annotation=["2:PCC", "2:RCU_version_R"], datatype=numpy.str, dims=(32,)) @log_exceptions() def delete_device(self): diff --git a/devices/devices/sdp/sdp.py b/devices/devices/sdp/sdp.py index 3c78775d2773e080c0849bad2a1a39ae9812234f..75e027b571cefe0bdfa68621b37f45dd26d98aae 100644 --- a/devices/devices/sdp/sdp.py +++ b/devices/devices/sdp/sdp.py @@ -99,12 +99,15 @@ class SDP(hardware_device): version_R = attribute(dtype=str, access=AttrWriteType.READ, fget=lambda self: get_version()) # SDP will switch from FPGA_mask_RW to TR_FPGA_mask_RW, offer both for now as its a critical flag - FPGA_firmware_version_R = attribute_wrapper(comms_annotation=["2:FPGA_firmware_version_R"], datatype=numpy.str_, dims=(16,)) - FPGA_hardware_version_R = attribute_wrapper(comms_annotation=["2:FPGA_hardware_version_R"], datatype=numpy.str_, dims=(16,)) + FPGA_firmware_version_R = attribute_wrapper(comms_annotation=["2:FPGA_firmware_version_R"], datatype=numpy.str, dims=(16,)) + FPGA_global_node_index_R = attribute_wrapper(comms_annotation=["2:FPGA_global_node_index_R"], datatype=numpy.uint32, dims=(16,)) + FPGA_hardware_version_R = attribute_wrapper(comms_annotation=["2:FPGA_hardware_version_R"], datatype=numpy.str, dims=(16,)) FPGA_processing_enable_R = attribute_wrapper(comms_annotation=["2:FPGA_processing_enable_R"], datatype=numpy.bool_, dims=(16,)) FPGA_processing_enable_RW = attribute_wrapper(comms_annotation=["2:FPGA_processing_enable_RW"], datatype=numpy.bool_, dims=(16,), access=AttrWriteType.READ_WRITE) FPGA_scrap_R = attribute_wrapper(comms_annotation=["2:FPGA_scrap_R"], datatype=numpy.int32, dims=(8192,)) FPGA_scrap_RW = attribute_wrapper(comms_annotation=["2:FPGA_scrap_RW"], datatype=numpy.int32, dims=(8192,), access=AttrWriteType.READ_WRITE) + FPGA_signal_input_mean_R = attribute_wrapper(comms_annotation=["2:FPGA_signal_input_mean_R"], datatype=numpy.double, dims=(12, 16)) + FPGA_signal_input_rms_R = attribute_wrapper(comms_annotation=["2:FPGA_signal_input_rms_R"], datatype=numpy.double, dims=(12, 16)) FPGA_sdp_info_antenna_band_index_R = attribute_wrapper(comms_annotation=["2:FPGA_sdp_info_antenna_band_index_R"], datatype=numpy.uint32, dims=(16,)) FPGA_sdp_info_block_period_R = attribute_wrapper(comms_annotation=["2:FPGA_sdp_info_block_period_R"], datatype=numpy.uint32, dims=(16,)) FPGA_sdp_info_f_adc_R = attribute_wrapper(comms_annotation=["2:FPGA_sdp_info_f_adc_R"], datatype=numpy.uint32, dims=(16,)) @@ -116,7 +119,7 @@ class SDP(hardware_device): FPGA_sdp_info_station_id_R = attribute_wrapper(comms_annotation=["2:FPGA_sdp_info_station_id_R"], datatype=numpy.uint32, dims=(16,)) FPGA_sdp_info_station_id_RW = attribute_wrapper(comms_annotation=["2:FPGA_sdp_info_station_id_RW"], datatype=numpy.uint32, dims=(16,), access=AttrWriteType.READ_WRITE) FPGA_subband_weights_R = attribute_wrapper(comms_annotation=["2:FPGA_subband_weights_R"], datatype=numpy.uint32, dims=(12 * 512, 16)) - FPGA_subband_weights_RW = attribute_wrapper(comms_annotation=["2:FPGA_subband_weights_R"], datatype=numpy.uint32, dims=(12 * 512, 16)) + FPGA_subband_weights_RW = attribute_wrapper(comms_annotation=["2:FPGA_subband_weights_RW"], datatype=numpy.uint32, dims=(12 * 512, 16), access=AttrWriteType.READ_WRITE) FPGA_temp_R = attribute_wrapper(comms_annotation=["2:FPGA_temp_R"], datatype=numpy.float_, dims=(16,)) FPGA_weights_R = attribute_wrapper(comms_annotation=["2:FPGA_weights_R"], datatype=numpy.int16, dims=(12 * 488 * 2, 16)) FPGA_weights_RW = attribute_wrapper(comms_annotation=["2:FPGA_weights_RW"], datatype=numpy.int16, dims=(12 * 488 * 2, 16), access=AttrWriteType.READ_WRITE) @@ -131,10 +134,13 @@ class SDP(hardware_device): TR_fpga_mask_R = attribute_wrapper(comms_annotation=["2:TR_fpga_mask_R"], datatype=numpy.bool_, dims=(16,)) TR_fpga_mask_RW = attribute_wrapper(comms_annotation=["2:TR_fpga_mask_RW"], datatype=numpy.bool_, dims=(16,), access=AttrWriteType.READ_WRITE) TR_fpga_communication_error_R = attribute_wrapper(comms_annotation=["2:TR_fpga_communication_error_R"], datatype=numpy.bool_, dims=(16,)) - TR_software_version_R = attribute_wrapper(comms_annotation=["2:TR_software_version_R"], datatype=numpy.str_) - TR_start_time_R = attribute_wrapper(comms_annotation=["2:TR_start_time_R"], datatype=numpy.int32) - TR_tod_R = attribute_wrapper(comms_annotation=["2:TR_tod_R"], datatype=numpy.uint64) - TR_tod_pps_delta_R = attribute_wrapper(comms_annotation=["2:TR_tod_pps_delta_R"], datatype=numpy.float_) + TR_sdp_config_first_fpga_nr_R = attribute_wrapper(comms_annotation=["2:TR_sdp_config_first_fpga_nr_R"], datatype=numpy.uint32) + TR_sdp_config_nof_beamsets_R = attribute_wrapper(comms_annotation=["2:TR_sdp_config_nof_beamsets_R"], datatype=numpy.uint32) + TR_sdp_config_nof_fpgas_R = attribute_wrapper(comms_annotation=["2:TR_sdp_config_nof_fpgas_R"], datatype=numpy.uint32) + TR_software_version_R = attribute_wrapper(comms_annotation=["2:TR_software_version_R"], datatype=numpy.str) + TR_start_time_R = attribute_wrapper(comms_annotation=["2:TR_start_time_R"], datatype=numpy.int64) + TR_tod_R = attribute_wrapper(comms_annotation=["2:TR_tod_R"], datatype=numpy.int64, dims=(2,)) + TR_tod_pps_delta_R = attribute_wrapper(comms_annotation=["2:TR_tod_pps_delta_R"], datatype=numpy.double) def always_executed_hook(self): """Method always executed before any TANGO command is executed.""" diff --git a/devices/devices/sdp/sst.py b/devices/devices/sdp/sst.py index 79fb6fb272b199d3069be03825cfd395f9d18929..3b2f36236a841adb0511b284cbeb4a0fbc6ee296 100644 --- a/devices/devices/sdp/sst.py +++ b/devices/devices/sdp/sst.py @@ -70,25 +70,27 @@ class SST(Statistics): # FPGA control points for SSTs FPGA_sst_offload_enable_RW = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_sst_offload_enable_RW"], datatype=numpy.bool_, dims=(16,), access=AttrWriteType.READ_WRITE) FPGA_sst_offload_enable_R = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_sst_offload_enable_R"], datatype=numpy.bool_, dims=(16,)) - FPGA_sst_offload_hdr_eth_destination_mac_RW = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_sst_offload_hdr_eth_destination_mac_RW"], datatype=numpy.str_, dims=(16,), access=AttrWriteType.READ_WRITE) - FPGA_sst_offload_hdr_eth_destination_mac_R = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_sst_offload_hdr_eth_destination_mac_R"], datatype=numpy.str_, dims=(16,)) - FPGA_sst_offload_hdr_ip_destination_address_RW = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_sst_offload_hdr_ip_destination_address_RW"], datatype=numpy.str_, dims=(16,), access=AttrWriteType.READ_WRITE) - FPGA_sst_offload_hdr_ip_destination_address_R = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_sst_offload_hdr_ip_destination_address_R"], datatype=numpy.str_, dims=(16,)) + FPGA_sst_offload_hdr_eth_destination_mac_RW = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_sst_offload_hdr_eth_destination_mac_RW"], datatype=numpy.str, dims=(16,), access=AttrWriteType.READ_WRITE) + FPGA_sst_offload_hdr_eth_destination_mac_R = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_sst_offload_hdr_eth_destination_mac_R"], datatype=numpy.str, dims=(16,)) + FPGA_sst_offload_hdr_ip_destination_address_RW = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_sst_offload_hdr_ip_destination_address_RW"], datatype=numpy.str, dims=(16,), access=AttrWriteType.READ_WRITE) + FPGA_sst_offload_hdr_ip_destination_address_R = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_sst_offload_hdr_ip_destination_address_R"], datatype=numpy.str, dims=(16,)) FPGA_sst_offload_hdr_udp_destination_port_RW = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_sst_offload_hdr_udp_destination_port_RW"], datatype=numpy.uint16, dims=(16,), access=AttrWriteType.READ_WRITE) FPGA_sst_offload_hdr_udp_destination_port_R = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_sst_offload_hdr_udp_destination_port_R"], datatype=numpy.uint16, dims=(16,)) FPGA_sst_offload_weighted_subbands_RW = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_sst_offload_weighted_subbands_RW"], datatype=numpy.bool_, dims=(16,), access=AttrWriteType.READ_WRITE) FPGA_sst_offload_weighted_subbands_R = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_sst_offload_weighted_subbands_R"], datatype=numpy.bool_, 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=(SSTCollector.MAX_INPUTS,), datatype=numpy.uint64) + nof_valid_payloads_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "nof_valid_payloads"}, dims=(SSTCollector.MAX_FPGAS,), datatype=numpy.uint64) # number of packets with invalid payloads - nof_payload_errors_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "nof_payload_errors"}, dims=(SSTCollector.MAX_INPUTS,), datatype=numpy.uint64) + nof_payload_errors_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "nof_payload_errors"}, dims=(SSTCollector.MAX_FPGAS,), datatype=numpy.uint64) # latest SSTs sst_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "sst_values"}, dims=(SSTCollector.MAX_SUBBANDS, SSTCollector.MAX_INPUTS), datatype=numpy.uint64) # reported timestamp for each row in the latest SSTs sst_timestamp_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "sst_timestamps"}, dims=(SSTCollector.MAX_INPUTS,), datatype=numpy.uint64) # integration interval for each row in the latest SSTs integration_interval_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "integration_intervals"}, dims=(SSTCollector.MAX_INPUTS,), datatype=numpy.float32) + # whether the subband data was calibrated by the SDP (that is, were subband weights applied) + subbands_calibrated_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "subbands_calibrated"}, dims=(SSTCollector.MAX_INPUTS,), datatype=numpy.bool_) # -------- # Overloaded functions diff --git a/devices/devices/sdp/statistics.py b/devices/devices/sdp/statistics.py index a19335c7a835b5903e0d08ede7e84d3492dc0331..7d0b970b089ff29931bfc088f8b4b208d347402c 100644 --- a/devices/devices/sdp/statistics.py +++ b/devices/devices/sdp/statistics.py @@ -84,8 +84,9 @@ class Statistics(hardware_device, metaclass=ABCMeta): version_R = attribute(dtype = str, access = AttrWriteType.READ, fget = lambda self: get_version()) - # number of UDP packets that were received + # number of UDP packets and bytes that were received nof_packets_received_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "udp", "parameter": "nof_packets_received"}, datatype=numpy.uint64) + nof_bytes_received_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "udp", "parameter": "nof_bytes_received"}, datatype=numpy.uint64) # number of UDP packets that were dropped because we couldn't keep up with processing nof_packets_dropped_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "udp", "parameter": "nof_packets_dropped"}, datatype=numpy.uint64) # last packet we processed @@ -96,7 +97,10 @@ class Statistics(hardware_device, metaclass=ABCMeta): # queue fill percentage, as reported by the consumer queue_collector_fill_percentage_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "queue", "parameter": "collector_fill_percentage"}, datatype=numpy.uint64) queue_replicator_fill_percentage_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "queue", "parameter": "replicator_fill_percentage"}, datatype=numpy.uint64) - replicator_clients_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "replicator", "parameter": "clients"}, dims=(128,), datatype=numpy.str_) + + replicator_clients_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "replicator", "parameter": "clients"}, dims=(128,), datatype=numpy.str) + replicator_nof_bytes_sent_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "replicator", "parameter": "nof_bytes_sent"}, datatype=numpy.uint64) + replicator_nof_packets_sent_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "replicator", "parameter": "nof_packets_sent"}, datatype=numpy.uint64) replicator_nof_tasks_pending_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "replicator", "parameter": "nof_tasks_pending"}, datatype=numpy.uint64) diff --git a/devices/devices/sdp/statistics_collector.py b/devices/devices/sdp/statistics_collector.py index d19ad01b2a10096ebc637b5a24d51917317afc9f..1bd8f3c12135a818526c48ecbff80408f290b7c9 100644 --- a/devices/devices/sdp/statistics_collector.py +++ b/devices/devices/sdp/statistics_collector.py @@ -3,7 +3,8 @@ from threading import Thread import logging import numpy -from .statistics_packet import SSTPacket +from .statistics_packet import SSTPacket, XSTPacket +from common.baselines import nr_baselines, baseline_index, baseline_from_index from clients.statistics_client_thread import StatisticsClientThread logger = logging.getLogger() @@ -11,11 +12,8 @@ logger = logging.getLogger() class StatisticsCollector: """ Base class to process statistics packets into parameters matrices. """ - # Maximum number of antenna inputs we support (used to determine array sizes) - MAX_INPUTS = 192 - - # Maximum number of subbands we support (used to determine array sizes) - MAX_SUBBANDS = 512 + # Maximum number of FPGAs we receive data from (used for diagnostics) + MAX_FPGAS = 16 def __init__(self): self.parameters = self._default_parameters() @@ -62,15 +60,16 @@ class SSTCollector(StatisticsCollector): defaults.update({ # Number of packets received so far that we could parse correctly and do not have a payload error - "nof_valid_payloads": numpy.zeros((self.MAX_INPUTS,), dtype=numpy.uint64), + "nof_valid_payloads": numpy.zeros((self.MAX_FPGAS,), dtype=numpy.uint64), # Packets that reported a payload error - "nof_payload_errors": numpy.zeros((self.MAX_INPUTS,), dtype=numpy.uint64), + "nof_payload_errors": numpy.zeros((self.MAX_FPGAS,), dtype=numpy.uint64), # Last value array we've constructed out of the packets "sst_values": numpy.zeros((self.MAX_INPUTS, self.MAX_SUBBANDS), dtype=numpy.uint64), "sst_timestamps": numpy.zeros((self.MAX_INPUTS,), dtype=numpy.float64), "integration_intervals": numpy.zeros((self.MAX_INPUTS,), dtype=numpy.float32), + "subbands_calibrated": numpy.zeros((self.MAX_INPUTS,), dtype=numpy.bool_), }) return defaults @@ -87,16 +86,120 @@ class SSTCollector(StatisticsCollector): if fields.payload_error: # cannot trust the data if a payload error is reported - self.parameters["nof_payload_errors"][input_index] += numpy.uint64(1) + self.parameters["nof_payload_errors"][fields.gn_index] += numpy.uint64(1) # don't raise, as packet is valid return # process the packet - self.parameters["nof_valid_payloads"][input_index] += numpy.uint64(1) + self.parameters["nof_valid_payloads"][fields.gn_index] += numpy.uint64(1) self.parameters["sst_values"][input_index][:fields.nof_statistics_per_packet] = fields.payload self.parameters["sst_timestamps"][input_index] = numpy.float64(fields.timestamp().timestamp()) self.parameters["integration_intervals"][input_index] = fields.integration_interval() + self.parameters["subbands_calibrated"][input_index] = fields.subband_calibrated_flag + +class XSTCollector(StatisticsCollector): + """ Class to process XST statistics packets. """ + + # Maximum number of antenna inputs we support (used to determine array sizes) + MAX_INPUTS = 192 + + # Maximum number of baselines we can receive + MAX_BASELINES = nr_baselines(MAX_INPUTS) + + # Expected block size is BLOCK_LENGTH x BLOCK_LENGTH + BLOCK_LENGTH = 12 + + # Expected number of blocks: enough to cover all baselines without the conjugates (that is, the top-left triangle of the matrix). + MAX_BLOCKS = nr_baselines(MAX_INPUTS // BLOCK_LENGTH) + + # Maximum number of subbands we support (used to determine array sizes) + MAX_SUBBANDS = 512 + + # Complex values are (real, imag). A bit silly, but we don't want magical constants. + VALUES_PER_COMPLEX = 2 + + def _default_parameters(self): + defaults = super()._default_parameters() + + defaults.update({ + # Number of packets received so far that we could parse correctly and do not have a payload error + "nof_valid_payloads": numpy.zeros((self.MAX_FPGAS,), dtype=numpy.uint64), + + # Packets that reported a payload error + "nof_payload_errors": numpy.zeros((self.MAX_FPGAS,), dtype=numpy.uint64), + + # Last value array we've constructed out of the packets + "xst_blocks": numpy.zeros((self.MAX_BLOCKS, self.BLOCK_LENGTH * self.BLOCK_LENGTH * self.VALUES_PER_COMPLEX), dtype=numpy.int64), + "xst_timestamps": numpy.zeros((self.MAX_BLOCKS,), dtype=numpy.float64), + "xst_subbands": numpy.zeros((self.MAX_BLOCKS,), dtype=numpy.uint16), + "integration_intervals": numpy.zeros((self.MAX_BLOCKS,), dtype=numpy.float32), + }) + + return defaults + + def parse_packet(self, packet): + fields = XSTPacket(packet) + + if fields.payload_error: + # cannot trust the data if a payload error is reported + self.parameters["nof_payload_errors"][fields.gn_index] += numpy.uint64(1) + + # don't raise, as packet is valid + return + + # the blocks must be of size BLOCK_LENGTH x BLOCK_LENGTH + if fields.nof_signal_inputs != self.BLOCK_LENGTH: + raise ValueError("Packet describes a block of {0} x {0} baselines, but we can only parse blocks of {1} x {1} baselines".format(fields.nof_signal_inputs, self.BLOCK_LENGTH)) + + # check whether set of baselines in this packet are not out of bounds + for antenna in (0,1): + if fields.first_baseline[antenna] + fields.nof_signal_inputs >= self.MAX_INPUTS: + # packet describes an input that is out of bounds for us + raise ValueError("Packet describes {0} x {0} baselines starting at {1}, but we are limited to describing MAX_INPUTS={2}".format(fields.nof_signal_inputs, fields.first_baseline, self.MAX_INPUTS)) + + # the blocks of baselines need to be tightly packed, and thus be provided at exact intervals + if fields.first_baseline[antenna] % self.BLOCK_LENGTH != 0: + raise ValueError("Packet describes baselines starting at %s, but we require a multiple of BLOCK_LENGTH=%d" % (fields.first_baseline, self.MAX_INPUTS)) + + # the payload contains complex values for the block of baselines of size BLOCK_LENGTH x BLOCK_LENGTH + # starting at baseline first_baseline. + # + # we honour this format, as we want to keep the metadata together with these blocks. we do need to put the blocks in a linear + # and tight order, however, so we calculate a block index. + block_index = baseline_index(fields.first_baseline[0] // self.BLOCK_LENGTH, fields.first_baseline[1] // self.BLOCK_LENGTH) + + # process the packet + self.parameters["nof_valid_payloads"][fields.gn_index] += numpy.uint64(1) + + block_index = baseline_index(fields.first_baseline[0], fields.first_baseline[1]) + + self.parameters["xst_blocks"][block_index][:fields.nof_statistics_per_packet] = fields.payload + self.parameters["xst_timestamps"][block_index] = numpy.float64(fields.timestamp().timestamp()) + self.parameters["xst_subbands"][block_index] = numpy.uint16(fields.subband_index) + self.parameters["integration_intervals"][block_index] = fields.integration_interval() + + def xst_values(self): + """ xst_blocks, but as a matrix[MAX_INPUTS][MAX_INPUTS] of complex values. """ + + matrix = numpy.zeros((self.MAX_INPUTS, self.MAX_INPUTS), dtype=numpy.complex64) + xst_blocks = self.parameters["xst_blocks"] + + for block_index in range(self.MAX_BLOCKS): + # convert real/imag int to complex float values. this works as real/imag come in pairs + block = xst_blocks[block_index].astype(numpy.float32).view(numpy.complex64) + + # reshape into [a][b] + block = block.reshape(self.BLOCK_LENGTH, self.BLOCK_LENGTH) + + # compute destination in matrix + first_baseline = baseline_from_index(block_index) + first_baseline = (first_baseline[0] * self.BLOCK_LENGTH, first_baseline[1] * self.BLOCK_LENGTH) + + # copy block into matrix + matrix[first_baseline[0]:first_baseline[0]+self.BLOCK_LENGTH, first_baseline[1]:first_baseline[1]+self.BLOCK_LENGTH] = block + + return matrix class StatisticsConsumer(Thread, StatisticsClientThread): @@ -159,4 +262,3 @@ class StatisticsConsumer(Thread, StatisticsClientThread): if self.is_alive(): # there is nothing we can do except wait (stall) longer, which could be indefinitely. logger.error(f"Statistics thread did not shut down after {self.DISCONNECT_TIMEOUT} seconds, just leaving it dangling. Please attach a debugger to thread ID {self.ident}.") - diff --git a/devices/devices/sdp/statistics_packet.py b/devices/devices/sdp/statistics_packet.py index 6843c99e62c79b2c9afa119aaf0b3b51709269f7..9bac227071dfbdec9ea0b0fd1fa63fa36176a8d9 100644 --- a/devices/devices/sdp/statistics_packet.py +++ b/devices/devices/sdp/statistics_packet.py @@ -117,9 +117,9 @@ class StatisticsPacket(object): self.nyquist_zone_index = get_bit_value(self.source_info, 13, 14) self.t_adc = get_bit_value(self.source_info, 12) self.fsub_type = get_bit_value(self.source_info, 11) - self.payload_error = get_bit_value(self.source_info, 10) - self.beam_repositioning_flag = get_bit_value(self.source_info, 9) - self.subband_calibrated_flag = get_bit_value(self.source_info, 8) + self.payload_error = (get_bit_value(self.source_info, 10) != 0) + self.beam_repositioning_flag = (get_bit_value(self.source_info, 9) != 0) + self.subband_calibrated_flag = (get_bit_value(self.source_info, 8) != 0) # self.source_info 5-7 are reserved self.gn_index = get_bit_value(self.source_info, 0, 4) @@ -210,6 +210,17 @@ class StatisticsPacket(object): return header + def payload(self, signed=False) -> numpy.array: + """ The payload of this packet, as a linear array. """ + + # derive which and how many elements to read from the packet header + bytecount_to_unsigned_struct_type = {1: 'b', 2: 'h', 4: 'i', 8: 'q'} if signed else {1: 'B', 2: 'H', 4: 'I', 8: 'Q'} + format_str = ">{}{}".format(self.nof_statistics_per_packet, + bytecount_to_unsigned_struct_type[self.nof_bytes_per_statistic]) + + return numpy.array( + struct.unpack(format_str, self.packet[self.header_size:self.header_size + struct.calcsize(format_str)])) + class SSTPacket(StatisticsPacket): """ @@ -245,16 +256,8 @@ class SSTPacket(StatisticsPacket): return header @property - def payload(self) -> numpy.array: - """ The payload of this packet, interpreted as SST data. """ - - # derive which and how many elements to read from the packet header - bytecount_to_unsigned_struct_type = {1: 'B', 2: 'H', 4: 'I', 8: 'Q'} - format_str = ">{}{}".format(self.nof_statistics_per_packet, - bytecount_to_unsigned_struct_type[self.nof_bytes_per_statistic]) - - return numpy.array( - struct.unpack(format_str, self.packet[self.header_size:self.header_size + struct.calcsize(format_str)])) + def payload(self): + return super().payload(signed=False) class XSTPacket(StatisticsPacket): @@ -263,9 +266,10 @@ class XSTPacket(StatisticsPacket): The following fields are exposed as properties & functions. - subband_index: subband number for which this packet contains statistics. - baseline: antenna pair for which this packet contains statistics. + first_baseline: first antenna pair for which this packet contains statistics. + + payload[nof_signal_inputs][nof_signal_inputs] the baselines, starting from first_baseline """ def __init__(self, packet): @@ -281,16 +285,20 @@ class XSTPacket(StatisticsPacket): super().unpack_data_id() self.subband_index = get_bit_value(self.data_id, 16, 24) - self.baseline = (get_bit_value(self.data_id, 8, 15), get_bit_value(self.data_id, 0, 7)) + self.first_baseline = (get_bit_value(self.data_id, 8, 15), get_bit_value(self.data_id, 0, 7)) def header(self): header = super().header() header["data_id"]["subband_index"] = self.subband_index - header["data_id"]["baseline"] = self.baseline + header["data_id"]["first_baseline"] = self.first_baseline return header + @property + def payload(self): + return super().payload(signed=True) + class BSTPacket(StatisticsPacket): """ diff --git a/devices/devices/sdp/xst.py b/devices/devices/sdp/xst.py new file mode 100644 index 0000000000000000000000000000000000000000..caeeb5d3488369ecaf17208d1b33c2b7e6c76511 --- /dev/null +++ b/devices/devices/sdp/xst.py @@ -0,0 +1,150 @@ +# -*- coding: utf-8 -*- +# +# This file is part of the XST project +# +# +# +# Distributed under the terms of the APACHE license. +# See LICENSE.txt for more info. + +""" XST Device Server for LOFAR2.0 + +""" + +# TODO(Corne): Remove sys.path.append hack once packaging is in place! +import os, sys +currentdir = os.path.dirname(os.path.realpath(__file__)) +parentdir = os.path.dirname(currentdir) +parentdir = os.path.dirname(parentdir) +sys.path.append(parentdir) + +# PyTango imports +from tango.server import run +from tango.server import device_property, attribute +from tango import AttrWriteType +# Additional import + +from clients.attribute_wrapper import attribute_wrapper +from clients.opcua_client import OPCUAConnection +from clients.statistics_client import StatisticsClient + +from devices.hardware_device import hardware_device + +from common.lofar_git import get_version +from common.lofar_logging import device_logging_to_python, log_exceptions + +from devices.sdp.statistics import Statistics +from devices.sdp.statistics_collector import XSTCollector + +import numpy + +__all__ = ["XST", "main"] + +class XST(Statistics): + + STATISTICS_COLLECTOR_CLASS = XSTCollector + + # ----------------- + # Device Properties + # ----------------- + + FPGA_xst_offload_hdr_eth_destination_mac_RW_default = device_property( + dtype='DevVarStringArray', + mandatory=True + ) + + FPGA_xst_offload_hdr_ip_destination_address_RW_default = device_property( + dtype='DevVarStringArray', + mandatory=True + ) + + FPGA_xst_offload_hdr_udp_destination_port_RW_default = device_property( + dtype='DevVarUShortArray', + mandatory=True + ) + + FPGA_xst_processing_enable_RW_default = device_property( + dtype='DevVarBooleanArray', + mandatory=False, + default_value=[True] * 16 + ) + + FPGA_xst_subband_select_RW_default = device_property( + dtype='DevVarULongArray', + mandatory=False, + default_value=[[0,102,0,0,0,0,0,0]] * 16 + ) + + # ---------- + # Attributes + # ---------- + + # FPGA control points for XSTs + FPGA_xst_integration_interval_RW = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_xst_integration_interval_RW"], datatype=numpy.double, dims=(8,16), access=AttrWriteType.READ_WRITE) + FPGA_xst_integration_interval_R = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_xst_integration_interval_R"], datatype=numpy.double, dims=(8,16)) + FPGA_xst_offload_enable_RW = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_xst_offload_enable_RW"], datatype=numpy.bool_, dims=(16,), access=AttrWriteType.READ_WRITE) + FPGA_xst_offload_enable_R = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_xst_offload_enable_R"], datatype=numpy.bool_, dims=(16,)) + FPGA_xst_offload_hdr_eth_destination_mac_RW = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_xst_offload_hdr_eth_destination_mac_RW"], datatype=numpy.str, dims=(16,), access=AttrWriteType.READ_WRITE) + FPGA_xst_offload_hdr_eth_destination_mac_R = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_xst_offload_hdr_eth_destination_mac_R"], datatype=numpy.str, dims=(16,)) + FPGA_xst_offload_hdr_ip_destination_address_RW = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_xst_offload_hdr_ip_destination_address_RW"], datatype=numpy.str, dims=(16,), access=AttrWriteType.READ_WRITE) + FPGA_xst_offload_hdr_ip_destination_address_R = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_xst_offload_hdr_ip_destination_address_R"], datatype=numpy.str, dims=(16,)) + FPGA_xst_offload_hdr_udp_destination_port_RW = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_xst_offload_hdr_udp_destination_port_RW"], datatype=numpy.uint16, dims=(16,), access=AttrWriteType.READ_WRITE) + FPGA_xst_offload_hdr_udp_destination_port_R = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_xst_offload_hdr_udp_destination_port_R"], datatype=numpy.uint16, dims=(16,)) + FPGA_xst_processing_enable_RW = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_xst_processing_enable_RW"], datatype=numpy.bool_, dims=(16,), access=AttrWriteType.READ_WRITE) + FPGA_xst_processing_enable_R = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2:FPGA_xst_processing_enable_R"], datatype=numpy.bool_, dims=(16,)) + FPGA_xst_subband_select_RW = attribute_wrapper(comms_id=OPCUAConnection, comms_annotation=["2: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=["2:FPGA_xst_subband_select_R"], datatype=numpy.uint32, dims=(8,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) + # number of packets with invalid payloads + nof_payload_errors_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "nof_payload_errors"}, dims=(XSTCollector.MAX_FPGAS,), datatype=numpy.uint64) + # latest XSTs + xst_blocks_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "xst_blocks"}, dims=(XSTCollector.BLOCK_LENGTH * XSTCollector.BLOCK_LENGTH * XSTCollector.VALUES_PER_COMPLEX, XSTCollector.MAX_BLOCKS), datatype=numpy.int64) + # reported timestamp for each row in the latest XSTs + xst_timestamp_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "xst_timestamps"}, dims=(XSTCollector.MAX_BLOCKS,), datatype=numpy.uint64) + # which subband the XSTs describe + xst_subbands_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "xst_subbands"}, dims=(XSTCollector.MAX_BLOCKS,), datatype=numpy.uint16) + # integration interval for each row in the latest XSTs + integration_interval_R = attribute_wrapper(comms_id=StatisticsClient, comms_annotation={"type": "statistics", "parameter": "integration_intervals"}, dims=(XSTCollector.MAX_BLOCKS,), datatype=numpy.float32) + + # xst_R, but as a matrix of input x input + xst_real_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),)) + xst_imag_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),)) + xst_power_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),)) + xst_phase_R = attribute(max_dim_x=XSTCollector.MAX_INPUTS, max_dim_y=XSTCollector.MAX_INPUTS, dtype=((numpy.float32,),)) + + def read_xst_real_R(self): + return numpy.real(self.statistics_client.collector.xst_values()) + + def read_xst_imag_R(self): + return numpy.imag(self.statistics_client.collector.xst_values()) + + def read_xst_power_R(self): + return numpy.abs(self.statistics_client.collector.xst_values()) + + def read_xst_phase_R(self): + return numpy.angle(self.statistics_client.collector.xst_values()) + + # -------- + # Overloaded functions + # -------- + + # -------- + # Commands + # -------- + +# ---------- +# Run server +# ---------- +def main(args=None, **kwargs): + """Main function of the XST Device module.""" + + from common.lofar_logging import configure_logger + configure_logger() + + return run((XST,), args=args, **kwargs) + + +if __name__ == '__main__': + main() diff --git a/devices/devices/unb2.py b/devices/devices/unb2.py index 059f2848d752954c84e0e2ece734dcac8816039e..7c2575991605354de5bba608906fb9ea248f021b 100644 --- a/devices/devices/unb2.py +++ b/devices/devices/unb2.py @@ -87,81 +87,81 @@ class UNB2(hardware_device): ##XXX Means Under discussion # Special case for the on off switch: instead of UNB2_Power_ON_OFF_R we use UNB2_POL_FPGA_CORE_VOUT_R as the MP - UNB2_Power_ON_OFF_RW = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_Power_ON_OFF_RW"], datatype=numpy.bool_, dims=(N_unb,), access=AttrWriteType.READ_WRITE) - UNB2_Front_Panel_LED_RW = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_Front_Panel_LED_RW"], datatype=numpy.uint8, dims=(N_unb,), access=AttrWriteType.READ_WRITE) - UNB2_Front_Panel_LED_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_Front_Panel_LED_R"], datatype=numpy.uint8, dims=(N_unb,)) - UNB2_mask_RW = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_mask_RW"], datatype=numpy.bool_, dims=(N_unb,), access=AttrWriteType.READ_WRITE) + UNB2_Power_ON_OFF_RW = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_Power_ON_OFF_RW"], datatype=numpy.bool_, dims=(N_unb,), access=AttrWriteType.READ_WRITE) + UNB2_Front_Panel_LED_RW = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_Front_Panel_LED_RW"], datatype=numpy.uint8, dims=(N_unb,), access=AttrWriteType.READ_WRITE) + UNB2_Front_Panel_LED_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_Front_Panel_LED_R"], datatype=numpy.uint8, dims=(N_unb,)) + UNB2_mask_RW = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_mask_RW"], datatype=numpy.bool_, dims=(N_unb,), access=AttrWriteType.READ_WRITE) # Not yet deployed - #UNB2_mask_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_mask_R"], datatype=numpy.bool_, dims=(N_unb,)) + #UNB2_mask_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_mask_R"], datatype=numpy.bool_, dims=(N_unb,)) ### Central MP per Uniboard # These three are only available in UNB2c - UNB2_I2C_bus_STATUS_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_I2C_bus_STATUS_R"], datatype=numpy.bool_, dims=(N_unb,)) + UNB2_I2C_bus_STATUS_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_I2C_bus_STATUS_R"], datatype=numpy.bool_, dims=(N_unb,)) ##UNB2_I2C_bus_STATUS_R will probably be renamed to UNB2_I2C_bus_OK_R - ##UNB2_I2C_bus_OK_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_I2C_bus_OK_R"], datatype=numpy.bool_, dims=(N_unb,)) - #UNB2_EEPROM_Serial_Number_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_EEPROM_Serial_Number_R"], datatype=numpy.str, dims=(N_unb,)) - UNB2_EEPROM_Unique_ID_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_EEPROM_Unique_ID_R"], datatype=numpy.uint32, dims=(N_unb,)) - UNB2_DC_DC_48V_12V_VIN_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_DC_DC_48V_12V_VIN_R"], datatype=numpy.double, dims=(N_unb,)) - UNB2_DC_DC_48V_12V_VOUT_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_DC_DC_48V_12V_VOUT_R"], datatype=numpy.double, dims=(N_unb,)) - UNB2_DC_DC_48V_12V_IOUT_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_DC_DC_48V_12V_IOUT_R"], datatype=numpy.double, dims=(N_unb,)) - UNB2_DC_DC_48V_12V_TEMP_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_DC_DC_48V_12V_TEMP_R"], datatype=numpy.double, dims=(N_unb,)) - UNB2_POL_QSFP_N01_VOUT_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_POL_QSFP_N01_VOUT_R"], datatype=numpy.double, dims=(N_unb,)) - UNB2_POL_QSFP_N01_IOUT_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_POL_QSFP_N01_IOUT_R"], datatype=numpy.double, dims=(N_unb,)) - UNB2_POL_QSFP_N01_TEMP_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_POL_QSFP_N01_TEMP_R"], datatype=numpy.double, dims=(N_unb,)) - UNB2_POL_QSFP_N23_VOUT_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_POL_QSFP_N23_VOUT_R"], datatype=numpy.double, dims=(N_unb,)) - UNB2_POL_QSFP_N23_IOUT_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_POL_QSFP_N23_IOUT_R"], datatype=numpy.double, dims=(N_unb,)) - UNB2_POL_QSFP_N23_TEMP_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_POL_QSFP_N23_TEMP_R"], datatype=numpy.double, dims=(N_unb,)) - UNB2_POL_SWITCH_1V2_VOUT_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_POL_SWITCH_1V2_VOUT_R"], datatype=numpy.double, dims=(N_unb,)) - UNB2_POL_SWITCH_1V2_IOUT_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_POL_SWITCH_1V2_IOUT_R"], datatype=numpy.double, dims=(N_unb,)) - UNB2_POL_SWITCH_1V2_TEMP_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_POL_SWITCH_1V2_TEMP_R"], datatype=numpy.double, dims=(N_unb,)) - UNB2_POL_SWITCH_PHY_VOUT_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_POL_SWITCH_PHY_VOUT_R"], datatype=numpy.double, dims=(N_unb,)) - UNB2_POL_SWITCH_PHY_IOUT_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_POL_SWITCH_PHY_IOUT_R"], datatype=numpy.double, dims=(N_unb,)) - UNB2_POL_SWITCH_PHY_TEMP_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_POL_SWITCH_PHY_TEMP_R"], datatype=numpy.double, dims=(N_unb,)) - UNB2_POL_CLOCK_VOUT_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_POL_CLOCK_VOUT_R"], datatype=numpy.double, dims=(N_unb,)) - UNB2_POL_CLOCK_IOUT_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_POL_CLOCK_IOUT_R"], datatype=numpy.double, dims=(N_unb,)) - UNB2_POL_CLOCK_TEMP_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_POL_CLOCK_TEMP_R"], datatype=numpy.double, dims=(N_unb,)) + ##UNB2_I2C_bus_OK_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_I2C_bus_OK_R"], datatype=numpy.bool_, dims=(N_unb,)) + #UNB2_EEPROM_Serial_Number_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_EEPROM_Serial_Number_R"], datatype=numpy.str, dims=(N_unb,)) + UNB2_EEPROM_Unique_ID_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_EEPROM_Unique_ID_R"], datatype=numpy.uint32, dims=(N_unb,)) + UNB2_DC_DC_48V_12V_VIN_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_DC_DC_48V_12V_VIN_R"], datatype=numpy.double, dims=(N_unb,)) + UNB2_DC_DC_48V_12V_VOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_DC_DC_48V_12V_VOUT_R"], datatype=numpy.double, dims=(N_unb,)) + UNB2_DC_DC_48V_12V_IOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_DC_DC_48V_12V_IOUT_R"], datatype=numpy.double, dims=(N_unb,)) + UNB2_DC_DC_48V_12V_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_DC_DC_48V_12V_TEMP_R"], datatype=numpy.double, dims=(N_unb,)) + UNB2_POL_QSFP_N01_VOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_QSFP_N01_VOUT_R"], datatype=numpy.double, dims=(N_unb,)) + UNB2_POL_QSFP_N01_IOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_QSFP_N01_IOUT_R"], datatype=numpy.double, dims=(N_unb,)) + UNB2_POL_QSFP_N01_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_QSFP_N01_TEMP_R"], datatype=numpy.double, dims=(N_unb,)) + UNB2_POL_QSFP_N23_VOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_QSFP_N23_VOUT_R"], datatype=numpy.double, dims=(N_unb,)) + UNB2_POL_QSFP_N23_IOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_QSFP_N23_IOUT_R"], datatype=numpy.double, dims=(N_unb,)) + UNB2_POL_QSFP_N23_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_QSFP_N23_TEMP_R"], datatype=numpy.double, dims=(N_unb,)) + UNB2_POL_SWITCH_1V2_VOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_SWITCH_1V2_VOUT_R"], datatype=numpy.double, dims=(N_unb,)) + UNB2_POL_SWITCH_1V2_IOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_SWITCH_1V2_IOUT_R"], datatype=numpy.double, dims=(N_unb,)) + UNB2_POL_SWITCH_1V2_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_SWITCH_1V2_TEMP_R"], datatype=numpy.double, dims=(N_unb,)) + UNB2_POL_SWITCH_PHY_VOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_SWITCH_PHY_VOUT_R"], datatype=numpy.double, dims=(N_unb,)) + UNB2_POL_SWITCH_PHY_IOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_SWITCH_PHY_IOUT_R"], datatype=numpy.double, dims=(N_unb,)) + UNB2_POL_SWITCH_PHY_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_SWITCH_PHY_TEMP_R"], datatype=numpy.double, dims=(N_unb,)) + UNB2_POL_CLOCK_VOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_CLOCK_VOUT_R"], datatype=numpy.double, dims=(N_unb,)) + UNB2_POL_CLOCK_IOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_CLOCK_IOUT_R"], datatype=numpy.double, dims=(N_unb,)) + UNB2_POL_CLOCK_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_CLOCK_TEMP_R"], datatype=numpy.double, dims=(N_unb,)) ### Local MP per FPGA - UNB2_FPGA_DDR4_SLOT_TEMP_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_FPGA_DDR4_SLOT_TEMP_R"], datatype=numpy.double, dims=((N_fpga * N_ddr), N_unb)) - #UNB2_FPGA_DDR4_SLOT_PART_NUMBER_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_FPGA_DDR4_SLOT_PART_NUMBER_R"], datatype=numpy.str, dims=(N_fpga * N_ddr), N_unb)) - #UNB2_FPGA_QSFP_CAGE_TEMP_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_FPGA_QSFP_CAGE_0_TEMP_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) - #UNB2_FPGA_QSFP_CAGE_1_TEMP_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_FPGA_QSFP_CAGE_1_TEMP_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) - #UNB2_FPGA_QSFP_CAGE_2_TEMP_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_FPGA_QSFP_CAGE_2_TEMP_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) - #UNB2_FPGA_QSFP_CAGE_3_TEMP_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_FPGA_QSFP_CAGE_3_TEMP_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) - #UNB2_FPGA_QSFP_CAGE_4_TEMP_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_FPGA_QSFP_CAGE_4_TEMP_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) - #UNB2_FPGA_QSFP_CAGE_5_TEMP_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_FPGA_QSFP_CAGE_5_TEMP_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) - #UNB2_FPGA_QSFP_CAGE_LOS_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_FPGA_QSFP_CAGE_0_LOS_R"], datatype=numpy.uint8, dims=(N_fpga, N_unb)) - #UNB2_FPGA_QSFP_CAGE_1_LOS_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_FPGA_QSFP_CAGE_1_LOS_R"], datatype=numpy.uint8, dims=(N_fpga, N_unb)) - #UNB2_FPGA_QSFP_CAGE_2_LOS_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_FPGA_QSFP_CAGE_2_LOS_R"], datatype=numpy.uint8, dims=(N_fpga, N_unb)) - #UNB2_FPGA_QSFP_CAGE_3_LOS_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_FPGA_QSFP_CAGE_3_LOS_R"], datatype=numpy.uint8, dims=(N_fpga, N_unb)) - #UNB2_FPGA_QSFP_CAGE_4_LOS_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_FPGA_QSFP_CAGE_4_LOS_R"], datatype=numpy.uint8, dims=(N_fpga, N_unb)) - #UNB2_FPGA_QSFP_CAGE_5_LOS_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_FPGA_QSFP_CAGE_5_LOS_R"], datatype=numpy.uint8, dims=(N_fpga, N_unb)) - #UNB2_FPGA_POL_CORE_VOUT_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_POL_FPGA_CORE_VOUT_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) - UNB2_FPGA_POL_CORE_IOUT_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_FPGA_POL_CORE_IOUT_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) - UNB2_FPGA_POL_CORE_TEMP_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_FPGA_POL_CORE_TEMP_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) - UNB2_FPGA_POL_ERAM_VOUT_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_FPGA_POL_ERAM_VOUT_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) - UNB2_FPGA_POL_ERAM_IOUT_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_FPGA_POL_ERAM_IOUT_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) - UNB2_FPGA_POL_ERAM_TEMP_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_FPGA_POL_ERAM_TEMP_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) - UNB2_FPGA_POL_RXGXB_VOUT_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_FPGA_POL_RXGXB_VOUT_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) - UNB2_FPGA_POL_RXGXB_IOUT_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_FPGA_POL_RXGXB_IOUT_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) - UNB2_FPGA_POL_RXGXB_TEMP_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_FPGA_POL_RXGXB_TEMP_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) - UNB2_FPGA_POL_TXGXB_VOUT_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_FPGA_POL_TXGXB_VOUT_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) - UNB2_FPGA_POL_TXGXB_IOUT_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_FPGA_POL_TXGXB_IOUT_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) - #UNB2_FPGA_POL_TXGXB_TEMP_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_POL_FPGA_TXGXB_TEMP_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) - UNB2_FPGA_POL_HGXB_VOUT_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_FPGA_POL_HGXB_VOUT_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) - UNB2_FPGA_POL_HGXB_IOUT_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_FPGA_POL_HGXB_IOUT_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) - UNB2_FPGA_POL_HGXB_TEMP_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_FPGA_POL_HGXB_TEMP_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) - UNB2_FPGA_POL_PGM_VOUT_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_FPGA_POL_PGM_VOUT_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) - UNB2_FPGA_POL_PGM_IOUT_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_FPGA_POL_PGM_IOUT_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) - UNB2_FPGA_POL_PGM_TEMP_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_FPGA_POL_PGM_TEMP_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) - - - ##UNB2_I2C_bus_QSFP_STATUS_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_I2C_bus_QSFP_STATUS_R"], datatype=numpy.int64, dims=((N_unb * N_fpga), N_qsfp)) - ##UNB2_I2C_bus_DDR4_STATUS_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_I2C_bus_DDR4_STATUS_R"], datatype=numpy.int64, dims=(N_ddr, N_fpga)) - ##UNB2_I2C_bus_FPGA_PS_STATUS_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_I2C_bus_FPGA_PS_STATUS_R"], datatype=numpy.int64, dims=(N_unb * N_fpga,)) - ##UNB2_I2C_bus_PS_STATUS_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_I2C_bus_PS_STATUS_R"], datatype=numpy.double, dims=(N_unb,)) - ##UNB2_translator_busy_R = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_translator_busy_R"], datatype=numpy.bool_) - ##UNB2_monitor_rate_RW = attribute_wrapper(comms_annotation=["2:RECV", "2:UNB2_monitor_rate_RW"], datatype=numpy.double, dims=(N_unb,), access=AttrWriteType.READ_WRITE) + UNB2_FPGA_DDR4_SLOT_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_DDR4_SLOT_TEMP_R"], datatype=numpy.double, dims=((N_fpga * N_ddr), N_unb)) + #UNB2_FPGA_DDR4_SLOT_PART_NUMBER_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_DDR4_SLOT_PART_NUMBER_R"], datatype=numpy.str, dims=(N_fpga * N_ddr), N_unb)) + #UNB2_FPGA_QSFP_CAGE_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_QSFP_CAGE_0_TEMP_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) + #UNB2_FPGA_QSFP_CAGE_1_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_QSFP_CAGE_1_TEMP_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) + #UNB2_FPGA_QSFP_CAGE_2_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_QSFP_CAGE_2_TEMP_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) + #UNB2_FPGA_QSFP_CAGE_3_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_QSFP_CAGE_3_TEMP_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) + #UNB2_FPGA_QSFP_CAGE_4_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_QSFP_CAGE_4_TEMP_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) + #UNB2_FPGA_QSFP_CAGE_5_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_QSFP_CAGE_5_TEMP_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) + #UNB2_FPGA_QSFP_CAGE_LOS_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_QSFP_CAGE_0_LOS_R"], datatype=numpy.uint8, dims=(N_fpga, N_unb)) + #UNB2_FPGA_QSFP_CAGE_1_LOS_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_QSFP_CAGE_1_LOS_R"], datatype=numpy.uint8, dims=(N_fpga, N_unb)) + #UNB2_FPGA_QSFP_CAGE_2_LOS_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_QSFP_CAGE_2_LOS_R"], datatype=numpy.uint8, dims=(N_fpga, N_unb)) + #UNB2_FPGA_QSFP_CAGE_3_LOS_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_QSFP_CAGE_3_LOS_R"], datatype=numpy.uint8, dims=(N_fpga, N_unb)) + #UNB2_FPGA_QSFP_CAGE_4_LOS_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_QSFP_CAGE_4_LOS_R"], datatype=numpy.uint8, dims=(N_fpga, N_unb)) + #UNB2_FPGA_QSFP_CAGE_5_LOS_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_QSFP_CAGE_5_LOS_R"], datatype=numpy.uint8, dims=(N_fpga, N_unb)) + #UNB2_FPGA_POL_CORE_VOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_FPGA_CORE_VOUT_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) + UNB2_FPGA_POL_CORE_IOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_CORE_IOUT_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) + UNB2_FPGA_POL_CORE_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_CORE_TEMP_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) + UNB2_FPGA_POL_ERAM_VOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_ERAM_VOUT_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) + UNB2_FPGA_POL_ERAM_IOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_ERAM_IOUT_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) + UNB2_FPGA_POL_ERAM_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_ERAM_TEMP_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) + UNB2_FPGA_POL_RXGXB_VOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_RXGXB_VOUT_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) + UNB2_FPGA_POL_RXGXB_IOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_RXGXB_IOUT_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) + UNB2_FPGA_POL_RXGXB_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_RXGXB_TEMP_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) + UNB2_FPGA_POL_TXGXB_VOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_TXGXB_VOUT_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) + UNB2_FPGA_POL_TXGXB_IOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_TXGXB_IOUT_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) + #UNB2_FPGA_POL_TXGXB_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_FPGA_TXGXB_TEMP_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) + UNB2_FPGA_POL_HGXB_VOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_HGXB_VOUT_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) + UNB2_FPGA_POL_HGXB_IOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_HGXB_IOUT_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) + UNB2_FPGA_POL_HGXB_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_HGXB_TEMP_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) + UNB2_FPGA_POL_PGM_VOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_PGM_VOUT_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) + UNB2_FPGA_POL_PGM_IOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_PGM_IOUT_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) + UNB2_FPGA_POL_PGM_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_PGM_TEMP_R"], datatype=numpy.double, dims=(N_fpga, N_unb)) + + + ##UNB2_I2C_bus_QSFP_STATUS_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_I2C_bus_QSFP_STATUS_R"], datatype=numpy.int64, dims=((N_unb * N_fpga), N_qsfp)) + ##UNB2_I2C_bus_DDR4_STATUS_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_I2C_bus_DDR4_STATUS_R"], datatype=numpy.int64, dims=(N_ddr, N_fpga)) + ##UNB2_I2C_bus_FPGA_PS_STATUS_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_I2C_bus_FPGA_PS_STATUS_R"], datatype=numpy.int64, dims=(N_unb * N_fpga,)) + ##UNB2_I2C_bus_PS_STATUS_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_I2C_bus_PS_STATUS_R"], datatype=numpy.double, dims=(N_unb,)) + ##UNB2_translator_busy_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_translator_busy_R"], datatype=numpy.bool_) + ##UNB2_monitor_rate_RW = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_monitor_rate_RW"], datatype=numpy.double, dims=(N_unb,), access=AttrWriteType.READ_WRITE) diff --git a/devices/examples/load_from_disk/ini_client.py b/devices/examples/load_from_disk/ini_client.py index dcc66a85ac7fae4cbe3d00a47fe46a809618938b..cd227f23458c672c08b3acf08ba65fa9a48b581d 100644 --- a/devices/examples/load_from_disk/ini_client.py +++ b/devices/examples/load_from_disk/ini_client.py @@ -25,7 +25,7 @@ ini_to_numpy_dict = { int: numpy.int64, float: numpy.float64, bool: numpy.bool_, - str: numpy.str_ + str: numpy.str } import os @@ -171,9 +171,9 @@ def data_handler(string, dtype): value = dtype(value) - elif dtype is numpy.str_: + elif dtype is numpy.str: for i in string.split(","): - val = numpy.str_(i) + val = numpy.str(i) value.append(val) value = numpy.array(value) diff --git a/devices/examples/load_from_disk/ini_device.py b/devices/examples/load_from_disk/ini_device.py index 4015faf0a45592c9cb2daacb8356471b26ee7c7c..07b2f419ab6b4cd5d78eb84a66c3906e169da99d 100644 --- a/devices/examples/load_from_disk/ini_device.py +++ b/devices/examples/load_from_disk/ini_device.py @@ -80,8 +80,8 @@ class ini_device(hardware_device): bool_scalar_R = attribute_wrapper(comms_annotation={"section": "scalar", "name": "bool_scalar_R"}, datatype=numpy.bool_) int_scalar_RW = attribute_wrapper(comms_annotation={"section": "scalar", "name": "int_scalar_RW"}, datatype=numpy.int64, access=AttrWriteType.READ_WRITE) int_scalar_R = attribute_wrapper(comms_annotation={"section": "scalar", "name": "int_scalar_R"}, datatype=numpy.int64) - str_scalar_RW = attribute_wrapper(comms_annotation={"section": "scalar", "name": "str_scalar_RW"}, datatype=numpy.str_, access=AttrWriteType.READ_WRITE) - str_scalar_R = attribute_wrapper(comms_annotation={"section": "scalar", "name": "str_scalar_R"}, datatype=numpy.str_) + str_scalar_RW = attribute_wrapper(comms_annotation={"section": "scalar", "name": "str_scalar_RW"}, datatype=numpy.str, access=AttrWriteType.READ_WRITE) + str_scalar_R = attribute_wrapper(comms_annotation={"section": "scalar", "name": "str_scalar_R"}, datatype=numpy.str) double_spectrum_RW = attribute_wrapper(comms_annotation={"section": "spectrum", "name": "double_spectrum_RW"}, datatype=numpy.double, dims=(4,), access=AttrWriteType.READ_WRITE) double_spectrum_R = attribute_wrapper(comms_annotation={"section": "spectrum", "name": "double_spectrum_R"}, datatype=numpy.double, dims=(4,)) @@ -89,8 +89,8 @@ class ini_device(hardware_device): bool_spectrum_R = attribute_wrapper(comms_annotation={"section": "spectrum", "name": "bool_spectrum_R"}, datatype=numpy.bool_, dims=(4,)) int_spectrum_RW = attribute_wrapper(comms_annotation={"section": "spectrum", "name": "int_spectrum_RW"}, datatype=numpy.int64, dims=(4,), access=AttrWriteType.READ_WRITE) int_spectrum_R = attribute_wrapper(comms_annotation={"section": "spectrum", "name": "int_spectrum_R"}, datatype=numpy.int64, dims=(4,)) - str_spectrum_RW = attribute_wrapper(comms_annotation={"section": "spectrum", "name": "str_spectrum_RW"}, datatype=numpy.str_, dims=(4,), access=AttrWriteType.READ_WRITE) - str_spectrum_R = attribute_wrapper(comms_annotation={"section": "spectrum", "name": "str_spectrum_R"}, datatype=numpy.str_, dims=(4,)) + str_spectrum_RW = attribute_wrapper(comms_annotation={"section": "spectrum", "name": "str_spectrum_RW"}, datatype=numpy.str, dims=(4,), access=AttrWriteType.READ_WRITE) + str_spectrum_R = attribute_wrapper(comms_annotation={"section": "spectrum", "name": "str_spectrum_R"}, datatype=numpy.str, dims=(4,)) double_image_RW = attribute_wrapper(comms_annotation={"section": "image", "name": "double_image_RW"}, datatype=numpy.double, dims=(3, 2), access=AttrWriteType.READ_WRITE) double_image_R = attribute_wrapper(comms_annotation={"section": "image", "name": "double_image_R"}, datatype=numpy.double, dims=(3, 2)) @@ -98,8 +98,8 @@ class ini_device(hardware_device): bool_image_R = attribute_wrapper(comms_annotation={"section": "image", "name": "bool_image_R"}, datatype=numpy.bool_, dims=(3, 2)) int_image_RW = attribute_wrapper(comms_annotation={"section": "image", "name": "int_image_RW"}, datatype=numpy.int64, dims=(3, 2), access=AttrWriteType.READ_WRITE) int_image_R = attribute_wrapper(comms_annotation={"section": "image", "name": "int_image_R"}, datatype=numpy.int64, dims=(3, 2)) - str_image_RW = attribute_wrapper(comms_annotation={"section": "image", "name": "str_image_RW"}, datatype=numpy.str_, dims=(3, 2), access=AttrWriteType.READ_WRITE) - str_image_R = attribute_wrapper(comms_annotation={"section": "image", "name": "str_image_R"}, datatype=numpy.str_, dims=(3, 2)) + str_image_RW = attribute_wrapper(comms_annotation={"section": "image", "name": "str_image_RW"}, datatype=numpy.str, dims=(3, 2), access=AttrWriteType.READ_WRITE) + str_image_R = attribute_wrapper(comms_annotation={"section": "image", "name": "str_image_R"}, datatype=numpy.str, dims=(3, 2)) # -------- # overloaded functions diff --git a/devices/examples/snmp/snmp.py b/devices/examples/snmp/snmp.py index a36f6b7305ef999b67ecf20a223fb5149b553a0f..2a912ce1443bbd8e83b662d4ed9764627d947943 100644 --- a/devices/examples/snmp/snmp.py +++ b/devices/examples/snmp/snmp.py @@ -70,15 +70,15 @@ class SNMP(hardware_device): # Attributes # ---------- - sys_description_R = attribute_wrapper(comms_annotation={"oids": "1.3.6.1.2.1.1.1.0"}, datatype=numpy.str_) - sys_objectID_R = attribute_wrapper(comms_annotation={"oids": "1.3.6.1.2.1.1.2.0", "type": "OID"}, datatype=numpy.str_) + sys_description_R = attribute_wrapper(comms_annotation={"oids": "1.3.6.1.2.1.1.1.0"}, datatype=numpy.str) + sys_objectID_R = attribute_wrapper(comms_annotation={"oids": "1.3.6.1.2.1.1.2.0", "type": "OID"}, datatype=numpy.str) sys_uptime_R = attribute_wrapper(comms_annotation={"oids": "1.3.6.1.2.1.1.3.0", "type": "TimeTicks"}, datatype=numpy.int64) - sys_name_R = attribute_wrapper(comms_annotation={"oids": "1.3.6.1.2.1.1.5.0"}, datatype=numpy.str_) - ip_route_mask_127_0_0_1_R = attribute_wrapper(comms_annotation={"oids": "1.3.6.1.2.1.4.21.1.11.127.0.0.1", "type": "IpAddress"}, datatype=numpy.str_) + sys_name_R = attribute_wrapper(comms_annotation={"oids": "1.3.6.1.2.1.1.5.0"}, datatype=numpy.str) + ip_route_mask_127_0_0_1_R = attribute_wrapper(comms_annotation={"oids": "1.3.6.1.2.1.4.21.1.11.127.0.0.1", "type": "IpAddress"}, datatype=numpy.str) TCP_active_open_R = attribute_wrapper(comms_annotation={"oids": "1.3.6.1.2.1.6.5.0", "type": "Counter32"}, datatype=numpy.int64) - sys_contact_RW = attribute_wrapper(comms_annotation={"oids": "1.3.6.1.2.1.1.4.0"}, datatype=numpy.str_, access=AttrWriteType.READ_WRITE) - sys_contact_R = attribute_wrapper(comms_annotation={"oids": "1.3.6.1.2.1.1.4.0"}, datatype=numpy.str_) + sys_contact_RW = attribute_wrapper(comms_annotation={"oids": "1.3.6.1.2.1.1.4.0"}, datatype=numpy.str, access=AttrWriteType.READ_WRITE) + sys_contact_R = attribute_wrapper(comms_annotation={"oids": "1.3.6.1.2.1.1.4.0"}, datatype=numpy.str) TCP_Curr_estab_R = attribute_wrapper(comms_annotation={"oids": "1.3.6.1.2.1.6.9.0", "type": "Gauge"}, datatype=numpy.int64) diff --git a/devices/examples/snmp/snmp_client.py b/devices/examples/snmp/snmp_client.py index 635450172387148734e5e3b42ed0f82f067a0048..96ac67140b9bdbdba7ab4d4fb8651b5e9674c219 100644 --- a/devices/examples/snmp/snmp_client.py +++ b/devices/examples/snmp/snmp_client.py @@ -12,11 +12,11 @@ __all__ = ["SNMP_client"] snmp_to_numpy_dict = { snmp.types.INTEGER: numpy.int64, snmp.types.TimeTicks: numpy.int64, - snmp.types.OCTET_STRING: numpy.str_, - snmp.types.OID: numpy.str_, + snmp.types.OCTET_STRING: numpy.str, + snmp.types.OID: numpy.str, snmp.types.Counter32: numpy.int64, snmp.types.Gauge32: numpy.int64, - snmp.types.IpAddress: numpy.str_, + snmp.types.IpAddress: numpy.str, } snmp_types = { @@ -24,9 +24,9 @@ snmp_types = { "Gauge": numpy.int64, "TimeTick": numpy.int64, "Counter32": numpy.int64, - "OctetString": numpy.str_, - "IpAddress": numpy.str_, - "OID": numpy.str_, + "OctetString": numpy.str, + "IpAddress": numpy.str, + "OID": numpy.str, } diff --git a/devices/integration_test/devices/test_device_unb2.py b/devices/integration_test/devices/test_device_unb2.py new file mode 100644 index 0000000000000000000000000000000000000000..97f31ab6ee162f8183db963e92f4f03b9ee7f617 --- /dev/null +++ b/devices/integration_test/devices/test_device_unb2.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- +# +# This file is part of the LOFAR 2.0 Station Software +# +# +# +# Distributed under the terms of the APACHE license. +# See LICENSE.txt for more info. + +import time + +from tango import DeviceProxy +from tango._tango import DevState + +from integration_test import base + + +class TestDeviceUNB2(base.IntegrationTestCase): + + def setUp(self): + """Intentionally recreate the device object in each test""" + super(TestDeviceUNB2, self).setUp() + + def tearDown(self): + """Turn device Off in teardown to prevent blocking tests""" + d = DeviceProxy("LTS/UNB2/1") + + try: + d.Off() + except Exception as e: + """Failing to turn Off devices should not raise errors here""" + print(f"Failed to turn device off in teardown {e}") + + def test_device_proxy_unb2(self): + """Test if we can successfully create a DeviceProxy and fetch state""" + + d = DeviceProxy("LTS/UNB2/1") + + self.assertEqual(DevState.OFF, d.state()) + + def test_device_unb2_initialize(self): + """Test if we can transition to standby""" + + d = DeviceProxy("LTS/UNB2/1") + + d.initialise() + + self.assertEqual(DevState.STANDBY, d.state()) + + def test_device_unb2_on(self): + """Test if we can transition to on""" + + d = DeviceProxy("LTS/UNB2/1") + + d.initialise() + + d.on() + + self.assertEqual(DevState.ON, d.state()) diff --git a/devices/statistics_writer/README.md b/devices/statistics_writer/README.md index ea722c6cf552443364b196264034d768690955be..62e940bacb6512eb702cc4fdd816b8ba61153958 100644 --- a/devices/statistics_writer/README.md +++ b/devices/statistics_writer/README.md @@ -12,10 +12,11 @@ and writing those matrices (as well as a bunch of metadata) to hdf5. The TCP statistics writer can be called with the `tcp_hdf5_writer.py` script. This script can be called with the following arguments: ``` - --address the address to connect to + --host the address to connect to --port the port to use + --file file to read from (as opposed to host and port) --interval The time between creating new files in hours - --location specifies the folder to write all the files + --output_dir specifies the folder to write all the files --mode sets the statistics type to be decoded options: "SST", "XST", "BST" --debug takes no arguments, when used prints a lot of extra data to help with debugging ``` diff --git a/devices/statistics_writer/data/test.h5 b/devices/statistics_writer/data/test.h5 deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/devices/statistics_writer/data/testing.h5 b/devices/statistics_writer/data/testing.h5 deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/devices/statistics_writer/hdf5_writer.py b/devices/statistics_writer/hdf5_writer.py new file mode 100644 index 0000000000000000000000000000000000000000..5a5eb5ee3fe4d5d05094874d65293ea7fbc68314 --- /dev/null +++ b/devices/statistics_writer/hdf5_writer.py @@ -0,0 +1,223 @@ +# imports for working with datetime objects +from datetime import datetime, timedelta +import pytz + +# python hdf5 +import h5py + +import numpy +import logging + +# import statistics classes with workaround +import sys +sys.path.append("..") +from devices.sdp.statistics_packet import SSTPacket, XSTPacket, BSTPacket, StatisticsPacket +import devices.sdp.statistics_collector as statistics_collector + + +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger("hdf5_writer") + +__all__ = ["hdf5_writer"] + +class hdf5_writer: + + SST_MODE = "SST" + XST_MODE = "XST" + BST_MODE = "BST" + + + def __init__(self, new_file_time_interval, file_location, statistics_mode): + + # all variables that deal with the matrix that's currently being decoded + self.current_matrix = None + self.current_timestamp = datetime.min.replace(tzinfo=pytz.UTC) + + # the header of the first packet of a new matrix is written as metadata. + # Assumes all subsequent headers of the same matrix are identical (minus index) + self.statistics_header = None + + # file handing + self.file_location = file_location + self.new_file_time_interval = timedelta(seconds=new_file_time_interval) + self.last_file_time = datetime.min.replace(tzinfo=pytz.UTC) + self.file = None + + # parameters that are configured depending on the mode the statistics writer is in (SST,XST,BST) + self.decoder = None + self.collector = None + self.store_function = None + self.mode = statistics_mode.upper() + self.config_mode() + + def next_packet(self, packet): + """ + All statistics packets come with a timestamp of the time they were measured. All the values will be spread across multiple packets. + As long as the timestamp is the same they belong in the same matrix. This code handles collecting the matrix from those multiple + packets as well as storing matrices and starting new ones + + The code receives new packets and checks the statistics timestamp of them. If the timestamp is higher than the current timestamp + it will close the current matrix, store it and start a new one. + """ + + # process the packet + statistics_packet = self.decoder(packet) + + if not self.statistics_header: + self.statistics_header = statistics_packet.header() + + # grab the timestamp + statistics_timestamp = statistics_packet.timestamp() + + # ignore packets with no timestamp, as they indicate FPGA processing was disabled + # and are useless anyway. + if statistics_packet.block_serial_number == 0: + logger.warning(f"Received statistics with no timestamp. Packet dropped.") + return + + # check if te statistics timestamp is unexpectedly older than the current one + if statistics_timestamp < self.current_timestamp: + logger.warning(f"Received statistics with earlier timestamp than is currently being processed ({statistics_timestamp}). Packet dropped.") + return + + # if this statistics packet has a new timestamp it means we need to start a new matrix + if statistics_timestamp > self.current_timestamp: + self.start_new_matrix(statistics_timestamp) + self.current_timestamp = statistics_timestamp + + self.process_packet(packet) + + def start_new_matrix(self, timestamp): + logger.info(f"starting new matrix with timestamp: {timestamp}") + """ + is called when a statistics packet with a newer timestamp is received. + Writes the matrix to the hdf5 file + Creates a new hdf5 file if needed + updates current timestamp and statistics matrix collector + """ + + # write the finished (and checks if its the first matrix) + if self.current_matrix is not None: + try: + self.write_matrix() + except Exception as e: + time = self.current_timestamp.strftime("%Y-%m-%d-%H-%M-%S-%f")[:-3] + logger.exception(f"Exception while attempting to write matrix to HDF5. Matrix: {time} dropped") + + # only start a new file if its time AND we are done with the previous matrix. + if timestamp >= self.new_file_time_interval + self.last_file_time: + self.start_new_hdf5(timestamp) + + # create a new and empty current_matrix + self.current_matrix = self.collector() + self.statistics_header = None + + def write_matrix(self): + logger.info("writing matrix to file") + """ + Writes the finished matrix to the hdf5 file + """ + + # create the new hdf5 group based on the timestamp of packets + current_group = self.file.create_group("{}_{}".format(self.mode, self.current_timestamp.strftime("%Y-%m-%d-%H-%M-%S-%f")[:-3])) + + # store the statistics values for the current group + self.store_function(current_group) + + # might be optional, but they're easy to add. + current_group.create_dataset(name="nof_payload_errors", data=self.current_matrix.parameters["nof_payload_errors"]) + current_group.create_dataset(name="nof_valid_payloads", data=self.current_matrix.parameters["nof_valid_payloads"]) + + # get the statistics header + header = self.statistics_header + + # can't store datetime objects, convert to string instead + header["timestamp"] = header["timestamp"].isoformat(timespec="milliseconds") + + # Stores the header of the packet received for this matrix as a list of atttributes + for k,v in header.items(): + if type(v) == dict: + for subk, subv in v.items(): + current_group.attrs[f"{k}_{subk}"] = subv + else: + current_group.attrs[k] = v + + def write_sst_matrix(self, current_group): + # store the SST values + current_group.create_dataset(name="sst_values", data=self.current_matrix.parameters["sst_values"].astype(numpy.float32), compression="gzip") + + def write_xst_matrix(self, current_group): + # requires a function call to transform the xst_blocks in to the right structure + current_group.create_dataset(name="xst_values", data=self.current_matrix.xst_values().astype(numpy.cfloat), compression="gzip") + + def write_bst_matrix(self, current_group): + raise NotImplementedError("BST values not implemented") + + + def process_packet(self, packet): + logger.debug(f"Processing packet") + """ + Adds the newly received statistics packet to the statistics matrix + """ + self.current_matrix.process_packet(packet) + + def start_new_hdf5(self, timestamp): + + if self.file is not None: + try: + self.file.close() + except Exception as e: + logger.exception(f"Error while attempting to close hdf5 file to disk. file {self.file} likely empty, please verify integrity.") + + current_time = str(timestamp.strftime("%Y-%m-%d-%H-%M-%S")) + logger.info(f"creating new file: {self.file_location}/{self.mode}_{current_time}.h5") + + try: + self.file = h5py.File(f"{self.file_location}/{self.mode}_{current_time}.h5", 'w') + except Exception as e: + logger.exception(f"Error while creating new file") + raise e + + self.last_file_time = timestamp + + def config_mode(self): + logger.debug(f"attempting to configure {self.mode} mode") + + """ + Configures the object for the correct statistics type to be used. + decoder: the class to decode a single packet + collector: the class to collect statistics packets + store_function: the function to write the mode specific data to file + """ + + if self.mode == self.SST_MODE: + self.decoder = SSTPacket + self.collector = statistics_collector.SSTCollector + self.store_function = self.write_sst_matrix + + elif self.mode == self.XST_MODE: + self.decoder = XSTPacket + self.collector = statistics_collector.XSTCollector + self.store_function = self.write_xst_matrix + + elif self.mode == self.BST_MODE: + self.store_function = self.write_bst_matrix + raise NotImplementedError("BST collector has not yet been implemented") + + else: + raise ValueError("invalid statistics mode specified '{}', please use 'SST', 'XST' or 'BST' ".format(self.mode)) + + def close_writer(self): + """ + Function that can be used to stop the writer without data loss. + """ + logger.debug("closing hdf5 file") + if self.file is not None: + if self.current_matrix is not None: + # Write matrix if one exists + # only creates file if there is a matrix to actually write + try: + self.write_matrix() + finally: + self.file.close() + logger.debug(f"{self.file} closed") diff --git a/devices/statistics_writer/tcp_receiver.py b/devices/statistics_writer/receiver.py similarity index 70% rename from devices/statistics_writer/tcp_receiver.py rename to devices/statistics_writer/receiver.py index 4112f926785d5fbb5b25672113c55445f8a8952b..919357764a2196cb7955e4ec77f2487b81d24d59 100644 --- a/devices/statistics_writer/tcp_receiver.py +++ b/devices/statistics_writer/receiver.py @@ -3,16 +3,15 @@ import socket import sys sys.path.append("..") from devices.sdp.statistics_packet import StatisticsPacket +import os + +class receiver: + """ Reads data from a file descriptor. """ -class tcp_receiver: HEADER_LENGTH = 32 - def __init__(self, HOST, PORT): - self.host = HOST - self.port = PORT - - self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.sock.connect((self.host, self.port)) + def __init__(self, fd): + self.fd = fd def get_packet(self) -> bytes: """ Read exactly one statistics packet from the TCP connection. """ @@ -36,11 +35,31 @@ class tcp_receiver: # try to read the remainder. # NOTE: recv() may return less data than requested, and returns 0 # if there is nothing left to read (end of stream) - more_data = self.sock.recv(data_length - len(data)) + more_data = os.read(self.fd, data_length - len(data)) if not more_data: # connection got dropped - raise IOError("Connection closed by peer") + raise EOFError("End of stream") data += more_data return data + +class tcp_receiver(receiver): + def __init__(self, HOST, PORT): + self.host = HOST + self.port = PORT + + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.sock.connect((self.host, self.port)) + + super().__init__(fd=self.sock.fileno()) + +class file_receiver(receiver): + def __init__(self, filename): + self.filename = filename + self.fileno = os.open(filename, os.O_RDONLY) + + super().__init__(fd=self.fileno) + + def __del__(self): + os.close(self.fileno) diff --git a/devices/statistics_writer/statistics_writer.py b/devices/statistics_writer/statistics_writer.py index a013cb779b758b8e5256aba23e1ac0ff9a24e9d7..444ee2323e950a0428513cb4506d8b2b2376fc27 100644 --- a/devices/statistics_writer/statistics_writer.py +++ b/devices/statistics_writer/statistics_writer.py @@ -1,187 +1,70 @@ -# imports for working with datetime objects -from datetime import datetime, timedelta -import pytz +import argparse +from receiver import tcp_receiver, file_receiver +from hdf5_writer import hdf5_writer -# python hdf5 -import h5py - -import numpy -import json -import logging - -# import statistics classes with workaround import sys -sys.path.append("..") -from devices.sdp.statistics_packet import SSTPacket, XSTPacket, BSTPacket, StatisticsPacket -import devices.sdp.statistics_collector as statistics_collector - +import signal +import logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger("statistics_writer") -__all__ = ["statistics_writer"] - -class statistics_writer: - - - def __init__(self, new_file_time_interval, file_location, statistics_mode): - - # all variables that deal with the SST matrix that's currently being decoded - self.current_matrix = None - self.current_timestamp = datetime.min.replace(tzinfo=pytz.UTC) - - # the header of the first packet of a new matrix is written as metadata. - # Assumes all subsequent headers of the same matrix are identical (minus index) - self.statistics_header = None - - # file handing - self.file_location = file_location - self.new_file_time_interval = timedelta(hours=new_file_time_interval) - self.last_file_time = datetime.min.replace(tzinfo=pytz.UTC) - self.file = None - - # config the writer for the correct statistics type - self.collector = None - self.decoder = None - self.mode = statistics_mode.upper() - self.config_mode() - - def next_packet(self, packet): - """ - All statistics packets come with a timestamp of the time they were measured. All the values will be spread across multiple packets. - As long as the timestamp is the same they belong in the same matrix. This code handles collecting the matrix from those multiple - packets as well as storing matrices and starting new ones - - The code receives new packets and checks the statistics timestamp of them. If the timestamp is higher than the current timestamp - it will close the current matrix, store it and start a new one. - """ - - # process the packet - statistics_packet = self.decoder(packet) - - # grab the timestamp - statistics_timestamp = statistics_packet.timestamp() - - # check if te statistics timestamp is unexpectedly older than the current one - if statistics_timestamp < self.current_timestamp: - logger.warning(f"Received statistics with earlier timestamp than is currently being processed ({statistics_timestamp}). Packet dropped.") - return - - # if this statistics packet has a new timestamp it means we need to start a new matrix - if statistics_timestamp > self.current_timestamp: - self.statistics_header = statistics_packet.header() - self.start_new_matrix(statistics_timestamp) - self.current_timestamp = statistics_timestamp - - self.process_packet(packet) - - def start_new_matrix(self, timestamp): - logger.debug(f"starting new matrix with timestamp: {timestamp}") - """ - is called when a statistics packet with a newer timestamp is received. - Writes the matrix to the hdf5 file - Creates a new hdf5 file if needed - updates current timestamp and statistics matrix collector - """ - - # write the finished (and checks if its the first matrix) - if self.current_matrix is not None: - try: - self.write_matrix() - except Exception as e: - time = str(self.current_timestamp.strftime("%Y-%m-%d-%H-%M-%S")) - logger.error(f"Exception while attempting to write matrix to HDF5. Matrix: {time} dropped: {e}") - - # only start a new file if its time AND we are done with the previous matrix. - if timestamp >= self.new_file_time_interval + self.last_file_time: - self.start_new_hdf5(timestamp) - - # create a new and empty current_matrix - self.current_matrix = self.collector() - - def write_matrix(self): - logger.debug("writing matrix to file") - """ - Writes the finished matrix to the hdf5 file - """ - - # create the new hdf5 group based on the timestamp of packets - current_group = self.file.create_group("{}_{}".format(self.mode, str(self.current_timestamp.strftime("%Y-%m-%d-%H-%M-%S")))) - - # store the statistics values - current_group.create_dataset(name=f"{self.mode}_values", data=self.current_matrix.parameters["sst_values"]) - - # might be optional, but they're easy to add. - current_group.create_dataset(name="nof_payload_errors", data=self.current_matrix.parameters["nof_payload_errors"]) - current_group.create_dataset(name="nof_valid_payloads", data=self.current_matrix.parameters["nof_valid_payloads"]) - - # get the statistics header - header = self.statistics_header - # can't store datetime objects in json, converted to string instead - header["timestamp"] = header["timestamp"].isoformat(timespec="milliseconds") - # convert the header to JSON - json_header = json.dumps(header) - # Stores the header of the packet received for this matrix - current_group.create_dataset(name='first_packet_header', data=numpy.str(json_header)) - - - def process_packet(self, packet): - logger.debug(f"Processing packet") - """ - Adds the newly received statistics packet to the statistics matrix - """ - self.current_matrix.process_packet(packet) - - def start_new_hdf5(self, timestamp): - - if self.file is not None: - try: - self.file.close() - except Exception as e: - logger.error(f"Error while attempting to close hdf5 file to disk. file {self.file} likely empty, please verify integrity. \r\n Exception: {e}") - - current_time = str(timestamp.strftime("%Y-%m-%d-%H-%M-%S")) - logger.debug(f"creating new file: {self.file_location}/{self.mode}_{current_time}.h5") - - try: - self.file = h5py.File(f"{self.file_location}/{self.mode}_{current_time}.h5", 'w') - except Exception as e: - logger.error(f"Error while creating new file: {e}") - raise e - - self.last_file_time = timestamp - - def config_mode(self): - logger.debug(f"attempting to configure {self.mode} mode") - - """ - Configures the object for the correct statistics type to be used. - """ +parser = argparse.ArgumentParser(description='Converts a stream of statistics packets into HDF5 files.') +parser.add_argument('--host', type=str, help='the host to connect to') +parser.add_argument('--port', type=int, default=0, help='the port to connect to, or 0 to use default port for the selected mode (default: %(default)s)') +parser.add_argument('--file', type=str, help='the file to read from') + +parser.add_argument('--mode', type=str, choices=['SST', 'XST', 'BST'], default='SST', help='sets the statistics type to be decoded options (default: %(default)s)') +parser.add_argument('--interval', type=float, default=3600, nargs="?", help='The time between creating new files in seconds (default: %(default)s)') +parser.add_argument('--output_dir', type=str, default=".", nargs="?", help='specifies the folder to write all the files (default: %(default)s)') +parser.add_argument('--debug', dest='debug', action='store_true', default=False, help='increase log output') + + +# create a data dumper that creates a new file every 10s (for testing) +if __name__ == "__main__": + args = parser.parse_args() + + # argparse arguments + host = args.host + port = args.port + filename = args.file + output_dir = args.output_dir + interval = args.interval + mode = args.mode + debug = args.debug + + if port == 0: + default_ports = { "SST": 5101, "XST": 5102, "BST": 5103 } + port = default_ports[mode] + + if debug: + logger.setLevel(logging.DEBUG) + logger.debug("Setting loglevel to DEBUG") + + # creates the TCP receiver that is given to the writer + if filename: + receiver = file_receiver(filename) + elif host and port: + receiver = tcp_receiver(host, port) + else: + logger.fatal("Must provide either a host and port, or a file to receive input from") + sys.exit(1) + + # create the writer + writer = hdf5_writer(new_file_time_interval=interval, file_location=output_dir, statistics_mode=mode) + + # start looping + try: + while True: + packet = receiver.get_packet() + writer.next_packet(packet) + except KeyboardInterrupt: + # user abort, don't complain + logger.warning("Received keyboard interrupt. Stopping.") + except EOFError: + # done processing all input, don't complain + logger.info("End of input.") + finally: + writer.close_writer() - if self.mode == 'SST': - self.decoder = SSTPacket - self.collector = statistics_collector.SSTCollector - elif self.mode == 'BST': - # self.decoder = XSTPacket - raise NotImplementedError("BST collector has not yet been implemented") - elif self.mode == 'XST': - # self.decoder = XSTPacket - raise NotImplementedError("BST collector has not yet been implemented") - else: - # make sure the mode is valid - raise ValueError("invalid statistics mode specified '{}', please use 'SST', 'XST' or 'BST' ".format(self.mode)) - def close_writer(self): - """ - Function that can be used to stop the writer without data loss. - """ - logger.debug("closing hdf5 file") - if self.file is not None: - if self.current_matrix is not None: - # Write matrix if one exists - # only creates file if there is a matrix to actually write - try: - self.write_matrix() - finally: - self.file.close() - logger.debug(f"{self.file} closed") diff --git a/devices/statistics_writer/tcp_hdf5_writer.py b/devices/statistics_writer/tcp_hdf5_writer.py deleted file mode 100644 index 8d290e293e2db82fa3e12e071e7dda4d6077ea40..0000000000000000000000000000000000000000 --- a/devices/statistics_writer/tcp_hdf5_writer.py +++ /dev/null @@ -1,59 +0,0 @@ -import argparse -from tcp_receiver import tcp_receiver -from statistics_writer import statistics_writer - -import sys -import signal - -import logging -logging.basicConfig(level=logging.INFO) -logger = logging.getLogger("statistics_writer") - -parser = argparse.ArgumentParser(description='Arguments to configure the TCP connection and writer mode') -parser.add_argument('--address', type=str, help='the address to connect to') -parser.add_argument('--port', type=int, help='the port to use') - -parser.add_argument('--interval', type=float, default=1, nargs="?", help='The time between creating new files in hours') -parser.add_argument('--location', type=str, default="data", nargs="?", help='specifies the folder to write all the files') -parser.add_argument('--mode', type=str, choices=['SST', 'XST', 'BST'], help='sets the statistics type to be decoded options: "SST", "XST", "BST"') -parser.add_argument('--debug', dest='debug', action='store_true', default=False, help='when used stores failed packets') - - -# create a data dumper that creates a new file every 10s (for testing) -if __name__ == "__main__": - def signal_handler(signum, frame): - writer.close_writer() - sys.exit(0) - - - signal.signal(signal.SIGINT, signal_handler) - - args = parser.parse_args() - - # argparse arguments - address = args.address - port = args.port - location = args.location - interval = args.interval - mode = args.mode - debug = args.debug - - if debug: - logger.setLevel(logging.DEBUG) - logger.debug("Setting loglevel to DEBUG") - - # creates the TCP receiver that is given to the writer - receiver = tcp_receiver(address, port) - - # create the writer - writer = statistics_writer(new_file_time_interval=interval, file_location=location, statistics_mode=mode) - - # start looping - try: - while True: - packet = receiver.get_packet() - writer.next_packet(packet) - except KeyboardInterrupt: - writer.close_writer() - - diff --git a/devices/statistics_writer/test/hdf5_explorer.py b/devices/statistics_writer/test/hdf5_explorer.py index 29cc88049086f5bea22c441d1ca12f91769c7135..102c36b79f7beeb6a34ffba9b95a495a85a76f6e 100644 --- a/devices/statistics_writer/test/hdf5_explorer.py +++ b/devices/statistics_writer/test/hdf5_explorer.py @@ -19,80 +19,43 @@ class statistics_data: the datasets in them. """ - - NOF_PAYLOAD_ERRORS = "nof_payload_errors" - NOF_VALID_PAYLOADS = "nof_valid_payloads" - FIRST_PACKET_HEADER = "first_packet_header" - STATISTICS_VALUES = "statistics_values" - - def __init__(self, file, statistics_name): - self.nof_valid_payloads = file.get(f"{statistics_name}/{statistics_data.NOF_VALID_PAYLOADS}") - self.nof_payload_errors = file.get(f"{statistics_name}/{statistics_data.NOF_PAYLOAD_ERRORS}") - self.first_packet_header = file.get(f"{statistics_name}/{statistics_data.FIRST_PACKET_HEADER}") - self.statistics_values = file.get(f"{statistics_name}/{statistics_data.STATISTICS_VALUES}") - - class explorer: """ This class serves both as a tool to test and verify the content of HDF5 files as well as provide an example of how you can go through HDF5 files. - - - The first 2 functions, print_high_level and print_full both call the hdf5 file.visititems function. this function - takes another function as argument and then calls that function for each and every group and dataset in the file. - - The last 2 functions do this without this file.visititems function and instead have knowledge of how we structure the - statistics data. """ def __init__(self, filename): self.file = h5py.File(filename, 'r') - def print_high_level(self): - """Calls a function that will go through all groups and datasets in the file and pass data along to another specified function""" - self.file.visititems(self._high_level_explorer) - - def print_full(self): - """Calls a function that will go through all groups and datasets in the file and pass data along to another specified function""" - self.file.visititems(self._full_explorer) - - def _full_explorer(self, name, obj): - """ - Called by the file.visititems(func) function. Gets called for each and every group and dataset. - Prints all groups and datasets including their content. - """ - - shift = name.count('/') * ' ' - data = self.file.get(name) - logger.debug(f"{shift}{name}: {data}") - logger.debug(numpy.array(data)) - - def _high_level_explorer(self, name, obj): - """ - Called by the file.visititems(func) function. Gets called for each and every group and dataset. - Only lists the groups and datasets without the actual content. - """ - shift = name.count('/') * ' ' - data = self.file.get(name) - logger.debug(f"{shift}{name}: {data}") - def print_all_statistics_full(self): """ Explores the file with knowledge of the file structure. assumes all top level groups are statistics and that all statistics groups are made up of datasets. Prints the groups, the datasets and the content of the datasets. - """ - # List all groups - logger.debug("Keys: %s" % self.file.keys()) + Can easily be modified to instead of just logging all the data, store it in whatever structure is needed. + """ for group_key in self.file.keys(): dataset = list(self.file[group_key]) + + #print group name + logger.debug(f" \n\ngroup: {group_key}") + + # Go through all the datasets for i in dataset: data = self.file.get(f"{group_key}/{i}") - logger.debug(group_key) - logger.debug(numpy.array(data)) + logger.debug(f" dataset: {i}") + logger.debug(f" Data: {numpy.array(data)}") + + # go through all the attributes in the group (This is the header info) + attr_keys = self.file[group_key].attrs.keys() + for i in attr_keys: + attr = self.file[group_key].attrs[i] + + logger.debug(f" {i}: {attr}") def print_all_statistics_top_level(self): """ @@ -108,7 +71,6 @@ class explorer: logger.debug(group_key) - # create a data dumper that creates a new file every 10s (for testing) if __name__ == "__main__": args = parser.parse_args() @@ -122,6 +84,7 @@ if __name__ == "__main__": """ Print only the names of all the statistics in this file """ + logger.debug("--------------Top level groups--------------") Explorer.print_all_statistics_top_level() diff --git a/devices/test/clients/test_attr_wrapper.py b/devices/test/clients/test_attr_wrapper.py index a293923acbf21774e9f221b650353f3410104a88..453e19c19d67b56eb339462cc1da7e0e8414451b 100644 --- a/devices/test/clients/test_attr_wrapper.py +++ b/devices/test/clients/test_attr_wrapper.py @@ -38,8 +38,8 @@ def dev_init(device): class TestAttributeTypes(base.TestCase): class str_scalar_device(hardware_device): - scalar_R = attribute_wrapper(comms_annotation="str_scalar_R", datatype=str) - scalar_RW = attribute_wrapper(comms_annotation="str_scalar_RW", datatype=str, access=AttrWriteType.READ_WRITE) + scalar_R = attribute_wrapper(comms_annotation="str_scalar_R", datatype=numpy.str) + scalar_RW = attribute_wrapper(comms_annotation="str_scalar_RW", datatype=numpy.str, access=AttrWriteType.READ_WRITE) def configure_for_initialise(self): dev_init(self) @@ -122,8 +122,8 @@ class TestAttributeTypes(base.TestCase): dev_init(self) class str_spectrum_device(hardware_device): - spectrum_R = attribute_wrapper(comms_annotation="str_spectrum_R", datatype=str, dims=spectrum_dims) - spectrum_RW = attribute_wrapper(comms_annotation="str_spectrum_RW", datatype=str, access=AttrWriteType.READ_WRITE, dims=spectrum_dims) + spectrum_R = attribute_wrapper(comms_annotation="str_spectrum_R", datatype=numpy.str, dims=spectrum_dims) + spectrum_RW = attribute_wrapper(comms_annotation="str_spectrum_RW", datatype=numpy.str, access=AttrWriteType.READ_WRITE, dims=spectrum_dims) def configure_for_initialise(self): dev_init(self) @@ -206,8 +206,8 @@ class TestAttributeTypes(base.TestCase): dev_init(self) class str_image_device(hardware_device): - image_R = attribute_wrapper(comms_annotation="str_image_R", datatype=str, dims=(2,3)) - image_RW = attribute_wrapper(comms_annotation="str_image_RW", datatype=str, access=AttrWriteType.READ_WRITE, dims=(2,3)) + image_R = attribute_wrapper(comms_annotation="str_image_R", datatype=numpy.str, dims=(2,3)) + image_RW = attribute_wrapper(comms_annotation="str_image_RW", datatype=numpy.str, access=AttrWriteType.READ_WRITE, dims=(2,3)) def configure_for_initialise(self): dev_init(self) @@ -333,20 +333,20 @@ class TestAttributeTypes(base.TestCase): if test_type == "scalar": - if dtype is str or dtype is numpy.str_: + if dtype is numpy.str: val = str_scalar_val else: val = dtype(1) proxy.scalar_RW = val elif test_type == "spectrum": - if dtype is str or dtype is numpy.str_: + if dtype is numpy.str: val = str_spectrum_val else: val = numpy.full(spectrum_dims, dtype=dtype, fill_value=1) print(val) proxy.spectrum_RW = val elif test_type == "image": - if dtype is str or dtype is numpy.str_: + if dtype is numpy.str: val = str_image_val else: val = numpy.full(image_dims, dtype=dtype, fill_value=1) @@ -408,7 +408,7 @@ class TestAttributeTypes(base.TestCase): proxy.on() if test_type == "scalar": - if dtype is str or dtype is numpy.str_: + if dtype is numpy.str: val = str_scalar_val else: val = dtype(1) @@ -416,7 +416,7 @@ class TestAttributeTypes(base.TestCase): result_R = proxy.scalar_R result_RW = proxy.scalar_RW elif test_type == "spectrum": - if dtype is str or dtype is numpy.str_: + if dtype is numpy.str: val = str_spectrum_val else: val = numpy.full(spectrum_dims, dtype=dtype, fill_value=1) @@ -424,7 +424,7 @@ class TestAttributeTypes(base.TestCase): result_R = proxy.spectrum_R result_RW = proxy.spectrum_RW elif test_type == "image": - if dtype is str or dtype is numpy.str_: + if dtype is numpy.str: val = str_image_val else: val = numpy.full(image_dims, dtype=dtype, fill_value=1) @@ -434,7 +434,7 @@ class TestAttributeTypes(base.TestCase): result_R = proxy.image_R result_RW = proxy.image_RW - if dtype != str: + if dtype != numpy.str: self.assertEqual(result_R.shape, image_dims, "not the correct dimensions") result_R = result_R.reshape(-1) @@ -450,7 +450,7 @@ class TestAttributeTypes(base.TestCase): self.assertTrue(comparison, " Value could not be handled by the atrribute_wrappers internal RW storer. attempted to write: {}".format(val)) comparison = result_R == val self.assertTrue(comparison, " value in the clients R attribute not equal to what was written. read: {}, wrote {}".format(result_R, val)) - elif dtype != str: + elif dtype != numpy.str: comparison = result_RW == val equal_arrays = comparison.all() self.assertTrue(equal_arrays, " Value could not be handled by the atrribute_wrappers internal RW storer. attempted to write: {}".format(val)) diff --git a/devices/test/clients/test_opcua_client.py b/devices/test/clients/test_opcua_client.py index 13b7863819fbcc9d60fc3ae95ad5a269546e200e..df9296c417857683955aa73ee3cbc0b7985ade76 100644 --- a/devices/test/clients/test_opcua_client.py +++ b/devices/test/clients/test_opcua_client.py @@ -164,7 +164,7 @@ class TestOPCua(base.TestCase): def test_type_map(self): for numpy_type, opcua_type in opcua_client.numpy_to_OPCua_dict.items(): # derive a default value that can get lost in a type translation - if numpy_type in [str, numpy.str, numpy.str_]: + if numpy_type in [str, numpy.str]: default_value = "foo" elif numpy_type == numpy.bool_: default_value = True @@ -191,7 +191,7 @@ class TestOPCua(base.TestCase): self.assertEqual(v.Value, reparsed_v.Value, msg=f"Conversion {numpy_type} -> {opcua_type} failed.") # does the OPC-UA type have the same datasize (and thus, precision?) - if numpy_type not in [str, numpy.str, numpy.str_]: + if numpy_type not in [str, numpy.str]: self.assertEqual(numpy_type().itemsize, getattr(opcua.ua.ua_binary.Primitives, opcua_type.name).size, msg=f"Conversion {numpy_type} -> {opcua_type} failed: precision mismatch") diff --git a/devices/test/clients/test_tcp_replicator.py b/devices/test/clients/test_tcp_replicator.py index 48419c1a0cab93be6af2b7849684032f46361612..a9babed0eb7af7a58544b3ff7535c3113ed12ca3 100644 --- a/devices/test/clients/test_tcp_replicator.py +++ b/devices/test/clients/test_tcp_replicator.py @@ -31,8 +31,6 @@ class TestTCPReplicator(base.TestCase): def setUp(self): super(TestTCPReplicator, self).setUp() - self.m_queue = mock.Mock() - self.m_server = mock.Mock() self.m_server.wait_closed.return_value = self.dummy_task() @@ -71,7 +69,7 @@ class TestTCPReplicator(base.TestCase): "tcp_bind": '0.0.0.0', # I should get set } - replicator = self.m_tcp_replicator(self.m_queue, test_options) + replicator = self.m_tcp_replicator(options=test_options) self.assertTrue(replicator.is_alive()) # Ensure replicator initialization does not modify static variable @@ -89,7 +87,7 @@ class TestTCPReplicator(base.TestCase): m_client = mock.Mock() # Create both a TCPReplicator and TCPServerProtocol separately - replicator = self.m_tcp_replicator(self.m_queue) + replicator = self.m_tcp_replicator() self.assertTrue(replicator.is_alive()) protocol = TCPReplicator.TCPServerProtocol( replicator._options, replicator._connected_clients) @@ -103,7 +101,7 @@ class TestTCPReplicator(base.TestCase): def test_start_stop(self): """Verify threading behavior, being able to start and stop the thread""" - replicator = self.m_tcp_replicator(self.m_queue) + replicator = self.m_tcp_replicator() self.assertTrue(replicator.is_alive()) # Give the thread 5 seconds to stop @@ -129,7 +127,7 @@ class TestTCPReplicator(base.TestCase): run_patcher.new_event_loop.return_value = m_loop # Constructor should raise an exception if the thread dies early - self.assertRaises(RuntimeError, self.m_tcp_replicator, self.m_queue) + self.assertRaises(RuntimeError, self.m_tcp_replicator) @timeout_decorator.timeout(5) def test_start_except_server(self): @@ -139,13 +137,13 @@ class TestTCPReplicator(base.TestCase): RuntimeError("Test Error") # Constructor should raise an exception if the thread dies early - self.assertRaises(RuntimeError, self.m_tcp_replicator, self.m_queue) + self.assertRaises(RuntimeError, self.m_tcp_replicator) @timeout_decorator.timeout(5) def test_start_stop_delete(self): """Verify that deleting the TCPReplicator object safely halts thread""" - replicator = self.m_tcp_replicator(self.m_queue) + replicator = self.m_tcp_replicator() self.assertTrue(replicator.is_alive()) del replicator @@ -157,7 +155,7 @@ class TestTCPReplicator(base.TestCase): m_client = mock.Mock() - replicator = self.m_tcp_replicator(self.m_queue) + replicator = self.m_tcp_replicator() self.assertTrue(replicator.is_alive()) replicator._connected_clients.append(m_client) @@ -176,7 +174,7 @@ class TestTCPReplicator(base.TestCase): m_client.transport.write.assert_called_once_with(m_data) def test_queue_start(self): - replicator = self.m_tcp_replicator(self.m_queue) + replicator = self.m_tcp_replicator() self.m_process_queue.assert_called_once_with(replicator) @@ -185,9 +183,7 @@ class TestTCPReplicator(base.TestCase): m_client = mock.Mock() - t_queue = Queue() - - replicator = self.m_tcp_replicator(t_queue) + replicator = self.m_tcp_replicator() self.assertTrue(replicator.is_alive()) # Patch _process_queue back into object and jump start it @@ -197,7 +193,7 @@ class TestTCPReplicator(base.TestCase): replicator._connected_clients.append(m_client) - t_queue.put(m_data) + replicator.put(m_data) # TODO(Corne): Find suitable primitive to synchronize async task update # with main thread. @@ -213,7 +209,7 @@ class TestTCPReplicator(base.TestCase): def test_disconnect(self,): m_client = mock.Mock() - replicator = self.m_tcp_replicator(self.m_queue) + replicator = self.m_tcp_replicator() self.assertTrue(replicator.is_alive()) replicator._connected_clients.append(m_client) diff --git a/devices/test/common/test_baselines.py b/devices/test/common/test_baselines.py new file mode 100644 index 0000000000000000000000000000000000000000..206b4ca0eccefe1012519c8236d158e52f1cdc38 --- /dev/null +++ b/devices/test/common/test_baselines.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# +# This file is part of the LOFAR 2.0 Station Software +# +# +# +# Distributed under the terms of the APACHE license. +# See LICENSE.txt for more info. + +from common import baselines + +from test import base + + +class TestBaselines(base.TestCase): + def test_nr_baselines(self): + # no baselines if no antennas + self.assertEqual(0, baselines.nr_baselines(0)) + # one antenna only has autocorrelation + self.assertEqual(1, baselines.nr_baselines(1)) + # two antennas each have autocorrelations + a baseline between each other + self.assertEqual(3, baselines.nr_baselines(2)) + + def test_baseline_indices(self): + """ Test whether baseline_from_index and baseline_index line up. """ + + for major in range(192): + for minor in range(major + 1): + idx = baselines.baseline_index(major, minor) + self.assertEqual((major, minor), baselines.baseline_from_index(idx), msg=f'baseline_index({major},{minor}) resulted in {idx}, and should match baseline_from_index({idx})') diff --git a/devices/test/devices/test_abstract_device.py b/devices/test/devices/test_abstract_device.py new file mode 100644 index 0000000000000000000000000000000000000000..f54383c9a1b85f5c9e442f51d7f04d061951f772 --- /dev/null +++ b/devices/test/devices/test_abstract_device.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- +# +# This file is part of the LOFAR 2.0 Station Software +# +# +# +# Distributed under the terms of the APACHE license. +# See LICENSE.txt for more info. + +import abc +from unittest import mock + +from tango import DevFailed +from tango import server +from tango.server import attribute + +from tango.test_context import DeviceTestContext + +from devices.abstract_device import AbstractDeviceMetas + +from test import base + + +class TestAbstractDevice(base.TestCase): + + class AbstractExample(object, metaclass=abc.ABCMeta): + """A pure abc.ABCMeta metaclass with an abstract method + + This is an abstract class that inherits object with the abc.ABCMeta as + metaclass + """ + + @abc.abstractmethod + def example_method(self): + raise NotImplementedError + + class TestHardwareDevice(server.Device, metaclass=AbstractDeviceMetas): + """This is your overarching abstract class with a combined metaclass + + Device is an object with DeviceMeta as metaclass + We use HardwareDeviceMetas as metaclass + + Our metaclass contract is now fulfilled. + """ + + @attribute(dtype=float) + def call_example_method(self): + return self.example_method() + + @abc.abstractmethod + def example_method(self): + raise NotImplementedError + + class ConcreteHardwareDevice(TestHardwareDevice): + + def example_method(self): + return 12 + + def setUp(self): + super(TestAbstractDevice, self).setUp() + + def test_instance_tango(self): + + try: + with DeviceTestContext(self.TestHardwareDevice, process=True) as proxy: + # Calling this method raises the NotImplementedError exception + proxy.call_example_method() + except Exception as e: + self.assertIsInstance(e, DevFailed) + + with DeviceTestContext(self.ConcreteHardwareDevice, process=True) as proxy: + self.assertEqual(12, proxy.call_example_method) + + @mock.patch.object(server, 'get_worker') + @mock.patch.object(server, 'LatestDeviceImpl') + def test_instance_error(self, m_worker, m_implement): + # Creating this object should raise a type error but it does not + # combining metaclasses in this way does not have the desired result. + # This is a known limitation of this approach + m_device = self.TestHardwareDevice(mock.Mock(), mock.Mock()) + + # Raising the NotImplementedError works as expected, however. + self.assertRaises(NotImplementedError, m_device.example_method) + + # Creating this object of a class that has a pure metaclass does raise + # the expected error. + self.assertRaises(TypeError, self.AbstractExample) + + diff --git a/devices/test/devices/test_statistics_collector.py b/devices/test/devices/test_statistics_collector.py new file mode 100644 index 0000000000000000000000000000000000000000..a3568b8e56452259b8754be3a76e862a20845fcb --- /dev/null +++ b/devices/test/devices/test_statistics_collector.py @@ -0,0 +1,80 @@ +from devices.sdp.statistics_collector import XSTCollector +from devices.sdp.statistics_packet import XSTPacket + +from test import base + +class TestXSTCollector(base.TestCase): + def test_valid_packet(self): + collector = XSTCollector() + + # a valid packet as obtained from SDP, with 64-bit BE 1+1j as payload + packet = b'X\x05\x00\x00\x00\x00\x00\x00\x10\x08\x00\x02\xfa\xef\x00f\x00\x00\x0c\x08\x01 \x14\x00\x00\x01!\xd9&z\x1b\xb3' + 288 * b'\x00\x00\x00\x00\x00\x00\x00\x01' + + # parse it ourselves to extract info nicely + fields = XSTPacket(packet) + fpga_index = fields.gn_index + + # this should not throw + collector.process_packet(packet) + + # counters should now be updated + self.assertEqual(1, collector.parameters["nof_packets"]) + self.assertEqual(0, collector.parameters["nof_invalid_packets"]) + + self.assertEqual(1, collector.parameters["nof_valid_payloads"][fpga_index]) + self.assertEqual(0, collector.parameters["nof_payload_errors"][fpga_index]) + + # check whether the data ended up in the right block, and the rest is still zero + xst_values = collector.xst_values() + + for baseline_a in range(collector.MAX_INPUTS): + for baseline_b in range(collector.MAX_INPUTS): + if baseline_b > baseline_a: + # only scan top-left triangle + continue + + baseline_a_was_in_packet = (fields.first_baseline[0] <= baseline_a < fields.first_baseline[0] + fields.nof_signal_inputs) + baseline_b_was_in_packet = (fields.first_baseline[1] <= baseline_b < fields.first_baseline[1] + fields.nof_signal_inputs) + + if baseline_a_was_in_packet and baseline_b_was_in_packet: + self.assertEqual(1+1j, xst_values[baseline_a][baseline_b], msg=f'element [{baseline_a}][{baseline_b}] did not end up in XST matrix.') + else: + self.assertEqual(0+0j, xst_values[baseline_a][baseline_b], msg=f'element [{baseline_a}][{baseline_b}] was not in packet, but was written to the XST matrix.') + + def test_invalid_packet(self): + collector = XSTCollector() + + # an invalid packet + packet = b'S\x05\x00\x00\x00\x00\x00\x00\x10\x08\x00\x02\xfa\xef\x00f\x00\x00\x0c\x08\x01 \x14\x00\x00\x01!\xd9&z\x1b\xb3' + 288 * b'\x00\x00\x00\x00\x00\x00\x00\x01' + + # this should throw + with self.assertRaises(ValueError): + collector.process_packet(packet) + + # counters should now be updated + self.assertEqual(1, collector.parameters["nof_packets"]) + self.assertEqual(1, collector.parameters["nof_invalid_packets"]) + + self.assertListEqual([0] * collector.MAX_FPGAS, list(collector.parameters["nof_valid_payloads"])) + self.assertListEqual([0] * collector.MAX_FPGAS, list(collector.parameters["nof_payload_errors"])) + + def test_payload_error(self): + collector = XSTCollector() + + # an valid packet with a payload error + packet = b'X\x05\x00\x00\x00\x00\x00\x00\x14\x08\x00\x02\xfa\xef\x00f\x00\x00\x0c\x08\x01 \x14\x00\x00\x01!\xd9&z\x1b\xb3' + 288 * b'\x00\x00\x00\x00\x00\x00\x00\x01' + + # parse it ourselves to extract info nicely + fields = XSTPacket(packet) + fpga_index = fields.gn_index + + # this should not throw + collector.process_packet(packet) + + # counters should now be updated + self.assertEqual(1, collector.parameters["nof_packets"]) + self.assertEqual(0, collector.parameters["nof_invalid_packets"]) + + self.assertEqual(0, collector.parameters["nof_valid_payloads"][fpga_index]) + self.assertEqual(1, collector.parameters["nof_payload_errors"][fpga_index]) + diff --git a/docker-compose/Makefile b/docker-compose/Makefile index 686f88f9e4887039cbe5206a5a88ecb8df9aed8c..b964653b1f1d0764c254b9e68c0d97ea8f3ef396 100644 --- a/docker-compose/Makefile +++ b/docker-compose/Makefile @@ -121,7 +121,8 @@ DOCKER_COMPOSE_ARGS := DISPLAY=$(DISPLAY) \ TANGO_LOFAR_CONTAINER_DIR=${TANGO_LOFAR_CONTAINER_DIR} MYSQL_HOST=$(MYSQL_HOST) \ CONTAINER_NAME_PREFIX=$(CONTAINER_NAME_PREFIX) \ COMPOSE_IGNORE_ORPHANS=true \ - CONTAINER_EXECUTION_UID=$(shell id -u) + CONTAINER_EXECUTION_UID=$(shell id -u) \ + DOCKER_GID=$(shell getent group docker | cut -d: -f 3) .PHONY: up down minimal start stop restart build build-nocache status clean pull help @@ -132,13 +133,13 @@ pull: ## pull the images from the Docker hub build: ## rebuild images # docker-compose does not support build dependencies, so manage those here - $(DOCKER_COMPOSE_ARGS) docker-compose -f lofar-device-base.yml -f networks.yml build - $(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) build $(SERVICE) + $(DOCKER_COMPOSE_ARGS) docker-compose -f lofar-device-base.yml -f networks.yml build --progress=plain + $(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) build --progress=plain $(SERVICE) build-nocache: ## rebuild images from scratch # docker-compose does not support build dependencies, so manage those here - $(DOCKER_COMPOSE_ARGS) docker-compose -f lofar-device-base.yml -f networks.yml build - $(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) build --no-cache $(SERVICE) + $(DOCKER_COMPOSE_ARGS) docker-compose -f lofar-device-base.yml -f networks.yml build --progress=plain + $(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) build --no-cache --progress=plain $(SERVICE) up: minimal ## start the base TANGO system and prepare all services $(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) up --no-start diff --git a/docker-compose/archiver.yml b/docker-compose/archiver.yml index 8006ece3b86f0013a5eedc1e066dc4c2f07b73af..5204b52c0ffd05fcee800d0f7faebc9345628a48 100644 --- a/docker-compose/archiver.yml +++ b/docker-compose/archiver.yml @@ -6,6 +6,8 @@ services: container_name: archiver-maria-db networks: - control + ports: + - "3307:3306/tcp" depends_on: - databaseds environment: diff --git a/docker-compose/device-docker.yml b/docker-compose/device-docker.yml new file mode 100644 index 0000000000000000000000000000000000000000..7ed5ecf40dd02a0a9d39941c144dc9958e2ed794 --- /dev/null +++ b/docker-compose/device-docker.yml @@ -0,0 +1,44 @@ +# +# Docker compose file that launches an interactive iTango session. +# +# Connect to the interactive session with 'docker attach itango'. +# Disconnect with the Docker deattach sequence: <CTRL>+<P> <CTRL>+<Q> +# +# Defines: +# - itango: iTango interactive session +# +# Requires: +# - lofar-device-base.yml +# +version: '2' + +services: + device-docker: + image: device-docker + # build explicitly, as docker-compose does not understand a local image + # being shared among services. + build: + context: lofar-device-base + args: + SOURCE_IMAGE: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}-tango-itango:${TANGO_ITANGO_VERSION} + container_name: ${CONTAINER_NAME_PREFIX}device-docker + networks: + - control + ports: + - "5705:5705" # unique port for this DS + volumes: + - ${TANGO_LOFAR_CONTAINER_MOUNT} + - /var/run/docker.sock:/var/run/docker.sock:rw # we want to control our sibling containers, NOT do docker-in-docker (dind) + user: ${CONTAINER_EXECUTION_UID}:${DOCKER_GID} # user that starts this container by definition has access rights to docker + environment: + - TANGO_HOST=${TANGO_HOST} + entrypoint: + - /usr/local/bin/wait-for-it.sh + - ${TANGO_HOST} + - --timeout=30 + - --strict + - -- + # configure CORBA to _listen_ on 0:port, but tell others we're _reachable_ through ${HOSTNAME}:port, since CORBA + # can't know about our Docker port forwarding + - python3 -u ${TANGO_LOFAR_CONTAINER_DIR}/devices/devices/docker_device.py LTS -v -ORBendPoint giop:tcp:0:5705 -ORBendPointPublish giop:tcp:${HOSTNAME}:5705 + restart: on-failure diff --git a/docker-compose/device-xst.yml b/docker-compose/device-xst.yml new file mode 100644 index 0000000000000000000000000000000000000000..8b0ba2d77cffe33caf4130f29ee83e1db1911f2b --- /dev/null +++ b/docker-compose/device-xst.yml @@ -0,0 +1,45 @@ +# +# Docker compose file that launches an interactive iTango session. +# +# Connect to the interactive session with 'docker attach itango'. +# Disconnect with the Docker deattach sequence: <CTRL>+<P> <CTRL>+<Q> +# +# Defines: +# - itango: iTango interactive session +# +# Requires: +# - lofar-device-base.yml +# +version: '2' + +services: + device-xst: + image: device-xst + # build explicitly, as docker-compose does not understand a local image + # being shared among services. + build: + context: lofar-device-base + args: + SOURCE_IMAGE: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}-tango-itango:${TANGO_ITANGO_VERSION} + container_name: ${CONTAINER_NAME_PREFIX}device-xst + networks: + - control + - data + ports: + - "5002:5002/udp" # port to receive XST UDP packets on + - "5102:5102/tcp" # port to emit XST TCP packets on + - "5706:5706" # unique port for this DS + volumes: + - ${TANGO_LOFAR_CONTAINER_MOUNT} + environment: + - TANGO_HOST=${TANGO_HOST} + entrypoint: + - /usr/local/bin/wait-for-it.sh + - ${TANGO_HOST} + - --timeout=30 + - --strict + - -- + # configure CORBA to _listen_ on 0:port, but tell others we're _reachable_ through ${HOSTNAME}:port, since CORBA + # can't know about our Docker port forwarding + - python3 -u ${TANGO_LOFAR_CONTAINER_DIR}/devices/devices/sdp/xst.py LTS -v -ORBendPoint giop:tcp:0:5706 -ORBendPointPublish giop:tcp:${HOSTNAME}:5706 + restart: on-failure diff --git a/docker-compose/grafana.yml b/docker-compose/grafana.yml index 1a9b3ee77aa53fcef367e1159c1b8623971ad5d7..b9060c70a53ecfb4d4027ebe1e78d9fe658050f6 100644 --- a/docker-compose/grafana.yml +++ b/docker-compose/grafana.yml @@ -6,9 +6,9 @@ # version: '2' -volumes: - grafana-data: {} - grafana-configs: {} +#volumes: +# grafana-data: {} +# grafana-configs: {} services: grafana: @@ -18,9 +18,9 @@ services: container_name: ${CONTAINER_NAME_PREFIX}grafana networks: - control - volumes: - - grafana-data:/var/lib/grafana - - grafana-configs:/etc/grafana + #volumes: + # - grafana-data:/var/lib/grafana + # - grafana-configs:/etc/grafana ports: - "3000:3000" restart: unless-stopped diff --git a/docker-compose/grafana/Dockerfile b/docker-compose/grafana/Dockerfile index 83bc4448c660717c7f36655b14e21ee3c7137425..bc766bcd3b0d71f346fd70e34fa27dd91fc27b04 100644 --- a/docker-compose/grafana/Dockerfile +++ b/docker-compose/grafana/Dockerfile @@ -1,8 +1,8 @@ FROM grafana/grafana +COPY grafana.ini /etc/grafana/ + # Add default configuration through provisioning (see https://grafana.com/docs/grafana/latest/administration/provisioning) -# -# Note: for changes to take effect, make sure you remove the grafana-data and grafana-configs docker volumes COPY datasources /etc/grafana/provisioning/datasources/ COPY dashboards /var/lib/grafana/dashboards/ COPY stationcontrol-dashboards.yaml /etc/grafana/provisioning/dashboards/ diff --git a/docker-compose/grafana/dashboards/docker.json b/docker-compose/grafana/dashboards/docker.json new file mode 100644 index 0000000000000000000000000000000000000000..0d486f65e35a9a1c4a5ca024d6a89b0a46991ff2 --- /dev/null +++ b/docker-compose/grafana/dashboards/docker.json @@ -0,0 +1,108 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "description": "", + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "id": 2, + "links": [], + "panels": [ + { + "datasource": "Prometheus", + "description": "Which Docker containers are running on the station.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "fillOpacity": 90, + "lineWidth": 0 + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "green", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 14, + "w": 19, + "x": 0, + "y": 0 + }, + "id": 2, + "options": { + "alignValue": "center", + "legend": { + "displayMode": "hidden", + "placement": "bottom" + }, + "mergeValues": true, + "rowHeight": 0.9, + "showValue": "never", + "tooltip": { + "mode": "single" + } + }, + "pluginVersion": "8.1.2", + "targets": [ + { + "exemplar": true, + "expr": "device_attribute{device=\"lts/docker/1\",name=~\".*_R\",name!=\"version_R\"}", + "instant": false, + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "title": "Docker Containers", + "transformations": [], + "type": "state-timeline" + } + ], + "schemaVersion": 30, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-30m", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Docker", + "uid": "buKx9ZHnk", + "version": 1 +} diff --git a/docker-compose/grafana/dashboards/home.json b/docker-compose/grafana/dashboards/home.json new file mode 100644 index 0000000000000000000000000000000000000000..51ed27cc87098fa85f7563d813c6807eb18a7b3d --- /dev/null +++ b/docker-compose/grafana/dashboards/home.json @@ -0,0 +1,2581 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "id": 3, + "links": [], + "panels": [ + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 15, + "panels": [], + "title": "Devices", + "type": "row" + }, + { + "datasource": "Prometheus", + "description": "Progress of station initialisation", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "yellow", + "value": null + }, + { + "color": "red", + "value": 1 + }, + { + "color": "green", + "value": 100 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 4, + "x": 0, + "y": 1 + }, + "id": 43, + "options": { + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": false, + "text": {} + }, + "pluginVersion": "8.1.2", + "targets": [ + { + "exemplar": true, + "expr": "device_attribute{device=\"lts/boot/1\",name=\"initialisation_progress_R\"}", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Station Initialisation", + "type": "gauge" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "color": "green", + "index": 1, + "text": "ON" + }, + "1": { + "color": "red", + "index": 3, + "text": "OFF" + }, + "7": { + "color": "yellow", + "index": 2, + "text": "STANDBY" + }, + "8": { + "color": "red", + "index": 0, + "text": "FAULT" + }, + "11": { + "color": "red", + "index": 4, + "text": "ALARM" + } + }, + "type": "value" + } + ], + "noValue": "???", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "string" + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 4, + "y": 1 + }, + "id": 4, + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "value_and_name" + }, + "pluginVersion": "8.1.2", + "targets": [ + { + "exemplar": true, + "expr": "device_attribute{label=\"State\",device=~\"lts/.*/1\"}", + "instant": false, + "interval": "", + "legendFormat": "{{device}}", + "refId": "A" + } + ], + "title": "Device States", + "type": "stat" + }, + { + "datasource": "ELK logs", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 10, + "y": 1 + }, + "id": 32, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom" + }, + "tooltip": { + "mode": "single" + } + }, + "targets": [ + { + "alias": "", + "bucketAggs": [ + { + "field": "extra.tango_device.keyword", + "id": "2", + "settings": { + "min_doc_count": "0", + "order": "desc", + "orderBy": "_term", + "size": "10" + }, + "type": "terms" + }, + { + "field": "@timestamp", + "id": "3", + "settings": { + "interval": "auto", + "min_doc_count": "0", + "trimEdges": "0" + }, + "type": "date_histogram" + } + ], + "metrics": [ + { + "id": "1", + "type": "count" + } + ], + "query": "level:(ERROR or FATAL)", + "refId": "A", + "timeField": "@timestamp" + } + ], + "title": "Errors", + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "displayMode": "auto" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "green", + "value": 100 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 4, + "x": 0, + "y": 7 + }, + "id": 44, + "options": { + "showHeader": false + }, + "pluginVersion": "8.1.2", + "targets": [ + { + "exemplar": true, + "expr": "device_attribute{device=\"lts/boot/1\",name=\"initialisation_status_R\"}", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Initialisation status", + "transformations": [ + { + "id": "labelsToFields", + "options": {} + }, + { + "id": "organize", + "options": { + "excludeByName": { + "Time": true, + "Value": true, + "device": true, + "device_attribute{device=\"lts/boot/1\", dim_x=\"1\", dim_y=\"0\", instance=\"tango-prometheus-exporter:8000\", job=\"tango\", label=\"initialisation_status_R\", name=\"initialisation_status_R\", str_value=\"Initialisation completed\", type=\"string\", x=\"0\", y=\"0\"}": true, + "dim_x": true, + "dim_y": true, + "instance": true, + "job": true, + "label": true, + "name": true, + "type": true, + "x": true, + "y": true + }, + "indexByName": { + "Time": 0, + "Value": 5, + "device": 1, + "dim_x": 2, + "dim_y": 3, + "instance": 4, + "job": 6, + "label": 7, + "name": 8, + "str_value": 9, + "type": 10, + "x": 11, + "y": 12 + }, + "renameByName": { + "name": "", + "str_value": "status" + } + } + } + ], + "type": "table" + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 10 + }, + "id": 17, + "panels": [], + "title": "RECV", + "type": "row" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "celsius" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 5, + "x": 0, + "y": 11 + }, + "id": 22, + "options": { + "legend": { + "calcs": [], + "displayMode": "hidden", + "placement": "bottom" + }, + "tooltip": { + "mode": "single" + } + }, + "pluginVersion": "8.1.2", + "targets": [ + { + "exemplar": true, + "expr": "device_attribute{device=\"lts/recv/1\",name=\"RCU_temperature_R\"} - 273.15", + "format": "time_series", + "hide": false, + "instant": false, + "interval": "", + "legendFormat": "{{x}}", + "refId": "A" + } + ], + "title": "RCU temperatures", + "transformations": [], + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "transparent", + "value": null + }, + { + "color": "red", + "value": 0 + }, + { + "color": "green", + "value": 3 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 5, + "y": 11 + }, + "id": 21, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "name" + }, + "pluginVersion": "8.1.2", + "targets": [ + { + "exemplar": true, + "expr": "sum by (x)(1 + (device_attribute{device=\"lts/recv/1\",name=\"RCU_ADC_lock_R\"} == bool 129)) * on(x) device_attribute{device=\"lts/recv/1\",name=\"RCU_mask_RW\"} - 3", + "interval": "", + "legendFormat": "{{y}}", + "refId": "A" + } + ], + "title": "RCU ADC lock", + "type": "stat" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "transparent", + "value": null + }, + { + "color": "red", + "value": 1 + }, + { + "color": "green", + "value": 2 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 11, + "y": 11 + }, + "id": 25, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "name" + }, + "pluginVersion": "8.1.2", + "targets": [ + { + "exemplar": true, + "expr": "(2 - device_attribute{device=\"lts/recv/1\",name=\"RCU_I2C_STATUS_R\"}) * on(x) device_attribute{device=\"lts/recv/1\",name=\"RCU_mask_RW\"}", + "interval": "", + "legendFormat": "{{y}}", + "refId": "A" + } + ], + "title": "RCU I2C status", + "type": "stat" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 3, + "x": 17, + "y": 11 + }, + "id": 24, + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "name" + }, + "pluginVersion": "8.1.2", + "targets": [ + { + "exemplar": true, + "expr": "1-device_attribute{device=\"lts/recv/1\",name=\"CLK_Enable_PWR_R\"}", + "interval": "", + "legendFormat": "Power", + "refId": "A" + }, + { + "exemplar": true, + "expr": "device_attribute{device=\"lts/recv/1\",name=\"CLK_I2C_STATUS_R\"}", + "hide": false, + "interval": "", + "legendFormat": "I2C", + "refId": "B" + }, + { + "exemplar": true, + "expr": "device_attribute{device=\"lts/recv/1\",name=\"CLK_PLL_error_R\"}", + "hide": false, + "interval": "", + "legendFormat": "PLL", + "refId": "C" + }, + { + "exemplar": true, + "expr": "1-device_attribute{device=\"lts/recv/1\",name=\"CLK_PLL_locked_R\"}", + "hide": false, + "interval": "", + "legendFormat": "PLL Lock", + "refId": "D" + } + ], + "title": "Clock", + "type": "stat" + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 19 + }, + "id": 46, + "panels": [], + "title": "Uniboard 2", + "type": "row" + }, + { + "datasource": "Prometheus", + "description": "Temperature sensors of each node on each board", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic", + "seriesBy": "max" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "line" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 85 + } + ] + }, + "unit": "celsius" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 5, + "x": 0, + "y": 20 + }, + "id": 48, + "options": { + "legend": { + "calcs": [], + "displayMode": "hidden", + "placement": "bottom" + }, + "tooltip": { + "mode": "single" + } + }, + "targets": [ + { + "exemplar": true, + "expr": "device_attribute{device=\"lts/unb2/1\",name=\"UNB2_FPGA_POL_CORE_TEMP_R\"}", + "interval": "", + "legendFormat": "Core board {{x}} node {{y}}", + "refId": "A" + }, + { + "exemplar": true, + "expr": "device_attribute{device=\"lts/unb2/1\",name=\"UNB2_FPGA_POL_ERAM_TEMP_R\"}", + "hide": false, + "interval": "", + "legendFormat": "ERAM board {{x}} node {{y}}", + "refId": "B" + }, + { + "exemplar": true, + "expr": "device_attribute{device=\"lts/unb2/1\",name=\"UNB2_FPGA_POL_RXGXB_TEMP_R\"}", + "hide": false, + "interval": "", + "legendFormat": "TrRx board {{x}} node {{y}}", + "refId": "C" + }, + { + "exemplar": true, + "expr": "device_attribute{device=\"lts/unb2/1\",name=\"UNB2_FPGA_POL_HXGB_TEMP_R\"}", + "hide": false, + "interval": "", + "legendFormat": "TrHx board {{x}} node {{y}}", + "refId": "D" + }, + { + "exemplar": true, + "expr": "device_attribute{device=\"lts/unb2/1\",name=\"UNB2_FPGA_POL_PGM_TEMP_R\"}", + "hide": false, + "interval": "", + "legendFormat": "IO board {{x}} node {{y}}", + "refId": "E" + }, + { + "hide": false, + "refId": "F" + } + ], + "title": "Uniboard2 Node Temperatures", + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "description": "Temperature sensors of the power supply on each board", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic", + "seriesBy": "max" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "line" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 85 + } + ] + }, + "unit": "celsius" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 5, + "x": 5, + "y": 20 + }, + "id": 50, + "options": { + "legend": { + "calcs": [], + "displayMode": "hidden", + "placement": "bottom" + }, + "tooltip": { + "mode": "single" + } + }, + "targets": [ + { + "exemplar": true, + "expr": "device_attribute{device=\"lts/unb2/1\",name=\"UNB2_POL_QSFP_N01_TEMP_R\"}", + "interval": "", + "legendFormat": "QSFP N01 board {{x}} ", + "refId": "A" + }, + { + "exemplar": true, + "expr": "device_attribute{device=\"lts/unb2/1\",name=\"UNB2_POL_QSFP_N23_TEMP_R\"}", + "hide": false, + "interval": "", + "legendFormat": "QSFP N23 board {{x}}", + "refId": "B" + }, + { + "exemplar": true, + "expr": "device_attribute{device=\"lts/unb2/1\",name=\"UNB2_POL_SWITCH_1V2_TEMP_R\"}", + "hide": false, + "interval": "", + "legendFormat": "Switch 1v2 board {{x}}", + "refId": "C" + }, + { + "exemplar": true, + "expr": "device_attribute{device=\"lts/unb2/1\",name=\"UNB2_POL_SWITCH_PHY_TEMP_R\"}", + "hide": false, + "interval": "", + "legendFormat": "Switch PHY board {{x}}", + "refId": "D" + }, + { + "exemplar": true, + "expr": "device_attribute{device=\"lts/unb2/1\",name=\"UNB2_POL_CLOCK_TEMP_R\"}", + "hide": false, + "interval": "", + "legendFormat": "Clock PWR board {{x}}", + "refId": "E" + }, + { + "exemplar": true, + "expr": "device_attribute{device=\"lts/unb2/1\",name=\"UNB2_DC_DC_48V_12V_TEMP_R\"}", + "hide": false, + "interval": "", + "legendFormat": "DC-DC board {{x}}", + "refId": "F" + } + ], + "title": "Uniboard2 Power Supply Temperatures", + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "description": "Voltage sensors of each node on each board", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMax": 2, + "axisSoftMin": 0, + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "line" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 85 + } + ] + }, + "unit": "volt" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 5, + "x": 10, + "y": 20 + }, + "id": 49, + "options": { + "legend": { + "calcs": [], + "displayMode": "hidden", + "placement": "bottom" + }, + "tooltip": { + "mode": "single" + } + }, + "targets": [ + { + "exemplar": true, + "expr": "device_attribute{device=\"lts/unb2/1\",name=\"UNB2_FPGA_POL_CORE_VOUT_R\"}", + "interval": "", + "legendFormat": "Core board {{x}} node {{y}}", + "refId": "A" + }, + { + "exemplar": true, + "expr": "device_attribute{device=\"lts/unb2/1\",name=\"UNB2_FPGA_POL_ERAM_VOUT_R\"}", + "hide": false, + "interval": "", + "legendFormat": "ERAM board {{x}} node {{y}}", + "refId": "B" + }, + { + "exemplar": true, + "expr": "device_attribute{device=\"lts/unb2/1\",name=\"UNB2_FPGA_POL_RXGXB_VOUT_R\"}", + "hide": false, + "interval": "", + "legendFormat": "TrRx board {{x}} node {{y}}", + "refId": "C" + }, + { + "exemplar": true, + "expr": "device_attribute{device=\"lts/unb2/1\",name=\"UNB2_FPGA_POL_HXGB_VOUT_R\"}", + "hide": false, + "interval": "", + "legendFormat": "TrHx board {{x}} node {{y}}", + "refId": "D" + }, + { + "exemplar": true, + "expr": "device_attribute{device=\"lts/unb2/1\",name=\"UNB2_FPGA_POL_PGM_VOUT_R\"}", + "hide": false, + "interval": "", + "legendFormat": "IO board {{x}} node {{y}}", + "refId": "E" + } + ], + "title": "Uniboard2 Voltages", + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "description": "Voltage sensors of the power supply on each board", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic", + "seriesBy": "max" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMin": 0, + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "line" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 85 + } + ] + }, + "unit": "volt" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 5, + "x": 15, + "y": 20 + }, + "id": 51, + "options": { + "legend": { + "calcs": [], + "displayMode": "hidden", + "placement": "bottom" + }, + "tooltip": { + "mode": "single" + } + }, + "targets": [ + { + "exemplar": true, + "expr": "device_attribute{device=\"lts/unb2/1\",name=\"UNB2_POL_QSFP_N01_VOUT_R\"}", + "interval": "", + "legendFormat": "QSFP N01 board {{x}} ", + "refId": "A" + }, + { + "exemplar": true, + "expr": "device_attribute{device=\"lts/unb2/1\",name=\"UNB2_POL_QSFP_N23_VOUT_R\"}", + "hide": false, + "interval": "", + "legendFormat": "QSFP N23 board {{x}}", + "refId": "B" + }, + { + "exemplar": true, + "expr": "device_attribute{device=\"lts/unb2/1\",name=\"UNB2_POL_SWITCH_1V2_VOUT_R\"}", + "hide": false, + "interval": "", + "legendFormat": "Switch 1v2 board {{x}}", + "refId": "C" + }, + { + "exemplar": true, + "expr": "device_attribute{device=\"lts/unb2/1\",name=\"UNB2_POL_SWITCH_PHY_VOUT_R\"}", + "hide": false, + "interval": "", + "legendFormat": "Switch PHY board {{x}}", + "refId": "D" + }, + { + "exemplar": true, + "expr": "device_attribute{device=\"lts/unb2/1\",name=\"UNB2_POL_CLOCK_VOUT_R\"}", + "hide": false, + "interval": "", + "legendFormat": "Clock PWR board {{x}}", + "refId": "E" + }, + { + "exemplar": true, + "expr": "device_attribute{device=\"lts/unb2/1\",name=\"UNB2_DC_DC_48V_12V_VOUT_R\"}", + "hide": false, + "interval": "", + "legendFormat": "DC-DC board {{x}}", + "refId": "F" + } + ], + "title": "Uniboard2 Power Supply Voltages", + "type": "timeseries" + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 28 + }, + "id": 19, + "panels": [], + "title": "SDP", + "type": "row" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "celsius" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 5, + "x": 0, + "y": 29 + }, + "id": 5, + "options": { + "legend": { + "calcs": [], + "displayMode": "hidden", + "placement": "bottom" + }, + "tooltip": { + "mode": "single" + } + }, + "pluginVersion": "8.1.2", + "targets": [ + { + "exemplar": true, + "expr": "device_attribute{device=\"lts/sdp/1\",name=\"FPGA_temp_R\"}", + "format": "time_series", + "hide": false, + "instant": false, + "interval": "", + "legendFormat": "{{x}}", + "refId": "A" + } + ], + "title": "FPGA temperatures", + "transformations": [], + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "transparent", + "value": null + }, + { + "color": "green", + "value": 50 + }, + { + "color": "red", + "value": 100 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 5, + "x": 5, + "y": 29 + }, + "id": 11, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "name" + }, + "pluginVersion": "8.1.2", + "targets": [ + { + "exemplar": true, + "expr": "(50+50*device_attribute{device=\"lts/sdp/1\",name=\"TR_fpga_communication_error_R\"}) * on(x) device_attribute{device=\"lts/sdp/1\",name=\"TR_fpga_mask_R\"}", + "format": "time_series", + "hide": false, + "instant": false, + "interval": "", + "legendFormat": "{{x}}", + "refId": "A" + } + ], + "title": "FPGA communication", + "transformations": [], + "type": "stat" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "transparent", + "value": null + }, + { + "color": "green", + "value": 50 + }, + { + "color": "red", + "value": 100 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 5, + "x": 10, + "y": 29 + }, + "id": 9, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "name" + }, + "pluginVersion": "8.1.2", + "targets": [ + { + "exemplar": true, + "expr": "(100-50*device_attribute{device=\"lts/sdp/1\",name=\"FPGA_processing_enable_R\"}) * on(x) device_attribute{device=\"lts/sdp/1\",name=\"TR_fpga_mask_R\"}", + "format": "time_series", + "hide": false, + "instant": false, + "interval": "", + "legendFormat": "{{x}}", + "refId": "A" + } + ], + "title": "FPGA processing enabled", + "transformations": [], + "type": "stat" + }, + { + "datasource": "Prometheus", + "description": "Measured difference between PTP and PPS", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": 60000, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "green", + "value": 0.001 + }, + { + "color": "red", + "value": 0.1 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 5, + "x": 15, + "y": 29 + }, + "id": 13, + "options": { + "legend": { + "calcs": [], + "displayMode": "hidden", + "placement": "bottom" + }, + "tooltip": { + "mode": "single" + } + }, + "pluginVersion": "8.1.2", + "targets": [ + { + "exemplar": true, + "expr": "device_attribute{device=\"lts/sdp/1\",name=\"TR_tod_pps_delta_R\"}", + "format": "time_series", + "hide": false, + "instant": false, + "interval": "", + "legendFormat": "{{x}}", + "refId": "A" + } + ], + "title": "FPGA Clock offset", + "transformations": [], + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "description": "Number of inputs that are fed from the SDP wave-form generator", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "index": 0, + "text": "OFF" + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 20, + "y": 29 + }, + "id": 12, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "value" + }, + "pluginVersion": "8.1.2", + "targets": [ + { + "exemplar": true, + "expr": "sum(sum by(x) (device_attribute{device=\"lts/sdp/1\",name=\"FPGA_wg_enable_RW\"}) * on(x) device_attribute{device=\"lts/sdp/1\",name=\"TR_fpga_mask_R\"})", + "format": "time_series", + "hide": false, + "instant": false, + "interval": "", + "legendFormat": "{{x}}", + "refId": "A" + } + ], + "title": "Waveform generator", + "transformations": [], + "type": "stat" + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 37 + }, + "id": 27, + "panels": [], + "title": "SST", + "type": "row" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "transparent", + "value": null + }, + { + "color": "green", + "value": 50 + }, + { + "color": "red", + "value": 100 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 5, + "x": 0, + "y": 38 + }, + "id": 28, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "name" + }, + "pluginVersion": "8.1.2", + "targets": [ + { + "exemplar": true, + "expr": "(100-50*device_attribute{device=\"lts/sst/1\",name=\"FPGA_sst_offload_enable_R\"}) * on(x) device_attribute{device=\"lts/sdp/1\",name=\"TR_fpga_mask_R\"}", + "format": "time_series", + "hide": false, + "instant": false, + "interval": "", + "legendFormat": "{{x}}", + "refId": "A" + } + ], + "title": "SST offloading enabled", + "transformations": [], + "type": "stat" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMin": 0, + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "transparent", + "value": null + }, + { + "color": "green", + "value": 50 + }, + { + "color": "red", + "value": 100 + } + ] + }, + "unit": "pps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 5, + "x": 5, + "y": 38 + }, + "id": 29, + "options": { + "legend": { + "calcs": [], + "displayMode": "hidden", + "placement": "bottom" + }, + "tooltip": { + "mode": "single" + } + }, + "pluginVersion": "8.1.2", + "targets": [ + { + "exemplar": true, + "expr": "rate(device_attribute{device=\"lts/sst/1\",name=\"nof_invalid_packets_R\"}[1m])", + "format": "time_series", + "hide": false, + "instant": false, + "interval": "", + "legendFormat": "invalid", + "refId": "A" + }, + { + "exemplar": true, + "expr": "rate(device_attribute{device=\"lts/sst/1\",name=\"nof_packets_dropped_R\"}[1m])", + "hide": false, + "interval": "", + "legendFormat": "dropped", + "refId": "B" + }, + { + "exemplar": true, + "expr": "rate(device_attribute{device=\"lts/sst/1\",name=\"nof_payload_errors_R\"}[1m])", + "hide": false, + "interval": "", + "legendFormat": "payload errors {{x}}", + "refId": "C" + } + ], + "title": "SST packet errors", + "transformations": [], + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMin": 0, + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "transparent", + "value": null + }, + { + "color": "green", + "value": 50 + }, + { + "color": "red", + "value": 100 + } + ] + }, + "unit": "binBps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 5, + "x": 10, + "y": 38 + }, + "id": 30, + "options": { + "legend": { + "calcs": [], + "displayMode": "hidden", + "placement": "bottom" + }, + "tooltip": { + "mode": "single" + } + }, + "pluginVersion": "8.1.2", + "targets": [ + { + "exemplar": true, + "expr": "sum(rate(device_attribute{device=\"lts/sst/1\",name=\"nof_bytes_received_R\"}[1m]))", + "format": "time_series", + "hide": false, + "instant": false, + "interval": "", + "legendFormat": "{{x}}", + "refId": "A" + } + ], + "title": "SST bytes received", + "transformations": [], + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "description": "Rate of SSTs replicated to connected clients.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMin": 0, + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "transparent", + "value": null + }, + { + "color": "green", + "value": 50 + }, + { + "color": "red", + "value": 100 + } + ] + }, + "unit": "binBps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 5, + "x": 15, + "y": 38 + }, + "id": 33, + "options": { + "legend": { + "calcs": [], + "displayMode": "hidden", + "placement": "bottom" + }, + "tooltip": { + "mode": "single" + } + }, + "pluginVersion": "8.1.2", + "targets": [ + { + "exemplar": true, + "expr": "rate(device_attribute{device=\"lts/sst/1\",name=\"replicator_nof_bytes_sent_R\"}[1m])", + "format": "time_series", + "hide": false, + "instant": false, + "interval": "", + "legendFormat": "{{x}}", + "refId": "A" + } + ], + "title": "SST bytes sent", + "transformations": [], + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "description": "Load of TCPReplicator class, which sends statistics packets to connected clients.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMax": 5, + "axisSoftMin": 0, + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "transparent", + "value": null + }, + { + "color": "green", + "value": 50 + }, + { + "color": "red", + "value": 100 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 3, + "x": 20, + "y": 38 + }, + "id": 34, + "options": { + "legend": { + "calcs": [], + "displayMode": "hidden", + "placement": "bottom" + }, + "tooltip": { + "mode": "single" + } + }, + "pluginVersion": "8.1.2", + "targets": [ + { + "exemplar": true, + "expr": "device_attribute{device=\"lts/sst/1\",name=\"replicator_nof_tasks_pending_R\"}", + "format": "time_series", + "hide": false, + "instant": false, + "interval": "", + "legendFormat": "{{x}}", + "refId": "A" + } + ], + "title": "SST Replicator load", + "transformations": [], + "type": "timeseries" + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 46 + }, + "id": 36, + "panels": [], + "title": "XST", + "type": "row" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "transparent", + "value": null + }, + { + "color": "green", + "value": 50 + }, + { + "color": "red", + "value": 100 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 5, + "x": 0, + "y": 47 + }, + "id": 37, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "name" + }, + "pluginVersion": "8.1.2", + "targets": [ + { + "exemplar": true, + "expr": "(100-50*device_attribute{device=\"lts/xst/1\",name=\"FPGA_xst_offload_enable_R\"}) * on(x) device_attribute{device=\"lts/sdp/1\",name=\"TR_fpga_mask_R\"}", + "format": "time_series", + "hide": false, + "instant": false, + "interval": "", + "legendFormat": "{{x}}", + "refId": "A" + } + ], + "title": "XST offloading enabled", + "transformations": [], + "type": "stat" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMin": 0, + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "transparent", + "value": null + }, + { + "color": "green", + "value": 50 + }, + { + "color": "red", + "value": 100 + } + ] + }, + "unit": "pps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 5, + "x": 5, + "y": 47 + }, + "id": 38, + "options": { + "legend": { + "calcs": [], + "displayMode": "hidden", + "placement": "bottom" + }, + "tooltip": { + "mode": "single" + } + }, + "pluginVersion": "8.1.2", + "targets": [ + { + "exemplar": true, + "expr": "rate(device_attribute{device=\"lts/xst/1\",name=\"nof_invalid_packets_R\"}[1m])", + "format": "time_series", + "hide": false, + "instant": false, + "interval": "", + "legendFormat": "invalid", + "refId": "A" + }, + { + "exemplar": true, + "expr": "rate(device_attribute{device=\"lts/xst/1\",name=\"nof_packets_dropped_R\"}[1m])", + "hide": false, + "interval": "", + "legendFormat": "dropped", + "refId": "B" + }, + { + "exemplar": true, + "expr": "rate(device_attribute{device=\"lts/xst/1\",name=\"nof_payload_errors_R\"}[1m])", + "hide": false, + "interval": "", + "legendFormat": "payload errors {{x}}", + "refId": "C" + } + ], + "title": "XST packet errors", + "transformations": [], + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMin": 0, + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "transparent", + "value": null + }, + { + "color": "green", + "value": 50 + }, + { + "color": "red", + "value": 100 + } + ] + }, + "unit": "binBps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 5, + "x": 10, + "y": 47 + }, + "id": 39, + "options": { + "legend": { + "calcs": [], + "displayMode": "hidden", + "placement": "bottom" + }, + "tooltip": { + "mode": "single" + } + }, + "pluginVersion": "8.1.2", + "targets": [ + { + "exemplar": true, + "expr": "sum(rate(device_attribute{device=\"lts/xst/1\",name=\"nof_bytes_received_R\"}[1m]))", + "format": "time_series", + "hide": false, + "instant": false, + "interval": "", + "legendFormat": "{{x}}", + "refId": "A" + } + ], + "title": "XST bytes received", + "transformations": [], + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "description": "Rate of XSTs replicated to connected clients.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMin": 0, + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "transparent", + "value": null + }, + { + "color": "green", + "value": 50 + }, + { + "color": "red", + "value": 100 + } + ] + }, + "unit": "binBps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 5, + "x": 15, + "y": 47 + }, + "id": 40, + "options": { + "legend": { + "calcs": [], + "displayMode": "hidden", + "placement": "bottom" + }, + "tooltip": { + "mode": "single" + } + }, + "pluginVersion": "8.1.2", + "targets": [ + { + "exemplar": true, + "expr": "rate(device_attribute{device=\"lts/xst/1\",name=\"replicator_nof_bytes_sent_R\"}[1m])", + "format": "time_series", + "hide": false, + "instant": false, + "interval": "", + "legendFormat": "{{x}}", + "refId": "A" + } + ], + "title": "XST bytes sent", + "transformations": [], + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "description": "Load of TCPReplicator class, which sends statistics packets to connected clients.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMax": 5, + "axisSoftMin": 0, + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "transparent", + "value": null + }, + { + "color": "green", + "value": 50 + }, + { + "color": "red", + "value": 100 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 3, + "x": 20, + "y": 47 + }, + "id": 41, + "options": { + "legend": { + "calcs": [], + "displayMode": "hidden", + "placement": "bottom" + }, + "tooltip": { + "mode": "single" + } + }, + "pluginVersion": "8.1.2", + "targets": [ + { + "exemplar": true, + "expr": "device_attribute{device=\"lts/xst/1\",name=\"replicator_nof_tasks_pending_R\"}", + "format": "time_series", + "hide": false, + "instant": false, + "interval": "", + "legendFormat": "{{x}}", + "refId": "A" + } + ], + "title": "XST Replicator load", + "transformations": [], + "type": "timeseries" + } + ], + "refresh": false, + "schemaVersion": 30, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-30m", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "LOFAR2.0 Station", + "uid": "6f7Pv8Vnz", + "version": 1 +} diff --git a/docker-compose/grafana/dashboards/lofar2.0-station.json b/docker-compose/grafana/dashboards/lofar2.0-station.json deleted file mode 100644 index 83d9ab3d5f213acbae116e43fa34486a00fb71ae..0000000000000000000000000000000000000000 --- a/docker-compose/grafana/dashboards/lofar2.0-station.json +++ /dev/null @@ -1,1258 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": "-- Grafana --", - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "target": { - "limit": 100, - "matchAny": false, - "tags": [], - "type": "dashboard" - }, - "type": "dashboard" - } - ] - }, - "editable": true, - "gnetId": null, - "graphTooltip": 0, - "links": [], - "panels": [ - { - "collapsed": false, - "datasource": null, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 15, - "panels": [], - "title": "Devices", - "type": "row" - }, - { - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [ - { - "options": { - "0": { - "color": "green", - "index": 1, - "text": "ON" - }, - "1": { - "color": "red", - "index": 3, - "text": "OFF" - }, - "7": { - "color": "yellow", - "index": 2, - "text": "STANDBY" - }, - "8": { - "color": "red", - "index": 0, - "text": "FAULT" - }, - "11": { - "color": "red", - "index": 4, - "text": "ALARM" - } - }, - "type": "value" - } - ], - "noValue": "???", - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "string" - }, - "overrides": [] - }, - "gridPos": { - "h": 9, - "w": 12, - "x": 0, - "y": 1 - }, - "id": 4, - "options": { - "colorMode": "background", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "value_and_name" - }, - "pluginVersion": "8.1.2", - "targets": [ - { - "exemplar": true, - "expr": "device_attribute{label=\"State\"}", - "instant": false, - "interval": "", - "legendFormat": "{{device}}", - "refId": "A" - } - ], - "title": "Device States", - "type": "stat" - }, - { - "datasource": "ELK logs", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 1 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 9, - "w": 12, - "x": 12, - "y": 1 - }, - "id": 32, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom" - }, - "tooltip": { - "mode": "single" - } - }, - "targets": [ - { - "alias": "", - "bucketAggs": [ - { - "field": "extra.tango_device.keyword", - "id": "2", - "settings": { - "min_doc_count": "0", - "order": "desc", - "orderBy": "_term", - "size": "10" - }, - "type": "terms" - }, - { - "field": "@timestamp", - "id": "3", - "settings": { - "interval": "auto", - "min_doc_count": "0", - "trimEdges": "0" - }, - "type": "date_histogram" - } - ], - "metrics": [ - { - "id": "1", - "type": "count" - } - ], - "query": "level:(ERROR or FATAL)", - "refId": "A", - "timeField": "@timestamp" - } - ], - "title": "Errors", - "type": "timeseries" - }, - { - "collapsed": false, - "datasource": null, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 10 - }, - "id": 17, - "panels": [], - "title": "RECV", - "type": "row" - }, - { - "datasource": "Prometheus", - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "celsius" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 5, - "x": 0, - "y": 11 - }, - "id": 22, - "options": { - "legend": { - "calcs": [], - "displayMode": "hidden", - "placement": "bottom" - }, - "tooltip": { - "mode": "single" - } - }, - "pluginVersion": "8.1.2", - "targets": [ - { - "exemplar": true, - "expr": "device_attribute{device=\"lts/recv/1\",name=\"RCU_temperature_R\"} - 273.15", - "format": "time_series", - "hide": false, - "instant": false, - "interval": "", - "legendFormat": "{{x}}", - "refId": "A" - } - ], - "title": "RCU temperatures", - "transformations": [], - "type": "timeseries" - }, - { - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "transparent", - "value": null - }, - { - "color": "red", - "value": 0 - }, - { - "color": "green", - "value": 3 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 6, - "x": 5, - "y": 11 - }, - "id": 21, - "options": { - "colorMode": "background", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "name" - }, - "pluginVersion": "8.1.2", - "targets": [ - { - "exemplar": true, - "expr": "sum by (x)(1 + (device_attribute{device=\"lts/recv/1\",name=\"RCU_ADC_lock_R\"} == bool 129)) * on(x) device_attribute{device=\"lts/recv/1\",name=\"RCU_mask_RW\"} - 3", - "interval": "", - "legendFormat": "{{y}}", - "refId": "A" - } - ], - "title": "RCU ADC lock", - "type": "stat" - }, - { - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "transparent", - "value": null - }, - { - "color": "red", - "value": 1 - }, - { - "color": "green", - "value": 2 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 6, - "x": 11, - "y": 11 - }, - "id": 25, - "options": { - "colorMode": "background", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "name" - }, - "pluginVersion": "8.1.2", - "targets": [ - { - "exemplar": true, - "expr": "(2 - device_attribute{device=\"lts/recv/1\",name=\"RCU_I2C_STATUS_R\"}) * on(x) device_attribute{device=\"lts/recv/1\",name=\"RCU_mask_RW\"}", - "interval": "", - "legendFormat": "{{y}}", - "refId": "A" - } - ], - "title": "RCU I2C status", - "type": "stat" - }, - { - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 1 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 3, - "x": 17, - "y": 11 - }, - "id": 24, - "options": { - "colorMode": "background", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "name" - }, - "pluginVersion": "8.1.2", - "targets": [ - { - "exemplar": true, - "expr": "1-device_attribute{device=\"lts/recv/1\",name=\"CLK_Enable_PWR_R\"}", - "interval": "", - "legendFormat": "Power", - "refId": "A" - }, - { - "exemplar": true, - "expr": "device_attribute{device=\"lts/recv/1\",name=\"CLK_I2C_STATUS_R\"}", - "hide": false, - "interval": "", - "legendFormat": "I2C", - "refId": "B" - }, - { - "exemplar": true, - "expr": "device_attribute{device=\"lts/recv/1\",name=\"CLK_PLL_error_R\"}", - "hide": false, - "interval": "", - "legendFormat": "PLL", - "refId": "C" - }, - { - "exemplar": true, - "expr": "1-device_attribute{device=\"lts/recv/1\",name=\"CLK_PLL_locked_R\"}", - "hide": false, - "interval": "", - "legendFormat": "PLL Lock", - "refId": "D" - } - ], - "title": "Clock", - "type": "stat" - }, - { - "collapsed": false, - "datasource": null, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 19 - }, - "id": 19, - "panels": [], - "title": "SDP", - "type": "row" - }, - { - "datasource": "Prometheus", - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "celsius" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 5, - "x": 0, - "y": 20 - }, - "id": 5, - "options": { - "legend": { - "calcs": [], - "displayMode": "hidden", - "placement": "bottom" - }, - "tooltip": { - "mode": "single" - } - }, - "pluginVersion": "8.1.2", - "targets": [ - { - "exemplar": true, - "expr": "device_attribute{device=\"lts/sdp/1\",name=\"FPGA_temp_R\"}", - "format": "time_series", - "hide": false, - "instant": false, - "interval": "", - "legendFormat": "{{x}}", - "refId": "A" - } - ], - "title": "FPGA temperatures", - "transformations": [], - "type": "timeseries" - }, - { - "datasource": "Prometheus", - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "transparent", - "value": null - }, - { - "color": "green", - "value": 50 - }, - { - "color": "red", - "value": 100 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 5, - "x": 5, - "y": 20 - }, - "id": 11, - "options": { - "colorMode": "background", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "name" - }, - "pluginVersion": "8.1.2", - "targets": [ - { - "exemplar": true, - "expr": "(50+50*device_attribute{device=\"lts/sdp/1\",name=\"TR_fpga_communication_error_R\"}) * on(x) device_attribute{device=\"lts/sdp/1\",name=\"TR_fpga_mask_R\"}", - "format": "time_series", - "hide": false, - "instant": false, - "interval": "", - "legendFormat": "{{x}}", - "refId": "A" - } - ], - "title": "FPGA communication", - "transformations": [], - "type": "stat" - }, - { - "datasource": "Prometheus", - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "transparent", - "value": null - }, - { - "color": "green", - "value": 50 - }, - { - "color": "red", - "value": 100 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 5, - "x": 10, - "y": 20 - }, - "id": 9, - "options": { - "colorMode": "background", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "name" - }, - "pluginVersion": "8.1.2", - "targets": [ - { - "exemplar": true, - "expr": "(100-50*device_attribute{device=\"lts/sdp/1\",name=\"FPGA_processing_enable_R\"}) * on(x) device_attribute{device=\"lts/sdp/1\",name=\"TR_fpga_mask_R\"}", - "format": "time_series", - "hide": false, - "instant": false, - "interval": "", - "legendFormat": "{{x}}", - "refId": "A" - } - ], - "title": "FPGA processing enabled", - "transformations": [], - "type": "stat" - }, - { - "datasource": "Prometheus", - "description": "Measured difference between PTP and PPS", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": 60000, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "decimals": 2, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "red", - "value": null - }, - { - "color": "green", - "value": 0.001 - }, - { - "color": "red", - "value": 0.1 - } - ] - }, - "unit": "s" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 5, - "x": 15, - "y": 20 - }, - "id": 13, - "options": { - "legend": { - "calcs": [], - "displayMode": "hidden", - "placement": "bottom" - }, - "tooltip": { - "mode": "single" - } - }, - "pluginVersion": "8.1.2", - "targets": [ - { - "exemplar": true, - "expr": "device_attribute{device=\"lts/sdp/1\",name=\"TR_tod_pps_delta_R\"}", - "format": "time_series", - "hide": false, - "instant": false, - "interval": "", - "legendFormat": "{{x}}", - "refId": "A" - } - ], - "title": "FPGA Clock offset", - "transformations": [], - "type": "timeseries" - }, - { - "datasource": "Prometheus", - "description": "Number of inputs that are fed from the SDP wave-form generator", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [ - { - "options": { - "0": { - "index": 0, - "text": "OFF" - } - }, - "type": "value" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 1 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 3, - "x": 20, - "y": 20 - }, - "id": 12, - "options": { - "colorMode": "background", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "value" - }, - "pluginVersion": "8.1.2", - "targets": [ - { - "exemplar": true, - "expr": "sum(sum by(x) (device_attribute{device=\"lts/sdp/1\",name=\"FPGA_wg_enable_RW\"}) * on(x) device_attribute{device=\"lts/sdp/1\",name=\"TR_fpga_mask_R\"})", - "format": "time_series", - "hide": false, - "instant": false, - "interval": "", - "legendFormat": "{{x}}", - "refId": "A" - } - ], - "title": "Waveform generator", - "transformations": [], - "type": "stat" - }, - { - "collapsed": true, - "datasource": null, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 28 - }, - "id": 27, - "panels": [], - "title": "SST", - "type": "row" - }, - { - "datasource": "Prometheus", - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "transparent", - "value": null - }, - { - "color": "green", - "value": 50 - }, - { - "color": "red", - "value": 100 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 5, - "x": 0, - "y": 29 - }, - "id": 28, - "options": { - "colorMode": "background", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "name" - }, - "pluginVersion": "8.1.2", - "targets": [ - { - "exemplar": true, - "expr": "(100-50*device_attribute{device=\"lts/sst/1\",name=\"FPGA_sst_offload_enable_R\"}) * on(x) device_attribute{device=\"lts/sdp/1\",name=\"TR_fpga_mask_R\"}", - "format": "time_series", - "hide": false, - "instant": false, - "interval": "", - "legendFormat": "{{x}}", - "refId": "A" - } - ], - "title": "SST offloading enabled", - "transformations": [], - "type": "stat" - }, - { - "datasource": "Prometheus", - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "transparent", - "value": null - }, - { - "color": "green", - "value": 50 - }, - { - "color": "red", - "value": 100 - } - ] - }, - "unit": "pps" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 5, - "x": 5, - "y": 29 - }, - "id": 29, - "options": { - "legend": { - "calcs": [], - "displayMode": "hidden", - "placement": "bottom" - }, - "tooltip": { - "mode": "single" - } - }, - "pluginVersion": "8.1.2", - "targets": [ - { - "exemplar": true, - "expr": "rate(device_attribute{device=\"lts/sst/1\",name=\"nof_invalid_packets_R\"}[1m])", - "format": "time_series", - "hide": false, - "instant": false, - "interval": "", - "legendFormat": "invalid", - "refId": "A" - }, - { - "exemplar": true, - "expr": "rate(device_attribute{device=\"lts/sst/1\",name=\"nof_packets_dropped_R\"}[1m])", - "hide": false, - "interval": "", - "legendFormat": "dropped", - "refId": "B" - }, - { - "exemplar": true, - "expr": "rate(device_attribute{device=\"lts/sst/1\",name=\"nof_payload_errors_R\"}[1m])", - "hide": false, - "interval": "", - "legendFormat": "payload errors {{x}}", - "refId": "C" - } - ], - "title": "SST packet errors", - "transformations": [], - "type": "timeseries" - }, - { - "datasource": "Prometheus", - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "transparent", - "value": null - }, - { - "color": "green", - "value": 50 - }, - { - "color": "red", - "value": 100 - } - ] - }, - "unit": "pps" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 5, - "x": 10, - "y": 29 - }, - "id": 30, - "options": { - "legend": { - "calcs": [], - "displayMode": "hidden", - "placement": "bottom" - }, - "tooltip": { - "mode": "single" - } - }, - "pluginVersion": "8.1.2", - "targets": [ - { - "exemplar": true, - "expr": "rate(device_attribute{device=\"lts/sst/1\",name=\"nof_valid_payloads_R\"}[1m])", - "format": "time_series", - "hide": false, - "instant": false, - "interval": "", - "legendFormat": "{{x}}", - "refId": "A" - } - ], - "title": "SST packets", - "transformations": [], - "type": "timeseries" - } - ], - "refresh": false, - "schemaVersion": 30, - "style": "dark", - "tags": [], - "templating": { - "list": [] - }, - "time": { - "from": "now-30m", - "to": "now" - }, - "timepicker": {}, - "timezone": "", - "title": "LOFAR2.0 Station", - "uid": "6f7Pv8Vnz", - "version": 1 -} diff --git a/docker-compose/grafana/grafana.ini b/docker-compose/grafana/grafana.ini new file mode 100644 index 0000000000000000000000000000000000000000..82f1f4bb004e5ba3c1078226e96decf09cdca4f5 --- /dev/null +++ b/docker-compose/grafana/grafana.ini @@ -0,0 +1,1006 @@ +##################### Grafana Configuration Example ##################### +# +# Everything has defaults so you only need to uncomment things you want to +# change + +# possible values : production, development +;app_mode = production + +# instance name, defaults to HOSTNAME environment variable value or hostname if HOSTNAME var is empty +;instance_name = ${HOSTNAME} + +#################################### Paths #################################### +[paths] +# Path to where grafana can store temp files, sessions, and the sqlite3 db (if that is used) +;data = /var/lib/grafana + +# Temporary files in `data` directory older than given duration will be removed +;temp_data_lifetime = 24h + +# Directory where grafana can store logs +;logs = /var/log/grafana + +# Directory where grafana will automatically scan and look for plugins +;plugins = /var/lib/grafana/plugins + +# folder that contains provisioning config files that grafana will apply on startup and while running. +;provisioning = conf/provisioning + +#################################### Server #################################### +[server] +# Protocol (http, https, h2, socket) +;protocol = http + +# The ip address to bind to, empty will bind to all interfaces +;http_addr = + +# The http port to use +;http_port = 3000 + +# The public facing domain name used to access grafana from a browser +;domain = localhost + +# Redirect to correct domain if host header does not match domain +# Prevents DNS rebinding attacks +;enforce_domain = false + +# The full public facing url you use in browser, used for redirects and emails +# If you use reverse proxy and sub path specify full url (with sub path) +;root_url = %(protocol)s://%(domain)s:%(http_port)s/ + +# Serve Grafana from subpath specified in `root_url` setting. By default it is set to `false` for compatibility reasons. +;serve_from_sub_path = false + +# Log web requests +;router_logging = false + +# the path relative working path +;static_root_path = public + +# enable gzip +;enable_gzip = false + +# https certs & key file +;cert_file = +;cert_key = + +# Unix socket path +;socket = + +# CDN Url +;cdn_url = + +# Sets the maximum time using a duration format (5s/5m/5ms) before timing out read of an incoming request and closing idle connections. +# `0` means there is no timeout for reading the request. +;read_timeout = 0 + +#################################### Database #################################### +[database] +# You can configure the database connection by specifying type, host, name, user and password +# as separate properties or as on string using the url properties. + +# Either "mysql", "postgres" or "sqlite3", it's your choice +;type = sqlite3 +;host = 127.0.0.1:3306 +;name = grafana +;user = root +# If the password contains # or ; you have to wrap it with triple quotes. Ex """#password;""" +;password = + +# Use either URL or the previous fields to configure the database +# Example: mysql://user:secret@host:port/database +;url = + +# For "postgres" only, either "disable", "require" or "verify-full" +;ssl_mode = disable + +# Database drivers may support different transaction isolation levels. +# Currently, only "mysql" driver supports isolation levels. +# If the value is empty - driver's default isolation level is applied. +# For "mysql" use "READ-UNCOMMITTED", "READ-COMMITTED", "REPEATABLE-READ" or "SERIALIZABLE". +;isolation_level = + +;ca_cert_path = +;client_key_path = +;client_cert_path = +;server_cert_name = + +# For "sqlite3" only, path relative to data_path setting +;path = grafana.db + +# Max idle conn setting default is 2 +;max_idle_conn = 2 + +# Max conn setting default is 0 (mean not set) +;max_open_conn = + +# Connection Max Lifetime default is 14400 (means 14400 seconds or 4 hours) +;conn_max_lifetime = 14400 + +# Set to true to log the sql calls and execution times. +;log_queries = + +# For "sqlite3" only. cache mode setting used for connecting to the database. (private, shared) +;cache_mode = private + +################################### Data sources ######################### +[datasources] +# Upper limit of data sources that Grafana will return. This limit is a temporary configuration and it will be deprecated when pagination will be introduced on the list data sources API. +;datasource_limit = 5000 + +#################################### Cache server ############################# +[remote_cache] +# Either "redis", "memcached" or "database" default is "database" +;type = database + +# cache connectionstring options +# database: will use Grafana primary database. +# redis: config like redis server e.g. `addr=127.0.0.1:6379,pool_size=100,db=0,ssl=false`. Only addr is required. ssl may be 'true', 'false', or 'insecure'. +# memcache: 127.0.0.1:11211 +;connstr = + +#################################### Data proxy ########################### +[dataproxy] + +# This enables data proxy logging, default is false +;logging = false + +# How long the data proxy waits to read the headers of the response before timing out, default is 30 seconds. +# This setting also applies to core backend HTTP data sources where query requests use an HTTP client with timeout set. +;timeout = 30 + +# How long the data proxy waits to establish a TCP connection before timing out, default is 10 seconds. +;dialTimeout = 10 + +# How many seconds the data proxy waits before sending a keepalive probe request. +;keep_alive_seconds = 30 + +# How many seconds the data proxy waits for a successful TLS Handshake before timing out. +;tls_handshake_timeout_seconds = 10 + +# How many seconds the data proxy will wait for a server's first response headers after +# fully writing the request headers if the request has an "Expect: 100-continue" +# header. A value of 0 will result in the body being sent immediately, without +# waiting for the server to approve. +;expect_continue_timeout_seconds = 1 + +# Optionally limits the total number of connections per host, including connections in the dialing, +# active, and idle states. On limit violation, dials will block. +# A value of zero (0) means no limit. +;max_conns_per_host = 0 + +# The maximum number of idle connections that Grafana will keep alive. +;max_idle_connections = 100 + +# How many seconds the data proxy keeps an idle connection open before timing out. +;idle_conn_timeout_seconds = 90 + +# If enabled and user is not anonymous, data proxy will add X-Grafana-User header with username into the request, default is false. +;send_user_header = false + +#################################### Analytics #################################### +[analytics] +# Server reporting, sends usage counters to stats.grafana.org every 24 hours. +# No ip addresses are being tracked, only simple counters to track +# running instances, dashboard and error counts. It is very helpful to us. +# Change this option to false to disable reporting. +;reporting_enabled = true + +# The name of the distributor of the Grafana instance. Ex hosted-grafana, grafana-labs +;reporting_distributor = grafana-labs + +# Set to false to disable all checks to https://grafana.net +# for new versions (grafana itself and plugins), check is used +# in some UI views to notify that grafana or plugin update exists +# This option does not cause any auto updates, nor send any information +# only a GET request to http://grafana.com to get latest versions +;check_for_updates = true + +# Google Analytics universal tracking code, only enabled if you specify an id here +;google_analytics_ua_id = + +# Google Tag Manager ID, only enabled if you specify an id here +;google_tag_manager_id = + +#################################### Security #################################### +[security] +# disable creation of admin user on first start of grafana +;disable_initial_admin_creation = false + +# default admin user, created on startup +;admin_user = admin + +# default admin password, can be changed before first start of grafana, or in profile settings +;admin_password = admin + +# used for signing +;secret_key = SW2YcwTIb9zpOOhoPsMm + +# disable gravatar profile images +;disable_gravatar = false + +# data source proxy whitelist (ip_or_domain:port separated by spaces) +;data_source_proxy_whitelist = + +# disable protection against brute force login attempts +;disable_brute_force_login_protection = false + +# set to true if you host Grafana behind HTTPS. default is false. +;cookie_secure = false + +# set cookie SameSite attribute. defaults to `lax`. can be set to "lax", "strict", "none" and "disabled" +;cookie_samesite = lax + +# set to true if you want to allow browsers to render Grafana in a <frame>, <iframe>, <embed> or <object>. default is false. +;allow_embedding = false + +# Set to true if you want to enable http strict transport security (HSTS) response header. +# This is only sent when HTTPS is enabled in this configuration. +# HSTS tells browsers that the site should only be accessed using HTTPS. +;strict_transport_security = false + +# Sets how long a browser should cache HSTS. Only applied if strict_transport_security is enabled. +;strict_transport_security_max_age_seconds = 86400 + +# Set to true if to enable HSTS preloading option. Only applied if strict_transport_security is enabled. +;strict_transport_security_preload = false + +# Set to true if to enable the HSTS includeSubDomains option. Only applied if strict_transport_security is enabled. +;strict_transport_security_subdomains = false + +# Set to true to enable the X-Content-Type-Options response header. +# The X-Content-Type-Options response HTTP header is a marker used by the server to indicate that the MIME types advertised +# in the Content-Type headers should not be changed and be followed. +;x_content_type_options = true + +# Set to true to enable the X-XSS-Protection header, which tells browsers to stop pages from loading +# when they detect reflected cross-site scripting (XSS) attacks. +;x_xss_protection = true + +# Enable adding the Content-Security-Policy header to your requests. +# CSP allows to control resources the user agent is allowed to load and helps prevent XSS attacks. +;content_security_policy = false + +# Set Content Security Policy template used when adding the Content-Security-Policy header to your requests. +# $NONCE in the template includes a random nonce. +# $ROOT_PATH is server.root_url without the protocol. +;content_security_policy_template = """script-src 'self' 'unsafe-eval' 'unsafe-inline' 'strict-dynamic' $NONCE;object-src 'none';font-src 'self';style-src 'self' 'unsafe-inline' blob:;img-src * data:;base-uri 'self';connect-src 'self' grafana.com ws://$ROOT_PATH wss://$ROOT_PATH;manifest-src 'self';media-src 'none';form-action 'self';""" + +#################################### Snapshots ########################### +[snapshots] +# snapshot sharing options +;external_enabled = true +;external_snapshot_url = https://snapshots-origin.raintank.io +;external_snapshot_name = Publish to snapshot.raintank.io + +# Set to true to enable this Grafana instance act as an external snapshot server and allow unauthenticated requests for +# creating and deleting snapshots. +;public_mode = false + +# remove expired snapshot +;snapshot_remove_expired = true + +#################################### Dashboards History ################## +[dashboards] +# Number dashboard versions to keep (per dashboard). Default: 20, Minimum: 1 +;versions_to_keep = 20 + +# Minimum dashboard refresh interval. When set, this will restrict users to set the refresh interval of a dashboard lower than given interval. Per default this is 5 seconds. +# The interval string is a possibly signed sequence of decimal numbers, followed by a unit suffix (ms, s, m, h, d), e.g. 30s or 1m. +;min_refresh_interval = 5s + +# Path to the default home dashboard. If this value is empty, then Grafana uses StaticRootPath + "dashboards/home.json" +default_home_dashboard_path = /var/lib/grafana/dashboards/home.json + +#################################### Users ############################### +[users] +# disable user signup / registration +;allow_sign_up = true + +# Allow non admin users to create organizations +;allow_org_create = true + +# Set to true to automatically assign new users to the default organization (id 1) +;auto_assign_org = true + +# Set this value to automatically add new users to the provided organization (if auto_assign_org above is set to true) +;auto_assign_org_id = 1 + +# Default role new users will be automatically assigned (if disabled above is set to true) +;auto_assign_org_role = Viewer + +# Require email validation before sign up completes +;verify_email_enabled = false + +# Background text for the user field on the login page +;login_hint = email or username +;password_hint = password + +# Default UI theme ("dark" or "light") +;default_theme = dark + +# Path to a custom home page. Users are only redirected to this if the default home dashboard is used. It should match a frontend route and contain a leading slash. +; home_page = + +# External user management, these options affect the organization users view +;external_manage_link_url = +;external_manage_link_name = +;external_manage_info = + +# Viewers can edit/inspect dashboard settings in the browser. But not save the dashboard. +;viewers_can_edit = false + +# Editors can administrate dashboard, folders and teams they create +;editors_can_admin = false + +# The duration in time a user invitation remains valid before expiring. This setting should be expressed as a duration. Examples: 6h (hours), 2d (days), 1w (week). Default is 24h (24 hours). The minimum supported duration is 15m (15 minutes). +;user_invite_max_lifetime_duration = 24h + +# Enter a comma-separated list of users login to hide them in the Grafana UI. These users are shown to Grafana admins and themselves. +; hidden_users = + +[auth] +# Login cookie name +;login_cookie_name = grafana_session + +# The maximum lifetime (duration) an authenticated user can be inactive before being required to login at next visit. Default is 7 days (7d). This setting should be expressed as a duration, e.g. 5m (minutes), 6h (hours), 10d (days), 2w (weeks), 1M (month). The lifetime resets at each successful token rotation. +;login_maximum_inactive_lifetime_duration = + +# The maximum lifetime (duration) an authenticated user can be logged in since login time before being required to login. Default is 30 days (30d). This setting should be expressed as a duration, e.g. 5m (minutes), 6h (hours), 10d (days), 2w (weeks), 1M (month). +;login_maximum_lifetime_duration = + +# How often should auth tokens be rotated for authenticated users when being active. The default is each 10 minutes. +;token_rotation_interval_minutes = 10 + +# Set to true to disable (hide) the login form, useful if you use OAuth, defaults to false +;disable_login_form = false + +# Set to true to disable the sign out link in the side menu. Useful if you use auth.proxy or auth.jwt, defaults to false +;disable_signout_menu = false + +# URL to redirect the user to after sign out +;signout_redirect_url = + +# Set to true to attempt login with OAuth automatically, skipping the login screen. +# This setting is ignored if multiple OAuth providers are configured. +;oauth_auto_login = false + +# OAuth state max age cookie duration in seconds. Defaults to 600 seconds. +;oauth_state_cookie_max_age = 600 + +# limit of api_key seconds to live before expiration +;api_key_max_seconds_to_live = -1 + +# Set to true to enable SigV4 authentication option for HTTP-based datasources. +;sigv4_auth_enabled = false + +#################################### Anonymous Auth ###################### +[auth.anonymous] +# enable anonymous access +enabled = true + +# specify organization name that should be used for unauthenticated users +;org_name = Main Org. + +# specify role for unauthenticated users +;org_role = Viewer + +# mask the Grafana version number for unauthenticated users +;hide_version = false + +#################################### GitHub Auth ########################## +[auth.github] +;enabled = false +;allow_sign_up = true +;client_id = some_id +;client_secret = some_secret +;scopes = user:email,read:org +;auth_url = https://github.com/login/oauth/authorize +;token_url = https://github.com/login/oauth/access_token +;api_url = https://api.github.com/user +;allowed_domains = +;team_ids = +;allowed_organizations = + +#################################### GitLab Auth ######################### +[auth.gitlab] +;enabled = false +;allow_sign_up = true +;client_id = some_id +;client_secret = some_secret +;scopes = api +;auth_url = https://gitlab.com/oauth/authorize +;token_url = https://gitlab.com/oauth/token +;api_url = https://gitlab.com/api/v4 +;allowed_domains = +;allowed_groups = + +#################################### Google Auth ########################## +[auth.google] +;enabled = false +;allow_sign_up = true +;client_id = some_client_id +;client_secret = some_client_secret +;scopes = https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email +;auth_url = https://accounts.google.com/o/oauth2/auth +;token_url = https://accounts.google.com/o/oauth2/token +;api_url = https://www.googleapis.com/oauth2/v1/userinfo +;allowed_domains = +;hosted_domain = + +#################################### Grafana.com Auth #################### +[auth.grafana_com] +;enabled = false +;allow_sign_up = true +;client_id = some_id +;client_secret = some_secret +;scopes = user:email +;allowed_organizations = + +#################################### Azure AD OAuth ####################### +[auth.azuread] +;name = Azure AD +;enabled = false +;allow_sign_up = true +;client_id = some_client_id +;client_secret = some_client_secret +;scopes = openid email profile +;auth_url = https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/authorize +;token_url = https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token +;allowed_domains = +;allowed_groups = + +#################################### Okta OAuth ####################### +[auth.okta] +;name = Okta +;enabled = false +;allow_sign_up = true +;client_id = some_id +;client_secret = some_secret +;scopes = openid profile email groups +;auth_url = https://<tenant-id>.okta.com/oauth2/v1/authorize +;token_url = https://<tenant-id>.okta.com/oauth2/v1/token +;api_url = https://<tenant-id>.okta.com/oauth2/v1/userinfo +;allowed_domains = +;allowed_groups = +;role_attribute_path = +;role_attribute_strict = false + +#################################### Generic OAuth ########################## +[auth.generic_oauth] +;enabled = false +;name = OAuth +;allow_sign_up = true +;client_id = some_id +;client_secret = some_secret +;scopes = user:email,read:org +;empty_scopes = false +;email_attribute_name = email:primary +;email_attribute_path = +;login_attribute_path = +;name_attribute_path = +;id_token_attribute_name = +;auth_url = https://foo.bar/login/oauth/authorize +;token_url = https://foo.bar/login/oauth/access_token +;api_url = https://foo.bar/user +;allowed_domains = +;team_ids = +;allowed_organizations = +;role_attribute_path = +;role_attribute_strict = false +;groups_attribute_path = +;tls_skip_verify_insecure = false +;tls_client_cert = +;tls_client_key = +;tls_client_ca = + +#################################### Basic Auth ########################## +[auth.basic] +;enabled = true + +#################################### Auth Proxy ########################## +[auth.proxy] +;enabled = false +;header_name = X-WEBAUTH-USER +;header_property = username +;auto_sign_up = true +;sync_ttl = 60 +;whitelist = 192.168.1.1, 192.168.2.1 +;headers = Email:X-User-Email, Name:X-User-Name +# Read the auth proxy docs for details on what the setting below enables +;enable_login_token = false + +#################################### Auth JWT ########################## +[auth.jwt] +;enabled = true +;header_name = X-JWT-Assertion +;email_claim = sub +;username_claim = sub +;jwk_set_url = https://foo.bar/.well-known/jwks.json +;jwk_set_file = /path/to/jwks.json +;cache_ttl = 60m +;expected_claims = {"aud": ["foo", "bar"]} +;key_file = /path/to/key/file + +#################################### Auth LDAP ########################## +[auth.ldap] +;enabled = false +;config_file = /etc/grafana/ldap.toml +;allow_sign_up = true + +# LDAP background sync (Enterprise only) +# At 1 am every day +;sync_cron = "0 0 1 * * *" +;active_sync_enabled = true + +#################################### AWS ########################### +[aws] +# Enter a comma-separated list of allowed AWS authentication providers. +# Options are: default (AWS SDK Default), keys (Access && secret key), credentials (Credentials field), ec2_iam_role (EC2 IAM Role) +; allowed_auth_providers = default,keys,credentials + +# Allow AWS users to assume a role using temporary security credentials. +# If true, assume role will be enabled for all AWS authentication providers that are specified in aws_auth_providers +; assume_role_enabled = true + +#################################### Azure ############################### +[azure] +# Azure cloud environment where Grafana is hosted +# Possible values are AzureCloud, AzureChinaCloud, AzureUSGovernment and AzureGermanCloud +# Default value is AzureCloud (i.e. public cloud) +;cloud = AzureCloud + +# Specifies whether Grafana hosted in Azure service with Managed Identity configured (e.g. Azure Virtual Machines instance) +# If enabled, the managed identity can be used for authentication of Grafana in Azure services +# Disabled by default, needs to be explicitly enabled +;managed_identity_enabled = false + +# Client ID to use for user-assigned managed identity +# Should be set for user-assigned identity and should be empty for system-assigned identity +;managed_identity_client_id = + +#################################### SMTP / Emailing ########################## +[smtp] +;enabled = false +;host = localhost:25 +;user = +# If the password contains # or ; you have to wrap it with triple quotes. Ex """#password;""" +;password = +;cert_file = +;key_file = +;skip_verify = false +;from_address = admin@grafana.localhost +;from_name = Grafana +# EHLO identity in SMTP dialog (defaults to instance_name) +;ehlo_identity = dashboard.example.com +# SMTP startTLS policy (defaults to 'OpportunisticStartTLS') +;startTLS_policy = NoStartTLS + +[emails] +;welcome_email_on_sign_up = false +;templates_pattern = emails/*.html, emails/*.txt +;content_types = text/html + +#################################### Logging ########################## +[log] +# Either "console", "file", "syslog". Default is console and file +# Use space to separate multiple modes, e.g. "console file" +;mode = console file + +# Either "debug", "info", "warn", "error", "critical", default is "info" +;level = info + +# optional settings to set different levels for specific loggers. Ex filters = sqlstore:debug +;filters = + +# For "console" mode only +[log.console] +;level = + +# log line format, valid options are text, console and json +;format = console + +# For "file" mode only +[log.file] +;level = + +# log line format, valid options are text, console and json +;format = text + +# This enables automated log rotate(switch of following options), default is true +;log_rotate = true + +# Max line number of single file, default is 1000000 +;max_lines = 1000000 + +# Max size shift of single file, default is 28 means 1 << 28, 256MB +;max_size_shift = 28 + +# Segment log daily, default is true +;daily_rotate = true + +# Expired days of log file(delete after max days), default is 7 +;max_days = 7 + +[log.syslog] +;level = + +# log line format, valid options are text, console and json +;format = text + +# Syslog network type and address. This can be udp, tcp, or unix. If left blank, the default unix endpoints will be used. +;network = +;address = + +# Syslog facility. user, daemon and local0 through local7 are valid. +;facility = + +# Syslog tag. By default, the process' argv[0] is used. +;tag = + +[log.frontend] +# Should Sentry javascript agent be initialized +;enabled = false + +# Sentry DSN if you want to send events to Sentry. +;sentry_dsn = + +# Custom HTTP endpoint to send events captured by the Sentry agent to. Default will log the events to stdout. +;custom_endpoint = /log + +# Rate of events to be reported between 0 (none) and 1 (all), float +;sample_rate = 1.0 + +# Requests per second limit enforced an extended period, for Grafana backend log ingestion endpoint (/log). +;log_endpoint_requests_per_second_limit = 3 + +# Max requests accepted per short interval of time for Grafana backend log ingestion endpoint (/log). +;log_endpoint_burst_limit = 15 + +#################################### Usage Quotas ######################## +[quota] +; enabled = false + +#### set quotas to -1 to make unlimited. #### +# limit number of users per Org. +; org_user = 10 + +# limit number of dashboards per Org. +; org_dashboard = 100 + +# limit number of data_sources per Org. +; org_data_source = 10 + +# limit number of api_keys per Org. +; org_api_key = 10 + +# limit number of alerts per Org. +;org_alert_rule = 100 + +# limit number of orgs a user can create. +; user_org = 10 + +# Global limit of users. +; global_user = -1 + +# global limit of orgs. +; global_org = -1 + +# global limit of dashboards +; global_dashboard = -1 + +# global limit of api_keys +; global_api_key = -1 + +# global limit on number of logged in users. +; global_session = -1 + +# global limit of alerts +;global_alert_rule = -1 + +#################################### Alerting ############################ +[alerting] +# Disable alerting engine & UI features +;enabled = true +# Makes it possible to turn off alert rule execution but alerting UI is visible +;execute_alerts = true + +# Default setting for new alert rules. Defaults to categorize error and timeouts as alerting. (alerting, keep_state) +;error_or_timeout = alerting + +# Default setting for how Grafana handles nodata or null values in alerting. (alerting, no_data, keep_state, ok) +;nodata_or_nullvalues = no_data + +# Alert notifications can include images, but rendering many images at the same time can overload the server +# This limit will protect the server from render overloading and make sure notifications are sent out quickly +;concurrent_render_limit = 5 + + +# Default setting for alert calculation timeout. Default value is 30 +;evaluation_timeout_seconds = 30 + +# Default setting for alert notification timeout. Default value is 30 +;notification_timeout_seconds = 30 + +# Default setting for max attempts to sending alert notifications. Default value is 3 +;max_attempts = 3 + +# Makes it possible to enforce a minimal interval between evaluations, to reduce load on the backend +;min_interval_seconds = 1 + +# Configures for how long alert annotations are stored. Default is 0, which keeps them forever. +# This setting should be expressed as a duration. Examples: 6h (hours), 10d (days), 2w (weeks), 1M (month). +;max_annotation_age = + +# Configures max number of alert annotations that Grafana stores. Default value is 0, which keeps all alert annotations. +;max_annotations_to_keep = + +#################################### Annotations ######################### +[annotations] +# Configures the batch size for the annotation clean-up job. This setting is used for dashboard, API, and alert annotations. +;cleanupjob_batchsize = 100 + +[annotations.dashboard] +# Dashboard annotations means that annotations are associated with the dashboard they are created on. + +# Configures how long dashboard annotations are stored. Default is 0, which keeps them forever. +# This setting should be expressed as a duration. Examples: 6h (hours), 10d (days), 2w (weeks), 1M (month). +;max_age = + +# Configures max number of dashboard annotations that Grafana stores. Default value is 0, which keeps all dashboard annotations. +;max_annotations_to_keep = + +[annotations.api] +# API annotations means that the annotations have been created using the API without any +# association with a dashboard. + +# Configures how long Grafana stores API annotations. Default is 0, which keeps them forever. +# This setting should be expressed as a duration. Examples: 6h (hours), 10d (days), 2w (weeks), 1M (month). +;max_age = + +# Configures max number of API annotations that Grafana keeps. Default value is 0, which keeps all API annotations. +;max_annotations_to_keep = + +#################################### Explore ############################# +[explore] +# Enable the Explore section +;enabled = true + +#################################### Internal Grafana Metrics ########################## +# Metrics available at HTTP API Url /metrics +[metrics] +# Disable / Enable internal metrics +;enabled = true +# Graphite Publish interval +;interval_seconds = 10 +# Disable total stats (stat_totals_*) metrics to be generated +;disable_total_stats = false + +#If both are set, basic auth will be required for the metrics endpoint. +; basic_auth_username = +; basic_auth_password = + +# Metrics environment info adds dimensions to the `grafana_environment_info` metric, which +# can expose more information about the Grafana instance. +[metrics.environment_info] +#exampleLabel1 = exampleValue1 +#exampleLabel2 = exampleValue2 + +# Send internal metrics to Graphite +[metrics.graphite] +# Enable by setting the address setting (ex localhost:2003) +;address = +;prefix = prod.grafana.%(instance_name)s. + +#################################### Grafana.com integration ########################## +# Url used to import dashboards directly from Grafana.com +[grafana_com] +;url = https://grafana.com + +#################################### Distributed tracing ############ +[tracing.jaeger] +# Enable by setting the address sending traces to jaeger (ex localhost:6831) +;address = localhost:6831 +# Tag that will always be included in when creating new spans. ex (tag1:value1,tag2:value2) +;always_included_tag = tag1:value1 +# Type specifies the type of the sampler: const, probabilistic, rateLimiting, or remote +;sampler_type = const +# jaeger samplerconfig param +# for "const" sampler, 0 or 1 for always false/true respectively +# for "probabilistic" sampler, a probability between 0 and 1 +# for "rateLimiting" sampler, the number of spans per second +# for "remote" sampler, param is the same as for "probabilistic" +# and indicates the initial sampling rate before the actual one +# is received from the mothership +;sampler_param = 1 +# sampling_server_url is the URL of a sampling manager providing a sampling strategy. +;sampling_server_url = +# Whether or not to use Zipkin propagation (x-b3- HTTP headers). +;zipkin_propagation = false +# Setting this to true disables shared RPC spans. +# Not disabling is the most common setting when using Zipkin elsewhere in your infrastructure. +;disable_shared_zipkin_spans = false + +#################################### External image storage ########################## +[external_image_storage] +# Used for uploading images to public servers so they can be included in slack/email messages. +# you can choose between (s3, webdav, gcs, azure_blob, local) +;provider = + +[external_image_storage.s3] +;endpoint = +;path_style_access = +;bucket = +;region = +;path = +;access_key = +;secret_key = + +[external_image_storage.webdav] +;url = +;public_url = +;username = +;password = + +[external_image_storage.gcs] +;key_file = +;bucket = +;path = + +[external_image_storage.azure_blob] +;account_name = +;account_key = +;container_name = + +[external_image_storage.local] +# does not require any configuration + +[rendering] +# Options to configure a remote HTTP image rendering service, e.g. using https://github.com/grafana/grafana-image-renderer. +# URL to a remote HTTP image renderer service, e.g. http://localhost:8081/render, will enable Grafana to render panels and dashboards to PNG-images using HTTP requests to an external service. +;server_url = +# If the remote HTTP image renderer service runs on a different server than the Grafana server you may have to configure this to a URL where Grafana is reachable, e.g. http://grafana.domain/. +;callback_url = +# Concurrent render request limit affects when the /render HTTP endpoint is used. Rendering many images at the same time can overload the server, +# which this setting can help protect against by only allowing a certain amount of concurrent requests. +;concurrent_render_request_limit = 30 + +[panels] +# If set to true Grafana will allow script tags in text panels. Not recommended as it enable XSS vulnerabilities. +;disable_sanitize_html = false + +[plugins] +;enable_alpha = false +;app_tls_skip_verify_insecure = false +# Enter a comma-separated list of plugin identifiers to identify plugins to load even if they are unsigned. Plugins with modified signatures are never loaded. +;allow_loading_unsigned_plugins = +# Enable or disable installing plugins directly from within Grafana. +;plugin_admin_enabled = false +;plugin_admin_external_manage_enabled = false +;plugin_catalog_url = https://grafana.com/grafana/plugins/ + +#################################### Grafana Live ########################################## +[live] +# max_connections to Grafana Live WebSocket endpoint per Grafana server instance. See Grafana Live docs +# if you are planning to make it higher than default 100 since this can require some OS and infrastructure +# tuning. 0 disables Live, -1 means unlimited connections. +;max_connections = 100 + +# allowed_origins is a comma-separated list of origins that can establish connection with Grafana Live. +# If not set then origin will be matched over root_url. Supports wildcard symbol "*". +;allowed_origins = + +# engine defines an HA (high availability) engine to use for Grafana Live. By default no engine used - in +# this case Live features work only on a single Grafana server. Available options: "redis". +# Setting ha_engine is an EXPERIMENTAL feature. +;ha_engine = + +# ha_engine_address sets a connection address for Live HA engine. Depending on engine type address format can differ. +# For now we only support Redis connection address in "host:port" format. +# This option is EXPERIMENTAL. +;ha_engine_address = "127.0.0.1:6379" + +#################################### Grafana Image Renderer Plugin ########################## +[plugin.grafana-image-renderer] +# Instruct headless browser instance to use a default timezone when not provided by Grafana, e.g. when rendering panel image of alert. +# See ICU’s metaZones.txt (https://cs.chromium.org/chromium/src/third_party/icu/source/data/misc/metaZones.txt) for a list of supported +# timezone IDs. Fallbacks to TZ environment variable if not set. +;rendering_timezone = + +# Instruct headless browser instance to use a default language when not provided by Grafana, e.g. when rendering panel image of alert. +# Please refer to the HTTP header Accept-Language to understand how to format this value, e.g. 'fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5'. +;rendering_language = + +# Instruct headless browser instance to use a default device scale factor when not provided by Grafana, e.g. when rendering panel image of alert. +# Default is 1. Using a higher value will produce more detailed images (higher DPI), but will require more disk space to store an image. +;rendering_viewport_device_scale_factor = + +# Instruct headless browser instance whether to ignore HTTPS errors during navigation. Per default HTTPS errors are not ignored. Due to +# the security risk it's not recommended to ignore HTTPS errors. +;rendering_ignore_https_errors = + +# Instruct headless browser instance whether to capture and log verbose information when rendering an image. Default is false and will +# only capture and log error messages. When enabled, debug messages are captured and logged as well. +# For the verbose information to be included in the Grafana server log you have to adjust the rendering log level to debug, configure +# [log].filter = rendering:debug. +;rendering_verbose_logging = + +# Instruct headless browser instance whether to output its debug and error messages into running process of remote rendering service. +# Default is false. This can be useful to enable (true) when troubleshooting. +;rendering_dumpio = + +# Additional arguments to pass to the headless browser instance. Default is --no-sandbox. The list of Chromium flags can be found +# here (https://peter.sh/experiments/chromium-command-line-switches/). Multiple arguments is separated with comma-character. +;rendering_args = + +# You can configure the plugin to use a different browser binary instead of the pre-packaged version of Chromium. +# Please note that this is not recommended, since you may encounter problems if the installed version of Chrome/Chromium is not +# compatible with the plugin. +;rendering_chrome_bin = + +# Instruct how headless browser instances are created. Default is 'default' and will create a new browser instance on each request. +# Mode 'clustered' will make sure that only a maximum of browsers/incognito pages can execute concurrently. +# Mode 'reusable' will have one browser instance and will create a new incognito page on each request. +;rendering_mode = + +# When rendering_mode = clustered you can instruct how many browsers or incognito pages can execute concurrently. Default is 'browser' +# and will cluster using browser instances. +# Mode 'context' will cluster using incognito pages. +;rendering_clustering_mode = +# When rendering_mode = clustered you can define maximum number of browser instances/incognito pages that can execute concurrently.. +;rendering_clustering_max_concurrency = + +# Limit the maximum viewport width, height and device scale factor that can be requested. +;rendering_viewport_max_width = +;rendering_viewport_max_height = +;rendering_viewport_max_device_scale_factor = + +# Change the listening host and port of the gRPC server. Default host is 127.0.0.1 and default port is 0 and will automatically assign +# a port not in use. +;grpc_host = +;grpc_port = + +[enterprise] +# Path to a valid Grafana Enterprise license.jwt file +;license_path = + +[feature_toggles] +# enable features, separated by spaces +enable = ngalert + +[date_formats] +# For information on what formatting patterns that are supported https://momentjs.com/docs/#/displaying/ + +# Default system date format used in time range picker and other places where full time is displayed +;full_date = YYYY-MM-DD HH:mm:ss + +# Used by graph and other places where we only show small intervals +;interval_second = HH:mm:ss +;interval_minute = HH:mm +;interval_hour = MM/DD HH:mm +;interval_day = MM/DD +;interval_month = YYYY-MM +;interval_year = YYYY + +# Experimental feature +;use_browser_locale = false + +# Default timezone for user preferences. Options are 'browser' for the browser local timezone or a timezone name from IANA Time Zone database, e.g. 'UTC' or 'Europe/Amsterdam' etc. +;default_timezone = browser + +[expressions] +# Enable or disable the expressions functionality. +;enabled = true + +[geomap] +# Set the JSON configuration for the default basemap +;default_baselayer_config = `{ +; "type": "xyz", +; "config": { +; "attribution": "Open street map", +; "url": "https://tile.openstreetmap.org/{z}/{x}/{y}.png" +; } +;}` + +# Enable or disable loading other base map layers +;enable_custom_baselayers = true diff --git a/docker-compose/hdbpp_viewer.yml b/docker-compose/hdbpp_viewer.yml index 481879729621d5c5828abddbbd047182d9b16278..9a1f9da06a3db1398e3f4060fd32de9850e6532e 100644 --- a/docker-compose/hdbpp_viewer.yml +++ b/docker-compose/hdbpp_viewer.yml @@ -12,8 +12,7 @@ services: hdbpp-viewer: image: ${LOCAL_DOCKER_REGISTRY_HOST}/${LOCAL_DOCKER_REGISTRY_USER}/hdbpp_viewer:${TANGO_HDBPP_VIEWER_VERSION} container_name: ${CONTAINER_NAME_PREFIX}hdbpp-viewer - networks: - - control + network_mode: host depends_on: - databaseds - dsconfig @@ -25,17 +24,17 @@ services: environment: - XAUTHORITY=${XAUTHORITY} - DISPLAY=${DISPLAY} - - TANGO_HOST=${TANGO_HOST} + - TANGO_HOST=localhost:10000 - HDB_TYPE=mysql - - HDB_MYSQL_HOST=archiver-maria-db - - HDB_MYSQL_PORT=3306 + - HDB_MYSQL_HOST=localhost + - HDB_MYSQL_PORT=3307 - HDB_USER=tango - HDB_PASSWORD=tango - HDB_NAME=hdbpp - CLASSPATH=JTango.jar:ATKCore.jar:ATKWidget.jar:jhdbviewer.jar:HDBPP.jar:jython.jar:jcalendar.jar entrypoint: - wait-for-it.sh - - ${TANGO_HOST} + - localhost:10000 - --strict - -- - ./hdbpp_viewer/hdbpp_viewer_script diff --git a/docker-compose/jupyter/Dockerfile b/docker-compose/jupyter/Dockerfile index 29f736cdca2fc843750612c6780ea7ad2dfa516e..b69ddfa7e5b6d6eaeab11b25f99258d0f0743daa 100644 --- a/docker-compose/jupyter/Dockerfile +++ b/docker-compose/jupyter/Dockerfile @@ -9,7 +9,6 @@ ARG CONTAINER_EXECUTION_UID=1000 ENV HOME=/home/user RUN sudo mkdir -p ${HOME} RUN sudo chown ${CONTAINER_EXECUTION_UID} -R ${HOME} -USER ${CONTAINER_EXECUTION_UID} RUN sudo pip3 install jupyter RUN sudo pip3 install ipykernel @@ -20,8 +19,6 @@ RUN sudo pip3 install matplotlib jupyterplot # Allow Download as -> PDF via html RUN sudo pip3 install nbconvert RUN sudo pip3 install notebook-as-pdf -# pyppeteer-install installs in the homedir, so run it as the user that will execute the notebook -RUN pyppeteer-install # see https://github.com/jupyter/nbconvert/issues/1434 RUN sudo bash -c "echo DEFAULT_ARGS += [\\\"--no-sandbox\\\"] >> /usr/local/lib/python3.7/dist-packages/pyppeteer/launcher.py" @@ -48,9 +45,15 @@ COPY jupyter-notebook /usr/local/bin/jupyter-notebook #Install further python modules RUN sudo pip3 install PyMySQL[rsa] sqlalchemy +# Packages to interface with testing hardware directly +RUN sudo pip3 install pyvisa pyvisa-py + # Add Tini. Tini operates as a process subreaper for jupyter. This prevents kernel crashes. ENV TINI_VERSION v0.6.0 ENV JUPYTER_RUNTIME_DIR=/tmp ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /usr/bin/tini RUN sudo chmod +x /usr/bin/tini +USER ${CONTAINER_EXECUTION_UID} +# pyppeteer-install installs in the homedir, so run it as the user that will execute the notebook +RUN pyppeteer-install diff --git a/docker-compose/jupyter/ipython-profiles/stationcontrol-jupyter/startup/01-devices.py b/docker-compose/jupyter/ipython-profiles/stationcontrol-jupyter/startup/01-devices.py index 504cdd2736714aef4a4744e51c98e17d8ba630c7..df75d5962a1327041995aa04c41d6d1e1c2ae914 100644 --- a/docker-compose/jupyter/ipython-profiles/stationcontrol-jupyter/startup/01-devices.py +++ b/docker-compose/jupyter/ipython-profiles/stationcontrol-jupyter/startup/01-devices.py @@ -2,7 +2,8 @@ recv = DeviceProxy("LTS/RECV/1") sdp = DeviceProxy("LTS/SDP/1") sst = DeviceProxy("LTS/SST/1") +xst = DeviceProxy("LTS/XST/1") unb2 = DeviceProxy("LTS/UNB2/1") # Put them in a list in case one wants to iterate -devices = [recv, sdp, sst, unb2] +devices = [recv, sdp, sst, xst, unb2] diff --git a/docker-compose/lofar-device-base/lofar-requirements.txt b/docker-compose/lofar-device-base/lofar-requirements.txt index 57ab2a14fbc6012c52e49c05f3e2119a3a886dd9..2214412a4365f4b804d6b20b0576c390482b1481 100644 --- a/docker-compose/lofar-device-base/lofar-requirements.txt +++ b/docker-compose/lofar-device-base/lofar-requirements.txt @@ -4,3 +4,4 @@ python-logstash-async gitpython PyMySQL[rsa] sqlalchemy +docker diff --git a/docker-compose/tango-prometheus-exporter/Dockerfile b/docker-compose/tango-prometheus-exporter/Dockerfile index 4f548bbc1a7ff9eebb906fe912cd7a74992bd558..1df83afa690c008f83868c1bc9c8d6c1a09323ef 100644 --- a/docker-compose/tango-prometheus-exporter/Dockerfile +++ b/docker-compose/tango-prometheus-exporter/Dockerfile @@ -12,4 +12,4 @@ RUN pip install -r /code/pip-requirements.txt WORKDIR /code ENV PYTHONPATH '/code/' -CMD ["python" , "/code/collector.py"] +CMD ["python", "-u", "/code/collector.py"] diff --git a/docker-compose/unb2-sim.yml b/docker-compose/unb2-sim.yml new file mode 100644 index 0000000000000000000000000000000000000000..e031e20f54ad6addec1fdbabf972661d6f4c8f9a --- /dev/null +++ b/docker-compose/unb2-sim.yml @@ -0,0 +1,20 @@ +# +# Docker compose file that launches a UNB2 simulator +# +# Defines: +# - unb2-sim +# +version: '2' + +services: + unb2-sim: + build: + context: unb2-sim + container_name: ${CONTAINER_NAME_PREFIX}unb2-sim + networks: + - control + volumes: + - ${HOME}:/hosthome + ports: + - "4844:4844" + restart: on-failure diff --git a/docker-compose/unb2-sim/Dockerfile b/docker-compose/unb2-sim/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..cbb4ce80185ee1cf0a84aabc457a7983e57b5651 --- /dev/null +++ b/docker-compose/unb2-sim/Dockerfile @@ -0,0 +1,10 @@ +FROM ubuntu:20.04 + +COPY requirements.txt /requirements.txt + +RUN apt-get update && apt-get install -y python3 python3-pip python3-yaml git && \ + pip3 install -r requirements.txt && \ + git clone --depth 1 --branch master https://git.astron.nl/lofar2.0/pypcc + +WORKDIR /pypcc +CMD ["python3","pypcc2.py","--simulator", "--port=4844", "--config=UNB2"] diff --git a/docker-compose/unb2-sim/requirements.txt b/docker-compose/unb2-sim/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..5badf0e57069366adedc43ca2c2b59dc7dc4b35d --- /dev/null +++ b/docker-compose/unb2-sim/requirements.txt @@ -0,0 +1,4 @@ +#check if this file is necessary +opcua +numpy +recordclass>=0.16,<0.16.1 \ No newline at end of file diff --git a/jupyter-notebooks/Docker_notebook.ipynb b/jupyter-notebooks/Docker_notebook.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..facc20c3e1e1d561504ef09b08e2e78296d0fb39 --- /dev/null +++ b/jupyter-notebooks/Docker_notebook.ipynb @@ -0,0 +1,139 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "waiting-chance", + "metadata": {}, + "outputs": [], + "source": [ + "import time" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "moving-alexandria", + "metadata": {}, + "outputs": [], + "source": [ + "d=DeviceProxy(\"LTS/Docker/1\")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "ranking-aluminum", + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Device is now in on state\n" + ] + } + ], + "source": [ + "state = str(d.state())\n", + "\n", + "\n", + "if state == \"OFF\" or state == \"FAULT\":\n", + " d.initialise()\n", + " time.sleep(1)\n", + "state = str(d.state())\n", + "if state == \"STANDBY\":\n", + " d.on()\n", + "state = str(d.state())\n", + "if state == \"ON\":\n", + " print(\"Device is now in on state\")\n", + "else:\n", + " print(\"warning, expected device to be in on state, is: \", state)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "0caa8146", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "version_R *L2SS-379-docker-device [0c12e90b1c311df82edf9ebd0b0e9a3a530f0a81]\n", + "archiver_maria_db_R True\n", + "archiver_maria_db_RW False\n", + "databaseds_R True\n", + "databaseds_RW False\n", + "device_recv_R False\n", + "device_recv_RW False\n", + "device_sdp_R True\n", + "device_sdp_RW False\n", + "device_sst_R True\n", + "device_sst_RW False\n", + "device_xst_R False\n", + "device_xst_RW False\n", + "device_unb2_R True\n", + "device_unb2_RW False\n", + "device_docker_R True\n", + "dsconfig_R True\n", + "dsconfig_RW False\n", + "elk_R True\n", + "elk_RW False\n", + "grafana_R True\n", + "grafana_RW False\n", + "hdbpp_cm_R True\n", + "hdbpp_cm_RW False\n", + "hdbpp_es_R True\n", + "hdbpp_es_RW False\n", + "itango_R True\n", + "itango_RW False\n", + "jupyter_R True\n", + "jupyter_RW False\n", + "prometheus_R True\n", + "prometheus_RW False\n", + "tangodb_R True\n", + "tangodb_RW False\n", + "tango_prometheus_exporter_R False\n", + "tango_prometheus_exporter_RW False\n", + "tango_rest_R True\n", + "tango_rest_RW False\n", + "State <function __get_command_func.<locals>.f at 0x7f84a8b4fb70>\n", + "Status <function __get_command_func.<locals>.f at 0x7f84a8b4fb70>\n" + ] + } + ], + "source": [ + "attr_names = d.get_attribute_list()\n", + "\n", + "\n", + "for i in attr_names:\n", + " exec(\"value = print(i, d.{})\".format(i))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "StationControl", + "language": "python", + "name": "stationcontrol" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/jupyter-notebooks/UNB2_notebook.ipynb b/jupyter-notebooks/UNB2_notebook.ipynb index 3e87179f3a0f0ef951da6a078ba5df3610a6696d..e140631ffc4ea0cee80e2374ec6b5f1289dbba24 100644 --- a/jupyter-notebooks/UNB2_notebook.ipynb +++ b/jupyter-notebooks/UNB2_notebook.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": 25, "id": "waiting-chance", "metadata": {}, "outputs": [], @@ -12,7 +12,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 26, "id": "moving-alexandria", "metadata": {}, "outputs": [], @@ -22,7 +22,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 28, "id": "ranking-aluminum", "metadata": { "scrolled": false @@ -55,7 +55,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 29, "id": "0caa8146", "metadata": {}, "outputs": [ @@ -63,68 +63,68 @@ "name": "stdout", "output_type": "stream", "text": [ - "version_R *L2SS-268-LR1_2_Read_hardware_status_of_UB2c_from_SDPHW [1007a5c5462b1aa3e8f81268f890f6c058413218]\n", + "version_R *L2SS-268-LR1_2_Read_hardware_status_of_UB2c_from_SDPHW [b2db449162be8e52013dbbd1a44d6d90a12491b5]\n", "UNB2_Power_ON_OFF_RW [False False]\n", "UNB2_Front_Panel_LED_RW [0 0]\n", "UNB2_Front_Panel_LED_R [0 0]\n", "UNB2_mask_RW [False False]\n", "UNB2_I2C_bus_STATUS_R [False False]\n", - "UNB2_EEPROM_Unique_ID_R [5947666 5947666]\n", - "UNB2_DC_DC_48V_12V_VIN_R [29.18505859 29.18505859]\n", - "UNB2_DC_DC_48V_12V_VOUT_R [12.00146484 11.98486328]\n", - "UNB2_DC_DC_48V_12V_IOUT_R [3.625 3.625]\n", - "UNB2_DC_DC_48V_12V_TEMP_R [37. 37.]\n", - "UNB2_POL_QSFP_N01_VOUT_R [3.28686523 3.28686523]\n", - "UNB2_POL_QSFP_N01_IOUT_R [1.55078125 1.55078125]\n", - "UNB2_POL_QSFP_N01_TEMP_R [33.75 33.75]\n", - "UNB2_POL_QSFP_N23_VOUT_R [3.28710938 3.28710938]\n", - "UNB2_POL_QSFP_N23_IOUT_R [1.25195312 1.25195312]\n", - "UNB2_POL_QSFP_N23_TEMP_R [40.625 40.625]\n", - "UNB2_POL_SWITCH_1V2_VOUT_R [1.19970703 1.19970703]\n", - "UNB2_POL_SWITCH_1V2_IOUT_R [1.73632812 1.73632812]\n", - "UNB2_POL_SWITCH_1V2_TEMP_R [45.125 45.125]\n", - "UNB2_POL_SWITCH_PHY_VOUT_R [1.00024414 1.00024414]\n", - "UNB2_POL_SWITCH_PHY_IOUT_R [0.52050781 0.52050781]\n", - "UNB2_POL_SWITCH_PHY_TEMP_R [46.1875 46.1875]\n", - "UNB2_POL_CLOCK_VOUT_R [2.49951172 2.49951172]\n", - "UNB2_POL_CLOCK_IOUT_R [0.94042969 0.94042969]\n", - "UNB2_POL_CLOCK_TEMP_R [42.875 42.875]\n", - "UNB2_FPGA_DDR4_SLOT_TEMP_R [[27.5 27.5 29.25 27.75 28.75 29.25 28.5 28.5 ]\n", - " [27.5 27.5 29.25 27.75 28.75 29.25 28.5 28.5 ]]\n", - "UNB2_FPGA_POL_CORE_IOUT_R [[5.921875 4.109375 3.76171875 3.55859375]\n", - " [5.921875 4.1015625 3.76171875 3.55859375]]\n", - "UNB2_FPGA_POL_CORE_TEMP_R [[30.84375 31.46875 32.4375 34.75 ]\n", - " [30.84375 31.5 32.375 34.6875 ]]\n", - "UNB2_FPGA_POL_ERAM_VOUT_R [[0.8996582 0.90014648 0.90014648 0.8996582 ]\n", - " [0.8996582 0.8996582 0.90014648 0.8996582 ]]\n", - "UNB2_FPGA_POL_ERAM_IOUT_R [[0.08764648 0.0880127 0.18725586 0.08703613]\n", - " [0.02593994 0.0880127 0.18725586 0.08703613]]\n", - "UNB2_FPGA_POL_ERAM_TEMP_R [[38.75 39.25 41. 41.4375]\n", - " [38.75 39.25 41. 41.4375]]\n", - "UNB2_FPGA_POL_RXGXB_VOUT_R [[0.90014648 0.89990234 0.90014648 0.90014648]\n", - " [0.90014648 0.89990234 0.90014648 0.90014648]]\n", - "UNB2_FPGA_POL_RXGXB_IOUT_R [[0.49755859 0.41113281 0.40234375 0.48876953]\n", - " [0.49755859 0.41113281 0.40234375 0.48876953]]\n", - "UNB2_FPGA_POL_RXGXB_TEMP_R [[34.75 38.0625 36.5 38.1875]\n", - " [34.75 38.0625 36.5 38.1875]]\n", - "UNB2_FPGA_POL_TXGXB_VOUT_R [[0.89990234 0.90014648 0.90014648 0.89990234]\n", - " [0.89990234 0.90014648 0.90014648 0.89990234]]\n", - "UNB2_FPGA_POL_TXGXB_IOUT_R [[0.17480469 0.12219238 0.06433105 0.13110352]\n", - " [0.17480469 0.12219238 0.06433105 0.13110352]]\n", - "UNB2_FPGA_POL_HGXB_VOUT_R [[1.80004883 1.79956055 1.79980469 1.79980469]\n", - " [1.80004883 1.79956055 1.79980469 1.79980469]]\n", - "UNB2_FPGA_POL_HGXB_IOUT_R [[0.67089844 0.76269531 0.80664062 0.7265625 ]\n", - " [0.67089844 0.76269531 0.80664062 0.7265625 ]]\n", - "UNB2_FPGA_POL_HGXB_TEMP_R [[40.375 41.8125 44.3125 40.625 ]\n", - " [40.375 41.8125 44.3125 40.625 ]]\n", - "UNB2_FPGA_POL_PGM_VOUT_R [[1.80029297 1.80004883 1.79931641 1.80029297]\n", - " [1.80029297 1.80004883 1.79931641 1.80029297]]\n", - "UNB2_FPGA_POL_PGM_IOUT_R [[0.13818359 0.17089844 0.31542969 0.10656738]\n", - " [0.1550293 0.17089844 0.31542969 0.10656738]]\n", - "UNB2_FPGA_POL_PGM_TEMP_R [[40.0625 42.1875 44.3125 40.3125]\n", - " [40.0625 42.1875 44.3125 40.3125]]\n", - "State <function __get_command_func.<locals>.f at 0x7f636d295510>\n", - "Status <function __get_command_func.<locals>.f at 0x7f636d295510>\n" + "UNB2_EEPROM_Unique_ID_R [0 0]\n", + "UNB2_DC_DC_48V_12V_VIN_R [0. 0.]\n", + "UNB2_DC_DC_48V_12V_VOUT_R [0. 0.]\n", + "UNB2_DC_DC_48V_12V_IOUT_R [0. 0.]\n", + "UNB2_DC_DC_48V_12V_TEMP_R [0. 0.]\n", + "UNB2_POL_QSFP_N01_VOUT_R [0. 0.]\n", + "UNB2_POL_QSFP_N01_IOUT_R [0. 0.]\n", + "UNB2_POL_QSFP_N01_TEMP_R [0. 0.]\n", + "UNB2_POL_QSFP_N23_VOUT_R [0. 0.]\n", + "UNB2_POL_QSFP_N23_IOUT_R [0. 0.]\n", + "UNB2_POL_QSFP_N23_TEMP_R [0. 0.]\n", + "UNB2_POL_SWITCH_1V2_VOUT_R [0. 0.]\n", + "UNB2_POL_SWITCH_1V2_IOUT_R [0. 0.]\n", + "UNB2_POL_SWITCH_1V2_TEMP_R [0. 0.]\n", + "UNB2_POL_SWITCH_PHY_VOUT_R [0. 0.]\n", + "UNB2_POL_SWITCH_PHY_IOUT_R [0. 0.]\n", + "UNB2_POL_SWITCH_PHY_TEMP_R [0. 0.]\n", + "UNB2_POL_CLOCK_VOUT_R [0. 0.]\n", + "UNB2_POL_CLOCK_IOUT_R [0. 0.]\n", + "UNB2_POL_CLOCK_TEMP_R [0. 0.]\n", + "UNB2_FPGA_DDR4_SLOT_TEMP_R [[0. 0. 0. 0. 0. 0. 0. 0.]\n", + " [0. 0. 0. 0. 0. 0. 0. 0.]]\n", + "UNB2_FPGA_POL_CORE_IOUT_R [[0. 0. 0. 0.]\n", + " [0. 0. 0. 0.]]\n", + "UNB2_FPGA_POL_CORE_TEMP_R [[0. 0. 0. 0.]\n", + " [0. 0. 0. 0.]]\n", + "UNB2_FPGA_POL_ERAM_VOUT_R [[0. 0. 0. 0.]\n", + " [0. 0. 0. 0.]]\n", + "UNB2_FPGA_POL_ERAM_IOUT_R [[0. 0. 0. 0.]\n", + " [0. 0. 0. 0.]]\n", + "UNB2_FPGA_POL_ERAM_TEMP_R [[0. 0. 0. 0.]\n", + " [0. 0. 0. 0.]]\n", + "UNB2_FPGA_POL_RXGXB_VOUT_R [[0. 0. 0. 0.]\n", + " [0. 0. 0. 0.]]\n", + "UNB2_FPGA_POL_RXGXB_IOUT_R [[0. 0. 0. 0.]\n", + " [0. 0. 0. 0.]]\n", + "UNB2_FPGA_POL_RXGXB_TEMP_R [[0. 0. 0. 0.]\n", + " [0. 0. 0. 0.]]\n", + "UNB2_FPGA_POL_TXGXB_VOUT_R [[0. 0. 0. 0.]\n", + " [0. 0. 0. 0.]]\n", + "UNB2_FPGA_POL_TXGXB_IOUT_R [[0. 0. 0. 0.]\n", + " [0. 0. 0. 0.]]\n", + "UNB2_FPGA_POL_HGXB_VOUT_R [[0. 0. 0. 0.]\n", + " [0. 0. 0. 0.]]\n", + "UNB2_FPGA_POL_HGXB_IOUT_R [[0. 0. 0. 0.]\n", + " [0. 0. 0. 0.]]\n", + "UNB2_FPGA_POL_HGXB_TEMP_R [[0. 0. 0. 0.]\n", + " [0. 0. 0. 0.]]\n", + "UNB2_FPGA_POL_PGM_VOUT_R [[0. 0. 0. 0.]\n", + " [0. 0. 0. 0.]]\n", + "UNB2_FPGA_POL_PGM_IOUT_R [[0. 0. 0. 0.]\n", + " [0. 0. 0. 0.]]\n", + "UNB2_FPGA_POL_PGM_TEMP_R [[0. 0. 0. 0.]\n", + " [0. 0. 0. 0.]]\n", + "State <function __get_command_func.<locals>.f at 0x7f4c210e1ea0>\n", + "Status <function __get_command_func.<locals>.f at 0x7f4c210e1ea0>\n" ] } ], @@ -138,7 +138,7 @@ }, { "cell_type": "code", - "execution_count": 78, + "execution_count": 30, "id": "929965c2", "metadata": {}, "outputs": [ @@ -171,7 +171,7 @@ }, { "cell_type": "code", - "execution_count": 82, + "execution_count": 22, "id": "6813164e", "metadata": {}, "outputs": [ @@ -201,7 +201,7 @@ }, { "cell_type": "code", - "execution_count": 74, + "execution_count": 23, "id": "e9b32ec7", "metadata": {}, "outputs": [ @@ -210,7 +210,7 @@ "output_type": "stream", "text": [ "Old values:\n", - " [ True True]\n", + " [False False]\n", "Values to be set:\n", " [False False]\n", "Values read back after setting:\n", @@ -231,25 +231,21 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 24, "id": "transsexual-battle", "metadata": {}, "outputs": [ { - "ename": "DevFailed", - "evalue": "DevFailed[\nDevError[\n desc = Read value for attribute FPGA_mask_RW has not been updated\n origin = Device_3Impl::read_attributes_no_except\n reason = API_AttrValueNotSet\nseverity = ERR]\n\nDevError[\n desc = Failed to read_attribute on device lts/sdp/1, attribute FPGA_mask_RW\n origin = DeviceProxy::read_attribute()\n reason = API_AttributeFailed\nseverity = ERR]\n]", + "ename": "AttributeError", + "evalue": "FPGA_mask_RW", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mDevFailed\u001b[0m Traceback (most recent call last)", + "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m/tmp/ipykernel_22/2885399456.py\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m values = [\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0;34m[\u001b[0m\u001b[0md\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mFPGA_mask_RW\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"FPGA_mask_RW\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 3\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0md\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mFPGA_scrap_R\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"FPGA_scrap_R\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0md\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mFPGA_scrap_RW\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"FPGA_scrap_RW\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0md\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mFPGA_status_R\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"FPGA_status_R\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/tango/device_proxy.py\u001b[0m in \u001b[0;36m__DeviceProxy__getattr\u001b[0;34m(self, name)\u001b[0m\n\u001b[1;32m 319\u001b[0m \u001b[0mattr_info\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__get_attr_cache\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mname_l\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 320\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mattr_info\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 321\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0m__get_attribute_value\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mattr_info\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 322\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 323\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mname_l\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__get_pipe_cache\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/tango/device_proxy.py\u001b[0m in \u001b[0;36m__get_attribute_value\u001b[0;34m(self, attr_info, name)\u001b[0m\n\u001b[1;32m 281\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__get_attribute_value\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mattr_info\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 282\u001b[0m \u001b[0m_\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0menum_class\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mattr_info\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 283\u001b[0;31m \u001b[0mattr_value\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mread_attribute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mvalue\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 284\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0menum_class\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 285\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0menum_class\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mattr_value\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/tango/green.py\u001b[0m in \u001b[0;36mgreener\u001b[0;34m(obj, *args, **kwargs)\u001b[0m\n\u001b[1;32m 193\u001b[0m \u001b[0mgreen_mode\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0maccess\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'green_mode'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 194\u001b[0m \u001b[0mexecutor\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mget_object_executor\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mobj\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mgreen_mode\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 195\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mexecutor\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrun\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfn\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkwargs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mwait\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mwait\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtimeout\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mtimeout\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 196\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 197\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mgreener\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/tango/green.py\u001b[0m in \u001b[0;36mrun\u001b[0;34m(self, fn, args, kwargs, wait, timeout)\u001b[0m\n\u001b[1;32m 107\u001b[0m \u001b[0;31m# Sychronous (no delegation)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 108\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0masynchronous\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0min_executor_context\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 109\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mfn\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 110\u001b[0m \u001b[0;31m# Asynchronous delegation\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 111\u001b[0m \u001b[0maccessor\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdelegate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfn\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/tango/device_proxy.py\u001b[0m in \u001b[0;36m__DeviceProxy__read_attribute\u001b[0;34m(self, value, extract_as)\u001b[0m\n\u001b[1;32m 439\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 440\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__DeviceProxy__read_attribute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mvalue\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mextract_as\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mExtractAs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mNumpy\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 441\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0m__check_read_attribute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_read_attribute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvalue\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mextract_as\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 442\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 443\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/tango/device_proxy.py\u001b[0m in \u001b[0;36m__check_read_attribute\u001b[0;34m(dev_attr)\u001b[0m\n\u001b[1;32m 155\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__check_read_attribute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdev_attr\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 156\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mdev_attr\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mhas_failed\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 157\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mDevFailed\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0mdev_attr\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_err_stack\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 158\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mdev_attr\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 159\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mDevFailed\u001b[0m: DevFailed[\nDevError[\n desc = Read value for attribute FPGA_mask_RW has not been updated\n origin = Device_3Impl::read_attributes_no_except\n reason = API_AttrValueNotSet\nseverity = ERR]\n\nDevError[\n desc = Failed to read_attribute on device lts/sdp/1, attribute FPGA_mask_RW\n origin = DeviceProxy::read_attribute()\n reason = API_AttributeFailed\nseverity = ERR]\n]" + "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/tango/device_proxy.py\u001b[0m in \u001b[0;36m__DeviceProxy__getattr\u001b[0;34m(self, name)\u001b[0m\n\u001b[1;32m 353\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mread_pipe\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 354\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 355\u001b[0;31m \u001b[0msix\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mraise_from\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mAttributeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcause\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 356\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 357\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/six.py\u001b[0m in \u001b[0;36mraise_from\u001b[0;34m(value, from_value)\u001b[0m\n", + "\u001b[0;31mAttributeError\u001b[0m: FPGA_mask_RW" ] } ], diff --git a/sbin/run_integration_test.sh b/sbin/run_integration_test.sh index a919b892767ef6a7d4eec83ba400cae79294190b..93b13300eb92afe2c95c7cb5c3292869019d9d96 100755 --- a/sbin/run_integration_test.sh +++ b/sbin/run_integration_test.sh @@ -8,8 +8,8 @@ fi # Start and stop sequence cd "$LOFAR20_DIR/docker-compose" || exit 1 -make stop device-sdp device-pcc device-sst sdptr-sim recv-sim -make start databaseds dsconfig jupyter elk +make stop device-sdp device-recv device-sst device-unb2 sdptr-sim recv-sim unb2-sim +make start databaseds dsconfig jupyter elk # Give dsconfig and databaseds time to start sleep 15 @@ -24,7 +24,7 @@ make start sdptr-sim recv-sim # Give the simulators time to start sleep 5 -make start device-sdp device-pcc device-sst +make start device-sdp device-recv device-sst device-unb2 # Give the devices time to start sleep 5