diff --git a/CDB/thijs_ConfigDb.json b/CDB/thijs_ConfigDb.json new file mode 100644 index 0000000000000000000000000000000000000000..839a4e6d475d3f0783fb8a2bbda12e2163d93eb0 --- /dev/null +++ b/CDB/thijs_ConfigDb.json @@ -0,0 +1,108 @@ +{ + "servers": { + "PCC": { + "1": { + "PCC": { + "LTS/PCC/1": { + "properties": { + "OPC_Server_Name": [ + "host.docker.internal" + ] + } + } + } + } + }, + "SDP": { + "1": { + "SDP": { + "LTS/SDP/1": { + "properties": { + "OPC_Server_Name": [ + "host.docker.internal" + ] + } + } + } + } + }, + "example_device": { + "1": { + "example_device": { + "LTS/example_device/1": { + "attribute_properties": { + "Ant_mask_RW": { + "archive_period": [ + "600000" + ] + } + }, + "properties": { + "OPC_Server_Name": [ + "host.docker.internal" + ], + "OPC_Server_Port": [ + "4842" + ], + "OPC_Time_Out": [ + "5.0" + ] + } + } + } + } + }, + "ini_device": { + "1": { + "ini_device": { + "LTS/ini_device/1": { + "attribute_properties": { + "Ant_mask_RW": { + "archive_period": [ + "600000" + ] + } + }, + "properties": { + "OPC_Server_Name": [ + "host.docker.internal" + ], + "OPC_Server_Port": [ + "4842" + ], + "OPC_Time_Out": [ + "5.0" + ] + } + } + } + } + }, + "SNMP": { + "1": { + "SNMP": { + "LTS/SNMP/1": { + "attribute_properties": { + "Ant_mask_RW": { + "archive_period": [ + "600000" + ] + } + }, + "properties": { + "SNMP_community": [ + "public" + ], + "SNMP_host": [ + "192.168.178.17" + ], + "SNMP_timeout": [ + "5.0" + ] + } + } + } + } + } + } +} diff --git a/devices/APSCTL.py b/devices/APSCTL.py deleted file mode 100644 index e4c4dd38eaa9ab3aa18665093275749a54f3d94f..0000000000000000000000000000000000000000 --- a/devices/APSCTL.py +++ /dev/null @@ -1,187 +0,0 @@ -# -*- coding: utf-8 -*- -# -# This file is part of the SDP project -# -# -# -# Distributed under the terms of the APACHE license. -# See LICENSE.txt for more info. - -""" SDP Device Server for LOFAR2.0 - -""" - -# PyTango imports -from tango.server import run -from tango.server import device_property -from tango import AttrWriteType - -#attribute extention and hardware device imports -from src.attribute_wrapper import attribute_wrapper -from src.hardware_device import hardware_device -import numpy -# Additional import - -from clients.opcua_connection import OPCUAConnection - - -__all__ = ["APSCTL", "main"] - -class APSCTL(hardware_device): - """ - - **Properties:** - - - Device Property - OPC_Server_Name - - Type:'DevString' - OPC_Server_Port - - Type:'DevULong' - OPC_Time_Out - - Type:'DevDouble' - """ - - # ----------------- - # Device Properties - # ----------------- - - OPC_Server_Name = device_property( - dtype='DevString', - mandatory=True - ) - - OPC_Server_Port = device_property( - dtype='DevULong', - mandatory=True - ) - - OPC_Time_Out = device_property( - dtype='DevDouble', - mandatory=True - ) - - # ---------- - # Attributes - # ---------- - N_unb = 2 - N_fpga = 4 - N_ddr = 2 - N_qsfp = 6 - - - # Central CP per Uniboard - 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_Mask_RW = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_Mask_RW"], datatype=numpy.bool_, dims=(N_unb,), access=AttrWriteType.READ_WRITE) - # Central MP per Uniboard - UNB2_I2C_bus_OK_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_I2C_bus_OK_R"], datatype=numpy.bool_, dims=(N_unb,)) - UNB2_Front_Panel_LED_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_Front_Panel_LED_R"], datatype=numpy.uint8, 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,)) - - # monitor points per FPGA - UNB2_FPGA_DDR4_SLOT_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_DDR4_SLOT_TEMP_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) - 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_unb * N_qsfp,N_fpga)) - UNB2_FPGA_QSFP_CAGE_0_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_QSFP_CAGE_0_TEMP_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) - 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_unb,N_fpga)) - 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_unb,N_fpga)) - 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_unb,N_fpga)) - 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_unb,N_fpga)) - 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_unb,N_fpga)) - UNB2_FPGA_QSFP_CAGE_0_LOS_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_QSFP_CAGE_0_LOS_R"], datatype=numpy.uint8, dims=(N_unb,N_fpga)) - 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_unb,N_fpga)) - 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_unb,N_fpga)) - 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_unb,N_fpga)) - 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_unb,N_fpga)) - 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_unb,N_fpga)) - UNB2_FPGA_POL_CORE_VOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_CORE_VOUT_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) - UNB2_FPGA_POL_CORE_IOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_CORE_IOUT_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) - UNB2_FPGA_POL_CORE_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_CORE_TEMP_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) - UNB2_FPGA_POL_ERAM_VOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_ERAM_VOUT_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) - UNB2_FPGA_POL_ERAM_IOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_ERAM_IOUT_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) - UNB2_FPGA_POL_ERAM_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_ERAM_TEMP_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) - UNB2_FPGA_POL_RXGXB_VOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_RXGXB_VOUT_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) - UNB2_FPGA_POL_RXGXB_IOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_RXGXB_IOUT_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) - UNB2_FPGA_POL_RXGXB_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_RXGXB_TEMP_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) - UNB2_FPGA_POL_TXGXB_VOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_TXGXB_VOUT_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) - UNB2_FPGA_POL_TXGXB_IOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_TXGXB_IOUT_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) - UNB2_FPGA_POL_TXGXB_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_POL_TXGXB_TEMP_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) - UNB2_FPGA_POL_HGXB_VOUT_R = attribute_wrapper(comms_annotation=["2:UNB2_FPGA_POL_HGXB_VOUT_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) - UNB2_FPGA_POL_HGXB_IOUT_R = attribute_wrapper(comms_annotation=["2:UNB2_FPGA_POL_HGXB_IOUT_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) - UNB2_FPGA_POL_HGXB_TEMP_R = attribute_wrapper(comms_annotation=["2:UNB2_FPGA_POL_HGXB_TEMP_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) - UNB2_FPGA_POL_PGM_VOUT_R = attribute_wrapper(comms_annotation=["2:UNB2_FPGA_POL_PGM_VOUT_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) - UNB2_FPGA_POL_PGM_IOUT_R = attribute_wrapper(comms_annotation=["2:UNB2_FPGA_POL_PGM_IOUT_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) - UNB2_FPGA_POL_PGM_TEMP_R = attribute_wrapper(comms_annotation=["2:UNB2_FPGA_POL_PGM_TEMP_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) - - - 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 - # -------- - def off(self): - """ user code here. is called when the state is set to OFF """ - - # Stop keep-alive - self.opcua_connection.stop() - - def initialise(self): - """ user code here. is called when the sate is set to INIT """ - """Initialises the attributes and properties of the PCC.""" - - # set up the OPC ua client - self.OPCua_client = OPCUAConnection("opc.tcp://{}:{}/".format(self.OPC_Server_Name, self.OPC_Server_Port), "http://lofar.eu", self.OPC_Time_Out, self.Fault, self) - - # map an access helper class - for i in self.attr_list(): - try: - i.set_comm_client(self.OPCua_client) - except: - self.debug_stream("error in getting APSCTL attribute: {} from client".format(i)) - - self.OPCua_client.start() - - # -------- - # Commands - # -------- - -# ---------- -# Run server -# ---------- -def main(args=None, **kwargs): - """Main function of the SDP module.""" - return run((APSCTL,), args=args, **kwargs) - - -if __name__ == '__main__': - main() - diff --git a/devices/PCC.py b/devices/PCC.py index 266725ab410b9b56c6d326a61f22cd8e332d54d6..ff0ed0914f43272ab195b8a55f8f550e7fdd3ceb 100644 --- a/devices/PCC.py +++ b/devices/PCC.py @@ -117,13 +117,13 @@ class PCC(hardware_device): # overloaded functions # -------- @log_exceptions() - def off(self): + def configure_for_off(self): """ user code here. is called when the state is set to OFF """ # Stop keep-alive self.OPCua_client.stop() @log_exceptions() - def initialise(self): + def configure_for_initialise(self): """ user code here. is called when the state is set to INIT """ # Init the dict that contains function to OPC-UA function mappings. diff --git a/devices/SDP.py b/devices/SDP.py index 7db55b341b5a7a86bcef2b0e9f04a44639ad7d4b..2d14a96fc6f977749a920d7b1414a2febb7fe7b6 100644 --- a/devices/SDP.py +++ b/devices/SDP.py @@ -116,14 +116,14 @@ class SDP(hardware_device): # overloaded functions # -------- @log_exceptions() - def off(self): + def configure_for_off(self): """ user code here. is called when the state is set to OFF """ # Stop keep-alive self.opcua_connection.stop() @log_exceptions() - def initialise(self): + def configure_for_initialise(self): """ user code here. is called when the sate is set to INIT """ """Initialises the attributes and properties of the SDP.""" diff --git a/devices/clients/ini_client.py b/devices/clients/ini_client.py new file mode 100644 index 0000000000000000000000000000000000000000..2f4d714b57dd57d327795fe59fd6edf43eb4c9fa --- /dev/null +++ b/devices/clients/ini_client.py @@ -0,0 +1,192 @@ +from util.comms_client import CommClient +import configparser +import numpy + +__all__ = ["ini_client"] + + +numpy_to_ini_dict = { + numpy.int64: int, + numpy.double: float, + numpy.float64: float, + numpy.bool_: bool, + str: str +} + +numpy_to_ini_get_dict = { + numpy.int64: configparser.ConfigParser.getint, + numpy.double: configparser.ConfigParser.getfloat, + numpy.float64: configparser.ConfigParser.getfloat, + numpy.bool_: configparser.ConfigParser.getboolean, + str: str +} + +ini_to_numpy_dict = { + int: numpy.int64, + float: numpy.float64, + bool: numpy.bool_, + str: numpy.str_ +} + +import os + +class ini_client(CommClient): + """ + this class provides an example implementation of a comms_client. + Durirng initialisation it creates a correctly shaped zero filled value. on read that value is returned and on write its modified. + """ + + def start(self): + super().start() + + def __init__(self, filename, fault_func, streams, try_interval=2): + """ + initialises the class and tries to connect to the client. + """ + self.config = configparser.ConfigParser() + self.filename = filename + + super().__init__(fault_func, streams, try_interval) + + # Explicitly connect + if not self.connect(): + # hardware or infra is down -- needs fixing first + fault_func() + return + + def connect(self): + self.config_file = open(self.filename, "r") + + self.connected = True # set connected to true + return True # if successful, return true. otherwise return false + + def disconnect(self): + self.connected = False # always force a reconnect, regardless of a successful disconnect + self.streams.debug_stream("disconnected from the 'client' ") + + def _setup_annotation(self, annotation): + """ + this function gives the client access to the comm client annotation data given to the attribute wrapper. + The annotation data can be used to provide whatever extra data is necessary in order to find/access the monitor/control point. + + the annotation can be in whatever format may be required. it is up to the user to handle its content + example annotation may include: + - a file path and file line/location + - COM object path + + Annotations: + name: Required, the name of the ini variable + section: Required, the section of the ini variable + + """ + + # as this is an example, just print the annotation + self.streams.debug_stream("annotation: {}".format(annotation)) + name = annotation.get('name') + if name is None: + ValueError("ini client requires a variable `name` in the annotation to set/get") + section = annotation.get('section') + if section is None: + ValueError("requires a `section` specified in the annotation to open") + + return section, name + + + def _setup_value_conversion(self, attribute): + """ + gives the client access to the attribute_wrapper object in order to access all + necessary data such as dimensionality and data type + """ + + dim_y = attribute.dim_y + dim_x = attribute.dim_x + + dtype = attribute.numpy_type + + return dim_y, dim_x, dtype + + def _setup_mapping(self, name, section, dtype, dim_y, dim_x): + """ + takes all gathered data to configure and return the correct read and write functions + """ + + def read_function(): + self.config.read_file(self.config_file) + value = self.config.get(section, name) + + value = data_handler(value, dtype) + + if dim_y > 1: + # if data is an image, slice it according to the y dimensions + value = numpy.array(numpy.split(value, indices_or_sections=dim_y)) + + return value + + def write_function(value): + + if type(value) is list: + write_value = ", ".join([str(v) for v in value]) + + else: + write_value = str(value) + + self.config.read_file(self.config_file) + self.config.set(section, name, write_value) + fp = open(self.filename, 'w') + self.config.write(fp) + + return read_function, write_function + + def setup_attribute(self, annotation=None, attribute=None): + """ + MANDATORY function: is used by the attribute wrapper to get read/write functions. + must return the read and write functions + """ + + # process the comms_annotation + section, name = self._setup_annotation(annotation) + + # get all the necessary data to set up the read/write functions from the attribute_wrapper + dim_y, dim_x, dtype = self._setup_value_conversion(attribute) + + # configure and return the read/write functions + read_function, write_function = self._setup_mapping(name, section, dtype, dim_y, dim_x) + + # return the read/write functions + return read_function, write_function + +def data_handler(string, dtype): + value = [] + + if dtype is numpy.bool_: + # Handle special case for Bools + for i in string.split(","): + i = i.strip(" ") + if "True" == i: + value.append(True) + elif "False" == i: + value.append(False) + else: + raise ValueError("String to bool failed. String is not True/False, but is: '{}'".format(i)) + + value = dtype(value) + + elif dtype is numpy.str_: + for i in string.split(","): + val = numpy.str_(i) + value.append(val) + + value = numpy.array(value) + + else: + # regular case, go through the separator + for i in string.split(","): + i = i.replace(" ", "") + val = dtype(i) + value.append(val) + + + # convert values from buildin type to numpy type + value = dtype(value) + + return value diff --git a/devices/ini_device.py b/devices/ini_device.py new file mode 100644 index 0000000000000000000000000000000000000000..0b81611830e9ed14e76141ee583a1e6a7ddb8c60 --- /dev/null +++ b/devices/ini_device.py @@ -0,0 +1,128 @@ +# -*- coding: utf-8 -*- +# +# This file wraps around a tango device class and provides a number of abstractions useful for hardware devices. It works together +# +# Distributed under the terms of the APACHE license. +# See LICENSE.txt for more info. + +""" + +""" + +# PyTango imports +from tango.server import run +from tango.server import device_property +from tango import AttrWriteType +from tango import DevState +# Additional import +from util.attribute_wrapper import attribute_wrapper +from util.hardware_device import hardware_device + + +import configparser +import numpy + +from clients.ini_client import * + + +__all__ = ["ini_device"] + + +def write_ini_file(filename): + with open(filename, 'w') as configfile: + + config = configparser.ConfigParser() + config['scalar'] = {} + config['scalar']['double_scalar_R'] = '1.2' + config['scalar']['bool_scalar_R'] = 'True' + config['scalar']['int_scalar_R'] = '5' + config['scalar']['str_scalar_R'] = 'this is a test' + + config['spectrum'] = {} + config['spectrum']['double_spectrum_R'] = '1.2, 2.3, 3.4, 4.5' + config['spectrum']['bool_spectrum_R'] = 'True, True, False, False' + config['spectrum']['int_spectrum_R'] = '1, 2, 3, 4' + config['spectrum']['str_spectrum_R'] = '"a", "b", "c", "d"' + + config['image'] = {} + config['image']['double_image_R'] = '1.2, 2.3, 3.4, 4.5, 5.6, 6.7' + config['image']['bool_image_R'] = 'True, True, False, False, True, False' + config['image']['int_image_R'] = '1, 2, 3, 4, 5, 6' + config['image']['str_image_R'] = '"a", "b", "c", "d", "e", "f"' + + config.write(configfile) + + + +class ini_device(hardware_device): + """ + This class is the minimal (read empty) implementation of a class using 'hardware_device' + """ + + # ---------- + # Attributes + # ---------- + """ + attribute wrapper objects can be declared here. All attribute wrapper objects will get automatically put in a list (attr_list) for easy access + + example = attribute_wrapper(comms_annotation="this is an example", datatype=numpy.double, dims=(8, 2), access=AttrWriteType.READ_WRITE) + ... + + """ + double_scalar_RW = attribute_wrapper(comms_annotation={"section": "scalar", "name": "double_scalar_RW"}, datatype=numpy.double, access=AttrWriteType.READ_WRITE) + double_scalar_R = attribute_wrapper(comms_annotation={"section": "scalar", "name": "double_scalar_R"}, datatype=numpy.double) + bool_scalar_RW = attribute_wrapper(comms_annotation={"section": "scalar", "name": "bool_scalar_RW"}, datatype=numpy.bool_, access=AttrWriteType.READ_WRITE) + 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_) + + 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,)) + bool_spectrum_RW = attribute_wrapper(comms_annotation={"section": "spectrum", "name": "bool_spectrum_RW"}, datatype=numpy.bool_, dims=(4,), access=AttrWriteType.READ_WRITE) + 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,)) + + 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)) + bool_image_RW = attribute_wrapper(comms_annotation={"section": "image", "name": "bool_image_RW"}, datatype=numpy.bool_, dims=(3, 2), access=AttrWriteType.READ_WRITE) + 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)) + + # -------- + # overloaded functions + # -------- + def configure_for_initialise(self): + """ user code here. is called when the sate is set to INIT """ + """Initialises the attributes and properties of the PCC.""" + + # set up the OPC ua client + self.ini_client = ini_client("example.ini", self.Fault, self) + + # map an access helper class + for i in self.attr_list(): + i.set_comm_client(self.ini_client) + + self.ini_client.start() + + +# ---------- +# Run server +# ---------- +def main(args=None, **kwargs): + write_ini_file("example.ini") + + + """Main function of the hardware device module.""" + return run((ini_device,), args=args, **kwargs) + + +if __name__ == '__main__': + main() diff --git a/devices/test_device.py b/devices/test_device.py index 5659da595706cded1e786a479a12238d723e5f1c..6a62907112ea1cf081436285aa0d21532ba24d0a 100644 --- a/devices/test_device.py +++ b/devices/test_device.py @@ -64,7 +64,7 @@ class test_device(hardware_device): # -------- # overloaded functions # -------- - def initialise(self): + def configure_for_initialise(self): """ user code here. is called when the sate is set to INIT """ """Initialises the attributes and properties of the PCC.""" diff --git a/devices/util/archiver.py b/devices/util/archiver.py old mode 100755 new mode 100644 diff --git a/devices/util/get_internal_attribute_history.py b/devices/util/get_internal_attribute_history.py old mode 100755 new mode 100644 diff --git a/devices/util/hardware_device.py b/devices/util/hardware_device.py index 3dea7b7c06c5fea821895f958823ff80e3aafa72..e0c9154c703a7cb82c42e9cdd7db76d68a011e05 100644 --- a/devices/util/hardware_device.py +++ b/devices/util/hardware_device.py @@ -85,7 +85,7 @@ class hardware_device(Device): self.set_state(DevState.INIT) self.setup_value_dict() - self.initialise() + self.configure_for_initialise() self.set_state(DevState.STANDBY) @@ -100,7 +100,7 @@ class hardware_device(Device): :return:None """ - self.on() + self.configure_for_on() self.set_state(DevState.ON) @command() @@ -119,7 +119,7 @@ class hardware_device(Device): # Turn off self.set_state(DevState.OFF) - self.off() + self.configure_for_off() # Turn off again, in case of race conditions through reconnecting self.set_state(DevState.OFF) @@ -138,18 +138,18 @@ class hardware_device(Device): :return:None """ - self.fault() + self.configure_for_fault() self.set_state(DevState.FAULT) # functions that can be overloaded - def fault(self): + def configure_for_fault(self): pass - def off(self): + def configure_for_off(self): pass - def on(self): + def configure_for_on(self): pass - def initialise(self): + def configure_for_initialise(self): pass def always_executed_hook(self): diff --git a/devices/util/lofar2_config.py b/devices/util/lofar2_config.py old mode 100755 new mode 100644 diff --git a/devices/util/lofar_logging.py b/devices/util/lofar_logging.py index aa7d3633138679c63fd1934cf8d5638df7b1cedf..4bedad018047614c16d65b75816ac12dc7dbd7d0 100644 --- a/devices/util/lofar_logging.py +++ b/devices/util/lofar_logging.py @@ -1,5 +1,6 @@ import logging from functools import wraps +import sys # Always also log the hostname because it makes the origin of the log clear. import socket @@ -14,7 +15,7 @@ def configure_logger(logger: logging.Logger, log_extra=None): # log to the tcp_input of logstash in our ELK stack handler = AsynchronousLogstashHandler("elk", 5959, database_path='pending_log_messages.db') - # configure log messages + # configure log messages formatter = LogstashFormatter(extra=log_extra, tags=["python", "lofar"]) handler.setFormatter(formatter) diff --git a/devices/util/startup.py b/devices/util/startup.py old mode 100755 new mode 100644 index f98097f994afc340fdb168311bcb524445658f1d..0f4bcbe702b1bd1edb873234763d56455b6009b4 --- a/devices/util/startup.py +++ b/devices/util/startup.py @@ -34,4 +34,3 @@ def startup(device: str, force_restart: bool): else: print("Device {} has successfully reached ON state.".format(device)) return proxy - diff --git a/jupyter-notebooks/ini_device.ipynb b/jupyter-notebooks/ini_device.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..ba365f263ca35e627b0430f26a02d53af059333a --- /dev/null +++ b/jupyter-notebooks/ini_device.ipynb @@ -0,0 +1,238 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 128, + "id": "waiting-chance", + "metadata": {}, + "outputs": [], + "source": [ + "import time\n", + "import numpy" + ] + }, + { + "cell_type": "code", + "execution_count": 146, + "id": "moving-alexandria", + "metadata": {}, + "outputs": [], + "source": [ + "d=DeviceProxy(\"LTS/ini_device/1\")" + ] + }, + { + "cell_type": "code", + "execution_count": 198, + "id": "ranking-aluminum", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Device is now in on state\n" + ] + } + ], + "source": [ + "state = str(d.state())\n", + "\n", + "if state == \"OFF\":\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" + ] + }, + { + "cell_type": "code", + "execution_count": 199, + "id": "beneficial-evidence", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "double_scalar_RW [0.]\n", + "double_scalar_R [1.2]\n", + "bool_scalar_RW [False]\n", + "bool_scalar_R [ True]\n", + "int_scalar_RW [0]\n", + "int_scalar_R [5]\n", + "str_scalar_RW ('',)\n", + "str_scalar_R ('this is',)\n", + "double_spectrum_RW [0. 0. 0. 0.]\n", + "double_spectrum_R [1.2 2.3 3.4 4.5]\n", + "bool_spectrum_RW [False False False False]\n", + "bool_spectrum_R [ True True False False]\n", + "int_spectrum_RW [0 0 0 0]\n", + "int_spectrum_R [1 2 3 4]\n", + "str_spectrum_RW ('', '', '', '')\n", + "str_spectrum_R ('\"a\"', ' \"b\"', ' \"c\"', ' \"d\"')\n", + "double_image_RW [[0. 0. 0.]\n", + " [0. 0. 0.]]\n", + "double_image_R [[1.2 2.3 3.4]\n", + " [4.5 5.6 6.7]]\n", + "bool_image_RW [[False False False]\n", + " [False False False]]\n", + "bool_image_R [[ True True False]\n", + " [False True False]]\n", + "int_image_RW [[0 0 0]\n", + " [0 0 0]]\n", + "int_image_R [[1 2 3]\n", + " [4 5 6]]\n", + "str_image_RW (('', '', ''), ('', '', ''))\n", + "str_image_R (('\"a\"', ' \"b\"', ' \"c\"'), (' \"d\"', ' \"e\"', ' \"f\"'))\n", + "State <function __get_command_func.<locals>.f at 0x7f3efee95c80>\n", + "Status <function __get_command_func.<locals>.f at 0x7f3efee95c80>\n" + ] + } + ], + "source": [ + "attr_names = d.get_attribute_list()\n", + "\n", + "for i in attr_names:\n", + " try:\n", + " exec(\"print(i, d.{})\".format(i))\n", + " except:\n", + " pass\n" + ] + }, + { + "cell_type": "code", + "execution_count": 93, + "id": "sharing-mechanics", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([0])" + ] + }, + "execution_count": 93, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "d.int_scalar_RW" + ] + }, + { + "cell_type": "code", + "execution_count": 203, + "id": "2f03759a", + "metadata": {}, + "outputs": [], + "source": [ + "d.str_image_RW = [[\"1\", \"2\", \"3\"],[\"4\", \"5\", \"6\"]]" + ] + }, + { + "cell_type": "code", + "execution_count": 204, + "id": "3187f3bb", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(('1', '2', '3'), ('4', '5', '6'))" + ] + }, + "execution_count": 204, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "d.str_image_RW" + ] + }, + { + "cell_type": "code", + "execution_count": 192, + "id": "eb406dce", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"['a', 'b', 'c', 'd', 'e', 'f']\"" + ] + }, + "execution_count": 192, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numpy.str_([\"a\", \"b\", \"c\", \"d\", \"e\", \"f\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 197, + "id": "7b270085", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "6" + ] + }, + "execution_count": 197, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "array = []\n", + "string = '\"a\", \"b\", \"c\", \"d\", \"e\", \"f\"'\n", + "\n", + "for i in string.split(\",\"):\n", + " value = numpy.str_(i)\n", + " array.append(value)\n", + "\n", + "len(array)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "69ecc437", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "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 +}