diff --git a/CDB/thijs_ConfigDb.json b/CDB/thijs_ConfigDb.json new file mode 100644 index 0000000000000000000000000000000000000000..b7e508732bf03b8919683b3cf2e34a52765a5ca3 --- /dev/null +++ b/CDB/thijs_ConfigDb.json @@ -0,0 +1,140 @@ +{ + "servers": { + "PCC": { + "1": { + "PCC": { + "LTS/PCC/1": { + "properties": { + "OPC_Server_Name": [ + "host.docker.internal" + ] + } + } + } + } + }, + "SDP": { + "1": { + "SDP": { + "LTS/SDP/1": { + "properties": { + "OPC_Server_Name": [ + "dop36.astron.nl" + ], + "OPC_Server_Port": [ + "4840" + ], + "OPC_Time_Out": [ + "5.0" + ] + } + } + } + } + }, + "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": [ + "4844" + ], + "OPC_Time_Out": [ + "5.0" + ] + } + } + } + } + }, + "APSCTL": { + "1": { + "APSCTL": { + "LTS/APSCTL/1": { + "attribute_properties": { + "Ant_mask_RW": { + "archive_period": [ + "600000" + ] + } + }, + "properties": { + "OPC_Server_Name": [ + "ltspi.astron.nl" + ], + "OPC_Server_Port": [ + "4844" + ], + "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 index e4c4dd38eaa9ab3aa18665093275749a54f3d94f..f1ea50f303e608230aaeba346dbd99bde8480773 100644 --- a/devices/APSCTL.py +++ b/devices/APSCTL.py @@ -15,18 +15,19 @@ 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 +from util.attribute_wrapper import attribute_wrapper +from util.hardware_device import hardware_device +from util.lofar_logging import device_logging_to_python, log_exceptions + +import numpy __all__ = ["APSCTL", "main"] +@device_logging_to_python({"device": "APSCTL"}) class APSCTL(hardware_device): """ @@ -68,71 +69,68 @@ class APSCTL(hardware_device): 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_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_ddr), N_fpga)) + 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_translator_busy_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_translator_busy_R"], datatype=numpy.bool_) + 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_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_monitor_rate_RW = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_monitor_rate_RW"], datatype=numpy.double, dims=(N_unb,), access=AttrWriteType.READ_WRITE) + UNB2_I2C_bus_STATUS_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_I2C_bus_STATUS_R"], datatype=numpy.double, dims=(N_unb,)) + 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_mask_RW = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_mask_RW"], datatype=numpy.double, dims=(N_unb,), access=AttrWriteType.READ_WRITE) + UNB2_Power_ON_OFF_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_Power_ON_OFF_R"], datatype=numpy.double, dims=(N_unb,), access=AttrWriteType.READ_WRITE) + + UNB2_FPGA_QSFP_CAGE_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_QSFP_CAGE_TEMP_R"], datatype=numpy.double, dims=(N_unb * N_qsfp,N_fpga)) + UNB2_FPGA_QSFP_CAGE_LOS_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_FPGA_QSFP_CAGE_LOS_R"], datatype=numpy.uint8, dims=(N_unb * N_qsfp,N_fpga)) + UNB2_FPGA_POL_HGXB_VOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "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:PCC", "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:PCC", "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:PCC", "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:PCC", "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:PCC", "2:UNB2_FPGA_POL_PGM_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_POL_FPGA_TXGXB_TEMP_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_FPGA_TXGXB_TEMP_R"], datatype=numpy.double, dims=(N_unb,N_fpga)) + UNB2_POL_FPGA_CORE_VOUT_R = attribute_wrapper(comms_annotation=["2:PCC", "2:UNB2_POL_FPGA_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_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,)) 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)) + 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_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,)) + # QualifiedName(2: UNB2_on) + # QualifiedName(2: UNB2_off) + @log_exceptions() def delete_device(self): """Hook to delete resources allocated in init_device. @@ -148,13 +146,15 @@ class APSCTL(hardware_device): # -------- # overloaded functions # -------- - def off(self): + @log_exceptions() + def configure_for_off(self): """ user code here. is called when the state is set to OFF """ # Stop keep-alive self.opcua_connection.stop() - def initialise(self): + @log_exceptions() + 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/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 6df8dec72cc803e3ecb155e54716521b30e62480..f3fbdcabacea0cd76760be08f2190f1134c76da2 100644 --- a/devices/SDP.py +++ b/devices/SDP.py @@ -65,39 +65,42 @@ class SDP(hardware_device): # Attributes # ---------- # SDP will switch from fpga_mask_RW to tr_fpga_mask_RW, offer both for now as its a critical flag - tr_fpga_mask_RW = attribute_wrapper(comms_annotation=["1:tr_fpga_mask_RW"], datatype=numpy.bool_, dims=(16,), access=AttrWriteType.READ_WRITE) - fpga_mask_RW = attribute_wrapper(comms_annotation=["1:fpga_mask_RW"], datatype=numpy.bool_, dims=(16,), access=AttrWriteType.READ_WRITE) - fpga_scrap_R = attribute_wrapper(comms_annotation=["1:fpga_scrap_R"], datatype=numpy.int32, dims=(2048,)) - fpga_scrap_RW = attribute_wrapper(comms_annotation=["1:fpga_scrap_RW"], datatype=numpy.int32, dims=(2048,), access=AttrWriteType.READ_WRITE) - fpga_status_R = attribute_wrapper(comms_annotation=["1:fpga_status_R"], datatype=numpy.bool_, dims=(16,)) - fpga_temp_R = attribute_wrapper(comms_annotation=["1:fpga_temp_R"], datatype=numpy.float_, dims=(16,)) - fpga_version_R = attribute_wrapper(comms_annotation=["1:fpga_version_R"], datatype=numpy.str_, dims=(16,)) - fpga_weights_R = attribute_wrapper(comms_annotation=["1:fpga_weights_R"], datatype=numpy.int16, dims=(16, 12 * 488 * 2)) - fpga_weights_RW = attribute_wrapper(comms_annotation=["1:fpga_weights_RW"], datatype=numpy.int16, dims=(16, 12 * 488 * 2), access=AttrWriteType.READ_WRITE) - fpga_processing_enable_RW = attribute_wrapper(comms_annotation=["1:fpga_processing_enable_RW"], datatype=numpy.bool_, dims=(16,), access=AttrWriteType.READ_WRITE) - fpga_processing_enable_R = attribute_wrapper(comms_annotation=["1:fpga_processing_enable_R"], datatype=numpy.bool_, dims=(16,)) - fpga_sst_offload_enable_RW = attribute_wrapper(comms_annotation=["1:fpga_sst_offload_enable_RW"], datatype=numpy.bool_, dims=(16,), access=AttrWriteType.READ_WRITE) - fpga_sst_offload_enable_R = attribute_wrapper(comms_annotation=["1:fpga_sst_offload_enable_R"], datatype=numpy.bool_, dims=(16,)) - fpga_sst_offload_dest_mac_RW = attribute_wrapper(comms_annotation=["1:fpga_sst_offload_dest_mac_RW"], datatype=numpy.str_, dims=(16,), access=AttrWriteType.READ_WRITE) - fpga_sst_offload_dest_mac_R = attribute_wrapper(comms_annotation=["1:fpga_sst_offload_dest_mac_R"], datatype=numpy.str_, dims=(16,)) - fpga_sst_offload_dest_ip_RW = attribute_wrapper(comms_annotation=["1:fpga_sst_offload_dest_ip_RW"], datatype=numpy.str_, dims=(16,), access=AttrWriteType.READ_WRITE) - fpga_sst_offload_dest_ip_R = attribute_wrapper(comms_annotation=["1:fpga_sst_offload_dest_ip_R"], datatype=numpy.str_, dims=(16,)) - fpga_sst_offload_dest_port_RW = attribute_wrapper(comms_annotation=["1:fpga_sst_offload_dest_port_RW"], datatype=numpy.uint16, dims=(16,), access=AttrWriteType.READ_WRITE) - fpga_sst_offload_dest_port_R = attribute_wrapper(comms_annotation=["1:fpga_sst_offload_dest_port_R"], datatype=numpy.uint16, dims=(16,)) - fpga_sdp_info_station_id_RW = attribute_wrapper(comms_annotation=["1:fpga_sdp_info_station_id_RW"], datatype=numpy.uint16, dims=(16,), access=AttrWriteType.READ_WRITE) - fpga_sdp_info_station_id_R = attribute_wrapper(comms_annotation=["1:fpga_sdp_info_station_id_R"], datatype=numpy.uint16, dims=(16,)) - fpga_sdp_info_observation_id_RW = attribute_wrapper(comms_annotation=["1:fpga_sdp_info_observation_id_RW"], datatype=numpy.uint32, dims=(16,), access=AttrWriteType.READ_WRITE) - fpga_sdp_info_observation_id_R = attribute_wrapper(comms_annotation=["1:fpga_sdp_info_observation_id_R"], datatype=numpy.uint32, dims=(16,)) - fpga_sdp_info_nyquist_sampling_zone_index_RW = attribute_wrapper(comms_annotation=["1:fpga_sdp_info_nyquist_sampling_zone_index_RW"], datatype=numpy.uint16, dims=(16,), access=AttrWriteType.READ_WRITE) - fpga_sdp_info_nyquist_sampling_zone_index_R = attribute_wrapper(comms_annotation=["1:fpga_sdp_info_nyquist_sampling_zone_index_R"], datatype=numpy.uint16, dims=(16,)) - fpga_sdp_info_subband_calibrated_flag_R = attribute_wrapper(comms_annotation=["1:fpga_sdp_info_subband_calibrated_flag_R"], datatype=numpy.uint16, dims=(16,)) - fpga_sdp_info_beamlet_scale_R = attribute_wrapper(comms_annotation=["1:fpga_sdp_info_beamlet_scale_R"], datatype=numpy.uint16, dims=(16,)) - - tr_busy_R = attribute_wrapper(comms_annotation=["1:tr_busy_R"], datatype=numpy.bool_) - # NOTE: typo in node name is 'tr_reload_W' should be 'tr_reload_RW' - tr_reload_RW = attribute_wrapper(comms_annotation=["1:tr_reload_W"], datatype=numpy.bool_, access=AttrWriteType.READ_WRITE) - tr_tod_R = attribute_wrapper(comms_annotation=["1:tr_tod_R"], datatype=numpy.uint64) - tr_uptime_R = attribute_wrapper(comms_annotation=["1:tr_uptime_R"], datatype=numpy.uint64) + tr_fpga_mask_RW = attribute_wrapper(comms_annotation=["2:tr_fpga_mask_RW"], datatype=numpy.bool_, dims=(16,), access=AttrWriteType.READ_WRITE) + fpga_mask_RW = attribute_wrapper(comms_annotation=["2:fpga_mask_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=(2048,)) + fpga_scrap_RW = attribute_wrapper(comms_annotation=["2:fpga_scrap_RW"], datatype=numpy.int32, dims=(2048,), access=AttrWriteType.READ_WRITE) + fpga_status_R = attribute_wrapper(comms_annotation=["2:fpga_status_R"], datatype=numpy.bool_, dims=(16,)) + fpga_temp_R = attribute_wrapper(comms_annotation=["2:fpga_temp_R"], datatype=numpy.float_, dims=(16,)) + fpga_version_R = attribute_wrapper(comms_annotation=["2:fpga_version_R"], datatype=numpy.str_, dims=(16,)) + fpga_weights_R = attribute_wrapper(comms_annotation=["2:fpga_weights_R"], datatype=numpy.int16, dims=(16, 12 * 488 * 2)) + fpga_weights_RW = attribute_wrapper(comms_annotation=["2:fpga_weights_RW"], datatype=numpy.int16, dims=(16, 12 * 488 * 2), access=AttrWriteType.READ_WRITE) + fpga_processing_enable_RW = attribute_wrapper(comms_annotation=["2:fpga_processing_enable_RW"], datatype=numpy.bool_, dims=(16,), access=AttrWriteType.READ_WRITE) + fpga_processing_enable_R = attribute_wrapper(comms_annotation=["2:fpga_processing_enable_R"], datatype=numpy.bool_, dims=(16,)) + fpga_sst_offload_enable_RW = attribute_wrapper(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_annotation=["2:fpga_sst_offload_enable_R"], datatype=numpy.bool_, dims=(16,)) + fpga_sst_offload_dest_mac_RW = attribute_wrapper(comms_annotation=["2:fpga_sst_offload_dest_mac_RW"], datatype=numpy.str_, dims=(16,), access=AttrWriteType.READ_WRITE) + fpga_sst_offload_dest_mac_R = attribute_wrapper(comms_annotation=["2:fpga_sst_offload_dest_mac_R"], datatype=numpy.str_, dims=(16,)) + fpga_sst_offload_dest_ip_RW = attribute_wrapper(comms_annotation=["2:fpga_sst_offload_dest_ip_RW"], datatype=numpy.str_, dims=(16,), access=AttrWriteType.READ_WRITE) + fpga_sst_offload_dest_ip_R = attribute_wrapper(comms_annotation=["2:fpga_sst_offload_dest_ip_R"], datatype=numpy.str_, dims=(16,)) + fpga_sst_offload_dest_port_RW = attribute_wrapper(comms_annotation=["2:fpga_sst_offload_dest_port_RW"], datatype=numpy.uint16, dims=(16,), access=AttrWriteType.READ_WRITE) + fpga_sst_offload_dest_port_R = attribute_wrapper(comms_annotation=["2:fpga_sst_offload_dest_port_R"], datatype=numpy.uint16, dims=(16,)) + fpga_sdp_info_station_id_RW = attribute_wrapper(comms_annotation=["2:fpga_sdp_info_station_id_RW"], datatype=numpy.uint16, dims=(16,), access=AttrWriteType.READ_WRITE) + fpga_sdp_info_station_id_R = attribute_wrapper(comms_annotation=["2:fpga_sdp_info_station_id_R"], datatype=numpy.uint16, dims=(16,)) + fpga_sdp_info_observation_id_RW = attribute_wrapper(comms_annotation=["2:fpga_sdp_info_observation_id_RW"], datatype=numpy.uint32, dims=(16,), access=AttrWriteType.READ_WRITE) + fpga_sdp_info_observation_id_R = attribute_wrapper(comms_annotation=["2:fpga_sdp_info_observation_id_R"], datatype=numpy.uint32, dims=(16,)) + fpga_sdp_info_nyquist_sampling_zone_index_RW = attribute_wrapper(comms_annotation=["2:fpga_sdp_info_nyquist_sampling_zone_index_RW"], datatype=numpy.uint16, dims=(16,), access=AttrWriteType.READ_WRITE) + fpga_sdp_info_nyquist_sampling_zone_index_R = attribute_wrapper(comms_annotation=["2:fpga_sdp_info_nyquist_sampling_zone_index_R"], datatype=numpy.uint16, dims=(16,)) + fpga_sdp_info_subband_calibrated_flag_R = attribute_wrapper(comms_annotation=["2:fpga_sdp_info_subband_calibrated_flag_R"], datatype=numpy.uint16, dims=(16,)) + fpga_sdp_info_beamlet_scale_R = attribute_wrapper(comms_annotation=["2:fpga_sdp_info_beamlet_scale_R"], datatype=numpy.uint16, dims=(16,)) + + tr_busy_R = attribute_wrapper(comms_annotation=["2:tr_busy_R"], datatype=numpy.bool_) + tr_reload_RW = attribute_wrapper(comms_annotation=["2:tr_reload_RW"], datatype=numpy.bool_, access=AttrWriteType.READ_WRITE) + tr_tod_R = attribute_wrapper(comms_annotation=["2:tr_tod_R"], datatype=numpy.uint64) + tr_uptime_R = attribute_wrapper(comms_annotation=["2:tr_uptime_R"], datatype=numpy.uint64) + + 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,)) + tr_software_version_R = attribute_wrapper(comms_annotation=["2:tr_software_version_R"], datatype=numpy.str_) def always_executed_hook(self): """Method always executed before any TANGO command is executed.""" @@ -120,14 +123,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.""" @@ -154,4 +157,3 @@ def main(args=None, **kwargs): if __name__ == '__main__': main() - diff --git a/devices/SNMP.py b/devices/SNMP.py new file mode 100644 index 0000000000000000000000000000000000000000..8b5d4a21c85cebbc562bd22baf5afec5e37b86d9 --- /dev/null +++ b/devices/SNMP.py @@ -0,0 +1,112 @@ +# -*- coding: utf-8 -*- +# +# This file is part of the PCC project +# +# +# +# Distributed under the terms of the APACHE license. +# See LICENSE.txt for more info. + +""" SNMP Device for LOFAR2.0 + +""" + +# PyTango imports +from tango.server import run +from tango.server import device_property +from tango import AttrWriteType + +# Additional import +from clients.SNMP_client import SNMP_client +from util.attribute_wrapper import attribute_wrapper +from util.hardware_device import hardware_device + +import numpy + +__all__ = ["SNMP", "main"] + + +class SNMP(hardware_device): + """ + + **Properties:** + + - Device Property + SNMP_community + - Type:'DevString' + SNMP_host + - Type:'DevULong' + SNMP_timeout + - Type:'DevDouble' + """ + + # ----------------- + # Device Properties + # ----------------- + + SNMP_community = device_property( + dtype='DevString', + mandatory=True + ) + + SNMP_host = device_property( + dtype='DevString', + mandatory=True + ) + + SNMP_timeout = device_property( + dtype='DevDouble', + mandatory=True + ) + + # ---------- + # 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_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_) + 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_) + + TCP_Curr_estab_R = attribute_wrapper(comms_annotation={"oids": "1.3.6.1.2.1.6.9.0", "type": "Gauge"}, datatype=numpy.int64) + + # inferred spectrum + if_index_R = attribute_wrapper(comms_annotation={"oids": "1.3.6.1.2.1.2.2.1.1"}, dims=(10,), datatype=numpy.int64) + + + # -------- + # overloaded functions + # -------- + def configure_for_initialise(self): + """ user code here. is called when the state is set to STANDBY """ + + # set up the SNMP ua client + self.snmp_manager = SNMP_client(self.SNMP_community, self.SNMP_host, self.SNMP_timeout, self.Fault, self) + + # map the attributes to the OPC ua comm client + for i in self.attr_list(): + i.set_comm_client(self.snmp_manager) + + self.snmp_manager.start() + + +# -------- +# Commands +# -------- + + +# ---------- +# Run server +# ---------- +def main(args=None, **kwargs): + """Main function of the PCC module.""" + return run((SNMP,), args=args, **kwargs) + + +if __name__ == '__main__': + main() diff --git a/devices/clients/SNMP_client.py b/devices/clients/SNMP_client.py new file mode 100644 index 0000000000000000000000000000000000000000..6230386aa300efbf9bf23ac1491b6a10354cba2f --- /dev/null +++ b/devices/clients/SNMP_client.py @@ -0,0 +1,160 @@ +from util.comms_client import CommClient +import snmp +import numpy +import traceback + +__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.Counter32: numpy.int64, + snmp.types.Gauge32: numpy.int64, + snmp.types.IpAddress: numpy.str_, +} + +snmp_types = { + "Integer": numpy.int64, + "Gauge": numpy.int64, + "TimeTick": numpy.int64, + "Counter32": numpy.int64, + "OctetString": numpy.str_, + "IpAddress": numpy.str_, + "OID": numpy.str_, +} + + +class SNMP_client(CommClient): + """ + messages to keep a check on the connection. On connection failure, reconnects once. + """ + + def start(self): + super().start() + + def __init__(self, community, host, timeout, fault_func, streams, try_interval=2): + """ + Create the SNMP and connect() to it + """ + super().__init__(fault_func, streams, try_interval) + + self.community = community + self.host = host + self.manager = snmp.Manager(community=bytes(community, "utf8")) + + # Explicitly connect + if not self.connect(): + # hardware or infra is down -- needs fixing first + fault_func() + return + + def connect(self): + """ + Try to connect to the client + """ + self.streams.debug_stream("Connecting to community: %s, host: %s", self.community, self.host) + + self.connected = True + return True + + def ping(self): + """ + ping the client to make sure the connection with the client is still functional. + """ + pass + + def _setup_annotation(self, annotation): + """ + This class's Implementation of the get_mapping function. returns the read and write functions + """ + + if isinstance(annotation, dict): + # check if required path inarg is present + if annotation.get('oids') is None: + ValueError("SNMP get attributes require an oid") + oids = annotation.get("oids") # required + else: + TypeError("SNMP attributes require a dict with oid(s)") + return + + dtype = annotation.get('type', None) + + return oids, dtype + + 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. + """ + + dim_x = attribute.dim_x + dim_y = attribute.dim_y + dtype = attribute.numpy_type + + return dim_x, dim_y, dtype + + def get_oids(self, x, y, in_oid): + + if x == 0: + x = 1 + if y == 0: + y = 1 + + nof_oids = x * y + + if nof_oids == 1: + # is scalar + if type(in_oid) is str: + # for ease of handling put single oid in a 1 element list + in_oid = [in_oid] + return in_oid + + elif type(in_oid) is list and len(in_oid) == nof_oids: + # already is an array and of the right length + return in_oid + elif type(in_oid) is list and len(in_oid) != nof_oids: + # already is an array but the wrong length. Unable to handle this + raise ValueError("SNMP oids need to either be a single value or an array the size of the attribute dimensions. got: {} expected: {}x{}={}".format(len(in_oid),x,y,x*y)) + else: + + return ["{}.{}".format(in_oid, i + 1) for i in range(nof_oids)] + + + 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 + """ + + # process the annotation + oids, dtype = self._setup_annotation(annotation) + + # get all the necessary data to set up the read/write functions from the attribute_wrapper + dim_x, dim_y, numpy_type = self.setup_value_conversion(attribute) + oids = self.get_oids(dim_x, dim_y, oids) + + def _read_function(): + vars = self.manager.get(self.host, *oids) + return [snmp_to_numpy_dict[type(i.value)](str(i.value)) for i in vars] + + if dtype is not None: + def _write_function(value): + if len(oids) == 1 and type(value) != list: + value = [value] + + for i in range(len(oids)): + self.manager.set(self.host, oids[i], snmp_types[dtype](value[i])) + else: + def _write_function(value): + if len(oids) == 1 and type(value) != list: + value = [value] + + for i in range(len(oids)): + self.manager.set(self.host, oids[i], value[i]) + + + # return the read/write functions + return _read_function, _write_function + + 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/clients/opcua_connection.py b/devices/clients/opcua_connection.py index f55922df8dba4ca5dbb6c78db5600a7287d5f9ad..85afdfa08ef2849b8434d95bbd5c38e467a91b6c 100644 --- a/devices/clients/opcua_connection.py +++ b/devices/clients/opcua_connection.py @@ -51,6 +51,7 @@ class OPCUAConnection(CommClient): fault_func() return + # determine namespace used try: if type(namespace) is str: @@ -64,6 +65,7 @@ class OPCUAConnection(CommClient): self.name_space_index = 2 self.obj = self.client.get_objects_node() + self.check_nodes() def _servername(self): return self.client.server_url.geturl() @@ -83,6 +85,21 @@ class OPCUAConnection(CommClient): self.streams.error_stream("Could not connect to server %s: %s", self._servername(), e) raise Exception("Could not connect to server %s", self._servername()) from e + def check_nodes(self): + """ + function purely for debugging/development only. Simply lists all top level nodes and the nodes below that + """ + + for i in self.obj.get_children(): + print(i.get_browse_name()) + for j in i.get_children(): + try: + print(j.get_browse_name(), j.get_data_type_as_variant_type()) + except: + print(j.get_browse_name()) + finally: + pass + def disconnect(self): """ @@ -160,7 +177,6 @@ class OPCUAConnection(CommClient): self.streams.debug_stream("connected OPC ua node {} of type {} to attribute with dimensions: {} x {} ".format(str(node_name)[:len(node_name)-1], str(ua_type)[len("VariantType."):], dim_x, dim_y)) except: pass - # return the read/write functions return prot_attr.read_function, prot_attr.write_function @@ -182,12 +198,19 @@ class ProtocolAttribute: """ value = numpy.array(self.node.get_value()) - if self.dim_y != 0: + if self.dim_y + self.dim_x == 1: + return numpy.array([value]) + elif self.dim_y != 0: value = numpy.array(numpy.split(value, indices_or_sections=self.dim_y)) + elif self.dim_y + self.dim_x == 1: + value = [numpy.array(value)] else: value = numpy.array(value) + return value + + def write_function(self, value): """ write_RW function diff --git a/devices/examples/HW_device_template.py b/devices/examples/HW_device_template.py index dd6cad99bd5824bd1b8a37a471aeb7796f32a05d..66b6bdb19c0b00703805dc2f76cce0d8208a36b2 100644 --- a/devices/examples/HW_device_template.py +++ b/devices/examples/HW_device_template.py @@ -17,6 +17,7 @@ from tango import AttrWriteType from util.attribute_wrapper import attribute_wrapper from util.hardware_device import hardware_device + __all__ = ["HW_dev"] @@ -55,24 +56,24 @@ class HW_dev(hardware_device): # -------- # overloaded functions # -------- - def fault(self): + def configure_for_fault(self): """ user code here. is called when the state is set to FAULT """ pass - def off(self): + def configure_for_off(self): """ user code here. is called when the state is set to OFF """ pass - def on(self): + def configure_for_on(self): """ user code here. is called when the state is set to ON """ pass - def standby(self): + def configure_for_standby(self): """ user code here. is called when the state is set to STANDBY """ pass - def initialise(self): + def configure_for_initialise(self): """ user code here. is called when the sate is set to INIT """ pass 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/docker-compose/Makefile b/docker-compose/Makefile index 693bc26cbe78a45ead288c49ed2929ca7944c920..f1b2cc5788c9f75cead9c8fdfaa1793e86f2cb81 100644 --- a/docker-compose/Makefile +++ b/docker-compose/Makefile @@ -2,30 +2,33 @@ MAKEPATH := $(abspath $(lastword $(MAKEFILE_LIST))) BASEDIR := $(notdir $(patsubst %/,%,$(dir $(MAKEPATH)))) +DOCKER_COMPOSE_ENV_FILE := $(abspath .env) COMPOSE_FILES := $(wildcard *.yml) -COMPOSE_FILE_ARGS := $(foreach yml,$(COMPOSE_FILES),-f $(yml)) +COMPOSE_FILE_ARGS := --env-file $(DOCKER_COMPOSE_ENV_FILE) $(foreach yml,$(COMPOSE_FILES),-f $(yml)) ATTACH_COMPOSE_FILE_ARGS := $(foreach yml,$(filter-out tango.yml,$(COMPOSE_FILES)),-f $(yml)) # If the first make argument is "start" or "stop"... ifeq (start,$(firstword $(MAKECMDGOALS))) - SERVICE_TARGET = true + SERVICE_TARGET = true else ifeq (stop,$(firstword $(MAKECMDGOALS))) - SERVICE_TARGET = true + SERVICE_TARGET = true else ifeq (attach,$(firstword $(MAKECMDGOALS))) - SERVICE_TARGET = true -ifndef NETWORK_MODE -$(error NETWORK_MODE must specify the network to attach to, e.g., make NETWORK_MODE=tangonet-powersupply ...) -endif -ifndef TANGO_HOST -$(error TANGO_HOST must specify the Tango database device, e.g., make TANGO_HOST=powersupply-databaseds:10000 ...) -endif + SERVICE_TARGET = true + ifndef NETWORK_MODE + $(error NETWORK_MODE must specify the network to attach to, e.g., make NETWORK_MODE=tangonet-powersupply ...) + endif + + ifndef TANGO_HOST + $(error TANGO_HOST must specify the Tango database device, e.g., make TANGO_HOST=powersupply-databaseds:10000 ...) + endif endif + ifdef SERVICE_TARGET - # .. then use the rest as arguments for the make target - SERVICE := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS)) - # ...and turn them into do-nothing targets - $(eval $(SERVICE):;@:) + # .. then use the rest as arguments for the make target + SERVICE := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS)) + # ...and turn them into do-nothing targets + $(eval $(SERVICE):;@:) endif # @@ -35,37 +38,41 @@ endif # time. # ifneq ($(CI_JOB_ID),) -NETWORK_MODE := tangonet-$(CI_JOB_ID) -CONTAINER_NAME_PREFIX := $(CI_JOB_ID)- + NETWORK_MODE := tangonet-$(CI_JOB_ID) + CONTAINER_NAME_PREFIX := $(CI_JOB_ID)- else -CONTAINER_NAME_PREFIX := -$(info Network mode cannot be host for the archiver! It won't work unless you set the env var CI_JOB_ID=local) + CONTAINER_NAME_PREFIX := + $(info Network mode cannot be host for the archiver! It won't work unless you set the env var CI_JOB_ID=local) endif ifeq ($(OS),Windows_NT) $(error Sorry, Windows is not supported yet) else - UNAME_S := $(shell uname -s) - ifeq ($(UNAME_S),Linux) - DISPLAY ?= :0.0 - NETWORK_MODE ?= host - XAUTHORITY_MOUNT := /tmp/.X11-unix:/tmp/.X11-unix - XAUTHORITY ?= /hosthome/.Xauthority - # /bin/sh (=dash) does not evaluate 'docker network' conditionals correctly - SHELL := /bin/bash - endif - ifeq ($(UNAME_S),Darwin) - IF_INTERFACE := $(shell scutil --nwi | grep 'Network interfaces:' | cut -d' ' -f3) - IP_ADDRESS := $(shell scutil --nwi | grep 'address' | cut -d':' -f2 | tr -d ' ' | head -n1) - DISPLAY := $(IP_ADDRESS):0 - # Make sure that Darwin, especially from macOS Catalina on, - # allows X access from our Docker containers. - ADD_TO_XHOST := $(shell xhost +$(IP_ADDRESS)) - # network_mode = host doesn't work on MacOS, so fix to the internal network - NETWORK_MODE ?= tangonet - XAUTHORITY_MOUNT := $(HOME)/.Xauthority:/hosthome/.Xauthority:ro - XAUTHORITY := /hosthome/.Xauthority - endif + UNAME_S := $(shell uname -s) + + ifeq ($(UNAME_S),Linux) + DISPLAY ?= :0.0 + NETWORK_MODE ?= host + XAUTHORITY_MOUNT := /tmp/.X11-unix:/tmp/.X11-unix + XAUTHORITY ?= /hosthome/.Xauthority + # /bin/sh (=dash) does not evaluate 'docker network' conditionals correctly + SHELL := /bin/bash + else ifeq ($(UNAME_S),Darwin) + IF_INTERFACE := $(shell scutil --nwi | grep 'Network interfaces:' | cut -d' ' -f3) + IP_ADDRESS := $(shell scutil --nwi | grep 'address' | cut -d':' -f2 | tr -d ' ' | head -n1) + DISPLAY := $(IP_ADDRESS):0 + # Make sure that Darwin, especially from macOS Catalina on, + # allows X access from our Docker containers. + ADD_TO_XHOST := $(shell xhost +$(IP_ADDRESS)) + # network_mode = host doesn't work on MacOS, so fix to the internal network + ifeq ($(NETWORK_MODE),) + NETWORK_MODE := tangonet + else + NETWORK_MODE := $(NETWORK_MODE) + endif + XAUTHORITY_MOUNT := $(HOME)/.Xauthority:/hosthome/.Xauthority:ro + XAUTHORITY := /hosthome/.Xauthority + endif endif # @@ -73,16 +80,33 @@ endif # machine rather than at the container. # ifeq ($(NETWORK_MODE),host) - TANGO_HOST := $(shell hostname):10000 - MYSQL_HOST := $(shell hostname):3306 + TANGO_HOST := $(shell hostname):10000 + MYSQL_HOST := $(shell hostname):3306 else - TANGO_HOST := $(CONTAINER_NAME_PREFIX)databaseds:10000 - MYSQL_HOST := $(CONTAINER_NAME_PREFIX)tangodb:3306 + ifeq ($(TANGO_HOST),) + TANGO_HOST := $(CONTAINER_NAME_PREFIX)databaseds:10000 + else + TANGO_HOST := $(TANGO_HOST) + endif + + ifeq ($(MYSQL_HOST),) + MYSQL_HOST := $(CONTAINER_NAME_PREFIX)tangodb:3306 + else + MYSQL_HOST := $(MYSQL_HOST) + endif endif -DOCKER_COMPOSE_ARGS := DISPLAY=$(DISPLAY) XAUTHORITY=$(XAUTHORITY) TANGO_HOST=$(TANGO_HOST) \ - NETWORK_MODE=$(NETWORK_MODE) XAUTHORITY_MOUNT=$(XAUTHORITY_MOUNT) TANGO_SKA_CONTAINER_MOUNT=$(TANGO_SKA_CONTAINER_MOUNT) TANGO_LOFAR_CONTAINER_MOUNT=$(TANGO_LOFAR_CONTAINER_MOUNT) 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) +DOCKER_COMPOSE_ARGS := DISPLAY=$(DISPLAY) \ + XAUTHORITY=$(XAUTHORITY) \ + TANGO_HOST=$(TANGO_HOST) \ + NETWORK_MODE=$(NETWORK_MODE) \ + XAUTHORITY_MOUNT=$(XAUTHORITY_MOUNT) \ + TANGO_SKA_CONTAINER_MOUNT=$(TANGO_SKA_CONTAINER_MOUNT) \ + TANGO_LOFAR_CONTAINER_MOUNT=$(TANGO_LOFAR_CONTAINER_MOUNT) \ + 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) .PHONY: up down minimal start stop status clean pull help @@ -132,4 +156,3 @@ clean: down ## clear all TANGO database entries help: ## show this help. @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' - diff --git a/docker-compose/device-pcc.yml b/docker-compose/device-pcc.yml index 73feab180ba03a8a6ae0abc11b89a460341fcf9a..026ceff8ded94fb2d6d8951e6f8b33b758dbb467 100644 --- a/docker-compose/device-pcc.yml +++ b/docker-compose/device-pcc.yml @@ -14,7 +14,11 @@ version: '2' services: device-pcc: - image: lofar-device-base + image: device-pcc + # build explicitly, as docker-compose does not understand a local image + # being shared among services. + build: + context: lofar-device-base container_name: ${CONTAINER_NAME_PREFIX}device-pcc network_mode: ${NETWORK_MODE} volumes: diff --git a/docker-compose/device-sdp.yml b/docker-compose/device-sdp.yml index fd98bfede32634c0ab380a0d2f4fe6fec0096267..30e069a5eb0d38c9ccb1e9dbe1ffaf678dd0627c 100644 --- a/docker-compose/device-sdp.yml +++ b/docker-compose/device-sdp.yml @@ -14,7 +14,11 @@ version: '2' services: device-sdp: - image: lofar-device-base + image: device-sdp + # build explicitly, as docker-compose does not understand a local image + # being shared among services. + build: + context: lofar-device-base container_name: ${CONTAINER_NAME_PREFIX}device-sdp network_mode: ${NETWORK_MODE} volumes: diff --git a/docker-compose/jupyter.yml b/docker-compose/jupyter.yml index 71daf48e08afed3ae2ba8c873b216561b9bde213..dbabfadba86064c724d9073f3077132716da5438 100644 --- a/docker-compose/jupyter.yml +++ b/docker-compose/jupyter.yml @@ -13,6 +13,8 @@ services: jupyter: build: context: jupyter + args: + CONTAINER_EXECUTION_UID: ${CONTAINER_EXECUTION_UID} container_name: ${CONTAINER_NAME_PREFIX}jupyter network_mode: ${NETWORK_MODE} volumes: diff --git a/docker-compose/jupyter/Dockerfile b/docker-compose/jupyter/Dockerfile index 97ef7ca63daa60331ae0e8dee8f5d70fa143be44..62fb7395184b9bc9f4540c8ad68acabc5dc26713 100644 --- a/docker-compose/jupyter/Dockerfile +++ b/docker-compose/jupyter/Dockerfile @@ -1,6 +1,10 @@ ARG VERSION=latest FROM nexus.engageska-portugal.pt/ska-docker/tango-itango:${VERSION} +# UID if the user that this container will run under. This is needed to give directories +# that are needed for temporary storage the proper owner and access rights. +ARG CONTAINER_EXECUTION_UID=1000 + RUN sudo pip3 install jupyter RUN sudo pip3 install ipykernel RUN sudo pip3 install jupyter_bokeh @@ -13,7 +17,6 @@ RUN sudo jupyter nbextension enable jupyter_bokeh --py --sys-prefix # Install profiles for ipython & jupyter COPY ipython-profiles /opt/ipython-profiles/ -RUN sudo chown tango.tango -R /opt/ipython-profiles COPY jupyter-kernels /usr/local/share/jupyter/kernels/ # Install patched jupyter executable @@ -27,5 +30,7 @@ ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /usr/ RUN sudo chmod +x /usr/bin/tini # Make sure Jupyter can write to the home directory -ENV HOME=/home/tango -RUN chmod a+rwx /home/tango +ENV HOME=/home/user +RUN sudo mkdir -p ${HOME} +RUN sudo chown ${CONTAINER_EXECUTION_UID} -R ${HOME} +RUN sudo chown ${CONTAINER_EXECUTION_UID} -R /opt/ipython-profiles 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 +}